Theory Randomized_Closest_Pair_Time

section ‹Speed›

text ‹In this section, we verify that the running time of the algorithm is linear with respect
to the length of the point sequence $p_1,\ldots,p_n$.

\emph{Proof:}
It is easy to see that the first phase and construction of the grid requires time proportional to
$n$. It is also easy to see that the number of point-comparisons is a bound for the number of
operations in the second phase. It is also possible to observe that the algorithm never compares a
point pair if they are in non-adjacent cells, i.e., if their distance is at least $2 d \sqrt{2}$.

This means we need to show that the expectation of $N(2 d \sqrt{2})$ is proportional to $n$
when $d$ is chosen according to the algorithm in the first phase. Because of the observation
from the last section, i.e., $N(2 d \sqrt{2}) \leq 11^2 N(d)$, it is enough to verify that the
expectation of $N(d)$ is linear.

Let us consider all pair distances:
$d_1 := d(p_1,p_2)$, $d_2 := d(p_1,p_3)$, \ldots, $d_m := d(p_{n-1},p_n)$ where
$m= \frac{n(n-1)}{2}$.

Then we can find a permutation $\sigma : \{1,\ldots,m\} \rightarrow \{1,\ldots,m\}$, s.t., the
distances are ordered, i.e.,
$d_{\sigma(i)} \leq d_{\sigma(j)}$ if $1 \leq i \leq j \leq m$.

The key observation is that $N(d_\sigma(i)) \leq i-1$, because $N$ counts the number of point pairs
which are closer than $d_{\sigma(i)}$, which can only be those corresponding to $d_{\sigma(1)},
d_{\sigma(2)}, \ldots, d_{\sigma(i-1)}$.

On the other hand the algorithm chooses the smallest of $n$ random samples from $d_1,\ldots,d_m$.
So the problem reduces to the computation of the expectation of the smallest element from $n$
random samples from ${1,\ldots,m}$. The mean of this can be estimated to be $\frac{m+1}{n+1}$
which is in $\mathcal O(n)$. \qed›

theory Randomized_Closest_Pair_Time
  imports
    Randomized_Closest_Pair_Growth
    Approximate_Model_Counting.ApproxMCAnalysis
    Distributed_Distinct_Elements.Distributed_Distinct_Elements_Balls_and_Bins
begin

lemma time_sample_distance: "map_pmf time (sample_distance ps) = return_pmf 1"
  unfolding sample_distance_def time_bind_tpmf
  by (simp add:return_tpmf_def bind_return_pmf) (simp add:map_pmf_def[symmetric] time_lift_pmf)

lemma time_first_phase:
  assumes "length ps  2"
  shows "map_pmf time (first_phase ps) = return_pmf (2*length ps)" (is "?L = ?R")
proof -
  let ?m = "replicate_tpmf (length ps) (sample_distance ps)"

  have ps_ne: "ps  []" using assms by auto

  have "?L = bind_pmf ?m (λx. lift_tm (min_list_tm (val x))  (λy. return_pmf (time x + time y)))"
    unfolding first_phase_def time_bind_tpmf by simp
  also have " = bind_pmf ?m (λx. return_pmf (time x + time (min_list_tm (val x))))"
    unfolding lift_tm_def bind_return_pmf by simp
  also have " = bind_pmf ?m (λx. return_pmf (time x + length (val x)))"
    using ps_ne set_val_replicate_tpmf(1) by (intro bind_pmf_cong refl
        arg_cong[where f="return_pmf"] arg_cong2[where f="(+)"] time_min_list) fastforce
  also have " = bind_pmf ?m (λx. return_pmf (time x + length ps))"
    using set_val_replicate_tpmf(1)
    by (intro bind_pmf_cong refl arg_cong[where f="return_pmf"] arg_cong2[where f="(+)"]) auto
  also have " = map_pmf (λx. x + length ps) (map_pmf time ?m)"
    unfolding map_pmf_def[symmetric] map_pmf_comp by simp
  also have " =  return_pmf (2 * length ps)"
    unfolding time_replicate_tpmf time_sample_distance by (simp add:sum_list_replicate)
  finally show ?thesis by simp
