(*
  File:   Akra_Bazzi_Real.thy
  Author: Manuel Eberl <manuel@pruvisto.org>

  The continuous version of the Akra-Bazzi theorem for functions on the reals.
*)

section \<open>The continuous Akra-Bazzi theorem\<close>
theory Akra_Bazzi_Real
imports
  Complex_Main
  Akra_Bazzi_Asymptotics
begin

text \<open>
  We want to be generic over the integral definition used; we fix some arbitrary
  notions of integrability and integral and assume just the properties we need.
  The user can then instantiate the theorems with any desired integral definition.
\<close>
locale akra_bazzi_integral =
  fixes integrable :: "(real \<Rightarrow> real) \<Rightarrow> real \<Rightarrow> real \<Rightarrow> bool"
    and integral   :: "(real \<Rightarrow> real) \<Rightarrow> real \<Rightarrow> real \<Rightarrow> real"
  assumes integrable_const: "c \<ge> 0 \<Longrightarrow> integrable (\<lambda>_. c) a b"
      and integral_const:   "c \<ge> 0 \<Longrightarrow> a \<le> b \<Longrightarrow> integral (\<lambda>_. c) a b = (b - a) * c"
      and integrable_subinterval:
            "integrable f a b \<Longrightarrow> a \<le> a' \<Longrightarrow> b' \<le> b \<Longrightarrow> integrable f a' b'"
      and integral_le:
            "integrable f a b \<Longrightarrow> integrable g a b \<Longrightarrow> (\<And>x. x \<in> {a..b} \<Longrightarrow> f x \<le> g x) \<Longrightarrow>
                 integral f a b \<le> integral g a b"
      and integral_combine:
            "a \<le> c \<Longrightarrow> c \<le> b \<Longrightarrow> integrable f a b \<Longrightarrow>
                 integral f a c + integral f c b = integral f a b"
begin
lemma integral_nonneg:
  "a \<le> b \<Longrightarrow> integrable f a b \<Longrightarrow> (\<And>x. x \<in> {a..b} \<Longrightarrow> f x \<ge> 0) \<Longrightarrow> integral f a b \<ge> 0"
  using integral_le[OF integrable_const[of 0], of f a b]  by (simp add: integral_const)
end

declare sum.cong[fundef_cong]

lemma strict_mono_imp_ex1_real:
  fixes f :: "real \<Rightarrow> real"
  assumes lim_neg_inf: "LIM x at_bot. f x :> at_top"
  assumes lim_inf: "(f \<longlongrightarrow> z) at_top"
  assumes mono: "\<And>a b. a < b \<Longrightarrow> f b < f a"
  assumes cont: "\<And>x. isCont f x"
  assumes y_greater_z: "z < y"
  shows   "\<exists>!x. f x = y"
proof (rule ex_ex1I)
  fix a b assume "f a = y" "f b = y"
  thus "a = b" by (cases rule: linorder_cases[of a b]) (auto dest: mono)
next
  from lim_neg_inf have "eventually (\<lambda>x. y \<le> f x) at_bot" by (subst (asm) filterlim_at_top) simp
  then obtain l where l: "\<And>x. x \<le> l \<Longrightarrow> y \<le> f x" by (subst (asm) eventually_at_bot_linorder) auto

  from order_tendstoD(2)[OF lim_inf y_greater_z]
    obtain u where u: "\<And>x. x \<ge> u \<Longrightarrow> f x < y" by (subst (asm) eventually_at_top_linorder) auto
  define a where "a = min l u"
  define b where "b = max l u"
  have a: "f a \<ge> y" unfolding a_def by (intro l) simp
  moreover have b: "f b < y" unfolding b_def by (intro u) simp
  moreover have a_le_b: "a \<le> b" by (simp add: a_def b_def)
  ultimately have "\<exists>x\<ge>a. x \<le> b \<and> f x = y" using cont by (intro IVT2) auto
  thus "\<exists>x. f x = y" by blast
qed

text \<open>The parameter @{term "p"} in the Akra-Bazzi theorem always exists and is unique.\<close>

definition akra_bazzi_exponent :: "real list \<Rightarrow> real list \<Rightarrow> real" where
  "akra_bazzi_exponent as bs \<equiv> (THE p. (\<Sum>i<length as. as!i * bs!i powr p) = 1)"

locale akra_bazzi_params =
  fixes k :: nat and as bs :: "real list"
  assumes length_as: "length as = k"
  and     length_bs: "length bs = k"
  and     k_not_0:   "k \<noteq> 0"
  and     a_ge_0:    "a \<in> set as \<Longrightarrow> a \<ge> 0"
  and     b_bounds:  "b \<in> set bs \<Longrightarrow> b \<in> {0<..<1}"
begin

abbreviation p :: real where "p \<equiv> akra_bazzi_exponent as bs"

lemma p_def: "p = (THE p. (\<Sum>i<k. as!i * bs!i powr p) = 1)"
  by (simp add: akra_bazzi_exponent_def length_as)

lemma b_pos: "b \<in> set bs \<Longrightarrow> b > 0" and b_less_1: "b \<in> set bs \<Longrightarrow> b < 1"
  using b_bounds by simp_all

lemma as_nonempty [simp]: "as \<noteq> []" and bs_nonempty [simp]: "bs \<noteq> []"
  using length_as length_bs k_not_0 by auto

lemma a_in_as[intro, simp]: "i < k \<Longrightarrow> as ! i \<in> set as"
  by (rule nth_mem) (simp add: length_as)

lemma b_in_bs[intro, simp]: "i < k \<Longrightarrow> bs ! i \<in> set bs"
  by (rule nth_mem) (simp add: length_bs)

end

locale akra_bazzi_params_nonzero =
  fixes k :: nat and as bs :: "real list"
  assumes length_as: "length as = k"
  and     length_bs: "length bs = k"
  and     a_ge_0:    "a \<in> set as \<Longrightarrow> a \<ge> 0"
  and     ex_a_pos:  "\<exists>a\<in>set as. a > 0"
  and     b_bounds:  "b \<in> set bs \<Longrightarrow> b \<in> {0<..<1}"
begin

sublocale akra_bazzi_params k as bs
 by unfold_locales (insert length_as length_bs a_ge_0 ex_a_pos b_bounds, auto)

lemma akra_bazzi_p_strict_mono:
  assumes "x < y"
  shows "(\<Sum>i<k. as!i * bs!i powr y) < (\<Sum>i<k. as!i * bs!i powr x)"
