Theory Ptolemys_Theorem
section ‹Ptolemy's Theorem›
theory Ptolemys_Theorem
imports
  "HOL-Analysis.Multivariate_Analysis"
begin
subsection ‹Preliminaries›
subsubsection ‹Additions to Rat theory›
hide_const (open) normalize
subsubsection ‹Additions to Transcendental theory›
text ‹
Lemmas about @{const arcsin} and @{const arccos} commonly involve to show that their argument is
in the domain of those partial functions, i.e., the argument @{term y} is between @{term "-1::real"}
and @{term "1::real"}.
As the argumentation for @{term "(-1::real) ≤ y"} and @{term "y ≤ (1::real)"} is often very similar,
we prefer to prove @{term "¦y¦ ≤ (1::real)"} to the two goals above.
The lemma for rewriting the term @{term "cos (arccos y)"} is already provided in the Isabelle
distribution with name @{thm [source] cos_arccos_abs}. Here, we further provide the analogue on
@{term "arcsin"} for rewriting @{term "sin (arcsin y)"}.
›
lemma sin_arcsin_abs: "¦y¦ ≤ 1 ⟹ sin (arcsin y) = y"
  by (simp add: abs_le_iff)
text ‹
The further lemmas are the required variants from existing lemmas @{thm [source] arccos_lbound}
and @{thm [source] arccos_ubound}.
›
lemma arccos_lbound_abs [simp]:
  "¦y¦ ≤ 1 ⟹ 0 ≤ arccos y"
by (simp add: arccos_lbound)
lemma arccos_ubound_abs [simp]:
  "¦y¦ ≤ 1 ⟹ arccos y ≤ pi"
by (simp add: arccos_ubound)
text ‹
As we choose angles to be between @{term "0::real"} between @{term "2 * pi"},
we need some lemmas to reason about the sign of @{term "sin x"}
for angles @{term "x"}.
›
lemma sin_ge_zero_iff:
  assumes "0 ≤ x" "x < 2 * pi"
  shows "0 ≤ sin x ⟷ x ≤ pi"
proof
  assume "0 ≤ sin x"
  show "x ≤ pi"
  proof (rule ccontr)
    assume "¬ x ≤ pi"
    from this ‹x < 2 * pi› have "sin x < 0"
      using sin_lt_zero by auto
    from this ‹0 ≤ sin x› show False by auto
  qed
next
  assume "x ≤ pi"
  from this ‹0 ≤ x› show "0 ≤ sin x" by (simp add: sin_ge_zero)
qed
lemma sin_less_zero_iff:
  assumes "0 ≤ x" "x < 2 * pi"
  shows "sin x < 0 ⟷ pi < x"
using assms sin_ge_zero_iff by fastforce
subsubsection ‹Addition to Finite-Cartesian-Product theory›
text ‹
Here follow generally useful additions and specialised equations
for two-dimensional real-valued vectors.
›
lemma axis_nth_eq_0 [simp]:
  assumes "i ≠ j"
  shows "axis i x $ j = 0"
using assms unfolding axis_def by simp
lemma norm_axis:
  fixes x :: real
  shows "norm (axis i x) = abs x"
by (simp add: norm_eq_sqrt_inner inner_axis_axis)
lemma norm_eq_on_real_2_vec:
  fixes x :: "real ^ 2"
  shows "norm x = sqrt ((x $ 1) ^ 2 + (x $ 2) ^ 2)"
by (simp add: norm_eq_sqrt_inner inner_vec_def UNIV_2 power2_eq_square)
lemma dist_eq_on_real_2_vec:
  fixes a b :: "real ^ 2"
  shows "dist a b = sqrt ((a $ 1 - b $ 1) ^ 2 + (a $ 2 - b $ 2) ^ 2)"
unfolding dist_norm norm_eq_on_real_2_vec by simp
subsection ‹Polar Form of Two-Dimensional Real-Valued Vectors›
subsubsection ‹Definitions to Transfer to Polar Form and Back›
definition of_radiant :: "real ⇒ real ^ 2"
where
  "of_radiant ω = axis 1 (cos ω) + axis 2 (sin ω)"
definition normalize :: "real ^ 2 ⇒ real ^ 2"
where
  "normalize p = (if p = 0 then axis 1 1 else (1 / norm p) *⇩R p)"
definition radiant_of :: "real ^ 2 ⇒ real"
where
  "radiant_of p = (THE ω. 0 ≤ ω ∧ ω < 2 * pi ∧ of_radiant ω = normalize p)"
