Theory BMSSP_Complexity

theory BMSSP_Complexity
  imports "HOL-Library.Landau_Symbols" BMSSP_Operational_Pull
begin

section ‹Core Cost Accounting›

text ‹
  This theory starts the checked complexity side of the development.  Earlier
  theories prove that the recursive BMSSP algorithm returns the right vertices
  and labels.  Here the same traces are interpreted as cost recurrences.  The
  first part is deliberately arithmetic: logarithmic target functions, level
  widths, level capacities, and the Big-O envelopes that eventually become the
  SSSP headline.

  The file then moves inside the graph locale and introduces the graph-size
  quantities used by the cost bounds.  Vertex counts, edge counts, outgoing
  ranges, and range-tree chains are all finite combinatorial objects.  The
  proofs establish that the lists produced by a partition loop are disjoint
  slices, so their vertex and outgoing-edge costs can be summed without double
  counting.

  The final part defines a costed operational relation for the abstract
  partition interface.  It is intentionally not the bucketed implementation; it
  is the reusable recurrence layer that later theories refine.  The bucketed
  proof plugs concrete local costs into these abstract cost expressions, while
  the top-level bounds instantiate the logarithmic parameter schedule.
›

definition sssp_log_factor where
  "sssp_log_factor n = (ln (real n + 2)) powr (2 / 3)"

definition sssp_log_factor_one_third where
  "sssp_log_factor_one_third n = (ln (real n + 2)) powr (1 / 3)"

definition sssp_time_target where
  "sssp_time_target m n = real (m n) * sssp_log_factor n"

definition bmssp_level_width :: "nat  nat  nat" where
  "bmssp_level_width t l = 2 ^ (l * t)"

definition bmssp_level_cap :: "nat  nat  nat  nat" where
  "bmssp_level_cap k t l = k * bmssp_level_width t l"

lemma bmssp_level_width_pos [simp]:
  "0 < bmssp_level_width t l"
  unfolding bmssp_level_width_def by simp

lemma bmssp_level_cap_eq:
  "bmssp_level_cap k t l = k * 2 ^ (l * t)"
  unfolding bmssp_level_cap_def bmssp_level_width_def by simp

lemma bmssp_level_width_mono:
  assumes "l  l'"
  shows "bmssp_level_width t l  bmssp_level_width t l'"
proof -
  have "l * t  l' * t"
    using assms by simp
  then show ?thesis
    unfolding bmssp_level_width_def by (simp add: power_increasing)
qed

lemma bmssp_level_width_Suc:
  "bmssp_level_width t (Suc l) = bmssp_level_width t l * 2 ^ t"
  unfolding bmssp_level_width_def by (simp add: power_add algebra_simps)

lemma bmssp_level_width_prev_times_two_le:
  assumes "0 < t" "0 < l"
  shows "2 * bmssp_level_width t (l - 1)  bmssp_level_width t l"
proof (cases l)
  case 0
  then show ?thesis
    using assms by simp
