Booleano

A1

Scrivere un tipo di dato boolean che definisce le formule booleane:
type boolean = 
  | True 
  | False
  | Var of int
  | Not of boolean
  | And of (boolean*boolean)
  | Or of (boolean*boolean)
  | Impl of (boolean*boolean)
  | Equiv of (boolean*boolean)
Con questo tipo, (x∧y)⇒x si scrive Impl(And(Var 0, Var 1),Var 0)

A2

Scrivere una funzione string_of_boolean che trasforma una formula booleana in una stringa.
let rec string_of_boolean f = 
  let comp v f1 f2 = (string_of_boolean f1)^v^(string_of_boolean f2) in
  let par f1 = "("^f1^")" in match f with 
    True -> "True"
  | False-> "False"
  | Var i -> "x"^(string_of_int i)
  | Not f1 -> "~"^(par (string_of_boolean f1))
  | And (f1,f2) -> par (comp "/\\" f1 f2)
  | Or (f1,f2) -> par (comp "\\/" f1 f2)
  | Impl (f1,f2) -> par (comp "->" f1 f2)
  | Equiv (f1,f2) -> par (comp "<->" f1 f2)
string_of_boolean (Impl(And(Var 0, Var 1),Var 0))vale "((x0/\\x1)->x0)"

A3

Scrivere una funzione eval che prende una formula e una valutazione delle variabili e restituisce il valore della formula per questa valutazione.
La valutazione è implementata come una lista. Il valore della variabile n è l'nesimo elemento della lista:
let rec eval e f = match f with
  True -> true
| False -> false
| Var i -> List.nth e i
| Not f1 -> not (eval e f1)
| And (f1,f2) -> (eval e f1) && (eval e f2)
| Or (f1,f2) ->  (eval e f1) || (eval e f2)
| Impl (f1,f2) -> (eval e f2) || (not (eval e f1))
| Equiv (f1,f2) -> (eval e f1)=(eval e f2) 
Per esempio,
eval [true;false;true] 
         (Impl (Or (And (Var 0,Var 1), Var 2), 
                Impl (Var 2,And (Var 0, Var 1))))
vale false

A4

Scrivere una funzione table che prende una formula e scrive la sua tabella di verità.

Per produrre la tabella, prima bisogna sapere il numero delle variabili:

let rec maxI f = match f with
     True -> 0
   | False -> 0
   | Var i -> i
   | Not f1 -> (maxI f1)
   | And (f1,f2) -> max (maxI f1) (maxI f2)
   | Or (f1,f2) -> max (maxI f1) (maxI f2)
   | Impl (f1,f2) -> max (maxI f1) (maxI f2)
   | Equiv (f1,f2) -> max (maxI f1) (maxI f2)
Adesso
let table f = 
  let n = maxI f in
  (* Calcola il numero delle cifre di  k *)
  let rec digit k = if k < 10 then 1 else 1+digit (k/10) in
  (* Lunghezza di una casella *)
  let size = max 5 ((digit n)+1)  in
  (* Un bool come una stringa *)
  let string_of_bool b = if b then " true" else "false" in
  (* Indentazione per valori booleani *)
  let indent = (String.make (size-5) ' ')^"|" in
  (* Generazione della tabella *)
  let rec gen e k = match k with
    0 ->
      begin
        List.iter (fun x -> print_string (string_of_bool x); print_string indent) e;
        print_string (string_of_bool (eval e f)); print_newline();
      end
  | _ -> gen (false::e) (k-1);gen (true::e) (k-1) in
  (* Separatore *)
  let sep = String.make ((n+2)*(size+1)) '-' in
  (* Sep *)
  print_string sep; print_newline();
  (* Prima riga *)
  for i=0 to n do
    print_string (String.make (max 0 (size-((digit i)+1))) ' ');
    print_string "x"; print_int i; print_string "|";
  done;
  print_string (string_of_boolean f); print_newline();
  (* Sep *)
  print_string sep; print_newline();
  (* Tabella *)
  gen [] (n+1);
  (* Sep *)
  print_string sep; print_newline()
Con table (And (Var 0, Var 1)) abbiamo:
------------------
   x0|   x1|(x0/\x1)
------------------
false|false|false
 true|false|false
false| true|false
 true| true| true
------------------

A5