text ‹
The vector @{term "of_radiant ω"} is the vector with length @{term "1::real"} and angle @{term "ω"}
to the first axis.
We normalize vectors to length @{term "1::real"} keeping their orientation with the normalize function.
Conversely, @{term "radiant_of p"} is the angle of vector @{term p} to the first axis, where we
choose @{term "radiant_of"} to return angles between @{term "0::real"} and @{term "2 * pi"},
following the usual high-school convention.
With these definitions, we can express the main result
@{term "norm p *⇩R of_radiant (radiant_of p) = p"}.
Note that the main result holds for any definition of @{term "radiant_of 0"}.
So, we choose to define @{term "normalize 0"} and @{term "radiant_of 0"}, such that
@{term "radiant_of 0 = 0"}.
›
subsubsection ‹Lemmas on @{const of_radiant}›
lemma nth_of_radiant_1 [simp]:
  "of_radiant ω $ 1 = cos ω"
unfolding of_radiant_def by simp
lemma nth_of_radiant_2 [simp]:
  "of_radiant ω $ 2 = sin ω"
unfolding of_radiant_def by simp
lemma norm_of_radiant:
  "norm (of_radiant ω) = 1"
unfolding of_radiant_def norm_eq_on_real_2_vec by simp
lemma of_radiant_plus_2pi:
  "of_radiant (ω + 2 * pi) = of_radiant ω"
unfolding of_radiant_def by simp
lemma of_radiant_minus_2pi:
  "of_radiant (ω - 2 * pi) = of_radiant ω"
proof -
  have "of_radiant (ω - 2 * pi) = of_radiant (ω - 2 * pi + 2 * pi)"
    by (simp only: of_radiant_plus_2pi)
  also have "… = of_radiant ω" by simp
  finally show ?thesis .
qed
subsubsection ‹Lemmas on @{const normalize}›
lemma normalize_eq:
  "norm p *⇩R normalize p = p"
unfolding normalize_def by simp
lemma norm_normalize:
  "norm (normalize p) = 1"
unfolding normalize_def by (auto simp add: norm_axis)
lemma nth_normalize [simp]:
  "¦normalize p $ i¦ ≤ 1"
using norm_normalize component_le_norm_cart by metis
lemma normalize_square:
  "(normalize p $ 1)⇧2 + (normalize p $ 2)⇧2 = 1"
using dot_square_norm[of "normalize p"]
by (simp add: inner_vec_def UNIV_2 power2_eq_square norm_normalize)
lemma nth_normalize_ge_zero_iff:
  "0 ≤ normalize p $ i ⟷ 0 ≤ p $ i"
proof
  assume "0 ≤ normalize p $ i"
  from this show "0 ≤ p $ i"
    unfolding normalize_def by (auto split: if_split_asm simp add: zero_le_divide_iff)
next
  assume "0 ≤ p $ i"
  have "0 ≤ axis 1 (1 :: real) $ i"
    using exhaust_2[of i] by auto
  from this ‹0 ≤ p $ i› show "0 ≤ normalize p $ i"
    unfolding normalize_def by auto
qed
lemma nth_normalize_less_zero_iff:
  "normalize p $ i < 0 ⟷ p $ i < 0"
using nth_normalize_ge_zero_iff leD leI by metis
lemma normalize_boundary_iff:
  "¦normalize p $ 1¦ = 1 ⟷ p $ 2 = 0"
proof
  assume "¦normalize p $ 1¦ = 1"
  from this have 1: "(p $ 1) ^ 2 = norm p ^ 2"
    unfolding normalize_def by (auto split: if_split_asm simp add: power2_eq_iff)
  moreover have "(p $ 1) ^ 2 + (p $ 2) ^ 2 = norm p ^ 2"
    using norm_eq_on_real_2_vec by auto
  ultimately show "p $ 2 = 0" by simp
next
  assume "p $ 2 = 0"
  from this have "¦p $ 1¦ = norm p"
    by (auto simp add: norm_eq_on_real_2_vec)
  from this show "¦normalize p $ 1¦ = 1"
    unfolding normalize_def by simp
qed
lemma between_normalize_if_distant_from_0:
  assumes "norm p ≥ 1"
  shows "between (0, p) (normalize p)"
using assms by (auto simp add: between_mem_segment closed_segment_def normalize_def)
lemma between_normalize_if_near_0:
  assumes "norm p ≤ 1"
  shows "between (0, normalize p) p"