next
  case (Suc l')
  have two_le: "(2::nat)  2 ^ t"
  proof -
    have "2 ^ 1  (2::nat) ^ t"
      by (rule power_increasing) (use assms(1) in simp_all)
    then show ?thesis
      by simp
  qed
  have "2 * bmssp_level_width t (l - 1) 
      (2 ^ t) * bmssp_level_width t (l - 1)"
    by (rule mult_right_mono[OF two_le]) simp
  also have " = bmssp_level_width t l"
    unfolding Suc bmssp_level_width_Suc by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma bmssp_level_cap_mono:
  assumes "l  l'"
  shows "bmssp_level_cap k t l  bmssp_level_cap k t l'"
  unfolding bmssp_level_cap_def
  using bmssp_level_width_mono[OF assms, of t] by simp

lemma bmssp_level_cap_ge_level:
  "k  bmssp_level_cap k t l"
  unfolding bmssp_level_cap_def
  using bmssp_level_width_pos[of t l]
  by (cases "bmssp_level_width t l") simp_all

lemma bmssp_level_size_constraint_arithmetic:
  assumes t_pos: "0 < t"
    and l_pos: "0 < l"
    and before: "acc < k * bmssp_level_width t l"
    and child: "child  4 * k * bmssp_level_width t (l - 1)"
    and extra: "extra  k * bmssp_level_width t l"
    and U_size: "U_size  acc + child + extra"
  shows "U_size  4 * k * bmssp_level_width t l"
proof -
  have acc_le: "acc  k * bmssp_level_width t l"
    using before by simp
  have child_le: "child  2 * k * bmssp_level_width t l"
  proof -
    have "4 * k * bmssp_level_width t (l - 1) 
        2 * k * bmssp_level_width t l"
    proof -
      have "2 * bmssp_level_width t (l - 1)  bmssp_level_width t l"
        by (rule bmssp_level_width_prev_times_two_le[OF t_pos l_pos])
      then have "2 * k * (2 * bmssp_level_width t (l - 1)) 
          2 * k * bmssp_level_width t l"
        by simp
      then show ?thesis
        by (simp add: algebra_simps)
    qed
    then show ?thesis
      using child by linarith
  qed
  have "U_size 
      k * bmssp_level_width t l +
      2 * k * bmssp_level_width t l +
      k * bmssp_level_width t l"
    using U_size acc_le child_le extra by linarith
  also have " = 4 * k * bmssp_level_width t l"
    by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma sssp_log_factor_pos [simp]:
  "0 < sssp_log_factor n"
proof -
  have "1 < real n + 2"
    by simp
  then have "0 < ln (real n + 2)"
    by (rule ln_gt_zero)
  then show ?thesis
    unfolding sssp_log_factor_def by simp
qed

lemma sssp_log_factor_one_third_pos [simp]:
  "0 < sssp_log_factor_one_third n"
proof -
  have "1 < real n + 2"
    by simp
  then have "0 < ln (real n + 2)"
    by (rule ln_gt_zero)
  then show ?thesis
    unfolding sssp_log_factor_one_third_def by simp
qed

lemma sssp_log_factor_mono:
  assumes "n  m"
  shows "sssp_log_factor n  sssp_log_factor m"
proof -
  have ln_le: "ln (real n + 2)  ln (real m + 2)"
    by (rule ln_mono) (use assms in simp_all)
  have ln_nonneg: "0  ln (real n + 2)"
    by simp
  show ?thesis
    unfolding sssp_log_factor_def
    by (rule powr_mono2) (use ln_nonneg ln_le in simp_all)
qed

lemma sssp_log_factor_one_third_mono:
  assumes "n  m"
  shows "sssp_log_factor_one_third n  sssp_log_factor_one_third m"
proof -
  have ln_le: "ln (real n + 2)  ln (real m + 2)"
    by (rule ln_mono) (use assms in simp_all)
  have ln_nonneg: "0  ln (real n + 2)"
    by simp
  show ?thesis
    unfolding sssp_log_factor_one_third_def
    by (rule powr_mono2) (use ln_nonneg ln_le in simp_all)
qed

lemma sssp_log_factor_square_arg_le:
  "sssp_log_factor (n * n)  2 * sssp_log_factor n"
proof -
  let ?a = "ln (real (n * n) + 2)"
  let ?b = "ln (real n + 2)"
  have arg_le: "real (n * n) + 2  (real n + 2) ^ 2"
    by (simp add: power2_eq_square algebra_simps)
  have arg_pos: "0 < real (n * n) + 2"
  proof -
    have "0  real (n * n)"
      by simp
    then show ?thesis
      by linarith
  qed
  have ln_le: "?a  2 * ?b"
  proof -
    have "?a  ln ((real n + 2) ^ 2)"
      by (rule ln_mono) (use arg_le in simp, rule arg_pos)
    also have " = 2 * ?b"
      by (simp add: ln_realpow)
    finally show ?thesis .
  qed
  have a_nonneg: "0  ?a"
    by simp
  have b_nonneg: "0  ?b"
    by simp
  have powr_le: "?a powr (2 / 3)  (2 * ?b) powr (2 / 3)"
    by (rule powr_mono2) (use a_nonneg ln_le in simp_all)
  have two_powr_le: "(2::real) powr (2 / 3)  2"
  proof -
    have "(2::real) powr (2 / 3)  2 powr 1"
      by (rule powr_mono) simp_all
    then show ?thesis
      by simp
  qed
  have "(2 * ?b) powr (2 / 3) =
      (2::real) powr (2 / 3) * (?b powr (2 / 3))"
    by (simp add: powr_mult)
  also have "  2 * (?b powr (2 / 3))"
    by (rule mult_right_mono[OF two_powr_le]) (use b_nonneg in simp)
  finally have "(2 * ?b) powr (2 / 3)  2 * (?b powr (2 / 3))" .
  then show ?thesis
    unfolding sssp_log_factor_def using powr_le by linarith
qed

lemma sssp_log_factor_one_third_square_arg_le:
  "sssp_log_factor_one_third (n * n) 
    2 * sssp_log_factor_one_third n"
proof -
  let ?a = "ln (real (n * n) + 2)"
  let ?b = "ln (real n + 2)"
  have arg_le: "real (n * n) + 2  (real n + 2) ^ 2"
    by (simp add: power2_eq_square algebra_simps)
  have arg_pos: "0 < real (n * n) + 2"
  proof -
    have "0  real (n * n)"
      by simp
    then show ?thesis
      by linarith
  qed
  have ln_le: "?a  2 * ?b"
  proof -
    have "?a  ln ((real n + 2) ^ 2)"
      by (rule ln_mono) (use arg_le in simp, rule arg_pos)
    also have " = 2 * ?b"
      by (simp add: ln_realpow)
    finally show ?thesis .
  qed
  have a_nonneg: "0  ?a"
    by simp
  have b_nonneg: "0  ?b"
    by simp
  have powr_le: "?a powr (1 / 3)  (2 * ?b) powr (1 / 3)"
    by (rule powr_mono2) (use a_nonneg ln_le in simp_all)
  have two_powr_le: "(2::real) powr (1 / 3)  2"
  proof -
    have "(2::real) powr (1 / 3)  2 powr 1"
      by (rule powr_mono) simp_all
    then show ?thesis
      by simp
  qed
  have "(2 * ?b) powr (1 / 3) =
      (2::real) powr (1 / 3) * (?b powr (1 / 3))"
    by (simp add: powr_mult)
  also have "  2 * (?b powr (1 / 3))"
    by (rule mult_right_mono[OF two_powr_le]) (use b_nonneg in simp)
  finally have "(2 * ?b) powr (1 / 3)  2 * (?b powr (1 / 3))" .
  then show ?thesis
    unfolding sssp_log_factor_one_third_def using powr_le by linarith
qed

lemma sssp_log_factor_one_third_square:
  "sssp_log_factor_one_third n * sssp_log_factor_one_third n =
    sssp_log_factor n"
proof -
  have "0 < ln (real n + 2)"
  proof -
    have "1 < real n + 2"
      by simp
    then show ?thesis
      by (rule ln_gt_zero)
  qed
  then show ?thesis
    unfolding sssp_log_factor_one_third_def sssp_log_factor_def
    by (simp add: powr_add [symmetric])
qed

lemma eventually_one_le_sssp_log_factor_one_third:
  "eventually (λn. 1  sssp_log_factor_one_third n) at_top"
proof (rule eventually_at_top_linorderI[of 1])
  fix n :: nat
  assume n_ge: "1  n"
  have "exp 1  (3::real)"
    by (rule exp_le)
  also have "  real n + 2"
    using n_ge by simp
  finally have exp_le_arg: "exp 1  real n + 2" .
  have ln_ge: "1  ln (real n + 2)"
    using exp_le_arg by (simp add: ln_ge_iff)
  show "1  sssp_log_factor_one_third n"
    unfolding sssp_log_factor_one_third_def
    by (rule ge_one_powr_ge_zero[OF ln_ge]) simp
qed

lemma sssp_time_target_nonneg [simp]:
  "0  sssp_time_target m n"
proof -
  have "0  real (m n)"
    by simp
  moreover have "0  sssp_log_factor n"
    using sssp_log_factor_pos[of n] by linarith
  ultimately show ?thesis
    unfolding sssp_time_target_def by (rule mult_nonneg_nonneg)
qed

theorem sssp_time_target_bigoI_norm:
  assumes C: "0 < C"
    and eventually_bound:
      "eventually
        (λn. norm (real (T n))  C * norm (sssp_time_target m n)) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
  using C eventually_bound unfolding bigo_def by blast

theorem sssp_time_target_bigoI:
  assumes C: "0 < C"
    and eventually_bound:
      "eventually (λn. real (T n)  C * sssp_time_target m n) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof (rule sssp_time_target_bigoI_norm[OF C])
  show "eventually
      (λn. norm (real (T n))  C * norm (sssp_time_target m n)) at_top"
    using eventually_bound
  proof eventually_elim
    case (elim n)
    then show ?case
      by simp
  qed
qed

definition bmssp_graph_time_bound where
  "bmssp_graph_time_bound A R l t m n =
    2 * A n * (2 * l n + 1) * n + (R n + l n * t n) * m n"

theorem bmssp_graph_time_bound_bigo_sssp_time_target:
  assumes Cn_pos: "0 < Cn"
    and Cv_pos: "0 < Cv"
    and Ce_pos: "0 < Ce"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and vertex_factor:
      "eventually
        (λn. real (2 * A n * (2 * l n + 1)) 
          Cv * sssp_log_factor n) at_top"
    and edge_factor:
      "eventually
        (λn. real (R n + l n * t n) 
          Ce * sssp_log_factor n) at_top"
  shows "(λn. real (bmssp_graph_time_bound A R l t m n))
     O(λn. sssp_time_target m n)"
proof (rule sssp_time_target_bigoI[where C = "Cv * Cn + Ce"])
  show "0 < Cv * Cn + Ce"
  proof -
    have "0 < Cv * Cn"
      using Cv_pos Cn_pos by (rule mult_pos_pos)
    then show ?thesis
      using Ce_pos by linarith
  qed
next
  show "eventually
      (λn. real (bmssp_graph_time_bound A R l t m n) 
        (Cv * Cn + Ce) * sssp_time_target m n) at_top"
    using vertex_count_dominated vertex_factor edge_factor
  proof eventually_elim
    case (elim n)
    let ?vf = "real (2 * A n * (2 * l n + 1))"
    let ?ef = "real (R n + l n * t n)"
    let ?log = "sssp_log_factor n"
    have vertex:
      "?vf * real n  (Cv * ?log) * (Cn * real (m n))"
    proof (rule mult_mono)
      show "?vf  Cv * ?log"
        using elim(2) .
      show "real n  Cn * real (m n)"
        using elim(1) .
      show "0  real n"
        by simp
      show "0  Cv * ?log"
      proof -
        have "0  Cv"
          using Cv_pos by linarith
        moreover have "0  ?log"
          using sssp_log_factor_pos[of n] by linarith
        ultimately show ?thesis
          by (rule mult_nonneg_nonneg)
      qed
    qed
    also have " = (Cv * Cn) * (real (m n) * ?log)"
      by (simp add: algebra_simps)
    finally have vertex':
      "?vf * real n  (Cv * Cn) * (real (m n) * ?log)" .
    have edge:
      "?ef * real (m n)  (Ce * ?log) * real (m n)"
      by (rule mult_right_mono[OF elim(3)]) simp
    also have " = Ce * (real (m n) * ?log)"
      by (simp add: algebra_simps)
    finally have edge':
      "?ef * real (m n)  Ce * (real (m n) * ?log)" .
    have "real (bmssp_graph_time_bound A R l t m n) =
        ?vf * real n + ?ef * real (m n)"
      unfolding bmssp_graph_time_bound_def by simp
    also have " 
        (Cv * Cn) * (real (m n) * ?log) +
        Ce * (real (m n) * ?log)"
      using vertex' edge' by linarith
    also have " = (Cv * Cn + Ce) * sssp_time_target m n"
      unfolding sssp_time_target_def by (simp add: algebra_simps)
    finally show ?case .
  qed
qed

theorem bmssp_cost_bound_bigo_sssp_time_target:
  assumes Cn_pos: "0 < Cn"
    and Cv_pos: "0 < Cv"
    and Ce_pos: "0 < Ce"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and vertex_factor:
      "eventually
        (λn. real (2 * A n * (2 * l n + 1)) 
          Cv * sssp_log_factor n) at_top"
    and edge_factor:
      "eventually
        (λn. real (R n + l n * t n) 
          Ce * sssp_log_factor n) at_top"
    and cost_bound:
      "eventually (λn. T n  bmssp_graph_time_bound A R l t m n) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof -
  have graph:
    "(λn. real (bmssp_graph_time_bound A R l t m n))
       O(λn. sssp_time_target m n)"
    by (rule bmssp_graph_time_bound_bigo_sssp_time_target
      [OF Cn_pos Cv_pos Ce_pos vertex_count_dominated vertex_factor
        edge_factor])
  then obtain C where C_pos: "0 < C"
    and eventually_graph:
      "eventually
        (λn. norm (real (bmssp_graph_time_bound A R l t m n)) 
          C * norm (sssp_time_target m n)) at_top"
    unfolding bigo_def by blast
  show ?thesis
  proof (rule sssp_time_target_bigoI_norm[OF C_pos])
    show "eventually
        (λn. norm (real (T n))  C * norm (sssp_time_target m n)) at_top"
      using cost_bound eventually_graph
    proof eventually_elim
      case (elim n)
      have "norm (real (T n)) 
          norm (real (bmssp_graph_time_bound A R l t m n))"
        using elim(1) by simp
      also have "  C * norm (sssp_time_target m n)"
        using elim(2) .
      finally show ?case .
    qed
  qed
qed

definition bmssp_refined_graph_time_bound where
  "bmssp_refined_graph_time_bound A R H l t m n =
    2 * A n * (2 * l n + 1) * n +
    (R n + t n + l n * H n) * m n"

theorem bmssp_refined_graph_time_bound_bigo_sssp_time_target:
  assumes Cn_pos: "0 < Cn"
    and Cv_pos: "0 < Cv"
    and Ce_pos: "0 < Ce"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and vertex_factor:
      "eventually
        (λn. real (2 * A n * (2 * l n + 1)) 
          Cv * sssp_log_factor n) at_top"
    and edge_factor:
      "eventually
        (λn. real (R n + t n + l n * H n) 
          Ce * sssp_log_factor n) at_top"
  shows "(λn. real (bmssp_refined_graph_time_bound A R H l t m n))
     O(λn. sssp_time_target m n)"
proof (rule sssp_time_target_bigoI[where C = "Cv * Cn + Ce"])
  show "0 < Cv * Cn + Ce"
  proof -
    have "0 < Cv * Cn"
      using Cv_pos Cn_pos by (rule mult_pos_pos)
    then show ?thesis
      using Ce_pos by linarith
  qed
next
  show "eventually
      (λn. real (bmssp_refined_graph_time_bound A R H l t m n) 
        (Cv * Cn + Ce) * sssp_time_target m n) at_top"
    using vertex_count_dominated vertex_factor edge_factor
  proof eventually_elim
    case (elim n)
    let ?vf = "real (2 * A n * (2 * l n + 1))"
    let ?ef = "real (R n + t n + l n * H n)"
    let ?log = "sssp_log_factor n"
    have vertex:
      "?vf * real n  (Cv * ?log) * (Cn * real (m n))"
    proof (rule mult_mono)
      show "?vf  Cv * ?log"
        using elim(2) .
      show "real n  Cn * real (m n)"
        using elim(1) .
      show "0  real n"
        by simp
      show "0  Cv * ?log"
      proof -
        have "0  Cv"
          using Cv_pos by linarith
        moreover have "0  ?log"
          using sssp_log_factor_pos[of n] by linarith
        ultimately show ?thesis
          by (rule mult_nonneg_nonneg)
      qed
    qed
    also have " = (Cv * Cn) * (real (m n) * ?log)"
      by (simp add: algebra_simps)
    finally have vertex':
      "?vf * real n  (Cv * Cn) * (real (m n) * ?log)" .
    have edge:
      "?ef * real (m n)  (Ce * ?log) * real (m n)"
      by (rule mult_right_mono[OF elim(3)]) simp
    also have " = Ce * (real (m n) * ?log)"
      by (simp add: algebra_simps)
    finally have edge':
      "?ef * real (m n)  Ce * (real (m n) * ?log)" .
    have "real (bmssp_refined_graph_time_bound A R H l t m n) =
        ?vf * real n + ?ef * real (m n)"
      unfolding bmssp_refined_graph_time_bound_def by simp
    also have " 
        (Cv * Cn) * (real (m n) * ?log) +
        Ce * (real (m n) * ?log)"
      using vertex' edge' by linarith
    also have " = (Cv * Cn + Ce) * sssp_time_target m n"
      unfolding sssp_time_target_def by (simp add: algebra_simps)
    finally show ?case .
  qed
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target:
  assumes Cn_pos: "0 < Cn"
    and Cv_pos: "0 < Cv"
    and Ce_pos: "0 < Ce"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and vertex_factor:
      "eventually
        (λn. real (2 * A n * (2 * l n + 1)) 
          Cv * sssp_log_factor n) at_top"
    and edge_factor:
      "eventually
        (λn. real (R n + t n + l n * H n) 
          Ce * sssp_log_factor n) at_top"
    and cost_bound:
      "eventually
        (λn. T n  bmssp_refined_graph_time_bound A R H l t m n) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof -
  have graph:
    "(λn. real (bmssp_refined_graph_time_bound A R H l t m n))
       O(λn. sssp_time_target m n)"
    by (rule bmssp_refined_graph_time_bound_bigo_sssp_time_target
      [OF Cn_pos Cv_pos Ce_pos vertex_count_dominated vertex_factor
        edge_factor])
  then obtain C where C_pos: "0 < C"
    and eventually_graph:
      "eventually
        (λn. norm (real (bmssp_refined_graph_time_bound A R H l t m n)) 
          C * norm (sssp_time_target m n)) at_top"
    unfolding bigo_def by blast
  show ?thesis
  proof (rule sssp_time_target_bigoI_norm[OF C_pos])
    show "eventually
        (λn. norm (real (T n))  C * norm (sssp_time_target m n)) at_top"
      using cost_bound eventually_graph
    proof eventually_elim
      case (elim n)
      have "norm (real (T n)) 
          norm (real (bmssp_refined_graph_time_bound A R H l t m n))"
        using elim(1) by simp
      also have "  C * norm (sssp_time_target m n)"
        using elim(2) .
      finally show ?case .
    qed
  qed
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_from_component_bounds:
  assumes Cn_pos: "0 < Cn"
    and Ca_pos: "0 < Ca"
    and Cl_nonneg: "0  Cl"
    and Cr_pos: "0 < Cr"
    and Ct_pos: "0 < Ct"
    and Ch_pos: "0 < Ch"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and A_bound:
      "eventually
        (λn. real (A n)  Ca * sssp_log_factor_one_third n) at_top"
    and l_bound:
      "eventually
        (λn. real (l n)  Cl * sssp_log_factor_one_third n) at_top"
    and R_bound:
      "eventually
        (λn. real (R n)  Cr * sssp_log_factor n) at_top"
    and t_bound:
      "eventually
        (λn. real (t n)  Ct * sssp_log_factor n) at_top"
    and H_bound:
      "eventually
        (λn. real (H n)  Ch * sssp_log_factor_one_third n) at_top"
    and cost_bound:
      "eventually
        (λn. T n  bmssp_refined_graph_time_bound A R H l t m n) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof (rule bmssp_refined_cost_bound_bigo_sssp_time_target
    [where Cn = Cn and Cv = "2 * Ca * (2 * Cl + 1)"
      and Ce = "Cr + Ct + Cl * Ch"])
  show "0 < Cn"
    using Cn_pos .
  show "0 < 2 * Ca * (2 * Cl + 1)"
    using Ca_pos Cl_nonneg by (intro mult_pos_pos) simp_all
  show "0 < Cr + Ct + Cl * Ch"
  proof -
    have "0  Cl * Ch"
      using Cl_nonneg Ch_pos by simp
    then show ?thesis
      using Cr_pos Ct_pos by linarith
  qed
  show "eventually (λn. real n  Cn * real (m n)) at_top"
    using vertex_count_dominated .
  show "eventually
      (λn. real (2 * A n * (2 * l n + 1)) 
        2 * Ca * (2 * Cl + 1) * sssp_log_factor n) at_top"
    using A_bound l_bound eventually_one_le_sssp_log_factor_one_third
  proof eventually_elim
    case (elim n)
    let ?L = "sssp_log_factor_one_third n"
    have L_nonneg: "0  ?L"
      using sssp_log_factor_one_third_pos[of n] by linarith
    have A_le: "real (A n)  Ca * ?L"
      using elim(1) .
    have l_le: "real (2 * l n + 1)  (2 * Cl + 1) * ?L"
    proof -
      have "real (2 * l n + 1) = 2 * real (l n) + 1"
        by simp
      also have "  2 * (Cl * ?L) + 1"
        using elim(2) by linarith
      also have "  2 * (Cl * ?L) + ?L"
        using elim(3) by linarith
      also have " = (2 * Cl + 1) * ?L"
        by (simp add: algebra_simps)
      finally show ?thesis .
    qed
    have l_rhs_nonneg: "0  (2 * Cl + 1) * ?L"
      using Cl_nonneg L_nonneg by (intro mult_nonneg_nonneg) simp_all
    have prod:
      "real (A n) * real (2 * l n + 1) 
        (Ca * ?L) * ((2 * Cl + 1) * ?L)"
      by (rule mult_mono[OF A_le l_le]) (use L_nonneg Ca_pos l_rhs_nonneg in simp_all)
    have "real (2 * A n * (2 * l n + 1)) =
        2 * (real (A n) * real (2 * l n + 1))"
      by (simp add: algebra_simps)
    also have "  2 * ((Ca * ?L) * ((2 * Cl + 1) * ?L))"
      using prod by simp
    also have " = 2 * Ca * (2 * Cl + 1) * sssp_log_factor n"
      using sssp_log_factor_one_third_square[of n]
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  show "eventually
      (λn. real (R n + t n + l n * H n) 
        (Cr + Ct + Cl * Ch) * sssp_log_factor n) at_top"
    using R_bound t_bound l_bound H_bound
  proof eventually_elim
    case (elim n)
    let ?L = "sssp_log_factor_one_third n"
    have L_nonneg: "0  ?L"
      using sssp_log_factor_one_third_pos[of n] by linarith
    have lH:
      "real (l n * H n)  (Cl * Ch) * sssp_log_factor n"
    proof -
      have prod:
        "real (l n) * real (H n)  (Cl * ?L) * (Ch * ?L)"
        by (rule mult_mono[OF elim(3) elim(4)])
          (use Cl_nonneg Ch_pos L_nonneg in simp_all)
      have "real (l n * H n) = real (l n) * real (H n)"
        by simp
      also have "  (Cl * ?L) * (Ch * ?L)"
        using prod .
      also have " = (Cl * Ch) * sssp_log_factor n"
        using sssp_log_factor_one_third_square[of n]
        by (simp add: algebra_simps)
      finally show ?thesis .
    qed
    have "real (R n + t n + l n * H n) =
        real (R n) + real (t n) + real (l n * H n)"
      by simp
    also have " 
        Cr * sssp_log_factor n + Ct * sssp_log_factor n +
        (Cl * Ch) * sssp_log_factor n"
      using elim(1) elim(2) lH by linarith
    also have " = (Cr + Ct + Cl * Ch) * sssp_log_factor n"
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  show "eventually
      (λn. T n  bmssp_refined_graph_time_bound A R H l t m n) at_top"
    using cost_bound .
qed

theorem bmssp_refined_graph_time_bound_bigo_sssp_time_target_slack:
  assumes Cn_pos: "0 < Cn"
    and Cm_pos: "0 < Cm"
    and Cv_pos: "0 < Cv"
    and Ce_pos: "0 < Ce"
    and vertex_count_dominated:
      "eventually (λn. real (v n)  Cn * real (m n)) at_top"
    and edge_count_dominated:
      "eventually (λn. real (m' n)  Cm * real (m n)) at_top"
    and vertex_factor:
      "eventually
        (λn. real (2 * A n * (2 * l n + 1)) 
          Cv * sssp_log_factor n) at_top"
    and edge_factor:
      "eventually
        (λn. real (R n + t n + l n * H n) 
          Ce * sssp_log_factor n) at_top"
  shows "(λn. real (bmssp_refined_graph_time_bound
      (λ_. A n) (λ_. R n) (λ_. H n) (λ_. l n) (λ_. t n)
      (λ_. m' n) (v n)))
     O(λn. sssp_time_target m n)"
proof (rule sssp_time_target_bigoI[where C = "Cv * Cn + Ce * Cm"])
  show "0 < Cv * Cn + Ce * Cm"
  proof -
    have "0 < Cv * Cn"
      using Cv_pos Cn_pos by (rule mult_pos_pos)
    moreover have "0 < Ce * Cm"
      using Ce_pos Cm_pos by (rule mult_pos_pos)
    ultimately show ?thesis
      by linarith
  qed
next
  show "eventually
      (λn. real (bmssp_refined_graph_time_bound
          (λ_. A n) (λ_. R n) (λ_. H n) (λ_. l n) (λ_. t n)
          (λ_. m' n) (v n)) 
        (Cv * Cn + Ce * Cm) * sssp_time_target m n) at_top"
    using vertex_count_dominated edge_count_dominated vertex_factor edge_factor
  proof eventually_elim
    case (elim n)
    let ?vf = "real (2 * A n * (2 * l n + 1))"
    let ?ef = "real (R n + t n + l n * H n)"
    let ?log = "sssp_log_factor n"
    have vertex:
      "?vf * real (v n)  (Cv * ?log) * (Cn * real (m n))"
    proof (rule mult_mono)
      show "?vf  Cv * ?log"
        using elim(3) .
      show "real (v n)  Cn * real (m n)"
        using elim(1) .
      show "0  real (v n)"
        by simp
      show "0  Cv * ?log"
      proof -
        have "0  Cv"
          using Cv_pos by linarith
        moreover have "0  ?log"
          using sssp_log_factor_pos[of n] by linarith
        ultimately show ?thesis
          by (rule mult_nonneg_nonneg)
      qed
    qed
    also have " = (Cv * Cn) * (real (m n) * ?log)"
      by (simp add: algebra_simps)
    finally have vertex':
      "?vf * real (v n)  (Cv * Cn) * (real (m n) * ?log)" .
    have edge:
      "?ef * real (m' n)  (Ce * ?log) * (Cm * real (m n))"
    proof (rule mult_mono)
      show "?ef  Ce * ?log"
        using elim(4) .
      show "real (m' n)  Cm * real (m n)"
        using elim(2) .
      show "0  real (m' n)"
        by simp
      show "0  Ce * ?log"
      proof -
        have "0  Ce"
          using Ce_pos by linarith
        moreover have "0  ?log"
          using sssp_log_factor_pos[of n] by linarith
        ultimately show ?thesis
          by (rule mult_nonneg_nonneg)
      qed
    qed
    also have " = (Ce * Cm) * (real (m n) * ?log)"
      by (simp add: algebra_simps)
    finally have edge':
      "?ef * real (m' n)  (Ce * Cm) * (real (m n) * ?log)" .
    have "real (bmssp_refined_graph_time_bound
          (λ_. A n) (λ_. R n) (λ_. H n) (λ_. l n) (λ_. t n)
          (λ_. m' n) (v n)) =
        ?vf * real (v n) + ?ef * real (m' n)"
      unfolding bmssp_refined_graph_time_bound_def by simp
    also have " 
        (Cv * Cn) * (real (m n) * ?log) +
        (Ce * Cm) * (real (m n) * ?log)"
      using vertex' edge' by linarith
    also have " = (Cv * Cn + Ce * Cm) * sssp_time_target m n"
      unfolding sssp_time_target_def by (simp add: algebra_simps)
    finally show ?case .
  qed
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_from_component_bounds_slack:
  assumes Cn_pos: "0 < Cn"
    and Cm_pos: "0 < Cm"
    and Ca_pos: "0 < Ca"
    and Cl_nonneg: "0  Cl"
    and Cr_pos: "0 < Cr"
    and Ct_pos: "0 < Ct"
    and Ch_pos: "0 < Ch"
    and vertex_count_dominated:
      "eventually (λn. real (v n)  Cn * real (m n)) at_top"
    and edge_count_dominated:
      "eventually (λn. real (m' n)  Cm * real (m n)) at_top"
    and A_bound:
      "eventually
        (λn. real (A n)  Ca * sssp_log_factor_one_third n) at_top"
    and l_bound:
      "eventually
        (λn. real (l n)  Cl * sssp_log_factor_one_third n) at_top"
    and R_bound:
      "eventually
        (λn. real (R n)  Cr * sssp_log_factor n) at_top"
    and t_bound:
      "eventually
        (λn. real (t n)  Ct * sssp_log_factor n) at_top"
    and H_bound:
      "eventually
        (λn. real (H n)  Ch * sssp_log_factor_one_third n) at_top"
    and cost_bound:
      "eventually
        (λn. T n  bmssp_refined_graph_time_bound
          (λ_. A n) (λ_. R n) (λ_. H n) (λ_. l n) (λ_. t n)
          (λ_. m' n) (v n)) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof -
  have Cv_pos: "0 < 2 * Ca * (2 * Cl + 1)"
    using Ca_pos Cl_nonneg by (intro mult_pos_pos) simp_all
  have Ce_pos: "0 < Cr + Ct + Cl * Ch"
  proof -
    have "0  Cl * Ch"
      using Cl_nonneg Ch_pos by simp
    then show ?thesis
      using Cr_pos Ct_pos by linarith
  qed
  have vertex_factor:
    "eventually
      (λn. real (2 * A n * (2 * l n + 1)) 
        2 * Ca * (2 * Cl + 1) * sssp_log_factor n) at_top"
    using A_bound l_bound eventually_one_le_sssp_log_factor_one_third
  proof eventually_elim
    case (elim n)
    let ?L = "sssp_log_factor_one_third n"
    have L_nonneg: "0  ?L"
      using sssp_log_factor_one_third_pos[of n] by linarith
    have A_le: "real (A n)  Ca * ?L"
      using elim(1) .
    have l_le: "real (2 * l n + 1)  (2 * Cl + 1) * ?L"
    proof -
      have "real (2 * l n + 1) = 2 * real (l n) + 1"
        by simp
      also have "  2 * (Cl * ?L) + 1"
        using elim(2) by linarith
      also have "  2 * (Cl * ?L) + ?L"
        using elim(3) by linarith
      also have " = (2 * Cl + 1) * ?L"
        by (simp add: algebra_simps)
      finally show ?thesis .
    qed
    have l_rhs_nonneg: "0  (2 * Cl + 1) * ?L"
      using Cl_nonneg L_nonneg by (intro mult_nonneg_nonneg) simp_all
    have prod:
      "real (A n) * real (2 * l n + 1) 
        (Ca * ?L) * ((2 * Cl + 1) * ?L)"
      by (rule mult_mono[OF A_le l_le])
        (use L_nonneg Ca_pos l_rhs_nonneg in simp_all)
    have "real (2 * A n * (2 * l n + 1)) =
        2 * (real (A n) * real (2 * l n + 1))"
      by (simp add: algebra_simps)
    also have "  2 * ((Ca * ?L) * ((2 * Cl + 1) * ?L))"
      using prod by simp
    also have " = 2 * Ca * (2 * Cl + 1) * sssp_log_factor n"
      using sssp_log_factor_one_third_square[of n]
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  have edge_factor:
    "eventually
      (λn. real (R n + t n + l n * H n) 
        (Cr + Ct + Cl * Ch) * sssp_log_factor n) at_top"
    using R_bound t_bound l_bound H_bound
  proof eventually_elim
    case (elim n)
    let ?L = "sssp_log_factor_one_third n"
    have L_nonneg: "0  ?L"
      using sssp_log_factor_one_third_pos[of n] by linarith
    have lH:
      "real (l n * H n)  (Cl * Ch) * sssp_log_factor n"
    proof -
      have prod:
        "real (l n) * real (H n)  (Cl * ?L) * (Ch * ?L)"
        by (rule mult_mono[OF elim(3) elim(4)])
          (use Cl_nonneg Ch_pos L_nonneg in simp_all)
      have "real (l n * H n) = real (l n) * real (H n)"
        by simp
      also have "  (Cl * ?L) * (Ch * ?L)"
        using prod .
      also have " = (Cl * Ch) * sssp_log_factor n"
        using sssp_log_factor_one_third_square[of n]
        by (simp add: algebra_simps)
      finally show ?thesis .
    qed
    have "real (R n + t n + l n * H n) =
        real (R n) + real (t n) + real (l n * H n)"
      by simp
    also have " 
        Cr * sssp_log_factor n + Ct * sssp_log_factor n +
        (Cl * Ch) * sssp_log_factor n"
      using elim(1) elim(2) lH by linarith
    also have " = (Cr + Ct + Cl * Ch) * sssp_log_factor n"
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  have graph:
    "(λn. real (bmssp_refined_graph_time_bound
        (λ_. A n) (λ_. R n) (λ_. H n) (λ_. l n) (λ_. t n)
        (λ_. m' n) (v n)))
       O(λn. sssp_time_target m n)"
    by (rule bmssp_refined_graph_time_bound_bigo_sssp_time_target_slack
      [OF Cn_pos Cm_pos Cv_pos Ce_pos vertex_count_dominated
        edge_count_dominated vertex_factor edge_factor])
  then obtain C where C_pos: "0 < C"
    and eventually_graph:
      "eventually
        (λn. norm (real (bmssp_refined_graph_time_bound
          (λ_. A n) (λ_. R n) (λ_. H n) (λ_. l n) (λ_. t n)
          (λ_. m' n) (v n))) 
          C * norm (sssp_time_target m n)) at_top"
    unfolding bigo_def by blast
  show ?thesis
  proof (rule sssp_time_target_bigoI_norm[OF C_pos])
    show "eventually
        (λn. norm (real (T n))  C * norm (sssp_time_target m n)) at_top"
      using cost_bound eventually_graph
    proof eventually_elim
      case (elim n)
      have "norm (real (T n)) 
          norm (real (bmssp_refined_graph_time_bound
            (λ_. A n) (λ_. R n) (λ_. H n) (λ_. l n) (λ_. t n)
            (λ_. m' n) (v n)))"
        using elim(1) by simp
      also have "  C * norm (sssp_time_target m n)"
        using elim(2) .
      finally show ?case .
    qed
  qed
qed

section ‹Logarithmic Parameter Schedule›

text ‹
  The paper's schedule balances two logarithmic scales.  The parameter usually
  called k› is chosen on the order of log^(1/3) n›, while the recursion
  depth and partition width use the corresponding log^(2/3) n› scale.  The
  two definitions below use ceilings of the real-valued factors so that the
  executable and inductive parameters remain natural numbers.

  The lemmas in this section are mostly analytic bookkeeping.  They show that
  the ceiling parameters are eventually positive, monotone enough for the
  recurrence, and asymptotically within constant factors of the real logarithmic
  targets.  Those facts are what let the refined graph-time expressions be
  rewritten as O(m * log^(2/3) n)›.
›

definition sssp_log_one_third_param where
  "sssp_log_one_third_param n = nat sssp_log_factor_one_third n"

definition sssp_log_two_thirds_param where
  "sssp_log_two_thirds_param n = nat sssp_log_factor n"

lemma sssp_log_one_third_param_pos [simp]:
  "0 < sssp_log_one_third_param n"
  unfolding sssp_log_one_third_param_def
  using sssp_log_factor_one_third_pos[of n] by simp

lemma sssp_log_two_thirds_param_pos [simp]:
  "0 < sssp_log_two_thirds_param n"
  unfolding sssp_log_two_thirds_param_def
  using sssp_log_factor_pos[of n] by simp

lemma real_cuberoot_at_top:
  "filterlim (λx::real. root 3 x) at_top at_top"
  by (rule filterlim_at_top_at_top
      [where Q = "λx. True" and P = "λx. True" and g = "λx. x ^ 3"])
    (auto intro: real_root_le_mono simp: odd_real_root_power_cancel)

lemma sssp_log_factor_one_third_at_top:
  "filterlim sssp_log_factor_one_third at_top at_top"
proof -
  have ln_at_top':
    "filterlim (λn::nat. ln (real (n + 2))) at_top at_top"
    by (intro filterlim_compose[OF ln_at_top]
        filterlim_compose[OF filterlim_real_sequentially]
        filterlim_add_const_nat_at_top)
  have root_ln_at_top:
    "filterlim (λn::nat. root 3 (ln (real (n + 2)))) at_top at_top"
    by (rule filterlim_compose[OF real_cuberoot_at_top ln_at_top'])
  have eq:
    "(λn::nat. root 3 (ln (real (n + 2)))) = sssp_log_factor_one_third"
  proof
    fix n :: nat
    have ln_nonneg: "0  ln (real (n + 2))"
      by simp
    have "root 3 (ln (real (n + 2))) =
        ln (real (n + 2)) powr (1 / 3)"
      using root_powr_inverse[of 3 "ln (real (n + 2))"] ln_nonneg
      by simp
    then show "root 3 (ln (real (n + 2))) = sssp_log_factor_one_third n"
      unfolding sssp_log_factor_one_third_def
      by (simp add: add.commute)
  qed
  show ?thesis
    using root_ln_at_top unfolding eq .
qed

lemma nat_ceiling_at_top:
  "filterlim (λx::real. nat x) at_top at_top"
proof (rule filterlim_at_top_at_top
    [where Q = "λx. 0  x" and P = "λ_::nat. True" and g = real])
  fix x y :: real
  assume x_nonneg: "0  x" and y_nonneg: "0  y" and xy: "x  y"
  have "x  y"
    by (rule ceiling_mono[OF xy])
  then show "nat x  nat y"
    by (rule nat_mono)
next
  fix n :: nat
  show "nat real n = n"
    by simp
next
  fix n :: nat
  show "0  real n"
    by simp
next
  show "eventually (λx::real. 0  x) at_top"
    by (rule eventually_ge_at_top)
next
  show "eventually (λ_::nat. True) at_top"
    by simp
qed

lemma sssp_log_one_third_param_at_top:
  "filterlim sssp_log_one_third_param at_top at_top"
  unfolding sssp_log_one_third_param_def
  by (rule filterlim_compose
    [OF nat_ceiling_at_top sssp_log_factor_one_third_at_top])

lemma eventually_less_sssp_log_one_third_param:
  "eventually (λn. K < sssp_log_one_third_param n) at_top"
proof -
  have "eventually (λn. Suc K  sssp_log_one_third_param n) at_top"
    using sssp_log_one_third_param_at_top
    unfolding filterlim_at_top by blast
  then show ?thesis
    by eventually_elim simp
qed

lemma sssp_log_two_thirds_param_le_one_third_square:
  "sssp_log_two_thirds_param n 
    sssp_log_one_third_param n * sssp_log_one_third_param n"
proof -
  let ?L = "sssp_log_factor_one_third n"
  let ?p = "sssp_log_one_third_param n"
  have L_nonneg: "0  ?L"
    using sssp_log_factor_one_third_pos[of n] by linarith
  have L_le_p: "?L  real ?p"
    unfolding sssp_log_one_third_param_def
    using L_nonneg by simp
  have "sssp_log_factor n  real (?p * ?p)"
  proof -
    have "?L * ?L  real ?p * real ?p"
      by (rule mult_mono[OF L_le_p L_le_p])
        (use L_nonneg in simp_all)
    then show ?thesis
      using sssp_log_factor_one_third_square[of n] by simp
  qed
  then show ?thesis
    unfolding sssp_log_two_thirds_param_def by simp
qed

lemma real_nat_ceiling_le_twice:
  fixes x :: real
  assumes "1  x"
  shows "real (nat x)  2 * x"
proof -
  have x_nonneg: "0  x"
    using assms by linarith
  have ceil_nat: "real (nat x) = of_int x"
    using of_nat_int_ceiling[OF x_nonneg] .
  have "of_int x - 1 < x"
    using ceiling_correct[of x] by blast
  then have "of_int x  x + 1"
    by linarith
  also have "  2 * x"
    using assms by linarith
  finally show ?thesis
    unfolding ceil_nat .
qed

lemma eventually_one_le_sssp_log_factor:
  "eventually (λn. 1  sssp_log_factor n) at_top"
  using eventually_one_le_sssp_log_factor_one_third
proof eventually_elim
  case (elim n)
  let ?L = "sssp_log_factor_one_third n"
  have "1  ?L * ?L"
  proof -
    have L_nonneg: "0  ?L"
      using elim by linarith
    have "(1::real) * 1  ?L * ?L"
      by (rule mult_mono[OF elim elim]) (simp_all add: L_nonneg)
    then show ?thesis
      by simp
  qed
  then show ?case
    using sssp_log_factor_one_third_square[of n] by simp
qed

lemma sssp_log_one_third_param_component_bound:
  "eventually
    (λn. real (sssp_log_one_third_param n) 
      2 * sssp_log_factor_one_third n) at_top"
  using eventually_one_le_sssp_log_factor_one_third
proof eventually_elim
  case (elim n)
  show ?case
    unfolding sssp_log_one_third_param_def
    by (rule real_nat_ceiling_le_twice[OF elim])
qed

lemma sssp_log_two_thirds_param_component_bound:
  "eventually
    (λn. real (sssp_log_two_thirds_param n) 
      2 * sssp_log_factor n) at_top"
  using eventually_one_le_sssp_log_factor
proof eventually_elim
  case (elim n)
  show ?case
    unfolding sssp_log_two_thirds_param_def
    by (rule real_nat_ceiling_le_twice[OF elim])
qed

lemma sssp_log_one_third_param_square_arg_component_bound:
  "eventually
    (λn. real (sssp_log_one_third_param (n * n)) 
      4 * sssp_log_factor_one_third n) at_top"
proof (rule eventually_at_top_linorderI[of 1])
  fix n :: nat
  assume n_ge: "1  n"
  have one_le: "1  sssp_log_factor_one_third (n * n)"
  proof -
    have "1  sssp_log_factor_one_third n"
    proof -
      have "exp 1  (3::real)"
        by (rule exp_le)
      also have "  real n + 2"
        using n_ge by simp
      finally have exp_le_arg: "exp 1  real n + 2" .
      have ln_ge: "1  ln (real n + 2)"
        using exp_le_arg by (simp add: ln_ge_iff)
      show ?thesis
        unfolding sssp_log_factor_one_third_def
        by (rule ge_one_powr_ge_zero[OF ln_ge]) simp
    qed
    also have "  sssp_log_factor_one_third (n * n)"
      by (rule sssp_log_factor_one_third_mono) (use n_ge in simp)
    finally show ?thesis .
  qed
  have "real (sssp_log_one_third_param (n * n)) 
      2 * sssp_log_factor_one_third (n * n)"
    unfolding sssp_log_one_third_param_def
    by (rule real_nat_ceiling_le_twice[OF one_le])
  also have "  2 * (2 * sssp_log_factor_one_third n)"
    using sssp_log_factor_one_third_square_arg_le[of n] by simp
  also have " = 4 * sssp_log_factor_one_third n"
    by simp
  finally show "real (sssp_log_one_third_param (n * n)) 
      4 * sssp_log_factor_one_third n" .
qed

lemma sssp_log_two_thirds_param_square_arg_component_bound:
  "eventually
    (λn. real (sssp_log_two_thirds_param (n * n)) 
      4 * sssp_log_factor n) at_top"
proof (rule eventually_at_top_linorderI[of 1])
  fix n :: nat
  assume n_ge: "1  n"
  have one_le: "1  sssp_log_factor (n * n)"
  proof -
    have "1  sssp_log_factor n"
    proof -
      have one_third: "1  sssp_log_factor_one_third n"
      proof -
        have "exp 1  (3::real)"
          by (rule exp_le)
        also have "  real n + 2"
          using n_ge by simp
        finally have exp_le_arg: "exp 1  real n + 2" .
        have ln_ge: "1  ln (real n + 2)"
          using exp_le_arg by (simp add: ln_ge_iff)
        show ?thesis
          unfolding sssp_log_factor_one_third_def
          by (rule ge_one_powr_ge_zero[OF ln_ge]) simp
      qed
      let ?L = "sssp_log_factor_one_third n"
      have "1  ?L * ?L"
      proof -
        have L_nonneg: "0  ?L"
          using one_third by linarith
        have "(1::real) * 1  ?L * ?L"
          by (rule mult_mono[OF one_third one_third])
            (simp_all add: L_nonneg)
        then show ?thesis
          by simp
      qed
      then show ?thesis
        using sssp_log_factor_one_third_square[of n] by simp
    qed
    also have "  sssp_log_factor (n * n)"
      by (rule sssp_log_factor_mono) (use n_ge in simp)
    finally show ?thesis .
  qed
  have "real (sssp_log_two_thirds_param (n * n)) 
      2 * sssp_log_factor (n * n)"
    unfolding sssp_log_two_thirds_param_def
    by (rule real_nat_ceiling_le_twice[OF one_le])
  also have "  2 * (2 * sssp_log_factor n)"
    using sssp_log_factor_square_arg_le[of n] by simp
  also have " = 4 * sssp_log_factor n"
    by simp
  finally show "real (sssp_log_two_thirds_param (n * n)) 
      4 * sssp_log_factor n" .
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_log_params:
  assumes Cn_pos: "0 < Cn"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and cost_bound:
      "eventually
        (λn. T n 
          bmssp_refined_graph_time_bound sssp_log_one_third_param
            sssp_log_two_thirds_param sssp_log_one_third_param
            sssp_log_one_third_param sssp_log_two_thirds_param m n) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof (rule bmssp_refined_cost_bound_bigo_sssp_time_target_from_component_bounds
    [where Cn = Cn and Ca = 2 and Cl = 2 and Cr = 2 and Ct = 2 and Ch = 2])
  show "0 < Cn"
    using Cn_pos .
  show "0 < (2::real)"
    by simp
  show "0  (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "eventually (λn. real n  Cn * real (m n)) at_top"
    using vertex_count_dominated .
  show "eventually
      (λn. real (sssp_log_one_third_param n) 
        2 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_component_bound)
  show "eventually
      (λn. real (sssp_log_one_third_param n) 
        2 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param n) 
        2 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param n) 
        2 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_component_bound)
  show "eventually
      (λn. real (sssp_log_one_third_param n) 
        2 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_component_bound)
  show "eventually
      (λn. T n 
        bmssp_refined_graph_time_bound sssp_log_one_third_param
          sssp_log_two_thirds_param sssp_log_one_third_param
          sssp_log_one_third_param sssp_log_two_thirds_param m n) at_top"
    using cost_bound .
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree:
  fixes D :: nat
  assumes Cn_pos: "0 < Cn"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and cost_bound:
      "eventually
        (λn. T n 
          bmssp_refined_graph_time_bound
            (λn. Suc D * sssp_log_one_third_param n)
            sssp_log_two_thirds_param sssp_log_one_third_param
            sssp_log_one_third_param sssp_log_two_thirds_param m n) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof (rule bmssp_refined_cost_bound_bigo_sssp_time_target_from_component_bounds
    [where Cn = Cn and Ca = "2 * real (Suc D)"
      and Cl = 2 and Cr = 2 and Ct = 2 and Ch = 2])
  show "0 < Cn"
    using Cn_pos .
  show "0 < 2 * real (Suc D)"
    by simp
  show "0  (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "eventually (λn. real n  Cn * real (m n)) at_top"
    using vertex_count_dominated .
  show "eventually
      (λn. real (Suc D * sssp_log_one_third_param n) 
        2 * real (Suc D) * sssp_log_factor_one_third n) at_top"
    using sssp_log_one_third_param_component_bound
  proof eventually_elim
    case (elim n)
    have "real (Suc D * sssp_log_one_third_param n) =
        real (Suc D) * real (sssp_log_one_third_param n)"
      by (simp add: algebra_simps)
    also have "  real (Suc D) *
        (2 * sssp_log_factor_one_third n)"
      using elim by simp
    also have " = 2 * real (Suc D) * sssp_log_factor_one_third n"
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  show "eventually
      (λn. real (sssp_log_one_third_param n) 
        2 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param n) 
        2 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param n) 
        2 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_component_bound)
  show "eventually
      (λn. real (sssp_log_one_third_param n) 
        2 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_component_bound)
  show "eventually
      (λn. T n 
        bmssp_refined_graph_time_bound
          (λn. Suc D * sssp_log_one_third_param n)
          sssp_log_two_thirds_param sssp_log_one_third_param
          sssp_log_one_third_param sssp_log_two_thirds_param m n) at_top"
    using cost_bound .
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree_slack:
  fixes D :: nat
  assumes Cn_pos: "0 < Cn"
    and Cm_pos: "0 < Cm"
    and vertex_count_dominated:
      "eventually (λn. real (v n)  Cn * real (m n)) at_top"
    and edge_count_dominated:
      "eventually (λn. real (m' n)  Cm * real (m n)) at_top"
    and cost_bound:
      "eventually
        (λn. T n 
          bmssp_refined_graph_time_bound
            (λ_. Suc D * sssp_log_one_third_param n)
            (λ_. sssp_log_two_thirds_param n)
            (λ_. sssp_log_one_third_param n)
            (λ_. sssp_log_one_third_param n)
            (λ_. sssp_log_two_thirds_param n)
            (λ_. m' n) (v n)) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof (rule bmssp_refined_cost_bound_bigo_sssp_time_target_from_component_bounds_slack
    [where Cn = Cn and Cm = Cm and Ca = "2 * real (Suc D)"
      and Cl = 2 and Cr = 2 and Ct = 2 and Ch = 2])
  show "0 < Cn"
    using Cn_pos .
  show "0 < Cm"
    using Cm_pos .
  show "0 < 2 * real (Suc D)"
    by simp
  show "0  (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "0 < (2::real)"
    by simp
  show "eventually (λn. real (v n)  Cn * real (m n)) at_top"
    using vertex_count_dominated .
  show "eventually (λn. real (m' n)  Cm * real (m n)) at_top"
    using edge_count_dominated .
  show "eventually
      (λn. real (Suc D * sssp_log_one_third_param n) 
        2 * real (Suc D) * sssp_log_factor_one_third n) at_top"
    using sssp_log_one_third_param_component_bound
  proof eventually_elim
    case (elim n)
    have "real (Suc D * sssp_log_one_third_param n) =
        real (Suc D) * real (sssp_log_one_third_param n)"
      by (simp add: algebra_simps)
    also have "  real (Suc D) *
        (2 * sssp_log_factor_one_third n)"
      using elim by simp
    also have " = 2 * real (Suc D) * sssp_log_factor_one_third n"
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  show "eventually
      (λn. real (sssp_log_one_third_param n) 
        2 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param n) 
        2 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param n) 
        2 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_component_bound)
  show "eventually
      (λn. real (sssp_log_one_third_param n) 
        2 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_component_bound)
  show "eventually
      (λn. T n 
        bmssp_refined_graph_time_bound
          (λ_. Suc D * sssp_log_one_third_param n)
          (λ_. sssp_log_two_thirds_param n)
          (λ_. sssp_log_one_third_param n)
          (λ_. sssp_log_one_third_param n)
          (λ_. sssp_log_two_thirds_param n)
          (λ_. m' n) (v n)) at_top"
    using cost_bound .
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree_square_arg:
  fixes D :: nat
  assumes Cn_pos: "0 < Cn"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and cost_bound:
      "eventually
        (λn. T n 
          bmssp_refined_graph_time_bound
            (λn. Suc D * sssp_log_one_third_param (n * n))
            (λn. sssp_log_two_thirds_param (n * n))
            (λn. sssp_log_one_third_param (n * n))
            (λn. sssp_log_one_third_param (n * n))
            (λn. sssp_log_two_thirds_param (n * n)) m n) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof (rule bmssp_refined_cost_bound_bigo_sssp_time_target_from_component_bounds
    [where Cn = Cn and Ca = "4 * real (Suc D)"
      and Cl = 4 and Cr = 4 and Ct = 4 and Ch = 4])
  show "0 < Cn"
    using Cn_pos .
  show "0 < 4 * real (Suc D)"
    by simp
  show "0  (4::real)"
    by simp
  show "0 < (4::real)"
    by simp
  show "0 < (4::real)"
    by simp
  show "0 < (4::real)"
    by simp
  show "eventually (λn. real n  Cn * real (m n)) at_top"
    using vertex_count_dominated .
  show "eventually
      (λn. real (Suc D * sssp_log_one_third_param (n * n)) 
        4 * real (Suc D) * sssp_log_factor_one_third n) at_top"
    using sssp_log_one_third_param_square_arg_component_bound
  proof eventually_elim
    case (elim n)
    have "real (Suc D * sssp_log_one_third_param (n * n)) =
        real (Suc D) * real (sssp_log_one_third_param (n * n))"
      by (simp add: algebra_simps)
    also have "  real (Suc D) *
        (4 * sssp_log_factor_one_third n)"
      using elim by simp
    also have " = 4 * real (Suc D) * sssp_log_factor_one_third n"
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  show "eventually
      (λn. real (sssp_log_one_third_param (n * n)) 
        4 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_square_arg_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param (n * n)) 
        4 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_square_arg_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param (n * n)) 
        4 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_square_arg_component_bound)
  show "eventually
      (λn. real (sssp_log_one_third_param (n * n)) 
        4 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_square_arg_component_bound)
  show "eventually
      (λn. T n 
        bmssp_refined_graph_time_bound
          (λn. Suc D * sssp_log_one_third_param (n * n))
          (λn. sssp_log_two_thirds_param (n * n))
          (λn. sssp_log_one_third_param (n * n))
          (λn. sssp_log_one_third_param (n * n))
          (λn. sssp_log_two_thirds_param (n * n)) m n) at_top"
    using cost_bound .
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree_square_arg_slack:
  fixes D :: nat
  assumes Cn_pos: "0 < Cn"
    and Cm_pos: "0 < Cm"
    and vertex_count_dominated:
      "eventually (λn. real (v n)  Cn * real (m n)) at_top"
    and edge_count_dominated:
      "eventually (λn. real (m' n)  Cm * real (m n)) at_top"
    and cost_bound:
      "eventually
        (λn. T n 
          bmssp_refined_graph_time_bound
            (λ_. Suc D * sssp_log_one_third_param (n * n))
            (λ_. sssp_log_two_thirds_param (n * n))
            (λ_. sssp_log_one_third_param (n * n))
            (λ_. sssp_log_one_third_param (n * n))
            (λ_. sssp_log_two_thirds_param (n * n))
            (λ_. m' n) (v n)) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof (rule bmssp_refined_cost_bound_bigo_sssp_time_target_from_component_bounds_slack
    [where Cn = Cn and Cm = Cm and Ca = "4 * real (Suc D)"
      and Cl = 4 and Cr = 4 and Ct = 4 and Ch = 4])
  show "0 < Cn"
    using Cn_pos .
  show "0 < Cm"
    using Cm_pos .
  show "0 < 4 * real (Suc D)"
    by simp
  show "0  (4::real)"
    by simp
  show "0 < (4::real)"
    by simp
  show "0 < (4::real)"
    by simp
  show "0 < (4::real)"
    by simp
  show "eventually (λn. real (v n)  Cn * real (m n)) at_top"
    using vertex_count_dominated .
  show "eventually (λn. real (m' n)  Cm * real (m n)) at_top"
    using edge_count_dominated .
  show "eventually
      (λn. real (Suc D * sssp_log_one_third_param (n * n)) 
        4 * real (Suc D) * sssp_log_factor_one_third n) at_top"
    using sssp_log_one_third_param_square_arg_component_bound
  proof eventually_elim
    case (elim n)
    have "real (Suc D * sssp_log_one_third_param (n * n)) =
        real (Suc D) * real (sssp_log_one_third_param (n * n))"
      by (simp add: algebra_simps)
    also have "  real (Suc D) *
        (4 * sssp_log_factor_one_third n)"
      using elim by simp
    also have " = 4 * real (Suc D) * sssp_log_factor_one_third n"
      by (simp add: algebra_simps)
    finally show ?case .
  qed
  show "eventually
      (λn. real (sssp_log_one_third_param (n * n)) 
        4 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_square_arg_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param (n * n)) 
        4 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_square_arg_component_bound)
  show "eventually
      (λn. real (sssp_log_two_thirds_param (n * n)) 
        4 * sssp_log_factor n) at_top"
    by (rule sssp_log_two_thirds_param_square_arg_component_bound)
  show "eventually
      (λn. real (sssp_log_one_third_param (n * n)) 
        4 * sssp_log_factor_one_third n) at_top"
    by (rule sssp_log_one_third_param_square_arg_component_bound)
  show "eventually
      (λn. T n 
        bmssp_refined_graph_time_bound
          (λ_. Suc D * sssp_log_one_third_param (n * n))
          (λ_. sssp_log_two_thirds_param (n * n))
          (λ_. sssp_log_one_third_param (n * n))
          (λ_. sssp_log_one_third_param (n * n))
          (λ_. sssp_log_two_thirds_param (n * n))
          (λ_. m' n) (v n)) at_top"
    using cost_bound .
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree_square_arg_real_slack:
  fixes D :: nat
  assumes Cn_pos: "0 < Cn"
    and Ccost_pos: "0 < Ccost"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and cost_bound:
      "eventually
        (λn. real (T n)  Ccost *
          real (bmssp_refined_graph_time_bound
            (λn. Suc D * sssp_log_one_third_param (n * n))
            (λn. sssp_log_two_thirds_param (n * n))
            (λn. sssp_log_one_third_param (n * n))
            (λn. sssp_log_one_third_param (n * n))
            (λn. sssp_log_two_thirds_param (n * n)) m n)) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof -
  let ?G = "λn. bmssp_refined_graph_time_bound
    (λn. Suc D * sssp_log_one_third_param (n * n))
    (λn. sssp_log_two_thirds_param (n * n))
    (λn. sssp_log_one_third_param (n * n))
    (λn. sssp_log_one_third_param (n * n))
    (λn. sssp_log_two_thirds_param (n * n)) m n"
  have G_bigo: "(λn. real (?G n))  O(λn. sssp_time_target m n)"
    by (rule bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree_square_arg
      [where D = D and T = ?G, OF Cn_pos vertex_count_dominated]) simp
  then obtain C where C_pos: "0 < C"
    and G_bound:
      "eventually
        (λn. norm (real (?G n)) 
          C * norm (sssp_time_target m n)) at_top"
    unfolding bigo_def by blast
  have Cprod_pos: "0 < Ccost * C"
    using Ccost_pos C_pos by simp
  show ?thesis
  proof (rule sssp_time_target_bigoI_norm[OF Cprod_pos])
    show "eventually
      (λn. norm (real (T n)) 
        Ccost * C * norm (sssp_time_target m n)) at_top"
      using cost_bound G_bound
    proof eventually_elim
      case (elim n)
      have "norm (real (T n)) = real (T n)"
        by simp
      also have "  Ccost * real (?G n)"
        using elim(1) .
      also have " = Ccost * norm (real (?G n))"
        by simp
      also have "  Ccost * (C * norm (sssp_time_target m n))"
        using elim(2) Ccost_pos by simp
      also have " = Ccost * C * norm (sssp_time_target m n)"
        by (simp add: algebra_simps)
      finally show ?case .
    qed
  qed
qed

theorem bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree_real_slack:
  fixes D :: nat
  assumes Cn_pos: "0 < Cn"
    and Ccost_pos: "0 < Ccost"
    and vertex_count_dominated:
      "eventually (λn. real n  Cn * real (m n)) at_top"
    and cost_bound:
      "eventually
        (λn. real (T n)  Ccost *
          real (bmssp_refined_graph_time_bound
            (λn. Suc D * sssp_log_one_third_param n)
            sssp_log_two_thirds_param sssp_log_one_third_param
            sssp_log_one_third_param sssp_log_two_thirds_param m n)) at_top"
  shows "(λn. real (T n))  O(λn. sssp_time_target m n)"
proof -
  let ?G = "λn. bmssp_refined_graph_time_bound
    (λn. Suc D * sssp_log_one_third_param n)
    sssp_log_two_thirds_param sssp_log_one_third_param
    sssp_log_one_third_param sssp_log_two_thirds_param m n"
  have G_bigo: "(λn. real (?G n))  O(λn. sssp_time_target m n)"
    by (rule bmssp_refined_cost_bound_bigo_sssp_time_target_log_params_bounded_degree
      [where D = D and T = ?G, OF Cn_pos vertex_count_dominated]) simp
  then obtain C where C_pos: "0 < C"
    and G_bound:
      "eventually
        (λn. norm (real (?G n)) 
          C * norm (sssp_time_target m n)) at_top"
    unfolding bigo_def by blast
  have Cprod_pos: "0 < Ccost * C"
    using Ccost_pos C_pos by simp
  show ?thesis
  proof (rule sssp_time_target_bigoI_norm[OF Cprod_pos])
    show "eventually
      (λn. norm (real (T n)) 
        Ccost * C * norm (sssp_time_target m n)) at_top"
      using cost_bound G_bound
    proof eventually_elim
      case (elim n)
      have "norm (real (T n)) = real (T n)"
        by simp
      also have "  Ccost * real (?G n)"
        using elim(1) .
      also have " = Ccost * norm (real (?G n))"
        by simp
      also have "  Ccost * (C * norm (sssp_time_target m n))"
        using elim(2) Ccost_pos by simp
      also have " = Ccost * C * norm (sssp_time_target m n)"
        by (simp add: algebra_simps)
      finally show ?case .
    qed
  qed
qed

context unique_shortest_digraph
begin

text ‹
  From this point on the arithmetic bounds are tied to a concrete finite graph.
  The definitions vertex_count› and edge_count› are simple wrappers around
  the locale's finite vertex and edge sets, but naming them keeps the final
  theorems independent of internal set notation.  The parent-edge construction
  is used to relate reachable vertices to edges, which is the standard sparse
  graph move behind replacing a vertex term by an edge term when all vertices
  are reachable.
›

definition vertex_count where
  "vertex_count = card V"

definition edge_count where
  "edge_count = card E"

definition parent_edge_to where
  "parent_edge_to v = (last (butlast (shortest_path_to v)), v)"

lemma vertex_count_pos [simp]:
  "0 < vertex_count"
proof -
  have "V  {}"
    using source_in_V by blast
  then show ?thesis
    unfolding vertex_count_def using finite_V by (simp add: card_gt_0_iff)
qed

lemma edge_count_le_vertex_count_square:
  "edge_count  vertex_count * vertex_count"
proof -
  have E_subset: "E  V × V"
    using edge_in_V by auto
  have "edge_count = card E"
    unfolding edge_count_def by simp
  also have "  card (V × V)"
    by (rule card_mono[OF _ E_subset]) (use finite_V in simp)
  also have " = vertex_count * vertex_count"
    unfolding vertex_count_def by (simp add: card_cartesian_product)
  finally show ?thesis .
qed

lemma parent_edge_to_in_E:
  assumes reach_v: "reachable s v"
    and v_ne: "v  s"
  shows "parent_edge_to v  E"
proof -
  let ?p = "shortest_path_to v"
  have sp: "shortest_walk s ?p v"
    by (rule shortest_path_to_shortest[OF reach_v])
  have walk_p: "walk ?p"
    using sp unfolding shortest_walk_def simple_walk_betw_def walk_betw_def by blast
  have p_ne: "?p  []"
    using sp unfolding shortest_walk_def simple_walk_betw_def walk_betw_def by blast
  have hd_p: "hd ?p = s"
    using sp unfolding shortest_walk_def simple_walk_betw_def walk_betw_def by blast
  have last_p: "last ?p = v"
    using sp unfolding shortest_walk_def simple_walk_betw_def walk_betw_def by blast
  have len_ge2: "1 < length ?p"
  proof (rule ccontr)
    assume "¬ 1 < length ?p"
    then have "length ?p = 1"
      using p_ne by (cases ?p) auto
    then have "hd ?p = last ?p"
      by (cases ?p) auto
    then show False
      using hd_p last_p v_ne by simp
  qed
  have but_ne: "butlast ?p  []"
    using len_ge2 by (cases ?p) auto
  obtain q u where but: "butlast ?p = q @ [u]"
    using but_ne by (cases "butlast ?p" rule: rev_cases) auto
  have p_decomp: "?p = q @ [u, v]"
  proof -
    have "?p = butlast ?p @ [last ?p]"
      using p_ne by simp
    then show ?thesis
      using but last_p by simp
  qed
  have i_lt: "Suc (length q) < length ?p"
    using p_decomp by simp
  have edge: "(?p ! length q, ?p ! Suc (length q))  E"
    by (rule walk_nth_edge[OF walk_p i_lt])
  have "?p ! length q = u"
    using p_decomp by (simp add: nth_append)
  moreover have "?p ! Suc (length q) = v"
    using p_decomp by (simp add: nth_append)
  ultimately have "(u, v)  E"
    using edge by simp
  moreover have "last (butlast ?p) = u"
    using but by simp
  ultimately show ?thesis
    unfolding parent_edge_to_def by simp
qed

theorem vertex_count_le_Suc_edge_count_if_all_reachable:
  assumes all_reachable: "v. v  V  reachable s v"
  shows "vertex_count  Suc edge_count"
proof -
  have image_subset: "parent_edge_to ` (V - {s})  E"
  proof
    fix e
    assume "e  parent_edge_to ` (V - {s})"
    then obtain v where v: "v  V" "v  s" "e = parent_edge_to v"
      by blast
    have "reachable s v"
      by (rule all_reachable[OF v(1)])
    then show "e  E"
      using parent_edge_to_in_E[OF _ v(2)] v(3) by simp
  qed
  have inj: "inj_on parent_edge_to (V - {s})"
    unfolding parent_edge_to_def inj_on_def by auto
  have card_image:
    "card (V - {s}) = card (parent_edge_to ` (V - {s}))"
    using inj finite_V by (simp add: card_image)
  also have "  edge_count"
    unfolding edge_count_def
    by (rule card_mono[OF finite_E image_subset])
  finally have without_s: "card (V - {s})  edge_count" .
  have V_decomp: "V = insert s (V - {s})"
    using source_in_V by blast
  have "card V = card (insert s (V - {s}))"
    using V_decomp by simp
  also have " = Suc (card (V - {s}))"
    by (rule card_insert_disjoint) (use finite_V in auto)
  finally have "card V = Suc (card (V - {s}))" .
  then show ?thesis
    using without_s unfolding vertex_count_def by simp
qed

corollary vertex_count_le_twice_edge_count_if_all_reachable:
  assumes all_reachable: "v. v  V  reachable s v"
    and nontrivial: "0 < edge_count"
  shows "vertex_count  2 * edge_count"
proof -
  have "vertex_count  Suc edge_count"
    by (rule vertex_count_le_Suc_edge_count_if_all_reachable[OF all_reachable])
  also have "  2 * edge_count"
    using nontrivial by simp
  finally show ?thesis .
qed

lemma card_subset_vertex_count:
  assumes "S  V"
  shows "card S  vertex_count"
  unfolding vertex_count_def
  by (rule card_mono[OF finite_V assms])

lemma eventually_top_level_threshold_exceeds_vertex_count:
  "eventually
    (λN. vertex_count <
      sssp_log_one_third_param N *
      bmssp_level_cap (sssp_log_one_third_param N)
        (sssp_log_two_thirds_param N) (sssp_log_one_third_param N))
    at_top"
  using eventually_less_sssp_log_one_third_param[of vertex_count]
proof eventually_elim
  case (elim N)
  let ?p = "sssp_log_one_third_param N"
  let ?q = "sssp_log_two_thirds_param N"
  let ?cap = "bmssp_level_cap ?p ?q ?p"
  have cap_pos: "0 < ?cap"
    unfolding bmssp_level_cap_def by simp
  have "?p  ?p * ?cap"
    using cap_pos by (cases ?cap) simp_all
  then show ?case
    using elim by linarith
qed

lemma find_pivots_pivots_capped_singleton_empty_if_params_exceed_vertex_count:
  assumes k_gt: "vertex_count < k"
    and cap_ge: "vertex_count  cap"
  shows "find_pivots_pivots_capped k cap d {s} B = {}"
proof -
  let ?seen = "find_pivots_seen_capped k cap d {s} B"
  have source_subset: "{s}  V"
    using source_in_V by simp
  have seen_subset: "?seen  V"
    unfolding find_pivots_seen_capped_def
    by (rule fp_iter_capped_seen_subset_V[OF source_subset source_subset])
  have card_seen_le: "card ?seen  vertex_count"
    by (rule card_subset_vertex_count[OF seen_subset])
  have no_overflow: "¬ card ?seen > cap"
    using card_seen_le cap_ge by linarith
  have no_large_tree:
    "{u  {s}. k  card (tree_of u  ?seen)} = {}"
  proof
    show "{u  {s}. k  card (tree_of u  ?seen)}  {}"
    proof
      fix u
      assume u_mem: "u  {u  {s}. k  card (tree_of u  ?seen)}"
      have tree_seen_subset: "tree_of u  ?seen  V"
        unfolding tree_of_def by blast
      have "card (tree_of u  ?seen)  vertex_count"
        by (rule card_subset_vertex_count[OF tree_seen_subset])
      moreover have "k  card (tree_of u  ?seen)"
        using u_mem by blast
      ultimately show "u  {}"
        using k_gt by linarith
    qed
  qed simp
  show ?thesis
    unfolding find_pivots_pivots_capped_def
    using no_overflow no_large_tree by simp
qed

lemma eventually_top_level_pivots_empty:
  "eventually
    (λN. find_pivots_pivots_capped (sssp_log_one_third_param N)
      (bmssp_level_cap (sssp_log_one_third_param N)
        (sssp_log_two_thirds_param N) (sssp_log_one_third_param N))
      d {s} B = {})
    at_top"
  using eventually_less_sssp_log_one_third_param[of vertex_count]
proof eventually_elim
  case (elim N)
  let ?p = "sssp_log_one_third_param N"
  let ?q = "sssp_log_two_thirds_param N"
  let ?cap = "bmssp_level_cap ?p ?q ?p"
  have cap_ge_p: "?p  ?cap"
    by (rule bmssp_level_cap_ge_level)
  have cap_ge_vertex: "vertex_count  ?cap"
    using elim cap_ge_p by linarith
  show ?case
    by (rule find_pivots_pivots_capped_singleton_empty_if_params_exceed_vertex_count
      [OF elim cap_ge_vertex])
qed

lemma edge_count_outgoing_bound:
  "card (outgoing_edges X)  edge_count"
proof -
  have "outgoing_edges X  E"
    unfolding outgoing_edges_def by blast
  then show ?thesis
    unfolding edge_count_def by (rule card_mono[OF finite_E])
qed

lemma edge_count_outgoing_bound_tree:
  "card (outgoing_edges (bound_tree S B))  edge_count"
  by (rule edge_count_outgoing_bound)

text ‹
  The range-based edge definitions are the combinatorial support for the
  direct-insert and lower-batch split.  An edge is charged to a range according
  to the relaxed value at its source, not merely according to the source
  vertex.  This matches the algorithm: a completed child range may generate
  relaxations that fall below the pull bound, inside the parent output bound, or
  outside the current problem.

  The length lemmas connect executable lists of relaxation pairs to the
  mathematical edge ranges.  The disjointness and split lemmas that follow make
  it possible to sum per-child edge charges and bound them by the outgoing edges
  of the assembled parent range.
›

definition outgoing_edges_range where
  "outgoing_edges_range U a B =
    {(u, v)  E. u  U  a  dist s u + w u v 
      below_bound (dist s u + w u v) B}"

lemma outgoing_edges_range_subset_E:
  "outgoing_edges_range U a B  E"
  unfolding outgoing_edges_range_def by blast

lemma outgoing_edges_range_subset_outgoing_edges:
  "outgoing_edges_range U a B  outgoing_edges U"
  unfolding outgoing_edges_range_def outgoing_edges_def by blast

lemma finite_outgoing_edges_range [simp]:
  "finite (outgoing_edges_range U a B)"
  by (rule finite_subset[OF outgoing_edges_range_subset_E finite_E])

lemma card_outgoing_edges_range_le_edge_count:
  "card (outgoing_edges_range U a B)  edge_count"
  unfolding edge_count_def
  by (rule card_mono[OF finite_E outgoing_edges_range_subset_E])

definition edge_relaxation_pairs_in_bound where
  "edge_relaxation_pairs_in_bound d U L B =
     map (λ(u, v). (v, d u + w u v))
       (filter (λ(u, v). L  d u + w u v 
          below_bound (d u + w u v) B)
        (edge_list_of (outgoing_edges U)))"

lemma edge_relaxation_pairs_in_bound_value_ge_low:
  assumes "(x, b)  set (edge_relaxation_pairs_in_bound d U L B)"
  shows "L  b"
  using assms unfolding edge_relaxation_pairs_in_bound_def by auto

lemma edge_relaxation_pairs_in_bound_value_below_bound:
  assumes "(x, b)  set (edge_relaxation_pairs_in_bound d U L B)"
  shows "below_bound b B"
  using assms unfolding edge_relaxation_pairs_in_bound_def by auto

lemma edge_relaxation_pairs_in_bound_length_le_outgoing_edges_range:
  assumes complete: "complete_on d U"
    and reaches: "u. u  U  reachable s u"
  shows "length (edge_relaxation_pairs_in_bound d U L B) 
    card (outgoing_edges_range U L B)"
proof -
  let ?es = "edge_list_of (outgoing_edges U)"
  let ?fs =
    "filter (λ(u, v). L  d u + w u v 
      below_bound (d u + w u v) B) ?es"
  have set_es: "set ?es = outgoing_edges U"
    using edge_list_of_properties(1)[OF finite_outgoing_edges] .
  have distinct_fs: "distinct ?fs"
    using edge_list_of_properties(2)[OF finite_outgoing_edges] by simp
  have subset:
    "set ?fs  outgoing_edges_range U L B"
  proof
    fix e
    assume e: "e  set ?fs"
    then obtain u v where ev: "e = (u, v)"
      by (metis surj_pair)
    have edge: "(u, v)  E"
      and uU: "u  U"
      and low: "L  d u + w u v"
      and high: "below_bound (d u + w u v) B"
      using e set_es unfolding ev outgoing_edges_def by auto
    have du: "d u = dist s u"
      using complete reaches[OF uU] uU unfolding complete_on_def by blast
    show "e  outgoing_edges_range U L B"
      using edge uU low high unfolding ev du outgoing_edges_range_def by simp
  qed
  have "length (edge_relaxation_pairs_in_bound d U L B) = length ?fs"
    unfolding edge_relaxation_pairs_in_bound_def by simp
  also have " = card (set ?fs)"
    using distinct_card[OF distinct_fs] by simp
  also have "  card (outgoing_edges_range U L B)"
    by (rule card_mono[OF finite_outgoing_edges_range subset])
  finally show ?thesis .
qed

lemma partition_batch_cost_bound_edge_relaxation_pairs_in_bound:
  assumes cost:
      "partition_batch_cost_bound c t
        (edge_relaxation_pairs_in_bound d U L B)"
    and complete: "complete_on d U"
    and reaches: "u. u  U  reachable s u"
  shows "c  t * card (outgoing_edges_range U L B)"
proof -
  have len_le: "length (edge_relaxation_pairs_in_bound d U L B) 
      card (outgoing_edges_range U L B)"
    by (rule edge_relaxation_pairs_in_bound_length_le_outgoing_edges_range
      [OF complete reaches])
  have "c  t * length (edge_relaxation_pairs_in_bound d U L B)"
    using cost unfolding partition_batch_cost_bound_def by simp
  also have "  t * card (outgoing_edges_range U L B)"
    using len_le by simp
  finally show ?thesis .
qed

lemma edge_relaxation_pairs_between_length_le_outgoing_edges_range:
  assumes complete: "complete_on d U"
    and reaches: "u. u  U  reachable s u"
  shows "length (edge_relaxation_pairs_between d U L H) 
    card (outgoing_edges_range U L (Fin H))"
proof -
  let ?es = "edge_list_of (outgoing_edges U)"
  let ?fs =
    "filter (λ(u, v). L  d u + w u v  d u + w u v < H) ?es"
  have set_es: "set ?es = outgoing_edges U"
    using edge_list_of_properties(1)[OF finite_outgoing_edges] .
  have distinct_fs: "distinct ?fs"
    using edge_list_of_properties(2)[OF finite_outgoing_edges] by simp
  have subset:
    "set ?fs  outgoing_edges_range U L (Fin H)"
  proof
    fix e
    assume e: "e  set ?fs"
    then obtain u v where ev: "e = (u, v)"
      by (metis surj_pair)
    have edge: "(u, v)  E"
      and uU: "u  U"
      and low: "L  d u + w u v"
      and high: "d u + w u v < H"
      using e set_es unfolding ev outgoing_edges_def by auto
    have du: "d u = dist s u"
      using complete reaches[OF uU] uU unfolding complete_on_def by blast
    show "e  outgoing_edges_range U L (Fin H)"
      using edge uU low high unfolding ev du outgoing_edges_range_def by simp
  qed
  have "length (edge_relaxation_pairs_between d U L H) = length ?fs"
    unfolding edge_relaxation_pairs_between_def by simp
  also have " = card (set ?fs)"
    using distinct_card[OF distinct_fs] by simp
  also have "  card (outgoing_edges_range U L (Fin H))"
    by (rule card_mono[OF finite_outgoing_edges_range subset])
  finally show ?thesis .
qed

lemma edge_relaxation_pairs_between_length_le_range_tree_outgoing_edges_range:
  assumes complete: "complete_on d (range_tree S a B)"
  shows "length (edge_relaxation_pairs_between d (range_tree S a B) L H) 
    card (outgoing_edges_range (range_tree S a B) L (Fin H))"
proof (rule edge_relaxation_pairs_between_length_le_outgoing_edges_range[OF complete])
  fix u
  assume "u  range_tree S a B"
  then show "reachable s u"
    unfolding range_tree_def tree_set_def tree_path_def by blast
qed

lemma outgoing_edges_range_mono_sources:
  assumes "U  W"
  shows "outgoing_edges_range U a B  outgoing_edges_range W a B"
  using assms unfolding outgoing_edges_range_def by blast

lemma outgoing_edges_range_split:
  assumes a_le_b: "a  b"
    and b_le_B: "bound_le (Fin b) B"
  shows "outgoing_edges_range U a B =
    outgoing_edges_range U a (Fin b)  outgoing_edges_range U b B"
  using assms unfolding outgoing_edges_range_def
  by (cases B) auto

lemma outgoing_edges_range_split_disjoint:
  "outgoing_edges_range U a (Fin b)  outgoing_edges_range U b B = {}"
  unfolding outgoing_edges_range_def by auto

lemma card_outgoing_edges_range_split:
  assumes a_le_b: "a  b"
    and b_le_B: "bound_le (Fin b) B"
  shows "card (outgoing_edges_range U a B) =
    card (outgoing_edges_range U a (Fin b)) +
    card (outgoing_edges_range U b B)"
proof -
  have split:
    "outgoing_edges_range U a B =
      outgoing_edges_range U a (Fin b)  outgoing_edges_range U b B"
    by (rule outgoing_edges_range_split[OF a_le_b b_le_B])
  have disj:
    "outgoing_edges_range U a (Fin b)  outgoing_edges_range U b B = {}"
    by (rule outgoing_edges_range_split_disjoint)
  show ?thesis
    unfolding split
    by (rule card_Un_disjoint) (use disj in auto)
qed

lemma card_outgoing_edges_range_lower_direct_split:
  assumes b_le_beta: "b  beta"
    and beta_le_B: "bound_le (Fin beta) B"
  shows "card (outgoing_edges_range U b (Fin beta)) +
    card (outgoing_edges_range U beta B) =
    card (outgoing_edges_range U b B)"
  using card_outgoing_edges_range_split[OF b_le_beta beta_le_B, of U]
  by simp

lemma card_outgoing_edges_range_zero_lower_direct_split_le:
  assumes beta_le_B: "bound_le (Fin beta) B"
    and nonneg:
      "u v. (u, v)  E; u  U  0  dist s u + w u v"
  shows "card (outgoing_edges_range U 0 (Fin beta)) +
    card (outgoing_edges_range U beta B) 
    card (outgoing_edges_range U 0 B)"
proof -
  let ?lower = "outgoing_edges_range U 0 (Fin beta)"
  let ?direct = "outgoing_edges_range U beta B"
  let ?full = "outgoing_edges_range U 0 B"
  have disj: "?lower  ?direct = {}"
    by (rule outgoing_edges_range_split_disjoint)
  have union_subset: "?lower  ?direct  ?full"
  proof
    fix e
    assume e: "e  ?lower  ?direct"
    show "e  ?full"
    proof (cases "e  ?lower")
      case True
      then show ?thesis
        using beta_le_B unfolding outgoing_edges_range_def
        by (cases B) auto
    next
      case False
      then have direct: "e  ?direct"
        using e by blast
      then obtain u v where e_uv: "e = (u, v)"
        by (metis surj_pair)
      have edge: "(u, v)  E" and uU: "u  U"
        using direct unfolding e_uv outgoing_edges_range_def by auto
      have "0  dist s u + w u v"
        by (rule nonneg[OF edge uU])
      then show ?thesis
        using direct unfolding e_uv outgoing_edges_range_def by auto
    qed
  qed
  have card_union:
    "card (?lower  ?direct) = card ?lower + card ?direct"
    using disj by (simp add: card_Un_disjoint)
  have "card (?lower  ?direct)  card ?full"
    by (rule card_mono[OF finite_outgoing_edges_range union_subset])
  then show ?thesis
    using card_union by linarith
qed

lemma outgoing_edges_range_disjoint_sources:
  assumes "U  W = {}"
  shows "outgoing_edges_range U a B  outgoing_edges_range W c D = {}"
  using assms unfolding outgoing_edges_range_def by blast

fun pairwise_disjoint_list where
  "pairwise_disjoint_list []  True"
| "pairwise_disjoint_list (X # Xs) 
    (Yset Xs. X  Y = {})  pairwise_disjoint_list Xs"

lemma pairwise_disjoint_list_outgoing_edges_range:
  assumes "pairwise_disjoint_list Us"
  shows "pairwise_disjoint_list (map (λU. outgoing_edges_range U a B) Us)"
  using assms
proof (induction Us)
  case Nil
  then show ?case by simp
next
  case (Cons U Us)
  have disj_head:
    "Yset (map (λU. outgoing_edges_range U a B) Us).
      outgoing_edges_range U a B  Y = {}"
  proof
    fix Y
    assume "Y  set (map (λU. outgoing_edges_range U a B) Us)"
    then obtain W where W: "W  set Us" "Y = outgoing_edges_range W a B"
      by auto
    have "U  W = {}"
      using Cons.prems W(1) by simp
    then show "outgoing_edges_range U a B  Y = {}"
      unfolding W(2) by (rule outgoing_edges_range_disjoint_sources)
  qed
  have tail: "pairwise_disjoint_list (map (λU. outgoing_edges_range U a B) Us)"
    using Cons.IH Cons.prems by simp
  show ?case
    using disj_head tail by simp
qed

lemma edge_outdegree_le_edge_count:
  "edge_outdegree_le edge_count"
  unfolding edge_outdegree_le_def
  using edge_count_outgoing_bound by blast

lemma card_Union_pairwise_disjoint_list:
  assumes disj: "pairwise_disjoint_list Xs"
    and finite_sets: "X. X  set Xs  finite X"
  shows "card ((set Xs)) = sum_list (map card Xs)"
  using assms
proof (induction Xs)
  case Nil
  then show ?case by simp
next
  case (Cons X Xs)
  have finite_X: "finite X"
    using Cons.prems by simp
  have finite_tail: "finite ((set Xs))"
    using Cons.prems by auto
  have disjoint_tail: "X  (set Xs) = {}"
    using Cons.prems by auto
  have tail:
    "card ((set Xs)) = sum_list (map card Xs)"
    using Cons.IH Cons.prems by auto
  have "card ((set (X # Xs))) =
      card X + card ((set Xs))"
    by (simp add: card_Un_disjoint finite_X finite_tail disjoint_tail)
  then show ?case
    using tail by simp
qed

lemma sum_card_outgoing_edges_range_disjoint_sources_le_edge_count:
  assumes disj: "pairwise_disjoint_list Us"
  shows "sum_list (map (λU. card (outgoing_edges_range U a B)) Us)  edge_count"
proof -
  have disj_ranges:
    "pairwise_disjoint_list (map (λU. outgoing_edges_range U a B) Us)"
    by (rule pairwise_disjoint_list_outgoing_edges_range[OF disj])
  have finite_ranges:
    "X. X  set (map (λU. outgoing_edges_range U a B) Us)  finite X"
    by auto
  have card_union:
    "card ((set (map (λU. outgoing_edges_range U a B) Us))) =
      sum_list (map card (map (λU. outgoing_edges_range U a B) Us))"
    by (rule card_Union_pairwise_disjoint_list[OF disj_ranges finite_ranges])
  have union_subset:
    "(set (map (λU. outgoing_edges_range U a B) Us))  E"
  proof
    fix e
    assume "e  (set (map (λU. outgoing_edges_range U a B) Us))"
    then obtain U where "U  set Us" and "e  outgoing_edges_range U a B"
      by auto
    then show "e  E"
      unfolding outgoing_edges_range_def by auto
  qed
  have sum_maps:
    "sum_list (map (λU. card (outgoing_edges_range U a B)) Us) =
      sum_list (map card (map (λU. outgoing_edges_range U a B) Us))"
    by (induction Us) simp_all
  have "sum_list (map (λU. card (outgoing_edges_range U a B)) Us) =
      card ((set (map (λU. outgoing_edges_range U a B) Us)))"
    using sum_maps card_union by simp
  also have "  edge_count"
    unfolding edge_count_def by (rule card_mono[OF finite_E union_subset])
  finally show ?thesis .
qed

lemma outgoing_edges_disjoint_if_sources_disjoint:
  assumes "A  B = {}"
  shows "outgoing_edges A  outgoing_edges B = {}"
  using assms unfolding outgoing_edges_def by auto

lemma pairwise_disjoint_list_outgoing_edges:
  assumes "pairwise_disjoint_list Xs"
  shows "pairwise_disjoint_list (map outgoing_edges Xs)"
  using assms
proof (induction Xs)
  case Nil
  then show ?case by simp
next
  case (Cons X Xs)
  have "Y. Y  set Xs  outgoing_edges X  outgoing_edges Y = {}"
    using Cons.prems outgoing_edges_disjoint_if_sources_disjoint by auto
  then show ?case
    using Cons by auto
qed

lemma outgoing_edges_Union_list:
  "outgoing_edges ((set Xs)) = (set (map outgoing_edges Xs))"
  unfolding outgoing_edges_def by auto

lemma outgoing_edges_mono:
  assumes "A  B"
  shows "outgoing_edges A  outgoing_edges B"
  using assms unfolding outgoing_edges_def by blast

fun range_tree_child_list where
  "range_tree_child_list S a [] = []"
| "range_tree_child_list S a (b # bs) =
    range_tree S a (Fin b) # range_tree_child_list S b bs"

lemma length_range_tree_child_list [simp]:
  "length (range_tree_child_list S a bs) = length bs"
  by (induction bs arbitrary: a) simp_all

lemma range_tree_child_list_set_subset_chain_list:
  "set (range_tree_child_list S a bs)  set (range_tree_chain_list S a bs B)"
proof (induction bs arbitrary: a)
  case Nil
  then show ?case by simp
next
  case (Cons b bs)
  have "set (range_tree_child_list S b bs) 
      set (range_tree_chain_list S b bs B)"
    using Cons.IH .
  then show ?case by auto
qed

lemma Union_range_tree_child_list_subset_chain:
  "(set (range_tree_child_list S a bs))  range_tree_chain S a bs B"
proof (induction bs arbitrary: a)
  case Nil
  then show ?case by simp
next
  case (Cons b bs)
  have "(set (range_tree_child_list S b bs)) 
      range_tree_chain S b bs B"
    using Cons.IH .
  then show ?case by auto
qed

fun range_tree_child_edge_range_list where
  "range_tree_child_edge_range_list S a [] [] = []"
| "range_tree_child_edge_range_list S a [] (b # bs) = []"
| "range_tree_child_edge_range_list S a (beta # betas) [] = []"
| "range_tree_child_edge_range_list S a (beta # betas) (b # bs) =
    outgoing_edges_range (range_tree S a (Fin b)) b (Fin beta) #
    range_tree_child_edge_range_list S b betas bs"

lemma range_tree_child_edge_range_list_subset_outgoing_sources:
  assumes "Y  set (range_tree_child_edge_range_list S a betas bs)"
  shows "Xset (range_tree_child_list S a bs). Y  outgoing_edges X"
  using assms
proof (induction betas arbitrary: a bs)
  case Nil
  then show ?case
    by (cases bs) simp_all
next
  case (Cons beta betas)
  show ?case
  proof (cases bs)
    case Nil
    then show ?thesis
      using Cons.prems by simp
  next
    case (Cons b bs')
    let ?head = "outgoing_edges_range (range_tree S a (Fin b)) b (Fin beta)"
    let ?tail = "range_tree_child_edge_range_list S b betas bs'"
    have Y_cases: "Y = ?head  Y  set ?tail"
      using Cons.prems Cons by simp
    then show ?thesis
    proof
      assume Y_head: "Y = ?head"
      have "?head  outgoing_edges (range_tree S a (Fin b))"
        by (rule outgoing_edges_range_subset_outgoing_edges)
      then show ?thesis
        unfolding Cons Y_head by auto
    next
      assume Y_tail: "Y  set ?tail"
      obtain X where X: "X  set (range_tree_child_list S b bs')"
        and YX: "Y  outgoing_edges X"
        using Cons.IH[OF Y_tail] by blast
      show ?thesis
        using X YX unfolding Cons by auto
    qed
  qed
qed

lemma range_tree_child_edge_range_list_member_subset_E:
  assumes "Y  set (range_tree_child_edge_range_list S a betas bs)"
  shows "Y  E"
proof
  fix e
  assume eY: "e  Y"
  obtain X where "X  set (range_tree_child_list S a bs)"
    and Y_subset: "Y  outgoing_edges X"
    using range_tree_child_edge_range_list_subset_outgoing_sources[OF assms]
    by blast
  then have "e  outgoing_edges X"
    using eY by blast
  then show "e  E"
    unfolding outgoing_edges_def by blast
qed

lemma pairwise_disjoint_list_range_tree_child_edge_range_list:
  assumes mono: "nondecreasing_from a bs"
  shows "pairwise_disjoint_list (range_tree_child_edge_range_list S a betas bs)"
  using mono
proof (induction betas arbitrary: a bs)
  case Nil
  then show ?case
    by (cases bs) simp_all
next
  case (Cons beta betas)
  show ?case
  proof (cases bs)
    case Nil
    then show ?thesis by simp
  next
    case (Cons b bs')
    let ?head = "outgoing_edges_range (range_tree S a (Fin b)) b (Fin beta)"
    let ?tail = "range_tree_child_edge_range_list S b betas bs'"
    have a_le_b: "a  b"
      using Cons.prems Cons by simp
    have mono_tail: "nondecreasing_from b bs'"
      using Cons.prems Cons by simp
    have head_tail_disj: "Yset ?tail. ?head  Y = {}"
    proof
      fix Y
      assume Y: "Y  set ?tail"
      obtain X where X: "X  set (range_tree_child_list S b bs')"
        and YX: "Y  outgoing_edges X"
        using range_tree_child_edge_range_list_subset_outgoing_sources[OF Y]
        by blast
      have X_subset: "X  range_tree_chain S b bs' Infinity"
        using X Union_range_tree_child_list_subset_chain[of S b bs' Infinity]
        by blast
      have source_disj: "range_tree S a (Fin b)  X = {}"
        using range_tree_disjoint_range_tree_chain_tail
          [OF a_le_b mono_tail, of S Infinity] X_subset by blast
      have out_disj:
        "outgoing_edges (range_tree S a (Fin b))  outgoing_edges X = {}"
        by (rule outgoing_edges_disjoint_if_sources_disjoint[OF source_disj])
      have head_subset: "?head  outgoing_edges (range_tree S a (Fin b))"
        by (rule outgoing_edges_range_subset_outgoing_edges)
      show "?head  Y = {}"
        using head_subset YX out_disj by blast
    qed
    have tail_disj: "pairwise_disjoint_list ?tail"
      by (rule Cons.IH[OF mono_tail])
    show ?thesis
      unfolding Cons by (simp add: head_tail_disj tail_disj)
  qed
qed

lemma sum_card_range_tree_child_edge_range_list_le_edge_count:
  assumes mono: "nondecreasing_from a bs"
  shows "sum_list (map card (range_tree_child_edge_range_list S a betas bs))
     edge_count"
proof -
  let ?Xs = "range_tree_child_edge_range_list S a betas bs"
  have disj: "pairwise_disjoint_list ?Xs"
    by (rule pairwise_disjoint_list_range_tree_child_edge_range_list[OF mono])
  have finite_sets: "X. X  set ?Xs  finite X"
    using range_tree_child_edge_range_list_member_subset_E
    by (meson finite_E finite_subset)
  have card_union:
    "card ((set ?Xs)) = sum_list (map card ?Xs)"
    by (rule card_Union_pairwise_disjoint_list[OF disj finite_sets])
  have union_subset: "(set ?Xs)  E"
  proof
    fix e
    assume "e  (set ?Xs)"
    then obtain X where X: "X  set ?Xs" and eX: "e  X"
      by blast
    show "e  E"
      using range_tree_child_edge_range_list_member_subset_E[OF X] eX by blast
  qed
  have "sum_list (map card ?Xs) = card ((set ?Xs))"
    using card_union by simp
  also have "  edge_count"
    unfolding edge_count_def by (rule card_mono[OF finite_E union_subset])
  finally show ?thesis .
qed

lemma sum_card_range_tree_child_edge_range_list_le_outgoing_edges_chain:
  assumes mono: "nondecreasing_from a bs"
  shows "sum_list (map card (range_tree_child_edge_range_list S a betas bs))
     card (outgoing_edges (range_tree_chain S a bs B))"
proof -
  let ?Rs = "range_tree_child_edge_range_list S a betas bs"
  let ?Xs = "range_tree_child_list S a bs"
  have disj: "pairwise_disjoint_list ?Rs"
    by (rule pairwise_disjoint_list_range_tree_child_edge_range_list[OF mono])
  have finite_sets: "R. R  set ?Rs  finite R"
    using range_tree_child_edge_range_list_member_subset_E
    by (meson finite_E finite_subset)
  have card_union:
    "card ((set ?Rs)) = sum_list (map card ?Rs)"
    by (rule card_Union_pairwise_disjoint_list[OF disj finite_sets])
  have union_subset_children: "(set ?Rs)  outgoing_edges ((set ?Xs))"
  proof
    fix e
    assume "e  (set ?Rs)"
    then obtain R where R: "R  set ?Rs" and eR: "e  R"
      by blast
    obtain X where X: "X  set ?Xs" and R_subset: "R  outgoing_edges X"
      using range_tree_child_edge_range_list_subset_outgoing_sources[OF R]
      by blast
    show "e  outgoing_edges ((set ?Xs))"
      using eR R_subset X unfolding outgoing_edges_def by blast
  qed
  have children_subset: "(set ?Xs)  range_tree_chain S a bs B"
    by (rule Union_range_tree_child_list_subset_chain)
  have union_subset:
    "(set ?Rs)  outgoing_edges (range_tree_chain S a bs B)"
    using union_subset_children outgoing_edges_mono[OF children_subset] by blast
  have "sum_list (map card ?Rs) = card ((set ?Rs))"
    using card_union by simp
  also have "  card (outgoing_edges (range_tree_chain S a bs B))"
    by (rule card_mono) (simp_all add: union_subset)
  finally show ?thesis .
qed

fun range_tree_child_direct_edge_range_list where
  "range_tree_child_direct_edge_range_list S B a [] [] = []"
| "range_tree_child_direct_edge_range_list S B a [] (b # bs) = []"
| "range_tree_child_direct_edge_range_list S B a (beta # betas) [] = []"
| "range_tree_child_direct_edge_range_list S B a (beta # betas) (b # bs) =
    outgoing_edges_range (range_tree S a (Fin b)) beta B #
    range_tree_child_direct_edge_range_list S B b betas bs"

fun range_tree_child_zero_edge_range_list where
  "range_tree_child_zero_edge_range_list S a [] [] = []"
| "range_tree_child_zero_edge_range_list S a [] (b # bs) = []"
| "range_tree_child_zero_edge_range_list S a (beta # betas) [] = []"
| "range_tree_child_zero_edge_range_list S a (beta # betas) (b # bs) =
    outgoing_edges_range (range_tree S a (Fin b)) 0 (Fin beta) #
    range_tree_child_zero_edge_range_list S b betas bs"

fun range_tree_child_full_edge_range_list where
  "range_tree_child_full_edge_range_list S B a [] = []"
| "range_tree_child_full_edge_range_list S B a (b # bs) =
    outgoing_edges_range (range_tree S a (Fin b)) 0 B #
    range_tree_child_full_edge_range_list S B b bs"

fun range_tree_child_bound_pair_list where
  "range_tree_child_bound_pair_list S a [] [] = []"
| "range_tree_child_bound_pair_list S a [] (b # bs) = []"
| "range_tree_child_bound_pair_list S a (beta # betas) [] = []"
| "range_tree_child_bound_pair_list S a (beta # betas) (b # bs) =
    (range_tree S a (Fin b), Fin beta) #
    range_tree_child_bound_pair_list S b betas bs"

lemma range_tree_child_direct_edge_range_list_subset_outgoing_sources:
  assumes "Y  set (range_tree_child_direct_edge_range_list S B a betas bs)"
  shows "Xset (range_tree_child_list S a bs). Y  outgoing_edges X"
  using assms
proof (induction betas arbitrary: a bs)
  case Nil
  then show ?case
    by (cases bs) simp_all
next
  case (Cons beta betas)
  show ?case
  proof (cases bs)
    case Nil
    then show ?thesis
      using Cons.prems by simp
  next
    case (Cons b bs')
    let ?head = "outgoing_edges_range (range_tree S a (Fin b)) beta B"
    let ?tail = "range_tree_child_direct_edge_range_list S B b betas bs'"
    have Y_cases: "Y = ?head  Y  set ?tail"
      using Cons.prems Cons by simp
    then show ?thesis
    proof
      assume Y_head: "Y = ?head"
      have "?head  outgoing_edges (range_tree S a (Fin b))"
        by (rule outgoing_edges_range_subset_outgoing_edges)
      then show ?thesis
        unfolding Cons Y_head by auto
    next
      assume Y_tail: "Y  set ?tail"
      obtain X where X: "X  set (range_tree_child_list S b bs')"
        and YX: "Y  outgoing_edges X"
        using Cons.IH[OF Y_tail] by blast
      show ?thesis
        using X YX unfolding Cons by auto
    qed
  qed
qed

lemma range_tree_child_direct_edge_range_list_member_subset_E:
  assumes "Y  set (range_tree_child_direct_edge_range_list S B a betas bs)"
  shows "Y  E"
proof
  fix e
  assume eY: "e  Y"
  obtain X where "X  set (range_tree_child_list S a bs)"
    and Y_subset: "Y  outgoing_edges X"
    using range_tree_child_direct_edge_range_list_subset_outgoing_sources[OF assms]
    by blast
  then have "e  outgoing_edges X"
    using eY by blast
  then show "e  E"
    unfolding outgoing_edges_def by blast
qed

lemma pairwise_disjoint_list_range_tree_child_direct_edge_range_list:
  assumes mono: "nondecreasing_from a bs"
  shows "pairwise_disjoint_list
    (range_tree_child_direct_edge_range_list S B a betas bs)"
  using mono
proof (induction betas arbitrary: a bs)
  case Nil
  then show ?case
    by (cases bs) simp_all
next
  case (Cons beta betas)
  show ?case
  proof (cases bs)
    case Nil
    then show ?thesis by simp
  next
    case (Cons b bs')
    let ?head = "outgoing_edges_range (range_tree S a (Fin b)) beta B"
    let ?tail = "range_tree_child_direct_edge_range_list S B b betas bs'"
    have a_le_b: "a  b"
      using Cons.prems Cons by simp
    have mono_tail: "nondecreasing_from b bs'"
      using Cons.prems Cons by simp
    have head_tail_disj: "Yset ?tail. ?head  Y = {}"
    proof
      fix Y
      assume Y: "Y  set ?tail"
      obtain X where X: "X  set (range_tree_child_list S b bs')"
        and YX: "Y  outgoing_edges X"
        using range_tree_child_direct_edge_range_list_subset_outgoing_sources[OF Y]
        by blast
      have X_subset: "X  range_tree_chain S b bs' Infinity"
        using X Union_range_tree_child_list_subset_chain[of S b bs' Infinity]
        by blast
      have source_disj: "range_tree S a (Fin b)  X = {}"
        using range_tree_disjoint_range_tree_chain_tail
          [OF a_le_b mono_tail, of S Infinity] X_subset by blast
      have out_disj:
        "outgoing_edges (range_tree S a (Fin b))  outgoing_edges X = {}"
        by (rule outgoing_edges_disjoint_if_sources_disjoint[OF source_disj])
      have head_subset: "?head  outgoing_edges (range_tree S a (Fin b))"
        by (rule outgoing_edges_range_subset_outgoing_edges)
      show "?head  Y = {}"
        using head_subset YX out_disj by blast
    qed
    have tail_disj: "pairwise_disjoint_list ?tail"
      by (rule Cons.IH[OF mono_tail])
    show ?thesis
      unfolding Cons by (simp add: head_tail_disj tail_disj)
  qed
qed

lemma sum_card_range_tree_child_direct_edge_range_list_le_edge_count:
  assumes mono: "nondecreasing_from a bs"
  shows "sum_list
    (map card (range_tree_child_direct_edge_range_list S B a betas bs))
     edge_count"
proof -
  let ?Xs = "range_tree_child_direct_edge_range_list S B a betas bs"
  have disj: "pairwise_disjoint_list ?Xs"
    by (rule pairwise_disjoint_list_range_tree_child_direct_edge_range_list
      [OF mono])
  have finite_sets: "X. X  set ?Xs  finite X"
    using range_tree_child_direct_edge_range_list_member_subset_E
    by (meson finite_E finite_subset)
  have card_union:
    "card ((set ?Xs)) = sum_list (map card ?Xs)"
    by (rule card_Union_pairwise_disjoint_list[OF disj finite_sets])
  have union_subset: "(set ?Xs)  E"
  proof
    fix e
    assume "e  (set ?Xs)"
    then obtain X where X: "X  set ?Xs" and eX: "e  X"
      by blast
    show "e  E"
      using range_tree_child_direct_edge_range_list_member_subset_E[OF X] eX
      by blast
  qed
  have "sum_list (map card ?Xs) = card ((set ?Xs))"
    using card_union by simp
  also have "  edge_count"
    unfolding edge_count_def by (rule card_mono[OF finite_E union_subset])
  finally show ?thesis .
qed

lemma sum_card_range_tree_child_direct_edge_range_list_le_outgoing_edges_chain:
  assumes mono: "nondecreasing_from a bs"
  shows "sum_list
    (map card (range_tree_child_direct_edge_range_list S B' a betas bs))
     card (outgoing_edges (range_tree_chain S a bs B))"
proof -
  let ?Rs = "range_tree_child_direct_edge_range_list S B' a betas bs"
  let ?Xs = "range_tree_child_list S a bs"
  have disj: "pairwise_disjoint_list ?Rs"
    by (rule pairwise_disjoint_list_range_tree_child_direct_edge_range_list
      [OF mono])
  have finite_sets: "R. R  set ?Rs  finite R"
    using range_tree_child_direct_edge_range_list_member_subset_E
    by (meson finite_E finite_subset)
  have card_union:
    "card ((set ?Rs)) = sum_list (map card ?Rs)"
    by (rule card_Union_pairwise_disjoint_list[OF disj finite_sets])
  have union_subset_children: "(set ?Rs)  outgoing_edges ((set ?Xs))"
  proof
    fix e
    assume "e  (set ?Rs)"
    then obtain R where R: "R  set ?Rs" and eR: "e  R"
      by blast
    obtain X where X: "X  set ?Xs" and R_subset: "R  outgoing_edges X"
      using range_tree_child_direct_edge_range_list_subset_outgoing_sources[OF R]
      by blast
    show "e  outgoing_edges ((set ?Xs))"
      using eR R_subset X unfolding outgoing_edges_def by blast
  qed
  have children_subset: "(set ?Xs)  range_tree_chain S a bs B"
    by (rule Union_range_tree_child_list_subset_chain)
  have union_subset:
    "(set ?Rs)  outgoing_edges (range_tree_chain S a bs B)"
    using union_subset_children outgoing_edges_mono[OF children_subset] by blast
  have "sum_list (map card ?Rs) = card ((set ?Rs))"
    using card_union by simp
  also have "  card (outgoing_edges (range_tree_chain S a bs B))"
    by (rule card_mono) (simp_all add: union_subset)
  finally show ?thesis .
qed

lemma range_tree_nonneg_edge_value:
  assumes edge: "(u, v)  E"
    and u_range: "u  range_tree S a B"
  shows "0  dist s u + w u v"
proof -
  have reach_u: "reachable s u"
    using u_range unfolding range_tree_def tree_set_def tree_path_def by blast
  have "0  dist s u"
    by (rule dist_nonneg[OF reach_u])
  moreover have "0  w u v"
    by (rule nonneg_weight[OF edge])
  ultimately show ?thesis
    by linarith
qed

lemma sum_card_range_tree_child_zero_direct_edge_ranges_le_full:
  assumes bounds: "bounds_le B betas"
  shows
    "sum_list (map card (range_tree_child_zero_edge_range_list S a betas bs)) +
     sum_list
       (map card (range_tree_child_direct_edge_range_list S B a betas bs))
      sum_list (map card (range_tree_child_full_edge_range_list S B a bs))"
  using bounds
proof (induction betas arbitrary: a bs)
  case Nil
  then show ?case
    by (cases bs) simp_all
next
  case (Cons beta betas)
  show ?case
  proof (cases bs)
    case Nil
    then show ?thesis
      by simp
  next
    case (Cons b bs')
    have beta_le_B: "bound_le (Fin beta) B"
      using Cons.prems by simp
    have bounds_tail: "bounds_le B betas"
      using Cons.prems by simp
    have head:
      "card (outgoing_edges_range (range_tree S a (Fin b)) 0 (Fin beta)) +
       card (outgoing_edges_range (range_tree S a (Fin b)) beta B)
        card (outgoing_edges_range (range_tree S a (Fin b)) 0 B)"
      by (rule card_outgoing_edges_range_zero_lower_direct_split_le
        [OF beta_le_B range_tree_nonneg_edge_value])
    have tail:
      "sum_list (map card (range_tree_child_zero_edge_range_list S b betas bs')) +
       sum_list
         (map card (range_tree_child_direct_edge_range_list S B b betas bs'))
        sum_list (map card
         (range_tree_child_full_edge_range_list S B b bs'))"
      by (rule Cons.IH[OF bounds_tail])
    show ?thesis
      unfolding Cons by simp (use head tail in linarith)
  qed
qed

lemma range_tree_child_full_edge_range_list_eq_map:
  "range_tree_child_full_edge_range_list S B a bs =
    map (λU. outgoing_edges_range U 0 B) (range_tree_child_list S a bs)"
  by (induction bs arbitrary: a) simp_all

lemma sum_card_range_tree_child_full_edge_range_list_le_outgoing_edges_range_chain:
  assumes mono: "nondecreasing_from a bs"
  shows "sum_list (map card (range_tree_child_full_edge_range_list S B a bs))
     card (outgoing_edges_range (range_tree_chain S a bs C) 0 B)"
proof -
  let ?Xs = "range_tree_child_list S a bs"
  let ?Rs = "map (λU. outgoing_edges_range U 0 B) ?Xs"
  have disj_children: "pairwise_disjoint_list ?Xs"
    using mono
  proof (induction bs arbitrary: a)
    case Nil
    then show ?case by simp
  next
    case (Cons b bs)
    have a_le_b: "a  b"
      using Cons.prems by simp
    have mono_tail: "nondecreasing_from b bs"
      using Cons.prems by simp
    have head_tail_disj:
      "Y. Y  set (range_tree_child_list S b bs) 
        range_tree S a (Fin b)  Y = {}"
    proof -
      fix Y
      assume Y: "Y  set (range_tree_child_list S b bs)"
      have "Y  range_tree_chain S b bs Infinity"
        using Y Union_range_tree_child_list_subset_chain[of S b bs Infinity]
        by blast
      moreover have
        "range_tree S a (Fin b)  range_tree_chain S b bs Infinity = {}"
        using range_tree_disjoint_range_tree_chain_tail[OF a_le_b mono_tail] .
      ultimately show "range_tree S a (Fin b)  Y = {}"
        by blast
    qed
    show ?case
      using head_tail_disj Cons.IH[OF mono_tail] by simp
  qed
  have disj: "pairwise_disjoint_list ?Rs"
    by (rule pairwise_disjoint_list_outgoing_edges_range[OF disj_children])
  have finite_sets: "R. R  set ?Rs  finite R"
    by auto
  have card_union:
    "card ((set ?Rs)) = sum_list (map card ?Rs)"
    by (rule card_Union_pairwise_disjoint_list[OF disj finite_sets])
  have children_subset: "(set ?Xs)  range_tree_chain S a bs C"
    by (rule Union_range_tree_child_list_subset_chain)
  have union_subset:
    "(set ?Rs)  outgoing_edges_range (range_tree_chain S a bs C) 0 B"
  proof
    fix e
    assume "e  (set ?Rs)"
    then obtain X where X: "X  set ?Xs"
      and eX: "e  outgoing_edges_range X 0 B"
      by auto
    have X_subset: "X  range_tree_chain S a bs C"
      using X children_subset by blast
    show "e  outgoing_edges_range (range_tree_chain S a bs C) 0 B"
      using outgoing_edges_range_mono_sources[OF X_subset] eX by blast
  qed
  have "sum_list (map card (range_tree_child_full_edge_range_list S B a bs)) =
      sum_list (map card ?Rs)"
    by (simp add: range_tree_child_full_edge_range_list_eq_map)
  also have " = card ((set ?Rs))"
    using card_union by simp
  also have " 
      card (outgoing_edges_range (range_tree_chain S a bs C) 0 B)"
    by (rule card_mono[OF finite_outgoing_edges_range union_subset])
  finally show ?thesis .
qed

lemma sum_card_range_tree_child_zero_direct_edge_ranges_le_outgoing_edges_range_chain:
  assumes mono: "nondecreasing_from a bs"
    and bounds: "bounds_le B betas"
  shows
    "sum_list (map card (range_tree_child_zero_edge_range_list S a betas bs)) +
     sum_list
       (map card (range_tree_child_direct_edge_range_list S B a betas bs))
      card (outgoing_edges_range (range_tree_chain S a bs C) 0 B)"
proof -
  have split:
    "sum_list (map card (range_tree_child_zero_edge_range_list S a betas bs)) +
     sum_list
       (map card (range_tree_child_direct_edge_range_list S B a betas bs))
      sum_list (map card (range_tree_child_full_edge_range_list S B a bs))"
    by (rule sum_card_range_tree_child_zero_direct_edge_ranges_le_full
      [OF bounds])
  have full:
    "sum_list (map card (range_tree_child_full_edge_range_list S B a bs))
       card (outgoing_edges_range (range_tree_chain S a bs C) 0 B)"
    by (rule sum_card_range_tree_child_full_edge_range_list_le_outgoing_edges_range_chain
      [OF mono])
  show ?thesis
    using split full by linarith
qed

lemma weighted_sum_child_lower_direct_edge_ranges_le:
  assumes mono: "nondecreasing_from a bs"
  shows
    "h * sum_list (map card (range_tree_child_edge_range_list S a betas bs)) +
     t * sum_list
       (map card (range_tree_child_direct_edge_range_list S B a betas bs))
      (h + t) * edge_count"
proof -
  have lower:
    "sum_list (map card (range_tree_child_edge_range_list S a betas bs))
       edge_count"
    by (rule sum_card_range_tree_child_edge_range_list_le_edge_count[OF mono])
  have direct:
    "sum_list
      (map card (range_tree_child_direct_edge_range_list S B a betas bs))
       edge_count"
    by (rule sum_card_range_tree_child_direct_edge_range_list_le_edge_count
      [OF mono])
  have lower_w:
    "h * sum_list (map card (range_tree_child_edge_range_list S a betas bs))
       h * edge_count"
    using lower by simp
  have direct_w:
    "t * sum_list
       (map card (range_tree_child_direct_edge_range_list S B a betas bs))
       t * edge_count"
    using direct by simp
  have "h * sum_list (map card (range_tree_child_edge_range_list S a betas bs)) +
      t * sum_list
        (map card (range_tree_child_direct_edge_range_list S B a betas bs))
       h * edge_count + t * edge_count"
    using lower_w direct_w by linarith
  also have " = (h + t) * edge_count"
    by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma weighted_sum_child_lower_direct_edge_ranges_le_outgoing_edges_chain:
  assumes mono: "nondecreasing_from a bs"
  shows
    "h * sum_list (map card (range_tree_child_edge_range_list S a betas bs)) +
     t * sum_list
       (map card (range_tree_child_direct_edge_range_list S B' a betas bs))
      (h + t) * card (outgoing_edges (range_tree_chain S a bs B))"
proof -
  have lower:
    "sum_list (map card (range_tree_child_edge_range_list S a betas bs))
       card (outgoing_edges (range_tree_chain S a bs B))"
    by (rule sum_card_range_tree_child_edge_range_list_le_outgoing_edges_chain
      [OF mono])
  have direct:
    "sum_list
      (map card (range_tree_child_direct_edge_range_list S B' a betas bs))
       card (outgoing_edges (range_tree_chain S a bs B))"
    by (rule sum_card_range_tree_child_direct_edge_range_list_le_outgoing_edges_chain
      [OF mono])
  have lower_w:
    "h * sum_list (map card (range_tree_child_edge_range_list S a betas bs))
       h * card (outgoing_edges (range_tree_chain S a bs B))"
    using lower by simp
  have direct_w:
    "t * sum_list
       (map card (range_tree_child_direct_edge_range_list S B' a betas bs))
       t * card (outgoing_edges (range_tree_chain S a bs B))"
    using direct by simp
  have "h * sum_list (map card (range_tree_child_edge_range_list S a betas bs)) +
      t * sum_list
        (map card (range_tree_child_direct_edge_range_list S B' a betas bs))
       h * card (outgoing_edges (range_tree_chain S a bs B)) +
        t * card (outgoing_edges (range_tree_chain S a bs B))"
    using lower_w direct_w by linarith
  also have " = (h + t) * card (outgoing_edges (range_tree_chain S a bs B))"
    by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma sum_card_outgoing_edges_le_degree:
  assumes degree: "edge_outdegree_le Δ"
    and sets: "X. X  set Xs  X  V"
  shows "sum_list (map (λX. card (outgoing_edges X)) Xs) 
    Δ * sum_list (map card Xs)"
  using sets
proof (induction Xs)
  case Nil
  then show ?case by simp
next
  case (Cons X Xs)
  have X_subset: "X  V"
    using Cons.prems by simp
  have X_bound: "card (outgoing_edges X)  Δ * card X"
    by (rule card_outgoing_edges_le[OF X_subset degree])
  have tail_bound:
    "sum_list (map (λX. card (outgoing_edges X)) Xs) 
      Δ * sum_list (map card Xs)"
    using Cons.IH Cons.prems by simp
  have "card (outgoing_edges X) +
      sum_list (map (λX. card (outgoing_edges X)) Xs) 
      Δ * card X + Δ * sum_list (map card Xs)"
    using X_bound tail_bound by simp
  also have " = Δ * sum_list (map card (X # Xs))"
    by (simp add: algebra_simps)
  finally show ?case
    by simp
qed

lemma card_outgoing_edges_Union_list_eq_sum:
  assumes disj: "pairwise_disjoint_list Xs"
  shows "card (outgoing_edges ((set Xs))) =
    sum_list (map (λX. card (outgoing_edges X)) Xs)"
proof -
  have disj_edges: "pairwise_disjoint_list (map outgoing_edges Xs)"
    using pairwise_disjoint_list_outgoing_edges[OF disj] .
  have finite_edges: "X. X  set (map outgoing_edges Xs)  finite X"
    by auto
  have "card (outgoing_edges ((set Xs))) =
      card ((set (map outgoing_edges Xs)))"
    using outgoing_edges_Union_list[of Xs] by simp
  also have " = sum_list (map (λY. card Y) (map outgoing_edges Xs))"
    by (rule card_Union_pairwise_disjoint_list[OF disj_edges finite_edges])
  also have " = sum_list (map (λX. card (outgoing_edges X)) Xs)"
    by (induction Xs) simp_all
  finally show ?thesis .
qed

lemma range_tree_chain_list_pairwise_disjoint:
  assumes mono: "nondecreasing_from a bs"
  shows "pairwise_disjoint_list (range_tree_chain_list S a bs B)"
  using assms
proof (induction bs arbitrary: a)
  case Nil
  then show ?case by simp
next
  case (Cons b bs)
  have a_le_b: "a  b"
    using Cons.prems by simp
  have mono_tail: "nondecreasing_from b bs"
    using Cons.prems by simp
  have head_tail_disj:
    "Y. Y  set (range_tree_chain_list S b bs B) 
      range_tree S a (Fin b)  Y = {}"
  proof -
    fix Y
    assume Y: "Y  set (range_tree_chain_list S b bs B)"
    have "Y  range_tree_chain S b bs B"
      using Y Union_range_tree_chain_list[of S b bs B] by blast
    moreover have
      "range_tree S a (Fin b)  range_tree_chain S b bs B = {}"
      using range_tree_disjoint_range_tree_chain_tail[OF a_le_b mono_tail] .
    ultimately show "range_tree S a (Fin b)  Y = {}"
      by blast
  qed
  show ?case
    using head_tail_disj Cons.IH[OF mono_tail] by simp
qed

lemma range_tree_child_list_pairwise_disjoint:
  assumes mono: "nondecreasing_from a bs"
  shows "pairwise_disjoint_list (range_tree_child_list S a bs)"
  using assms
proof (induction bs arbitrary: a)
  case Nil
  then show ?case by simp
next
  case (Cons b bs)
  have a_le_b: "a  b"
    using Cons.prems by simp
  have mono_tail: "nondecreasing_from b bs"
    using Cons.prems by simp
  have head_tail_disj:
    "Y. Y  set (range_tree_child_list S b bs) 
      range_tree S a (Fin b)  Y = {}"
  proof -
    fix Y
    assume Y: "Y  set (range_tree_child_list S b bs)"
    have "Y  range_tree_chain S b bs Infinity"
      using Y Union_range_tree_child_list_subset_chain[of S b bs Infinity] by blast
    moreover have
      "range_tree S a (Fin b)  range_tree_chain S b bs Infinity = {}"
      using range_tree_disjoint_range_tree_chain_tail[OF a_le_b mono_tail] .
    ultimately show "range_tree S a (Fin b)  Y = {}"
      by blast
  qed
  show ?case
    using head_tail_disj Cons.IH[OF mono_tail] by simp
qed

theorem card_range_tree_child_list_le_chain:
  assumes mono: "nondecreasing_from a bs"
  shows "sum_list (map card (range_tree_child_list S a bs)) 
    card (range_tree_chain S a bs B)"
proof -
  let ?Xs = "range_tree_child_list S a bs"
  have disj: "pairwise_disjoint_list ?Xs"
    by (rule range_tree_child_list_pairwise_disjoint[OF mono])
  have finite_sets: "X. X  set ?Xs  finite X"
    using range_tree_child_list_set_subset_chain_list
      finite_range_tree_chain_list_sets by blast
  have card_union:
    "card ((set ?Xs)) = sum_list (map card ?Xs)"
    by (rule card_Union_pairwise_disjoint_list[OF disj finite_sets])
  have subset: "(set ?Xs)  range_tree_chain S a bs B"
    by (rule Union_range_tree_child_list_subset_chain)
  have "sum_list (map card ?Xs) = card ((set ?Xs))"
    using card_union by simp
  also have "  card (range_tree_chain S a bs B)"
    by (rule card_mono) (simp_all add: subset)
  finally show ?thesis .
qed

lemma sum_card_list_all2_le:
  assumes "list_all2 (λX Y. card X  card Y) Xs Ys"
  shows "sum_list (map card Xs)  sum_list (map card Ys)"
  using assms
proof (induction rule: list_all2_induct)
  case Nil
  then show ?case by simp
next
  case (Cons X Y Xs Ys)
  then show ?case by simp
qed

lemma sum_card_dominated_by_disjoint_union:
  assumes disj: "pairwise_disjoint_list Ys"
    and finite_sets: "Y. Y  set Ys  finite Y"
    and dominated: "list_all2 (λX Y. card X  card Y) Xs Ys"
  shows "sum_list (map card Xs)  card ((set Ys))"
proof -
  have "sum_list (map card Xs)  sum_list (map card Ys)"
    by (rule sum_card_list_all2_le[OF dominated])
  also have " = card ((set Ys))"
    using card_Union_pairwise_disjoint_list[OF disj finite_sets] by simp
  finally show ?thesis .
qed

theorem sum_card_dominated_by_range_tree_child_list_le_chain:
  assumes mono: "nondecreasing_from a bs"
    and dominated:
      "list_all2 (λX R. card X  card R) Xs
        (range_tree_child_list S a bs)"
  shows "sum_list (map card Xs)  card (range_tree_chain S a bs B)"
proof -
  have "sum_list (map card Xs) 
      sum_list (map card (range_tree_child_list S a bs))"
    by (rule sum_card_list_all2_le[OF dominated])
  also have "  card (range_tree_chain S a bs B)"
    by (rule card_range_tree_child_list_le_chain[OF mono])
  finally show ?thesis .
qed

theorem card_outgoing_edges_range_tree_chain_eq_sum_list:
  assumes mono: "nondecreasing_from a bs"
  shows "card (outgoing_edges (range_tree_chain S a bs B)) =
    sum_list
      (map (λX. card (outgoing_edges X)) (range_tree_chain_list S a bs B))"
proof -
  have disj: "pairwise_disjoint_list (range_tree_chain_list S a bs B)"
    using range_tree_chain_list_pairwise_disjoint[OF mono] .
  have "card (outgoing_edges (range_tree_chain S a bs B)) =
      card (outgoing_edges ((set (range_tree_chain_list S a bs B))))"
    using Union_range_tree_chain_list[of S a bs B] by simp
  also have " =
      sum_list
        (map (λX. card (outgoing_edges X)) (range_tree_chain_list S a bs B))"
    by (rule card_outgoing_edges_Union_list_eq_sum[OF disj])
  finally show ?thesis .
qed

theorem card_outgoing_edges_range_tree_child_list_le_chain:
  assumes mono: "nondecreasing_from a bs"
  shows "sum_list
      (map (λX. card (outgoing_edges X)) (range_tree_child_list S a bs)) 
    card (outgoing_edges (range_tree_chain S a bs B))"
proof -
  let ?Xs = "range_tree_child_list S a bs"
  have disj: "pairwise_disjoint_list ?Xs"
    by (rule range_tree_child_list_pairwise_disjoint[OF mono])
  have card_union:
    "card (outgoing_edges ((set ?Xs))) =
      sum_list (map (λX. card (outgoing_edges X)) ?Xs)"
    by (rule card_outgoing_edges_Union_list_eq_sum[OF disj])
  have subset: "(set ?Xs)  range_tree_chain S a bs B"
    by (rule Union_range_tree_child_list_subset_chain)
  have outgoing_subset:
    "outgoing_edges ((set ?Xs)) 
      outgoing_edges (range_tree_chain S a bs B)"
    by (rule outgoing_edges_mono[OF subset])
  have "sum_list (map (λX. card (outgoing_edges X)) ?Xs) =
      card (outgoing_edges ((set ?Xs)))"
    using card_union by simp
  also have "  card (outgoing_edges (range_tree_chain S a bs B))"
    by (rule card_mono) (simp_all add: outgoing_subset)
  finally show ?thesis .
qed

lemma sum_outgoing_card_list_all2_le:
  assumes "list_all2
    (λX Y. card (outgoing_edges X)  card (outgoing_edges Y)) Xs Ys"
  shows "sum_list (map (λX. card (outgoing_edges X)) Xs) 
    sum_list (map (λY. card (outgoing_edges Y)) Ys)"
  using assms
proof (induction rule: list_all2_induct)
  case Nil
  then show ?case by simp
next
  case (Cons X Y Xs Ys)
  then show ?case by simp
qed

theorem sum_outgoing_dominated_by_range_tree_child_list_le_chain:
  assumes mono: "nondecreasing_from a bs"
    and dominated:
      "list_all2
        (λX R. card (outgoing_edges X)  card (outgoing_edges R)) Xs
        (range_tree_child_list S a bs)"
  shows "sum_list (map (λX. card (outgoing_edges X)) Xs) 
    card (outgoing_edges (range_tree_chain S a bs B))"
proof -
  have "sum_list (map (λX. card (outgoing_edges X)) Xs) 
      sum_list
        (map (λR. card (outgoing_edges R)) (range_tree_child_list S a bs))"
    by (rule sum_outgoing_card_list_all2_le[OF dominated])
  also have "  card (outgoing_edges (range_tree_chain S a bs B))"
    by (rule card_outgoing_edges_range_tree_child_list_le_chain[OF mono])
  finally show ?thesis .
qed

lemma edge_costs_le_range_tree_chain_outgoing:
  assumes mono: "nondecreasing_from a bs"
    and costs: "list_all2 (λc X. c  C * card (outgoing_edges X)) costs
      (range_tree_chain_list S a bs B)"
  shows "sum_list costs  C * card (outgoing_edges (range_tree_chain S a bs B))"
proof -
  have "sum_list costs 
      sum_list
        (map (λX. C * card (outgoing_edges X))
          (range_tree_chain_list S a bs B))"
    using costs
  proof (induction rule: list_all2_induct)
    case Nil
    then show ?case by simp
  next
    case (Cons c X costs Xs)
    then show ?case by simp
  qed
  also have " =
      C * sum_list
        (map (λX. card (outgoing_edges X)) (range_tree_chain_list S a bs B))"
  proof -
    define Xs where "Xs = range_tree_chain_list S a bs B"
    have "sum_list (map (λX. C * card (outgoing_edges X)) Xs) =
        C * sum_list (map (λX. card (outgoing_edges X)) Xs)"
      by (induction Xs) (simp_all add: algebra_simps)
    then show ?thesis
      unfolding Xs_def .
  qed
  also have " = C * card (outgoing_edges (range_tree_chain S a bs B))"
    using card_outgoing_edges_range_tree_chain_eq_sum_list[OF mono, of S B] by simp
  finally show ?thesis .
qed

text ‹
  These two cost shapes are the local language of the runtime proof.  The level
  bound level_range_cost_bound› is the simple recurrence form: a vertex term
  scaled by the recursion level and an outgoing-edge term scaled by the range
  coefficient.  The amortised bound bmssp_amortized_cost_bound› adds the
  extra direct-edge range term used by the bucketed partition, where direct
  inserts have their own cost story.

  The summation lemmas below say that if every recursive child call satisfies
  one of these bounds on its own range slice, then the sum of child costs can be
  rewritten as sums over the corresponding child range lists.  Later theories
  collapse those sums using the disjointness lemmas above.
›

definition level_range_cost_bound where
  "level_range_cost_bound A R l X =
     A * l * card X + R * card (outgoing_edges X)"

definition bmssp_amortized_cost_bound where
  "bmssp_amortized_cost_bound A R h t q L B X =
     A * L * card X + (R + q * h) * card (outgoing_edges X) +
     t * card (outgoing_edges_range X 0 B)"

lemma sum_bmssp_amortized_child_bounds_le:
  assumes bounds:
    "list_all2 (λc_child UB. case UB of (U_child, B_child) 
      c_child 
        bmssp_amortized_cost_bound A R h t q L B_child U_child)
      child_costs (range_tree_child_bound_pair_list S a betas bs)"
  shows "sum_list child_costs 
    A * L * sum_list (map card (range_tree_child_list S a bs)) +
    (R + q * h) *
      sum_list (map (λU. card (outgoing_edges U))
        (range_tree_child_list S a bs)) +
    t * sum_list
      (map card (range_tree_child_zero_edge_range_list S a betas bs))"
  using bounds
proof (induction betas arbitrary: a bs child_costs)
  case Nil
  then have costs_nil: "child_costs = []"
    by (cases bs; cases child_costs) auto
  show ?case
    using costs_nil by simp
next
  case (Cons beta betas)
  note outer_prems = Cons.prems
  note outer_IH = Cons.IH
  show ?case
  proof (cases bs)
    case Nil
    then have costs_nil: "child_costs = []"
      using outer_prems by (cases child_costs) auto
    show ?thesis
      using Nil costs_nil by simp
  next
    case bs_Cons: (Cons b bs')
    show ?thesis
    proof (cases child_costs)
      case Nil
      then show ?thesis
        by simp
    next
      case costs_Cons: (Cons c_child child_costs_tail)
      have head:
        "c_child 
          bmssp_amortized_cost_bound A R h t q L (Fin beta)
            (range_tree S a (Fin b))"
        using outer_prems unfolding bs_Cons costs_Cons by simp
      have tail_bounds:
        "list_all2 (λc_child UB. case UB of (U_child, B_child) 
          c_child 
            bmssp_amortized_cost_bound A R h t q L B_child U_child)
          child_costs_tail (range_tree_child_bound_pair_list S b betas bs')"
        using outer_prems unfolding bs_Cons costs_Cons by simp
      have tail:
        "sum_list child_costs_tail 
          A * L * sum_list (map card (range_tree_child_list S b bs')) +
          (R + q * h) *
            sum_list (map (λU. card (outgoing_edges U))
              (range_tree_child_list S b bs')) +
          t * sum_list
            (map card (range_tree_child_zero_edge_range_list S b betas bs'))"
        by (rule outer_IH[OF tail_bounds])
      have head_unfolded:
        "c_child 
          A * L * card (range_tree S a (Fin b)) +
          (R + q * h) * card (outgoing_edges (range_tree S a (Fin b))) +
          t * card (outgoing_edges_range (range_tree S a (Fin b)) 0
            (Fin beta))"
        using head unfolding bmssp_amortized_cost_bound_def .
      show ?thesis
        unfolding bs_Cons costs_Cons
        using head_unfolded tail by (simp add: algebra_simps)
    qed
  qed
qed

lemma child_costs_le_level_range_chain_bound:
  assumes mono: "nondecreasing_from a bs"
    and costs: "list_all2 (λc X. c  level_range_cost_bound A R l X) costs
      (range_tree_chain_list S a bs B)"
  shows "sum_list costs 
    A * l * card (range_tree_chain S a bs B) +
    R * card (outgoing_edges (range_tree_chain S a bs B))"
proof -
  define Xs where "Xs = range_tree_chain_list S a bs B"
  have cost_sum:
    "sum_list costs  sum_list (map (level_range_cost_bound A R l) Xs)"
    using costs unfolding Xs_def
  proof (induction rule: list_all2_induct)
    case Nil
    then show ?case by simp
  next
    case (Cons c X costs Xs)
    then show ?case
      unfolding level_range_cost_bound_def by simp
  qed
  have split_sum:
    "sum_list (map (level_range_cost_bound A R l) Xs) =
      A * l * sum_list (map (λX. card X) Xs) +
      R * sum_list (map (λX. card (outgoing_edges X)) Xs)"
  proof (induction Xs)
    case Nil
    then show ?case
      unfolding level_range_cost_bound_def by simp
  next
    case (Cons X Xs)
    then show ?case
      unfolding level_range_cost_bound_def by (simp add: algebra_simps)
  qed
  have card_sum:
    "sum_list (map (λX. card X) Xs) = card (range_tree_chain S a bs B)"
    using card_range_tree_chain_eq_sum_list[OF mono, of S B]
    unfolding Xs_def by simp
  have edge_sum:
    "sum_list (map (λX. card (outgoing_edges X)) Xs) =
      card (outgoing_edges (range_tree_chain S a bs B))"
    using card_outgoing_edges_range_tree_chain_eq_sum_list[OF mono, of S B]
    unfolding Xs_def by simp
  show ?thesis
    using cost_sum split_sum card_sum edge_sum by simp
qed

theorem concrete_partition_loop_trace_card_decomposition:
  assumes trace: "concrete_partition_loop_trace P B a bs d' B' Us U"
  shows "card U =
    card (bound_tree P (Fin a)) + card (range_tree_chain P a bs B')"
proof -
  have mono: "nondecreasing_from a bs"
    and bounds: "bounds_le B' (a # bs)"
    using trace unfolding concrete_partition_loop_trace_def by blast+
  have post: "bmssp_post_full d P B d' B' U"
    using concrete_partition_loop_trace_post[OF trace] .
  then have U_eq: "U = bound_tree P B'"
    unfolding bmssp_post_full_def by blast
  show ?thesis
    using card_bound_tree_range_chain_union[OF mono bounds, of P] U_eq by simp
qed

theorem concrete_partition_loop_trace_range_card_le:
  assumes trace: "concrete_partition_loop_trace P B a bs d' B' Us U"
  shows "card (range_tree_chain P a bs B')  card U"
  using concrete_partition_loop_trace_card_decomposition[OF trace]
  by simp

definition concrete_capped_step_core_cost where
  "concrete_capped_step_core_cost k cap d S B child_costs =
     fp_iter_capped_scan_cost k cap d S S B + sum_list child_costs"

definition base_case_scan_cost where
  "base_case_scan_cost Δ k x B = card (outgoing_edges (base_case_vertices k x B))"

theorem base_case_scan_cost_le:
  assumes degree: "edge_outdegree_le Δ"
  shows "base_case_scan_cost Δ k x B  Δ * k"
proof -
  have order_subset: "set (base_case_order x B)  V"
    using base_case_order_set[of x B]
    unfolding bound_tree_def by auto
  have vertices_subset: "base_case_vertices k x B  V"
  proof (cases "length (base_case_order x B)  k")
    case True
    then show ?thesis
      using order_subset unfolding base_case_vertices_def by (simp add: Let_def)
  next
    case False
    then show ?thesis
      using order_subset unfolding base_case_vertices_def
      by (auto simp: Let_def dest: in_set_takeD)
  qed
  have "base_case_scan_cost Δ k x B 
      Δ * card (base_case_vertices k x B)"
    unfolding base_case_scan_cost_def
    by (rule card_outgoing_edges_le[OF vertices_subset degree])
  also have "  Δ * k"
    using card_base_case_vertices_le[of k x B] by simp
  finally show ?thesis .
qed

theorem base_case_scan_cost_le_outgoing_edges:
  "base_case_scan_cost Δ k x B 
    card (outgoing_edges (base_case_vertices k x B))"
  unfolding base_case_scan_cost_def
  by simp

theorem base_case_scan_cost_le_edge_count:
  "base_case_scan_cost edge_count k x B  edge_count * k"
  by (rule base_case_scan_cost_le[OF edge_outdegree_le_edge_count])

definition concrete_capped_step_accounted_cost where
  "concrete_capped_step_accounted_cost k cap d S B child_costs edge_costs =
     fp_iter_capped_scan_cost k cap d S S B +
     sum_list child_costs + sum_list edge_costs"

theorem concrete_capped_step_core_cost_le_range:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and trace: "concrete_partition_loop_trace
      (find_pivots_pivots_capped k cap d S B) B a bs d' B' Us U_loop"
    and child_costs: "list_all2 (λc X. c  C * card X) costs
      (range_tree_chain_list (find_pivots_pivots_capped k cap d S B) a bs B')"
  shows "concrete_capped_step_core_cost k cap d S B costs 
    k * Δ * cap +
      C * card (range_tree_chain
        (find_pivots_pivots_capped k cap d S B) a bs B')"
proof -
  let ?P = "find_pivots_pivots_capped k cap d S B"
  have mono: "nondecreasing_from a bs"
    using trace unfolding concrete_partition_loop_trace_def by blast
  have scan:
    "fp_iter_capped_scan_cost k cap d S S B  k * Δ * cap"
    using find_pivots_capped_scan_cost_le[OF S_subset degree S_cap] .
  have child:
    "sum_list costs  C * card (range_tree_chain ?P a bs B')"
    using child_costs_le_range_tree_chain_card[OF mono child_costs] .
  show ?thesis
    unfolding concrete_capped_step_core_cost_def using scan child by linarith
qed

theorem concrete_capped_step_core_cost_le_level_chain:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and trace: "concrete_partition_loop_trace
      (find_pivots_pivots_capped k cap d S B) B a bs d' B' Us U_loop"
    and child_costs: "list_all2 (λc X. c  level_range_cost_bound A R l X) costs
      (range_tree_chain_list (find_pivots_pivots_capped k cap d S B) a bs B')"
  shows "concrete_capped_step_core_cost k cap d S B costs 
    k * Δ * cap +
    A * l *
      card (range_tree_chain (find_pivots_pivots_capped k cap d S B) a bs B') +
    R * card (outgoing_edges
      (range_tree_chain (find_pivots_pivots_capped k cap d S B) a bs B'))"
proof -
  let ?P = "find_pivots_pivots_capped k cap d S B"
  have mono: "nondecreasing_from a bs"
    using trace unfolding concrete_partition_loop_trace_def by blast
  have scan:
    "fp_iter_capped_scan_cost k cap d S S B  k * Δ * cap"
    using find_pivots_capped_scan_cost_le[OF S_subset degree S_cap] .
  have child:
    "sum_list costs 
      A * l * card (range_tree_chain ?P a bs B') +
      R * card (outgoing_edges (range_tree_chain ?P a bs B'))"
    using child_costs_le_level_range_chain_bound[OF mono child_costs] .
  show ?thesis
    unfolding concrete_capped_step_core_cost_def using scan child by linarith
qed

theorem concrete_capped_step_core_cost_closes_level_bound:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and trace: "concrete_partition_loop_trace
      (find_pivots_pivots_capped k cap d S B) B a bs d' B' Us U_loop"
    and child_costs: "list_all2 (λc X. c  level_range_cost_bound A R l X) costs
      (range_tree_chain_list (find_pivots_pivots_capped k cap d S B) a bs B')"
    and scan_budget: "k * Δ * cap  A * card U_loop"
  shows "concrete_capped_step_core_cost k cap d S B costs 
    level_range_cost_bound A R (Suc l) U_loop"
proof -
  let ?P = "find_pivots_pivots_capped k cap d S B"
  have children:
    "list_all2 (λU X. U = X  complete_on d' U) Us
      (range_tree_chain_list ?P a bs B')"
    using trace unfolding concrete_partition_loop_trace_def by blast
  have U_def: "U_loop = bound_tree ?P (Fin a)  (set Us)"
    using trace unfolding concrete_partition_loop_trace_def by blast
  have Union_eq:
    "(set Us) = (set (range_tree_chain_list ?P a bs B'))"
    using children by (induction rule: list_all2_induct) auto
  have U_eq_chain:
    "U_loop = bound_tree ?P (Fin a)  range_tree_chain ?P a bs B'"
    using U_def Union_eq Union_range_tree_chain_list[of ?P a bs B'] by simp
  have finite_U: "finite U_loop"
    using U_eq_chain by simp
  have chain_subset: "range_tree_chain ?P a bs B'  U_loop"
    using U_eq_chain by blast
  have card_chain_le: "card (range_tree_chain ?P a bs B')  card U_loop"
    by (rule card_mono[OF finite_U chain_subset])
  have outgoing_subset:
    "outgoing_edges (range_tree_chain ?P a bs B')  outgoing_edges U_loop"
    using outgoing_edges_mono[OF chain_subset] .
  have finite_out_U: "finite (outgoing_edges U_loop)"
    by simp
  have card_out_le:
    "card (outgoing_edges (range_tree_chain ?P a bs B')) 
      card (outgoing_edges U_loop)"
    by (rule card_mono[OF finite_out_U outgoing_subset])
  have step:
    "concrete_capped_step_core_cost k cap d S B costs 
      k * Δ * cap +
      A * l * card (range_tree_chain ?P a bs B') +
      R * card (outgoing_edges (range_tree_chain ?P a bs B'))"
    by (rule concrete_capped_step_core_cost_le_level_chain
      [OF S_subset degree S_cap trace child_costs])
  also have " 
      A * card U_loop + A * l * card U_loop +
      R * card (outgoing_edges U_loop)"
    using scan_budget card_chain_le card_out_le
    by (intro add_mono mult_left_mono) simp_all
  also have " = level_range_cost_bound A R (Suc l) U_loop"
    unfolding level_range_cost_bound_def by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma scan_budget_from_loop_card:
  assumes degree_factor: "Δ  A"
    and progress: "k * cap  card U_loop"
  shows "k * Δ * cap  A * card U_loop"
proof -
  have "k * Δ * cap  k * A * cap"
    using degree_factor by (intro mult_left_mono mult_right_mono) simp_all
  also have " = A * (k * cap)"
    by (simp add: algebra_simps)
  also have "  A * card U_loop"
    using progress by simp
  finally show ?thesis .
qed

lemma scan_insert_budget_from_loop_card:
  assumes degree_factor: "Δ  A"
    and insert_factor: "t  A * k"
    and progress: "k * cap  card U_loop"
  shows "k * Δ * cap + t * cap  2 * A * card U_loop"
proof -
  have scan: "k * Δ * cap  A * card U_loop"
    by (rule scan_budget_from_loop_card[OF degree_factor progress])
  have insert: "t * cap  A * card U_loop"
  proof -
    have "t * cap  (A * k) * cap"
      using insert_factor by simp
    also have " = A * (k * cap)"
      by (simp add: algebra_simps)
    also have "  A * card U_loop"
      using progress by simp
    finally show ?thesis .
  qed
  have "k * Δ * cap + t * cap  A * card U_loop + A * card U_loop"
    using scan insert by simp
  also have " = 2 * A * card U_loop"
    by simp
  finally show ?thesis .
qed

lemma loop_overhead_budget_from_progress:
  assumes factor: "Suc t  A"
    and progress: "M * q  card U"
  shows "(Suc t) * M * q  A * card U"
proof -
  have "(Suc t) * M * q  A * (M * q)"
  proof -
    have "(Suc t) * (M * q)  A * (M * q)"
      by (rule mult_right_mono[OF factor]) simp
    then show ?thesis
      by (simp add: algebra_simps)
  qed
  also have "  A * card U"
    using progress by simp
  finally show ?thesis .
qed

theorem concrete_capped_step_core_cost_closes_level_bound_from_progress:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and trace: "concrete_partition_loop_trace
      (find_pivots_pivots_capped k cap d S B) B a bs d' B' Us U_loop"
    and child_costs: "list_all2 (λc X. c  level_range_cost_bound A R l X) costs
      (range_tree_chain_list (find_pivots_pivots_capped k cap d S B) a bs B')"
    and degree_factor: "Δ  A"
    and progress: "k * cap  card U_loop"
  shows "concrete_capped_step_core_cost k cap d S B costs 
    level_range_cost_bound A R (Suc l) U_loop"
proof -
  have scan_budget: "k * Δ * cap  A * card U_loop"
    by (rule scan_budget_from_loop_card[OF degree_factor progress])
  show ?thesis
    by (rule concrete_capped_step_core_cost_closes_level_bound
      [OF S_subset degree S_cap trace child_costs scan_budget])
qed

theorem concrete_capped_step_core_cost_le_loop_vertices:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and trace: "concrete_partition_loop_trace
      (find_pivots_pivots_capped k cap d S B) B a bs d' B' Us U_loop"
    and child_costs: "list_all2 (λc X. c  C * card X) costs
      (range_tree_chain_list (find_pivots_pivots_capped k cap d S B) a bs B')"
  shows "concrete_capped_step_core_cost k cap d S B costs 
    k * Δ * cap + C * card U_loop"
proof -
  let ?P = "find_pivots_pivots_capped k cap d S B"
  have children:
    "list_all2 (λU X. U = X  complete_on d' U) Us
      (range_tree_chain_list ?P a bs B')"
    using trace unfolding concrete_partition_loop_trace_def by blast
  have U_def: "U_loop = bound_tree ?P (Fin a)  (set Us)"
    using trace unfolding concrete_partition_loop_trace_def by blast
  have Union_eq:
    "(set Us) = (set (range_tree_chain_list ?P a bs B'))"
    using children by (induction rule: list_all2_induct) auto
  have U_eq_chain:
    "U_loop = bound_tree ?P (Fin a)  range_tree_chain ?P a bs B'"
    using U_def Union_eq Union_range_tree_chain_list[of ?P a bs B'] by simp
  have finite_U: "finite U_loop"
    using U_eq_chain by simp
  have chain_subset: "range_tree_chain ?P a bs B'  U_loop"
    using U_eq_chain by blast
  have card_chain_le: "card (range_tree_chain ?P a bs B')  card U_loop"
    by (rule card_mono[OF finite_U chain_subset])
  have "concrete_capped_step_core_cost k cap d S B costs 
      k * Δ * cap + C * card (range_tree_chain ?P a bs B')"
    by (rule concrete_capped_step_core_cost_le_range
      [OF S_subset degree S_cap trace child_costs])
  also have "  k * Δ * cap + C * card U_loop"
    using card_chain_le by simp
  finally show ?thesis .
qed

theorem concrete_capped_step_accounted_cost_le:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and trace: "concrete_partition_loop_trace
      (find_pivots_pivots_capped k cap d S B) B a bs d' B' Us U_loop"
    and child_costs: "list_all2 (λc X. c  C * card X) child_costs
      (range_tree_chain_list (find_pivots_pivots_capped k cap d S B) a bs B')"
    and edge_costs: "list_all2 (λc X. c  R * card (outgoing_edges X)) edge_costs
      (range_tree_chain_list (find_pivots_pivots_capped k cap d S B) a bs B')"
  shows "concrete_capped_step_accounted_cost k cap d S B child_costs edge_costs 
    k * Δ * cap + C * card U_loop +
    R * card (outgoing_edges
      (range_tree_chain (find_pivots_pivots_capped k cap d S B) a bs B'))"
proof -
  let ?P = "find_pivots_pivots_capped k cap d S B"
  have mono: "nondecreasing_from a bs"
    using trace unfolding concrete_partition_loop_trace_def by blast
  have core:
    "concrete_capped_step_core_cost k cap d S B child_costs 
      k * Δ * cap + C * card U_loop"
    by (rule concrete_capped_step_core_cost_le_loop_vertices
      [OF S_subset degree S_cap trace child_costs])
  have core_unfolded:
    "fp_iter_capped_scan_cost k cap d S S B + sum_list child_costs 
      k * Δ * cap + C * card U_loop"
    using core unfolding concrete_capped_step_core_cost_def .
  have edges:
    "sum_list edge_costs  R * card (outgoing_edges (range_tree_chain ?P a bs B'))"
    using edge_costs_le_range_tree_chain_outgoing[OF mono edge_costs] .
  show ?thesis
    unfolding concrete_capped_step_accounted_cost_def
    using core_unfolded edges by linarith
qed

theorem full_operational_partition_loop_state_trace_and_degree_cost:
  assumes run:
    "full_operational_partition_loop_state M t k cap l d P B d' D a betas bs B' Us U c"
    and sound: "sound_label d"
    and pre: "bmssp_pre_full d P B"
    and P_reaches: "x. x  P  reachable s x"
    and degree: "edge_outdegree_le Δ"
  obtains child_costs child_sets where
    "concrete_partition_loop_trace P B a bs d' B' Us U"
    "length child_costs = length child_sets"
    "X. X  set child_sets  X  V"
    "c  sum_list child_costs + M * length child_costs +
      t * (Δ * sum_list (map card child_sets) + M * length child_costs)"
proof -
  obtain child_costs child_sets where trace:
      "concrete_partition_loop_trace P B a bs d' B' Us U"
    and len: "length child_costs = length child_sets"
    and sets: "X. X  set child_sets  X  V"
    and cost:
      "c  sum_list child_costs + M * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          M * length child_costs)"
    using full_operational_partition_loop_state_trace_and_complete_edge_cost
      [OF run sound pre P_reaches] by blast
  have edge_sum:
    "sum_list (map (λX. card (outgoing_edges X)) child_sets) 
      Δ * sum_list (map card child_sets)"
    by (rule sum_card_outgoing_edges_le_degree[OF degree sets])
  have cost_degree:
    "c  sum_list child_costs + M * length child_costs +
      t * (Δ * sum_list (map card child_sets) + M * length child_costs)"
  proof -
    have "sum_list child_costs + M * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          M * length child_costs) 
        sum_list child_costs + M * length child_costs +
        t * (Δ * sum_list (map card child_sets) + M * length child_costs)"
      using edge_sum by simp
    then show ?thesis
      using cost by linarith
  qed
  show thesis
    using that trace len sets cost_degree by blast
qed

definition partition_initial_insert_cost_bound where
  "partition_initial_insert_cost_bound c t P  c  t * card P"

lemma partition_initial_insert_cost_boundD:
  assumes "partition_initial_insert_cost_bound c t P"
  shows "c  t * card P"
  using assms unfolding partition_initial_insert_cost_bound_def .

lemma partition_initial_insert_cost_bound_le:
  assumes cost: "partition_initial_insert_cost_bound c t P"
    and card_le: "card P  N"
  shows "c  t * N"
proof -
  have "c  t * card P"
    using cost by (rule partition_initial_insert_cost_boundD)
  also have "  t * N"
    using card_le by simp
  finally show ?thesis .
qed

lemma find_pivots_pivots_capped_subset:
  "find_pivots_pivots_capped k cap d S B  S"
  unfolding find_pivots_pivots_capped_def by auto

lemma find_pivots_pivots_capped_card_le:
  assumes S_subset: "S  V"
  shows "card (find_pivots_pivots_capped k cap d S B)  card S"
proof -
  have finite_S: "finite S"
    using finite_subset[OF S_subset finite_V] .
  show ?thesis
    by (rule card_mono[OF finite_S find_pivots_pivots_capped_subset])
qed

lemma find_pivots_pivots_capped_scaled_card_le:
  assumes S_subset: "S  V"
    and S_k_cap: "k * card S  cap"
  shows "k * card (find_pivots_pivots_capped k cap d S B)  cap"
proof -
  have "card (find_pivots_pivots_capped k cap d S B)  card S"
    by (rule find_pivots_pivots_capped_card_le[OF S_subset])
  then have "k * card (find_pivots_pivots_capped k cap d S B) 
      k * card S"
    by simp
  then show ?thesis
    using S_k_cap by linarith
qed

lemma find_pivots_pivots_capped_tree_antichain:
  assumes anti: "tree_antichain S"
  shows "tree_antichain (find_pivots_pivots_capped k cap d S B)"
  by (rule tree_antichain_subset[OF anti find_pivots_pivots_capped_subset])

lemma partition_initial_insert_cost_bound_capped_pivots_le:
  assumes cost: "partition_initial_insert_cost_bound c t
      (find_pivots_pivots_capped k cap d S B)"
    and S_subset: "S  V"
    and S_cap: "card S  cap"
  shows "c  t * cap"
proof -
  have pivots_S:
    "card (find_pivots_pivots_capped k cap d S B)  card S"
    by (rule find_pivots_pivots_capped_card_le[OF S_subset])
  have pivots_le: "card (find_pivots_pivots_capped k cap d S B)  cap"
    by (rule order_trans[OF pivots_S S_cap])
  show ?thesis
    by (rule partition_initial_insert_cost_bound_le[OF cost pivots_le])
qed

lemma find_pivots_scan_and_initial_insert_cost_le:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    k * Δ * cap + t * cap"
proof -
  have scan: "fp_iter_capped_scan_cost k cap d S S B  k * Δ * cap"
    by (rule find_pivots_capped_scan_cost_le[OF S_subset degree S_cap])
  have insert_le: "c_insert  t * cap"
    by (rule partition_initial_insert_cost_bound_capped_pivots_le
      [OF insert S_subset S_cap])
  show ?thesis
    using scan insert_le by simp
qed

lemma find_pivots_scan_and_initial_insert_cost_le_edge_count:
  assumes S_subset: "S  V"
    and S_cap: "card S  cap"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    k * edge_count * cap + t * cap"
  by (rule find_pivots_scan_and_initial_insert_cost_le
    [OF S_subset edge_outdegree_le_edge_count S_cap insert])

lemma find_pivots_scan_and_initial_insert_budget_from_progress:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
    and degree_factor: "Δ  A"
    and insert_factor: "t  A * k"
    and progress: "k * cap  card U"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    2 * A * card U"
proof -
  have local:
    "fp_iter_capped_scan_cost k cap d S S B + c_insert 
      k * Δ * cap + t * cap"
    by (rule find_pivots_scan_and_initial_insert_cost_le
      [OF S_subset degree S_cap insert])
  have budget: "k * Δ * cap + t * cap  2 * A * card U"
    by (rule scan_insert_budget_from_loop_card
      [OF degree_factor insert_factor progress])
  show ?thesis
    using local budget by linarith
qed

lemma find_pivots_pivots_capped_card_le_seen_if_no_overflow:
  assumes S_subset: "S  V"
    and anti: "tree_antichain S"
    and k_pos: "0 < k"
    and no_overflow:
      "¬ card (find_pivots_seen_capped k cap d S B) > cap"
  shows "card (find_pivots_pivots_capped k cap d S B) 
    card (find_pivots_seen_capped k cap d S B)"
proof -
  let ?P = "find_pivots_pivots_capped k cap d S B"
  let ?W = "find_pivots_seen_capped k cap d S B"
  have cases:
      "k * card ?P  card ?W  (?P = S  card ?W > cap)"
    by (rule find_pivots_pivots_capped_card_times_le_seen_or_overflow
      [OF S_subset anti])
  then have mult_le: "k * card ?P  card ?W"
    using no_overflow by blast
  have "card ?P  k * card ?P"
    using k_pos by (cases k) simp_all
  then show ?thesis
    using mult_le by linarith
qed

lemma partition_initial_insert_cost_bound_capped_pivots_le_seen_scaled_from_mult:
  assumes insert: "partition_initial_insert_cost_bound c t P"
    and pivot_mult: "k * card P  card W"
    and insert_factor: "t  A * k"
  shows "c  A * card W"
proof -
  have "c  t * card P"
    by (rule partition_initial_insert_cost_boundD[OF insert])
  also have "  (A * k) * card P"
    using insert_factor by simp
  also have " = A * (k * card P)"
    by (simp add: algebra_simps)
  also have "  A * card W"
    using pivot_mult by simp
  finally show ?thesis .
qed

lemma find_pivots_pivots_capped_card_times_le_seen:
  assumes S_subset: "S  V"
    and anti: "tree_antichain S"
    and S_k_cap: "k * card S  cap"
  shows "k * card (find_pivots_pivots_capped k cap d S B) 
    card (find_pivots_seen_capped k cap d S B)"
proof -
  let ?P = "find_pivots_pivots_capped k cap d S B"
  let ?W = "find_pivots_seen_capped k cap d S B"
  have cases: "k * card ?P  card ?W  (?P = S  card ?W > cap)"
    by (rule find_pivots_pivots_capped_card_times_le_seen_or_overflow
      [OF S_subset anti])
  then show ?thesis
  proof
    assume "k * card ?P  card ?W"
    then show ?thesis .
  next
    assume overflow: "?P = S  card ?W > cap"
    then have "k * card ?P  cap"
      using S_k_cap by simp
    also have "  card ?W"
      using overflow by simp
    finally show ?thesis .
  qed
qed

lemma partition_initial_insert_cost_bound_capped_pivots_le_seen_scaled:
  assumes insert: "partition_initial_insert_cost_bound c t
      (find_pivots_pivots_capped k cap d S B)"
    and S_subset: "S  V"
    and anti: "tree_antichain S"
    and S_k_cap: "k * card S  cap"
    and insert_factor: "t  A * k"
  shows "c  A * card (find_pivots_seen_capped k cap d S B)"
proof -
  have pivot_mult:
    "k * card (find_pivots_pivots_capped k cap d S B) 
      card (find_pivots_seen_capped k cap d S B)"
    by (rule find_pivots_pivots_capped_card_times_le_seen
      [OF S_subset anti S_k_cap])
  show ?thesis
    by (rule partition_initial_insert_cost_bound_capped_pivots_le_seen_scaled_from_mult
      [OF insert pivot_mult insert_factor])
qed

lemma partition_initial_insert_cost_bound_capped_pivots_le_seen_if_no_overflow:
  assumes insert: "partition_initial_insert_cost_bound c t
      (find_pivots_pivots_capped k cap d S B)"
    and S_subset: "S  V"
    and anti: "tree_antichain S"
    and k_pos: "0 < k"
    and no_overflow:
      "¬ card (find_pivots_seen_capped k cap d S B) > cap"
  shows "c  t * card (find_pivots_seen_capped k cap d S B)"
proof -
  have pivots_le:
    "card (find_pivots_pivots_capped k cap d S B) 
      card (find_pivots_seen_capped k cap d S B)"
    by (rule find_pivots_pivots_capped_card_le_seen_if_no_overflow
      [OF S_subset anti k_pos no_overflow])
  show ?thesis
    by (rule partition_initial_insert_cost_bound_le[OF insert pivots_le])
qed

lemma partition_initial_insert_cost_bound_capped_pivots_le_seen:
  assumes insert: "partition_initial_insert_cost_bound c t
      (find_pivots_pivots_capped k cap d S B)"
    and S_subset: "S  V"
    and S_cap: "card S  cap"
    and anti: "tree_antichain S"
    and k_pos: "0 < k"
  shows "c  t * card (find_pivots_seen_capped k cap d S B)"
proof (cases "card (find_pivots_seen_capped k cap d S B) > cap")
  case True
  have c_le_cap: "c  t * cap"
    by (rule partition_initial_insert_cost_bound_capped_pivots_le
      [OF insert S_subset S_cap])
  have "t * cap  t * card (find_pivots_seen_capped k cap d S B)"
    using True by simp
  then show ?thesis
    using c_le_cap by linarith
next
  case False
  show ?thesis
    by (rule partition_initial_insert_cost_bound_capped_pivots_le_seen_if_no_overflow
      [OF insert S_subset anti k_pos False])
qed

lemma find_pivots_scan_and_initial_insert_cost_le_seen_if_no_overflow:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and anti: "tree_antichain S"
    and k_pos: "0 < k"
    and no_overflow:
      "¬ card (find_pivots_seen_capped k cap d S B) > cap"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    (k * Δ + t) * card (find_pivots_seen_capped k cap d S B)"
proof -
  let ?W = "find_pivots_seen_capped k cap d S B"
  have scan: "fp_iter_capped_scan_cost k cap d S S B 
      k * Δ * card ?W"
    by (rule find_pivots_capped_scan_cost_le_seen[OF S_subset degree])
  have insert_le: "c_insert  t * card ?W"
    by (rule partition_initial_insert_cost_bound_capped_pivots_le_seen_if_no_overflow
      [OF insert S_subset anti k_pos no_overflow])
  have "fp_iter_capped_scan_cost k cap d S S B + c_insert 
      k * Δ * card ?W + t * card ?W"
    using scan insert_le by simp
  also have " = (k * Δ + t) * card ?W"
    by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma find_pivots_scan_and_initial_insert_cost_le_seen:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and anti: "tree_antichain S"
    and k_pos: "0 < k"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    (k * Δ + t) * card (find_pivots_seen_capped k cap d S B)"
proof -
  let ?W = "find_pivots_seen_capped k cap d S B"
  have scan: "fp_iter_capped_scan_cost k cap d S S B 
      k * Δ * card ?W"
    by (rule find_pivots_capped_scan_cost_le_seen[OF S_subset degree])
  have insert_le: "c_insert  t * card ?W"
    by (rule partition_initial_insert_cost_bound_capped_pivots_le_seen
      [OF insert S_subset S_cap anti k_pos])
  have "fp_iter_capped_scan_cost k cap d S S B + c_insert 
      k * Δ * card ?W + t * card ?W"
    using scan insert_le by simp
  also have " = (k * Δ + t) * card ?W"
    by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma find_pivots_scan_and_initial_insert_cost_le_seen_scaled:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and anti: "tree_antichain S"
    and S_k_cap: "k * card S  cap"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
    and insert_factor: "t  A_insert * k"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    (k * Δ + A_insert) * card (find_pivots_seen_capped k cap d S B)"
proof -
  let ?W = "find_pivots_seen_capped k cap d S B"
  have scan: "fp_iter_capped_scan_cost k cap d S S B 
      k * Δ * card ?W"
    by (rule find_pivots_capped_scan_cost_le_seen[OF S_subset degree])
  have insert_le: "c_insert  A_insert * card ?W"
    by (rule partition_initial_insert_cost_bound_capped_pivots_le_seen_scaled
      [OF insert S_subset anti S_k_cap insert_factor])
  have "fp_iter_capped_scan_cost k cap d S S B + c_insert 
      k * Δ * card ?W + A_insert * card ?W"
    using scan insert_le by simp
  also have " = (k * Δ + A_insert) * card ?W"
    by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma find_pivots_scan_and_initial_insert_budget_from_seen_scaled:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and anti: "tree_antichain S"
    and S_k_cap: "k * card S  cap"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
    and insert_factor: "t  A_insert * k"
    and factor: "k * Δ + A_insert  A"
    and seen_progress:
      "card (find_pivots_seen_capped k cap d S B)  card U"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    A * card U"
proof -
  let ?W = "find_pivots_seen_capped k cap d S B"
  have local:
    "fp_iter_capped_scan_cost k cap d S S B + c_insert 
      (k * Δ + A_insert) * card ?W"
    by (rule find_pivots_scan_and_initial_insert_cost_le_seen_scaled
      [OF S_subset degree anti S_k_cap insert insert_factor])
  have "(k * Δ + A_insert) * card ?W  A * card U"
    by (rule mult_mono[OF factor seen_progress]) simp_all
  then show ?thesis
    using local by linarith
qed

lemma find_pivots_scan_and_initial_insert_budget_from_seen:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and anti: "tree_antichain S"
    and k_pos: "0 < k"
    and no_overflow:
      "¬ card (find_pivots_seen_capped k cap d S B) > cap"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
    and factor: "k * Δ + t  A"
    and seen_progress:
      "card (find_pivots_seen_capped k cap d S B)  card U"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    A * card U"
proof -
  let ?W = "find_pivots_seen_capped k cap d S B"
  have local:
    "fp_iter_capped_scan_cost k cap d S S B + c_insert 
      (k * Δ + t) * card ?W"
    by (rule find_pivots_scan_and_initial_insert_cost_le_seen_if_no_overflow
      [OF S_subset degree anti k_pos no_overflow insert])
  have "(k * Δ + t) * card ?W  A * card U"
    by (rule mult_mono[OF factor seen_progress]) simp_all
  then show ?thesis
    using local by linarith
qed

lemma find_pivots_scan_and_initial_insert_budget_from_seen_total:
  assumes S_subset: "S  V"
    and degree: "edge_outdegree_le Δ"
    and S_cap: "card S  cap"
    and anti: "tree_antichain S"
    and k_pos: "0 < k"
    and insert: "partition_initial_insert_cost_bound c_insert t
      (find_pivots_pivots_capped k cap d S B)"
    and factor: "k * Δ + t  A"
    and seen_progress:
      "card (find_pivots_seen_capped k cap d S B)  card U"
  shows "fp_iter_capped_scan_cost k cap d S S B + c_insert 
    A * card U"
proof -
  let ?W = "find_pivots_seen_capped k cap d S B"
  have local:
    "fp_iter_capped_scan_cost k cap d S S B + c_insert 
      (k * Δ + t) * card ?W"
    by (rule find_pivots_scan_and_initial_insert_cost_le_seen
      [OF S_subset degree S_cap anti k_pos insert])
  have "(k * Δ + t) * card ?W  A * card U"
    by (rule mult_mono[OF factor seen_progress]) simp_all
  then show ?thesis
    using local by linarith
qed

text ‹
  The relation below is the first costed operational run in this file.  It
  refines the full operational relation by adding a natural-number cost to each
  BMSSP call and each partition loop.  The constructors charge base-case scans,
  FindPivots scans and initial inserts, pulls, batch prepends, recursive child
  calls, and the remaining tail of the loop.

  This relation is intentionally generic in the partition-cost predicates.  It
  does not know whether a sorted list, bucketed partition, or another data
  structure implements the primitive operations.  Its job is to expose the exact
  cost skeleton that the later refinement layers and the final headline theorem
  instantiate.
›

inductive costed_full_operational_partition_loop_state
  and costed_full_operational_bmssp where
  Cost_State_Done:
    "keys_of D = {} 
     bound_le (Fin a) B 
     complete_on d' (bound_tree P (Fin a)) 
     costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
       [] [] (Fin a) [range_tree P a (Fin a)]
       (bound_tree P (Fin a)  (set [range_tree P a (Fin a)])) 0"
| Cost_State_Step:
    "pull_separates D (M_of l) Bmax S_pull beta D_pull 
     bound_le (Fin beta) B 
     bmssp_pre_full d (split_below d P beta) (Fin beta) 
     S_pull = split_below d P beta 
     a  b 
     complete_on d' (bound_tree P (Fin a)) 
     costed_full_operational_bmssp Δ M_of t k cap l d S_pull (Fin beta)
       d_child (Fin b) U_child c_child 
     complete_preserved d_child d' U_child 
     batch =
       edge_relaxation_pairs_between d_child U_child b beta @
       label_pairs_between d S_pull b beta 
     D_next = batch_min_update D_pull batch 
     partition_pull_cost_bound c_pull S_pull 
     partition_batch_cost_bound c_batch t batch 
     costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d'
       D_next b betas bs B' Us_tail U_tail c_tail 
     c = c_pull + c_batch + c_child + c_tail 
     costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
       (beta # betas) (b # bs) B'
       (range_tree P a (Fin b) # Us_tail)
       (bound_tree P (Fin a) 
        (set (range_tree P a (Fin b) # Us_tail))) c"
| Cost_Base:
    "S = {x} 
     costed_full_operational_bmssp Δ M_of t k cap 0 d S B
       (λv. if v  base_case_vertices k x B then dist s v else d v)
       (base_case_bound k x B)
       (base_case_vertices k x B)
       (base_case_scan_cost Δ k x B)"
| Cost_Step:
    "D = label_partition_view
       (find_pivots_label_capped k cap d S B)
       (find_pivots_pivots_capped k cap d S B) 
     partition_initial_insert_cost_bound c_insert t
       (find_pivots_pivots_capped k cap d S B) 
     costed_full_operational_partition_loop_state Δ M_of t k cap l
       (find_pivots_label_capped k cap d S B)
       (find_pivots_pivots_capped k cap d S B) B d' D a betas bs B'
       Us U_loop c_loop 
     complete_on d'
       {v  bound_tree S B'. find_pivots_label_capped k cap d S B v = dist s v} 
     U = U_loop 
       {v  bound_tree S B'. find_pivots_label_capped k cap d S B v = dist s v} 
     c = fp_iter_capped_scan_cost k cap d S S B + c_insert + c_loop 
     costed_full_operational_bmssp Δ M_of t k cap (Suc l) d S B d' B' U c"

theorem costed_full_operational_partition_loop_state_refines:
  "costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
     betas bs B' Us U c 
   full_operational_partition_loop_state (M_of l) t k cap l d P B d' D a
     betas bs B' Us U c"
and costed_full_operational_bmssp_refines:
  "costed_full_operational_bmssp Δ M_of t k cap l d S B d' B' U c 
   full_operational_bmssp k cap l d S B d' B' U"
proof (induction rule:
    costed_full_operational_partition_loop_state_costed_full_operational_bmssp.inducts)
  case (Cost_State_Done D a B d' P Δ M_of t k cap l d)
  show ?case
    by (rule full_operational_partition_loop_state.State_Done
      [OF Cost_State_Done(1) Cost_State_Done(2) Cost_State_Done(3)])
next
  case (Cost_State_Step D M_of l Bmax S_pull beta D_pull B d P a b d'
      Δ t k cap d_child U_child c_child batch D_next c_pull c_batch
      betas bs B' Us_tail U_tail c_tail c)
  have child_run:
    "full_operational_bmssp k cap l d S_pull (Fin beta)
      d_child (Fin b) U_child"
    using Cost_State_Step by blast
  have tail_run:
    "full_operational_partition_loop_state (M_of l) t k cap l d P B d'
      D_next b betas bs B' Us_tail U_tail c_tail"
    using Cost_State_Step by blast
  show ?case
  proof (rule full_operational_partition_loop_state.State_Step)
    show "pull_separates D (M_of l) Bmax S_pull beta D_pull"
      using Cost_State_Step by blast
    show "bound_le (Fin beta) B"
      using Cost_State_Step by blast
    show "bmssp_pre_full d (split_below d P beta) (Fin beta)"
      using Cost_State_Step by blast
    show "S_pull = split_below d P beta"
      using Cost_State_Step by blast
    show "a  b"
      using Cost_State_Step by blast
    show "complete_on d' (bound_tree P (Fin a))"
      using Cost_State_Step by blast
    show "full_operational_bmssp k cap l d S_pull (Fin beta)
        d_child (Fin b) U_child"
      using child_run .
    show "complete_preserved d_child d' U_child"
      using Cost_State_Step by blast
    show "batch =
        edge_relaxation_pairs_between d_child U_child b beta @
        label_pairs_between d S_pull b beta"
      using Cost_State_Step by blast
    show "D_next = batch_min_update D_pull batch"
      using Cost_State_Step by blast
    show "partition_pull_cost_bound c_pull S_pull"
      using Cost_State_Step by blast
    show "partition_batch_cost_bound c_batch t batch"
      using Cost_State_Step by blast
    show "full_operational_partition_loop_state (M_of l) t k cap l d P B d'
        D_next b betas bs B' Us_tail U_tail c_tail"
      using tail_run .
    show "c = c_pull + c_batch + c_child + c_tail"
      using Cost_State_Step by blast
  qed
next
  case (Cost_Base S x Δ M_of t k cap d B)
  show ?case
    by (rule Full_Base[OF Cost_Base.hyps])
next
  case (Cost_Step D k cap d S B c_insert t Δ M_of l d' a betas bs B'
      Us U_loop c_loop U c)
  have state:
    "full_operational_partition_loop_state (M_of l) t k cap l
       (find_pivots_label_capped k cap d S B)
       (find_pivots_pivots_capped k cap d S B) B d' D a betas bs B'
       Us U_loop c_loop"
    using Cost_Step by blast
  have loop:
    "full_operational_partition_loop k cap l
       (find_pivots_label_capped k cap d S B)
       (find_pivots_pivots_capped k cap d S B) B d' a betas bs B'
       Us U_loop"
    by (rule full_operational_partition_loop_state_refines[OF state])
  show ?case
  proof (rule Full_Step)
    show "full_operational_partition_loop k cap l
        (find_pivots_label_capped k cap d S B)
        (find_pivots_pivots_capped k cap d S B) B d' a betas bs B'
        Us U_loop"
      using loop .
    show "complete_on d'
        {v  bound_tree S B'.
         find_pivots_label_capped k cap d S B v = dist s v}"
      using Cost_Step by blast
    show "U = U_loop 
        {v  bound_tree S B'.
         find_pivots_label_capped k cap d S B v = dist s v}"
      using Cost_Step by blast
  qed
qed

theorem costed_full_operational_bmssp_correct:
  assumes run:
    "costed_full_operational_bmssp Δ M_of t k cap l d S B d' B' U c"
    and sound: "sound_label d"
    and pre: "bmssp_pre_full d S B"
    and S_reaches: "x. x  S  reachable s x"
  shows "bmssp_post_full d S B d' B' U"
proof -
  have run': "full_operational_bmssp k cap l d S B d' B' U"
    by (rule costed_full_operational_bmssp_refines[OF run])
  show ?thesis
    by (rule full_operational_bmssp_correct[OF run' sound pre S_reaches])
qed

lemma costed_full_operational_partition_loop_state_lengths:
  assumes run:
    "costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
      betas bs B' Us U c"
  shows "length betas = length bs"
    and "length Us = Suc (length bs)"
proof -
  have state:
    "full_operational_partition_loop_state (M_of l) t k cap l d P B d' D a
      betas bs B' Us U c"
    by (rule costed_full_operational_partition_loop_state_refines[OF run])
  show "length betas = length bs"
    by (rule full_operational_partition_loop_state_lengths(1)[OF state])
  show "length Us = Suc (length bs)"
    by (rule full_operational_partition_loop_state_lengths(2)[OF state])
qed

theorem finite_initial_label_costed_full_operational_top_level_correct:
  assumes all_reachable: "v. v  V  reachable s v"
    and run:
      "costed_full_operational_bmssp Δ M_of t k cap l finite_initial_label {s}
        Infinity d' Infinity U c"
  shows "sssp_correct d'"
proof -
  have run': "full_operational_bmssp k cap l finite_initial_label {s}
      Infinity d' Infinity U"
    by (rule costed_full_operational_bmssp_refines[OF run])
  show ?thesis
    by (rule finite_initial_label_full_operational_top_level_correct
      [OF all_reachable run'])
qed

theorem costed_full_operational_partition_loop_state_trace_and_degree_cost:
  assumes run:
    "costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
      betas bs B' Us U c"
    and sound: "sound_label d"
    and pre: "bmssp_pre_full d P B"
    and P_reaches: "x. x  P  reachable s x"
    and degree: "edge_outdegree_le Δ"
  obtains child_costs child_sets where
    "concrete_partition_loop_trace P B a bs d' B' Us U"
    "length child_costs = length child_sets"
    "X. X  set child_sets  X  V"
    "c  sum_list child_costs + (M_of l) * length child_costs +
      t * (Δ * sum_list (map card child_sets) + (M_of l) * length child_costs)"
proof -
  have state:
    "full_operational_partition_loop_state (M_of l) t k cap l d P B d' D a
      betas bs B' Us U c"
    by (rule costed_full_operational_partition_loop_state_refines[OF run])
  show thesis
    by (rule full_operational_partition_loop_state_trace_and_degree_cost
      [OF state sound pre P_reaches degree that])
qed

theorem costed_full_operational_partition_loop_state_child_call_data:
  "costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
      betas bs B' Us U c 
   sound_label d 
   bmssp_pre_full d P B 
   (x. x  P  reachable s x) 
   child_costs child_sets.
      length child_costs = length child_sets 
      length child_costs = length bs 
      list_all2
        (λc_child U_child. S_child B_child d_child B_child'.
          costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
            d_child B_child' U_child c_child 
          bmssp_pre_full d S_child B_child 
          (xS_child. reachable s x) 
          card S_child  M_of l)
        child_costs child_sets 
      (Xset child_sets. X  V) 
      c  sum_list child_costs + (M_of l) * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          (M_of l) * length child_costs)"
and costed_full_operational_bmssp_child_call_data_trivial:
  "costed_full_operational_bmssp Δ M_of t k cap l d S B d' B' U c  True"
proof (induction rule:
    costed_full_operational_partition_loop_state_costed_full_operational_bmssp.inducts)
  case (Cost_State_Done D a B d' P Δ M_of t k cap l d)
  show ?case
    by (intro exI[of _ "[]"] exI[of _ "[]"]) simp
next
  case (Cost_State_Step D M_of l Bmax S_pull beta D_pull B d P a b d'
      Δ t k cap d_child U_child c_child batch D_next c_pull c_batch
      betas bs B' Us_tail U_tail c_tail c)
  obtain child_costs child_sets where len_tail:
      "length child_costs = length child_sets"
    and child_len_tail: "length child_costs = length bs"
    and calls_tail:
      "list_all2
        (λc_child U_child. S_child B_child d_child B_child'.
          costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
            d_child B_child' U_child c_child 
          bmssp_pre_full d S_child B_child 
          (xS_child. reachable s x) 
          card S_child  M_of l)
        child_costs child_sets"
    and sets_tail: "Xset child_sets. X  V"
    and tail:
      "c_tail  sum_list child_costs + M_of l * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          M_of l * length child_costs)"
    using Cost_State_Step.IH(8)
      [OF Cost_State_Step.prems(1) Cost_State_Step.prems(2) Cost_State_Step.prems(3)]
    by blast
  have child_reaches:
    "x. x  S_pull  reachable s x"
    using Cost_State_Step
    unfolding split_below_def by blast
  have child_pre: "bmssp_pre_full d S_pull (Fin beta)"
    using Cost_State_Step by blast
  have child_run:
    "costed_full_operational_bmssp Δ M_of t k cap l d S_pull (Fin beta)
      d_child (Fin b) U_child c_child"
    using Cost_State_Step by blast
  have child_post:
    "bmssp_post_full d S_pull (Fin beta) d_child (Fin b) U_child"
    by (rule costed_full_operational_bmssp_correct
      [OF child_run Cost_State_Step.prems(1) child_pre child_reaches])
  have child_set: "U_child  V"
    using child_post unfolding bmssp_post_full_def bound_tree_def by blast
  have P_subset: "P  V"
    using Cost_State_Step unfolding bmssp_pre_full_def by blast
  have finite_P: "finite P"
    using finite_subset[OF P_subset finite_V] .
  have S_pull_subset: "S_pull  P"
    using Cost_State_Step unfolding split_below_def by blast
  have finite_S_pull: "finite S_pull"
    using finite_subset[OF S_pull_subset finite_P] .
  have card_pull: "card S_pull  M_of l"
    using Cost_State_Step unfolding pull_separates_def by blast
  have pull_cost: "c_pull  M_of l"
    using Cost_State_Step card_pull
    unfolding partition_pull_cost_bound_def by linarith
  have edge_len:
    "length (edge_relaxation_pairs_between d_child U_child b beta) 
      card (outgoing_edges U_child)"
    by (rule edge_relaxation_pairs_between_length_le_outgoing)
  have label_len:
    "length (label_pairs_between d S_pull b beta)  card S_pull"
    by (rule label_pairs_between_length_le_card[OF finite_S_pull])
  have batch_len:
    "length batch  card (outgoing_edges U_child) + M_of l"
    using Cost_State_Step edge_len label_len card_pull by simp
  have batch_cost:
    "c_batch  t * (card (outgoing_edges U_child) + M_of l)"
  proof -
    have "c_batch  t * length batch"
      using Cost_State_Step
      unfolding partition_batch_cost_bound_def by simp
    also have "  t * (card (outgoing_edges U_child) + M_of l)"
      using batch_len by simp
    finally show ?thesis .
  qed
  let ?child_costs = "c_child # child_costs"
  let ?child_sets = "U_child # child_sets"
  have len: "length ?child_costs = length ?child_sets"
    using len_tail by simp
  have child_len: "length ?child_costs = length (b # bs)"
    using child_len_tail by simp
  have call_head:
    "S_child B_child d_child' B_child'.
      costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
        d_child' B_child' U_child c_child 
      bmssp_pre_full d S_child B_child 
      (xS_child. reachable s x) 
      card S_child  M_of l"
    using child_run child_pre child_reaches card_pull by blast
  have calls:
    "list_all2
      (λc_child U_child. S_child B_child d_child B_child'.
        costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
          d_child B_child' U_child c_child 
        bmssp_pre_full d S_child B_child 
        (xS_child. reachable s x) 
        card S_child  M_of l)
      ?child_costs ?child_sets"
    using call_head calls_tail by simp
  have sets: "Xset ?child_sets. X  V"
    using child_set sets_tail by simp
  have cost:
    "c  sum_list ?child_costs + M_of l * length ?child_costs +
      t * (sum_list (map (λX. card (outgoing_edges X)) ?child_sets) +
        M_of l * length ?child_costs)"
  proof -
    have c_eq: "c = c_pull + c_batch + c_child + c_tail"
      using Cost_State_Step by blast
    have "c_pull + c_batch + c_child + c_tail 
        M_of l + t * (card (outgoing_edges U_child) + M_of l) +
        c_child +
        (sum_list child_costs + M_of l * length child_costs +
          t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
            M_of l * length child_costs))"
      using pull_cost batch_cost tail by simp
    also have " =
        sum_list ?child_costs + M_of l * length ?child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) ?child_sets) +
          M_of l * length ?child_costs)"
      by (simp add: algebra_simps)
    finally show ?thesis
      using c_eq by simp
  qed
  show ?case
  proof (intro exI[of _ ?child_costs] exI[of _ ?child_sets] conjI)
    show "length ?child_costs = length ?child_sets"
      using len .
    show "length ?child_costs = length (b # bs)"
      using child_len .
    show "list_all2
      (λc_child U_child. S_child B_child d_child B_child'.
        costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
          d_child B_child' U_child c_child 
        bmssp_pre_full d S_child B_child 
        (xS_child. reachable s x) 
        card S_child  M_of l)
      ?child_costs ?child_sets"
      using calls .
    show "Xset ?child_sets. X  V"
      using sets .
    show "c  sum_list ?child_costs + M_of l * length ?child_costs +
      t * (sum_list (map (λX. card (outgoing_edges X)) ?child_sets) +
        M_of l * length ?child_costs)"
      using cost .
  qed
next
  case (Cost_Base S x Δ M_of t k cap d B)
  show ?case
    by simp
next
  case (Cost_Step D k cap d S B c_insert t Δ M_of l d' a betas bs B'
      Us U_loop c_loop U c)
  show ?case
    by simp
qed

theorem costed_full_operational_partition_loop_state_child_calls_and_cost:
  assumes run:
    "costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
      betas bs B' Us U c"
    and sound: "sound_label d"
    and pre: "bmssp_pre_full d P B"
    and P_reaches: "x. x  P  reachable s x"
  obtains child_costs child_sets where
    "concrete_partition_loop_trace P B a bs d' B' Us U"
    "length child_costs = length child_sets"
    "length child_costs = length bs"
    "list_all2
      (λc_child U_child. S_child B_child d_child B_child'.
        costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
          d_child B_child' U_child c_child 
        bmssp_pre_full d S_child B_child 
        (xS_child. reachable s x) 
        card S_child  M_of l)
      child_costs child_sets"
    "X. X  set child_sets  X  V"
    "c  sum_list child_costs + (M_of l) * length child_costs +
      t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
        (M_of l) * length child_costs)"
proof -
  have state:
    "full_operational_partition_loop_state (M_of l) t k cap l d P B d' D a
      betas bs B' Us U c"
    by (rule costed_full_operational_partition_loop_state_refines[OF run])
  have loop:
    "full_operational_partition_loop k cap l d P B d' a betas bs B' Us U"
    by (rule full_operational_partition_loop_state_refines[OF state])
  have trace: "concrete_partition_loop_trace P B a bs d' B' Us U"
    by (rule full_operational_partition_loop_trace[OF loop sound pre P_reaches])
  have data:
    "child_costs child_sets.
      length child_costs = length child_sets 
      length child_costs = length bs 
      list_all2
        (λc_child U_child. S_child B_child d_child B_child'.
          costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
            d_child B_child' U_child c_child 
          bmssp_pre_full d S_child B_child 
          (xS_child. reachable s x) 
          card S_child  M_of l)
        child_costs child_sets 
      (Xset child_sets. X  V) 
      c  sum_list child_costs + (M_of l) * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          (M_of l) * length child_costs)"
    by (rule costed_full_operational_partition_loop_state_child_call_data
      [OF run sound pre P_reaches])
  obtain child_costs child_sets where len:
      "length child_costs = length child_sets"
    and child_len: "length child_costs = length bs"
    and calls:
      "list_all2
        (λc_child U_child. S_child B_child d_child B_child'.
          costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
            d_child B_child' U_child c_child 
          bmssp_pre_full d S_child B_child 
          (xS_child. reachable s x) 
          card S_child  M_of l)
        child_costs child_sets"
    and sets: "Xset child_sets. X  V"
    and cost:
      "c  sum_list child_costs + (M_of l) * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          (M_of l) * length child_costs)"
    using data by blast
  show thesis
  proof (rule that[OF trace len child_len calls _ cost])
    fix X
    assume "X  set child_sets"
    then show "X  V"
      using sets by blast
  qed
qed

theorem costed_full_operational_partition_loop_state_cost_from_child_bound:
  assumes run:
    "costed_full_operational_partition_loop_state Δ M_of t k cap l d P B d' D a
      betas bs B' Us U c"
    and sound: "sound_label d"
    and pre: "bmssp_pre_full d P B"
    and P_reaches: "x. x  P  reachable s x"
    and degree: "edge_outdegree_le Δ"
    and child_bound:
      "c_child U_child S_child B_child d_child B_child'.
        costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
            d_child B_child' U_child c_child;
          bmssp_pre_full d S_child B_child;
          x. x  S_child  reachable s x;
          card S_child  M_of l
         c_child  C * card U_child"
  obtains child_sets where
    "concrete_partition_loop_trace P B a bs d' B' Us U"
    "X. X  set child_sets  X  V"
    "length child_sets = length bs"
    "c  (C + t * Δ) * sum_list (map card child_sets) +
      (Suc t) * (M_of l) * length bs"
proof -
  have state:
    "full_operational_partition_loop_state (M_of l) t k cap l d P B d' D a
      betas bs B' Us U c"
    by (rule costed_full_operational_partition_loop_state_refines[OF run])
  have loop:
    "full_operational_partition_loop k cap l d P B d' a betas bs B' Us U"
    by (rule full_operational_partition_loop_state_refines[OF state])
  have trace:
      "concrete_partition_loop_trace P B a bs d' B' Us U"
    by (rule full_operational_partition_loop_trace[OF loop sound pre P_reaches])
  have data:
    "child_costs child_sets.
      length child_costs = length child_sets 
      length child_costs = length bs 
      list_all2
        (λc_child U_child. S_child B_child d_child B_child'.
          costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
            d_child B_child' U_child c_child 
          bmssp_pre_full d S_child B_child 
          (xS_child. reachable s x) 
          card S_child  M_of l)
        child_costs child_sets 
      (Xset child_sets. X  V) 
      c  sum_list child_costs + (M_of l) * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          (M_of l) * length child_costs)"
    by (rule costed_full_operational_partition_loop_state_child_call_data
      [OF run sound pre P_reaches])
  obtain child_costs child_sets where len: "length child_costs = length child_sets"
    and child_len: "length child_costs = length bs"
    and calls:
      "list_all2
        (λc_child U_child. S_child B_child d_child B_child'.
          costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
            d_child B_child' U_child c_child 
          bmssp_pre_full d S_child B_child 
          (xS_child. reachable s x) 
          card S_child  M_of l)
        child_costs child_sets"
    and sets: "X. X  set child_sets  X  V"
    and cost:
      "c  sum_list child_costs + (M_of l) * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          (M_of l) * length child_costs)"
    using data by blast
  have child_sets_len: "length child_sets = length bs"
    using len child_len by simp
  have child_cost_bounds:
    "list_all2 (λc_child U_child. c_child  C * card U_child)
      child_costs child_sets"
    using calls
  proof (induction rule: list_all2_induct)
    case Nil
    then show ?case by simp
  next
    case (Cons c_child child_costs U_child child_sets)
    obtain S_child B_child d_child B_child' where child:
        "costed_full_operational_bmssp Δ M_of t k cap l d S_child B_child
          d_child B_child' U_child c_child"
      and child_pre: "bmssp_pre_full d S_child B_child"
      and child_reaches: "xS_child. reachable s x"
      and child_card: "card S_child  M_of l"
      using Cons.hyps by blast
    have head: "c_child  C * card U_child"
      by (rule child_bound[OF child child_pre _ child_card])
        (use child_reaches in blast)
    show ?case
      using head Cons.IH Cons.hyps by simp
  qed
  have child_sum:
    "sum_list child_costs  C * sum_list (map card child_sets)"
  proof -
    have "sum_list child_costs 
        sum_list (map (λX. C * card X) child_sets)"
      using sum_list_le_if_list_all2[OF child_cost_bounds] .
    also have " = C * sum_list (map card child_sets)"
      by (induction child_sets) (simp_all add: algebra_simps)
    finally show ?thesis .
  qed
  have edge_sum:
    "sum_list (map (λX. card (outgoing_edges X)) child_sets) 
      Δ * sum_list (map card child_sets)"
    by (rule sum_card_outgoing_edges_le_degree[OF degree sets])
  have "c 
      C * sum_list (map card child_sets) + (M_of l) * length child_sets +
      t * (Δ * sum_list (map card child_sets) +
        (M_of l) * length child_sets)"
  proof -
    have middle:
      "(M_of l) * length child_costs  (M_of l) * length child_sets"
      using len by simp
    have inside:
      "sum_list (map (λX. card (outgoing_edges X)) child_sets) +
        (M_of l) * length child_costs 
       Δ * sum_list (map card child_sets) +
        (M_of l) * length child_sets"
      using edge_sum middle by linarith
    have tail_term:
      "t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          (M_of l) * length child_costs) 
       t * (Δ * sum_list (map card child_sets) +
          (M_of l) * length child_sets)"
      using inside by simp
    have "sum_list child_costs + (M_of l) * length child_costs +
        t * (sum_list (map (λX. card (outgoing_edges X)) child_sets) +
          (M_of l) * length child_costs) 
        C * sum_list (map card child_sets) + (M_of l) * length child_sets +
        t * (Δ * sum_list (map card child_sets) +
          (M_of l) * length child_sets)"
      using child_sum middle tail_term by linarith
    then show ?thesis
      using cost by linarith
  qed
  also have " =
      (C + t * Δ) * sum_list (map card child_sets) +
      (Suc t) * (M_of l) * length child_sets"
    by (simp add: algebra_simps)
  finally have final:
    "c  (C + t * Δ) * sum_list (map card child_sets) +
      (Suc t) * (M_of l) * length bs"
    using child_sets_len by simp
  show thesis
  proof (rule that[OF trace _ child_sets_len final])
    fix X
    assume "X  set child_sets"
    then show "X  V"
      using sets by blast
  qed
qed

end

end