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