qed

lemma time_build_grid: "time (build_grid ps d) = length ps"
  unfolding build_grid_def by (simp add:time_custom_tick)

lemma time_lookup_neighborhood:
  "time (lookup_neighborhood (grid ps d) p)  39+3*(length(val(lookup_neighborhood (grid ps d) p)))"
  (is "?L  ?R")
proof -
  define s where "s = [(0, 0), (0, 1), (1, - 1), (1, 0), (1::int, 1::int)]"
  define t where "t = concat (map (λx. filter (λy. to_grid d y = x + to_grid d p) ps) s)"
  define u where "u = time (remove1_tm p t)"

  have t_eq: "length t+length s=(xs. Suc (length (filter (λy. to_grid d y=x+to_grid d p) ps)))"
    unfolding t_def by (induction s) auto

  have a:"u  1 + length t" unfolding u_def using time_remove1 by auto

  have "?L = 5+5*length s + length t + (length t + length s) + u"
    unfolding lookup_neighborhood_def s_def[symmetric] t_eq u_def
    by (simp add:time_map_tm comp_def grid_def sum_list_triv t_def)
  also have " = 5+6*length s + 2*length t + u" by simp
  also have "  5+6*length s + 2*length t + (1+length t)" using a by simp
  also have " = 36 + 3*length t" unfolding s_def by simp
  also have "  36 + 3 * (1+length (remove1 p t))"
    by (intro add_mono mult_left_mono) (auto simp:length_remove1)
  also have " = 39 + 3*(length (val (lookup_neighborhood (grid ps d) p)))"
    unfolding lookup_neighborhood_def s_def[symmetric] t_def
    by (simp add:val_remove1 comp_def grid_def)
  finally show ?thesis by simp
qed

lemma time_calc_dists_neighborhood:
  "time (calc_dists_neighborhood (grid ps d) p) 
  40 + 5 * (length (val (lookup_neighborhood (grid ps d) p)))" (is "?L  ?R")
proof -
  let ?g = "grid ps d"
  have "?L = 2* (length (val (lookup_neighborhood ?g p))) + 1 + time (lookup_neighborhood ?g p)"
    unfolding calc_dists_neighborhood_def by (simp add:time_map_tm sum_list_triv)
  also have "  2* (length (val (lookup_neighborhood ?g p))) +1 +
    (39 + 3* (length (val (lookup_neighborhood ?g p))))"
    by (intro add_mono mult_right_mono time_lookup_neighborhood) auto
  also have " = 40 + 5 * (length (val (lookup_neighborhood ?g p)))" by simp
  finally show ?thesis by simp
qed

lemma time_second_phase:
  fixes ps :: "point list"
  assumes "d > 0" "min_dist ps  d" "length ps  2"
  shows "time (second_phase d ps)  2 + 44 * length ps + 7 * close_point_size ps (2 * sqrt 2 * d)"
    (is "?L  ?R")
