Library Coq.micromega.ZMicromega


Require Import List.
Require Import Bool.
Require Import OrderedRing.
Require Import RingMicromega.
Require FSetPositive FSetEqProperties.
Require Import ZCoeff.
Require Import Refl.
Require Import ZArith.

Ltac flatten_bool :=
  repeat match goal with
           [ id : (_ && _)%bool = true |- _ ] => destruct (andb_prop _ _ id); clear id
           | [ id : (_ || _)%bool = true |- _ ] => destruct (orb_prop _ _ id); clear id
         end.

Ltac inv H := inversion H ; try subst ; clear H.

Require Import EnvRing.

Open Scope Z_scope.

Lemma Zsor : SOR 0 1 Z.add Z.mul Z.sub Z.opp (@eq Z) Z.le Z.lt.

Lemma ZSORaddon :
  SORaddon 0 1 Z.add Z.mul Z.sub Z.opp (@eq Z) Z.le
  0%Z 1%Z Z.add Z.mul Z.sub Z.opp
  Zeq_bool Z.leb
  (fun x => x) (fun x => x) (pow_N 1 Z.mul).

Fixpoint Zeval_expr (env : PolEnv Z) (e: PExpr Z) : Z :=
  match e with
    | PEc c => c
    | PEX _ x => env x
    | PEadd e1 e2 => Zeval_expr env e1 + Zeval_expr env e2
    | PEmul e1 e2 => Zeval_expr env e1 * Zeval_expr env e2
    | PEpow e1 n => Z.pow (Zeval_expr env e1) (Z.of_N n)
    | PEsub e1 e2 => (Zeval_expr env e1) - (Zeval_expr env e2)
    | PEopp e => Z.opp (Zeval_expr env e)
  end.

Definition eval_expr := eval_pexpr Z.add Z.mul Z.sub Z.opp (fun x => x) (fun x => x) (pow_N 1 Z.mul).

Lemma ZNpower : forall r n, r ^ Z.of_N n = pow_N 1 Z.mul r n.

Lemma Zeval_expr_compat : forall env e, Zeval_expr env e = eval_expr env e.

Definition Zeval_op2 (o : Op2) : Z -> Z -> Prop :=
match o with
| OpEq => @eq Z
| OpNEq => fun x y => ~ x = y
| OpLe => Z.le
| OpGe => Z.ge
| OpLt => Z.lt
| OpGt => Z.gt
end.

Definition Zeval_formula (env : PolEnv Z) (f : Formula Z):=
  let (lhs, op, rhs) := f in
    (Zeval_op2 op) (Zeval_expr env lhs) (Zeval_expr env rhs).

Definition Zeval_formula' :=
  eval_formula Z.add Z.mul Z.sub Z.opp (@eq Z) Z.le Z.lt (fun x => x) (fun x => x) (pow_N 1 Z.mul).

Lemma Zeval_formula_compat : forall env f, Zeval_formula env f <-> Zeval_formula' env f.

Definition eval_nformula :=
  eval_nformula 0 Z.add Z.mul (@eq Z) Z.le Z.lt (fun x => x) .

Definition Zeval_op1 (o : Op1) : Z -> Prop :=
match o with
| Equal => fun x : Z => x = 0
| NonEqual => fun x : Z => x <> 0
| Strict => fun x : Z => 0 < x
| NonStrict => fun x : Z => 0 <= x
end.

Lemma Zeval_nformula_dec : forall env d, (eval_nformula env d) \/ ~ (eval_nformula env d).

Definition ZWitness := Psatz Z.

Definition ZWeakChecker := check_normalised_formulas 0 1 Z.add Z.mul Zeq_bool Z.leb.

Lemma ZWeakChecker_sound : forall (l : list (NFormula Z)) (cm : ZWitness),
  ZWeakChecker l cm = true ->
  forall env, make_impl (eval_nformula env) l False.

Definition psub := psub Z0 Z.add Z.sub Z.opp Zeq_bool.

Definition padd := padd Z0 Z.add Zeq_bool.

Definition pmul := pmul 0 1 Z.add Z.mul Zeq_bool.

Definition normZ := norm 0 1 Z.add Z.mul Z.sub Z.opp Zeq_bool.

