open Format
open Sig
open Num
module A = Literal
module Sy = Symbols
module T = Term
let ale = Hstring.make "<="
let alt = Hstring.make "<"
let is_le n = Hstring.compare n ale = 0
let is_lt n = Hstring.compare n alt = 0
let is_mult h = Sy.equal (Sy.Op Sy.Mult) h
let mod_symb = Sy.name (Hstring.make "@mod")
module Type (X:Sig.X) : Polynome.T with type r = X.r = struct
let mult _ _ = assert false
include Polynome.Make(struct include X let mult = mult end)
end
module Make
(X : Sig.X)
(P : Polynome.T with type r = X.r)
(C : Sig.C with type t = P.t and type r = X.r) = struct
type t = P.t
type r = P.r
let name = "arith"
let is_mine_a a =
match A.LT.view a with
| A.Builtin (_,p,_) -> is_le p || is_lt p
| _ -> false
let is_mine_symb = function
| Sy.Int _ | Sy.Real _
| Sy.Op (Sy.Plus | Sy.Minus | Sy.Mult | Sy.Div | Sy.Modulo) -> true
| _ -> false
let is_mine_type p =
let ty = P.type_info p in
ty = Ty.Tint || ty = Ty.Treal
let unsolvable _ = false
let empty_polynome ty = P.create [] (Int 0) ty
let is_mine p = match P.is_monomial p with
| Some (a,x,b) when a =/ (Int 1) && b =/ (Int 0) -> x
| _ -> C.embed p
let embed r = match C.extract r with
| Some p -> p
| _ -> P.create [Int 1, r] (Int 0) (X.type_info r)
let check_int exn p =
if P.type_info p = Ty.Tint then
let _, c = P.to_list p in
let ppmc = P.ppmc_denominators p in
if not (is_integer_num (ppmc */ c)) then raise exn
let fresh_string =
let cpt = ref 0 in
fun () ->
incr cpt;
"!k" ^ (string_of_int !cpt)
let fresh_name () =
T.make (Sy.name (Hstring.make (fresh_string()))) [] Ty.Tint
let mk_modulo md t1 t2 ctx =
let zero = T.int "0" in
let c1 = A.LT.make (A.Builtin(true, ale, [zero; md])) in
let c2 = A.LT.make (A.Builtin(true, alt, [md; t2])) in
let k = fresh_name () in
let t3 = T.make (Sy.Op Sy.Mult) [t2;k] Ty.Tint in
let t3 = T.make (Sy.Op Sy.Plus) [t3;md] Ty.Tint in
let c3 = A.LT.make (A.Eq (t1, t3)) in
c3 :: c2 :: c1 :: ctx
let mk_euc_division p p2 t1 t2 ctx =
match P.to_list p2 with
| [], coef_p2 ->
let md = T.make (Sy.Op Sy.Modulo) [t1;t2] Ty.Tint in
let r, ctx' = X.make md in
let rp = P.mult (P.create [] ((Int 1) //coef_p2) Ty.Tint) (embed r) in
P.sub p rp, ctx' @ ctx
| _ -> assert false
let rec mke coef p t ctx =
let {T.f = sb ; xs = xs; ty = ty} = T.view t in
match sb, xs with
| (Sy.Int n | Sy.Real n), _ ->
let c = coef */ (num_of_string (Hstring.view n)) in
P.add (P.create [] c ty) p, ctx
| Sy.Op Sy.Mult, [t1;t2] ->
let p1, ctx = mke coef (empty_polynome ty) t1 ctx in
let p2, ctx = mke (Int 1) (empty_polynome ty) t2 ctx in
P.add p (P.mult p1 p2), ctx
| Sy.Op Sy.Div, [t1;t2] ->
let p1, ctx = mke coef (empty_polynome ty) t1 ctx in
let p2, ctx = mke (Int 1) (empty_polynome ty) t2 ctx in
let p3, ctx =
try
let p, approx = P.div p1 p2 in
if approx then mk_euc_division p p2 t1 t2 ctx
else p, ctx
with Division_by_zero | Polynome.Maybe_zero ->
P.create [coef, X.term_embed t] (Int 0) ty, ctx
in
P.add p p3, ctx
| Sy.Op Sy.Plus , [t1;t2] ->
let p2, ctx = mke coef p t2 ctx in
mke coef p2 t1 ctx
| Sy.Op Sy.Minus , [t1;t2] ->
let p2, ctx = mke (minus_num coef) p t2 ctx in
mke coef p2 t1 ctx
| Sy.Op Sy.Modulo , [t1;t2] ->
let p1, ctx = mke coef (empty_polynome ty) t1 ctx in
let p2, ctx = mke (Int 1) (empty_polynome ty) t2 ctx in
let p3, ctx =
try P.modulo p1 p2, ctx
with e ->
let t = T.make mod_symb [t1; t2] Ty.Tint in
let ctx = match e with
| Division_by_zero | Polynome.Maybe_zero -> ctx
| Polynome.Not_a_num -> mk_modulo t t1 t2 ctx
| _ -> assert false
in
P.create [coef, X.term_embed t] (Int 0) ty, ctx
in
P.add p p3, ctx
| _ ->
let a, ctx' = X.make t in
let ctx = ctx' @ ctx in
match C.extract a with
| Some p' -> P.add p (P.mult (P.create [] coef ty) p'), ctx
| _ -> P.add p (P.create [coef, a] (Int 0) ty), ctx
let arith_to_ac p = p
let make t =
let {T.ty = ty} = T.view t in
let p, ctx = mke (Int 1) (empty_polynome ty) t [] in
is_mine (arith_to_ac p), ctx
let rec expand p n acc =
assert (n >=0);
if n = 0 then acc else expand p (n-1) (p::acc)
let rec number_of_vars l =
List.fold_left (fun acc (r, n) -> acc + n * nb_vars_in_alien r) 0 l
and nb_vars_in_alien r =
match C.extract r with
| Some p ->
let l, _ = P.to_list p in
List.fold_left (fun acc (a, x) -> max acc (nb_vars_in_alien x)) 0 l
| None -> 1
let max_list_ = function
| [] -> 0
| [ _, x ] -> nb_vars_in_alien x
| (_, x) :: l ->
let acc = nb_vars_in_alien x in
List.fold_left (fun acc (_, x) -> max acc (nb_vars_in_alien x)) acc l
let type_info p = P.type_info p
let is_int r = X.type_info r = Ty.Tint
module XS = Set.Make(struct type t = X.r let compare = X.compare end)
let xs_of_list =
List.fold_left (fun s x -> XS.add x s) XS.empty
let rec leaves p =
let s =
List.fold_left
(fun s (_, a) -> XS.union (xs_of_list (X.leaves a)) s)
XS.empty (fst (P.to_list p))
in
XS.elements s
let subst x t p =
let p = P.subst x (embed t) p in
let ty = P.type_info p in
let l, c = P.to_list p in
let p =
List.fold_left
(fun p (ai, xi) ->
let xi' = X.subst x t xi in
let p' = match C.extract xi' with
| Some p' -> P.mult (P.create [] ai ty) p'
| _ -> P.create [ai, xi'] (Int 0) ty
in
P.add p p')
(P.create [] c ty) l
in
check_int (Exception.Unsolvable) p;
is_mine p
let compare = P.compare
let hash = P.hash
let mod_sym a b =
let m = mod_num a b in
let m =
if m </ Int 0 then
if m >=/ minus_num b then m +/ b else assert false
else
if m <=/ b then m else assert false
in
if m </ b // (Int 2) then m else m -/ b
let mult_const p c =
P.mult p (P.create [] c (P.type_info p))
let map_monomes f l ax =
List.fold_left
(fun acc (a,x) ->
let a = f a in if a =/ Int 0 then acc else (a, x) :: acc)
[ax] l
let apply_subst sb v =
is_mine (List.fold_left (fun v (x, p) -> embed (subst x p v)) v sb)
let is_mine_p = List.map (fun (x,p) -> x, is_mine p)
let extract_min = function
| [] -> assert false
| [c] -> c, []
| (a, x) :: s ->
List.fold_left
(fun ((a, x), l) (b, y) ->
if abs_num a <=/ abs_num b then
(a, x), ((b, y) :: l)
else (b, y), ((a, x):: l)) ((a, x),[]) s
let rec omega l b =
let (a, x), l = extract_min l in
let p = P.create l b Ty.Tint in
match a with
| Int 0 -> assert false
| Int 1 ->
let p = mult_const p (Int (-1)) in
[x, is_mine p]
| Int (-1) ->
[x, is_mine p]
| _ ->
let a, l, b =
if compare_num a (Int 0) < 0 then
(minus_num a,
List.map (fun (a,x) -> minus_num a,x) l, (minus_num b))
else (a, l, b)
in
omega_sigma [] a x l b
and omega_sigma sbs a x l b =
let m = a +/ Int 1 in
let sigma = X.term_embed (fresh_name ()) in
let mm_sigma = (minus_num m, sigma) in
let l_mod = map_monomes (fun a -> mod_sym a m) l mm_sigma in
let b_mod = minus_num (mod_sym (minus_num b) m) in
let p = P.create l_mod b_mod Ty.Tint in
let sbs = (x, p) :: sbs in
let p' = P.add (P.mult_const a p) (P.create l b Ty.Tint) in
let sbs2 = solve_int p' in
let sbs = List.map (fun (x, v) -> x, apply_subst sbs2 v) sbs in
let sbs2 = List.filter (fun (y, _) -> y <> sigma) sbs2 in
List.rev_append sbs sbs2
and solve_int p =
if P.is_empty p then raise Not_found;
let pgcd = P.pgcd_numerators p in
let ppmc = P.ppmc_denominators p in
let p = mult_const p (ppmc // pgcd) in
let l, b = P.to_list p in
if not (is_integer_num b) then raise Exception.Unsolvable;
omega l b
let is_null p =
if snd (P.to_list p) <>/ (Int 0) then raise Exception.Unsolvable;
[]
let solve_int p =
try solve_int p with Not_found -> is_null p
let solve_real p =
try
let a, x = P.choose p in
let p =
P.mult
(P.create [] ((Int (-1)) // a) (P.type_info p))
(P.remove x p)
in
[x, is_mine p]
with Not_found -> is_null p
let safe_distribution p =
let l, c = P.to_list p in
let ty = P.type_info p in
List.fold_left
(fun p (coef, x) -> P.add p (P.create [coef,x] (Int 0) ty))
(P.create [] c ty) l
let solve_aux r1 r2 =
let p1 = embed r1 in
let p2 = embed r2 in
let ty = P.type_info p2 in
let p = P.add p1 (P.mult (P.create [] (Int (-1)) ty) p2) in
let pp = safe_distribution p in
if ty = Ty.Treal then solve_real pp else solve_int pp
let solve r1 r2 =
let sbs = solve_aux r1 r2 in
List.fast_sort (fun (a,_) (x,y) -> X.compare x a) sbs
let print = P.print
let fully_interpreted sb =
match sb with
| Sy.Op (Sy.Plus | Sy.Minus) -> true
| _ -> false
let term_extract _ = None
module Rel = Fm.Make (X)
(struct
include P
let poly_of = embed
let alien_of = is_mine
end)
end