%****************************************************************
%                 A Little LIST PROCESSING
% 
%  Lists are an important and fundamental data structure in
%         any practical LP language like PROLOG.
%****************************************************************

:- ensure_loaded('utilities').


%==========================================================
% First element of a list
%==========================================================

first([First | Rest],First).

%==========================================================
%
% An element is a member of a list
%  if it is the first element or if it is
%  a member of the Tail of the list.
%
%==========================================================
my_member(H,[H | _]).
my_member(H,[_ | T]) :- my_member(H,T).


%==========================================================
% Another version of member. How is it
%  different.
%==========================================================
isMember(H,[H | _]) :- !.
isMember(H,[_ | T]) :- isMember(H,T).


%==========================================================
% insert
%
%  -True for List,X,NewList where NewList
%     is List with X inserted in any position.
%  -There are as many solutions are there are positions
%    in List. 
%
%  -Terminates because not(=([],[H | T]]).
%
%  -Trying calling with only the last argument bound
%  -Tring calling with first two arg. bound.
%==========================================================

%
%True of X is in the first position
%
insert(L,X,[X | L]).

%
%True of X is in the first position of any "tail" of Input List
%
insert([H | T],X,[H | L]) :- 
    insert(T,X,L).

%==========================================================
% Generate Permutations of a List
%
%   - A Permutation of a list is a permutation
%     of the tail with the first inserted in the tail
%     at every possible position.
%
% Recursively traverses the list til the end
% as it builds the list back up it permutes each added
% element using insert.
%
% Example Trace building list back up: calls to insert
%   		[],c--->[c]
%
%              	[c],b-->[b,c],[c,b]
%
%		[b,c],a-->[a,b,c],[b,a,c],[b,c,a]
%		[c,b],a-->[c,b,a],[c,a,b],[c,b,a]
%
% Dummy gets "counted down" to ensure
%   termination in case first arg is a variable.
%
%
%==========================================================
perm(Xs,Ys) :-
    perm(Xs,Ys,Ys).

perm([],[],[]).
perm([X | Xs],Y1s,[_ | Dummy]) :-  	
       perm(Xs,Ys,Dummy), %
       insert(Ys,X,Y1s).  %


%==========================================================
% Are two lists permutations of one another
%==========================================================
isPerm([],[]).
isPerm([X | Xs],Ys) :-
         isMember(X,Ys),
         del(X,Ys,Y1s),
         isPerm(Xs,Y1s).

%==========================================================
% Delete an element from a list.
%==========================================================
del(X,[],_) :- fail,!.
del(X,[X | Ys],Ys) :- !.
del(X,[H | Ys],[H | Y1s]) :-
        del(X,Ys,Y1s).
          

%==========================================================
% The Length of a list is one plus the length
%    of its tail.
%==========================================================
list_len([],0).
list_len([_|Tail],N) :-
     list_len(Tail,M),
     N is M + 1.

%==========================================================
% Appending Two Lists.
%==========================================================
%Base Case
%
%A list L is the empty list appended to L
%
appnd([],L,L).

%Inductive Step
%
% A new list with the Head H and the tail TL is
% the result of appending a list with the head H and the tail T
% to a list L where TL is the result of appending the tail of the first list T to L.
%
appnd([H | T],L,[H | TL]) :-
           appnd(T,L,TL).


%==========================================================
% Reversing a List with simplicity.
%
% The Reverse of a list can be
%  described as the First attached to the end of 
%  the reverse of the Rest
%==========================================================
my_reverse([],[]).
my_reverse([First | Rest],Rev) :-
	my_reverse(Rest,RevRest),
	append(RevRest,[First],Rev).

%==========================================================
% Reversing a List with Expertise
%
%   A PROLOG naturally traverses the list "PUSH" the
%    the elements on another list.
%==========================================================
better_reverse(L,ReversedL) :-
	better_reverse(L,[],ReversedL).

better_reverse([],Reversed,Reversed).
better_reverse([X | Xs],Stack,Reversed) :- 
	better_reverse(Xs,[X | Stack],Reversed).


%==========================================================
% Build a list of a given size
%==========================================================
build_list(Size,Element,List) :-
	build_list(Size,Element,[],List).

build_list(0,_,List,List) :- !.
build_list(Size,Element,Stack,List) :-
	Size > 0,
	S is Size - 1,
	build_list(S,Element,[Element | Stack],List).

	
/*
Try These queries and compare results
	build_list(200,x,L),ms(my_reverse(L,R),Time).

	build_list(200,x,L),ms(better_reverse(L,R),Time).
*/
	