%% ----------------------------------------
%% ----------------------------------------
%% Atenție la antetele predicatelor și la prefixul parametrilor
%% p(+Arg1, -Arg2, ?Arg3)
%%  * Arg1 va fi instanțiat atunci când se va încerca satisfacerea p/3
%%  * Arg2 se va instanția odată cu satisfacerea p/3
%%  * Arg3 va putea fi instanțiat sau nu atunci când se va satisface p/3:
%%
%% Interogați Prolog cu "checkXX." pentru a verifica rezolvarea exercițiului XX.
%% Pentru a vedea progresul general, trimiteți scopul "check_all".
%% ----------------------------------------
%% ----------------------------------------

%% rmDup/2
%% rmDup(+inList, -outList)
%% Elimia duplicatele din inList, rezultatul este intors in outList prin
%% unificare.

rmDup(_, _) :- fail.

check1:-
	rmDup([], R1), R1 == [], write('.'),
	rmDup([1,2,2,4,2,5,9],R), R == [1,4,2,5,9], write('.'),
	nl, writeln('rmDup rezolvat corect!'), !.

%% ----------------------------------------

%% rmVal/3
%% rmVal(+inList, -outList)

rmVal(_, _, _) :- fail.

check2:-
	rmVal([], 2, X), X == [], write('.'),
	L1 = [1,2,3,4], rmVal(L1, 0, R1), R1 == L1, write('.'),
	rmVal([1, 2, 2, 4, 2, 5, 9], 2, R), R == [1, 4, 5, 9], write('.'),
	nl, writeln('rmVal rezolvat corect!'), !.

%% ----------------------------------------

%% my_reverse/2
%% my_reverse(?List, ?RevList)
%% 'RevList' este o listă ce conține elementele listei 'List' în ordine inversă.
%% Regulile pot conține și predicatul append (predefinit in prolog).

my_reverse(_, _):-fail.

check3a:-
        my_reverse([], []), write('.'),
        my_reverse([1,2,3], [3,2,1]), write('.'),
        my_reverse([1,2,3], Rev3), Rev3 == [3,2,1], write('.'),
        my_reverse(List4, [3,2,1]), List4 == [1,2,3], write('.'),
        my_reverse([1,X2,X3], [3,2,X1]), X1 == 1, X2 == 2, X3 == 3, write('.'),
        nl, writeln('my_reverse rezolvat corect!'),
        !.

%% ----------------------------------------

%% my_reverse_acc/3
%% my_reverse_acc(?List, ?Acc, ?RevList)
%% 'RevList' este o listă ce conține elementele listei 'List' în ordine inversă
%% și elementele listei 'Acc'.
%% (Indicație: 'Acc' se va comporta precum un acumulator)
%% Regulile nu trebuie să conține alte predicate (în afară de "cut" și ",").

my_reverse_acc(_, _, _):- fail.

check3b:-
        my_reverse_acc([], [], []), write('.'),
        my_reverse_acc([1,2,3], [0], [3,2,1,0]), write('.'),
        my_reverse_acc([1,2,3], [0], Rev3), Rev3 == [3,2,1,0], write('.'),
        my_reverse_acc(List4, [0], [3,2,1,0]), List4 == [1,2,3], write('.'),
        my_reverse_acc([X2,1], [3], [X1,2,3]), X1 == 1, X2 == 2, write('.'),
        nl, writeln('my_reverse cu acumulator rezolvat corect!'),
        !.

check3:-
        check3a, check3b.

%% ----------------------------------------

%% extract/4
%% extract(+List, +Start, +End, -Range)
%% 'Range' reprezintă lista formată din elementele listei 'List' aflate
%% pe pozițiile din intervalul 'Start' .. 'End'. 'Start' va fi întotdeauna
%% mai mic decât 'End'. Primul element se află pe poziția 0. Dacă 'End'
%% depășește lungimea listei, 'Range' va conține toate elementele de la 'Start'
%% la finalul listei.

extract(_, _, _, _):- fail.

check4:-
        extract([a,b,c,d,e], 0, 3, [a,b,c,d]), write('.'),
        extract([a,b,c,d,e], 1, 10, [b,c,d,e]), write('.'),
        extract([a,b,C,D,e], 2, 4, [c,d,e]), C == c, D == d, write('.'),
        extract([a,b,C1,D1,e], 2, 4, [C2,D2,e]), C1 == C2, D1 == D2, write('.'),
        extract([a,b,c,_,d], 3, 4, [X,X]), write('.'),
        nl, writeln('extract rezolvat corect!'),
        !.