Definition eval_pol := eval_pol Z.add Z.mul (fun x => x).

Lemma eval_pol_sub : forall env lhs rhs, eval_pol env (psub lhs rhs) = eval_pol env lhs - eval_pol env rhs.

Lemma eval_pol_add : forall env lhs rhs, eval_pol env (padd lhs rhs) = eval_pol env lhs + eval_pol env rhs.

Lemma eval_pol_mul : forall env lhs rhs, eval_pol env (pmul lhs rhs) = eval_pol env lhs * eval_pol env rhs.

Lemma eval_pol_norm : forall env e, eval_expr env e = eval_pol env (normZ e) .

Definition xnormalise (t:Formula Z) : list (NFormula Z) :=
  let (lhs,o,rhs) := t in
    let lhs := normZ lhs in
      let rhs := normZ rhs in
    match o with
      | OpEq =>
        ((psub lhs (padd rhs (Pc 1))),NonStrict)::((psub rhs (padd lhs (Pc 1))),NonStrict)::nil
      | OpNEq => (psub lhs rhs,Equal) :: nil
      | OpGt => (psub rhs lhs,NonStrict) :: nil
      | OpLt => (psub lhs rhs,NonStrict) :: nil
      | OpGe => (psub rhs (padd lhs (Pc 1)),NonStrict) :: nil
      | OpLe => (psub lhs (padd rhs (Pc 1)),NonStrict) :: nil
    end.

Require Import Coq.micromega.Tauto BinNums.

Definition normalise {T : Type} (t:Formula Z) (tg:T) : cnf (NFormula Z) T :=
  List.map (fun x => (x,tg)::nil) (xnormalise t).

Lemma normalise_correct : forall (T: Type) env t (tg:T), eval_cnf eval_nformula env (normalise t tg) <-> Zeval_formula env t.

Definition xnegate (t:RingMicromega.Formula Z) : list (NFormula Z) :=
  let (lhs,o,rhs) := t in
    let lhs := normZ lhs in
      let rhs := normZ rhs in
    match o with
      | OpEq => (psub lhs rhs,Equal) :: nil
      | OpNEq => ((psub lhs (padd rhs (Pc 1))),NonStrict)::((psub rhs (padd lhs (Pc 1))),NonStrict)::nil
      | OpGt => (psub lhs (padd rhs (Pc 1)),NonStrict) :: nil
      | OpLt => (psub rhs (padd lhs (Pc 1)),NonStrict) :: nil
      | OpGe => (psub lhs rhs,NonStrict) :: nil
      | OpLe => (psub rhs lhs,NonStrict) :: nil
    end.

Definition negate {T : Type} (t:Formula Z) (tg:T) : cnf (NFormula Z) T :=
  List.map (fun x => (x,tg)::nil) (xnegate t).

Lemma negate_correct : forall T env t (tg:T), eval_cnf eval_nformula env (negate t tg) <-> ~ Zeval_formula env t.
  Opaque padd.
  Transparent padd.

Definition Zunsat := check_inconsistent 0 Zeq_bool Z.leb.

Definition Zdeduce := nformula_plus_nformula 0 Z.add Zeq_bool.

Definition cnfZ (Annot TX AF : Type) (f : TFormula (Formula Z) Annot TX AF) :=
  rxcnf Zunsat Zdeduce normalise negate true f.

Definition ZweakTautoChecker (w: list ZWitness) (f : BFormula (Formula Z)) : bool :=
  @tauto_checker (Formula Z) (NFormula Z) unit Zunsat Zdeduce normalise negate ZWitness (fun cl => ZWeakChecker (List.map fst cl)) f w.


Require Import Zdiv.
Open Scope Z_scope.

Definition ceiling (a b:Z) : Z :=
  let (q,r) := Z.div_eucl a b in
    match r with
      | Z0 => q
      | _ => q + 1
    end.

Require Import Znumtheory.

Lemma Zdivide_ceiling : forall a b, (b | a) -> ceiling a b = Z.div a b.

Lemma narrow_interval_lower_bound a b x :
  a > 0 -> a * x >= b -> x >= ceiling b a.

NB: narrow_interval_upper_bound is Zdiv.Zdiv_le_lower_bound

