Theory Poincare_Lines_Ideal_Points
theory Poincare_Lines_Ideal_Points
imports Poincare_Lines
begin
subsection‹Ideal points of h-lines›
text‹\emph{Ideal points} of an h-line are points where the h-line intersects the unit disc.›
subsubsection ‹Calculation of ideal points›
text ‹We decided to define ideal points constructively, i.e., we calculate the coordinates of ideal
points for a given h-line explicitly. Namely, if the h-line is determined by $A$ and $B$, the two
intersection points are $$\frac{B}{|B|^2}\left(-A \pm i\cdot \sqrt{|B|^2 - A^2}\right).$$›
definition calc_ideal_point1_cvec :: "complex ⇒ complex ⇒ complex_vec" where
 [simp]:  "calc_ideal_point1_cvec A B =
    (let discr = Re ((cmod B)⇧2 - (Re A)⇧2) in
         (B*(-A - 𝗂*sqrt(discr)), (cmod B)⇧2))"
definition calc_ideal_point2_cvec :: "complex ⇒ complex ⇒ complex_vec" where
  [simp]: "calc_ideal_point2_cvec A B =
    (let discr = Re ((cmod B)⇧2 - (Re A)⇧2) in
         (B*(-A + 𝗂*sqrt(discr)), (cmod B)⇧2))"
definition calc_ideal_points_cmat_cvec :: "complex_mat ⇒ complex_vec set" where
 [simp]:  "calc_ideal_points_cmat_cvec H =
    (if is_poincare_line_cmat H then
        let (A, B, C, D) = H
         in {calc_ideal_point1_cvec A B, calc_ideal_point2_cvec A B}
     else
        {(-1, 1), (1, 1)})"
lift_definition calc_ideal_points_clmat_hcoords :: "circline_mat ⇒ complex_homo_coords set" is calc_ideal_points_cmat_cvec
  by (auto simp add: Let_def split: if_split_asm)
lift_definition calc_ideal_points :: "circline ⇒ complex_homo set" is calc_ideal_points_clmat_hcoords
proof transfer
  fix H1 H2
  assume hh: "hermitean H1 ∧ H1 ≠ mat_zero" "hermitean H2 ∧ H2 ≠ mat_zero"
  obtain A1 B1 C1 D1 A2 B2 C2 D2 where *: "H1 = (A1, B1, C1, D1)" "H2 = (A2, B2, C2, D2)"
    by (cases H1, cases H2, auto)
  assume "circline_eq_cmat H1 H2"
  then obtain k where k: "k ≠ 0" "H2 = cor k *⇩s⇩m H1"
    by auto
  thus "rel_set (≈⇩v) (calc_ideal_points_cmat_cvec H1) (calc_ideal_points_cmat_cvec H2)"
  proof (cases "is_poincare_line_cmat H1")
    case True
    hence "is_poincare_line_cmat H2"
      using k * hermitean_mult_real[of H1 k] hh
      by (auto simp add: power2_eq_square norm_mult)
    have **: "sqrt (¦k¦ * cmod B1 * (¦k¦ * cmod B1) - k * Re D1 * (k * Re D1)) =
         ¦k¦ * sqrt(cmod B1 * cmod B1 - Re D1 * Re D1)"
    proof-
      have "¦k¦ * cmod B1 * (¦k¦ * cmod B1) - k * Re D1 * (k * Re D1) =
            k⇧2 * (cmod B1 * cmod B1 - Re D1 * Re D1)"
        by (simp add: power2_eq_square field_simps)
      thus ?thesis
        by (simp add: real_sqrt_mult)
    qed
    show ?thesis
      using ‹is_poincare_line_cmat H1› ‹is_poincare_line_cmat H2›
      using * k
      apply (simp add: Let_def)
      apply safe
      apply (simp add: power2_eq_square rel_set_def norm_mult)
      apply safe
         apply (cases "k > 0")
          apply (rule_tac x="(cor k)⇧2" in exI)
          apply (subst **)
          apply (simp add: power2_eq_square field_simps)
         apply (erule notE, rule_tac x="(cor k)⇧2" in exI)
         apply (subst **)
         apply (simp add: power2_eq_square field_simps)
        apply (cases "k > 0")
        apply (erule notE, rule_tac x="(cor k)⇧2" in exI)
        apply (subst **)
        apply (simp add: power2_eq_square field_simps)
        apply (rule_tac x="(cor k)⇧2" in exI)
        apply (subst **)
        apply (simp add: power2_eq_square field_simps)
       apply (cases "k > 0")
        apply (rule_tac x="(cor k)⇧2" in exI)
        apply (subst **)
        apply (simp add: power2_eq_square field_simps)
       apply (erule notE, rule_tac x="(cor k)⇧2" in exI)
       apply (subst **)
       apply (simp add: power2_eq_square field_simps)
      apply (rule_tac x="(cor k)⇧2" in exI)
      apply (cases "k > 0")
       apply (erule notE, rule_tac x="(cor k)⇧2" in exI)
       apply (subst **)
       apply (simp add: power2_eq_square field_simps)
      apply (subst **)
      apply (simp add: power2_eq_square field_simps)
      done
  next
    case False
    hence "¬ is_poincare_line_cmat H2"
      using k * hermitean_mult_real[of H1 k] hh
      by (auto simp add: power2_eq_square norm_mult)
    have "rel_set (≈⇩v) {(- 1, 1), (1, 1)} {(- 1, 1), (1, 1)}"
      by (simp add: rel_set_def)
    thus ?thesis
      using ‹¬ is_poincare_line_cmat H1› ‹¬ is_poincare_line_cmat H2›
      using *
      by (auto simp add: Let_def)
  qed
