%% Let's write the following list-processing func:

/*  [+,+,-]   
  remove(L,Itm,L1): L1 = L, but L1 has one copy of Itm removed.
    (Note that Itm *must* occur in L, to satisfy this.)
*/
% tests:
%   remove( [3], 3, [] ).
%   remove( [3,4,5], 3, [4,5] ).
%   remove( [3,4,5], 4, [3,5] ).
%   remove( [3,4,5], 5, [3,4] ).
%   remove( [3,4,3], 3, [4,3] ).
%
% N.B. the tests above don't capture/define all we'd want,
%    e.g.      remove([3,4,5],  3, X)  should resolve to ONLY   X = [4,5]   and no other sol'ns
%    likewise  remove([3,4,5], 99, X)  should have NO solutions

% use "\==" for not-equals -- this works on both numbers *and* symbols.




%%%%%%%%%%%%%%
% Sorting:
% We'll just provide the 'spec' for sorting:

% Tests:
%mysort( [], [] ).
%mysort( [5,3,4],  [3,4,5] ).

% What's wrong with this def'n?
%
% L2 is a sorted version of L1 if:
%  - L2 is in ascending order (er, non-decreasing).



% Btw, we can write `nondecreasing`:

% tests:
%    nondecreasing([]).
%    nondecreasing([3]).
%    nondecreasing([3,4]).
%    nondecreasing([3,4,5]).

%%% TODO:  write nondecreasing

nondecreasing([]).
nondecreasing([_]).
nondecreasing([Frst,Scnd|Rst]) :- Frst =< Scnd, nondecreasing([Scnd|Rst]).




% So if we have 
%.  badsort(L1,L2) :- nondecreasing(L2).
% what do we get for solving `badsort([3,4,5],X)` ?

%%% TODO: try it

% D'oh! So L2 being nondecreasing isn't *enough* to be a sorted version of L1.
% So we need to add a criterion:
%
% L2 is a sorted version of L1 if:
%  - L2 is in ascending order (er, non-decreasing).
%  - L1 is a permutation (re-arrangement) of L2, and

scrapSort(L1,L2) :- permutation(L1,L2), nondecreasing(L2).
 



/* [+,-]
   permutation(L1,L2): L2 is a permutation of L1
 */
permutation([],[]).
permutation(X,[F|R]) :- remove1(X,F,Z), permutation(Z,R).


/* Note: Writing `permutation` looks simple once it's done, 
 *  but is actually kinda tricky to come up with.
 * A couple of false-starts (and explanations) are at:
 *   http://www.radford.edu/~itec380/2009fall-ibarland/Lectures/lect12c.P
 */





% Reflecting on scrapSort:
%   Consider the *order* of prolog's solutions when we try:
%   permutation([3,4,5],X).
%   permutation([5,4,3],X).
% How *many* permutations does scrapSort([3,4,5],X) look at?
% How about scrapSort([5,4,3],X) ?
%
% Now, consider this problem amplified:
%     scrapSort([8,7,6,5,4,3,2,1],X).  runs okay. But
% But scrapSort([10,9,8,7,6,5,4,3,2,1],X).  takes a moment (~2.5s).
%     scrapSort([11,10,9,8,7,6,5,4,3,2,1],X).  takes too long (~27s).
% 
%
% The name "scrapSort" was chosen because its performance
% is a piece of scrap: O(n!).





%%%%%%%%%%%%%%%%%%%%

% Ironically, 'quicksort' is more straightforward than 'scrapsort',
% even though we specify *how* to sort rather than *what sorted means*.
% (which is the *opposite* of our rationale of what 'descriptive languages' are for).

qsort([],[]).
qsort([F|R],L2) :- partition([F|R],F,Sm,Eq,Bg), 
                   qsort(Sm,SmSorted), 
                   qsort(Bg,BgSorted), 
                   appendThree(SmSorted,Eq,BgSorted,L2).

/* [+,+,-,-,-]
   partition(Lst,Pivot,Smalls,Equals,Larges) :
   partition Lst into those elements that are smaller, equal-to, and larger than Pivot.
   */
partition([],_,[],[],[]).
partition([F|R],Pivot,Smallers,Equals,Biggers) :- 
  F<Pivot,
  Smallers = [F|SmallerRest],
  partition(R,Pivot,SmallerRest,Equals,Biggers).
partition([F|R],Pivot,Smallers,Equals,Biggers) :- 
  F=Pivot, 
  Equals = [F|EqualRest],
  partition(R,Pivot,Smallers,EqualRest,Biggers).
partition([F|R],Pivot,Smallers,Equals,Biggers) :- 
  F>Pivot, 
  Biggers = [F|BiggerRest],
  partition(R,Pivot,Smallers,Equals,BiggerRest).

/* N.B. You can remove the '=' terms from the above lines,
   reducing the code by 4 lines.  Left in for clarity (?).
  */

% Note that we partition into *three* sections,
% so that we always *reduce* the size of a partition
% (even if our pivot is the maximum value).
% Study the sol'n above, to see how the Prolog
% makes sure we always recur on smaller lists (even
% when the pivot is the maximum (or minimum) value.






/* appendThree(L1,L2,L3,All) : All is the elements of L1 appended to L2 appended to L3. */
appendThree(L1,L2,L3,All) :- append(L1,L2,L1and2), append(L1and2,L3,All).

/* The following is the same as the append/3 included with [basics]. */
append([],L2,L2).
append([F|R],L2,[F|AllRest]) :- append(R,L2,AllRest).