Require Import QArith.

Inductive ZArithProof :=
| DoneProof
| RatProof : ZWitness -> ZArithProof -> ZArithProof
| CutProof : ZWitness -> ZArithProof -> ZArithProof
| EnumProof : ZWitness -> ZWitness -> list ZArithProof -> ZArithProof

.


Require Import Znumtheory.

Definition isZ0 (x:Z) :=
  match x with
    | Z0 => true
    | _ => false
  end.

Lemma isZ0_0 : forall x, isZ0 x = true <-> x = 0.

Lemma isZ0_n0 : forall x, isZ0 x = false <-> x <> 0.

Definition ZgcdM (x y : Z) := Z.max (Z.gcd x y) 1.

Fixpoint Zgcd_pol (p : PolC Z) : (Z * Z) :=
  match p with
    | Pc c => (0,c)
    | Pinj _ p => Zgcd_pol p
    | PX p _ q =>
      let (g1,c1) := Zgcd_pol p in
        let (g2,c2) := Zgcd_pol q in
          (ZgcdM (ZgcdM g1 c1) g2 , c2)
  end.


Fixpoint Zdiv_pol (p:PolC Z) (x:Z) : PolC Z :=
  match p with
    | Pc c => Pc (Z.div c x)
    | Pinj j p => Pinj j (Zdiv_pol p x)
    | PX p j q => PX (Zdiv_pol p x) j (Zdiv_pol q x)
  end.

Inductive Zdivide_pol (x:Z): PolC Z -> Prop :=
| Zdiv_Pc : forall c, (x | c) -> Zdivide_pol x (Pc c)
| Zdiv_Pinj : forall p, Zdivide_pol x p -> forall j, Zdivide_pol x (Pinj j p)
| Zdiv_PX : forall p q, Zdivide_pol x p -> Zdivide_pol x q -> forall j, Zdivide_pol x (PX p j q).

Lemma Zdiv_pol_correct : forall a p, 0 < a -> Zdivide_pol a p ->
  forall env, eval_pol env p = a * eval_pol env (Zdiv_pol p a).

Lemma Zgcd_pol_ge : forall p, fst (Zgcd_pol p) >= 0.

Lemma Zdivide_pol_Zdivide : forall p x y, Zdivide_pol x p -> (y | x) -> Zdivide_pol y p.

Lemma Zdivide_pol_one : forall p, Zdivide_pol 1 p.

Lemma Zgcd_minus : forall a b c, (a | c - b ) -> (Z.gcd a b | c).

Lemma Zdivide_pol_sub : forall p a b,
  0 < Z.gcd a b ->
  Zdivide_pol a (PsubC Z.sub p b) ->
   Zdivide_pol (Z.gcd a b) p.

Lemma Zdivide_pol_sub_0 : forall p a,
  Zdivide_pol a (PsubC Z.sub p 0) ->
   Zdivide_pol a p.

Lemma Zgcd_pol_div : forall p g c,
  Zgcd_pol p = (g, c) -> Zdivide_pol g (PsubC Z.sub p c).

Lemma Zgcd_pol_correct_lt : forall p env g c, Zgcd_pol p = (g,c) -> 0 < g -> eval_pol env p = g * (eval_pol env (Zdiv_pol (PsubC Z.sub p c) g)) + c.

Definition makeCuttingPlane (p : PolC Z) : PolC Z * Z :=
  let (g,c) := Zgcd_pol p in
    if Z.gtb g Z0
      then (Zdiv_pol (PsubC Z.sub p c) g , Z.opp (ceiling (Z.opp c) g))
      else (p,Z0).

Definition genCuttingPlane (f : NFormula Z) : option (PolC Z * Z * Op1) :=
  let (e,op) := f in
    match op with
      | Equal => let (g,c) := Zgcd_pol e in
        if andb (Z.gtb g Z0) (andb (negb (Zeq_bool c Z0)) (negb (Zeq_bool (Z.gcd g c) g)))
          then None
          else
            let (p,c) := makeCuttingPlane e in
              Some (p,c,Equal)
      | NonEqual => Some (e,Z0,op)
      | Strict => let (p,c) := makeCuttingPlane (PsubC Z.sub e 1) in
        Some (p,c,NonStrict)
      | NonStrict => let (p,c) := makeCuttingPlane e in
        Some (p,c,NonStrict)
    end.