qed
text ‹Correctness of the calculation›
text‹We show that for every h-line its two calculated ideal points are different and are on the
intersection of that line and the unit circle.›
text ‹Calculated ideal points are on the unit circle›
lemma calc_ideal_point_1_unit:
  assumes "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  assumes "(z1, z2) = calc_ideal_point1_cvec A B"
  shows "z1 * cnj z1 = z2 * cnj z2"
proof-
  let ?discr = "Re ((cmod B)⇧2 - (Re A)⇧2)"
  have "?discr > 0"
    using assms
    by (simp add: cmod_power2)
  have "(B*(-A - 𝗂*sqrt(?discr))) * cnj (B*(-A - 𝗂*sqrt(?discr))) = (B * cnj B) * (A⇧2 + cor (abs ?discr))"
    using ‹is_real A› eq_cnj_iff_real[of A]
    by (simp add: field_simps power2_eq_square)
  also have "... = (B * cnj B) * (cmod B)⇧2"
    using ‹?discr > 0›
    using assms
    using complex_of_real_Re[of "(cmod B)⇧2 - (Re A)⇧2"] complex_of_real_Re[of A] ‹is_real A›
    by (simp add: power2_eq_square)
  also have "... = (cmod B)⇧2 * cnj ((cmod B)⇧2)"
    using complex_cnj_complex_of_real complex_mult_cnj_cmod
    by presburger
  finally show ?thesis
    using assms
    by simp
qed
lemma calc_ideal_point_2_unit:
  assumes "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  assumes "(z1, z2) = calc_ideal_point2_cvec A B"
  shows "z1 * cnj z1 = z2 * cnj z2"
proof-
  let ?discr = "Re ((cmod B)⇧2 - (Re A)⇧2)"
  have "?discr > 0"
    using assms
    by (simp add: cmod_power2)
  have "(B*(-A + 𝗂*sqrt(?discr))) * cnj (B*(-A + 𝗂*sqrt(?discr))) = (B * cnj B) * (A⇧2 + cor (abs ?discr))"
    using ‹is_real A› eq_cnj_iff_real[of A]
    by (simp add: field_simps power2_eq_square)
  also have "... = (B * cnj B) * (cmod B)⇧2"
    using ‹?discr > 0›
    using assms
    using complex_of_real_Re[of "(cmod B)⇧2 - (Re A)⇧2"] complex_of_real_Re[of A] ‹is_real A›
    by (simp add: power2_eq_square)
  also have "... = (cmod B)⇧2 * cnj ((cmod B)⇧2)"
    using complex_cnj_complex_of_real complex_mult_cnj_cmod
    by presburger
  finally show ?thesis
    using assms
    by simp
