Theory Ferrack

(*  Title:      HOL/Decision_Procs/Ferrack.thy
    Author:     Amine Chaieb
*)

theory Ferrack
imports Complex_Main Dense_Linear_Order DP_Library
  "HOL-Library.Code_Target_Numeral"
begin

section ‹Quantifier elimination for ℝ (0, 1, +, <)›

  (*********************************************************************************)
  (****                            SHADOW SYNTAX AND SEMANTICS                  ****)
  (*********************************************************************************)

datatype (plugins del: size) num = C int | Bound nat | CN nat int num | Neg num | Add num num| Sub num num
  | Mul int num

instantiation num :: size
begin

primrec size_num :: "num  nat"
where
  "size_num (C c) = 1"
| "size_num (Bound n) = 1"
| "size_num (Neg a) = 1 + size_num a"
| "size_num (Add a b) = 1 + size_num a + size_num b"
| "size_num (Sub a b) = 3 + size_num a + size_num b"
| "size_num (Mul c a) = 1 + size_num a"
| "size_num (CN n c a) = 3 + size_num a "

instance ..

end

  (* Semantics of numeral terms (num) *)
primrec Inum :: "real list  num  real"
where
  "Inum bs (C c) = (real_of_int c)"
| "Inum bs (Bound n) = bs!n"
| "Inum bs (CN n c a) = (real_of_int c) * (bs!n) + (Inum bs a)"
| "Inum bs (Neg a) = -(Inum bs a)"
| "Inum bs (Add a b) = Inum bs a + Inum bs b"
| "Inum bs (Sub a b) = Inum bs a - Inum bs b"
| "Inum bs (Mul c a) = (real_of_int c) * Inum bs a"
    (* FORMULAE *)
datatype (plugins del: size) fm  =
  T| F| Lt num| Le num| Gt num| Ge num| Eq num| NEq num|
  Not fm| And fm fm|  Or fm fm| Imp fm fm| Iff fm fm| E fm| A fm

instantiation fm :: size
begin

primrec size_fm :: "fm  nat"
where
  "size_fm (Not p) = 1 + size_fm p"
| "size_fm (And p q) = 1 + size_fm p + size_fm q"
| "size_fm (Or p q) = 1 + size_fm p + size_fm q"
| "size_fm (Imp p q) = 3 + size_fm p + size_fm q"
| "size_fm (Iff p q) = 3 + 2 * (size_fm p + size_fm q)"
| "size_fm (E p) = 1 + size_fm p"
| "size_fm (A p) = 4 + size_fm p"
| "size_fm T = 1"
| "size_fm F = 1"
| "size_fm (Lt _) = 1"
| "size_fm (Le _) = 1"
| "size_fm (Gt _) = 1"
| "size_fm (Ge _) = 1"
| "size_fm (Eq _) = 1"
| "size_fm (NEq _) = 1"

instance ..

end

lemma size_fm_pos [simp]: "size p > 0" for p :: fm
  by (induct p) simp_all

  (* Semantics of formulae (fm) *)
primrec Ifm ::"real list  fm  bool"
where
  "Ifm bs T = True"
| "Ifm bs F = False"
| "Ifm bs (Lt a) = (Inum bs a < 0)"
| "Ifm bs (Gt a) = (Inum bs a > 0)"
| "Ifm bs (Le a) = (Inum bs a  0)"
| "Ifm bs (Ge a) = (Inum bs a  0)"
| "Ifm bs (Eq a) = (Inum bs a = 0)"
| "Ifm bs (NEq a) = (Inum bs a  0)"
| "Ifm bs (Not p) = (¬ (Ifm bs p))"
| "Ifm bs (And p q) = (Ifm bs p  Ifm bs q)"
| "Ifm bs (Or p q) = (Ifm bs p  Ifm bs q)"
| "Ifm bs (Imp p q) = ((Ifm bs p)  (Ifm bs q))"
| "Ifm bs (Iff p q) = (Ifm bs p = Ifm bs q)"
| "Ifm bs (E p) = (x. Ifm (x#bs) p)"
| "Ifm bs (A p) = (x. Ifm (x#bs) p)"

lemma IfmLeSub: " Inum bs s = s' ; Inum bs t = t'   Ifm bs (Le (Sub s t)) = (s'  t')"
  by simp

lemma IfmLtSub: " Inum bs s = s' ; Inum bs t = t'   Ifm bs (Lt (Sub s t)) = (s' < t')"
  by simp

lemma IfmEqSub: " Inum bs s = s' ; Inum bs t = t'   Ifm bs (Eq (Sub s t)) = (s' = t')"
  by simp

lemma IfmNot: " (Ifm bs p = P)  (Ifm bs (Not p) = (¬P))"
  by simp

lemma IfmAnd: "  Ifm bs p = P ; Ifm bs q = Q  (Ifm bs (And p q) = (P  Q))"
  by simp

lemma IfmOr: "  Ifm bs p = P ; Ifm bs q = Q  (Ifm bs (Or p q) = (P  Q))"
  by simp

lemma IfmImp: "  Ifm bs p = P ; Ifm bs q = Q  (Ifm bs (Imp p q) = (P  Q))"
  by simp

lemma IfmIff: "  Ifm bs p = P ; Ifm bs q = Q  (Ifm bs (Iff p q) = (P = Q))"
  by simp

lemma IfmE: " (!! x. Ifm (x#bs) p = P x)  (Ifm bs (E p) = (x. P x))"
  by simp

lemma IfmA: " (!! x. Ifm (x#bs) p = P x)  (Ifm bs (A p) = (x. P x))"
  by simp

fun not:: "fm  fm"
where
  "not (Not p) = p"
| "not T = F"
| "not F = T"
| "not p = Not p"

lemma not[simp]: "Ifm bs (not p) = Ifm bs (Not p)"
  by (cases p) auto

