:- module(_,_,[clpq]).
% :- module(_,_,[clpr]).
% :- use_package(clpq).

:- use_module(library(prolog_sys)).
:- use_module(library(aggregates)).
:- use_module(library(lists),[length/2]).

 %% Query examples:

 %% hp(3, 4, Z).  
 %% hp(X, Y, f(3,4)).  
 %% hp(X, Y, Z).

 %% hc(3, 4, Z).  
 %% hc(X, Y, f(3,4)).  
 %% hc(X, Y, Z).

 %% ap(3, 4, Z).
 %% ap(X, 4, 7).

 %% ac(3, 4, Z).
 %% ac(X, 4, 7).
 %% ac(X, Y, 7).

hp(X,Y,Z) :- Z = f(X,Y).
hc(X,Y,Z) :- Z = f(X,Y).

ap(X,Y,Z) :- Z is X + Y.
ac(X,Y,Z) :- Z .=. X+Y.

 %% solution_gen_test_prolog(X, Y, Z).
 %% solution_gen_test_clp(X, Y, Z).
 %% solution_cons_gen_prolog(X, Y, Z).
 %% solution_cons_gen_clp(X, Y, Z).

solution_gen_test_prolog(X, Y, Z) :-
    p(X), p(Y), p(Z),
    prolog_test(X, Y, Z).

p(11). p(3). p(7). p(16). p(15). p(14).

prolog_test(X, Y, Z) :-  Y is X + 1, Z is Y + 1.


solution_gen_test_clp(X, Y, Z) :-
    p(X), p(Y), p(Z),
    clp_test(X, Y, Z).

clp_test(X, Y, Z) :-  Y .=. X + 1, Z .=. Y + 1.

solution_cons_gen_prolog(X, Y, Z) :-
    prolog_test(X, Y, Z),
    p(X), p(Y), p(Z).

solution_cons_gen_clp(X, Y, Z) :-
    clp_test(X, Y, Z), 
    p(X), p(Y), p(Z).


%% Making the search space a little larger:
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).
%% p(0).  p(0). p(0). p(0).  p(0).  p(0).

example1(X, _Y) :- X .=. 4 + 3.
example2(X, Y)  :- X .=. 4 + 3, Y = f(X, _Z).
example3(X, Y)  :- X + Y .=<. 4, Y .>=. 4, X .>=. 0.
example4(X, Y)  :- X + Y .=<. 4, Y .>=. 4, X .>=. 0.
example5(X, Y)  :- Y .>=. 4, X .>=. 0.

%% prod([2, 3], [4, 5], K).   
%% prod([2, 3], [5, X2], 22). 
%% prod([2, 7, 3], [Vx, Vy, Vz], 0). 
%% prod([3,1],[X,Y],5), prod([1,8],[X,Y],3).

prod([], [], Resul) :- 
	Resul .=. 0.
prod([X|Xs], [Y|Ys], Resul) :-
	Resul .=. X * Y + Rest,
	prod(Xs, Ys, Rest).

% CLPR:

pprod([], [], 0).
pprod([X|Xs], [Y|Ys], X * Y + Rest) :-
	pprod(Xs, Ys, Rest).

%% prod([X|Xs],[Y|Ys],Resul) :-
%% 	prod(Xs,Ys,Rest),
%% 	Resul is X*Y+Rest.

%% system([X, Y], [[3, 1],[1, 8]],[5, 3]).
%% system([37/23, 4/23], [[3, 1],[1, 8]],[X, Y]).

system(_Vars, [], []). 
system(Vars, [Co|Coefs], [Ind|Indeps]) :- 
    prod(Vars, Co, Ind),
    system(Vars, Coefs, Indeps).

% Nondeterministic (several possible intervals):

rx(X) :- X .>. 3, X .<. 4.
rx(X) :- X .>. 5, X .<. 7.

foo(X,Y) :- rx(X), Y .>. 0, Y .<. 6, X - Y .=. 1.


%% X*X + 2*X + 1 .=. 0.

%% cossin(X,Y).

cossin(X,Y) :- X .=. sin(cos(Y)), Y .=. 2 + Y * 3.

%% X+Y .=<. 4, Y .>=.4, X .>=. 0.

plfib(0,0).
plfib(1,1).
plfib(N,R) :-
	N > 1, 
	N1 is N-1,
	N2 is N-2,
	plfib(N1,F1),
	plfib(N2,F2),
	R is F1+F2.

fib(N,N) :- N .=. 0.
fib(N,N) :- N .=. 1.
fib(N,R) :- 
            N .>. 1,
  	    F1 .>=. 0, 
	    F2 .>=. 0, 
	    N1 .=. N - 1,
	    N2 .=. N - 2,
            fib(N1,F1),
	    fib(N2,F2),
	    R .=. F1 + F2.

%----------------------------------------
% plfib(3, X).
%
% fib(3, X).
%----------------------------------------

c_add(c(Re1,Im1), c(Re2,Im2), c(Re12,Im12)) :- 
	Re12 .=. Re1+Re2,
	Im12 .=. Im1+Im2.

c_mult(c(Re1, Im1), c(Re2, Im2), c(Re3, Im3)) :-
      Re3 .=. Re1 * Re2 - Im1 * Im2,
      Im3 .=. Re1 * Im2 + Re2 * Im1.

circuit(resistor(R), V, I, _W) :- 
    c_mult(I, c(R, 0), V).

circuit(inductor(L), V, I, W) :- 
    Im .=. W * L,
    c_mult(I, c(0, Im), V).

circuit(capacitor(C), V, I, W) :- 
    Im .=. -1 / (W * C),
    c_mult(I, c(0, Im), V).

circuit(parallel(N1, N2), V, I, W) :-
       c_add(I1, I2, I),
       circuit(N1, V, I1, W),
       circuit(N2, V, I2, W).

