/-
Copyright (c) 2022 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers
-/
import analysis.inner_product_space.orientation
import analysis.inner_product_space.pi_L2
import analysis.special_functions.complex.circle

/-!
# Oriented angles.

This file defines oriented angles in real inner product spaces.

## Main definitions

* `orientation.oangle` is the oriented angle between two vectors with respect to an orientation.

* `orientation.rotation` is the rotation by an oriented angle with respect to an orientation.

## Implementation notes

The definitions here use the `real.angle` type, angles modulo `2 * π`. For some purposes,
angles modulo `π` are more convenient, because results are true for such angles with less
configuration dependence. Results that are only equalities modulo `π` can be represented
modulo `2 * π` as equalities of `(2 : ℤ) • θ`.

Definitions and results in the `orthonormal` namespace, with respect to a particular choice
of orthonormal basis, are mainly for use in setting up the API and proving that certain
definitions do not depend on the choice of basis for a given orientation. Applications should
generally use the definitions and results in the `orientation` namespace instead.

## References

* Evan Chen, Euclidean Geometry in Mathematical Olympiads.

-/

noncomputable theory

open_locale real

namespace orthonormal

variables {V : Type*} [inner_product_space ℝ V]
variables {b : basis (fin 2) ℝ V} (hb : orthonormal ℝ b)
include hb

/-- The oriented angle from `x` to `y`, modulo `2 * π`. If either vector is 0, this is 0. -/
def oangle (x y : V) : real.angle :=
complex.arg ((complex.isometry_of_orthonormal hb).symm y /
  (complex.isometry_of_orthonormal hb).symm x)

/-- If the first vector passed to `oangle` is 0, the result is 0. -/
@[simp] lemma oangle_zero_left (x : V) : hb.oangle 0 x = 0 :=
by simp [oangle]

/-- If the second vector passed to `oangle` is 0, the result is 0. -/
@[simp] lemma oangle_zero_right (x : V) : hb.oangle x 0 = 0 :=
by simp [oangle]

/-- If the two vectors passed to `oangle` are the same, the result is 0. -/
@[simp] lemma oangle_self (x : V) : hb.oangle x x = 0 :=
begin
  by_cases h : x = 0;
    simp [oangle, h]
end

/-- Swapping the two vectors passed to `oangle` negates the angle. -/
lemma oangle_rev (x y : V) : hb.oangle y x = -hb.oangle x y :=
begin
  simp only [oangle],
  convert complex.arg_inv_coe_angle _,
  exact inv_div.symm
end

/-- Adding the angles between two vectors in each order results in 0. -/
@[simp] lemma oangle_add_oangle_rev (x y : V) : hb.oangle x y + hb.oangle y x = 0 :=
by simp [hb.oangle_rev y x]