qed
lemma calc_ideal_points_on_unit_circle:
  shows "∀ z ∈ calc_ideal_points H. z ∈ circline_set unit_circle"
  unfolding circline_set_def
  apply simp
proof (transfer, transfer)
  fix H
  assume hh: "hermitean H ∧ H ≠ mat_zero"
  obtain A B C D where *: "H = (A, B, C, D)"
    by (cases H, auto)
  have "∀ (z1, z2) ∈ calc_ideal_points_cmat_cvec H. z1 * cnj z1 = z2 * cnj z2"
    using hermitean_elems[of A B C D]
    unfolding calc_ideal_points_cmat_cvec_def
    using calc_ideal_point_1_unit[of A B]
    using calc_ideal_point_2_unit[of A B]
    using hh *
    apply (cases "calc_ideal_point1_cvec A B", cases "calc_ideal_point2_cvec A B")
    apply (auto simp add: Let_def simp del: calc_ideal_point1_cvec_def calc_ideal_point2_cvec_def)
    done
  thus "Ball (calc_ideal_points_cmat_cvec H) (on_circline_cmat_cvec unit_circle_cmat)"
    using on_circline_cmat_cvec_unit
    by (auto simp del: on_circline_cmat_cvec_def calc_ideal_points_cmat_cvec_def)
qed
text ‹Calculated ideal points are on the h-line›
lemma calc_ideal_point1_sq:
  assumes "(z1, z2) = calc_ideal_point1_cvec A B" "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  shows "z1 * cnj z1 + z2 * cnj z2 = 2 * (B * cnj B)⇧2"
proof-
  let ?discr = "Re ((cmod B)⇧2 - (Re A)⇧2)"
  have "?discr > 0"
    using assms
    by (simp add: cmod_power2)
  have "z1 * cnj z1 = (B * cnj B) * (-A + 𝗂*sqrt(?discr))*(-A - 𝗂*sqrt(?discr))"
    using assms eq_cnj_iff_real[of A]
    by (simp)
  also have "... = (B * cnj B) * (A⇧2 + ?discr)"
    using complex_of_real_Re[of A] ‹is_real A› ‹?discr > 0›
    by (simp add: power2_eq_square field_simps)
  finally
  have "z1 * cnj z1 = (B * cnj B)⇧2"
    using complex_of_real_Re[of "(cmod B)⇧2 - (Re A)⇧2"] complex_of_real_Re[of A] ‹is_real A›
    using complex_mult_cnj_cmod[of B]
    by (simp add: power2_eq_square)
  moreover
  have "z2 * cnj z2 = (B * cnj B)⇧2"
    using assms
    by simp
  ultimately
  show ?thesis
    by simp
qed
lemma calc_ideal_point2_sq:
  assumes "(z1, z2) = calc_ideal_point2_cvec A B" "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  shows "z1 * cnj z1 + z2 * cnj z2 = 2 * (B * cnj B)⇧2"
proof-
  let ?discr = "Re ((cmod B)⇧2 - (Re A)⇧2)"
  have "?discr > 0"
    using assms
    by (simp add: cmod_power2)
  have "z1 * cnj z1 = (B * cnj B) * (-A + 𝗂*sqrt(?discr))*(-A - 𝗂*sqrt(?discr))"
    using assms eq_cnj_iff_real[of A]
    by simp
  also have "... = (B * cnj B) * (A⇧2 + ?discr)"
    using complex_of_real_Re[of A] ‹is_real A› ‹?discr > 0›
    by (simp add: power2_eq_square field_simps)
  finally
  have "z1 * cnj z1 = (B * cnj B)⇧2"
    using complex_of_real_Re[of "(cmod B)⇧2 - (Re A)⇧2"] complex_of_real_Re[of A] ‹is_real A›
    using complex_mult_cnj_cmod[of B]
    by (simp add: power2_eq_square)
  moreover
  have "z2 * cnj z2 = (B * cnj B)⇧2"
    using assms
    by simp
  ultimately
  show ?thesis
    by simp