Scrivere una funzione sat che verifica se una formula è soddisfacibile.
let sat f = 
  let n = maxI f in
  let rec satn e k = match k with
    0 -> eval e f
  | _ -> if (satn (false::e) (k-1)) then true else satn (true::e) (k-1) 
  in
  satn [] (n+1)

A6

Scrivere una funzione taut che verifica se una formula è una tautologia.
let taut f = 
  let n = maxI f in
  let rec tautn e k = match k with
    0 -> eval e f
  | _ -> if (tautn (false::e) (k-1)) then (tautn (true::e) (k-1)) else false 
  in
  tautn [] (n+1)

*A7

Dare una valutazione delle variabili che rende la formula x⇒ (y ⇒ x) vera.

ogni valutazione va bene, la formula è una tautologia.

*A8

Dare una valutazione delle variabili che rende la formula x⇒ (y ⇒ x) falsa.

non c'è tale valutazione, la formula è una tautologia.

*A9

Dare una valutazione delle variabili che rende la formula ¬y⇒ (x ∧ y) vera.

la formula è equivalente a y, dunque y=false la rende vera.

*A10

Dare una valutazione delle variabili che rende la formula ¬y⇒ (x ∧ y) falsa.

la formula è equivalente a y, dunque y=false la rende falsa.

*A11

Dare una valutazione delle variabili che rende la formula ((x⇒ y) ⇒ x) ⇒ x vera.

ogni valutazione va bene, la formula è una tautologia.

*A12

Dare una valutazione delle variabili che rende la formula ((x⇒ y) ⇒ x) ⇒ x falsa.

non c'è tale valutazione, la formula è una tautologia.

*A13

Dare la tabella di verità per la formula (x∨ ¬y) ⇒ (x ∧ y).

xyx∨¬yx∧y(x∨ ¬y) ⇒ (x ∧ y)
falsofalsoveroverovero
falsoveroverofalsofalso
verofalsofalsofalsovero
veroveroverofalsofalso

Problema delle N regine

B1

Scrivere una funzione exactly_one che prende una variabile iniziale i, un incremento j ed un numero n e restituisce la formula booleana che esprime che esattamente una delle variabili x(i), x(i+j),...,x(i+(n-1)*j) è vera.
let exactly_one i j n = 
  let rec  f k n1 = 
    if (n1=1) then (Not (Var k))
    else And (Not (Var k), f (k+j) (n1-1))
  in 
  let rec  g k n2 = 
    if (n2=1) then (Var k)
    else And (Impl (Var k, f (k+j) (n2-1)),
              Impl (Not (Var k), g (k+j) (n2-1)))
  in  g i n

B2

Scrivere una funzione at_most_one che prende una variabile iniziale i, un incremento j e un numero n e restituisce la formula booleana che esprime che al massimo una delle variabili x(i), x(i+j),...,x(i+(n-1)*j) è vera.
let at_most_one i j n = 
  let rec  f k n1 = 
    if (n1=1) then (Not (Var k))
    else And (Not (Var k), f (k+j) (n1-1))
  in 
  let rec  g k n2 = 
    if (n2=2) then  Impl (Var k, f (k+j) (n2-1))
    else And (Impl (Var k, f (k+j) (n2-1)),Impl (Not (Var k), g (k+j) (n2-1)))
  in  if (n=1) then True else g i n

B3

Scrivere una funzione chessboard che prende la dimensione n e restituisce la formula corrispondente al problema delle n regine.
let gen_rows n = 
  let rec f k n1 = 
    if (n1=1) then exactly_one k 1 n
    else And(exactly_one k 1 n, f (k+n) (n1-1))
  in f 0 n

let gen_cols n = 
  let rec f k n1 =
    if (n1=1) then exactly_one k n n
    else And(exactly_one k n n, f (k+1) (n1-1))
  in f 0 n

let gen_diags n =
  let rec  f k n1 = 
      if (n1=1) then  at_most_one k (n-1) n
      else  And (And (And (at_most_one k (n+1) n1,
                          at_most_one k (n-1) (n-n1+1)),
                     And (at_most_one (n*n-k-1) (-(n+1)) n1,
                          at_most_one (n*n-k-1) (-(n-1)) (n-n1+1))),
                f (k+1) (n1-1))
  in 
    if (n=1) then True
    else And (at_most_one 0 (n+1) n, f 1 (n-1))

