% General utilities for running test cases.
grade(File, Tests) :-
        nl, write('Loading file '''), write(File), write('''.'), nl,
        catch(consult(File), E, (print_message(error,E), fail)), !,
        nl, write('Beginning test cases...'), nl,
        run_tests(Tests, 0, Total, 0, Outof),
        nl, write('Grade = '), write(Total), write(' / '), write(Outof),
        nl, nl.
grade(_, Tests) :- addup(Tests, 0, Outof),
        nl, write('Grade = 0.0 / '), write(Outof), nl, nl.

% Add up total grade in tests, without running anything.
addup([], Outof, Outof).
addup([(_,Value) | Tail], Partial, Outof) :-
        Updated is Partial + Value, addup(Tail, Updated, Outof).

% Run a suite of test cases.
run_tests([], Total, Total, Outof, Outof).  % when all tests have been run
run_tests([(Goal,Value) | Tail], Grade, Total, Partial, Outof) :-
        write('...'), write(Goal), call(Goal), !,
        write(': '), write(Value), nl,
        Correct is Grade + Value, Updated is Partial + Value,
        run_tests(Tail, Correct, Total, Updated, Outof).
run_tests([(_,Value) | Tail], Grade, Total, Partial, Outof) :-
        write(': 0.0 -- goal failed.'), nl, Updated is Partial + Value,
        run_tests(Tail, Grade, Total, Updated, Outof).

% Test cases for range.
test_range :- grade('range.pl', [
  (findall((Min,Max), range([],Min,Max), []), 0.5),
  (findall((Min,Max), range([a,b,c],Min,Max), []), 0.5),
  (findall((Min,Max), range([1],Min,Max), [(1,1)]), 0.5),
  (findall((Min,Max), range([1,1,1],Min,Max), [(1,1)]), 0.5),
  (findall((Min,Max), range([-1,0,1],Min,Max), [(-1,1)]), 0.5),
  (findall((Min,Max), range([a,1,b],Min,Max), [(1,1)]), 0.5),
  (findall((Min,Max), range([1,a,-1],Min,Max), [(-1,1)]), 0.5),
  (findall((Min,Max), range([[],-0.5,a,b,1,2.5,0,c],Min,Max), [(-0.5,2.5)]),
    0.5)
  ]).

% Test cases for uniquify.
test_uniquify :- grade('uniquify.pl', [
  (findall(L, uniquify([],L), [[]]), 0.5),
  (findall(L, uniquify([a],L), [[a]]), 0.5),
  (findall(L, uniquify([a,2,c,4],L), [[a,2,c,4]]), 0.5),
  (findall(L, uniquify([1,1],L), [[1]]), 0.5),
  (findall(L, uniquify([a,a,a,a,a],L), [[a]]), 0.5),
  ((uniquify([a,1,b,c,1,d],A),
    member(A, [[a,b,c,1,d], [a,1,b,c,d]])), 0.5),
  ((uniquify([1,2,a,3,4,a,a,5,6,a,a,a],B),
    member(B, [[1,2,3,4,5,6,a], [1,2,3,4,a,5,6], [1,2,a,3,4,5,6]])), 0.5),
  ((uniquify([1,a,2,b,3,a,1,4,2,b,c,1,a,d],C),
    sort(C,[1,2,3,4,a,b,c,d])), 0.5)
  ]).

% Test cases for flatten.
test_flatten :- grade('flatten.pl', [
  (findall(L, flatten([],L), [[]]), 0.5),
  (findall(L, flatten([a,b,c],L), [[a,b,c]]), 0.5),
  (findall(L, flatten([[]],L), [[]]), 0.5),
  (findall(L, flatten([[1,2,3]],L), [[1,2,3]]), 0.5),
  (findall(L, flatten([a,b,c,[1,2,3]],L), [[a,b,c,1,2,3]]), 0.5),
  (findall(L, flatten([[a,b,c],1,2,3],L), [[a,b,c,1,2,3]]), 0.5),
  (findall(L, flatten([[[a,b,c]]],L), [[a,b,c]]), 0.5),
  (findall(L, flatten([[a,[[b],c]],[],[[1,[2]],3]],L), [[a,b,c,1,2,3]]),
    0.5)
  ]).

% Test cases for permute.
test_permute :- grade('permute.pl', [
  (findall(L, permute([],L), [[]]), 0.5),
  (findall(L, permute(L,[]), [[]]), 0.5),
  (findall(L, permute([a],L), [[a]]), 0.5),
  (findall(L, permute(L,[1]), [[1]]), 0.5),
  ((findall(L, permute([a,b,c],L), A),
    sort(A, [[a,b,c],[a,c,b],[b,a,c],[b,c,a],[c,a,b],[c,b,a]])), 0.5),
  ((findall(L, permute(L,[1,2,3]), B),
    sort(B, [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]])), 0.5)
  ]).

% Run next test case for permute manually, so execution can be stopped.
% Worth 1 mark.
%
%?- permute(L, P).
%
%L = [],
%P = [] ;
%
%L = [_G260],
%P = [_G260] ;
%
%L = [_G260, _G263],
%P = [_G260, _G263] ;
%
%L = [_G260, _G263],
%P = [_G263, _G260] ;
%
%L = [_G260, _G263, _G266],
%P = [_G260, _G263, _G266] ;
%
%L = [_G260, _G263, _G266],
%P = [_G263, _G260, _G266] ;
%
%L = [_G260, _G263, _G266],
%P = [_G263, _G266, _G260] ;
%
%L = [_G260, _G263, _G266],
%P = [_G260, _G266, _G263] ;
%
%L = [_G260, _G263, _G266],
%P = [_G266, _G260, _G263] ;
%
%L = [_G260, _G263, _G266],
%P = [_G266, _G263, _G260] ;
%
%L = [_G260, _G263, _G266, _G269],
%P = [_G260, _G263, _G266, _G269] .

% Test cases for simple.
test_simple :- grade('simple.pl', [
  (findall(S, simple(x,S), [x]), 0.4),
  (findall(S, simple(1,S), [1]), 0.3),
  (findall(S, simple(3+2,S), [5]), 0.3),
  (findall(S, simple(3-2,S), [1]), 0.3),
  (findall(S, simple(3*2,S), [6]), 0.3),
  (findall(S, simple(3/2,S), [1.5]), 0.3),
  (findall(S, simple(3^2,S), [9]), 0.3),
  (findall(S, simple(1+2*3-4/5,S), [6.2]), 0.2),
  (findall(S, simple(0+x,S), [x]), 0.2),
  (findall(S, simple(x+0,S), [x]), 0.2),
  (findall(S, simple(x-0,S), [x]), 0.2),
  (findall(S, simple(1*x,S), [x]), 0.2),
  (findall(S, simple(x*1,S), [x]), 0.2),
  (findall(S, simple(x/1,S), [x]), 0.2),
  (findall(S, simple(x^1,S), [x]), 0.2),
  (findall(S, simple(0+(2+1-x),S), [3-x]), 0.2),
  (findall(S, simple((2+1-x)+0,S), [3-x]), 0.2),
  (findall(S, simple((2+1-x)-0,S), [3-x]), 0.2),
  (findall(S, simple(1*(2+1-x),S), [3-x]), 0.2),
  (findall(S, simple((2+1-x)*1,S), [3-x]), 0.2),
  (findall(S, simple((2+1-x)/1,S), [3-x]), 0.2),
  (findall(S, simple((2+1-x)^1,S), [3-x]), 0.2),
  (findall(S, simple(0-x,S), [-x]), 0.2),
  (findall(S, simple(-1*x,S), [-x]), 0.2),
  (findall(S, simple(x* -1,S), [-x]), 0.2),
  (findall(S, simple(x/ -1,S), [-x]), 0.2),
  (findall(S, simple(0-(2+1-x),S), [-(3-x)]), 0.2),
  (findall(S, simple(-1*(2+1-x),S), [-(3-x)]), 0.2),
  (findall(S, simple((2+1-x)* -1,S), [-(3-x)]), 0.2),
  (findall(S, simple((2+1-x)/ -1,S), [-(3-x)]), 0.2),
  (findall(S, simple(x-x,S), [0]), 0.2),
  (findall(S, simple(-x+x,S), [0]), 0.2),
  (findall(S, simple(-((2+1)*x)+(3*x^1),S), [0]), 0.2),
  (findall(S, simple(((2+1)*x)-(3*x^1),S), [0]), 0.2),
  (findall(S, simple(0*x,S), [0]), 0.2),
  (findall(S, simple(x*0,S), [0]), 0.2),
  (findall(S, simple(0/x,S), [0]), 0.2),
  (findall(S, simple(0*(2+1-x),S), [0]), 0.2),
  (findall(S, simple((2+1-x)*0,S), [0]), 0.2),
  (findall(S, simple(0/(2+1-x),S), [0]), 0.2),
  (findall(S, simple(x/x,S), [1]), 0.2),
  (findall(S, simple(x*1/x,S), [1]), 0.2),
  (findall(S, simple(1/x*x,S), [1]), 0.2),
  (findall(S, simple(((2+1)*x)/(3*x^1),S), [1]), 0.2),
  (findall(S, simple(((2+1)*x)*1/(3*x^1),S), [1]), 0.2),
  (findall(S, simple(1/((2+1)*x)*(3*x^1),S), [1]), 0.2),
  (findall(S, simple(x^0,S), [1]), 0.2),
  (findall(S, simple(((2+1)*x)^0,S), [1]), 0.2),
  (findall(S, simple(3*2*x^2-1*x^0+(3-2)*x,S), [6*x^2-1+x]), 0.4),
  (findall(S, simple(2*x-3*x,S), [2*x-3*x]), 0.4),
  (findall(S, simple(3*x^0-(y+z+0)/(1*y+z*1),S), [2]), 0.4),
  (findall(S, simple(3^(y/y*1)+(z* -1+(z+y*0)),S), [3]), 0.4)
  ]).

