summaryrefslogblamecommitdiff
path: root/abstract/formula.ml
blob: a124a84a14d98d4124dd2fde687e93c7cf59f367 (plain) (tree)
1
2
3
4
5
6
7
8
9
        

             


                              


                    

                                                 




                       

                                                     


                  






                                                                           
 








                      
                                         
 



                                                       
                                                      

                                     


                                 

                                                 

                     











                                               
 

                                                   




                                            
 


                           

                       
 



                                                                    






                                                        

                                                               


                                   
                                                                              
                             
                             
                      
                                                                   








                           
                             

                                                          




                                                        

 




                                                                         
                                                                   







                                                    
                               







                                
                                                      
                  
                                                  
                         
               


                                         
                







                                                       

                 

                                        
                         
                            
                           



                                                  
       


                                                      
 




































                                                                            



                             








                                                     





































                                                                         













                                                                 

                                                



                                  
open Ast
open Util
open Ast_util

(* AST for logical formulas *)


(* Numerical part *)

(* bool on numerical operators : is it float ? *)

type num_expr =
  (* constants *)
  | NIntConst of int
  | NRealConst of float
  (* operators *)
  | NBinary of binary_op * num_expr * num_expr * bool
  | NUnary of unary_op * num_expr * bool
  (* identifier *)
  | NIdent of id

type num_cons_op =
  | CONS_EQ | CONS_NE
  | CONS_GT | CONS_GE
type num_cons = num_expr * num_cons_op  (* always imply right member = 0 *)


(* Logical part *)


(* Enumerated types *)
type enum_expr =
  | EIdent of id
  | EItem of string

type enum_op =
  | E_EQ | E_NE

type enum_cons = enum_op * id * enum_expr

type bool_expr =
  (* constants *)
  | BConst of bool
  (* operators from numeric values to boolean values *)
  | BRel of binary_rel_op * num_expr * num_expr * bool
  (* operators on enumerated types *)
  | BEnumCons of enum_cons
  (* boolean operators *)
  | BAnd of bool_expr * bool_expr
  | BOr of bool_expr * bool_expr
  | BTernary of bool_expr * bool_expr * bool_expr
      (* (A and B) or ((not A) and C) *)
  | BNot of bool_expr

let is_false = function
  | BConst false | BNot(BConst true) -> true
  | _ -> false
let is_true = function
  | BConst true | BNot(BConst false) -> true
  | _ -> false

let f_and a b =
  if is_false a || is_false b then BConst false
  else if is_true a then b
  else if is_true b then a
  else BAnd(a, b)

let f_and_list = List.fold_left f_and (BConst true)

let f_or a b =
  if is_true a || is_true b then BConst true
  else if is_false a then b
  else if is_false b then a
  else BOr(a, b)

let f_ternary c a b =
  if is_true c then a
  else if is_false c then b
  else
      BTernary(c, a, b)

let f_e_op op a b = match a, b with
  | EItem i, EItem j -> BConst (if op = E_EQ then i = j else i <> j)
  | EIdent x, v | v, EIdent x -> BEnumCons(op, x, v)
let f_e_eq = f_e_op E_EQ

(* Write all formula without using the NOT operator *)

let rec eliminate_not = function
  | BNot e -> eliminate_not_negate e
  | BAnd(a, b) -> BAnd(eliminate_not a, eliminate_not b)
  | BOr(a, b) -> BOr(eliminate_not a, eliminate_not b)
  | BTernary(c, a, b) ->
    BTernary(eliminate_not c, eliminate_not a, eliminate_not b)
  | x -> x
