Theory Approximation_Bounds

(* 
  Author:     Johannes Hoelzl, TU Muenchen
  Coercions removed by Dmitriy Traytel

  This file contains only general material about computing lower/upper bounds
  on real functions. Approximation.thy contains the actual approximation algorithm
  and the approximation oracle. This is in order to make a clear separation between 
  "morally immaculate" material about upper/lower bounds and the trusted oracle/reflection.
*)

theory Approximation_Bounds
imports
  Complex_Main
  "HOL-Library.Interval_Float"
  Dense_Linear_Order
begin

declare powr_neg_one [simp]
declare powr_neg_numeral [simp]

context includes interval.lifting begin

section "Horner Scheme"

subsection Define auxiliary helper horner› function

primrec horner :: "(nat  nat)  (nat  nat  nat)  nat  nat  nat  real  real" where
"horner F G 0 i k x       = 0" |
"horner F G (Suc n) i k x = 1 / k - x * horner F G n (F i) (G i k) x"

lemma horner_schema':
  fixes x :: real and a :: "nat  real"
  shows "a 0 - x * ( i=0..<n. (-1)^i * a (Suc i) * x^i) = ( i=0..<Suc n. (-1)^i * a i * x^i)"
proof -
  have shift_pow: "i. - (x * ((-1)^i * a (Suc i) * x ^ i)) = (-1)^(Suc i) * a (Suc i) * x ^ (Suc i)"
    by auto
  show ?thesis
    unfolding sum_distrib_left shift_pow uminus_add_conv_diff [symmetric] sum_negf[symmetric]
    sum.atLeast_Suc_lessThan[OF zero_less_Suc]
    sum.reindex[OF inj_Suc, unfolded comp_def, symmetric, of "λ n. (-1)^n  *a n * x^n"] by auto
qed

lemma horner_schema:
  fixes f :: "nat  nat" and G :: "nat  nat  nat" and F :: "nat  nat"
  assumes f_Suc: "n. f (Suc n) = G ((F ^^ n) s) (f n)"
  shows "horner F G n ((F ^^ j') s) (f j') x = ( j = 0..< n. (- 1) ^ j * (1 / (f (j' + j))) * x ^ j)"
proof (induct n arbitrary: j')
  case 0
  then show ?case by auto
next
  case (Suc n)
  show ?case unfolding horner.simps Suc[where j'="Suc j'", unfolded funpow.simps comp_def f_Suc]
    using horner_schema'[of "λ j. 1 / (f (j' + j))"] by auto
qed