definition conj :: "fm  fm  fm"
where
  "conj p q =
   (if p = F  q = F then F
    else if p = T then q
    else if q = T then p
    else if p = q then p else And p q)"

lemma conj[simp]: "Ifm bs (conj p q) = Ifm bs (And p q)"
  by (cases "p = F  q = F", simp_all add: conj_def) (cases p, simp_all)

definition disj :: "fm  fm  fm"
where
  "disj p q =
   (if p = T  q = T then T
    else if p = F then q
    else if q = F then p
    else if p = q then p else Or p q)"

lemma disj[simp]: "Ifm bs (disj p q) = Ifm bs (Or p q)"
  by (cases "p = T  q = T", simp_all add: disj_def) (cases p, simp_all)

definition imp :: "fm  fm  fm"
where
  "imp p q =
   (if p = F  q = T  p = q then T
    else if p = T then q
    else if q = F then not p
    else Imp p q)"

lemma imp[simp]: "Ifm bs (imp p q) = Ifm bs (Imp p q)"
  by (cases "p = F  q = T") (simp_all add: imp_def)

definition iff :: "fm  fm  fm"
where
  "iff p q =
   (if p = q then T
    else if p = Not q  Not p = q then F
    else if p = F then not q
    else if q = F then not p
    else if p = T then q
    else if q = T then p
    else Iff p q)"

lemma iff[simp]: "Ifm bs (iff p q) = Ifm bs (Iff p q)"
  by (unfold iff_def, cases "p = q", simp, cases "p = Not q", simp) (cases "Not p = q", auto)

lemma conj_simps:
  "conj F Q = F"
  "conj P F = F"
  "conj T Q = Q"
  "conj P T = P"
  "conj P P = P"
  "P  T  P  F  Q  T  Q  F  P  Q  conj P Q = And P Q"
  by (simp_all add: conj_def)

lemma disj_simps:
  "disj T Q = T"
  "disj P T = T"
  "disj F Q = Q"
  "disj P F = P"
  "disj P P = P"
  "P  T  P  F  Q  T  Q  F  P  Q  disj P Q = Or P Q"
  by (simp_all add: disj_def)

lemma imp_simps:
  "imp F Q = T"
  "imp P T = T"
  "imp T Q = Q"
  "imp P F = not P"
  "imp P P = T"
  "P  T  P  F  P  Q  Q  T  Q  F  imp P Q = Imp P Q"
  by (simp_all add: imp_def)

lemma trivNot: "p  Not p" "Not p  p"
  by (induct p) auto

lemma iff_simps:
  "iff p p = T"
  "iff p (Not p) = F"
  "iff (Not p) p = F"
  "iff p F = not p"
  "iff F p = not p"
  "p  Not T  iff T p = p"
  "p Not T  iff p T = p"
  "pq  p Not q  q Not p  p F  q F  p  T  q  T  iff p q = Iff p q"
  using trivNot
  by (simp_all add: iff_def, cases p, auto)

  (* Quantifier freeness *)
fun qfree:: "fm  bool"
where
  "qfree (E p) = False"
| "qfree (A p) = False"
| "qfree (Not p) = qfree p"
| "qfree (And p q) = (qfree p  qfree q)"
| "qfree (Or  p q) = (qfree p  qfree q)"
| "qfree (Imp p q) = (qfree p  qfree q)"
| "qfree (Iff p q) = (qfree p  qfree q)"
| "qfree p = True"

  (* Boundedness and substitution *)
primrec numbound0:: "num  bool" (* a num is INDEPENDENT of Bound 0 *)
where
  "numbound0 (C c) = True"
| "numbound0 (Bound n) = (n > 0)"
| "numbound0 (CN n c a) = (n  0  numbound0 a)"
| "numbound0 (Neg a) = numbound0 a"
| "numbound0 (Add a b) = (numbound0 a  numbound0 b)"
| "numbound0 (Sub a b) = (numbound0 a  numbound0 b)"
| "numbound0 (Mul i a) = numbound0 a"

lemma numbound0_I:
  assumes nb: "numbound0 a"
  shows "Inum (b#bs) a = Inum (b'#bs) a"
  using nb by (induct a) simp_all

primrec bound0:: "fm  bool" (* A Formula is independent of Bound 0 *)
where
  "bound0 T = True"
| "bound0 F = True"
| "bound0 (Lt a) = numbound0 a"
| "bound0 (Le a) = numbound0 a"
| "bound0 (Gt a) = numbound0 a"
| "bound0 (Ge a) = numbound0 a"
| "bound0 (Eq a) = numbound0 a"
| "bound0 (NEq a) = numbound0 a"
| "bound0 (Not p) = bound0 p"
| "bound0 (And p q) = (bound0 p  bound0 q)"
| "bound0 (Or p q) = (bound0 p  bound0 q)"
| "bound0 (Imp p q) = ((bound0 p)  (bound0 q))"
| "bound0 (Iff p q) = (bound0 p  bound0 q)"
| "bound0 (E p) = False"
| "bound0 (A p) = False"