circuit(series(N1, N2), V, I, W) :-
       c_add(V1, V2, V),
       circuit(N1, V1, I, W),
       circuit(N2, V2, I, W).


/*
?- circuit(parallel(inductor(0.073), 
                    series(capacitor(C), resistor(R))), 
           c(4.5, 0), c(0.65, 0), 2400).
?- circuit(C, 
           c(4.5, 0), c(0.65, 0), 2400).
*/

plall(N,L) :- findall(_X,plqueens(N,_X),_S), length(_S,L).
clpall(N,L) :- findall(_X,queens(N,_X),_S), length(_S,L).

% In Prolog.

plqueens(N, Qs) :- 
        plqueens_list(N, Ns), 
        plqueens_(Ns, [], Qs).

plqueens_([], Qs, Qs).
plqueens_(Unplaced, Placed, Qs) :-
    selectq(Unplaced, Q, NewUnplaced), 
    pl_no_attack(Placed, Q, 1),
    plqueens_(NewUnplaced, [Q|Placed], Qs).

pl_no_attack([], _Queen, _Nb).
pl_no_attack([Y|Ys], Queen, Nb) :-
        Queen =\= Y + Nb, 
        Queen =\= Y - Nb, 
        Nb1 is Nb + 1,
        pl_no_attack(Ys, Queen, Nb1).

selectq([X|Ys], X, Ys).
selectq([Y|Ys], X, [Y|Zs]) :- selectq(Ys, X, Zs).

plqueens_list(0, []).
plqueens_list(N, [N|Ns]) :- N > 0, N1 is N - 1, plqueens_list(N1, Ns).

%% plqueens_list(N, L) :- plqueens_list_(N, [], L). 
%% 
%% plqueens_list_(0, L, L).
%% plqueens_list_(N, Ns, L) :- 
%%        N > 0, 
%%        N1 is N - 1, 
%%        plqueens_list_(N1, [N|Ns], L).






%% plqueens(4, Qs).


%% CLP version

queens(N, Qs) :- 
	constrain_values(N, N, Qs), place_queens(N, Qs).

constrain_values(N, _N, []) :-
	N .=. 0.
constrain_values(N, Range, [X|Xs]) :-
        N .>. 0, X .>. 0, X .=<. Range, 
	N1 .=. N - 1,
        constrain_values(N1, Range, Xs), no_attack(Xs, X, 1).

no_attack([], _Queen, _Nb).
no_attack([Y|Ys], Queen, Nb) :-
        Queen .<>. Y + Nb,
        Queen .<>. Y - Nb,
	N1 .=. Nb + 1,
        no_attack(Ys, Queen, N1).

place_queens(N, _) :-
	N .=. 0.
place_queens(N, Q) :- 
	N .>. 0, 
	memberq(N, Q), 
	N1 .=. N - 1, 
	place_queens(N1, Q).

memberq(X, [X|_]).
memberq(X, [_|Xs]) :- memberq(X, Xs).

%% queens(4, Qs).
%% constrain_values(4, 4, Qs)
%% constrain_values(4, 4, Qs), Qs = [3,1|OQs].
%% constrain_values(4, 4, Qs), Qs = [3,2|OQs].
%% member(4, [A, B, C, D]).

 %% no_attack([], _Queen, _Nb).
 %% no_attack([Y|Ys], Queen, Nb) :-
 %%         % Queen .<>. Y + Nb, % In Ciao!!!
 %%         abs(Queen - (Y + Nb)) .>. 0,  % Queen =\= Y + Nb
 %%         abs(Queen - (Y - Nb)) .>. 0,  % Queen =\= Y - Nb
 %% 	N1 .=. Nb + 1,
 %%         no_attack(Ys, Queen, N1).

clock(Q) :-
	statistics(walltime,_),
	findall(Q,Q,_),
	statistics(walltime,[_,T]),
	S is T/1000,
	display('Computed in '), display(S), display(' seconds.').

% Other version:

%% CLP version

queens_2(N, Qs) :- 
	constrain_values_2(N, N, Qs), place_queens_2(N, Qs).

constrain_values_2(N, _N, []) :-
	N .=. 0.
constrain_values_2(N, Range, [X|Xs]) :-
        N .>. 0, X .>. 0, X .=<. Range, 
	N1 .=. N - 1,
        constrain_values_2(N1, Range, Xs), no_attack_2(Xs, X, 1).

no_attack_2([], _Queen, _Nb).
no_attack_2([Y|Ys], Queen, Nb) :-
        Queen .<>. Y + Nb, 
        Queen .<>. Y - Nb, 
	N1 .=. Nb + 1,
        no_attack_2(Ys, Queen, N1).

place_queens_2(N, _) :-
	N .=. 0.
place_queens_2(N, Q) :- 
	N .>. 0, 
	member(N, Q), 
	N1 .=. N - 1, 
	place_queens_2(N1, Q).

 %% Equations over Finite Trees
 %% Check that two trees are isomorphic (same elements in each level)
 %% iso(t(a, b, t(X, Y, Z)), t(a, t(u, v, W), L)).

iso(Tree, Tree). 
iso(t(R, I1, D1), t(R, I2, D2)) :- 
    iso(I1, D2), 
    iso(D1, I2). 

 %% ?- iso(t(a, t(b, c, d), t(e, f, g)), T).
 %% T = t(a,t(b,c,d),t(e,f,g)) ? ;
 %% T = t(a,t(e,f,g),t(b,c,d)) ? ;
 %% T = t(a,t(e,g,f),t(b,c,d)) ? ;
 %% T = t(a,t(e,f,g),t(b,d,c)) ? ;
 %% T = t(a,t(e,g,f),t(b,d,c)) ? ;