proof -
  define s where "s = concat (map (λx. val (calc_dists_neighborhood (val (build_grid ps d)) x)) ps)"

  have len_s: "length s = (xps. length (val (lookup_neighborhood (grid ps d) x)))"
    unfolding s_def by (simp add:calc_dists_neighborhood_def build_grid_val length_concat comp_def)
  also have " = (xps. size (mset (val (lookup_neighborhood (grid ps d) x))))"
    by simp
  also have " 
    (xps. size({#y∈# mset ps. to_grid d y-to_grid d x{(0,0),(0,1),(1,-1),(1,0),(1,1)}#}))"
    unfolding lookup_neighborhood by (intro sum_list_mono size_mset_mono) simp
  also have "  (xps. size({#y∈# mset ps. k{1,2}. ¦y$k/d-x$k/d¦1 #}))"
    unfolding to_grid_def by (intro sum_list_mono size_mset_mono multiset_filter_mono_2) auto
  also have "  (xps. size({#y∈# mset ps. dist y x < d * real_of_int (1 + 1) * sqrt 2#}))"
    using exhaust_2
    by (intro sum_list_mono size_mset_mono multiset_filter_mono_2 grid_dist_upperI[OF assms(1)])
      blast
  also have " = (xps. length (filter (λy. dist x y < 2 * sqrt 2 * d) ps))"
    by (simp add:dist_commute ac_simps) (metis mset_filter size_mset)
  also have " = close_point_size ps ((2* sqrt 2)*d)"
    unfolding close_point_size_def product_concat_map filter_concat length_concat
    by (simp add:comp_def)
  finally have len_s_bound: "length s  close_point_size ps (2* sqrt 2*d)" by simp

  obtain u v where "u  set ps" "v  set (val (lookup_neighborhood (grid ps d) u))"
    using second_phase_aux[OF assms] that by metis
  hence "False" if "length s = 0"
    using that unfolding len_s sum_list_eq_0_iff by simp
  hence s_ne: "s  []" by auto

  have "?L = 2 + 4*length ps + (length s + time (min_list_tm s)) +
    (ips. time (calc_dists_neighborhood (val (build_grid ps d)) i))"
    unfolding second_phase_def by (simp add:time_map_tm s_def[symmetric] time_build_grid)
  also have "  2 + 4*length ps + (length s + time (min_list_tm s)) +
    (ips. 40+5* length (val (lookup_neighborhood (grid ps d) i)))"
    unfolding build_grid_val by (intro add_mono sum_list_mono time_calc_dists_neighborhood) auto
  also have " = 2 + 44*length ps + (length s + time (min_list_tm s)) +
    (ips. 5* length (val (lookup_neighborhood (grid ps d) i)))"
    by (simp add:sum_list_addf sum_list_triv)
  also have " = 2 + 44*length ps + 7*(length s)"
    unfolding time_min_list[OF s_ne] len_s by (simp add:sum_list_const_mult)
  also have "  2 + 44* length ps + 7 * close_point_size ps (2* sqrt 2*d)"
    by (intro add_mono mult_left_mono len_s_bound) auto
  finally show  ?thesis by simp
qed

lemma mono_close_point_size: "mono (close_point_size ps)"
  unfolding close_point_size_def by (intro monoI length_filter_P_impl_Q) auto

lemma close_point_size_bound: "close_point_size ps x  length ps^2"
  unfolding close_point_size_def power2_eq_square using length_filter_le length_product by metis

lemma map_product: "map (map_prod f g) (List.product xs ys) = List.product (map f xs) (map g ys)"
  unfolding product_concat_map by (simp add:map_concat comp_def)

lemma close_point_size_bound_2:
  "close_point_size ps d  length ps + 2 * card {(u,v). dist (ps!u) (ps!v)<d  u<v  v<length ps}"
  (is "?L  ?R")
proof -
  let ?n = "length ps"
  let ?h = "λx. dist (ps ! fst x) (ps ! snd x) < d"
  have e : "List.product ps ps = map (map_prod ((!)ps) ((!) ps)) (List.product [0..<?n] [0..<?n])"
    unfolding map_product by (simp add:map_nth)

  have "?L = length (filter (λx. dist (ps ! fst x) (ps ! snd x) < d) (List.product[0..<?n][0..<?n]))"
    unfolding close_point_size_def e by (simp add:comp_def case_prod_beta')
  also have " = card {x. ?h x  fst x < ?n  snd x < ?n}"
    by (subst distinct_length_filter) (simp_all add:distinct_product Int_def mem_Times_iff)
  also have " = card ({x. ?h xfst x<?nsnd x<?nfst x  snd x}{x. ?h xfst x=snd xsnd x<?n})"
    by (intro arg_cong[where f="card"]) auto
  also have "  card{x. ?h xfst x<?nsnd x<?nfst x  snd x}+card{x. ?h xfst x=snd xsnd x<?n}"
    by (intro card_Un_le)
  also have "  card{x. ?h xfst x<?nsnd x<?nfst x  snd x}+card((λx. (x,x))`{k. k<?n})"
    by (intro add_mono order.refl card_mono finite_imageI) auto
  also have "  card{x. ?h xfst x<?nsnd x<?nfst x  snd x}+?n"
    by (subst card_image) (auto intro:inj_onI)
  also have " =card ({x. ?h xfst x<snd xsnd x<?n}{x. ?h xsnd x<fst xfst x<?n})+?n"
    by (intro arg_cong2[where f="(+)"] arg_cong[where f="card"]) auto
  also have "  (card {x. ?h xfst x<snd xsnd x<?n} + card {x. ?h xsnd x<fst xfst x<?n})+?n"
    by (intro add_mono card_Un_le order.refl)
  also have
    "=(card{x. ?h xfst x<snd xsnd x<?n}+card (prod.swap`{x. ?h xsnd x<fst xfst x<?n}))+?n"
    by (subst card_image) auto
  also have " = (card{x. ?h xfst x<snd xsnd x<?n}+card ({x. ?h xfst x<snd xsnd x<?n}))+?n"
    by (intro arg_cong2[where f="(+)"] arg_cong[where f="card"]) (auto simp:dist_commute)
  also have " = ?R" by (simp add:case_prod_beta')
  finally show ?thesis by simp
qed

lemma card_card_estimate:
  fixes f :: "'a  ('b :: linorder)"
  assumes "finite S"
  shows "card {x  S. a  card {y  S. f y < f x }}  card S - a" (is "?L  ?R")
proof -
  define T where "T = {x  S. card {y  S. f y < f x} < a}"

  have T_range: "T  S" unfolding T_def by auto
  hence fin_T: "finite T" using assms finite_subset by auto

  have d:"a  card T  T= S"
  proof (rule ccontr)
    define x where "x = arg_min_on f (S-T)"

    assume a:"¬(a  card T  T=S)"
    hence c:"S - T  {}" using T_range by auto
    hence b:"x  S-T" using assms unfolding x_def by (intro arg_min_if_finite) auto

    have "False" if "y  S-T" "f y < f x" for y
      using arg_min_if_finite[OF _ c] that assms unfolding x_def by auto
    hence "card {y  S. f y < f x}  card T" by (intro card_mono fin_T) auto
    also have " < a" using a by simp
    finally have "card {y  S. f y < f x} < a" by simp
    thus "False" using b unfolding T_def by simp
  qed
  have "?L = card (S - T)" unfolding T_def by (intro arg_cong[where f="card"]) auto
  also have " = card S - card T" using fin_T T_range by (intro card_Diff_subset) auto
  also have "  card S - a" using d by auto
  finally show ?thesis by simp
qed

lemma finite_map_pmf:
  assumes "finite (set_pmf S)"
  shows "finite (set_pmf (map_pmf f S))"
  using assms by simp

lemma finite_replicate_pmf:
  assumes "finite (set_pmf S)"
  shows "finite (set_pmf (replicate_pmf n S))"
  using assms unfolding set_replicate_pmf lists_eq_set
  by (simp add:finite_lists_length_eq)

lemma power_sum_approx: "(k<m. (real k)^n)  m^(n+1)/real (n+1)"
proof (induction m)
  case 0 thus ?case by simp
next
  case (Suc m)
  have "(k<Suc m. real k ^ n) = (k<m. real k ^ n) + real m ^ n" by simp
  also have "...  real m ^ (n+1) / real (n+1) + real m^n" by (intro add_mono Suc order.refl)
  also have "... = (real m^(n+1)+(real (m+1)-m)*real (n+1)*real m^((n+1)-1)) / real (n+1)"
    by (simp add:field_simps)
  also have "  (real m^(n+1)+(real (m+1)^(n+1)-real m^(n+1))) / real (n+1)"
    by (intro divide_right_mono add_mono order.refl power_diff_est_2) simp_all
  also have " = real (Suc m) ^ (n + 1) / real (n + 1)" by simp
  finally show ?case by simp
qed

lemma exp_close_point_size:
  assumes "length ps  2"
  shows "(d. real (close_point_size ps d) (map_pmf val (first_phase ps)))  2* real (length ps)"
    (is "?L  ?R")
proof -
  let ?n = "length ps"
  define T where "T = {i. fst i<snd isnd i<?n}"
  let ?I = "{..<?n}"
  let ?dpmf = "map_pmf (λi. dist (ps!fst i) (ps!snd i)) (pmf_of_set T)"
  let ?q = "prod_pmf {..<?n} (λ_. ?dpmf)"
  let ?h = "λx. dist (ps ! fst x) (ps ! snd x)"
  let ?cps = "λd. card {(u,v). dist (ps!u) (ps!v)<d  u<v  v<length ps}"
  let ?m = "?n * (?n - 1) div 2"

  have card_T: "card T = ?m"
  proof -
    have "2 * card T = 2 * card {(x,y)  {..<?n}×{..<?n}. x < y}"
      unfolding T_def by (intro arg_cong[where f="card"] arg_cong2[where f="(*)"]) auto
    also have " = card {..<?n} * (card {..<?n}-1)" by (intro card_ordered_pairs) simp
    also have " = ?n * (?n-1)" by simp
    finally have " 2 * card T= ?n * (?n-1)" by simp
    thus ?thesis by simp
  qed

  have "2 * 1  ?n * (?n-1)" using assms by (intro mult_mono) auto
  hence "card T > 0" unfolding card_T using assms by (intro div_2_gt_zero) simp
  hence T_fin_ne: "finite T" "T  {}" by (auto simp: card_ge_0_finite)

  have x_neI:"x  []" if "x  set_pmf (replicate_pmf ?n ?dpmf)" for x
    using that assms by (auto simp:set_replicate_pmf)

  have a:"map_pmf val (first_phase ps) = map_pmf min_list (replicate_pmf ?n ?dpmf)"
    unfolding first_phase_def val_tpmf_simps val_replicate_tpmf val_sample_distance
      T_def[symmetric] map_pmf_def[symmetric] by (intro map_pmf_cong val_min_list x_neI) auto

  hence b: "{x. t < ?cps x} = {}" if "t  {..<?m}" for t
  proof -
    have "?cps x  card T" for x
      using T_fin_ne(1) unfolding T_def by (intro card_mono) auto
    moreover have "card T  t" using that unfolding card_T by (simp add:not_less)
    ultimately have "?cps x  t" for x using order.trans by auto
    thus ?thesis using not_less by auto
  qed

  have d: "{y. t< ?cps (min_list (map y [0..<?n]))} = {..<?n}{y. t < ?cps y}" (is "?L2=?R2") for t
  proof (rule Set.set_eqI)
    fix x
    have "x  ?L2  (t < ?cps (min_list (map x [0..<?n])))" by simp
    also have "  (t < ?cps (Min (x ` {0..<?n})))"
      using assms by (subst min_list_Min) auto
    also have "  (t < Min (?cps ` x ` {0..<?n}))"
      using assms by (intro arg_cong2[where f="(<)"] mono_Min_commute refl finite_imageI monoI
          card_mono finite_subset[OF _ T_fin_ne(1)]) (auto simp:T_def)
    also have "  (i{0..<?n}. t < ?cps (x i))"
      using assms by (subst Min_gr_iff) auto
    also have "  x  ?R2" by auto
    finally show "x  ?L2  x  ?R2" by simp
  qed

  have c: "measure (replicate_pmf ?n ?dpmf) {x. t<?cps(min_list x)}(real (?m-(t+1))/real ?m)^?n"
    (is "?L1  ?R1") for t
  proof -
    have "?L1 = measure(replicate_pmf(length [0..<?n]) ?dpmf) {x. t < ?cps (min_list x)}"
      by simp
    also have " = measure (map_pmf (λf. map f [0..<?n]) (prod_pmf(set[0..<?n])(λ_.?dpmf)))
      {x. t<?cps(min_list x)}"
      by (intro arg_cong2[where f="λx. measure (measure_pmf x)"] replicate_pmf_Pi_pmf) auto
    also have " = measure ?q {y. t < ?cps (min_list (map y [0..<?n]))}"
      by (simp add:atLeast0LessThan)
    also have " = measure (prod_pmf {..<?n} (λ_. ?dpmf)) ({..<?n}  {y. t < ?cps y})"
      unfolding d by simp
    also have " = measure ?dpmf {y. t < ?cps y}^?n"
      by (subst measure_Pi_pmf_Pi) simp_all
    also have " = measure ?dpmf {y. t+1  ?cps y}^?n"
      by (intro measure_pmf_cong arg_cong2[where f="(λx y. x^y)"] refl) auto
    also have "  measure (pmf_of_set T) {y. t+1 card {x  T. ?h x<?h y}}^?n"
      unfolding T_def by (auto simp:case_prod_beta' conj_commute)
    also have " = (real (card {yT. t+1 card {x  T. ?h x<?h y}})/real (card T))^?n"
      unfolding measure_pmf_of_set[OF T_fin_ne(2,1)] Int_def by simp
    also have "  (real (card T - (t+1))/real (card T))^?n"
      by (intro power_mono divide_right_mono of_nat_mono card_card_estimate T_fin_ne) auto
    also have "... = (real (?m - (t+1))/real ?m)^?n"
      unfolding card_T by auto
    finally show ?thesis by simp
  qed

  have "ennreal ?L = (ds. real (close_point_size ps (min_list ds)) replicate_pmf ?n ?dpmf)"
    unfolding a by simp
  also have "  (ds. real (?n + 2*?cps (min_list ds)) replicate_pmf ?n ?dpmf)" using T_fin_ne
    by (intro integral_mono_AE ennreal_leI AE_pmfI close_point_size_bound_2 of_nat_mono
          integrable_measure_pmf_finite finite_replicate_pmf) auto
  also have " = ennreal ?n + 2*ennreal(ds. real (?cps (min_list ds)) replicate_pmf ?n ?dpmf)"
    by (simp add:ennreal_mult' integrable_measure_pmf_finite finite_replicate_pmf T_fin_ne)
  also have " = ennreal ?n + 2*+ x. ennreal (real (?cps (min_list x))) replicate_pmf ?n ?dpmf"
    by (intro arg_cong2[where f="(+)"]  arg_cong2[where f="(*)"] finite_replicate_pmf
        nn_integral_eq_integral[symmetric] integrable_measure_pmf_finite) (auto simp:T_fin_ne)
  also have " = ennreal ?n +2*+ x. ennreal_of_enat (?cps (min_list x)) replicate_pmf ?n ?dpmf"
    by (intro nn_integral_cong arg_cong2[where f="(+)"]  arg_cong2[where f="(*)"] refl)
      (simp add: ennreal_of_nat_eq_real_of_nat)
  also have " = ennreal ?n +2*(t. emeasure (replicate_pmf ?n ?dpmf) {x. t<?cps (min_list x)})"
    by (subst nn_integral_enat_function) simp_all
  also have " =ennreal ?n+2*(t<?m. emeasure(replicate_pmf ?n ?dpmf){x. t<?cps (min_list x)})"
    using b by (intro arg_cong2[where f="(+)"] arg_cong2[where f="(*)"] suminf_finite) auto
  also have " =ennreal ?n+2*ennreal(t<?m. measure(replicate_pmf ?n ?dpmf){x. t<?cps(min_list x)})"
    unfolding measure_pmf.emeasure_eq_measure by simp
  also have "  ennreal ?n+2*ennreal (t<?m. (real (?m - (t+1))/real ?m)^?n)"
    by (intro add_mono order.refl iffD2[OF ennreal_mult_le_mult_iff] ennreal_leI sum_mono c) auto
  also have " = ennreal ?n+ennreal (2*(t<?m. (real (?m - (t+1))^?n/real ?m^?n)))"
    using ennreal_mult' by (auto simp:algebra_simps power_divide)
  also have " = ennreal (real ?n + (2*(t<?m. (real (?m - (t+1))^?n/real ?m^?n))))"
    by (intro ennreal_plus[symmetric] mult_nonneg_nonneg sum_nonneg) simp_all
  also have " = ennreal (real ?n + (2*(t<?m. (real (?m - (t+1))^?n))/real ?m^?n))"
    by (simp add:sum_divide_distrib[symmetric])
  also have " = ennreal (real ?n + (2*(t<?m. (real t^?n))/real ?m^?n))"
    by (intro arg_cong[where f="ennreal"] arg_cong2[where f="(+)"] arg_cong2[where f="(*)"]
         arg_cong2[where f="(/)"] refl sum.reindex_bij_betw bij_betwI[where g="λx. ?m - (x+1)"])
       auto
  also have "  ennreal (real ?n + (2 * (real ?m^(?n+1)/real (?n +1)))/real ?m^?n)"
    by (intro ennreal_leI add_mono divide_right_mono mult_left_mono power_sum_approx) auto
  also have " = ennreal (real ?n + (2 * (real ?m^(?n+1)/real ?m^?n)/ real (?n +1)))"
    by simp
  also have " = ennreal (real ?n + ((2 * ?m)/ real (?n+1)))" by (simp add:field_simps)
  also have " = ennreal (real ?n + (?n*(?n-1)/ real (?n+1)))"
    by (metis even_mult_iff even_numeral even_two_times_div_two odd_two_times_div_two_nat)
  also have " = ennreal ((real ?n*(real ?n+1) +real ?n * (real ?n-real 1)) / real (?n+1))"
    using assms by (subst of_nat_diff[symmetric]) (auto simp:field_simps)
  also have " = ennreal (2*real ?n * real ?n / real (?n+1))"
    using assms by (simp add:field_simps)
  also have "  ennreal (2*real ?n * real ?n / real ?n)"
    using assms by (intro ennreal_leI mult_right_mono divide_left_mono mult_pos_pos) auto
  also have " = ennreal (2*real ?n)" by simp
  finally have "ennreal ?L  ennreal (2*real ?n)" by simp
  thus "?L  2*real ?n" by simp
qed

definition time_closest_pair :: "real  real"
  where "time_closest_pair n = 2 + 1740 * n"

text ‹Main results of this section:›

theorem time_closest_pair:
  assumes "length ps  2"
  shows "(x. real (time x) closest_pair ps)  time_closest_pair (length ps)" (is "?L  ?R")
proof -
  let ?n = "length ps"
  let ?cps = "close_point_size ps"
  let ?p = "map_pmf val (first_phase ps)"

  have "(0,1)  {i. fst i < snd i  snd i < length ps}" using assms by auto
  hence a:"finite {i. fst i < snd i  snd i < length ps}" "{i. fst i<snd i  snd i<length ps}  {}"
    using fin_nat_pairs[where n="length ps"] by (auto simp:case_prod_beta')

  have "finite (set_pmf (map_pmf val (sample_distance ps)))"
    unfolding sample_distance_def val_tpmf_simps map_pmf_def[symmetric] using a
    by (intro finite_map_pmf) auto

  hence int[simp]: "integrable (measure_pmf (map_pmf val (first_phase ps))) f" for f :: "real  real"
    unfolding first_phase_def val_tpmf_simps val_replicate_tpmf unfolding map_pmf_def[symmetric]
    by (metis integrable_measure_pmf_finite finite_replicate_pmf finite_map_pmf)

  have "map_pmf time (closest_pair ps) = first_phase ps 
    (λx. return_pmf (if  val x = 0 then (tick 0) else second_phase (val x) ps) 
    (λy. return_pmf (time x + time y)))"
    using time_first_phase[OF assms]
    unfolding closest_pair_def time_bind_tpmf lift_tm_def if_distrib if_distribR by simp
  also have "... = map_pmf (λx. time x + (if val x = 0 then 1 else time (second_phase (val x) ps)))
    (first_phase ps)"
    unfolding bind_return_pmf map_pmf_def by (simp cong:if_cong)
  also have "... =  map_pmf (λx. 2*length ps +
    (if val x = 0 then 1 else time (second_phase (val x) ps))) (first_phase ps)"
    using time_first_phase[OF assms] unfolding map_pmf_eq_return_pmf_iff
    by (intro map_pmf_cong refl arg_cong2[where f="(+)"]) simp
  also have "... = map_pmf (λx. 2*length ps + (if x=0 then 1 else time (second_phase x ps))) ?p"
    unfolding map_pmf_comp by simp
  finally have a:"map_pmf time (closest_pair ps) =
    map_pmf (λx. 2*length ps + (if x=0 then 1 else time (second_phase x ps))) ?p" by simp

  have "(x. real (time x) closest_pair ps) = (x. real x map_pmf time (closest_pair ps))"
    by simp
  also have " = (d. 2 * real ?n + (if d=0 then 1 else time (second_phase d ps)) ?p)"
    unfolding a by simp
  also have "  (d. 2 * real ?n + (if d0 then 1 else 2+44*?n+7*?cps ((2* sqrt 2)*d)) ?p)"
    using first_phase[OF assms] min_dist_nonneg[OF assms] order.trans unfolding AE_measure_pmf_iff
    by (intro integral_mono_AE int AE_pmfI of_nat_mono mono_intros
        time_second_phase[OF _ _ assms(1)] refl dual_order.not_eq_order_implies_strict) auto
  also have " = (d. 2*real ?n+(if d0 then 1 else 2+44*real ?n+7*real(?cps ((2* sqrt 2)*d))) ?p)"
    by (intro integral_cong_AE) simp_all
  also have "  (d. 2 * real ?n +
    (if d0 then 1 else 2+44*real ?n+7*((2* sqrt 2 * (2* sqrt 2)+3)^2 * real (?cps d))) ?p)"
    using  growth_lemma[where a="2* sqrt 2"]
    by (intro integral_mono_AE int AE_pmfI mono_intros mult_right_mono) auto
  also have " 
    (d. 2 * real ?n + (2+44*real ?n+7*((2* sqrt 2 * (2* sqrt 2)+3)^2 * real (?cps d))) ?p)"
    by (intro integral_mono_AE int AE_pmfI mono_intros mult_right_mono) simp
  also have " = (d. (2+46*real ?n)+847 * real (?cps d) ?p)" by (simp add:algebra_simps)
  also have " = (d. 2+46*real ?n ?p)+(d. 847* real (?cps d) ?p)"
    by (intro Bochner_Integration.integral_add int)
  also have " = (2+46*real ?n)+847*(d. real (?cps d) ?p)"
    by (intro arg_cong2[where f="(+)"]) simp_all
  also have "  (2+46*real ?n)+847*(2 * real ?n)"
    by (intro mono_intros mult_left_mono exp_close_point_size assms) simp
  also have "  = 2+1740* real ?n" by simp
  finally show ?thesis unfolding time_closest_pair_def by simp
qed

theorem asymptotic_time_closest_pair:
  "time_closest_pair  O(λx. x)"
  unfolding time_closest_pair_def by simp

end