proof (intro sum_strict_mono_ex1 ballI)
  from ex_a_pos obtain a where "a \<in> set as" "a > 0" by blast
  then obtain i where "i < k" "as!i > 0" by (force simp: in_set_conv_nth length_as)
  with b_bounds \<open>x < y\<close> have "as!i * bs!i powr y < as!i * bs!i powr x"
    by (intro mult_strict_left_mono powr_less_mono') auto
  with \<open>i < k\<close> show "\<exists>i\<in>{..<k}. as!i * bs!i powr y < as!i * bs!i powr x" by blast
next
  fix i assume "i \<in> {..<k}"
  with a_ge_0 b_bounds[of "bs!i"] \<open>x < y\<close> show "as!i * bs!i powr y \<le> as!i * bs!i powr x"
    by (intro mult_left_mono powr_mono') simp_all
qed simp_all

lemma akra_bazzi_p_mono:
  assumes "x \<le> y"
  shows "(\<Sum>i<k. as!i * bs!i powr y) \<le> (\<Sum>i<k. as!i * bs!i powr x)"
apply (cases "x < y")
using akra_bazzi_p_strict_mono[of x y] assms apply simp_all
done

lemma akra_bazzi_p_unique:
  "\<exists>!p. (\<Sum>i<k. as!i * bs!i powr p) = 1"
proof (rule strict_mono_imp_ex1_real)
  from as_nonempty have [simp]: "k > 0" by (auto simp: length_as[symmetric])
  have [simp]: "\<And>i. i < k \<Longrightarrow> as!i \<ge> 0" by (rule a_ge_0) simp
  from ex_a_pos obtain a where "a \<in> set as" "a > 0" by blast
  then obtain i where i: "i < k" "as!i > 0" by (force simp: in_set_conv_nth length_as)

  hence "LIM p at_bot. as!i * bs!i powr p :> at_top" using b_bounds i
    by (intro filterlim_tendsto_pos_mult_at_top[OF tendsto_const] real_powr_at_bot_neg) simp_all
  moreover have "\<forall>p. as!i*bs!i powr p \<le> (\<Sum>i\<in>{..<k}. as ! i * bs ! i powr p)"
  proof
    fix p :: real
    from a_ge_0 b_bounds have "(\<Sum>i\<in>{..<k}-{i}. as ! i * bs ! i powr p) \<ge> 0"
      by (intro sum_nonneg mult_nonneg_nonneg) simp_all
    also have "as!i * bs!i powr p + ... = (\<Sum>i\<in>insert i {..<k}. as ! i * bs ! i powr p)"
      by (simp add: sum.insert_remove)
    also from i have "insert i {..<k} = {..<k}" by blast
    finally show "as!i*bs!i powr p \<le> (\<Sum>i\<in>{..<k}. as ! i * bs ! i powr p)" by simp
  qed
  ultimately show "LIM p at_bot. \<Sum>i<k. as ! i * bs ! i powr p :> at_top"
    by (rule filterlim_at_top_mono[OF _ always_eventually])
next
  from b_bounds show "((\<lambda>x. \<Sum>i<k. as ! i * bs ! i powr x) \<longlongrightarrow> (\<Sum>i<k. 0)) at_top"
    by (intro tendsto_sum tendsto_mult_right_zero real_powr_at_top_neg) simp_all
next
  fix x
  from b_bounds have A: "\<And>i. i < k \<Longrightarrow> bs ! i > 0" by simp
  show "isCont (\<lambda>x. \<Sum>i<k. as ! i * bs ! i powr x) x"
    using b_bounds[OF nth_mem] by (intro continuous_intros) (auto dest: A)
qed (simp_all add: akra_bazzi_p_strict_mono)

lemma p_props:  "(\<Sum>i<k. as!i * bs!i powr p) = 1"
  and p_unique: "(\<Sum>i<k. as!i * bs!i powr p') = 1 \<Longrightarrow> p = p'"
proof-
  from theI'[OF akra_bazzi_p_unique] the1_equality[OF akra_bazzi_p_unique]
    show "(\<Sum>i<k. as!i * bs!i powr p) = 1" "(\<Sum>i<k. as!i * bs!i powr p') = 1 \<Longrightarrow> p = p'"
    unfolding p_def by - blast+
qed

lemma p_greaterI: "1 < (\<Sum>i<k. as!i * bs!i powr p') \<Longrightarrow> p' < p"
  by (rule disjE[OF le_less_linear, of p p'], drule akra_bazzi_p_mono, subst (asm) p_props, simp_all)

lemma p_lessI: "1 > (\<Sum>i<k. as!i * bs!i powr p') \<Longrightarrow> p' > p"
  by (rule disjE[OF le_less_linear, of p' p], drule akra_bazzi_p_mono, subst (asm) p_props, simp_all)

lemma p_geI: "1 \<le> (\<Sum>i<k. as!i * bs!i powr p') \<Longrightarrow> p' \<le> p"
  by (rule disjE[OF le_less_linear, of p' p], simp, drule akra_bazzi_p_strict_mono,
      subst (asm) p_props, simp_all)

lemma p_leI: "1 \<ge> (\<Sum>i<k. as!i * bs!i powr p') \<Longrightarrow> p' \<ge> p"
  by (rule disjE[OF le_less_linear, of p p'], simp, drule akra_bazzi_p_strict_mono,
      subst (asm) p_props, simp_all)

lemma p_boundsI: "(\<Sum>i<k. as!i * bs!i powr x) \<le> 1 \<and> (\<Sum>i<k. as!i * bs!i powr y) \<ge> 1 \<Longrightarrow> p \<in> {y..x}"
  by (elim conjE, drule p_leI, drule p_geI, simp)

lemma p_boundsI': "(\<Sum>i<k. as!i * bs!i powr x) < 1 \<and> (\<Sum>i<k. as!i * bs!i powr y) > 1 \<Longrightarrow> p \<in> {y<..<x}"
  by (elim conjE, drule p_lessI, drule p_greaterI, simp)

lemma p_nonneg: "sum_list as \<ge> 1 \<Longrightarrow> p \<ge> 0"
proof (rule p_geI)
  assume "sum_list as \<ge> 1"
  also have "... = (\<Sum>i<k. as!i)" by (simp add: sum_list_sum_nth length_as atLeast0LessThan)
  also {
    fix i assume "i < k"
    with b_bounds have "bs!i > 0" by simp
    hence "as!i * bs!i powr 0 = as!i" by simp
  }
  hence "(\<Sum>i<k. as!i) = (\<Sum>i<k. as!i * bs!i powr 0)" by (intro sum.cong) simp_all
  finally show "1 \<le> (\<Sum>i<k. as ! i * bs ! i powr 0)" .
qed

end

locale akra_bazzi_real_recursion =
  fixes as bs :: "real list" and hs :: "(real \<Rightarrow> real) list" and k :: nat and x\<^sub>0 x\<^sub>1 hb e p :: real
  assumes length_as: "length as = k"
  and     length_bs: "length bs = k"
  and     length_hs: "length hs = k"
  and     k_not_0:   "k \<noteq> 0"
  and     a_ge_0:    "a \<in> set as \<Longrightarrow> a \<ge> 0"
  and     b_bounds:  "b \<in> set bs \<Longrightarrow> b \<in> {0<..<1}"

  (* The recursively-defined function *)
  and     x0_ge_1:      "x\<^sub>0 \<ge> 1"
  and     x0_le_x1:     "x\<^sub>0 \<le> x\<^sub>1"
  and     x1_ge:        "b \<in> set bs \<Longrightarrow> x\<^sub>1 \<ge> 2 * x\<^sub>0 * inverse b"
  (* Bounds on the variation functions *)
  and     e_pos:        "e > 0"
  and     h_bounds:     "x \<ge> x\<^sub>1 \<Longrightarrow> h \<in> set hs \<Longrightarrow> \<bar>h x\<bar> \<le> hb * x / ln x powr (1 + e)"
  (* Asymptotic inequalities *)
  and     asymptotics:  "x \<ge> x\<^sub>0 \<Longrightarrow> b \<in> set bs \<Longrightarrow> akra_bazzi_asymptotics b hb e p x"
begin

sublocale akra_bazzi_params k as bs
  using length_as length_bs k_not_0 a_ge_0 b_bounds by unfold_locales

lemma h_in_hs[intro, simp]: "i < k \<Longrightarrow> hs ! i \<in> set hs"
  by (rule nth_mem) (simp add: length_hs)

lemma x1_gt_1: "x\<^sub>1 > 1"
proof-
  from bs_nonempty obtain b where "b \<in> set bs" by (cases bs) auto
  from b_pos[OF this] b_less_1[OF this] x0_ge_1 have "1 < 2 * x\<^sub>0 * inverse b"
    by (simp add: field_simps)
  also from x1_ge and \<open>b \<in> set bs\<close> have "... \<le> x\<^sub>1" by simp
  finally show ?thesis .
qed

lemma x1_ge_1: "x\<^sub>1 \<ge> 1" using x1_gt_1 by simp

lemma x1_pos: "x\<^sub>1 > 0" using x1_ge_1 by simp

lemma bx_le_x: "x \<ge> 0 \<Longrightarrow> b \<in> set bs \<Longrightarrow>  b * x \<le> x"
  using b_pos[of b] b_less_1[of b] by (intro mult_left_le_one_le) (simp_all)

lemma x0_pos: "x\<^sub>0 > 0" using x0_ge_1 by simp

lemma
  assumes "x \<ge> x\<^sub>0" "b \<in> set bs"
  shows x0_hb_bound0: "hb / ln x powr (1 + e) < b/2"
  and   x0_hb_bound1: "hb / ln x powr (1 + e) < (1 - b) / 2"
  and   x0_hb_bound2: "x*(1 - b - hb / ln x powr (1 + e)) > 1"
using asymptotics[OF assms] unfolding akra_bazzi_asymptotic_defs by blast+

lemma step_diff:
  assumes "i < k" "x \<ge> x\<^sub>1"
  shows   "bs ! i * x + (hs ! i) x + 1 < x"
proof-
  have "bs ! i * x + (hs ! i) x + 1 \<le> bs ! i * x + \<bar>(hs ! i) x\<bar> + 1" by simp
  also from assms have "\<bar>(hs ! i) x\<bar> \<le> hb * x / ln x powr (1 + e)" by (simp add: h_bounds)
  also from assms x0_le_x1 have "x*(1 - bs ! i - hb / ln x powr (1 + e)) > 1"
    by (simp add: x0_hb_bound2)
  hence "bs ! i * x + hb * x / ln x powr (1 + e) + 1 < x" by (simp add: algebra_simps)
  finally show ?thesis by simp
qed

lemma step_le_x: "i < k \<Longrightarrow> x \<ge> x\<^sub>1 \<Longrightarrow> bs ! i * x + (hs ! i) x \<le> x"
  by (drule (1) step_diff) simp

lemma x0_hb_bound0': "\<And>x b. x \<ge> x\<^sub>0 \<Longrightarrow> b \<in> set bs \<Longrightarrow> hb / ln x powr (1 + e) < b"
  by (drule (1) x0_hb_bound0, erule less_le_trans) (simp add: b_pos)

lemma step_pos:
  assumes "i < k" "x \<ge> x\<^sub>1"
  shows   "bs ! i * x + (hs ! i) x > 0"
proof-
  from assms x0_le_x1 have "hb / ln x powr (1 + e) < bs ! i" by (simp add: x0_hb_bound0')
  with assms x0_pos x0_le_x1 have "x * 0 < x * (bs ! i - hb / ln x powr (1 + e))" by simp
  also have "... = bs ! i * x - hb * x / ln x powr (1 + e)"
    by (simp add: algebra_simps)
  also from assms have "-hb * x / ln x powr (1 + e) \<le> -\<bar>(hs ! i) x\<bar>" by (simp add: h_bounds)
  hence "bs ! i * x - hb * x / ln x powr (1 + e) \<le> bs ! i * x + -\<bar>(hs ! i) x\<bar>" by simp
  also have "-\<bar>(hs ! i) x\<bar> \<le> (hs ! i) x" by simp
  finally show "bs ! i * x + (hs ! i) x > 0" by simp
qed

lemma step_nonneg: "i < k \<Longrightarrow> x \<ge> x\<^sub>1 \<Longrightarrow> bs ! i * x + (hs ! i) x \<ge> 0"
  by (drule (1) step_pos) simp

lemma step_nonneg': "i < k \<Longrightarrow> x \<ge> x\<^sub>1 \<Longrightarrow> bs ! i + (hs ! i) x / x \<ge> 0"
  by (frule (1) step_nonneg, insert x0_pos x0_le_x1) (simp_all add: field_simps)

lemma hb_nonneg: "hb \<ge> 0"
proof-
  from k_not_0 and length_hs have "hs \<noteq> []" by auto
  then obtain h where h: "h \<in> set hs" by (cases hs) auto
  have "0 \<le> \<bar>h x\<^sub>1\<bar>" by simp
  also from h have "\<bar>h x\<^sub>1\<bar> \<le> hb * x\<^sub>1 / ln x\<^sub>1 powr (1+e)" by (intro h_bounds) simp_all
  finally have "0 \<le> hb * x\<^sub>1 / ln x\<^sub>1 powr (1 + e)" .
  hence "0 \<le> ... * (ln x\<^sub>1 powr (1 + e) / x\<^sub>1)"
    by (rule mult_nonneg_nonneg) (intro divide_nonneg_nonneg, insert x1_pos, simp_all)
  also have "... = hb" using x1_gt_1 by (simp add: field_simps)
  finally show ?thesis .
qed

lemma x0_hb_bound3:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "x - (bs ! i * x + (hs ! i) x) \<le> x"
proof-
  have "-(hs ! i) x \<le> \<bar>(hs ! i) x\<bar>" by simp
  also from assms have "... \<le> hb * x / ln x powr (1 + e)" by (simp add: h_bounds)
  also have "... = x * (hb / ln x powr (1 + e))" by simp
  also from assms x0_pos x0_le_x1 have "... < x * bs ! i"
    by (intro mult_strict_left_mono x0_hb_bound0') simp_all
  finally show ?thesis by (simp add: algebra_simps)
qed

lemma x0_hb_bound4:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "(bs ! i + (hs ! i) x / x) > bs ! i / 2"
proof-
  from assms x0_le_x1 have "hb / ln x powr (1 + e) < bs ! i / 2" by (intro x0_hb_bound0) simp_all
  with assms x0_pos x0_le_x1 have "(-bs ! i / 2) * x < (-hb / ln x powr (1 + e)) * x"
    by (intro mult_strict_right_mono) simp_all
  also from assms x0_pos have "... \<le> -\<bar>(hs ! i) x\<bar>" using h_bounds by simp
  also have "... \<le> (hs ! i) x" by simp
  finally show ?thesis using assms x1_pos by (simp add: field_simps)
qed

lemma x0_hb_bound4': "x \<ge> x\<^sub>1 \<Longrightarrow> i < k \<Longrightarrow> (bs ! i + (hs ! i) x / x) \<ge> bs ! i / 2"
  by (drule (1) x0_hb_bound4) simp

lemma x0_hb_bound5:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "(bs ! i + (hs ! i) x / x) \<le> bs ! i * 3/2"
proof-
  have "(hs ! i) x \<le> \<bar>(hs ! i) x\<bar>" by simp
  also from assms have "... \<le> hb * x / ln x powr (1 + e)" by (simp add: h_bounds)
  also have "... = x * (hb / ln x powr (1 + e))" by simp
  also from assms x0_pos x0_le_x1 have "... < x * (bs ! i / 2)"
    by (intro mult_strict_left_mono x0_hb_bound0) simp_all
  finally show ?thesis using assms x1_pos by (simp add: field_simps)
qed

lemma x0_hb_bound6:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "x * ((1 - bs ! i) / 2) \<le> x - (bs ! i * x + (hs ! i) x)"
proof-
  from assms x0_le_x1 have "hb / ln x powr (1 + e) < (1 - bs ! i) / 2" using x0_hb_bound1 by simp
  with assms x1_pos have "x * ((1 - bs ! i) / 2) \<le> x * (1 - (bs ! i + hb / ln x powr (1 + e)))"
    by (intro mult_left_mono) (simp_all add: field_simps)
  also have "... = x - bs ! i * x + -hb * x / ln x powr (1 + e)" by (simp add: algebra_simps)
  also from h_bounds assms have "-hb * x / ln x powr (1 + e) \<le> -\<bar>(hs ! i) x\<bar>"
    by (simp add: length_hs)
  also have "... \<le> -(hs ! i) x" by simp
  finally show ?thesis by (simp add: algebra_simps)
qed

lemma x0_hb_bound7:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "bs!i*x + (hs!i) x > x\<^sub>0"
proof-
  from assms x0_le_x1 have x': "x \<ge> x\<^sub>0" by simp
  from x1_ge assms have "2 * x\<^sub>0 * inverse (bs!i) \<le> x\<^sub>1" by simp
  with assms b_pos have "x\<^sub>0 \<le> x\<^sub>1 * (bs!i / 2)" by (simp add: field_simps)
  also from assms x' have "bs!i/2 < bs!i + (hs!i) x / x" by (intro x0_hb_bound4)
  also from assms step_nonneg' x' have "x\<^sub>1 * ... \<le> x * ..." by (intro mult_right_mono) (simp_all)
  also from assms x1_pos have "x * (bs!i + (hs!i) x / x) = bs!i*x + (hs!i) x"
    by (simp add: field_simps)
  finally show ?thesis using x1_pos by simp
qed

lemma x0_hb_bound7': "x \<ge> x\<^sub>1 \<Longrightarrow> i < k \<Longrightarrow> bs!i*x + (hs!i) x > 1"
  by (rule le_less_trans[OF _ x0_hb_bound7]) (insert x0_le_x1 x0_ge_1, simp_all)

lemma x0_hb_bound8:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "bs!i*x - hb * x / ln x powr (1+e) > x\<^sub>0"
proof-
  from assms have "2 * x\<^sub>0 * inverse (bs!i) \<le> x\<^sub>1" by (intro x1_ge) simp_all
  with b_pos assms have "x\<^sub>0 \<le> x\<^sub>1 * (bs!i/2)" by (simp add: field_simps)
  also from assms b_pos have "... \<le> x * (bs!i/2)" by simp
  also from assms x0_le_x1 have "hb / ln x powr (1+e) < bs!i/2" by (intro x0_hb_bound0) simp_all
  with assms have "bs!i/2 < bs!i - hb / ln x powr (1+e)" by (simp add: field_simps)
  also have "x * ... = bs!i*x - hb * x / ln x powr (1+e)" by (simp add: algebra_simps)
  finally show ?thesis using assms x1_pos by (simp add: field_simps)
qed

lemma x0_hb_bound8':
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "bs!i*x + hb * x / ln x powr (1+e) > x\<^sub>0"
proof-
  from assms have "x\<^sub>0 < bs!i*x - hb * x / ln x powr (1+e)" by (rule x0_hb_bound8)
  also from assms hb_nonneg x1_pos have "hb * x / ln x powr (1+e) \<ge> 0"
    by (intro mult_nonneg_nonneg divide_nonneg_nonneg) simp_all
  hence "bs!i*x - hb * x / ln x powr (1+e) \<le> bs!i*x + hb * x / ln x powr (1+e)" by simp
  finally show ?thesis .
qed

lemma
  assumes "x \<ge> x\<^sub>0"
  shows   asymptotics1: "i < k \<Longrightarrow> 1 + ln x powr (- e / 2) \<le>
             (1 - hb * inverse (bs!i) * ln x powr -(1+e)) powr p *
             (1 + ln (bs!i*x + hb*x/ln x powr (1+e)) powr (-e/2))"
  and     asymptotics2: "i < k \<Longrightarrow> 1 - ln x powr (- e / 2) \<ge>
             (1 + hb * inverse (bs!i) * ln x powr -(1+e)) powr p *
             (1 - ln (bs!i*x + hb*x/ln x powr (1+e)) powr (-e/2))"
  and     asymptotics1': "i < k \<Longrightarrow> 1 + ln x powr (- e / 2) \<le>
             (1 + hb * inverse (bs!i) * ln x powr -(1+e)) powr p *
             (1 + ln (bs!i*x + hb*x/ln x powr (1+e)) powr (-e/2))"
  and     asymptotics2': "i < k \<Longrightarrow> 1 - ln x powr (- e / 2) \<ge>
             (1 - hb * inverse (bs!i) * ln x powr -(1+e)) powr p *
             (1 - ln (bs!i*x + hb*x/ln x powr (1+e)) powr (-e/2))"
  and     asymptotics3: "(1 + ln x powr (- e / 2)) / 2 \<le> 1"
  and     asymptotics4: "(1 - ln x powr (- e / 2)) * 2 \<ge> 1"
  and     asymptotics5: "i < k \<Longrightarrow> ln (bs!i*x - hb*x*ln x powr -(1+e)) powr (-e/2) < 1"
apply -
using assms asymptotics[of x "bs!i"] unfolding akra_bazzi_asymptotic_defs
apply simp_all[4]
using assms asymptotics[of x "bs!0"] unfolding akra_bazzi_asymptotic_defs
apply simp_all[2]
using assms asymptotics[of x "bs!i"] unfolding akra_bazzi_asymptotic_defs
apply simp_all
done

lemma x0_hb_bound9:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "ln (bs!i*x + (hs!i) x) powr -(e/2) < 1"
proof-
  from b_pos assms have "0 < bs!i/2" by simp
  also from assms x0_le_x1 have "... < bs!i + (hs!i) x / x" by (intro x0_hb_bound4) simp_all
  also from assms x1_pos have "x * ... = bs!i*x + (hs!i) x" by (simp add: field_simps)
  finally have pos: "bs!i*x + (hs!i) x > 0" using assms x1_pos by simp
  from x0_hb_bound8[OF assms] x0_ge_1 have pos': "bs!i*x - hb * x / ln x powr (1+e) > 1" by simp

  from assms have "-(hb * x / ln x powr (1+e)) \<le> -\<bar>(hs!i) x\<bar>"
    by (intro le_imp_neg_le h_bounds) simp_all
  also have "... \<le> (hs!i) x" by simp
  finally have "ln (bs!i*x - hb * x / ln x powr (1+e)) \<le> ln (bs!i*x + (hs!i) x)"
    using assms b_pos x0_pos pos' by (intro ln_mono mult_pos_pos pos) simp_all
  hence "ln (bs!i*x + (hs!i) x) powr -(e/2) \<le> ln (bs!i*x - hb * x / ln x powr (1+e)) powr -(e/2)"
    using assms e_pos asymptotics5[of x] pos' by (intro powr_mono2' ln_gt_zero) simp_all
  also have "... < 1" using asymptotics5[of x i] assms x0_le_x1
    by (subst (asm) powr_minus) (simp_all add: field_simps)
  finally show ?thesis .
qed

definition akra_bazzi_measure :: "real \<Rightarrow> nat" where
  "akra_bazzi_measure x = nat \<lceil>x\<rceil>"

lemma akra_bazzi_measure_decreases:
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "akra_bazzi_measure (bs!i*x + (hs!i) x) < akra_bazzi_measure x"
proof-
  from step_diff assms have "(bs!i * x + (hs!i) x) + 1 < x" by (simp add: algebra_simps)
  hence "\<lceil>(bs!i * x + (hs!i) x) + 1\<rceil> \<le> \<lceil>x\<rceil>" by (intro ceiling_mono) simp
  hence "\<lceil>(bs!i * x + (hs!i) x)\<rceil> < \<lceil>x\<rceil>" by simp
  with assms x1_pos have "nat \<lceil>(bs!i * x + (hs!i) x)\<rceil> < nat \<lceil>x\<rceil>" by (subst nat_mono_iff) simp_all
  thus ?thesis unfolding akra_bazzi_measure_def .
qed

lemma akra_bazzi_induct[consumes 1, case_names base rec]:
  assumes "x \<ge> x\<^sub>0"
  assumes base: "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> P x"
  assumes rec:  "\<And>x. x > x\<^sub>1 \<Longrightarrow> (\<And>i. i < k \<Longrightarrow> P (bs!i*x + (hs!i) x)) \<Longrightarrow> P x"
  shows   "P x"
proof (insert \<open>x \<ge> x\<^sub>0\<close>, induction "akra_bazzi_measure x" arbitrary: x rule: less_induct)
  case less
  show ?case
  proof (cases "x \<le> x\<^sub>1")
    case True
    with base and \<open>x \<ge> x\<^sub>0\<close> show ?thesis .
  next
    case False
    hence x: "x > x\<^sub>1" by simp
    thus ?thesis
    proof (rule rec)
      fix i assume i: "i < k"
      from x0_hb_bound7[OF _ i, of x] x have "bs!i*x + (hs!i) x \<ge> x\<^sub>0" by simp
      with i x show "P (bs ! i * x + (hs ! i) x)"
        by (intro less akra_bazzi_measure_decreases) simp_all
    qed
  qed
qed

end

locale akra_bazzi_real = akra_bazzi_real_recursion +
  fixes integrable integral
  assumes integral: "akra_bazzi_integral integrable integral"
  fixes f :: "real \<Rightarrow> real"
  and   g :: "real \<Rightarrow> real"
  and   C :: real
  assumes p_props:      "(\<Sum>i<k. as!i * bs!i powr p) = 1"
  and     f_base:       "x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f x \<ge> 0"
  and     f_rec:        "x > x\<^sub>1 \<Longrightarrow> f x = g x + (\<Sum>i<k. as!i * f (bs!i * x + (hs!i) x))"
  and     g_nonneg:     "x \<ge> x\<^sub>0 \<Longrightarrow> g x \<ge> 0"
  and     C_bound:      "b \<in> set bs \<Longrightarrow> x \<ge> x\<^sub>1 \<Longrightarrow> C*x \<le> b*x - hb*x/ln x powr (1+e)"
  and     g_integrable: "x \<ge> x\<^sub>0 \<Longrightarrow> integrable (\<lambda>u. g u / u powr (p + 1)) x\<^sub>0 x"
begin

interpretation akra_bazzi_integral integrable integral by (rule integral)

lemma akra_bazzi_integrable:
  "a \<ge> x\<^sub>0 \<Longrightarrow> a \<le> b \<Longrightarrow> integrable (\<lambda>x. g x / x powr (p + 1)) a b"
  by (rule integrable_subinterval[OF g_integrable, of b]) simp_all

definition g_approx :: "nat \<Rightarrow> real \<Rightarrow> real" where
  "g_approx i x = x powr p * integral (\<lambda>u. g u / u powr (p + 1)) (bs!i * x + (hs!i) x) x"

lemma f_nonneg: "x \<ge> x\<^sub>0 \<Longrightarrow> f x \<ge> 0"
proof (induction x rule: akra_bazzi_induct)
  case (base x)
  with f_base[of x] show ?case by simp
next
  case (rec x)
  with x0_le_x1 have "g x \<ge> 0" by (intro g_nonneg) simp_all
  moreover {
    fix i assume i: "i < k"
    with rec.IH have "f (bs!i*x + (hs!i) x) \<ge> 0" by simp
    with i have "as!i * f (bs!i*x + (hs!i) x) \<ge> 0"
        by (intro mult_nonneg_nonneg[OF a_ge_0]) simp_all
  }
  hence "(\<Sum>i<k. as!i * f (bs!i*x + (hs!i) x)) \<ge> 0" by (intro sum_nonneg) blast
  ultimately show "f x \<ge> 0" using rec.hyps by (subst f_rec) simp_all
qed

definition f_approx :: "real \<Rightarrow> real" where
  "f_approx x = x powr p * (1 + integral (\<lambda>u. g u / u powr (p + 1)) x\<^sub>0 x)"

lemma f_approx_aux:
  assumes "x \<ge> x\<^sub>0"
  shows   "1 + integral (\<lambda>u. g u / u powr (p + 1)) x\<^sub>0 x \<ge> 1"
proof-
  from assms have "integral (\<lambda>u. g u / u powr (p + 1)) x\<^sub>0 x \<ge> 0"
    by (intro integral_nonneg ballI g_nonneg divide_nonneg_nonneg g_integrable) simp_all
  thus ?thesis by simp
qed

lemma f_approx_pos: "x \<ge> x\<^sub>0 \<Longrightarrow> f_approx x > 0"
  unfolding f_approx_def by (intro mult_pos_pos, insert x0_pos, simp, drule f_approx_aux, simp)

lemma f_approx_nonneg: "x \<ge> x\<^sub>0 \<Longrightarrow> f_approx x \<ge> 0"
  using f_approx_pos[of x] by simp

lemma f_approx_bounded_below:
  obtains c where "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f_approx x \<ge> c" "c > 0"
proof-
  {
    fix x assume x: "x \<ge> x\<^sub>0" "x \<le> x\<^sub>1"
    with x0_pos have "x powr p \<ge> min (x\<^sub>0 powr p) (x\<^sub>1 powr p)"
      by (intro powr_lower_bound) simp_all
    with x have "f_approx x \<ge> min (x\<^sub>0 powr p) (x\<^sub>1 powr p) * 1"
      unfolding f_approx_def by (intro mult_mono f_approx_aux) simp_all
  }
  from this x0_pos x1_pos show ?thesis by (intro that[of "min (x\<^sub>0 powr p) (x\<^sub>1 powr p)"]) auto
qed

lemma asymptotics_aux:
  assumes "x \<ge> x\<^sub>1" "i < k"
  assumes "s \<equiv> (if p \<ge> 0 then 1 else -1)"
  shows "(bs!i*x - s*hb*x*ln x powr -(1+e)) powr p \<le> (bs!i*x + (hs!i) x) powr p" (is "?thesis1")
  and   "(bs!i*x + (hs!i) x) powr p \<le> (bs!i*x + s*hb*x*ln x powr -(1+e)) powr p" (is "?thesis2")
proof-
  from assms x1_gt_1 have ln_x_pos: "ln x > 0" by simp
  from assms x1_pos have x_pos: "x > 0" by simp
  from assms x0_le_x1 have *: "hb / ln x powr (1+e) < bs!i/2" by (intro x0_hb_bound0) simp_all
  with hb_nonneg ln_x_pos have "(bs!i - hb * ln x powr -(1+e)) > 0"
    by (subst powr_minus) (simp_all add: field_simps)
  with * have "0 < x * (bs!i - hb * ln x powr -(1+e))" using x_pos
    by (subst (asm) powr_minus, intro mult_pos_pos)
  hence A: "0 < bs!i*x - hb * x * ln x powr -(1+e)" by (simp add: algebra_simps)

  from assms have "-(hb*x*ln x powr -(1+e)) \<le> -\<bar>(hs!i) x\<bar>"
    using h_bounds[of x "hs!i"] by (subst neg_le_iff_le, subst powr_minus) (simp add: field_simps)
  also have "... \<le> (hs!i) x" by simp
  finally have B: "bs!i*x - hb*x*ln x powr -(1+e) \<le> bs!i*x + (hs!i) x" by simp

  have "(hs!i) x \<le> \<bar>(hs!i) x\<bar>" by simp
  also from assms have "... \<le> (hb*x*ln x powr -(1+e))"
     using h_bounds[of x "hs!i"] by (subst powr_minus) (simp_all add: field_simps)
  finally have C: "bs!i*x + hb*x*ln x powr -(1+e) \<ge> bs!i*x + (hs!i) x" by simp

  from A B C show ?thesis1
    by (cases "p \<ge> 0") (auto intro: powr_mono2 powr_mono2' simp: assms(3))
  from A B C show ?thesis2
    by (cases "p \<ge> 0") (auto intro: powr_mono2 powr_mono2' simp: assms(3))
qed

lemma asymptotics1':
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "(bs!i*x) powr p * (1 + ln x powr (-e/2)) \<le>
           (bs!i*x + (hs!i) x) powr p * (1 + ln (bs!i*x + (hs!i) x) powr (-e/2))"
proof-
  from assms x0_le_x1 have x: "x \<ge> x\<^sub>0" by simp
  from b_pos[of "bs!i"] assms have b_pos: "bs!i > 0" "bs!i \<noteq> 0" by simp_all
  from b_less_1[of "bs!i"] assms have b_less_1: "bs!i < 1" by simp
  from x1_gt_1 assms have ln_x_pos: "ln x > 0" by simp
  have mono: "\<And>a b. a \<le> b \<Longrightarrow> (bs!i*x) powr p * a \<le> (bs!i*x) powr p * b"
    by (rule mult_left_mono) simp_all

  define s :: real where [abs_def]: "s = (if p \<ge> 0 then 1 else -1)"
  have "1 + ln x powr (-e/2) \<le>
          (1 - s*hb*inverse(bs!i)*ln x powr -(1+e)) powr p *
          (1 + ln (bs!i*x + hb * x / ln x powr (1+e)) powr (-e/2))" (is "_ \<le> ?A * ?B")
    using assms x unfolding s_def using asymptotics1[OF x assms(2)] asymptotics1'[OF x assms(2)]
    by simp
  also have "(bs!i*x) powr p * ... = (bs!i*x) powr p * ?A * ?B" by simp
  also from x0_hb_bound0'[OF x, of "bs!i"] hb_nonneg x ln_x_pos assms
    have "s*hb * ln x powr -(1 + e) < bs ! i"
    by (subst powr_minus) (simp_all add: field_simps s_def)
  hence "(bs!i*x) powr p * ?A = (bs!i*x*(1 - s*hb*inverse (bs!i)*ln x powr -(1+e))) powr p"
    using b_pos assms x x0_pos b_less_1 ln_x_pos
    by (subst powr_mult[symmetric]) (simp_all add: s_def field_simps)
  also have "bs!i*x*(1 - s*hb*inverse (bs!i)*ln x powr -(1+e)) = bs!i*x - s*hb*x*ln x powr -(1+e)"
    using b_pos assms by (simp add: algebra_simps)
  also have "?B = 1 + ln (bs!i*x + hb*x*ln x powr -(1+e)) powr (-e/2)"
    by (subst powr_minus) (simp add: field_simps)

  also {
    from x assms have "(bs!i*x - s*hb*x*ln x powr -(1+e)) powr p \<le> (bs!i*x + (hs!i) x) powr p"
      using asymptotics_aux(1)[OF assms(1,2) s_def] by blast
    moreover {
      have "(hs!i) x \<le> \<bar>(hs!i) x\<bar>" by simp
      also from assms have "\<bar>(hs!i) x\<bar> \<le> hb * x / ln x powr (1+e)" by (intro h_bounds) simp_all
      finally have "(hs ! i) x \<le> hb * x * ln x powr -(1 + e)"
        by (subst powr_minus) (simp_all add: field_simps)
      moreover from x hb_nonneg x0_pos have "hb * x * ln x powr -(1+e) \<ge> 0"
        by (intro mult_nonneg_nonneg) simp_all
      ultimately have "1 + ln (bs!i*x + hb * x * ln x powr -(1+e)) powr (-e/2) \<le>
                       1 + ln (bs!i*x + (hs!i) x) powr (-e/2)" using assms x e_pos b_pos x0_pos
      by (intro add_left_mono powr_mono2' ln_mono ln_gt_zero step_pos x0_hb_bound7'
                add_pos_nonneg mult_pos_pos) simp_all
    }
    ultimately have "(bs!i*x - s*hb*x*ln x powr -(1+e)) powr p *
                         (1 + ln (bs!i*x + hb * x * ln x powr -(1+e)) powr (-e/2))
                     \<le> (bs!i*x + (hs!i) x) powr p * (1 + ln (bs!i*x + (hs!i) x) powr (-e/2))"
      by (rule mult_mono) simp_all
  }
  finally show ?thesis by (simp_all add: mono)
qed

lemma asymptotics2':
  assumes "x \<ge> x\<^sub>1" "i < k"
  shows   "(bs!i*x + (hs!i) x) powr p * (1 - ln (bs!i*x + (hs!i) x) powr (-e/2)) \<le>
           (bs!i*x) powr p * (1 - ln x powr (-e/2))"
proof-
  define s :: real where "s = (if p \<ge> 0 then 1 else -1)"
  from assms x0_le_x1 have x: "x \<ge> x\<^sub>0" by simp
  from assms x1_gt_1 have ln_x_pos: "ln x > 0" by simp
  from b_pos[of "bs!i"] assms have b_pos: "bs!i > 0" "bs!i \<noteq> 0" by simp_all
  from b_pos hb_nonneg have pos: "1 + s * hb * (inverse (bs!i) * ln x powr -(1+e)) > 0"
    using x0_hb_bound0'[OF x, of "bs!i"] b_pos assms ln_x_pos
    by (subst powr_minus) (simp add: field_simps s_def)
  have mono: "\<And>a b. a \<le> b \<Longrightarrow> (bs!i*x) powr p * a \<le> (bs!i*x) powr p * b"
    by (rule mult_left_mono) simp_all

  let ?A = "(1 + s*hb*inverse(bs!i)*ln x powr -(1+e)) powr p"
  let ?B = "1 - ln (bs!i*x + (hs!i) x) powr (-e/2)"
  let ?B' = "1 - ln (bs!i*x + hb * x / ln x powr (1+e)) powr (-e/2)"

  from assms x have "(bs!i*x + (hs!i) x) powr p \<le> (bs!i*x + s*hb*x*ln x powr -(1+e)) powr p"
    by (intro asymptotics_aux(2)) (simp_all add: s_def)
  moreover from x0_hb_bound9[OF assms(1,2)] have "?B \<ge> 0" by (simp add: field_simps)
  ultimately have "(bs!i*x + (hs!i) x) powr p * ?B \<le>
                   (bs!i*x + s*hb*x*ln x powr -(1+e)) powr p * ?B" by (rule mult_right_mono)
  also from assms e_pos pos have "?B \<le> ?B'"
  proof -
    from x0_hb_bound8'[OF assms(1,2)] x0_hb_bound8[OF assms(1,2)] x0_ge_1
    have *: "bs ! i * x + s*hb * x / ln x powr (1 + e) > 1" by (simp add: s_def)
    moreover from * have "... > 0" by simp
    moreover from x0_hb_bound7[OF assms(1,2)] x0_ge_1 have "bs ! i * x + (hs ! i) x > 1" by simp
    moreover {
      have "(hs!i) x \<le> \<bar>(hs!i) x\<bar>" by simp
      also from assms x0_le_x1 have "... \<le> hb*x/ln x powr (1+e)" by (intro h_bounds) simp_all
      finally have "bs!i*x + (hs!i) x \<le> bs!i*x + hb*x/ln x powr (1+e)" by simp
    }
    ultimately show "?B \<le> ?B'" using assms e_pos x step_pos
      by (intro diff_left_mono powr_mono2' ln_mono ln_gt_zero) simp_all
  qed
  hence "(bs!i*x + s*hb*x*ln x powr -(1+e)) powr p * ?B \<le>
             (bs!i*x + s*hb*x*ln x powr -(1+e)) powr p * ?B'" by (intro mult_left_mono) simp_all
  also have "bs!i*x + s*hb*x*ln x powr -(1+e) = bs!i*x*(1 + s*hb*inverse (bs!i)*ln x powr -(1+e))"
    using b_pos by (simp_all add: field_simps)
  also have "... powr p = (bs!i*x) powr p * ?A"
    using b_pos x x0_pos pos by (intro powr_mult) simp_all
  also have "(bs!i*x) powr p * ?A * ?B' = (bs!i*x) powr p * (?A * ?B')" by simp
  also have "?A * ?B' \<le> 1 - ln x powr (-e/2)" using assms x
    using asymptotics2[OF x assms(2)] asymptotics2'[OF x assms(2)] by (simp add: s_def)
  finally show ?thesis by (simp_all add: mono)
qed

lemma Cx_le_step:
  assumes "i < k" "x \<ge> x\<^sub>1"
  shows   "C*x \<le> bs!i*x + (hs!i) x"
proof-
  from assms have "C*x \<le> bs!i*x - hb*x/ln x powr (1+e)" by (intro C_bound) simp_all
  also from assms have "-(hb*x/ln x powr (1+e)) \<le> -\<bar>(hs!i) x\<bar>"
    by (subst neg_le_iff_le, intro h_bounds) simp_all
  hence "bs!i*x - hb*x/ln x powr (1+e) \<le> bs!i*x + -\<bar>(hs!i) x\<bar>" by simp
  also have "-\<bar>(hs!i) x\<bar> \<le> (hs!i) x" by simp
  finally show ?thesis by simp
qed

end

locale akra_bazzi_nat_to_real = akra_bazzi_real_recursion +
  fixes f :: "nat \<Rightarrow> real"
  and   g :: "real \<Rightarrow> real"
  assumes f_base: "real x \<ge> x\<^sub>0 \<Longrightarrow> real x \<le> x\<^sub>1 \<Longrightarrow> f x \<ge> 0"
  and     f_rec:  "real x > x\<^sub>1 \<Longrightarrow>
                          f x = g (real x) + (\<Sum>i<k. as!i * f (nat \<lfloor>bs!i * x + (hs!i) (real x)\<rfloor>))"
  and     x0_int: "real (nat \<lfloor>x\<^sub>0\<rfloor>) = x\<^sub>0"
begin

function f' :: "real \<Rightarrow> real" where
  "x \<le> x\<^sub>1 \<Longrightarrow> f' x = f (nat \<lfloor>x\<rfloor>)"
| "x > x\<^sub>1 \<Longrightarrow> f' x = g x + (\<Sum>i<k. as!i * f' (bs!i * x + (hs!i) x))"
by (force, simp_all)
termination by (relation "Wellfounded.measure akra_bazzi_measure")
               (simp_all add: akra_bazzi_measure_decreases)

lemma f'_base: "x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f' x \<ge> 0"
  apply (subst f'.simps(1), assumption)
  apply (rule f_base)
  apply (rule order.trans[of _ "real (nat \<lfloor>x\<^sub>0\<rfloor>)"], simp add: x0_int)
  apply (subst of_nat_le_iff, intro nat_mono floor_mono, assumption)
  using x0_pos apply linarith
  done

lemmas f'_rec = f'.simps(2)

end

locale akra_bazzi_real_lower = akra_bazzi_real +
  fixes fb2 gb2 c2 :: real
  assumes f_base2:   "x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f x \<ge> fb2"
  and     fb2_pos:   "fb2 > 0"
  and     g_growth2: "\<forall>x\<ge>x\<^sub>1. \<forall>u\<in>{C*x..x}. c2 * g x \<ge> g u"
  and     c2_pos:    "c2 > 0"
  and     g_bounded: "x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> g x \<le> gb2"
begin

interpretation akra_bazzi_integral integrable integral by (rule integral)

lemma gb2_nonneg: "gb2 \<ge> 0" using g_bounded[of x\<^sub>0] x0_le_x1 x0_pos g_nonneg[of x\<^sub>0] by simp

lemma g_growth2':
  assumes "x \<ge> x\<^sub>1" "i < k" "u \<in> {bs!i*x+(hs!i) x..x}"
  shows   "c2 * g x \<ge> g u"
proof-
  from assms have "C*x \<le> bs!i*x+(hs!i) x" by (intro Cx_le_step)
  with assms have "u \<in> {C*x..x}" by auto
  with assms g_growth2 show ?thesis by simp
qed

lemma g_bounds2:
  obtains c4 where "\<And>x i. x \<ge> x\<^sub>1 \<Longrightarrow> i < k \<Longrightarrow> g_approx i x \<le> c4 * g x" "c4 > 0"
proof-
  define c4
    where "c4 = Max {c2 / min 1 (min ((b/2) powr (p+1)) ((b*3/2) powr (p+1))) |b. b \<in> set bs}"

  {
    from bs_nonempty obtain b where b: "b \<in> set bs" by (cases bs) auto
    let ?m = "min 1 (min ((b/2) powr (p+1)) ((b*3/2) powr (p+1)))"
    from b b_pos have "?m > 0" unfolding min_def by (auto simp: not_le)
    with b b_pos c2_pos have "c2 / ?m > 0" by (simp_all add: field_simps)
    with b have "c4 > 0" unfolding c4_def by (subst Max_gr_iff) (simp, simp, blast)
  }

  {
    fix x i assume i: "i < k" and x: "x \<ge> x\<^sub>1"
    have powr_negD: "a powr b \<le> 0 \<Longrightarrow> a = 0"
      for a b :: real unfolding powr_def by (simp split: if_split_asm)
    let ?m = "min 1 (min ((bs!i/2) powr (p+1)) ((bs!i*3/2) powr (p+1)))"
    have "min 1 ((bs!i + (hs ! i) x / x) powr (p+1)) \<ge> min 1 (min ((bs!i/2) powr (p+1)) ((bs!i*3/2) powr (p+1)))"
      apply (insert x i x0_le_x1 x1_pos step_pos b_pos[OF b_in_bs[OF i]],
             rule min.mono, simp, cases "p + 1 \<ge> 0")
      apply (rule order.trans[OF min.cobounded1 powr_mono2[OF _ _ x0_hb_bound4']], simp_all add: field_simps) []
      apply (rule order.trans[OF min.cobounded2 powr_mono2'[OF _ _ x0_hb_bound5]], simp_all add: field_simps) []
      done
    with i b_pos[of "bs!i"] have "c2 / min 1 ((bs!i + (hs ! i) x / x) powr (p+1)) \<le> c2 / ?m" using c2_pos
      unfolding min_def by (intro divide_left_mono) (auto intro!: mult_pos_pos dest!: powr_negD)

    also from i x have "... \<le> c4" unfolding c4_def by (intro Max.coboundedI) auto
    finally have "c2 / min 1 ((bs!i + (hs ! i) x / x) powr (p+1)) \<le> c4" .
  } note c4 = this

  {
    fix x :: real and i :: nat
    assume x: "x \<ge> x\<^sub>1" and i: "i < k"
    from x x1_pos have x_pos: "x > 0" by simp
    let ?x' = "bs ! i * x + (hs ! i) x"
    let ?x'' = "bs ! i + (hs ! i) x / x"
    from x x1_ge_1 i g_growth2' x0_le_x1 c2_pos
      have c2: "c2 > 0" "\<forall>u\<in>{?x'..x}. g u \<le> c2 * g x" by auto

    from x0_le_x1 x i have x'_le_x: "?x' \<le> x" by (intro step_le_x) simp_all
    let ?m = "min (?x' powr (p + 1)) (x powr (p + 1))"
    define m' where "m' = min 1 (?x'' powr (p + 1))"
    have [simp]: "bs ! i > 0" by (intro b_pos nth_mem) (simp add: i length_bs)
    from x0_le_x1 x i have [simp]: "?x' > 0" by (intro step_pos) simp_all

    {
      fix u assume u: "u \<ge> ?x'" "u \<le> x"
      have "?m \<le> u powr (p + 1)" using x u by (intro powr_lower_bound mult_pos_pos) simp_all
      moreover from c2 and u have "g u \<le> c2 * g x" by simp
      ultimately have "g u * ?m \<le> c2 * g x * u powr (p + 1)" using c2 x x1_pos x0_le_x1
        by (intro mult_mono mult_nonneg_nonneg g_nonneg) auto
    }
    hence "integral (\<lambda>u. g u / u powr (p+1)) ?x' x \<le> integral (\<lambda>u. c2 * g x / ?m) ?x' x"
      using x_pos step_pos[OF i x] x0_hb_bound7[OF x i] c2 x x0_le_x1
      by (intro integral_le x'_le_x akra_bazzi_integrable ballI integrable_const)
         (auto simp: field_simps intro!: mult_nonneg_nonneg g_nonneg)

    also from x0_pos x x0_le_x1 x'_le_x c2 have "... = (x - ?x') * (c2 * g x / ?m)"
      by (subst integral_const) (simp_all add: g_nonneg)
    also from c2 x_pos x x0_le_x1 have "c2 * g x \<ge> 0"
      by (intro mult_nonneg_nonneg g_nonneg) simp_all
    with x i x0_le_x1 have "(x - ?x') * (c2 * g x / ?m) \<le> x * (c2 * g x / ?m)"
      by (intro x0_hb_bound3 mult_right_mono) (simp_all add: field_simps)

    also have "x powr (p + 1) = x powr (p + 1) * 1" by simp
    also have "(bs ! i * x + (hs ! i) x) powr (p + 1) =
               (bs ! i + (hs ! i) x / x) powr (p + 1) * x powr (p + 1)"
      using x x1_pos step_pos[OF i x] x_pos i x0_le_x1
      by (subst powr_mult[symmetric]) (simp add: field_simps, simp, simp add: algebra_simps)
    also have "... = x powr (p + 1) * (bs ! i + (hs ! i) x / x) powr (p + 1)" by simp
    also have "min ... (x powr (p + 1) * 1) = x powr (p + 1) * m'" unfolding m'_def using x_pos
      by (subst min.commute, intro min_mult_left[symmetric]) simp

    also from x_pos have "x * (c2 * g x / (x powr (p + 1) * m')) = (c2/m') * (g x / x powr p)"
      by (simp add: field_simps powr_add)
    also from x i g_nonneg x0_le_x1 x1_pos have "... \<le> c4 * (g x / x powr p)" unfolding m'_def
      by (intro mult_right_mono c4) (simp_all add: field_simps)
    finally have "g_approx i x \<le> c4 * g x"
      unfolding g_approx_def using x_pos by (simp add: field_simps)
  }
  thus ?thesis using that \<open>c4 > 0\<close> by blast
qed

lemma f_approx_bounded_above:
  obtains c where "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f_approx x \<le> c" "c > 0"
proof-
  let ?m1 = "max (x\<^sub>0 powr p) (x\<^sub>1 powr p)"
  let ?m2 = "max (x\<^sub>0 powr (-(p+1))) (x\<^sub>1 powr (-(p+1)))"
  let ?m3 = "gb2 * ?m2"
  let ?m4 = "1 + (x\<^sub>1 - x\<^sub>0) * ?m3"
  let ?int = "\<lambda>x. integral (\<lambda>u. g u / u powr (p + 1)) x\<^sub>0 x"
  {
    fix x assume x: "x \<ge> x\<^sub>0" "x \<le> x\<^sub>1"
    with x0_pos have "x powr p \<le> ?m1" "?m1 \<ge> 0" by (intro powr_upper_bound) (simp_all add: max_def)
    moreover {
      fix u assume u: "u \<in> {x\<^sub>0..x}"
      have "g u / u powr (p + 1) = g u * u powr (-(p+1))"
        by (subst powr_minus) (simp add: field_simps)
      also from u x x0_pos have "u powr (-(p+1)) \<le> ?m2"
        by (intro powr_upper_bound) simp_all
      hence "g u * u powr (-(p+1)) \<le> g u * ?m2"
        using u g_nonneg x0_pos by (intro mult_left_mono) simp_all
      also from x u x0_pos have "g u \<le> gb2" by (intro g_bounded) simp_all
      hence "g u * ?m2 \<le> gb2 * ?m2" by (intro mult_right_mono) (simp_all add: max_def)
      finally have "g u / u powr (p + 1) \<le> ?m3" .
    } note A = this
    {
      from A x gb2_nonneg have "?int x \<le> integral (\<lambda>_. ?m3) x\<^sub>0 x"
        by (intro integral_le akra_bazzi_integrable integrable_const mult_nonneg_nonneg)
           (simp_all add: le_max_iff_disj)
      also from x gb2_nonneg have "... \<le> (x - x\<^sub>0) * ?m3"
        by (subst integral_const) (simp_all add: le_max_iff_disj)
      also from x gb2_nonneg have "... \<le> (x\<^sub>1 - x\<^sub>0) * ?m3"
        by (intro mult_right_mono mult_nonneg_nonneg) (simp_all add: max_def)
      finally have "1 + ?int x \<le> ?m4" by simp
    }
    moreover from x g_nonneg x0_pos have "?int x \<ge> 0"
      by (intro integral_nonneg akra_bazzi_integrable) (simp_all add: powr_def field_simps)
    hence "1 + ?int x \<ge> 0" by simp
    ultimately have "f_approx x \<le> ?m1 * ?m4"
      unfolding f_approx_def by (intro mult_mono)
    hence "f_approx x \<le> max 1 (?m1 * ?m4)" by simp
  }
  from that[OF this] show ?thesis by auto
qed

lemma f_bounded_below:
  assumes c': "c' > 0"
  obtains c where "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> 2 * (c * f_approx x) \<le> f x" "c \<le> c'" "c > 0"
proof-
  obtain c where c: "\<And>x. x\<^sub>0 \<le> x \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f_approx x \<le> c" "c > 0"
    by (rule f_approx_bounded_above) blast
  {
    fix x assume x: "x\<^sub>0 \<le> x" "x \<le> x\<^sub>1"
    with c have "inverse c * f_approx x \<le> 1" by (simp add: field_simps)
    moreover from x f_base2 x0_pos have "f x \<ge> fb2" by auto
    ultimately have "inverse c * f_approx x * fb2 \<le> 1 * f x" using fb2_pos
      by (intro mult_mono) simp_all
    hence "inverse c * fb2 * f_approx x \<le> f x" by (simp add: field_simps)
    moreover have "min c' (inverse c * fb2) * f_approx x \<le> inverse c * fb2 * f_approx x"
      using f_approx_nonneg x c
      by (intro mult_right_mono f_approx_nonneg) (simp_all add: field_simps)
    ultimately have "2 * (min c' (inverse c * fb2) / 2 * f_approx x) \<le> f x" by simp
  }
  moreover from c' have "min c' (inverse c * fb2) / 2 \<le> c'" by simp
  moreover have "min c' (inverse c * fb2) / 2 > 0"
    using c fb2_pos c' by simp
  ultimately show ?thesis by (rule that)
qed

lemma akra_bazzi_lower:
  obtains c5 where "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> f x \<ge> c5 * f_approx x" "c5 > 0"
proof-
  obtain c4 where c4: "\<And>x i. x \<ge> x\<^sub>1 \<Longrightarrow> i < k \<Longrightarrow> g_approx i x \<le> c4 * g x" "c4 > 0"
    by (rule g_bounds2) blast
  hence "inverse c4 / 2 > 0" by simp
  then obtain c5 where c5: "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> 2 * (c5 * f_approx x) \<le> f x"
                           "c5 \<le> inverse c4 / 2" "c5 > 0"
    by (rule f_bounded_below) blast

  {
  fix x :: real assume x: "x \<ge> x\<^sub>0"
  from c5 x have  " c5 * 1 * f_approx x \<le> c5 * (1 + ln x powr (- e / 2)) * f_approx x"
    by (intro mult_right_mono mult_left_mono f_approx_nonneg) simp_all
  also from x have "c5 * (1 + ln x powr (-e/2)) * f_approx x \<le> f x"
  proof (induction x rule: akra_bazzi_induct)
    case (base x)
    have "1 + ln x powr (-e/2) \<le> 2" using asymptotics3 base by simp
    hence "(1 + ln x powr (-e/2)) * (c5 * f_approx x) \<le> 2 * (c5 * f_approx x)"
      using c5 f_approx_nonneg base x0_ge_1 by (intro mult_right_mono mult_nonneg_nonneg) simp_all
    also from base have "2 * (c5 * f_approx x) \<le> f x"  by (intro c5) simp_all
    finally show ?case by (simp add: algebra_simps)
  next
    case (rec x)
    let ?a = "\<lambda>i. as!i" and ?b = "\<lambda>i. bs!i" and ?h = "\<lambda>i. hs!i"
    let ?int = "integral (\<lambda>u. g u / u powr (p+1)) x\<^sub>0 x"
    let ?int1 = "\<lambda>i. integral (\<lambda>u. g u / u powr (p+1)) x\<^sub>0 (?b i*x+?h i x)"
    let ?int2 = "\<lambda>i. integral (\<lambda>u. g u / u powr (p+1)) (?b i*x+?h i x) x"
    let ?l = "ln x powr (-e/2)" and ?l' = "\<lambda>i. ln (?b i*x + ?h i x) powr (-e/2)"

    from rec and x0_le_x1 x0_ge_1 have x: "x \<ge> x\<^sub>0" and x_gt_1: "x > 1" by simp_all
    with x0_pos have x_pos: "x > 0" and x_nonneg: "x \<ge> 0" by simp_all
    from c5 c4 have "c5 * c4 \<le> 1/2" by (simp add: field_simps)
    moreover from asymptotics3 x have "(1 + ?l) \<le> 2" by (simp add: field_simps)
    ultimately have "(c5*c4)*(1 + ?l) \<le> (1/2) * 2" by (rule mult_mono) simp_all
    hence "0 \<le> 1 - c5*c4*(1 + ?l)" by simp
    with g_nonneg[OF x] have "0 \<le> g x * ..." by (intro mult_nonneg_nonneg) simp_all
    hence "c5 * (1 + ?l) * f_approx x \<le> c5 * (1 + ?l) * f_approx x + g x - c5*c4*(1 + ?l) * g x"
      by (simp add: algebra_simps)
    also from x_gt_1 have "... = c5 * x powr p * (1 + ?l) * (1 + ?int - c4*g x/x powr p) + g x"
      by (simp add: field_simps f_approx_def powr_minus)
    also have "c5 * x powr p * (1 + ?l) * (1 + ?int - c4*g x/x powr p) =
                 (\<Sum>i<k. (?a i * ?b i powr p) * (c5 * x powr p * (1 + ?l) * (1 + ?int - c4*g x/x powr p)))"
      by (subst sum_distrib_right[symmetric]) (simp add: p_props)
    also have "... \<le> (\<Sum>i<k. ?a i * f (?b i*x + ?h i x))"
    proof (intro sum_mono, clarify)
      fix i assume i: "i < k"
      let ?f = "c5 * ?a i * (?b i * x) powr p"
      from rec.hyps i have "x\<^sub>0 < bs ! i * x + (hs ! i) x" by (intro x0_hb_bound7) simp_all
      hence "1 + ?int1 i \<ge> 1" by (intro f_approx_aux x0_hb_bound7) simp_all
      hence int_nonneg: "1 + ?int1 i \<ge> 0" by simp

      have "(?a i * ?b i powr p) * (c5 * x powr p * (1 + ?l) * (1 + ?int - c4*g x/x powr p)) =
            ?f * (1 + ?l) * (1 + ?int - c4*g x/x powr p)" (is "?expr = ?A * ?B")
            using x_pos b_pos[of "bs!i"] i by (subst powr_mult) simp_all
      also from rec.hyps i have "g_approx i x \<le> c4 * g x" by (intro c4) simp_all
      hence "c4*g x/x powr p \<ge> ?int2 i" unfolding g_approx_def using x_pos
        by (simp add: field_simps)
      hence "?A * ?B \<le> ?A * (1 + (?int - ?int2 i))" using i c5 a_ge_0
        by (intro mult_left_mono mult_nonneg_nonneg) simp_all
      also from rec.hyps i have "x\<^sub>0 < bs ! i * x + (hs ! i) x" by (intro x0_hb_bound7) simp_all
      hence "?int - ?int2 i = ?int1 i"
        apply (subst diff_eq_eq, subst eq_commute)
        apply (intro integral_combine akra_bazzi_integrable)
        apply (insert rec.hyps step_le_x[OF i, of x], simp_all)
        done
      also have "?A * (1 + ?int1 i) = (c5*?a i*(1 + ?int1 i)) * ((?b i*x) powr p * (1 + ?l))"
        by (simp add: algebra_simps)
      also have "... \<le> (c5*?a i*(1 + ?int1 i)) * ((?b i*x + ?h i x) powr p * (1 + ?l' i))"
        using rec.hyps i c5 a_ge_0 int_nonneg
        by (intro mult_left_mono asymptotics1' mult_nonneg_nonneg) simp_all
      also have "... = ?a i*(c5*(1 + ?l' i)*f_approx (?b i*x + ?h i x))"
        by (simp add: algebra_simps f_approx_def)
      also from i have "... \<le> ?a i * f (?b i*x + ?h i x)"
        by (intro mult_left_mono a_ge_0 rec.IH) simp_all
      finally show "?expr \<le> ?a i * f (?b i*x + ?h i x)" .
    qed
    also have "... + g x = f x" using f_rec[of x] rec.hyps x0_le_x1 by simp
    finally show ?case by simp
  qed
  finally have "c5 * f_approx x \<le> f x" by simp
  }
  from this and c5(3) show ?thesis by (rule that)
qed

lemma akra_bazzi_bigomega:
  "f \<in> \<Omega>(\<lambda>x. x powr p * (1 + integral (\<lambda>u. g u / u powr (p + 1)) x\<^sub>0 x))"
apply (fold f_approx_def, rule akra_bazzi_lower, erule landau_omega.bigI)
apply (subst eventually_at_top_linorder, rule exI[of _ x\<^sub>0])
apply (simp add: f_nonneg f_approx_nonneg)
done

end

locale akra_bazzi_real_upper = akra_bazzi_real +
  fixes fb1 c1 :: real
  assumes f_base1:   "x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f x \<le> fb1"
  and     g_growth1: "\<forall>x\<ge>x\<^sub>1. \<forall>u\<in>{C*x..x}. c1 * g x \<le> g u"
  and     c1_pos:    "c1 > 0"
begin

interpretation akra_bazzi_integral integrable integral by (rule integral)

lemma g_growth1':
  assumes "x \<ge> x\<^sub>1" "i < k" "u \<in> {bs!i*x+(hs!i) x..x}"
  shows   "c1 * g x \<le> g u"
proof-
  from assms have "C*x \<le> bs!i*x+(hs!i) x" by (intro Cx_le_step)
  with assms have "u \<in> {C*x..x}" by auto
  with assms g_growth1 show ?thesis by simp
qed

lemma g_bounds1:
  obtains c3 where
    "\<And>x i. x \<ge> x\<^sub>1 \<Longrightarrow> i < k \<Longrightarrow> c3 * g x \<le> g_approx i x" "c3 > 0"
proof-
  define c3 where "c3 =
    Min {c1*((1-b)/2) / max 1 (max ((b/2) powr (p+1)) ((b*3/2) powr (p+1))) |b. b \<in> set bs}"

  {
    fix b assume b: "b \<in> set bs"
    let ?x = "max 1 (max ((b/2) powr (p+1)) ((b*3/2) powr (p+1)))"
    have "?x \<ge> 1" by simp
    hence "?x > 0" by (rule less_le_trans[OF zero_less_one])
    with b b_less_1 c1_pos have "c1*((1-b)/2) / ?x > 0"
      by (intro divide_pos_pos mult_pos_pos) (simp_all add: algebra_simps)
  }
  hence "c3 > 0" unfolding c3_def by (subst Min_gr_iff) auto

  {
    fix x i assume i: "i < k" and x: "x \<ge> x\<^sub>1"
    with b_less_1 have b_less_1': "bs ! i < 1" by simp
    let ?m = "max 1 (max ((bs!i/2) powr (p+1)) ((bs!i*3/2) powr (p+1)))"
    from i x have "c3 \<le> c1*((1-bs!i)/2) / ?m" unfolding c3_def by (intro Min.coboundedI) auto
    also have "max 1 ((bs!i + (hs ! i) x / x) powr (p+1)) \<le> max 1 (max ((bs!i/2) powr (p+1)) ((bs!i*3/2) powr (p+1)))"
      apply (insert x i x0_le_x1 x1_pos step_pos[OF i x] b_pos[OF b_in_bs[OF i]],
             rule max.mono, simp, cases "p + 1 \<ge> 0")
      apply (rule order.trans[OF powr_mono2[OF _ _ x0_hb_bound5] max.cobounded2], simp_all add: field_simps) []
      apply (rule order.trans[OF powr_mono2'[OF _ _ x0_hb_bound4'] max.cobounded1], simp_all add: field_simps) []
      done
    with b_less_1' c1_pos have "c1*((1-bs!i)/2) / ?m \<le>
          c1*((1-bs!i)/2) / max 1 ((bs!i + (hs ! i) x / x) powr (p+1))"
      by (intro divide_left_mono mult_nonneg_nonneg) (simp_all add: algebra_simps)
    finally have "c3 \<le> c1*((1-bs!i)/2) / max 1 ((bs!i + (hs ! i) x / x) powr (p+1))" .
  } note c3 = this

  {
    fix x :: real and i :: nat
    assume x: "x \<ge> x\<^sub>1" and i: "i < k"
    from x x1_pos have x_pos: "x > 0" by simp
    let ?x' = "bs ! i * x + (hs ! i) x"
    let ?x'' = "bs ! i + (hs ! i) x / x"
    from x x1_ge_1 x0_le_x1 i c1_pos g_growth1'
      have c1: "c1 > 0" "\<forall>u\<in>{?x'..x}. g u \<ge> c1 * g x" by auto
    define b' where "b' = (1 - bs!i)/2"

    from x x0_le_x1 i have x'_le_x: "?x' \<le> x" by (intro step_le_x) simp_all
    let ?m = "max (?x' powr (p + 1)) (x powr (p + 1))"
    define m' where "m' = max 1 (?x'' powr (p + 1))"
    have [simp]: "bs ! i > 0" by (intro b_pos nth_mem) (simp add: i length_bs)
    from x x0_le_x1 i have x'_pos: "?x' > 0" by (intro step_pos) simp_all
    have m_pos: "?m > 0" unfolding max_def using x_pos step_pos[OF i x] by auto
    with x x0_le_x1 c1 have c1_g_m_nonneg: "c1 * g x / ?m \<ge> 0"
      by (intro mult_nonneg_nonneg divide_nonneg_pos g_nonneg) simp_all

    from x i g_nonneg x0_le_x1 have "c3 * (g x / x powr p) \<le> (c1*b'/m') * (g x / x powr p)"
      unfolding m'_def b'_def by (intro mult_right_mono c3) (simp_all add: field_simps)
    also from x_pos have "... = (x * b') * (c1 * g x / (x powr (p + 1) * m'))"
      by (simp add: field_simps powr_add)
    also from x i c1_pos x1_pos x0_le_x1
      have "... \<le> (x - ?x') * (c1 * g x / (x powr (p + 1) * m'))"
      unfolding b'_def m'_def by (intro x0_hb_bound6 mult_right_mono mult_nonneg_nonneg
                                        divide_nonneg_nonneg g_nonneg) simp_all
    also have "x powr (p + 1) * m' =
                 max (x powr (p + 1) * (bs ! i + (hs ! i) x / x) powr (p + 1)) (x powr (p + 1) * 1)"
      unfolding m'_def using x_pos by (subst max.commute, intro max_mult_left) simp
    also have "(x powr (p + 1) * (bs ! i + (hs ! i) x / x) powr (p + 1)) =
                 (bs ! i + (hs ! i) x / x) powr (p + 1) * x powr (p + 1)" by simp
    also have "... = (bs ! i * x + (hs ! i) x) powr (p + 1)"
      using x x1_pos step_pos[OF i x] x_pos i x0_le_x1 x_pos
      by (subst powr_mult[symmetric]) (simp add: field_simps, simp, simp add: algebra_simps)
    also have "x powr (p + 1) * 1 = x powr (p + 1)" by simp
    also have "(x - ?x') * (c1 * g x / ?m) = integral (\<lambda>_. c1 * g x / ?m) ?x' x"
      using x'_le_x by (subst integral_const[OF c1_g_m_nonneg]) auto
    also {
      fix u assume u: "u \<ge> ?x'" "u \<le> x"
      have "u powr (p + 1) \<le> ?m" using x u x'_pos by (intro powr_upper_bound mult_pos_pos) simp_all
      moreover from x'_pos u have "u \<ge> 0" by simp
      moreover from c1 and u have "c1 * g x \<le> g u" by simp
      ultimately have "c1 * g x * u powr (p + 1) \<le> g u * ?m" using c1 x u x0_hb_bound7[OF x i]
        by (intro mult_mono g_nonneg) auto
      with m_pos u step_pos[OF i x]
        have "c1 * g x / ?m \<le> g u / u powr (p + 1)" by (simp add: field_simps)
    }
    hence "integral (\<lambda>_. c1 * g x / ?m) ?x' x \<le> integral (\<lambda>u. g u / u powr (p + 1)) ?x' x"
      using x0_hb_bound7[OF x i] x'_le_x
      by (intro integral_le ballI akra_bazzi_integrable integrable_const c1_g_m_nonneg) simp_all
    finally have "c3 * g x \<le> g_approx i x" using x_pos
      unfolding g_approx_def by (simp add: field_simps)
  }
  thus ?thesis using that \<open>c3 > 0\<close> by blast
qed

lemma f_bounded_above:
  assumes c': "c' > 0"
  obtains c where "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f x \<le> (1/2) * (c * f_approx x)" "c \<ge> c'" "c > 0"
proof-
  obtain c where c: "\<And>x. x\<^sub>0 \<le> x \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f_approx x \<ge> c" "c > 0"
    by (rule f_approx_bounded_below) blast
  have fb1_nonneg: "fb1 \<ge> 0" using f_base1[of "x\<^sub>0"] f_nonneg[of x\<^sub>0] x0_le_x1 by simp
  {
    fix x assume x: "x \<ge> x\<^sub>0" "x \<le> x\<^sub>1"
    with f_base1 x0_pos have "f x \<le> fb1" by simp
    moreover from c and x have "f_approx x \<ge> c" by blast
    ultimately have "f x * c \<le> fb1 * f_approx x" using c fb1_nonneg by (intro mult_mono) simp_all
    also from f_approx_nonneg x have "... \<le> (fb1 + 1) * f_approx x" by (simp add: algebra_simps)
    finally have "f x \<le> ((fb1+1) / c) * f_approx x" by (simp add: field_simps c)
    also have "... \<le> max ((fb1+1) / c) c' * f_approx x"
      by (intro mult_right_mono) (simp_all add: f_approx_nonneg x)
    finally have "f x \<le> 1/2 * (max ((fb1+1) / c) c' * 2 * f_approx x)" by simp
  }
  moreover have "max ((fb1+1) / c) c' * 2 \<ge> max ((fb1+1) / c) c'"
    by (subst mult_le_cancel_left1) (insert c', simp)
  hence "max ((fb1+1) / c) c' * 2 \<ge> c'" by (rule order.trans[OF max.cobounded2])
  moreover from fb1_nonneg and c have "(fb1+1) / c > 0" by simp
  hence "max ((fb1+1) / c) c' * 2 > 0" by simp
  ultimately show ?thesis by (rule that)
qed

lemma akra_bazzi_upper:
  obtains c6 where "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> f x \<le> c6 * f_approx x" "c6 > 0"
proof-
  obtain c3 where c3: "\<And>x i. x \<ge> x\<^sub>1 \<Longrightarrow> i < k \<Longrightarrow> c3 * g x \<le> g_approx i x" "c3 > 0"
    by (rule g_bounds1) blast
  hence "2 / c3 > 0" by simp
  then obtain c6 where c6: "\<And>x. x \<ge> x\<^sub>0 \<Longrightarrow> x \<le> x\<^sub>1 \<Longrightarrow> f x \<le> 1/2 * (c6 * f_approx x)"
                           "c6 \<ge> 2 / c3" "c6 > 0"
    by (rule f_bounded_above) blast

  {
  fix x :: real assume x: "x \<ge> x\<^sub>0"
  hence "f x \<le> c6 * (1 - ln x powr (-e/2)) * f_approx x"
  proof (induction x rule: akra_bazzi_induct)
    case (base x)
    from base have "f x \<le> 1/2 * (c6 * f_approx x)"  by (intro c6) simp_all
    also have "1 - ln x powr (-e/2) \<ge> 1/2" using asymptotics4 base by simp
    hence "(1 - ln x powr (-e/2)) * (c6 * f_approx x) \<ge> 1/2 * (c6 * f_approx x)"
      using c6 f_approx_nonneg base x0_ge_1 by (intro mult_right_mono mult_nonneg_nonneg) simp_all
    finally show ?case by (simp add: algebra_simps)
  next
    case (rec x)
    let ?a = "\<lambda>i. as!i" and ?b = "\<lambda>i. bs!i" and ?h = "\<lambda>i. hs!i"
    let ?int = "integral (\<lambda>u. g u / u powr (p+1)) x\<^sub>0 x"
    let ?int1 = "\<lambda>i. integral (\<lambda>u. g u / u powr (p+1)) x\<^sub>0 (?b i*x+?h i x)"
    let ?int2 = "\<lambda>i. integral (\<lambda>u. g u / u powr (p+1)) (?b i*x+?h i x) x"
    let ?l = "ln x powr (-e/2)" and ?l' = "\<lambda>i. ln (?b i*x + ?h i x) powr (-e/2)"

    from rec and x0_le_x1 have x: "x \<ge> x\<^sub>0" by simp
    with x0_pos have x_pos: "x > 0" and x_nonneg: "x \<ge> 0" by simp_all
    from c6 c3 have "c6 * c3 \<ge> 2" by (simp add: field_simps)
    have "f x = (\<Sum>i<k. ?a i * f (?b i*x + ?h i x)) + g x" (is "_ = ?sum + _")
      using f_rec[of x] rec.hyps x0_le_x1 by simp
    also have "?sum \<le> (\<Sum>i<k. (?a i*?b i powr p) * (c6*x powr p*(1 - ?l)*(1 + ?int - c3*g x/x powr p)))" (is "_ \<le> ?sum'")
    proof (rule sum_mono, clarify)
      fix i assume i: "i < k"
      from rec.hyps i have "x\<^sub>0 < bs ! i * x + (hs ! i) x" by (intro x0_hb_bound7) simp_all
      hence "1 + ?int1 i \<ge> 1" by (intro f_approx_aux x0_hb_bound7) simp_all
      hence int_nonneg: "1 + ?int1 i \<ge> 0" by simp
      have l_le_1: "ln x powr -(e/2) \<le> 1" using asymptotics3[OF x] by (simp add: field_simps)

      from i have "f (?b i*x + ?h i x) \<le> c6 * (1 - ?l' i) * f_approx (?b i*x + ?h i x)"
        by (rule rec.IH)
      hence "?a i * f (?b i*x + ?h i x) \<le> ?a i * ..." using a_ge_0 i
        by (intro mult_left_mono) simp_all
      also have "... = (c6*?a i*(1 + ?int1 i)) * ((?b i*x + ?h i x) powr p * (1 - ?l' i))"
        unfolding f_approx_def by (simp add: algebra_simps)
      also from i rec.hyps c6 a_ge_0
        have "... \<le> (c6*?a i*(1 + ?int1 i)) * ((?b i*x) powr p * (1 - ?l))"
        by (intro mult_left_mono asymptotics2' mult_nonneg_nonneg int_nonneg) simp_all
      also have "... = (1 + ?int1 i) * (c6*?a i*(?b i*x) powr p * (1 - ?l))"
        by (simp add: algebra_simps)
      also from rec.hyps i have "x\<^sub>0 < bs ! i * x + (hs ! i) x" by (intro x0_hb_bound7) simp_all
      hence "?int1 i = ?int - ?int2 i"
        apply (subst eq_diff_eq)
        apply (intro integral_combine akra_bazzi_integrable)
        apply (insert rec.hyps step_le_x[OF i, of x], simp_all)
        done
      also from rec.hyps i have "c3 * g x \<le> g_approx i x" by (intro c3) simp_all
      hence "?int2 i \<ge> c3*g x/x powr p" unfolding g_approx_def using x_pos
        by (simp add: field_simps)
      hence "(1 + (?int - ?int2 i)) * (c6*?a i*(?b i*x) powr p * (1 - ?l)) \<le>
             (1 + ?int - c3*g x/x powr p) * (c6*?a i*(?b i*x) powr p * (1 - ?l))"
             using i c6 a_ge_0 l_le_1
             by (intro mult_right_mono mult_nonneg_nonneg) (simp_all add: field_simps)
      also have "... = (?a i*?b i powr p) * (c6*x powr p*(1 - ?l) * (1 + ?int - c3*g x/x powr p))"
        using b_pos[of "bs!i"] x x0_pos i by (subst powr_mult) (simp_all add: algebra_simps)
      finally show "?a i * f (?b i*x + ?h i x) \<le> ..." .
    qed

    hence "?sum + g x \<le> ?sum' + g x" by simp
    also have "... = c6 * x powr p * (1 - ?l) * (1 + ?int - c3*g x/x powr p) + g x"
      by (simp add: sum_distrib_right[symmetric] p_props)
    also have "... = c6 * (1 - ?l) * f_approx x - (c6*c3*(1 - ?l) - 1) * g x"
      unfolding f_approx_def using x_pos by (simp add: field_simps)
    also {
       from c6 c3 have "c6*c3 \<ge> 2" by (simp add: field_simps)
       moreover have "(1 - ?l) \<ge> 1/2" using asymptotics4[OF x] by simp
       ultimately have "c6*c3*(1 - ?l) \<ge> 2 * (1/2)" by (intro mult_mono) simp_all
       with x x_pos have "(c6*c3*(1 - ?l) - 1) * g x \<ge> 0"
         by (intro mult_nonneg_nonneg g_nonneg) simp_all
       hence "c6 * (1 - ?l) * f_approx x - (c6*c3*(1 - ?l) - 1) * g x \<le>
                  c6 * (1 - ?l) * f_approx x" by (simp add: algebra_simps)
    }
    finally show ?case .
  qed
  also from x c6 have "... \<le> c6 * 1 * f_approx x"
    by (intro mult_left_mono mult_right_mono f_approx_nonneg) simp_all
  finally have "f x \<le> c6 * f_approx x" by simp
  }
  from this and c6(3) show ?thesis by (rule that)
qed

lemma akra_bazzi_bigo:
  "f \<in> O(\<lambda>x. x powr p *(1 + integral (\<lambda>u. g u / u powr (p + 1)) x\<^sub>0 x))"
apply (fold f_approx_def, rule akra_bazzi_upper, erule landau_o.bigI)
apply (subst eventually_at_top_linorder, rule exI[of _ x\<^sub>0])
apply (simp add: f_nonneg f_approx_nonneg)
done

end

end


(*
  File:    Going_To_Filter.thy
  Author:  Manuel Eberl, TU München

  A filter describing the points x such that f(x) tends to some other filter.
*)

section \<open>The \<open>going_to\<close> filter\<close>

theory Going_To_Filter
  imports Complex_Main
begin

definition going_to_within :: "('a \<Rightarrow> 'b) \<Rightarrow> 'b filter \<Rightarrow> 'a set \<Rightarrow> 'a filter"
  (\<open>(_)/ going'_to (_)/ within (_)\<close> [1000,60,60] 60) where
  "f going_to F within A = inf (filtercomap f F) (principal A)"

abbreviation going_to :: "('a \<Rightarrow> 'b) \<Rightarrow> 'b filter \<Rightarrow> 'a filter"
    (infix \<open>going'_to\<close> 60)
    where "f going_to F \<equiv> f going_to F within UNIV"

text \<open>
  The \<open>going_to\<close> filter is, in a sense, the opposite of \<^term>\<open>filtermap\<close>.
  It corresponds to the intuition of, given a function $f: A \to B$ and a filter $F$ on the
  range of $B$, looking at such values of $x$ that $f(x)$ approaches $F$. This can be
  written as \<^term>\<open>f going_to F\<close>.

  A classic example is the \<^term>\<open>at_infinity\<close> filter, which describes the neigbourhood
  of infinity (i.\,e.\ all values sufficiently far away from the zero). This can also be written
  as \<^term>\<open>norm going_to at_top\<close>.

  Additionally, the \<open>going_to\<close> filter can be restricted with an optional `within' parameter.
  For instance, if one would would want to consider the filter of complex numbers near infinity
  that do not lie on the negative real line, one could write
  \<^term>\<open>norm going_to at_top within - complex_of_real ` {..0}\<close>.

  A third, less mathematical example lies in the complexity analysis of algorithms.
  Suppose we wanted to say that an algorithm on lists takes $O(n^2)$ time where $n$ is
  the length of the input list. We can write this using the Landau symbols from the AFP,
  where the underlying filter is \<^term>\<open>length going_to at_top\<close>. If, on the other hand,
  we want to look the complexity of the algorithm on sorted lists, we could use the filter
  \<^term>\<open>length going_to at_top within {xs. sorted xs}\<close>.
\<close>

lemma going_to_def: "f going_to F = filtercomap f F"
  by (simp add: going_to_within_def)

lemma eventually_going_toI [intro]:
  assumes "eventually P F"
  shows   "eventually (\<lambda>x. P (f x)) (f going_to F)"
  using assms by (auto simp: going_to_def)

lemma filterlim_going_toI_weak [intro]: "filterlim f F (f going_to F within A)"
  unfolding going_to_within_def
  by (meson filterlim_filtercomap filterlim_iff inf_le1 le_filter_def)

lemma going_to_mono: "F \<le> G \<Longrightarrow> A \<subseteq> B \<Longrightarrow> f going_to F within A \<le> f going_to G within B"
  unfolding going_to_within_def by (intro inf_mono filtercomap_mono) simp_all

lemma going_to_inf:
  "f going_to (inf F G) within A = inf (f going_to F within A) (f going_to G within A)"
  by (simp add: going_to_within_def filtercomap_inf inf_assoc inf_commute inf_left_commute)

lemma going_to_sup:
  "f going_to (sup F G) within A \<ge> sup (f going_to F within A) (f going_to G within A)"
  by (auto simp: going_to_within_def intro!: inf.coboundedI1 filtercomap_sup filtercomap_mono)

lemma going_to_top [simp]: "f going_to top within A = principal A"
  by (simp add: going_to_within_def)

lemma going_to_bot [simp]: "f going_to bot within A = bot"
  by (simp add: going_to_within_def)

lemma going_to_principal:
  "f going_to principal A within B = principal (f -` A \<inter> B)"
  by (simp add: going_to_within_def)

lemma going_to_within_empty [simp]: "f going_to F within {} = bot"
  by (simp add: going_to_within_def)

lemma going_to_within_union [simp]:
  "f going_to F within (A \<union> B) = sup (f going_to F within A) (f going_to F within B)"
  by (simp add: going_to_within_def flip: inf_sup_distrib1)

lemma eventually_going_to_at_top_linorder:
  fixes f :: "'a \<Rightarrow> 'b :: linorder"
  shows "eventually P (f going_to at_top within A) \<longleftrightarrow> (\<exists>C. \<forall>x\<in>A. f x \<ge> C \<longrightarrow> P x)"
  unfolding going_to_within_def eventually_filtercomap
    eventually_inf_principal eventually_at_top_linorder by fast

lemma eventually_going_to_at_bot_linorder:
  fixes f :: "'a \<Rightarrow> 'b :: linorder"
  shows "eventually P (f going_to at_bot within A) \<longleftrightarrow> (\<exists>C. \<forall>x\<in>A. f x \<le> C \<longrightarrow> P x)"
  unfolding going_to_within_def eventually_filtercomap
    eventually_inf_principal eventually_at_bot_linorder by fast

lemma eventually_going_to_at_top_dense:
  fixes f :: "'a \<Rightarrow> 'b :: {linorder,no_top}"
  shows "eventually P (f going_to at_top within A) \<longleftrightarrow> (\<exists>C. \<forall>x\<in>A. f x > C \<longrightarrow> P x)"
  unfolding going_to_within_def eventually_filtercomap
    eventually_inf_principal eventually_at_top_dense by fast

lemma eventually_going_to_at_bot_dense:
  fixes f :: "'a \<Rightarrow> 'b :: {linorder,no_bot}"
  shows "eventually P (f going_to at_bot within A) \<longleftrightarrow> (\<exists>C. \<forall>x\<in>A. f x < C \<longrightarrow> P x)"
  unfolding going_to_within_def eventually_filtercomap
    eventually_inf_principal eventually_at_bot_dense by fast

lemma eventually_going_to_nhds:
  "eventually P (f going_to nhds a within A) \<longleftrightarrow>
     (\<exists>S. open S \<and> a \<in> S \<and> (\<forall>x\<in>A. f x \<in> S \<longrightarrow> P x))"
  unfolding going_to_within_def eventually_filtercomap eventually_inf_principal
    eventually_nhds by fast

lemma eventually_going_to_at:
  "eventually P (f going_to (at a within B) within A) \<longleftrightarrow>
     (\<exists>S. open S \<and> a \<in> S \<and> (\<forall>x\<in>A. f x \<in> B \<inter> S - {a} \<longrightarrow> P x))"
  unfolding at_within_def going_to_inf eventually_inf_principal
            eventually_going_to_nhds going_to_principal by fast

lemma norm_going_to_at_top_eq: "norm going_to at_top = at_infinity"
  by (simp add: eventually_at_infinity eventually_going_to_at_top_linorder filter_eq_iff)

lemmas at_infinity_altdef = norm_going_to_at_top_eq [symmetric]

end


theory FOL_substitution
imports FOL_formula
begin

section{*FOL Substitution *}
text{* Here we we set up substitution for FOL_Formulas. The goal is the Class Existence Theorem
       of the next section, which says that FOL_Formulas define classes.*}

primrec FOL_MaxVar2 :: "FOL_Formula \<Rightarrow> nat"
--"In contrast to FOL_MaxVar, FOL_MaxVar2 looks at every variable even the quantified ones."
where
  "FOL_MaxVar2 FTrue = 0"
  | "FOL_MaxVar2 FFalse = 0"
  | "FOL_MaxVar2 (FBelongs x y) = max (FOL_Var x) (FOL_Var y)"
  | "FOL_MaxVar2 (FEquals x y) = max (FOL_Var x) (FOL_Var y)"
  | "FOL_MaxVar2 (FAnd \<phi> \<psi>) = max (FOL_MaxVar2 \<phi>) (FOL_MaxVar2 \<psi>)"
  | "FOL_MaxVar2 (FNot \<phi>) = FOL_MaxVar2 \<phi>"
  | "FOL_MaxVar2 (FOr \<phi> \<psi>) = max (FOL_MaxVar2 \<phi>) (FOL_MaxVar2 \<psi>)"
  | "FOL_MaxVar2 (FImp \<phi> \<psi>) = max (FOL_MaxVar2 \<phi>) (FOL_MaxVar2 \<psi>)"
  | "FOL_MaxVar2 (FIff \<phi> \<psi>) = max (FOL_MaxVar2 \<phi>) (FOL_MaxVar2 \<psi>)"
  | "FOL_MaxVar2 (FEx n \<phi>) = max n (FOL_MaxVar2 \<phi>)"
  | "FOL_MaxVar2 (FAll n \<phi>) = max n (FOL_MaxVar2 \<phi>)"

text{* We now define Finite partial functions (Fpf) from nat to nat, as in John Harrison's "Handbook of Practical
       Logic and Automated Reasoning", and prove some basic facts about them. *}

datatype Fpf = Nil | Elem nat nat Fpf
(*    Examples:
      Nil ~ Id\<^sub>n\<^sub>a\<^sub>t
      Elem 10 20 Nil ~ (10 \<mapsto> 20)Id\<^sub>n\<^sub>a\<^sub>t
      Elem 10 20 (Elem 30 40 Nil) ~ (10 \<mapsto> 20) (30 \<mapsto> 40)Id\<^sub>n\<^sub>a\<^sub>t
      Elem 10 30 (Elem 10 20 Nil) ~ (10 \<mapsto> 30) (10 \<mapsto> 20)Id\<^sub>n\<^sub>a\<^sub>t = (1 \<mapsto> 3)Id\<^sub>n\<^sub>a\<^sub>t*)

(* Fp function application *)
primrec assoc :: "[Fpf, nat] \<Rightarrow> nat"
where
  "assoc Nil k = k"
| "assoc (Elem x y rest) k = (if x = k then y else (assoc rest k))"

(* associable lst k = True \<longleftrightarrow> k occurs in any of the Elem-blocks
as the first or second field
Example associable (Elem 10 20 (Elem 30 40 Nil)) 30 = True
        associable (Elem 10 20 (Elem 30 40 Nil)) 50 = False
*)
primrec associable :: "[Fpf, nat] \<Rightarrow> bool"
where
  "associable Nil k = False"
| "associable (Elem x y rest) k = ((x = k) \<or> (associable rest k) \<or> (y = k))"

(* Substitution of all variables  with their image under an fpf
   which is itself if it does not occur in the domain. *)
primrec subst_t :: "[FOL_Term, Fpf] \<Rightarrow> FOL_Term"
where
  "subst_t (FVar n) fpf = FVar (assoc fpf n)"
| "subst_t (FConst y) fpf = FConst y"

(* Yields a variable that does not occur in a given fpf  *)
primrec alpha_convert_0 :: "[Fpf] \<Rightarrow> nat"
where
  "alpha_convert_0 Nil = 0"
| "alpha_convert_0 (Elem x y rest) = (max (Suc x) (max (Suc y) (alpha_convert_0 rest)))"

(* Yields a variable that does neither occur in a given fpf nor in a given FOL_Formula *)
definition alpha_convert :: "[FOL_Formula, Fpf] \<Rightarrow> nat"
where
  "alpha_convert p f = (max (Suc (FOL_MaxVar2 p)) (alpha_convert_0 f))"

(* Substitution of all variables  with their image under an fpf.
   Gives new names to quantified variables even if it is not necessary
   because that makes some of the following theorems easier to prove. *)
fun subst_f :: "[FOL_Formula, Fpf] \<Rightarrow> FOL_Formula"
where
  "subst_f FTrue fpf = FTrue"
| "subst_f FFalse fpf = FFalse"
| "subst_f (FBelongs x y) fpf = (FBelongs (subst_t x fpf) (subst_t y fpf))"
| "subst_f (FEquals x y) fpf = (FEquals (subst_t x fpf) (subst_t y fpf))"
| "subst_f (FAnd p q) fpf = (FAnd (subst_f p fpf) (subst_f q fpf))"
| "subst_f (FOr p q) fpf = (FOr (subst_f p fpf) (subst_f q fpf))"
| "subst_f (FNot p) fpf = (FNot (subst_f p fpf))"
| "subst_f (FImp p q) fpf = (FImp (subst_f p fpf) (subst_f q fpf))"
| "subst_f (FIff p q) fpf = (FIff (subst_f p fpf) (subst_f q fpf))"
| "subst_f (FEx n q) fpf = (FEx (alpha_convert (FEx n q) fpf)
                                (subst_f q (Elem n (alpha_convert (FEx n q) fpf) fpf)))"
| "subst_f (FAll n q) fpf = (FAll (alpha_convert  (FEx n q) fpf)
                                  (subst_f q (Elem n (alpha_convert (FEx n q) fpf) fpf)))"

(* Updates an intepretation i :: nat \<Rightarrow> Set according to a given Fpf f, i.e.
   replaces i(n) with i ( f(n) ) for every n. *)
definition Update2 :: "[nat \<Rightarrow> Set, Fpf] \<Rightarrow> (nat \<Rightarrow> Set)"
where "Update2 i fpf k =  i (assoc fpf k) "

(* substitution of a single variable. Just convenience definitions for proof
  of ex_definable_lemma in Class_Comprehension.thy. *)
definition single_subst_t :: "[FOL_Term, nat, nat] \<Rightarrow> FOL_Term"
where "single_subst_t x i j = subst_t x (Elem i j Nil)"

definition single_subst_f :: "[FOL_Formula, nat, nat] \<Rightarrow> FOL_Formula"
where "single_subst_f x i j = subst_f x (Elem i j Nil)"

lemma MaxVar_comp: "FOL_MaxVar \<phi> \<le> FOL_MaxVar2 \<phi>"
by (induct \<phi>, auto)

(* Same as FOL_MaxVar_Dom but with FOL_MaxVar2 instead of FOL_MaxVar *)
lemma FOL_MaxVar2_Dom: "\<forall>i. \<forall>j. (\<forall>k \<le> FOL_MaxVar2 \<phi>. i(k) = j(k)) \<longrightarrow> FOL_True \<phi> i \<longleftrightarrow> FOL_True \<phi> j"
using FOL_MaxVar_Dom MaxVar_comp le_trans by blast

(* Convenience lemma *)
lemma FOL_MaxVar2_Dom_For_Use:
"(\<And>k. k \<le> FOL_MaxVar2 \<phi> \<Longrightarrow> i(k) = j(k)) \<Longrightarrow> FOL_True \<phi> i \<longleftrightarrow> FOL_True \<phi> j"
using FOL_MaxVar2_Dom by blast

(* Facts about updates.*)

lemma Update_lemma_0: "Update2 i Nil k = (i k)"
by (metis Update2_def assoc.simps(1))

lemma Update_lemma_0': "Update2 i Nil = i"
using Update_lemma_0 Update2_def by auto

lemma Update_lemma_1: "Update2 i (Elem k x2 rest) k = (i x2)"
by (metis Update2_def assoc.simps(2))

lemma Update_lemma_2: "k \<noteq> l \<Longrightarrow> Update2 i (Elem k x2 rest) l = (Update2 i rest) l"
by (metis Update2_def assoc.simps(2))

(* Relation of Update to Update2  *)
lemma Update_lemma_3: "(Update (Update2 i fpf) x (i y)) = (Update2 i (Elem x y fpf))"
proof -
  have "(Update2 i (Elem x y fpf)) =
   (\<lambda> k::nat. if x = k then (i y) else (Update2 i fpf) k)" using Update_lemma_2 Update_lemma_1 by auto
  also have "\<dots> = (Update (Update2 i fpf) x (i y))" using Update_def by auto
  finally show ?thesis by auto
qed

(* Update and Update2 "commute" if the variable that is replaced by Update
   does not occur in the fpf used by Update2 *)
lemma Update_lemma_4:
"\<And>i. \<not> associable fpf x' \<Longrightarrow> (Update2 (Update i x' y) fpf) = (Update (Update2 i fpf) x' y)"
proof (induct fpf)
  fix i
  assume "\<not> associable Nil x'"
  have "Update2 (Update i x' y) Nil = Update i x' y" using Update_lemma_0  by auto
  then show "Update2 (Update i x' y) Nil = Update (Update2 i Nil) x' y"
  using Update_lemma_0' by auto
  next
  fix x1 x2 fpf i
  assume iasm: "\<And>i. (\<not> associable fpf x' \<Longrightarrow> Update2 (Update i x' y) fpf = Update (Update2 i fpf) x' y)"
  assume v: "\<not> associable (Elem x1 x2 fpf) x'"
  then have "\<not> associable fpf x'" by auto
  from v have w: "x' \<noteq> x1" by auto
  from v have w2: "x' \<noteq> x2" by auto
  have "Update2 (Update i x' y) (Elem x1 x2 fpf) =
  (Update (Update2 (Update i x' y) fpf) x1 ((Update i x' y) x2))" using Update_lemma_3  by auto
  also have "\<dots> =
  (Update (Update (Update2 i fpf) x' y) x1 ((Update i x' y) x2))" using iasm v by auto
  also have "\<dots> =
  (Update (Update (Update2 i fpf) x' y) x1 (i x2))" using w2 Update_def by auto
  also have "\<dots> =
  (Update (Update (Update2 i fpf) x' y) x1 (i x2))" using w2 Update_def by auto
  then have "\<dots> =
  (Update (Update (Update2 i fpf) x1 (i x2)) x' y)" using w Update_def by auto
  also have "\<dots> =
  (Update (Update2 i (Elem x1 x2 fpf)) x' y)" using Update_lemma_3 by auto
  finally show "Update2 (Update i x' y) (Elem x1 x2 fpf) = Update (Update2 i (Elem x1 x2 fpf)) x' y"
  .
qed

(* facts about associable *)

lemma associable_0: "\<And>x. associable fpf x \<Longrightarrow> x < (alpha_convert_0 fpf)"
by (induct fpf , auto, fastforce)

lemma associable_1: "\<not> associable fpf (alpha_convert (FEx x1 \<phi>) fpf)"
using associable_0 alpha_convert_def max.cobounded2 not_less by fastforce

(* Technical lemmas used in the proof of subst_lemma*)
lemma subst_help_lemma_1: "\<And> x1 \<phi> y. x' = (alpha_convert (FEx x1 \<phi>) fpf) \<Longrightarrow> (Update2 (Update i x' y) fpf) x' = y"
using Update_def Update_lemma_4 associable_1 by auto

lemma subst_help_lemma_2:
assumes "x' = alpha_convert (FEx x1 \<phi>) fpf"
shows "FOL_True \<phi> (Update (Update (Update2 i fpf) x' y) x1 y) =
       FOL_True \<phi> (Update (Update2 i fpf) x1 y)"
using Update_def alpha_convert_def assms
by (simp add: FOL_MaxVar2_Dom Suc_n_not_le_n max_def)

(* Substitution of variables in a formula can be replaced by updating the interpretation
   and vice versa. *)

(* TO DO: Write the proof below in Isar *)
lemma substitution_lemma: "\<And> fpf i. (FOL_True (subst_f \<phi> fpf) i) =  FOL_True \<phi> (Update2 i fpf)"
apply(induct \<phi>)
apply(simp)+
apply (metis FOL_Eval.simps(1) FOL_Eval.simps(2) FOL_Term.exhaust Update2_def subst_t.simps(1)
             subst_t.simps(2))
apply(simp)
apply (metis FOL_Eval.simps(1) FOL_Eval.simps(2) FOL_Term.exhaust Update2_def subst_t.simps(1)
             subst_t.simps(2))
apply(simp)
apply(simp)
apply(simp)
apply(simp)
apply(simp)
proof -
  fix x1 \<phi> fpf i
  show " (\<And>fpf i. FOL_True (subst_f \<phi> fpf) i = FOL_True \<phi> (Update2 i fpf)) \<Longrightarrow>
       FOL_True (subst_f (FEx x1 \<phi>) fpf) i = FOL_True (FEx x1 \<phi>) (Update2 i fpf)"
  proof -
    def "x'" \<equiv> "(alpha_convert (FEx x1 \<phi>) fpf)"
    assume iasm: "(\<And>fpf i. FOL_True (subst_f \<phi> fpf) i = FOL_True \<phi> (Update2 i fpf))"
    have "FOL_True (subst_f (FEx x1 \<phi>) fpf) i =
       FOL_True (FEx x' (subst_f \<phi> (Elem x1 x' fpf))) i"
    using x'_def by auto
    also have "\<dots> =
      (\<exists> y::Set. FOL_True (subst_f \<phi> (Elem x1 x' fpf)) (Update i x' y))" by auto
    also have "\<dots> =
      (\<exists> y::Set. FOL_True \<phi> (Update2 (Update i x' y) (Elem x1 x' fpf)))" using iasm by auto
    also have "\<dots> =
      (\<exists> y::Set. FOL_True \<phi> (Update (Update2 (Update i x' y) fpf) x1
                                    ((Update2 (Update i x' y) fpf) x')))"
    by (metis Update_def Update_lemma_3 subst_help_lemma_1 x'_def)
    also have "\<dots> =
      (\<exists> y::Set. FOL_True \<phi> (Update (Update2 (Update i x' y) fpf) x1 y))"
    using subst_help_lemma_1 x'_def by auto
    also have "\<dots> =
      (\<exists> y::Set. FOL_True \<phi> (Update (Update (Update2 i fpf) x' y) x1 y))"
    by (simp add: Update_lemma_4 associable_1 x'_def)
    also have "\<dots> =
      (\<exists> y::Set. FOL_True \<phi> (Update (Update2 i fpf) x1 y))"
    using subst_help_lemma_2 x'_def by auto
    also have "\<dots> =
      FOL_True (FEx x1 \<phi>) (Update2 i fpf) " by auto
    finally show ?thesis .
   qed
  next
   fix x1 \<phi> fpf i
  show " (\<And>fpf i. FOL_True (subst_f \<phi> fpf) i = FOL_True \<phi> (Update2 i fpf)) \<Longrightarrow>
       FOL_True (subst_f (FAll x1 \<phi>) fpf) i = FOL_True (FAll x1 \<phi>) (Update2 i fpf)"
  proof -
    def "x'" \<equiv> "(alpha_convert (FEx x1 \<phi>) fpf)"
    assume iasm: "(\<And>fpf i. FOL_True (subst_f \<phi> fpf) i = FOL_True \<phi> (Update2 i fpf))"
    have "FOL_True (subst_f (FAll x1 \<phi>) fpf) i =
       FOL_True (FAll x' (subst_f \<phi> (Elem x1 x' fpf))) i"
    using x'_def by auto
    also have "\<dots> =
      (\<forall> y::Set. FOL_True (subst_f \<phi> (Elem x1 x' fpf)) (Update i x' y))" by auto
    also have "\<dots> =
      (\<forall> y::Set. FOL_True \<phi> (Update2 (Update i x' y) (Elem x1 x' fpf)))" using iasm by auto
    also have "\<dots> =
      (\<forall> y::Set. FOL_True \<phi> (Update (Update2 (Update i x' y) fpf) x1
                                     ((Update2 (Update i x' y) fpf) x')))"
    by (metis Update_def Update_lemma_3 subst_help_lemma_1 x'_def)
    also have "\<dots> =
      (\<forall> y::Set. FOL_True \<phi> (Update (Update2 (Update i x' y) fpf) x1 y))"
    using subst_help_lemma_1 x'_def by auto
    also have "\<dots> =
      (\<forall> y::Set. FOL_True \<phi> (Update (Update (Update2 i fpf) x' y) x1 y))"
    by (simp add: Update_lemma_4 associable_1 x'_def)
    also have "\<dots> =
      (\<forall> y::Set. FOL_True \<phi> (Update (Update2 i fpf) x1 y))"
    using subst_help_lemma_2 x'_def by auto
    also have "\<dots> =
      FOL_True (FAll x1 \<phi>) (Update2 i fpf) " by auto
    finally show ?thesis .
  qed
qed

(* Similar to subst_lemma only replacing a single variable. *)
lemma single_subst_lemma: "\<And> k l i. FOL_True (single_subst_f \<phi> k l) i =
                           FOL_True \<phi> (Update i k (i l))"
using FOL_MaxVar_Dom Update2_def Update_def single_subst_f_def substitution_lemma by auto

end


section \<open> Bouncing Ball \<close>

theory utp_bouncing_ball
  imports "UTP1-Hybrid.utp_hybrid"
begin

subsection \<open> State-space \<close>

text \<open> We first setup the state-space and prove this is a topological (T2) space \<close>

alphabet bball =
  height :: real
  velocity :: real

setup_lifting type_definition_bball_ext

instantiation bball_ext :: (t2_space) t2_space
begin
  lift_definition open_bball_ext :: "'a bball_scheme set \<Rightarrow> bool" is "open" .
  instance by (intro_classes, (transfer, auto simp add: separation_t2)+)
end

subsection \<open> Constants \<close>

text \<open> Next we define some constants; the ODE (ordinary differential equation) and its solution \<close>

abbreviation grav :: real where
"grav \<equiv> -9.81"

subsection \<open> Differential Equations and Solutions \<close>

text \<open> The ODE specifies two continuous variables, for velocity and height respectively. It
  does not depend on time which makes it an autonomous ODE. \<close>

abbreviation grav_ode :: "(real \<times> real) ODE" where
"grav_ode \<equiv> (\<lambda> t (v, h). (- grav, v))"

text \<open> We also present the following solution to the ODE, which is a function from initial values
  of the continuous variables to a continuous function that shows how the variables change with time. \<close>

abbreviation grav_sol :: "real \<times> real \<Rightarrow> real \<Rightarrow> real \<times> real" where
"grav_sol \<equiv> \<lambda> (v\<^sub>0, h\<^sub>0) \<tau>. (v\<^sub>0 - grav * \<tau>, v\<^sub>0 * \<tau> - grav * (\<tau> * \<tau>) / 2 + h\<^sub>0)"

lemma grav_ode_sol:
  "(\<langle>{&velocity,&height} \<bullet> grav_ode(ti)\<rangle>\<^sub>h) = {&velocity,&height} \<leftarrow>\<^sub>h \<guillemotleft>grav_sol\<guillemotright>($velocity, $height)\<^sub>a(\<guillemotleft>ti\<guillemotright>)\<^sub>a"
proof -
  have 1:"\<forall>l>0. unique_on_strip 0 {0..l} grav_ode 1"
    by (auto, unfold_locales, auto intro!: continuous_on_Pair continuous_on_const continuous_on_fst continuous_on_snd simp add: lipschitz_on_def dist_Pair_Pair prod.case_eq_if)
  have 2:"\<forall> v\<^sub>0 h\<^sub>0. \<forall>l>0. ((grav_sol (v\<^sub>0, h\<^sub>0)) solves_ode grav_ode) {0..l} UNIV"
    by (clarify, ode_cert)
  from 1 2 have sol:"\<forall> v\<^sub>0 h\<^sub>0. \<forall>l>0. ((grav_sol (v\<^sub>0, h\<^sub>0)) usolves_ode grav_ode from 0) {0..l} UNIV"
    by (auto, rule_tac uos_impl_uniq_sol[where L=1], simp_all)
  show ?thesis
    apply (subst ode_solution[where \<F>="grav_sol"])
    apply (simp_all add: lens_indep_sym)
    using sol apply (simp)
    apply (rel_auto)
  done
qed

subsection \<open> System Definition \<close>

definition bouncing_ball :: "(unit, bball) hyrel" where
  "bouncing_ball =
     (\<^bold>c:velocity, \<^bold>c:height) :=\<^sub>r (0, 2.0) ;;
      (\<langle>{&velocity,&height} \<bullet> grav_ode(ti)\<rangle>\<^sub>h until\<^sub>h ($height\<acute> \<le>\<^sub>u 0) ;;
       \<^bold>c:velocity :=\<^sub>r (- 0.8 * &\<^bold>c:velocity))\<^sup>\<star>"

subsection \<open> Example Properties \<close>

lemma "\<lceil>$height\<acute> \<ge>\<^sub>u 0\<rceil>\<^sub>h \<sqsubseteq> bouncing_ball"
  apply (simp add: bouncing_ball_def)
  apply (rule ustar_inductr)
  apply (rel_simp)
oops

end