Theory Closest_Pair
section "Closest Pair Algorithm"
theory Closest_Pair
imports Common
begin
text‹
Formalization of a slightly optimized divide-and-conquer algorithm solving the Closest Pair Problem
based on the presentation of Cormen \emph{et al.} \<^cite>‹"Introduction-to-Algorithms:2009"›.
›
subsection "Functional Correctness Proof"
subsubsection "Combine Step"
fun find_closest_tm :: "point ⇒ real ⇒ point list ⇒ point tm" where
"find_closest_tm _ _ [] =1 return undefined"
| "find_closest_tm _ _ [p] =1 return p"
| "find_closest_tm p δ (p⇩0 # ps) =1 (
if δ ≤ snd p⇩0 - snd p then
return p⇩0
else
do {
p⇩1 <- find_closest_tm p (min δ (dist p p⇩0)) ps;
if dist p p⇩0 ≤ dist p p⇩1 then
return p⇩0
else
return p⇩1
}
)"
fun find_closest :: "point ⇒ real ⇒ point list ⇒ point" where
"find_closest _ _ [] = undefined"
| "find_closest _ _ [p] = p"
| "find_closest p δ (p⇩0 # ps) = (
if δ ≤ snd p⇩0 - snd p then
p⇩0
else
let p⇩1 = find_closest p (min δ (dist p p⇩0)) ps in
if dist p p⇩0 ≤ dist p p⇩1 then
p⇩0
else
p⇩1
)"
lemma find_closest_eq_val_find_closest_tm:
"val (find_closest_tm p δ ps) = find_closest p δ ps"
by (induction p δ ps rule: find_closest.induct) (auto simp: Let_def)
lemma find_closest_set:
"0 < length ps ⟹ find_closest p δ ps ∈ set ps"
by (induction p δ ps rule: find_closest.induct)
(auto simp: Let_def)
lemma find_closest_dist:
assumes "sorted_snd (p # ps)" "∃q ∈ set ps. dist p q < δ"
shows "∀q ∈ set ps. dist p (find_closest p δ ps) ≤ dist p q"
using assms
proof (induction p δ ps rule: find_closest.induct)
case (3 p δ p⇩0 p⇩2 ps)
let ?ps = "p⇩0 # p⇩2 # ps"
define p⇩1 where p⇩1_def: "p⇩1 = find_closest p (min δ (dist p p⇩0)) (p⇩2 # ps)"
have A: "¬ δ ≤ snd p⇩0 - snd p"
proof (rule ccontr)
assume B: "¬ ¬ δ ≤ snd p⇩0 - snd p"
have "∀q ∈ set ?ps. snd p ≤ snd q"
using sorted_snd_def "3.prems"(1) by simp
moreover have "∀q ∈ set ?ps. δ ≤ snd q - snd p"
using sorted_snd_def "3.prems"(1) B by auto
ultimately have "∀q ∈ set ?ps. δ ≤ dist (snd p) (snd q)"
using dist_real_def by simp
hence "∀q ∈ set ?ps. δ ≤ dist p q"
using dist_snd_le order_trans
apply (auto split: prod.splits) by fastforce+
thus False
using "3.prems"(2) by fastforce
qed
show ?case
proof cases
assume "∃q ∈ set (p⇩2 # ps). dist p q < min δ (dist p p⇩0)"
hence "∀q ∈ set (p⇩2 # ps). dist p p⇩1 ≤ dist p q"
using "3.IH" "3.prems"(1) A p⇩1_def sorted_snd_def by simp
thus ?thesis
using p⇩1_def A by (auto split: prod.splits)
next
assume B: "¬ (∃q ∈ set (p⇩2 # ps). dist p q < min δ (dist p p⇩0))"
hence "dist p p⇩0 < δ"
using "3.prems"(2) p⇩1_def by auto
hence C: "∀q ∈ set ?ps. dist p p⇩0 ≤ dist p q"
using p⇩1_def B by auto
have "p⇩1 ∈ set (p⇩2 # ps)"
using p⇩1_def find_closest_set by blast
hence "dist p p⇩0 ≤ dist p p⇩1"
using p⇩1_def C by auto
thus ?thesis
using p⇩1_def A C by (auto split: prod.splits)
qed
qed auto
declare find_closest.simps [simp del]
fun find_closest_pair_tm :: "(point * point) ⇒ point list ⇒ (point × point) tm" where
"find_closest_pair_tm (c⇩0, c⇩1) [] =1 return (c⇩0, c⇩1)"
| "find_closest_pair_tm (c⇩0, c⇩1) [_] =1 return (c⇩0, c⇩1)"
| "find_closest_pair_tm (c⇩0, c⇩1) (p⇩0 # ps) =1 (
do {
p⇩1 <- find_closest_tm p⇩0 (dist c⇩0 c⇩1) ps;
if dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1 then
find_closest_pair_tm (c⇩0, c⇩1) ps
else
find_closest_pair_tm (p⇩0, p⇩1) ps
}
)"
fun find_closest_pair :: "(point * point) ⇒ point list ⇒ (point × point)" where
"find_closest_pair (c⇩0, c⇩1) [] = (c⇩0, c⇩1)"
| "find_closest_pair (c⇩0, c⇩1) [_] = (c⇩0, c⇩1)"
| "find_closest_pair (c⇩0, c⇩1) (p⇩0 # ps) = (
let p⇩1 = find_closest p⇩0 (dist c⇩0 c⇩1) ps in
if dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1 then
find_closest_pair (c⇩0, c⇩1) ps
else
find_closest_pair (p⇩0, p⇩1) ps
)"
lemma find_closest_pair_eq_val_find_closest_pair_tm:
"val (find_closest_pair_tm (c⇩0, c⇩1) ps) = find_closest_pair (c⇩0, c⇩1) ps"
by (induction "(c⇩0, c⇩1)" ps arbitrary: c⇩0 c⇩1 rule: find_closest_pair.induct)
(auto simp: Let_def find_closest_eq_val_find_closest_tm)
lemma find_closest_pair_set:
assumes "(C⇩0, C⇩1) = find_closest_pair (c⇩0, c⇩1) ps"
shows "(C⇩0 ∈ set ps ∧ C⇩1 ∈ set ps) ∨ (C⇩0 = c⇩0 ∧ C⇩1 = c⇩1)"
using assms
proof (induction "(c⇩0, c⇩1)" ps arbitrary: c⇩0 c⇩1 C⇩0 C⇩1 rule: find_closest_pair.induct)
case (3 c⇩0 c⇩1 p⇩0 p⇩2 ps)
define p⇩1 where p⇩1_def: "p⇩1 = find_closest p⇩0 (dist c⇩0 c⇩1) (p⇩2 # ps)"
hence A: "p⇩1 ∈ set (p⇩2 # ps)"
using find_closest_set by blast
show ?case
proof (cases "dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1")
case True
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (c⇩0, c⇩1) (p⇩2 # ps)"
using prod.collapse by blast
note defs = p⇩1_def C'_def
hence "(C⇩0' ∈ set (p⇩2 # ps) ∧ C⇩1' ∈ set (p⇩2 # ps)) ∨ (C⇩0' = c⇩0 ∧ C⇩1' = c⇩1)"
using "3.hyps"(1) True p⇩1_def by blast
moreover have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs True "3.prems" by (auto split: prod.splits, metis Pair_inject)+
ultimately show ?thesis
by auto
next
case False
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (p⇩0, p⇩1) (p⇩2 # ps)"
using prod.collapse by blast
note defs = p⇩1_def C'_def
hence "(C⇩0' ∈ set (p⇩2 # ps) ∧ C⇩1' ∈ set (p⇩2 # ps)) ∨ (C⇩0' = p⇩0 ∧ C⇩1' = p⇩1)"
using "3.hyps"(2) p⇩1_def False by blast
moreover have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs False "3.prems" by (auto split: prod.splits, metis Pair_inject)+
ultimately show ?thesis
using A by auto
qed
qed auto
lemma find_closest_pair_c0_ne_c1:
"c⇩0 ≠ c⇩1 ⟹ distinct ps ⟹ (C⇩0, C⇩1) = find_closest_pair (c⇩0, c⇩1) ps ⟹ C⇩0 ≠ C⇩1"
proof (induction "(c⇩0, c⇩1)" ps arbitrary: c⇩0 c⇩1 C⇩0 C⇩1 rule: find_closest_pair.induct)
case (3 c⇩0 c⇩1 p⇩0 p⇩2 ps)
define p⇩1 where p⇩1_def: "p⇩1 = find_closest p⇩0 (dist c⇩0 c⇩1) (p⇩2 # ps)"
hence A: "p⇩0 ≠ p⇩1"
using "3.prems"(1,2)
by (metis distinct.simps(2) find_closest_set length_pos_if_in_set list.set_intros(1))
show ?case
proof (cases "dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1")
case True
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (c⇩0, c⇩1) (p⇩2 # ps)"
using prod.collapse by blast
note defs = p⇩1_def C'_def
hence "C⇩0' ≠ C⇩1'"
using "3.hyps"(1) "3.prems"(1,2) True p⇩1_def by simp
moreover have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs True "3.prems"(3) by (auto split: prod.splits, metis Pair_inject)+
ultimately show ?thesis
by simp
next
case False
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (p⇩0, p⇩1) (p⇩2 # ps)"
using prod.collapse by blast
note defs = p⇩1_def C'_def
hence "C⇩0' ≠ C⇩1'"
using "3.hyps"(2) "3.prems"(2) A False p⇩1_def by simp
moreover have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs False "3.prems"(3) by (auto split: prod.splits, metis Pair_inject)+
ultimately show ?thesis
by simp
qed
qed auto
lemma find_closest_pair_dist_mono:
assumes "(C⇩0, C⇩1) = find_closest_pair (c⇩0, c⇩1) ps"
shows "dist C⇩0 C⇩1 ≤ dist c⇩0 c⇩1"
using assms
proof (induction "(c⇩0, c⇩1)" ps arbitrary: c⇩0 c⇩1 C⇩0 C⇩1 rule: find_closest_pair.induct)
case (3 c⇩0 c⇩1 p⇩0 p⇩2 ps)
define p⇩1 where p⇩1_def: "p⇩1 = find_closest p⇩0 (dist c⇩0 c⇩1) (p⇩2 # ps)"
show ?case
proof (cases "dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1")
case True
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (c⇩0, c⇩1) (p⇩2 # ps)"
using prod.collapse by blast
note defs = p⇩1_def C'_def
hence "dist C⇩0' C⇩1' ≤ dist c⇩0 c⇩1"
using "3.hyps"(1) True p⇩1_def by simp
moreover have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs True "3.prems" by (auto split: prod.splits, metis Pair_inject)+
ultimately show ?thesis
by simp
next
case False
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (p⇩0, p⇩1) (p⇩2 # ps)"
using prod.collapse by blast
note defs = p⇩1_def C'_def
hence "dist C⇩0' C⇩1' ≤ dist p⇩0 p⇩1"
using "3.hyps"(2) False p⇩1_def by blast
moreover have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs False "3.prems"(1) by (auto split: prod.splits, metis Pair_inject)+
ultimately show ?thesis
using False by simp
qed
qed auto
lemma find_closest_pair_dist:
assumes "sorted_snd ps" "(C⇩0, C⇩1) = find_closest_pair (c⇩0, c⇩1) ps"
shows "sparse (dist C⇩0 C⇩1) (set ps)"
using assms
proof (induction "(c⇩0, c⇩1)" ps arbitrary: c⇩0 c⇩1 C⇩0 C⇩1 rule: find_closest_pair.induct)
case (3 c⇩0 c⇩1 p⇩0 p⇩2 ps)
define p⇩1 where p⇩1_def: "p⇩1 = find_closest p⇩0 (dist c⇩0 c⇩1) (p⇩2 # ps)"
show ?case
proof cases
assume "∃p ∈ set (p⇩2 # ps). dist p⇩0 p < dist c⇩0 c⇩1"
hence A: "∀p ∈ set (p⇩2 # ps). dist p⇩0 p⇩1 ≤ dist p⇩0 p" "dist p⇩0 p⇩1 < dist c⇩0 c⇩1"
using p⇩1_def find_closest_dist "3.prems"(1) le_less_trans by blast+
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (p⇩0, p⇩1) (p⇩2 # ps)"
using prod.collapse by blast
hence B: "(C⇩0', C⇩1') = find_closest_pair (c⇩0, c⇩1) (p⇩0 # p⇩2 # ps)"
using A(2) p⇩1_def by simp
have "sparse (dist C⇩0' C⇩1') (set (p⇩2 # ps))"
using "3.hyps"(2)[of p⇩1 C⇩0' C⇩1'] p⇩1_def C'_def "3.prems"(1) A(2) sorted_snd_def by fastforce
moreover have "dist C⇩0' C⇩1' ≤ dist p⇩0 p⇩1"
using C'_def find_closest_pair_dist_mono by blast
ultimately have "sparse (dist C⇩0' C⇩1') (set (p⇩0 # p⇩2 # ps))"
using A sparse_identity order_trans by blast
thus ?thesis
using B by (metis "3.prems"(2) Pair_inject)
next
assume A: "¬ (∃p ∈ set (p⇩2 # ps). dist p⇩0 p < dist c⇩0 c⇩1)"
hence B: "dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1"
using find_closest_set[of "p⇩2 # ps" p⇩0 "dist c⇩0 c⇩1"] p⇩1_def by auto
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = find_closest_pair (c⇩0, c⇩1) (p⇩2 # ps)"
using prod.collapse by blast
hence C: "(C⇩0', C⇩1') = find_closest_pair (c⇩0, c⇩1) (p⇩0 # p⇩2 # ps)"
using B p⇩1_def by simp
have "sparse (dist C⇩0' C⇩1') (set (p⇩2 # ps))"
using "3.hyps"(1)[of p⇩1 C⇩0' C⇩1'] p⇩1_def C'_def B "3.prems" sorted_snd_def by simp
moreover have "dist C⇩0' C⇩1' ≤ dist c⇩0 c⇩1"
using C'_def find_closest_pair_dist_mono by blast
ultimately have "sparse (dist C⇩0' C⇩1') (set (p⇩0 # p⇩2 # ps))"
using A sparse_identity[of "dist C⇩0' C⇩1'" "p⇩2 # ps" p⇩0] order_trans by force
thus ?thesis
using C by (metis "3.prems"(2) Pair_inject)
qed
qed (auto simp: sparse_def)
declare find_closest_pair.simps [simp del]
fun combine_tm :: "(point × point) ⇒ (point × point) ⇒ int ⇒ point list ⇒ (point × point) tm" where
"combine_tm (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps =1 (
let (c⇩0, c⇩1) = if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R) in
do {
ps' <- filter_tm (λp. dist p (l, snd p) < dist c⇩0 c⇩1) ps;
find_closest_pair_tm (c⇩0, c⇩1) ps'
}
)"
fun combine :: "(point × point) ⇒ (point × point) ⇒ int ⇒ point list ⇒ (point × point)" where
"combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps = (
let (c⇩0, c⇩1) = if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R) in
let ps' = filter (λp. dist p (l, snd p) < dist c⇩0 c⇩1) ps in
find_closest_pair (c⇩0, c⇩1) ps'
)"
lemma combine_eq_val_combine_tm:
"val (combine_tm (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps) = combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps"
by (auto simp: filter_eq_val_filter_tm find_closest_pair_eq_val_find_closest_pair_tm)
lemma combine_set:
assumes "(c⇩0, c⇩1) = combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps"
shows "(c⇩0 ∈ set ps ∧ c⇩1 ∈ set ps) ∨ (c⇩0 = p⇩0⇩L ∧ c⇩1 = p⇩1⇩L) ∨ (c⇩0 = p⇩0⇩R ∧ c⇩1 = p⇩1⇩R)"
proof -
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = (if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R))"
by metis
define ps' where ps'_def: "ps' = filter (λp. dist p (l, snd p) < dist C⇩0' C⇩1') ps"
obtain C⇩0 C⇩1 where C_def: "(C⇩0, C⇩1) = find_closest_pair (C⇩0', C⇩1') ps'"
using prod.collapse by blast
note defs = C'_def ps'_def C_def
have "(C⇩0 ∈ set ps' ∧ C⇩1 ∈ set ps') ∨ (C⇩0 = C⇩0' ∧ C⇩1 = C⇩1')"
using C_def find_closest_pair_set by blast+
hence "(C⇩0 ∈ set ps ∧ C⇩1 ∈ set ps)∨ (C⇩0 = C⇩0' ∧ C⇩1 = C⇩1')"
using ps'_def by auto
moreover have "C⇩0 = c⇩0" "C⇩1 = c⇩1"
using assms defs apply (auto split: if_splits prod.splits) by (metis Pair_inject)+
ultimately show ?thesis
using C'_def by (auto split: if_splits)
qed
lemma combine_c0_ne_c1:
assumes "p⇩0⇩L ≠ p⇩1⇩L" "p⇩0⇩R ≠ p⇩1⇩R" "distinct ps"
assumes "(c⇩0, c⇩1) = combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps"
shows "c⇩0 ≠ c⇩1"
proof -
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = (if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R))"
by metis
define ps' where ps'_def: "ps' = filter (λp. dist p (l, snd p) < dist C⇩0' C⇩1') ps"
obtain C⇩0 C⇩1 where C_def: "(C⇩0, C⇩1) = find_closest_pair (C⇩0', C⇩1') ps'"
using prod.collapse by blast
note defs = C'_def ps'_def C_def
have "C⇩0 ≠ C⇩1"
using defs find_closest_pair_c0_ne_c1[of C⇩0' C⇩1' ps'] assms by (auto split: if_splits)
moreover have "C⇩0 = c⇩0" "C⇩1 = c⇩1"
using assms(4) defs apply (auto split: if_splits prod.splits) by (metis Pair_inject)+
ultimately show ?thesis
by blast
qed
lemma combine_dist:
assumes "sorted_snd ps" "set ps = ps⇩L ∪ ps⇩R"
assumes "∀p ∈ ps⇩L. fst p ≤ l" "∀p ∈ ps⇩R. l ≤ fst p"
assumes "sparse (dist p⇩0⇩L p⇩1⇩L) ps⇩L" "sparse (dist p⇩0⇩R p⇩1⇩R) ps⇩R"
assumes "(c⇩0, c⇩1) = combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps"
shows "sparse (dist c⇩0 c⇩1) (set ps)"
proof -
obtain C⇩0' C⇩1' where C'_def: "(C⇩0', C⇩1') = (if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R))"
by metis
define ps' where ps'_def: "ps' = filter (λp. dist p (l, snd p) < dist C⇩0' C⇩1') ps"
obtain C⇩0 C⇩1 where C_def: "(C⇩0, C⇩1) = find_closest_pair (C⇩0', C⇩1') ps'"
using prod.collapse by blast
note defs = C'_def ps'_def C_def
have EQ: "C⇩0 = c⇩0" "C⇩1 = c⇩1"
using defs assms(7) apply (auto split: if_splits prod.splits) by (metis Pair_inject)+
have ps': "ps' = filter (λp. l - dist C⇩0' C⇩1' < fst p ∧ fst p < l + dist C⇩0' C⇩1') ps"
using ps'_def dist_transform by simp
have ps⇩L: "sparse (dist C⇩0' C⇩1') ps⇩L"
using assms(3,5) C'_def sparse_def apply (auto split: if_splits) by force+
have ps⇩R: "sparse (dist C⇩0' C⇩1') ps⇩R"
using assms(4,6) C'_def sparse_def apply (auto split: if_splits) by force+
have "sorted_snd ps'"
using ps'_def assms(1) sorted_snd_def sorted_wrt_filter by blast
hence *: "sparse (dist C⇩0 C⇩1) (set ps')"
using find_closest_pair_dist C_def by simp
have "∀p⇩0 ∈ set ps. ∀p⇩1 ∈ set ps. p⇩0 ≠ p⇩1 ∧ dist p⇩0 p⇩1 < dist C⇩0' C⇩1' ⟶ p⇩0 ∈ set ps' ∧ p⇩1 ∈ set ps'"
using set_band_filter ps' ps⇩L ps⇩R assms(2,3,4) by blast
moreover have "dist C⇩0 C⇩1 ≤ dist C⇩0' C⇩1'"
using C_def find_closest_pair_dist_mono by blast
ultimately have "∀p⇩0 ∈ set ps. ∀p⇩1 ∈ set ps. p⇩0 ≠ p⇩1 ∧ dist p⇩0 p⇩1 < dist C⇩0 C⇩1 ⟶ p⇩0 ∈ set ps' ∧ p⇩1 ∈ set ps'"
by simp
hence "sparse (dist C⇩0 C⇩1) (set ps)"
using sparse_def * by (meson not_less)
thus ?thesis
using EQ by blast
qed
declare combine.simps [simp del]
declare combine_tm.simps[simp del]
subsubsection "Divide and Conquer Algorithm"
declare split_at_take_drop_conv [simp add]
function closest_pair_rec_tm :: "point list ⇒ (point list × point × point) tm" where
"closest_pair_rec_tm xs =1 (
do {
n <- length_tm xs;
if n ≤ 3 then
do {
ys <- mergesort_tm snd xs;
p <- closest_pair_bf_tm xs;
return (ys, p)
}
else
do {
(xs⇩L, xs⇩R) <- split_at_tm (n div 2) xs;
(ys⇩L, p⇩0⇩L, p⇩1⇩L) <- closest_pair_rec_tm xs⇩L;
(ys⇩R, p⇩0⇩R, p⇩1⇩R) <- closest_pair_rec_tm xs⇩R;
ys <- merge_tm snd ys⇩L ys⇩R;
(p⇩0, p⇩1) <- combine_tm (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) (fst (hd xs⇩R)) ys;
return (ys, p⇩0, p⇩1)
}
}
)"
by pat_completeness auto
termination closest_pair_rec_tm
by (relation "Wellfounded.measure (λxs. length xs)")
(auto simp add: length_eq_val_length_tm split_at_eq_val_split_at_tm)
function closest_pair_rec :: "point list ⇒ (point list * point * point)" where
"closest_pair_rec xs = (
let n = length xs in
if n ≤ 3 then
(mergesort snd xs, closest_pair_bf xs)
else
let (xs⇩L, xs⇩R) = split_at (n div 2) xs in
let (ys⇩L, p⇩0⇩L, p⇩1⇩L) = closest_pair_rec xs⇩L in
let (ys⇩R, p⇩0⇩R, p⇩1⇩R) = closest_pair_rec xs⇩R in
let ys = merge snd ys⇩L ys⇩R in
(ys, combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) (fst (hd xs⇩R)) ys)
)"
by pat_completeness auto
termination closest_pair_rec
by (relation "Wellfounded.measure (λxs. length xs)")
(auto simp: Let_def)
declare split_at_take_drop_conv [simp del]
lemma closest_pair_rec_simps:
assumes "n = length xs" "¬ (n ≤ 3)"
shows "closest_pair_rec xs = (
let (xs⇩L, xs⇩R) = split_at (n div 2) xs in
let (ys⇩L, p⇩0⇩L, p⇩1⇩L) = closest_pair_rec xs⇩L in
let (ys⇩R, p⇩0⇩R, p⇩1⇩R) = closest_pair_rec xs⇩R in
let ys = merge snd ys⇩L ys⇩R in
(ys, combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) (fst (hd xs⇩R)) ys)
)"
using assms by (auto simp: Let_def)
declare closest_pair_rec.simps [simp del]
lemma closest_pair_rec_eq_val_closest_pair_rec_tm:
"val (closest_pair_rec_tm xs) = closest_pair_rec xs"
proof (induction rule: length_induct)
case (1 xs)
define n where "n = length xs"
obtain xs⇩L xs⇩R where xs_def: "(xs⇩L, xs⇩R) = split_at (n div 2) xs"
by (metis surj_pair)
note defs = n_def xs_def
show ?case
proof cases
assume "n ≤ 3"
then show ?thesis
using defs
by (auto simp: length_eq_val_length_tm mergesort_eq_val_mergesort_tm
closest_pair_bf_eq_val_closest_pair_bf_tm closest_pair_rec.simps)
next
assume asm: "¬ n ≤ 3"
have "length xs⇩L < length xs" "length xs⇩R < length xs"
using asm defs by (auto simp: split_at_take_drop_conv)
hence "val (closest_pair_rec_tm xs⇩L) = closest_pair_rec xs⇩L"
"val (closest_pair_rec_tm xs⇩R) = closest_pair_rec xs⇩R"
using "1.IH" by blast+
thus ?thesis
using asm defs
apply (subst closest_pair_rec.simps, subst closest_pair_rec_tm.simps)
by (auto simp del: closest_pair_rec_tm.simps
simp add: Let_def length_eq_val_length_tm merge_eq_val_merge_tm
split_at_eq_val_split_at_tm combine_eq_val_combine_tm
split: prod.split)
qed
qed
lemma closest_pair_rec_set_length_sorted_snd:
assumes "(ys, p) = closest_pair_rec xs"
shows "set ys = set xs ∧ length ys = length xs ∧ sorted_snd ys"
using assms
proof (induction xs arbitrary: ys p rule: length_induct)
case (1 xs)
let ?n = "length xs"
show ?case
proof (cases "?n ≤ 3")
case True
thus ?thesis using "1.prems" sorted_snd_def
by (auto simp: mergesort closest_pair_rec.simps)
next
case False
obtain XS⇩L XS⇩R where XS⇩L⇩R_def: "(XS⇩L, XS⇩R) = split_at (?n div 2) xs"
using prod.collapse by blast
define L where "L = fst (hd XS⇩R)"
obtain YS⇩L P⇩L where YSP⇩L_def: "(YS⇩L, P⇩L) = closest_pair_rec XS⇩L"
using prod.collapse by blast
obtain YS⇩R P⇩R where YSP⇩R_def: "(YS⇩R, P⇩R) = closest_pair_rec XS⇩R"
using prod.collapse by blast
define YS where "YS = merge (λp. snd p) YS⇩L YS⇩R"
define P where "P = combine P⇩L P⇩R L YS"
note defs = XS⇩L⇩R_def L_def YSP⇩L_def YSP⇩R_def YS_def P_def
have "length XS⇩L < length xs" "length XS⇩R < length xs"
using False defs by (auto simp: split_at_take_drop_conv)
hence IH: "set XS⇩L = set YS⇩L" "set XS⇩R = set YS⇩R"
"length XS⇩L = length YS⇩L" "length XS⇩R = length YS⇩R"
"sorted_snd YS⇩L" "sorted_snd YS⇩R"
using "1.IH" defs by metis+
have "set xs = set XS⇩L ∪ set XS⇩R"
using defs by (auto simp: set_take_drop split_at_take_drop_conv)
hence SET: "set xs = set YS"
using set_merge IH(1,2) defs by fast
have "length xs = length XS⇩L + length XS⇩R"
using defs by (auto simp: split_at_take_drop_conv)
hence LENGTH: "length xs = length YS"
using IH(3,4) length_merge defs by metis
have SORTED: "sorted_snd YS"
using IH(5,6) by (simp add: defs sorted_snd_def sorted_merge)
have "(YS, P) = closest_pair_rec xs"
using False closest_pair_rec_simps defs by (auto simp: Let_def split: prod.split)
hence "(ys, p) = (YS, P)"
using "1.prems" by argo
thus ?thesis
using SET LENGTH SORTED by simp
qed
qed
lemma closest_pair_rec_distinct:
assumes "distinct xs" "(ys, p) = closest_pair_rec xs"
shows "distinct ys"
using assms
proof (induction xs arbitrary: ys p rule: length_induct)
case (1 xs)
let ?n = "length xs"
show ?case
proof (cases "?n ≤ 3")
case True
thus ?thesis using "1.prems"
by (auto simp: mergesort closest_pair_rec.simps)
next
case False
obtain XS⇩L XS⇩R where XS⇩L⇩R_def: "(XS⇩L, XS⇩R) = split_at (?n div 2) xs"
using prod.collapse by blast
define L where "L = fst (hd XS⇩R)"
obtain YS⇩L P⇩L where YSP⇩L_def: "(YS⇩L, P⇩L) = closest_pair_rec XS⇩L"
using prod.collapse by blast
obtain YS⇩R P⇩R where YSP⇩R_def: "(YS⇩R, P⇩R) = closest_pair_rec XS⇩R"
using prod.collapse by blast
define YS where "YS = merge (λp. snd p) YS⇩L YS⇩R"
define P where "P = combine P⇩L P⇩R L YS"
note defs = XS⇩L⇩R_def L_def YSP⇩L_def YSP⇩R_def YS_def P_def
have "length XS⇩L < length xs" "length XS⇩R < length xs"
using False defs by (auto simp: split_at_take_drop_conv)
moreover have "distinct XS⇩L" "distinct XS⇩R"
using "1.prems"(1) defs by (auto simp: split_at_take_drop_conv)
ultimately have IH: "distinct YS⇩L" "distinct YS⇩R"
using "1.IH" defs by blast+
have "set XS⇩L ∩ set XS⇩R = {}"
using "1.prems"(1) defs by (auto simp: split_at_take_drop_conv set_take_disj_set_drop_if_distinct)
moreover have "set XS⇩L = set YS⇩L" "set XS⇩R = set YS⇩R"
using closest_pair_rec_set_length_sorted_snd defs by blast+
ultimately have "set YS⇩L ∩ set YS⇩R = {}"
by blast
hence DISTINCT: "distinct YS"
using distinct_merge IH defs by blast
have "(YS, P) = closest_pair_rec xs"
using False closest_pair_rec_simps defs by (auto simp: Let_def split: prod.split)
hence "(ys, p) = (YS, P)"
using "1.prems" by argo
thus ?thesis
using DISTINCT by blast
qed
qed
lemma closest_pair_rec_c0_c1:
assumes "1 < length xs" "distinct xs" "(ys, c⇩0, c⇩1) = closest_pair_rec xs"
shows "c⇩0 ∈ set xs ∧ c⇩1 ∈ set xs ∧ c⇩0 ≠ c⇩1"
using assms
proof (induction xs arbitrary: ys c⇩0 c⇩1 rule: length_induct)
case (1 xs)
let ?n = "length xs"
show ?case
proof (cases "?n ≤ 3")
case True
hence "(c⇩0, c⇩1) = closest_pair_bf xs"
using "1.prems"(3) closest_pair_rec.simps by simp
thus ?thesis
using "1.prems"(1,2) closest_pair_bf_c0_c1 by simp
next
case False
obtain XS⇩L XS⇩R where XS⇩L⇩R_def: "(XS⇩L, XS⇩R) = split_at (?n div 2) xs"
using prod.collapse by blast
define L where "L = fst (hd XS⇩R)"
obtain YS⇩L C⇩0⇩L C⇩1⇩L where YSC⇩0⇩1⇩L_def: "(YS⇩L, C⇩0⇩L, C⇩1⇩L) = closest_pair_rec XS⇩L"
using prod.collapse by metis
obtain YS⇩R C⇩0⇩R C⇩1⇩R where YSC⇩0⇩1⇩R_def: "(YS⇩R, C⇩0⇩R, C⇩1⇩R) = closest_pair_rec XS⇩R"
using prod.collapse by metis
define YS where "YS = merge (λp. snd p) YS⇩L YS⇩R"
obtain C⇩0 C⇩1 where C⇩0⇩1_def: "(C⇩0, C⇩1) = combine (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) L YS"
using prod.collapse by metis
note defs = XS⇩L⇩R_def L_def YSC⇩0⇩1⇩L_def YSC⇩0⇩1⇩R_def YS_def C⇩0⇩1_def
have "1 < length XS⇩L" "length XS⇩L < length xs" "distinct XS⇩L"
using False "1.prems"(2) defs by (auto simp: split_at_take_drop_conv)
hence "C⇩0⇩L ∈ set XS⇩L" "C⇩1⇩L ∈ set XS⇩L" and IHL1: "C⇩0⇩L ≠ C⇩1⇩L"
using "1.IH" defs by metis+
hence IHL2: "C⇩0⇩L ∈ set xs" "C⇩1⇩L ∈ set xs"
using split_at_take_drop_conv in_set_takeD fst_conv defs by metis+
have "1 < length XS⇩R" "length XS⇩R < length xs" "distinct XS⇩R"
using False "1.prems"(2) defs by (auto simp: split_at_take_drop_conv)
hence "C⇩0⇩R ∈ set XS⇩R" "C⇩1⇩R ∈ set XS⇩R" and IHR1: "C⇩0⇩R ≠ C⇩1⇩R"
using "1.IH" defs by metis+
hence IHR2: "C⇩0⇩R ∈ set xs" "C⇩1⇩R ∈ set xs"
using split_at_take_drop_conv in_set_dropD snd_conv defs by metis+
have *: "(YS, C⇩0, C⇩1) = closest_pair_rec xs"
using False closest_pair_rec_simps defs by (auto simp: Let_def split: prod.split)
have YS: "set xs = set YS" "distinct YS"
using "1.prems"(2) closest_pair_rec_set_length_sorted_snd closest_pair_rec_distinct * by blast+
have "C⇩0 ∈ set xs" "C⇩1 ∈ set xs"
using combine_set IHL2 IHR2 YS defs by blast+
moreover have "C⇩0 ≠ C⇩1"
using combine_c0_ne_c1 IHL1(1) IHR1(1) YS defs by blast
ultimately show ?thesis
using "1.prems"(3) * by (metis Pair_inject)
qed
qed
lemma closest_pair_rec_dist:
assumes "1 < length xs" "sorted_fst xs" "(ys, c⇩0, c⇩1) = closest_pair_rec xs"
shows "sparse (dist c⇩0 c⇩1) (set xs)"
using assms
proof (induction xs arbitrary: ys c⇩0 c⇩1 rule: length_induct)
case (1 xs)
let ?n = "length xs"
show ?case
proof (cases "?n ≤ 3")
case True
hence "(c⇩0, c⇩1) = closest_pair_bf xs"
using "1.prems"(3) closest_pair_rec.simps by simp
thus ?thesis
using "1.prems"(1,3) closest_pair_bf_dist by metis
next
case False
obtain XS⇩L XS⇩R where XS⇩L⇩R_def: "(XS⇩L, XS⇩R) = split_at (?n div 2) xs"
using prod.collapse by blast
define L where "L = fst (hd XS⇩R)"
obtain YS⇩L C⇩0⇩L C⇩1⇩L where YSC⇩0⇩1⇩L_def: "(YS⇩L, C⇩0⇩L, C⇩1⇩L) = closest_pair_rec XS⇩L"
using prod.collapse by metis
obtain YS⇩R C⇩0⇩R C⇩1⇩R where YSC⇩0⇩1⇩R_def: "(YS⇩R, C⇩0⇩R, C⇩1⇩R) = closest_pair_rec XS⇩R"
using prod.collapse by metis
define YS where "YS = merge (λp. snd p) YS⇩L YS⇩R"
obtain C⇩0 C⇩1 where C⇩0⇩1_def: "(C⇩0, C⇩1) = combine (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) L YS"
using prod.collapse by metis
note defs = XS⇩L⇩R_def L_def YSC⇩0⇩1⇩L_def YSC⇩0⇩1⇩R_def YS_def C⇩0⇩1_def
have XSLR: "XS⇩L = take (?n div 2) xs" "XS⇩R = drop (?n div 2) xs"
using defs by (auto simp: split_at_take_drop_conv)
have "1 < length XS⇩L" "length XS⇩L < length xs"
using False XSLR by simp_all
moreover have "sorted_fst XS⇩L"
using "1.prems"(2) XSLR by (auto simp: sorted_fst_def sorted_wrt_take)
ultimately have L: "sparse (dist C⇩0⇩L C⇩1⇩L) (set XS⇩L)"
"set XS⇩L = set YS⇩L"
using 1 closest_pair_rec_set_length_sorted_snd closest_pair_rec_c0_c1
YSC⇩0⇩1⇩L_def by blast+
hence IHL: "sparse (dist C⇩0⇩L C⇩1⇩L) (set YS⇩L)"
by argo
have "1 < length XS⇩R" "length XS⇩R < length xs"
using False XSLR by simp_all
moreover have "sorted_fst XS⇩R"
using "1.prems"(2) XSLR by (auto simp: sorted_fst_def sorted_wrt_drop)
ultimately have R: "sparse (dist C⇩0⇩R C⇩1⇩R) (set XS⇩R)"
"set XS⇩R = set YS⇩R"
using 1 closest_pair_rec_set_length_sorted_snd closest_pair_rec_c0_c1
YSC⇩0⇩1⇩R_def by blast+
hence IHR: "sparse (dist C⇩0⇩R C⇩1⇩R) (set YS⇩R)"
by argo
have *: "(YS, C⇩0, C⇩1) = closest_pair_rec xs"
using False closest_pair_rec_simps defs by (auto simp: Let_def split: prod.split)
have "set xs = set YS" "sorted_snd YS"
using "1.prems"(2) closest_pair_rec_set_length_sorted_snd closest_pair_rec_distinct * by blast+
moreover have "∀p ∈ set YS⇩L. fst p ≤ L"
using False "1.prems"(2) XSLR L_def L(2) sorted_fst_take_less_hd_drop by simp
moreover have "∀p ∈ set YS⇩R. L ≤ fst p"
using False "1.prems"(2) XSLR L_def R(2) sorted_fst_hd_drop_less_drop by simp
moreover have "set YS = set YS⇩L ∪ set YS⇩R"
using set_merge defs by fast
moreover have "(C⇩0, C⇩1) = combine (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) L YS"
by (auto simp add: defs)
ultimately have "sparse (dist C⇩0 C⇩1) (set xs)"
using combine_dist IHL IHR by auto
moreover have "(YS, C⇩0, C⇩1) = (ys, c⇩0, c⇩1)"
using "1.prems"(3) * by simp
ultimately show ?thesis
by blast
qed
qed
fun closest_pair_tm :: "point list ⇒ (point * point) tm" where
"closest_pair_tm [] =1 return undefined"
| "closest_pair_tm [_] =1 return undefined"
| "closest_pair_tm ps =1 (
do {
xs <- mergesort_tm fst ps;
(_, p) <- closest_pair_rec_tm xs;
return p
}
)"
fun closest_pair :: "point list ⇒ (point * point)" where
"closest_pair [] = undefined"
| "closest_pair [_] = undefined"
| "closest_pair ps = (let (_, p) = closest_pair_rec (mergesort fst ps) in p)"
lemma closest_pair_eq_val_closest_pair_tm:
"val (closest_pair_tm ps) = closest_pair ps"
by (induction ps rule: induct_list012)
(auto simp del: closest_pair_rec_tm.simps mergesort_tm.simps
simp add: closest_pair_rec_eq_val_closest_pair_rec_tm mergesort_eq_val_mergesort_tm
split: prod.split)
lemma closest_pair_simps:
"1 < length ps ⟹ closest_pair ps = (let (_, p) = closest_pair_rec (mergesort fst ps) in p)"
by (induction ps rule: induct_list012) auto
declare closest_pair.simps [simp del]
theorem closest_pair_c0_c1:
assumes "1 < length ps" "distinct ps" "(c⇩0, c⇩1) = closest_pair ps"
shows "c⇩0 ∈ set ps" "c⇩1 ∈ set ps" "c⇩0 ≠ c⇩1"
using assms closest_pair_rec_c0_c1[of "mergesort fst ps"]
by (auto simp: closest_pair_simps mergesort split: prod.splits)
theorem closest_pair_dist:
assumes "1 < length ps" "(c⇩0, c⇩1) = closest_pair ps"
shows "sparse (dist c⇩0 c⇩1) (set ps)"
using assms sorted_fst_def closest_pair_rec_dist[of "mergesort fst ps"] closest_pair_rec_c0_c1[of "mergesort fst ps"]
by (auto simp: closest_pair_simps mergesort split: prod.splits)
subsection "Time Complexity Proof"
subsubsection "Core Argument"
lemma core_argument:
fixes δ :: real and p :: point and ps :: "point list"
assumes "distinct (p # ps)" "sorted_snd (p # ps)" "0 ≤ δ" "set (p # ps) = ps⇩L ∪ ps⇩R"
assumes "∀q ∈ set (p # ps). l - δ < fst q ∧ fst q < l + δ"
assumes "∀q ∈ ps⇩L. fst q ≤ l" "∀q ∈ ps⇩R. l ≤ fst q"
assumes "sparse δ ps⇩L" "sparse δ ps⇩R"
shows "length (filter (λq. snd q - snd p ≤ δ) ps) ≤ 7"
proof -
define PS where "PS = p # ps"
define R where "R = cbox (l - δ, snd p) (l + δ, snd p + δ)"
define RPS where "RPS = { p ∈ set PS. p ∈ R }"
define LSQ where "LSQ = cbox (l - δ, snd p) (l, snd p + δ)"
define LSQPS where "LSQPS = { p ∈ ps⇩L. p ∈ LSQ }"
define RSQ where "RSQ = cbox (l, snd p) (l + δ, snd p + δ)"
define RSQPS where "RSQPS = { p ∈ ps⇩R. p ∈ RSQ }"
note defs = PS_def R_def RPS_def LSQ_def LSQPS_def RSQ_def RSQPS_def
have "R = LSQ ∪ RSQ"
using defs cbox_right_un by auto
moreover have "∀p ∈ ps⇩L. p ∈ RSQ ⟶ p ∈ LSQ"
using RSQ_def LSQ_def assms(6) by auto
moreover have "∀p ∈ ps⇩R. p ∈ LSQ ⟶ p ∈ RSQ"
using RSQ_def LSQ_def assms(7) by auto
ultimately have "RPS = LSQPS ∪ RSQPS"
using LSQPS_def RSQPS_def PS_def RPS_def assms(4) by blast
have "sparse δ LSQPS"
using assms(8) LSQPS_def sparse_def by simp
hence CLSQPS: "card LSQPS ≤ 4"
using max_points_square[of LSQPS "l - δ" "snd p" δ] assms(3) LSQ_def LSQPS_def by auto
have "sparse δ RSQPS"
using assms(9) RSQPS_def sparse_def by simp
hence CRSQPS: "card RSQPS ≤ 4"
using max_points_square[of RSQPS l "snd p" δ] assms(3) RSQ_def RSQPS_def by auto
have CRPS: "card RPS ≤ 8"
using CLSQPS CRSQPS card_Un_le[of LSQPS RSQPS] ‹RPS = LSQPS ∪ RSQPS› by auto
have "set (p # filter (λq. snd q - snd p ≤ δ) ps) ⊆ RPS"
proof standard
fix q
assume *: "q ∈ set (p # filter (λq. snd q - snd p ≤ δ) ps)"
hence CPS: "q ∈ set PS"
using PS_def by auto
hence "snd p ≤ snd q" "snd q ≤ snd p + δ"
using assms(2,3) PS_def sorted_snd_def * by (auto split: if_splits)
moreover have "l - δ < fst q" "fst q < l + δ"
using CPS assms(5) PS_def by blast+
ultimately have "q ∈ R"
using R_def mem_cbox_2D[of "l - δ" "fst q" "l + δ" "snd p" "snd q" "snd p + δ"]
by (simp add: prod.case_eq_if)
thus "q ∈ RPS"
using CPS RPS_def by simp
qed
moreover have "finite RPS"
by (simp add: RPS_def)
ultimately have "card (set (p # filter (λq. snd q - snd p ≤ δ) ps)) ≤ 8"
using CRPS card_mono[of RPS "set (p # filter (λq. snd q - snd p ≤ δ) ps)"] by simp
moreover have "distinct (p # filter (λq. snd q - snd p ≤ δ) ps)"
using assms(1) by simp
ultimately have "length (p # filter (λq. snd q - snd p ≤ δ) ps) ≤ 8"
using assms(1) PS_def distinct_card by metis
thus ?thesis
by simp
qed
subsubsection "Combine Step"
fun t_find_closest :: "point ⇒ real ⇒ point list ⇒ nat" where
"t_find_closest _ _ [] = 1"
| "t_find_closest _ _ [_] = 1"
| "t_find_closest p δ (p⇩0 # ps) = 1 + (
if δ ≤ snd p⇩0 - snd p then 0
else t_find_closest p (min δ (dist p p⇩0)) ps
)"
lemma t_find_closest_eq_time_find_closest_tm:
"t_find_closest p δ ps = time (find_closest_tm p δ ps)"
by (induction p δ ps rule: t_find_closest.induct)
(auto simp: time_simps)
lemma t_find_closest_mono:
"δ' ≤ δ ⟹ t_find_closest p δ' ps ≤ t_find_closest p δ ps"
by (induction rule: t_find_closest.induct)
(auto simp: Let_def min_def)
lemma t_find_closest_cnt:
"t_find_closest p δ ps ≤ 1 + length (filter (λq. snd q - snd p ≤ δ) ps)"
proof (induction p δ ps rule: t_find_closest.induct)
case (3 p δ p⇩0 p⇩2 ps)
show ?case
proof (cases "δ ≤ snd p⇩0 - snd p")
case True
thus ?thesis
by simp
next
case False
hence *: "snd p⇩0 - snd p ≤ δ"
by simp
have "t_find_closest p δ (p⇩0 # p⇩2 # ps) = 1 + t_find_closest p (min δ (dist p p⇩0)) (p⇩2 # ps)"
using False by simp
also have "... ≤ 1 + 1 + length (filter (λq. snd q - snd p ≤ min δ (dist p p⇩0)) (p⇩2 # ps))"
using False 3 by simp
also have "... ≤ 1 + 1 + length (filter (λq. snd q - snd p ≤ δ) (p⇩2 # ps))"
using * by (meson add_le_cancel_left length_filter_P_impl_Q min.bounded_iff)
also have "... ≤ 1 + length (filter (λq. snd q - snd p ≤ δ) (p⇩0 # p⇩2 # ps))"
using False by simp
ultimately show ?thesis
by simp
qed
qed auto
corollary t_find_closest_bound:
fixes δ :: real and p :: point and ps :: "point list" and l :: int
assumes "distinct (p # ps)" "sorted_snd (p # ps)" "0 ≤ δ" "set (p # ps) = ps⇩L ∪ ps⇩R"
assumes "∀p' ∈ set (p # ps). l - δ < fst p' ∧ fst p' < l + δ"
assumes "∀p ∈ ps⇩L. fst p ≤ l" "∀p ∈ ps⇩R. l ≤ fst p"
assumes "sparse δ ps⇩L" "sparse δ ps⇩R"
shows "t_find_closest p δ ps ≤ 8"
using assms core_argument[of p ps δ ps⇩L ps⇩R l] t_find_closest_cnt[of p δ ps] by linarith
fun t_find_closest_pair :: "(point * point) ⇒ point list ⇒ nat" where
"t_find_closest_pair _ [] = 1"
| "t_find_closest_pair _ [_] = 1"
| "t_find_closest_pair (c⇩0, c⇩1) (p⇩0 # ps) = 1 + (
let p⇩1 = find_closest p⇩0 (dist c⇩0 c⇩1) ps in
t_find_closest p⇩0 (dist c⇩0 c⇩1) ps + (
if dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1 then
t_find_closest_pair (c⇩0, c⇩1) ps
else
t_find_closest_pair (p⇩0, p⇩1) ps
))"
lemma t_find_closest_pair_eq_time_find_closest_pair_tm:
"t_find_closest_pair (c⇩0, c⇩1) ps = time (find_closest_pair_tm (c⇩0, c⇩1) ps)"
by (induction "(c⇩0, c⇩1)" ps arbitrary: c⇩0 c⇩1 rule: t_find_closest_pair.induct)
(auto simp: time_simps find_closest_eq_val_find_closest_tm t_find_closest_eq_time_find_closest_tm)
lemma t_find_closest_pair_bound:
assumes "distinct ps" "sorted_snd ps" "δ = dist c⇩0 c⇩1" "set ps = ps⇩L ∪ ps⇩R"
assumes "∀p ∈ set ps. l - Δ < fst p ∧ fst p < l + Δ"
assumes "∀p ∈ ps⇩L. fst p ≤ l" "∀p ∈ ps⇩R. l ≤ fst p"
assumes "sparse Δ ps⇩L" "sparse Δ ps⇩R" "δ ≤ Δ"
shows "t_find_closest_pair (c⇩0, c⇩1) ps ≤ 9 * length ps + 1"
using assms
proof (induction "(c⇩0, c⇩1)" ps arbitrary: δ c⇩0 c⇩1 ps⇩L ps⇩R rule: t_find_closest_pair.induct)
case (3 c⇩0 c⇩1 p⇩0 p⇩2 ps)
let ?ps = "p⇩2 # ps"
define p⇩1 where p⇩1_def: "p⇩1 = find_closest p⇩0 (dist c⇩0 c⇩1) ?ps"
define PS⇩L where PS⇩L_def: "PS⇩L = ps⇩L - { p⇩0 }"
define PS⇩R where PS⇩R_def: "PS⇩R = ps⇩R - { p⇩0 }"
note defs = p⇩1_def PS⇩L_def PS⇩R_def
have *: "0 ≤ Δ"
using "3.prems"(3,10) zero_le_dist[of c⇩0 c⇩1] by argo
hence "t_find_closest p⇩0 Δ ?ps ≤ 8"
using t_find_closest_bound[of p⇩0 ?ps Δ ps⇩L ps⇩R] "3.prems" by blast
hence A: "t_find_closest p⇩0 (dist c⇩0 c⇩1) ?ps ≤ 8"
by (metis "3.prems"(3,10) order_trans t_find_closest_mono)
have B: "distinct ?ps" "sorted_snd ?ps"
using "3.prems"(1,2) sorted_snd_def by simp_all
have C: "set ?ps = PS⇩L ∪ PS⇩R"
using defs "3.prems"(1,4) by auto
have D: "∀p ∈ set ?ps. l - Δ < fst p ∧ fst p < l + Δ"
using "3.prems"(5) by simp
have E: "∀p ∈ PS⇩L. fst p ≤ l" "∀p ∈ PS⇩R. l ≤ fst p"
using defs "3.prems"(6,7) by simp_all
have F: "sparse Δ PS⇩L" "sparse Δ PS⇩R"
using defs "3.prems"(8,9) sparse_def by simp_all
show ?case
proof (cases "dist c⇩0 c⇩1 ≤ dist p⇩0 p⇩1")
case True
hence "t_find_closest_pair (c⇩0, c⇩1) ?ps ≤ 9 * length ?ps + 1"
using "3.hyps"(1) "3.prems"(3,10) defs(1) B C D E F by blast
moreover have "t_find_closest_pair (c⇩0, c⇩1) (p⇩0 # ?ps) =
1 + t_find_closest p⇩0 (dist c⇩0 c⇩1) ?ps + t_find_closest_pair (c⇩0, c⇩1) ?ps"
using defs True by (auto split: prod.splits)
ultimately show ?thesis
using A by auto
next
case False
moreover have "0 ≤ dist p⇩0 p⇩1"
by auto
ultimately have "t_find_closest_pair (p⇩0, p⇩1) ?ps ≤ 9 * length ?ps + 1"
using "3.hyps"(2) "3.prems"(3,10) defs(1) B C D E F by auto
moreover have "t_find_closest_pair (c⇩0, c⇩1) (p⇩0 # ?ps) =
1 + t_find_closest p⇩0 (dist c⇩0 c⇩1) ?ps + t_find_closest_pair (p⇩0, p⇩1) ?ps"
using defs False by (auto split: prod.splits)
ultimately show ?thesis
using A by simp
qed
qed auto
fun t_combine :: "(point * point) ⇒ (point * point) ⇒ int ⇒ point list ⇒ nat" where
"t_combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps = 1 + (
let (c⇩0, c⇩1) = if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R) in
let ps' = filter (λp. dist p (l, snd p) < dist c⇩0 c⇩1) ps in
time (filter_tm (λp. dist p (l, snd p) < dist c⇩0 c⇩1) ps) + t_find_closest_pair (c⇩0, c⇩1) ps'
)"
lemma t_combine_eq_time_combine_tm:
"t_combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps = time (combine_tm (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps)"
by (auto simp: combine_tm.simps time_simps t_find_closest_pair_eq_time_find_closest_pair_tm filter_eq_val_filter_tm)
lemma t_combine_bound:
fixes ps :: "point list"
assumes "distinct ps" "sorted_snd ps" "set ps = ps⇩L ∪ ps⇩R"
assumes "∀p ∈ ps⇩L. fst p ≤ l" "∀p ∈ ps⇩R. l ≤ fst p"
assumes "sparse (dist p⇩0⇩L p⇩1⇩L) ps⇩L" "sparse (dist p⇩0⇩R p⇩1⇩R) ps⇩R"
shows "t_combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps ≤ 10 * length ps + 3"
proof -
obtain c⇩0 c⇩1 where c_def:
"(c⇩0, c⇩1) = (if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R))" by metis
let ?P = "(λp. dist p (l, snd p) < dist c⇩0 c⇩1)"
define ps' where ps'_def: "ps' = filter ?P ps"
define ps⇩L' where ps⇩L'_def: "ps⇩L' = { p ∈ ps⇩L. ?P p }"
define ps⇩R' where ps⇩R'_def: "ps⇩R' = { p ∈ ps⇩R. ?P p }"
note defs = c_def ps'_def ps⇩L'_def ps⇩R'_def
have "sparse (dist c⇩0 c⇩1) ps⇩L" "sparse (dist c⇩0 c⇩1) ps⇩R"
using assms(6,7) sparse_mono c_def by (auto split: if_splits)
hence "sparse (dist c⇩0 c⇩1) ps⇩L'" "sparse (dist c⇩0 c⇩1) ps⇩R'"
using ps⇩L'_def ps⇩R'_def sparse_def by auto
moreover have "distinct ps'"
using ps'_def assms(1) by simp
moreover have "sorted_snd ps'"
using ps'_def assms(2) sorted_snd_def sorted_wrt_filter by blast
moreover have "0 ≤ dist c⇩0 c⇩1"
by simp
moreover have "set ps' = ps⇩L' ∪ ps⇩R'"
using assms(3) defs(2,3,4) filter_Un by auto
moreover have "∀p ∈ set ps'. l - dist c⇩0 c⇩1 < fst p ∧ fst p < l + dist c⇩0 c⇩1"
using ps'_def dist_transform by force
moreover have "∀p ∈ ps⇩L'. fst p ≤ l" "∀p ∈ ps⇩R'. l ≤ fst p"
using assms(4,5) ps⇩L'_def ps⇩R'_def by blast+
ultimately have "t_find_closest_pair (c⇩0, c⇩1) ps' ≤ 9 * length ps' + 1"
using t_find_closest_pair_bound by blast
moreover have "length ps' ≤ length ps"
using ps'_def by simp
ultimately have *: "t_find_closest_pair (c⇩0, c⇩1) ps' ≤ 9 * length ps + 1"
by simp
have "t_combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps =
1 + time (filter_tm ?P ps) + t_find_closest_pair (c⇩0, c⇩1) ps'"
using defs by (auto split: prod.splits)
also have "... = 2 + length ps + t_find_closest_pair (c⇩0, c⇩1) ps'"
using time_filter_tm by auto
finally show ?thesis
using * by simp
qed
declare t_combine.simps [simp del]
subsubsection "Divide and Conquer Algorithm"
lemma time_closest_pair_rec_tm_simps_1:
assumes "length xs ≤ 3"
shows "time (closest_pair_rec_tm xs) = 1 + time (length_tm xs) + time (mergesort_tm snd xs) + time (closest_pair_bf_tm xs)"
using assms by (auto simp: time_simps length_eq_val_length_tm)
lemma time_closest_pair_rec_tm_simps_2:
assumes "¬ (length xs ≤ 3)"
shows "time (closest_pair_rec_tm xs) = 1 + (
let (xs⇩L, xs⇩R) = val (split_at_tm (length xs div 2) xs) in
let (ys⇩L, p⇩L) = val (closest_pair_rec_tm xs⇩L) in
let (ys⇩R, p⇩R) = val (closest_pair_rec_tm xs⇩R) in
let ys = val (merge_tm (λp. snd p) ys⇩L ys⇩R) in
time (length_tm xs) + time (split_at_tm (length xs div 2) xs) + time (closest_pair_rec_tm xs⇩L) +
time (closest_pair_rec_tm xs⇩R) + time (merge_tm (λp. snd p) ys⇩L ys⇩R) + t_combine p⇩L p⇩R (fst (hd xs⇩R)) ys
)"
using assms
apply (subst closest_pair_rec_tm.simps)
by (auto simp del: closest_pair_rec_tm.simps
simp add: time_simps length_eq_val_length_tm t_combine_eq_time_combine_tm
split: prod.split)
function closest_pair_recurrence :: "nat ⇒ real" where
"n ≤ 3 ⟹ closest_pair_recurrence n = 3 + n + mergesort_recurrence n + n * n"
| "3 < n ⟹ closest_pair_recurrence n = 7 + 13 * n +
closest_pair_recurrence (nat ⌊real n / 2⌋) + closest_pair_recurrence (nat ⌈real n / 2⌉)"
by force simp_all
termination by akra_bazzi_termination simp_all
lemma closest_pair_recurrence_nonneg[simp]:
"0 ≤ closest_pair_recurrence n"
by (induction n rule: closest_pair_recurrence.induct) auto
lemma time_closest_pair_rec_conv_closest_pair_recurrence:
assumes "distinct ps" "sorted_fst ps"
shows "time (closest_pair_rec_tm ps) ≤ closest_pair_recurrence (length ps)"
using assms
proof (induction ps rule: length_induct)
case (1 ps)
let ?n = "length ps"
show ?case
proof (cases "?n ≤ 3")
case True
hence "time (closest_pair_rec_tm ps) = 1 + time (length_tm ps) + time (mergesort_tm snd ps) + time (closest_pair_bf_tm ps)"
using time_closest_pair_rec_tm_simps_1 by simp
moreover have "closest_pair_recurrence ?n = 3 + ?n + mergesort_recurrence ?n + ?n * ?n"
using True by simp
moreover have "time (length_tm ps) ≤ 1 + ?n" "time (mergesort_tm snd ps) ≤ mergesort_recurrence ?n"
"time (closest_pair_bf_tm ps) ≤ 1 + ?n * ?n"
using time_length_tm[of ps] time_mergesort_conv_mergesort_recurrence[of snd ps] time_closest_pair_bf_tm[of ps] by auto
ultimately show ?thesis
by linarith
next
case False
obtain XS⇩L XS⇩R where XS_def: "(XS⇩L, XS⇩R) = val (split_at_tm (?n div 2) ps)"
using prod.collapse by blast
obtain YS⇩L C⇩0⇩L C⇩1⇩L where CP⇩L_def: "(YS⇩L, C⇩0⇩L, C⇩1⇩L) = val (closest_pair_rec_tm XS⇩L)"
using prod.collapse by metis
obtain YS⇩R C⇩0⇩R C⇩1⇩R where CP⇩R_def: "(YS⇩R, C⇩0⇩R, C⇩1⇩R) = val (closest_pair_rec_tm XS⇩R)"
using prod.collapse by metis
define YS where "YS = val (merge_tm (λp. snd p) YS⇩L YS⇩R)"
obtain C⇩0 C⇩1 where C⇩0⇩1_def: "(C⇩0, C⇩1) = val (combine_tm (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) (fst (hd XS⇩R)) YS)"
using prod.collapse by metis
note defs = XS_def CP⇩L_def CP⇩R_def YS_def C⇩0⇩1_def
have XSLR: "XS⇩L = take (?n div 2) ps" "XS⇩R = drop (?n div 2) ps"
using defs by (auto simp: split_at_take_drop_conv split_at_eq_val_split_at_tm)
hence "length XS⇩L = ?n div 2" "length XS⇩R = ?n - ?n div 2"
by simp_all
hence *: "(nat ⌊real ?n / 2⌋) = length XS⇩L" "(nat ⌈real ?n / 2⌉) = length XS⇩R"
by linarith+
have "length XS⇩L = length YS⇩L" "length XS⇩R = length YS⇩R"
using defs closest_pair_rec_set_length_sorted_snd closest_pair_rec_eq_val_closest_pair_rec_tm by metis+
hence L: "?n = length YS⇩L + length YS⇩R"
using defs XSLR by fastforce
have "1 < length XS⇩L" "length XS⇩L < length ps"
using False XSLR by simp_all
moreover have "distinct XS⇩L" "sorted_fst XS⇩L"
using XSLR "1.prems"(1,2) sorted_fst_def sorted_wrt_take by simp_all
ultimately have "time (closest_pair_rec_tm XS⇩L) ≤ closest_pair_recurrence (length XS⇩L)"
using "1.IH" by simp
hence IHL: "time (closest_pair_rec_tm XS⇩L) ≤ closest_pair_recurrence (nat ⌊real ?n / 2⌋)"
using * by simp
have "1 < length XS⇩R" "length XS⇩R < length ps"
using False XSLR by simp_all
moreover have "distinct XS⇩R" "sorted_fst XS⇩R"
using XSLR "1.prems"(1,2) sorted_fst_def sorted_wrt_drop by simp_all
ultimately have "time (closest_pair_rec_tm XS⇩R) ≤ closest_pair_recurrence (length XS⇩R)"
using "1.IH" by simp
hence IHR: "time (closest_pair_rec_tm XS⇩R) ≤ closest_pair_recurrence (nat ⌈real ?n / 2⌉)"
using * by simp
have "(YS, C⇩0, C⇩1) = val (closest_pair_rec_tm ps)"
using False closest_pair_rec_simps defs by (auto simp: Let_def length_eq_val_length_tm split!: prod.split)
hence "set ps = set YS" "length ps = length YS" "distinct YS" "sorted_snd YS"
using "1.prems" closest_pair_rec_set_length_sorted_snd closest_pair_rec_distinct
closest_pair_rec_eq_val_closest_pair_rec_tm by auto
moreover have "∀p ∈ set YS⇩L. fst p ≤ fst (hd XS⇩R)"
using False "1.prems"(2) XSLR ‹length XS⇩L < length ps› ‹length XS⇩L = length ps div 2›
CP⇩L_def sorted_fst_take_less_hd_drop closest_pair_rec_set_length_sorted_snd
closest_pair_rec_eq_val_closest_pair_rec_tm by metis
moreover have "∀p ∈ set YS⇩R. fst (hd XS⇩R) ≤ fst p"
using False "1.prems"(2) XSLR CP⇩R_def sorted_fst_hd_drop_less_drop
closest_pair_rec_set_length_sorted_snd closest_pair_rec_eq_val_closest_pair_rec_tm by metis
moreover have "set YS = set YS⇩L ∪ set YS⇩R"
using set_merge defs by (metis merge_eq_val_merge_tm)
moreover have "sparse (dist C⇩0⇩L C⇩1⇩L) (set YS⇩L)"
using CP⇩L_def ‹1 < length XS⇩L› ‹distinct XS⇩L› ‹sorted_fst XS⇩L›
closest_pair_rec_dist closest_pair_rec_set_length_sorted_snd
closest_pair_rec_eq_val_closest_pair_rec_tm by auto
moreover have "sparse (dist C⇩0⇩R C⇩1⇩R) (set YS⇩R)"
using CP⇩R_def ‹1 < length XS⇩R› ‹distinct XS⇩R› ‹sorted_fst XS⇩R›
closest_pair_rec_dist closest_pair_rec_set_length_sorted_snd
closest_pair_rec_eq_val_closest_pair_rec_tm by auto
ultimately have combine_bound: "t_combine (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) (fst (hd XS⇩R)) YS ≤ 3 + 10 * ?n"
using t_combine_bound[of YS "set YS⇩L" "set YS⇩R" "fst (hd XS⇩R)"] by (simp add: add.commute)
have "time (closest_pair_rec_tm ps) = 1 + time (length_tm ps) + time (split_at_tm (?n div 2) ps) +
time (closest_pair_rec_tm XS⇩L) + time (closest_pair_rec_tm XS⇩R) + time (merge_tm (λp. snd p) YS⇩L YS⇩R) +
t_combine (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) (fst (hd XS⇩R)) YS"
using time_closest_pair_rec_tm_simps_2[OF False] defs
by (auto simp del: closest_pair_rec_tm.simps simp add: Let_def split: prod.split)
also have "... ≤ 7 + 13 * ?n + time (closest_pair_rec_tm XS⇩L) + time (closest_pair_rec_tm XS⇩R)"
using time_merge_tm[of "(λp. snd p)" YS⇩L YS⇩R] L combine_bound by (simp add: time_length_tm time_split_at_tm)
also have "... ≤ 7 + 13 * ?n + closest_pair_recurrence (nat ⌊real ?n / 2⌋) +
closest_pair_recurrence (nat ⌈real ?n / 2⌉)"
using IHL IHR by simp
also have "... = closest_pair_recurrence (length ps)"
using False by simp
finally show ?thesis
by simp
qed
qed
theorem closest_pair_recurrence:
"closest_pair_recurrence ∈ Θ(λn. n * ln n)"
by (master_theorem) auto
theorem time_closest_pair_rec_bigo:
"(λxs. time (closest_pair_rec_tm xs)) ∈ O[length going_to at_top within { ps. distinct ps ∧ sorted_fst ps }]((λn. n * ln n) o length)"
proof -
have 0: "⋀ps. ps ∈ { ps. distinct ps ∧ sorted_fst ps } ⟹
time (closest_pair_rec_tm ps) ≤ (closest_pair_recurrence o length) ps"
unfolding comp_def using time_closest_pair_rec_conv_closest_pair_recurrence by auto
show ?thesis
using bigo_measure_trans[OF 0] bigthetaD1[OF closest_pair_recurrence] of_nat_0_le_iff by blast
qed
definition closest_pair_time :: "nat ⇒ real" where
"closest_pair_time n = 1 + mergesort_recurrence n + closest_pair_recurrence n"
lemma time_closest_pair_conv_closest_pair_recurrence:
assumes "distinct ps"
shows "time (closest_pair_tm ps) ≤ closest_pair_time (length ps)"
using assms
unfolding closest_pair_time_def
proof (induction rule: induct_list012)
case (3 x y zs)
let ?ps = "x # y # zs"
define xs where "xs = val (mergesort_tm fst ?ps)"
have *: "distinct xs" "sorted_fst xs" "length xs = length ?ps"
using xs_def mergesort(4)[OF "3.prems", of fst] mergesort(1)[of fst ?ps] mergesort(3)[of fst ?ps]
sorted_fst_def mergesort_eq_val_mergesort_tm by metis+
have "time (closest_pair_tm ?ps) = 1 + time (mergesort_tm fst ?ps) + time (closest_pair_rec_tm xs)"
using xs_def by (auto simp del: mergesort_tm.simps closest_pair_rec_tm.simps simp add: time_simps split: prod.split)
also have "... ≤ 1 + mergesort_recurrence (length ?ps) + time (closest_pair_rec_tm xs)"
using time_mergesort_conv_mergesort_recurrence[of fst ?ps] by simp
also have "... ≤ 1 + mergesort_recurrence (length ?ps) + closest_pair_recurrence (length ?ps)"
using time_closest_pair_rec_conv_closest_pair_recurrence[of xs] * by auto
finally show ?case
by blast
qed (auto simp: time_simps)
corollary closest_pair_time:
"closest_pair_time ∈ O(λn. n * ln n)"
unfolding closest_pair_time_def
using mergesort_recurrence closest_pair_recurrence sum_in_bigo(1) const_1_bigo_n_ln_n by blast
corollary time_closest_pair_bigo:
"(λps. time (closest_pair_tm ps)) ∈ O[length going_to at_top within { ps. distinct ps }]((λn. n * ln n) o length)"
proof -
have 0: "⋀ps. ps ∈ { ps. distinct ps } ⟹
time (closest_pair_tm ps) ≤ (closest_pair_time o length) ps"
unfolding comp_def using time_closest_pair_conv_closest_pair_recurrence by auto
show ?thesis
using bigo_measure_trans[OF 0] closest_pair_time by fastforce
qed
subsection "Code Export"
subsubsection "Combine Step"
fun find_closest_code :: "point ⇒ int ⇒ point list ⇒ (int * point)" where
"find_closest_code _ _ [] = undefined"
| "find_closest_code p _ [p⇩0] = (dist_code p p⇩0, p⇩0)"
| "find_closest_code p δ (p⇩0 # ps) = (
let δ⇩0 = dist_code p p⇩0 in
if δ ≤ (snd p⇩0 - snd p)⇧2 then
(δ⇩0, p⇩0)
else
let (δ⇩1, p⇩1) = find_closest_code p (min δ δ⇩0) ps in
if δ⇩0 ≤ δ⇩1 then
(δ⇩0, p⇩0)
else
(δ⇩1, p⇩1)
)"
lemma find_closest_code_dist_eq:
"0 < length ps ⟹ (δ⇩c, c) = find_closest_code p δ ps ⟹ δ⇩c = dist_code p c"
proof (induction p δ ps arbitrary: δ⇩c c rule: find_closest_code.induct)
case (3 p δ p⇩0 p⇩2 ps)
show ?case
proof cases
assume "δ ≤ (snd p⇩0 - snd p)⇧2"
thus ?thesis
using "3.prems"(2) by simp
next
assume A: "¬ δ ≤ (snd p⇩0 - snd p)⇧2"
define δ⇩0 where δ⇩0_def: "δ⇩0 = dist_code p p⇩0"
obtain δ⇩1 p⇩1 where δ⇩1_def: "(δ⇩1, p⇩1) = find_closest_code p (min δ δ⇩0) (p⇩2 # ps)"
by (metis surj_pair)
note defs = δ⇩0_def δ⇩1_def
have "δ⇩1 = dist_code p p⇩1"
using "3.IH"[of δ⇩0 δ⇩1 p⇩1] A defs by simp
thus ?thesis
using defs "3.prems" by (auto simp: Let_def split: if_splits prod.splits)
qed
qed simp_all
declare find_closest.simps [simp add]
lemma find_closest_code_eq:
assumes "0 < length ps" "δ = dist c⇩0 c⇩1" "δ' = dist_code c⇩0 c⇩1" "sorted_snd (p # ps)"
assumes "c = find_closest p δ ps" "(δ⇩c', c') = find_closest_code p δ' ps"
shows "c = c'"
using assms
proof (induction p δ ps arbitrary: δ' c⇩0 c⇩1 c δ⇩c' c' rule: find_closest.induct)
case (3 p δ p⇩0 p⇩2 ps)
define δ⇩0 δ⇩0' where δ⇩0_def: "δ⇩0 = dist p p⇩0" "δ⇩0' = dist_code p p⇩0"
obtain p⇩1 δ⇩1' p⇩1' where δ⇩1_def: "p⇩1 = find_closest p (min δ δ⇩0) (p⇩2 # ps)"
"(δ⇩1', p⇩1') = find_closest_code p (min δ' δ⇩0') (p⇩2 # ps)"
by (metis surj_pair)
note defs = δ⇩0_def δ⇩1_def
show ?case
proof cases
assume *: "δ ≤ snd p⇩0 - snd p"
hence "δ' ≤ (snd p⇩0 - snd p)⇧2"
using "3.prems"(2,3) dist_eq_dist_code_abs_le by fastforce
thus ?thesis
using * "3.prems"(5,6) by simp
next
assume *: "¬ δ ≤ snd p⇩0 - snd p"
moreover have "0 ≤ snd p⇩0 - snd p"
using "3.prems"(4) sorted_snd_def by simp
ultimately have A: "¬ δ' ≤ (snd p⇩0 - snd p)⇧2"
using "3.prems"(2,3) dist_eq_dist_code_abs_le[of c⇩0 c⇩1 "snd p⇩0 - snd p"] by simp
have "min δ δ⇩0 = δ ⟷ min δ' δ⇩0' = δ'" "min δ δ⇩0 = δ⇩0 ⟷ min δ' δ⇩0' = δ⇩0'"
by (metis "3.prems"(2,3) defs(1,2) dist_eq_dist_code_le min.commute min_def)+
moreover have "sorted_snd (p # p⇩2 # ps)"
using "3.prems"(4) sorted_snd_def by simp
ultimately have B: "p⇩1 = p⇩1'"
using "3.IH"[of c⇩0 c⇩1 δ' p⇩1 δ⇩1' p⇩1'] "3.IH"[of p p⇩0 δ⇩0' p⇩1 δ⇩1' p⇩1'] "3.prems"(2,3) defs * by auto
have "δ⇩1' = dist_code p p⇩1'"
using find_closest_code_dist_eq defs by blast
hence "δ⇩0 ≤ dist p p⇩1 ⟷ δ⇩0' ≤ δ⇩1'"
using defs(1,2) dist_eq_dist_code_le by (simp add: B)
thus ?thesis
using "3.prems"(5,6) * A B defs by (auto simp: Let_def split: prod.splits)
qed
qed auto
fun find_closest_pair_code :: "(int * point * point) ⇒ point list ⇒ (int * point * point)" where
"find_closest_pair_code (δ, c⇩0, c⇩1) [] = (δ, c⇩0, c⇩1)"
| "find_closest_pair_code (δ, c⇩0, c⇩1) [p] = (δ, c⇩0, c⇩1)"
| "find_closest_pair_code (δ, c⇩0, c⇩1) (p⇩0 # ps) = (
let (δ', p⇩1) = find_closest_code p⇩0 δ ps in
if δ ≤ δ' then
find_closest_pair_code (δ, c⇩0, c⇩1) ps
else
find_closest_pair_code (δ', p⇩0, p⇩1) ps
)"
lemma find_closest_pair_code_dist_eq:
assumes "δ = dist_code c⇩0 c⇩1" "(Δ, C⇩0, C⇩1) = find_closest_pair_code (δ, c⇩0, c⇩1) ps"
shows "Δ = dist_code C⇩0 C⇩1"
using assms
proof (induction "(δ, c⇩0, c⇩1)" ps arbitrary: δ c⇩0 c⇩1 Δ C⇩0 C⇩1 rule: find_closest_pair_code.induct)
case (3 δ c⇩0 c⇩1 p⇩0 p⇩2 ps)
obtain δ' p⇩1 where δ'_def: "(δ', p⇩1) = find_closest_code p⇩0 δ (p⇩2 # ps)"
by (metis surj_pair)
hence A: "δ' = dist_code p⇩0 p⇩1"
using find_closest_code_dist_eq by blast
show ?case
proof (cases "δ ≤ δ'")
case True
obtain Δ' C⇩0' C⇩1' where Δ'_def: "(Δ', C⇩0', C⇩1') = find_closest_pair_code (δ, c⇩0, c⇩1) (p⇩2 # ps)"
by (metis prod_cases4)
note defs = δ'_def Δ'_def
hence "Δ' = dist_code C⇩0' C⇩1'"
using "3.hyps"(1)[of "(δ', p⇩1)" δ' p⇩1] "3.prems"(1) True δ'_def by blast
moreover have "Δ = Δ'" "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs True "3.prems"(2) apply (auto split: prod.splits) by (metis Pair_inject)+
ultimately show ?thesis
by simp
next
case False
obtain Δ' C⇩0' C⇩1' where Δ'_def: "(Δ', C⇩0', C⇩1') = find_closest_pair_code (δ', p⇩0, p⇩1) (p⇩2 # ps)"
by (metis prod_cases4)
note defs = δ'_def Δ'_def
hence "Δ' = dist_code C⇩0' C⇩1'"
using "3.hyps"(2)[of "(δ', p⇩1)" δ' p⇩1] A False δ'_def by blast
moreover have "Δ = Δ'" "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using defs False "3.prems"(2) apply (auto split: prod.splits) by (metis Pair_inject)+
ultimately show ?thesis
by simp
qed
qed auto
declare find_closest_pair.simps [simp add]
lemma find_closest_pair_code_eq:
assumes "δ = dist c⇩0 c⇩1" "δ' = dist_code c⇩0 c⇩1" "sorted_snd ps"
assumes "(C⇩0, C⇩1) = find_closest_pair (c⇩0, c⇩1) ps"
assumes "(Δ', C⇩0', C⇩1') = find_closest_pair_code (δ', c⇩0, c⇩1) ps"
shows "C⇩0 = C⇩0' ∧ C⇩1 = C⇩1'"
using assms
proof (induction "(c⇩0, c⇩1)" ps arbitrary: δ δ' c⇩0 c⇩1 C⇩0 C⇩1 Δ' C⇩0' C⇩1' rule: find_closest_pair.induct)
case (3 c⇩0 c⇩1 p⇩0 p⇩2 ps)
obtain p⇩1 δ⇩p' p⇩1' where δ⇩p_def: "p⇩1 = find_closest p⇩0 δ (p⇩2 # ps)"
"(δ⇩p', p⇩1') = find_closest_code p⇩0 δ' (p⇩2 # ps)"
by (metis surj_pair)
hence A: "δ⇩p' = dist_code p⇩0 p⇩1'"
using find_closest_code_dist_eq by blast
have B: "p⇩1 = p⇩1'"
using "3.prems"(1,2,3) δ⇩p_def find_closest_code_eq by blast
show ?case
proof (cases "δ ≤ dist p⇩0 p⇩1")
case True
hence C: "δ' ≤ δ⇩p'"
by (simp add: "3.prems"(1,2) A B dist_eq_dist_code_le)
obtain C⇩0⇩i C⇩1⇩i Δ⇩i' C⇩0⇩i' C⇩1⇩i' where Δ⇩i_def:
"(C⇩0⇩i, C⇩1⇩i) = find_closest_pair (c⇩0, c⇩1) (p⇩2 # ps)"
"(Δ⇩i', C⇩0⇩i', C⇩1⇩i') = find_closest_pair_code (δ', c⇩0, c⇩1) (p⇩2 # ps)"
by (metis prod_cases3)
note defs = δ⇩p_def Δ⇩i_def
have "sorted_snd (p⇩2 # ps)"
using "3.prems"(3) sorted_snd_def by simp
hence "C⇩0⇩i = C⇩0⇩i' ∧ C⇩1⇩i = C⇩1⇩i'"
using "3.hyps"(1) "3.prems"(1,2) True defs by blast
moreover have "C⇩0 = C⇩0⇩i" "C⇩1 = C⇩1⇩i"
using defs(1,3) True "3.prems"(1,4) apply (auto split: prod.splits) by (metis Pair_inject)+
moreover have "Δ' = Δ⇩i'" "C⇩0' = C⇩0⇩i'" "C⇩1' = C⇩1⇩i'"
using defs(2,4) C "3.prems"(5) apply (auto split: prod.splits) by (metis Pair_inject)+
ultimately show ?thesis
by simp
next
case False
hence C: "¬ δ' ≤ δ⇩p'"
by (simp add: "3.prems"(1,2) A B dist_eq_dist_code_le)
obtain C⇩0⇩i C⇩1⇩i Δ⇩i' C⇩0⇩i' C⇩1⇩i' where Δ⇩i_def:
"(C⇩0⇩i, C⇩1⇩i) = find_closest_pair (p⇩0, p⇩1) (p⇩2 # ps)"
"(Δ⇩i', C⇩0⇩i', C⇩1⇩i') = find_closest_pair_code (δ⇩p', p⇩0, p⇩1') (p⇩2 # ps)"
by (metis prod_cases3)
note defs = δ⇩p_def Δ⇩i_def
have "sorted_snd (p⇩2 # ps)"
using "3.prems"(3) sorted_snd_def by simp
hence "C⇩0⇩i = C⇩0⇩i' ∧ C⇩1⇩i = C⇩1⇩i'"
using "3.prems"(1) "3.hyps"(2) A B False defs by blast
moreover have "C⇩0 = C⇩0⇩i" "C⇩1 = C⇩1⇩i"
using defs(1,3) False "3.prems"(1,4) apply (auto split: prod.splits) by (metis Pair_inject)+
moreover have "Δ' = Δ⇩i'" "C⇩0' = C⇩0⇩i'" "C⇩1' = C⇩1⇩i'"
using defs(2,4) C "3.prems"(5) apply (auto split: prod.splits) by (metis Pair_inject)+
ultimately show ?thesis
by simp
qed
qed auto
fun combine_code :: "(int * point * point) ⇒ (int * point * point) ⇒ int ⇒ point list ⇒ (int * point * point)" where
"combine_code (δ⇩L, p⇩0⇩L, p⇩1⇩L) (δ⇩R, p⇩0⇩R, p⇩1⇩R) l ps = (
let (δ, c⇩0, c⇩1) = if δ⇩L < δ⇩R then (δ⇩L, p⇩0⇩L, p⇩1⇩L) else (δ⇩R, p⇩0⇩R, p⇩1⇩R) in
let ps' = filter (λp. (fst p - l)⇧2 < δ) ps in
find_closest_pair_code (δ, c⇩0, c⇩1) ps'
)"
lemma combine_code_dist_eq:
assumes "δ⇩L = dist_code p⇩0⇩L p⇩1⇩L" "δ⇩R = dist_code p⇩0⇩R p⇩1⇩R"
assumes "(δ, c⇩0, c⇩1) = combine_code (δ⇩L, p⇩0⇩L, p⇩1⇩L) (δ⇩R, p⇩0⇩R, p⇩1⇩R) l ps"
shows "δ = dist_code c⇩0 c⇩1"
using assms by (auto simp: find_closest_pair_code_dist_eq split: if_splits)
lemma combine_code_eq:
assumes "δ⇩L' = dist_code p⇩0⇩L p⇩1⇩L" "δ⇩R' = dist_code p⇩0⇩R p⇩1⇩R" "sorted_snd ps"
assumes "(c⇩0, c⇩1) = combine (p⇩0⇩L, p⇩1⇩L) (p⇩0⇩R, p⇩1⇩R) l ps"
assumes "(δ', c⇩0', c⇩1') = combine_code (δ⇩L', p⇩0⇩L, p⇩1⇩L) (δ⇩R', p⇩0⇩R, p⇩1⇩R) l ps"
shows "c⇩0 = c⇩0' ∧ c⇩1 = c⇩1'"
proof -
obtain C⇩0⇩i C⇩1⇩i Δ⇩i' C⇩0⇩i' C⇩1⇩i' where Δ⇩i_def:
"(C⇩0⇩i, C⇩1⇩i) = (if dist p⇩0⇩L p⇩1⇩L < dist p⇩0⇩R p⇩1⇩R then (p⇩0⇩L, p⇩1⇩L) else (p⇩0⇩R, p⇩1⇩R))"
"(Δ⇩i', C⇩0⇩i', C⇩1⇩i') = (if δ⇩L' < δ⇩R' then (δ⇩L', p⇩0⇩L, p⇩1⇩L) else (δ⇩R', p⇩0⇩R, p⇩1⇩R))"
by metis
define ps' ps'' where ps'_def:
"ps' = filter (λp. dist p (l, snd p) < dist C⇩0⇩i C⇩1⇩i) ps"
"ps'' = filter (λp. (fst p - l)⇧2 < Δ⇩i') ps"
obtain C⇩0 C⇩1 Δ' C⇩0' C⇩1' where Δ_def:
"(C⇩0, C⇩1) = find_closest_pair (C⇩0⇩i, C⇩1⇩i) ps'"
"(Δ', C⇩0', C⇩1') = find_closest_pair_code (Δ⇩i', C⇩0⇩i', C⇩1⇩i') ps''"
by (metis prod_cases3)
note defs = Δ⇩i_def ps'_def Δ_def
have *: "C⇩0⇩i = C⇩0⇩i'" "C⇩1⇩i = C⇩1⇩i'" "Δ⇩i' = dist_code C⇩0⇩i' C⇩1⇩i'"
using Δ⇩i_def assms(1,2,3,4) dist_eq_dist_code_lt by (auto split: if_splits)
hence "⋀p. ¦fst p - l¦ < dist C⇩0⇩i C⇩1⇩i ⟷ (fst p - l)⇧2 < Δ⇩i'"
using dist_eq_dist_code_abs_lt by (metis (mono_tags) of_int_abs)
hence "ps' = ps''"
using ps'_def dist_fst_abs by auto
moreover have "sorted_snd ps'"
using assms(3) ps'_def sorted_snd_def sorted_wrt_filter by blast
ultimately have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using * find_closest_pair_code_eq Δ_def by blast+
moreover have "C⇩0 = c⇩0" "C⇩1 = c⇩1"
using assms(4) defs(1,3,5) apply (auto simp: combine.simps split: prod.splits) by (metis Pair_inject)+
moreover have "C⇩0' = c⇩0'" "C⇩1' = c⇩1'"
using assms(5) defs(2,4,6) apply (auto split: prod.splits) by (metis prod.inject)+
ultimately show ?thesis
by blast
qed
subsubsection "Divide and Conquer Algorithm"
function closest_pair_rec_code :: "point list ⇒ (point list * int * point * point)" where
"closest_pair_rec_code xs = (
let n = length xs in
if n ≤ 3 then
(mergesort snd xs, closest_pair_bf_code xs)
else
let (xs⇩L, xs⇩R) = split_at (n div 2) xs in
let l = fst (hd xs⇩R) in
let (ys⇩L, p⇩L) = closest_pair_rec_code xs⇩L in
let (ys⇩R, p⇩R) = closest_pair_rec_code xs⇩R in
let ys = merge snd ys⇩L ys⇩R in
(ys, combine_code p⇩L p⇩R l ys)
)"
by pat_completeness auto
termination closest_pair_rec_code
by (relation "Wellfounded.measure (λxs. length xs)")
(auto simp: split_at_take_drop_conv Let_def)
lemma closest_pair_rec_code_simps:
assumes "n = length xs" "¬ (n ≤ 3)"
shows "closest_pair_rec_code xs = (
let (xs⇩L, xs⇩R) = split_at (n div 2) xs in
let l = fst (hd xs⇩R) in
let (ys⇩L, p⇩L) = closest_pair_rec_code xs⇩L in
let (ys⇩R, p⇩R) = closest_pair_rec_code xs⇩R in
let ys = merge snd ys⇩L ys⇩R in
(ys, combine_code p⇩L p⇩R l ys)
)"
using assms by (auto simp: Let_def)
declare combine.simps combine_code.simps closest_pair_rec_code.simps [simp del]
lemma closest_pair_rec_code_dist_eq:
assumes "1 < length xs" "(ys, δ, c⇩0, c⇩1) = closest_pair_rec_code xs"
shows "δ = dist_code c⇩0 c⇩1"
using assms
proof (induction xs arbitrary: ys δ c⇩0 c⇩1 rule: length_induct)
case (1 xs)
let ?n = "length xs"
show ?case
proof (cases "?n ≤ 3")
case True
hence "(δ, c⇩0, c⇩1) = closest_pair_bf_code xs"
using "1.prems"(2) closest_pair_rec_code.simps by simp
thus ?thesis
using "1.prems"(1) closest_pair_bf_code_dist_eq by simp
next
case False
obtain XS⇩L XS⇩R where XS⇩L⇩R_def: "(XS⇩L, XS⇩R) = split_at (?n div 2) xs"
using prod.collapse by blast
define L where "L = fst (hd XS⇩R)"
obtain YS⇩L Δ⇩L C⇩0⇩L C⇩1⇩L where YSC⇩0⇩1⇩L_def: "(YS⇩L, Δ⇩L, C⇩0⇩L, C⇩1⇩L) = closest_pair_rec_code XS⇩L"
using prod.collapse by metis
obtain YS⇩R Δ⇩R C⇩0⇩R C⇩1⇩R where YSC⇩0⇩1⇩R_def: "(YS⇩R, Δ⇩R, C⇩0⇩R, C⇩1⇩R) = closest_pair_rec_code XS⇩R"
using prod.collapse by metis
define YS where "YS = merge (λp. snd p) YS⇩L YS⇩R"
obtain Δ C⇩0 C⇩1 where C⇩0⇩1_def: "(Δ, C⇩0, C⇩1) = combine_code (Δ⇩L, C⇩0⇩L, C⇩1⇩L) (Δ⇩R, C⇩0⇩R, C⇩1⇩R) L YS"
using prod.collapse by metis
note defs = XS⇩L⇩R_def L_def YSC⇩0⇩1⇩L_def YSC⇩0⇩1⇩R_def YS_def C⇩0⇩1_def
have "1 < length XS⇩L" "length XS⇩L < length xs"
using False "1.prems"(1) defs by (auto simp: split_at_take_drop_conv)
hence IHL: "Δ⇩L = dist_code C⇩0⇩L C⇩1⇩L"
using "1.IH" defs by metis+
have "1 < length XS⇩R" "length XS⇩R < length xs"
using False "1.prems"(1) defs by (auto simp: split_at_take_drop_conv)
hence IHR: "Δ⇩R = dist_code C⇩0⇩R C⇩1⇩R"
using "1.IH" defs by metis+
have *: "(YS, Δ, C⇩0, C⇩1) = closest_pair_rec_code xs"
using False closest_pair_rec_code_simps defs by (auto simp: Let_def split: prod.split)
moreover have "Δ = dist_code C⇩0 C⇩1"
using combine_code_dist_eq IHL IHR C⇩0⇩1_def by blast
ultimately show ?thesis
using "1.prems"(2) * by (metis Pair_inject)
qed
qed
lemma closest_pair_rec_ys_eq:
assumes "1 < length xs"
assumes "(ys, c⇩0, c⇩1) = closest_pair_rec xs"
assumes "(ys', δ', c⇩0', c⇩1') = closest_pair_rec_code xs"
shows "ys = ys'"
using assms
proof (induction xs arbitrary: ys c⇩0 c⇩1 ys' δ' c⇩0' c⇩1' rule: length_induct)
case (1 xs)
let ?n = "length xs"
show ?case
proof (cases "?n ≤ 3")
case True
hence "ys = mergesort snd xs"
using "1.prems"(2) closest_pair_rec.simps by simp
moreover have "ys' = mergesort snd xs"
using "1.prems"(3) closest_pair_rec_code.simps by (simp add: True)
ultimately show ?thesis
using "1.prems"(1) by simp
next
case False
obtain XS⇩L XS⇩R where XS⇩L⇩R_def: "(XS⇩L, XS⇩R) = split_at (?n div 2) xs"
using prod.collapse by blast
define L where "L = fst (hd XS⇩R)"
obtain YS⇩L C⇩0⇩L C⇩1⇩L YS⇩L' Δ⇩L' C⇩0⇩L' C⇩1⇩L' where YSC⇩0⇩1⇩L_def:
"(YS⇩L, C⇩0⇩L, C⇩1⇩L) = closest_pair_rec XS⇩L"
"(YS⇩L', Δ⇩L', C⇩0⇩L', C⇩1⇩L') = closest_pair_rec_code XS⇩L"
using prod.collapse by metis
obtain YS⇩R C⇩0⇩R C⇩1⇩R YS⇩R' Δ⇩R' C⇩0⇩R' C⇩1⇩R' where YSC⇩0⇩1⇩R_def:
"(YS⇩R, C⇩0⇩R, C⇩1⇩R) = closest_pair_rec XS⇩R"
"(YS⇩R', Δ⇩R', C⇩0⇩R', C⇩1⇩R') = closest_pair_rec_code XS⇩R"
using prod.collapse by metis
define YS YS' where YS_def:
"YS = merge (λp. snd p) YS⇩L YS⇩R"
"YS' = merge (λp. snd p) YS⇩L' YS⇩R'"
obtain C⇩0 C⇩1 Δ' C⇩0' C⇩1' where C⇩0⇩1_def:
"(C⇩0, C⇩1) = combine (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) L YS"
"(Δ', C⇩0', C⇩1') = combine_code (Δ⇩L', C⇩0⇩L', C⇩1⇩L') (Δ⇩R', C⇩0⇩R', C⇩1⇩R') L YS'"
using prod.collapse by metis
note defs = XS⇩L⇩R_def L_def YSC⇩0⇩1⇩L_def YSC⇩0⇩1⇩R_def YS_def C⇩0⇩1_def
have "1 < length XS⇩L" "length XS⇩L < length xs"
using False "1.prems"(1) defs by (auto simp: split_at_take_drop_conv)
hence IHL: "YS⇩L = YS⇩L'"
using "1.IH" defs by metis
have "1 < length XS⇩R" "length XS⇩R < length xs"
using False "1.prems"(1) defs by (auto simp: split_at_take_drop_conv)
hence IHR: "YS⇩R = YS⇩R'"
using "1.IH" defs by metis
have "(YS, C⇩0, C⇩1) = closest_pair_rec xs"
using False closest_pair_rec_simps defs(1,2,3,5,7,9)
by (auto simp: Let_def split: prod.split)
moreover have "(YS', Δ', C⇩0', C⇩1') = closest_pair_rec_code xs"
using False closest_pair_rec_code_simps defs(1,2,4,6,8,10)
by (auto simp: Let_def split: prod.split)
moreover have "YS = YS'"
using IHL IHR YS_def by simp
ultimately show ?thesis
by (metis "1.prems"(2,3) Pair_inject)
qed
qed
lemma closest_pair_rec_code_eq:
assumes "1 < length xs"
assumes "(ys, c⇩0, c⇩1) = closest_pair_rec xs"
assumes "(ys', δ', c⇩0', c⇩1') = closest_pair_rec_code xs"
shows "c⇩0 = c⇩0' ∧ c⇩1 = c⇩1'"
using assms
proof (induction xs arbitrary: ys c⇩0 c⇩1 ys' δ' c⇩0' c⇩1' rule: length_induct)
case (1 xs)
let ?n = "length xs"
show ?case
proof (cases "?n ≤ 3")
case True
hence "(c⇩0, c⇩1) = closest_pair_bf xs"
using "1.prems"(2) closest_pair_rec.simps by simp
moreover have "(δ', c⇩0', c⇩1') = closest_pair_bf_code xs"
using "1.prems"(3) closest_pair_rec_code.simps by (simp add: True)
ultimately show ?thesis
using "1.prems"(1) closest_pair_bf_code_eq by simp
next
case False
obtain XS⇩L XS⇩R where XS⇩L⇩R_def: "(XS⇩L, XS⇩R) = split_at (?n div 2) xs"
using prod.collapse by blast
define L where "L = fst (hd XS⇩R)"
obtain YS⇩L C⇩0⇩L C⇩1⇩L YS⇩L' Δ⇩L' C⇩0⇩L' C⇩1⇩L' where YSC⇩0⇩1⇩L_def:
"(YS⇩L, C⇩0⇩L, C⇩1⇩L) = closest_pair_rec XS⇩L"
"(YS⇩L', Δ⇩L', C⇩0⇩L', C⇩1⇩L') = closest_pair_rec_code XS⇩L"
using prod.collapse by metis
obtain YS⇩R C⇩0⇩R C⇩1⇩R YS⇩R' Δ⇩R' C⇩0⇩R' C⇩1⇩R' where YSC⇩0⇩1⇩R_def:
"(YS⇩R, C⇩0⇩R, C⇩1⇩R) = closest_pair_rec XS⇩R"
"(YS⇩R', Δ⇩R', C⇩0⇩R', C⇩1⇩R') = closest_pair_rec_code XS⇩R"
using prod.collapse by metis
define YS YS' where YS_def:
"YS = merge (λp. snd p) YS⇩L YS⇩R"
"YS' = merge (λp. snd p) YS⇩L' YS⇩R'"
obtain C⇩0 C⇩1 Δ' C⇩0' C⇩1' where C⇩0⇩1_def:
"(C⇩0, C⇩1) = combine (C⇩0⇩L, C⇩1⇩L) (C⇩0⇩R, C⇩1⇩R) L YS"
"(Δ', C⇩0', C⇩1') = combine_code (Δ⇩L', C⇩0⇩L', C⇩1⇩L') (Δ⇩R', C⇩0⇩R', C⇩1⇩R') L YS'"
using prod.collapse by metis
note defs = XS⇩L⇩R_def L_def YSC⇩0⇩1⇩L_def YSC⇩0⇩1⇩R_def YS_def C⇩0⇩1_def
have "1 < length XS⇩L" "length XS⇩L < length xs"
using False "1.prems"(1) defs by (auto simp: split_at_take_drop_conv)
hence IHL: "C⇩0⇩L = C⇩0⇩L'" "C⇩1⇩L = C⇩1⇩L'"
using "1.IH" defs by metis+
have "1 < length XS⇩R" "length XS⇩R < length xs"
using False "1.prems"(1) defs by (auto simp: split_at_take_drop_conv)
hence IHR: "C⇩0⇩R = C⇩0⇩R'" "C⇩1⇩R = C⇩1⇩R'"
using "1.IH" defs by metis+
have "sorted_snd YS⇩L" "sorted_snd YS⇩R"
using closest_pair_rec_set_length_sorted_snd YSC⇩0⇩1⇩L_def(1) YSC⇩0⇩1⇩R_def(1) by blast+
hence "sorted_snd YS"
using sorted_merge sorted_snd_def YS_def by blast
moreover have "YS = YS'"
using defs ‹1 < length XS⇩L› ‹1 < length XS⇩R› closest_pair_rec_ys_eq by blast
moreover have "Δ⇩L' = dist_code C⇩0⇩L' C⇩1⇩L'" "Δ⇩R' = dist_code C⇩0⇩R' C⇩1⇩R'"
using defs ‹1 < length XS⇩L› ‹1 < length XS⇩R› closest_pair_rec_code_dist_eq by blast+
ultimately have "C⇩0 = C⇩0'" "C⇩1 = C⇩1'"
using combine_code_eq IHL IHR C⇩0⇩1_def by blast+
moreover have "(YS, C⇩0, C⇩1) = closest_pair_rec xs"
using False closest_pair_rec_simps defs(1,2,3,5,7,9)
by (auto simp: Let_def split: prod.split)
moreover have "(YS', Δ', C⇩0', C⇩1') = closest_pair_rec_code xs"
using False closest_pair_rec_code_simps defs(1,2,4,6,8,10)
by (auto simp: Let_def split: prod.split)
ultimately show ?thesis
using "1.prems"(2,3) by (metis Pair_inject)
qed
qed
declare closest_pair.simps [simp add]
fun closest_pair_code :: "point list ⇒ (point * point)" where
"closest_pair_code [] = undefined"
| "closest_pair_code [_] = undefined"
| "closest_pair_code ps = (let (_, _, c⇩0, c⇩1) = closest_pair_rec_code (mergesort fst ps) in (c⇩0, c⇩1))"
lemma closest_pair_code_eq:
"closest_pair ps = closest_pair_code ps"
proof (induction ps rule: induct_list012)
case (3 x y zs)
obtain ys c⇩0 c⇩1 ys' δ' c⇩0' c⇩1' where *:
"(ys, c⇩0, c⇩1) = closest_pair_rec (mergesort fst (x # y # zs))"
"(ys', δ', c⇩0', c⇩1') = closest_pair_rec_code (mergesort fst (x # y # zs))"
by (metis prod_cases3)
moreover have "1 < length (mergesort fst (x # y # zs))"
using length_mergesort[of fst "x # y # zs"] by simp
ultimately have "c⇩0 = c⇩0'" "c⇩1 = c⇩1'"
using closest_pair_rec_code_eq by blast+
thus ?case
using * by (auto split: prod.splits)
qed auto
export_code closest_pair_code in OCaml
module_name Verified
end