%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Aggregators
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Logical Operators
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Logic Product.
% and_prod(_, tv(0,[]), tv(0,[])):-!. % revisar para quitar, sale 0.0 y eso provoca un error
and_prod(tv(X, Elem1), tv(Y, Elem2), tv(Z, Elem0)):- pri_prod(X,Y,Z), append(Elem1, Elem2, Elem0), !.
or_prod(tv(X, Elem1),  tv(Y, Elem2), tv(Z, Elem0)):- pri_prod(X,Y,U1), pri_add(X,Y,U2), pri_sub(U2,U1,Z), append(Elem1, Elem2, Elem0), !.

% Luka Logic
and_luka(tv(X, Elem1), tv(Y, Elem2), tv(Z, Elem0)):- pri_add(X,Y,U1), pri_sub(U1,1,U2), pri_max(U2,0.0,Z), append(Elem1, Elem2, Elem0), !.
or_luka(tv(X, Elem1),  tv(Y, Elem2), tv(Z, Elem0)):- pri_add(X,Y,U1), pri_min(U1,1,Z), append(Elem1, Elem2, Elem0), !.

% Godel Logic
and_godel(tv(X, Elem1), tv(Y, Elem2), tv(Z, Elem0)):- pri_min(X,Y,Z), append(Elem1, Elem2, Elem0), !.
or_godel(tv(X, Elem1),  tv(Y, Elem2), tv(Z, Elem0)):- pri_max(X,Y,Z), append(Elem1, Elem2, Elem0), !.

% Average or prioritization. The lists are concatenated but the items will always be an empty list
agr_avg(tv(V1,E1), tv(V2,E2), tv(T1,F1), tv(T2,F2), tv(V3,G)):- V3 is (V1*T1 + V2*T2)/(T1+T2),
	append(E1,E2,A1), append(F1,F2,A2), append(A1,A2,G).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RELATIONAL OPERATORS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/* agregator condition more general
            agr_cond(Operation, TV_OPI, TV_OPD, TV_Res)

                 Operation = Operacion que se realizara
		 TV_OPI = Lista con Operadores Isquierdo
		 TV_OPD = Operador Derecho
		 TV_Res = tv result

      Realiza una operacion Relacional, Operation (<, >, =, <>, ...),
      entre los elementos TV_OPI y TV_OPD, y devuelve el mayor grado de
      verdad encontrado en TV_Res


*/
agr_cond(Operation, TV1, TVB, Sol):- !, tv_to_listTv(TV1, TV1_List), agr_cond(Operation, TV1_List, TVB, tv(0.0, []), Sol).

agr_cond(_, tv(_, []), _, Sol, Sol):- !.                           % Cuando termina de recorrer devuelve la ultima solucion valida

agr_cond(Operation, tv(_, [tv(A, _)]), tv(_,[tValue(B)]), _, tv(Sol,[])):- !, % si se compara con un valor de verdad
	                              Condition =..[Operation, A, B],
				      ((Condition, !, Sol is 1);(Sol is 0)).

/* Se eliminan los elements... porque puede haber un valor de Tag que se necesite que este acompañado por otros tags...
*/
agr_cond(Operation, tv(_, [tv(_, [element(_,_,_)])|Rest]), tv(_, B), tv(TV2,[]), Sol):-!,
	agr_cond(Operation, tv(_, Rest), tv(_, B), tv(TV2,[]), Sol).

agr_cond(Operation, tv(_, [tv(TV, [A])|Rest]), tv(_, B), tv(TV2,[]), Sol):-
	name(A,N), name(A1, N),	Condition =..[Operation, A1, B],
	        (
		 ( Condition, !,
		              ((TV > TV2, !, agr_cond(Operation, tv(_,Rest), tv(_, B), tv(TV,[]),  Sol));
		                            (agr_cond(Operation, tv(_,Rest), tv(_, B), tv(TV2,[]), Sol))));
   		              (agr_cond(Operation, tv(_,Rest), tv(_, B), tv(TV2,[]), Sol))
		).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PRIMITIVE name(N3,N1), name(N4, N2)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Basic Operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pri_prod(X,Y,Z):- Z is X * Y.
