https://github.com/HeikoBecker/Dandelion
Raw File
Tip revision: 405b118a81db2f4443042751973cbaf9891bdb94 authored by Heiko Becker on 09 February 2022, 05:52:09 UTC
Add a missing theorem
Tip revision: 405b118
sturmScript.sml
(**
  Proof of Sturm's theorem, ported from Harrison material
**)
open pred_setTheory listTheory bossLib RealArith realTheory polyTheory;
open renameTheory;
open preambleDandelion;

val _ = new_theory "sturm";

(** HOL-Light compatibility **)
val REAL_MUL_AC = REAL_MUL_ASSOC;
val SPEC = Q.SPEC;
val SPECL = Q.SPECL;
val REAL_ARITH = fn t => REAL_ARITH (Term t);
val SUBGOAL_THEN = fn t => SUBGOAL_THEN (Term t);
val UNDISCH_TAC = fn t => UNDISCH_TAC (Term t);
val EXISTS_TAC = fn t => EXISTS_TAC (Term t);
val GEN_REWRITE_TAC = jrhUtils.GEN_REWR_TAC;

(* ========================================================================= *)
(* Formalization of Sturm sequences and Sturm's theorem.                     *)
(* ========================================================================= *)

(**
do_list override_interface
 ["divides",`poly_divides:real list->real list->bool`;
  "exp",`poly_exp:real list -> num -> real list`;
   "diff",`poly_diff:real list->real list`];; **)

(* ------------------------------------------------------------------------- *)
(* Dreary lemmas about sign alternations.                                    *)
(* ------------------------------------------------------------------------- *)

Theorem SIGN_LEMMA0:
  ! a b c:real.
    &0 < a /\ &0 < b /\ &0 < c ==>
    &0 < a * b /\ &0 < a * c /\ &0 < b * c
Proof
  rpt strip_tac >> irule REAL_LT_MUL >> gs[]
QED

Theorem SIGN_LEMMA1:
  !a b c:real. a * b > &0 ==> (c * a < &0 <=> c * b < &0)
Proof
  REPEAT GEN_TAC >> REWRITE_TAC[real_gt]
  >> REPEAT_TCL DISJ_CASES_THEN ASSUME_TAC (SPEC `a:real` REAL_LT_NEGTOTAL)
  >> REPEAT_TCL DISJ_CASES_THEN ASSUME_TAC (SPEC `b:real` REAL_LT_NEGTOTAL)
  >> REPEAT_TCL DISJ_CASES_THEN ASSUME_TAC (SPEC `c:real` REAL_LT_NEGTOTAL)
  >> ASM_REWRITE_TAC[REAL_MUL_RZERO, REAL_MUL_LZERO, REAL_LT_REFL]
  >> POP_ASSUM_LIST(MP_TAC o MATCH_MP SIGN_LEMMA0 o end_itlist CONJ)
  >> REWRITE_TAC[REAL_MUL_LNEG, REAL_MUL_RNEG, REAL_NEG_NEG]
  >> REWRITE_TAC[REAL_MUL_AC] >> REAL_ARITH_TAC
QED

Theorem SIGN_LEMMA2:
  !a b c:real. a * b > &0 ==> (a * c < &0 <=> b * c < &0)
Proof
  REPEAT GEN_TAC
  >> DISCH_THEN(MP_TAC o SPEC_ALL o MATCH_MP SIGN_LEMMA1)
  >> REAL_ARITH_TAC
QED

Theorem SIGN_LEMMA3:
  !a b:real. a < &0 ==> (a * b > &0 <=> b < &0)
Proof
  REPEAT GEN_TAC
  >> REWRITE_TAC[REAL_ARITH `a:real < &0 <=> -(&1) * a > &0`]
  >> DISCH_THEN(MP_TAC o MATCH_MP SIGN_LEMMA1)
  >> DISCH_THEN(MP_TAC o SPEC `-b`)
  >> REWRITE_TAC[REAL_MUL_AC, real_gt, REAL_MUL_LNEG, REAL_MUL_RNEG,
              REAL_NEG_NEG, REAL_MUL_LID, REAL_MUL_RID] >> REAL_ARITH_TAC
QED

Theorem SIGN_LEMMA5:
   (a:real) * b < &0 <=> a > &0 /\ b < &0 \/ a < &0 /\ b > &0
Proof
  REPEAT_TCL DISJ_CASES_THEN ASSUME_TAC
                (SPEC `a:real` REAL_LT_NEGTOTAL)
  >> REPEAT_TCL DISJ_CASES_THEN ASSUME_TAC
   (SPEC `b:real` REAL_LT_NEGTOTAL)
  >> gs[REAL_MUL_LZERO, REAL_MUL_RZERO, REAL_LT_REFL, real_gt]
  >> POP_ASSUM_LIST(MP_TAC o end_itlist CONJ)
  >> DISCH_THEN(fn th => MP_TAC th >> ASSUME_TAC th)
  >-(
    POP_ASSUM(MP_TAC o MATCH_MP REAL_LT_MUL)
    >> REWRITE_TAC[REAL_MUL_LNEG, REAL_MUL_RNEG, REAL_NEG_NEG]
    >> REAL_ARITH_TAC)
  >- (
    rpt strip_tac >> ‘0 = a * 0’ by REAL_ARITH_TAC
    >> pop_assum $ once_rewrite_tac o single
    >> irule REAL_LT_LMUL_IMP >> gs[])
  >- (
    rpt strip_tac >> ‘0 = 0 * b’ by REAL_ARITH_TAC
    >> pop_assum $ once_rewrite_tac o single
    >> irule REAL_LT_RMUL_IMP >> gs[])
  >> rpt strip_tac >> EQ_TAC >> rpt strip_tac >>  gs[]
  >- (
    ‘0 < -a ∧ 0 < -b’ by (conj_tac >> REAL_ASM_ARITH_TAC)
    >> ‘0 < -a * -b’ by (irule REAL_LT_MUL >> gs[])
    >> gs[REAL_MUL_LNEG, REAL_MUL_RNEG, REAL_NEG_NEG]
    >> ‘0 < 0’ by REAL_ASM_ARITH_TAC
    >> gs[])
  >> REAL_ASM_ARITH_TAC
QED

Theorem SIGN_LEMMA4:
  !a b c:real. a * b < &0 /\ ~(c = &0) ==> (c * a < &0 <=> ~(c * b < &0))
Proof
  REPEAT STRIP_TAC
  >> MP_TAC(SPECL [`a:real`, `-b`, `c:real`] SIGN_LEMMA2)
  >> ASM_REWRITE_TAC[REAL_MUL_RNEG, real_gt]
  >> ASM_REWRITE_TAC[REAL_ARITH `&0 < -a:real <=> a:real < &0`]
  >> REWRITE_TAC[REAL_MUL_AC] >> DISCH_THEN SUBST1_TAC
  >> REWRITE_TAC[REAL_MUL_RNEG, REAL_MUL_AC]
  >> SUBGOAL_THEN `~((b:real) * (c:real) = &0)` MP_TAC
  >- (
    ASM_REWRITE_TAC[REAL_ENTIRE, DE_MORGAN_THM]
    >> DISCH_TAC >> UNDISCH_TAC `(a:real) * (b:real) < &0`
    >> ASM_REWRITE_TAC[REAL_MUL_LZERO, REAL_MUL_RZERO, REAL_LT_REFL]
  )
  >> rpt strip_tac >> EQ_TAC >> strip_tac >> gs[SIGN_LEMMA5]
  >> REAL_ASM_ARITH_TAC
QED

Theorem SIGN_LEMMA6:
  !a b:real. a * b <= &0 <=> a >= &0 /\ b <= &0 \/ a <= &0 /\ b >= &0
Proof
  REWRITE_TAC[real_ge, REAL_LE_LT, REAL_ENTIRE, SIGN_LEMMA5] >> rpt gen_tac
  >> Cases_on `a = &0` >> Cases_on `b = &0`
  >> ASM_REWRITE_TAC[REAL_MUL_LZERO, REAL_MUL_RZERO, REAL_LT_REFL]
  >> REWRITE_TAC[real_gt]
  >> (REAL_ARITH_TAC ORELSE metis_tac[])
QED

(* ------------------------------------------------------------------------- *)
(* The number of variations in sign of a list of reals.                      *)
(* ------------------------------------------------------------------------- *)

Definition varrec_def:
  varrec prev [] = 0 ∧
  varrec prev (CONS h t) =
    if prev * (h:real) < &0 then SUC(varrec h t)
    else if h = &0 then varrec prev t
    else varrec (h:real) (t:real list)
End

Definition variation_def:
  variation (l:real list) = varrec (&0) (l:real list)
End

(* ------------------------------------------------------------------------- *)
(* Show that it depends only on the sign of the "previous element".          *)
(* ------------------------------------------------------------------------- *)

Theorem VARREC_SIGN:
  !(l:real list) (r s:real). r * s > &0 ==> (varrec r l = varrec s l)
Proof
  Induct_on ‘l’ >> REPEAT GEN_TAC >> REWRITE_TAC[varrec_def]
  >> DISCH_TAC
  >> FIRST_ASSUM(fn th => REWRITE_TAC[MATCH_MP SIGN_LEMMA2 th])
  >> Cases_on `s * h < &0` >> ASM_REWRITE_TAC[]
  >> COND_CASES_TAC >> REWRITE_TAC[]
  >> FIRST_ASSUM MATCH_MP_TAC >> ASM_REWRITE_TAC[]
QED

(* ------------------------------------------------------------------------- *)
(* Middle is irrelevant if surrounding elements have opposite sign.          *)
(* ------------------------------------------------------------------------- *)

Theorem VARREC_STRADDLE:
 !f g h x.
   poly f x * poly h x < &0 ⇒
   (varrec (poly f x) (MAP (\p. poly p x) (CONS g (CONS h l))) =
    SUC (varrec (poly h x) (MAP (\p. poly p x) l)))
Proof
  REPEAT GEN_TAC >> REWRITE_TAC[listTheory.MAP, varrec_def]
  >> Cases_on `poly h x = &0`
  >> Cases_on `poly g x = &0`
  >> ASM_REWRITE_TAC[REAL_MUL_LZERO, REAL_MUL_RZERO, real_gt, REAL_LT_REFL]
  >- (gs[])
  >> DISCH_TAC >> ASM_REWRITE_TAC[]
  >> jrhUtils.GEN_REWR_TAC (LAND_CONV o RATOR_CONV o RATOR_CONV o RAND_CONV o LAND_CONV)
   [REAL_MUL_SYM]
  >> MP_TAC(SPECL [`poly f x`, `poly h x`, `poly g x`] SIGN_LEMMA4)
  >> ASM_REWRITE_TAC[] >> DISCH_THEN SUBST1_TAC >>
  Cases_on `poly g x * poly h x < &0` >> gs[SIGN_LEMMA5]
  >> TRY COND_CASES_TAC >> gs[] >> REAL_ASM_ARITH_TAC
QED

(* ------------------------------------------------------------------------- *)
(* Property of being a (standard) Sturm sequence.                            *)
(* ------------------------------------------------------------------------- *)

Definition STURM_def:
  STURM f f' [] = f' poly_divides f ∧
  STURM f f' (g::gs) = ((∃ k. &0 < k ∧ f' poly_divides (f + k ## g)) ∧
                              degree g < degree f' ∧ STURM f' g gs)
End

(* ------------------------------------------------------------------------- *)
(* If a polynomial doesn't have a root in an interval, sign doesn't change.  *)
(* ------------------------------------------------------------------------- *)

Theorem STURM_NOROOT:
  ∀ a b p.
    a ≤ b ∧
    (∀ x. a <= x /\ x <= b ==> ~(poly p x = &0)) ⇒
    poly p a * poly p b > &0
Proof
  REPEAT GEN_TAC >> REWRITE_TAC[real_gt]
  >> jrhUtils.GEN_REWR_TAC (LAND_CONV o LAND_CONV) [REAL_LE_LT]
  >> DISCH_THEN(CONJUNCTS_THEN2 DISJ_CASES_TAC ASSUME_TAC)
  >- (
    SUBGOAL_THEN `~(poly p a = &0) /\ ~(poly p b = &0)` STRIP_ASSUME_TAC
    >- (
     CONJ_TAC >> FIRST_ASSUM MATCH_MP_TAC
     >> UNDISCH_TAC `a:real < b:real` >> REAL_ARITH_TAC
    )
    >> REPEAT_TCL DISJ_CASES_THEN MP_TAC (SPEC `poly p a` REAL_LT_NEGTOTAL)
    >> REPEAT_TCL DISJ_CASES_THEN MP_TAC (SPEC `poly p b` REAL_LT_NEGTOTAL)
    >> ASM_REWRITE_TAC[]
    >> REWRITE_TAC[PROVE [] “a ==> b ==> c <=> b /\ a ==> c”]
    >> TRY(DISCH_THEN(MP_TAC o MATCH_MP REAL_LT_MUL)
           >> REWRITE_TAC[REAL_MUL_LNEG, REAL_MUL_RNEG, REAL_NEG_NEG]
           >> NO_TAC)
    >> REWRITE_TAC[REAL_ARITH `&0 < -a:real <=> a:real < &0`]
    >> REWRITE_TAC[REAL_ARITH `&0 < x:real <=> x:real > &0`]
    >> UNDISCH_TAC `a:real < b:real`
    >> REWRITE_TAC[GSYM satTheory.AND_IMP] THENL
       [DISCH_THEN(Q.X_CHOOSE_THEN `x:real` MP_TAC o MATCH_MP POLY_IVT_NEG),
        DISCH_THEN(Q.X_CHOOSE_THEN `x:real` MP_TAC o MATCH_MP POLY_IVT_POS)]
    >> REPEAT(DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC))
    >> CONV_TAC CONTRAPOS_CONV >> DISCH_TAC
    >> FIRST_ASSUM MATCH_MP_TAC >> CONJ_TAC
    >> MATCH_MP_TAC REAL_LT_IMP_LE >> ASM_REWRITE_TAC[])
  >> gs[]
QED

(* ------------------------------------------------------------------------- *)
(* Now we get the changes in the variation.                                  *)
(* ------------------------------------------------------------------------- *)

Theorem STURM_NOROOT_NOVAR_LEMMA:
  ∀ f a b l.
    a <= b ∧
    (∀ x. a <= x /\ x <= b ==> ~(poly f x = &0)) ⇒
    (varrec r (MAP (\p. poly p a) (CONS f l)) +
     varrec (poly f a) (MAP (\p. poly p b) l) =
     varrec r (MAP (\p. poly p b) (CONS f l)) +
     varrec (poly f a) (MAP (\p. poly p a) l))
