/*==============================================================================
 *	LPAD and CP-Logic reasoning suite
 *	File: parsing.pl
 *	Parses predicates to load LPADs (main predicate: parse(FileNameNoExt)
 *	Copyright (c) 2009, Stefano Bragaglia
 *============================================================================*/
  
:- dynamic rule/4, def_rule/2.

% :- source.
% :- yap_flag(single_var_warnings, on).





/* BUILTIN PREDICATES
 * ------------------
 * This section declares the builtin predicates.
 */
builtin(_A is _B).
builtin(_A > _B).
builtin(_A < _B).
builtin(_A >= _B).
builtin(_A =< _B).
builtin(_A =:= _B).
builtin(_A =\= _B).
builtin(true).
builtin(false).
builtin(_A = _B).
builtin(_A==_B).
builtin(_A\=_B).
builtin(_A\==_B).
builtin(length(_L, _N)).
builtin(member(_El, _L)).
builtin(average(_L, _Av)).
builtin(max_list(_L, _Max)).
builtin(min_list(_L, _Max)).
builtin(nth0(_, _, _)).
builtin(nth(_, _, _)).
builtin(eraseall(_Id)).
builtin(recordz(_Id, _Item, _)).
builtin(recordzifnot(_Id, _Item, _)).



member_eq(Item, [Head|_Tail]) :- 
	Item==Head, !.
	
member_eq(Item, [_Head|Tail]) :- 
	member_eq(Item, Tail).



not_already_present_with_a_different_head(_HeadId, _RuleId, _Subst, []).

not_already_present_with_a_different_head(HeadId, RuleId, Subst, [(HeadId1, RuleId, Subst1)|Tail]) :- 
	not_different(HeadId, HeadId1, Subst, Subst1), !, 
	not_already_present_with_a_different_head(HeadId, RuleId, Subst, Tail).
	
not_already_present_with_a_different_head(HeadId, RuleId, Subst, [(_HeadId1, RuleId1, _Subst1)|Tail]) :- 
	RuleId \== RuleId1, 
	not_already_present_with_a_different_head(HeadId, RuleId, Subst, Tail).



not_different(_HeadId, _HeadId1, Subst, Subst1) :- 
	Subst \= Subst1, !.	

not_different(HeadId, HeadId1, Subst, Subst1) :- 
	HeadId \= HeadId1, !, 
	dif(Subst, Subst1).	

not_different(HeadId, HeadId, Subst, Subst).



get_groundc([], [], [], P, P) :- !.

get_groundc([H|T], [H|T1], TV, P0, P1) :- 
	ground(H), !, 
	H=(N, R, S), 
	rule_by_num(R, S, _N, Head, _Body), 
	nth0(N, Head, (_A:P)), 
	P2 is P0*P, 
	get_groundc(T, T1, TV, P2, P1).

get_groundc([H|T], T1, [H|TV], P0, P1) :- 
	get_groundc(T, T1, TV, P0, P1).

get_prob([], P, P) :- !.

get_prob([H|T], P0, P1) :- 
	H=(N, R, S), 
	rule_by_num(R, S, _N, Head, _Body), 
	nth0(N, Head, (_A:P)), 
	P2 is P0*P, 
	get_prob(T, P2, P1).




find_rulec(H, (R, S, N), Body, C, P) :- 
	rule(H, P, N, R, S, _NH, _Head, Body), 
	not_already_present_with_a_different_head(N, R, S, C).



/* var2numbers([(Rule, Subst)|CoupleTail], Index, [[Index, Heads, Probs]|TripleTail])
 * ----------------------------------------------------------------------------------
 * This tail recursive predicate converts a list of couples (Rule, Subst) into a
 * list of triples (Index, Count, Probs).
 * Rule and Subst are the index of their equivalent rule and substitution.
 * Index is a progressive identifier starting from 0.
 * Count is the number of head atoms and Probs is the vector of their 
 * probabilities.
 *
 * INPUT
 *  - Couples: list of couples to convert.
 *
 * OUTPUT
 *  - Triples: list of equivalent triples.
 */
var2numbers([], _N, []).