/-- Negating the first vector passed to `oangle` adds `π` to the angle. -/
lemma oangle_neg_left {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  hb.oangle (-x) y = hb.oangle x y + π :=
begin
  simp only [oangle, div_neg_eq_neg_div, map_neg],
  refine complex.arg_neg_coe_angle _,
  simp [hx, hy]
end

/-- Negating the second vector passed to `oangle` adds `π` to the angle. -/
lemma oangle_neg_right {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  hb.oangle x (-y) = hb.oangle x y + π :=
begin
  simp only [oangle, neg_div, map_neg],
  refine complex.arg_neg_coe_angle _,
  simp [hx, hy]
end

/-- Negating the first vector passed to `oangle` does not change twice the angle. -/
@[simp] lemma two_zsmul_oangle_neg_left (x y : V) :
  (2 : ℤ) • hb.oangle (-x) y = (2 : ℤ) • hb.oangle x y :=
begin
  by_cases hx : x = 0,
  { simp [hx] },
  { by_cases hy : y = 0,
    { simp [hy] },
    { simp [hb.oangle_neg_left hx hy] } }
end

/-- Negating the second vector passed to `oangle` does not change twice the angle. -/
@[simp] lemma two_zsmul_oangle_neg_right (x y : V) :
  (2 : ℤ) • hb.oangle x (-y) = (2 : ℤ) • hb.oangle x y :=
begin
  by_cases hx : x = 0,
  { simp [hx] },
  { by_cases hy : y = 0,
    { simp [hy] },
    { simp [hb.oangle_neg_right hx hy] } }
end

/-- Negating both vectors passed to `oangle` does not change the angle. -/
@[simp] lemma oangle_neg_neg (x y : V) : hb.oangle (-x) (-y) = hb.oangle x y :=
by simp [oangle, neg_div_neg_eq]

/-- Negating the first vector produces the same angle as negating the second vector. -/
lemma oangle_neg_left_eq_neg_right (x y : V) : hb.oangle (-x) y = hb.oangle x (-y) :=
by rw [←neg_neg y, oangle_neg_neg, neg_neg]

/-- The angle between the negation of a nonzero vector and that vector is `π`. -/
@[simp] lemma oangle_neg_self_left {x : V} (hx : x ≠ 0) : hb.oangle (-x) x = π :=
by simp [oangle_neg_left, hx]

/-- The angle between a nonzero vector and its negation is `π`. -/
@[simp] lemma oangle_neg_self_right {x : V} (hx : x ≠ 0) : hb.oangle x (-x) = π :=
by simp [oangle_neg_right, hx]

/-- Twice the angle between the negation of a vector and that vector is 0. -/
@[simp] lemma two_zsmul_oangle_neg_self_left (x : V) : (2 : ℤ) • hb.oangle (-x) x = 0 :=
begin
  by_cases hx : x = 0;
    simp [hx]
end

/-- Twice the angle between a vector and its negation is 0. -/
@[simp] lemma two_zsmul_oangle_neg_self_right (x : V) : (2 : ℤ) • hb.oangle x (-x) = 0 :=
begin
  by_cases hx : x = 0;
    simp [hx]
end

/-- Adding the angles between two vectors in each order, with the first vector in each angle
negated, results in 0. -/
@[simp] lemma oangle_add_oangle_rev_neg_left (x y : V) :
  hb.oangle (-x) y + hb.oangle (-y) x = 0 :=
by rw [oangle_neg_left_eq_neg_right, oangle_rev, add_left_neg]

/-- Adding the angles between two vectors in each order, with the second vector in each angle
negated, results in 0. -/
@[simp] lemma oangle_add_oangle_rev_neg_right (x y : V) :
  hb.oangle x (-y) + hb.oangle y (-x) = 0 :=
by rw [hb.oangle_rev (-x), oangle_neg_left_eq_neg_right, add_neg_self]

/-- Multiplying the first vector passed to `oangle` by a positive real does not change the
angle. -/
@[simp] lemma oangle_smul_left_of_pos (x y : V) {r : ℝ} (hr : 0 < r) :
  hb.oangle (r • x) y = hb.oangle x y :=
begin
  simp only [oangle, linear_isometry_equiv.map_smul, complex.real_smul],
  rw [mul_comm, div_mul_eq_div_mul_one_div, one_div, mul_comm, ←complex.of_real_inv],
  congr' 1,
  exact complex.arg_real_mul _ (inv_pos.2 hr)
end

/-- Multiplying the second vector passed to `oangle` by a positive real does not change the
angle. -/
@[simp] lemma oangle_smul_right_of_pos (x y : V) {r : ℝ} (hr : 0 < r) :
  hb.oangle x (r • y) = hb.oangle x y :=
begin
  simp only [oangle, linear_isometry_equiv.map_smul, complex.real_smul],
  congr' 1,
  rw mul_div_assoc,
  exact complex.arg_real_mul _ hr
end

/-- Multiplying the first vector passed to `oangle` by a negative real produces the same angle
as negating that vector. -/
@[simp] lemma oangle_smul_left_of_neg (x y : V) {r : ℝ} (hr : r < 0) :
  hb.oangle (r • x) y = hb.oangle (-x) y :=
by rw [←neg_neg r, neg_smul, ←smul_neg, hb.oangle_smul_left_of_pos _ _ (neg_pos_of_neg hr)]

/-- Multiplying the second vector passed to `oangle` by a negative real produces the same angle
as negating that vector. -/
@[simp] lemma oangle_smul_right_of_neg (x y : V) {r : ℝ} (hr : r < 0) :
  hb.oangle x (r • y) = hb.oangle x (-y) :=
by rw [←neg_neg r, neg_smul, ←smul_neg, hb.oangle_smul_right_of_pos _ _ (neg_pos_of_neg hr)]

/-- The angle between a nonnegative multiple of a vector and that vector is 0. -/
@[simp] lemma oangle_smul_left_self_of_nonneg (x : V) {r : ℝ} (hr : 0 ≤ r) :
  hb.oangle (r • x) x = 0 :=
begin
  rcases hr.lt_or_eq with (h|h),
  { simp [h] },
  { simp [h.symm] }
end

/-- The angle between a vector and a nonnegative multiple of that vector is 0. -/
@[simp] lemma oangle_smul_right_self_of_nonneg (x : V) {r : ℝ} (hr : 0 ≤ r) :
  hb.oangle x (r • x) = 0 :=
begin
  rcases hr.lt_or_eq with (h|h),
  { simp [h] },
  { simp [h.symm] }
end

/-- The angle between two nonnegative multiples of the same vector is 0. -/
@[simp] lemma oangle_smul_smul_self_of_nonneg (x : V) {r₁ r₂ : ℝ} (hr₁ : 0 ≤ r₁) (hr₂ : 0 ≤ r₂) :
  hb.oangle (r₁ • x) (r₂ • x) = 0 :=
begin
  rcases hr₁.lt_or_eq with (h|h),
  { simp [h, hr₂] },
  { simp [h.symm] }
end

/-- Multiplying the first vector passed to `oangle` by a nonzero real does not change twice the
angle. -/
@[simp] lemma two_zsmul_oangle_smul_left_of_ne_zero (x y : V) {r : ℝ} (hr : r ≠ 0) :
  (2 : ℤ) • hb.oangle (r • x) y = (2 : ℤ) • hb.oangle x y :=
begin
  rcases hr.lt_or_lt with (h|h);
    simp [h]
end

/-- Multiplying the second vector passed to `oangle` by a nonzero real does not change twice the
angle. -/
@[simp] lemma two_zsmul_oangle_smul_right_of_ne_zero (x y : V) {r : ℝ} (hr : r ≠ 0) :
  (2 : ℤ) • hb.oangle x (r • y) = (2 : ℤ) • hb.oangle x y :=
begin
  rcases hr.lt_or_lt with (h|h);
    simp [h]
end

/-- Twice the angle between a multiple of a vector and that vector is 0. -/
@[simp] lemma two_zsmul_oangle_smul_left_self (x : V) {r : ℝ} :
  (2 : ℤ) • hb.oangle (r • x) x = 0 :=
begin
  rcases lt_or_le r 0 with (h|h);
    simp [h]
end

/-- Twice the angle between a vector and a multiple of that vector is 0. -/
@[simp] lemma two_zsmul_oangle_smul_right_self (x : V) {r : ℝ} :
  (2 : ℤ) • hb.oangle x (r • x) = 0 :=
begin
  rcases lt_or_le r 0 with (h|h);
    simp [h]
end

/-- Twice the angle between two multiples of a vector is 0. -/
@[simp] lemma two_zsmul_oangle_smul_smul_self (x : V) {r₁ r₂ : ℝ} :
  (2 : ℤ) • hb.oangle (r₁ • x) (r₂ • x) = 0 :=
begin
  by_cases h : r₁ = 0;
    simp [h]
end

/-- Two vectors are equal if and only if they have equal norms and zero angle between them. -/
lemma eq_iff_norm_eq_and_oangle_eq_zero (x y : V) : x = y ↔ ∥x∥ = ∥y∥ ∧ hb.oangle x y = 0 :=
begin
  split,
  { intro h,
    simp [h] },
  { rintro ⟨hn, ha⟩,
    rw [oangle] at ha,
    by_cases hy0 : y = 0,
    { simpa [hy0] using hn },
    { have hx0 : x ≠ 0 := norm_ne_zero_iff.1 (hn.symm ▸ norm_ne_zero_iff.2 hy0),
      have hx0' : (complex.isometry_of_orthonormal hb).symm x ≠ 0,
      { simp [hx0] },
      have hy0' : (complex.isometry_of_orthonormal hb).symm y ≠ 0,
      { simp [hy0] },
      rw [complex.arg_div_coe_angle hy0' hx0', sub_eq_zero, complex.arg_coe_angle_eq_iff,
          complex.arg_eq_arg_iff hy0' hx0', ←complex.norm_eq_abs, ←complex.norm_eq_abs,
          linear_isometry_equiv.norm_map, linear_isometry_equiv.norm_map, hn,
          ←complex.of_real_div, div_self (norm_ne_zero_iff.2 hy0), complex.of_real_one,
          one_mul, linear_isometry_equiv.map_eq_iff] at ha,
      exact ha.symm } }
end

/-- Two vectors with equal norms are equal if and only if they have zero angle between them. -/
lemma eq_iff_oangle_eq_zero_of_norm_eq {x y : V} (h : ∥x∥ = ∥y∥) : x = y ↔ hb.oangle x y = 0 :=
⟨λ he, ((hb.eq_iff_norm_eq_and_oangle_eq_zero x y).1 he).2,
 λ ha, (hb.eq_iff_norm_eq_and_oangle_eq_zero x y).2 ⟨h, ha⟩⟩

/-- Two vectors with zero angle between them are equal if and only if they have equal norms. -/
lemma eq_iff_norm_eq_of_oangle_eq_zero {x y : V} (h : hb.oangle x y = 0) : x = y ↔ ∥x∥ = ∥y∥ :=
⟨λ he, ((hb.eq_iff_norm_eq_and_oangle_eq_zero x y).1 he).1,
 λ hn, (hb.eq_iff_norm_eq_and_oangle_eq_zero x y).2 ⟨hn, h⟩⟩

/-- Given three nonzero vectors, the angle between the first and the second plus the angle
between the second and the third equals the angle between the first and the third. -/
@[simp] lemma oangle_add {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  hb.oangle x y + hb.oangle y z = hb.oangle x z :=
begin
  simp_rw [oangle],
  rw ←complex.arg_mul_coe_angle,
  { rw [mul_comm, div_mul_div_cancel],
    simp [hy] },
  { simp [hx, hy] },
  { simp [hy, hz] }
end

/-- Given three nonzero vectors, the angle between the second and the third plus the angle
between the first and the second equals the angle between the first and the third. -/
@[simp] lemma oangle_add_swap {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
   hb.oangle y z + hb.oangle x y = hb.oangle x z :=
by rw [add_comm, hb.oangle_add hx hy hz]

/-- Given three nonzero vectors, the angle between the first and the third minus the angle
between the first and the second equals the angle between the second and the third. -/
@[simp] lemma oangle_sub_left {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  hb.oangle x z - hb.oangle x y = hb.oangle y z :=
by rw [sub_eq_iff_eq_add, hb.oangle_add_swap hx hy hz]

/-- Given three nonzero vectors, the angle between the first and the third minus the angle
between the second and the third equals the angle between the first and the second. -/
@[simp] lemma oangle_sub_right {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  hb.oangle x z - hb.oangle y z = hb.oangle x y :=
by rw [sub_eq_iff_eq_add, hb.oangle_add hx hy hz]

/-- Given three nonzero vectors, adding the angles between them in cyclic order results in 0. -/
@[simp] lemma oangle_add_cyc3 {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  hb.oangle x y + hb.oangle y z + hb.oangle z x = 0 :=
by simp [hx, hy, hz]

/-- Given three nonzero vectors, adding the angles between them in cyclic order, with the first
vector in each angle negated, results in π. If the vectors add to 0, this is a version of the
sum of the angles of a triangle. -/
@[simp] lemma oangle_add_cyc3_neg_left {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  hb.oangle (-x) y + hb.oangle (-y) z + hb.oangle (-z) x = π :=
by rw [hb.oangle_neg_left hx hy, hb.oangle_neg_left hy hz, hb.oangle_neg_left hz hx,
       (show hb.oangle x y + π + (hb.oangle y z + π) + (hb.oangle z x + π) =
         hb.oangle x y + hb.oangle y z + hb.oangle z x + (π + π + π : real.angle), by abel),
       hb.oangle_add_cyc3 hx hy hz, real.angle.coe_pi_add_coe_pi, zero_add, zero_add]

/-- Given three nonzero vectors, adding the angles between them in cyclic order, with the second
vector in each angle negated, results in π. If the vectors add to 0, this is a version of the
sum of the angles of a triangle. -/
@[simp] lemma oangle_add_cyc3_neg_right {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  hb.oangle x (-y) + hb.oangle y (-z) + hb.oangle z (-x) = π :=
by simp_rw [←oangle_neg_left_eq_neg_right, hb.oangle_add_cyc3_neg_left hx hy hz]

/-- Pons asinorum, oriented vector angle form. -/
lemma oangle_sub_eq_oangle_sub_rev_of_norm_eq {x y : V} (h : ∥x∥ = ∥y∥) :
  hb.oangle x (x - y) = hb.oangle (y - x) y :=
begin
  by_cases hx : x = 0,
  { simp [hx] },
  { have hy : y ≠ 0 := norm_ne_zero_iff.1 (h ▸ norm_ne_zero_iff.2 hx),
    simp_rw [hb.oangle_rev y, oangle, linear_isometry_equiv.map_sub,
             ←complex.arg_conj_coe_angle, sub_div,
             div_self (((complex.isometry_of_orthonormal hb).symm.map_eq_zero_iff).not.2 hx),
             div_self (((complex.isometry_of_orthonormal hb).symm.map_eq_zero_iff).not.2 hy),
             map_sub, map_one],
    rw ←inv_div,
    simp_rw [complex.inv_def, complex.norm_sq_div, ←complex.sq_abs, ←complex.norm_eq_abs,
             linear_isometry_equiv.norm_map, h],
    simp [hy] }
end

/-- The angle at the apex of an isosceles triangle is `π` minus twice a base angle, oriented
vector angle form. -/
lemma oangle_eq_pi_sub_two_zsmul_oangle_sub_of_norm_eq {x y : V} (hn : x ≠ y) (h : ∥x∥ = ∥y∥) :
  hb.oangle y x = π - (2 : ℤ) • hb.oangle (y - x) y :=
begin
  rw two_zsmul,
  rw [←hb.oangle_sub_eq_oangle_sub_rev_of_norm_eq h] { occs := occurrences.pos [1] },
  rw [eq_sub_iff_add_eq, ←oangle_neg_neg, ←add_assoc],
  have hy : y ≠ 0,
  { rintro rfl,
    rw [norm_zero, norm_eq_zero] at h,
    exact hn h },
  have hx : x ≠ 0 := norm_ne_zero_iff.1 (h.symm ▸ norm_ne_zero_iff.2 hy),
  convert hb.oangle_add_cyc3_neg_right (neg_ne_zero.2 hy) hx (sub_ne_zero_of_ne hn.symm);
    simp
end

/-- Angle at center of a circle equals twice angle at circumference, oriented vector angle
form. -/
lemma oangle_eq_two_zsmul_oangle_sub_of_norm_eq {x y z : V} (hxyne : x ≠ y) (hxzne : x ≠ z)
  (hxy : ∥x∥ = ∥y∥) (hxz : ∥x∥ = ∥z∥) : hb.oangle y z = (2 : ℤ) • hb.oangle (y - x) (z - x) :=
begin
  have hy : y ≠ 0,
  { rintro rfl,
    rw [norm_zero, norm_eq_zero] at hxy,
    exact hxyne hxy },
  have hx : x ≠ 0 := norm_ne_zero_iff.1 (hxy.symm ▸ norm_ne_zero_iff.2 hy),
  have hz : z ≠ 0 := norm_ne_zero_iff.1 (hxz ▸ norm_ne_zero_iff.2 hx),
  calc hb.oangle y z = hb.oangle x z - hb.oangle x y : (hb.oangle_sub_left hx hy hz).symm
       ...           = (π - (2 : ℤ) • hb.oangle (x - z) x) -
                       (π - (2 : ℤ) • hb.oangle (x - y) x) :
         by rw [hb.oangle_eq_pi_sub_two_zsmul_oangle_sub_of_norm_eq hxzne.symm hxz.symm,
                hb.oangle_eq_pi_sub_two_zsmul_oangle_sub_of_norm_eq hxyne.symm hxy.symm]
       ...           = (2 : ℤ) • (hb.oangle (x - y) x - hb.oangle (x - z) x) : by abel
       ...           = (2 : ℤ) • hb.oangle (x - y) (x - z) :
         by rw hb.oangle_sub_right (sub_ne_zero_of_ne hxyne) (sub_ne_zero_of_ne hxzne) hx
       ...           = (2 : ℤ) • hb.oangle (y - x) (z - x) :
         by rw [←oangle_neg_neg, neg_sub, neg_sub]
end

/-- Angle at center of a circle equals twice angle at circumference, oriented vector angle
form with radius specified. -/
lemma oangle_eq_two_zsmul_oangle_sub_of_norm_eq_real {x y z : V} (hxyne : x ≠ y) (hxzne : x ≠ z)
  {r : ℝ} (hx : ∥x∥ = r) (hy : ∥y∥ = r) (hz : ∥z∥ = r) :
  hb.oangle y z = (2 : ℤ) • hb.oangle (y - x) (z - x) :=
hb.oangle_eq_two_zsmul_oangle_sub_of_norm_eq hxyne hxzne (hy.symm ▸ hx) (hz.symm ▸ hx)

/-- Oriented vector angle version of "angles in same segment are equal" and "opposite angles of
a cyclic quadrilateral add to π", for oriented angles mod π (for which those are the same
result), represented here as equality of twice the angles. -/
lemma two_zsmul_oangle_sub_eq_two_zsmul_oangle_sub_of_norm_eq {x₁ x₂ y z : V} (hx₁yne : x₁ ≠ y)
  (hx₁zne : x₁ ≠ z) (hx₂yne : x₂ ≠ y) (hx₂zne : x₂ ≠ z) {r : ℝ} (hx₁ : ∥x₁∥ = r) (hx₂ : ∥x₂∥ = r)
  (hy : ∥y∥ = r) (hz : ∥z∥ = r) :
  (2 : ℤ) • hb.oangle (y - x₁) (z - x₁) = (2 : ℤ) • hb.oangle (y - x₂) (z - x₂) :=
hb.oangle_eq_two_zsmul_oangle_sub_of_norm_eq_real hx₁yne hx₁zne hx₁ hy hz ▸
  hb.oangle_eq_two_zsmul_oangle_sub_of_norm_eq_real hx₂yne hx₂zne hx₂ hy hz

/-- A rotation by the oriented angle `θ`. -/
def rotation (θ : real.angle) : V ≃ₗᵢ[ℝ] V :=
((complex.isometry_of_orthonormal hb).symm.trans (rotation (real.angle.exp_map_circle θ))).trans
  (complex.isometry_of_orthonormal hb)

/-- The determinant of `rotation` (as a linear map) is equal to `1`. -/
@[simp] lemma det_rotation (θ : real.angle) :
  ((hb.rotation θ).to_linear_equiv : V →ₗ[ℝ] V).det = 1 :=
by simp [rotation, ←linear_isometry_equiv.to_linear_equiv_symm, ←linear_equiv.comp_coe]

/-- The determinant of `rotation` (as a linear equiv) is equal to `1`. -/
@[simp] lemma linear_equiv_det_rotation (θ : real.angle) :
  (hb.rotation θ).to_linear_equiv.det = 1 :=
by simp [rotation, ←linear_isometry_equiv.to_linear_equiv_symm]

/-- The inverse of `rotation` is rotation by the negation of the angle. -/
@[simp] lemma rotation_symm (θ : real.angle) : (hb.rotation θ).symm = hb.rotation (-θ) :=
by simp [rotation, linear_isometry_equiv.trans_assoc]

/-- Rotation by 0 is the identity. -/
@[simp] lemma rotation_zero : hb.rotation 0 = linear_isometry_equiv.refl ℝ V :=
by simp [rotation]

/-- Rotation by π is negation. -/
lemma rotation_pi : hb.rotation π = linear_isometry_equiv.neg ℝ :=
begin
  ext x,
  simp [rotation]
end

/-- Rotating twice is equivalent to rotating by the sum of the angles. -/
@[simp] lemma rotation_trans (θ₁ θ₂ : real.angle) :
  (hb.rotation θ₁).trans (hb.rotation θ₂) = hb.rotation (θ₂ + θ₁) :=
begin
  simp only [rotation, ←linear_isometry_equiv.trans_assoc],
  ext1 x,
  simp
end

/-- Rotating the first vector by `θ` subtracts `θ` from the angle between two vectors. -/
@[simp] lemma oangle_rotation_left {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) (θ : real.angle) :
  hb.oangle (hb.rotation θ x) y = hb.oangle x y - θ :=
begin
  simp [oangle, rotation, complex.arg_div_coe_angle, complex.arg_mul_coe_angle, hx, hy,
        ne_zero_of_mem_circle],
  abel
end

/-- Rotating the second vector by `θ` adds `θ` to the angle between two vectors. -/
@[simp] lemma oangle_rotation_right {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) (θ : real.angle) :
  hb.oangle x (hb.rotation θ y) = hb.oangle x y + θ :=
begin
  simp [oangle, rotation, complex.arg_div_coe_angle, complex.arg_mul_coe_angle, hx, hy,
        ne_zero_of_mem_circle],
  abel
end

/-- The rotation of a vector by `θ` has an angle of `-θ` from that vector. -/
@[simp] lemma oangle_rotation_self_left {x : V} (hx : x ≠ 0) (θ : real.angle) :
  hb.oangle (hb.rotation θ x) x = -θ :=
by simp [hx]

/-- A vector has an angle of `θ` from the rotation of that vector by `θ`. -/
@[simp] lemma oangle_rotation_self_right {x : V} (hx : x ≠ 0) (θ : real.angle) :
  hb.oangle x (hb.rotation θ x) = θ :=
by simp [hx]

/-- Rotating the first vector by the angle between the two vectors results an an angle of 0. -/
@[simp] lemma oangle_rotation_oangle_left (x y : V) :
  hb.oangle (hb.rotation (hb.oangle x y) x) y = 0 :=
begin
  by_cases hx : x = 0,
  { simp [hx] },
  { by_cases hy : y = 0,
    { simp [hy] },
    { simp [hx, hy] } }
end

/-- Rotating the first vector by the angle between the two vectors and swapping the vectors
results an an angle of 0. -/
@[simp] lemma oangle_rotation_oangle_right (x y : V) :
  hb.oangle y (hb.rotation (hb.oangle x y) x) = 0 :=
begin
  rw [oangle_rev],
  simp
end

/-- Rotating both vectors by the same angle does not change the angle between those vectors. -/
@[simp] lemma oangle_rotation (x y : V) (θ : real.angle) :
  hb.oangle (hb.rotation θ x) (hb.rotation θ y) = hb.oangle x y :=
begin
  by_cases hx : x = 0; by_cases hy : y = 0;
    simp [hx, hy]
end

/-- A rotation of a nonzero vector equals that vector if and only if the angle is zero. -/
@[simp] lemma rotation_eq_self_iff_angle_eq_zero {x : V} (hx : x ≠ 0) (θ : real.angle) :
  hb.rotation θ x = x ↔ θ = 0 :=
begin
  split,
  { intro h,
    rw eq_comm,
    simpa [hx, h] using hb.oangle_rotation_right hx hx θ },
  { intro h,
    simp [h] }
end

/-- A nonzero vector equals a rotation of that vector if and only if the angle is zero. -/
@[simp] lemma eq_rotation_self_iff_angle_eq_zero {x : V} (hx : x ≠ 0) (θ : real.angle) :
  x = hb.rotation θ x ↔ θ = 0 :=
by rw [←hb.rotation_eq_self_iff_angle_eq_zero hx, eq_comm]

/-- A rotation of a vector equals that vector if and only if the vector or the angle is zero. -/
lemma rotation_eq_self_iff (x : V) (θ : real.angle) :
  hb.rotation θ x = x ↔ x = 0 ∨ θ = 0 :=
begin
  by_cases h : x = 0;
    simp [h]
end

/-- A vector equals a rotation of that vector if and only if the vector or the angle is zero. -/
lemma eq_rotation_self_iff (x : V) (θ : real.angle) :
  x = hb.rotation θ x ↔ x = 0 ∨ θ = 0 :=
by rw [←rotation_eq_self_iff, eq_comm]

/-- Rotating a vector by the angle to another vector gives the second vector if and only if the
norms are equal. -/
@[simp] lemma rotation_oangle_eq_iff_norm_eq (x y : V) :
  hb.rotation (hb.oangle x y) x = y ↔ ∥x∥ = ∥y∥ :=
begin
  split,
  { intro h,
    rw [←h, linear_isometry_equiv.norm_map] },
  { intro h,
    rw hb.eq_iff_oangle_eq_zero_of_norm_eq;
      simp [h] }
end

/-- The angle between two nonzero vectors is `θ` if and only if the second vector is the first
rotated by `θ` and scaled by the ratio of the norms. -/
lemma oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero {x y : V} (hx : x ≠ 0) (hy : y ≠ 0)
  (θ : real.angle) : hb.oangle x y = θ ↔ y = (∥y∥ / ∥x∥) • hb.rotation θ x :=
begin
  have hp := div_pos (norm_pos_iff.2 hy) (norm_pos_iff.2 hx),
  split,
  { rintro rfl,
    rw [←linear_isometry_equiv.map_smul, ←hb.oangle_smul_left_of_pos x y hp,
        eq_comm, rotation_oangle_eq_iff_norm_eq, norm_smul, real.norm_of_nonneg hp.le,
        div_mul_cancel _ (norm_ne_zero_iff.2 hx)] },
  { intro hye,
    rw [hye, hb.oangle_smul_right_of_pos _ _ hp, hb.oangle_rotation_self_right hx] }
end

/-- The angle between two nonzero vectors is `θ` if and only if the second vector is the first
rotated by `θ` and scaled by a positive real. -/
lemma oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero {x y : V} (hx : x ≠ 0) (hy : y ≠ 0)
  (θ : real.angle) : hb.oangle x y = θ ↔ ∃ r : ℝ, 0 < r ∧ y = r • hb.rotation θ x :=
begin
  split,
  { intro h,
    rw hb.oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero hx hy at h,
    exact ⟨∥y∥ / ∥x∥, div_pos (norm_pos_iff.2 hy) (norm_pos_iff.2 hx), h⟩ },
  { rintro ⟨r, hr, rfl⟩,
    rw [hb.oangle_smul_right_of_pos _ _ hr, hb.oangle_rotation_self_right hx] }
end

/-- The angle between two vectors is `θ` if and only if they are nonzero and the second vector
is the first rotated by `θ` and scaled by the ratio of the norms, or `θ` and at least one of the
vectors are zero. -/
lemma oangle_eq_iff_eq_norm_div_norm_smul_rotation_or_eq_zero {x y : V} (θ : real.angle) :
  hb.oangle x y = θ ↔
    (x ≠ 0 ∧ y ≠ 0 ∧ y = (∥y∥ / ∥x∥) • hb.rotation θ x) ∨ (θ = 0 ∧ (x = 0 ∨ y = 0)) :=
begin
  by_cases hx : x = 0,
  { simp [hx, eq_comm] },
  { by_cases hy : y = 0,
    { simp [hy, eq_comm] },
    { rw hb.oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero hx hy,
      simp [hx, hy] } }
end

/-- The angle between two vectors is `θ` if and only if they are nonzero and the second vector
is the first rotated by `θ` and scaled by a positive real, or `θ` and at least one of the
vectors are zero. -/
lemma oangle_eq_iff_eq_pos_smul_rotation_or_eq_zero {x y : V} (θ : real.angle) :
  hb.oangle x y = θ ↔
    (x ≠ 0 ∧ y ≠ 0 ∧ ∃ r : ℝ, 0 < r ∧ y = r • hb.rotation θ x) ∨ (θ = 0 ∧ (x = 0 ∨ y = 0)) :=
begin
  by_cases hx : x = 0,
  { simp [hx, eq_comm] },
  { by_cases hy : y = 0,
    { simp [hy, eq_comm] },
    { rw hb.oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero hx hy,
      simp [hx, hy] } }
end

/-- Complex conjugation as a linear isometric equivalence in `V`. Note that this definition
depends on the choice of basis, not just on its orientation; for most geometrical purposes,
the `reflection` definitions should be preferred instead. -/
def conj_lie : V ≃ₗᵢ[ℝ] V :=
((complex.isometry_of_orthonormal hb).symm.trans complex.conj_lie).trans
  (complex.isometry_of_orthonormal hb)

/-- The determinant of `conj_lie` (as a linear map) is equal to `-1`. -/
@[simp] lemma det_conj_lie : (hb.conj_lie.to_linear_equiv : V →ₗ[ℝ] V).det = -1 :=
by simp [conj_lie, ←linear_isometry_equiv.to_linear_equiv_symm, ←linear_equiv.comp_coe]

/-- The determinant of `conj_lie` (as a linear equiv) is equal to `-1`. -/
@[simp] lemma linear_equiv_det_conj_lie : hb.conj_lie.to_linear_equiv.det = -1 :=
by simp [conj_lie, ←linear_isometry_equiv.to_linear_equiv_symm]

/-- `conj_lie` is its own inverse. -/
@[simp] lemma conj_lie_symm : hb.conj_lie.symm = hb.conj_lie :=
rfl

/-- Applying `conj_lie` to both vectors negates the angle between those vectors. -/
@[simp] lemma oangle_conj_lie (x y : V) :
  hb.oangle (hb.conj_lie x) (hb.conj_lie y) = -hb.oangle x y :=
by simp only [orthonormal.conj_lie, linear_isometry_equiv.symm_apply_apply, orthonormal.oangle,
  eq_self_iff_true, function.comp_app, complex.arg_coe_angle_eq_iff,
  linear_isometry_equiv.coe_trans, neg_inj, complex.conj_lie_apply, complex.arg_conj_coe_angle,
  ←(star_ring_end ℂ).map_div]

/-- Any linear isometric equivalence in `V` is `rotation` or `conj_lie` composed with
`rotation`. -/
lemma exists_linear_isometry_equiv_eq (f : V ≃ₗᵢ[ℝ] V) :
  ∃ θ : real.angle, f = hb.rotation θ ∨ f = hb.conj_lie.trans (hb.rotation θ) :=
begin
  cases linear_isometry_complex (((complex.isometry_of_orthonormal hb).trans f).trans
    (complex.isometry_of_orthonormal hb).symm) with a ha,
  use complex.arg a,
  rcases ha with (ha|ha),
  { left,
    simp only [rotation, ←ha, linear_isometry_equiv.trans_assoc, linear_isometry_equiv.refl_trans,
               linear_isometry_equiv.symm_trans_self, real.angle.exp_map_circle_coe,
               exp_map_circle_arg],
    simp [←linear_isometry_equiv.trans_assoc] },
  { right,
    simp only [rotation, conj_lie, linear_isometry_equiv.trans_assoc,
               real.angle.exp_map_circle_coe, exp_map_circle_arg],
    simp only [←linear_isometry_equiv.trans_assoc, linear_isometry_equiv.self_trans_symm,
               linear_isometry_equiv.trans_refl],
    simp_rw [linear_isometry_equiv.trans_assoc complex.conj_lie, ←ha],
    simp only [linear_isometry_equiv.trans_assoc, linear_isometry_equiv.refl_trans,
               linear_isometry_equiv.symm_trans_self],
    simp [←linear_isometry_equiv.trans_assoc] }
end

/-- Any linear isometric equivalence in `V` with positive determinant is `rotation`. -/
lemma exists_linear_isometry_equiv_eq_of_det_pos {f : V ≃ₗᵢ[ℝ] V}
  (hd : 0 < (f.to_linear_equiv : V →ₗ[ℝ] V).det) : ∃ θ : real.angle, f = hb.rotation θ :=
begin
  rcases hb.exists_linear_isometry_equiv_eq f with ⟨θ, (hf|hf)⟩,
  { exact ⟨θ, hf⟩ },
  { simp [hf, ←linear_equiv.coe_det] at hd,
    norm_num at hd }
end

/-- Any linear isometric equivalence in `V` with negative determinant is `conj_lie` composed
with `rotation`. -/
lemma exists_linear_isometry_equiv_eq_of_det_neg {f : V ≃ₗᵢ[ℝ] V}
  (hd : (f.to_linear_equiv : V →ₗ[ℝ] V).det < 0) :
  ∃ θ : real.angle, f = hb.conj_lie.trans (hb.rotation θ) :=
begin
  rcases hb.exists_linear_isometry_equiv_eq f with ⟨θ, (hf|hf)⟩,
  { simp [hf, ←linear_equiv.coe_det] at hd,
    norm_num at hd },
  { exact ⟨θ, hf⟩ }
end

/-- Two bases with the same orientation are related by a `rotation`. -/
lemma exists_linear_isometry_equiv_map_eq_of_orientation_eq {b₂ : basis (fin 2) ℝ V}
  (hb₂ : orthonormal ℝ b₂) (ho : b.orientation = b₂.orientation) :
  ∃ θ : real.angle, b₂ = b.map (hb.rotation θ).to_linear_equiv :=
begin
  have h : b₂ = b.map (hb.equiv hb₂ (equiv.refl _)).to_linear_equiv,
  { rw hb.map_equiv, simp },
  rw [eq_comm, h, b.orientation_comp_linear_equiv_eq_iff_det_pos] at ho,
  cases hb.exists_linear_isometry_equiv_eq_of_det_pos ho with θ hθ,
  rw hθ at h,
  exact ⟨θ, h⟩
end

/-- Two bases with opposite orientations are related by `conj_lie` composed with a `rotation`. -/
lemma exists_linear_isometry_equiv_map_eq_of_orientation_eq_neg {b₂ : basis (fin 2) ℝ V}
  (hb₂ : orthonormal ℝ b₂) (ho : b.orientation = -b₂.orientation) :
  ∃ θ : real.angle, b₂ = b.map (hb.conj_lie.trans (hb.rotation θ)).to_linear_equiv :=
begin
  have h : b₂ = b.map (hb.equiv hb₂ (equiv.refl _)).to_linear_equiv,
  { rw hb.map_equiv, simp },
  rw [eq_neg_iff_eq_neg, h, b.orientation_comp_linear_equiv_eq_neg_iff_det_neg] at ho,
  cases hb.exists_linear_isometry_equiv_eq_of_det_neg ho with θ hθ,
  rw hθ at h,
  exact ⟨θ, h⟩
end

/-- The angle between two vectors, with respect to a basis given by `basis.map` with a linear
isometric equivalence, equals the angle between those two vectors, transformed by the inverse of
that equivalence, with respect to the original basis. -/
@[simp] lemma oangle_map (x y : V) (f : V ≃ₗᵢ[ℝ] V) :
  (hb.map_linear_isometry_equiv f).oangle x y = hb.oangle (f.symm x) (f.symm y) :=
by simp [oangle]

/-- The value of `oangle` does not depend on the choice of basis for a given orientation. -/
lemma oangle_eq_of_orientation_eq {b₂ : basis (fin 2) ℝ V} (hb₂ : orthonormal ℝ b₂)
  (ho : b.orientation = b₂.orientation) (x y : V) : hb.oangle x y = hb₂.oangle x y :=
begin
  obtain ⟨θ, rfl⟩ := hb.exists_linear_isometry_equiv_map_eq_of_orientation_eq hb₂ ho,
  simp [hb]
end

/-- Negating the orientation negates the value of `oangle`. -/
lemma oangle_eq_neg_of_orientation_eq_neg {b₂ : basis (fin 2) ℝ V} (hb₂ : orthonormal ℝ b₂)
  (ho : b.orientation = -b₂.orientation) (x y : V) : hb.oangle x y = -hb₂.oangle x y :=
begin
  obtain ⟨θ, rfl⟩ := hb.exists_linear_isometry_equiv_map_eq_of_orientation_eq_neg hb₂ ho,
  rw hb.oangle_map,
  simp [hb]
end

/-- `rotation` does not depend on the choice of basis for a given orientation. -/
lemma rotation_eq_of_orientation_eq {b₂ : basis (fin 2) ℝ V} (hb₂ : orthonormal ℝ b₂)
  (ho : b.orientation = b₂.orientation) (θ : real.angle) : hb.rotation θ = hb₂.rotation θ :=
begin
  obtain ⟨θ₂, rfl⟩ := hb.exists_linear_isometry_equiv_map_eq_of_orientation_eq hb₂ ho,
  simp_rw [rotation, complex.map_isometry_of_orthonormal hb],
  simp only [linear_isometry_equiv.trans_assoc, linear_isometry_equiv.self_trans_symm,
             linear_isometry_equiv.refl_trans, linear_isometry_equiv.symm_trans],
  simp only [←linear_isometry_equiv.trans_assoc, _root_.rotation_symm, _root_.rotation_trans,
             mul_comm (real.angle.exp_map_circle θ), ←mul_assoc, mul_right_inv, one_mul]
end

/-- Negating the orientation negates the angle in `rotation`. -/
lemma rotation_eq_rotation_neg_of_orientation_eq_neg {b₂ : basis (fin 2) ℝ V}
  (hb₂ : orthonormal ℝ b₂) (ho : b.orientation = -b₂.orientation) (θ : real.angle) :
  hb.rotation θ = hb₂.rotation (-θ) :=
begin
  obtain ⟨θ₂, rfl⟩ := hb.exists_linear_isometry_equiv_map_eq_of_orientation_eq_neg hb₂ ho,
  simp_rw [rotation, complex.map_isometry_of_orthonormal hb, conj_lie],
  simp only [linear_isometry_equiv.trans_assoc, linear_isometry_equiv.self_trans_symm,
             linear_isometry_equiv.refl_trans, linear_isometry_equiv.symm_trans],
  congr' 1,
  simp only [←linear_isometry_equiv.trans_assoc, _root_.rotation_symm,
             linear_isometry_equiv.symm_symm, linear_isometry_equiv.self_trans_symm,
             linear_isometry_equiv.trans_refl, complex.conj_lie_symm],
  congr' 1,
  ext1 x,
  simp only [linear_isometry_equiv.coe_trans, function.comp_app, rotation_apply,
             complex.conj_lie_apply, map_mul, star_ring_end_self_apply, ←coe_inv_circle_eq_conj,
             inv_inv, real.angle.exp_map_circle_neg, ←mul_assoc],
  congr' 1,
  simp only [mul_comm (real.angle.exp_map_circle θ₂ : ℂ), mul_assoc],
  rw [←submonoid.coe_mul, mul_left_inv, submonoid.coe_one, mul_one]
end

end orthonormal

namespace orientation

open finite_dimensional

variables {V : Type*} [inner_product_space ℝ V]
variables [hd2 : fact (finrank ℝ V = 2)] (o : orientation ℝ V (fin 2))
include hd2 o

local notation `ob` := o.fin_orthonormal_basis_orthonormal dec_trivial hd2.out

/-- The oriented angle from `x` to `y`, modulo `2 * π`. If either vector is 0, this is 0.
See `inner_product_geometry.angle` for the corresponding unoriented angle definition. -/
def oangle (x y : V) : real.angle :=
(ob).oangle x y

/-- If the first vector passed to `oangle` is 0, the result is 0. -/
@[simp] lemma oangle_zero_left (x : V) : o.oangle 0 x = 0 :=
(ob).oangle_zero_left x

/-- If the second vector passed to `oangle` is 0, the result is 0. -/
@[simp] lemma oangle_zero_right (x : V) : o.oangle x 0 = 0 :=
(ob).oangle_zero_right x

/-- If the two vectors passed to `oangle` are the same, the result is 0. -/
@[simp] lemma oangle_self (x : V) : o.oangle x x = 0 :=
(ob).oangle_self x

/-- Swapping the two vectors passed to `oangle` negates the angle. -/
lemma oangle_rev (x y : V) : o.oangle y x = -o.oangle x y :=
(ob).oangle_rev x y

/-- Adding the angles between two vectors in each order results in 0. -/
@[simp] lemma oangle_add_oangle_rev (x y : V) : o.oangle x y + o.oangle y x = 0 :=
(ob).oangle_add_oangle_rev x y

/-- Negating the first vector passed to `oangle` adds `π` to the angle. -/
lemma oangle_neg_left {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  o.oangle (-x) y = o.oangle x y + π :=
(ob).oangle_neg_left hx hy

/-- Negating the second vector passed to `oangle` adds `π` to the angle. -/
lemma oangle_neg_right {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  o.oangle x (-y) = o.oangle x y + π :=
(ob).oangle_neg_right hx hy

/-- Negating the first vector passed to `oangle` does not change twice the angle. -/
@[simp] lemma two_zsmul_oangle_neg_left (x y : V) :
  (2 : ℤ) • o.oangle (-x) y = (2 : ℤ) • o.oangle x y :=
(ob).two_zsmul_oangle_neg_left x y

/-- Negating the second vector passed to `oangle` does not change twice the angle. -/
@[simp] lemma two_zsmul_oangle_neg_right (x y : V) :
  (2 : ℤ) • o.oangle x (-y) = (2 : ℤ) • o.oangle x y :=
(ob).two_zsmul_oangle_neg_right x y

/-- Negating both vectors passed to `oangle` does not change the angle. -/
@[simp] lemma oangle_neg_neg (x y : V) : o.oangle (-x) (-y) = o.oangle x y :=
(ob).oangle_neg_neg x y

/-- Negating the first vector produces the same angle as negating the second vector. -/
lemma oangle_neg_left_eq_neg_right (x y : V) : o.oangle (-x) y = o.oangle x (-y) :=
(ob).oangle_neg_left_eq_neg_right x y

/-- The angle between the negation of a nonzero vector and that vector is `π`. -/
@[simp] lemma oangle_neg_self_left {x : V} (hx : x ≠ 0) : o.oangle (-x) x = π :=
(ob).oangle_neg_self_left hx

/-- The angle between a nonzero vector and its negation is `π`. -/
@[simp] lemma oangle_neg_self_right {x : V} (hx : x ≠ 0) : o.oangle x (-x) = π :=
(ob).oangle_neg_self_right hx

/-- Twice the angle between the negation of a vector and that vector is 0. -/
@[simp] lemma two_zsmul_oangle_neg_self_left (x : V) : (2 : ℤ) • o.oangle (-x) x = 0 :=
(ob).two_zsmul_oangle_neg_self_left x

/-- Twice the angle between a vector and its negation is 0. -/
@[simp] lemma two_zsmul_oangle_neg_self_right (x : V) : (2 : ℤ) • o.oangle x (-x) = 0 :=
(ob).two_zsmul_oangle_neg_self_right x

/-- Adding the angles between two vectors in each order, with the first vector in each angle
negated, results in 0. -/
@[simp] lemma oangle_add_oangle_rev_neg_left (x y : V) :
  o.oangle (-x) y + o.oangle (-y) x = 0 :=
(ob).oangle_add_oangle_rev_neg_left x y

/-- Adding the angles between two vectors in each order, with the second vector in each angle
negated, results in 0. -/
@[simp] lemma oangle_add_oangle_rev_neg_right (x y : V) :
  o.oangle x (-y) + o.oangle y (-x) = 0 :=
(ob).oangle_add_oangle_rev_neg_right x y

/-- Multiplying the first vector passed to `oangle` by a positive real does not change the
angle. -/
@[simp] lemma oangle_smul_left_of_pos (x y : V) {r : ℝ} (hr : 0 < r) :
  o.oangle (r • x) y = o.oangle x y :=
(ob).oangle_smul_left_of_pos x y hr

/-- Multiplying the second vector passed to `oangle` by a positive real does not change the
angle. -/
@[simp] lemma oangle_smul_right_of_pos (x y : V) {r : ℝ} (hr : 0 < r) :
  o.oangle x (r • y) = o.oangle x y :=
(ob).oangle_smul_right_of_pos x y hr

/-- Multiplying the first vector passed to `oangle` by a negative real produces the same angle
as negating that vector. -/
@[simp] lemma oangle_smul_left_of_neg (x y : V) {r : ℝ} (hr : r < 0) :
  o.oangle (r • x) y = o.oangle (-x) y :=
(ob).oangle_smul_left_of_neg x y hr

/-- Multiplying the second vector passed to `oangle` by a negative real produces the same angle
as negating that vector. -/
@[simp] lemma oangle_smul_right_of_neg (x y : V) {r : ℝ} (hr : r < 0) :
  o.oangle x (r • y) = o.oangle x (-y) :=
(ob).oangle_smul_right_of_neg x y hr

/-- The angle between a nonnegative multiple of a vector and that vector is 0. -/
@[simp] lemma oangle_smul_left_self_of_nonneg (x : V) {r : ℝ} (hr : 0 ≤ r) :
  o.oangle (r • x) x = 0 :=
(ob).oangle_smul_left_self_of_nonneg x hr

/-- The angle between a vector and a nonnegative multiple of that vector is 0. -/
@[simp] lemma oangle_smul_right_self_of_nonneg (x : V) {r : ℝ} (hr : 0 ≤ r) :
  o.oangle x (r • x) = 0 :=
(ob).oangle_smul_right_self_of_nonneg x hr

/-- The angle between two nonnegative multiples of the same vector is 0. -/
@[simp] lemma oangle_smul_smul_self_of_nonneg (x : V) {r₁ r₂ : ℝ} (hr₁ : 0 ≤ r₁) (hr₂ : 0 ≤ r₂) :
  o.oangle (r₁ • x) (r₂ • x) = 0 :=
(ob).oangle_smul_smul_self_of_nonneg x hr₁ hr₂

/-- Multiplying the first vector passed to `oangle` by a nonzero real does not change twice the
angle. -/
@[simp] lemma two_zsmul_oangle_smul_left_of_ne_zero (x y : V) {r : ℝ} (hr : r ≠ 0) :
  (2 : ℤ) • o.oangle (r • x) y = (2 : ℤ) • o.oangle x y :=
(ob).two_zsmul_oangle_smul_left_of_ne_zero x y hr

/-- Multiplying the second vector passed to `oangle` by a nonzero real does not change twice the
angle. -/
@[simp] lemma two_zsmul_oangle_smul_right_of_ne_zero (x y : V) {r : ℝ} (hr : r ≠ 0) :
  (2 : ℤ) • o.oangle x (r • y) = (2 : ℤ) • o.oangle x y :=
(ob).two_zsmul_oangle_smul_right_of_ne_zero x y hr

/-- Twice the angle between a multiple of a vector and that vector is 0. -/
@[simp] lemma two_zsmul_oangle_smul_left_self (x : V) {r : ℝ} :
  (2 : ℤ) • o.oangle (r • x) x = 0 :=
(ob).two_zsmul_oangle_smul_left_self x

/-- Twice the angle between a vector and a multiple of that vector is 0. -/
@[simp] lemma two_zsmul_oangle_smul_right_self (x : V) {r : ℝ} :
  (2 : ℤ) • o.oangle x (r • x) = 0 :=
(ob).two_zsmul_oangle_smul_right_self x

/-- Twice the angle between two multiples of a vector is 0. -/
@[simp] lemma two_zsmul_oangle_smul_smul_self (x : V) {r₁ r₂ : ℝ} :
  (2 : ℤ) • o.oangle (r₁ • x) (r₂ • x) = 0 :=
(ob).two_zsmul_oangle_smul_smul_self x

/-- Two vectors are equal if and only if they have equal norms and zero angle between them. -/
lemma eq_iff_norm_eq_and_oangle_eq_zero (x y : V) : x = y ↔ ∥x∥ = ∥y∥ ∧ o.oangle x y = 0 :=
(ob).eq_iff_norm_eq_and_oangle_eq_zero x y

/-- Two vectors with equal norms are equal if and only if they have zero angle between them. -/
lemma eq_iff_oangle_eq_zero_of_norm_eq {x y : V} (h : ∥x∥ = ∥y∥) : x = y ↔ o.oangle x y = 0 :=
(ob).eq_iff_oangle_eq_zero_of_norm_eq h

/-- Two vectors with zero angle between them are equal if and only if they have equal norms. -/
lemma eq_iff_norm_eq_of_oangle_eq_zero {x y : V} (h : o.oangle x y = 0) : x = y ↔ ∥x∥ = ∥y∥ :=
(ob).eq_iff_norm_eq_of_oangle_eq_zero h

/-- Given three nonzero vectors, the angle between the first and the second plus the angle
between the second and the third equals the angle between the first and the third. -/
@[simp] lemma oangle_add {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  o.oangle x y + o.oangle y z = o.oangle x z :=
(ob).oangle_add hx hy hz

/-- Given three nonzero vectors, the angle between the second and the third plus the angle
between the first and the second equals the angle between the first and the third. -/
@[simp] lemma oangle_add_swap {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
   o.oangle y z + o.oangle x y = o.oangle x z :=
(ob).oangle_add_swap hx hy hz

/-- Given three nonzero vectors, the angle between the first and the third minus the angle
between the first and the second equals the angle between the second and the third. -/
@[simp] lemma oangle_sub_left {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  o.oangle x z - o.oangle x y = o.oangle y z :=
(ob).oangle_sub_left hx hy hz

/-- Given three nonzero vectors, the angle between the first and the third minus the angle
between the second and the third equals the angle between the first and the second. -/
@[simp] lemma oangle_sub_right {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  o.oangle x z - o.oangle y z = o.oangle x y :=
(ob).oangle_sub_right hx hy hz

/-- Given three nonzero vectors, adding the angles between them in cyclic order results in 0. -/
@[simp] lemma oangle_add_cyc3 {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  o.oangle x y + o.oangle y z + o.oangle z x = 0 :=
(ob).oangle_add_cyc3 hx hy hz

/-- Given three nonzero vectors, adding the angles between them in cyclic order, with the first
vector in each angle negated, results in π. If the vectors add to 0, this is a version of the
sum of the angles of a triangle. -/
@[simp] lemma oangle_add_cyc3_neg_left {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  o.oangle (-x) y + o.oangle (-y) z + o.oangle (-z) x = π :=
(ob).oangle_add_cyc3_neg_left hx hy hz

/-- Given three nonzero vectors, adding the angles between them in cyclic order, with the second
vector in each angle negated, results in π. If the vectors add to 0, this is a version of the
sum of the angles of a triangle. -/
@[simp] lemma oangle_add_cyc3_neg_right {x y z : V} (hx : x ≠ 0) (hy : y ≠ 0) (hz : z ≠ 0) :
  o.oangle x (-y) + o.oangle y (-z) + o.oangle z (-x) = π :=
(ob).oangle_add_cyc3_neg_right hx hy hz

/-- Pons asinorum, oriented vector angle form. -/
lemma oangle_sub_eq_oangle_sub_rev_of_norm_eq {x y : V} (h : ∥x∥ = ∥y∥) :
  o.oangle x (x - y) = o.oangle (y - x) y :=
(ob).oangle_sub_eq_oangle_sub_rev_of_norm_eq h

/-- The angle at the apex of an isosceles triangle is `π` minus twice a base angle, oriented
vector angle form. -/
lemma oangle_eq_pi_sub_two_zsmul_oangle_sub_of_norm_eq {x y : V} (hn : x ≠ y) (h : ∥x∥ = ∥y∥) :
  o.oangle y x = π - (2 : ℤ) • o.oangle (y - x) y :=
(ob).oangle_eq_pi_sub_two_zsmul_oangle_sub_of_norm_eq hn h

/-- Angle at center of a circle equals twice angle at circumference, oriented vector angle
form. -/
lemma oangle_eq_two_zsmul_oangle_sub_of_norm_eq {x y z : V} (hxyne : x ≠ y) (hxzne : x ≠ z)
  (hxy : ∥x∥ = ∥y∥) (hxz : ∥x∥ = ∥z∥) : o.oangle y z = (2 : ℤ) • o.oangle (y - x) (z - x) :=
(ob).oangle_eq_two_zsmul_oangle_sub_of_norm_eq hxyne hxzne hxy hxz

/-- Angle at center of a circle equals twice angle at circumference, oriented vector angle
form with radius specified. -/
lemma oangle_eq_two_zsmul_oangle_sub_of_norm_eq_real {x y z : V} (hxyne : x ≠ y) (hxzne : x ≠ z)
  {r : ℝ} (hx : ∥x∥ = r) (hy : ∥y∥ = r) (hz : ∥z∥ = r) :
  o.oangle y z = (2 : ℤ) • o.oangle (y - x) (z - x) :=
(ob).oangle_eq_two_zsmul_oangle_sub_of_norm_eq_real hxyne hxzne hx hy hz

/-- Oriented vector angle version of "angles in same segment are equal" and "opposite angles of
a cyclic quadrilateral add to π", for oriented angles mod π (for which those are the same
result), represented here as equality of twice the angles. -/
lemma two_zsmul_oangle_sub_eq_two_zsmul_oangle_sub_of_norm_eq {x₁ x₂ y z : V} (hx₁yne : x₁ ≠ y)
  (hx₁zne : x₁ ≠ z) (hx₂yne : x₂ ≠ y) (hx₂zne : x₂ ≠ z) {r : ℝ} (hx₁ : ∥x₁∥ = r) (hx₂ : ∥x₂∥ = r)
  (hy : ∥y∥ = r) (hz : ∥z∥ = r) :
  (2 : ℤ) • o.oangle (y - x₁) (z - x₁) = (2 : ℤ) • o.oangle (y - x₂) (z - x₂) :=
(ob).two_zsmul_oangle_sub_eq_two_zsmul_oangle_sub_of_norm_eq hx₁yne hx₁zne hx₂yne hx₂zne hx₁ hx₂
  hy hz

/-- A rotation by the oriented angle `θ`. -/
def rotation (θ : real.angle) : V ≃ₗᵢ[ℝ] V :=
(ob).rotation θ

/-- The determinant of `rotation` (as a linear map) is equal to `1`. -/
@[simp] lemma det_rotation (θ : real.angle) :
  ((o.rotation θ).to_linear_equiv : V →ₗ[ℝ] V).det = 1 :=
(ob).det_rotation θ

/-- The determinant of `rotation` (as a linear equiv) is equal to `1`. -/
@[simp] lemma linear_equiv_det_rotation (θ : real.angle) :
  (o.rotation θ).to_linear_equiv.det = 1 :=
(ob).linear_equiv_det_rotation θ

/-- The inverse of `rotation` is rotation by the negation of the angle. -/
@[simp] lemma rotation_symm (θ : real.angle) : (o.rotation θ).symm = o.rotation (-θ) :=
(ob).rotation_symm θ

/-- Rotation by 0 is the identity. -/
@[simp] lemma rotation_zero : o.rotation 0 = linear_isometry_equiv.refl ℝ V :=
(ob).rotation_zero

/-- Rotation by π is negation. -/
lemma rotation_pi : o.rotation π = linear_isometry_equiv.neg ℝ :=
(ob).rotation_pi

/-- Rotating twice is equivalent to rotating by the sum of the angles. -/
@[simp] lemma rotation_trans (θ₁ θ₂ : real.angle) :
  (o.rotation θ₁).trans (o.rotation θ₂) = o.rotation (θ₂ + θ₁) :=
(ob).rotation_trans θ₁ θ₂

/-- Rotating the first vector by `θ` subtracts `θ` from the angle between two vectors. -/
@[simp] lemma oangle_rotation_left {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) (θ : real.angle) :
  o.oangle (o.rotation θ x) y = o.oangle x y - θ :=
(ob).oangle_rotation_left hx hy θ

/-- Rotating the second vector by `θ` adds `θ` to the angle between two vectors. -/
@[simp] lemma oangle_rotation_right {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) (θ : real.angle) :
  o.oangle x (o.rotation θ y) = o.oangle x y + θ :=
(ob).oangle_rotation_right hx hy θ

/-- The rotation of a vector by `θ` has an angle of `-θ` from that vector. -/
@[simp] lemma oangle_rotation_self_left {x : V} (hx : x ≠ 0) (θ : real.angle) :
  o.oangle (o.rotation θ x) x = -θ :=
(ob).oangle_rotation_self_left hx θ

/-- A vector has an angle of `θ` from the rotation of that vector by `θ`. -/
@[simp] lemma oangle_rotation_self_right {x : V} (hx : x ≠ 0) (θ : real.angle) :
  o.oangle x (o.rotation θ x) = θ :=
(ob).oangle_rotation_self_right hx θ

/-- Rotating the first vector by the angle between the two vectors results an an angle of 0. -/
@[simp] lemma oangle_rotation_oangle_left (x y : V) :
  o.oangle (o.rotation (o.oangle x y) x) y = 0 :=
(ob).oangle_rotation_oangle_left x y

/-- Rotating the first vector by the angle between the two vectors and swapping the vectors
results an an angle of 0. -/
@[simp] lemma oangle_rotation_oangle_right (x y : V) :
  o.oangle y (o.rotation (o.oangle x y) x) = 0 :=
(ob).oangle_rotation_oangle_right x y

/-- Rotating both vectors by the same angle does not change the angle between those vectors. -/
@[simp] lemma oangle_rotation (x y : V) (θ : real.angle) :
  o.oangle (o.rotation θ x) (o.rotation θ y) = o.oangle x y :=
(ob).oangle_rotation x y θ

/-- A rotation of a nonzero vector equals that vector if and only if the angle is zero. -/
@[simp] lemma rotation_eq_self_iff_angle_eq_zero {x : V} (hx : x ≠ 0) (θ : real.angle) :
  o.rotation θ x = x ↔ θ = 0 :=
(ob).rotation_eq_self_iff_angle_eq_zero hx θ

/-- A nonzero vector equals a rotation of that vector if and only if the angle is zero. -/
@[simp] lemma eq_rotation_self_iff_angle_eq_zero {x : V} (hx : x ≠ 0) (θ : real.angle) :
  x = o.rotation θ x ↔ θ = 0 :=
(ob).eq_rotation_self_iff_angle_eq_zero hx θ

/-- A rotation of a vector equals that vector if and only if the vector or the angle is zero. -/
lemma rotation_eq_self_iff (x : V) (θ : real.angle) :
  o.rotation θ x = x ↔ x = 0 ∨ θ = 0 :=
(ob).rotation_eq_self_iff x θ

/-- A vector equals a rotation of that vector if and only if the vector or the angle is zero. -/
lemma eq_rotation_self_iff (x : V) (θ : real.angle) :
  x = o.rotation θ x ↔ x = 0 ∨ θ = 0 :=
(ob).eq_rotation_self_iff x θ

/-- Rotating a vector by the angle to another vector gives the second vector if and only if the
norms are equal. -/
@[simp] lemma rotation_oangle_eq_iff_norm_eq (x y : V) :
  o.rotation (o.oangle x y) x = y ↔ ∥x∥ = ∥y∥ :=
(ob).rotation_oangle_eq_iff_norm_eq x y

/-- The angle between two nonzero vectors is `θ` if and only if the second vector is the first
rotated by `θ` and scaled by the ratio of the norms. -/
lemma oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero {x y : V} (hx : x ≠ 0) (hy : y ≠ 0)
  (θ : real.angle) : o.oangle x y = θ ↔ y = (∥y∥ / ∥x∥) • o.rotation θ x :=
(ob).oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero hx hy θ

/-- The angle between two nonzero vectors is `θ` if and only if the second vector is the first
rotated by `θ` and scaled by a positive real. -/
lemma oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero {x y : V} (hx : x ≠ 0) (hy : y ≠ 0)
  (θ : real.angle) : o.oangle x y = θ ↔ ∃ r : ℝ, 0 < r ∧ y = r • o.rotation θ x :=
(ob).oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero hx hy θ

/-- The angle between two vectors is `θ` if and only if they are nonzero and the second vector
is the first rotated by `θ` and scaled by the ratio of the norms, or `θ` and at least one of the
vectors are zero. -/
lemma oangle_eq_iff_eq_norm_div_norm_smul_rotation_or_eq_zero {x y : V} (θ : real.angle) :
  o.oangle x y = θ ↔
    (x ≠ 0 ∧ y ≠ 0 ∧ y = (∥y∥ / ∥x∥) • o.rotation θ x) ∨ (θ = 0 ∧ (x = 0 ∨ y = 0)) :=
(ob).oangle_eq_iff_eq_norm_div_norm_smul_rotation_or_eq_zero θ

/-- The angle between two vectors is `θ` if and only if they are nonzero and the second vector
is the first rotated by `θ` and scaled by a positive real, or `θ` and at least one of the
vectors are zero. -/
lemma oangle_eq_iff_eq_pos_smul_rotation_or_eq_zero {x y : V} (θ : real.angle) :
  o.oangle x y = θ ↔
    (x ≠ 0 ∧ y ≠ 0 ∧ ∃ r : ℝ, 0 < r ∧ y = r • o.rotation θ x) ∨ (θ = 0 ∧ (x = 0 ∨ y = 0)) :=
(ob).oangle_eq_iff_eq_pos_smul_rotation_or_eq_zero θ

/-- Any linear isometric equivalence in `V` with positive determinant is `rotation`. -/
lemma exists_linear_isometry_equiv_eq_of_det_pos {f : V ≃ₗᵢ[ℝ] V}
  (hd : 0 < (f.to_linear_equiv : V →ₗ[ℝ] V).det) : ∃ θ : real.angle, f = o.rotation θ :=
(ob).exists_linear_isometry_equiv_eq_of_det_pos hd

/-- The angle between two vectors, with respect to an orientation given by `orientation.map`
with a linear isometric equivalence, equals the angle between those two vectors, transformed by
the inverse of that equivalence, with respect to the original orientation. -/
@[simp] lemma oangle_map (x y : V) (f : V ≃ₗᵢ[ℝ] V) :
  (orientation.map (fin 2) f.to_linear_equiv o).oangle x y = o.oangle (f.symm x) (f.symm y) :=
begin
  convert (ob).oangle_map x y f using 1,
  refine orthonormal.oangle_eq_of_orientation_eq _ _ _ _ _,
  simp_rw [basis.orientation_map, orientation.fin_orthonormal_basis_orientation]
end

/-- `orientation.oangle` equals `orthonormal.oangle` for any orthonormal basis with that
orientation. -/
lemma oangle_eq_basis_oangle {b : basis (fin 2) ℝ V} (hb : orthonormal ℝ b)
  (h : b.orientation = o) (x y : V) : o.oangle x y = hb.oangle x y :=
begin
  rw oangle,
  refine orthonormal.oangle_eq_of_orientation_eq _ _ _ _ _,
  simp [h]
end

/-- Negating the orientation negates the value of `oangle`. -/
lemma oangle_neg_orientation_eq_neg (x y : V) : (-o).oangle x y = -(o.oangle x y) :=
begin
  simp_rw oangle,
  refine orthonormal.oangle_eq_neg_of_orientation_eq_neg _ _ _ _ _,
  simp_rw orientation.fin_orthonormal_basis_orientation
end

/-- `orientation.rotation` equals `orthonormal.rotation` for any orthonormal basis with that
orientation. -/
lemma rotation_eq_basis_rotation {b : basis (fin 2) ℝ V} (hb : orthonormal ℝ b)
  (h : b.orientation = o) (θ : ℝ) : o.rotation θ = hb.rotation θ :=
begin
  rw rotation,
  refine orthonormal.rotation_eq_of_orientation_eq _ _ _ _,
  simp [h]
end

/-- Negating the orientation negates the angle in `rotation`. -/
lemma rotation_neg_orientation_eq_neg (θ : real.angle) :
  (-o).rotation θ = o.rotation (-θ) :=
begin
  simp_rw rotation,
  refine orthonormal.rotation_eq_rotation_neg_of_orientation_eq_neg _ _ _ _,
  simp_rw orientation.fin_orthonormal_basis_orientation
end

end orientation


import data.real.basic

variables a b c : ℝ

#check pow_two_nonneg
#check pow_two_nonneg b
#check @add_le_add

-- BEGIN
example {z : ℝ} (h : ∃ x y, z = x^2 + y^2 ∨ z = x^2 + y^2 + 1) :
  z ≥ 0 :=
begin
  cases h with a ha,
  cases ha with b hb,
  have ha2 : a ^ 2 ≥ 0,
    from pow_two_nonneg a,
  have hb2 : b ^ 2 ≥ 0,
    from pow_two_nonneg b,
  have ha2b2 := add_nonneg ha2 hb2,
  cases hb,
  { rw hb, exact ha2b2 },
  { rw hb, exact add_nonneg ha2b2 zero_le_one },
end
-- END


import binary_nat
import data.nat.basic
import data.nat.pow
import tactic.suggest
import tactic.linarith

open binary_nat
open costed_binary_nat
open complexity

universe u
variables {α : Type u}

lemma zero_lt_two_pow {n : ℕ} : 0 < (2 ^ n) :=
nat.lt_of_lt_of_le (by simp) (nat.one_le_two_pow _)

lemma pow_two_pow_square {n m : ℕ} : n ^ 2 ^ (m + 1) = n ^ 2 ^ m * n ^ 2 ^ m :=
by  rw [pow_succ, mul_comm 2, pow_mul, pow_succ, pow_one]

lemma one_lt_two_two_pow : ∀ n : ℕ, 1 < (2 ^ 2 ^ n)
| 0 := by simp
| (n+1) := begin
  rw [pow_two_pow_square],
  apply lt_mul_of_lt_of_one_le,
  apply one_lt_two_two_pow,
  apply le_of_lt,
  apply one_lt_two_two_pow,
end

lemma mod_cancel {x y: ℕ} (z : ℕ) (H: x = y): x % z = y % z := by simp[H]

theorem toNat_bound : Π {n}, ∀ x : binary_nat n, toNat x < 2 ^ 2 ^ n
| 0 := begin
  intro x,
  cases x,
  all_goals { simp[toNat] },
end
| (n+1) := begin
  intro x,
  cases x with _ xu xl,
  simp [toNat],
  apply @nat.lt_of_lt_of_le _ (2 ^ 2 ^ n * xu.toNat + 2 ^ 2 ^ n),
  apply add_lt_add_left,
  apply toNat_bound,
  rw [← nat.mul_succ, pow_two_pow_square],
  apply nat.mul_le_mul_left,
  apply nat.le_of_lt_succ,
  apply nat.succ_lt_succ,
  apply toNat_bound,
end

theorem toNat_cancel : Π {n}, ∀ x y : binary_nat n,
  toNat x = toNat y ↔ x = y
| 0 := begin
  intros x y,
  cases x,
  all_goals {cases y},
  all_goals {simp [toNat]},
end
| (n+1) := begin
  intros x y,
  split,
  cases x with _ xu xl,
  cases y with _ yu yl,
  simp [toNat, nat.mul_comm (2^2^n)],
  intro p,
  have q := mod_cancel (2^2^n) p,
  simp [nat.mul_add_mod, nat.mod_eq_of_lt (toNat_bound _)] at q,
  rw [toNat_cancel] at q,
  simp [q, toNat_cancel] at p,
  exact ⟨p, q⟩,
  intro p,
  simp [p],
end

theorem zero_value : Π (n), toNat (cost.value_of (zero n)) = 0
| 0 := by simp [zero, toNat]
| (n+1) := begin
  simp [zero, zero_value, toNat],
end

lemma nat_mul_add_div_left (y z : ℕ) {x : ℕ} (H: 0 < x) : (x * y + z) / x = y + z / x :=
by simp [add_comm, nat.add_mul_div_left _ _ H]

lemma mod_absorb {x y z : ℕ} (H: 0 < x) : x * (y % x) + (z % x) = (x * y + (z % x)) % (x ^ 2) :=
begin
  have g := show (x * (y % x) + (z % x) < x ^ 2),
  begin
    apply @nat.lt_of_lt_of_le _ (x * (y % x) + x),
    apply add_lt_add_left,
    apply nat.mod_lt _ H,
    rw [← nat.mul_succ, pow_succ, pow_one],
    apply nat.mul_le_mul_left,
    apply nat.le_of_lt_succ,
    apply nat.succ_lt_succ,
    apply nat.mod_lt _ H,
  end,
  rw [← nat.mod_eq_of_lt g],
  rw [← nat.mul_mod_mul_left],
  rw [pow_succ, pow_one],
  rw [nat.mod_add_mod],
end

lemma mul_lt_div {a b c : ℕ} : a < c → b < c → a * b / c < c :=
begin
  intros p q,
  apply @lt_of_mul_lt_mul_left _ c,
  apply @nat.lt_of_add_lt_add_right _ (a*b%c),
  rw [nat.div_add_mod],
  apply nat.lt_add_right,
  cases a,
  simp,
  assumption,
  apply @nat.lt_trans _ (a.succ*c),
  apply nat.mul_lt_mul_of_pos_left,
  assumption,
  apply nat.zero_lt_succ,
  apply nat.mul_lt_mul_of_pos_right,
  assumption,
  apply pos_of_gt,
  assumption,
  apply nat.zero_le _,
end

lemma add_mul_lt_div {a b c d e : ℕ} : (a < b) → (c < b) → (d + b < e) → (d + a*c/b < e) :=
begin
  intros p q r,
  apply nat.lt_trans _ r,
  rw add_lt_add_iff_left,
  exact mul_lt_div p q,
end

theorem add_with_carry_value : Π {n}, ∀ x y : binary_nat n, ∀ z : binary_nat 0,
  (toNat (cost.value_of (add_with_carry x y z)).fst = (toNat x + toNat y + toNat z) / 2 ^ 2 ^ n) ∧
  (toNat (cost.value_of (add_with_carry x y z)).snd = (toNat x + toNat y + toNat z) % 2 ^ 2 ^ n)
| 0 := begin
  intros x y z,
  cases x,
  all_goals { cases y },
  all_goals { cases z },
  all_goals { simp [add_with_carry, toNat] },
  all_goals { ring },
end
| (n+1) := begin
  intros x y z,
  cases x with binary_nat.intro xu xl,
  cases y with binary_nat.intro yu yl,
  simp [add_with_carry, toNat,
      (add_with_carry_value _ _ _).left,
      (add_with_carry_value _ _ _).right],
  split,
  ring_nf,
  rw [pow_two_pow_square],
  rw [← nat.div_div_eq_div_mul,
      nat_mul_add_div_left _ _ zero_lt_two_pow,
      nat_mul_add_div_left _ _ zero_lt_two_pow],
  rw [mod_absorb zero_lt_two_pow],
  rw [← pow_mul, nat.mul_comm _ 2, ← pow_succ],
  apply mod_cancel,
  simp [mul_add, nat.add_assoc],
  rw [nat.div_add_mod],
  ring,
end

theorem add_value { n : ℕ } {x y : binary_nat n } :
    toNat (cost.value_of (add x y)) = (toNat x + toNat y) % 2 ^ 2 ^ n :=
by simp[add, toNat, add_with_carry_value x y bit_false]

theorem traditional_multiply_value : Π {n}, ∀ {x y : binary_nat n },
    toNat (cost.value_of (traditional_multiply x y)) = (toNat x * toNat y)
| 0 := begin
  intros x y,
  cases x,
  all_goals {cases y},
  all_goals {simp [traditional_multiply, toNat]},
end
| (n+1) := begin
  intros x y,
  cases x with binary_nat.intro xu xl,
  cases y with binary_nat.intro yu yl,
  simp [traditional_multiply, toNat,
      zero_value, add_value, traditional_multiply_value],
  cases h : (cost.value_of (traditional_multiply xu yl)) with _ ulu ull,
  cases g : (cost.value_of (traditional_multiply xl yu)) with _ luu lul,
  simp [split, toNat],
  rw [← toNat_cancel] at h,
  rw [← toNat_cancel] at g,
  have H : (∀ {n x y z : ℕ}, x = 2^2^n * y + z → 2^2^ n * x = 2^2^(n + 1) * y + 2^2^n * z),
  { intros n x y z p,
    rw [pow_two_pow_square, p],
    ring },
  simp [traditional_multiply_value, toNat] at h,
  simp [traditional_multiply_value, toNat] at g,
  rw [← H h, ← H g, nat.mod_eq_of_lt],
  simp [mul_add, add_mul, pow_two_pow_square],
  ring,
  simp [pow_two_pow_square],
  simp [← nat.add_assoc],
  have reduce : ∀ {a b c d e: ℕ }, a < c → b < c → e + c*c + 1 < d + 2*c →
  e + a*b < d,
  { intros a b c d e p q r,
    rw [← add_lt_add_iff_right (2*c)],
    apply nat.lt_of_le_of_lt _ r,
    simp [nat.add_assoc],
    cases c,
    linarith,
    simp [nat.succ_eq_add_one, mul_add, add_mul],
    ring_nf,
    simp [← nat.add_assoc, add_mul],
    apply nat.mul_le_mul,
    apply nat.le_of_lt_succ q,
    apply nat.le_of_lt_succ p, },
  apply reduce (toNat_bound _) (toNat_bound _),
  apply @nat.lt_of_div_lt_div _ _ (2^2^n),
  simp [nat.add_assoc, nat.mul_assoc,
    nat_mul_add_div_left _ _ zero_lt_two_pow, nat.div_eq_zero (one_lt_two_two_pow _)],
  simp [← nat.add_assoc, nat.add_comm _ (2^2^n)],
  apply reduce (toNat_bound _) (toNat_bound _),
  simp [nat.add_right_comm _ (xu.toNat * yl.toNat)],
  apply reduce (toNat_bound _) (toNat_bound _),
  simp [← nat.add_assoc, nat.add_right_comm _ 1],
  apply @nat.lt_of_div_lt_div _ _ (2^2^n),
  simp [nat.add_assoc, nat.mul_assoc,
        nat_mul_add_div_left _ _ zero_lt_two_pow,
        nat.div_eq_zero (one_lt_two_two_pow _),
        nat.add_mul_div_right],
  rw [nat.add_comm, ← nat.succ_add, nat.succ_eq_add_one],
  apply reduce (toNat_bound _) (toNat_bound _),
  linarith,
end


-- ---------------------------------------------------------------------
-- Ejercicio 1. Demostrar que hay algún número real entre 2 y 3.
-- ----------------------------------------------------------------------

import data.real.basic

-- 1ª demostración
-- ===============

example : ∃ x : ℝ, 2 < x ∧ x < 3 :=
begin
  have h : 2 < (5 : ℝ) / 2 ∧ (5 : ℝ) / 2 < 3,
    by norm_num,
  show ∃ x : ℝ, 2 < x ∧ x < 3,
    by exact Exists.intro (5 / 2) h,
end

-- 2ª demostración
-- ===============

example : ∃ x : ℝ, 2 < x ∧ x < 3 :=
begin
  have h : 2 < (5 : ℝ) / 2 ∧ (5 : ℝ) / 2 < 3,
    by norm_num,
  show ∃ x : ℝ, 2 < x ∧ x < 3,
    by exact ⟨5 / 2, h⟩,
end

-- 3ª demostración
-- ===============

example : ∃ x : ℝ, 2 < x ∧ x < 3 :=
begin
  use 5 / 2,
  norm_num
end

-- 4ª demostración
-- ===============

example : ∃ x : ℝ, 2 < x ∧ x < 3 :=
⟨5 / 2, by norm_num⟩


/-
Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import measure_theory.constructions.prod
import measure_theory.group.measure

/-!
# Measure theory in the product of groups
In this file we show properties about measure theory in products of measurable groups
and properties of iterated integrals in measurable groups.

These lemmas show the uniqueness of left invariant measures on measurable groups, up to
scaling. In this file we follow the proof and refer to the book *Measure Theory* by Paul Halmos.

The idea of the proof is to use the translation invariance of measures to prove `μ(t) = c * μ(s)`
for two sets `s` and `t`, where `c` is a constant that does not depend on `μ`. Let `e` and `f` be
the characteristic functions of `s` and `t`.
Assume that `μ` and `ν` are left-invariant measures. Then the map `(x, y) ↦ (y * x, x⁻¹)`
preserves the measure `μ × ν`, which means that
```
  ∫ x, ∫ y, h x y ∂ν ∂μ = ∫ x, ∫ y, h (y * x) x⁻¹ ∂ν ∂μ
```
If we apply this to `h x y := e x * f y⁻¹ / ν ((λ h, h * y⁻¹) ⁻¹' s)`, we can rewrite the RHS to
`μ(t)`, and the LHS to `c * μ(s)`, where `c = c(ν)` does not depend on `μ`.
Applying this to `μ` and to `ν` gives `μ (t) / μ (s) = ν (t) / ν (s)`, which is the uniqueness up to
scalar multiplication.

The proof in [Halmos] seems to contain an omission in §60 Th. A, see
`measure_theory.measure_lintegral_div_measure`.

-/

noncomputable theory
open set (hiding prod_eq) function measure_theory filter (hiding map)
open_locale classical ennreal pointwise measure_theory

variables (G : Type*) [measurable_space G]
variables [group G] [has_measurable_mul₂ G]
variables (μ ν : measure G) [sigma_finite ν] [sigma_finite μ] {s : set G}

/-- The map `(x, y) ↦ (x, xy)` as a `measurable_equiv`. -/
@[to_additive "The map `(x, y) ↦ (x, x + y)` as a `measurable_equiv`."]
protected def measurable_equiv.shear_mul_right [has_measurable_inv G] : G × G ≃ᵐ G × G :=
{ measurable_to_fun  := measurable_fst.prod_mk measurable_mul,
  measurable_inv_fun := measurable_fst.prod_mk $ measurable_fst.inv.mul measurable_snd,
  .. equiv.prod_shear (equiv.refl _) equiv.mul_left }

/-- The map `(x, y) ↦ (x, y / x)` as a `measurable_equiv` with as inverse `(x, y) ↦ (x, yx)` -/
@[to_additive
  "The map `(x, y) ↦ (x, y - x)` as a `measurable_equiv` with as inverse `(x, y) ↦ (x, y + x)`."]
protected def measurable_equiv.shear_div_right [has_measurable_inv G] : G × G ≃ᵐ G × G :=
{ measurable_to_fun  := measurable_fst.prod_mk $ measurable_snd.div measurable_fst,
  measurable_inv_fun := measurable_fst.prod_mk $ measurable_snd.mul measurable_fst,
  .. equiv.prod_shear (equiv.refl _) (equiv.div_right) }

variables {G}

namespace measure_theory

open measure

section left_invariant

/-- The multiplicative shear mapping `(x, y) ↦ (x, xy)` preserves the measure `μ × ν`.
This condition is part of the definition of a measurable group in [Halmos, §59].
There, the map in this lemma is called `S`. -/
@[to_additive measure_preserving_prod_add
  /-" The shear mapping `(x, y) ↦ (x, x + y)` preserves the measure `μ × ν`. "-/]
lemma measure_preserving_prod_mul [is_mul_left_invariant ν] :
  measure_preserving (λ z : G × G, (z.1, z.1 * z.2)) (μ.prod ν) (μ.prod ν) :=
(measure_preserving.id μ).skew_product measurable_mul $
  filter.eventually_of_forall $ map_mul_left_eq_self ν

/-- The map `(x, y) ↦ (y, yx)` sends the measure `μ × ν` to `ν × μ`.
This is the map `SR` in [Halmos, §59].
`S` is the map `(x, y) ↦ (x, xy)` and `R` is `prod.swap`. -/
@[to_additive measure_preserving_prod_add_swap
  /-" The map `(x, y) ↦ (y, y + x)` sends the measure `μ × ν` to `ν × μ`. "-/]
lemma measure_preserving_prod_mul_swap [is_mul_left_invariant μ] :
  measure_preserving (λ z : G × G, (z.2, z.2 * z.1)) (μ.prod ν) (ν.prod μ) :=
(measure_preserving_prod_mul ν μ).comp measure_preserving_swap

@[to_additive]
lemma measurable_measure_mul_right (hs : measurable_set s) :
  measurable (λ x, μ ((λ y, y * x) ⁻¹' s)) :=
begin
  suffices : measurable (λ y,
    μ ((λ x, (x, y)) ⁻¹' ((λ z : G × G, ((1 : G), z.1 * z.2)) ⁻¹' (univ ×ˢ s)))),
  { convert this, ext1 x, congr' 1 with y : 1, simp },
  apply measurable_measure_prod_mk_right,
  exact measurable_const.prod_mk measurable_mul (measurable_set.univ.prod hs)
end

variables [has_measurable_inv G]

/-- The map `(x, y) ↦ (x, x⁻¹y)` is measure-preserving.
This is the function `S⁻¹` in [Halmos, §59],
where `S` is the map `(x, y) ↦ (x, xy)`. -/
@[to_additive measure_preserving_prod_neg_add
  "The map `(x, y) ↦ (x, - x + y)` is measure-preserving."]
lemma measure_preserving_prod_inv_mul [is_mul_left_invariant ν] :
  measure_preserving (λ z : G × G, (z.1, z.1⁻¹ * z.2)) (μ.prod ν) (μ.prod ν) :=
(measure_preserving_prod_mul μ ν).symm $ measurable_equiv.shear_mul_right G

variables [is_mul_left_invariant μ]

/-- The map `(x, y) ↦ (y, y⁻¹x)` sends `μ × ν` to `ν × μ`.
This is the function `S⁻¹R` in [Halmos, §59],
where `S` is the map `(x, y) ↦ (x, xy)` and `R` is `prod.swap`. -/
@[to_additive measure_preserving_prod_neg_add_swap
  "The map `(x, y) ↦ (y, - y + x)` sends `μ × ν` to `ν × μ`."]
lemma measure_preserving_prod_inv_mul_swap :
  measure_preserving (λ z : G × G, (z.2, z.2⁻¹ * z.1)) (μ.prod ν) (ν.prod μ) :=
(measure_preserving_prod_inv_mul ν μ).comp measure_preserving_swap

/-- The map `(x, y) ↦ (yx, x⁻¹)` is measure-preserving.
This is the function `S⁻¹RSR` in [Halmos, §59],
where `S` is the map `(x, y) ↦ (x, xy)` and `R` is `prod.swap`. -/
@[to_additive measure_preserving_add_prod_neg
  "The map `(x, y) ↦ (y + x, - x)` is measure-preserving."]
lemma measure_preserving_mul_prod_inv [is_mul_left_invariant ν] :
  measure_preserving (λ z : G × G, (z.2 * z.1, z.1⁻¹)) (μ.prod ν) (μ.prod ν) :=
begin
  convert (measure_preserving_prod_inv_mul_swap ν μ).comp
    (measure_preserving_prod_mul_swap μ ν),
  ext1 ⟨x, y⟩,
  simp_rw [function.comp_apply, mul_inv_rev, inv_mul_cancel_right]
end

@[to_additive] lemma quasi_measure_preserving_inv :
  quasi_measure_preserving (has_inv.inv : G → G) μ μ :=
begin
  refine ⟨measurable_inv, absolutely_continuous.mk $ λ s hsm hμs, _⟩,
  rw [map_apply measurable_inv hsm, inv_preimage],
  have hf : measurable (λ z : G × G, (z.2 * z.1, z.1⁻¹)) :=
    (measurable_snd.mul measurable_fst).prod_mk measurable_fst.inv,
  suffices : map (λ z : G × G, (z.2 * z.1, z.1⁻¹)) (μ.prod μ) (s⁻¹ ×ˢ s⁻¹) = 0,
  { simpa only [(measure_preserving_mul_prod_inv μ μ).map_eq,
      prod_prod, mul_eq_zero, or_self] using this },
  have hsm' : measurable_set (s⁻¹ ×ˢ s⁻¹) := hsm.inv.prod hsm.inv,
  simp_rw [map_apply hf hsm', prod_apply_symm (hf hsm'), preimage_preimage, mk_preimage_prod,
    inv_preimage, inv_inv, measure_mono_null (inter_subset_right _ _) hμs, lintegral_zero]
end

@[to_additive]
lemma measure_inv_null : μ s⁻¹ = 0 ↔ μ s = 0 :=
begin
  refine ⟨λ hs, _, (quasi_measure_preserving_inv μ).preimage_null⟩,
  rw [← inv_inv s],
  exact (quasi_measure_preserving_inv μ).preimage_null hs
end

@[to_additive]
lemma inv_absolutely_continuous : μ.inv ≪ μ :=
(quasi_measure_preserving_inv μ).absolutely_continuous

@[to_additive]
lemma absolutely_continuous_inv : μ ≪ μ.inv :=
begin
  refine absolutely_continuous.mk (λ s hs, _),
  simp_rw [inv_apply μ s, measure_inv_null, imp_self]
end

@[to_additive]
lemma lintegral_lintegral_mul_inv [is_mul_left_invariant ν]
  (f : G → G → ℝ≥0∞) (hf : ae_measurable (uncurry f) (μ.prod ν)) :
  ∫⁻ x, ∫⁻ y, f (y * x) x⁻¹ ∂ν ∂μ = ∫⁻ x, ∫⁻ y, f x y ∂ν ∂μ :=
begin
  have h : measurable (λ z : G × G, (z.2 * z.1, z.1⁻¹)) :=
  (measurable_snd.mul measurable_fst).prod_mk measurable_fst.inv,
  have h2f : ae_measurable (uncurry $ λ x y, f (y * x) x⁻¹) (μ.prod ν) :=
    hf.comp_quasi_measure_preserving (measure_preserving_mul_prod_inv μ ν).quasi_measure_preserving,
  simp_rw [lintegral_lintegral h2f, lintegral_lintegral hf],
  conv_rhs { rw [← (measure_preserving_mul_prod_inv μ ν).map_eq] },
  symmetry,
  exact lintegral_map' (hf.mono' (measure_preserving_mul_prod_inv μ ν).map_eq.absolutely_continuous)
    h.ae_measurable,
end

@[to_additive]
lemma measure_mul_right_null (y : G) :
  μ ((λ x, x * y) ⁻¹' s) = 0 ↔ μ s = 0 :=
calc μ ((λ x, x * y) ⁻¹' s) = 0 ↔ μ ((λ x, y⁻¹ * x) ⁻¹' s⁻¹)⁻¹ = 0 :
  by simp_rw [← inv_preimage, preimage_preimage, mul_inv_rev, inv_inv]
... ↔ μ s = 0 : by simp only [measure_inv_null μ, measure_preimage_mul]

@[to_additive]
lemma measure_mul_right_ne_zero
  (h2s : μ s ≠ 0) (y : G) : μ ((λ x, x * y) ⁻¹' s) ≠ 0 :=
(not_iff_not_of_iff (measure_mul_right_null μ y)).mpr h2s

@[to_additive]
lemma absolutely_continuous_map_mul_right (g : G) : μ ≪ map (* g) μ :=
begin
  refine absolutely_continuous.mk (λ s hs, _),
  rw [map_apply (measurable_mul_const g) hs, measure_mul_right_null], exact id
end

@[to_additive]
lemma absolutely_continuous_map_div_left (g : G) : μ ≪ map (λ h, g / h) μ :=
begin
  simp_rw [div_eq_mul_inv],
  rw [← map_map (measurable_const_mul g) measurable_inv],
  conv_lhs { rw [← map_mul_left_eq_self μ g] },
  exact (absolutely_continuous_inv μ).map (measurable_const_mul g)
end

/-- This is the computation performed in the proof of [Halmos, §60 Th. A]. -/
@[to_additive "This is the computation performed in the proof of [Halmos, §60 Th. A]."]
lemma measure_mul_lintegral_eq
  [is_mul_left_invariant ν] (sm : measurable_set s) (f : G → ℝ≥0∞) (hf : measurable f) :
  μ s * ∫⁻ y, f y ∂ν = ∫⁻ x, ν ((λ z, z * x) ⁻¹' s) * f (x⁻¹) ∂μ :=
begin
  rw [← set_lintegral_one, ← lintegral_indicator _ sm,
    ← lintegral_lintegral_mul (measurable_const.indicator sm).ae_measurable hf.ae_measurable,
    ← lintegral_lintegral_mul_inv μ ν],
  swap, { exact (((measurable_const.indicator sm).comp measurable_fst).mul
      (hf.comp measurable_snd)).ae_measurable },
  have ms : ∀ x : G, measurable (λ y, ((λ z, z * x) ⁻¹' s).indicator (λ z, (1 : ℝ≥0∞)) y) :=
  λ x, measurable_const.indicator (measurable_mul_const _ sm),
  have : ∀ x y, s.indicator (λ (z : G), (1 : ℝ≥0∞)) (y * x) =
    ((λ z, z * x) ⁻¹' s).indicator (λ (b : G), 1) y,
  { intros x y, symmetry, convert indicator_comp_right (λ y, y * x), ext1 z, refl },
  simp_rw [this, lintegral_mul_const _ (ms _), lintegral_indicator _ (measurable_mul_const _ sm),
    set_lintegral_one],
end

/-- Any two nonzero left-invariant measures are absolutely continuous w.r.t. each other. -/
@[to_additive /-" Any two nonzero left-invariant measures are absolutely continuous w.r.t. each
other. "-/]
lemma absolutely_continuous_of_is_mul_left_invariant [is_mul_left_invariant ν] (hν : ν ≠ 0) :
  μ ≪ ν :=
begin
  refine absolutely_continuous.mk (λ s sm hνs, _),
  have h1 := measure_mul_lintegral_eq μ ν sm 1 measurable_one,
  simp_rw [pi.one_apply, lintegral_one, mul_one, (measure_mul_right_null ν _).mpr hνs,
    lintegral_zero, mul_eq_zero, measure_univ_eq_zero.not.mpr hν, or_false] at h1,
  exact h1
end

@[to_additive]
lemma ae_measure_preimage_mul_right_lt_top [is_mul_left_invariant ν]
  (sm : measurable_set s) (hμs : μ s ≠ ∞) :
  ∀ᵐ x ∂μ, ν ((λ y, y * x) ⁻¹' s) < ∞ :=
begin
  refine ae_of_forall_measure_lt_top_ae_restrict' ν.inv _ _,
  intros A hA h2A h3A,
  simp only [ν.inv_apply] at h3A,
  apply ae_lt_top (measurable_measure_mul_right ν sm),
  have h1 := measure_mul_lintegral_eq μ ν sm (A⁻¹.indicator 1) (measurable_one.indicator hA.inv),
  rw [lintegral_indicator _ hA.inv] at h1,
  simp_rw [pi.one_apply, set_lintegral_one, ← image_inv, indicator_image inv_injective, image_inv,
    ← indicator_mul_right _ (λ x, ν ((λ y, y * x) ⁻¹' s)), function.comp, pi.one_apply,
    mul_one] at h1,
  rw [← lintegral_indicator _ hA, ← h1],
  exact ennreal.mul_ne_top hμs h3A.ne,
end

@[to_additive]
lemma ae_measure_preimage_mul_right_lt_top_of_ne_zero [is_mul_left_invariant ν]
  (sm : measurable_set s) (h2s : ν s ≠ 0) (h3s : ν s ≠ ∞) :
  ∀ᵐ x ∂μ, ν ((λ y, y * x) ⁻¹' s) < ∞ :=
begin
  refine (ae_measure_preimage_mul_right_lt_top ν ν sm h3s).filter_mono _,
  refine (absolutely_continuous_of_is_mul_left_invariant μ ν _).ae_le,
  refine mt _ h2s,
  intro hν,
  rw [hν, measure.coe_zero, pi.zero_apply]
end

/-- A technical lemma relating two different measures. This is basically [Halmos, §60 Th. A].
  Note that if `f` is the characteristic function of a measurable set `t` this states that
  `μ t = c * μ s` for a constant `c` that does not depend on `μ`.

  Note: There is a gap in the last step of the proof in [Halmos].
  In the last line, the equality `g(x⁻¹)ν(sx⁻¹) = f(x)` holds if we can prove that
  `0 < ν(sx⁻¹) < ∞`. The first inequality follows from §59, Th. D, but the second inequality is
  not justified. We prove this inequality for almost all `x` in
  `measure_theory.ae_measure_preimage_mul_right_lt_top_of_ne_zero`. -/
@[to_additive "A technical lemma relating two different measures. This is basically
[Halmos, §60 Th. A]. Note that if `f` is the characteristic function of a measurable set `t` this
states that `μ t = c * μ s` for a constant `c` that does not depend on `μ`.

Note: There is a gap in the last step of the proof in [Halmos]. In the last line, the equality
`g(-x) + ν(s - x) = f(x)` holds if we can prove that `0 < ν(s - x) < ∞`. The first inequality
follows from §59, Th. D, but the second inequality is not justified. We prove this inequality for
almost all `x` in `measure_theory.ae_measure_preimage_add_right_lt_top_of_ne_zero`."]
lemma measure_lintegral_div_measure [is_mul_left_invariant ν]
  (sm : measurable_set s) (h2s : ν s ≠ 0) (h3s : ν s ≠ ∞)
  (f : G → ℝ≥0∞) (hf : measurable f) :
  μ s * ∫⁻ y, f y⁻¹ / ν ((λ x, x * y⁻¹) ⁻¹' s) ∂ν = ∫⁻ x, f x ∂μ :=
begin
  set g := λ y, f y⁻¹ / ν ((λ x, x * y⁻¹) ⁻¹' s),
  have hg : measurable g := (hf.comp measurable_inv).div
    ((measurable_measure_mul_right ν sm).comp measurable_inv),
  simp_rw [measure_mul_lintegral_eq μ ν sm g hg, g, inv_inv],
  refine lintegral_congr_ae _,
  refine (ae_measure_preimage_mul_right_lt_top_of_ne_zero μ ν sm h2s h3s).mono (λ x hx , _),
  simp_rw [ennreal.mul_div_cancel' (measure_mul_right_ne_zero ν h2s _) hx.ne]
end

@[to_additive]
lemma measure_mul_measure_eq [is_mul_left_invariant ν] {s t : set G}
  (hs : measurable_set s) (ht : measurable_set t) (h2s : ν s ≠ 0) (h3s : ν s ≠ ∞) :
    μ s * ν t = ν s * μ t :=
begin
  have h1 := measure_lintegral_div_measure ν ν hs h2s h3s (t.indicator (λ x, 1))
    (measurable_const.indicator ht),
  have h2 := measure_lintegral_div_measure μ ν hs h2s h3s (t.indicator (λ x, 1))
    (measurable_const.indicator ht),
  rw [lintegral_indicator _ ht, set_lintegral_one] at h1 h2,
  rw [← h1, mul_left_comm, h2],
end

/-- Left invariant Borel measures on a measurable group are unique (up to a scalar). -/
@[to_additive /-" Left invariant Borel measures on an additive measurable group are unique
  (up to a scalar). "-/]
lemma measure_eq_div_smul [is_mul_left_invariant ν]
  (hs : measurable_set s) (h2s : ν s ≠ 0) (h3s : ν s ≠ ∞) : μ = (μ s / ν s) • ν :=
begin
  ext1 t ht,
  rw [smul_apply, smul_eq_mul, mul_comm, ← mul_div_assoc, mul_comm,
    measure_mul_measure_eq μ ν hs ht h2s h3s, mul_div_assoc, ennreal.mul_div_cancel' h2s h3s]
end

end left_invariant

section right_invariant

@[to_additive measure_preserving_prod_add_right]
lemma measure_preserving_prod_mul_right [is_mul_right_invariant ν] :
  measure_preserving (λ z : G × G, (z.1, z.2 * z.1)) (μ.prod ν) (μ.prod ν) :=
(measure_preserving.id μ).skew_product (by exact measurable_snd.mul measurable_fst) $
  filter.eventually_of_forall $ map_mul_right_eq_self ν

/-- The map `(x, y) ↦ (y, xy)` sends the measure `μ × ν` to `ν × μ`. -/
@[to_additive measure_preserving_prod_add_swap_right
  /-" The map `(x, y) ↦ (y, x + y)` sends the measure `μ × ν` to `ν × μ`. "-/]
lemma measure_preserving_prod_mul_swap_right [is_mul_right_invariant μ] :
  measure_preserving (λ z : G × G, (z.2, z.1 * z.2)) (μ.prod ν) (ν.prod μ) :=
(measure_preserving_prod_mul_right ν μ).comp measure_preserving_swap

/-- The map `(x, y) ↦ (xy, y)` preserves the measure `μ × ν`. -/
@[to_additive measure_preserving_add_prod
  /-" The map `(x, y) ↦ (x + y, y)` preserves the measure `μ × ν`. "-/]
lemma measure_preserving_mul_prod [is_mul_right_invariant μ] :
  measure_preserving (λ z : G × G, (z.1 * z.2, z.2)) (μ.prod ν) (μ.prod ν) :=
measure_preserving_swap.comp $ by apply measure_preserving_prod_mul_swap_right μ ν

variables [has_measurable_inv G]

/-- The map `(x, y) ↦ (x, y / x)` is measure-preserving. -/
@[to_additive measure_preserving_prod_sub
  "The map `(x, y) ↦ (x, y - x)` is measure-preserving."]

/-- The map `(x, y) ↦ (y, x / y)` sends `μ × ν` to `ν × μ`. -/
@[to_additive measure_preserving_prod_sub_swap
  "The map `(x, y) ↦ (y, x - y)` sends `μ × ν` to `ν × μ`."]
lemma measure_preserving_prod_div_swap [is_mul_right_invariant μ] :
  measure_preserving (λ z : G × G, (z.2, z.1 / z.2)) (μ.prod ν) (ν.prod μ) :=
(measure_preserving_prod_div ν μ).comp measure_preserving_swap

/-- The map `(x, y) ↦ (x / y, y)` preserves the measure `μ × ν`. -/
@[to_additive measure_preserving_sub_prod
  /-" The map `(x, y) ↦ (x - y, y)` preserves the measure `μ × ν`. "-/]
lemma measure_preserving_div_prod [is_mul_right_invariant μ] :
  measure_preserving (λ z : G × G, (z.1 / z.2, z.2)) (μ.prod ν) (μ.prod ν) :=
measure_preserving_swap.comp $ by apply measure_preserving_prod_div_swap μ ν

/-- The map `(x, y) ↦ (xy, x⁻¹)` is measure-preserving. -/
@[to_additive measure_preserving_add_prod_neg_right
  "The map `(x, y) ↦ (x + y, - x)` is measure-preserving."]
lemma measure_preserving_mul_prod_inv_right [is_mul_right_invariant μ] [is_mul_right_invariant ν] :
  measure_preserving (λ z : G × G, (z.1 * z.2, z.1⁻¹)) (μ.prod ν) (μ.prod ν) :=
begin
  convert (measure_preserving_prod_div_swap ν μ).comp
    (measure_preserving_prod_mul_swap_right μ ν),
  ext1 ⟨x, y⟩,
  simp_rw [function.comp_apply, div_mul_eq_div_div_swap, div_self', one_div]
end

end right_invariant

section quasi_measure_preserving

variables [has_measurable_inv G]

@[to_additive]
lemma quasi_measure_preserving_inv_of_right_invariant [is_mul_right_invariant μ] :
  quasi_measure_preserving (has_inv.inv : G → G) μ μ :=
begin
  rw [← μ.inv_inv],
  exact (quasi_measure_preserving_inv μ.inv).mono
    (inv_absolutely_continuous μ.inv) (absolutely_continuous_inv μ.inv)
end

@[to_additive]
lemma quasi_measure_preserving_div_left [is_mul_left_invariant μ] (g : G) :
  quasi_measure_preserving (λ h : G, g / h) μ μ :=
begin
  simp_rw [div_eq_mul_inv],
  exact (measure_preserving_mul_left μ g).quasi_measure_preserving.comp
    (quasi_measure_preserving_inv μ)
end

@[to_additive]
lemma quasi_measure_preserving_div_left_of_right_invariant [is_mul_right_invariant μ] (g : G) :
  quasi_measure_preserving (λ h : G, g / h) μ μ :=
begin
  rw [← μ.inv_inv],
  exact (quasi_measure_preserving_div_left μ.inv g).mono
    (inv_absolutely_continuous μ.inv) (absolutely_continuous_inv μ.inv)
end

@[to_additive]
lemma quasi_measure_preserving_div_of_right_invariant [is_mul_right_invariant μ] :
  quasi_measure_preserving (λ (p : G × G), p.1 / p.2) (μ.prod ν) μ :=
begin
  refine quasi_measure_preserving.prod_of_left measurable_div (eventually_of_forall $ λ y, _),
  exact (measure_preserving_div_right μ y).quasi_measure_preserving
end

@[to_additive]
lemma quasi_measure_preserving_div [is_mul_left_invariant μ] :
  quasi_measure_preserving (λ (p : G × G), p.1 / p.2) (μ.prod ν) μ :=
(quasi_measure_preserving_div_of_right_invariant μ.inv ν).mono
  ((absolutely_continuous_inv μ).prod absolutely_continuous.rfl)
  (inv_absolutely_continuous μ)

/-- A *left*-invariant measure is quasi-preserved by *right*-multiplication.
This should not be confused with `(measure_preserving_mul_right μ g).quasi_measure_preserving`. -/
@[to_additive /-"A *left*-invariant measure is quasi-preserved by *right*-addition.
This should not be confused with `(measure_preserving_add_right μ g).quasi_measure_preserving`. "-/]
lemma quasi_measure_preserving_mul_right [is_mul_left_invariant μ] (g : G) :
  quasi_measure_preserving (λ h : G, h * g) μ μ :=
begin
  refine ⟨measurable_mul_const g, absolutely_continuous.mk $ λ s hs, _⟩,
  rw [map_apply (measurable_mul_const g) hs, measure_mul_right_null], exact id,
end

/-- A *right*-invariant measure is quasi-preserved by *left*-multiplication.
This should not be confused with `(measure_preserving_mul_left μ g).quasi_measure_preserving`. -/
@[to_additive /-"A *right*-invariant measure is quasi-preserved by *left*-addition.
This should not be confused with `(measure_preserving_add_left μ g).quasi_measure_preserving`. "-/]
lemma quasi_measure_preserving_mul_left [is_mul_right_invariant μ] (g : G) :
  quasi_measure_preserving (λ h : G, g * h) μ μ :=
begin
  have := (quasi_measure_preserving_mul_right μ.inv g⁻¹).mono
    (inv_absolutely_continuous μ.inv) (absolutely_continuous_inv μ.inv),
  rw [μ.inv_inv] at this,
  have := (quasi_measure_preserving_inv_of_right_invariant μ).comp
    (this.comp (quasi_measure_preserving_inv_of_right_invariant μ)),
  simp_rw [function.comp, mul_inv_rev, inv_inv] at this,
  exact this
end

end quasi_measure_preserving

end measure_theory


-- import utils
variable b : bool
#check b.rec_on
#check nat.add
inductive fin' : ℕ → Type
| zero : fin' 0
| succ : Π {n : ℕ}, fin' n → fin' (n +1)

-- example : ∀ (n : ℕ) (i : fin n), i.cast_succ = (↑i : fin (n +1)) :=

-- def foo (n : ℕ) : fin n → bool
-- | 0      := false
-- | (i +1) := true

-- def fin_cycle : Π n : ℕ, fin n → fin n
-- | (n +1) ⟨0,    _⟩ := fin.last n
-- | (n +1) ⟨i +1, p⟩ := let fin_i : fin n := ⟨i, nat.pred_le_pred p⟩
--                        in {! !} --fin.succ (fin.succ (fin_double n fin_i))

def fin_is_even : Π n : ℕ, fin n → bool
| 0      i                        := i.elim0
| (n +1) ⟨0,    _⟩                := true
| (n +1) ⟨i_val +1, succ_i_is_lt⟩ :=
     let i : fin n := ⟨i_val, nat.pred_le_pred succ_i_is_lt⟩
      in bnot (fin_is_even n i)

@[pattern]
def fin.zero {n : ℕ} : fin (n +1) := ⟨0, nat.zero_lt_succ n⟩

attribute [pattern] fin.succ

def fin_is_even' : Π n : ℕ, fin n → bool
| 0      i            := i.elim0
| (n +1) fin.zero     := true
| (n +1) (fin.succ i) := bnot (fin_is_even' n i)

I still have another question regarding `fin`.  Sometimes, I want to use pattern matching on `i : fin n` in the same way as one will do in `ℕ` but since the inductive part is in `i.val` so I need to do as follows:

```lean
def fin_is_even : Π n : ℕ, fin n → bool
| 0      i                        := i.elim0
| (n +1) ⟨0,    _⟩                := true
| (n +1) ⟨i_val +1, succ_i_is_lt⟩ :=hwere
      in bnot (fin_is_even n i)
```

Ok, it works but in practice, I find it quite annoying to write `let i : fin n := ⟨i_val, nat.pred_le_pred succ_i_is_lt⟩` every time whereas `i` should be obtained directly from something like `fin.succ i`. Therefore, I try to use `@[pattern]` to help me with this as follows:

```lean
@[pattern]
def fin.zero {n : ℕ} : fin (n +1) := ⟨0, nat.zero_lt_succ n⟩

attribute [pattern] fin.succ

def fin_is_even' : Π n : ℕ, fin n → bool
| 0      i            := i.elim0
| (n +1) fin.zero     := true
| (n +1) (fin.succ i) := bnot (fin_is_even n i)
```

However I get an error, what is wrong with my code?  Did I misunderstand anything about `@[pattern]`?

section
@[pattern]
def fin_zero {n : ℕ} : fin (n +1) := ⟨0, nat.zero_lt_succ n⟩

@[pattern]
def fin_succ {n : ℕ} : fin n → fin n.succ
| ⟨a, h⟩ := ⟨a.succ, nat.succ_lt_succ h⟩

def fin_is_zero (n : ℕ) : fin n → bool
| fin_zero     := false
| (fin_succ i) := true
end


/-
Copyright (c) 2020 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import linear_algebra.affine_space.affine_map
import tactic.field_simp

/-!
# Slope of a function

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

In this file we define the slope of a function `f : k → PE` taking values in an affine space over
`k` and prove some basic theorems about `slope`. The `slope` function naturally appears in the Mean
Value Theorem, and in the proof of the fact that a function with nonnegative second derivative on an
interval is convex on this interval.

## Tags

affine space, slope
-/

open affine_map
variables {k E PE : Type*} [field k] [add_comm_group E] [module k E] [add_torsor E PE]

include E

/-- `slope f a b = (b - a)⁻¹ • (f b -ᵥ f a)` is the slope of a function `f` on the interval
`[a, b]`. Note that `slope f a a = 0`, not the derivative of `f` at `a`. -/
def slope (f : k → PE) (a b : k) : E := (b - a)⁻¹ • (f b -ᵥ f a)

lemma slope_fun_def (f : k → PE) : slope f = λ a b, (b - a)⁻¹ • (f b -ᵥ f a) := rfl

omit E

lemma slope_def_field (f : k → k) (a b : k) : slope f a b = (f b - f a) / (b - a) :=
(div_eq_inv_mul _ _).symm

lemma slope_fun_def_field (f : k → k) (a : k) : slope f a = λ b, (f b - f a) / (b - a) :=
(div_eq_inv_mul _ _).symm

@[simp] lemma slope_same (f : k → PE) (a : k) : (slope f a a : E) = 0 :=
by rw [slope, sub_self, inv_zero, zero_smul]

include E

lemma slope_def_module (f : k → E) (a b : k) : slope f a b = (b - a)⁻¹ • (f b - f a) := rfl

@[simp] lemma sub_smul_slope (f : k → PE) (a b : k) : (b - a) • slope f a b = f b -ᵥ f a :=
begin
  rcases eq_or_ne a b with rfl | hne,
  { rw [sub_self, zero_smul, vsub_self] },
  { rw [slope, smul_inv_smul₀ (sub_ne_zero.2 hne.symm)] }
end

lemma sub_smul_slope_vadd (f : k → PE) (a b : k) : (b - a) • slope f a b +ᵥ f a = f b :=
by rw [sub_smul_slope, vsub_vadd]

@[simp] lemma slope_vadd_const (f : k → E) (c : PE) :
  slope (λ x, f x +ᵥ c) = slope f :=
begin
  ext a b,
  simp only [slope, vadd_vsub_vadd_cancel_right, vsub_eq_sub]
end

@[simp] lemma slope_sub_smul (f : k → E) {a b : k} (h : a ≠ b):
  slope (λ x, (x - a) • f x) a b = f b :=
by simp [slope, inv_smul_smul₀ (sub_ne_zero.2 h.symm)]

lemma eq_of_slope_eq_zero {f : k → PE} {a b : k} (h : slope f a b = (0:E)) : f a = f b :=
by rw [← sub_smul_slope_vadd f a b, h, smul_zero, zero_vadd]

lemma affine_map.slope_comp {F PF : Type*} [add_comm_group F] [module k F] [add_torsor F PF]
  (f : PE →ᵃ[k] PF) (g : k → PE) (a b : k) :
  slope (f ∘ g) a b = f.linear (slope g a b) :=
by simp only [slope, (∘), f.linear.map_smul, f.linear_map_vsub]

lemma linear_map.slope_comp {F : Type*} [add_comm_group F] [module k F]
  (f : E →ₗ[k] F) (g : k → E) (a b : k) :
  slope (f ∘ g) a b = f (slope g a b) :=
f.to_affine_map.slope_comp g a b

lemma slope_comm (f : k → PE) (a b : k) : slope f a b = slope f b a :=
by rw [slope, slope, ← neg_vsub_eq_vsub_rev, smul_neg, ← neg_smul, neg_inv, neg_sub]

/-- `slope f a c` is a linear combination of `slope f a b` and `slope f b c`. This version
explicitly provides coefficients. If `a ≠ c`, then the sum of the coefficients is `1`, so it is
actually an affine combination, see `line_map_slope_slope_sub_div_sub`. -/
lemma sub_div_sub_smul_slope_add_sub_div_sub_smul_slope (f : k → PE) (a b c : k) :
  ((b - a) / (c - a)) • slope f a b + ((c - b) / (c - a)) • slope f b c = slope f a c :=
begin
  by_cases hab : a = b,
  { subst hab,
    rw [sub_self, zero_div, zero_smul, zero_add],
    by_cases hac : a = c,
    { simp [hac] },
    { rw [div_self (sub_ne_zero.2 $ ne.symm hac), one_smul] } },
  by_cases hbc : b = c, { subst hbc, simp [sub_ne_zero.2 (ne.symm hab)] },
  rw [add_comm],
  simp_rw [slope, div_eq_inv_mul, mul_smul, ← smul_add,
    smul_inv_smul₀ (sub_ne_zero.2 $ ne.symm hab), smul_inv_smul₀ (sub_ne_zero.2 $ ne.symm hbc),
    vsub_add_vsub_cancel],
end

/-- `slope f a c` is an affine combination of `slope f a b` and `slope f b c`. This version uses
`line_map` to express this property. -/
lemma line_map_slope_slope_sub_div_sub (f : k → PE) (a b c : k) (h : a ≠ c) :
  line_map (slope f a b) (slope f b c) ((c - b) / (c - a)) = slope f a c :=
by  field_simp [sub_ne_zero.2 h.symm, ← sub_div_sub_smul_slope_add_sub_div_sub_smul_slope f a b c,
  line_map_apply_module]

/-- `slope f a b` is an affine combination of `slope f a (line_map a b r)` and
`slope f (line_map a b r) b`. We use `line_map` to express this property. -/
lemma line_map_slope_line_map_slope_line_map (f : k → PE) (a b r : k) :
  line_map (slope f (line_map a b r) b) (slope f a (line_map a b r)) r = slope f a b :=
begin
  obtain (rfl|hab) : a = b ∨ a ≠ b := classical.em _, { simp },
  rw [slope_comm _ a, slope_comm _ a, slope_comm _ _ b],
  convert line_map_slope_slope_sub_div_sub f b (line_map a b r) a hab.symm using 2,
  rw [line_map_apply_ring, eq_div_iff (sub_ne_zero.2 hab), sub_mul, one_mul, mul_sub, ← sub_sub,
    sub_sub_cancel]
end


import tactic.interactive

namespace xnat

open nat

definition le1 : nat → nat → Prop
| zero zero := true
| (succ m) zero := false
| zero (succ p) := true
| (succ m) (succ p) := le1 m p

lemma zero_le1 (b : ℕ) : le1 0 b :=
begin
  cases b,
    trivial,
    trivial
end

inductive le2 : ℕ → ℕ → Prop
| refl (a : ℕ) : le2 a a
| succ (a b : ℕ) : le2 a b → le2 a (succ b)

lemma zero_le2 (b : ℕ) : le2 0 b :=
begin
  induction b with c hc,
    exact le2.refl 0,
    exact le2.succ 0 c hc
end

def le3 (a b : ℕ) : Prop := ∃ c, a + c = b

lemma zero_le3 (b : ℕ) : le3 0 b :=
begin
  exact ⟨b, nat.zero_add b⟩,
end

lemma succ_le_succ1 (a b : ℕ) : le1 a b → le1 (a + 1) (b + 1) := id

lemma succ_le_succ3 (a b : ℕ) : le3 a b → le3 (a + 1) (b + 1) :=
begin
  intro h,
  cases h with c hc,
  use c,
  rw ←hc,
  simp, -- probably wouldn't work for xnat
end

lemma h23 (a b : ℕ) : le2 a b → le3 a b :=
begin
  induction b with d hd,
  intro h,
  cases h with _ _ b h2,
    use 0,
  intro h,
  cases h with _ _ _ h,
    use 0,
  cases hd h with w hw,
  use succ w,
  rw ←hw,
end

lemma h31 (a b : ℕ) : le3 a b → le1 a b :=
begin
  revert a,
  induction b with c hc,
  { intros a h,
    cases h with c h,
    cases a, trivial,
    exfalso,
    revert h,
    show succ a + c ≠ 0,
    suffices : succ a + c = succ (a + c),
      rw this, exact dec_trivial,
    show (a + 1) + c = (a + c) + 1,
    simp,
  },
  intros a h,
  cases a, trivial,
  show le1 a c,
  apply hc,
  cases h with d hd,
  use d,
  apply succ_inj, -- wouldn't work for xnat
  rw ←hd,
  show (a + d) + 1 = (a + 1) + d,
  simp, -- woudn't work for xnat
end

theorem h21 (a b : ℕ) : le2 a b → le1 a b :=
begin
  intro h,
  apply h31 a b,
  exact h23 a b h
end

theorem h13 (a b : ℕ) : le1 a b → le3 a b :=
begin
  revert b,
  induction a with a ha,
    intros b hb, exact zero_le3 _,
  intros b hb,
  cases b with b,
    cases hb,
  cases ha b hb with c hc,
  use c,
  rw ←hc,
  show (a + 1) + c = (a + c) + 1,
  simp,
end

theorem h32 (a b : ℕ) : le3 a b → le2 a b :=
begin
  induction b with b hb,
  { intro h,
    cases a with a,
      exact le2.refl _,
    exfalso,
    cases h with c hc,
    revert hc,
    show succ a + c ≠ 0,
    suffices : succ a + c = succ (a + c),
      rw this, exact dec_trivial,
    show (a + 1) + c = (a + c) + 1,
    simp,
  },
  intro h,
  cases h with c hc,
  cases c,
    rw ←hc, exact le2.refl _,
  apply le2.succ,
  apply hb,
  use c,
  apply succ_inj,
  rw ←hc,
end

lemma h12 (a b : ℕ) : le1 a b → le2 a b :=
begin
  intro h,
  apply h32,
  apply h13,
  assumption,
end

lemma e12 (a b : ℕ) : le1 a b ↔ le2 a b :=
begin
  split,
    exact h12 a b,
    exact h21 a b
end

lemma e13 (a b : ℕ) : le1 a b ↔ le3 a b :=
begin
  split,
    exact h13 a b,
    exact h31 a b
end

lemma succ_le_succ2 (a b : ℕ) : le2 a b → le2 (a + 1) (b + 1) :=
begin
  rw ←e12,
  rw ←e12,
  exact succ_le_succ1 a b,
end

theorem inequality_A11 (a b t : nat) : le1 a b → le1 (a + t) (b + t) :=
begin
  intro h,
  induction t with e he,
    exact h,
  show le1 (succ (a + e)) (succ (b + e)),
  exact he
end

theorem inequality_A13 (a b t : nat) : le3 a b → le3 (a + t) (b + t) :=
begin
  intro h,
  cases h with c hc,
  use c,
  rw ←hc,
  simp,
end

theorem inequality_A23 (a b c : nat) : le3 a b → le3 b c → le3 a c :=
begin
  intros hab hbc,
  cases hab with d hd,
  cases hbc with e he,
  use d + e,
  rw ←he,
  rw ←hd,
  simp,
end

theorem inequality_A3a3 (a b : ℕ) : le3 a b ∨ le3 b a :=
begin
  revert a,
  induction b with b hb,
    intro a, right, exact zero_le3 a,
  intro a,
  cases a, left, exact zero_le3 _,
  cases hb a,
  { left,
    cases h with c hc,
    use c,
    rw ←hc,
    show (a + 1) + c = (a + c) + 1,
    simp },
  { right,
    cases h with c hc,
    use c,
    rw ←hc,
    show (b + 1) + c = (b + c) + 1,
    simp,
  },
end

lemma zero_of_add_eq (a b : ℕ) : a + b = a → b = 0 :=
begin
  intro h,
  induction a with a ha,
    rw zero_add at h, assumption,
  apply ha,
  apply succ_inj,
  rw ←h,
  simp,
end

theorem inequality_A3b3 (a b : ℕ) : le3 a b ∧ le3 b a → a = b :=
begin
  intro h,
  cases h with h1 h2,
  cases h1 with c hc,
  cases h2 with d hd,
  rw ←hc at hd,
  rw add_assoc at hd,
  have hcd : c + d = 0 := zero_of_add_eq _ _ hd,
  cases c,
    exact hc,
  exfalso,
  suffices : succ c + d = succ (c + d),
    rw this at hcd,
    cases hcd,
  simp,
end

theorem A4_thing (a b : ℕ) : a ≠ 0 → b ≠ 0 → a * b ≠ 0 :=
begin
  intro ha,
  intro hb,
  cases a,
    exfalso, apply ha, refl,
  cases b,
    exfalso, apply hb, refl,
  show succ _ ≠ 0,
  exact dec_trivial
end

end xnat


import Mathlib.Algebra.Ring.Basic
import Mathlib.Data.Rat.Basic
import Mathlib.Tactic.FieldSimp
import Mathlib.Tactic.Ring

/-!
## `field_simp` tests.
-/

/-
Check that `field_simp` works for units of a ring.
-/

variable {R : Type _} [CommRing R] (a b c d e f g : R) (u₁ u₂ : Rˣ)

/--
Check that `divp_add_divp_same` takes priority over `divp_add_divp`.
-/
example : a /ₚ u₁ + b /ₚ u₁ = (a + b) /ₚ u₁ :=
by field_simp

/--
Check that `divp_sub_divp_same` takes priority over `divp_sub_divp`.
-/
example : a /ₚ u₁ - b /ₚ u₁ = (a - b) /ₚ u₁ :=
by field_simp

/-
Combining `eq_divp_iff_mul_eq` and `divp_eq_iff_mul_eq`.

This example is currently commented out because it is weirdly slow.
See https://github.com/leanprover/lean4/issues/2055.

It works with `set_option maxHeartbeats 300000`.
-/
--example : a /ₚ u₁ = b /ₚ u₂ ↔ a * u₂ = b * u₁ :=
--by field_simp

/--
Making sure inverses of units are rewritten properly.
-/
example : ↑u₁⁻¹ = 1 /ₚ u₁ := by field_simp

/--
Checking arithmetic expressions.
-/
example : (f - (e + c * -(a /ₚ u₁) * b + d) - g) =
  (f * u₁ - (e * u₁ + c * (-a) * b + d * u₁) - g * u₁) /ₚ u₁ :=
by field_simp

/--
Division of units.
-/
example : a /ₚ (u₁ / u₂) = a * u₂ /ₚ u₁ :=
by field_simp

example : a /ₚ u₁ /ₚ u₂ = a /ₚ (u₂ * u₁) :=
by field_simp

/--
Test that the discharger can clear nontrivial denominators in ℚ.
-/
example (x : ℚ) (h₀ : x ≠ 0) :
    (4 / x)⁻¹ * ((3 * x^3) / x)^2 * ((1 / (2 * x))⁻¹)^3 = 18 * x^8 := by
  field_simp
  ring