Proof
  REPEAT GEN_TAC >> DISCH_TAC
  >> jrhUtils.GEN_REWR_TAC (LAND_CONV o LAND_CONV o RAND_CONV) [listTheory.MAP]
  >> jrhUtils.GEN_REWR_TAC (RAND_CONV o LAND_CONV o RAND_CONV) [listTheory.MAP]
  >> REWRITE_TAC[varrec_def]
  >> FIRST_ASSUM(ASSUME_TAC o MATCH_MP STURM_NOROOT)
  >> FIRST_ASSUM(ASSUME_TAC o MATCH_MP SIGN_LEMMA1)
  >> FIRST_ASSUM(ASSUME_TAC o MATCH_MP VARREC_SIGN)
  >> ASM_REWRITE_TAC[]
  >> SUBGOAL_THEN `~(poly f a = &0) /\ ~(poly f b = &0)` STRIP_ASSUME_TAC
  >- (
    CONJ_TAC >> FIRST_ASSUM(MATCH_MP_TAC o CONJUNCT2)
    >> ASM_REWRITE_TAC[REAL_LE_REFL])
  >> ASM_REWRITE_TAC[]
  >> COND_CASES_TAC >> gs[]
QED

Theorem STURM_NOROOT_NOVAR:
  ∀ f a b l.
    a <= b ∧
    (∀ x. a <= x /\ x <= b ==> ~(poly f x = &0)) ∧
    (varrec (poly f a) (MAP (\p. poly p a) l) =
     varrec (poly f a) (MAP (\p. poly p b) l)) ⇒
    (varrec r (MAP (\p. poly p a) (CONS f l)) =
     varrec r (MAP (\p. poly p b) (CONS f l)))
Proof
  REPEAT GEN_TAC >> REWRITE_TAC[CONJ_ASSOC]
  >> DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC)
  >> DISCH_THEN(MP_TAC o SPEC_ALL o MATCH_MP STURM_NOROOT_NOVAR_LEMMA)
  >> gs[]
QED

fun check (f:term -> bool) t = if f t then t else raise Feedback.mk_HOL_ERR "" "" ""