proof -
  have "0 ≤ norm p" by simp
  from assms have "p = (norm p / norm p) *⇩R p ∧ 0 ≤ norm p ∧ norm p ≤ 1" by auto
  from this have "∃u. p = (u / norm p) *⇩R p ∧ 0 ≤ u ∧ u ≤ 1" by blast
  from this show ?thesis
    by (auto simp add: between_mem_segment closed_segment_def normalize_def)
qed
subsubsection ‹Lemmas on @{const radiant_of}›
lemma radiant_of:
  "0 ≤ radiant_of p ∧ radiant_of p < 2 * pi ∧ of_radiant (radiant_of p) = normalize p"
proof -
  let ?a = "if 0 ≤ p $ 2 then arccos (normalize p $ 1) else pi + arccos (- (normalize p $ 1))"
  have "0 ≤ ?a ∧ ?a < 2 * pi ∧ of_radiant ?a = normalize p"
  proof -
    have "0 ≤ ?a" by auto
    moreover have "?a < 2 * pi"
    proof cases
      assume "0 ≤ p $ 2"
      from this have "?a ≤ pi" by simp
      from this show ?thesis
        using pi_gt_zero by linarith
    next
      assume "¬ 0 ≤ p $ 2"
      have "arccos (- normalize p $ 1) < pi"
      proof -
        have "¦normalize p $ 1¦ ≠ 1"
          using ‹¬ 0 ≤ p $ 2› by (simp only: normalize_boundary_iff)
        from this have "arccos (- normalize p $ 1) ≠ pi"
          unfolding arccos_minus_1[symmetric] by (subst arccos_eq_iff) auto
        moreover have "arccos (- normalize p $ 1) ≤ pi" by simp
        ultimately show "arccos (- normalize p $ 1) < pi" by linarith
      qed
      from this ‹¬ 0 ≤ p $ 2› show ?thesis by simp
    qed
    moreover have "of_radiant ?a = normalize p"
    proof -
      have "of_radiant ?a $ i = normalize p $ i" for i
      proof -
        have "of_radiant ?a $ 1 = normalize p $ 1"
          unfolding of_radiant_def by (simp add: cos_arccos_abs)
        moreover have "of_radiant ?a $ 2 = normalize p $ 2"
        proof cases
          assume "0 ≤ p $ 2"
          have "sin (arccos (normalize p $ 1)) = sqrt (1 - (normalize p $ 1) ^ 2)"
            by (simp add: sin_arccos_abs)
          also have "… = normalize p $ 2"
          proof -
            have "1 - (normalize p $ 1)⇧2 = (normalize p $ 2)⇧2"
              using normalize_square[of p] by auto
            from this ‹0 ≤ p $ 2› show ?thesis by (simp add: nth_normalize_ge_zero_iff)
          qed
          finally show ?thesis
            using ‹0 ≤ p $ 2› unfolding of_radiant_def by auto
        next
          assume "¬ 0 ≤ p $ 2"
          have "- sin (arccos (- normalize p $ 1)) = - sqrt (1 - (normalize p $ 1)⇧2)"
            by (simp add: sin_arccos_abs)
          also have "… = normalize p $ 2"
          proof -
            have "1 - (normalize p $ 1)⇧2 = (normalize p $ 2)⇧2"
              using normalize_square[of p] by auto
            from this ‹¬ 0 ≤ p $ 2› show ?thesis
              using nth_normalize_ge_zero_iff by fastforce
          qed
          finally show ?thesis
            using ‹¬ 0 ≤ p $ 2› unfolding of_radiant_def by auto
        qed
        ultimately show ?thesis by (metis exhaust_2[of i])
      qed
      from this show ?thesis by (simp add: vec_eq_iff)
    qed
    ultimately show ?thesis by blast
  qed
  moreover {
    fix ω
    assume "0 ≤ ω ∧ ω < 2 * pi ∧ of_radiant ω = normalize p"
    from this have "0 ≤ ω" "ω < 2 * pi" "normalize p = of_radiant ω" by auto
    from this have "cos ω = normalize p $ 1" "sin ω = normalize p $ 2" by auto
    have "ω = ?a"
    proof cases
      assume "0 ≤ p $ 2"
      from this have "ω ≤ pi"
        using ‹0 ≤ ω› ‹ω < 2 * pi› ‹sin ω = normalize p $ 2›
        by (simp add: sin_ge_zero_iff[symmetric] nth_normalize_ge_zero_iff)
      from ‹0 ≤ ω› this have "ω = arccos (cos ω)" by (simp add: arccos_cos)
      from ‹cos ω = normalize p $ 1› this have "ω = arccos (normalize p $ 1)"
        by (simp add: arccos_eq_iff)
      from this show "ω = ?a" using ‹0 ≤ p $ 2› by auto
    next
      assume "¬ 0 ≤ p $ 2"
      from this have "ω > pi"
        using ‹0 ≤ ω› ‹ω < 2 * pi› ‹sin ω = normalize p $ 2›
        by (simp add: sin_less_zero_iff[symmetric] nth_normalize_less_zero_iff)
      from this ‹ω < 2 * pi› have "ω - pi = arccos (cos (ω - pi))"
        by (auto simp only: arccos_cos)
      from this ‹cos ω = normalize p $ 1› have "ω - pi = arccos (- normalize p $ 1)" by simp
      from this have "ω = pi + arccos (- normalize p $ 1)" by simp
      from this show "ω = ?a" using ‹¬ 0 ≤ p $ 2› by auto
    qed
  }
  ultimately show ?thesis
    unfolding radiant_of_def by (rule theI)