qed
lemma calc_ideal_point1_mix:
  assumes "(z1, z2) = calc_ideal_point1_cvec A B" "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  shows "B * cnj z1 * z2 + cnj B * z1 * cnj z2 = - 2 * A * (B * cnj B)⇧2 "
proof-
  have "B*cnj z1 + cnj B*z1 = -2*A*B*cnj B"
    using assms eq_cnj_iff_real[of A]
    by (simp, simp add: field_simps)
  moreover
  have "cnj z2 = z2"
    using assms
    by simp
  hence "B*cnj z1*z2 + cnj B*z1*cnj z2 = (B*cnj z1 + cnj B*z1)*z2"
    by (simp add: field_simps)
  ultimately
  have "B*cnj z1*z2 + cnj B*z1*cnj z2 = -2*A*(B* cnj B)*z2"
    by simp
  also have "… = -2*A*(B * cnj B)⇧2"
    using assms
    using complex_mult_cnj_cmod[of B]
    by (simp add: power2_eq_square)
  finally
  show ?thesis
    .
qed
lemma calc_ideal_point2_mix:
  assumes "(z1, z2) = calc_ideal_point2_cvec A B" "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  shows "B * cnj z1 * z2 + cnj B * z1 * cnj z2 = - 2 * A * (B * cnj B)⇧2 "
proof-
  have "B*cnj z1 + cnj B*z1 = -2*A*B*cnj B"
    using assms eq_cnj_iff_real[of A]
    by (simp, simp add: field_simps)
  moreover
  have "cnj z2 = z2"
    using assms
    by simp
  hence "B*cnj z1*z2 + cnj B*z1*cnj z2 = (B*cnj z1 + cnj B*z1)*z2"
    by (simp add: field_simps)
  ultimately
  have "B*cnj z1*z2 + cnj B*z1*cnj z2 = -2*A*(B* cnj B)*z2"
    by simp
  also have "… = -2*A*(B * cnj B)⇧2"
    using assms
    using complex_mult_cnj_cmod[of B]
    by (simp add: power2_eq_square)
  finally
  show ?thesis
    .
qed
lemma calc_ideal_point1_on_circline:
  assumes "(z1, z2) = calc_ideal_point1_cvec A B" "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  shows "A*z1*cnj z1 + B*cnj z1*z2 + cnj B*z1*cnj z2 + A*z2*cnj z2 = 0" (is "?lhs = 0")
proof-
  have "?lhs = A * (z1 * cnj z1 + z2 * cnj z2) + (B * cnj z1 * z2 + cnj B * z1 * cnj z2)"
    by (simp add: field_simps)
  also have "... = 2*A*(B*cnj B)⇧2 + (-2*A*(B*cnj B)⇧2)"
    using calc_ideal_point1_sq[OF assms]
    using calc_ideal_point1_mix[OF assms]
    by simp
  finally
  show ?thesis
    by simp
qed
lemma calc_ideal_point2_on_circline:
  assumes "(z1, z2) = calc_ideal_point2_cvec A B" "is_real A" "(cmod B)⇧2 > (cmod A)⇧2"
  shows "A*z1*cnj z1 + B*cnj z1*z2 + cnj B*z1*cnj z2 + A*z2*cnj z2 = 0" (is "?lhs = 0")
proof-
  have "?lhs = A * (z1 * cnj z1 + z2 * cnj z2) + (B * cnj z1 * z2 + cnj B * z1 * cnj z2)"
    by (simp add: field_simps)
  also have "... = 2*A*(B*cnj B)⇧2 + (-2*A*(B*cnj B)⇧2)"
    using calc_ideal_point2_sq[OF assms]
    using calc_ideal_point2_mix[OF assms]
    by simp
  finally
  show ?thesis
    by simp
qed
lemma calc_ideal_points_on_circline:
  assumes "is_poincare_line H"
  shows "∀ z ∈ calc_ideal_points H. z ∈ circline_set H"
  using assms
  unfolding circline_set_def
  apply simp