let chessboard n =
  if (n<2) then True
  else And (And (gen_rows n, gen_cols n), gen_diags n)

Algoritmo di Davis-Putnam

C1

Scrivere una funzione expand che cambia le implicazioni e le equivalenze con le formule equivalenti.
let rec expand f = match f with
  Var _ | True | False   -> f
| And(a,b) -> And(expand a, expand b)
| Or(a,b) -> Or(expand a, expand b)
| Not a -> Not (expand a)
| Impl(a,b) -> Or(Not (expand a),expand b)
| Equiv(a,b) -> And (Or(expand a,Not (expand b)),Or(Not (expand a),expand b))

C2

Scrivere una funzione check_cnf che verifica se una formula di tipo boolean è in forma CNF.
let check_cnf f = 
  let rec only_neg f = match f with
    True | False | Var _ -> true
  | Not a -> only_neg a
  | _ -> false in
  let rec only_or f = match f with
    Or(a,b) -> (only_or a) && (only_or b)
  | _ -> only_neg f in
  let rec only_and f = match f with
    And(a,b) -> (only_and a) && (only_and b)
  | _ -> only_or f in
  only_and f

C3

Scrivere una funzione push_neg che, data una formula booleana contenente solo ∧,∨,¬, mette in fondo le negazioni.
let rec push_neg f = match f with
  Or (a,b) -> Or (push_neg a, push_neg b)
| And (a,b) -> And (push_neg a, push_neg b)
| Not a -> rpush_neg a
| _ -> f
and
rpush_neg f = match f with
  Or (a,b) -> And (rpush_neg a, rpush_neg b)
| And (a,b) -> Or (rpush_neg a, rpush_neg b)
| Not a -> push_neg a
| True -> False
| False -> True
| _ -> Not f

C4

Scrivere una funzione push_or che, data una formula booleana contenente solo ∧,∨,¬, mette le disgiunzioni dopo le congiunzioni.
let push_or f =
  let rec push_rules a b = match (a,b) with
    And (a1,a2), _ -> And (push_rules a1 b, push_rules a2 b)
  | _, And (b1,b2) -> And (push_rules a b1, push_rules a b2)
  |  _ -> Or (a,b) in
  let rec push f = match f with
    And (a,b) -> And (push a, push b)
  | Or (a,b) -> push_rules (push a) (push b)
  | _ -> f in
  push f

C5

Scrivere una funzione cnf che trasforma una formula booleana in una formula equivalente in forma CNF.
let cnf f = push_or (push_neg (expand f))

*C6

Mettere in forma CNF la formula (x∨¬y)⇒z.

C7

Scrivere una funzione eval_disj che valuta una formula (composta unicamente di disgiunzioni, variabili e negazioni di variabili) rispettivamente a una valutazione di alcune variabili, e dice se la formula è vera o falsa o quali sono le variabili della formula che non hanno valore nella valutazione data.
La valutazione può essere rappresentata come una lista. I valori dentro questa lista sono vero, falso o sconosciuto.
(* Il tipo dei valori: vero, falso, o sconosciuto *)
type value = 
    TRUE 
  | FALSE
  | NONE

(* Trovare il valore di una variable dentro la lista *)
let rec get_env e i = match (e,i) with
  (v::e1), 0 -> v
| (v::e1), _ -> get_env e1 (i-1)
| [], _ -> NONE

(* Il tipo del risultato della valutazione *)
type eval = 
  Satisfied 
| Contradiction 
| Vars of boolean list

let rec eval_disj e f = match f with 
  Var i ->
    begin
     match get_env e i with
       TRUE -> Satisfied
     | FALSE -> Contradiction
     | NONE -> Vars [Var i]
    end
| Not (Var i) ->
    begin
     match get_env e i with
       TRUE ->  Contradiction
     | FALSE -> Satisfied
     | NONE ->  Vars  [Not (Var i)]
    end
| Or (f1,f2) -> 
     begin 
       match eval_disj e f1 with
         Satisfied -> Satisfied 
       | Vars l1 ->
           begin 
             match eval_disj e f2 with
               Satisfied -> Satisfied
             | Contradiction -> Vars l1
             | Vars l2 -> Vars (List.append l1 l2)
           end
       | Contradiction -> eval_disj e f2
      end
| False -> Contradiction
| _ -> Satisfied

C8