lemma bound0_I:
  assumes bp: "bound0 p"
  shows "Ifm (b#bs) p = Ifm (b'#bs) p"
  using bp numbound0_I[where b="b" and bs="bs" and b'="b'"]
  by (induct p) auto

lemma not_qf[simp]: "qfree p  qfree (not p)"
  by (cases p) auto

lemma not_bn[simp]: "bound0 p  bound0 (not p)"
  by (cases p) auto


lemma conj_qf[simp]: "qfree p ; qfree q  qfree (conj p q)"
  using conj_def by auto
lemma conj_nb[simp]: "bound0 p ; bound0 q  bound0 (conj p q)"
  using conj_def by auto

lemma disj_qf[simp]: "qfree p ; qfree q  qfree (disj p q)"
  using disj_def by auto
lemma disj_nb[simp]: "bound0 p ; bound0 q  bound0 (disj p q)"
  using disj_def by auto

lemma imp_qf[simp]: "qfree p ; qfree q  qfree (imp p q)"
  using imp_def by (cases "p=F  q=T",simp_all add: imp_def)
lemma imp_nb[simp]: "bound0 p ; bound0 q  bound0 (imp p q)"
  using imp_def by (cases "p=F  q=T  p=q",simp_all add: imp_def)

lemma iff_qf[simp]: "qfree p ; qfree q  qfree (iff p q)"
  unfolding iff_def by (cases "p = q") auto
lemma iff_nb[simp]: "bound0 p ; bound0 q  bound0 (iff p q)"
  using iff_def unfolding iff_def by (cases "p = q") auto

fun decrnum:: "num  num"
where
  "decrnum (Bound n) = Bound (n - 1)"
| "decrnum (Neg a) = Neg (decrnum a)"
| "decrnum (Add a b) = Add (decrnum a) (decrnum b)"
| "decrnum (Sub a b) = Sub (decrnum a) (decrnum b)"
| "decrnum (Mul c a) = Mul c (decrnum a)"
| "decrnum (CN n c a) = CN (n - 1) c (decrnum a)"
| "decrnum a = a"

fun decr :: "fm  fm"
where
  "decr (Lt a) = Lt (decrnum a)"
| "decr (Le a) = Le (decrnum a)"
| "decr (Gt a) = Gt (decrnum a)"
| "decr (Ge a) = Ge (decrnum a)"
| "decr (Eq a) = Eq (decrnum a)"
| "decr (NEq a) = NEq (decrnum a)"
| "decr (Not p) = Not (decr p)"
| "decr (And p q) = conj (decr p) (decr q)"
| "decr (Or p q) = disj (decr p) (decr q)"
| "decr (Imp p q) = imp (decr p) (decr q)"
| "decr (Iff p q) = iff (decr p) (decr q)"
| "decr p = p"

lemma decrnum:
  assumes nb: "numbound0 t"
  shows "Inum (x # bs) t = Inum bs (decrnum t)"
  using nb by (induct t rule: decrnum.induct) simp_all

lemma decr:
  assumes nb: "bound0 p"
  shows "Ifm (x # bs) p = Ifm bs (decr p)"
  using nb by (induct p rule: decr.induct) (simp_all add: decrnum)

lemma decr_qf: "bound0 p  qfree (decr p)"
  by (induct p) simp_all

fun isatom :: "fm  bool" (* test for atomicity *)
where
  "isatom T = True"
| "isatom F = True"
| "isatom (Lt a) = True"
| "isatom (Le a) = True"
| "isatom (Gt a) = True"
| "isatom (Ge a) = True"
| "isatom (Eq a) = True"
| "isatom (NEq a) = True"
| "isatom p = False"

lemma bound0_qf: "bound0 p  qfree p"
  by (induct p) simp_all

definition djf :: "('a  fm)  'a  fm  fm"
where
  "djf f p q =
   (if q = T then T
    else if q = F then f p
    else (let fp = f p in case fp of T  T | F  q | _  Or (f p) q))"

definition evaldjf :: "('a  fm)  'a list  fm"
  where "evaldjf f ps = foldr (djf f) ps F"

lemma djf_Or: "Ifm bs (djf f p q) = Ifm bs (Or (f p) q)"
  by (cases "q = T", simp add: djf_def, cases "q = F", simp add: djf_def)
    (cases "f p", simp_all add: Let_def djf_def)


lemma djf_simps:
  "djf f p T = T"
  "djf f p F = f p"
  "q  T  q  F  djf f p q = (let fp = f p in case fp of T  T | F  q | _  Or (f p) q)"
  by (simp_all add: djf_def)

lemma evaldjf_ex: "Ifm bs (evaldjf f ps)  (p  set ps. Ifm bs (f p))"
  by (induct ps) (simp_all add: evaldjf_def djf_Or)

lemma evaldjf_bound0:
  assumes nb: "x set xs. bound0 (f x)"
  shows "bound0 (evaldjf f xs)"
  using nb by (induct xs, auto simp add: evaldjf_def djf_def Let_def) (case_tac "f a", auto)

lemma evaldjf_qf:
  assumes nb: "x set xs. qfree (f x)"
  shows "qfree (evaldjf f xs)"
  using nb by (induct xs, auto simp add: evaldjf_def djf_def Let_def) (case_tac "f a", auto)

fun disjuncts :: "fm  fm list"
where
  "disjuncts (Or p q) = disjuncts p @ disjuncts q"
| "disjuncts F = []"
| "disjuncts p = [p]"

lemma disjuncts: "(q set (disjuncts p). Ifm bs q) = Ifm bs p"
  by (induct p rule: disjuncts.induct) auto

lemma disjuncts_nb: "bound0 p  q set (disjuncts p). bound0 q"
proof -
  assume nb: "bound0 p"
  then have "list_all bound0 (disjuncts p)"
    by (induct p rule: disjuncts.induct) auto
  then show ?thesis
    by (simp only: list_all_iff)
qed

lemma disjuncts_qf: "qfree p  q set (disjuncts p). qfree q"
proof -
  assume qf: "qfree p"
  then have "list_all qfree (disjuncts p)"
    by (induct p rule: disjuncts.induct) auto
  then show ?thesis
    by (simp only: list_all_iff)
qed

definition DJ :: "(fm  fm)  fm  fm"
  where "DJ f p = evaldjf f (disjuncts p)"

lemma DJ:
  assumes fdj: "p q. Ifm bs (f (Or p q)) = Ifm bs (Or (f p) (f q))"
    and fF: "f F = F"
  shows "Ifm bs (DJ f p) = Ifm bs (f p)"
proof -
  have "Ifm bs (DJ f p) = (q  set (disjuncts p). Ifm bs (f q))"
    by (simp add: DJ_def evaldjf_ex)
  also have " = Ifm bs (f p)"
    using fdj fF by (induct p rule: disjuncts.induct) auto
  finally show ?thesis .
qed

lemma DJ_qf:
  assumes fqf: "p. qfree p  qfree (f p)"
  shows "p. qfree p  qfree (DJ f p) "
proof clarify
  fix p
  assume qf: "qfree p"
  have th: "DJ f p = evaldjf f (disjuncts p)"
    by (simp add: DJ_def)
  from disjuncts_qf[OF qf] have "q set (disjuncts p). qfree q" .
  with fqf have th':"q set (disjuncts p). qfree (f q)"
    by blast
  from evaldjf_qf[OF th'] th show "qfree (DJ f p)"
    by simp
qed

lemma DJ_qe:
  assumes qe: "bs p. qfree p  qfree (qe p)  (Ifm bs (qe p) = Ifm bs (E p))"
  shows "bs p. qfree p  qfree (DJ qe p)  (Ifm bs ((DJ qe p)) = Ifm bs (E p))"
proof clarify
  fix p :: fm
  fix bs
  assume qf: "qfree p"
  from qe have qth: "p. qfree p  qfree (qe p)"
    by blast
  from DJ_qf[OF qth] qf have qfth: "qfree (DJ qe p)"
    by auto
  have "Ifm bs (DJ qe p)  (q set (disjuncts p). Ifm bs (qe q))"
    by (simp add: DJ_def evaldjf_ex)
  also have "  (q  set(disjuncts p). Ifm bs (E q))"
    using qe disjuncts_qf[OF qf] by auto
  also have " = Ifm bs (E p)"
    by (induct p rule: disjuncts.induct) auto
  finally show "qfree (DJ qe p)  Ifm bs (DJ qe p) = Ifm bs (E p)"
    using qfth by blast
qed

  (* Simplification *)

fun maxcoeff:: "num  int"
where
  "maxcoeff (C i) = ¦i¦"
| "maxcoeff (CN n c t) = max ¦c¦ (maxcoeff t)"
| "maxcoeff t = 1"

lemma maxcoeff_pos: "maxcoeff t  0"
  by (induct t rule: maxcoeff.induct, auto)

fun numgcdh:: "num  int  int"
where
  "numgcdh (C i) = (λg. gcd i g)"
| "numgcdh (CN n c t) = (λg. gcd c (numgcdh t g))"
| "numgcdh t = (λg. 1)"

definition numgcd :: "num  int"
  where "numgcd t = numgcdh t (maxcoeff t)"

fun reducecoeffh:: "num  int  num"
where
  "reducecoeffh (C i) = (λg. C (i div g))"
| "reducecoeffh (CN n c t) = (λg. CN n (c div g) (reducecoeffh t g))"
| "reducecoeffh t = (λg. t)"

definition reducecoeff :: "num  num"
where
  "reducecoeff t =
   (let g = numgcd t
    in if g = 0 then C 0 else if g = 1 then t else reducecoeffh t g)"

fun dvdnumcoeff:: "num  int  bool"
where
  "dvdnumcoeff (C i) = (λg. g dvd i)"
| "dvdnumcoeff (CN n c t) = (λg. g dvd c  dvdnumcoeff t g)"
| "dvdnumcoeff t = (λg. False)"

lemma dvdnumcoeff_trans:
  assumes gdg: "g dvd g'"
    and dgt':"dvdnumcoeff t g'"
  shows "dvdnumcoeff t g"
  using dgt' gdg
  by (induct t rule: dvdnumcoeff.induct) (simp_all add: gdg dvd_trans[OF gdg])

declare dvd_trans [trans add]

lemma natabs0: "nat ¦x¦ = 0  x = 0"
  by arith

lemma numgcd0:
  assumes g0: "numgcd t = 0"
  shows "Inum bs t = 0"
  using g0[simplified numgcd_def]
  by (induct t rule: numgcdh.induct) (auto simp add: natabs0 maxcoeff_pos max.absorb2)

lemma numgcdh_pos:
  assumes gp: "g  0"
  shows "numgcdh t g  0"
  using gp by (induct t rule: numgcdh.induct) auto

lemma numgcd_pos: "numgcd t 0"
  by (simp add: numgcd_def numgcdh_pos maxcoeff_pos)

lemma reducecoeffh:
  assumes gt: "dvdnumcoeff t g"
    and gp: "g > 0"
  shows "real_of_int g *(Inum bs (reducecoeffh t g)) = Inum bs t"
  using gt
proof (induct t rule: reducecoeffh.induct)
  case (1 i)
  then have gd: "g dvd i"
    by simp
  with assms show ?case
    by (simp add: real_of_int_div[OF gd])
next
  case (2 n c t)
  then have gd: "g dvd c"
    by simp
  from assms 2 show ?case
    by (simp add: real_of_int_div[OF gd] algebra_simps)
qed (auto simp add: numgcd_def gp)

fun ismaxcoeff:: "num  int  bool"
where
  "ismaxcoeff (C i) = (λx. ¦i¦  x)"
| "ismaxcoeff (CN n c t) = (λx. ¦c¦  x  ismaxcoeff t x)"
| "ismaxcoeff t = (λx. True)"

lemma ismaxcoeff_mono: "ismaxcoeff t c  c  c'  ismaxcoeff t c'"
  by (induct t rule: ismaxcoeff.induct) auto

lemma maxcoeff_ismaxcoeff: "ismaxcoeff t (maxcoeff t)"
proof (induct t rule: maxcoeff.induct)
  case (2 n c t)
  then have H:"ismaxcoeff t (maxcoeff t)" .
  have thh: "maxcoeff t  max ¦c¦ (maxcoeff t)"
    by simp
  from ismaxcoeff_mono[OF H thh] show ?case
    by simp
qed simp_all

lemma zgcd_gt1:
  "¦i¦ > 1  ¦j¦ > 1  ¦i¦ = 0  ¦j¦ > 1  ¦i¦ > 1  ¦j¦ = 0"
  if "gcd i j > 1" for i j :: int
proof -
  have "¦k¦  1  k = - 1  k = 0  k = 1" for k :: int
    by auto
  with that show ?thesis
    by (auto simp add: not_less)
qed

lemma numgcdh0:"numgcdh t m = 0   m =0"
  by (induct t rule: numgcdh.induct) auto

lemma dvdnumcoeff_aux:
  assumes "ismaxcoeff t m"
    and mp: "m  0"
    and "numgcdh t m > 1"
  shows "dvdnumcoeff t (numgcdh t m)"
  using assms
proof (induct t rule: numgcdh.induct)
  case (2 n c t)
  let ?g = "numgcdh t m"
  from 2 have th: "gcd c ?g > 1"
    by simp
  from zgcd_gt1[OF th] numgcdh_pos[OF mp, where t="t"]
  consider "¦c¦ > 1" "?g > 1" | "¦c¦ = 0" "?g > 1" | "?g = 0"
    by auto
  then show ?case
  proof cases
    case 1
    with 2 have th: "dvdnumcoeff t ?g"
      by simp
    have th': "gcd c ?g dvd ?g"
      by simp
    from dvdnumcoeff_trans[OF th' th] show ?thesis
      by simp
  next
    case "2'": 2
    with 2 have th: "dvdnumcoeff t ?g"
      by simp
    have th': "gcd c ?g dvd ?g"
      by simp
    from dvdnumcoeff_trans[OF th' th] show ?thesis
      by simp
  next
    case 3
    then have "m = 0" by (rule numgcdh0)
    with 2 3 show ?thesis by simp
  qed
qed auto

lemma dvdnumcoeff_aux2:
  assumes "numgcd t > 1"
  shows "dvdnumcoeff t (numgcd t)  numgcd t > 0"
  using assms
proof (simp add: numgcd_def)
  let ?mc = "maxcoeff t"
  let ?g = "numgcdh t ?mc"
  have th1: "ismaxcoeff t ?mc"
    by (rule maxcoeff_ismaxcoeff)
  have th2: "?mc  0"
    by (rule maxcoeff_pos)
  assume H: "numgcdh t ?mc > 1"
  from dvdnumcoeff_aux[OF th1 th2 H] show "dvdnumcoeff t ?g" .
qed

lemma reducecoeff: "real_of_int (numgcd t) * (Inum bs (reducecoeff t)) = Inum bs t"
proof -
  let ?g = "numgcd t"
  have "?g  0"
    by (simp add: numgcd_pos)
  then consider "?g = 0" | "?g = 1" | "?g > 1" by atomize_elim auto
  then show ?thesis
  proof cases
    case 1
    then show ?thesis by (simp add: numgcd0)
  next
    case 2
    then show ?thesis by (simp add: reducecoeff_def)
  next
    case g1: 3
    from dvdnumcoeff_aux2[OF g1] have th1: "dvdnumcoeff t ?g" and g0: "?g > 0"
      by blast+
    from reducecoeffh[OF th1 g0, where bs="bs"] g1 show ?thesis
      by (simp add: reducecoeff_def Let_def)
  qed
qed

lemma reducecoeffh_numbound0: "numbound0 t  numbound0 (reducecoeffh t g)"
  by (induct t rule: reducecoeffh.induct) auto

lemma reducecoeff_numbound0: "numbound0 t  numbound0 (reducecoeff t)"
  using reducecoeffh_numbound0 by (simp add: reducecoeff_def Let_def)

fun numadd:: "num  num  num"
where
  "numadd (CN n1 c1 r1) (CN n2 c2 r2) =
   (if n1 = n2 then
    (let c = c1 + c2
     in (if c = 0 then numadd r1 r2 else CN n1 c (numadd r1 r2)))
    else if n1  n2 then (CN n1 c1 (numadd r1 (CN n2 c2 r2)))
    else (CN n2 c2 (numadd (CN n1 c1 r1) r2)))"
| "numadd (CN n1 c1 r1) t = CN n1 c1 (numadd r1 t)"
| "numadd t (CN n2 c2 r2) = CN n2 c2 (numadd t r2)"
| "numadd (C b1) (C b2) = C (b1 + b2)"
| "numadd a b = Add a b"

lemma numadd [simp]: "Inum bs (numadd t s) = Inum bs (Add t s)"
  by (induct t s rule: numadd.induct) (simp_all add: Let_def algebra_simps add_eq_0_iff)

lemma numadd_nb [simp]: "numbound0 t  numbound0 s  numbound0 (numadd t s)"
  by (induct t s rule: numadd.induct) (simp_all add: Let_def)

fun nummul:: "num  int  num"
where
  "nummul (C j) = (λi. C (i * j))"
| "nummul (CN n c a) = (λi. CN n (i * c) (nummul a i))"
| "nummul t = (λi. Mul i t)"

lemma nummul[simp]: "i. Inum bs (nummul t i) = Inum bs (Mul i t)"
  by (induct t rule: nummul.induct) (auto simp add: algebra_simps)

lemma nummul_nb[simp]: "i. numbound0 t  numbound0 (nummul t i)"
  by (induct t rule: nummul.induct) auto

definition numneg :: "num  num"
  where "numneg t = nummul t (- 1)"

definition numsub :: "num  num  num"
  where "numsub s t = (if s = t then C 0 else numadd s (numneg t))"

lemma numneg[simp]: "Inum bs (numneg t) = Inum bs (Neg t)"
  using numneg_def by simp

lemma numneg_nb[simp]: "numbound0 t  numbound0 (numneg t)"
  using numneg_def by simp

lemma numsub[simp]: "Inum bs (numsub a b) = Inum bs (Sub a b)"
  using numsub_def by simp

lemma numsub_nb[simp]: " numbound0 t ; numbound0 s  numbound0 (numsub t s)"
  using numsub_def by simp

primrec simpnum:: "num  num"
where
  "simpnum (C j) = C j"
| "simpnum (Bound n) = CN n 1 (C 0)"
| "simpnum (Neg t) = numneg (simpnum t)"
| "simpnum (Add t s) = numadd (simpnum t) (simpnum s)"
| "simpnum (Sub t s) = numsub (simpnum t) (simpnum s)"
| "simpnum (Mul i t) = (if i = 0 then C 0 else nummul (simpnum t) i)"
| "simpnum (CN n c t) = (if c = 0 then simpnum t else numadd (CN n c (C 0)) (simpnum t))"

lemma simpnum_ci[simp]: "Inum bs (simpnum t) = Inum bs t"
  by (induct t) simp_all

lemma simpnum_numbound0[simp]: "numbound0 t  numbound0 (simpnum t)"
  by (induct t) simp_all

fun nozerocoeff:: "num  bool"
where
  "nozerocoeff (C c) = True"
| "nozerocoeff (CN n c t) = (c  0  nozerocoeff t)"
| "nozerocoeff t = True"

lemma numadd_nz : "nozerocoeff a  nozerocoeff b  nozerocoeff (numadd a b)"
  by (induct a b rule: numadd.induct) (simp_all add: Let_def)

lemma nummul_nz : "i. i0  nozerocoeff a  nozerocoeff (nummul a i)"
  by (induct a rule: nummul.induct) (auto simp add: Let_def numadd_nz)

lemma numneg_nz : "nozerocoeff a  nozerocoeff (numneg a)"
  by (simp add: numneg_def nummul_nz)

lemma numsub_nz: "nozerocoeff a  nozerocoeff b  nozerocoeff (numsub a b)"
  by (simp add: numsub_def numneg_nz numadd_nz)

lemma simpnum_nz: "nozerocoeff (simpnum t)"
  by (induct t) (simp_all add: numadd_nz numneg_nz numsub_nz nummul_nz)

lemma maxcoeff_nz: "nozerocoeff t  maxcoeff t = 0  t = C 0"
  by (induction t rule: maxcoeff.induct) auto

lemma numgcd_nz:
  assumes nz: "nozerocoeff t"
    and g0: "numgcd t = 0"
  shows "t = C 0"
proof -
  from g0 have th:"numgcdh t (maxcoeff t) = 0"
    by (simp add: numgcd_def)
  from numgcdh0[OF th] have th:"maxcoeff t = 0" .
  from maxcoeff_nz[OF nz th] show ?thesis .
qed

definition simp_num_pair :: "(num × int)  num × int"
where
  "simp_num_pair =
    (λ(t,n).
     (if n = 0 then (C 0, 0)
      else
       (let t' = simpnum t ; g = numgcd t' in
         if g > 1 then
          (let g' = gcd n g
           in if g' = 1 then (t', n) else (reducecoeffh t' g', n div g'))
         else (t', n))))"

lemma simp_num_pair_ci:
  shows "((λ(t,n). Inum bs t / real_of_int n) (simp_num_pair (t,n))) =
    ((λ(t,n). Inum bs t / real_of_int n) (t, n))"
  (is "?lhs = ?rhs")
proof -
  let ?t' = "simpnum t"
  let ?g = "numgcd ?t'"
  let ?g' = "gcd n ?g"
  show ?thesis
  proof (cases "n = 0")
    case True
    then show ?thesis
      by (simp add: Let_def simp_num_pair_def)
  next
    case nnz: False
    show ?thesis
    proof (cases "?g > 1")
      case False
      then show ?thesis by (simp add: Let_def simp_num_pair_def)
    next
      case g1: True
      then have g0: "?g > 0"
        by simp
      from g1 nnz have gp0: "?g'  0"
        by simp
      then have g'p: "?g' > 0"
        using gcd_ge_0_int[where x="n" and y="numgcd ?t'"] by arith
      then consider "?g' = 1" | "?g' > 1" by arith
      then show ?thesis
      proof cases
        case 1
        then show ?thesis
          by (simp add: Let_def simp_num_pair_def)
      next
        case g'1: 2
        from dvdnumcoeff_aux2[OF g1] have th1: "dvdnumcoeff ?t' ?g" ..
        let ?tt = "reducecoeffh ?t' ?g'"
        let ?t = "Inum bs ?tt"
        have gpdg: "?g' dvd ?g" by simp
        have gpdd: "?g' dvd n" by simp
        have gpdgp: "?g' dvd ?g'" by simp
        from reducecoeffh[OF dvdnumcoeff_trans[OF gpdg th1] g'p]
        have th2:"real_of_int ?g' * ?t = Inum bs ?t'"
          by simp
        from g1 g'1 have "?lhs = ?t / real_of_int (n div ?g')"
          by (simp add: simp_num_pair_def Let_def)
        also have " = (real_of_int ?g' * ?t) / (real_of_int ?g' * (real_of_int (n div ?g')))"
          by simp
        also have " = (Inum bs ?t' / real_of_int n)"
          using real_of_int_div[OF gpdd] th2 gp0 by simp
        finally have "?lhs = Inum bs t / real_of_int n"
          by simp
        then show ?thesis
          by (simp add: simp_num_pair_def)
      qed
    qed
  qed
qed

lemma simp_num_pair_l:
  assumes tnb: "numbound0 t"
    and np: "n > 0"
    and tn: "simp_num_pair (t, n) = (t', n')"
  shows "numbound0 t'  n' > 0"
proof -
  let ?t' = "simpnum t"
  let ?g = "numgcd ?t'"
  let ?g' = "gcd n ?g"
  show ?thesis
  proof (cases "n = 0")
    case True
    then show ?thesis
      using assms by (simp add: Let_def simp_num_pair_def)
  next
    case nnz: False
    show ?thesis
    proof (cases "?g > 1")
      case False
      then show ?thesis
        using assms by (auto simp add: Let_def simp_num_pair_def)
    next
      case g1: True
      then have g0: "?g > 0" by simp
      from g1 nnz have gp0: "?g'  0" by simp
      then have g'p: "?g' > 0" using gcd_ge_0_int[where x="n" and y="numgcd ?t'"]
        by arith
      then consider "?g'= 1" | "?g' > 1" by arith
      then show ?thesis
      proof cases
        case 1
        then show ?thesis
          using assms g1 by (auto simp add: Let_def simp_num_pair_def)
      next
        case g'1: 2
        have gpdg: "?g' dvd ?g" by simp
        have gpdd: "?g' dvd n" by simp
        have gpdgp: "?g' dvd ?g'" by simp
        from zdvd_imp_le[OF gpdd np] have g'n: "?g'  n" .
        from zdiv_mono1[OF g'n g'p, simplified div_self[OF gp0]] have "n div ?g' > 0"
          by simp
        then show ?thesis
          using assms g1 g'1
          by (auto simp add: simp_num_pair_def Let_def reducecoeffh_numbound0)
      qed
    qed
  qed
qed

fun simpfm :: "fm  fm"
where
  "simpfm (And p q) = conj (simpfm p) (simpfm q)"
| "simpfm (Or p q) = disj (simpfm p) (simpfm q)"
| "simpfm (Imp p q) = imp (simpfm p) (simpfm q)"
| "simpfm (Iff p q) = iff (simpfm p) (simpfm q)"
| "simpfm (Not p) = not (simpfm p)"
| "simpfm (Lt a) = (let a' = simpnum a in case a' of C v  if (v < 0) then T else F | _  Lt a')"
| "simpfm (Le a) = (let a' = simpnum a in case a' of C v  if (v  0)  then T else F | _  Le a')"
| "simpfm (Gt a) = (let a' = simpnum a in case a' of C v  if (v > 0)  then T else F | _  Gt a')"
| "simpfm (Ge a) = (let a' = simpnum a in case a' of C v  if (v  0)  then T else F | _  Ge a')"
| "simpfm (Eq a) = (let a' = simpnum a in case a' of C v  if (v = 0)  then T else F | _  Eq a')"
| "simpfm (NEq a) = (let a' = simpnum a in case a' of C v  if (v  0)  then T else F | _  NEq a')"
| "simpfm p = p"

lemma simpfm: "Ifm bs (simpfm p) = Ifm bs p"
proof (induct p rule: simpfm.induct)
  case (6 a)
  let ?sa = "simpnum a"
  from simpnum_ci have sa: "Inum bs ?sa = Inum bs a"
    by simp
  consider v where "?sa = C v" | "¬ (v. ?sa = C v)" by blast
  then show ?case
  proof cases
    case 1
    then show ?thesis using sa by simp
  next
    case 2
    then show ?thesis using sa by (cases ?sa) (simp_all add: Let_def)
  qed
next
  case (7 a)
  let ?sa = "simpnum a"
  from simpnum_ci have sa: "Inum bs ?sa = Inum bs a"
    by simp
  consider v where "?sa = C v" | "¬ (v. ?sa = C v)" by blast
  then show ?case
  proof cases
    case 1
    then show ?thesis using sa by simp
  next
    case 2
    then show ?thesis using sa by (cases ?sa) (simp_all add: Let_def)
  qed
next
  case (8 a)
  let ?sa = "simpnum a"
  from simpnum_ci have sa: "Inum bs ?sa = Inum bs a"
    by simp
  consider v where "?sa = C v" | "¬ (v. ?sa = C v)" by blast
  then show ?case
  proof cases
    case 1
    then show ?thesis using sa by simp
  next
    case 2
    then show ?thesis using sa by (cases ?sa) (simp_all add: Let_def)
  qed
next
  case (9 a)
  let ?sa = "simpnum a"
  from simpnum_ci have sa: "Inum bs ?sa = Inum bs a"
    by simp
  consider v where "?sa = C v" | "¬ (v. ?sa = C v)" by blast
  then show ?case
  proof cases
    case 1
    then show ?thesis using sa by simp
  next
    case 2
    then show ?thesis using sa by (cases ?sa) (simp_all add: Let_def)
  qed
next
  case (10 a)
  let ?sa = "simpnum a"
  from simpnum_ci have sa: "Inum bs ?sa = Inum bs a"
    by simp
  consider v where "?sa = C v" | "¬ (v. ?sa = C v)" by blast
  then show ?case
  proof cases
    case 1
    then show ?thesis using sa by simp
  next
    case 2
    then show ?thesis using sa by (cases ?sa) (simp_all add: Let_def)
  qed
next
  case (11 a)
  let ?sa = "simpnum a"
  from simpnum_ci have sa: "Inum bs ?sa = Inum bs a"
    by simp
  consider v where "?sa = C v" | "¬ (v. ?sa = C v)" by blast
  then show ?case
  proof cases
    case 1
    then show ?thesis using sa by simp
  next
    case 2
    then show ?thesis using sa by (cases ?sa) (simp_all add: Let_def)
  qed
qed (induct p rule: simpfm.induct, simp_all)


lemma simpfm_bound0: "bound0 p  bound0 (simpfm p)"
proof (induct p rule: simpfm.induct)
  case (6 a)
  then have nb: "numbound0 a" by simp
  then have "numbound0 (simpnum a)" by (simp only: simpnum_numbound0[OF nb])
  then show ?case by (cases "simpnum a") (auto simp add: Let_def)
next
  case (7 a)
  then have nb: "numbound0 a" by simp
  then have "numbound0 (simpnum a)" by (simp only: simpnum_numbound0[OF nb])
  then show ?case by (cases "simpnum a") (auto simp add: Let_def)
next
  case (8 a)
  then have nb: "numbound0 a" by simp
  then have "numbound0 (simpnum a)" by (simp only: simpnum_numbound0[OF nb])
  then show ?case by (cases "simpnum a") (auto simp add: Let_def)
next
  case (9 a)
  then have nb: "numbound0 a" by simp
  then have "numbound0 (simpnum a)" by (simp only: simpnum_numbound0[OF nb])
  then show ?case by (cases "simpnum a") (auto simp add: Let_def)
next
  case (10 a)
  then have nb: "numbound0 a" by simp
  then have "numbound0 (simpnum a)" by (simp only: simpnum_numbound0[OF nb])
  then show ?case by (cases "simpnum a") (auto simp add: Let_def)
next
  case (11 a)
  then have nb: "numbound0 a" by simp
  then have "numbound0 (simpnum a)" by (simp only: simpnum_numbound0[OF nb])
  then show ?case by (cases "simpnum a") (auto simp add: Let_def)
qed (auto simp add: disj_def imp_def iff_def conj_def)

lemma simpfm_qf: "qfree p  qfree (simpfm p)"
  apply (induct p rule: simpfm.induct)
  apply (auto simp add: Let_def)
  apply (case_tac "simpnum a", auto)+
  done

fun prep :: "fm  fm"
where
  "prep (E T) = T"
| "prep (E F) = F"
| "prep (E (Or p q)) = disj (prep (E p)) (prep (E q))"
| "prep (E (Imp p q)) = disj (prep (E (Not p))) (prep (E q))"
| "prep (E (Iff p q)) = disj (prep (E (And p q))) (prep (E (And (Not p) (Not q))))"
| "prep (E (Not (And p q))) = disj (prep (E (Not p))) (prep (E(Not q)))"
| "prep (E (Not (Imp p q))) = prep (E (And p (Not q)))"
| "prep (E (Not (Iff p q))) = disj (prep (E (And p (Not q)))) (prep (E(And (Not p) q)))"
| "prep (E p) = E (prep p)"
| "prep (A (And p q)) = conj (prep (A p)) (prep (A q))"
| "prep (A p) = prep (Not (E (Not p)))"
| "prep (Not (Not p)) = prep p"
| "prep (Not (And p q)) = disj (prep (Not p)) (prep (Not q))"
| "prep (Not (A p)) = prep (E (Not p))"
| "prep (Not (Or p q)) = conj (prep (Not p)) (prep (Not q))"
| "prep (Not (Imp p q)) = conj (prep p) (prep (Not q))"
| "prep (Not (Iff p q)) = disj (prep (And p (Not q))) (prep (And (Not p) q))"
| "prep (Not p) = not (prep p)"
| "prep (Or p q) = disj (prep p) (prep q)"
| "prep (And p q) = conj (prep p) (prep q)"
| "prep (Imp p q) = prep (Or (Not p) q)"
| "prep (Iff p q) = disj (prep (And p q)) (prep (And (Not p) (Not q)))"
| "prep p = p"

lemma prep: "bs. Ifm bs (prep p) = Ifm bs p"
  by (induct p rule: prep.induct) auto

  (* Generic quantifier elimination *)
fun qelim :: "fm  (fm  fm)  fm"
where
  "qelim (E p) = (λqe. DJ qe (qelim p qe))"
| "qelim (A p) = (λqe. not (qe ((qelim (Not p) qe))))"
| "qelim (Not p) = (λqe. not (qelim p qe))"
| "qelim (And p q) = (λqe. conj (qelim p qe) (qelim q qe))"
| "qelim (Or  p q) = (λqe. disj (qelim p qe) (qelim q qe))"
| "qelim (Imp p q) = (λqe. imp (qelim p qe) (qelim q qe))"
| "qelim (Iff p q) = (λqe. iff (qelim p qe) (qelim q qe))"
| "qelim p = (λy. simpfm p)"

lemma qelim_ci:
  assumes qe_inv: "bs p. qfree p  qfree (qe p)  (Ifm bs (qe p) = Ifm bs (E p))"
  shows "bs. qfree (qelim p qe)  (Ifm bs (qelim p qe) = Ifm bs p)"
  using qe_inv DJ_qe[OF qe_inv]
  by (induct p rule: qelim.induct)
    (auto simp add: simpfm simpfm_qf simp del: simpfm.simps)

fun minusinf:: "fm  fm" (* Virtual substitution of -∞*)
where
  "minusinf (And p q) = conj (minusinf p) (minusinf q)"
| "minusinf (Or p q) = disj (minusinf p) (minusinf q)"
| "minusinf (Eq  (CN 0 c e)) = F"
| "minusinf (NEq (CN 0 c e)) = T"
| "minusinf (Lt  (CN 0 c e)) = T"
| "minusinf (Le  (CN 0 c e)) = T"
| "minusinf (Gt  (CN 0 c e)) = F"
| "minusinf (Ge  (CN 0 c e)) = F"
| "minusinf p = p"

fun plusinf:: "fm  fm" (* Virtual substitution of +∞*)
where
  "plusinf (And p q) = conj (plusinf p) (plusinf q)"
| "plusinf (Or p q) = disj (plusinf p) (plusinf q)"
| "plusinf (Eq  (CN 0 c e)) = F"
| "plusinf (NEq (CN 0 c e)) = T"
| "plusinf (Lt  (CN 0 c e)) = F"
| "plusinf (Le  (CN 0 c e)) = F"
| "plusinf (Gt  (CN 0 c e)) = T"
| "plusinf (Ge  (CN 0 c e)) = T"
| "plusinf p = p"