Theory Poincare_Between

theory Poincare_Between
  imports Poincare_Distance
begin

(* ------------------------------------------------------------------ *)
section‹H-betweenness in the Poincar\'e model›
(* ------------------------------------------------------------------ *)

subsection ‹H-betwenness expressed by a cross-ratio›

text‹The point $v$ is h-between $u$ and $w$ if the cross-ratio between the pairs $u$ and $w$ and $v$
and inverse of $v$ is real and negative.›
definition poincare_between :: "complex_homo  complex_homo  complex_homo  bool" where
  "poincare_between u v w 
         u = v  v = w 
         (let cr = cross_ratio u v w (inversion v)
           in is_real (to_complex cr)  Re (to_complex cr) < 0)"

subsubsection ‹H-betwenness is preserved by h-isometries›

text ‹Since they preserve cross-ratio and inversion, h-isometries (unit disc preserving Möbius
transformations and conjugation) preserve h-betweeness.›

lemma unit_disc_fix_moebius_preserve_poincare_between [simp]:
  assumes "unit_disc_fix M" and "u  unit_disc" and "v  unit_disc" and "w  unit_disc"
  shows "poincare_between (moebius_pt M u) (moebius_pt M v) (moebius_pt M w) 
         poincare_between u v w"
proof (cases "u = v  v = w")
  case True
  thus ?thesis
    using assms
    unfolding poincare_between_def
    by auto
next
  case False
  moreover
  hence "moebius_pt M u  moebius_pt M v  moebius_pt M v  moebius_pt M w"
    by auto
  moreover
  have "v  inversion v" "w  inversion v"
    using inversion_noteq_unit_disc[of v w]
    using inversion_noteq_unit_disc[of v v]
    using v  unit_disc w  unit_disc
    by auto
  ultimately
  show ?thesis
    using assms
    using unit_circle_fix_moebius_pt_inversion[of M v, symmetric]
    unfolding poincare_between_def
    by (simp del: unit_circle_fix_moebius_pt_inversion)
qed

lemma conjugate_preserve_poincare_between [simp]:
  assumes "u  unit_disc" and "v  unit_disc" and "w  unit_disc"
  shows "poincare_between (conjugate u) (conjugate v) (conjugate w) 
         poincare_between u v w"
proof (cases "u = v  v = w")
  case True
  thus ?thesis
    using assms
    unfolding poincare_between_def
    by auto
next
  case False
  moreover
  hence "conjugate u  conjugate v  conjugate v  conjugate w"
    using conjugate_inj by blast
  moreover
  have "v  inversion v" "w  inversion v"
    using inversion_noteq_unit_disc[of v w]
    using inversion_noteq_unit_disc[of v v]
    using v  unit_disc w  unit_disc
    by auto
  ultimately
  show ?thesis
    using assms
    using conjugate_cross_ratio[of v w "inversion v" u]
    unfolding poincare_between_def
    by (metis conjugate_id_iff conjugate_involution inversion_def inversion_sym o_apply)
qed


subsubsection ‹Some elementary properties of h-betwenness›

lemma poincare_between_nonstrict [simp]:
  shows "poincare_between u u v" and "poincare_between u v v"
  by (simp_all add: poincare_between_def)                       

lemma poincare_between_sandwich:
  assumes "u  unit_disc" and "v  unit_disc"
  assumes "poincare_between u v u"
  shows "u = v"
proof (rule ccontr)
  assume "¬ ?thesis"
  thus False
    using assms
    using inversion_noteq_unit_disc[of v u]
    using cross_ratio_1[of v u "inversion v"]
    unfolding poincare_between_def Let_def
    by auto
qed

lemma poincare_between_rev:
  assumes "u  unit_disc" and "v  unit_disc" and "w  unit_disc"
  shows "poincare_between u v w  poincare_between w v u"       
  using assms 
  using inversion_noteq_unit_disc[of v w]
  using inversion_noteq_unit_disc[of v u]
  using cross_ratio_commute_13[of u v w "inversion v"]
  using cross_ratio_not_inf[of w "inversion v" v u]
  using cross_ratio_not_zero[of w v u "inversion v"]
  using inf_or_of_complex[of "cross_ratio w v u (inversion v)"]
  unfolding poincare_between_def
  by (auto simp add: Let_def Im_complex_div_eq_0 Re_divide divide_less_0_iff)

subsubsection ‹H-betwenness and h-collinearity›

text‹Three points can be in an h-between relation only when they are h-collinear.›
lemma poincare_between_poincare_collinear [simp]:       
  assumes in_disc: "u  unit_disc"  "v  unit_disc"  "w  unit_disc"
  assumes betw: "poincare_between u v w"
  shows "poincare_collinear {u, v, w}"
proof (cases "u = v  v = w")
  case True
  thus ?thesis
    using assms
    by auto
next
  case False
  hence distinct: "distinct [u, v, w, inversion v]"
    using in_disc inversion_noteq_unit_disc[of v v] inversion_noteq_unit_disc[of v u] inversion_noteq_unit_disc[of v w]
    using betw poincare_between_sandwich[of w v]
    by (auto simp add: poincare_between_def Let_def)

  then obtain H where *: "{u, v, w, inversion v}  circline_set H"
    using assms
    unfolding poincare_between_def
    using four_points_on_circline_iff_cross_ratio_real[of u v w "inversion v"]
    by auto
  hence "H = poincare_line u v"
    using assms distinct
    using unique_circline_set[of u v "inversion v"]
    using poincare_line[of u v] poincare_line_inversion[of u v]
    unfolding circline_set_def
    by auto
  thus ?thesis
    using * assms False
    unfolding poincare_collinear_def
    by (rule_tac x="poincare_line u v" in exI) simp
qed

lemma poincare_between_poincare_line_uvz:
  assumes "u  v" and "u  unit_disc" and "v  unit_disc" and
          "z  unit_disc" and "poincare_between u v z"
  shows "z  circline_set (poincare_line u v)"
  using assms
  using poincare_between_poincare_collinear[of u v z]
  using unique_poincare_line[OF assms(1-3)]
  unfolding poincare_collinear_def
  by auto

lemma poincare_between_poincare_line_uzv:
  assumes "u  v" and "u  unit_disc" and "v  unit_disc" and
          "z  unit_disc" "poincare_between u z v"
  shows "z  circline_set (poincare_line u v)"
  using assms
  using poincare_between_poincare_collinear[of u z v]
  using unique_poincare_line[OF assms(1-3)]
  unfolding poincare_collinear_def
  by auto