Definition nformula_of_cutting_plane (t : PolC Z * Z * Op1) : NFormula Z :=
  let (e_z, o) := t in
    let (e,z) := e_z in
      (padd e (Pc z) , o).

Definition is_pol_Z0 (p : PolC Z) : bool :=
  match p with
    | Pc Z0 => true
    | _ => false
  end.

Lemma is_pol_Z0_eval_pol : forall p, is_pol_Z0 p = true -> forall env, eval_pol env p = 0.

Definition eval_Psatz : list (NFormula Z) -> ZWitness -> option (NFormula Z) :=
  eval_Psatz 0 1 Z.add Z.mul Zeq_bool Z.leb.

Definition valid_cut_sign (op:Op1) :=
  match op with
    | Equal => true
    | NonStrict => true
    | _ => false
  end.

Module Vars.
  Import FSetPositive.
  Include PositiveSet.

  Module Facts := FSetEqProperties.EqProperties(PositiveSet).

  Lemma mem_union_l : forall x s s',
      mem x s = true ->
      mem x (union s s') = true.

  Lemma mem_union_r : forall x s s',
      mem x s' = true ->
      mem x (union s s') = true.

  Lemma mem_singleton : forall p,
      mem p (singleton p) = true.

  Lemma mem_elements : forall x v,
      mem x v = true <-> List.In x (PositiveSet.elements v).

  Definition max_element (vars : t) :=
    fold Pos.max vars xH.

  Lemma max_element_max :
    forall x vars, mem x vars = true -> Pos.le x (max_element vars).

  Definition is_subset (v1 v2 : t) :=
    forall x, mem x v1 = true -> mem x v2 = true.

  Lemma is_subset_union_l : forall v1 v2,
      is_subset v1 (union v1 v2).

  Lemma is_subset_union_r : forall v1 v2,
      is_subset v1 (union v2 v1).

  End Vars.

Fixpoint vars_of_pexpr (e : PExpr Z) : Vars.t :=
  match e with
  | PEc _ => Vars.empty
  | PEX _ x => Vars.singleton x
  | PEadd e1 e2 | PEsub e1 e2 | PEmul e1 e2 =>
    let v1 := vars_of_pexpr e1 in
    let v2 := vars_of_pexpr e2 in
    Vars.union v1 v2
  | PEopp c => vars_of_pexpr c
  | PEpow e n => vars_of_pexpr e
  end.

Definition vars_of_formula (f : Formula Z) :=
  match f with
  | Build_Formula l o r =>
    let v1 := vars_of_pexpr l in
    let v2 := vars_of_pexpr r in
    Vars.union v1 v2
  end.

Fixpoint vars_of_bformula {TX : Type} {TG : Type} {ID : Type}
         (F : @GFormula (Formula Z) TX TG ID) : Vars.t :=
  match F with
  | TT => Vars.empty
  | FF => Vars.empty
  | X p => Vars.empty
  | A a t => vars_of_formula a
  | Cj f1 f2 | D f1 f2 | I f1 _ f2 =>
                         let v1 := vars_of_bformula f1 in
                         let v2 := vars_of_bformula f2 in
                         Vars.union v1 v2
  | Tauto.N f => vars_of_bformula f
  end.

Definition bound_var (v : positive) : Formula Z :=
  Build_Formula (PEX _ v) OpGe (PEc 0).

Definition mk_eq_pos (x : positive) (y:positive) (t : positive) : Formula Z :=
  Build_Formula (PEX _ x) OpEq (PEsub (PEX _ y) (PEX _ t)).

Section BOUND.
  Context {TX TG ID : Type}.

  Variable tag_of_var : positive -> positive -> option bool -> TG.

  Definition bound_vars (fr : positive)
             (v : Vars.t) : @GFormula (Formula Z) TX TG ID :=
    Vars.fold (fun k acc =>
                 let y := (xO (fr + k)) in
                 let z := (xI (fr + k)) in
                 Cj
                   (Cj (A (mk_eq_pos k y z) (tag_of_var fr k None))
                       (Cj (A (bound_var y) (tag_of_var fr k (Some false)))
                           (A (bound_var z) (tag_of_var fr k (Some true)))))
                   acc) v TT.

  Definition bound_problem (F : @GFormula (Formula Z) TX TG ID) : GFormula :=
    let v := vars_of_bformula F in
    I (bound_vars (Pos.succ (Vars.max_element v)) v) None F.

  Definition bound_problem_fr (fr : positive) (F : @GFormula (Formula Z) TX TG ID) : GFormula :=
    let v := vars_of_bformula F in
    I (bound_vars fr v) None F.

End BOUND.

Fixpoint ZChecker (l:list (NFormula Z)) (pf : ZArithProof) {struct pf} : bool :=
  match pf with
    | DoneProof => false
    | RatProof w pf =>
      match eval_Psatz l w with
        | None => false
        | Some f =>
          if Zunsat f then true
            else ZChecker (f::l) pf
      end
    | CutProof w pf =>
      match eval_Psatz l w with
        | None => false
        | Some f =>
          match genCuttingPlane f with
            | None => true
            | Some cp => ZChecker (nformula_of_cutting_plane cp::l) pf
          end
      end


    | EnumProof w1 w2 pf =>
       match eval_Psatz l w1 , eval_Psatz l w2 with
         | Some f1 , Some f2 =>
           match genCuttingPlane f1 , genCuttingPlane f2 with
             |Some (e1,z1,op1) , Some (e2,z2,op2) =>
               if (valid_cut_sign op1 && valid_cut_sign op2 && is_pol_Z0 (padd e1 e2))
                 then
                   (fix label (pfs:list ZArithProof) :=
                   fun lb ub =>
                     match pfs with
                       | nil => if Z.gtb lb ub then true else false
                       | pf::rsr => andb (ZChecker ((psub e1 (Pc lb), Equal) :: l) pf) (label rsr (Z.add lb 1%Z) ub)
                     end) pf (Z.opp z1) z2
                  else false
              | _ , _ => true
           end
          | _ , _ => false
    end
end.

Fixpoint bdepth (pf : ZArithProof) : nat :=
  match pf with
    | DoneProof => O
    | RatProof _ p => S (bdepth p)
    | CutProof _ p => S (bdepth p)
    | EnumProof _ _ l => S (List.fold_right (fun pf x => Max.max (bdepth pf) x) O l)
  end.

Require Import Wf_nat.

Lemma in_bdepth : forall l a b y, In y l -> ltof ZArithProof bdepth y (EnumProof a b l).

Lemma eval_Psatz_sound : forall env w l f',
  make_conj (eval_nformula env) l ->
  eval_Psatz l w = Some f' -> eval_nformula env f'.

Lemma makeCuttingPlane_ns_sound : forall env e e' c,
  eval_nformula env (e, NonStrict) ->
  makeCuttingPlane e = (e',c) ->
  eval_nformula env (nformula_of_cutting_plane (e', c, NonStrict)).

Lemma cutting_plane_sound : forall env f p,
  eval_nformula env f ->
  genCuttingPlane f = Some p ->
   eval_nformula env (nformula_of_cutting_plane p).

Lemma genCuttingPlaneNone : forall env f,
  genCuttingPlane f = None ->
  eval_nformula env f -> False.

Lemma ZChecker_sound : forall w l, ZChecker l w = true -> forall env, make_impl (eval_nformula env) l False.

Definition ZTautoChecker (f : BFormula (Formula Z)) (w: list ZArithProof): bool :=
  @tauto_checker (Formula Z) (NFormula Z) unit Zunsat Zdeduce normalise negate ZArithProof (fun cl => ZChecker (List.map fst cl)) f w.

Lemma ZTautoChecker_sound : forall f w, ZTautoChecker f w = true -> forall env, eval_f (fun x => x) (Zeval_formula env) f.

Record is_diff_env_elt (fr : positive) (env env' : positive -> Z) (x:positive):=
  {
    eq_env : env x = env' x;
    eq_diff : env x = env' (xO (fr+ x)) - env' (xI (fr + x));
    pos_xO : env' (xO (fr+x)) >= 0;
    pos_xI : env' (xI (fr+x)) >= 0;
  }.

Definition is_diff_env (s : Vars.t) (env env' : positive -> Z) :=
  let fr := Pos.succ (Vars.max_element s) in
  forall x, Vars.mem x s = true ->
            is_diff_env_elt fr env env' x.

Definition mk_diff_env (s : Vars.t) (env : positive -> Z) :=
  let fr := Vars.max_element s in
  fun x =>
    if Pos.leb x fr
    then env x
    else
      let fr' := Pos.succ fr in
      match x with
      | xO x => if Z.leb (env (x - fr')%positive) 0
                then 0 else env (x -fr')%positive
      | xI x => if Z.leb (env (x - fr')%positive) 0
                then - (env (x - fr')%positive) else 0
      | xH => 0
      end.

Lemma le_xO : forall x, (x <= xO x)%positive.

Lemma leb_xO_false :
  (forall x y, x <=? y = false ->
             xO x <=? y = false)%positive.

Lemma leb_xI_false :
  (forall x y, x <=? y = false ->
             xI x <=? y = false)%positive.

Lemma is_diff_env_ex : forall s env,
    is_diff_env s env (mk_diff_env s env).

Lemma env_bounds : forall tg env s,
    let fr := Pos.succ (Vars.max_element s) in
    exists env', is_diff_env s env env'
                   /\
                   eval_bf (Zeval_formula env') (bound_vars tg fr s).

Definition agree_env (v : Vars.t) (env env' : positive -> Z) : Prop :=
  forall x, Vars.mem x v = true -> env x = env' x.

Lemma agree_env_subset : forall s1 s2 env env',
    agree_env s1 env env' ->
    Vars.is_subset s2 s1 ->
    agree_env s2 env env'.

Lemma agree_env_union : forall s1 s2 env env',
    agree_env (Vars.union s1 s2) env env' ->
    agree_env s1 env env' /\ agree_env s2 env env'.

Lemma agree_env_eval_expr :
  forall env env' e
         (AGREE : agree_env (vars_of_pexpr e) env env'),
  Zeval_expr env e = Zeval_expr env' e.

Lemma agree_env_eval_bf :
  forall env env' f
         (AGREE: agree_env (vars_of_bformula f) env env'),
    eval_bf (Zeval_formula env') f <->
    eval_bf (Zeval_formula env) f.

Lemma bound_problem_sound : forall tg f,
    (forall env' : PolEnv Z,
        eval_bf (Zeval_formula env')
                 (bound_problem tg f)) ->
    forall env,
      eval_bf (Zeval_formula env) f.

Definition ZTautoCheckerExt (f : BFormula (Formula Z)) (w : list ZArithProof) : bool :=
  ZTautoChecker (bound_problem (fun _ _ _ => tt) f) w.

Lemma ZTautoCheckerExt_sound : forall f w, ZTautoCheckerExt f w = true -> forall env, eval_bf (Zeval_formula env) f.

Fixpoint xhyps_of_pt (base:nat) (acc : list nat) (pt:ZArithProof) : list nat :=
  match pt with
    | DoneProof => acc
    | RatProof c pt => xhyps_of_pt (S base ) (xhyps_of_psatz base acc c) pt
    | CutProof c pt => xhyps_of_pt (S base ) (xhyps_of_psatz base acc c) pt
    | EnumProof c1 c2 l =>
      let acc := xhyps_of_psatz base (xhyps_of_psatz base acc c2) c1 in
        List.fold_left (xhyps_of_pt (S base)) l acc
  end.

Definition hyps_of_pt (pt : ZArithProof) : list nat := xhyps_of_pt 0 nil pt.

Open Scope Z_scope.

To ease bindings from ml code
Definition make_impl := Refl.make_impl.
Definition make_conj := Refl.make_conj.

Require VarMap.

Definition env := PolEnv Z.
Definition node := @VarMap.Branch Z.
Definition empty := @VarMap.Empty Z.
Definition leaf := @VarMap.Elt Z.

Definition coneMember := ZWitness.

Definition eval := eval_formula.

Definition prod_pos_nat := prod positive nat.

Notation n_of_Z := Z.to_N (only parsing).