qed
lemma radiant_of_bounds [simp]:
  "0 ≤ radiant_of p" "radiant_of p < 2 * pi"
using radiant_of by auto
lemma radiant_of_weak_ubound [simp]:
  "radiant_of p ≤ 2 * pi"
using radiant_of_bounds(2)[of p] by linarith
subsubsection ‹Main Equations for Transforming to Polar Form›
lemma polar_form_eq:
  "norm p *⇩R of_radiant (radiant_of p) = p"
using radiant_of normalize_eq by simp
lemma relative_polar_form_eq:
  "Q + dist P Q *⇩R of_radiant (radiant_of (P - Q)) = P"
proof -
  have "norm (P - Q) *⇩R of_radiant (radiant_of (P - Q)) = P - Q"
    unfolding polar_form_eq ..
  moreover have "dist P Q = norm (P - Q)" by (simp add: dist_norm)
  ultimately show ?thesis by (metis add.commute diff_add_cancel)
qed
subsection ‹Ptolemy's Theorem›
lemma dist_circle_segment:
  assumes "0 ≤ radius" "0 ≤ α" "α ≤ β" "β ≤ 2 * pi"
  shows "dist (center + radius *⇩R of_radiant α) (center + radius *⇩R of_radiant β) = 2 * radius * sin ((β - α) / 2)"
    (is "?lhs = ?rhs")
proof -
  have trigonometry: "(cos α - cos β)⇧2 + (sin α - sin β)⇧2 = (2 *  sin ((β - α) / 2))⇧2"
  proof -
    have sin_diff_minus: "sin ((α - β) / 2) = - sin ((β - α) / 2)"
      by (simp only: sin_minus[symmetric] minus_divide_left minus_diff_eq)
    have "(cos α - cos β)⇧2 + (sin α - sin β)⇧2 =
      (2 * sin ((α + β) / 2) * sin ((β - α) / 2))⇧2 + (2 * sin ((α - β) / 2) * cos ((α + β) / 2))⇧2"
      by (simp only: cos_diff_cos sin_diff_sin)
    also have "… = (2 * sin ((β - α) / 2))⇧2 * ((sin ((α + β) / 2))⇧2 + (cos ((α + β) / 2))⇧2)"
      unfolding sin_diff_minus by algebra
    also have "… = (2 *  sin ((β - α) / 2))⇧2" by simp
    finally show ?thesis .
  qed
  from assms have "0 ≤ sin ((β - α) / 2)" by (simp add: sin_ge_zero)
  have "?lhs = sqrt (radius⇧2 * ((cos α - cos β)⇧2 + (sin α - sin β)⇧2))"
    unfolding dist_eq_on_real_2_vec by simp algebra
  also have "… = sqrt (radius⇧2 *  (2 * sin ((β - α) / 2))⇧2)" by (simp add: trigonometry)
  also have "… = ?rhs"
    using ‹0 ≤ radius› ‹0 ≤ sin ((β - α) / 2)› by (simp add: real_sqrt_mult)
  finally show ?thesis .
qed
theorem ptolemy_trigonometric:
  fixes ω⇩1 ω⇩2 ω⇩3 :: real
  shows "sin (ω⇩1 + ω⇩2) * sin (ω⇩2 + ω⇩3) = sin ω⇩1 * sin ω⇩3 + sin ω⇩2 * sin (ω⇩1 + ω⇩2 + ω⇩3)"
