% list library 
:- module(list,[last/2,butlast/2,second/2,cons/3,
                nth/3,nth0/3,iota/3,take/3,drop/3,make_list/3,reverse/2,
                remove_at/3,insert_at/4,qsort/2,permutation/2,flatten/2,min_list/2,max_list/2,same_set/2]).

last([],[]).
last([X],X).
last([X|Xs],Y) :-
    last(Xs,Y).

butlast([],[]).
butlast([X],[]).
butlast([X|Xs],[X|Y]) :-
    butlast(Xs,Y).

cons(X,[],[X]).
cons(X,Y,[X|Y]).

second([],[]).
second([_,X|_],X).

nth(N,[],[]).
nth(1,[X|Xs],X).
nth(N,[X|Xs],Y) :-
    N1 is N-1,
    nth(N1,Xs,Y).

nth0(N,[],[]).
nth0(0,[X|Xs],X).
nth0(N,[X|Xs],Y) :-
    N1 is N-1,
    nth0(N1,Xs,Y).

iota(S,S,[S]).
iota(S,E,[S|L]) :-
    S1 is S+1,
    iota(S1,E,L).

take(0,L,[]).
take(N,[L|Ls],[L|Y]) :-
    N1 is N-1,
    take(N1,Ls,Y).

drop(0,L,L).
drop(N,[L|Ls],Y) :-
    N1 is N-1,
    drop(N1,Ls,Y).

make_list(0,X,[]).
make_list(N,X,[X|Y]) :-
    N1 is N-1,
    make_list(N1,X,Y).

reverse([],[]).
reverse([X|Xs],Y) :-
    reverse(Xs,Y1),
    append(Y1,[X],Y).

remove_at(0,[X|Xs],Xs).
remove_at(N,[X|Xs],[X|Y]) :-
    N1 is N-1,
    remove_at(N1,Xs,Y).

insert_at(0,X,L,[X|L]).
insert_at(N,X,[L|Ls],[L|Y]) :-
    N1 is N-1,
    insert_at(N1,X,Ls,Y).

qsort([], []).
qsort([Pivot|Rest], Sorted) :-
    partition(Pivot, Rest, Left, Right), 
    qsort(Left, SortedLeft),          
    qsort(Right, SortedRight),       
    append(SortedLeft, [Pivot|SortedRight], Sorted). 

partition(_, [], [], []). 
partition(Pivot, [H|T], [H|Left], Right) :-
    H =< Pivot,  
    partition(Pivot, T, Left, Right).
partition(Pivot, [H|T], Left, [H|Right]) :-
    H > Pivot,  
    partition(Pivot, T, Left, Right).

permutation([], []).
permutation(L, [X|L2]) :-
    del(X, L, L1),
    permutation(L1, L2).

del(X, [X|L], L).
del(X, [Y|L], [Y|L1]) :-
    del(X, L, L1).

flatten([],[]).
flatten([L|Ls],[L|Y]) :-
    atomic(L),
    flatten(Ls,Y).
flatten([L|Ls],Z) :-
    list(L),
    flatten(L,Y1),
    flatten(Ls,Y2),
    append(Y1,Y2,Z).

min_list([L],L).
min_list([L|Ls],L) :-
    min_list(Ls,X),
    L < X.
min_list([L|Ls],X) :-
    min_list(Ls,X),
    L >= X.

max_list([L],L).
max_list([L|Ls],L) :-
    max_list(Ls,X),
    L > X.
max_list([L|Ls],X) :-
    max_list(Ls,X),
    L =< X.

same_set(L1,L2) :-
    sort(L1,S1),
    sort(L2,S2),
    S1 = S2.