lemma horner_bounds':
  fixes lb :: "nat  nat  nat  float  float" and ub :: "nat  nat  nat  float  float"
  assumes "0  real_of_float x" and f_Suc: "n. f (Suc n) = G ((F ^^ n) s) (f n)"
    and lb_0: " i k x. lb 0 i k x = 0"
    and lb_Suc: " n i k x. lb (Suc n) i k x = float_plus_down prec
        (lapprox_rat prec 1 k)
        (- float_round_up prec (x * (ub n (F i) (G i k) x)))"
    and ub_0: " i k x. ub 0 i k x = 0"
    and ub_Suc: " n i k x. ub (Suc n) i k x = float_plus_up prec
        (rapprox_rat prec 1 k)
        (- float_round_down prec (x * (lb n (F i) (G i k) x)))"
  shows "(lb n ((F ^^ j') s) (f j') x)  horner F G n ((F ^^ j') s) (f j') x 
         horner F G n ((F ^^ j') s) (f j') x  (ub n ((F ^^ j') s) (f j') x)"
  (is "?lb n j'  ?horner n j'  ?horner n j'  ?ub n j'")
proof (induct n arbitrary: j')
  case 0
  thus ?case unfolding lb_0 ub_0 horner.simps by auto
next
  case (Suc n)
  thus ?case using lapprox_rat[of prec 1 "f j'"] using rapprox_rat[of 1 "f j'" prec]
    Suc[where j'="Suc j'"] 0  real_of_float x
    by (auto intro!: add_mono mult_left_mono float_round_down_le float_round_up_le
      order_trans[OF add_mono[OF _ float_plus_down_le]]
      order_trans[OF _ add_mono[OF _ float_plus_up_le]]
      simp add: lb_Suc ub_Suc field_simps f_Suc)
qed

subsection "Theorems for floating point functions implementing the horner scheme"

text 

Here term_typef :: nat  nat is the sequence defining the Taylor series, the coefficients are
all alternating and reciprocs. We use termG and termF to describe the computation of termf.



lemma horner_bounds:
  fixes F :: "nat  nat" and G :: "nat  nat  nat"
  assumes "0  real_of_float x" and f_Suc: "n. f (Suc n) = G ((F ^^ n) s) (f n)"
    and lb_0: " i k x. lb 0 i k x = 0"
    and lb_Suc: " n i k x. lb (Suc n) i k x = float_plus_down prec
        (lapprox_rat prec 1 k)
        (- float_round_up prec (x * (ub n (F i) (G i k) x)))"
    and ub_0: " i k x. ub 0 i k x = 0"
    and ub_Suc: " n i k x. ub (Suc n) i k x = float_plus_up prec
        (rapprox_rat prec 1 k)
        (- float_round_down prec (x * (lb n (F i) (G i k) x)))"
  shows "(lb n ((F ^^ j') s) (f j') x)  (j=0..<n. (- 1) ^ j * (1 / (f (j' + j))) * (x ^ j))"
      (is "?lb")
    and "(j=0..<n. (- 1) ^ j * (1 / (f (j' + j))) * (x ^ j))  (ub n ((F ^^ j') s) (f j') x)"
      (is "?ub")
proof -
  have "?lb   ?ub"
    using horner_bounds'[where lb=lb, OF 0  real_of_float x f_Suc lb_0 lb_Suc ub_0 ub_Suc]
    unfolding horner_schema[where f=f, OF f_Suc] by simp
  thus "?lb" and "?ub" by auto
qed

lemma horner_bounds_nonpos:
  fixes F :: "nat  nat" and G :: "nat  nat  nat"
  assumes "real_of_float x  0" and f_Suc: "n. f (Suc n) = G ((F ^^ n) s) (f n)"
    and lb_0: " i k x. lb 0 i k x = 0"
    and lb_Suc: " n i k x. lb (Suc n) i k x = float_plus_down prec
        (lapprox_rat prec 1 k)
        (float_round_down prec (x * (ub n (F i) (G i k) x)))"
    and ub_0: " i k x. ub 0 i k x = 0"
    and ub_Suc: " n i k x. ub (Suc n) i k x = float_plus_up prec
        (rapprox_rat prec 1 k)
        (float_round_up prec (x * (lb n (F i) (G i k) x)))"
  shows "(lb n ((F ^^ j') s) (f j') x)  (j=0..<n. (1 / (f (j' + j))) * real_of_float x ^ j)" (is "?lb")
    and "(j=0..<n. (1 / (f (j' + j))) * real_of_float x ^ j)  (ub n ((F ^^ j') s) (f j') x)" (is "?ub")
proof -
  have diff_mult_minus: "x - y * z = x + - y * z" for x y z :: float by simp
  have sum_eq: "(j=0..<n. (1 / (f (j' + j))) * real_of_float x ^ j) =
    (j = 0..<n. (- 1) ^ j * (1 / (f (j' + j))) * real_of_float (- x) ^ j)"
    by (auto simp add: field_simps power_mult_distrib[symmetric])
  have "0  real_of_float (-x)" using assms by auto
  from horner_bounds[where G=G and F=F and f=f and s=s and prec=prec
    and lb="λ n i k x. lb n i k (-x)" and ub="λ n i k x. ub n i k (-x)",
    unfolded lb_Suc ub_Suc diff_mult_minus,
    OF this f_Suc lb_0 _ ub_0 _]
  show "?lb" and "?ub" unfolding minus_minus sum_eq
    by (auto simp: minus_float_round_up_eq minus_float_round_down_eq)
qed


subsection Selectors for next even or odd number

text 
The horner scheme computes alternating series. To get the upper and lower bounds we need to
guarantee to access a even or odd member. To do this we use termget_odd and termget_even.


definition get_odd :: "nat  nat" where
  "get_odd n = (if odd n then n else (Suc n))"

definition get_even :: "nat  nat" where
  "get_even n = (if even n then n else (Suc n))"

lemma get_odd[simp]: "odd (get_odd n)"
  unfolding get_odd_def by (cases "odd n") auto

lemma get_even[simp]: "even (get_even n)"
  unfolding get_even_def by (cases "even n") auto

lemma get_odd_ex: " k. Suc k = get_odd n  odd (Suc k)"
  by (auto simp: get_odd_def odd_pos intro!: exI[of _ "n - 1"])

lemma get_even_double: "i. get_even n = 2 * i"
  using get_even by (blast elim: evenE)

lemma get_odd_double: "i. get_odd n = 2 * i + 1"
  using get_odd by (blast elim: oddE)


section "Power function"

definition float_power_bnds :: "nat  nat  float  float  float * float" where
"float_power_bnds prec n l u =
  (if 0 < l then (power_down_fl prec l n, power_up_fl prec u n)
  else if odd n then
    (- power_up_fl prec ¦l¦ n,
      if u < 0 then - power_down_fl prec ¦u¦ n else power_up_fl prec u n)
  else if u < 0 then (power_down_fl prec ¦u¦ n, power_up_fl prec ¦l¦ n)
  else (0, power_up_fl prec (max ¦l¦ ¦u¦) n))"

lemma le_minus_power_downI: "0  x  x ^ n  - a  a  - power_down prec x n"
  by (subst le_minus_iff) (auto intro: power_down_le power_mono_odd)

lemma float_power_bnds:
  "(l1, u1) = float_power_bnds prec n l u  x  {l .. u}  (x::real) ^ n  {l1..u1}"
  by (auto
    simp: float_power_bnds_def max_def real_power_up_fl real_power_down_fl minus_le_iff
    split: if_split_asm
    intro!: power_up_le power_down_le le_minus_power_downI
    intro: power_mono_odd power_mono power_mono_even zero_le_even_power)

lemma bnds_power:
  "(x::real) l u. (l1, u1) = float_power_bnds prec n l u  x  {l .. u} 
    l1  x ^ n  x ^ n  u1"
  using float_power_bnds by auto

lift_definition power_float_interval :: "nat  nat  float interval  float interval"
  is "λp n (l, u). float_power_bnds p n l u"
  using float_power_bnds
  by (auto simp: bnds_power dest!: float_power_bnds[OF sym])

lemma lower_power_float_interval:
  "lower (power_float_interval p n x) = fst (float_power_bnds p n (lower x) (upper x))"
  by transfer auto
lemma upper_power_float_interval:
  "upper (power_float_interval p n x) = snd (float_power_bnds p n (lower x) (upper x))"
  by transfer auto

lemma power_float_intervalI: "x r X  x ^ n r power_float_interval p n X"
  using float_power_bnds[OF prod.collapse]
  by (auto simp: set_of_eq lower_power_float_interval upper_power_float_interval)

lemma power_float_interval_mono:
  "set_of (power_float_interval prec n A)
     set_of (power_float_interval prec n B)"
  if "set_of A  set_of B"
proof -
  define la where "la = real_of_float (lower A)"
  define ua where "ua = real_of_float (upper A)"
  define lb where "lb = real_of_float (lower B)"
  define ub where "ub = real_of_float (upper B)"
  have ineqs: "lb  la" "la  ua" "ua  ub" "lb  ub"
    using that lower_le_upper[of A] lower_le_upper[of B]
    by (auto simp: la_def ua_def lb_def ub_def set_of_eq)
  show ?thesis
    using ineqs
    by (simp add: set_of_subset_iff float_power_bnds_def max_def
        power_down_fl.rep_eq power_up_fl.rep_eq
        lower_power_float_interval upper_power_float_interval
        la_def[symmetric] ua_def[symmetric] lb_def[symmetric] ub_def[symmetric])
      (auto intro!: power_down_mono power_up_mono intro: order_trans[where y=0])
qed


section Approximation utility functions

lift_definition plus_float_interval::"nat  float interval  float interval  float interval"
  is "λprec. λ(a1, a2). λ(b1, b2). (float_plus_down prec a1 b1, float_plus_up prec a2 b2)"
  by (auto intro!: add_mono simp: float_plus_down_le float_plus_up_le)

lemma lower_plus_float_interval:
  "lower (plus_float_interval prec ivl ivl') = float_plus_down prec (lower ivl) (lower ivl')"
  by transfer auto
lemma upper_plus_float_interval:
  "upper (plus_float_interval prec ivl ivl') = float_plus_up prec (upper ivl) (upper ivl')"
  by transfer auto

lemma mult_float_interval_ge:
  "real_interval A + real_interval B  real_interval (plus_float_interval prec A B)"
  unfolding less_eq_interval_def
  by transfer
     (auto simp: lower_plus_float_interval upper_plus_float_interval
           intro!: order.trans[OF float_plus_down] order.trans[OF _ float_plus_up])

lemma plus_float_interval:
  "set_of (real_interval A) + set_of (real_interval B) 
    set_of (real_interval (plus_float_interval prec A B))"
proof -
  have "set_of (real_interval A) + set_of (real_interval B) 
          set_of (real_interval A + real_interval B)"
    by (simp add: set_of_plus)
  also have "  set_of (real_interval (plus_float_interval prec A B))"
    using mult_float_interval_ge[of A B prec] by (simp add: set_of_subset_iff')
  finally show ?thesis .
qed

lemma plus_float_intervalI:
  "x + y r plus_float_interval prec A B"
  if "x i real_interval A" "y i real_interval B"
  using plus_float_interval[of A B] that by auto

lemma plus_float_interval_mono:
  "plus_float_interval prec A B  plus_float_interval prec X Y"
  if "A  X" "B  Y"
  using that
  by (auto simp: less_eq_interval_def lower_plus_float_interval upper_plus_float_interval
                 float_plus_down.rep_eq float_plus_up.rep_eq plus_down_mono plus_up_mono)

lemma plus_float_interval_monotonic:
  "set_of (ivl + ivl')  set_of (plus_float_interval prec ivl ivl')"
  using float_plus_down_le float_plus_up_le lower_plus_float_interval upper_plus_float_interval
  by (simp add: set_of_subset_iff)


definition bnds_mult :: "nat  float  float  float  float  float × float" where
  "bnds_mult prec a1 a2 b1 b2 =
      (float_plus_down prec (nprt a1 * pprt b2)
          (float_plus_down prec (nprt a2 * nprt b2)
            (float_plus_down prec (pprt a1 * pprt b1) (pprt a2 * nprt b1))),
        float_plus_up prec (pprt a2 * pprt b2)
            (float_plus_up prec (pprt a1 * nprt b2)
              (float_plus_up prec (nprt a2 * pprt b1) (nprt a1 * nprt b1))))"

lemma bnds_mult:
  fixes prec :: nat and a1 aa2 b1 b2 :: float
  assumes "(l, u) = bnds_mult prec a1 a2 b1 b2"
  assumes "a  {real_of_float a1..real_of_float a2}"
  assumes "b  {real_of_float b1..real_of_float b2}"
  shows   "a * b  {real_of_float l..real_of_float u}"
proof -
  from assms have "real_of_float l  a * b" 
    by (intro order.trans[OF _ mult_ge_prts[of a1 a a2 b1 b b2]])
       (auto simp: bnds_mult_def intro!: float_plus_down_le)
  moreover from assms have "real_of_float u  a * b" 
    by (intro order.trans[OF mult_le_prts[of a1 a a2 b1 b b2]])
       (auto simp: bnds_mult_def intro!: float_plus_up_le)
  ultimately show ?thesis by simp
qed

lift_definition mult_float_interval::"nat  float interval  float interval  float interval"
  is "λprec. λ(a1, a2). λ(b1, b2). bnds_mult prec a1 a2 b1 b2"
  by (auto dest!: bnds_mult[OF sym])

lemma lower_mult_float_interval:
  "lower (mult_float_interval p x y) = fst (bnds_mult p (lower x) (upper x) (lower y) (upper y))"
  by transfer auto
lemma upper_mult_float_interval:
  "upper (mult_float_interval p x y) = snd (bnds_mult p (lower x) (upper x) (lower y) (upper y))"
  by transfer auto

lemma mult_float_interval:
  "set_of (real_interval A) * set_of (real_interval B) 
    set_of (real_interval (mult_float_interval prec A B))"
proof -
  let ?bm = "bnds_mult prec (lower A) (upper A) (lower B) (upper B)"
  show ?thesis
    using bnds_mult[of "fst ?bm" "snd ?bm", simplified, OF refl]
    by (auto simp: set_of_eq set_times_def upper_mult_float_interval lower_mult_float_interval)
qed

lemma mult_float_intervalI:
  "x * y r mult_float_interval prec A B"
  if "x i real_interval A" "y i real_interval B"
  using mult_float_interval[of A B] that
  by (auto simp: )

lemma mult_float_interval_mono':
  "set_of (mult_float_interval prec A B)  set_of (mult_float_interval prec X Y)"
  if "set_of A  set_of X" "set_of B  set_of Y"
  using that
  apply transfer
  unfolding bnds_mult_def atLeastatMost_subset_iff float_plus_down.rep_eq float_plus_up.rep_eq
  by (auto simp: float_plus_down.rep_eq float_plus_up.rep_eq mult_float_mono1 mult_float_mono2)

lemma mult_float_interval_mono:
  "mult_float_interval prec A B  mult_float_interval prec X Y"
  if "A  X" "B  Y"
  using mult_float_interval_mono'[of A X B Y prec] that
  by (simp add: set_of_subset_iff')


definition map_bnds :: "(nat  float  float)  (nat  float  float) 
                           nat  (float × float)  (float × float)" where
  "map_bnds lb ub prec = (λ(l,u). (lb prec l, ub prec u))"

lemma map_bnds:
  assumes "(lf, uf) = map_bnds lb ub prec (l, u)"
  assumes "mono f"
  assumes "x  {real_of_float l..real_of_float u}"
  assumes "real_of_float (lb prec l)  f (real_of_float l)"
  assumes "real_of_float (ub prec u)  f (real_of_float u)"
  shows   "f x  {real_of_float lf..real_of_float uf}"
proof -
  from assms have "real_of_float lf = real_of_float (lb prec l)"
    by (simp add: map_bnds_def)
  also have "real_of_float (lb prec l)  f (real_of_float l)"  by fact
  also from assms have "  f x"
    by (intro monoD[OF mono f]) auto
  finally have lf: "real_of_float lf  f x" .

  from assms have "f x  f (real_of_float u)"
    by (intro monoD[OF mono f]) auto
  also have "  real_of_float (ub prec u)" by fact
  also from assms have " = real_of_float uf"
    by (simp add: map_bnds_def)
  finally have uf: "f x  real_of_float uf" .

  from lf uf show ?thesis by simp
qed


section "Square root"

text 
The square root computation is implemented as newton iteration. As first first step we use the
nearest power of two greater than the square root.


fun sqrt_iteration :: "nat  nat  float  float" where
"sqrt_iteration prec 0 x = Float 1 ((bitlen ¦mantissa x¦ + exponent x) div 2 + 1)" |
"sqrt_iteration prec (Suc m) x = (let y = sqrt_iteration prec m x
                                  in Float 1 (- 1) * float_plus_up prec y (float_divr prec x y))"

lemma compute_sqrt_iteration_base[code]:
  shows "sqrt_iteration prec n (Float m e) =
    (if n = 0 then Float 1 ((if m = 0 then 0 else bitlen ¦m¦ + e) div 2 + 1)
    else (let y = sqrt_iteration prec (n - 1) (Float m e) in
      Float 1 (- 1) * float_plus_up prec y (float_divr prec (Float m e) y)))"
  using bitlen_Float by (cases n) simp_all

function ub_sqrt lb_sqrt :: "nat  float  float" where
"ub_sqrt prec x = (if 0 < x then (sqrt_iteration prec prec x)
              else if x < 0 then - lb_sqrt prec (- x)
                            else 0)" |
"lb_sqrt prec x = (if 0 < x then (float_divl prec x (sqrt_iteration prec prec x))
              else if x < 0 then - ub_sqrt prec (- x)
                            else 0)"
by pat_completeness auto
termination by (relation "measure (λ v. let (prec, x) = case_sum id id v in (if x < 0 then 1 else 0))", auto)

declare lb_sqrt.simps[simp del]
declare ub_sqrt.simps[simp del]

lemma sqrt_ub_pos_pos_1:
  assumes "sqrt x < b" and "0 < b" and "0 < x"
  shows "sqrt x < (b + x / b)/2"
proof -
  from assms have "0 < (b - sqrt x)2 " by simp
  also have " = b2 - 2 * b * sqrt x + (sqrt x)2" by algebra
  also have " = b2 - 2 * b * sqrt x + x" using assms by simp
  finally have "0 < b2 - 2 * b * sqrt x + x" .
  hence "0 < b / 2 - sqrt x + x / (2 * b)" using assms
    by (simp add: field_simps power2_eq_square)
  thus ?thesis by (simp add: field_simps)
qed

lemma sqrt_iteration_bound:
  assumes "0 < real_of_float x"
  shows "sqrt x < sqrt_iteration prec n x"
proof (induct n)
  case 0
  show ?case
  proof (cases x)
    case (Float m e)
    hence "0 < m"
      using assms
      by (auto simp: algebra_split_simps)
    hence "0 < sqrt m" by auto

    have int_nat_bl: "(nat (bitlen m)) = bitlen m"
      using bitlen_nonneg by auto

    have "x = (m / 2^nat (bitlen m)) * 2 powr (e + (nat (bitlen m)))"
      unfolding Float by (auto simp: powr_realpow[symmetric] field_simps powr_add)
    also have " < 1 * 2 powr (e + nat (bitlen m))"
    proof (rule mult_strict_right_mono, auto)
      show "m < 2^nat (bitlen m)"
        using bitlen_bounds[OF 0 < m, THEN conjunct2]
        unfolding of_int_less_iff[of m, symmetric] by auto
    qed
    finally have "sqrt x < sqrt (2 powr (e + bitlen m))"
      unfolding int_nat_bl by auto
    also have "  2 powr ((e + bitlen m) div 2 + 1)"
    proof -
      let ?E = "e + bitlen m"
      have E_mod_pow: "2 powr (?E mod 2) < 4"
      proof (cases "?E mod 2 = 1")
        case True
        thus ?thesis by auto
      next
        case False
        have "0  ?E mod 2" by auto
        have "?E mod 2 < 2" by auto
        from this[THEN zless_imp_add1_zle]
        have "?E mod 2  0" using False by auto
        from xt1(5)[OF 0  ?E mod 2 this]
        show ?thesis by auto
      qed
      hence "sqrt (2 powr (?E mod 2)) < sqrt (2 * 2)"
        by (intro real_sqrt_less_mono) auto
      hence E_mod_pow: "sqrt (2 powr (?E mod 2)) < 2" by auto

      have E_eq: "2 powr ?E = 2 powr (?E div 2 + ?E div 2 + ?E mod 2)"
        by auto
      have "sqrt (2 powr ?E) = sqrt (2 powr (?E div 2) * 2 powr (?E div 2) * 2 powr (?E mod 2))"
        unfolding E_eq unfolding powr_add[symmetric] by (metis of_int_add)
      also have " = 2 powr (?E div 2) * sqrt (2 powr (?E mod 2))"
        unfolding real_sqrt_mult[of _ "2 powr (?E mod 2)"] real_sqrt_abs2 by auto
      also have " < 2 powr (?E div 2) * 2 powr 1"
        by (rule mult_strict_left_mono) (auto intro: E_mod_pow)
      also have " = 2 powr (?E div 2 + 1)"
        unfolding add.commute[of _ 1] powr_add[symmetric] by simp
      finally show ?thesis by auto
    qed
    finally show ?thesis using 0 < m
      unfolding Float
      by (subst compute_sqrt_iteration_base) (simp add: ac_simps)
  qed
next
  case (Suc n)
  let ?b = "sqrt_iteration prec n x"
  have "0 < sqrt x"
    using 0 < real_of_float x by auto
  also have " < real_of_float ?b"
    using Suc .
  finally have "sqrt x < (?b + x / ?b)/2"
    using sqrt_ub_pos_pos_1[OF Suc _ 0 < real_of_float x] by auto
  also have "  (?b + (float_divr prec x ?b))/2"
    by (rule divide_right_mono, auto simp add: float_divr)
  also have " = (Float 1 (- 1)) * (?b + (float_divr prec x ?b))"
    by simp
  also have "  (Float 1 (- 1)) * (float_plus_up prec ?b (float_divr prec x ?b))"
    by (auto simp add: algebra_simps float_plus_up_le)
  finally show ?case
    unfolding sqrt_iteration.simps Let_def distrib_left .
qed

lemma sqrt_iteration_lower_bound:
  assumes "0 < real_of_float x"
  shows "0 < real_of_float (sqrt_iteration prec n x)" (is "0 < ?sqrt")
proof -
  have "0 < sqrt x" using assms by auto
  also have " < ?sqrt" using sqrt_iteration_bound[OF assms] .
  finally show ?thesis .
qed

lemma lb_sqrt_lower_bound:
  assumes "0  real_of_float x"
  shows "0  real_of_float (lb_sqrt prec x)"
proof (cases "0 < x")
  case True
  hence "0 < real_of_float x" and "0  x"
    using 0  real_of_float x by auto
  hence "0 < sqrt_iteration prec prec x"
    using sqrt_iteration_lower_bound by auto
  hence "0  real_of_float (float_divl prec x (sqrt_iteration prec prec x))"
    using float_divl_lower_bound[OF 0  x] unfolding less_eq_float_def by auto
  thus ?thesis
    unfolding lb_sqrt.simps using True by auto
next
  case False
  with 0  real_of_float x have "real_of_float x = 0" by auto
  thus ?thesis
    unfolding lb_sqrt.simps by auto
qed

lemma bnds_sqrt': "sqrt x  {(lb_sqrt prec x) .. (ub_sqrt prec x)}"
proof -
  have lb: "lb_sqrt prec x  sqrt x" if "0 < x" for x :: float
  proof -
    from that have "0 < real_of_float x" and "0  real_of_float x" by auto
    hence sqrt_gt0: "0 < sqrt x" by auto
    hence sqrt_ub: "sqrt x < sqrt_iteration prec prec x"
      using sqrt_iteration_bound by auto
    have "(float_divl prec x (sqrt_iteration prec prec x)) 
          x / (sqrt_iteration prec prec x)" by (rule float_divl)
    also have " < x / sqrt x"
      by (rule divide_strict_left_mono[OF sqrt_ub 0 < real_of_float x
               mult_pos_pos[OF order_less_trans[OF sqrt_gt0 sqrt_ub] sqrt_gt0]])
    also have " = sqrt x"
      unfolding inverse_eq_iff_eq[of _ "sqrt x", symmetric]
                sqrt_divide_self_eq[OF 0  real_of_float x, symmetric] by auto
    finally show ?thesis
      unfolding lb_sqrt.simps if_P[OF 0 < x] by auto
  qed
  have ub: "sqrt x  ub_sqrt prec x" if "0 < x" for x :: float
  proof -
    from that have "0 < real_of_float x" by auto
    hence "0 < sqrt x" by auto
    hence "sqrt x < sqrt_iteration prec prec x"
      using sqrt_iteration_bound by auto
    then show ?thesis
      unfolding ub_sqrt.simps if_P[OF 0 < x] by auto
  qed
  show ?thesis
    using lb[of "-x"] ub[of "-x"] lb[of x] ub[of x]
    by (auto simp add: lb_sqrt.simps ub_sqrt.simps real_sqrt_minus)
qed

lemma bnds_sqrt: "(x::real) lx ux.
  (l, u) = (lb_sqrt prec lx, ub_sqrt prec ux)  x  {lx .. ux}  l  sqrt x  sqrt x  u"
proof ((rule allI) +, rule impI, erule conjE, rule conjI)
  fix x :: real
  fix lx ux
  assume "(l, u) = (lb_sqrt prec lx, ub_sqrt prec ux)"
    and x: "x  {lx .. ux}"
  hence l: "l = lb_sqrt prec lx " and u: "u = ub_sqrt prec ux" by auto

  have "sqrt lx  sqrt x" using x by auto
  from order_trans[OF _ this]
  show "l  sqrt x" unfolding l using bnds_sqrt'[of lx prec] by auto

  have "sqrt x  sqrt ux" using x by auto
  from order_trans[OF this]
  show "sqrt x  u" unfolding u using bnds_sqrt'[of ux prec] by auto
qed

lift_definition sqrt_float_interval::"nat  float interval  float interval"
  is "λprec. λ(lx, ux). (lb_sqrt prec lx, ub_sqrt prec ux)"
  using bnds_sqrt'
  by auto (meson order_trans real_sqrt_le_iff)

lemma lower_float_interval: "lower (sqrt_float_interval prec X) = lb_sqrt prec (lower X)"
  by transfer auto

lemma upper_float_interval: "upper (sqrt_float_interval prec X) = ub_sqrt prec (upper X)"
  by transfer auto

lemma sqrt_float_interval:
  "sqrt ` set_of (real_interval X)  set_of (real_interval (sqrt_float_interval prec X))"
  using bnds_sqrt
  by (auto simp: set_of_eq lower_float_interval upper_float_interval)

lemma sqrt_float_intervalI: "sqrt x r sqrt_float_interval p X" if "x r X"
  using sqrt_float_interval[of X p] that
  by auto

section "Arcus tangens and π"

subsection "Compute arcus tangens series"

text 
As first step we implement the computation of the arcus tangens series. This is only valid in the range
term{-1 :: real .. 1}. This is used to compute π and then the entire arcus tangens.


fun ub_arctan_horner :: "nat  nat  nat  float  float"
and lb_arctan_horner :: "nat  nat  nat  float  float" where
  "ub_arctan_horner prec 0 k x = 0"
| "ub_arctan_horner prec (Suc n) k x = float_plus_up prec
      (rapprox_rat prec 1 k) (- float_round_down prec (x * (lb_arctan_horner prec n (k + 2) x)))"
| "lb_arctan_horner prec 0 k x = 0"
| "lb_arctan_horner prec (Suc n) k x = float_plus_down prec
      (lapprox_rat prec 1 k) (- float_round_up prec (x * (ub_arctan_horner prec n (k + 2) x)))"

lemma arctan_0_1_bounds':
  assumes "0  real_of_float y" "real_of_float y  1"
    and "even n"
  shows "arctan (sqrt y) 
      {(sqrt y * lb_arctan_horner prec n 1 y) .. (sqrt y * ub_arctan_horner prec (Suc n) 1 y)}"
proof -
  let ?c = "λi. (- 1) ^ i * (1 / (i * 2 + (1::nat)) * sqrt y ^ (i * 2 + 1))"
  let ?S = "λn.  i=0..<n. ?c i"

  have "0  sqrt y" using assms by auto
  have "sqrt y  1" using assms by auto
  from even n obtain m where "2 * m = n" by (blast elim: evenE)

  have "arctan (sqrt y)  { ?S n .. ?S (Suc n) }"
  proof (cases "sqrt y = 0")
    case True
    then show ?thesis by simp
  next
    case False
    hence "0 < sqrt y" using 0  sqrt y by auto
    hence prem: "0 < 1 / (0 * 2 + (1::nat)) * sqrt y ^ (0 * 2 + 1)" by auto

    have "¦ sqrt y ¦  1"  using 0  sqrt y sqrt y  1 by auto
    from mp[OF summable_Leibniz(2)[OF zeroseq_arctan_series[OF this]
      monoseq_arctan_series[OF this]] prem, THEN spec, of m, unfolded 2 * m = n]
    show ?thesis unfolding arctan_series[OF ¦ sqrt y ¦  1] Suc_eq_plus1 atLeast0LessThan .
  qed
  note arctan_bounds = this[unfolded atLeastAtMost_iff]

  have F: "n. 2 * Suc n + 1 = 2 * n + 1 + 2" by auto

  note bounds = horner_bounds[where s=1 and f="λi. 2 * i + 1" and j'=0
    and lb="λn i k x. lb_arctan_horner prec n k x"
    and ub="λn i k x. ub_arctan_horner prec n k x",
    OF 0  real_of_float y F lb_arctan_horner.simps ub_arctan_horner.simps]

  have "(sqrt y * lb_arctan_horner prec n 1 y)  arctan (sqrt y)"
  proof -
    have "(sqrt y * lb_arctan_horner prec n 1 y)  ?S n"
      using bounds(1) 0  sqrt y
      apply (simp only: power_add power_one_right mult.assoc[symmetric] sum_distrib_right[symmetric])
      apply (simp only: mult.commute[where 'a=real] mult.commute[of _ "2::nat"] power_mult)
      apply (auto intro!: mult_left_mono)
      done
    also have "  arctan (sqrt y)" using arctan_bounds ..
    finally show ?thesis .
  qed
  moreover
  have "arctan (sqrt y)  (sqrt y * ub_arctan_horner prec (Suc n) 1 y)"
  proof -
    have "arctan (sqrt y)  ?S (Suc n)" using arctan_bounds ..
    also have "  (sqrt y * ub_arctan_horner prec (Suc n) 1 y)"
      using bounds(2)[of "Suc n"] 0  sqrt y
      apply (simp only: power_add power_one_right mult.assoc[symmetric] sum_distrib_right[symmetric])
      apply (simp only: mult.commute[where 'a=real] mult.commute[of _ "2::nat"] power_mult)
      apply (auto intro!: mult_left_mono)
      done
    finally show ?thesis .
  qed
  ultimately show ?thesis by auto
qed

lemma arctan_0_1_bounds:
  assumes "0  real_of_float y" "real_of_float y  1"
  shows "arctan (sqrt y) 
    {(sqrt y * lb_arctan_horner prec (get_even n) 1 y) ..
      (sqrt y * ub_arctan_horner prec (get_odd n) 1 y)}"
  using
    arctan_0_1_bounds'[OF assms, of n prec]
    arctan_0_1_bounds'[OF assms, of "n + 1" prec]
    arctan_0_1_bounds'[OF assms, of "n - 1" prec]
  by (auto simp: get_even_def get_odd_def odd_pos
    simp del: ub_arctan_horner.simps lb_arctan_horner.simps)

lemma arctan_lower_bound:
  assumes "0  x"
  shows "x / (1 + x2)  arctan x" (is "?l x  _")
proof -
  have "?l x - arctan x  ?l 0 - arctan 0"
    using assms
    by (intro DERIV_nonpos_imp_nonincreasing[where f="λx. ?l x - arctan x"])
      (auto intro!: derivative_eq_intros simp: add_nonneg_eq_0_iff field_simps)
  thus ?thesis by simp
qed

lemma arctan_divide_mono: "0 < x  x  y  arctan y / y  arctan x / x"
  by (rule DERIV_nonpos_imp_nonincreasing[where f="λx. arctan x / x"])
    (auto intro!: derivative_eq_intros divide_nonpos_nonneg
      simp: inverse_eq_divide arctan_lower_bound)

lemma arctan_mult_mono: "0  x  x  y  x * arctan y  y * arctan x"
  using arctan_divide_mono[of x y] by (cases "x = 0") (simp_all add: field_simps)

lemma arctan_mult_le:
  assumes "0  x" "x  y" "y * z  arctan y"
  shows "x * z  arctan x"
proof (cases "x = 0")
  case True
  then show ?thesis by simp
next
  case False
  with assms have "z  arctan y / y" by (simp add: field_simps)
  also have "  arctan x / x" using assms x  0 by (auto intro!: arctan_divide_mono)
  finally show ?thesis using assms x  0 by (simp add: field_simps)
qed

lemma arctan_le_mult:
  assumes "0 < x" "x  y" "arctan x  x * z"
  shows "arctan y  y * z"
proof -
  from assms have "arctan y / y  arctan x / x" by (auto intro!: arctan_divide_mono)
  also have "  z" using assms by (auto simp: field_simps)
  finally show ?thesis using assms by (simp add: field_simps)
qed

lemma arctan_0_1_bounds_le:
  assumes "0  x" "x  1" "0 < real_of_float xl" "real_of_float xl  x * x" "x * x  real_of_float xu" "real_of_float xu  1"
  shows "arctan x 
      {x * lb_arctan_horner p1 (get_even n) 1 xu .. x * ub_arctan_horner p2 (get_odd n) 1 xl}"
proof -
  from assms have "real_of_float xl  1" "sqrt (real_of_float xl)  x" "x  sqrt (real_of_float xu)" "0  real_of_float xu"
    "0  real_of_float xl" "0 < sqrt (real_of_float xl)"
    by (auto intro!: real_le_rsqrt real_le_lsqrt simp: power2_eq_square)
  from arctan_0_1_bounds[OF 0  real_of_float xu  real_of_float xu  1]
  have "sqrt (real_of_float xu) * real_of_float (lb_arctan_horner p1 (get_even n) 1 xu)  arctan (sqrt (real_of_float xu))"
    by simp
  from arctan_mult_le[OF 0  x x  sqrt _  this]
  have "x * real_of_float (lb_arctan_horner p1 (get_even n) 1 xu)  arctan x" .
  moreover
  from arctan_0_1_bounds[OF 0  real_of_float xl  real_of_float xl  1]
  have "arctan (sqrt (real_of_float xl))  sqrt (real_of_float xl) * real_of_float (ub_arctan_horner p2 (get_odd n) 1 xl)"
    by simp
  from arctan_le_mult[OF 0 < sqrt xl sqrt xl  x this]
  have "arctan x  x * real_of_float (ub_arctan_horner p2 (get_odd n) 1 xl)" .
  ultimately show ?thesis by simp
qed

lemma arctan_0_1_bounds_round:
  assumes "0  real_of_float x" "real_of_float x  1"
  shows "arctan x 
      {real_of_float x * lb_arctan_horner p1 (get_even n) 1 (float_round_up (Suc p2) (x * x)) ..
        real_of_float x * ub_arctan_horner p3 (get_odd n) 1 (float_round_down (Suc p4) (x * x))}"
  using assms
  apply (cases "x > 0")
   apply (intro arctan_0_1_bounds_le)
   apply (auto simp: float_round_down.rep_eq float_round_up.rep_eq
    intro!: truncate_up_le1 mult_le_one truncate_down_le truncate_up_le truncate_down_pos
      mult_pos_pos)
  done


subsection "Compute π"

definition ub_pi :: "nat  float" where
  "ub_pi prec =
    (let
      A = rapprox_rat prec 1 5 ;
      B = lapprox_rat prec 1 239
    in ((Float 1 2) * float_plus_up prec
      ((Float 1 2) * float_round_up prec (A * (ub_arctan_horner prec (get_odd (prec div 4 + 1)) 1
        (float_round_down (Suc prec) (A * A)))))
      (- float_round_down prec (B * (lb_arctan_horner prec (get_even (prec div 14 + 1)) 1
        (float_round_up (Suc prec) (B * B)))))))"

definition lb_pi :: "nat  float" where
  "lb_pi prec =
    (let
      A = lapprox_rat prec 1 5 ;
      B = rapprox_rat prec 1 239
    in ((Float 1 2) * float_plus_down prec
      ((Float 1 2) * float_round_down prec (A * (lb_arctan_horner prec (get_even (prec div 4 + 1)) 1
        (float_round_up (Suc prec) (A * A)))))
      (- float_round_up prec (B * (ub_arctan_horner prec (get_odd (prec div 14 + 1)) 1
        (float_round_down (Suc prec) (B * B)))))))"

lemma pi_boundaries: "pi  {(lb_pi n) .. (ub_pi n)}"
proof -
  have machin_pi: "pi = 4 * (4 * arctan (1 / 5) - arctan (1 / 239))"
    unfolding machin[symmetric] by auto

  {
    fix prec n :: nat
    fix k :: int
    assume "1 < k" hence "0  k" and "0 < k" and "1  k" by auto
    let ?k = "rapprox_rat prec 1 k"
    let ?kl = "float_round_down (Suc prec) (?k * ?k)"
    have "1 div k = 0" using div_pos_pos_trivial[OF _ 1 < k] by auto

    have "0  real_of_float ?k" by (rule order_trans[OF _ rapprox_rat]) (auto simp add: 0  k)
    have "real_of_float ?k  1"
      by (auto simp add: 0 < k 1  k less_imp_le
        intro!: mult_le_one order_trans[OF _ rapprox_rat] rapprox_rat_le1)
    have "1 / k  ?k" using rapprox_rat[where x=1 and y=k] by auto
    hence "arctan (1 / k)  arctan ?k" by (rule arctan_monotone')
    also have "  (?k * ub_arctan_horner prec (get_odd n) 1 ?kl)"
      using arctan_0_1_bounds_round[OF 0  real_of_float ?k real_of_float ?k  1]
      by auto
    finally have "arctan (1 / k)  ?k * ub_arctan_horner prec (get_odd n) 1 ?kl" .
  } note ub_arctan = this

  {
    fix prec n :: nat
    fix k :: int
    assume "1 < k" hence "0  k" and "0 < k" by auto
    let ?k = "lapprox_rat prec 1 k"
    let ?ku = "float_round_up (Suc prec) (?k * ?k)"
    have "1 div k = 0" using div_pos_pos_trivial[OF _ 1 < k] by auto
    have "1 / k  1" using 1 < k by auto
    have "0  real_of_float ?k" using lapprox_rat_nonneg[where x=1 and y=k, OF zero_le_one 0  k]
      by (auto simp add: 1 div k = 0)
    have "0  real_of_float (?k * ?k)" by simp
    have "real_of_float ?k  1" using lapprox_rat by (rule order_trans, auto simp add: 1 / k  1)
    hence "real_of_float (?k * ?k)  1" using 0  real_of_float ?k by (auto intro!: mult_le_one)

    have "?k  1 / k" using lapprox_rat[where x=1 and y=k] by auto

    have "?k * lb_arctan_horner prec (get_even n) 1 ?ku  arctan ?k"
      using arctan_0_1_bounds_round[OF 0  real_of_float ?k real_of_float ?k  1]
      by auto
    also have "  arctan (1 / k)" using ?k  1 / k by (rule arctan_monotone')
    finally have "?k * lb_arctan_horner prec (get_even n) 1 ?ku  arctan (1 / k)" .
  } note lb_arctan = this

  have "pi  ub_pi n "
    unfolding ub_pi_def machin_pi Let_def times_float.rep_eq Float_num
    using lb_arctan[of 239] ub_arctan[of 5] powr_realpow[of 2 2]
    by (intro mult_left_mono float_plus_up_le float_plus_down_le)
      (auto intro!: mult_left_mono float_round_down_le float_round_up_le diff_mono)
  moreover have "lb_pi n  pi"
    unfolding lb_pi_def machin_pi Let_def times_float.rep_eq Float_num
    using lb_arctan[of 5] ub_arctan[of 239]
    by (intro mult_left_mono float_plus_up_le float_plus_down_le)
      (auto intro!: mult_left_mono float_round_down_le float_round_up_le diff_mono)
  ultimately show ?thesis by auto
qed

lift_definition pi_float_interval::"nat  float interval" is "λprec. (lb_pi prec, ub_pi prec)"
  using pi_boundaries
  by (auto intro: order_trans)

lemma lower_pi_float_interval: "lower (pi_float_interval prec) = lb_pi prec"
  by transfer auto
lemma upper_pi_float_interval: "upper (pi_float_interval prec) = ub_pi prec"
  by transfer auto
lemma pi_float_interval: "pi  set_of (real_interval (pi_float_interval prec))"
  using pi_boundaries
  by (auto simp: set_of_eq lower_pi_float_interval upper_pi_float_interval)


subsection "Compute arcus tangens in the entire domain"

function lb_arctan :: "nat  float  float" and ub_arctan :: "nat  float  float" where
  "lb_arctan prec x =
    (let
      ub_horner = λ x. float_round_up prec
        (x *
          ub_arctan_horner prec (get_odd (prec div 4 + 1)) 1 (float_round_down (Suc prec) (x * x)));
      lb_horner = λ x. float_round_down prec
        (x *
          lb_arctan_horner prec (get_even (prec div 4 + 1)) 1 (float_round_up (Suc prec) (x * x)))
    in
      if x < 0 then - ub_arctan prec (-x)
      else if x  Float 1 (- 1) then lb_horner x
      else if x  Float 1 1 then
        Float 1 1 *
        lb_horner
          (float_divl prec x
            (float_plus_up prec 1
              (ub_sqrt prec (float_plus_up prec 1 (float_round_up prec (x * x))))))
      else let inv = float_divr prec 1 x in
        if inv > 1 then 0
        else float_plus_down prec (lb_pi prec * Float 1 (- 1)) ( - ub_horner inv))"

| "ub_arctan prec x =
    (let
      lb_horner = λ x. float_round_down prec
        (x *
          lb_arctan_horner prec (get_even (prec div 4 + 1)) 1 (float_round_up (Suc prec) (x * x))) ;
      ub_horner = λ x. float_round_up prec
        (x *
          ub_arctan_horner prec (get_odd (prec div 4 + 1)) 1 (float_round_down (Suc prec) (x * x)))
    in if x < 0 then - lb_arctan prec (-x)
    else if x  Float 1 (- 1) then ub_horner x
    else if x  Float 1 1 then
      let y = float_divr prec x
        (float_plus_down
          (Suc prec) 1 (lb_sqrt prec (float_plus_down prec 1 (float_round_down prec (x * x)))))
      in if y > 1 then ub_pi prec * Float 1 (- 1) else Float 1 1 * ub_horner y
    else float_plus_up prec (ub_pi prec * Float 1 (- 1)) ( - lb_horner (float_divl prec 1 x)))"
by pat_completeness auto
termination
by (relation "measure (λ v. let (prec, x) = case_sum id id v in (if x < 0 then 1 else 0))", auto)

declare ub_arctan_horner.simps[simp del]
declare lb_arctan_horner.simps[simp del]

lemma lb_arctan_bound':
  assumes "0  real_of_float x"
  shows "lb_arctan prec x  arctan x"
proof -
  have "¬ x < 0" and "0  x"
    using 0  real_of_float x by (auto intro!: truncate_up_le )

  let "?ub_horner x" =
      "x * ub_arctan_horner prec (get_odd (prec div 4 + 1)) 1 (float_round_down (Suc prec) (x * x))"
    and "?lb_horner x" =
      "x * lb_arctan_horner prec (get_even (prec div 4 + 1)) 1 (float_round_up (Suc prec) (x * x))"

  show ?thesis
  proof (cases "x  Float 1 (- 1)")
    case True
    hence "real_of_float x  1" by simp
    from arctan_0_1_bounds_round[OF 0  real_of_float x real_of_float x  1]
    show ?thesis
      unfolding lb_arctan.simps Let_def if_not_P[OF ¬ x < 0] if_P[OF True] using 0  x
      by (auto intro!: float_round_down_le)
  next
    case False
    hence "0 < real_of_float x" by auto
    let ?R = "1 + sqrt (1 + real_of_float x * real_of_float x)"
    let ?sxx = "float_plus_up prec 1 (float_round_up prec (x * x))"
    let ?fR = "float_plus_up prec 1 (ub_sqrt prec ?sxx)"
    let ?DIV = "float_divl prec x ?fR"

    have divisor_gt0: "0 < ?R" by (auto intro: add_pos_nonneg)

    have "sqrt (1 + x*x)  sqrt ?sxx"
      by (auto simp: float_plus_up.rep_eq plus_up_def float_round_up.rep_eq intro!: truncate_up_le)
    also have "  ub_sqrt prec ?sxx"
      using bnds_sqrt'[of ?sxx prec] by auto
    finally
    have "sqrt (1 + x*x)  ub_sqrt prec ?sxx" .
    hence "?R  ?fR" by (auto simp: float_plus_up.rep_eq plus_up_def intro!: truncate_up_le)
    hence "0 < ?fR" and "0 < real_of_float ?fR" using 0 < ?R by auto

    have monotone: "?DIV  x / ?R"
    proof -
      have "?DIV  real_of_float x / ?fR" by (rule float_divl)
      also have "  x / ?R" by (rule divide_left_mono[OF ?R  ?fR 0  real_of_float x mult_pos_pos[OF order_less_le_trans[OF divisor_gt0 ?R  real_of_float ?fR] divisor_gt0]])
      finally show ?thesis .
    qed

    show ?thesis
    proof (cases "x  Float 1 1")
      case True
      have "x  sqrt (1 + x * x)"
        using real_sqrt_sum_squares_ge2[where x=1, unfolded numeral_2_eq_2] by auto
      also note   (ub_sqrt prec ?sxx)
      finally have "real_of_float x  ?fR"
        by (auto simp: float_plus_up.rep_eq plus_up_def intro!: truncate_up_le)
      moreover have "?DIV  real_of_float x / ?fR"
        by (rule float_divl)
      ultimately have "real_of_float ?DIV  1"
        unfolding divide_le_eq_1_pos[OF 0 < real_of_float ?fR, symmetric] by auto

      have "0  real_of_float ?DIV"
        using float_divl_lower_bound[OF 0  x] 0 < ?fR
        unfolding less_eq_float_def by auto

      from arctan_0_1_bounds_round[OF 0  real_of_float (?DIV) real_of_float (?DIV)  1]
      have "Float 1 1 * ?lb_horner ?DIV  2 * arctan ?DIV"
        by simp
      also have "  2 * arctan (x / ?R)"
        using arctan_monotone'[OF monotone] by (auto intro!: mult_left_mono arctan_monotone')
      also have "2 * arctan (x / ?R) = arctan x"
        using arctan_half[symmetric] unfolding numeral_2_eq_2 power_Suc2 power_0 mult_1_left .
      finally show ?thesis
        unfolding lb_arctan.simps Let_def if_not_P[OF ¬ x < 0]
          if_not_P[OF ¬ x  Float 1 (- 1)] if_P[OF True]
        by (auto simp: float_round_down.rep_eq
          intro!: order_trans[OF mult_left_mono[OF truncate_down]])
    next
      case False
      hence "2 < real_of_float x" by auto
      hence "1  real_of_float x" by auto

      let "?invx" = "float_divr prec 1 x"
      have "0  arctan x" using arctan_monotone'[OF 0  real_of_float x]
        using arctan_tan[of 0, unfolded tan_zero] by auto

      show ?thesis
      proof (cases "1 < ?invx")
        case True
        show ?thesis
          unfolding lb_arctan.simps Let_def if_not_P[OF ¬ x < 0]
            if_not_P[OF ¬ x  Float 1 (- 1)] if_not_P[OF False] if_P[OF True]
          using 0  arctan x by auto
      next
        case False
        hence "real_of_float ?invx  1" by auto
        have "0  real_of_float ?invx"
          by (rule order_trans[OF _ float_divr]) (auto simp add: 0  real_of_float x)

        have "1 / x  0" and "0 < 1 / x"
          using 0 < real_of_float x by auto

        have "arctan (1 / x)  arctan ?invx"
          unfolding one_float.rep_eq[symmetric] by (rule arctan_monotone', rule float_divr)
        also have "  ?ub_horner ?invx"
          using arctan_0_1_bounds_round[OF 0  real_of_float ?invx real_of_float ?invx  1]
          by (auto intro!: float_round_up_le)
        also note float_round_up
        finally have "pi / 2 - float_round_up prec (?ub_horner ?invx)  arctan x"
          using 0  arctan x arctan_inverse[OF 1 / x  0]
          unfolding sgn_pos[OF 0 < 1 / real_of_float x] le_diff_eq by auto
        moreover
        have "lb_pi prec * Float 1 (- 1)  pi / 2"
          unfolding Float_num times_divide_eq_right mult_1_left using pi_boundaries by simp
        ultimately
        show ?thesis
          unfolding lb_arctan.simps Let_def if_not_P[OF ¬ x < 0]
            if_not_P[OF ¬ x  Float 1 (- 1)] if_not_P[OF ¬ x  Float 1 1] if_not_P[OF False]
          by (auto intro!: float_plus_down_le)
      qed
    qed
  qed
qed

lemma ub_arctan_bound':
  assumes "0  real_of_float x"
  shows "arctan x  ub_arctan prec x"
proof -
  have "¬ x < 0" and "0  x"
    using 0  real_of_float x by auto

  let "?ub_horner x" =
    "float_round_up prec (x * ub_arctan_horner prec (get_odd (prec div 4 + 1)) 1 (float_round_down (Suc prec) (x * x)))"
  let "?lb_horner x" =
    "float_round_down prec (x * lb_arctan_horner prec (get_even (prec div 4 + 1)) 1 (float_round_up (Suc prec) (x * x)))"

  show ?thesis
  proof (cases "x  Float 1 (- 1)")
    case True
    hence "real_of_float x  1" by auto
    show ?thesis
      unfolding ub_arctan.simps Let_def if_not_P[OF ¬ x < 0] if_P[OF True]
      using arctan_0_1_bounds_round[OF 0  real_of_float x real_of_float x  1]
      by (auto intro!: float_round_up_le)
  next
    case False
    hence "0 < real_of_float x" by auto
    let ?R = "1 + sqrt (1 + real_of_float x * real_of_float x)"
    let ?sxx = "float_plus_down prec 1 (float_round_down prec (x * x))"
    let ?fR = "float_plus_down (Suc prec) 1 (lb_sqrt prec ?sxx)"
    let ?DIV = "float_divr prec x ?fR"

    have sqr_ge0: "0  1 + real_of_float x * real_of_float x"
      using sum_power2_ge_zero[of 1 "real_of_float x", unfolded numeral_2_eq_2] by auto
    hence "0  real_of_float (1 + x*x)" by auto

    hence divisor_gt0: "0 < ?R" by (auto intro: add_pos_nonneg)

    have "lb_sqrt prec ?sxx  sqrt ?sxx"
      using bnds_sqrt'[of ?sxx] by auto
    also have "  sqrt (1 + x*x)"
      by (auto simp: float_plus_down.rep_eq plus_down_def float_round_down.rep_eq truncate_down_le)
    finally have "lb_sqrt prec ?sxx  sqrt (1 + x*x)" .
    hence "?fR  ?R"
      by (auto simp: float_plus_down.rep_eq plus_down_def truncate_down_le)
    have "0 < real_of_float ?fR"
      by (auto simp: float_plus_down.rep_eq plus_down_def float_round_down.rep_eq
        intro!: truncate_down_ge1 lb_sqrt_lower_bound order_less_le_trans[OF zero_less_one]
        truncate_down_nonneg add_nonneg_nonneg)
    have monotone: "x / ?R  (float_divr prec x ?fR)"
    proof -
      from divide_left_mono[OF ?fR  ?R 0  real_of_float x mult_pos_pos[OF divisor_gt0 0 < real_of_float ?fR]]
      have "x / ?R  x / ?fR" .
      also have "  ?DIV" by (rule float_divr)
      finally show ?thesis .
    qed

    show ?thesis
    proof (cases "x  Float 1 1")
      case True
      show ?thesis
      proof (cases "?DIV > 1")
        case True
        have "pi / 2  ub_pi prec * Float 1 (- 1)"
          unfolding Float_num times_divide_eq_right mult_1_left using pi_boundaries by auto
        from order_less_le_trans[OF arctan_ubound this, THEN less_imp_le]
        show ?thesis
          unfolding ub_arctan.simps Let_def if_not_P[OF ¬ x < 0]
            if_not_P[OF ¬ x  Float 1 (- 1)] if_P[OF x  Float 1 1] if_P[OF True] .
      next
        case False
        hence "real_of_float ?DIV  1" by auto

        have "0  x / ?R"
          using 0  real_of_float x 0 < ?R unfolding zero_le_divide_iff by auto
        hence "0  real_of_float ?DIV"
          using monotone by (rule order_trans)

        have "arctan x = 2 * arctan (x / ?R)"
          using arctan_half unfolding numeral_2_eq_2 power_Suc2 power_0 mult_1_left .
        also have "  2 * arctan (?DIV)"
          using arctan_monotone'[OF monotone] by (auto intro!: mult_left_mono)
        also have "  (Float 1 1 * ?ub_horner ?DIV)" unfolding Float_num
          using arctan_0_1_bounds_round[OF 0  real_of_float ?DIV real_of_float ?DIV  1]
          by (auto intro!: float_round_up_le)
        finally show ?thesis
          unfolding ub_arctan.simps Let_def if_not_P[OF ¬ x < 0]
            if_not_P[OF ¬ x  Float 1 (- 1)] if_P[OF x  Float 1 1] if_not_P[OF False] .
      qed
    next
      case False
      hence "2 < real_of_float x" by auto
      hence "1  real_of_float x" by auto
      hence "0 < real_of_float x" by auto
      hence "0 < x" by auto

      let "?invx" = "float_divl prec 1 x"
      have "0  arctan x"
        using arctan_monotone'[OF 0  real_of_float x] and arctan_tan[of 0, unfolded tan_zero] by auto

      have "real_of_float ?invx  1"
        unfolding less_float_def
        by (rule order_trans[OF float_divl])
          (auto simp add: 1  real_of_float x divide_le_eq_1_pos[OF 0 < real_of_float x])
      have "0  real_of_float ?invx"
        using 0 < x by (intro float_divl_lower_bound) auto

      have "1 / x  0" and "0 < 1 / x"
        using 0 < real_of_float x by auto

      have "(?lb_horner ?invx)  arctan (?invx)"
        using arctan_0_1_bounds_round[OF 0  real_of_float ?invx real_of_float ?invx  1]
        by (auto intro!: float_round_down_le)
      also have "  arctan (1 / x)"
        unfolding one_float.rep_eq[symmetric] by (rule arctan_monotone') (rule float_divl)
      finally have "arctan x  pi / 2 - (?lb_horner ?invx)"
        using 0  arctan x arctan_inverse[OF 1 / x  0]
        unfolding sgn_pos[OF 0 < 1 / x] le_diff_eq by auto
      moreover
      have "pi / 2  ub_pi prec * Float 1 (- 1)"
        unfolding Float_num times_divide_eq_right mult_1_right
        using pi_boundaries by auto
      ultimately
      show ?thesis
        unfolding ub_arctan.simps Let_def if_not_P[OF ¬ x < 0]
          if_not_P[OF ¬ x  Float 1 (- 1)] if_not_P[OF False]
        by (auto intro!: float_round_up_le float_plus_up_le)
    qed
  qed
qed

lemma arctan_boundaries: "arctan x  {(lb_arctan prec x) .. (ub_arctan prec x)}"
proof (cases "0  x")
  case True
  hence "0  real_of_float x" by auto
  show ?thesis
    using ub_arctan_bound'[OF 0  real_of_float x] lb_arctan_bound'[OF 0  real_of_float x]
    unfolding atLeastAtMost_iff by auto
next
  case False
  let ?mx = "-x"
  from False have "x < 0" and "0  real_of_float ?mx"
    by auto
  hence bounds: "lb_arctan prec ?mx  arctan ?mx  arctan ?mx  ub_arctan prec ?mx"
    using ub_arctan_bound'[OF 0  real_of_float ?mx] lb_arctan_bound'[OF 0  real_of_float ?mx] by auto
  show ?thesis
    unfolding minus_float.rep_eq arctan_minus lb_arctan.simps[where x=x]
      ub_arctan.simps[where x=x] Let_def if_P[OF x < 0]
    unfolding atLeastAtMost_iff using bounds[unfolded minus_float.rep_eq arctan_minus]
    by (simp add: arctan_minus)
qed

lemma bnds_arctan: " (x::real) lx ux. (l, u) = (lb_arctan prec lx, ub_arctan prec ux)  x  {lx .. ux}  l  arctan x  arctan x  u"
proof (rule allI, rule allI, rule allI, rule impI)
  fix x :: real
  fix lx ux
  assume "(l, u) = (lb_arctan prec lx, ub_arctan prec ux)  x  {lx .. ux}"
  hence l: "lb_arctan prec lx = l "
    and u: "ub_arctan prec ux = u"
    and x: "x  {lx .. ux}"
    by auto
  show "l  arctan x  arctan x  u"
  proof
    show "l  arctan x"
    proof -
      from arctan_boundaries[of lx prec, unfolded l]
      have "l  arctan lx" by (auto simp del: lb_arctan.simps)
      also have "  arctan x" using x by (auto intro: arctan_monotone')
      finally show ?thesis .
    qed
    show "arctan x  u"
    proof -
      have "arctan x  arctan ux" using x by (auto intro: arctan_monotone')
      also have "  u" using arctan_boundaries[of ux prec, unfolded u] by (auto simp del: ub_arctan.simps)
      finally show ?thesis .
    qed
  qed
qed

lemmas [simp del] = lb_arctan.simps ub_arctan.simps

lemma lb_arctan: "arctan (real_of_float x)  y  real_of_float (lb_arctan prec x)  y"
  and ub_arctan: "y  arctan x  y  ub_arctan prec x"
  for x::float and y::real
  using arctan_boundaries[of x prec] by auto

lift_definition arctan_float_interval :: "nat  float interval  float interval"
  is "λprec. λ(lx, ux). (lb_arctan prec lx, ub_arctan prec ux)"
  by (auto intro!: lb_arctan ub_arctan arctan_monotone')

lemma lower_arctan_float_interval: "lower (arctan_float_interval p x) = lb_arctan p (lower x)"
  by transfer auto
lemma upper_arctan_float_interval: "upper (arctan_float_interval p x) = ub_arctan p (upper x)"
  by transfer auto

lemma arctan_float_interval:
  "arctan ` set_of (real_interval x)  set_of (real_interval (arctan_float_interval p x))"
  by (auto simp: set_of_eq lower_arctan_float_interval upper_arctan_float_interval
      intro!: lb_arctan ub_arctan arctan_monotone')

lemma arctan_float_intervalI:
  "arctan x r arctan_float_interval p X" if "x r X"
  using arctan_float_interval[of X p] that
  by auto


section "Sinus and Cosinus"

subsection "Compute the cosinus and sinus series"

fun ub_sin_cos_aux :: "nat  nat  nat  nat  float  float"
and lb_sin_cos_aux :: "nat  nat  nat  nat  float  float" where
  "ub_sin_cos_aux prec 0 i k x = 0"
| "ub_sin_cos_aux prec (Suc n) i k x = float_plus_up prec
    (rapprox_rat prec 1 k) (-
      float_round_down prec (x * (lb_sin_cos_aux prec n (i + 2) (k * i * (i + 1)) x)))"
| "lb_sin_cos_aux prec 0 i k x = 0"
| "lb_sin_cos_aux prec (Suc n) i k x = float_plus_down prec
    (lapprox_rat prec 1 k) (-
      float_round_up prec (x * (ub_sin_cos_aux prec n (i + 2) (k * i * (i + 1)) x)))"

lemma cos_aux:
  shows "(lb_sin_cos_aux prec n 1 1 (x * x))  ( i=0..<n. (- 1) ^ i * (1/(fact (2 * i))) * x ^(2 * i))" (is "?lb")
  and "( i=0..<n. (- 1) ^ i * (1/(fact (2 * i))) * x^(2 * i))  (ub_sin_cos_aux prec n 1 1 (x * x))" (is "?ub")
proof -
  have "0  real_of_float (x * x)" by auto
  let "?f n" = "fact (2 * n) :: nat"
  have f_eq: "?f (Suc n) = ?f n * ((λi. i + 2) ^^ n) 1 * (((λi. i + 2) ^^ n) 1 + 1)" for n
  proof -
    have "m. ((λi. i + 2) ^^ n) m = m + 2 * n" by (induct n) auto
    then show ?thesis by auto
  qed
  from horner_bounds[where lb="lb_sin_cos_aux prec" and ub="ub_sin_cos_aux prec" and j'=0,
    OF 0  real_of_float (x * x) f_eq lb_sin_cos_aux.simps ub_sin_cos_aux.simps]
  show ?lb and ?ub
    by (auto simp add: power_mult power2_eq_square[of "real_of_float x"])
qed

lemma lb_sin_cos_aux_zero_le_one: "lb_sin_cos_aux prec n i j 0  1"
  by (cases j n rule: nat.exhaust[case_product nat.exhaust])
    (auto intro!: float_plus_down_le order_trans[OF lapprox_rat])

lemma one_le_ub_sin_cos_aux: "odd n  1  ub_sin_cos_aux prec n i (Suc 0) 0"
  by (cases n) (auto intro!: float_plus_up_le order_trans[OF _ rapprox_rat])

lemma cos_boundaries:
  assumes "0  real_of_float x" and "x  pi / 2"
  shows "cos x  {(lb_sin_cos_aux prec (get_even n) 1 1 (x * x)) .. (ub_sin_cos_aux prec (get_odd n) 1 1 (x * x))}"
proof (cases "real_of_float x = 0")
  case False
  hence "real_of_float x  0" by auto
  hence "0 < x" and "0 < real_of_float x"
    using 0  real_of_float x by auto
  have "0 < x * x"
    using 0 < x by simp

  have morph_to_if_power: "( i=0..<n. (-1::real) ^ i * (1/(fact (2 * i))) * x ^ (2 * i)) =
    ( i = 0 ..< 2 * n. (if even(i) then ((- 1) ^ (i div 2))/((fact i)) else 0) * x ^ i)"
    (is "?sum = ?ifsum") for x n
  proof -
    have "?sum = ?sum + ( j = 0 ..< n. 0)" by auto
    also have " =
      ( j = 0 ..< n. (- 1) ^ ((2 * j) div 2) / ((fact (2 * j))) * x ^(2 * j)) + ( j = 0 ..< n. 0)" by auto
    also have " = ( i = 0 ..< 2 * n. if even i then (- 1) ^ (i div 2) / ((fact i)) * x ^ i else 0)"
      unfolding sum_split_even_odd atLeast0LessThan ..
    also have " = ( i = 0 ..< 2 * n. (if even i then (- 1) ^ (i div 2) / ((fact i)) else 0) * x ^ i)"
      by (rule sum.cong) auto
    finally show ?thesis .
  qed

  { fix n :: nat assume "0 < n"
    hence "0 < 2 * n" by auto
    obtain t where "0 < t" and "t < real_of_float x" and
      cos_eq: "cos x = ( i = 0 ..< 2 * n. (if even(i) then ((- 1) ^ (i div 2))/((fact i)) else 0) * (real_of_float x) ^ i)
      + (cos (t + 1/2 * (2 * n) * pi) / (fact (2*n))) * (real_of_float x)^(2*n)"
      (is "_ = ?SUM + ?rest / ?fact * ?pow")
      using Maclaurin_cos_expansion2[OF 0 < real_of_float x 0 < 2 * n]
      unfolding cos_coeff_def atLeast0LessThan by auto

    have "cos t * (- 1) ^ n = cos t * cos (n * pi) + sin t * sin (n * pi)" by auto
    also have " = cos (t + n * pi)" by (simp add: cos_add)
    also have " = ?rest" by auto
    finally have "cos t * (- 1) ^ n = ?rest" .
    moreover
    have "t  pi / 2" using t < real_of_float x and x  pi / 2 by auto
    hence "0  cos t" using 0 < t and cos_ge_zero by auto
    ultimately have even: "even n  0  ?rest" and odd: "odd n  0  - ?rest " by auto

    have "0 < ?fact" by auto
    have "0 < ?pow" using 0 < real_of_float x by auto

    {
      assume "even n"
      have "(lb_sin_cos_aux prec n 1 1 (x * x))  ?SUM"
        unfolding morph_to_if_power[symmetric] using cos_aux by auto
      also have "  cos x"
      proof -
        from even[OF even n] 0 < ?fact 0 < ?pow
        have "0  (?rest / ?fact) * ?pow" by simp
        thus ?thesis unfolding cos_eq by auto
      qed
      finally have "(lb_sin_cos_aux prec n 1 1 (x * x))  cos x" .
    } note lb = this

    {
      assume "odd n"
      have "cos x  ?SUM"
      proof -
        from 0 < ?fact and 0 < ?pow and odd[OF odd n]
        have "0  (- ?rest) / ?fact * ?pow"
          by (metis mult_nonneg_nonneg divide_nonneg_pos less_imp_le)
        thus ?thesis unfolding cos_eq by auto
      qed
      also have "  (ub_sin_cos_aux prec n 1 1 (x * x))"
        unfolding morph_to_if_power[symmetric] using cos_aux by auto
      finally have "cos x  (ub_sin_cos_aux prec n 1 1 (x * x))" .
    } note ub = this and lb
  } note ub = this(1) and lb = this(2)

  have "cos x  (ub_sin_cos_aux prec (get_odd n) 1 1 (x * x))"
    using ub[OF odd_pos[OF get_odd] get_odd] .
  moreover have "(lb_sin_cos_aux prec (get_even n) 1 1 (x * x))  cos x"
  proof (cases "0 < get_even n")
    case True
    show ?thesis using lb[OF True get_even] .
  next
    case False
    hence "get_even n = 0" by auto
    have "- (pi / 2)  x"
      by (rule order_trans[OF _ 0 < real_of_float x[THEN less_imp_le]]) auto
    with x  pi / 2 show ?thesis
      unfolding get_even n = 0 lb_sin_cos_aux.simps minus_float.rep_eq zero_float.rep_eq
      using cos_ge_zero by auto
  qed
  ultimately show ?thesis by auto
next
  case True
  hence "x = 0"
    by (simp add: real_of_float_eq)
  thus ?thesis
    using lb_sin_cos_aux_zero_le_one one_le_ub_sin_cos_aux
    by simp
qed

lemma sin_aux:
  assumes "0  real_of_float x"
  shows "(x * lb_sin_cos_aux prec n 2 1 (x * x)) 
      ( i=0..<n. (- 1) ^ i * (1/(fact (2 * i + 1))) * x^(2 * i + 1))" (is "?lb")
    and "( i=0..<n. (- 1) ^ i * (1/(fact (2 * i + 1))) * x^(2 * i + 1)) 
      (x * ub_sin_cos_aux prec n 2 1 (x * x))" (is "?ub")
proof -
  have "0  real_of_float (x * x)" by auto
  let "?f n" = "fact (2 * n + 1) :: nat"
  have f_eq: "?f (Suc n) = ?f n * ((λi. i + 2) ^^ n) 2 * (((λi. i + 2) ^^ n) 2 + 1)" for n
  proof -
    have F: "m. ((λi. i + 2) ^^ n) m = m + 2 * n" by (induct n) auto
    show ?thesis
      unfolding F by auto
  qed
  from horner_bounds[where lb="lb_sin_cos_aux prec" and ub="ub_sin_cos_aux prec" and j'=0,
    OF 0  real_of_float (x * x) f_eq lb_sin_cos_aux.simps ub_sin_cos_aux.simps]
  show "?lb" and "?ub" using 0  real_of_float x
    apply (simp_all only: power_add power_one_right mult.assoc[symmetric] sum_distrib_right[symmetric])
    apply (simp_all only: mult.commute[where 'a=real] of_nat_fact)
    apply (auto intro!: mult_left_mono simp add: power_mult power2_eq_square[of "real_of_float x"])
    done
qed

lemma sin_boundaries:
  assumes "0  real_of_float x"
    and "x  pi / 2"
  shows "sin x  {(x * lb_sin_cos_aux prec (get_even n) 2 1 (x * x)) .. (x * ub_sin_cos_aux prec (get_odd n) 2 1 (x * x))}"
proof (cases "real_of_float x = 0")
  case False
  hence "real_of_float x  0" by auto
  hence "0 < x" and "0 < real_of_float x"
    using 0  real_of_float x by auto
  have "0 < x * x"
    using 0 < x by simp

  have sum_morph: "(j = 0 ..< n. (- 1) ^ (((2 * j + 1) - Suc 0) div 2) / ((fact (2 * j + 1))) * x ^(2 * j + 1)) =
    ( i = 0 ..< 2 * n. (if even(i) then 0 else ((- 1) ^ ((i - Suc 0) div 2))/((fact i))) * x ^ i)"
    (is "?SUM = _") for x :: real and n
  proof -
    have pow: "!!i. x ^ (2 * i + 1) = x * x ^ (2 * i)"
      by auto
    have "?SUM = ( j = 0 ..< n. 0) + ?SUM"
      by auto
    also have " = ( i = 0 ..< 2 * n. if even i then 0 else (- 1) ^ ((i - Suc 0) div 2) / ((fact i)) * x ^ i)"
      unfolding sum_split_even_odd atLeast0LessThan ..
    also have " = ( i = 0 ..< 2 * n. (if even i then 0 else (- 1) ^ ((i - Suc 0) div 2) / ((fact i))) * x ^ i)"
      by (rule sum.cong) auto
    finally show ?thesis .
  qed

  { fix n :: nat assume "0 < n"
    hence "0 < 2 * n + 1" by auto
    obtain t where "0 < t" and "t < real_of_float x" and
      sin_eq: "sin x = ( i = 0 ..< 2 * n + 1. (if even(i) then 0 else ((- 1) ^ ((i - Suc 0) div 2))/((fact i))) * (real_of_float x) ^ i)
      + (sin (t + 1/2 * (2 * n + 1) * pi) / (fact (2*n + 1))) * (real_of_float x)^(2*n + 1)"
      (is "_ = ?SUM + ?rest / ?fact * ?pow")
      using Maclaurin_sin_expansion3[OF 0 < 2 * n + 1 0 < real_of_float x]
      unfolding sin_coeff_def atLeast0LessThan by auto

    have "?rest = cos t * (- 1) ^ n"
      unfolding sin_add cos_add of_nat_add distrib_right distrib_left by auto
    moreover
    have "t  pi / 2"
      using t < real_of_float x and x  pi / 2 by auto
    hence "0  cos t"
      using 0 < t and cos_ge_zero by auto
    ultimately have even: "even n  0  ?rest" and odd: "odd n  0  - ?rest"
      by auto

    have "0 < ?fact"
      by (simp del: fact_Suc)
    have "0 < ?pow"
      using 0 < real_of_float x by (rule zero_less_power)

    {
      assume "even n"
      have "(x * lb_sin_cos_aux prec n 2 1 (x * x)) 
            ( i = 0 ..< 2 * n. (if even(i) then 0 else ((- 1) ^ ((i - Suc 0) div 2))/((fact i))) * (real_of_float x) ^ i)"
        using sin_aux[OF 0  real_of_float x] unfolding sum_morph[symmetric] by auto
      also have "  ?SUM" by auto
      also have "  sin x"
      proof -
        from even[OF even n] 0 < ?fact 0 < ?pow
        have "0  (?rest / ?fact) * ?pow" by simp
        thus ?thesis unfolding sin_eq by auto
      qed
      finally have "(x * lb_sin_cos_aux prec n 2 1 (x * x))  sin x" .
    } note lb = this

    {
      assume "odd n"
      have "sin x  ?SUM"
      proof -
        from 0 < ?fact and 0 < ?pow and odd[OF odd n]
        have "0  (- ?rest) / ?fact * ?pow"
          by (metis mult_nonneg_nonneg divide_nonneg_pos less_imp_le)
        thus ?thesis unfolding sin_eq by auto
      qed
      also have "  ( i = 0 ..< 2 * n. (if even(i) then 0 else ((- 1) ^ ((i - Suc 0) div 2))/((fact i))) * (real_of_float x) ^ i)"
         by auto
      also have "  (x * ub_sin_cos_aux prec n 2 1 (x * x))"
        using sin_aux[OF 0  real_of_float x] unfolding sum_morph[symmetric] by auto
      finally have "sin x  (x * ub_sin_cos_aux prec n 2 1 (x * x))" .
    } note ub = this and lb
  } note ub = this(1) and lb = this(2)

  have "sin x  (x * ub_sin_cos_aux prec (get_odd n) 2 1 (x * x))"
    using ub[OF odd_pos[OF get_odd] get_odd] .
  moreover have "(x * lb_sin_cos_aux prec (get_even n) 2 1 (x * x))  sin x"
  proof (cases "0 < get_even n")
    case True
    show ?thesis
      using lb[OF True get_even] .
  next
    case False
    hence "get_even n = 0" by auto
    with x  pi / 2 0  real_of_float x
    show ?thesis
      unfolding get_even n = 0 ub_sin_cos_aux.simps minus_float.rep_eq
      using sin_ge_zero by auto
  qed
  ultimately show ?thesis by auto
next
  case True
  show ?thesis
  proof (cases "n = 0")
    case True
    thus ?thesis
      unfolding n = 0 get_even_def get_odd_def
      using real_of_float x = 0 lapprox_rat[where x="-1" and y=1] by auto
  next
    case False
    with not0_implies_Suc obtain m where "n = Suc m" by blast
    thus ?thesis
      unfolding n = Suc m get_even_def get_odd_def
      using real_of_float x = 0 rapprox_rat[where x=1 and y=1] lapprox_rat[where x=1 and y=1]
      by (cases "even (Suc m)") auto
  qed
qed


subsection "Compute the cosinus in the entire domain"

definition lb_cos :: "nat  float  float" where
"lb_cos prec x = (let
    horner = λ x. lb_sin_cos_aux prec (get_even (prec div 4 + 1)) 1 1 (x * x) ;
    half = λ x. if x < 0 then - 1 else float_plus_down prec (Float 1 1 * x * x) (- 1)
  in if x < Float 1 (- 1) then horner x
else if x < 1          then half (horner (x * Float 1 (- 1)))
                       else half (half (horner (x * Float 1 (- 2)))))"

definition ub_cos :: "nat  float  float" where
"ub_cos prec x = (let
    horner = λ x. ub_sin_cos_aux prec (get_odd (prec div 4 + 1)) 1 1 (x * x) ;
    half = λ x. float_plus_up prec (Float 1 1 * x * x) (- 1)
  in if x < Float 1 (- 1) then horner x
else if x < 1          then half (horner (x * Float 1 (- 1)))
                       else half (half (horner (x * Float 1 (- 2)))))"

lemma lb_cos:
  assumes "0  real_of_float x" and "x  pi"
  shows "cos x  {(lb_cos prec x) .. (ub_cos prec x)}" (is "?cos x  {(?lb x) .. (?ub x) }")
proof -
  have x_half[symmetric]: "cos x = 2 * cos (x / 2) * cos (x / 2) - 1" for x :: real
  proof -
    have "cos x = cos (x / 2 + x / 2)"
      by auto
    also have " = cos (x / 2) * cos (x / 2) + sin (x / 2) * sin (x / 2) - sin (x / 2) * sin (x / 2) + cos (x / 2) * cos (x / 2) - 1"
      unfolding cos_add by auto
    also have " = 2 * cos (x / 2) * cos (x / 2) - 1"
      by algebra
    finally show ?thesis .
  qed

  have "¬ x < 0" using 0  real_of_float x by auto
  let "?ub_horner x" = "ub_sin_cos_aux prec (get_odd (prec div 4 + 1)) 1 1 (x * x)"
  let "?lb_horner x" = "lb_sin_cos_aux prec (get_even (prec div 4 + 1)) 1 1 (x * x)"
  let "?ub_half x" = "float_plus_up prec (Float 1 1 * x * x) (- 1)"
  let "?lb_half x" = "if x < 0 then - 1 else float_plus_down prec (Float 1 1 * x * x) (- 1)"

  show ?thesis
  proof (cases "x < Float 1 (- 1)")
    case True
    hence "x  pi / 2"
      using pi_ge_two by auto
    show ?thesis
      unfolding lb_cos_def[where x=x] ub_cos_def[where x=x]
        if_not_P[OF ¬ x < 0] if_P[OF x < Float 1 (- 1)] Let_def
      using cos_boundaries[OF 0  real_of_float x x  pi / 2] .
  next
    case False
    { fix y x :: float let ?x2 = "(x * Float 1 (- 1))"
      assume "y  cos ?x2" and "-pi  x" and "x  pi"
      hence "- (pi / 2)  ?x2" and "?x2  pi / 2"
        using pi_ge_two unfolding Float_num by auto
      hence "0  cos ?x2"
        by (rule cos_ge_zero)

      have "(?lb_half y)  cos x"
      proof (cases "y < 0")
        case True
        show ?thesis
          using cos_ge_minus_one unfolding if_P[OF True] by auto
      next
        case False
        hence "0  real_of_float y" by auto
        from mult_mono[OF y  cos ?x2 y  cos ?x2 0  cos ?x2 this]
        have "real_of_float y * real_of_float y  cos ?x2 * cos ?x2" .
        hence "2 * real_of_float y * real_of_float y  2 * cos ?x2 * cos ?x2"
          by auto
        hence "2 * real_of_float y * real_of_float y - 1  2 * cos (x / 2) * cos (x / 2) - 1"
          unfolding Float_num by auto
        thus ?thesis
          unfolding if_not_P[OF False] x_half Float_num
          by (auto intro!: float_plus_down_le)
      qed
    } note lb_half = this

    { fix y x :: float let ?x2 = "(x * Float 1 (- 1))"
      assume ub: "cos ?x2  y" and "- pi  x" and "x  pi"
      hence "- (pi / 2)  ?x2" and "?x2  pi / 2"
        using pi_ge_two unfolding Float_num by auto
      hence "0  cos ?x2" by (rule cos_ge_zero)

      have "cos x  (?ub_half y)"
      proof -
        have "0  real_of_float y"
          using 0  cos ?x2 ub by (rule order_trans)
        from mult_mono[OF ub ub this 0  cos ?x2]
        have "cos ?x2 * cos ?x2  real_of_float y * real_of_float y" .
        hence "2 * cos ?x2 * cos ?x2  2 * real_of_float y * real_of_float y"
          by auto
        hence "2 * cos (x / 2) * cos (x / 2) - 1  2 * real_of_float y * real_of_float y - 1"
          unfolding Float_num by auto
        thus ?thesis
          unfolding x_half Float_num
          by (auto intro!: float_plus_up_le)
      qed
    } note ub_half = this

    let ?x2 = "x * Float 1 (- 1)"
    let ?x4 = "x * Float 1 (- 1) * Float 1 (- 1)"

    have "-pi  x"
      using pi_ge_zero[THEN le_imp_neg_le, unfolded minus_zero] 0  real_of_float x
      by (rule order_trans)

    show ?thesis
    proof (cases "x < 1")
      case True
      hence "real_of_float x  1" by auto
      have "0  real_of_float ?x2" and "?x2  pi / 2"
        using pi_ge_two 0  real_of_float x using assms by auto
      from cos_boundaries[OF this]
      have lb: "(?lb_horner ?x2)  ?cos ?x2" and ub: "?cos ?x2  (?ub_horner ?x2)"
        by auto

      have "(?lb x)  ?cos x"
      proof -
        from lb_half[OF lb -pi  x x  pi]
        show ?thesis
          unfolding lb_cos_def[where x=x] Let_def
          using ¬ x < 0 ¬ x < Float 1 (- 1) x < 1 by auto
      qed
      moreover have "?cos x  (?ub x)"
      proof -
        from ub_half[OF ub -pi  x x  pi]
        show ?thesis
          unfolding ub_cos_def[where x=x] Let_def
          using ¬ x < 0 ¬ x < Float 1 (- 1) x < 1 by auto
      qed
      ultimately show ?thesis by auto
    next
      case False
      have "0  real_of_float ?x4" and "?x4  pi / 2"
        using pi_ge_two 0  real_of_float x x  pi unfolding Float_num by auto
      from cos_boundaries[OF this]
      have lb: "(?lb_horner ?x4)  ?cos ?x4" and ub: "?cos ?x4  (?ub_horner ?x4)"
        by auto

      have eq_4: "?x2 * Float 1 (- 1) = x * Float 1 (- 2)"
        by (auto simp: real_of_float_eq)

      have "(?lb x)  ?cos x"
      proof -
        have "-pi  ?x2" and "?x2  pi"
          using pi_ge_two 0  real_of_float x x  pi by auto
        from lb_half[OF lb_half[OF lb this] -pi  x x  pi, unfolded eq_4]
        show ?thesis
          unfolding lb_cos_def[where x=x] if_not_P[OF ¬ x < 0]
            if_not_P[OF ¬ x < Float 1 (- 1)] if_not_P[OF ¬ x < 1] Let_def .
      qed
      moreover have "?cos x  (?ub x)"
      proof -
        have "-pi  ?x2" and "?x2  pi"
          using pi_ge_two 0  real_of_float x  x  pi by auto
        from ub_half[OF ub_half[OF ub this] -pi  x x  pi, unfolded eq_4]
        show ?thesis
          unfolding ub_cos_def[where x=x] if_not_P[OF ¬ x < 0]
            if_not_P[OF ¬ x < Float 1 (- 1)] if_not_P[OF ¬ x < 1] Let_def .
      qed
      ultimately show ?thesis by auto
    qed
  qed
qed

lemma lb_cos_minus:
  assumes "-pi  x"
    and "real_of_float x  0"
  shows "cos (real_of_float(-x))  {(lb_cos prec (-x)) .. (ub_cos prec (-x))}"
proof -
  have "0  real_of_float (-x)" and "(-x)  pi"
    using -pi  x real_of_float x  0 by auto
  from lb_cos[OF this] show ?thesis .
qed

definition bnds_cos :: "nat  float  float  float * float" where
"bnds_cos prec lx ux = (let
    lpi = float_round_down prec (lb_pi prec) ;
    upi = float_round_up prec (ub_pi prec) ;
    k = floor_fl (float_divr prec (lx + lpi) (2 * lpi)) ;
    lx = float_plus_down prec lx (- k * 2 * (if k < 0 then lpi else upi)) ;
    ux = float_plus_up prec ux (- k * 2 * (if k < 0 then upi else lpi))
  in   if - lpi  lx  ux  0    then (lb_cos prec (-lx), ub_cos prec (-ux))
  else if 0  lx  ux  lpi      then (lb_cos prec ux, ub_cos prec lx)
  else if - lpi  lx  ux  lpi  then (min (lb_cos prec (-lx)) (lb_cos prec ux), Float 1 0)
  else if 0  lx  ux  2 * lpi  then (Float (- 1) 0, max (ub_cos prec lx) (ub_cos prec (- (ux - 2 * lpi))))
  else if -2 * lpi  lx  ux  0 then (Float (- 1) 0, max (ub_cos prec (lx + 2 * lpi)) (ub_cos prec (-ux)))
                                 else (Float (- 1) 0, Float 1 0))"

lemma floor_int: obtains k :: int where "real_of_int k = (floor_fl f)"
  by (simp add: floor_fl_def)

lemma cos_periodic_nat[simp]:
  fixes n :: nat
  shows "cos (x + n * (2 * pi)) = cos x"
proof (induct n arbitrary: x)
  case 0
  then show ?case by simp
next
  case (Suc n)
  have split_pi_off: "x + (Suc n) * (2 * pi) = (x + n * (2 * pi)) + 2 * pi"
    unfolding Suc_eq_plus1 of_nat_add of_int_1 distrib_right by auto
  show ?case
    unfolding split_pi_off using Suc by auto
qed

lemma cos_periodic_int[simp]:
  fixes i :: int
  shows "cos (x + i * (2 * pi)) = cos x"
proof (cases "0  i")
  case True
  hence i_nat: "real_of_int i = nat i" by auto
  show ?thesis
    unfolding i_nat by auto
next
  case False
    hence i_nat: "i = - real (nat (-i))" by auto
  have "cos x = cos (x + i * (2 * pi) - i * (2 * pi))"
    by auto
  also have " = cos (x + i * (2 * pi))"
    unfolding i_nat mult_minus_left diff_minus_eq_add by (rule cos_periodic_nat)
  finally show ?thesis by auto
qed

lemma bnds_cos: "(x::real) lx ux. (l, u) =
  bnds_cos prec lx ux  x  {lx .. ux}  l  cos x  cos x  u"
proof (rule allI | rule impI | erule conjE)+
  fix x :: real
  fix lx ux
  assume bnds: "(l, u) = bnds_cos prec lx ux" and x: "x  {lx .. ux}"

  let ?lpi = "float_round_down prec (lb_pi prec)"
  let ?upi = "float_round_up prec (ub_pi prec)"
  let ?k = "floor_fl (float_divr prec (lx + ?lpi) (2 * ?lpi))"
  let ?lx2 = "(- ?k * 2 * (if ?k < 0 then ?lpi else ?upi))"
  let ?ux2 = "(- ?k * 2 * (if ?k < 0 then ?upi else ?lpi))"
  let ?lx = "float_plus_down prec lx ?lx2"
  let ?ux = "float_plus_up prec ux ?ux2"

  obtain k :: int where k: "k = real_of_float ?k"
    by (rule floor_int)

  have upi: "pi  ?upi" and lpi: "?lpi  pi"
    using float_round_up[of "ub_pi prec" prec] pi_boundaries[of prec]
      float_round_down[of prec "lb_pi prec"]
    by auto
  hence "lx + ?lx2  x - k * (2 * pi)  x - k * (2 * pi)  ux + ?ux2"
    using x
    by (cases "k = 0")
      (auto intro!: add_mono
        simp add: k [symmetric] uminus_add_conv_diff [symmetric]
        simp del: uminus_add_conv_diff)
  hence "?lx  x - k * (2 * pi)  x - k * (2 * pi)  ?ux"
    by (auto intro!: float_plus_down_le float_plus_up_le)
  note lx = this[THEN conjunct1] and ux = this[THEN conjunct2]
  hence lx_less_ux: "?lx  real_of_float ?ux" by (rule order_trans)

  { assume "- ?lpi  ?lx" and x_le_0: "x - k * (2 * pi)  0"
    with lpi[THEN le_imp_neg_le] lx
    have pi_lx: "- pi  ?lx" and lx_0: "real_of_float ?lx  0"
      by simp_all

    have "(lb_cos prec (- ?lx))  cos (real_of_float (- ?lx))"
      using lb_cos_minus[OF pi_lx lx_0] by simp
    also have "  cos (x + (-k) * (2 * pi))"
      using cos_monotone_minus_pi_0'[OF pi_lx lx x_le_0]
      by (simp only: uminus_float.rep_eq of_int_minus
        cos_minus mult_minus_left) simp
    finally have "(lb_cos prec (- ?lx))  cos x"
      unfolding cos_periodic_int . }
  note negative_lx = this

  { assume "0  ?lx" and pi_x: "x - k * (2 * pi)  pi"
    with lx
    have pi_lx: "?lx  pi" and lx_0: "0  real_of_float ?lx"
      by auto

    have "cos (x + (-k) * (2 * pi))  cos ?lx"
      using cos_monotone_0_pi_le[OF lx_0 lx pi_x]
      by (simp only: of_int_minus
        cos_minus mult_minus_left) simp
    also have "  (ub_cos prec ?lx)"
      using lb_cos[OF lx_0 pi_lx] by simp
    finally have "cos x  (ub_cos prec ?lx)"
      unfolding cos_periodic_int . }
  note positive_lx = this

  { assume pi_x: "- pi  x - k * (2 * pi)" and "?ux  0"
    with ux
    have pi_ux: "- pi  ?ux" and ux_0: "real_of_float ?ux  0"
      by simp_all

    have "cos (x + (-k) * (2 * pi))  cos (real_of_float (- ?ux))"
      using cos_monotone_minus_pi_0'[OF pi_x ux ux_0]
      by (simp only: uminus_float.rep_eq of_int_minus
          cos_minus mult_minus_left) simp
    also have "  (ub_cos prec (- ?ux))"
      using lb_cos_minus[OF pi_ux ux_0, of prec] by simp
    finally have "cos x  (ub_cos prec (- ?ux))"
      unfolding cos_periodic_int . }
  note negative_ux = this

  { assume "?ux  ?lpi" and x_ge_0: "0  x - k * (2 * pi)"
    with lpi ux
    have pi_ux: "?ux  pi" and ux_0: "0  real_of_float ?ux"
      by simp_all

    have "(lb_cos prec ?ux)  cos ?ux"
      using lb_cos[OF ux_0 pi_ux] by simp
    also have "  cos (x + (-k) * (2 * pi))"
      using cos_monotone_0_pi_le[OF x_ge_0 ux pi_ux]
      by (simp only: of_int_minus
        cos_minus mult_minus_left) simp
    finally have "(lb_cos prec ?ux)  cos x"
      unfolding cos_periodic_int . }
  note positive_ux = this

  show "l  cos x  cos x  u"
  proof (cases "- ?lpi  ?lx  ?ux  0")
    case True
    with bnds have l: "l = lb_cos prec (-?lx)" and u: "u = ub_cos prec (-?ux)"
      by (auto simp add: bnds_cos_def Let_def)
    from True lpi[THEN le_imp_neg_le] lx ux
    have "- pi  x - k * (2 * pi)" and "x - k * (2 * pi)  0"
      by auto
    with True negative_ux negative_lx show ?thesis
      unfolding l u by simp
  next
    case 1: False
    show ?thesis
    proof (cases "0  ?lx  ?ux  ?lpi")
      case True with bnds 1
      have l: "l = lb_cos prec ?ux"
        and u: "u = ub_cos prec ?lx"
        by (auto simp add: bnds_cos_def Let_def)
      from True lpi lx ux
      have "0  x - k * (2 * pi)" and "x - k * (2 * pi)  pi"
        by auto
      with True positive_ux positive_lx show ?thesis
        unfolding l u by simp
    next
      case 2: False
      show ?thesis
      proof (cases "- ?lpi  ?lx  ?ux  ?lpi")
        case Cond: True
        with bnds 1 2 have l: "l = min (lb_cos prec (-?lx)) (lb_cos prec ?ux)"
          and u: "u = Float 1 0"
          by (auto simp add: bnds_cos_def Let_def)
        show ?thesis
          unfolding u l using negative_lx positive_ux Cond
          by (cases "x - k * (2 * pi) < 0") (auto simp add: real_of_float_min)
      next
        case 3: False
        show ?thesis
        proof (cases "0  ?lx  ?ux  2 * ?lpi")
          case Cond: True
          with bnds 1 2 3
          have l: "l = Float (- 1) 0"
            and u: "u = max (ub_cos prec ?lx) (ub_cos prec (- (?ux - 2 * ?lpi)))"
            by (auto simp add: bnds_cos_def Let_def)

          have "cos x  real_of_float u"
          proof (cases "x - k * (2 * pi) < pi")
            case True
            hence "x - k * (2 * pi)  pi" by simp
            from positive_lx[OF Cond[THEN conjunct1] this] show ?thesis
              unfolding u by (simp add: real_of_float_max)
          next
            case False
            hence "pi  x - k * (2 * pi)" by simp
            hence pi_x: "- pi  x - k * (2 * pi) - 2 * pi" by simp

            have "?ux  2 * pi"
              using Cond lpi by auto
            hence "x - k * (2 * pi) - 2 * pi  0"
              using ux by simp

            have ux_0: "real_of_float (?ux - 2 * ?lpi)  0"
              using Cond by auto

            from 2 and Cond have "¬ ?ux  ?lpi" by auto
            hence "- ?lpi  ?ux - 2 * ?lpi" by auto
            hence pi_ux: "- pi  (?ux - 2 * ?lpi)"
              using lpi[THEN le_imp_neg_le] by auto

            have x_le_ux: "x - k * (2 * pi) - 2 * pi  (?ux - 2 * ?lpi)"
              using ux lpi by auto
            have "cos x = cos (x + (-k) * (2 * pi) + (-1::int) * (2 * pi))"
              unfolding cos_periodic_int ..
            also have "  cos ((?ux - 2 * ?lpi))"
              using cos_monotone_minus_pi_0'[OF pi_x x_le_ux ux_0]
              by (simp only: minus_float.rep_eq of_int_minus of_int_1
                mult_minus_left mult_1_left) simp
            also have " = cos ((- (?ux - 2 * ?lpi)))"
              unfolding uminus_float.rep_eq cos_minus ..
            also have "  (ub_cos prec (- (?ux - 2 * ?lpi)))"
              using lb_cos_minus[OF pi_ux ux_0] by simp
            finally show ?thesis unfolding u by (simp add: real_of_float_max)
          qed
          thus ?thesis unfolding l by auto
        next
          case 4: False
          show ?thesis
          proof (cases "-2 * ?lpi  ?lx  ?ux  0")
            case Cond: True
            with bnds 1 2 3 4 have l: "l = Float (- 1) 0"
              and u: "u = max (ub_cos prec (?lx + 2 * ?lpi)) (ub_cos prec (-?ux))"
              by (auto simp add: bnds_cos_def Let_def)

            have "cos x  u"
            proof (cases "-pi < x - k * (2 * pi)")
              case True
              hence "-pi  x - k * (2 * pi)" by simp
              from negative_ux[OF this Cond[THEN conjunct2]] show ?thesis
                unfolding u by (simp add: real_of_float_max)
            next
              case False
              hence "x - k * (2 * pi)  -pi" by simp
              hence pi_x: "x - k * (2 * pi) + 2 * pi  pi" by simp

              have "-2 * pi  ?lx" using Cond lpi by auto

              hence "0  x - k * (2 * pi) + 2 * pi" using lx by simp

              have lx_0: "0  real_of_float (?lx + 2 * ?lpi)"
                using Cond lpi by auto

              from 1 and Cond have "¬ -?lpi  ?lx" by auto
              hence "?lx + 2 * ?lpi  ?lpi" by auto
              hence pi_lx: "(?lx + 2 * ?lpi)  pi"
                using lpi[THEN le_imp_neg_le] by auto

              have lx_le_x: "(?lx + 2 * ?lpi)  x - k * (2 * pi) + 2 * pi"
                using lx lpi by auto

              have "cos x = cos (x + (-k) * (2 * pi) + (1 :: int) * (2 * pi))"
                unfolding cos_periodic_int ..
              also have "  cos ((?lx + 2 * ?lpi))"
                using cos_monotone_0_pi_le[OF lx_0 lx_le_x pi_x]
                by (simp only: minus_float.rep_eq of_int_minus of_int_1
                  mult_minus_left mult_1_left) simp
              also have "  (ub_cos prec (?lx + 2 * ?lpi))"
                using lb_cos[OF lx_0 pi_lx] by simp
              finally show ?thesis unfolding u by (simp add: real_of_float_max)
            qed
            thus ?thesis unfolding l by auto
          next
            case False
            with bnds 1 2 3 4 show ?thesis
              by (auto simp add: bnds_cos_def Let_def)
          qed
        qed
      qed
    qed
  qed
qed

lemma bnds_cos_lower: "x. real_of_float xl  x  x  real_of_float xu  cos x  y  real_of_float (fst (bnds_cos prec xl xu))  y"
  and bnds_cos_upper: "x. real_of_float xl  x  x  real_of_float xu  y  cos x  y  real_of_float (snd (bnds_cos prec xl xu))"
  for xl xu::float and y::real
  using bnds_cos[of "fst (bnds_cos prec xl xu)" "snd (bnds_cos prec xl xu)" prec]
  by force+

lift_definition cos_float_interval :: "nat  float interval  float interval"
  is "λprec. λ(lx, ux). bnds_cos prec lx ux"
  using bnds_cos
  by auto (metis (full_types) order_refl order_trans)

lemma lower_cos_float_interval: "lower (cos_float_interval p x) = fst (bnds_cos p (lower x) (upper x))"
  by transfer auto
lemma upper_cos_float_interval: "upper (cos_float_interval p x) = snd (bnds_cos p (lower x) (upper x))"
  by transfer auto

lemma cos_float_interval:
  "cos ` set_of (real_interval x)  set_of (real_interval (cos_float_interval p x))"
  by (auto simp: set_of_eq bnds_cos_lower bnds_cos_upper lower_cos_float_interval
      upper_cos_float_interval)

lemma cos_float_intervalI: "cos x r cos_float_interval p X" if "x r X"
  using cos_float_interval[of X p] that
  by auto


section "Exponential function"

subsection "Compute the series of the exponential function"

fun ub_exp_horner :: "nat  nat  nat  nat  float  float"
  and lb_exp_horner :: "nat  nat  nat  nat  float  float"
where
"ub_exp_horner prec 0 i k x       = 0" |
"ub_exp_horner prec (Suc n) i k x = float_plus_up prec
    (rapprox_rat prec 1 (int k)) (float_round_up prec (x * lb_exp_horner prec n (i + 1) (k * i) x))" |
"lb_exp_horner prec 0 i k x       = 0" |
"lb_exp_horner prec (Suc n) i k x = float_plus_down prec
    (lapprox_rat prec 1 (int k)) (float_round_down prec (x * ub_exp_horner prec n (i + 1) (k * i) x))"

lemma bnds_exp_horner:
  assumes "real_of_float x  0"
  shows "exp x  {lb_exp_horner prec (get_even n) 1 1 x .. ub_exp_horner prec (get_odd n) 1 1 x}"
proof -
  have f_eq: "fact (Suc n) = fact n * ((λi::nat. i + 1) ^^ n) 1" for n
  proof -
    have F: " m. ((λi. i + 1) ^^ n) m = n + m"
      by (induct n) auto
    show ?thesis
      unfolding F by auto
  qed

  note bounds = horner_bounds_nonpos[where f="fact" and lb="lb_exp_horner prec" and ub="ub_exp_horner prec" and j'=0 and s=1,
    OF assms f_eq lb_exp_horner.simps ub_exp_horner.simps]

  have "lb_exp_horner prec (get_even n) 1 1 x  exp x"
  proof -
    have "lb_exp_horner prec (get_even n) 1 1 x  (j = 0..<get_even n. 1 / (fact j) * real_of_float x ^ j)"
      using bounds(1) by auto
    also have "  exp x"
    proof -
      obtain t where "¦t¦  ¦real_of_float x¦" and "exp x = (m = 0..<get_even n. real_of_float x ^ m / (fact m)) + exp t / (fact (get_even n)) * (real_of_float x) ^ (get_even n)"
        using Maclaurin_exp_le unfolding atLeast0LessThan by blast
      moreover have "0  exp t / (fact (get_even n)) * (real_of_float x) ^ (get_even n)"
        by (auto simp: zero_le_even_power)
      ultimately show ?thesis using get_odd exp_gt_zero by auto
    qed
    finally show ?thesis .
  qed
  moreover
  have "exp x  ub_exp_horner prec (get_odd n) 1 1 x"
  proof -
    have x_less_zero: "real_of_float x ^ get_odd n  0"
    proof (cases "real_of_float x = 0")
      case True
      have "(get_odd n)  0" using get_odd[THEN odd_pos] by auto
      thus ?thesis unfolding True power_0_left by auto
    next
      case False hence "real_of_float x < 0" using real_of_float x  0 by auto
      show ?thesis by (rule less_imp_le, auto simp add: real_of_float x < 0)
    qed
    obtain t where "¦t¦  ¦real_of_float x¦"
      and "exp x = (m = 0..<get_odd n. (real_of_float x) ^ m / (fact m)) + exp t / (fact (get_odd n)) * (real_of_float x) ^ (get_odd n)"
      using Maclaurin_exp_le unfolding atLeast0LessThan by blast
    moreover have "exp t / (fact (get_odd n)) * (real_of_float x) ^ (get_odd n)  0"
      by (auto intro!: mult_nonneg_nonpos divide_nonpos_pos simp add: x_less_zero)
    ultimately have "exp x  (j = 0..<get_odd n. 1 / (fact j) * real_of_float x ^ j)"
      using get_odd exp_gt_zero by auto
    also have "  ub_exp_horner prec (get_odd n) 1 1 x"
      using bounds(2) by auto
    finally show ?thesis .
  qed
  ultimately show ?thesis by auto
qed

lemma ub_exp_horner_nonneg: "real_of_float x  0 
  0  real_of_float (ub_exp_horner prec (get_odd n) (Suc 0) (Suc 0) x)"
  using bnds_exp_horner[of x prec n]
  by (intro order_trans[OF exp_ge_zero]) auto


subsection "Compute the exponential function on the entire domain"

function ub_exp :: "nat  float  float" and lb_exp :: "nat  float  float" where
"lb_exp prec x =
  (if 0 < x then float_divl prec 1 (ub_exp prec (-x))
  else
    let
      horner = (λ x. let  y = lb_exp_horner prec (get_even (prec + 2)) 1 1 x in
        if y  0 then Float 1 (- 2) else y)
    in
      if x < - 1 then
        power_down_fl prec (horner (float_divl prec x (- floor_fl x))) (nat (- int_floor_fl x))
      else horner x)" |
"ub_exp prec x =
  (if 0 < x then float_divr prec 1 (lb_exp prec (-x))
  else if x < - 1 then
    power_up_fl prec
      (ub_exp_horner prec (get_odd (prec + 2)) 1 1
        (float_divr prec x (- floor_fl x))) (nat (- int_floor_fl x))
  else ub_exp_horner prec (get_odd (prec + 2)) 1 1 x)"
  by pat_completeness auto
termination
  by (relation "measure (λ v. let (prec, x) = case_sum id id v in (if 0 < x then 1 else 0))") auto

lemma exp_m1_ge_quarter: "(1 / 4 :: real)  exp (- 1)"
proof -
  have eq4: "4 = Suc (Suc (Suc (Suc 0)))" by auto
  have "1 / 4 = (Float 1 (- 2))"
    unfolding Float_num by auto
  also have "  lb_exp_horner 3 (get_even 3) 1 1 (- 1)"
    by (subst less_eq_float.rep_eq [symmetric]) code_simp
  also have "  exp (- 1 :: float)"
    using bnds_exp_horner[where x="- 1"] by auto
  finally show ?thesis
    by simp
qed

lemma lb_exp_pos:
  assumes "¬ 0 < x"
  shows "0 < lb_exp prec x"
proof -
  let "?lb_horner x" = "lb_exp_horner prec (get_even (prec + 2)) 1 1 x"
  let "?horner x" = "let y = ?lb_horner x in if y  0 then Float 1 (- 2) else y"
  have pos_horner: "0 < ?horner x" for x
    unfolding Let_def by (cases "?lb_horner x  0") auto
  moreover have "0 < real_of_float ((?horner x) ^ num)" for x :: float and num :: nat
  proof -
    have "0 < real_of_float (?horner x) ^ num" using 0 < ?horner x by simp
    also have " = (?horner x) ^ num" by auto
    finally show ?thesis .
  qed
  ultimately show ?thesis
    unfolding lb_exp.simps if_not_P[OF ¬ 0 < x] Let_def
    by (cases "floor_fl x", cases "x < - 1")
      (auto simp: real_power_up_fl real_power_down_fl intro!: power_up_less power_down_pos)
qed

lemma exp_boundaries':
  assumes "x  0"
  shows "exp x  { (lb_exp prec x) .. (ub_exp prec x)}"
proof -
  let "?lb_exp_horner x" = "lb_exp_horner prec (get_even (prec + 2)) 1 1 x"
  let "?ub_exp_horner x" = "ub_exp_horner prec (get_odd (prec + 2)) 1 1 x"

  have "real_of_float x  0" and "¬ x > 0"
    using x  0 by auto
  show ?thesis
  proof (cases "x < - 1")
    case False
    hence "- 1  real_of_float x" by auto
    show ?thesis
    proof (cases "?lb_exp_horner x  0")
      case True
      from ¬ x < - 1
      have "- 1  real_of_float x" by auto
      hence "exp (- 1)  exp x"
        unfolding exp_le_cancel_iff .
      from order_trans[OF exp_m1_ge_quarter this] have "Float 1 (- 2)  exp x"
        unfolding Float_num .
      with True show ?thesis
        using bnds_exp_horner real_of_float x  0 ¬ x > 0 ¬ x < - 1 by auto
    next
      case False
      thus ?thesis
        using bnds_exp_horner real_of_float x  0 ¬ x > 0 ¬ x < - 1 by (auto simp add: Let_def)
    qed
  next
    case True
    let ?num = "nat (- int_floor_fl x)"

    have "real_of_int (int_floor_fl x) < - 1"
      using int_floor_fl[of x] x < - 1 by simp
    hence "real_of_int (int_floor_fl x) < 0" by simp
    hence "int_floor_fl x < 0" by auto
    hence "1  - int_floor_fl x" by auto
    hence "0 < nat (- int_floor_fl x)" by auto
    hence "0 < ?num"  by auto
    hence "real ?num  0" by auto
    have num_eq: "real ?num = - int_floor_fl x"
      using 0 < nat (- int_floor_fl x) by auto
    have "0 < - int_floor_fl x"
      using 0 < ?num[unfolded of_nat_less_iff[symmetric]] by simp
    hence "real_of_int (int_floor_fl x) < 0"
      unfolding less_float_def by auto
    have fl_eq: "real_of_int (- int_floor_fl x) = real_of_float (- floor_fl x)"
      by (simp add: floor_fl_def int_floor_fl_def)
    from 0 < - int_floor_fl x have "0  real_of_float (- floor_fl x)"
      by (simp add: floor_fl_def int_floor_fl_def)
    from real_of_int (int_floor_fl x) < 0 have "real_of_float (floor_fl x) < 0"
      by (simp add: floor_fl_def int_floor_fl_def)
    have "exp x  ub_exp prec x"
    proof -
      have div_less_zero: "real_of_float (float_divr prec x (- floor_fl x))  0"
        using float_divr_nonpos_pos_upper_bound[OF real_of_float x  0 0  real_of_float (- floor_fl x)]
        unfolding less_eq_float_def zero_float.rep_eq .

      have "exp x = exp (?num * (x / ?num))"
        using real ?num  0 by auto
      also have " = exp (x / ?num) ^ ?num"
        unfolding exp_of_nat_mult ..
      also have "  exp (float_divr prec x (- floor_fl x)) ^ ?num"
        unfolding num_eq fl_eq
        by (rule power_mono, rule exp_le_cancel_iff[THEN iffD2], rule float_divr) auto
      also have "  (?ub_exp_horner (float_divr prec x (- floor_fl x))) ^ ?num"
        unfolding real_of_float_power
        by (rule power_mono, rule bnds_exp_horner[OF div_less_zero, unfolded atLeastAtMost_iff, THEN conjunct2], auto)
      also have "  real_of_float (power_up_fl prec (?ub_exp_horner (float_divr prec x (- floor_fl x))) ?num)"
        by (auto simp add: real_power_up_fl intro!: power_up ub_exp_horner_nonneg div_less_zero)
      finally show ?thesis
        unfolding ub_exp.simps if_not_P[OF ¬ 0 < x] if_P[OF x < - 1] floor_fl_def Let_def .
    qed
    moreover
    have "lb_exp prec x  exp x"
    proof -
      let ?divl = "float_divl prec x (- floor_fl x)"
      let ?horner = "?lb_exp_horner ?divl"

      show ?thesis
      proof (cases "?horner  0")
        case False
        hence "0  real_of_float ?horner" by auto

        have div_less_zero: "real_of_float (float_divl prec x (- floor_fl x))  0"
          using real_of_float (floor_fl x) < 0 real_of_float x  0
          by (auto intro!: order_trans[OF float_divl] divide_nonpos_neg)

        have "(?lb_exp_horner (float_divl prec x (- floor_fl x))) ^ ?num 
          exp (float_divl prec x (- floor_fl x)) ^ ?num"
          using 0  real_of_float ?horner[unfolded floor_fl_def[symmetric]]
            bnds_exp_horner[OF div_less_zero, unfolded atLeastAtMost_iff, THEN conjunct1]
          by (auto intro!: power_mono)
        also have "  exp (x / ?num) ^ ?num"
          unfolding num_eq fl_eq
          using float_divl by (auto intro!: power_mono simp del: uminus_float.rep_eq)
        also have " = exp (?num * (x / ?num))"
          unfolding exp_of_nat_mult ..
        also have " = exp x"
          using real ?num  0 by auto
        finally show ?thesis
          using False
          unfolding lb_exp.simps if_not_P[OF ¬ 0 < x] if_P[OF x < - 1]
            int_floor_fl_def Let_def if_not_P[OF False]
          by (auto simp: real_power_down_fl intro!: power_down_le)
      next
        case True
        have "power_down_fl prec (Float 1 (- 2))  ?num  (Float 1 (- 2)) ^ ?num"
          by (metis Float_le_zero_iff less_imp_le linorder_not_less
            not_numeral_le_zero numeral_One power_down_fl)
        then have "power_down_fl prec (Float 1 (- 2))  ?num  real_of_float (Float 1 (- 2)) ^ ?num"
          by simp
        also
        have "real_of_float (floor_fl x)  0" and "real_of_float (floor_fl x)  0"
          using real_of_float (floor_fl x) < 0 by auto
        from divide_right_mono_neg[OF floor_fl[of x] real_of_float (floor_fl x)  0, unfolded divide_self[OF real_of_float (floor_fl x)  0]]
        have "- 1  x / (- floor_fl x)"
          unfolding minus_float.rep_eq by auto
        from order_trans[OF exp_m1_ge_quarter this[unfolded exp_le_cancel_iff[where x="- 1", symmetric]]]
        have "Float 1 (- 2)  exp (x / (- floor_fl x))"
          unfolding Float_num .
        hence "real_of_float (Float 1 (- 2)) ^ ?num  exp (x / (- floor_fl x)) ^ ?num"
          by (metis Float_num(5) power_mono zero_le_divide_1_iff zero_le_numeral)
        also have " = exp x"
          unfolding num_eq fl_eq exp_of_nat_mult[symmetric]
          using real_of_float (floor_fl x)  0 by auto
        finally show ?thesis
          unfolding lb_exp.simps if_not_P[OF ¬ 0 < x] if_P[OF x < - 1]
            int_floor_fl_def Let_def if_P[OF True] real_of_float_power .
      qed
    qed
    ultimately show ?thesis by auto
  qed
qed

lemma exp_boundaries: "exp x  { lb_exp prec x .. ub_exp prec x }"
proof -
  show ?thesis
  proof (cases "0 < x")
    case False
    hence "x  0" by auto
    from exp_boundaries'[OF this] show ?thesis .
  next
    case True
    hence "-x  0" by auto

    have "lb_exp prec x  exp x"
    proof -
      from exp_boundaries'[OF -x  0]
      have ub_exp: "exp (- real_of_float x)  ub_exp prec (-x)"
        unfolding atLeastAtMost_iff minus_float.rep_eq by auto

      have "float_divl prec 1 (ub_exp prec (-x))  1 / ub_exp prec (-x)"
        using float_divl[where x=1] by auto
      also have "  exp x"
        using ub_exp[unfolded inverse_le_iff_le[OF order_less_le_trans[OF exp_gt_zero ub_exp]
          exp_gt_zero, symmetric]]
        unfolding exp_minus nonzero_inverse_inverse_eq[OF exp_not_eq_zero] inverse_eq_divide
        by auto
      finally show ?thesis
        unfolding lb_exp.simps if_P[OF True] .
    qed
    moreover
    have "exp x  ub_exp prec x"
    proof -
      have "¬ 0 < -x" using 0 < x by auto

      from exp_boundaries'[OF -x  0]
      have lb_exp: "lb_exp prec (-x)  exp (- real_of_float x)"
        unfolding atLeastAtMost_iff minus_float.rep_eq by auto

      have "exp x  (1 :: float) / lb_exp prec (-x)"
        using lb_exp lb_exp_pos[OF ¬ 0 < -x, of prec]
        by (simp del: lb_exp.simps add: exp_minus field_simps)
      also have "  float_divr prec 1 (lb_exp prec (-x))"
        using float_divr .
      finally show ?thesis
        unfolding ub_exp.simps if_P[OF True] .
    qed
    ultimately show ?thesis
      by auto
  qed
qed

lemma bnds_exp: "(x::real) lx ux. (l, u) =
  (lb_exp prec lx, ub_exp prec ux)  x  {lx .. ux}  l  exp x  exp x  u"
proof (rule allI, rule allI, rule allI, rule impI)
  fix x :: real and lx ux
  assume "(l, u) = (lb_exp prec lx, ub_exp prec ux)  x  {lx .. ux}"
  hence l: "lb_exp prec lx = l " and u: "ub_exp prec ux = u" and x: "x  {lx .. ux}"
    by auto
  show "l  exp x  exp x  u"
  proof
    show "l  exp x"
    proof -
      from exp_boundaries[of lx prec, unfolded l]
      have "l  exp lx" by (auto simp del: lb_exp.simps)
      also have "  exp x" using x by auto
      finally show ?thesis .
    qed
    show "exp x  u"
    proof -
      have "exp x  exp ux" using x by auto
      also have "  u" using exp_boundaries[of ux prec, unfolded u] by (auto simp del: ub_exp.simps)
      finally show ?thesis .
    qed
  qed
qed

lemmas [simp del] = lb_exp.simps ub_exp.simps

lemma lb_exp: "exp x  y  lb_exp prec x  y"
  and ub_exp: "y  exp x  y  ub_exp prec x"
  for x::float and y::real using exp_boundaries[of x prec] by auto

lift_definition exp_float_interval :: "nat  float interval  float interval"
  is "λprec. λ(lx, ux). (lb_exp prec lx, ub_exp prec ux)"
  by (auto simp: lb_exp ub_exp)

lemma lower_exp_float_interval: "lower (exp_float_interval p x) = lb_exp p (lower x)"
  by transfer auto
lemma upper_exp_float_interval: "upper (exp_float_interval p x) = ub_exp p (upper x)"
  by transfer auto

lemma exp_float_interval:
  "exp ` set_of (real_interval x)  set_of (real_interval (exp_float_interval p x))"
  using exp_boundaries
  by (auto simp: set_of_eq lower_exp_float_interval upper_exp_float_interval lb_exp ub_exp)

lemma exp_float_intervalI:
  "exp x r exp_float_interval p X" if "x r X"
  using exp_float_interval[of X p] that
  by auto


section "Logarithm"

subsection "Compute the logarithm series"

fun ub_ln_horner :: "nat  nat  nat  float  float"
and lb_ln_horner :: "nat  nat  nat  float  float" where
"ub_ln_horner prec 0 i x       = 0" |
"ub_ln_horner prec (Suc n) i x = float_plus_up prec
    (rapprox_rat prec 1 (int i)) (- float_round_down prec (x * lb_ln_horner prec n (Suc i) x))" |
"lb_ln_horner prec 0 i x       = 0" |
"lb_ln_horner prec (Suc n) i x = float_plus_down prec
    (lapprox_rat prec 1 (int i)) (- float_round_up prec (x * ub_ln_horner prec n (Suc i) x))"

lemma ln_bounds:
  assumes "0  x"
    and "x < 1"
  shows "(i=0..<2*n. (- 1) ^ i * (1 / real (i + 1)) * x ^ (Suc i))  ln (x + 1)" (is "?lb")
  and "ln (x + 1)  (i=0..<2*n + 1. (- 1) ^ i * (1 / real (i + 1)) * x ^ (Suc i))" (is "?ub")
proof -
  let "?a n" = "(1/real (n +1)) * x ^ (Suc n)"

  have ln_eq: "( i. (- 1) ^ i * ?a i) = ln (x + 1)"
    using ln_series[of "x + 1"] 0  x x < 1 by auto

  have "norm x < 1" using assms by auto
  have "?a  0" unfolding Suc_eq_plus1[symmetric] inverse_eq_divide[symmetric]
    using tendsto_mult[OF LIMSEQ_inverse_real_of_nat LIMSEQ_Suc[OF LIMSEQ_power_zero[OF norm x < 1]]] by auto
  have "0  ?a n" for n
    by (rule mult_nonneg_nonneg) (auto simp: 0  x)
  have "?a (Suc n)  ?a n" for n
    unfolding inverse_eq_divide[symmetric]
  proof (rule mult_mono)
    show "0  x ^ Suc (Suc n)"
      by (auto simp add: 0  x)
    have "x ^ Suc (Suc n)  x ^ Suc n * 1"
      unfolding power_Suc2 mult.assoc[symmetric]
      by (rule mult_left_mono, fact less_imp_le[OF x < 1]) (auto simp: 0  x)
    thus "x ^ Suc (Suc n)  x ^ Suc n" by auto
  qed auto
  from summable_Leibniz'(2,4)[OF ?a  0 n. 0  ?a n, OF n. ?a (Suc n)  ?a n, unfolded ln_eq]
  show ?lb and ?ub
    unfolding atLeast0LessThan by auto
qed

lemma ln_float_bounds:
  assumes "0  real_of_float x"
    and "real_of_float x < 1"
  shows "x * lb_ln_horner prec (get_even n) 1 x  ln (x + 1)" (is "?lb  ?ln")
    and "ln (x + 1)  x * ub_ln_horner prec (get_odd n) 1 x" (is "?ln  ?ub")
proof -
  obtain ev where ev: "get_even n = 2 * ev" using get_even_double ..
  obtain od where od: "get_odd n = 2 * od + 1" using get_odd_double ..

  let "?s n" = "(- 1) ^ n * (1 / real (1 + n)) * (real_of_float x)^(Suc n)"

  have "?lb  sum ?s {0 ..< 2 * ev}"
    unfolding power_Suc2 mult.assoc[symmetric] times_float.rep_eq sum_distrib_right[symmetric]
    unfolding mult.commute[of "real_of_float x"] ev 
    using horner_bounds(1)[where G="λ i k. Suc k" and F="λx. x" and f="λx. x" 
                    and lb="λn i k x. lb_ln_horner prec n k x" 
                    and ub="λn i k x. ub_ln_horner prec n k x" and j'=1 and n="2*ev",
      OF 0  real_of_float x refl lb_ln_horner.simps ub_ln_horner.simps] 0  real_of_float x
    unfolding real_of_float_power
    by (rule mult_right_mono)
  also have "  ?ln"
    using ln_bounds(1)[OF 0  real_of_float x real_of_float x < 1] by auto
  finally show "?lb  ?ln" .

  have "?ln  sum ?s {0 ..< 2 * od + 1}"
    using ln_bounds(2)[OF 0  real_of_float x real_of_float x < 1] by auto
  also have "  ?ub"
    unfolding power_Suc2 mult.assoc[symmetric] times_float.rep_eq sum_distrib_right[symmetric]
    unfolding mult.commute[of "real_of_float x"] od
    using horner_bounds(2)[where G="λ i k. Suc k" and F="λx. x" and f="λx. x" and lb="λn i k x. lb_ln_horner prec n k x" and ub="λn i k x. ub_ln_horner prec n k x" and j'=1 and n="2 * od + 1",
      OF 0  real_of_float x refl lb_ln_horner.simps ub_ln_horner.simps] 0  real_of_float x
    unfolding real_of_float_power
    by (rule mult_right_mono)
  finally show "?ln  ?ub" .
qed

lemma ln_add:
  fixes x :: real
  assumes "0 < x" and "0 < y"
  shows "ln (x + y) = ln x + ln (1 + y / x)"
proof -
  have "x  0" using assms by auto
  have "x + y = x * (1 + y / x)"
    unfolding distrib_left times_divide_eq_right nonzero_mult_div_cancel_left[OF x  0]
    by auto
  moreover
  have "0 < y / x" using assms by auto
  hence "0 < 1 + y / x" by auto
  ultimately show ?thesis
    using ln_mult assms by auto
qed


subsection "Compute the logarithm of 2"

definition ub_ln2 where "ub_ln2 prec = (let third = rapprox_rat (max prec 1) 1 3
                                        in float_plus_up prec
                                          ((Float 1 (- 1) * ub_ln_horner prec (get_odd prec) 1 (Float 1 (- 1))))
                                           (float_round_up prec (third * ub_ln_horner prec (get_odd prec) 1 third)))"
definition lb_ln2 where "lb_ln2 prec = (let third = lapprox_rat prec 1 3
                                        in float_plus_down prec
                                          ((Float 1 (- 1) * lb_ln_horner prec (get_even prec) 1 (Float 1 (- 1))))
                                           (float_round_down prec (third * lb_ln_horner prec (get_even prec) 1 third)))"

lemma ub_ln2: "ln 2  ub_ln2 prec" (is "?ub_ln2")
  and lb_ln2: "lb_ln2 prec  ln 2" (is "?lb_ln2")
proof -
  let ?uthird = "rapprox_rat (max prec 1) 1 3"
  let ?lthird = "lapprox_rat prec 1 3"

  have ln2_sum: "ln 2 = ln (1/2 + 1) + ln (1 / 3 + 1::real)"
    using ln_add[of "3 / 2" "1 / 2"] by auto
  have lb3: "?lthird  1 / 3" using lapprox_rat[of prec 1 3] by auto
  hence lb3_ub: "real_of_float ?lthird < 1" by auto
  have lb3_lb: "0  real_of_float ?lthird" using lapprox_rat_nonneg[of 1 3] by auto
  have ub3: "1 / 3  ?uthird" using rapprox_rat[of 1 3] by auto
  hence ub3_lb: "0  real_of_float ?uthird" by auto

  have lb2: "0  real_of_float (Float 1 (- 1))" and ub2: "real_of_float (Float 1 (- 1)) < 1"
    unfolding Float_num by auto

  have "0  (1::int)" and "0 < (3::int)" by auto
  have ub3_ub: "real_of_float ?uthird < 1"
    by (simp add: Float.compute_rapprox_rat Float.compute_lapprox_rat rapprox_posrat_less1)

  have third_gt0: "(0 :: real) < 1 / 3 + 1" by auto
  have uthird_gt0: "0 < real_of_float ?uthird + 1" using ub3_lb by auto
  have lthird_gt0: "0 < real_of_float ?lthird + 1" using lb3_lb by auto

  show ?ub_ln2
    unfolding ub_ln2_def Let_def ln2_sum Float_num(4)[symmetric]
  proof (rule float_plus_up_le, rule add_mono, fact ln_float_bounds(2)[OF lb2 ub2])
    have "ln (1 / 3 + 1)  ln (real_of_float ?uthird + 1)"
      unfolding ln_le_cancel_iff[OF third_gt0 uthird_gt0] using ub3 by auto
    also have "  ?uthird * ub_ln_horner prec (get_odd prec) 1 ?uthird"
      using ln_float_bounds(2)[OF ub3_lb ub3_ub] .
    also note float_round_up
    finally show "ln (1 / 3 + 1)  float_round_up prec (?uthird * ub_ln_horner prec (get_odd prec) 1 ?uthird)" .
  qed
  show ?lb_ln2
    unfolding lb_ln2_def Let_def ln2_sum Float_num(4)[symmetric]
  proof (rule float_plus_down_le, rule add_mono, fact ln_float_bounds(1)[OF lb2 ub2])
    have "?lthird * lb_ln_horner prec (get_even prec) 1 ?lthird  ln (real_of_float ?lthird + 1)"
      using ln_float_bounds(1)[OF lb3_lb lb3_ub] .
    note float_round_down_le[OF this]
    also have "  ln (1 / 3 + 1)"
      unfolding ln_le_cancel_iff[OF lthird_gt0 third_gt0]
      using lb3 by auto
    finally show "float_round_down prec (?lthird * lb_ln_horner prec (get_even prec) 1 ?lthird) 
      ln (1 / 3 + 1)" .
  qed
qed


subsection "Compute the logarithm in the entire domain"

function ub_ln :: "nat  float  float option" and lb_ln :: "nat  float  float option" where
"ub_ln prec x = (if x  0          then None
            else if x < 1          then Some (- the (lb_ln prec (float_divl (max prec 1) 1 x)))
            else let horner = λx. float_round_up prec (x * ub_ln_horner prec (get_odd prec) 1 x) in
                 if x  Float 3 (- 1) then Some (horner (x - 1))
            else if x < Float 1 1  then Some (float_round_up prec (horner (Float 1 (- 1)) + horner (x * rapprox_rat prec 2 3 - 1)))
                                   else let l = bitlen (mantissa x) - 1 in
                                        Some (float_plus_up prec (float_round_up prec (ub_ln2 prec * (Float (exponent x + l) 0))) (horner (Float (mantissa x) (- l) - 1))))" |
"lb_ln prec x = (if x  0          then None
            else if x < 1          then Some (- the (ub_ln prec (float_divr prec 1 x)))
            else let horner = λx. float_round_down prec (x * lb_ln_horner prec (get_even prec) 1 x) in
                 if x  Float 3 (- 1) then Some (horner (x - 1))
            else if x < Float 1 1  then Some (float_round_down prec (horner (Float 1 (- 1)) +
                                              horner (max (x * lapprox_rat prec 2 3 - 1) 0)))
                                   else let l = bitlen (mantissa x) - 1 in
                                        Some (float_plus_down prec (float_round_down prec (lb_ln2 prec * (Float (exponent x + l) 0))) (horner (Float (mantissa x) (- l) - 1))))"
  by pat_completeness auto

termination
proof (relation "measure (λ v. let (prec, x) = case_sum id id v in (if x < 1 then 1 else 0))", auto)
  fix prec and x :: float
  assume "¬ real_of_float x  0" and "real_of_float x < 1" and "real_of_float (float_divl (max prec (Suc 0)) 1 x) < 1"
  hence "0 < real_of_float x" "1  max prec (Suc 0)" "real_of_float x < 1"
    by auto
  from float_divl_pos_less1_bound[OF 0 < real_of_float x real_of_float x < 1[THEN less_imp_le] 1  max prec (Suc 0)]
  show False
    using real_of_float (float_divl (max prec (Suc 0)) 1 x) < 1 by auto
next
  fix prec x
  assume "¬ real_of_float x  0" and "real_of_float x < 1" and "real_of_float (float_divr prec 1 x) < 1"
  hence "0 < x" by auto
  from float_divr_pos_less1_lower_bound[OF 0 < x, of prec] real_of_float x < 1 show False
    using real_of_float (float_divr prec 1 x) < 1 by auto
qed

lemmas float_pos_eq_mantissa_pos = mantissa_pos_iff[symmetric]

lemma Float_pos_eq_mantissa_pos: "Float m e > 0  m > 0"
  using powr_gt_zero[of 2 "e"]
  by (auto simp add: zero_less_mult_iff zero_float_def simp del: powr_gt_zero dest: less_zeroE)

lemma Float_representation_aux:
  fixes m e
  defines [THEN meta_eq_to_obj_eq]: "x  Float m e"
  assumes "x > 0"
  shows "Float (exponent x + (bitlen (mantissa x) - 1)) 0 = Float (e + (bitlen m - 1)) 0" (is ?th1)
    and "Float (mantissa x) (- (bitlen (mantissa x) - 1)) = Float m ( - (bitlen m - 1))"  (is ?th2)
proof -
  from assms have mantissa_pos: "m > 0" "mantissa x > 0"
    using Float_pos_eq_mantissa_pos[of m e] float_pos_eq_mantissa_pos[of x] by simp_all
  thus ?th1
    using bitlen_Float[of m e] assms
    by (auto simp add: zero_less_mult_iff intro!: arg_cong2[where f=Float])
  have "x  0"
    unfolding zero_float_def[symmetric] using 0 < x by auto
  from denormalize_shift[OF x_def this] obtain i where
    i: "m = mantissa x * 2 ^ i" "e = exponent x - int i" .

  have "2 powr (1 - (real_of_int (bitlen (mantissa x)) + real_of_int i)) =
    2 powr (1 - (real_of_int (bitlen (mantissa x)))) * inverse (2 powr (real i))"
    by (simp add: powr_minus[symmetric] powr_add[symmetric] field_simps)
  hence "real_of_int (mantissa x) * 2 powr (1 - real_of_int (bitlen (mantissa x))) =
    (real_of_int (mantissa x) * 2 ^ i) * 2 powr (1 - real_of_int (bitlen (mantissa x * 2 ^ i)))"
    using mantissa x > 0 by (simp add: powr_realpow)
  then show ?th2
    unfolding i
    by (auto simp: real_of_float_eq)
qed

lemma compute_ln[code]:
  fixes m e
  defines "x  Float m e"
  shows "ub_ln prec x = (if x  0          then None
              else if x < 1          then Some (- the (lb_ln prec (float_divl (max prec 1) 1 x)))
            else let horner = λx. float_round_up prec (x * ub_ln_horner prec (get_odd prec) 1 x) in
                 if x  Float 3 (- 1) then Some (horner (x - 1))
            else if x < Float 1 1  then Some (float_round_up prec (horner (Float 1 (- 1)) + horner (x * rapprox_rat prec 2 3 - 1)))
                                   else let l = bitlen m - 1 in
                                        Some (float_plus_up prec (float_round_up prec (ub_ln2 prec * (Float (e + l) 0))) (horner (Float m (- l) - 1))))"
    (is ?th1)
  and "lb_ln prec x = (if x  0          then None
            else if x < 1          then Some (- the (ub_ln prec (float_divr prec 1 x)))
            else let horner = λx. float_round_down prec (x * lb_ln_horner prec (get_even prec) 1 x) in
                 if x  Float 3 (- 1) then Some (horner (x - 1))
            else if x < Float 1 1  then Some (float_round_down prec (horner (Float 1 (- 1)) +
                                              horner (max (x * lapprox_rat prec 2 3 - 1) 0)))
                                   else let l = bitlen m - 1 in
                                        Some (float_plus_down prec (float_round_down prec (lb_ln2 prec * (Float (e + l) 0))) (horner (Float m (- l) - 1))))"
    (is ?th2)
proof -
  from assms Float_pos_eq_mantissa_pos have "x > 0  m > 0"
    by simp
  thus ?th1 ?th2
    using Float_representation_aux[of m e]
    unfolding x_def[symmetric]
    by (auto dest: not_le_imp_less)
qed

lemma ln_shifted_float:
  assumes "0 < m"
  shows "ln (Float m e) = ln 2 * (e + (bitlen m - 1)) + ln (Float m (- (bitlen m - 1)))"
proof -
  let ?B = "2^nat (bitlen m - 1)"
  define bl where "bl = bitlen m - 1"
  have "0 < real_of_int m" and "X. (0 :: real) < 2^X" and "0 < (2 :: real)" and "m  0"
    using assms by auto
  hence "0  bl" by (simp add: bitlen_alt_def bl_def)
  show ?thesis
  proof (cases "0  e")
    case True
    thus ?thesis
      unfolding bl_def[symmetric] using 0 < real_of_int m 0  bl
      apply (simp add: ln_mult)
      apply (cases "e=0")
        apply (cases "bl = 0", simp_all add: powr_minus ln_inverse ln_powr)
        apply (cases "bl = 0", simp_all add: powr_minus ln_inverse ln_powr field_simps)
      done
  next
    case False
    hence "0 < -e" by auto
    have lne: "ln (2 powr real_of_int e) = ln (inverse (2 powr - e))"
      by (simp add: powr_minus)
    hence pow_gt0: "(0::real) < 2^nat (-e)"
      by auto
    hence inv_gt0: "(0::real) < inverse (2^nat (-e))"
      by auto
    show ?thesis
      using False unfolding bl_def[symmetric]
      using 0 < real_of_int m 0  bl
      by (auto simp add: lne ln_mult ln_powr ln_div field_simps)
  qed
qed

lemma ub_ln_lb_ln_bounds':
  assumes "1  x"
  shows "the (lb_ln prec x)  ln x  ln x  the (ub_ln prec x)"
    (is "?lb  ?ln  ?ln  ?ub")
proof (cases "x < Float 1 1")
  case True
  hence "real_of_float (x - 1) < 1" and "real_of_float x < 2" by auto
  have "¬ x  0" and "¬ x < 1" using 1  x by auto
  hence "0  real_of_float (x - 1)" using 1  x by auto

  have [simp]: "(Float 3 (- 1)) = 3 / 2" by simp

  show ?thesis
  proof (cases "x  Float 3 (- 1)")
    case True
    show ?thesis
      unfolding lb_ln.simps
      unfolding ub_ln.simps Let_def
      using ln_float_bounds[OF 0  real_of_float (x - 1) real_of_float (x - 1) < 1, of prec]
        ¬ x  0 ¬ x < 1 True
      by (auto intro!: float_round_down_le float_round_up_le)
  next
    case False
    hence *: "3 / 2 < x" by auto

    with ln_add[of "3 / 2" "x - 3 / 2"]
    have add: "ln x = ln (3 / 2) + ln (real_of_float x * 2 / 3)"
      by (auto simp add: algebra_simps diff_divide_distrib)

    let "?ub_horner x" = "float_round_up prec (x * ub_ln_horner prec (get_odd prec) 1 x)"
    let "?lb_horner x" = "float_round_down prec (x * lb_ln_horner prec (get_even prec) 1 x)"

    { have up: "real_of_float (rapprox_rat prec 2 3)  1"
        by (rule rapprox_rat_le1) simp_all
      have low: "2 / 3  rapprox_rat prec 2 3"
        by (rule order_trans[OF _ rapprox_rat]) simp
      from mult_less_le_imp_less[OF * low] *
      have pos: "0 < real_of_float (x * rapprox_rat prec 2 3 - 1)" by auto

      have "ln (real_of_float x * 2/3)
         ln (real_of_float (x * rapprox_rat prec 2 3 - 1) + 1)"
      proof (rule ln_le_cancel_iff[symmetric, THEN iffD1])
        show "real_of_float x * 2 / 3  real_of_float (x * rapprox_rat prec 2 3 - 1) + 1"
          using * low by auto
        show "0 < real_of_float x * 2 / 3" using * by simp
        show "0 < real_of_float (x * rapprox_rat prec 2 3 - 1) + 1" using pos by auto
      qed
      also have "  ?ub_horner (x * rapprox_rat prec 2 3 - 1)"
      proof (rule float_round_up_le, rule ln_float_bounds(2))
        from mult_less_le_imp_less[OF real_of_float x < 2 up] low *
        show "real_of_float (x * rapprox_rat prec 2 3 - 1) < 1" by auto
        show "0  real_of_float (x * rapprox_rat prec 2 3 - 1)" using pos by auto
      qed
     finally have "ln x  ?ub_horner (Float 1 (-1))
          + ?ub_horner ((x * rapprox_rat prec 2 3 - 1))"
        using ln_float_bounds(2)[of "Float 1 (- 1)" prec prec] add
        by (auto intro!: add_mono float_round_up_le)
      note float_round_up_le[OF this, of prec]
    }
    moreover
    { let ?max = "max (x * lapprox_rat prec 2 3 - 1) 0"

      have up: "lapprox_rat prec 2 3  2/3"
        by (rule order_trans[OF lapprox_rat], simp)

      have low: "0  real_of_float (lapprox_rat prec 2 3)"
        using lapprox_rat_nonneg[of 2 3 prec] by simp

      have "?lb_horner ?max
         ln (real_of_float ?max + 1)"
      proof (rule float_round_down_le, rule ln_float_bounds(1))
        from mult_less_le_imp_less[OF real_of_float x < 2 up] * low
        show "real_of_float ?max < 1" by (cases "real_of_float (lapprox_rat prec 2 3) = 0",
          auto simp add: real_of_float_max)
        show "0  real_of_float ?max" by (auto simp add: real_of_float_max)
      qed
      also have "  ln (real_of_float x * 2/3)"
      proof (rule ln_le_cancel_iff[symmetric, THEN iffD1])
        show "0 < real_of_float ?max + 1" by (auto simp add: real_of_float_max)
        show "0 < real_of_float x * 2/3" using * by auto
        show "real_of_float ?max + 1  real_of_float x * 2/3" using * up
          by (cases "0 < real_of_float x * real_of_float (lapprox_posrat prec 2 3) - 1",
              auto simp add: max_def)
      qed
      finally have "?lb_horner (Float 1 (- 1)) + ?lb_horner ?max  ln x"
        using ln_float_bounds(1)[of "Float 1 (- 1)" prec prec] add
        by (auto intro!: add_mono float_round_down_le)
      note float_round_down_le[OF this, of prec]
    }
    ultimately
    show ?thesis unfolding lb_ln.simps unfolding ub_ln.simps Let_def
      using ¬ x  0 ¬ x < 1 True False by auto
  qed
next
  case False
  hence "¬ x  0" and "¬ x < 1" "0 < x" "¬ x  Float 3 (- 1)"
    using 1  x by auto
  show ?thesis
  proof -
    define m where "m = mantissa x"
    define e where "e = exponent x"
    from Float_mantissa_exponent[of x] have Float: "x = Float m e"
      by (simp add: m_def e_def)
    let ?s = "Float (e + (bitlen m - 1)) 0"
    let ?x = "Float m (- (bitlen m - 1))"

    have "0 < m" and "m  0" using 0 < x Float powr_gt_zero[of 2 e]
      by (auto simp add: zero_less_mult_iff)
    define bl where "bl = bitlen m - 1"
    then have bitlen: "bitlen m = bl + 1"
      by simp
    hence "bl  0"
      using m > 0 by (auto simp add: bitlen_alt_def)
    have "1  Float m e"
      using 1  x Float unfolding less_eq_float_def by auto
    from bitlen_div[OF 0 < m] float_gt1_scale[OF 1  Float m e] bl  0
    have x_bnds: "0  real_of_float (?x - 1)" "real_of_float (?x - 1) < 1"
      using abs_real_le_2_powr_bitlen [of m] m > 0
      by (simp_all add: bitlen powr_realpow [symmetric] powr_minus powr_add field_simps)
    {
      have "float_round_down prec (lb_ln2 prec * ?s)  ln 2 * (e + (bitlen m - 1))"
          (is "real_of_float ?lb2  _")
        apply (rule float_round_down_le)
        unfolding nat_0 power_0 mult_1_right times_float.rep_eq
        using lb_ln2[of prec]
      proof (rule mult_mono)
        from float_gt1_scale[OF 1  Float m e]
        show "0  real_of_float (Float (e + (bitlen m - 1)) 0)" by simp
      qed auto
      moreover
      from ln_float_bounds(1)[OF x_bnds]
      have "float_round_down prec ((?x - 1) * lb_ln_horner prec (get_even prec) 1 (?x - 1))  ln ?x" (is "real_of_float ?lb_horner  _")
        by (auto intro!: float_round_down_le)
      ultimately have "float_plus_down prec ?lb2 ?lb_horner  ln x"
        unfolding Float ln_shifted_float[OF 0 < m, of e] by (auto intro!: float_plus_down_le)
    }
    moreover
    {
      from ln_float_bounds(2)[OF x_bnds]
      have "ln ?x  float_round_up prec ((?x - 1) * ub_ln_horner prec (get_odd prec) 1 (?x - 1))"
          (is "_  real_of_float ?ub_horner")
        by (auto intro!: float_round_up_le)
      moreover
      have "ln 2 * (e + (bitlen m - 1))  float_round_up prec (ub_ln2 prec * ?s)"
          (is "_  real_of_float ?ub2")
        apply (rule float_round_up_le)
        unfolding nat_0 power_0 mult_1_right times_float.rep_eq
        using ub_ln2[of prec]
      proof (rule mult_mono)
        from float_gt1_scale[OF 1  Float m e]
        show "0  real_of_int (e + (bitlen m - 1))" by auto
        have "0  ln (2 :: real)" by simp
        thus "0  real_of_float (ub_ln2 prec)" using ub_ln2[of prec] by arith
      qed auto
      ultimately have "ln x  float_plus_up prec ?ub2 ?ub_horner"
        unfolding Float ln_shifted_float[OF 0 < m, of e]
        by (auto intro!: float_plus_up_le)
    }
    ultimately show ?thesis
      unfolding lb_ln.simps
      unfolding ub_ln.simps
      unfolding if_not_P[OF ¬ x  0] if_not_P[OF ¬ x < 1]
        if_not_P[OF False] if_not_P[OF ¬ x  Float 3 (- 1)] Let_def
      unfolding plus_float.rep_eq e_def[symmetric] m_def[symmetric]
      by simp
  qed
qed

lemma ub_ln_lb_ln_bounds:
  assumes "0 < x"
  shows "the (lb_ln prec x)  ln x  ln x  the (ub_ln prec x)"
    (is "?lb  ?ln  ?ln  ?ub")
proof (cases "x < 1")
  case False
  hence "1  x"
    unfolding less_float_def less_eq_float_def by auto
  show ?thesis
    using ub_ln_lb_ln_bounds'[OF 1  x] .
next
  case True
  have "¬ x  0" using 0 < x by auto
  from True have "real_of_float x  1" "x  1"
    by simp_all
  have "0 < real_of_float x" and "real_of_float x  0"
    using 0 < x by auto
  hence A: "0 < 1 / real_of_float x" by auto

  {
    let ?divl = "float_divl (max prec 1) 1 x"
    have A': "1  ?divl" using float_divl_pos_less1_bound[OF 0 < real_of_float x real_of_float x  1] by auto
    hence B: "0 < real_of_float ?divl" by auto

    have "ln ?divl  ln (1 / x)" unfolding ln_le_cancel_iff[OF B A] using float_divl[of _ 1 x] by auto
    hence "ln x  - ln ?divl" unfolding nonzero_inverse_eq_divide[OF real_of_float x  0, symmetric] ln_inverse[OF 0 < real_of_float x] by auto
    from this ub_ln_lb_ln_bounds'[OF A', THEN conjunct1, THEN le_imp_neg_le]
    have "?ln  - the (lb_ln prec ?divl)" unfolding uminus_float.rep_eq by (rule order_trans)
  } moreover
  {
    let ?divr = "float_divr prec 1 x"
    have A': "1  ?divr" using float_divr_pos_less1_lower_bound[OF 0 < x x  1] unfolding less_eq_float_def less_float_def by auto
    hence B: "0 < real_of_float ?divr" by auto

    have "ln (1 / x)  ln ?divr" unfolding ln_le_cancel_iff[OF A B] using float_divr[of 1 x] by auto
    hence "- ln ?divr  ln x" unfolding nonzero_inverse_eq_divide[OF real_of_float x  0, symmetric] ln_inverse[OF 0 < real_of_float x] by auto
    from ub_ln_lb_ln_bounds'[OF A', THEN conjunct2, THEN le_imp_neg_le] this
    have "- the (ub_ln prec ?divr)  ?ln" unfolding uminus_float.rep_eq by (rule order_trans)
  }
  ultimately show ?thesis unfolding lb_ln.simps[where x=x]  ub_ln.simps[where x=x]
    unfolding if_not_P[OF ¬ x  0] if_P[OF True] by auto
qed

lemma lb_ln:
  assumes "Some y = lb_ln prec x"
  shows "y  ln x" and "0 < real_of_float x"
proof -
  have "0 < x"
  proof (rule ccontr)
    assume "¬ 0 < x"
    hence "x  0"
      unfolding less_eq_float_def less_float_def by auto
    thus False
      using assms by auto
  qed
  thus "0 < real_of_float x" by auto
  have "the (lb_ln prec x)  ln x"
    using ub_ln_lb_ln_bounds[OF 0 < x] ..
  thus "y  ln x"
    unfolding assms[symmetric] by auto
qed

lemma ub_ln:
  assumes "Some y = ub_ln prec x"
  shows "ln x  y" and "0 < real_of_float x"
proof -
  have "0 < x"
  proof (rule ccontr)
    assume "¬ 0 < x"
    hence "x  0" by auto
    thus False
      using assms by auto
  qed
  thus "0 < real_of_float x" by auto
  have "ln x  the (ub_ln prec x)"
    using ub_ln_lb_ln_bounds[OF 0 < x] ..
  thus "ln x  y"
    unfolding assms[symmetric] by auto
qed

lemma bnds_ln: "(x::real) lx ux. (Some l, Some u) =
  (lb_ln prec lx, ub_ln prec ux)  x  {lx .. ux}  l  ln x  ln x  u"
proof (rule allI, rule allI, rule allI, rule impI)
  fix x :: real
  fix lx ux
  assume "(Some l, Some u) = (lb_ln prec lx, ub_ln prec ux)  x  {lx .. ux}"
  hence l: "Some l = lb_ln prec lx " and u: "Some u = ub_ln prec ux" and x: "x  {lx .. ux}"
    by auto

  have "ln ux  u" and "0 < real_of_float ux"
    using ub_ln u by auto
  have "l  ln lx" and "0 < real_of_float lx" and "0 < x"
    using lb_ln[OF l] x by auto

  from ln_le_cancel_iff[OF 0 < real_of_float lx 0 < x] l  ln lx
  have "l  ln x"
    using x unfolding atLeastAtMost_iff by auto
  moreover
  from ln_le_cancel_iff[OF 0 < x 0 < real_of_float ux] ln ux  real_of_float u
  have "ln x  u"
    using x unfolding atLeastAtMost_iff by auto
  ultimately show "l  ln x  ln x  u" ..
qed

lemmas [simp del] = lb_ln.simps ub_ln.simps

lemma lb_lnD:
  "y  ln x  0 < real_of_float x" if "lb_ln prec x = Some y"
  using lb_ln[OF that[symmetric]] by auto

lemma ub_lnD:
  "ln x  y 0 < real_of_float x" if "ub_ln prec x = Some y"
  using ub_ln[OF that[symmetric]] by auto

lift_definition(code_dt) ln_float_interval :: "nat  float interval  float interval option"
  is "λprec. λ(lx, ux).
    Option.bind (lb_ln prec lx) (λl.
      Option.bind (ub_ln prec ux) (λu. Some (l, u)))"
  by (auto simp: pred_option_def bind_eq_Some_conv ln_le_cancel_iff[symmetric]
      simp del: ln_le_cancel_iff dest!: lb_lnD ub_lnD)

lemma ln_float_interval_eq_Some_conv:
  "ln_float_interval p x = Some y 
    lb_ln p (lower x) = Some (lower y)  ub_ln p (upper x) = Some (upper y)"
  by transfer (auto simp: bind_eq_Some_conv)

lemma ln_float_interval: "ln ` set_of (real_interval x)  set_of (real_interval y)"
  if "ln_float_interval p x = Some y"
  using that lb_ln[of "lower y" p "lower x"]
    ub_ln[of "lower y" p "lower x"]
  apply (auto simp add: set_of_eq ln_float_interval_eq_Some_conv ln_le_cancel_iff)
   apply (meson less_le_trans ln_less_cancel_iff not_le)
  by (meson less_le_trans ln_less_cancel_iff not_le ub_lnD)

lemma ln_float_intervalI:
  "ln x  set_of' (ln_float_interval p X)" if "x r X"
  using ln_float_interval[of p X] that
  by (auto simp: set_of'_def split: option.splits)

lemma ln_float_interval_eqI:
  "ln x r IVL" if "ln_float_interval p X = Some IVL" "x r X"
  using ln_float_intervalI[of x X p] that
  by (auto simp: set_of'_def split: option.splits)


section Real power function

definition bnds_powr :: "nat  float  float  float  float  (float × float) option" where
  "bnds_powr prec l1 u1 l2 u2 = (
     if l1 = 0  u1 = 0 then
       Some (0, 0)
     else if l1 = 0  l2  1 then
       let uln = the (ub_ln prec u1)
       in  Some (0, ub_exp prec (float_round_up prec (uln * (if uln  0 then u2 else l2))))
     else if l1  0 then
       None
     else
       Some (map_bnds lb_exp ub_exp prec 
               (bnds_mult prec (the (lb_ln prec l1)) (the (ub_ln prec u1)) l2 u2)))"

lemma mono_exp_real: "mono (exp :: real  real)"
  by (auto simp: mono_def)

lemma ub_exp_nonneg: "real_of_float (ub_exp prec x)  0"
proof -
  have "0  exp (real_of_float x)" by simp
  also from exp_boundaries[of x prec] 
    have "  real_of_float (ub_exp prec x)" by simp
  finally show ?thesis .
qed

lemma bnds_powr:
  assumes lu: "Some (l, u) = bnds_powr prec l1 u1 l2 u2"
  assumes x: "x  {real_of_float l1..real_of_float u1}"
  assumes y: "y  {real_of_float l2..real_of_float u2}"
  shows   "x powr y  {real_of_float l..real_of_float u}"
proof -
  consider "l1 = 0" "u1 = 0" | "l1 = 0" "u1  0" "l2  1" | 
           "l1  0" "¬(l1 = 0  (u1 = 0  l2  1))" | "l1 > 0" by force
  thus ?thesis
  proof cases
    assume "l1 = 0" "u1 = 0"
    with x lu show ?thesis by (auto simp: bnds_powr_def)
  next
    assume A: "l1 = 0" "u1  0" "l2  1"
    define uln where "uln = the (ub_ln prec u1)"
    show ?thesis
    proof (cases "x = 0")
      case False
      with A x y have "x powr y = exp (ln x * y)" by (simp add: powr_def)
      also {
        from A x False have "ln x  ln (real_of_float u1)" by simp
        also from ub_ln_lb_ln_bounds[of u1 prec] A y x False
          have "ln (real_of_float u1)  real_of_float uln" by (simp add: uln_def del: lb_ln.simps)
        also from A x y have " * y  real_of_float uln * (if uln  0 then u2 else l2)"
          by (auto intro: mult_left_mono mult_left_mono_neg)
        also have "  real_of_float (float_round_up prec (uln * (if uln  0 then u2 else l2)))"
          by (simp add: float_round_up_le)
        finally have "ln x * y  " using A y by - simp
      }
      also have "exp (real_of_float (float_round_up prec (uln * (if uln  0 then u2 else l2)))) 
                   real_of_float (ub_exp prec (float_round_up prec
                       (uln * (if uln  0 then u2 else l2))))"
        using exp_boundaries by simp
      finally show ?thesis using A x y lu 
        by (simp add: bnds_powr_def uln_def Let_def del: lb_ln.simps ub_ln.simps)
    qed (insert x y lu A, simp_all add: bnds_powr_def Let_def ub_exp_nonneg
                                   del: lb_ln.simps ub_ln.simps)
  next
    assume "l1  0" "¬(l1 = 0  (u1 = 0  l2  1))"
    with lu show ?thesis by (simp add: bnds_powr_def split: if_split_asm)
  next
    assume l1: "l1 > 0"
    obtain lm um where lmum:
      "(lm, um) = bnds_mult prec (the (lb_ln prec l1)) (the (ub_ln prec u1)) l2 u2"
      by (cases "bnds_mult prec (the (lb_ln prec l1)) (the (ub_ln prec u1)) l2 u2") simp
    with l1 have "(l, u) = map_bnds lb_exp ub_exp prec (lm, um)"
      using lu by (simp add: bnds_powr_def del: lb_ln.simps ub_ln.simps split: if_split_asm)
    hence "exp (ln x * y)  {real_of_float l..real_of_float u}"
    proof (rule map_bnds[OF _ mono_exp_real], goal_cases)
      case 1
      let ?lln = "the (lb_ln prec l1)" and ?uln = "the (ub_ln prec u1)"
      from ub_ln_lb_ln_bounds[of l1 prec] ub_ln_lb_ln_bounds[of u1 prec] x l1
        have "real_of_float ?lln  ln (real_of_float l1)  
              ln (real_of_float u1)  real_of_float ?uln"
        by (auto simp del: lb_ln.simps ub_ln.simps)
      moreover from l1 x have "ln (real_of_float l1)  ln x  ln x  ln (real_of_float u1)"
        by auto
      ultimately have ln: "real_of_float ?lln  ln x  ln x  real_of_float ?uln" by simp
      from lmum show ?case
        by (rule bnds_mult) (insert y ln, simp_all)
    qed (insert exp_boundaries[of lm prec] exp_boundaries[of um prec], simp_all)
    with x l1 show ?thesis
      by (simp add: powr_def mult_ac)
  qed
qed

lift_definition(code_dt) powr_float_interval :: "nat  float interval  float interval  float interval option"
  is "λprec. λ(l1, u1). λ(l2, u2). bnds_powr prec l1 u1 l2 u2"
  by (auto simp: pred_option_def dest!: bnds_powr[OF sym])

lemma powr_float_interval:
  "{x powr y | x y. x  set_of (real_interval X)  y  set_of (real_interval Y)}
     set_of (real_interval R)"
  if "powr_float_interval prec X Y = Some R"
  using that
  by transfer (auto dest!: bnds_powr[OF sym])

lemma powr_float_intervalI:
  "x powr y  set_of' (powr_float_interval p X Y)"
  if "x r X" "y r Y"
  using powr_float_interval[of p X Y] that
  by (auto simp: set_of'_def split: option.splits)

lemma powr_float_interval_eqI:
  "x powr y r IVL"
  if "powr_float_interval p X Y = Some IVL" "x r X" "y r Y"
  using powr_float_intervalI[of x X y Y p] that
  by (auto simp: set_of'_def split: option.splits)

end

end