proof (transfer, transfer)
  fix H
  assume hh: "hermitean H ∧ H ≠ mat_zero"
  obtain A B C D where *: "H = (A, B, C, D)"
    by (cases H, auto)
  obtain z11 z12 z21 z22 where **: "(z11, z12) = calc_ideal_point1_cvec A B" "(z21, z22) = calc_ideal_point2_cvec A B"
    by (cases "calc_ideal_point1_cvec A B", cases "calc_ideal_point2_cvec A B") auto
  assume "is_poincare_line_cmat H"
  hence "∀ (z1, z2) ∈ calc_ideal_points_cmat_cvec H. A*z1*cnj z1 + B*cnj z1*z2 + C*z1*cnj z2 + D*z2*cnj z2 = 0"
    using * ** hh
    using hermitean_elems[of A B C D]
    using calc_ideal_point1_on_circline[of z11 z12 A B]
    using calc_ideal_point2_on_circline[of z21 z22 A B]
    by (auto simp del: calc_ideal_point1_cvec_def calc_ideal_point2_cvec_def)
  thus "Ball (calc_ideal_points_cmat_cvec H) (on_circline_cmat_cvec H)"
    using on_circline_cmat_cvec_circline_equation *
    by (auto simp del: on_circline_cmat_cvec_def calc_ideal_points_cmat_cvec_def simp add: field_simps)
qed
text ‹Calculated ideal points of an h-line are different›
lemma calc_ideal_points_cvec_different [simp]:
  assumes "(cmod B)⇧2 > (cmod A)⇧2" "is_real A"
  shows "¬ (calc_ideal_point1_cvec A B ≈⇩v calc_ideal_point2_cvec A B)"
  using assms
  by (auto) (auto simp add: cmod_def)
lemma calc_ideal_points_different:
  assumes "is_poincare_line H"
  shows  "∃ i1 ∈ (calc_ideal_points H). ∃ i2 ∈ (calc_ideal_points H). i1 ≠ i2"
  using assms
proof (transfer, transfer)
  fix H
  assume hh: "hermitean H ∧ H ≠ mat_zero" "is_poincare_line_cmat H"
  obtain A B C D where *: "H = (A, B, C, D)"
    by (cases H, auto)
  hence "is_real A" using hh hermitean_elems by auto
  thus "∃i1∈calc_ideal_points_cmat_cvec H. ∃i2∈calc_ideal_points_cmat_cvec H. ¬ i1 ≈⇩v i2"
    using * hh calc_ideal_points_cvec_different[of A B]
    apply (rule_tac x="calc_ideal_point1_cvec A B" in bexI)
    apply (rule_tac x="calc_ideal_point2_cvec A B" in bexI)
    by auto
qed
lemma two_calc_ideal_points [simp]:
  assumes "is_poincare_line H"                
  shows "card (calc_ideal_points H) = 2"
proof-
  have  "∃ x ∈ calc_ideal_points H. ∃ y ∈ calc_ideal_points H. ∀ z ∈ calc_ideal_points H. z = x ∨ z = y"
    by (transfer, transfer, case_tac H, simp del: calc_ideal_point1_cvec_def calc_ideal_point2_cvec_def)
  then obtain x y where *: "calc_ideal_points H = {x, y}"
    by auto
  moreover
  have "x ≠ y"
    using calc_ideal_points_different[OF assms] *
    by auto
  ultimately
  show ?thesis
    by auto
qed
subsubsection ‹Ideal points›
text ‹Next we give a genuine definition of ideal points -- these are the intersections of the h-line with the unit circle›
definition ideal_points :: "circline ⇒ complex_homo set" where
  "ideal_points H = circline_intersection H unit_circle"
text ‹Ideal points are on the unit circle and on the h-line›
lemma ideal_points_on_unit_circle:
  shows "∀ z ∈ ideal_points H. z ∈ circline_set unit_circle"
  unfolding ideal_points_def circline_intersection_def circline_set_def
  by simp
lemma ideal_points_on_circline:
  shows "∀ z ∈ ideal_points H. z ∈ circline_set H"
  unfolding ideal_points_def circline_intersection_def circline_set_def
  by simp
text ‹For each h-line there are exactly two ideal points›
lemma two_ideal_points:
  assumes "is_poincare_line H"
  shows "card (ideal_points H) = 2"
