/* alberi binari */
/* is_tree(T) : 
   T e' un albero binario */
is_tree(empty).
is_tree(t(_,Left,Right)) :-
        is_tree(Left),
        is_tree(Right).



/* in_tree(El,T): El occorre come etichetta in T */
%% caso base: El occorre in un albero che ha El come radice

in_tree(El,t(El,_, _)). %% uso dell’unificazione

%% casi ricorsivi: El occorre in un albero se occorre
%% nel suo sottoalbero sinistro o in quello destro
in_tree(El,t(_,Left, _)) :-
	in_tree(El,Left).
in_tree(El, t(_,_,Right)) :-
	in_tree(El,Right).

/* Le ultime due clausole si possono compattare usando l'OR del Prolog:

in_tree(El,t(_,Left,Right)) :-
    in_tree(El,Left) ; in_tree(El,Right).
   */

/* per fare test senza riscrivere ogni volta lo stesso albero,
   possiamo usare un predicato che memorizza l'albero */
albero(t(a,t(b,t(c,t(d,empty,empty),t(e,empty,empty)),empty),
t(f,t(g,t(h,empty,empty),empty),t(i,empty,empty)))).

/* size(+T,?N): T ha N nodi */
size(empty,0).
size(t(_,Left,Right),H) :-
        size(Left,N), size(Right,M), H is N+M+1.

/* height(+T,?N): T ha altezza N */
height(empty,0).
height(t(_,Left,Right),H) :-
        height(Left,N), height(Right,M),
        (N>M, H is N+1 ; N=<M, H is M+1).

/* procedura per generare un albero completo di profondita' P
   etichettato da interi */
completo(+P,?T) :- competo(P,1,T).

completo(0,_,empty).
completo(N,K,t(K,Left,Right)) :-
	M is N-1,
	Kleft is K*2,
	completo(M,Kleft,Left),
	Kright is Kleft+1,
	completo(M,Kright,Right).

/* liste */
/* mem=member predefinito,
   member(?Elem, ?List):
    True if Elem is a member of List.
   */
mem(X,Lista) :-
	Lista=[X|_] ;
	Lista=[_|Coda], mem(X,Coda).

/* lung(L,N) = la lista L ha lunghezza N */
lung([],0).
lung([_|Coda],N) :- length(Coda,M), N is M+1.

/* sum_of_lung(+L,?N) = L e' una lista di liste e N e' 
          la somma delle lunghezze delle liste  in L */
sum_of_lung([],0).
sum_of_lung([Prima|Resto],N) :-
        length(Prima,P), sum_of_lung(Resto,R), N is P+R.

/* concat=append */
concat([],X,X).
concat([X|L1],L2,[X|L3]) :- concat(L1,L2,L3).

/* append3(L1,L2,L3,Result) = Result e’ la concatenazione
di L1, L2 e L3 */
append3(L1,L2,L3,Result) :-
	append(L1,L2,L),
	append(L,L3,Result).

/*  nexto(X,Y,L) = X e Y sono due elementi adiacenti in L */
/*nexto(X,Y,L) :- append3(_,[X,Y],_,L).*/
nexto(X,Y,[X,Y|_]).
nexto(X,Y,[_|Coda]) :- nexto(X,Y,Coda).

/* backtracking */
/*
  +T,?Leaf,?Ramo) = Ramo è un ramo di T dalla radice a una foglia
etichettata da Leaf */

branch(t(X,empty,empty),X,[X]).
branch(t(X,Left,Right),Leaf,[X|Ramo]) :-
	branch(Left,Leaf,Ramo) ;
	branch(Right,Leaf,Ramo).

/* operatori per rappresentare i connettivi logici */
:- op(600,yfx,&).
:- op(650,yfx,v).
:- op(670,yfx,=>).
:- op(680,yfx,<=>).

/* subforms(+A,-List): List è una lista con tutte le sottoformule di A. */

subforms(-A,[-A|Rest]) :-
	!, subforms(A,Rest).
subforms(A,[A|Rest]) :-
	(A = F & G ; A = F v G ;
	 A = F => G ; A = F <=> G),
	!,
	subforms(F,Fforms), subforms(G,Gforms),
	append(Fforms,Gforms,Rest).
subforms(A,[A]).