subsubsection ‹H-betweeness on Euclidean segments› 

text‹If the three points lie on an h-line that is a Euclidean line (e.g., if it contains zero),
h-betweenness can be characterized much simpler than in the definition.›

lemma poincare_between_x_axis_u0v:
  assumes "is_real u'" and "u'  0" and "v'  0"
  shows "poincare_between (of_complex u') 0h (of_complex v')  is_real v'  Re u' * Re v' < 0"
proof-
  have "Re u'  0"
    using is_real u' u'  0
    using complex_eq_if_Re_eq
    by auto
  have nz: "of_complex u'  0h" "of_complex v'  0h"
    by (simp_all add: u'  0 v'  0)
  hence "0h  of_complex v'"
    by metis

  let ?cr = "cross_ratio (of_complex u') 0h (of_complex v') h"
  have "is_real (to_complex ?cr)  Re (to_complex ?cr) < 0  is_real v'  Re u' * Re v' < 0"
    using cross_ratio_0inf[of v' u'] v'  0 u'  0 is_real u'
    by (metis Re_complex_div_lt_0 Re_mult_real complex_cnj_divide divide_cancel_left eq_cnj_iff_real to_complex_of_complex)
  thus ?thesis
    unfolding poincare_between_def inversion_zero
    using of_complex u'  0h 0h  of_complex v'
    by simp
qed

lemma poincare_between_u0v:
  assumes "u  unit_disc" and "v  unit_disc" and "u  0h" and "v  0h"
  shows "poincare_between u 0h v  ( k < 0. to_complex u = cor k * to_complex v)" (is "?P u v")
proof (cases "u = v")
  case True
  thus ?thesis
    using assms
    using inf_or_of_complex[of v]
    using poincare_between_sandwich[of u "0h"]      
    by auto