Theorem STURM_NOVAR_LEMMA:
  ∀ n l f f' c.
    (LENGTH l = n) ∧
    STURM f f' l ∧
    a <= c ∧ c <= b ∧
    (∀ x.
       a <= x /\ x <= b /\ EXISTS (\p. poly p x = &0) (CONS f (CONS f' l)) ⇒
       (x = c)) ∧
    ~(poly f c = &0) ⇒
    (varrec (poly f a) (MAP (\p. poly p a) (CONS f' l)) =
     varrec (poly f b) (MAP (\p. poly p b) (CONS f' l)))
Proof
  completeInduct_on ‘n’ >> rpt GEN_TAC >> DISCH_TAC
  >> Induct_on ‘l’  >> REWRITE_TAC[STURM_def]
  >- (
    REPEAT STRIP_TAC >>SUBGOAL_THEN `~(poly f' c = &0)` ASSUME_TAC
    >- (
      UNDISCH_TAC `~(poly f c = &0)` >> CONV_TAC CONTRAPOS_CONV
      >> REWRITE_TAC[] >> DISCH_TAC
      >> UNDISCH_TAC `f' poly_divides f` >> REWRITE_TAC[poly_divides]
      >> DISCH_THEN(CHOOSE_THEN SUBST1_TAC)
      >> ASM_REWRITE_TAC[POLY_MUL, REAL_MUL_LZERO]
    )
    >> FIRST_ASSUM(Tactic.UNDISCH_TAC o check is_forall o concl)
    >> REWRITE_TAC[listTheory.EXISTS_DEF] >> DISCH_TAC
    >> SUBGOAL_THEN `(!x. a <= x /\ x <= b ==> ~(poly f x = &0)) /\
                  (!x. a <= x /\ x <= b ==> ~(poly f' x = &0))`
    STRIP_ASSUME_TAC
    >- mesonLib.ASM_MESON_TAC[]
    >> `a:real <= b:real`
      by ( UNDISCH_TAC `c:real <= b:real` >> UNDISCH_TAC `a:real <= c:real` >> REAL_ARITH_TAC)
    >> MATCH_MP_TAC EQ_TRANS
    >> Q.EXISTS_TAC `varrec (poly f b) (MAP (\p. poly p a) [f'])`
    >> CONJ_TAC
    >- (
      MATCH_MP_TAC VARREC_SIGN
      >> MATCH_MP_TAC STURM_NOROOT >> ASM_REWRITE_TAC[]
    )
    >> MATCH_MP_TAC STURM_NOROOT_NOVAR >> ASM_REWRITE_TAC[]
    >> REWRITE_TAC[listTheory.MAP]
  )
  >> REPEAT STRIP_TAC
  >> `!x. a <= x /\ x <= b ==> ~(poly f x = &0)` by
    (
      UNDISCH_TAC `~(poly f c = &0)`
      >> UNDISCH_TAC ‘∀x. a ≤ x ∧ x ≤ b ∧
                       EXISTS (λp. poly p x = 0) (f::f'::h::l) ⇒ x = c’
      >> REWRITE_TAC[EXISTS_DEF] >> mesonLib.MESON_TAC[]
    )
  >> `a:real <= b:real` by
    ( UNDISCH_TAC `c:real <= b:real` >> UNDISCH_TAC `a:real <= c:real` >> REAL_ARITH_TAC )
  >> MATCH_MP_TAC EQ_TRANS
  >> EXISTS_TAC
      `varrec (poly f b) (MAP (\p. poly p a) (CONS f' (CONS h l)))`
  >> CONJ_TAC
  >- (
    MATCH_MP_TAC VARREC_SIGN
    >> MATCH_MP_TAC STURM_NOROOT >>ASM_REWRITE_TAC[]
  )
  >> ASM_CASES_TAC “poly f' c = &0” >> ASM_REWRITE_TAC[]
  >- (
    UNDISCH_TAC `f' poly_divides f + k ## h`
    >> REWRITE_TAC[poly_divides]
    >> DISCH_THEN(X_CHOOSE_THEN “q:real list”  MP_TAC)
    >> DISCH_THEN(MP_TAC o C AP_THM “c:real”)
    >> REWRITE_TAC[POLY_MUL,POLY_ADD]
    >> ASM_REWRITE_TAC[REAL_MUL_LZERO]
    >> REWRITE_TAC[REAL_ARITH ‘((x:real) + (y:real) = &0) <=> (x = -y)’]
    >> DISCH_TAC
    >> `(!x. a <= x /\ x <= b ==> ~(poly f x = &0)) /\
                    (!x. a <= x /\ x <= b ==> ~(poly h x = &0))` by
      (
        CONJ_TAC
        >- metis_tac[]
        >> X_GEN_TAC “x:real”
        >> UNDISCH_TAC `!x. a <= x /\ x <= b /\
           EXISTS (\p. poly p x = &0) (CONS f (CONS f' (CONS h l)))
           ==> (x = c)`
        >> DISCH_THEN(MP_TAC o SPEC `x:real`)
        >> REWRITE_TAC[EXISTS_DEF]
        >> `(poly h c = &0) <=> (poly f c = &0)` by
          (
            UNDISCH_TAC `poly f c = - (poly (k ## h) c)`
            >> DISCH_THEN SUBST1_TAC >> REWRITE_TAC[POLY_CMUL]
            >> REWRITE_TAC[GSYM REAL_MUL_LNEG]
            >> REWRITE_TAC[REAL_ENTIRE]
            >> ASM_CASES_TAC “poly h c = &0”
            >- ASM_REWRITE_TAC[]
            >> UNDISCH_TAC `&0 < k:real` >> REAL_ARITH_TAC
          )
        >> pop_assum mp_tac
        >> ASM_REWRITE_TAC[ASSUME “~(poly f c = &0)”]
        >> UNDISCH_TAC `~(poly f c = &0)` >> mesonLib.MESON_TAC[]
      )
    >> `poly f a * poly f b > &0 /\
                    poly h a * poly h b > &0`
        by
        (
          CONJ_TAC
          >- (
          MATCH_MP_TAC STURM_NOROOT >> metis_tac[]
          )
          >> MATCH_MP_TAC STURM_NOROOT >> metis_tac[]
        )
    >> SUBGOAL_THEN `(poly f a * poly h a < &0) /\
                    (poly f b * poly h b < &0)`
      MP_TAC
    >- (
      SUBGOAL_THEN `a <= c /\
                      (!x. a <= x /\ x <= c ==> ~(poly (f * h) x = &0))`
          MP_TAC
      >- (
        CONJ_TAC
        >- ASM_REWRITE_TAC[]
        >>  REWRITE_TAC[REAL_ENTIRE, POLY_MUL, DE_MORGAN_THM]
        >> GEN_TAC >> STRIP_TAC >> CONJ_TAC
        >> FIRST_ASSUM MATCH_MP_TAC >> ASM_REWRITE_TAC[]
        >> MATCH_MP_TAC REAL_LE_TRANS >> EXISTS_TAC `c:real`
        >> ASM_REWRITE_TAC[] >> first_assum match_mp_tac
        >> ASM_REWRITE_TAC[]
        >> UNDISCH_TAC ‘x:real≤c:real’ >> UNDISCH_TAC ‘c:real≤b:real’
        >> REAL_ARITH_TAC
      )
      >> DISCH_THEN(MP_TAC o MATCH_MP STURM_NOROOT)
      >> SUBGOAL_THEN `c <= b /\
                      (!x. c <= x /\ x <= b ==> ~(poly (f * h) x = &0))`
          MP_TAC
      >- (
        CONJ_TAC
        >- ASM_REWRITE_TAC[]
        >>  REWRITE_TAC[REAL_ENTIRE, POLY_MUL, DE_MORGAN_THM]
        >> GEN_TAC >> STRIP_TAC >> CONJ_TAC
        >> FIRST_ASSUM MATCH_MP_TAC >> ASM_REWRITE_TAC[]
        >> MATCH_MP_TAC REAL_LE_TRANS >> EXISTS_TAC `c:real`
        >> ASM_REWRITE_TAC[] >> first_assum match_mp_tac
        >> ASM_REWRITE_TAC[] >> UNDISCH_TAC ‘a:real≤c:real’
        >> UNDISCH_TAC ‘c:real ≤x:real’ >> REAL_ARITH_TAC
      )
      >> DISCH_THEN(MP_TAC o MATCH_MP STURM_NOROOT)
      >> SUBGOAL_THEN `poly (f * h) c < &0` MP_TAC
      >- (
        ASM_REWRITE_TAC[POLY_MUL, REAL_MUL_LNEG]
        >> REWRITE_TAC[REAL_ARITH `-x < &0 <=> &0 < x:real`, POLY_CMUL]
        >> REWRITE_TAC[GSYM REAL_MUL_ASSOC]
        >> MATCH_MP_TAC REAL_LT_MUL >> ASM_REWRITE_TAC[]
        >> REWRITE_TAC[REAL_POSSQ]
        >> FIRST_ASSUM MATCH_MP_TAC >> ASM_REWRITE_TAC[]
      )
      >> GEN_REWRITE_TAC (RAND_CONV o RAND_CONV o LAND_CONV o LAND_CONV)
                         [REAL_MUL_SYM]
      >> DISCH_THEN(fn th => REWRITE_TAC[MATCH_MP SIGN_LEMMA3 th])
      >> REWRITE_TAC[POLY_MUL] >> metis_tac[]
    )
    >> DISCH_THEN(CONJUNCTS_THEN (ASSUME_TAC o MATCH_MP VARREC_STRADDLE))
    >> MATCH_MP_TAC EQ_TRANS
    >> EXISTS_TAC
        `varrec (poly f a) (MAP (\p. poly p a) (CONS f' (CONS h l)))`
    >> CONJ_TAC
    >- (
      MATCH_MP_TAC VARREC_SIGN >> ONCE_REWRITE_TAC[REAL_MUL_SYM]
      >> ASM_REWRITE_TAC[]
    )
    >> ASM_REWRITE_TAC[ arithmeticTheory.LESS_EQ_MONO]
    >> MP_TAC(ISPEC “l:(real list)list” list_CASES)
    >> DISCH_THEN(DISJ_CASES_THEN2 SUBST_ALL_TAC MP_TAC)
    >- REWRITE_TAC[varrec_def, MAP]
    >> DISCH_THEN(X_CHOOSE_THEN “g:real list”  MP_TAC)
    >> DISCH_THEN(X_CHOOSE_THEN “m:(real list)list” SUBST_ALL_TAC)
    >> FIRST_ASSUM(MP_TAC o SPEC `PRE(PRE n)`)
    >> UNDISCH_TAC `LENGTH (CONS (h:real list) (CONS g m)) = n`
    >> REWRITE_TAC[LENGTH] THEN DISCH_THEN(SUBST_ALL_TAC o SYM)
    >> ‘PRE (PRE (SUC (SUC (LENGTH m)))) = LENGTH m’ by rw[]
    >> pop_assum $ rewrite_tac o single
    >> ‘LENGTH m < SUC (SUC (LENGTH m))’ by rw[]
    >> pop_assum $ rewrite_tac o single
    >> DISCH_THEN(MP_TAC o SPECL [`m:(real list)list`, `h:real list`])
    >> DISCH_THEN(MP_TAC o SPECL [`g:real list`, `c:real`])
    >> ASM_REWRITE_TAC[]
    >> ‘STURM h g m ∧
        (∀x. a ≤ x ∧ x ≤ b ∧ EXISTS (λp. poly p x = 0) (h::g::m) ⇒ x = c)
        ∧  poly h c ≠ 0’
      by (
        CONJ_TAC
        >- metis_tac[STURM_def]
        >> CONJ_TAC
        >- ( rpt strip_tac >> rw[] )
        >> first_assum match_mp_tac >> metis_tac[]
      )
    >> rpt strip_tac >> AP_TERM_TAC
    >> first_assum  MATCH_MP_TAC >> metis_tac[]
  )
  >> SUBGOAL_THEN `~(poly f' a = &0) /\ ~(poly f' b = &0)`
                                      STRIP_ASSUME_TAC
  >- (
    RULE_ASSUM_TAC(REWRITE_RULE[EXISTS_DEF])
    >> mesonLib.ASM_MESON_TAC[REAL_LE_REFL, REAL_LT_REFL]
  )
  >> FIRST_ASSUM(MP_TAC o SPEC `PRE n`)
  >> UNDISCH_TAC `LENGTH (CONS (h:real list) l) = n`
  >> REWRITE_TAC[LENGTH] >> DISCH_THEN(SUBST_ALL_TAC o SYM)
  >> ‘PRE (SUC (LENGTH l)) = LENGTH l’ by rw[]
  >> pop_assum $ rewrite_tac o single
  >> ‘LENGTH l < SUC (LENGTH l)’ by rw[]
  >> pop_assum $ rewrite_tac o single
  >> DISCH_THEN(MP_TAC o SPECL [`l:(real list)list`, `f':real list`])
  >> DISCH_THEN(MP_TAC o SPECL [`h:real list`, `c:real`])
  >> RULE_ASSUM_TAC(REWRITE_RULE[STURM_def]) THEN ASM_REWRITE_TAC[]
  >> W(C SUBGOAL_THEN (fn th => REWRITE_TAC[th]) o (fn t => `^t`) o lhand o lhand o snd)
  >- (
    REWRITE_TAC[EXISTS_DEF]
    >> REPEAT STRIP_TAC >> FIRST_ASSUM MATCH_MP_TAC
    >> ASM_REWRITE_TAC[EXISTS_DEF]
    >> first_assum match_mp_tac
    >> ASM_REWRITE_TAC[EXISTS_DEF] >> first_assum match_mp_tac
    >> ASM_REWRITE_TAC[EXISTS_DEF]
  )
  >> DISCH_TAC
  >> ONCE_REWRITE_TAC[MAP] >> REWRITE_TAC[varrec_def]
  >> ASM_REWRITE_TAC[]
  >> SUBGOAL_THEN `!x. a <= x /\ x <= b ==> ~(poly f' x = &0)`
                                          ASSUME_TAC
  >- (
    UNDISCH_TAC `~(poly f' c = &0)`
    >> UNDISCH_TAC `!x. a <= x /\ x <= b /\
       EXISTS (\p. poly p x = &0) (CONS f (CONS f' (CONS h l)))
       ==> (x = c)`
    >> REWRITE_TAC[EXISTS_DEF] >> mesonLib.MESON_TAC[]
  )
  >> MP_TAC(SPECL [`a:real`, `b:real`, `f':real list`] STURM_NOROOT)
  >> ASM_REWRITE_TAC[] >> rpt strip_tac
  >> ‘(poly f b * poly f' a < &0) ⇔ (poly f b * poly f' b < &0)’ by
    ( irule SIGN_LEMMA1 >> metis_tac[] )
  >> metis_tac[]
QED

Theorem STURM_NOVAR:
 ∀ l f f' c.
        STURM f f' l ∧
        a ≤ c ∧ c ≤ b ∧
        (∀ x.
           a ≤ x ∧ x ≤ b ∧ EXISTS (\p. poly p x = &0) (CONS f (CONS f' l)) ⇒
           (x = c)) ∧
        ~(poly f c = &0) ⇒
        (varrec (poly f a) (MAP (\p. poly p a) (CONS f' l)) =
         varrec (poly f b) (MAP (\p. poly p b) (CONS f' l)))
Proof
  REPEAT STRIP_TAC >> MATCH_MP_TAC STURM_NOVAR_LEMMA >>
  MAP_EVERY EXISTS_TAC [`LENGTH(l:(real list) list)`, `c:real`] >>
  ASM_REWRITE_TAC[]
QED

Theorem STURM_VAR_DLEMMA:
 !p a b.
        a ≤ b ∧
        (!x. a <= x /\ x <= b ==> ~(poly p x = &0))
        ⇒ (!x. a <= x /\ x <= b ==> poly p x > &0) \/
            (!x. a <= x /\ x <= b ==> poly p x < &0)
Proof
  REPEAT GEN_TAC >> STRIP_TAC
  >> REPEAT_TCL DISJ_CASES_THEN ASSUME_TAC (SPEC `poly p a` REAL_LT_NEGTOTAL)
  >- (
    FIRST_ASSUM(Tactic.UNDISCH_TAC o check is_forall o concl)
    >> DISCH_THEN(MP_TAC o SPEC `a:real`) >> ASM_REWRITE_TAC[REAL_LE_REFL]
  )
  >-(
    DISJ1_TAC >> REPEAT STRIP_TAC >> REWRITE_TAC[real_gt]
    >> SUBGOAL_THEN `~(poly p x = &0) /\ ~(poly p x < &0)` MP_TAC
    >- (
      CONJ_TAC
      >- ( FIRST_ASSUM MATCH_MP_TAC >> ASM_REWRITE_TAC[REAL_LE_REFL])
      >> DISCH_TAC >> MP_TAC(SPEC `p:real list` POLY_IVT_NEG)
      >> DISCH_THEN(MP_TAC o SPECL [`a:real`, `x:real`])
      >> ASM_REWRITE_TAC[real_gt, NOT_IMP, NOT_EXISTS_THM, REAL_LT_REFL]
      >> jrhUtils.GEN_REWR_TAC LAND_CONV [REAL_LT_LE]
      >> ASM_REWRITE_TAC[]
      >> ASM_CASES_TAC “a:real = x”
      >- mesonLib.ASM_MESON_TAC[REAL_LT_ANTISYM]
      >> ASM_REWRITE_TAC[]
      >>  ‘ (¬∃x'. a < x' ∧ x' < x ∧ poly p x' = 0) ⇔
              ∀x'. ~(a < x' ∧ x' < x ∧ poly p x'=0)’ by
        (gs[NOT_EXISTS_THM])
      >> pop_assum $ rewrite_tac o single
      >> X_GEN_TAC “y:real”
      >> REWRITE_TAC[tautLib.TAUT `~(a /\ b /\ c) <=> a /\ b ==> ~c`]
      >> DISCH_TAC
      >> FIRST_ASSUM MATCH_MP_TAC
      >> UNDISCH_TAC `a:real < y:real /\ y:real < x:real`
      >> UNDISCH_TAC `x:real <= b:real`
      >> REAL_ARITH_TAC
    )
    >> REAL_ARITH_TAC
  )
  >> DISJ2_TAC >> REPEAT STRIP_TAC
  >> SUBGOAL_THEN `~(poly p x = &0) /\ ~(poly p x > &0)` MP_TAC
  >- (
    CONJ_TAC
    >- ( FIRST_ASSUM MATCH_MP_TAC >> ASM_REWRITE_TAC[REAL_LE_REFL] )
    >> DISCH_TAC >> MP_TAC(SPEC `p:real list` POLY_IVT_POS)
    >> DISCH_THEN(MP_TAC o SPECL [`a:real`, `x:real`])
    >> RULE_ASSUM_TAC(Ho_Rewrite.REWRITE_RULE
                        [RealArith.REAL_ARITH “&0 < - x <=> x:real < &0”])
    >> ASM_REWRITE_TAC[real_gt, NOT_IMP, NOT_EXISTS_THM, REAL_LT_REFL]
    >> GEN_REWRITE_TAC LAND_CONV [REAL_LT_LE]
    >> ASM_REWRITE_TAC[]
    >> ASM_CASES_TAC “a:real = x”
    >-  mesonLib.ASM_MESON_TAC[REAL_LT_ANTISYM, real_gt]
    >> ASM_REWRITE_TAC[]
    >> ‘(¬∃x'. a < x' ∧ x' < x ∧ poly p x' = 0) ⇔
          ∀x'. ~(a < x' ∧ x' < x ∧ poly p x'=0)’
      by (gs[NOT_EXISTS_THM])
    >> pop_assum $ rewrite_tac o single
    >> X_GEN_TAC “y:real”
    >> REWRITE_TAC[tautLib.TAUT `~(a /\ b /\ c) <=> a /\ b ==> ~c`]
    >> DISCH_TAC >> FIRST_ASSUM MATCH_MP_TAC
    >> UNDISCH_TAC `a:real < y:real /\ y:real < x:real`
    >> UNDISCH_TAC `x:real <= b:real`
    >> REAL_ARITH_TAC
  )
  >> REAL_ARITH_TAC
QED

Theorem  STURM_VAR_LEMMA:
 !l f f' c.
        rsquarefree f /\
        (f' = diff f) /\
        STURM f f' l /\
        a < c /\ c < b /\
        (!x. a <= x /\ x <= b /\ EXISTS (\p. poly p x = &0) (CONS f (CONS f' l))
             ==> (x = c)) /\
        (poly f c = &0)
        ==> poly f a * poly f' a < &0 /\
            poly f b * poly f' b > &0
Proof
  REPEAT GEN_TAC >> STRIP_TAC
  >> SUBGOAL_THEN `a:real <= b:real` ASSUME_TAC
  >- (
    UNDISCH_TAC `a:real < c:real` >> UNDISCH_TAC `c:real < b:real`
    >> REAL_ARITH_TAC
  )
  >> SUBGOAL_THEN `!x. a <= x /\ x <= b ==> ~(poly (diff f) x = &0)` ASSUME_TAC
  >- (
    X_GEN_TAC “x:real” >> STRIP_TAC
    >> FIRST_ASSUM(MP_TAC o GEN_REWRITE_RULE I empty_rewrites [RSQUAREFREE_ROOTS])
    >> DISCH_THEN(MP_TAC o SPEC `c:real`) >>  ASM_REWRITE_TAC[]
    >> rpt strip_tac >> ‘x=c’ by (first_x_assum irule >> gs[EXISTS_DEF])
    >> rpt BasicProvers.VAR_EQ_TAC
    >> mesonLib.ASM_MESON_TAC[]
  )
  >> MP_TAC(SPECL [`diff f`, `a:real`, `b:real`] STURM_VAR_DLEMMA)
  >> ASM_REWRITE_TAC[] >> DISCH_THEN DISJ_CASES_TAC
  >- (
    CONJ_TAC
    >- (
      rewrite_tac[GSYM realTheory.REAL_NEG_GT0]
      >> MP_TAC(SPECL [`f:real list`, `a:real`, `c:real`] POLY_MVT)
      >> ASM_REWRITE_TAC[REAL_SUB_LZERO]
      >> DISCH_THEN(X_CHOOSE_THEN “x:real” STRIP_ASSUME_TAC)
      >> rewrite_tac[GSYM REAL_MUL_LNEG]
      >> MATCH_MP_TAC REAL_LT_MUL
      >> CONJ_TAC
      >- (
        pop_assum $ rewrite_tac o single
        >> MATCH_MP_TAC REAL_LT_MUL
        >> CONJ_TAC
        >- ( rewrite_tac[ REAL_SUB_LT] >> rw[] )
        >> REWRITE_TAC[REAL_ARITH `&0 < x <=> x:real > &0`]
        >> first_assum irule >> rw[REAL_LT_IMP_LE]
        >> UNDISCH_TAC `x:real < c:real`
        >> UNDISCH_TAC `c:real < b:real` >> REAL_ARITH_TAC
      )
      >> REWRITE_TAC[REAL_ARITH `&0 < x <=> x:real > &0`]
      >> first_assum irule
      >> CONJ_TAC
      >- ASM_REWRITE_TAC[REAL_LE_REFL]
      >> rw[]
    )
    >> MP_TAC(SPECL [`f:real list`, `c:real`, `b:real`] POLY_MVT)
    >> ASM_REWRITE_TAC[REAL_SUB_RZERO]
    >> DISCH_THEN(X_CHOOSE_THEN “x:real” STRIP_ASSUME_TAC)
    >> ‘∀x. &0<x:real ⇒ x > &0’ by rw[real_gt]
    >> first_assum irule >>  MATCH_MP_TAC REAL_LT_MUL
    >> CONJ_TAC
    >- (
      qpat_assum ‘poly f b = (b − c) * poly (diff f) x’$ rewrite_tac o single
      >> MATCH_MP_TAC REAL_LT_MUL
      >> CONJ_TAC
      >- (
        UNDISCH_TAC `c:real < x:real`
        >> UNDISCH_TAC `x:real < b:real` >> REAL_ARITH_TAC
      )
      >> REWRITE_TAC[REAL_ARITH `&0 < x <=> x:real > &0`]
      >> pop_assum $ kall_tac
      >> first_assum irule
      >> CONJ_TAC
      >- (
        UNDISCH_TAC `a:real < c:real` >> UNDISCH_TAC `c:real < x:real`
        >> REAL_ARITH_TAC
      )
      >> rw[REAL_LT_IMP_LE]
    )
    >> REWRITE_TAC[REAL_ARITH `&0 < x:real <=> x > &0`]
    >> pop_assum $ kall_tac
    >> first_assum irule
    >> CONJ_TAC
    >- rw[]
    >> ASM_REWRITE_TAC[REAL_LE_REFL]
  )
  >> CONJ_TAC
  >- (
    rewrite_tac[GSYM realTheory.REAL_NEG_GT0]
    >> MP_TAC(SPECL [`f:real list`, `a:real`, `c:real`] POLY_MVT)
    >> ASM_REWRITE_TAC[REAL_SUB_LZERO]
    >> DISCH_THEN(X_CHOOSE_THEN “x:real” STRIP_ASSUME_TAC)
    >> rewrite_tac[REAL_NEG_RMUL]
    >> MATCH_MP_TAC REAL_LT_MUL
    >> CONJ_TAC
    >- (
      rewrite_tac[GSYM realTheory.REAL_NEG_LT0]
      >> pop_assum $ rewrite_tac o single
      >> rewrite_tac[GSYM realTheory.REAL_NEG_GT0]
      >> rewrite_tac[REAL_NEG_RMUL]
      >> MATCH_MP_TAC REAL_LT_MUL
      >> CONJ_TAC
      >- ( rewrite_tac[REAL_SUB_LT] >> rw[] )
      >> rewrite_tac[realTheory.REAL_NEG_GT0]
      >> first_assum irule
      >> CONJ_TAC
      >-  rw[REAL_LT_IMP_LE]
      >> UNDISCH_TAC `x:real < c:real`
      >> UNDISCH_TAC `c:real < b:real` >> REAL_ARITH_TAC
    )
    >> rewrite_tac[realTheory.REAL_NEG_GT0]
    >> first_assum irule
    >> CONJ_TAC
    >-  ASM_REWRITE_TAC[REAL_LE_REFL]
    >> rw[]
  )
  >> rewrite_tac[real_gt]
  >> ONCE_REWRITE_TAC[REAL_ARITH `&0 < (x:real) * (y:real) <=> &0 < -x * - y` ]
  >> MP_TAC(SPECL [`f:real list`, `c:real`, `b:real`] POLY_MVT)
  >> ASM_REWRITE_TAC[REAL_SUB_RZERO]
  >> DISCH_THEN(X_CHOOSE_THEN “x:real” STRIP_ASSUME_TAC)
  >> MATCH_MP_TAC REAL_LT_MUL
  >> CONJ_TAC
  >- (
    pop_assum $ rewrite_tac o single
    >> rewrite_tac[REAL_NEG_RMUL]
    >> MATCH_MP_TAC REAL_LT_MUL
    >> CONJ_TAC
    >- ( rewrite_tac[REAL_SUB_LT] >> rw[] )
    >> rewrite_tac[realTheory.REAL_NEG_GT0]
    >> first_assum irule
    >> CONJ_TAC
    >- (
      UNDISCH_TAC `a:real < c:real` >> UNDISCH_TAC `c:real < x:real`
      >> REAL_ARITH_TAC
    )
    >> rw[REAL_LT_IMP_LE]
  )
  >> rewrite_tac[realTheory.REAL_NEG_GT0]
  >> first_assum irule
  >> CONJ_TAC
  >- rw[]
  >> ASM_REWRITE_TAC[REAL_LE_REFL]
QED

Theorem  STURM_VAR:
 !l f f' c.
        rsquarefree f /\
        (f' = diff f) /\
        STURM f f' l /\
        a < c /\ c < b /\
        (!x. a <= x /\ x <= b /\ EXISTS (\p. poly p x = &0) (CONS f (CONS f' l))
             ==> (x = c)) /\
        (poly f c = &0)
        ==> (varrec (poly f a) (MAP (\p. poly p a) (CONS f' l)) =
             SUC(varrec (poly f b) (MAP (\p. poly p b) (CONS f' l))))
Proof
  ListConv1.LIST_INDUCT_TAC
  >- (
    REPEAT GEN_TAC
    >> DISCH_THEN(STRIP_ASSUME_TAC o MATCH_MP STURM_VAR_LEMMA)
    >> ASM_REWRITE_TAC[listTheory.MAP, varrec_def]
    >> ‘~(poly f b * poly f' b < &0) /\ ~(poly f' b = &0)’ by
      (
        UNDISCH_TAC `poly f b * poly f' b > &0`
        >> ASM_CASES_TAC “poly f' b = &0”
        >- ASM_REWRITE_TAC[real_gt, REAL_MUL_LZERO, REAL_MUL_RZERO, REAL_LT_REFL]
        >> ASM_REWRITE_TAC[] >> REAL_ARITH_TAC
      )
    >> gs[]
  )
  >> REPEAT GEN_TAC >> DISCH_TAC
  >> FIRST_ASSUM(STRIP_ASSUME_TAC o MATCH_MP STURM_VAR_LEMMA)
  >> ONCE_REWRITE_TAC[listTheory.MAP] >> REWRITE_TAC[varrec_def]
  >> ‘poly f a * poly f' a < &0 /\
                  ~(poly f b * poly f' b < &0) /\
                  ~(poly f' a = &0) /\ ~(poly f' b = &0)’ by
    (
      UNDISCH_TAC `poly f b * poly f' b > &0`
      >> UNDISCH_TAC `poly f a * poly f' a < &0`
      >> POP_ASSUM_LIST(K ALL_TAC)
      >> ASM_CASES_TAC “poly f' a = &0”
      >- ASM_REWRITE_TAC
          [real_gt, REAL_MUL_LZERO, REAL_MUL_RZERO, REAL_LT_REFL]
      >> ASM_CASES_TAC “poly f' b = &0”
      >- ASM_REWRITE_TAC
            [real_gt, REAL_MUL_LZERO, REAL_MUL_RZERO, REAL_LT_REFL]
      >> rpt strip_tac
      >- metis_tac[]
      >> assume_tac REAL_LT_ANTISYM
      >> pop_assum $ qspecl_then [‘ poly f b * poly f' b’, ‘&0’] assume_tac
      >> ‘(poly f b * poly f' b < 0 ∧ 0 < poly f b * poly f' b) ⇔ F’
         by metis_tac[]
      >> first_assum match_mp_tac
      >> CONJ_TAC
      >- metis_tac[]
      >> rewrite_tac[GSYM real_gt] >> metis_tac[]
    )
  >> ‘poly f a * (λp. poly p a) f' < 0 ⇔ T’ by metis_tac[]
  >> pop_assum $ rewrite_tac o single
  >> ‘ poly f b * (λp. poly p b) f' < 0 ⇔ F’ by metis_tac[]
  >> pop_assum $ rewrite_tac o single
  >> ‘(λp. poly p b) f' = 0 ⇔ F’ by metis_tac[]
  >> pop_assum $ rewrite_tac o single
  >> AP_TERM_TAC
  >> qpat_x_assum `_ /\ _ /\ _` mp_tac >> rpt strip_tac
  >> `STURM f' h l` by
    (
      qpat_x_assum `STURM _ _ _` $ mp_tac o REWRITE_RULE [STURM_def]
      >> metis_tac[]
    )
  >> pop_assum $ mp_then Any mp_tac STURM_NOVAR >> BETA_TAC
  >> disch_then irule
  >> EXISTS_TAC `c:real`
  >> REPEAT CONJ_TAC
  >- metis_tac[EXISTS_DEF]
  >- (
    MP_TAC(SPEC `f:real list` RSQUAREFREE_ROOTS)
    >> ASM_REWRITE_TAC[]
    >> DISCH_THEN(MP_TAC o SPEC `c:real`) >> ASM_REWRITE_TAC[]
  )
  >> MATCH_MP_TAC REAL_LT_IMP_LE >> ASM_REWRITE_TAC[]
QED

(* ------------------------------------------------------------------------- *)
(* The main lemma.                                                           *)
(* ------------------------------------------------------------------------- *)

Theorem STURM_COMPONENT:
 !l f a b c.
        rsquarefree f /\
        STURM f (diff f) l /\
        a <= c /\ c <= b /\
        ~(poly f a = &0) /\
        ~(poly f b = &0) /\
        (!x. a <= x /\ x <= b /\
             EXISTS (\p. poly p x = &0) (CONS f (CONS (diff f) l))
             ==> (x = c))
        ==> (variation (MAP (\p. poly p a) (CONS f (CONS (diff f) l))) =
                if poly f c = &0 then
                    SUC(variation (MAP (\p. poly p b)
                                       (CONS f (CONS (diff f) l))))
                else variation (MAP (\p. poly p b)
                                (CONS f (CONS (diff f) l))))
Proof
  REPEAT STRIP_TAC >> REWRITE_TAC[variation_def]
  >> ONCE_REWRITE_TAC[rich_listTheory.MAP]
  >> REWRITE_TAC[varrec_def, REAL_MUL_LZERO, REAL_MUL_RZERO, REAL_LT_REFL]
  >> ASM_CASES_TAC “poly f c = &0”
  >- (
    ASM_REWRITE_TAC[]
    >> ‘((λp. poly p a) f = 0) ⇔ F’ by metis_tac[]
    >> pop_assum $ rewrite_tac o single
    >> ‘ (λp. poly p b) f = 0 ⇔ F’ by metis_tac[]
    >> pop_assum $ rewrite_tac o single
    >> ‘((λp. poly p a) f) =  poly f a’ by metis_tac[]
    >> ‘((λp. poly p b) f) =  poly f b’ by metis_tac[]
    >> ASM_REWRITE_TAC[] >> irule STURM_VAR
    >> rpt strip_tac
    >- metis_tac[]
    >- metis_tac[]
    >- (
      EXISTS_TAC `c:real`
      >> CONJ_TAC
      >- metis_tac[]
      >> CONJ_TAC
      >- metis_tac[]
      >> CONJ_TAC
      >- ( ASM_REWRITE_TAC[REAL_LT_LE] >> metis_tac[] )
      >> ASM_REWRITE_TAC[REAL_LT_LE] >> metis_tac[]
    )
    >> metis_tac[]
  )
  >> ‘((λp. poly p a) f = 0) ⇔ F’ by metis_tac[]
  >> pop_assum $ rewrite_tac o single
  >> ‘ (λp. poly p b) f = 0 ⇔ F’ by metis_tac[]
  >> pop_assum $ rewrite_tac o single
  >> ‘((λp. poly p a) f) =  poly f a’ by metis_tac[]
  >> ‘((λp. poly p b) f) =  poly f b’ by metis_tac[]
  >> ‘poly f c = 0 ⇔ F’ by metis_tac[]
  >> pop_assum $ rewrite_tac o single
  >> ASM_REWRITE_TAC[] >> MATCH_MP_TAC STURM_NOVAR
  >> EXISTS_TAC ‘c:real’
  >> rpt strip_tac
  >- metis_tac[]
  >- metis_tac[]
  >- metis_tac[]
  >> first_x_assum irule >> metis_tac[]
QED

Theorem FINITE_UNION_IMP:
  FINITE s ∧ FINITE t ⇒ FINITE (s UNION t)
Proof
  gs[pred_setTheory.FINITE_UNION]
QED
(* ------------------------------------------------------------------------- *)
(* Roots of a list of polynomials (maybe in interval) is finite.             *)
(* ------------------------------------------------------------------------- *)

Theorem POLYS_ROOTS_FINITE_SET:
 !l. EVERY (\p. ~(poly p = poly [])) l ==>
     FINITE { x | EXISTS (\p. poly p x = &0) l }
Proof
  ListConv1.LIST_INDUCT_TAC
  >- (
    SUBGOAL_THEN `{ x | EXISTS (\p. poly p x = &0) [] } = {}`
    (fn th => REWRITE_TAC[polyTheory.POLY_ROOTS_FINITE_SET, th])
    >> REWRITE_TAC[pred_setTheory.EXTENSION, pred_setTheory.NOT_IN_EMPTY,
                listTheory.EXISTS_DEF]
    >> gs[]
  )
  >> SUBGOAL_THEN `{ x | EXISTS (\p. poly p x = &0) (CONS h t) } =
                  { x | poly h x = &0 } UNION { x | EXISTS (\p. poly p x = &0) t }`
                                        SUBST1_TAC
  >- (
    REWRITE_TAC[pred_setTheory.EXTENSION, pred_setTheory.IN_UNION,
                 listTheory.EXISTS_DEF]
    >> gs[]
  )
  >> REWRITE_TAC[listTheory.EVERY_DEF] >> STRIP_TAC
  >> STRIP_TAC >> gs[]
  >> ‘FINITE {x | poly h x = 0}’ by metis_tac[polyTheory.POLY_ROOTS_FINITE_SET]
  >> imp_res_tac FINITE_UNION_IMP
  >> rpt $ pop_assum mp_tac
  >> rewrite_tac[pred_setTheory.UNION_DEF, IN_DEF]
  >> simp[]
QED

Theorem  POLYS_INTERVAL_ROOTS_FINITE_SET:
 !l a b. EVERY (\p. ~(poly p = poly [])) l ==>
         FINITE { x | a <= x /\ x <= b /\ EXISTS (\p. poly p x = &0) l }
Proof
  REPEAT GEN_TAC >> DISCH_TAC
  >> MATCH_MP_TAC pred_setTheory.SUBSET_FINITE_I
  >> EXISTS_TAC `{ x | EXISTS (\p. poly p x = &0) l }`
  >> CONJ_TAC
  >- ( MATCH_MP_TAC POLYS_ROOTS_FINITE_SET >> ASM_REWRITE_TAC[] )
  >> REWRITE_TAC[pred_setTheory.SUBSET_DEF] >> gs[]
QED


(* ------------------------------------------------------------------------- *)
(* Proof that we can lay out a finite set in a linear sequence.              *)
(* ------------------------------------------------------------------------- *)

Theorem FINITE_LEAST:
  !(s:real -> bool). FINITE s ==> (s = {}) \/ ?a. a IN s /\ !x. x IN s ==> a <= x
Proof
  qspec_then ‘\s:real->bool. s = ∅ ∨ ∃a. a ∈ s ∧ ∀x. x ∈ s ⇒ a ≤ x’
             mp_tac pred_setTheory.FINITE_INDUCT
  >> reverse impl_tac
  >- gs[]
  >> rpt conj_tac
  >> simp[]
  >> rpt strip_tac
  >- (
    EXISTS_TAC ‘e:real’ >> CONJ_TAC
    >- ( DISJ1_TAC >> gs[] )
    >> strip_tac >> gs[]
  )
  >> DISJ_CASES_TAC(SPECL [`a:real`, `e:real`] REAL_LE_TOTAL)
  >- ( qexists_tac ‘a’ >> gs[] >> rpt strip_tac >> gs[] )
  >> qexists_tac ‘e’ >> gs[] >> rpt strip_tac >> gs[]
  >> mesonLib.ASM_MESON_TAC[REAL_LE_TRANS, REAL_LE_REFL]
QED


Theorem FINITE_LEAST_DELETE :
 !(s:real -> bool). s HAS_SIZE (SUC n)
       ==> ?a. a IN s /\ (s DELETE a) HAS_SIZE n /\
               !x. x IN (s DELETE a) ==> a < x
Proof
  GEN_TAC >> DISCH_THEN(fn th => MP_TAC th >> MP_TAC th)
  >> DISCH_THEN(STRIP_ASSUME_TAC o REWRITE_RULE[pred_setTheory.HAS_SIZE_SUC])
  >> REWRITE_TAC[pred_setTheory.HAS_SIZE]
  >> DISCH_THEN(MP_TAC o MATCH_MP FINITE_LEAST o CONJUNCT1)
  >> ASM_REWRITE_TAC[]
  >> DISCH_THEN(X_CHOOSE_THEN “a:real” STRIP_ASSUME_TAC)
  >> EXISTS_TAC `a:real` >> ASM_REWRITE_TAC[]
  >> REWRITE_TAC[GSYM pred_setTheory.HAS_SIZE, pred_setTheory.IN_DELETE]
  >> REWRITE_TAC[REAL_LT_LE] >> mesonLib.ASM_MESON_TAC[]
QED

Theorem HAS_SIZE_ORDER:
 !n (s: real->bool). s HAS_SIZE n ==>
         ?i. (!x. x IN s <=> ?k. k < n /\ (x = i k)) /\
             (!k. i k < i (SUC k))
Proof
  Induct_on ‘n’
  >- (
    GEN_TAC >> REWRITE_TAC[pred_setTheory.HAS_SIZE_0]
    >> DISCH_THEN SUBST1_TAC
    >> REWRITE_TAC[pred_setTheory.NOT_IN_EMPTY] >> gs[]
    >> EXISTS_TAC `real_of_num` >> strip_tac >> gs[REAL_LT]
  )
  >> GEN_TAC >> DISCH_THEN(MP_TAC o MATCH_MP FINITE_LEAST_DELETE)
  >> DISCH_THEN(X_CHOOSE_THEN “a:real” (CONJUNCTS_THEN2 ASSUME_TAC MP_TAC))
  >> DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC)
  >> DISCH_THEN(fn th => ASSUME_TAC th >> MP_TAC th)
  >> DISCH_THEN(ANTE_RES_THEN MP_TAC)
  >> DISCH_THEN(X_CHOOSE_THEN “i:num->real”  STRIP_ASSUME_TAC)
  >> ASM_CASES_TAC “(n:num)=0”
  >- (
    EXISTS_TAC `\(k:num). a + (&k:real)` >> gs[] >> rpt strip_tac >> eq_tac
    >- (
      strip_tac >> EXISTS_TAC ‘(n:num)’ >> CONJ_TAC
      >- gs[]
      >> gs[] >> metis_tac[]
    )
    >> strip_tac >> gs[]
    >> ‘(k:num)=0’ by gs[] >> pop_assum $ rewrite_tac o single >> gs[]
  )
  >> EXISTS_TAC `\k. if k = 0 then a:real else i(PRE k)`
  >> REWRITE_TAC[] >> CONJ_TAC
  >- (
    X_GEN_TAC “x:real” >> ASM_CASES_TAC “x:real = a”
    >- (
      UNDISCH_TAC `x:real = a` >> DISCH_THEN SUBST_ALL_TAC
      >> ASM_REWRITE_TAC[] >> EXISTS_TAC `0:num`
      >> CONJ_TAC
      >- gs[]
      >> gs[]
    )
    >> SUBGOAL_THEN `x IN s <=> x IN (s DELETE a:real)` SUBST1_TAC
    >- ( REWRITE_TAC[IN_DELETE] >> ASM_REWRITE_TAC[] )
    >> ASM_REWRITE_TAC[] >> EQ_TAC
    >- (
      DISCH_THEN(X_CHOOSE_THEN “k:num” STRIP_ASSUME_TAC)
      >> EXISTS_TAC `SUC k`
      >> CONJ_TAC
      >- gs[arithmeticTheory.LT_SUC]
      >> gs[]
    )
    >> rpt strip_tac
    >> EXISTS_TAC `PRE k`
    >> `x = (if k = 0 then a:real else i (PRE k))` by gs[]
    >> UNDISCH_TAC `x = (if k = 0 then a:real else i (PRE k))`
    >> UNDISCH_TAC `k < SUC n`
    >> SPEC_TAC(“k:num”,“k:num”)
    >> rpt strip_tac
    >- (
      Induct_on ‘k'’
      >- gs[]
      >> rpt strip_tac >> gs[]
    )
    >> Induct_on ‘k'’
    >- gs[]
    >> rpt strip_tac >> gs[]
  )
  >> rpt strip_tac >> gs[]
  >> ASM_CASES_TAC “(k:num)=0”
  >- (
    gs[] >> first_assum match_mp_tac
    >> EXISTS_TAC `0:num` >> ASM_REWRITE_TAC[]
    >> gs[]
  )
  >> gs[]
  >> qpat_assum ‘ ∀k. i k < i (SUC k)’ $ qspec_then ‘PRE k’ assume_tac
  >> ‘SUC(PRE k) = k’ by gs[]
  >> ‘i k = i (SUC (PRE k))’ by
    ( pop_assum $ rewrite_tac o single)
  >> pop_assum $ rewrite_tac o single >> metis_tac[]
QED

Theorem FINITE_ORDER :
 !(s:real -> bool). FINITE s ==>
         ?i N. (!x. x IN s <=> ?k. k < N /\ (x = i k)) /\
               (!k. i k < i (SUC k))
Proof
  REPEAT STRIP_TAC
  >> GEN_EXISTS_TAC "N" `CARD(s:real->bool)`
  >> MATCH_MP_TAC HAS_SIZE_ORDER
  >> ASM_REWRITE_TAC[HAS_SIZE]
QED

(* ------------------------------------------------------------------------- *)
(* We can enumerate the roots in order.                                      *)
(* ------------------------------------------------------------------------- *)

Theorem POLYS_ENUM_ROOTS :
 !l. EVERY (\p. ~(poly p = poly [])) l
       ==> ?i N. (!k. i k < i (SUC k)) /\
                 (!x. a <= x /\ x <= b /\ EXISTS (\p. poly p x = &0) l <=>
                      ?n:num. n < N /\ (x = i n))
Proof
  GEN_TAC
  >> DISCH_THEN (MP_TAC o MATCH_MP POLYS_INTERVAL_ROOTS_FINITE_SET)
  >> DISCH_THEN(MP_TAC o SPECL [`a:real`, `b:real`])
  >> DISCH_THEN(MP_TAC o MATCH_MP FINITE_ORDER) >> strip_tac
  >> qexists_tac ‘i’ >> qexists_tac ‘N’ >> gs[]
QED


(* ------------------------------------------------------------------------- *)
(* Hence we can get separating intervals for the various roots.              *)
(* ------------------------------------------------------------------------- *)

Theorem lemma0[local]:
  (!x y. x * inv(&2) <= y <=> x <= &2 * y) ∧
  (!x y. x <= y * inv(&2) <=> &2 * x <= y)
Proof
  rpt strip_tac
  >- (
    ‘&2*y = y* &2’ by metis_tac[REAL_MUL_COMM]
    >> pop_assum $ rewrite_tac o single
    >> ‘x* inv(&2) = x / &2’ by gs[GSYM real_div]
    >> pop_assum $ rewrite_tac o single
    >> irule REAL_LE_LDIV_EQ >> gs[]
  )
  >> ‘&2 * x = x * &2’ by metis_tac[REAL_MUL_COMM]
  >> pop_assum $ rewrite_tac o single
  >> ‘y * inv(&2) = y / &2’ by  gs[GSYM real_div]
  >> pop_assum $ rewrite_tac o single
  >> irule REAL_LE_RDIV_EQ >> gs[]
QED


Theorem lemma1[local]:
 ∀x1 x2:real. a <= x1 /\ x1 < x2 ==> a <= (x1 + x2) / &2
Proof
  REWRITE_TAC[real_div, lemma0] >> REAL_ARITH_TAC
QED

Theorem lemma2[local]:
  ∀ x0 x1:real. x0 < x1 /\ x1 < x2 ==> (x0 + x1) / &2 <= (x1 + x2) / &2
Proof
  REWRITE_TAC[real_div, lemma0, REAL_MUL_ASSOC]
  >> REAL_ARITH_TAC
QED

Theorem lemma3[local]:
 ∀x1 x2:real.  x1 < x2 /\ x2 <= b ==> (x1 + x2) / &2 <= b
Proof
  REWRITE_TAC[real_div, lemma0] >> REAL_ARITH_TAC
QED

Theorem lemma8a[local]:
  (!k. (i: num -> real) k < i(SUC k)) ==> !m n. m < n ==> i m < i n
Proof
  STRIP_TAC >> GEN_TAC
  >> rpt strip_tac
  >> SUBGOAL_THEN ‘∃ (p:num). n = m + (p+1)’ MP_TAC
  >- ( irule arithmeticTheory.LESS_ADD_1 >> gs[] )
  >> gs[boolTheory.PULL_EXISTS]
  >> REWRITE_TAC[GSYM arithmeticTheory.ADD1]
  >> Q.SPEC_TAC (`n`, `n`) >> Q.SPEC_TAC (`m`, `m`)
  >> Induct_on ‘p’
  >- ( REWRITE_TAC[arithmeticTheory.ADD_CLAUSES] >> gs[] )
  >> REWRITE_TAC[arithmeticTheory.ADD_CLAUSES]
  >> rpt STRIP_TAC
  >> MATCH_MP_TAC REAL_LT_TRANS
  >> EXISTS_TAC `i(SUC(m' + p)):real` >> gs[]
  >> ‘SUC (m' + p) = m' + SUC p’ by REWRITE_TAC[arithmeticTheory.ADD_CLAUSES]
  >> pop_assum $ once_rewrite_tac o single >> first_x_assum irule
QED

Theorem lemma8b[local]:
  (!k. (i: num->real) k < i(SUC k)) ==> !m n. i m < i n <=> m < n
Proof
  DISCH_THEN(MP_TAC o MATCH_MP lemma8a)
  >> mesonLib.MESON_TAC[REAL_LT_REFL, REAL_LT_ANTISYM,
                        arithmeticTheory.LESS_LESS_CASES]
QED

Theorem lemma8[local]:
  (!k. (i: num->real) k < i(SUC k)) ==> !m n. (i m <= i n) <=> m <= n
Proof
  DISCH_THEN(MP_TAC o MATCH_MP lemma8b)
  >> REWRITE_TAC[GSYM arithmeticTheory.NOT_LESS_EQUAL, GSYM REAL_NOT_LE]
  >> mesonLib.MESON_TAC[]
QED

Theorem lemma5[local]:
  (!k. (i: num->real) k < i(SUC k)) ==> !k n. i k <= (i n + i(SUC n)) / &2
                                 ==> k <= n
Proof
  DISCH_TAC
  >> REWRITE_TAC[arithmeticTheory.LESS_EQ_IFF_LESS_SUC]
  >> FIRST_ASSUM(fn th => REWRITE_TAC[GSYM(MATCH_MP lemma8b th)])
  >> REPEAT GEN_TAC >> REWRITE_TAC[real_div, lemma0]
  >> POP_ASSUM(MP_TAC o SPEC `n:num`) >> REAL_ARITH_TAC
QED

Theorem lemma6[local]:
  (!k. (i:num->real) k < i(SUC k)) ==> !k n. (i k + i (SUC k)) / &2 <= i n
                                 ==> SUC k <= n
Proof
  DISCH_TAC >> REWRITE_TAC[GSYM arithmeticTheory.LESS_EQ]
  >> FIRST_ASSUM(fn th => REWRITE_TAC[GSYM(MATCH_MP lemma8b th)])
  >> REPEAT GEN_TAC >> REWRITE_TAC[real_div, lemma0]
  >> POP_ASSUM(MP_TAC o SPEC `k:num`) >> REAL_ARITH_TAC
QED

Theorem lemma7[local]:
  (!k. (i: num->real) k < i(SUC k)) ==> !k n. (i n + i(SUC n)) / &2 <= i k /\
                                 i k <= (i(SUC n) + i(SUC(SUC n))) / &2
                                 ==> (k = SUC n)
Proof
  REPEAT STRIP_TAC
  >> irule arithmeticTheory.LESS_EQUAL_ANTISYM
  >> CONJ_TAC
  >- ( FIRST_ASSUM(MATCH_MP_TAC o MATCH_MP lemma6) >> gs[] )
  >> FIRST_ASSUM(MATCH_MP_TAC o MATCH_MP lemma5) >> gs[]
QED

Theorem lemma4[local]:
  (!k. (i:num->real) k < i(SUC k)) ==> !k n. ~((i k + i (SUC k)) / &2 = i n)
Proof
  DISCH_TAC >> REPEAT GEN_TAC
  >> ONCE_REWRITE_TAC[GSYM REAL_LE_ANTISYM] >> STRIP_TAC
  >> SUBGOAL_THEN `~(SUC k <= k)` MP_TAC
  >-  gs[]
  >> REWRITE_TAC[]
  >> MATCH_MP_TAC arithmeticTheory.LESS_EQ_TRANS
  >> EXISTS_TAC `n:num`
  >> CONJ_TAC
  >- ( FIRST_ASSUM(MATCH_MP_TAC o MATCH_MP lemma6) >> gs[] )
  >> FIRST_ASSUM(MATCH_MP_TAC o MATCH_MP lemma5) >> gs[]
QED

Theorem POLYS_INTERVAL_SEPARATION:
 !f l a b.
       a <= b /\
       EVERY (\p. ~(poly p = poly [])) l /\
       ~(poly f a = &0) /\
       ~(poly f b = &0)
       ==> ?i N. (i 0 = a) /\ (i N = b) /\
                 (!k. i(k) <= i(SUC k)) /\
                 (!k. k <= N ==> ~(poly f (i k) = &0)) /\
                 (!k. ?c. i(k) <= c /\ c <= i(SUC k) /\
                          !x. i(k) <= x /\ x <= i(SUC k) /\
                              EXISTS (\p. poly p x = &0) (CONS f l)
                              ==> (x = c))
Proof
  REPEAT STRIP_TAC
  >> SUBGOAL_THEN `EVERY (\p. ~(poly p = poly [])) (CONS f l)` MP_TAC
  >- (
    ASM_REWRITE_TAC[EVERY_DEF] >> UNDISCH_TAC `~(poly f a = &0)`
    >> CONV_TAC CONTRAPOS_CONV >> REWRITE_TAC[] >> gs[]
    >> REWRITE_TAC[poly_def]
  )
  >> DISCH_THEN(MP_TAC o MATCH_MP POLYS_ENUM_ROOTS)
  >> DISCH_THEN(X_CHOOSE_THEN “i:num->real” MP_TAC)
  >> DISCH_THEN(X_CHOOSE_THEN “N:num” STRIP_ASSUME_TAC)
  >> EXISTS_TAC `\n. if n = 0 then a:real else if n < N then
                       (i(PRE n) + i(n)) / &2 else b`
  >> EXISTS_TAC `SUC N` >> gs[]
  >> REPEAT CONJ_TAC
  >- (
    GEN_TAC >> gs[]
    >> ASM_CASES_TAC “k:num=0”
    >- (
      ASM_CASES_TAC  “k<N:num”
      >- (
        ASM_CASES_TAC “SUC k < N”
        >> ASM_REWRITE_TAC[REAL_LE_REFL]
        >- (
          MATCH_MP_TAC lemma1 >> ASM_REWRITE_TAC[]
          >> FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) 0`)
          >> CONV_TAC CONTRAPOS_CONV
          >> DISCH_THEN(fn th => REWRITE_TAC[th])
          >> EXISTS_TAC `k:num` >> ASM_REWRITE_TAC[]
        )
      )
      >> gs[]
    )
    >> gs[] >> ASM_CASES_TAC “k<N:num”
    >- (
      ASM_CASES_TAC “SUC k < N”
      >- (
        gs[] >> ‘k = (SUC (PRE k))’ by gs[]
        >> ‘i k = i (SUC (PRE k))’ by metis_tac[]
        >> ‘i (PRE k) + i k = i k + i (PRE k)’ by real_tac
        >> ntac 2 $ pop_assum $ once_rewrite_tac o single
        >> gs[]
        >> last_assum $ qspec_then ‘PRE k’ assume_tac >> gs[]
        >> ‘i (PRE k) < i (SUC k)’ suffices_by real_tac
        >> irule REAL_LT_TRANS
        >> qexists_tac ‘i (SUC (PRE k))’ >> gs[]
        >> ‘(SUC (PRE k)) = k’ by gs[]
        >> pop_assum $ once_rewrite_tac o single
        >> gs[])
      >> asm_rewrite_tac[]
      >> MATCH_MP_TAC lemma3
      >> CONJ_TAC
      >- ( ‘k = (SUC (PRE k))’ by gs[] >> metis_tac[] )
      >> FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) k`)
      >> CONV_TAC CONTRAPOS_CONV
      >> DISCH_THEN(fn th => REWRITE_TAC[th])
      >> EXISTS_TAC `k:num` >> ASM_REWRITE_TAC[]
    )
    >> gs[]
  )
  >- (
    GEN_TAC >> ASM_CASES_TAC “k:num =0”
    >- (
      ASM_REWRITE_TAC[]
      >> DISCH_TAC >> gs[]
    )
    >> gs[]
    >> COND_CASES_TAC >> ASM_REWRITE_TAC[]
    >> DISCH_TAC
    >> FIRST_ASSUM(MP_TAC o SPEC `(i (PRE k) + i k) / &2`)
    >> SUBGOAL_THEN `a:real <= ((i:num->real) (PRE k) + i k) / &2 /\
                  (i (PRE k) + i k) / &2 <= b:real`
        (fn th => REWRITE_TAC[th])
    >- (
      CONJ_TAC
      >- (
        MATCH_MP_TAC lemma1
        >> CONJ_TAC
        >- (
          FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) (PRE k)`)
          >> CONV_TAC CONTRAPOS_CONV
          >> DISCH_THEN(fn th => REWRITE_TAC[th])
          >> EXISTS_TAC `(PRE k):num`
          >> gs[]
        )
        >> ‘k = (SUC (PRE k))’ by gs[]
        >> ‘i k = i (SUC (PRE k))’ by metis_tac[] >> gs[]
      )
      >> MATCH_MP_TAC lemma3
      >> CONJ_TAC
      >- ( ‘k = (SUC (PRE k))’ by gs[] >> metis_tac[] )
      >> FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) k`)
      >> CONV_TAC CONTRAPOS_CONV
      >> DISCH_THEN(fn th => REWRITE_TAC[th])
      >> EXISTS_TAC `k:num` >> ASM_REWRITE_TAC[]
    )
    >> CONV_TAC CONTRAPOS_CONV >> gs[] >> strip_tac
    >> gen_tac >> rewrite_tac[IMP_DISJ_THM]
    >> DISJ1_TAC
    >> ‘k= SUC(PRE k)’ by gs[]
    >> ‘i k = i (SUC (PRE k))’ by metis_tac[]
    >> pop_assum $ rewrite_tac o single
    >> irule lemma4 >> metis_tac[]
  )
  >> GEN_TAC >> gs[]
  >> ASM_CASES_TAC “k:num =0” >> ASM_REWRITE_TAC[]
  >- (
    ASM_CASES_TAC “N:num=0”
    >- (
      EXISTS_TAC `a:real` >> ASM_REWRITE_TAC[REAL_LE_REFL]
      >> rw[]
    )
    >> ASM_CASES_TAC “N= SUC 0” >> rw[]
    >- (
      EXISTS_TAC `(i:num->real) 0` >> gs[]
      >> REPEAT CONJ_TAC
      >- (
        FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) 0`)
        >> CONV_TAC CONTRAPOS_CONV
        >> DISCH_THEN(fn th => REWRITE_TAC[th])
        >> EXISTS_TAC `0:num` >> gs[]
      )
      >-(
        FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) 0`)
        >> CONV_TAC CONTRAPOS_CONV
        >> DISCH_THEN(fn th => REWRITE_TAC[th])
        >> EXISTS_TAC `0:num` >> gs[]
      )
      >> gen_tac >> strip_tac >> gs[]
      >> ‘n=0’ by gs[] >> metis_tac[]
    )
    >> SUBGOAL_THEN `SUC 0 < N` ASSUME_TAC
    >- (
      REWRITE_TAC[GSYM arithmeticTheory.NOT_LESS_EQUAL]
      >> gs[]
    )
    >> ASM_REWRITE_TAC[] >> EXISTS_TAC `(i:num->real) 0`
    >> SUBGOAL_THEN `a:real <= i 0 /\
                    (i:num ->real) 0 <= (i 0 + i (SUC 0)) / &2 /\
                    (i 0 + i (SUC 0)) / &2 <= b:real`
                                               STRIP_ASSUME_TAC
    >- (
      REPEAT CONJ_TAC
      >- (
        FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) 0`)
        >> CONV_TAC CONTRAPOS_CONV
        >> DISCH_THEN(fn th => REWRITE_TAC[th])
        >> EXISTS_TAC `0:num` >> rw[]
      )
      >- (
        MATCH_MP_TAC lemma1
        >> ASM_REWRITE_TAC[REAL_LE_REFL]
      )
      >> MATCH_MP_TAC lemma3 >> ASM_REWRITE_TAC[]
      >> FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) (SUC 0)`)
      >> CONV_TAC CONTRAPOS_CONV
      >> DISCH_THEN(fn th => REWRITE_TAC[th])
      >> EXISTS_TAC `SUC 0` >> ASM_REWRITE_TAC[]
    )
    >> ‘1 = SUC 0’ by gs[] >> pop_assum $ rewrite_tac o single
    >> ASM_REWRITE_TAC[] >> conj_tac >- gs[]
    >> X_GEN_TAC “x:real”
    >> ‘ (poly f x = 0 ∨ EXISTS (λp. poly p x = 0) l) =
         EXISTS (\p. poly p x = &0) (CONS f l) ’ by gs[]
    >> pop_assum $ rewrite_tac o single
    >> STRIP_TAC
    >> SUBGOAL_THEN `a <= x /\ x <= b /\
                    EXISTS (\p. poly p x = &0) (CONS f l)`
                                                MP_TAC
    >- (
      REPEAT CONJ_TAC
      >- ASM_REWRITE_TAC[]
      >- (
        MATCH_MP_TAC REAL_LE_TRANS
        >> EXISTS_TAC `((i:num->real) 0 + i (SUC 0)) / &2`
        >> gs[]
      )
      >> rw[]
    )
    >> first_assum $ qspec_then ‘x’ (fn th => let val (th1, th2) =
               EQ_IMP_RULE th in assume_tac th1 end)
    >> rpt strip_tac >> qpat_x_assum ‘_ ⇒ ∃ n. n < N ∧ x = i n’ mp_tac
    >> impl_tac
    >- (rpt conj_tac >> gs[])
    >> DISCH_THEN(X_CHOOSE_THEN “n:num” STRIP_ASSUME_TAC)
    >> ASM_REWRITE_TAC[] >> AP_TERM_TAC
    >> UNDISCH_TAC `2 * x:real <= (i 0 + i (SUC 0))`
    >> ASM_REWRITE_TAC[]
    >> FIRST_ASSUM(ASSUME_TAC o MATCH_MP lemma5)
    >> gs[]
    >> ‘1 = SUC 0’ by gs[]
    >> pop_assum $ rewrite_tac o single
    >> DISCH_THEN(ANTE_RES_THEN MP_TAC) >> gs[]
  )
  >> ASM_CASES_TAC “SUC k < N”
  >- (
    SUBGOAL_THEN `k < N:num` ASSUME_TAC
    >- gs[]
    >> ASM_REWRITE_TAC[]
    >> EXISTS_TAC `(i:num->real) k` >> REPEAT CONJ_TAC
    >- (
      MATCH_MP_TAC lemma3
      >> CONJ_TAC
      >- (
        ‘k = SUC (PRE k)’ by gs[]
        >> metis_tac[]
      )
      >> gs[]
    )
    >- ( MATCH_MP_TAC lemma1 >> gs[] )
    >>  X_GEN_TAC “x:real”
    >> ‘ (poly f x = 0 ∨ EXISTS (λp. poly p x = 0) l) =
         EXISTS (\p. poly p x = &0) (CONS f l) ’ by gs[]
    >> pop_assum $ rewrite_tac o single
    >> STRIP_TAC
    >> SUBGOAL_THEN `a <= x /\ x <= b /\
                       EXISTS (\p. poly p x = &0) (CONS f l)`
                              MP_TAC
    >- (
      REPEAT CONJ_TAC
      >- (
        MATCH_MP_TAC REAL_LE_TRANS
        >> EXISTS_TAC `((i:num->real) (PRE k) + i k) / &2`
        >> ASM_REWRITE_TAC[]
        >> MATCH_MP_TAC lemma1 >> CONJ_TAC
        >- (
          FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) (PRE k)`)
          >> CONV_TAC CONTRAPOS_CONV
          >> DISCH_THEN(fn th => REWRITE_TAC[th])
          >> EXISTS_TAC `PRE k` >> ASM_REWRITE_TAC[]
          >> UNDISCH_TAC `k < N:num` >> gs[]
        )
        >> ‘k= SUC (PRE k)’ by gs[]
        >> metis_tac[]
      )
      >- (
        MATCH_MP_TAC REAL_LE_TRANS
        >> EXISTS_TAC `((i:num->real) k + i(SUC k)) / &2`
        >> ASM_REWRITE_TAC[]
        >> MATCH_MP_TAC lemma3 >> ASM_REWRITE_TAC[]
        >> FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) (SUC k)`)
        >> CONV_TAC CONTRAPOS_CONV
        >> DISCH_THEN(fn th => REWRITE_TAC[th])
        >> EXISTS_TAC `SUC k` >> ASM_REWRITE_TAC[]
      )
      >> gs[]
    )
    >> first_assum $ qspec_then ‘x’ (fn th => let val (th1, th2) =
                 EQ_IMP_RULE th in assume_tac th1 end)
    >> rpt strip_tac >> qpat_x_assum ‘_ ⇒ ∃ n. n < N ∧ x = i n’ mp_tac
    >> impl_tac
    >- (rpt conj_tac >> gs[])
    >> DISCH_THEN(X_CHOOSE_THEN “n:num” STRIP_ASSUME_TAC)
    >> ASM_REWRITE_TAC[] >> AP_TERM_TAC
    >> SUBGOAL_THEN `((i:num->real) (PRE k) + i k) / &2 <= i n /\
                    i n <= (i k + i (SUC k)) / &2`
                                             MP_TAC
    >- (
      FIRST_ASSUM(UNDISCH_TAC o (fn t => `^t`) o check is_eq o concl)
      >> DISCH_THEN SUBST_ALL_TAC >> ASM_REWRITE_TAC[]
    )
    >> UNDISCH_TAC `~(k:num = 0)`
    >> SPEC_TAC(“k:num”,“m:num”)
    >> Induct_on ‘m’
    >- gs[]
    >> strip_tac >> ‘PRE (SUC m) = m’ by gs[]
    >> pop_assum $ rewrite_tac o single
    >> FIRST_ASSUM(MATCH_ACCEPT_TAC o MATCH_MP lemma7)
  )
  >> ASM_CASES_TAC “k< N:num” >> ASM_REWRITE_TAC[]
  >- (
    EXISTS_TAC `(i:num->real) k`
    >> ASM_REWRITE_TAC[CONJ_ASSOC]
    >> ‘ ((i (PRE k) + i k) / 2 ≤ i k ∧ i k ≤ b)’ by
      (
        CONJ_TAC
        >- (
          irule REAL_MIDDLE2
          >> ‘k= SUC (PRE k)’ by gs[]
          >> irule REAL_LT_IMP_LE >> metis_tac[]
        )
        >> FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) k`)
        >> CONV_TAC CONTRAPOS_CONV
        >> DISCH_THEN(fn th => REWRITE_TAC[th])
        >> EXISTS_TAC ‘k:num’ >> ASM_REWRITE_TAC[]
      )
    >> ASM_REWRITE_TAC[]
    >> X_GEN_TAC “x:real”
    >> ‘ (poly f x = 0 ∨ EXISTS (λp. poly p x = 0) l) =
         EXISTS (\p. poly p x = &0) (CONS f l) ’ by gs[]
    >> pop_assum $ rewrite_tac o single
    >> STRIP_TAC
    >> SUBGOAL_THEN `a <= x /\ x <= b /\
        EXISTS (\p. poly p x = &0) (CONS f l)`
              MP_TAC
    >- (
      REPEAT CONJ_TAC
      >- (
        MATCH_MP_TAC REAL_LE_TRANS
        >> EXISTS_TAC `((i:num->real) (PRE k) + i k) / &2`
        >> ASM_REWRITE_TAC[] >> MATCH_MP_TAC lemma1
        >> CONJ_TAC
        >- (
          FIRST_ASSUM(MP_TAC o SPEC `(i:num->real) (PRE k)`)
          >> CONV_TAC CONTRAPOS_CONV
          >> DISCH_THEN(fn th => REWRITE_TAC[th])
          >> EXISTS_TAC `PRE k` >> ASM_REWRITE_TAC[]
          >> UNDISCH_TAC `k < N:num` >> gs[]
        )
        >> ‘k= SUC (PRE k)’ by gs[] >> metis_tac[]
      )
      >- ASM_REWRITE_TAC[]
      >> metis_tac[]
    )
    >> first_assum $ qspec_then ‘x’ (fn th => let val (th1, th2) =
                       EQ_IMP_RULE th in assume_tac th1 end)
    >> rpt strip_tac >> qpat_x_assum ‘_ ⇒ ∃ n. n < N ∧ x = i n’ mp_tac
    >> impl_tac
    >- (rpt conj_tac >> gs[])
    >> DISCH_THEN(X_CHOOSE_THEN “n:num” STRIP_ASSUME_TAC)
    >> ASM_REWRITE_TAC[] >> AP_TERM_TAC
    >> FIRST_ASSUM(MP_TAC o SPECL [`n:num`, `PRE k`] o MATCH_MP lemma7)
    >> FIRST_ASSUM(UNDISCH_TAC o (fn t => `^t`) o check is_eq o concl)
    >> DISCH_THEN SUBST_ALL_TAC
    >> UNDISCH_TAC `((i:num->real) (PRE k) + i k) / &2 <= i n`
    >> SUBGOAL_THEN `(i:num->real) n <= (i k + i(SUC k)) / &2` MP_TAC
    >- (
      MATCH_MP_TAC lemma1 >> ASM_REWRITE_TAC[]
      >> FIRST_ASSUM(fn th => REWRITE_TAC[MATCH_MP lemma8 th])
      >> UNDISCH_TAC `n < N:num`
      >> UNDISCH_TAC `~(SUC k < N)` >> gs[]
    )
    >> UNDISCH_TAC `~(k:num = 0)` >>  SPEC_TAC(“k:num”, “m:num”)
    >> Induct_on ‘m’
    >- gs[]
    >> gs[] >> mesonLib.MESON_TAC[]
  )
  >> gs[] >> EXISTS_TAC ‘b:real’
  >> ASM_REWRITE_TAC[REAL_LE_REFL]
  >> REWRITE_TAC[CONJ_ASSOC, REAL_LE_ANTISYM]
  >> gs[]
QED


(* ------------------------------------------------------------------------- *)
(* Basic lemma.                                                              *)
(* ------------------------------------------------------------------------- *)

Theorem  STURM_LEMMA_lemma[local] :
 ∀ s: real->bool. (s INTER t = EMPTY) ==> FINITE s /\ FINITE t ==>
 (CARD (s UNION t) = CARD s + CARD t)
Proof
  rpt strip_tac
  >> ‘CARD (s ∩ t) = 0’ by gs[CARD_DEF]
  >> ‘CARD (s ∪ t) = CARD (s ∪ t) + CARD (s ∩ t)’ by gs[]
  >> metis_tac[CARD_UNION]
QED

Theorem STURM_LEMMA:
 !i n.
        rsquarefree f /\
        STURM f (diff f) l /\
        (!k. i k <= i (SUC k)) /\
        (!k. k <= n ==> ~(poly f (i k) = &0)) /\
        (!k. ?c. i k <= c /\
                 c <= i (SUC k) /\
                 (!x. i k <= x /\
                      x <= i (SUC k) /\
                      EXISTS (\p. poly p x = &0) (CONS f (CONS (diff f) l))
                      ==> (x = c)))
        ==> FINITE { x | i 0 <= x /\ x <= i n /\ (poly f x = &0) } /\
            (variation (MAP (\p. poly p (i n))
                            (CONS f (CONS (diff f) l))) +
             CARD { x | i 0 <= x /\ x <= i n /\ (poly f x = &0) } =
             variation (MAP (\p. poly p (i 0))
                        (CONS f (CONS (diff f) l))))
Proof
  GEN_TAC >> Induct_on ‘n’
  >- (
    STRIP_TAC
    >> SUBGOAL_THEN `{x | (i:num->real) 0 <= x /\ x <= (i:num->real) 0 /\ (poly f x = &0)} = {}`
       SUBST1_TAC
    >- (
      REWRITE_TAC[EXTENSION, NOT_IN_EMPTY]
      >> REWRITE_TAC[CONJ_ASSOC, REAL_LE_ANTISYM]
      >> UNDISCH_TAC `!(k:num). k <= 0 ==> ~(poly f (i k) = &0)`
      >> DISCH_THEN(MP_TAC o SPEC `0`)
      >> REWRITE_TAC[arithmeticTheory.LESS_EQ_REFL] >> gs[]
    )
    >> CONJ_TAC
    >- metis_tac[FINITE_EMPTY]
    >> gs[CARD_DEF]
  )
(** second subgoal begins **)
  >> STRIP_TAC
  >> SUBGOAL_THEN `({x | i 0 <= x /\ x <= i (SUC n) /\ (poly f x = &0)} =
                   {x | i 0 <= x /\ x <= i n /\ (poly f x = &0)} UNION
                   {x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)}) /\
                  ({x | i 0 <= x /\ x <= i n /\ (poly f x = &0)} INTER
                   {x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)} =
                   EMPTY)`
    STRIP_ASSUME_TAC
  >- (
    REWRITE_TAC[EXTENSION, IN_INTER, IN_UNION]
    >> CONJ_TAC
    >- (
      GEN_TAC >> gs[] >> REWRITE_TAC[CONJ_ASSOC]
      >> REWRITE_TAC[tautLib.TAUT `a /\ c \/ b /\ c <=> (a \/ b) /\ c`]
      >> MATCH_MP_TAC(tautLib.TAUT `(c ==> (a <=> b)) ==> (a /\ c <=> b /\ c)`)
      >> DISCH_TAC >> eq_tac
      >- (
        strip_tac
        >> Cases_on `x <= i n` >> gs[REAL_NOT_LE, REAL_LT_LE]
      )
      >> strip_tac THEN gs[]
      >- (
        MATCH_MP_TAC REAL_LE_TRANS
        >> EXISTS_TAC `(i:num->real) n` >> gs[]
      )
      >> MATCH_MP_TAC REAL_LE_TRANS >> EXISTS_TAC `(i:num->real) n`
      >> gs[] >> SPEC_TAC(“n:num”, “m:num”)
      >> Induct_on ‘m’
      >- gs[]
      >> MATCH_MP_TAC REAL_LE_TRANS
      >> EXISTS_TAC `(i:num->real) m` >> gs[]
    )
    >> GEN_TAC >> REWRITE_TAC[NOT_IN_EMPTY] >> STRIP_TAC
    >> UNDISCH_TAC `!k. k <= SUC n ==> ~(poly f (i k) = &0)` >> gs[]
    >> EXISTS_TAC ‘n:num’ >> gs[arithmeticTheory.LESS_EQ_SUC_REFL]
    >> SUBGOAL_THEN `(i:num->real) n = x`
          (fn th => ASM_REWRITE_TAC[th])
    >> ONCE_REWRITE_TAC[GSYM REAL_LE_ANTISYM]
    >> ASM_REWRITE_TAC[]
  )
  >> FIRST_ASSUM(UNDISCH_TAC o (fn t => `^t`) o check is_imp o concl)
  >> ASM_REWRITE_TAC[]
  >> SUBGOAL_THEN `!k:num. k <= n ==> ~(poly f (i k) = &0)` ASSUME_TAC
  >- (
    GEN_TAC >> DISCH_TAC >> FIRST_ASSUM MATCH_MP_TAC
    >> UNDISCH_TAC `k <= n:num` >> gs[]
  )
  >> ASM_REWRITE_TAC[]
  >> DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC)
  >> FIRST_ASSUM(X_CHOOSE_THEN “c:real” STRIP_ASSUME_TAC o SPEC `n:num`)
  >> MP_TAC(SPECL [`l:(real list)list`, `f:real list`] STURM_COMPONENT)
  >> DISCH_THEN(MP_TAC o SPECL [`(i:num->real) n`, `(i:num->real)(SUC n)`])
  >> DISCH_THEN(MP_TAC o SPEC `c:real`) >> ASM_REWRITE_TAC[]
  >> SUBGOAL_THEN `~(poly f (i n) = &0) /\ ~(poly f (i (SUC n)) = &0)`
                                           ASSUME_TAC
  >- (
    UNDISCH_TAC `!k:num. k <= n ==> ~(poly f (i k) = &0)`
    >> DISCH_THEN(K ALL_TAC) >> CONJ_TAC
    >> FIRST_ASSUM MATCH_MP_TAC >> rw[]
  )
  >> ASM_REWRITE_TAC[]
  >> ASM_CASES_TAC “poly f c = &0” >> ASM_REWRITE_TAC[]
  >- (
    SUBGOAL_THEN
    `FINITE {x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)} /\
    (CARD {x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)} = 1)`
          STRIP_ASSUME_TAC
    >- (
      SUBGOAL_THEN `{x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)} =
                      {c}`
                         SUBST_ALL_TAC
      >- (
        REWRITE_TAC[EXTENSION, IN_INSERT, NOT_IN_EMPTY]
        >> X_GEN_TAC “x:real” >> EQ_TAC
        >- (
          STRIP_TAC
          >> ASM_REWRITE_TAC[] >> FIRST_ASSUM MATCH_MP_TAC
          >> ASM_REWRITE_TAC[EXISTS_DEF]
          >> ‘i n ≤ x ∧ x ≤ i (SUC n) ∧ poly f x = 0’ by
            (
              pop_assum mp_tac
              >> disch_then $ MATCH_ACCEPT_TAC o
                                    SIMP_RULE std_ss [IN_GSPEC_IFF]
            )
          >> metis_tac[]
        )
        >> strip_tac >> rw[]
      )
      >> REWRITE_TAC[FINITE_INSERT] >> rw[CARD_SING, FINITE_EMPTY]
    )
    >> rpt strip_tac
    >- ( REWRITE_TAC[FINITE_UNION] >> rw[] )
    >> FIRST_ASSUM(MP_TAC o MATCH_MP STURM_LEMMA_lemma)
    >> ASM_REWRITE_TAC[]
    >> DISCH_THEN SUBST_ALL_TAC
    >> REWRITE_TAC[GSYM arithmeticTheory.ADD1, arithmeticTheory.ADD_CLAUSES,
              arithmeticTheory.LESS_EQ_MONO]
    >> ‘variation (MAP (λp. poly p (i (SUC n))) (f::diff f::l)) +
        CARD {x | i 0 ≤ x ∧ x ≤ i n ∧ poly f x = 0} =
        CARD {x | i 0 ≤ x ∧ x ≤ i n ∧ poly f x = 0} +
        variation (MAP (λp. poly p (i (SUC n))) (f::diff f::l))
       ’ by metis_tac[arithmeticTheory.ADD_COMM]
    >> pop_assum $ rewrite_tac o single
    >> rewrite_tac[arithmeticTheory.SUC_ADD_SYM] >> metis_tac[]
    >> ‘SUC (variation (MAP (λp. poly p (i (SUC n))) (f::diff f::l))) =
        variation (MAP (λp. poly p (i n)) (f::diff f::l)) ’ by metis_tac[]
  )
  >> SUBGOAL_THEN
       `FINITE {x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)} /\
        (CARD {x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)} = 0)`
         STRIP_ASSUME_TAC
  >- (
    SUBGOAL_THEN `{x | i n <= x /\ x <= i (SUC n) /\ (poly f x = &0)} =
                      EMPTY`
                           SUBST_ALL_TAC
    >- (
      REWRITE_TAC[EXTENSION, IN_INSERT, NOT_IN_EMPTY]
      >> X_GEN_TAC “x:real” >> STRIP_TAC
      >> UNDISCH_TAC `~(poly f c = &0)`
      >> SUBGOAL_THEN `x:real = c` (fn th => ASM_REWRITE_TAC[SYM th])
      >- (
        FIRST_ASSUM MATCH_MP_TAC >> ASM_REWRITE_TAC[EXISTS_DEF]
        >> ‘i n ≤ x ∧ x ≤ i (SUC n) ∧ poly f x = 0’ by
          (
            pop_assum mp_tac
            >> disch_then $ MATCH_ACCEPT_TAC o SIMP_RULE std_ss [IN_GSPEC_IFF]
          )
        >> metis_tac[]
      )
      >> ‘i n ≤ x ∧ x ≤ i (SUC n) ∧ poly f x = 0’ by
        (
          pop_assum mp_tac
          >> disch_then $ MATCH_ACCEPT_TAC o SIMP_RULE std_ss [IN_GSPEC_IFF]
        )
    )
    >> rw[]
  )
  >> FIRST_ASSUM(MP_TAC o MATCH_MP STURM_LEMMA_lemma)
  >> ASM_REWRITE_TAC[]
  >> DISCH_THEN SUBST_ALL_TAC >> DISCH_THEN SUBST1_TAC
  >> DISCH_THEN(SUBST1_TAC o SYM)
  >> REWRITE_TAC[GSYM arithmeticTheory.ADD1, arithmeticTheory.ADD_CLAUSES,
              arithmeticTheory.LESS_EQ_MONO]
  >> MATCH_MP_TAC FINITE_UNION_IMP >> ASM_REWRITE_TAC[]
QED

(* ------------------------------------------------------------------------- *)
(* We just need to show that things in Sturm sequence are nontrivial.        *)
(* ------------------------------------------------------------------------- *)

Theorem STURM_NONZERO_LEMMA:
 !l f f'. ~(poly f = poly []) /\ STURM f f' l
          ==> ~(poly f' = poly [])
Proof
  ListConv1.LIST_INDUCT_TAC >> REWRITE_TAC[STURM_def]
  >- (
    REPEAT GEN_TAC >> CONV_TAC CONTRAPOS_CONV
    >> REWRITE_TAC[DE_MORGAN_THM]
    >> DISCH_TAC >> ASM_CASES_TAC “f' poly_divides f”
    >> ASM_REWRITE_TAC[]
    >> UNDISCH_TAC `f' poly_divides f` >> REWRITE_TAC[poly_divides]
    >> DISCH_THEN(CHOOSE_THEN SUBST1_TAC)
    >> ASM_REWRITE_TAC[FUN_EQ_THM, POLY_MUL, REAL_MUL_LZERO, poly_def]
    >> gs[]
  )
  >> REPEAT GEN_TAC >> CONV_TAC CONTRAPOS_CONV
  >> REWRITE_TAC[] >> DISCH_TAC >> ASM_REWRITE_TAC[]
  >> DISCH_THEN(MP_TAC o el 3 o CONJUNCTS)
  >> REWRITE_TAC[arithmeticTheory.NOT_LESS]
  >> FIRST_ASSUM(SUBST1_TAC o  MATCH_MP DEGREE_ZERO)
  >> gs[degree]
QED

Theorem STURM_NONZERO :
 !l f f'. ~(poly f = poly []) /\ STURM f f' l
          ==> EVERY (\p. ~(poly p = poly [])) (CONS f' l)
Proof
  ListConv1.LIST_INDUCT_TAC >> REWRITE_TAC[STURM_def]
  >- (
    REWRITE_TAC[listTheory.EVERY_DEF] >> REPEAT GEN_TAC >> STRIP_TAC
    >> gs[] >>  MATCH_MP_TAC STURM_NONZERO_LEMMA
    >> EXISTS_TAC `[]:(real list)list`
    >> EXISTS_TAC `f:real list` >> ASM_REWRITE_TAC[STURM_def]
  )
  >> REPEAT STRIP_TAC >> ONCE_REWRITE_TAC[listTheory.EVERY_DEF]
  >> ASM_REWRITE_TAC[]
  >> SUBGOAL_THEN `~(poly f' = poly [])` ASSUME_TAC
  >- (
    DISCH_THEN(SUBST_ALL_TAC o MATCH_MP DEGREE_ZERO)
    >> UNDISCH_TAC `degree h < 0` >> gs[]
  )
  >> CONJ_TAC
  >- metis_tac[]
  >> FIRST_ASSUM MATCH_MP_TAC
  >> EXISTS_TAC `f':real list` >> metis_tac[]
QED


(* ------------------------------------------------------------------------- *)
(* And finally...                                                            *)
(* ------------------------------------------------------------------------- *)

Theorem STURM_STRONG:
 !f a b l.
    a <= b /\
    ~(poly f a = &0) /\
    ~(poly f b = &0) /\
    rsquarefree f /\
    STURM f (diff f) l
    ==> FINITE {x | a <= x /\ x <= b /\ (poly f x = &0)} /\
        (variation
           (MAP (\p. poly p b) (CONS f (CONS (diff f) l))) +
         CARD {x | a <= x /\ x <= b /\ (poly f x = &0)} =
         variation
         (MAP (\p. poly p a) (CONS f (CONS (diff f) l))))
Proof
  REPEAT GEN_TAC >> STRIP_TAC
  >> SUBGOAL_THEN `EVERY (\p. ~(poly p = poly [])) (CONS (diff f) l)`
               ASSUME_TAC
  >- (
    MATCH_MP_TAC STURM_NONZERO
    >> EXISTS_TAC `f:real list` >> ASM_REWRITE_TAC[]
    >> DISCH_THEN SUBST_ALL_TAC
    >> UNDISCH_TAC `~(poly [] a = &0)` >> REWRITE_TAC[poly_def]
  )
  >> MP_TAC(SPECL [`f:real list`, `CONS (diff f) l`, `a:real`, `b:real`]
             POLYS_INTERVAL_SEPARATION)
  >> ASM_REWRITE_TAC[]
  >> DISCH_THEN(X_CHOOSE_THEN “i:num->real” MP_TAC)
  >> DISCH_THEN(X_CHOOSE_THEN “N:num” MP_TAC)
  >> DISCH_THEN(CONJUNCTS_THEN2 (SUBST_ALL_TAC o SYM) MP_TAC)
  >> DISCH_THEN(CONJUNCTS_THEN2 (SUBST_ALL_TAC o SYM) MP_TAC)
  >> STRIP_TAC >> MATCH_MP_TAC STURM_LEMMA
  >> ASM_REWRITE_TAC[]
QED


Theorem STURM_THM:
!f a b l.
    a <= b /\
    ~(poly f a = &0) /\
    ~(poly f b = &0) /\
    rsquarefree f /\
    STURM f (diff f) l
    ==> {x | a <= x /\ x <= b /\ (poly f x = &0)}
        HAS_SIZE
        (variation
           (MAP (\p. poly p a) (CONS f (CONS (diff f) l))) -
         variation
         (MAP (\p. poly p b) (CONS f (CONS (diff f) l))))
Proof
  REPEAT GEN_TAC
  >> DISCH_THEN(STRIP_ASSUME_TAC o MATCH_MP STURM_STRONG)
  >> ASM_REWRITE_TAC[pred_setTheory.HAS_SIZE]
  >> UNDISCH_TAC
    `variation (MAP (\p. poly p b) (CONS f (CONS (diff f) l))) +
       CARD {x | a <= x /\ x <= b /\ (poly f x = &0)} =
  variation (MAP (\p. poly p a) (CONS f (CONS (diff f) l)))`
  >> gs[]
QED

(* ------------------------------------------------------------------------- *)
(* Show that what we get at the end of the Sturm sequence is a GCD.          *)
(* ------------------------------------------------------------------------- *)

Theorem STURM_GCD :
!l k f f'.
        STURM f f' (CONS k l)
        ==> ?q e r s. (poly f = poly (q * LAST (CONS k l))) /\
                      (poly f' = poly (e * LAST (CONS k l))) /\
                      (poly (LAST (CONS k l)) =
                       poly (r * f + s * f'))
Proof
  ListConv1.LIST_INDUCT_TAC
  >- (
    REPEAT GEN_TAC >> REWRITE_TAC[STURM_def, LAST_DEF]
    >> DISCH_THEN(CONJUNCTS_THEN2 MP_TAC STRIP_ASSUME_TAC)
    >> DISCH_THEN(X_CHOOSE_THEN “c:real” STRIP_ASSUME_TAC)
    >> UNDISCH_TAC `k poly_divides f'` >> REWRITE_TAC[poly_divides]
    >> DISCH_THEN(X_CHOOSE_TAC “e:real list”)
    >> UNDISCH_TAC `f' poly_divides f + c ## k`
    >> REWRITE_TAC[poly_divides]
    >> DISCH_THEN(X_CHOOSE_TAC “g:real list”)
    >> EXISTS_TAC `e * g + [-c]`
    >> EXISTS_TAC `e:real list`
    >> EXISTS_TAC `[-(inv(c))]`
    >> EXISTS_TAC `inv(c) ## g`
    >> SUBGOAL_THEN `poly f = poly ((e * g + [- c]) * k)` ASSUME_TAC
    >- (
      REWRITE_TAC[FUN_EQ_THM] >> X_GEN_TAC “x:real”
      >> UNDISCH_TAC `poly (f + c ## k) = poly (f' * g)`
      >> DISCH_THEN(MP_TAC o SPEC `x:real` o ONCE_REWRITE_RULE[FUN_EQ_THM])
      >> REWRITE_TAC[POLY_ADD, POLY_MUL, POLY_CMUL]
      >> ASM_REWRITE_TAC[poly_def, REAL_MUL_RZERO, REAL_ADD_RID, POLY_MUL]
      >> REAL_ARITH_TAC
    )
    >> ASM_REWRITE_TAC[]
    >> ASM_REWRITE_TAC[FUN_EQ_THM, POLY_MUL, REAL_MUL_AC]
    >> CONJ_TAC
    >- ( X_GEN_TAC “x:real” >> rw[REAL_MUL_SYM] )
    >> X_GEN_TAC “x:real”
    >> ASM_REWRITE_TAC[POLY_MUL, POLY_ADD, POLY_CMUL]
    >> ASM_REWRITE_TAC[poly_def, REAL_MUL_RZERO, REAL_ADD_RID, POLY_MUL]
    >> REWRITE_TAC[REAL_MUL_RNEG, REAL_ADD_LDISTRIB, REAL_ADD_RDISTRIB,REAL_MUL_AC]
    >> rewrite_tac[REAL_MUL_LNEG] >> rewrite_tac[REAL_NEGNEG]
    >> ‘c⁻¹ * poly e x * poly g x * poly k x =
        c⁻¹ * poly g x * poly k x * poly e x’
      by
      (
        ‘poly e x * poly g x * poly k x =
         poly g x * poly k x * poly e x’
         by
         (
           ‘poly e x * poly g x * poly k x =
            poly e x * (poly g x * poly k x)’ by
            rewrite_tac[GSYM REAL_MUL_ASSOC]
           >> pop_assum $ rewrite_tac o single
           >> rw [REAL_MUL_SYM]
         )
        >> REAL_ARITH_TAC
      )
    >> pop_assum $ rewrite_tac o single
    >> REWRITE_TAC[REAL_ARITH `(-a + b) + (a:real) = (b:real)`]
    >> ONCE_REWRITE_TAC[REAL_MUL_SYM]
    >> `inv(c) * (c:real) = &1` by
      (
        MATCH_MP_TAC REAL_MUL_LINV
        >> UNDISCH_TAC `&0 < c:real` >> REAL_ARITH_TAC
      )
    >> gs[]
  )
  (* second goal starts here *)
  >> REPEAT GEN_TAC >> ONCE_REWRITE_TAC[LAST_DEF]
  >> ONCE_REWRITE_TAC[STURM_def] >> REWRITE_TAC[NOT_CONS_NIL]
  >> DISCH_THEN(CONJUNCTS_THEN MP_TAC)
  >> DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC)
  >> DISCH_THEN(ANTE_RES_THEN MP_TAC) >> STRIP_TAC
  >> DISCH_THEN(X_CHOOSE_THEN “c:real” MP_TAC)
  >> DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC)
  >> REWRITE_TAC[poly_divides]
  >> DISCH_THEN(X_CHOOSE_THEN “u:real list” ASSUME_TAC)
  >> EXISTS_TAC `q * u + (-c) ## e`
  >> EXISTS_TAC `q:real list`
  >> EXISTS_TAC `-(inv c) ## s`
  >> EXISTS_TAC `r + inv c ## s * u`
  >> ASM_REWRITE_TAC[]
  >> SUBGOAL_THEN `poly f = poly ((q * u + - c ## e) * LAST (CONS h l))`
                                ASSUME_TAC
  >- (
    UNDISCH_TAC `poly (LAST (CONS h l)) = poly (r * f' + s * k)`
    >> DISCH_THEN(K ALL_TAC)
    >> REWRITE_TAC[FUN_EQ_THM] >> X_GEN_TAC “x:real”
    >> UNDISCH_TAC `poly (f + c ## k) = poly (f' * u)`
    >> DISCH_THEN(MP_TAC o SPEC `x:real` o ONCE_REWRITE_RULE[FUN_EQ_THM])
    >> REWRITE_TAC[POLY_ADD, POLY_MUL, POLY_CMUL]
    >> ASM_REWRITE_TAC[poly_def, REAL_MUL_RZERO, REAL_ADD_RID, POLY_MUL]
    >> REAL_ARITH_TAC
  )
  >> ASM_REWRITE_TAC[]
  >> UNDISCH_TAC `poly (LAST (CONS h l)) = poly (r * f' + s * k)`
  >> DISCH_THEN(K ALL_TAC)
  >> REWRITE_TAC[FUN_EQ_THM] >> X_GEN_TAC “x:real”
  >> ASM_REWRITE_TAC[POLY_ADD, POLY_MUL, POLY_CMUL, POLY_NEG]
  >> REWRITE_TAC[REAL_ADD_LDISTRIB, REAL_ADD_RDISTRIB]
  >> REWRITE_TAC[REAL_MUL_LNEG, REAL_MUL_RNEG, REAL_MUL_AC]
  >> REWRITE_TAC[REAL_NEG_NEG]
  >> ‘c⁻¹ * poly s x * poly q x * poly u x * poly (LAST (h::l)) x =
      c⁻¹ * poly s x * poly u x * poly q x * poly (LAST (h::l)) x ’
    by REAL_ARITH_TAC >> pop_assum $ rewrite_tac o single
  >> REWRITE_TAC[REAL_ARITH `-a + b + ((c:real) + (a:real)) = (b:real) + c`]
  >> ‘ c⁻¹ * poly s x * c * poly e x * poly (LAST (h::l)) x =
       poly s x * poly e x * poly (LAST (h::l)) x’ by
    (
      ‘c⁻¹ * poly s x * c * poly e x * poly (LAST (h::l)) x=
       (inv(c) * c) * poly s x * poly e x * poly (LAST (h::l)) x’
       by REAL_ARITH_TAC >> pop_assum $ rewrite_tac o single
      >> `inv(c) * (c:real) = &1` by
        (
          MATCH_MP_TAC REAL_MUL_LINV
          >> UNDISCH_TAC `&0 < c:real` >> REAL_ARITH_TAC
        )
      >> pop_assum $ rewrite_tac o single
      >> REAL_ARITH_TAC
    )
  >> pop_assum $ rewrite_tac o single
  >> REAL_ARITH_TAC
QED


(* ------------------------------------------------------------------------- *)
(* Hence avoid a separate check for squarefreeness.                          *)
(* ------------------------------------------------------------------------- *)

Theorem STURM_THEOREM :
 !f a b l d.
        a <= b /\
        ~(poly f a = &0) /\
        ~(poly f b = &0) /\
        ~(poly (diff f) = poly []) /\
        STURM f (diff f) l /\
        ~(l = []) /\
        (LAST l = [d]) /\
        ~(d = &0)
        ==> {x | a <= x /\ x <= b /\ (poly f x = &0)} HAS_SIZE
            (variation (MAP (\p. poly p a) (CONS f (CONS (diff f) l))) -
             variation (MAP (\p. poly p b) (CONS f (CONS (diff f) l))))
Proof
  REPEAT STRIP_TAC >> MATCH_MP_TAC STURM_THM
  >> ASM_REWRITE_TAC[]
  >> UNDISCH_TAC `LAST l = [d:real]`
  >> UNDISCH_TAC `STURM f (diff f) l`
  >> UNDISCH_TAC `~(l:(real list)list = [])`
  >> SPEC_TAC(“l:(real list)list”, “l:(real list)list”)
  >> ListConv1.LIST_INDUCT_TAC
  >- REWRITE_TAC[NOT_CONS_NIL]
  >> gen_tac >> strip_tac
  >> DISCH_THEN(STRIP_ASSUME_TAC o MATCH_MP STURM_GCD)
  >> DISCH_THEN SUBST_ALL_TAC
  >> MP_TAC(SPECL [`f:real list`, `q:real list`] POLY_SQUAREFREE_DECOMP)
  >> DISCH_THEN(MP_TAC o SPECL [`[d:real]`, `e:real list`])
  >> DISCH_THEN(MP_TAC o SPECL [`r:real list`, `s:real list`])
  >> UNDISCH_TAC `~(poly (diff f) = poly [])`
  >> DISCH_THEN(fn th => REWRITE_TAC[th])
  >> ASM_REWRITE_TAC[] >> DISCH_THEN(MP_TAC o CONJUNCT1)
  >> REWRITE_TAC[rsquarefree]
  >> SUBGOAL_THEN `(poly q = poly []) <=> (poly (q * [d]) = poly [])`
                                                                 ASSUME_TAC
  >- (
    ASM_REWRITE_TAC[poly_def, REAL_ENTIRE, FUN_EQ_THM, POLY_MUL]
    >> ASM_REWRITE_TAC[REAL_MUL_RZERO, REAL_ADD_RID]
  )
  >> ASM_REWRITE_TAC[]
  >> MATCH_MP_TAC(tautLib.TAUT `(p ==> (q <=> r)) ==> (p /\ q ==> p /\ r)`)
  >> STRIP_TAC
  >> SUBGOAL_THEN `!a:real. poly_order a (f:real list) = poly_order a (q:real list)`
  (fn th => REWRITE_TAC[th])
  >> X_GEN_TAC “c:real” >> MATCH_MP_TAC EQ_TRANS
  >> EXISTS_TAC `poly_order (c:real) ((q:real list) * [d:real])`
  >> CONJ_TAC
  >- ( MATCH_MP_TAC ORDER_POLY >> ASM_REWRITE_TAC[] )
  >> FIRST_ASSUM(fn th => REWRITE_TAC[MATCH_MP ORDER_MUL th])
  >> SUBGOAL_THEN `poly_order c [d] = 0` (fn th => REWRITE_TAC[th, POLY_ADD_CLAUSES])
  >> MP_TAC(SPECL [`[d:real]`, `c:real`] ORDER_ROOT)
  >> ASM_REWRITE_TAC[poly_def, REAL_MUL_RZERO, REAL_ADD_RID]
  >> gs[] >> gs[]
QED


(* ------------------------------------------------------------------------- *)
(* A conversion for calculating variations.                                  *)
(* ------------------------------------------------------------------------- *)
(*
val VARIATION_CONV =
  let
    val HO_GEN_REWRITE_CONV = Ho_Rewrite.GEN_REWRITE_CONV
    val variation_conv = HO_GEN_REWRITE_CONV I [variation_def]
    val cond_conv = HO_GEN_REWRITE_CONV I [COND_CLAUSES]
    val sig_conv = HO_GEN_REWRITE_CONV I [SIGN_LEMMA5]
    val varrec0_conv = HO_GEN_REWRITE_CONV I [CONJUNCT1 varrec_def]
    val varrec1_conv = HO_GEN_REWRITE_CONV I [CONJUNCT2 varrec_def]
  in
    let
      fun VARREC_CONV tm =
        varrec0_conv tm
        handle HOL_ERR _ =>
        let
         val th1 = (varrec1_conv THENC
                    RATOR_CONV(LAND_CONV(sig_conv THENC REAL_RAT_REDUCE_CONV)) THENC
                    cond_conv) tm
         val tm1 = rand(concl th1)
        in
          if is_cond tm1 then
            let val th2 = (RATOR_CONV(LAND_CONV REAL_RAT_REDUCE_CONV) THENC
                           cond_conv THENC
                           VARREC_CONV) tm1
            in
              TRANS th1 th2
            end
          else
            TRANS th1 (RAND_CONV VARREC_CONV tm1)
        end
    in
  variation_conv THENC VARREC_CONV THENC
                 DEPTH_CONV NUM_SUC_CONV
                 end
                 end; *)

val _ = export_theory();
back to top