and eliminate_not_negate = function
  | BConst x -> BConst(not x)
  | BEnumCons(op, a, b) -> BEnumCons((if op = E_EQ then E_NE else E_EQ), a, b)
  | BNot e -> eliminate_not e
  | BRel(r, a, b, is_real) ->
    if r = AST_EQ then
      BOr(BRel(AST_LT, a, b, is_real), BRel(AST_GT, a, b, is_real))
    else
      let r' = match r with
      | AST_EQ -> AST_NE
      | AST_NE -> AST_EQ
      | AST_LT -> AST_GE
      | AST_LE -> AST_GT
      | AST_GT -> AST_LE
      | AST_GE -> AST_LT
      in
      BRel(r', a, b, is_real)
  | BTernary (c, a, b) -> 
    eliminate_not_negate(BOr(BAnd(c, a), BAnd(BNot c, b)))
  | BAnd(a, b) ->
    BOr(eliminate_not_negate a, eliminate_not_negate b)
  | BOr(a, b) ->
    BAnd(eliminate_not_negate a, eliminate_not_negate b)



(*
  In big ANDs, try to separate levels of /\ and levels of \/
  We also use this step to simplify trues and falses that may be present.
*)

type conslist = enum_cons list * num_cons list * conslist_bool_expr
and conslist_bool_expr =
  | CLTrue
  | CLFalse
  | CLAnd of conslist_bool_expr * conslist_bool_expr
  | CLOr of conslist * conslist

let rec conslist_of_f = function
  | BNot e -> conslist_of_f (eliminate_not_negate e)
  | BRel (op, a, b, is_real) ->
    let x, y, op = match op with
      | AST_EQ -> a, b, CONS_EQ
      | AST_NE -> a, b, CONS_NE
      | AST_GT -> a, b, CONS_GT
      | AST_GE -> a, b, CONS_GE
      | AST_LT -> b, a, CONS_GT
      | AST_LE -> b, a, CONS_GE
    in
    let cons = if y = NIntConst 0 || y = NRealConst 0.
      then (x, op)
      else (NBinary(AST_MINUS, x, y, is_real), op)
    in [], [cons], CLTrue
  | BConst x ->
    [], [], if x then CLTrue else CLFalse
  | BEnumCons e ->
    [e], [], CLTrue
  | BOr(a, b) ->
    let eca, ca, ra = conslist_of_f a in
    let ecb, cb, rb = conslist_of_f b in
    begin match eca, ca, ra, ecb, cb, rb with
      | _, _, CLFalse, _, _, _ -> ecb, cb, rb
      | _, _, _, _, _, CLFalse -> eca, ca, ra
      | [], [], CLTrue, _, _, _ -> [], [], CLTrue
      | _, _, _, [], [], CLTrue -> [], [], CLTrue
      | _ -> [], [], CLOr((eca, ca, ra), (ecb, cb, rb))
    end
  | BAnd(a, b) ->
    let eca, ca, ra = conslist_of_f a in
    let ecb, cb, rb = conslist_of_f b in
    let cons = ca @ cb in
    let econs = eca @ ecb in
    begin match ra, rb with
      | CLFalse, _ | _, CLFalse -> [], [], CLFalse
      | CLTrue, _ -> econs, cons, rb
      | _, CLTrue -> econs, cons, ra
      | _, _ -> econs, cons, CLAnd(ra, rb)
    end
  | BTernary(c, a, b) ->
    conslist_of_f (BOr (BAnd(c, a), BAnd(BNot(c), b)))



(*
  Simplify formula considering something is true
*)

let rec get_root_true = function
  | BAnd(a, b) ->
    get_root_true a @ get_root_true b
  | BEnumCons e -> [BEnumCons e]
  | BRel (a, b, c, d) -> [BRel (a, b, c, d)]
  | _ -> []

let rec simplify true_eqs e =
  if List.exists
      (fun f ->
        try eliminate_not e = eliminate_not f with _ -> false)
      true_eqs
    then BConst true
  else if List.exists
      (fun f ->
        match e, f with
        | BEnumCons(E_EQ, a, EItem v), BEnumCons(E_EQ, b, EItem w)
          when a = b && v <> w -> true
        | _ -> try eliminate_not e = eliminate_not_negate f with _ -> false)
      true_eqs
    then BConst false
  else
    match e with
    | BAnd(a, b) ->
      f_and
        (simplify true_eqs a)
        (simplify true_eqs b)
    | BOr(a, b) ->
      f_or
        (simplify true_eqs a)
        (simplify true_eqs b)
    | BTernary(c, a, b) ->
      f_ternary
        (simplify true_eqs c)
        (simplify true_eqs a)
        (simplify true_eqs b)
    | BNot(n) ->
      begin match simplify true_eqs n with
      | BConst e -> BConst (not e)
      | x -> BNot x
      end
    | v -> v
    
let rec simplify_k true_eqs e =
  List.fold_left f_and (simplify true_eqs e) true_eqs
  


(*
  Simplify a formula replacing a variable with another
*)

let rec formula_replace_evars repls e =
  let new_name x =
    try List.assoc x repls
    with Not_found -> x
  in
  match e with
  | BOr(a, b) ->
    f_or (formula_replace_evars repls a) (formula_replace_evars repls b)
  | BAnd(a, b) ->
    f_and (formula_replace_evars repls a) (formula_replace_evars repls b)
  | BTernary(c, a, b) ->
    f_ternary
      (formula_replace_evars repls c)
      (formula_replace_evars repls a)
      (formula_replace_evars repls b)
  | BNot(n) ->
    begin match formula_replace_evars repls n with
    | BConst e -> BConst (not e)
    | x -> BNot x
    end
  | BEnumCons (op, a, EIdent b) ->
    let a', b' = new_name a, new_name b in
    if a' = b' then
      match op with | E_EQ -> BConst true | E_NE -> BConst false
    else
      BEnumCons(op, a', EIdent b')
  | BEnumCons (op, a, EItem x) ->
    BEnumCons (op, new_name a, EItem x)
  | x -> x



(*
  Extract all cases that make a formula true, to a limited degree

  [A || B; not (A || B)] -> A; (B && (not A)); not A && not B]
*)

let rec split_cases c =
  let c = List.map eliminate_not c in
  let u, l = List.fold_left
    (fun (u, l) c ->
      match c with
      | BOr(a, b) ->
        true, a::(BAnd(BNot a, b))::l
      | BTernary(c, a, b) ->
        true, (BAnd(c, a))::(BAnd(BNot c, b))::l
      | x -> u, x::l)
    (false, []) c
  in
    if u then split_cases l else l