Scrivere una funzione eval_conj che prende una formula in forma CNF e una valutazione e dice se per questa valutazione la formula è vera, falsa, o ci sono candidati alla risoluzione unitaria.
let rec eval_conj e f = match f with 
  And (f1, f2) ->
    begin
     match eval_conj e f1 with
       Contradiction -> Contradiction
     | Satisfied -> eval_conj e f2 
     | Vars l1 ->
         begin
           match eval_conj e f2 with 
             Satisfied -> Vars l1
           | Contradiction -> Contradiction
           | Vars l2 -> Vars (List.append l1 l2)
         end
    end
|  _ ->
         begin
           match eval_disj e f with
             Satisfied -> Satisfied
           | Contradiction -> Contradiction
           | Vars [v1] -> Vars [v1]
           | Vars _ -> Vars []
         end

C9

Scrivere una funzione davis che implementa l'algoritmo di Davis-Putnam.
(* Update della lista e all'indice i con il valore v
  Usa il tipo option per dire se questo update e' possible *)
let rec set_env e i v = match (e,i) with
  (NONE::e1), 0 -> Some (v::e1)
| (v1::e1), 0 -> if (v1=v) then (Some e) else None
| (v1::e1), _ -> 
    begin 
      match (set_env e1 (i-1) v) with
        None -> None
      | Some e2 -> Some (v1::e2)
    end
| [], _ -> set_env [NONE] i v

(* Da una lista costruisce due liste dove il primo NONE della
   lista iniziale e' stato rimpiazzato con TRUE e FALSE rispettivamente *)
let rec split_env e  = match e with
  []  -> [TRUE],[FALSE]
| NONE::e1 -> TRUE::e1, FALSE::e1
| v::e1 -> let l1,l2 = split_env e1 in
            v::l1,v::l2 

(* Assegna dentro e a v il valore TRUE *)
let set_var e v = match v with
  (Var i) -> set_env e i TRUE
| (Not (Var i)) -> set_env e i FALSE
| _ -> Some e

(* Assegna a tutte le variabili dentro l il valore TRUE *)
let rec set_vars e l = match l with
  [] -> Some e
| v::l1 ->
   begin 
     match set_var e v with
       None -> None
     | Some e1 -> set_vars e1 l1
   end


let rec davis f = 
  let rec  davisn e = match eval_conj e f with
    Satisfied -> true
  | Contradiction -> false
  | Vars [] -> 
      let e1,e2 = split_env e in
        if (davisn e1) then true else (davisn e2)
  | Vars l -> 
      begin
        match set_vars e l with
          None -> false
        | Some e1 -> davisn e1
      end
  in davisn []

(¬x ∨ z) ∧ (y ∨ z).

Algoritmo di Stålmarck

D1

Scrivere una funzione transform che cambia le disgiunzioni e le implicazioni con le formule equivalenti.
let rec transform f = match f with
  Var _ | True | False   -> f
| And(a,b) -> And (transform a, transform b)
| Or(a,b) -> Not (And (transform (Not a), transform (Not b)))
| Not a -> Not (transform a)
| Impl(a,b) -> Not (And (transform a, Not (transform b)))
| Equiv(a,b) -> Equiv (transform a, transform b)

D2

Scrivere una funzione make_triplets che trasforma una formula in una lista di triple.
(* Una tripla e' composta da 3 numeri relativi:
     True -> 1
     False-> -1
     (Var i)-> i+2
     (Not (Var i)) -> -(i+2) *)
type triplet = Andt of int*int*int | Equivt of int*int*int

(* Ritorna la variabile di testa e la lista di triplet *)
let make_triplets f = 
  (* la prima variabile disponibile *)
  let nvar = (maxI f)+3 in
  (* Espande implicazione e disgiunzione *)
  let f1= transform f in
  let rec iter f n l = match f with
    True -> 1,l,n
  | False -> -1,l,n
  | Var i -> i+2,l,n
  | Not f1 -> let i1,l1,n1 = iter f1 n l in -i1,l1,n1
  | And(f1,f2) ->
      let i1,l1,n1 = iter f1 n l in 
      let i2,l2,n2 = iter f2 n1 l1 in 
        n2,(Andt (n2,i1,i2))::l2,n2+1
  | Equiv(f1,f2) ->
      let i1,l1,n1 = iter f1 n l in 
      let i2,l2,n2 = iter f2 n1 l1 in 
        n2,(Equivt (n2,i1,i2))::l2,n2+1
  (* Impossibile *)
  | _ -> 1,l,n 
  in let i1,l1,n1=iter f1 nvar [] 
  in i1,l1

D3

Definire un tipo di dato per rappresentare l'ambiente (la collezione di equazioni). Su tale tipo, definire le funzioni:
(* {v1=true,v2=-v3, v2=v5, v4=v6}
   come numeri interi (true->1, vi-> i+2)
   {2=1,3=-4,3=6,5=7} e' rappresentato come:
     [(1, Class [1,2]);
      (2, Ref 1);
      (3, Class [3,-4;6]);
      (4, Ref (-3));
      (5, Class [5,7]);
      (6, Ref (3));
      (7, Ref (5))]

*)

type tvalue = Ref of int | Class of int list
type env = (int*tvalue) list


(* Trovare il valore di una variabile dentro l'ambiente
   il valore di default e' Class[i] *)
let rec get_env e i = match e with
  ((j,v)::e1) -> if (i<j) then Class[i] else
                 if (j<i) then get_env e1 i else v
| [] -> Class [i]

(* Update dell'ambiente *)
let rec set_env e i v = match e with
  ((j,v1)::e1) -> if (i<j) then (i,v)::e else
                  if (j<i) then (j,v1)::set_env e1 i v else (i,v)::e1
| [] -> [(i,v)]

(* Ritorna il numero piu` piccolo (in valore assoluto) che e' uguale a v *)
let get_min e v = 
  if v>=0 then
    (match get_env e v with
       Ref i -> i
     | Class l -> v)
  else
    (match get_env e (-v) with
       Ref i -> -i
     | Class l -> v)

(* Ritorna la classe d'equivalenze *)
let rec get_class e v = 
  if 0<v then
    (match get_env e v with
       Ref i -> get_class e i
     | Class l -> l)
  else
    List.map (fun x -> -x)
    (match get_env e (-v) with
       Ref i -> get_class e i
     | Class l -> l)

(* Da' il segno di n: 1 se n>=0 altrimenti -1 *)
let sign n = if 0 <= n then 1 else -1

(* Fa un append di due liste ordinate *)
let rec merge_list l1 l2 = match (l1,l2) with
  _,[] -> l1
| [],_ -> l2
|(a::l1'),(b::l2') -> let res = (abs a)-(abs b) in
                      if res<0
                      then a::merge_list l1' l2
                      else if res=0
                      then a::merge_list l1' l2'
                      else b::merge_list l1 l2'


(* Usa l'indice 0 per dire se un ambiente e' una contraddizione *)
let make_contradictory e = set_env e 0 (Ref (-1))
let is_contradictory e = 
  match get_min e 0 with
    -1 -> true
  | _ -> false


(* Aggiunge una equazione all'ambiente. 
   Usa il tipo option per dire se e' possibile o no *)
let add_eq e v1 v2 = 
  let v1'= get_min e v1 in
  let v2'= get_min e v2 in
  if v1'=v2' then e else 
  if v1'= -v2' then make_contradictory e else 
  let v1'',v2'' =  if (abs v1') <= (abs v2')
                   then abs v1',(sign v1')*v2'
                   else abs v2',(sign v2')*v1'  in
  let l1 = get_class e v1'' in
  let l2 = get_class e v2'' in
  (* Tutti gli elementi di l2 devono puntare su v1''*)
  let e1 = List.fold_left (fun e1 v -> 
                   if 0<v then set_env e1 v (Ref v1'')
                   else set_env e1 (-v) (Ref (-v1''))) e l2 in
  (* La classe d'equivalenze di v1'' e' l'unione di l1 e l2 *)
  set_env e1 v1'' (Class (merge_list l1 l2)) 

D4

Scrivere una funzione propagate che prende un ambiente e due variabili v1 e v2 e fa la propagazione dell'equazione v1=v2.
let ttrue = 1
let tfalse = -1

let rec apply_rule tl e t = match t with
  Andt (v1,v2,v3) -> 
    let v1'=(get_min e v1) in
    let v2'=(get_min e v2) in
    let v3'=(get_min e v3) in
      if v1'=ttrue then propagate tl (propagate tl e v2' v3') v3' ttrue
      else if v2'=ttrue then propagate tl e v1' v3'
      else if v2'=tfalse then propagate tl e v1' tfalse
      else if v3'=ttrue then propagate tl e v1' v2'
      else if v3'=tfalse then propagate tl e v1' tfalse
      else if v1'= -v2' then propagate tl (propagate tl e  v1' v3') v3' tfalse
      else if v1'= -v3' then propagate tl (propagate tl e v1' v2') v2' tfalse
      else if v2'=v3' then propagate tl e v1' v2'
      else if v2'= -v3' then propagate tl e v1' tfalse
      else e
| Equivt (v1,v2,v3) -> 
    let v1'=(get_min e v1) in
    let v2'=(get_min e v2) in
    let v3'=(get_min e v3) in
      if v1'=ttrue then propagate tl e v2' v3'
      else if v1'=tfalse then propagate tl e v2' (-v3')
      else if v2'=ttrue then propagate tl e v1' v3'
      else if v2'=tfalse then propagate tl e v1' (-v3')
      else if v3'=ttrue then propagate tl e v1' v2'
      else if v3'=tfalse then propagate tl e v1' (-v2')
      else if v1'=v2' then propagate tl e v3' ttrue
      else if v1'= -v2' then propagate tl e v3' tfalse
      else if v1'=v3' then propagate tl e v2' ttrue
      else if v1'= -v3' then propagate tl e v2' tfalse
      else if v2'=v3' then propagate tl e v1' ttrue
      else if v2'= -v3' then propagate tl e v1' tfalse
      else e
and propagate tl e v1 v2 =
    let e1 = add_eq e v1 v2 in
    if is_contradictory e1 then e1 else
    if (e=e1) then e else 
    propagate_list tl e1 tl

and propagate_list tl e l = match l with
  [] -> e
| t::l1 -> let e1 = apply_rule tl e t in
             if is_contradictory e1 then e1
             else propagate_list tl e1 l1

D5

Scrivere una funzione get_intersect che calcola l'intersezione di due ambienti.
(* Aggiunge ad e tutte le equazioni che sono vere per e1 ed e2  *)
let get_intersect e1 e2 e =
 let rec min_elt l1 l2 = match (l1,l2) with
   [],_ -> 0
 | _,[] -> 0 
 | (a1::l1'),(a2::l2') ->
     let res = (abs a1)-(abs a2) in
       if res<0
       then min_elt l1' l2
       else if 0<res
       then min_elt l1 l2'
       else if a1<>a2
       then min_elt l1' l2'
       else a1 in
 let rec inter e l1 l2 = match (l1,l2) with
   [],_ -> e
 | _,[] -> e
 | (i1,v1)::l1',(i2,v2)::l2' ->
     if (i1<i2) then inter e l1' l2
     else if (i2<i1) then inter e l1 l2' 
     else
        inter (add_eq e i1
                 (min_elt (get_class e1 i1) (get_class e2 i2)))
               l1' l2'  in

 if is_contradictory e1 then e2
 else if is_contradictory e2 then e1 
 else inter e e1 e2

D6

Scrivere una funzione stal che, dati un livello d'iterazione ed una formula, utilizza l'algoritmo di Stålmarck per sapere se la formula è una tautologia.
let stal n f =
 let i,l = make_triplets f in
 let ai = abs i in
 let rec find_var e  m = 
   if ai<m then None
   else if m=(get_min e m) then Some m
   else find_var e (m+1) in
 (* Prova tutte le variabili *)
 let rec try_all e n m =
   match find_var e m with
     None -> e
  |  Some i ->
      let e1 = stalN n e i ttrue in
      let e2 = stalN n e i tfalse in
      let e3 = get_intersect e1 e2 e in
      if is_contradictory e3 then e3 
      else try_all e3 n (m+1) 
 and
  (* Itera fino a quando c'è una contraddizione o l'ambiente non cambia *)
  repeat_try_all e n = 
    let e1 = try_all e n 2 in
    if (is_contradictory e1) ||  e=e1 then e1
    else repeat_try_all e1 n
 and
  stalN n e v1 v2 = 
   let e1 = propagate l e v1 v2 in
   if (is_contradictory e1) || (n=0) then e1
   else repeat_try_all e1 (n-1) in
 if is_contradictory (stalN n [] i tfalse)
 then "Tautologia" else "Nessuna conclusione"

Laurent Théry
Last modified: Tue Apr 30 01:42:15 MEST 2002