:- dynamic([diverge/0,member_/2,append_/3,insert/3,perm/2]). 
:- dynamic([edge/2,path/2]).

% test(+P, -D, -G, -N, -J)
% test a single atom goal P = p(ts) which does not depend on others
% D = reified program for p, G = reified goal, N = next free variable
% J = success or failure
test(P, D, G, N, J) :-
	P =.. [Pred|Xs],
	length(Xs, Arity),
	reify(Pred/Arity, D),
	reify_goal(P, G, N),			% N-1 = # of free vars in goal
	solve(G, [prog(Pred/Arity, D)], N, J).

% test2([Pred1/Arity1, ..., Predn/Arityn], A, J)
% reify Pred1...Predn, solve goal A, J = success or failure, backtrackable
test2(Preds, A, J) :-
	resid_prog(Preds, Gamma),
	reify_goal(A, G, N),
	solve(G, Gamma, N, J).

test3(Preds, A, J) :-
	resid_prog(Preds, Gamma),
	reify_goal(A, G, N),
	prove(G, Gamma, N, J).

/*
test(member_(1, [2,1,3]), D, G, N, J).
test(member_(1, [2,4,3]), D, G, N, J).
test(member_(X, [1,2,3,4]), D, G, N, J).
test(append_([1,2],[3,4],[1,2,3,4]), D, G, N, J).
test(append_([1,2],[3,4],Zs), D, G, N, J).
test(append_(Xs, Ys, [1,2,3,4]), D, G, N, J).
test2([insert/3],insert(1, [1,2,3], Zs), J).
test2([perm/2,insert/3], perm([1,2,3], _), J).
*/
% Test predicates
% All test predicates must be declared dynamic

% member_(X, Ys) iff X is a member of the list Ys
% will succeed multiple times for each member of Ys equal to X.
member_(X, [X|_]).
member_(X, [_|Ys]) :- member_(X, Ys).

% append_(Xs, Ys, Zs) iff Zs is the result of appending list Ys to list Xs
append_([], Ys, Ys).
append_([X|Xs], Ys, [X|Zs]) :- append_(Xs, Ys, Zs). 

% insert(X, Ys, Zs) iff Zs is result of insert X somewhere in Ys
insert(X, Ys, [X|Ys]).
insert(X, [Y|Ys], [Y|Zs]) :- insert(X, Ys, Zs). 

% perm(Xs, Zs) iff Zs is a permutation of Xs
perm([], []).
perm([X|Xs], Zs) :-
	perm(Xs, Ys),
	insert(X, Ys, Zs).

% only good under breadth-first
edge(a,d).
edge(d,a).
edge(a,b).
edge(b,c).
path(X,Y) :- edge(X,Y).
path(X,Z) :- path(X,Y), path(Y,Z).
/*
test2([edge/2,path/2],path(a,c),J).  will not terminate
test3([edge/2,path/2],path(a,c),J).  will succeed once, then diverge (only one path)
*/

% diverge will never terminate
diverge :- diverge ; true.