pri_sub(X,Y,Z) :- Z is X-Y.
pri_add(X,Y,Z) :- Z is X+Y.
pri_min(X,Y,Z) :- (X=<Y,Z=X;X>Y,Z=Y).
pri_max(X,Y,Z) :- (X=<Y,Z=Y;X>Y,Z=X). % max(x,y)


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Aggregator Fuse
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Si el hijo no existe, y no hay hermanos.
agr_fuse(_,tv(0.0,[[tag(noExist),[]],[],[]]), [], tv(0.0,[[tag(noExist),[]],[],[]])):-!.
agr_fuse(_,tv(0.0,[[tag(noExist),[]],[],[]]), [], tv(0.0,[[tag(noExist),[]],[],[]])):-!.
agr_fuse(tv(0.0, [[tag(noExist), []], [], []]),_,tv(0.0, [[tag(noExist), []], [], []]),tv(0.0, [[tag(noExist), []], [], []])):-!.
agr_fuse(tv(0.0, [[tag(noExist), []], [], []]),_, TV4, TV4):-!.



% If exist a value other than void between the son and brother, then the truth value will be one, otherwise zero.
agr_fuse(tv(_,[]),      tv(0.0,[]), tv(0.0,[]),    tv(0.0,[])):-!.
% agr_fuse(tv(_,[]), tv(0,[]), TV4, tv(1.0, [[], [], TV4])):-!.

/* Cuando el actual no existe ni tampoco tiene hijos, pero si hay hermano entonces el hermano se convierte en Iam
el valor de X se pone a 1, porque se supone que no encontro ningun
hermano antes, y se anula el DOWN, ya que ahora solo se penaliza... si
se encontro con anterioridad el valor de buscado.
Se podria decir que se pierde el TV del hermano.
*/
agr_fuse(tv(_,[]), tv(0.0,[]), tv(_, Elem), tv(1.0, Elem)):-!. %%  porque


% Se modifico aqui para arrastrar el valor de verdad.
agr_fuse(tv(X,[]),      TV3,      tv(0.0,[]),  tv(Y, [[], TV3, []])):- !, X1 is X*1.0,((X1=0.0,!, Y is 1.0);( Y is X)).
agr_fuse(tv(X,[]),      TV3,      TV4,         tv(Y, [[], TV3, TV4])):-!, X1 is X*1.0,((X1=0.0,!, Y is 1.0);( Y is X)).

% Used when you have a consultation with a relative path. It creates a new branch, where they put the sons
agr_fuse(tv(X, [Element, Son, Sibling]), tv(0.0,[]), tv(0.0,[]), tv(X, [Element, Son, Sibling])):-!.
agr_fuse(tv(X, [Element, Son, _]),       tv(0.0,[]), TV4,        tv(X, [Element, Son, TV4])):-!.
agr_fuse(tv(X, [Element, Son, _]),       TV3,        TV4,        tv(X, [Element, Son, TV_Aux])):-
	agr_fuse(tv(0.0,[]), TV3, TV4, TV_Aux), !.

% TAG  is the sought
agr_fuse(tv(X, Element), tv(0.0,[]), tv(0.0,[]), tv(X, [Element, [],  []])):-!.
agr_fuse(tv(X, Element), tv(0.0,[]), TV4,        tv(X, [Element, [],  TV4])):-!.
agr_fuse(tv(X, Element), TV3,        tv(0.0,[]), tv(X, [Element, TV3, []])):-!.
agr_fuse(tv(X, Element), TV3,        TV4,        tv(X, [Element, TV3, TV4])):-!.






% Agrega un Tv al final de la estructura. para debug... revisar
agr_paste(tv(0.0,[]), tv(0.0,[]), tv(0.0,[])):- !.
agr_paste(tv(0.0,[]), TV0, TV0):- !.
agr_paste(TV0, tv(0.0,[]), TV0):- !.
agr_paste(tv(X,[Element, Son, []]),TV_Sib, tv(X,[Element, Son, TV_Sib])):- !.
agr_paste(tv(X,[Element, Son, Sibling]),TV_Sib, tv(X,[Element, Son, SiblingR])):- agr_paste(Sibling, TV_Sib, SiblingR),!.






























