% Some things need to be defined in P#
append([],X,X).
append([H|T],Y,[H|Z]) :- append(T,Y,Z). 

member(A,[A|_]).
member(A,[_|B]) :- member(A,B).

%maplist(Pred,L,Result)
maplist(_,[],[]).
maplist(PartialPred,[H|T],[Var|Res]) :- 
	PartialPred =.. L,
	append(L,[H,Var],L1),
	Pred =.. L1,
	call(Pred),
	maplist(PartialPred,T,Res).

random(N,X) :- X is integer(random*N).

swritef(S,L) :- 
	swritef_aux(Chars,[],L),
	atom_chars(S,Chars).

swritef_aux(S,S,[]).
swritef_aux(S,X,[H|T]) :- 
	swritef_aux2(S1,H), 
	append(X,S1,S2),
	swritef_aux(S,S2,T).
	
swritef_aux2(S,I) :- integer(I), !, number_chars(I,S).
swritef_aux2(S,A) :- atom(A), !, atom_chars(A,S).
swritef_aux2(S,A=B) :- swritef_aux(S,[A,'=',B]).
	
string_concat(S0,S1,S) :- 
	atom_chars(S0,Chars0),
	atom_chars(S1,Chars1),
	append(Chars0,Chars1,Chars2), 
	atom_chars(S,Chars2).	

% main code, slightly modified to run with P#
allaccesspathsfrom(X,Paths,N) :-
	allpaths(X,L,N),
	maplist(accesspath,L,P0),
	maplist(format_HQL,P0,P),
	zip2(L,P,Paths).

format_HQL(select(obj,T,W),S) :-
	!, random(65536,N),
	format_from(T,N,S1),
	format_HQL_where(W,N,S2),
	string_concat(S1,S2,S).
format_HQL(select(X,T,W),S) :-
	!, random(65536,N),
	swritef(S0,['select a',N,'.',X]),
	format_from(T,N,S1),
	format_HQL_where(W,N,S2),
	string_concat(S0,S1,S01),
	string_concat(S01,S2,S).
format_HQL(param_id,':id ') :- !.
format_HQL(X,S) :- swritef(S,[X]).
		
format_from(T,N,S) :- swritef(S,[' from ',T,' a',N]).
format_HQL_where(X=E,N,S) :-
	format_HQL(E,S1),
	swritef(S,[' where a',N,'.',X,' in (',S1,')']).
		
zip2([],_,[]).
zip2([A|L],[B|M],[[A,B]|N]) :- zip2(L,M,N).

not_retrogressive(X) :- !, not(retrogressive(X)).

retrogressive([Path,_]) :- retrogressive2(Path).

retrogressive2([]) :- fail.
retrogressive2([arc(X,Y,Dir,Prop)|Arcs]) :- (arc(Y,X,OtherDir,Prop),Arcs), Dir \== OtherDir. 
retrogressive2([_|Arcs]) :- retrogressive2(Arcs).

significant_endpoints(L,[Path,_]) :- endpoints(Path,Start,Finish), member(Start,L), member(Finish,L).

print_arcs(Arcs,Sout) :- 
	Arcs=[arc(From,_,_,_)|_],
	swritef(Sin,[From]),
	print_arcs2(Arcs,Sin,Sout).
print_arcs2([],S,S).
print_arcs2([arc(_,To,forward,Prop)|Arcs],Sin,Sout) :-
	swritef(Sint,[Sin,'>',Prop,'>',To]),
	print_arcs2(Arcs,Sint,Sout).
print_arcs2([arc(_,To,reverse,Prop)|Arcs],Sin,Sout) :-
	swritef(Sint,[Sin,'<',Prop,'<',To]),
	print_arcs2(Arcs,Sint,Sout).

print_arcs_alt(Arcs,Sout) :- 
	Arcs=[arc(From,_,_,_)|_],
	swritef(Sin,['From ',From]),
	print_arcs2_alt(Arcs,Sin,Sout).