var2numbers([(Rule, Subst)|CoupleTail], Index, [[Index, Heads, Probs]|TripleTail]) :- 
	find_probs(Rule, Subst, Probs), 
	length(Probs, Heads), 
	Next is Index+1, 
	var2numbers(CoupleTail, Next, TripleTail).



/* build_formula(ListC, Formula, VarIn, VarOut) 
 * --------------------------------------------
 * This predicate parses a given list of C sets with a given list of variables 
 * and returns the equivalent formula with its list of variables.
 * 
 * Note: each Formula is expressed in the form: [Term1, ..., TermN], where each 
 *       term is expressed in the form: [Factor1, ..., FactorM], where each 
 *       factor is hence expressed in the form: (Var, Name).
 *       Finally, Var is the index of the multivalued variable Var, and Value is 
 *       the index of its value.
 *
 * INPUT
 *  - ListC: given list of C sets.
 *  - VarIn: list of variables pertaining to ListC.
 * 
 * OUTPUT
 *  - Formula: the formula equivalent to ListC.
 *  - VarOut: list of variables pertaining to Formula.
 */
build_formula([], [], Var, Var, Count, Count).
%% Closing condition: stop if no more terms (current Var is final Var, current Count is final Count)

build_formula([D|TD], [F|TF], VarIn, VarOut, C0, C1) :- 
	length(D, NC), 
	C2 is C0+NC, 
	reverse(D, D1), 
	build_term(D1, F, VarIn, Var1), 
	build_formula(TD, TF, Var1, VarOut, C2, C1).
	%% Recursive call: procedd to next terms, building rest of formula and handling vars and count.

build_formula([], [], Var, Var).

build_formula([D|TD], [F|TF], VarIn, VarOut) :- 
	build_term(D, F, VarIn, Var1), 
	build_formula(TD, TF, Var1, VarOut).



build_term([], [], Var, Var).

build_term([(_, pruned, _)|TC], TF, VarIn, VarOut) :- !,
	build_term(TC, TF, VarIn, VarOut).

build_term([(N, R, S)|TC], [[NVar, N]|TF], VarIn, VarOut) :-
	(nth0_eq(0, NVar, VarIn, (R, S)) ->
		Var1=VarIn;
		append(VarIn, [(R, S)], Var1),
		length(VarIn, NVar)), 
	build_term(TC, TF, Var1, VarOut).							



find_probs(R, S, Probs) :- 
	rule_by_num(R, S, _N, Head, _Body), 
	get_probs(Head, Probs).


	
get_probs(uniform(_A:1/Num, _P, _Number), ListP) :- 
	Prob is 1/Num, 
	list_el(Num, Prob, ListP).

get_probs([], []).

get_probs([_H:P|T], [P1|T1]) :- 
	P1 is P, 
	get_probs(T, T1).



list_el(0, _P, []) :- !.

list_el(N, P, [P|T]) :- 
	N1 is N-1, 
	list_el(N1, P, T).



/* nth0_eq(PosIn, PosOut, List, Elem) 
 * ----------------------------------
 * This predicate searches for an element that matches with the given one in the 
 * given list, starting from the given position, and returns its position.
 *
 * INPUT
 *  - PosIn: initial position.
 *  - List: list to parse.
 *  - Elem: element to match.
 *
 * OUTPUT
 *  - PosOut: next position of a matching element.
 */
nth0_eq(N, N, [H|_T], Elem) :- 
	H==Elem, !.

nth0_eq(NIn, NOut, [_H|T], Elem) :- 
	N1 is NIn+1, 
	nth0_eq(N1, NOut, T, Elem).



list2and([X], X) :- 
	X\=(_, _), !.

list2and([H|T], (H, Ta)) :- !, 
	list2and(T, Ta).



list2or([X], X) :- 
	X\=;(_, _), !.

list2or([H|T], (H ; Ta)) :- !, 
	list2or(T, Ta).



choose_clausesc(_G, C, [], C).

choose_clausesc(CG0, CIn, [D|T], COut) :- 
	member((N, R, S), D), 
	choose_clauses_present(N, R, S, CG0, CIn, COut, T).
	