proof-
  have "H ≠ unit_circle"
    using assms not_is_poincare_line_unit_circle
    by auto
  let ?int = "circline_intersection H unit_circle"
  obtain i1 i2 where "i1 ∈ ?int" "i2 ∈ ?int" "i1 ≠ i2"
    using calc_ideal_points_on_circline[OF assms]
    using calc_ideal_points_on_unit_circle[of H]
    using calc_ideal_points_different[OF assms]
    unfolding circline_intersection_def circline_set_def
    by auto
  thus ?thesis
    unfolding ideal_points_def
    using circline_intersection_at_most_2_points[OF ‹H ≠ unit_circle›]
    using card_geq_2_iff_contains_2_elems[of ?int]
    by auto
qed
text ‹They are exactly the two points that our calculation finds›
lemma ideal_points_unique:
  assumes "is_poincare_line H"
  shows "ideal_points H = calc_ideal_points H"
proof-
  have "calc_ideal_points H ⊆ ideal_points H"
    using calc_ideal_points_on_circline[OF assms]
    using calc_ideal_points_on_unit_circle[of H]
    unfolding ideal_points_def circline_intersection_def circline_set_def
    by auto
  moreover
  have "H ≠ unit_circle"
    using not_is_poincare_line_unit_circle assms
    by auto
  hence "finite (ideal_points H)"
    using circline_intersection_at_most_2_points[of H unit_circle]
    unfolding ideal_points_def
    by auto
  ultimately
  show ?thesis
    using card_subset_eq[of "ideal_points H" "calc_ideal_points H"]
    using two_calc_ideal_points[OF assms]
    using two_ideal_points[OF assms]
    by auto
qed
text ‹For each h-line we can obtain two different ideal points›
lemma obtain_ideal_points:
  assumes "is_poincare_line H"
  obtains i1 i2 where "i1 ≠ i2" "ideal_points H = {i1, i2}"
  using two_ideal_points[OF assms] card_eq_2_iff_doubleton[of "ideal_points H"]
  by blast
text ‹Ideal points of each h-line constructed from two points in the disc are different than those two points›
lemma ideal_points_different:
  assumes "u ∈ unit_disc" "v ∈ unit_disc" "u ≠ v"
  assumes "ideal_points (poincare_line u v) = {i1, i2}"
  shows "i1 ≠ i2" "u ≠ i1" "u ≠ i2" "v ≠ i1" "v ≠ i2"
proof-
  have "i1 ∈ ocircline_set ounit_circle" "i2 ∈ ocircline_set ounit_circle"
    using assms(3) assms(4) ideal_points_on_unit_circle is_poincare_line_poincare_line
    by fastforce+
  thus "u ≠ i1" "u ≠ i2" "v ≠ i1" "v ≠ i2"
    using assms(1-2)
    using disc_inter_ocircline_set[of ounit_circle]
    unfolding unit_disc_def
    by auto
  show "i1 ≠ i2"
    using assms
    by (metis doubleton_eq_iff is_poincare_line_poincare_line obtain_ideal_points)
qed
text ‹H-line is uniquely determined by its ideal points›
lemma ideal_points_line_unique:        
  assumes "is_poincare_line H" "ideal_points H = {i1, i2}"
  shows "H = poincare_line i1 i2"
  by (smt assms(1) assms(2) calc_ideal_points_on_unit_circle circline_set_def ex_poincare_line_points ideal_points_different(1) ideal_points_on_circline ideal_points_unique insertI1 insert_commute inversion_unit_circle mem_Collect_eq unique_poincare_line_general)
text ‹Ideal points of some special h-lines›
text‹Ideal points of @{term x_axis}›
lemma ideal_points_x_axis
  [simp]: "ideal_points x_axis = {of_complex (-1), of_complex 1}"
proof (subst ideal_points_unique, simp)
  have "calc_ideal_points_clmat_hcoords x_axis_clmat = {of_complex_hcoords (- 1), of_complex_hcoords 1}"
    by transfer auto
  thus "calc_ideal_points x_axis = {of_complex (- 1), of_complex 1}"
    by (simp add: calc_ideal_points.abs_eq of_complex.abs_eq x_axis_def)