print_arcs2_alt([],S,S).
print_arcs2_alt([arc(_,To,forward,Prop)|Arcs],Sin,Sout) :-
	swritef(Sint,[Sin,', go to ',To,' using ',Prop]),
	print_arcs2_alt(Arcs,Sint,Sout).
print_arcs2_alt([arc(_,To,reverse,Prop)|Arcs],Sin,Sout) :-
	swritef(Sint,[Sin,', go to ',To, ' having it as ', Prop]),
	print_arcs2_alt(Arcs,Sint,Sout).
		
print_paths([]).
print_paths([[Path,AccessPath]|L]) :-
	multiplicity(Path,Mult),
	(Mult=single;write('*')),
	print_arcs(Path,S),
	writeln(S),
	print_arcs_alt(Path,S2),
	writeln(S2),
	%writeln(Mult),
	writeln(AccessPath),
	nl,
	print_paths(L).

multiplicity(AccessPath,single) :- forall(member(X,AccessPath),X=arc(_,_,forward,_)), !.
multiplicity(_,multiple).
		
%endpoints(Path,Start,Finish)
endpoints([arc(Start,Finish,_,_)],Start,Finish).
endpoints([arc(Start,_,_,_)|Path],Start,Finish) :- endpoints(Path,_,Finish).

% path(from_concept, to_concept, path, length)
% A path contains arcs that are either forward links or reverse links
path(_,_,_,0) :- !, fail.
path(X,Y,[arc(X,Y,forward,Prop)],1) :- link(X,Y,Prop).
path(X,Y,[arc(X,Y,reverse,Prop)],1) :- link(Y,X,Prop).
path(X,Y,[arc(X,Z,forward,Prop)|P],N) :- link(X,Z,Prop), N1 is N-1, path(Z,Y,P,N1).
% A reverse link cannot be followed by the corresponding forward link
path(X,Y,[arc(X,W,reverse,Prop),arc(W,U,Dir,Prop2)|P],N) :- link(Z,X,Prop), N1 is N-1, path(Z,Y,[arc(W,U,Dir,Prop2)|P],N1), X \== U.

% allpaths(from_concept, paths, maxlength)
allpaths(X,L,N) :- allpathsaux(X,L,1,N).

% allpathsaux(from_concept, paths, length, maxlength)
allpathsaux(_,[],I,N) :- I is N+1, !.
allpathsaux(X,L,I,N) :- 
	findall(L0,path(X,_,L0,I),L1), 
	I1 is I+1, 
	allpathsaux(X,L2,I1,N),
	append(L1,L2,L).

% accesspath(path,accesspath)
% NB. select(obj, dog, id=select(dog, flee, id=param_id)) when using HQL
%  could be expressed simply as select(dog, flee, id=param_id). This is a 
%  simplification that is not done here.
accesspath(P,A) :- P=[arc(_,X,_,_)|_], accesspathaux(X,P,param_id,A).

% Merge cascaded selects on the same table
accesspatharc(arc(X,_,forward,Prop),ID,select(Prop,X,E)) :- embed(ID,select(id,X,E)),!.
accesspatharc(arc(X,_,forward,Prop),ID,select(Prop,X,id=ID1)) :- embed(ID,ID1).
accesspatharc(arc(_,Y,reverse,Prop),ID,select(obj,Y,Prop=ID1)) :- embed(ID,ID1).

accesspathaux(C,[],A1,A) :- standalone(C,A1,A).
accesspathaux(_,[Arc|L],ID,A) :- accesspatharc(Arc,ID,A1), Arc=arc(_,Y,_,_), accesspathaux(Y,L,A1,A).

embed(select(obj,C,W),select(id,C,W)) :- !.
embed(X,X).

standalone(_,select(obj,C,W),select(obj,C,W)) :- !.
standalone(C,A,select(obj,C,id=A)).