%% ----------------------------------------

%% factorial/2
%% factorial(+N, -Fact)
%% 'Fact' este factorialul lui 'N'.
%% N va fi mereu legat la un număr natural.

factorial(_, _):- fail.

check5:-
        factorial(1, F1), F1 == 1, write('.'),
        factorial(4, F2), F2 == 24, write('.'),
        factorial(5, 120), write('.'),
        factorial(6, 720), write('.'),
        factorial(7, 5040), write('.'),
        nl, writeln('factorial rezolvat corect!'),
        !.

%% ----------------------------------------

%% palindrom/2
%% palindrom(+List)
%% 'List' este un palindrom.

palindrom(_) :- fail.

check6 :-
        palindrom([1,2,3,2,1]), write('.'),
        palindrom([1,2,3,3,2,1]), write('.'),
        \+ palindrom([1,2,3,0,2,1]), write('.'),
        palindrom([1,2,3,X3,X2,X1]), X3 == 3, X2 == 2, X1 == 1, write('.'),
        \+ palindrom([1,2,3,X,_,X]), write('.'),
        nl, writeln('palindrom rezolvat corect!'),
        !.


%% ----------------------------------------

%% sublista/2
%% sublista(+List, ?SubList)
%% 'SubList' este o sublistă a lui 'List' ('SubList' poate fi obținută prin
%% eliminarea a zero sau mai multe elemente din 'List'

sublista(_, _) :- fail.

check7 :-
        sublista([1,2,3,4,5],[2,3,4]), write('.'),
        sublista([1,2,3,4,5],[1,2,3,4,5]), write('.'),
        \+ sublista([1,2,3,4,5],[2,1,4]), write('.'),
        nl, writeln('sublista rezolvat corect!'),
        !.

%% ----------------------------------------

%% merge_sort/2
%% merge_sort(+List,-SortedList)
%% 'SortedList' conține elementele listei 'List' în ordine crescătoare
%% Satisfacerea predicatul merge_sort trebuie să realizeze sortarea listei
%% 'List' prin metoda merge_sort.
%% Se vor scrie 2 predicate auxiliare: split/3 și merge/3.

merge_sort(_, _) :- fail.

%%Idee
%%
% function merge_sort(m)
%   if length(m) ≤ 1
%     return m
%   left, right = split_in_half(m)
%   left = merge_sort(left)
%   right = merge_sort(right)
%   result = merge(left, right)
%   return result
%
% Construiti functii care sa va faca pasii, pe rand.
%%
%%

check8:-
        merge_sort([], []), write('.'),
        merge_sort([1], [1]), write('.'),
        merge_sort([1,3,2], [1,2,3]), write('.'),
        merge_sort([7,4,1,3,4,2], [1,2,3,4,4,7]), write('.'),
        merge_sort([7,4,1,3,4,2], L4), L4 == [1,2,3,4,4,7], write('.'),
        nl, writeln('merge sort rezolvat corect!'),
        !.

%% ----------------------------------------

%% my_flatten/2
%% my_flatten(+List,-FlattenedList)
%% Hint: Use the predefined predicates is_list/1 and append/3

my_flatten(_, _) :- fail.

check9:-
        my_flatten([a, [b, e]], X1), X1 == [a, b, e], write('.'),
        my_flatten([a, [b, [c, d], e]], X), X == [a, b, c, d, e], write('.'),
        nl, writeln('flatten rezolvat corect!'),
        !.

%% compress/2
%% compress(+List,-CompressedList)

compress(_, _) :- fail.

check10:-
        compress([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X), X == [a,b,c,a,d,e], write('.'),
        nl, writeln('compress rezolvat corect!'),
        !.


%% encode/2
%% compress(+List,-EncodedList)

encode(_, _) :- fail.

check11:-
        encode([a,a],X), X == [[2,a]], write('.'),
        encode([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X), X == [[4,a],[1,b],[2,c],[2,a],[1,d],[4,e]], write('.'),
        nl, writeln('encode rezolvat corect!'),
        !.

%% drop/3
%% compress(+List,+EveryNth,-NewList)

drop(_, _, _) :- fail.

check12:-
        drop([a,b,c,d,e,f,g,h,i,k],3,X), X == [a,b,d,e,g,h,k], write('.'),
        nl, writeln('drop rezolvat corect!'),
        !.