qed
text ‹Ideal points are proportional vectors only if h-line is a line segment passing trough zero›
lemma ideal_points_proportional:
  assumes "is_poincare_line H" "ideal_points H = {i1, i2}" "to_complex i1 = cor k * to_complex i2"
  shows "0⇩h ∈ circline_set H"
proof-
  have "i1 ≠ i2"
    using ‹ideal_points H = {i1, i2}›
    using ‹is_poincare_line H› ex_poincare_line_points ideal_points_different(1) by blast
  have "i1 ∈ circline_set unit_circle" "i2 ∈ circline_set unit_circle"
    using assms calc_ideal_points_on_unit_circle ideal_points_unique 
    by blast+
  hence "cmod (cor k) = 1"
    using ‹to_complex i1 = cor k * to_complex i2›
    by (metis (mono_tags, lifting) circline_set_unit_circle imageE mem_Collect_eq mult.right_neutral norm_mult to_complex_of_complex unit_circle_set_def)
  hence "k = -1"
    using ‹to_complex i1 = cor k * to_complex i2› ‹i1 ≠ i2›
    using ‹i1 ∈ circline_set unit_circle› ‹i2 ∈ circline_set unit_circle›
    by (smt (verit, best) mult_cancel_right1 norm_of_real not_inf_on_unit_circle'' of_complex_to_complex of_real_1) 
  have "∀ i1 ∈ calc_ideal_points H. ∀ i2 ∈ calc_ideal_points H. is_poincare_line H ∧ i1 ≠ i2 ∧ to_complex i1 = - to_complex i2 ⟶ 
           0⇩h ∈ circline_set H"
    unfolding circline_set_def
  proof (simp, transfer, transfer, safe)
    fix A B C D i11 i12 i21 i22 k
    assume H:"hermitean (A, B, C, D)" "(A, B, C, D) ≠ mat_zero" 
    assume line: "is_poincare_line_cmat (A, B, C, D)"
    assume i1: "(i11, i12) ∈ calc_ideal_points_cmat_cvec (A, B, C, D)"
    assume i2:"(i21, i22) ∈ calc_ideal_points_cmat_cvec (A, B, C, D)"
    assume "¬ (i11, i12) ≈⇩v (i21, i22)"
    assume opposite: "to_complex_cvec (i11, i12) = - to_complex_cvec (i21, i22)"
    let ?discr =  "sqrt ((cmod B)⇧2 - (Re D)⇧2)"
    let ?den = "(cmod B)⇧2"
    let ?i1 = "B * (- D - 𝗂 * ?discr)"
    let ?i2 = "B * (- D + 𝗂 * ?discr)"
    have "i11 = ?i1 ∨ i11 = ?i2" "i12 = ?den"
      "i21 = ?i1 ∨ i21 = ?i2" "i22 = ?den"  
      using i1 i2 H line
      by (auto split: if_split_asm)
    hence i: "i11 = ?i1 ∧ i21 = ?i2 ∨ i11 = ?i2 ∧ i21 = ?i1"
      using ‹¬ (i11, i12) ≈⇩v (i21, i22)›
      by auto
    have "?den ≠ 0"
      using line
      by auto
    hence "i11 = - i21"
      using opposite ‹i12 = ?den› ‹i22 = ?den›
      by (simp add: nonzero_neg_divide_eq_eq2)
    hence "?i1 = - ?i2"
      using i 
      by (metis add.inverse_inverse)
    hence "D = 0"
      using ‹?den ≠ 0›
      by (simp add: field_simps)
    thus "on_circline_cmat_cvec (A, B, C, D) 0⇩v"
      by (simp add: vec_cnj_def)
  qed
  thus ?thesis
    using assms ‹k = -1›
    using calc_ideal_points_different ideal_points_unique
    by fastforce
qed
text ‹Transformations of ideal points›
text ‹Möbius transformations that fix the unit disc when acting on h-lines map their ideal points to ideal points.›
lemma ideal_points_moebius_circline [simp]:
  assumes  "unit_circle_fix M" "is_poincare_line H"
  shows "ideal_points (moebius_circline M H) = (moebius_pt M) ` (ideal_points H)" (is "?I' = ?M ` ?I")