next                                                 
  case False
  have " u. u  unit_disc  u  0h  ?P u v" (is "?P' v")
  proof (rule wlog_rotation_to_positive_x_axis)
    fix φ v
    let ?M = "moebius_pt (moebius_rotation φ)"
    assume 1: "v  unit_disc" "v  0h"
    assume 2: "?P' (?M v)"
    show "?P' v"
    proof (rule allI, rule impI, (erule conjE)+)
      fix u
      assume 3: "u  unit_disc" "u  0h"  
      have "poincare_between (?M u) 0h (?M v)  poincare_between u 0h v"
        using u  unit_disc v  unit_disc
        using unit_disc_fix_moebius_preserve_poincare_between unit_disc_fix_rotation zero_in_unit_disc 
        by fastforce
      thus "?P u v"
        using 1 2[rule_format, of "?M u"] 3
        using inf_or_of_complex[of u] inf_or_of_complex[of v]
        by auto
    qed
  next
    fix x
    assume 1: "is_real x" "0 < Re x" "Re x < 1"
    hence "x  0"
      by auto
    show "?P' (of_complex x)"    
    proof (rule allI, rule impI, (erule conjE)+)
      fix u
      assume 2: "u  unit_disc" "u  0h"
      then obtain u' where "u = of_complex u'"
        using inf_or_of_complex[of u]
        by auto
      show "?P u (of_complex x)"
        using 1 2 x  0 u = of_complex u'
        using poincare_between_rev[of u "0h" "of_complex x"]
        using poincare_between_x_axis_u0v[of x u'] is_real x
        apply (auto simp add: cmod_eq_Re)
        apply (rule_tac x="Re u' / Re x" in exI, simp add: divide_neg_pos algebra_split_simps)
        using mult_neg_pos mult_pos_neg
        by blast
    qed
  qed fact+
  thus ?thesis
    using assms
    by auto
qed

lemma poincare_between_u0v_polar_form:
  assumes "x  unit_disc" and "y  unit_disc" and "x  0h" and "y  0h" and 
          "to_complex x = cor rx * cis φ" "to_complex y = cor ry * cis φ"
  shows "poincare_between x 0h y  rx * ry < 0" (is "?P x y rx ry")
proof-
  from assms have "rx  0" "ry  0"
    using inf_or_of_complex[of x] inf_or_of_complex[of y]
    by auto

  have "(k<0. cor rx = cor k * cor ry ) = (rx * ry < 0)"
  proof
    assume "k<0. cor rx = cor k * cor ry"
    then obtain k where "k < 0" "cor rx = cor k * cor ry"
      by auto
    hence "rx = k * ry"
      using of_real_eq_iff
      by fastforce
    thus "rx * ry < 0" 
      using k < 0 rx  0 ry  0
      by (smt divisors_zero mult_nonneg_nonpos mult_nonpos_nonpos zero_less_mult_pos2)
  next
    assume "rx * ry < 0"
    hence "rx = (rx/ry)*ry" "rx / ry < 0"
      using rx  0 ry  0
      by (auto simp add: divide_less_0_iff algebra_split_simps)
    thus "k<0. cor rx = cor k * cor ry"
      using rx  0 ry  0
      by (rule_tac x="rx / ry" in exI, simp)
  qed
  thus ?thesis
    using assms                                 
    using poincare_between_u0v[OF assms(1-4)]
    by auto
qed

lemma poincare_between_x_axis_0uv:
  fixes x y :: real
  assumes "-1 < x" and "x < 1" and "x  0"
  assumes "-1 < y" and "y < 1" and "y  0"
  shows "poincare_between 0h (of_complex x) (of_complex y) 
        (x < 0  y < 0  y  x)  (x > 0  y > 0  x  y)" (is "?lhs  ?rhs")
proof (cases "x = y")
  case True
  thus ?thesis
    using assms
    unfolding poincare_between_def
    by auto
next
  case False
  let ?x = "of_complex x" and ?y = "of_complex y"

  have "?x  unit_disc" "?y  unit_disc"
    using assms
    by auto

  have distinct: "distinct [0h, ?x, ?y, inversion ?x]"
    using x  0 y  0 x  y ?x  unit_disc ?y  unit_disc
    using inversion_noteq_unit_disc[of ?x ?y]
    using inversion_noteq_unit_disc[of ?x ?x]
    using inversion_noteq_unit_disc[of ?x "0h"]
    using of_complex_inj[of x y]
    by (metis distinct_length_2_or_more distinct_singleton of_complex_zero_iff of_real_eq_0_iff of_real_eq_iff zero_in_unit_disc)

  let ?cr = "cross_ratio 0h ?x ?y (inversion ?x)"
  have "Re (to_complex ?cr) = x2 * (x*y - 1) / (x * (y - x))"
    using x  0 x  y
    unfolding inversion_def
    by simp (transfer, transfer, auto simp add: vec_cnj_def power2_eq_square field_simps split: if_split_asm)
  moreover
  { 
    fix a b :: real
    assume "b  0"
    hence "a < 0  b2 * a < (0::real)"
      by (metis mult.commute mult_eq_0_iff mult_neg_pos mult_pos_pos not_less_iff_gr_or_eq not_real_square_gt_zero power2_eq_square)
  }
  hence "x2 * (x*y - 1) < 0"
    using assms
    by (smt minus_mult_minus mult_le_cancel_left1)
  moreover
  have "x * (y - x) > 0  ?rhs"
    using x  0 y  0 x  y
    by (smt mult_le_0_iff)
  ultimately
  have *: "Re (to_complex ?cr) < 0  ?rhs"
    by (simp add: divide_less_0_iff)

  show ?thesis
  proof
    assume ?lhs
    have "is_real (to_complex ?cr)" "Re (to_complex ?cr) < 0"
      using ?lhs distinct
      unfolding poincare_between_def Let_def
      by auto
    thus ?rhs
      using *
      by simp
  next
    assume ?rhs
    hence "Re (to_complex ?cr) < 0"
      using *
      by simp
    moreover
    have "{0h, of_complex (cor x), of_complex (cor y), inversion (of_complex (cor x))}  circline_set x_axis"
      using x  0 is_real_inversion[of "cor x"]
      using inf_or_of_complex[of "inversion ?x"]
      by (auto simp del: inversion_of_complex)
    hence "is_real (to_complex ?cr)"
      using four_points_on_circline_iff_cross_ratio_real[OF distinct]
      by auto
    ultimately
    show ?lhs
      using distinct
      unfolding poincare_between_def Let_def
      by auto
  qed
qed

lemma poincare_between_0uv:
  assumes "u  unit_disc" and "v  unit_disc" and "u  0h" and "v  0h"
  shows "poincare_between 0h u v 
         (let u' = to_complex u; v' = to_complex v in Arg u' = Arg v'  cmod u'  cmod v')" (is "?P u v")
proof (cases "u = v")
  case True
  thus ?thesis
    by simp
next
  case False
  have " v. v  unit_disc  v  0h  v  u  (poincare_between 0h u v  (let u' = to_complex u; v' = to_complex v in Arg u' = Arg v'  cmod u'  cmod v'))" (is "?P' u")
  proof (rule wlog_rotation_to_positive_x_axis)
    show "u  unit_disc" "u  0h"
      by fact+
  next
    fix x
    assume *: "is_real x" "0 < Re x" "Re x < 1"
    hence "of_complex x  unit_disc" "of_complex x  0h" "of_complex x  circline_set x_axis"
      unfolding circline_set_x_axis
      by (auto simp add: cmod_eq_Re)
    show "?P' (of_complex x)"
    proof safe
      fix v
      assume "v  unit_disc" "v  0h" "v  of_complex x" "poincare_between 0h (of_complex x) v"
      hence "v  circline_set x_axis"
        using poincare_between_poincare_line_uvz[of "0h" "of_complex x" v]
        using poincare_line_0_real_is_x_axis[of "of_complex x"]
        using of_complex x  0h v  0h v  of_complex x of_complex x  unit_disc of_complex x  circline_set x_axis
        by auto
      obtain v' where "v = of_complex v'"
        using v  unit_disc
        using inf_or_of_complex[of v]
        by auto
      hence **: "v = of_complex v'" "-1 < Re v'" "Re v' < 1" "Re v'  0" "is_real v'"
        using v  unit_disc v  0h v  circline_set x_axis of_complex_inj[of v']
        unfolding circline_set_x_axis
        by (auto simp add: cmod_eq_Re real_imag_0)
      show "let u' = to_complex (of_complex x); v' = to_complex v in Arg u' = Arg v'  cmod u'  cmod v'"
        using poincare_between_x_axis_0uv[of "Re x" "Re v'"] * **
        using poincare_between 0h (of_complex x) v
        using arg_complex_of_real_positive[of "Re x"] arg_complex_of_real_negative[of "Re x"]
        using arg_complex_of_real_positive[of "Re v'"] arg_complex_of_real_negative[of "Re v'"]
        by (auto simp add: cmod_eq_Re)
    next
      fix v
      assume "v  unit_disc" "v  0h" "v  of_complex x"
      then obtain v' where **: "v = of_complex v'" "v'  0" "v'  x"
        using inf_or_of_complex[of v]
        by auto blast
      assume "let u' = to_complex (of_complex x); v' = to_complex v in Arg u' = Arg v'  cmod u'  cmod v'"
      hence ***: "Re x < 0  Re v' < 0  Re v'  Re x  0 < Re x  0 < Re v'  Re x  Re v'" "is_real v'"
        using arg_pi_iff[of x] arg_pi_iff[of v']
        using arg_0_iff[of x] arg_0_iff[of v']
        using * **
        by (smt cmod_Re_le_iff to_complex_of_complex)+
      have "-1 < Re v'" "Re v' < 1" "Re v'  0" "is_real v'"
        using v  unit_disc ** is_real v'
        by (auto simp add: cmod_eq_Re complex_eq_if_Re_eq)
      thus "poincare_between 0h (of_complex x) v"
        using poincare_between_x_axis_0uv[of "Re x" "Re v'"] * ** ***
        by simp
    qed
  next
    fix φ u
    assume "u  unit_disc" "u  0h"
    let ?M = "moebius_rotation φ"
    assume *: "?P' (moebius_pt ?M u)"
    show "?P' u"
    proof (rule allI, rule impI, (erule conjE)+)
      fix v
      assume "v  unit_disc" "v  0h" "v  u"
      have "moebius_pt ?M v  moebius_pt ?M u"
        using v  u
        by auto
      obtain u' v' where "v = of_complex v'" "u = of_complex u'" "v'  0" "u'  0"
        using inf_or_of_complex[of u] inf_or_of_complex[of v]
        using v  unit_disc u  unit_disc v  0h u  0h
        by auto
      thus "?P u v"
        using *[rule_format, of "moebius_pt ?M v"]
        using moebius_pt ?M v  moebius_pt ?M u
        using unit_disc_fix_moebius_preserve_poincare_between[of ?M "0h" u v]
        using v  unit_disc u  unit_disc v  0h u  0h
        using arg_mult_eq[of "cis φ" u' v']
        by simp (auto simp add: arg_mult norm_mult)
    qed
  qed
  thus ?thesis
    using assms False
    by auto
qed

lemma poincare_between_y_axis_0uv:
  fixes x y :: real
  assumes "-1 < x" and "x < 1" and "x  0"
  assumes "-1 < y" and "y < 1" and "y  0"
  shows "poincare_between 0h (of_complex (𝗂 * x)) (of_complex (𝗂 * y)) 
        (x < 0  y < 0  y  x)  (x > 0  y > 0  x  y)" (is "?lhs  ?rhs")
  using assms
  using poincare_between_0uv[of "of_complex (𝗂 * x)" "of_complex (𝗂 * y)"]
  using arg_pi2_iff[of "𝗂 * cor x"] arg_pi2_iff[of "𝗂 * cor y"]
  using arg_minus_pi2_iff[of "𝗂 * cor x"] arg_minus_pi2_iff[of "𝗂 * cor y"]
  apply (simp add: norm_mult)
  apply (smt (verit, best))
  done

lemma poincare_between_x_axis_uvw:
  fixes x y z :: real
  assumes "-1 < x" and "x < 1" 
  assumes "-1 < y" and "y < 1" and "y  x"
  assumes "-1 < z" and "z < 1" and "z  x"
  shows "poincare_between (of_complex x) (of_complex y) (of_complex z) 
        (y < x  z < x  z  y)  (y > x  z > x  y  z)"  (is "?lhs  ?rhs")
proof (cases "x = 0  y = 0  z = 0")
  case True
  thus ?thesis
  proof (cases "x = 0")
    case True
    thus ?thesis
      using poincare_between_x_axis_0uv assms
      by simp
  next
    case False
    show ?thesis
    proof (cases "z = 0")
      case True
      thus ?thesis
        using poincare_between_x_axis_0uv assms poincare_between_rev
        by (smt norm_of_real of_complex_zero of_real_0 poincare_between_nonstrict(2) unit_disc_iff_cmod_lt_1)
    next
      case False
      have "y = 0"
        using x  0 z  0 x = 0  y = 0  z = 0
        by simp

      have "poincare_between (of_complex x) 0h (of_complex z) = (is_real z  x * z < 0)"
        using x  0 z  0 poincare_between_x_axis_u0v 
        by auto
      moreover
      have "x * z < 0  ?rhs"
        using True x  0 z  0
        by (smt zero_le_mult_iff)
      ultimately
      show ?thesis
        using y = 0
        by auto
    qed
  qed
next
  case False
  thus ?thesis
  proof (cases "z = y")
    case True
    thus ?thesis
      using assms
      unfolding poincare_between_def
      by auto
  next
    case False
    let ?x = "of_complex x" and ?y = "of_complex y" and ?z = "of_complex z"
  
    have "?x  unit_disc" "?y  unit_disc" "?z  unit_disc"
      using assms
      by auto
  
    have distinct: "distinct [?x, ?y, ?z, inversion ?y]"
      using y  x z  x False ?x  unit_disc ?y  unit_disc ?z  unit_disc
      using inversion_noteq_unit_disc[of ?y ?y]
      using inversion_noteq_unit_disc[of ?y ?x]
      using inversion_noteq_unit_disc[of ?y ?z]
      using of_complex_inj[of x y]  of_complex_inj[of y z]  of_complex_inj[of x z]
      by auto

    have "cor y * cor x  1"
      using assms
      by (smt minus_mult_minus mult_less_cancel_left2 mult_less_cancel_right2 of_real_1 of_real_eq_iff of_real_mult)
  
    let ?cr = "cross_ratio ?x ?y ?z (inversion ?y)"
    have "Re (to_complex ?cr) = (x - y) * (z*y - 1)/ ((x*y - 1)*(z - y))"
    proof-
      have " y x z. y  x; z  x; z  y; cor y * cor x  1; x  0; y  0; z  0  
           (y * y + y * (y * (x * z)) - (y * x + y * (y * (y * z)))) /
           (y * y + y * (y * (x * z)) - (y * z + y * (y * (y * x)))) =
           (y + y * (x * z) - (x + y * (y * z))) / (y + y * (x * z) - (z + y * (y * x)))"
        by (metis (no_types, opaque_lifting) ab_group_add_class.ab_diff_conv_add_uminus distrib_left mult_divide_mult_cancel_left_if mult_minus_right)
      thus ?thesis
        using y  x z  x False ¬ (x = 0  y = 0  z = 0)
        using cor y * cor x  1
        unfolding inversion_def
        by (transfer, transfer, auto simp add: vec_cnj_def power2_eq_square field_simps split: if_split_asm)
    qed
      
    moreover
    have "(x*y - 1) < 0"
      using assms
      by (smt minus_mult_minus mult_less_cancel_right2 zero_less_mult_iff)
    moreover
    have "(z*y - 1) < 0"
      using assms
      by (smt minus_mult_minus mult_less_cancel_right2 zero_less_mult_iff)
    moreover
    have "(x - y) / (z - y) < 0  ?rhs"
      using y  x z  x False ¬ (x = 0  y = 0  z = 0)
      by (smt divide_less_cancel divide_nonneg_nonpos divide_nonneg_pos divide_nonpos_nonneg divide_nonpos_nonpos)
    ultimately
    have *: "Re (to_complex ?cr) < 0  ?rhs"
      by (smt algebra_split_simps(24) minus_divide_left zero_less_divide_iff zero_less_mult_iff)
    show ?thesis
    proof
      assume ?lhs
      have "is_real (to_complex ?cr)" "Re (to_complex ?cr) < 0"
        using ?lhs distinct
        unfolding poincare_between_def Let_def
        by auto
      thus ?rhs
        using *
        by simp
    next
      assume ?rhs
      hence "Re (to_complex ?cr) < 0"
        using *
        by simp
      moreover
      have "{of_complex (cor x), of_complex (cor y), of_complex (cor z), inversion (of_complex (cor y))}  circline_set x_axis"
        using ¬ (x = 0  y = 0  z = 0) is_real_inversion[of "cor y"]
        using inf_or_of_complex[of "inversion ?y"]
        by (auto simp del: inversion_of_complex)
      hence "is_real (to_complex ?cr)"
        using four_points_on_circline_iff_cross_ratio_real[OF distinct]
        by auto
      ultimately
      show ?lhs
        using distinct
        unfolding poincare_between_def Let_def
        by auto
    qed
  qed
qed

subsubsection ‹H-betweenness and h-collinearity›

text‹For three h-collinear points at least one of the three possible h-betweeness relations must
hold.›
lemma poincare_collinear3_between:
  assumes "u  unit_disc" and "v  unit_disc" and "w  unit_disc"
  assumes "poincare_collinear {u, v, w}"
  shows "poincare_between u v w  poincare_between u w v  poincare_between v u w" (is "?P' u v w")
proof (cases "u=v")
  case True
  thus ?thesis
    using assms
    by auto
next
  case False
  have " w. w  unit_disc  poincare_collinear {u, v, w}  ?P' u v w" (is "?P u v")
  proof (rule wlog_positive_x_axis[where P="?P"])
    fix x
    assume x: "is_real x" "0 < Re x" "Re x < 1"
    hence "x  0"
      using complex.expand[of x 0]
      by auto
    hence *: "poincare_line 0h (of_complex x) = x_axis"
      using x poincare_line_0_real_is_x_axis[of "of_complex x"]
      unfolding circline_set_x_axis
      by auto
    have "of_complex x  unit_disc"
      using x
      by (auto simp add: cmod_eq_Re)
    have "of_complex x  0h"
      using x  0
      by auto
    show "?P 0h (of_complex x)"
    proof safe
      fix w
      assume "w  unit_disc"
      assume "poincare_collinear {0h, of_complex x, w}"
      hence "w  circline_set x_axis"
        using * unique_poincare_line[of "0h" "of_complex x"] of_complex x  unit_disc x  0 of_complex x  0h
        unfolding poincare_collinear_def
        by auto
      then obtain w' where w': "w = of_complex w'" "is_real w'"
        using w  unit_disc
        using inf_or_of_complex[of w]
        unfolding circline_set_x_axis
        by auto
      hence "-1 < Re w'" "Re w' < 1"
        using w  unit_disc
        by (auto simp add: cmod_eq_Re)
      assume 1: "¬ poincare_between (of_complex x) 0h w"
      hence "w  0h" "w'  0"
        using w'
        unfolding poincare_between_def
        by auto
      hence "Re w'  0"
        using w' complex.expand[of w' 0]
        by auto

      have "Re w'  0"
        using 1 poincare_between_x_axis_u0v[of x w'] Re x > 0 is_real x x  0 w'  0 w'
        using mult_pos_neg
        by force

      moreover

      assume "¬ poincare_between 0h (of_complex x) w"
      hence "Re w' < Re x"
        using poincare_between_x_axis_0uv[of "Re x" "Re w'"]
        using w' x -1 < Re w' Re w' < 1 Re w'  0
        by auto

      ultimately
      show "poincare_between 0h w (of_complex x)"
        using poincare_between_x_axis_0uv[of "Re w'" "Re x"]
        using w' x -1 < Re w' Re w' < 1 Re w'  0
        by auto
    qed
  next
    show "u  unit_disc" "v  unit_disc" "u  v"
      by fact+
  next
    fix M u v
    assume 1: "unit_disc_fix M" "u  unit_disc" "v  unit_disc" "u  v"
    let ?Mu = "moebius_pt M u" and ?Mv = "moebius_pt M v"
    assume 2: "?P ?Mu ?Mv"
    show "?P u v"
    proof safe
      fix w
      assume "w  unit_disc" "poincare_collinear {u, v, w}" "¬ poincare_between u v w" "¬ poincare_between v u w"
      thus "poincare_between u w v"
        using 1 2[rule_format, of "moebius_pt M w"]
        by simp
    qed
  qed
  thus ?thesis
    using assms
    by simp
qed

lemma poincare_collinear3_iff:
  assumes "u  unit_disc" "v  unit_disc"  "w  unit_disc"
  shows "poincare_collinear {u, v, w}  poincare_between u v w  poincare_between v u w  poincare_between v w u"
  using assms 
  by (metis poincare_collinear3_between insert_commute poincare_between_poincare_collinear poincare_between_rev)

subsection ‹Some properties of betweenness›

lemma poincare_between_transitivity:
  assumes "a  unit_disc" and "x  unit_disc" and "b  unit_disc" and "y  unit_disc" and
          "poincare_between a x b" and "poincare_between a b y"
  shows "poincare_between x b y"
proof(cases "a = b")
  case True
  thus ?thesis
    using assms
    using poincare_between_sandwich by blast
next
  case False
  have " x.  y. poincare_between a x b  poincare_between a b y  x  unit_disc
                   y  unit_disc  poincare_between x b y" (is "?P a b")
  proof (rule wlog_positive_x_axis[where P="?P"])
    show "a  unit_disc"
      using assms by simp
  next
    show "b  unit_disc"
      using assms by simp
  next
    show "a  b"
      using False by simp
  next
    fix M u v
    assume *: "unit_disc_fix M" "u  unit_disc" "v  unit_disc" "u  v" 
              "x y. poincare_between (moebius_pt M u) x (moebius_pt M v)  
                  poincare_between (moebius_pt M u) (moebius_pt M v) y 
                  x  unit_disc  y  unit_disc 
                  poincare_between x (moebius_pt M v) y"
    show "x y. poincare_between u x v  poincare_between u v y  x  unit_disc  y  unit_disc 
                 poincare_between x v y"
    proof safe
      fix x y
      assume "poincare_between u x v" "poincare_between u v y" " x  unit_disc" "y  unit_disc"

      have "poincare_between (moebius_pt M u) (moebius_pt M x) (moebius_pt M v)" 
        using poincare_between u x v unit_disc_fix M x  unit_disc u  unit_disc v  unit_disc
        by simp
      moreover
      have "poincare_between (moebius_pt M u) (moebius_pt M v) (moebius_pt M y)"
        using poincare_between u v y unit_disc_fix M y  unit_disc u  unit_disc v  unit_disc
        by simp
      moreover
      have "(moebius_pt M x)  unit_disc"
        using unit_disc_fix M x  unit_disc by simp
      moreover
      have "(moebius_pt M y)  unit_disc"
        using unit_disc_fix M y  unit_disc by simp
      ultimately
      have "poincare_between (moebius_pt M x) (moebius_pt M v) (moebius_pt M y)"
        using * by blast
      thus "poincare_between x v y"
        using y  unit_disc * x  unit_disc by simp
    qed
  next
    fix x
    assume xx: "is_real x" "0 < Re x" "Re x < 1"
    hence "of_complex x  unit_disc"
      using cmod_eq_Re by auto
    hence "of_complex x  h"
      by simp
    have " of_complex x  0h"
      using xx by auto
    have "of_complex x  circline_set x_axis"
      using xx by simp
    show "m n. poincare_between 0h m (of_complex x)  poincare_between 0h (of_complex x) n 
            m  unit_disc  n  unit_disc  poincare_between m (of_complex x) n"
    proof safe
      fix m n
      assume **: "poincare_between 0h m (of_complex x)" "poincare_between 0h (of_complex x) n"
                 "m  unit_disc" " n  unit_disc"
      show "poincare_between m (of_complex x) n"
      proof(cases "m = 0h")
        case True
        thus ?thesis
          using ** by auto
      next
        case False
        hence "m  circline_set x_axis"
          using poincare_between_poincare_line_uzv[of "0h" "of_complex x" m]
          using poincare_line_0_real_is_x_axis[of "of_complex x"] 
          using of_complex x  unit_disc of_complex x  h of_complex x  0h
          using of_complex x  circline_set x_axis m  unit_disc **(1)
          by simp
        then obtain m' where "m = of_complex m'" "is_real m'"
          using inf_or_of_complex[of m] m  unit_disc
          unfolding circline_set_x_axis
          by auto
        hence "Re m'  Re x"
          using poincare_between 0h m (of_complex x) xx of_complex x  0h
          using False ** of_complex x  unit_disc
          using cmod_Re_le_iff poincare_between_0uv by auto
 
        have "n  0h"
          using **(2, 4) of_complex x  0h of_complex x  unit_disc
          using poincare_between_sandwich by fastforce
        have "n  circline_set x_axis"
          using poincare_between_poincare_line_uvz[of "0h" "of_complex x" n]
          using poincare_line_0_real_is_x_axis[of "of_complex x"] 
          using of_complex x  unit_disc of_complex x  h of_complex x  0h
          using of_complex x  circline_set x_axis n  unit_disc **(2)
          by simp
        then obtain n' where "n = of_complex n'" "is_real n'"
          using inf_or_of_complex[of n] n  unit_disc
          unfolding circline_set_x_axis
          by auto
        hence "Re x  Re n'"
          using poincare_between 0h (of_complex x) n xx of_complex x  0h
          using False ** of_complex x  unit_disc n  0h
          using cmod_Re_le_iff poincare_between_0uv
          by (metis Re_complex_of_real arg_0_iff rcis_cmod_Arg rcis_zero_arg to_complex_of_complex)
        
        have "poincare_between (of_complex m') (of_complex x) (of_complex n')" 
          using Re x  Re n' Re m'  Re x
          using poincare_between_x_axis_uvw[of "Re m'" "Re x" "Re n'"]
          using is_real n' is_real m' n  unit_disc n = of_complex n'
          using xx m = of_complex m' m  unit_disc
          by (smt complex_of_real_Re norm_of_real poincare_between_def unit_disc_iff_cmod_lt_1)

        thus ?thesis
          using n = of_complex n' m = of_complex m'
          by auto
      qed
    qed
  qed 
  thus ?thesis
    using assms
    by blast
qed

(* ------------------------------------------------------------------ *)
subsection‹Poincare between - sum distances›
(* ------------------------------------------------------------------ *)

text‹Another possible definition of the h-betweenness relation is given in terms of h-distances
between pairs of points. We prove it as a characterization equivalent to our cross-ratio based
definition.›

lemma poincare_between_sum_distances_x_axis_u0v:
  assumes "of_complex u'  unit_disc" "of_complex v'  unit_disc"
  assumes "is_real u'" "u'  0" "v'  0"
  shows  "poincare_distance (of_complex u') 0h + poincare_distance 0h (of_complex v') = poincare_distance (of_complex u') (of_complex v') 
          is_real v'  Re u' * Re v' < 0" (is "?P u' v'  ?Q u' v'")
proof-
  have "Re u'  0"
    using is_real u' u'  0
    using complex_eq_if_Re_eq
    by simp

  let ?u = "cmod u'" and ?v = "cmod v'" and ?uv = "cmod (u' - v')"
  have disc: "?u2 < 1" "?v2 < 1"
    using unit_disc_cmod_square_lt_1[OF assms(1)]
    using unit_disc_cmod_square_lt_1[OF assms(2)]
    by auto
  have "poincare_distance (of_complex u') 0h + poincare_distance 0h (of_complex v') =
              arcosh (((1 + ?u2) * (1 + ?v2) + 4 * ?u * ?v) / ((1 - ?u2) * (1 - ?v2)))" (is "_ = arcosh ?r1")
          using poincare_distance_formula_zero_sum[OF assms(1-2)]
          by (simp add: Let_def)
  moreover
  have "poincare_distance (of_complex u') (of_complex v') =
              arcosh (((1 - ?u2) * (1 - ?v2) + 2 * ?uv2) / ((1 - ?u2) * (1 - ?v2)))" (is "_ = arcosh ?r2")
    using disc
    using poincare_distance_formula[OF assms(1-2)]
    by (subst add_divide_distrib) simp
  moreover
  have "arcosh ?r1 = arcosh ?r2  ?Q u' v'"
  proof
    assume "arcosh ?r1 = arcosh ?r2"
    hence "?r1 = ?r2"
    proof (subst (asm) arcosh_eq_iff)
      show "?r1  1"
      proof-
        have "(1 - ?u2) * (1 - ?v2)  (1 + ?u2) * (1 + ?v2) + 4 * ?u * ?v"
          by (simp add: field_simps)
        thus ?thesis
          using disc
          by simp
      qed
    next
      show "?r2  1"
        using disc
        by simp
    qed
    hence "(1 + ?u2) * (1 + ?v2) + 4 * ?u * ?v = (1 - ?u2) * (1 - ?v2) + 2 * ?uv2"
      using disc
      by auto              
    hence "(cmod (u' - v'))2 = (cmod u' + cmod v')2"
      by (simp add: field_simps power2_eq_square)
    hence *: "Re u' * Re v' + ¦Re u'¦ * sqrt ((Im v')2 + (Re v')2) = 0"
      using is_real u'
      unfolding cmod_power2 cmod_def
      by (simp add: field_simps) (simp add: power2_eq_square field_simps)
    hence "sqrt ((Im v')2 + (Re v')2) = ¦Re v'¦"
      using Re u'  0 v'  0
      by (smt complex_neq_0 mult.commute mult_cancel_right mult_minus_left real_sqrt_gt_0_iff)
    hence "Im v' = 0"
      by (smt Im_eq_0 norm_complex_def)
    moreover
    hence "Re u' * Re v' = - ¦Re u'¦ * ¦Re v'¦"
      using *
      by simp
    hence "Re u' * Re v' < 0"
      using Re u'  0 v'  0
      by (simp add: is_real v' complex_eq_if_Re_eq)
    ultimately
    show "?Q u' v'"
      by simp
  next
    assume "?Q u' v'"
    hence "is_real v'" "Re u' * Re v' < 0"
      by auto
    have "?r1 = ?r2"
    proof (cases "Re u' > 0")
      case True
      hence "Re v' < 0"
        using Re u' * Re v' < 0
        by (smt zero_le_mult_iff)
      show ?thesis
        using disc is_real u' is_real v'
        using Re u' > 0 Re v' < 0
        unfolding cmod_power2 cmod_def
        by simp (simp add: power2_eq_square field_simps)
    next
      case False
      hence "Re u' < 0"
        using Re u'  0
        by simp
      hence "Re v' > 0"
        using Re u' * Re v' < 0
        by (smt zero_le_mult_iff)
      show ?thesis
        using disc is_real u' is_real v'
        using Re u' < 0 Re v' > 0
        unfolding cmod_power2 cmod_def
        by simp (simp add: power2_eq_square field_simps)
    qed
    thus "arcosh ?r1 = arcosh ?r2"
      by metis
  qed
  ultimately
  show ?thesis
    by simp
qed

text‹
  Different proof of the previous theorem relying on the cross-ratio definition, and not the distance formula.
  We suppose that this could be also used to prove the triangle inequality.
›
lemma poincare_between_sum_distances_x_axis_u0v_different_proof:
  assumes "of_complex u'  unit_disc" "of_complex v'  unit_disc"
  assumes "is_real u'" "u'  0" "v'  0" (* additional condition *) "is_real v'"
  shows  "poincare_distance (of_complex u') 0h + poincare_distance 0h (of_complex v') = poincare_distance (of_complex u') (of_complex v') 
          Re u' * Re v' < 0" (is "?P u' v'  ?Q u' v'")
proof-
  have "-1 < Re u'" "Re u' < 1" "Re u'  0"
    using assms
    by (auto simp add: cmod_eq_Re complex_eq_if_Re_eq)
  have "-1 < Re v'" "Re v' < 1" "Re v'  0"
    using assms
    by (auto simp add: cmod_eq_Re complex_eq_if_Re_eq)

  have "¦ln (Re ((1 - u') / (1 + u')))¦ + ¦ln (Re ((1 - v') / (1 + v')))¦ =
        ¦ln (Re ((1 + u') * (1 - v') / ((1 - u') * (1 + v'))))¦  Re u' * Re v' < 0" (is "¦ln ?a1¦  + ¦ln ?a2¦ = ¦ln ?a3¦  _")
  proof-
    have 1: "0 < ?a1" "ln ?a1 > 0  Re u' < 0"
      using Re u' < 1 Re u' > -1 is_real u'
      using complex_is_Real_iff
      by auto
    have 2: "0 < ?a2" "ln ?a2 > 0  Re v' < 0"
      using Re v' < 1 Re v' > -1 is_real v'
      using complex_is_Real_iff
      by auto
    have 3: "0 < ?a3" "ln ?a3 > 0  Re v' < Re u'"
      using Re u' < 1 Re u' > -1 is_real u'
      using Re v' < 1 Re v' > -1 is_real v'
      using complex_is_Real_iff
       by auto (simp add: field_simps)+
    show ?thesis
    proof
      assume *: "Re u' * Re v' < 0"
      show "¦ln ?a1¦ + ¦ln ?a2¦ = ¦ln ?a3¦"
      proof (cases "Re u' > 0")
        case True
        hence "Re v' < 0"
          using *
          by (smt mult_nonneg_nonneg)
        show ?thesis
          using 1 2 3 Re u' > 0 Re v' < 0
          using Re u' < 1 Re u' > -1 is_real u'
          using Re v' < 1 Re v' > -1 is_real v'
          using complex_is_Real_iff
          using ln_div ln_mult
          by simp
      next
        case False
        hence "Re v' > 0" "Re u' < 0"
          using *
          by (smt zero_le_mult_iff)+
        show ?thesis
          using 1 2 3 Re u' < 0 Re v' > 0
          using Re u' < 1 Re u' > -1 is_real u'
          using Re v' < 1 Re v' > -1 is_real v'
          using complex_is_Real_iff
          using ln_div ln_mult
          by simp
      qed
    next
      assume *: "¦ln ?a1¦ + ¦ln ?a2¦ = ¦ln ?a3¦"
      {
        assume "Re u' > 0" "Re v' > 0"
        hence False
          using * 1 2 3
          using Re u' < 1 Re u' > -1 is_real u'
          using Re v' < 1 Re v' > -1 is_real v'
          using complex_is_Real_iff
          using ln_mult ln_div
          by (cases "Re v' < Re u'") auto
      }
      moreover
      {
        assume "Re u' < 0" "Re v' < 0"
        hence False
          using * 1 2 3
          using Re u' < 1 Re u' > -1 is_real u'
          using Re v' < 1 Re v' > -1 is_real v'
          using complex_is_Real_iff
          using ln_mult ln_div
          by (cases "Re v' < Re u'") auto
      }
      ultimately
      show "Re u' * Re v' < 0"
        using Re u'  0 Re v'  0
        by (smt divisors_zero mult_le_0_iff)
    qed
  qed
  thus ?thesis
    using assms
    apply (subst poincare_distance_sym, simp, simp)
    apply (subst poincare_distance_zero_x_axis, simp, simp add: circline_set_x_axis)
    apply (subst poincare_distance_zero_x_axis, simp, simp add: circline_set_x_axis)
    apply (subst poincare_distance_x_axis_x_axis, simp, simp, simp add: circline_set_x_axis, simp add: circline_set_x_axis)
    apply simp
    done
qed

lemma poincare_between_sum_distances:
  assumes "u  unit_disc" and "v  unit_disc" and "w  unit_disc"
  shows "poincare_between u v w  
         poincare_distance u v + poincare_distance v w = poincare_distance u w" (is "?P' u v w")
proof (cases "u = v")
  case True
  thus ?thesis
    using assms
    by simp
next
  case False
  have " w. w  unit_disc  (poincare_between u v w  poincare_distance u v + poincare_distance v w = poincare_distance u w)" (is "?P u v")
  proof (rule wlog_positive_x_axis)
    fix x
    assume "is_real x" "0 < Re x" "Re x < 1"
    have "of_complex x  circline_set x_axis"
      using is_real x
      by (auto simp add: circline_set_x_axis)

    have "of_complex x  unit_disc"
      using is_real x 0 < Re x Re x < 1
      by (simp add: cmod_eq_Re)

    have "x  0"
      using is_real x Re x > 0
      by auto

    show "?P (of_complex x) 0h"
    proof (rule allI, rule impI)
      fix w
      assume "w  unit_disc"
      then obtain w' where "w = of_complex w'"
        using inf_or_of_complex[of w]
        by auto

      show "?P' (of_complex x) 0h w"
      proof (cases "w = 0h")
        case True
        thus ?thesis
          by simp
      next
        case False
        hence "w'  0"
          using w = of_complex w'
          by auto

        show ?thesis
          using is_real x x  0 w = of_complex w' w'  0
          using of_complex x  unit_disc w  unit_disc
          apply simp
          apply (subst poincare_between_x_axis_u0v, simp_all)
          apply (subst poincare_between_sum_distances_x_axis_u0v, simp_all)
          done
      qed
    qed
  next
    show "v  unit_disc" "u  unit_disc"
      using assms
      by auto
  next
    show "v  u"
      using u  v
      by simp
  next
    fix M u v
    assume *: "unit_disc_fix M" "u  unit_disc" "v  unit_disc" "u  v" and
          **: "?P (moebius_pt M v) (moebius_pt M u)"
    show "?P v u"
    proof (rule allI, rule impI)
      fix w
      assume "w  unit_disc"
      hence "moebius_pt M w  unit_disc"
        using unit_disc_fix M
        by auto
      thus "?P' v u w"
        using u  unit_disc v  unit_disc w  unit_disc unit_disc_fix M
        using **[rule_format, of "moebius_pt M w"]
        by auto
    qed
  qed
  thus ?thesis
    using assms
    by simp
qed

subsection ‹Some more properties of h-betweenness.›

text ‹Some lemmas proved earlier are proved almost directly using the sum of distances characterization.›

lemma unit_disc_fix_moebius_preserve_poincare_between':
  assumes "unit_disc_fix M" and "u  unit_disc" and "v  unit_disc" and "w  unit_disc"
  shows "poincare_between (moebius_pt M u) (moebius_pt M v) (moebius_pt M w) 
         poincare_between u v w"
  using assms
  using poincare_between_sum_distances
  by simp

lemma conjugate_preserve_poincare_between':
  assumes "u  unit_disc" "v  unit_disc" "w  unit_disc"
  shows "poincare_between (conjugate u) (conjugate v) (conjugate w)  poincare_between u v w"
  using assms
  using poincare_between_sum_distances
  by simp

text ‹There is a unique point on a ray on the given distance from the given starting point›
lemma unique_poincare_distance_on_ray:
  assumes "d  0" "u  v" "u  unit_disc" "v  unit_disc"
  assumes "y  unit_disc" "poincare_distance u y = d" "poincare_between u v y"
  assumes "z  unit_disc" "poincare_distance u z = d" "poincare_between u v z"
  shows "y = z"
proof-
  have " d y z. d  0 
        y  unit_disc  poincare_distance u y = d  poincare_between u v y 
        z  unit_disc  poincare_distance u z = d  poincare_between u v z  y = z" (is "?P u v")
  proof (rule wlog_positive_x_axis[where P="?P"])
    fix x
    assume x: "is_real x" "0 < Re x" "Re x < 1"
    hence "x  0"
      using complex.expand[of x 0]
      by auto
    hence *: "poincare_line 0h (of_complex x) = x_axis"
      using x poincare_line_0_real_is_x_axis[of "of_complex x"]
      unfolding circline_set_x_axis
      by auto
    have "of_complex x  unit_disc"
      using x
      by (auto simp add: cmod_eq_Re)
    have "Arg x = 0"
      using x
      using arg_0_iff by blast
    show "?P 0h (of_complex x)"
    proof safe
      fix y z
      assume "y  unit_disc" "z  unit_disc"
      then obtain y' z' where yz: "y = of_complex y'" "z = of_complex z'"
        using inf_or_of_complex[of y] inf_or_of_complex[of z]
        by auto
      assume betw: "poincare_between 0h (of_complex x) y"  "poincare_between 0h (of_complex x) z"
      hence "y  0h" "z  0h"
        using x  0 of_complex x  unit_disc y  unit_disc
        using poincare_between_sandwich[of "0h" "of_complex x"]
        using of_complex_zero_iff[of x]
        by force+

      hence "Arg y' = 0" "cmod y'  cmod x" "Arg z' = 0" "cmod z'  cmod x"
        using poincare_between_0uv[of "of_complex x" y] poincare_between_0uv[of "of_complex x" z]
        using of_complex x  unit_disc x  0 Arg x = 0 y  unit_disc z  unit_disc betw yz
        by (simp_all add: Let_def)
      hence *: "is_real y'" "is_real z'" "Re y' > 0" "Re z' > 0"
        using arg_0_iff[of y'] arg_0_iff[of z'] x y  0h z  0h yz
        by auto
      assume "poincare_distance 0h z = poincare_distance 0h y" "0  poincare_distance 0h y"
      thus "y = z"
        using * yz y  unit_disc z  unit_disc
        using unique_x_axis_poincare_distance_positive[of "poincare_distance 0h y"]
        by (auto simp add: cmod_eq_Re unit_disc_to_complex_inj)
    qed
  next
    show "u  unit_disc" "v  unit_disc" "u  v"
      by fact+
  next
    fix M u v
    assume *: "unit_disc_fix M" "u  unit_disc" "v  unit_disc" "u  v"
    assume **: "?P (moebius_pt M u) (moebius_pt M v)"
    show "?P u v"
    proof safe
      fix d y z
      assume ***: "0  poincare_distance u y"
             "y  unit_disc" "poincare_between u v y"
             "z  unit_disc" "poincare_between u v z"
             "poincare_distance u z = poincare_distance u y"
      let ?Mu = "moebius_pt M u" and ?Mv = "moebius_pt M v" and ?My = "moebius_pt M y" and ?Mz = "moebius_pt M z"
      have "?Mu  unit_disc" "?Mv  unit_disc" "?My  unit_disc" "?Mz  unit_disc"
        using u  unit_disc v  unit_disc y  unit_disc z  unit_disc
        using unit_disc_fix M
        by auto
      hence "?My = ?Mz"
        using * ***
        using **[rule_format, of "poincare_distance ?Mu ?My" ?My ?Mz]
        by simp
      thus "y = z"
        using bij_moebius_pt[of M]
        unfolding bij_def inj_on_def
        by blast
    qed
  qed
  thus ?thesis
    using assms
    by auto
qed

end