choose_clausesc(G0, CIn, [D|T], COut) :- 
	member((N, R, S), D), 
	new_head(N, R, S, N1), 
	\+ already_present(N1, R, S, CIn), 
	\+ already_present(N1, R, S, G0), 
	impose_dif_cons(R, S, CIn), 
	choose_clausesc(G0, [(N1, R, S)|CIn], T, COut).



choose_clauses_present(N, R, S, CG0, CIn, COut, T) :- 
	already_present_with_a_different_head_ground(N, R, S, CG0), !, 
	choose_clausesc(CG0, CIn, T, COut).

choose_clauses_present(N, R, S, CG0, CIn, COut, T) :- 
	already_present_with_a_different_head(N, R, S, CIn), 
	choose_a_head(N, R, S, CIn, C1), 
	choose_clausesc(CG0, C1, T, COut).



/* new_head(N, R, S, N1)
 * ---------------------
 * This predicate selects an head for rule R different from N with substitution 
 * S and returns it in N1.
 */
new_head(N, R, S, N1) :- 
	rule_by_num(R, S, Numbers, Head, _Body), 
	Head\=uniform(_, _, _), !, 
	nth0(N, Numbers, _Elem, Rest), 
	member(N1, Rest).

new_head(N, R, S, N1) :- 
	rule_uniform(_A, R, S, Numbers, 1/Tot, _L, _Number, _Body), 
	listN(0, Tot, Numbers), 
	nth0(N, Numbers, _Elem, Rest), 
	member(N1, Rest).



/* already_present(N, R, S, [(N, R, SH)|_T])
 * -----------------------------------------
 * This predicate checks if a rule R with head N and selection S (or one of its 
 * generalizations is in C) is already present in C.
 */
already_present(N, R, S, [(N, R, SH)|_T]) :- 
	S=SH.

already_present(N, R, S, [_H|T]) :- 
	already_present(N, R, S, T).



already_present_with_a_different_head(N, R, S, [(NH, R, SH)|_T]) :- 
	\+ \+ S=SH, NH \= N.

already_present_with_a_different_head(N, R, S, [_H|T]) :- 
	already_present_with_a_different_head(N, R, S, T).

already_present_with_a_different_head_ground(N, R, S, [(NH, R, SH)|_T]) :- 
	S=SH, NH \= N.

already_present_with_a_different_head_ground(N, R, S, [_H|T]) :- 
	already_present_with_a_different_head_ground(N, R, S, T).



impose_dif_cons(_R, _S, []) :- !.

impose_dif_cons(R, S, [(_NH, R, SH)|T]) :- !, 
	dif(S, SH), 
	impose_dif_cons(R, S, T).

impose_dif_cons(R, S, [_H|T]) :- 
	impose_dif_cons(R, S, T).



/* choose_a_head(N, R, S, [(NH, R, SH)|T], [(NH, R, SH)|T])
 * --------------------------------------------------------
 * This predicate chooses and returns an head. 
 * It instantiates a more general rule if it is contained in C with a different 
 * head.
 */
choose_a_head(N, R, S, [(NH, R, SH)|T], [(NH, R, SH)|T]) :- 
	S=SH, 
	dif(N, NH).

/* choose_a_head(N, R, S, [(NH, R, SH)|T], [(NH, R, S), (NH, R, SH)|T])
 * --------------------------------------------------------------------
 * This predicate chooses and returns an head.
 * It instantiates a more general rule if it is contained in C with a different 
 * head.
 * It ensures the same ground clause is not generated again.
 */
choose_a_head(N, R, S, [(NH, R, SH)|T], [(NH, R, S), (NH, R, SH)|T]) :- 
	\+ \+ S=SH, S\==SH, 
	dif(N, NH), 
	dif(S, SH).

choose_a_head(N, R, S, [H|T], [H|T1]) :- 
	choose_a_head(N, R, S, T, T1).



listN(N, N, []) :- !.

listN(NIn, N, [NIn|T]) :- 
	N1 is NIn+1, 
	listN(N1, N, T).