proof-
  obtain i1 i2 where *: "i1 ≠ i2" "?I = {i1, i2}"
    using assms(2)
    by (rule obtain_ideal_points)
  let ?Mi1 = "?M i1" and ?Mi2 = "?M i2"
  have "?Mi1 ∈ ?M ` (circline_set H)"
       "?Mi2 ∈ ?M ` (circline_set H)"
       "?Mi1 ∈ ?M ` (circline_set unit_circle)"
       "?Mi2 ∈ ?M ` (circline_set unit_circle)"
    using *
    unfolding ideal_points_def circline_intersection_def circline_set_def
    by blast+
  hence "?Mi1 ∈ ?I'"
        "?Mi2 ∈ ?I'"
    using unit_circle_fix_iff[of M] assms
    unfolding ideal_points_def circline_intersection_def circline_set_def
    by (metis mem_Collect_eq moebius_circline)+
  moreover
  have "?Mi1 ≠ ?Mi2"
    using bij_moebius_pt[of M] *
    using moebius_pt_invert by blast
  moreover
  have "is_poincare_line (moebius_circline M H)"
    using assms unit_circle_fix_preserve_is_poincare_line
    by simp
  ultimately
  have "?I' = {?Mi1, ?Mi2}"
    using two_ideal_points[of "moebius_circline M H"]
    using card_eq_2_doubleton[of ?I' ?Mi1 ?Mi2]
    by simp
  thus ?thesis
    using *(2)
    by auto
qed
lemma ideal_points_poincare_line_moebius [simp]:
  assumes "unit_disc_fix M"  "u ∈ unit_disc" "v ∈ unit_disc" "u ≠ v"
  assumes "ideal_points (poincare_line u v) = {i1, i2}"
  shows "ideal_points (poincare_line (moebius_pt M u) (moebius_pt M v)) = {moebius_pt M i1, moebius_pt M i2}"
  using assms
  by auto
text ‹Conjugation also maps ideal points to ideal points›
lemma ideal_points_conjugate [simp]:
  assumes "is_poincare_line H"
  shows "ideal_points (conjugate_circline H) = conjugate ` (ideal_points H)" (is "?I' = ?M ` ?I")
proof-
  obtain i1 i2 where *: "i1 ≠ i2" "?I = {i1, i2}"
    using assms
    by (rule obtain_ideal_points)
  let ?Mi1 = "?M i1" and ?Mi2 = "?M i2"
  have "?Mi1 ∈ ?M ` (circline_set H)"
       "?Mi2 ∈ ?M ` (circline_set H)"
       "?Mi1 ∈ ?M ` (circline_set unit_circle)"
       "?Mi2 ∈ ?M ` (circline_set unit_circle)"
    using *
    unfolding ideal_points_def circline_intersection_def circline_set_def
    by blast+                                   
  hence "?Mi1 ∈ ?I'"
        "?Mi2 ∈ ?I'"
    unfolding ideal_points_def circline_intersection_def circline_set_def
    using circline_set_conjugate_circline circline_set_def conjugate_unit_circle_set 
    by blast+
  moreover
  have "?Mi1 ≠ ?Mi2"
    using ‹i1 ≠ i2›
    by (auto simp add: conjugate_inj)
  moreover
  have "is_poincare_line (conjugate_circline H)"
    using assms
    by simp
  ultimately
  have "?I' = {?Mi1, ?Mi2}"
    using two_ideal_points[of "conjugate_circline H"]
    using card_eq_2_doubleton[of ?I' ?Mi1 ?Mi2]
    by simp
  thus ?thesis
    using *(2)
    by auto
qed
lemma ideal_points_poincare_line_conjugate [simp]:
  assumes"u ∈ unit_disc" "v ∈ unit_disc" "u ≠ v"
  assumes "ideal_points (poincare_line u v) = {i1, i2}"
  shows "ideal_points (poincare_line (conjugate u) (conjugate v)) = {conjugate i1, conjugate i2}"
  using assms
  by auto
end