proof -
  have "sin (ω⇩1 + ω⇩2) * sin (ω⇩2 + ω⇩3) = ((sin ω⇩2)⇧2 + (cos ω⇩2)⇧2) * sin ω⇩1 * sin ω⇩3 + sin ω⇩2 * sin (ω⇩1 + ω⇩2 + ω⇩3)"
    by (simp only: sin_add cos_add) algebra
  also have "… = sin ω⇩1 * sin ω⇩3 + sin ω⇩2 * sin (ω⇩1 + ω⇩2 + ω⇩3)" by simp
  finally show ?thesis .
qed
theorem ptolemy:
  fixes A B C D center :: "real ^ 2"
  assumes "dist center A = radius" and "dist center B = radius"
  assumes "dist center C = radius" and "dist center D = radius"
  assumes ordering_of_points:
    "radiant_of (A - center) ≤ radiant_of (B - center)"
    "radiant_of (B - center) ≤ radiant_of (C - center)"
    "radiant_of (C - center) ≤ radiant_of (D - center)"
  shows "dist A C * dist B D = dist A B * dist C D + dist A D * dist B C"
proof -
  from ‹dist center A = radius› have "0 ≤ radius" by auto
  define α β γ δ
    where "α = radiant_of (A - center)" and "β = radiant_of (B - center)"
    and "γ = radiant_of (C - center)" and "δ = radiant_of (D - center)"
  from ordering_of_points have angle_basics:
    "α ≤ β" "β ≤ γ" "γ ≤ δ"
    "0 ≤ α" "α ≤ 2 * pi" "0 ≤ β" "β ≤ 2 * pi"
    "0 ≤ γ" "γ ≤ 2 * pi" "0 ≤ δ" "δ ≤ 2 * pi"
    unfolding α_def β_def γ_def δ_def by auto
  from assms(1-4) have
    "A = center + radius *⇩R of_radiant α" "B = center + radius *⇩R of_radiant β"
    "C = center + radius *⇩R of_radiant γ" "D = center + radius *⇩R of_radiant δ"
    unfolding α_def β_def γ_def δ_def
    using relative_polar_form_eq dist_commute by metis+
  from this have dist_eqs:
    "dist A C = 2 * radius * sin ((γ - α) / 2)"
    "dist B D = 2 * radius * sin ((δ - β) / 2)"
    "dist A B = 2 * radius * sin ((β - α) / 2)"
    "dist C D = 2 * radius * sin ((δ - γ) / 2)"
    "dist A D = 2 * radius * sin ((δ - α) / 2)"
    "dist B C = 2 * radius * sin ((γ - β) / 2)"
    using angle_basics ‹radius ≥ 0› dist_circle_segment by (auto)
  have "dist A C * dist B D = 4 * radius ^ 2 * sin ((γ - α) / 2) * sin ((δ - β) / 2)"
    unfolding dist_eqs by (simp add: power2_eq_square)
  also have "… = 4 * radius ^ 2 * (sin ((β - α) / 2) * sin ((δ - γ) / 2) + sin ((γ - β) / 2) * sin ((δ - α) / 2))"
  proof -
    define ω⇩1 ω⇩2 ω⇩3 where "ω⇩1 = (β - α) / 2" and "ω⇩2 = (γ - β) / 2" and "ω⇩3 = (δ - γ) / 2"
    have "(γ - α) / 2 = ω⇩1 + ω⇩2" and "(δ - β) / 2 = ω⇩2 + ω⇩3" and "(δ - α) / 2 = ω⇩1 + ω⇩2 + ω⇩3"
      unfolding ω⇩1_def ω⇩2_def ω⇩3_def by (auto simp add: field_simps)
    have "sin ((γ - α) / 2) * sin ((δ - β) / 2) = sin (ω⇩1 + ω⇩2) * sin (ω⇩2 + ω⇩3)"
      using ‹(γ - α) / 2 = ω⇩1 + ω⇩2› ‹(δ - β) / 2 = ω⇩2 + ω⇩3› by (simp only:)
    also have "… = sin ω⇩1 * sin ω⇩3 + sin ω⇩2 * sin (ω⇩1 + ω⇩2 + ω⇩3)"
      by (rule ptolemy_trigonometric)
    also have "… = (sin ((β - α) / 2) * sin ((δ - γ) / 2) + sin ((γ - β) / 2) * sin ((δ - α) / 2))"
      using ω⇩1_def ω⇩2_def ω⇩3_def ‹(δ - α) / 2 = ω⇩1 + ω⇩2 + ω⇩3› by (simp only:)
    finally show ?thesis by simp
  qed
  also have "… = dist A B * dist C D + dist A D * dist B C"
    unfolding dist_eqs by (simp add: distrib_left power2_eq_square)
  finally show ?thesis .
qed
end