% Generate CNF groundings.

% Specify your theory to be grounded by defining unary predicates for the variable domains,
% and specifying the clause schemas using the axiom predicates.  The body of the axiom predicate
% specifies the domains of the variables and any other constraints (for example, inequalities
% between variables).  There may be any number of definitions given for axiom.
% Invoking :- theory. prints the formula to the terminal, and invoking :- savetheory('filename').
% prints the formula to the specified file.
% Invoking :-interpret('solution_file','cnf_file') prints the positive literals from solution_file
% using the mapping specified in the comments of cnf_file.
%
% Example:
% % swipl
% ?- [schema].
% ?- savetheory('foo.cnf').
% ?- halt.
% % walksat -out foo.sol < foo.cnf
% % swipl
% ?- [schema].
% ?- interpret('foo.sol','foo.cnf').

% Edit the following example to specify your theory.

person(alice).
person(bob).
person(carl).
person(donald).
time(I):-between(1,10,I).

% Example: all X in person . all Y in person such that Y is not equal to X . friend(X,Y).
axiom([friend(X,Y)]):-person(X),person(Y),Y \= X.

% Example: all I in time . pickup(I) => holding(I+1)
axiom([not(pickup(I)),holding(J)]):-time(I),J is I+1.

% Example: all I in time . all J in time such that I < J . before(I,J).
axiom([before(I,J)]):-time(I),time(J),I<J.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%				
% Do not change the following lines.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

savetheory(File):-open(File,write,FD),with_output_to(FD,theory),close(FD).

interpret(SOLUTION,CNF):-open(SOLUTION,read,SFD),open(CNF,read,CFD),initvarmap,readMap(CFD),readSolution(SFD),close(CFD),close(SFD).

theory:-clauses(C),writeclauses(C),initvarmap,dimacs(C,D),writemap,writecnf(D).

clauses(C):-findall(A,axiom(A),C).

:- dynamic varmap/2.
:- dynamic maxlit/1.
initvarmap :- retractall(varmap(X,N)),retractall(maxlit(M)),assert(maxlit(0)).

writeclauses(C):-member(X,C),write('c '),write(X),nl,fail.
writeclauses(C).

dimacs([],[]).
dimacs([C|RC],[D|RD]):-dimacs1(C,D),!,dimacs(RC,RD).

dimacs1([],[]).
dimacs1([T|RT],[N|RN]):-dimacs2(T,N),!,dimacs1(RT,RN).

dimacs2(not(X),N):-!,dimacs2(X,N2),N is -N2.
dimacs2(X,N):-varmap(X,N),!.
dimacs2(X,N):-maxlit(M),retract(maxlit(M)),N is M+1,assert(maxlit(N)),assert(varmap(X,N)).

writecnf(D):-write('p cnf '),maxlit(M),write(M),write(' '),length(D,N),write(N),nl,writecnf0(D).

writecnf0(D):-member(C,D),writecnf1(C),fail.
writecnf0(D).

writecnf1(C):-member(L,C),write(L),write(' '),fail.
writecnf1(C):-write(0),nl.

writemap:-varmap(P,N),write('c map '),write(N),write(' '),write(P),nl,fail.
writemap.

readMap(CFD):-repeat,read_line_to_codes(CFD,Data),processMapping(Data),!.
processMapping(end_of_file) :- !.
processMapping(Data):-
	Data = [99, 32, 109, 97, 112, 32 | R], % c map ...
	open_chars_stream(R,DS),
	getNumber(DS,LitNum),
	getString(DS,LitSym),
	close(DS),
	assert(varmap(LitSym,LitNum)),
	fail.

readSolution(SFD):-repeat,getNumber(SFD,N),processSolution(N),!.
processSolution(end_of_file) :- !.
processSolution(N) :- (N>0 -> varmap(LitSym,N),write(LitSym),nl),fail.

% read whitespace separated code lists

whitespace(32).
whitespace(10).
whitespace(13).
whitespace(9).

getCodeList(S,L):-get_code(S,X),(X<0 -> L = []; whitespace(X) -> getCodeList(S,L); L = [X|R], getCodeList1(S,R)).
getCodeList1(S,R):-get_code(S,X),(X<0 -> R = []; whitespace(X) -> R = []; R = [X|RR], getCodeList1(S,RR)).

% read whitespace separated numbers

getNumber(S,N):-getCodeList(S,X),(X = [] -> N = end_of_file; number_codes(N,X)).

% read whitespace separated terms

getTerm(S,T):-getCodeList(S,X),(X = [] -> T = end_of_file; read_term_from_codes(X,T,[])).

% read whitespace separated atoms

getAtom(S,A):-getCodeList(S,X),(X = [] -> A = end_of_file; atom_codes(A,X)).

% read whitespace separated strings

getString(S,A):-getCodeList(S,X),(X = [] -> A = end_of_file; string_to_list(A,X)).
