/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.equiv.encodable.basic
import Mathlib.algebra.euclidean_domain
import Mathlib.data.nat.gcd
import Mathlib.data.int.cast
import Mathlib.PostPort

universes l u

namespace Mathlib

/-!
# Basics for the Rational Numbers

## Summary

We define a rational number `q` as a structure `{ num, denom, pos, cop }`, where
- `num` is the numerator of `q`,
- `denom` is the denominator of `q`,
- `pos` is a proof that `denom > 0`, and
- `cop` is a proof `num` and `denom` are coprime.

We then define the expected (discrete) field structure on `ℚ` and prove basic lemmas about it.
Moreoever, we provide the expected casts from `ℕ` and `ℤ` into `ℚ`, i.e. `(↑n : ℚ) = n / 1`.

## Main Definitions

- `rat` is the structure encoding `ℚ`.
- `rat.mk n d` constructs a rational number `q = n / d` from `n d : ℤ`.

## Notations

- `/.` is infix notation for `rat.mk`.

## Tags

rat, rationals, field, ℚ, numerator, denominator, num, denom
-/

/-- `rat`, or `ℚ`, is the type of rational numbers. It is defined
  as the set of pairs ⟨n, d⟩ of integers such that `d` is positive and `n` and
  `d` are coprime. This representation is preferred to the quotient
  because without periodic reduction, the numerator and denominator can grow
  exponentially (for example, adding 1/2 to itself repeatedly). -/
structure rat
  mk' ::
where (num : ℤ) (denom : ℕ) (pos : 0 < denom) (cop : nat.coprime (int.nat_abs num) denom)

notation:1024 "ℚ" => Mathlib.rat

namespace rat

protected def repr : ℚ → string :=
  sorry

protected instance has_repr : has_repr ℚ :=
  has_repr.mk rat.repr

protected instance has_to_string : has_to_string ℚ :=
  has_to_string.mk rat.repr

protected instance encodable : encodable ℚ :=
  encodable.of_equiv (sigma fun (n : ℤ) => Subtype fun (d : ℕ) => 0 < d ∧ nat.coprime (int.nat_abs n) d)
    (equiv.mk (fun (_x : ℚ) => sorry)
      (fun (_x : sigma fun (n : ℤ) => Subtype fun (d : ℕ) => 0 < d ∧ nat.coprime (int.nat_abs n) d) => sorry) sorry sorry)

/-- Embed an integer as a rational number -/
def of_int (n : ℤ) : ℚ :=
  mk' n 1 nat.one_pos sorry

protected instance has_zero : HasZero ℚ :=
  { zero := of_int 0 }

protected instance has_one : HasOne ℚ :=
  { one := of_int 1 }

protected instance inhabited : Inhabited ℚ :=
  { default := 0 }

/-- Form the quotient `n / d` where `n:ℤ` and `d:ℕ+` (not necessarily coprime) -/
def mk_pnat (n : ℤ) : ℕ+ → ℚ :=
  sorry

/-- Form the quotient `n / d` where `n:ℤ` and `d:ℕ`. In the case `d = 0`, we
  define `n / 0 = 0` by convention. -/
def mk_nat (n : ℤ) (d : ℕ) : ℚ :=
  dite (d = 0) (fun (d0 : d = 0) => 0) fun (d0 : ¬d = 0) => mk_pnat n { val := d, property := nat.pos_of_ne_zero d0 }

/-- Form the quotient `n / d` where `n d : ℤ`. -/
def mk : ℤ → ℤ → ℚ :=
  sorry

theorem mk_pnat_eq (n : ℤ) (d : ℕ) (h : 0 < d) : mk_pnat n { val := d, property := h } = mk n ↑d := sorry

theorem mk_nat_eq (n : ℤ) (d : ℕ) : mk_nat n d = mk n ↑d :=
  rfl

@[simp] theorem mk_zero (n : ℤ) : mk n 0 = 0 :=
  rfl

@[simp] theorem zero_mk_pnat (n : ℕ+) : mk_pnat 0 n = 0 := sorry

@[simp] theorem zero_mk_nat (n : ℕ) : mk_nat 0 n = 0 := sorry

@[simp] theorem zero_mk (n : ℤ) : mk 0 n = 0 := sorry

@[simp] theorem mk_eq_zero {a : ℤ} {b : ℤ} (b0 : b ≠ 0) : mk a b = 0 ↔ a = 0 := sorry

theorem mk_eq {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hb : b ≠ 0) (hd : d ≠ 0) : mk a b = mk c d ↔ a * d = c * b := sorry

@[simp] theorem div_mk_div_cancel_left {a : ℤ} {b : ℤ} {c : ℤ} (c0 : c ≠ 0) : mk (a * c) (b * c) = mk a b := sorry

@[simp] theorem num_denom {a : ℚ} : mk (num a) ↑(denom a) = a := sorry

theorem num_denom' {n : ℤ} {d : ℕ} {h : 0 < d} {c : nat.coprime (int.nat_abs n) d} : mk' n d h c = mk n ↑d :=
  Eq.symm num_denom

theorem of_int_eq_mk (z : ℤ) : of_int z = mk z 1 :=
  num_denom'

def num_denom_cases_on {C : ℚ → Sort u} (a : ℚ) (H : (n : ℤ) → (d : ℕ) → 0 < d → nat.coprime (int.nat_abs n) d → C (mk n ↑d)) : C a :=
  sorry

def num_denom_cases_on' {C : ℚ → Sort u} (a : ℚ) (H : (n : ℤ) → (d : ℕ) → d ≠ 0 → C (mk n ↑d)) : C a :=
  num_denom_cases_on a fun (n : ℤ) (d : ℕ) (h : 0 < d) (c : nat.coprime (int.nat_abs n) d) => H n d sorry

theorem num_dvd (a : ℤ) {b : ℤ} (b0 : b ≠ 0) : num (mk a b) ∣ a := sorry

theorem denom_dvd (a : ℤ) (b : ℤ) : ↑(denom (mk a b)) ∣ b := sorry

protected def add : ℚ → ℚ → ℚ :=
  sorry

protected instance has_add : Add ℚ :=
  { add := rat.add }

theorem lift_binop_eq (f : ℚ → ℚ → ℚ) (f₁ : ℤ → ℤ → ℤ → ℤ → ℤ) (f₂ : ℤ → ℤ → ℤ → ℤ → ℤ) (fv : ∀ {n₁ : ℤ} {d₁ : ℕ} {h₁ : 0 < d₁} {c₁ : nat.coprime (int.nat_abs n₁) d₁} {n₂ : ℤ} {d₂ : ℕ} {h₂ : 0 < d₂}
  {c₂ : nat.coprime (int.nat_abs n₂) d₂},
  f (mk' n₁ d₁ h₁ c₁) (mk' n₂ d₂ h₂ c₂) = mk (f₁ n₁ (↑d₁) n₂ ↑d₂) (f₂ n₁ (↑d₁) n₂ ↑d₂)) (f0 : ∀ {n₁ d₁ n₂ d₂ : ℤ}, d₁ ≠ 0 → d₂ ≠ 0 → f₂ n₁ d₁ n₂ d₂ ≠ 0) (a : ℤ) (b : ℤ) (c : ℤ) (d : ℤ) (b0 : b ≠ 0) (d0 : d ≠ 0) (H : ∀ {n₁ d₁ n₂ d₂ : ℤ}, a * d₁ = n₁ * b → c * d₂ = n₂ * d → f₁ n₁ d₁ n₂ d₂ * f₂ a b c d = f₁ a b c d * f₂ n₁ d₁ n₂ d₂) : f (mk a b) (mk c d) = mk (f₁ a b c d) (f₂ a b c d) := sorry

@[simp] theorem add_def {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (b0 : b ≠ 0) (d0 : d ≠ 0) : mk a b + mk c d = mk (a * d + c * b) (b * d) := sorry

protected def neg : ℚ → ℚ :=
  sorry

protected instance has_neg : Neg ℚ :=
  { neg := rat.neg }

@[simp] theorem neg_def {a : ℤ} {b : ℤ} : -mk a b = mk (-a) b := sorry

protected def mul : ℚ → ℚ → ℚ :=
  sorry

protected instance has_mul : Mul ℚ :=
  { mul := rat.mul }

@[simp] theorem mul_def {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (b0 : b ≠ 0) (d0 : d ≠ 0) : mk a b * mk c d = mk (a * c) (b * d) := sorry

protected def inv : ℚ → ℚ :=
  sorry

protected instance has_inv : has_inv ℚ :=
  has_inv.mk rat.inv

@[simp] theorem inv_def {a : ℤ} {b : ℤ} : mk a b⁻¹ = mk b a := sorry

protected theorem add_zero (a : ℚ) : a + 0 = a := sorry

protected theorem zero_add (a : ℚ) : 0 + a = a := sorry

protected theorem add_comm (a : ℚ) (b : ℚ) : a + b = b + a := sorry

protected theorem add_assoc (a : ℚ) (b : ℚ) (c : ℚ) : a + b + c = a + (b + c) := sorry

protected theorem add_left_neg (a : ℚ) : -a + a = 0 := sorry

protected theorem mul_one (a : ℚ) : a * 1 = a := sorry

protected theorem one_mul (a : ℚ) : 1 * a = a := sorry

protected theorem mul_comm (a : ℚ) (b : ℚ) : a * b = b * a := sorry

protected theorem mul_assoc (a : ℚ) (b : ℚ) (c : ℚ) : a * b * c = a * (b * c) := sorry

protected theorem add_mul (a : ℚ) (b : ℚ) (c : ℚ) : (a + b) * c = a * c + b * c := sorry

protected theorem mul_add (a : ℚ) (b : ℚ) (c : ℚ) : a * (b + c) = a * b + a * c := sorry

protected theorem zero_ne_one : 0 ≠ 1 :=
  mt (fun (h : 0 = mk 1 1) => iff.mp (mk_eq_zero one_ne_zero) (Eq.symm h)) one_ne_zero

protected theorem mul_inv_cancel (a : ℚ) : a ≠ 0 → a * (a⁻¹) = 1 := sorry

protected theorem inv_mul_cancel (a : ℚ) (h : a ≠ 0) : a⁻¹ * a = 1 :=
  Eq.trans (rat.mul_comm (a⁻¹) a) (rat.mul_inv_cancel a h)

protected instance decidable_eq : DecidableEq ℚ :=
  id
    fun (_v : ℚ) =>
      cases_on _v
        fun (num : ℤ) (denom : ℕ) (pos : 0 < denom) (cop : nat.coprime (int.nat_abs num) denom) (w : ℚ) =>
          cases_on w
            fun (w_num : ℤ) (w_denom : ℕ) (w_pos : 0 < w_denom) (w_cop : nat.coprime (int.nat_abs w_num) w_denom) =>
              decidable.by_cases
                (fun (ᾰ : num = w_num) =>
                  Eq._oldrec
                    (fun (w_cop : nat.coprime (int.nat_abs num) w_denom) =>
                      decidable.by_cases
                        (fun (ᾰ : denom = w_denom) =>
                          Eq._oldrec
                            (fun (w_pos : 0 < denom) (w_cop : nat.coprime (int.nat_abs num) denom) => is_true sorry) ᾰ
                            w_pos w_cop)
                        fun (ᾰ : ¬denom = w_denom) => isFalse sorry)
                    ᾰ w_cop)
                fun (ᾰ : ¬num = w_num) => isFalse sorry

protected instance field : field ℚ :=
  field.mk rat.add rat.add_assoc 0 rat.zero_add rat.add_zero rat.neg
    (comm_ring.sub._default rat.add rat.add_assoc 0 rat.zero_add rat.add_zero rat.neg) rat.add_left_neg rat.add_comm
    rat.mul rat.mul_assoc 1 rat.one_mul rat.mul_one rat.mul_add rat.add_mul rat.mul_comm rat.inv sorry rat.mul_inv_cancel
    sorry

/- Extra instances to short-circuit type class resolution -/

protected instance division_ring : division_ring ℚ :=
  field.to_division_ring

-- TODO(Mario): this instance slows down data.real.basic

protected instance integral_domain : integral_domain ℚ :=
  field.to_integral_domain

--instance : domain ℚ          := by apply_instance

protected instance nontrivial : nontrivial ℚ :=
  euclidean_domain.to_nontrivial ℚ

--instance : ring ℚ            := by apply_instance

protected instance comm_ring : comm_ring ℚ :=
  euclidean_domain.to_comm_ring ℚ

protected instance comm_semiring : comm_semiring ℚ :=
  comm_ring.to_comm_semiring

protected instance semiring : semiring ℚ :=
  ring.to_semiring

protected instance add_comm_group : add_comm_group ℚ :=
  ring.to_add_comm_group ℚ

protected instance add_group : add_group ℚ :=
  add_comm_group.to_add_group ℚ

protected instance add_comm_monoid : add_comm_monoid ℚ :=
  add_comm_group.to_add_comm_monoid ℚ

protected instance add_monoid : add_monoid ℚ :=
  sub_neg_monoid.to_add_monoid ℚ

protected instance add_left_cancel_semigroup : add_left_cancel_semigroup ℚ :=
  add_left_cancel_monoid.to_add_left_cancel_semigroup ℚ

protected instance add_right_cancel_semigroup : add_right_cancel_semigroup ℚ :=
  add_right_cancel_monoid.to_add_right_cancel_semigroup ℚ

protected instance add_comm_semigroup : add_comm_semigroup ℚ :=
  add_comm_monoid.to_add_comm_semigroup ℚ

protected instance add_semigroup : add_semigroup ℚ :=
  add_monoid.to_add_semigroup ℚ

protected instance comm_monoid : comm_monoid ℚ :=
  comm_semiring.to_comm_monoid ℚ

protected instance monoid : monoid ℚ :=
  ring.to_monoid ℚ

protected instance comm_semigroup : comm_semigroup ℚ :=
  comm_ring.to_comm_semigroup ℚ

protected instance semigroup : semigroup ℚ :=
  monoid.to_semigroup ℚ

theorem sub_def {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (b0 : b ≠ 0) (d0 : d ≠ 0) : mk a b - mk c d = mk (a * d - c * b) (b * d) := sorry

@[simp] theorem denom_neg_eq_denom (q : ℚ) : denom (-q) = denom q :=
  cases_on q
    fun (q_num : ℤ) (q_denom : ℕ) (q_pos : 0 < q_denom) (q_cop : nat.coprime (int.nat_abs q_num) q_denom) =>
      idRhs (denom (-mk' q_num q_denom q_pos q_cop) = denom (-mk' q_num q_denom q_pos q_cop)) rfl

@[simp] theorem num_neg_eq_neg_num (q : ℚ) : num (-q) = -num q :=
  cases_on q
    fun (q_num : ℤ) (q_denom : ℕ) (q_pos : 0 < q_denom) (q_cop : nat.coprime (int.nat_abs q_num) q_denom) =>
      idRhs (num (-mk' q_num q_denom q_pos q_cop) = num (-mk' q_num q_denom q_pos q_cop)) rfl

@[simp] theorem num_zero : num 0 = 0 :=
  rfl

theorem zero_of_num_zero {q : ℚ} (hq : num q = 0) : q = 0 := sorry

theorem zero_iff_num_zero {q : ℚ} : q = 0 ↔ num q = 0 := sorry

theorem num_ne_zero_of_ne_zero {q : ℚ} (h : q ≠ 0) : num q ≠ 0 :=
  fun (this : num q = 0) => h (zero_of_num_zero this)

@[simp] theorem num_one : num 1 = 1 :=
  rfl

@[simp] theorem denom_one : denom 1 = 1 :=
  rfl

theorem denom_ne_zero (q : ℚ) : denom q ≠ 0 :=
  ne_of_gt (pos q)

theorem eq_iff_mul_eq_mul {p : ℚ} {q : ℚ} : p = q ↔ num p * ↑(denom q) = num q * ↑(denom p) := sorry

theorem mk_num_ne_zero_of_ne_zero {q : ℚ} {n : ℤ} {d : ℤ} (hq : q ≠ 0) (hqnd : q = mk n d) : n ≠ 0 := sorry

theorem mk_denom_ne_zero_of_ne_zero {q : ℚ} {n : ℤ} {d : ℤ} (hq : q ≠ 0) (hqnd : q = mk n d) : d ≠ 0 := sorry

theorem mk_ne_zero_of_ne_zero {n : ℤ} {d : ℤ} (h : n ≠ 0) (hd : d ≠ 0) : mk n d ≠ 0 :=
  fun (this : mk n d = 0) => h (iff.mp (mk_eq_zero hd) this)

theorem mul_num_denom (q : ℚ) (r : ℚ) : q * r = mk (num q * num r) ↑(denom q * denom r) := sorry

theorem div_num_denom (q : ℚ) (r : ℚ) : q / r = mk (num q * ↑(denom r)) (↑(denom q) * num r) := sorry

theorem num_denom_mk {q : ℚ} {n : ℤ} {d : ℤ} (hn : n ≠ 0) (hd : d ≠ 0) (qdf : q = mk n d) : ∃ (c : ℤ), n = c * num q ∧ d = c * ↑(denom q) := sorry

theorem mk_pnat_num (n : ℤ) (d : ℕ+) : num (mk_pnat n d) = n / ↑(nat.gcd (int.nat_abs n) ↑d) :=
  subtype.cases_on d
    fun (d_val : ℕ) (d_property : 0 < d_val) => Eq.refl (num (mk_pnat n { val := d_val, property := d_property }))

theorem mk_pnat_denom (n : ℤ) (d : ℕ+) : denom (mk_pnat n d) = ↑d / nat.gcd (int.nat_abs n) ↑d :=
  subtype.cases_on d
    fun (d_val : ℕ) (d_property : 0 < d_val) => Eq.refl (denom (mk_pnat n { val := d_val, property := d_property }))

theorem mul_num (q₁ : ℚ) (q₂ : ℚ) : num (q₁ * q₂) = num q₁ * num q₂ / ↑(nat.gcd (int.nat_abs (num q₁ * num q₂)) (denom q₁ * denom q₂)) := sorry

theorem mul_denom (q₁ : ℚ) (q₂ : ℚ) : denom (q₁ * q₂) = denom q₁ * denom q₂ / nat.gcd (int.nat_abs (num q₁ * num q₂)) (denom q₁ * denom q₂) := sorry

theorem mul_self_num (q : ℚ) : num (q * q) = num q * num q := sorry

theorem mul_self_denom (q : ℚ) : denom (q * q) = denom q * denom q := sorry

theorem add_num_denom (q : ℚ) (r : ℚ) : q + r = mk (num q * ↑(denom r) + ↑(denom q) * num r) (↑(denom q) * ↑(denom r)) := sorry

theorem coe_int_eq_mk (z : ℤ) : ↑z = mk z 1 := sorry

theorem mk_eq_div (n : ℤ) (d : ℤ) : mk n d = ↑n / ↑d := sorry

theorem exists_eq_mul_div_num_and_eq_mul_div_denom {n : ℤ} {d : ℤ} (n_ne_zero : n ≠ 0) (d_ne_zero : d ≠ 0) : ∃ (c : ℤ), n = c * num (↑n / ↑d) ∧ d = c * ↑(denom (↑n / ↑d)) :=
  num_denom_mk n_ne_zero d_ne_zero
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑n / ↑d = mk n d)) (Eq.symm (mk_eq_div n d)))) (Eq.refl (mk n d)))

theorem coe_int_eq_of_int (z : ℤ) : ↑z = of_int z :=
  Eq.trans (coe_int_eq_mk z) (Eq.symm (of_int_eq_mk z))

@[simp] theorem coe_int_num (n : ℤ) : num ↑n = n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (num ↑n = n)) (coe_int_eq_of_int n))) (Eq.refl (num (of_int n)))

@[simp] theorem coe_int_denom (n : ℤ) : denom ↑n = 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (denom ↑n = 1)) (coe_int_eq_of_int n))) (Eq.refl (denom (of_int n)))

theorem coe_int_num_of_denom_eq_one {q : ℚ} (hq : denom q = 1) : ↑(num q) = q := sorry

theorem denom_eq_one_iff (r : ℚ) : denom r = 1 ↔ ↑(num r) = r :=
  { mp := coe_int_num_of_denom_eq_one, mpr := fun (h : ↑(num r) = r) => h ▸ coe_int_denom (num r) }

protected instance int.can_lift : can_lift ℚ ℤ :=
  can_lift.mk coe (fun (q : ℚ) => denom q = 1) sorry

theorem coe_nat_eq_mk (n : ℕ) : ↑n = mk (↑n) 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑n = mk (↑n) 1)) (Eq.symm (int.cast_coe_nat n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑↑n = mk (↑n) 1)) (coe_int_eq_mk ↑n))) (Eq.refl (mk (↑n) 1)))

@[simp] theorem coe_nat_num (n : ℕ) : num ↑n = ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (num ↑n = ↑n)) (Eq.symm (int.cast_coe_nat n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (num ↑↑n = ↑n)) (coe_int_num ↑n))) (Eq.refl ↑n))

@[simp] theorem coe_nat_denom (n : ℕ) : denom ↑n = 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (denom ↑n = 1)) (Eq.symm (int.cast_coe_nat n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (denom ↑↑n = 1)) (coe_int_denom ↑n))) (Eq.refl 1))

-- Will be subsumed by `int.coe_inj` after we have defined

-- `linear_ordered_field ℚ` (which implies characteristic zero).

theorem coe_int_inj (m : ℤ) (n : ℤ) : ↑m = ↑n ↔ m = n := sorry

theorem inv_def' {q : ℚ} : q⁻¹ = ↑(denom q) / ↑(num q) := sorry

@[simp] theorem mul_denom_eq_num {q : ℚ} : q * ↑(denom q) = ↑(num q) := sorry

theorem denom_div_cast_eq_one_iff (m : ℤ) (n : ℤ) (hn : n ≠ 0) : denom (↑m / ↑n) = 1 ↔ n ∣ m := sorry

theorem num_div_eq_of_coprime {a : ℤ} {b : ℤ} (hb0 : 0 < b) (h : nat.coprime (int.nat_abs a) (int.nat_abs b)) : num (↑a / ↑b) = a := sorry

theorem denom_div_eq_of_coprime {a : ℤ} {b : ℤ} (hb0 : 0 < b) (h : nat.coprime (int.nat_abs a) (int.nat_abs b)) : ↑(denom (↑a / ↑b)) = b := sorry

theorem div_int_inj {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hb0 : 0 < b) (hd0 : 0 < d) (h1 : nat.coprime (int.nat_abs a) (int.nat_abs b)) (h2 : nat.coprime (int.nat_abs c) (int.nat_abs d)) (h : ↑a / ↑b = ↑c / ↑d) : a = c ∧ b = d := sorry

theorem coe_int_div_self (n : ℤ) : ↑(n / n) = ↑n / ↑n := sorry

theorem coe_nat_div_self (n : ℕ) : ↑(n / n) = ↑n / ↑n :=
  coe_int_div_self ↑n

theorem coe_int_div (a : ℤ) (b : ℤ) (h : b ∣ a) : ↑(a / b) = ↑a / ↑b := sorry

theorem coe_nat_div (a : ℕ) (b : ℕ) (h : b ∣ a) : ↑(a / b) = ↑a / ↑b := sorry

protected theorem forall {p : ℚ → Prop} : (∀ (r : ℚ), p r) ↔ ∀ (a b : ℤ), p (↑a / ↑b) := sorry

protected theorem exists {p : ℚ → Prop} : (∃ (r : ℚ), p r) ↔ ∃ (a : ℤ), ∃ (b : ℤ), p (↑a / ↑b) := sorry


import data.list tools.super .library_dev.data.list.comb .library_dev.data.list.set
open nat function tactic

-- lemmas of list

namespace list

variables {A B: Type}

private theorem mem_cons (x : A) (l : list A) : x ∈ x :: l :=
or.inl rfl

-- def upto : ℕ → list ℕ
-- | 0      := []
-- | (succ n) := n :: upto n

-- theorem lt_of_mem_upto {n i : nat} : i ∈ upto n → i < n :=
-- nat.rec_on n (λ h, absurd h (not_mem_nil i))
-- (λ a ih h, or.elim h
-- (begin intro l, rw l, apply lt_succ_self end)
-- (λ r, lt_trans (ih r) (lt_succ_self a)))

-- theorem upto_succ (n : nat) : upto (succ n) = n :: upto n := rfl

-- theorem length_upto : ∀ n, length (upto n) = n
-- | 0        := rfl
-- | (succ n) := begin simp [upto_succ, length_cons, length_upto] end

/- map -/
theorem map_nil (f : A → B) : list.map f [] = [] := rfl

end list

open list

-- lemmas of fin

namespace fin

attribute [simp]
private theorem length_nil {A : Type} : length (@nil A) = 0 :=
rfl

lemma val_mk (n i : nat) (Plt : i < n) : fin.val (fin.mk i Plt) = i := rfl

def upto (n : ℕ) : list (fin n) :=
dmap (λ i, i < n) fin.mk (list.upto n)

lemma map_val_upto (n : nat) : map fin.val (upto n) = list.upto n :=
map_dmap_of_inv_of_pos (val_mk n) (@lt_of_mem_upto n)

lemma length_upto (n : nat) : length (fin.upto n) = n :=
calc
  length (fin.upto n) = length (list.upto n) : (map_val_upto n ▸ eq.symm (length_map fin.val (upto n)))
  ... = n                    : length_upto n

lemma upto_ne_nil_of_ne_zero (n : nat) (Hn : n ≠ 0) : fin.upto n ≠ [] :=
begin
  intro Hup,
  apply Hn,
  rewrite [-(@length_nil (fin n)), -Hup],
  apply eq.symm (length_upto _)
end

lemma mem_upto (n : nat) : ∀ (i : fin n), i ∈ upto n :=
take i, fin.rec_on i
  (take ival Piltn,
    have ival ∈ list.upto n, from mem_upto_of_lt Piltn,
mem_dmap Piltn this)

end fin

-- lemmas of bigops

-- namespace group_bigops

-- variables {A B : Type}

-- definition Suml (f : A → ℕ) : list A → ℕ
-- | [] := 0
-- | (a :: ls) := f a + Suml ls

-- definition addf {A B : Type} [sgB : add_semigroup B] (f : A → B) : B → A → B :=
-- λ b a, b + f a

-- definition Suml [add_monoid B] (l : list A) (f : A → B) : B :=
-- list.foldl (addf f) 0 l

-- theorem Suml_nil (f : A → B) : Suml [] f = 0 := Prodl_nil f

-- -- check add_monoid

-- end group_bigops

-- definitions

section

-- Suml

variables {A : Type}

definition Suml (f : A → ℕ) : list A → ℕ
| [] := 0
| (a :: ls) := f a + Suml ls

theorem le_add_of_le {a b c : ℕ} : a ≤ b → a ≤ b + c :=
begin
induction c with n ih, intro,super, intro h2,
apply le_succ_of_le, exact ih h2
end

theorem le_of_mem_Suml {f : A → ℕ} {a : A} {l : list A} :
a ∈ l → f a ≤ Suml f l :=
begin
induction l with b ls ih, intro h, exact absurd h (not_mem_nil _),
dsimp [Suml], intro h, assert h' : a = b ∨ a ∈ ls, exact h,
cases h' with l r, rw l, apply le_add_right,
rw add_comm,apply le_add_of_le, exact ih r
end

end

open list fin

inductive finite_tree : Type
| cons : Π {n : ℕ},  (fin n → finite_tree) → finite_tree

namespace finite_tree

-- definition size : finite_tree → ℕ
-- | (@cons n ts) := Suml (fin.upto n) (λ i, size (ts i)) + 1
def size : finite_tree → ℕ
| (@cons n ts) := Suml (λ i, size (ts i)) (fin.upto n) + 1

-- theorem exists_eq_cons_of_ne_nil {A : Type} {l : list A} : l ≠ [] → ∃ a, ∃ l', l = a::l' :=
-- list.rec_on l (λ H, absurd rfl H) (λ a l' IH H, ⟨a, ⟨l',rfl⟩⟩)

theorem pos_of_size (t : finite_tree) : 0 < size t :=
finite_tree.rec_on t (λ n ts ih, dec_trivial)

theorem lt_of_size_branches_aux {n : ℕ} (ts : fin n → finite_tree) (k : fin n) : size (ts k) < Suml (λ i, size (ts i)) (upto n) + 1 :=
begin
assert kin : k ∈ upto n, exact mem_upto n k,
assert h : size (ts k) ≤ Suml (λ i, size (ts i)) (upto n),
apply le_of_mem_Suml kin,
apply lt_succ_of_le, assumption
end

def embeds : finite_tree → finite_tree → Prop
| (@cons _ ts) (@cons _ us) := (∃ j, embeds (cons ts) (us j)) ∨
                                  (∃ f, injective f ∧ ∀ i, embeds (ts i) (us (f i)))

infix ` ≼ `:50 := embeds  -- \preceq

def node {ts : fin 0 → finite_tree} : finite_tree := @cons 0 ts

def {u} fin_zero_absurd {α : Sort u} (i : fin 0) : α :=
absurd i.2 (not_lt_zero i.1)

theorem node_embeds {ts : fin 0 → finite_tree} (t : finite_tree) : @cons 0 ts ≼ t :=
begin
  induction t with n a ih,
  dsimp [embeds],
  pose f : fin 0 → fin n := λ i : fin 0, fin_zero_absurd i,
  apply or.inr,
  existsi f,
  split,
    intros i j hij, exact fin_zero_absurd i,
  intro i, exact fin_zero_absurd i
end

theorem not_embeds_node {n : ℕ} {ts : fin (succ n) → finite_tree}
  {tt : fin 0 → finite_tree}: ¬ cons ts ≼ cons tt :=
begin
  intro h,
  dsimp [embeds] at h,
  cases h with h₁ h₂,
  { cases h₁ with i hi, exact fin_zero_absurd i },
  cases h₂ with f hf, exact fin_zero_absurd (f ⟨0, zero_lt_succ _⟩)
end

theorem cons_embeds_cons_left {m n : ℕ} {ss : fin m → finite_tree} {ts : fin n → finite_tree}
    {j : fin n} (H : cons ss ≼ ts j) :
  cons ss ≼ cons ts :=
begin
cases m,
apply node_embeds, cases n,
exact fin_zero_absurd j,
apply or.inl (exists.intro j H)
end

theorem cons_embeds_cons_right {m n : ℕ} {ss : fin m → finite_tree} {ts : fin n → finite_tree}
    {f : fin m → fin n} (injf : injective f) (Hf : ∀ i, ss i ≼ ts (f i)) :
  cons ss ≼ cons ts :=
begin cases m, apply node_embeds, cases n,
exact fin_zero_absurd (f 0),
apply or.inr (exists.intro f (and.intro injf Hf))
end

theorem embeds_refl (t : finite_tree) : t ≼ t :=
begin
induction t with n a ih,
cases n, apply node_embeds,
apply cons_embeds_cons_right,
apply injective_id, exact ih
end

theorem embeds_trans_aux : ∀ {u s t}, t ≼ u → s ≼ t → s ≼ u :=
begin
  intro u,
  induction u with ul us ihu,
    intros s t, cases s with sl ss,
    cases t with tl ts,
  intro H₁, dsimp [embeds] at H₁, cases H₁ with H₁₁ H₁₂,
  cases H₁₁ with i H₁₁, intro H₂,
    apply cons_embeds_cons_left (ihu _ H₁₁ H₂),
    cases H₁₂ with f Hf, cases Hf with injf Hf,
  intro H₂, dsimp [embeds] at H₂, cases H₂ with H₂₁ H₂₂,
    cases H₂₁ with j H₂₁,
    apply cons_embeds_cons_left (ihu _ (Hf j) H₂₁),
    cases H₂₂ with g Hg, cases Hg with injg Hg,
    apply cons_embeds_cons_right,
    apply injective_comp injf injg,
    intro i, apply ihu _ (Hf (g i)) (Hg i)
end

theorem embeds_trans {s t u : finite_tree} (H₁ : s ≼ t) (H₂ : t ≼ u) : s ≼ u :=
embeds_trans_aux H₂ H₁

-- proposition cons_embeds_iff {m : ℕ} (ss : fin m → finite_tree) (t : finite_tree) :
--   cons ss ≼ t ↔ ∃ n (ts : fin n → finite_tree), t = cons ts ∧
--                   ((∃ j, cons ss ≼ ts j) ∨ (∃ f, injective f ∧ ∀ i, ss i ≼ ts (f i))) :=
-- begin
--   apply iff.intro,
--     intro H, cases t with n ts,
--       contradiction,
--     existsi n, existsi ts, split, reflexivity, apply cons_embeds_cons_dest H,
--   intro H, cases H with n H, cases H with ts H, cases H with teq H,
--   rewrite teq, exact H
-- end

end finite_tree


/-
Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 -/
import measure_theory.measurable_space

import measure_theory.measure_space
import measure_theory.outer_measure
import measure_theory.lebesgue_measure
import measure_theory.integration
import measure_theory.set_integral
import measure_theory.borel_space
import data.set.countable
import formal_ml.nnreal
import formal_ml.sum
import formal_ml.core
import formal_ml.measurable_space
import formal_ml.semiring
import formal_ml.real_measurable_space
import formal_ml.set
import formal_ml.filter_util
import topology.instances.ennreal
import formal_ml.int

def is_absolutely_continuous_wrt
  {Ω:Type*} {M:measurable_space Ω} (μ ν:measure_theory.measure Ω):Prop :=
  ∀ A:set Ω, measurable_set A → (ν A = 0) → (μ A = 0)

lemma measure_zero_of_is_absolutely_continuous_wrt
  {Ω:Type*} {M:measurable_space Ω} (μ ν:measure_theory.measure Ω) (A:set Ω):
  is_absolutely_continuous_wrt μ ν →
  measurable_set A → (ν A = 0) → (μ A = 0) :=
begin
  intros A1 A2 A3,
  unfold is_absolutely_continuous_wrt at A1,
  apply A1 A A2 A3,
end

noncomputable def lebesgue_measure_on_borel := measure_theory.real.measure_space.volume

lemma lebesgue_measure_on_borel_def:
  lebesgue_measure_on_borel = measure_theory.real.measure_space.volume := rfl

lemma lebesgue_measure_on_borel_apply {S:set ℝ}:
  lebesgue_measure_on_borel S = measure_theory.lebesgue_outer S := rfl

def is_absolutely_continuous_wrt_lebesgue
  (μ:measure_theory.measure ℝ):Prop :=
  is_absolutely_continuous_wrt μ lebesgue_measure_on_borel

lemma prob_zero_of_is_absolute_continuous_wrt_lebesgue (μ:measure_theory.measure ℝ) (E:set ℝ):is_absolutely_continuous_wrt_lebesgue μ →
   measurable_set E →
   measure_theory.lebesgue_outer E = 0 →
    μ E=0 :=
begin
  intros A1 A2 A3,
  apply measure_zero_of_is_absolutely_continuous_wrt μ lebesgue_measure_on_borel
  _ A1 A2,
  rw lebesgue_measure_on_borel_apply,
  apply A3,
end


/-
Copyright (c) 2021 Benjamin Davidson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Benjamin Davidson
-/
import measure_theory.integral.interval_integral
import analysis.special_functions.trigonometric.arctan_deriv

/-!
# Integration of specific interval integrals

This file contains proofs of the integrals of various specific functions. This includes:
* Integrals of simple functions, such as `id`, `pow`, `inv`, `exp`, `log`
* Integrals of some trigonometric functions, such as `sin`, `cos`, `1 / (1 + x^2)`
* The integral of `cos x ^ 2 - sin x ^ 2`
* Reduction formulae for the integrals of `sin x ^ n` and `cos x ^ n` for `n ≥ 2`
* The computation of `∫ x in 0..π, sin x ^ n` as a product for even and odd `n` (used in proving the
  Wallis product for pi)
* Integrals of the form `sin x ^ m * cos x ^ n`

With these lemmas, many simple integrals can be computed by `simp` or `norm_num`.
See `test/integration.lean` for specific examples.

This file also contains some facts about the interval integrability of specific functions.

This file is still being developed.

## Tags

integrate, integration, integrable, integrability
-/

open real nat set finset
open_locale real big_operators interval
variables {a b : ℝ} (n : ℕ)

namespace interval_integral
open measure_theory
variables {f : ℝ → ℝ} {μ ν : measure ℝ} [is_locally_finite_measure μ] (c d : ℝ)

/-! ### Interval integrability -/

@[simp]
lemma interval_integrable_pow : interval_integrable (λ x, x^n) μ a b :=
(continuous_pow n).interval_integrable a b

lemma interval_integrable_zpow {n : ℤ} (h : 0 ≤ n ∨ (0 : ℝ) ∉ [a, b]) :
  interval_integrable (λ x, x ^ n) μ a b :=
(continuous_on_id.zpow₀ n $ λ x hx, h.symm.imp (ne_of_mem_of_not_mem hx) id).interval_integrable

/-- See `interval_integrable_rpow'` for a version with a weaker hypothesis on `r`, but assuming the
measure is volume. -/
lemma interval_integrable_rpow {r : ℝ} (h : 0 ≤ r ∨ (0 : ℝ) ∉ [a, b]) :
  interval_integrable (λ x, x ^ r) μ a b :=
(continuous_on_id.rpow_const $ λ x hx, h.symm.imp (ne_of_mem_of_not_mem hx) id).interval_integrable

/-- See `interval_integrable_rpow` for a version applying to any locally finite measure, but with a
stronger hypothesis on `r`. -/
lemma interval_integrable_rpow' {r : ℝ} (h : -1 < r) :
  interval_integrable (λ x, x ^ r) volume a b :=
begin
  suffices : ∀ (c : ℝ), interval_integrable (λ x, x ^ r) volume 0 c,
  { exact interval_integrable.trans (this a).symm (this b) },
  have : ∀ (c : ℝ), (0 ≤ c) → interval_integrable (λ x, x ^ r) volume 0 c,
  { intros c hc,
    rw [interval_integrable_iff, uIoc_of_le hc],
    have hderiv : ∀ x ∈ Ioo 0 c, has_deriv_at (λ x : ℝ, x ^ (r + 1) / (r + 1)) (x ^ r) x,
    { intros x hx, convert (real.has_deriv_at_rpow_const (or.inl hx.1.ne')).div_const (r + 1),
      field_simp [(by linarith : r + 1 ≠ 0)], ring, },
    apply integrable_on_deriv_of_nonneg hc _ hderiv,
    { intros x hx, apply rpow_nonneg_of_nonneg hx.1.le, },
    { refine (continuous_on_id.rpow_const _).div_const _, intros x hx, right, linarith } },
  intro c, rcases le_total 0 c with hc|hc,
  { exact this c hc },
  { rw [interval_integrable.iff_comp_neg, neg_zero],
    have m := (this (-c) (by linarith)).smul (cos (r * π)),
    rw interval_integrable_iff at m ⊢,
    refine m.congr_fun _ measurable_set_Ioc, intros x hx,
    rw uIoc_of_le (by linarith : 0 ≤ -c) at hx,
    simp only [pi.smul_apply, algebra.id.smul_eq_mul, log_neg_eq_log, mul_comm,
      rpow_def_of_pos hx.1, rpow_def_of_neg (by linarith [hx.1] : -x < 0)], }
end

/-- See `interval_integrable_cpow'` for a version with a weaker hypothesis on `r`, but assuming the
measure is volume. -/
lemma interval_integrable_cpow {r : ℂ} (h : 0 ≤ r.re ∨ (0 : ℝ) ∉ [a, b]) :
  interval_integrable (λ x : ℝ, (x : ℂ) ^ r) μ a b :=
begin
  by_cases h2 : (0:ℝ) ∉ [a,b],
  { -- Easy case #1: 0 ∉ [a, b] -- use continuity.
    refine (continuous_at.continuous_on (λ x hx, _)).interval_integrable,
    exact complex.continuous_at_of_real_cpow_const _ _ (or.inr $ ne_of_mem_of_not_mem hx h2) },
  rw [eq_false_intro h2, or_false] at h,
  rcases lt_or_eq_of_le h with h'|h',
  { -- Easy case #2: 0 < re r -- again use continuity
    exact (complex.continuous_of_real_cpow_const h').interval_integrable _ _ },
  -- Now the hard case: re r = 0 and 0 is in the interval.
  refine (interval_integrable.interval_integrable_norm_iff _).mp _,
  { refine (measurable_of_continuous_on_compl_singleton (0:ℝ) _).ae_strongly_measurable,
    exact continuous_at.continuous_on
      (λ x hx, complex.continuous_at_of_real_cpow_const x r (or.inr hx)) },
  -- reduce to case of integral over `[0, c]`
  suffices : ∀ (c : ℝ), interval_integrable (λ x : ℝ, ‖↑x ^ r‖) μ 0 c,
    from (this a).symm.trans (this b),
  intro c,
  rcases le_or_lt 0 c with hc | hc,
  { -- case `0 ≤ c`: integrand is identically 1
    have : interval_integrable (λ x, 1 : ℝ → ℝ) μ 0 c,
      from interval_integrable_const,
    rw interval_integrable_iff_integrable_Ioc_of_le hc at this ⊢,
    refine integrable_on.congr_fun this (λ x hx, _) measurable_set_Ioc,
    dsimp only,
    rw [complex.norm_eq_abs, complex.abs_cpow_eq_rpow_re_of_pos hx.1, ←h', rpow_zero], },
  { -- case `c < 0`: integrand is identically constant, *except* at `x = 0` if `r ≠ 0`.
    apply interval_integrable.symm,
    rw [interval_integrable_iff_integrable_Ioc_of_le hc.le],
    have : Ioc c 0 = Ioo c 0 ∪ {(0:ℝ)},
    { rw [←Ioo_union_Icc_eq_Ioc hc (le_refl 0), ←Icc_def],
      simp_rw [←le_antisymm_iff, set_of_eq_eq_singleton'] },
    rw [this, integrable_on_union, and.comm], split,
    { refine integrable_on_singleton_iff.mpr (or.inr _),
      exact is_finite_measure_on_compacts_of_is_locally_finite_measure.lt_top_of_is_compact
        is_compact_singleton },
    { have : ∀ (x : ℝ), x ∈ Ioo c 0 → ‖complex.exp (↑π * complex.I * r)‖ = ‖(x:ℂ) ^ r‖,
      { intros x hx,
        rw [complex.of_real_cpow_of_nonpos hx.2.le, norm_mul, ←complex.of_real_neg,
          complex.norm_eq_abs (_ ^ _), complex.abs_cpow_eq_rpow_re_of_pos (neg_pos.mpr hx.2),
          ←h', rpow_zero, one_mul] },
      refine integrable_on.congr_fun _ this measurable_set_Ioo,
      rw integrable_on_const,
      refine or.inr ((measure_mono set.Ioo_subset_Icc_self).trans_lt _),
      exact is_finite_measure_on_compacts_of_is_locally_finite_measure.lt_top_of_is_compact
        is_compact_Icc } },
end

/-- See `interval_integrable_cpow` for a version applying to any locally finite measure, but with a
stronger hypothesis on `r`. -/
lemma interval_integrable_cpow' {r : ℂ} (h : -1 < r.re) :
  interval_integrable (λ x:ℝ, (x:ℂ) ^ r) volume a b :=
begin
  suffices : ∀ (c : ℝ), interval_integrable (λ x, (x : ℂ) ^ r) volume 0 c,
  { exact interval_integrable.trans (this a).symm (this b) },
  have : ∀ (c : ℝ), (0 ≤ c) → interval_integrable (λ x, (x : ℂ) ^ r) volume 0 c,
  { intros c hc,
    rw ←interval_integrable.interval_integrable_norm_iff,
    { rw interval_integrable_iff,
      apply integrable_on.congr_fun,
      { rw ←interval_integrable_iff, exact (interval_integral.interval_integrable_rpow' h) },
      { intros x hx,
        rw uIoc_of_le hc at hx,
        dsimp only,
        rw [complex.norm_eq_abs, complex.abs_cpow_eq_rpow_re_of_pos hx.1] },
      { exact measurable_set_uIoc } },
    { refine continuous_on.ae_strongly_measurable _ measurable_set_uIoc,
      refine continuous_at.continuous_on (λ x hx, _),
      rw uIoc_of_le hc at hx,
      refine (continuous_at_cpow_const (or.inl _)).comp complex.continuous_of_real.continuous_at,
      rw complex.of_real_re,
      exact hx.1 } },
  intro c, rcases le_total 0 c with hc | hc,
  { exact this c hc },
  { rw [interval_integrable.iff_comp_neg, neg_zero],
    have m := (this (-c) (by linarith)).const_mul (complex.exp (π * complex.I * r)),
    rw [interval_integrable_iff, uIoc_of_le (by linarith : 0 ≤ -c)] at m ⊢,
    refine m.congr_fun (λ x hx, _) measurable_set_Ioc,
    dsimp only,
    have : -x ≤ 0, by linarith [hx.1],
    rw [complex.of_real_cpow_of_nonpos this, mul_comm],
    simp }
end

@[simp]
lemma interval_integrable_id : interval_integrable (λ x, x) μ a b :=
continuous_id.interval_integrable a b

@[simp]
lemma interval_integrable_const : interval_integrable (λ x, c) μ a b :=
continuous_const.interval_integrable a b

lemma interval_integrable_one_div (h : ∀ x : ℝ, x ∈ [a, b] → f x ≠ 0)
  (hf : continuous_on f [a, b]) :
  interval_integrable (λ x, 1 / f x) μ a b :=
(continuous_on_const.div hf h).interval_integrable

@[simp]
lemma interval_integrable_inv (h : ∀ x : ℝ, x ∈ [a, b] → f x ≠ 0)
  (hf : continuous_on f [a, b]) :
  interval_integrable (λ x, (f x)⁻¹) μ a b :=
by simpa only [one_div] using interval_integrable_one_div h hf

@[simp]
lemma interval_integrable_exp : interval_integrable exp μ a b :=
continuous_exp.interval_integrable a b

@[simp]
lemma _root_.interval_integrable.log
  (hf : continuous_on f [a, b]) (h : ∀ x : ℝ, x ∈ [a, b] → f x ≠ 0) :
  interval_integrable (λ x, log (f x)) μ a b :=
(continuous_on.log hf h).interval_integrable

@[simp]
lemma interval_integrable_log (h : (0:ℝ) ∉ [a, b]) :
  interval_integrable log μ a b :=
interval_integrable.log continuous_on_id $ λ x hx, ne_of_mem_of_not_mem hx h

@[simp]
lemma interval_integrable_sin : interval_integrable sin μ a b :=
continuous_sin.interval_integrable a b

@[simp]
lemma interval_integrable_cos : interval_integrable cos μ a b :=
continuous_cos.interval_integrable a b

lemma interval_integrable_one_div_one_add_sq : interval_integrable (λ x : ℝ, 1 / (1 + x^2)) μ a b :=
begin
  refine (continuous_const.div _ (λ x, _)).interval_integrable a b,
  { continuity },
  { nlinarith },
end

@[simp]
lemma interval_integrable_inv_one_add_sq : interval_integrable (λ x : ℝ, (1 + x^2)⁻¹) μ a b :=
by simpa only [one_div] using interval_integrable_one_div_one_add_sq

/-! ### Integrals of the form `c * ∫ x in a..b, f (c * x + d)` -/

@[simp]
lemma mul_integral_comp_mul_right : c * ∫ x in a..b, f (x * c) = ∫ x in a*c..b*c, f x :=
smul_integral_comp_mul_right f c

@[simp]
lemma mul_integral_comp_mul_left : c * ∫ x in a..b, f (c * x) = ∫ x in c*a..c*b, f x :=
smul_integral_comp_mul_left f c

@[simp]
lemma inv_mul_integral_comp_div : c⁻¹ * ∫ x in a..b, f (x / c) = ∫ x in a/c..b/c, f x :=
inv_smul_integral_comp_div f c

@[simp]
lemma mul_integral_comp_mul_add : c * ∫ x in a..b, f (c * x + d) = ∫ x in c*a+d..c*b+d, f x :=
smul_integral_comp_mul_add f c d

@[simp]
lemma mul_integral_comp_add_mul : c * ∫ x in a..b, f (d + c * x) = ∫ x in d+c*a..d+c*b, f x :=
smul_integral_comp_add_mul f c d

@[simp]
lemma inv_mul_integral_comp_div_add : c⁻¹ * ∫ x in a..b, f (x / c + d) = ∫ x in a/c+d..b/c+d, f x :=
inv_smul_integral_comp_div_add f c d

@[simp]
lemma inv_mul_integral_comp_add_div : c⁻¹ * ∫ x in a..b, f (d + x / c) = ∫ x in d+a/c..d+b/c, f x :=
inv_smul_integral_comp_add_div f c d

@[simp]
lemma mul_integral_comp_mul_sub : c * ∫ x in a..b, f (c * x - d) = ∫ x in c*a-d..c*b-d, f x :=
smul_integral_comp_mul_sub f c d

@[simp]
lemma mul_integral_comp_sub_mul : c * ∫ x in a..b, f (d - c * x) = ∫ x in d-c*b..d-c*a, f x :=
smul_integral_comp_sub_mul f c d

@[simp]
lemma inv_mul_integral_comp_div_sub : c⁻¹ * ∫ x in a..b, f (x / c - d) = ∫ x in a/c-d..b/c-d, f x :=
inv_smul_integral_comp_div_sub f c d

@[simp]
lemma inv_mul_integral_comp_sub_div : c⁻¹ * ∫ x in a..b, f (d - x / c) = ∫ x in d-b/c..d-a/c, f x :=
inv_smul_integral_comp_sub_div f c d

end interval_integral

open interval_integral

/-! ### Integrals of simple functions -/

lemma integral_cpow {r : ℂ} (h : -1 < r.re ∨ (r ≠ -1 ∧ (0 : ℝ) ∉ [a, b])) :
  ∫ (x : ℝ) in a..b, (x : ℂ) ^ r = (b ^ (r + 1) - a ^ (r + 1)) / (r + 1) :=
begin
  rw sub_div,
  have hr : r + 1 ≠ 0,
  { cases h,
    { apply_fun complex.re,
      rw [complex.add_re, complex.one_re, complex.zero_re, ne.def, add_eq_zero_iff_eq_neg],
      exact h.ne' },
    { rw [ne.def, ←add_eq_zero_iff_eq_neg] at h, exact h.1 } },
  by_cases hab : (0:ℝ) ∉ [a, b],
  { refine integral_eq_sub_of_has_deriv_at (λ x hx, _) (interval_integrable_cpow $ or.inr hab),
    refine has_deriv_at_of_real_cpow (ne_of_mem_of_not_mem hx hab) _,
    contrapose! hr, rwa add_eq_zero_iff_eq_neg },
  replace h : -1 < r.re, by tauto,
  suffices : ∀ (c : ℝ), ∫ (x : ℝ) in 0..c, (x : ℂ) ^ r =
    c ^ (r + 1) / (r + 1) - 0 ^ (r + 1) / (r + 1),
  { rw [←integral_add_adjacent_intervals (@interval_integrable_cpow' a 0 r h)
      (@interval_integrable_cpow' 0 b r h), integral_symm, this a, this b, complex.zero_cpow hr],
    ring },
  intro c,
  apply integral_eq_sub_of_has_deriv_right,
  { refine ((complex.continuous_of_real_cpow_const _).div_const _).continuous_on,
    rwa [complex.add_re, complex.one_re, ←neg_lt_iff_pos_add] },
  { refine λ x hx, (has_deriv_at_of_real_cpow _ _).has_deriv_within_at,
    { rcases le_total c 0 with hc | hc,
      { rw max_eq_left hc at hx, exact hx.2.ne }, { rw min_eq_left hc at hx, exact hx.1.ne' } },
    { contrapose! hr, rw hr, ring } },
  { exact interval_integrable_cpow' h }
end

lemma integral_rpow {r : ℝ} (h : -1 < r ∨ (r ≠ -1 ∧ (0 : ℝ) ∉ [a, b])) :
  ∫ x in a..b, x ^ r = (b ^ (r + 1) - a ^ (r + 1)) / (r + 1) :=
begin
  have h' : -1 < (r:ℂ).re ∨ (r:ℂ) ≠ -1 ∧ (0:ℝ) ∉ [a, b],
  { cases h,
    { left, rwa complex.of_real_re },
    { right, rwa [←complex.of_real_one, ←complex.of_real_neg, ne.def, complex.of_real_inj] } },
  have : ∫ x in a..b, (x:ℂ) ^ (r :ℂ) = ((b:ℂ) ^ (r + 1 : ℂ) - (a:ℂ) ^ (r + 1 : ℂ)) / (r + 1),
    from integral_cpow h',
  apply_fun complex.re at this, convert this,
  { simp_rw [interval_integral_eq_integral_uIoc, complex.real_smul, complex.of_real_mul_re],
    { change complex.re with is_R_or_C.re,
      rw ←integral_re, refl,
      refine interval_integrable_iff.mp _,
      cases h',
      { exact interval_integrable_cpow' h' }, { exact interval_integrable_cpow (or.inr h'.2) } } },
  { rw (by push_cast : ((r:ℂ) + 1) = ((r + 1 : ℝ) : ℂ)),
    simp_rw [div_eq_inv_mul, ←complex.of_real_inv, complex.of_real_mul_re, complex.sub_re],
    refl }
end

lemma integral_zpow {n : ℤ} (h : 0 ≤ n ∨ n ≠ -1 ∧ (0 : ℝ) ∉ [a, b]) :
  ∫ x in a..b, x ^ n = (b ^ (n + 1) - a ^ (n + 1)) / (n + 1) :=
begin
  replace h : -1 < (n : ℝ) ∨ (n : ℝ) ≠ -1 ∧ (0 : ℝ) ∉ [a, b], by exact_mod_cast h,
  exact_mod_cast integral_rpow h,
end

@[simp] lemma integral_pow : ∫ x in a..b, x ^ n = (b ^ (n + 1) - a ^ (n + 1)) / (n + 1) :=
by simpa only [←int.coe_nat_succ, zpow_coe_nat] using integral_zpow (or.inl (int.coe_nat_nonneg n))

/-- Integral of `|x - a| ^ n` over `Ι a b`. This integral appears in the proof of the
Picard-Lindelöf/Cauchy-Lipschitz theorem. -/
lemma integral_pow_abs_sub_uIoc :
  ∫ x in Ι a b, |x - a| ^ n = |b - a| ^ (n + 1) / (n + 1) :=
begin
  cases le_or_lt a b with hab hab,
  { calc ∫ x in Ι a b, |x - a| ^ n = ∫ x in a..b, |x - a| ^ n :
      by rw [uIoc_of_le hab, ← integral_of_le hab]
    ... = ∫ x in 0..(b - a), x ^ n :
      begin
        simp only [integral_comp_sub_right (λ x, |x| ^ n), sub_self],
        refine integral_congr (λ x hx, congr_arg2 has_pow.pow (abs_of_nonneg $ _) rfl),
        rw uIcc_of_le (sub_nonneg.2 hab) at hx,
        exact hx.1
      end
    ... = |b - a| ^ (n + 1) / (n + 1) : by simp [abs_of_nonneg (sub_nonneg.2 hab)] },
  { calc ∫ x in Ι a b, |x - a| ^ n = ∫ x in b..a, |x - a| ^ n :
      by rw [uIoc_of_lt hab, ← integral_of_le hab.le]
    ... = ∫ x in b - a..0, (-x) ^ n :
      begin
        simp only [integral_comp_sub_right (λ x, |x| ^ n), sub_self],
        refine integral_congr (λ x hx, congr_arg2 has_pow.pow (abs_of_nonpos $ _) rfl),
        rw uIcc_of_le (sub_nonpos.2 hab.le) at hx,
        exact hx.2
      end
    ... = |b - a| ^ (n + 1) / (n + 1) :
      by simp [integral_comp_neg (λ x, x ^ n), abs_of_neg (sub_neg.2 hab)] }
end

@[simp]
lemma integral_id : ∫ x in a..b, x = (b ^ 2 - a ^ 2) / 2 :=
by simpa using integral_pow 1

@[simp]
lemma integral_one : ∫ x in a..b, (1 : ℝ) = b - a :=
by simp only [mul_one, smul_eq_mul, integral_const]

lemma integral_const_on_unit_interval : ∫ x in a..(a + 1), b = b :=
by simp

@[simp]
lemma integral_inv (h : (0:ℝ) ∉ [a, b]) : ∫ x in a..b, x⁻¹ = log (b / a) :=
begin
  have h' := λ x hx, ne_of_mem_of_not_mem hx h,
  rw [integral_deriv_eq_sub' _ deriv_log' (λ x hx, differentiable_at_log (h' x hx))
        (continuous_on_inv₀.mono $ subset_compl_singleton_iff.mpr h),
      log_div (h' b right_mem_uIcc) (h' a left_mem_uIcc)],
end

@[simp]
lemma integral_inv_of_pos (ha : 0 < a) (hb : 0 < b) : ∫ x in a..b, x⁻¹ = log (b / a) :=
integral_inv $ not_mem_uIcc_of_lt ha hb

@[simp]
lemma integral_inv_of_neg (ha : a < 0) (hb : b < 0) : ∫ x in a..b, x⁻¹ = log (b / a) :=
integral_inv $ not_mem_uIcc_of_gt ha hb

lemma integral_one_div (h : (0:ℝ) ∉ [a, b]) : ∫ x : ℝ in a..b, 1/x = log (b / a) :=
by simp only [one_div, integral_inv h]

lemma integral_one_div_of_pos (ha : 0 < a) (hb : 0 < b) : ∫ x : ℝ in a..b, 1/x = log (b / a) :=
by simp only [one_div, integral_inv_of_pos ha hb]

lemma integral_one_div_of_neg (ha : a < 0) (hb : b < 0) : ∫ x : ℝ in a..b, 1/x = log (b / a) :=
by simp only [one_div, integral_inv_of_neg ha hb]

@[simp]
lemma integral_exp : ∫ x in a..b, exp x = exp b - exp a :=
by rw integral_deriv_eq_sub'; norm_num [continuous_on_exp]

lemma integral_exp_mul_complex {c : ℂ} (hc : c ≠ 0) :
  ∫ x in a..b, complex.exp (c * x) = (complex.exp (c * b) - complex.exp (c * a)) / c :=
begin
  have D : ∀ (x : ℝ), has_deriv_at (λ (y : ℝ), complex.exp (c * y) / c) (complex.exp (c * x)) x,
  { intro x,
    conv { congr, skip, rw ←mul_div_cancel (complex.exp (c * x)) hc, },
    convert ((complex.has_deriv_at_exp _).comp x _).div_const c using 1,
    simpa only [mul_one] using ((has_deriv_at_id (x:ℂ)).const_mul _).comp_of_real, },
  rw integral_deriv_eq_sub' _ (funext (λ x, (D x).deriv)) (λ x hx, (D x).differentiable_at),
  { ring_nf },
  { apply continuous.continuous_on, continuity,}
end

@[simp]
lemma integral_log (h : (0:ℝ) ∉ [a, b]) :
  ∫ x in a..b, log x = b * log b - a * log a - b + a :=
begin
  obtain ⟨h', heq⟩ := ⟨λ x hx, ne_of_mem_of_not_mem hx h, λ x hx, mul_inv_cancel (h' x hx)⟩,
  convert integral_mul_deriv_eq_deriv_mul (λ x hx, has_deriv_at_log (h' x hx))
      (λ x hx, has_deriv_at_id x)
      (continuous_on_inv₀.mono $ subset_compl_singleton_iff.mpr h).interval_integrable
      continuous_on_const.interval_integrable using 1;
    simp [integral_congr heq, mul_comm, ← sub_add],
end

@[simp]
lemma integral_log_of_pos (ha : 0 < a) (hb : 0 < b) :
  ∫ x in a..b, log x = b * log b - a * log a - b + a :=
integral_log $ not_mem_uIcc_of_lt ha hb

@[simp]
lemma integral_log_of_neg (ha : a < 0) (hb : b < 0) :
  ∫ x in a..b, log x = b * log b - a * log a - b + a :=
integral_log $ not_mem_uIcc_of_gt ha hb

@[simp]
lemma integral_sin : ∫ x in a..b, sin x = cos a - cos b :=
by rw integral_deriv_eq_sub' (λ x, -cos x); norm_num [continuous_on_sin]

@[simp]
lemma integral_cos : ∫ x in a..b, cos x = sin b - sin a :=
by rw integral_deriv_eq_sub'; norm_num [continuous_on_cos]

lemma integral_cos_mul_complex {z : ℂ} (hz : z ≠ 0) (a b : ℝ) :
  ∫ x in a..b, complex.cos (z * x) = complex.sin (z * b) / z - complex.sin (z * a) / z :=
begin
  apply integral_eq_sub_of_has_deriv_at,
  swap,
  { apply continuous.interval_integrable,
    exact complex.continuous_cos.comp (continuous_const.mul complex.continuous_of_real) },
  intros x hx,
  have a := complex.has_deriv_at_sin (↑x * z),
  have b : has_deriv_at (λ y, y * z : ℂ → ℂ) z ↑x := has_deriv_at_mul_const _,
  have c : has_deriv_at (λ (y : ℂ), complex.sin (y * z)) _ ↑x := has_deriv_at.comp x a b,
  convert has_deriv_at.comp_of_real (c.div_const z),
  { simp_rw mul_comm },
  { rw [mul_div_cancel _ hz, mul_comm] },
end

lemma integral_cos_sq_sub_sin_sq :
  ∫ x in a..b, cos x ^ 2 - sin x ^ 2 = sin b * cos b - sin a * cos a :=
by simpa only [sq, sub_eq_add_neg, neg_mul_eq_mul_neg] using integral_deriv_mul_eq_sub
  (λ x hx, has_deriv_at_sin x) (λ x hx, has_deriv_at_cos x) continuous_on_cos.interval_integrable
  continuous_on_sin.neg.interval_integrable

@[simp]
lemma integral_inv_one_add_sq : ∫ x : ℝ in a..b, (1 + x^2)⁻¹ = arctan b - arctan a :=
begin
  simp only [← one_div],
  refine integral_deriv_eq_sub' _ _ _ (continuous_const.div _ (λ x, _)).continuous_on,
  { norm_num },
  { norm_num },
  { continuity },
  { nlinarith },
end

lemma integral_one_div_one_add_sq : ∫ x : ℝ in a..b, 1 / (1 + x^2) = arctan b - arctan a :=
by simp only [one_div, integral_inv_one_add_sq]

section rpow_cpow
open complex

lemma integral_mul_cpow_one_add_sq {t : ℂ} (ht : t ≠ -1) :
  ∫ x : ℝ in a..b, (x:ℂ) * (1 + x ^ 2) ^ t =
  (1 + b ^ 2) ^ (t + 1) / (2 * (t + 1)) - (1 + a ^ 2) ^ (t + 1) / (2 * (t + 1)) :=
begin
  have : t + 1 ≠ 0 := by { contrapose! ht, rwa add_eq_zero_iff_eq_neg at ht },
  apply integral_eq_sub_of_has_deriv_at,
  { intros x hx,
    have f : has_deriv_at (λ y:ℂ, 1 + y ^ 2) (2 * x) x,
    { convert (has_deriv_at_pow 2 (x:ℂ)).const_add 1, { norm_cast }, { simp } },
    have g : ∀ {z : ℂ}, (0 < z.re) → has_deriv_at (λ z, z ^ (t + 1) / (2 * (t + 1))) (z ^ t / 2) z,
    { intros z hz,
      have : z ≠ 0 := by { contrapose! hz, rw [hz, zero_re], },
      convert (has_deriv_at.cpow_const (has_deriv_at_id _) (or.inl hz)).div_const
        (2 * (t + 1)) using 1,
      field_simp,
      ring },
    convert (has_deriv_at.comp ↑x (g _) f).comp_of_real using 1,
    { field_simp, ring },
    { rw [add_re, one_re, ←of_real_pow, of_real_re],
      exact (add_pos_of_pos_of_nonneg zero_lt_one (sq_nonneg x)) } },
  { apply continuous.interval_integrable,
    refine continuous_of_real.mul _,
    apply continuous.cpow,
    { exact continuous_const.add (continuous_of_real.pow 2)},
    { exact continuous_const },
    { intro a,
      rw [add_re, one_re, ←of_real_pow, of_real_re],
      exact or.inl (add_pos_of_pos_of_nonneg zero_lt_one (sq_nonneg a)) } }
end

lemma integral_mul_rpow_one_add_sq {t : ℝ} (ht : t ≠ -1) :
  ∫ x : ℝ in a..b, x * (1 + x ^ 2) ^ t =
  (1 + b ^ 2) ^ (t + 1) / (2 * (t + 1)) - (1 + a ^ 2) ^ (t + 1) / (2 * (t + 1)) :=
begin
  have : ∀ (x s : ℝ), (((1 + x ^ 2) ^ s : ℝ) : ℂ) = (1 + (x : ℂ) ^ 2) ^ ↑s,
  { intros x s,
    rw [of_real_cpow, of_real_add, of_real_pow, of_real_one],
    exact add_nonneg zero_le_one (sq_nonneg x), },
  rw ←of_real_inj,
  convert integral_mul_cpow_one_add_sq (_ : (t:ℂ) ≠ -1),
  { rw ←interval_integral.integral_of_real,
    congr' with x:1,
    rw [of_real_mul, this x t] },
  { simp_rw [of_real_sub, of_real_div, this a (t + 1), this b (t + 1)],
    push_cast },
  { rw [←of_real_one, ←of_real_neg, ne.def, of_real_inj],
    exact ht },
end

end rpow_cpow

/-! ### Integral of `sin x ^ n` -/

lemma integral_sin_pow_aux :
  ∫ x in a..b, sin x ^ (n + 2) = sin a ^ (n + 1) * cos a - sin b ^ (n + 1) * cos b
    + (n + 1) * (∫ x in a..b, sin x ^ n) - (n + 1) * ∫ x in a..b, sin x ^ (n + 2) :=
begin
  let C := sin a ^ (n + 1) * cos a - sin b ^ (n + 1) * cos b,
  have h : ∀ α β γ : ℝ, (β * α * γ) * α = β * (α * α * γ) := λ α β γ, by ring,
  have hu : ∀ x ∈ _, has_deriv_at (λ y, sin y ^ (n + 1)) ((n + 1 : ℕ) * cos x * sin x ^ n) x :=
    λ x hx, by simpa only [mul_right_comm] using (has_deriv_at_sin x).pow (n+1),
  have hv : ∀ x ∈ [a, b], has_deriv_at (-cos) (sin x) x :=
    λ x hx, by simpa only [neg_neg] using (has_deriv_at_cos x).neg,
  have H := integral_mul_deriv_eq_deriv_mul hu hv _ _,
  calc  ∫ x in a..b, sin x ^ (n + 2)
      = ∫ x in a..b, sin x ^ (n + 1) * sin x                   : by simp only [pow_succ']
  ... = C + (n + 1) * ∫ x in a..b, cos x ^ 2 * sin x ^ n       : by simp [H, h, sq]
  ... = C + (n + 1) * ∫ x in a..b, sin x ^ n - sin x ^ (n + 2) : by simp [cos_sq', sub_mul,
                                                                          ← pow_add, add_comm]
  ... = C + (n + 1) * (∫ x in a..b, sin x ^ n) - (n + 1) * ∫ x in a..b, sin x ^ (n + 2) :
    by rw [integral_sub, mul_sub, add_sub_assoc]; apply continuous.interval_integrable; continuity,
  all_goals { apply continuous.interval_integrable, continuity },
end

/-- The reduction formula for the integral of `sin x ^ n` for any natural `n ≥ 2`. -/
lemma integral_sin_pow :
  ∫ x in a..b, sin x ^ (n + 2) = (sin a ^ (n + 1) * cos a - sin b ^ (n + 1) * cos b) / (n + 2)
    + (n + 1) / (n + 2) * ∫ x in a..b, sin x ^ n :=
begin
  have : (n : ℝ) + 2 ≠ 0 := by exact_mod_cast succ_ne_zero n.succ,
  field_simp,
  convert eq_sub_iff_add_eq.mp (integral_sin_pow_aux n),
  ring,
end

@[simp]
lemma integral_sin_sq : ∫ x in a..b, sin x ^ 2 = (sin a * cos a - sin b * cos b + b - a) / 2 :=
by field_simp [integral_sin_pow, add_sub_assoc]

theorem integral_sin_pow_odd :
  ∫ x in 0..π, sin x ^ (2 * n + 1) = 2 * ∏ i in range n, (2 * i + 2) / (2 * i + 3) :=
begin
  induction n with k ih, { norm_num },
  rw [prod_range_succ_comm, mul_left_comm, ← ih, mul_succ, integral_sin_pow],
  norm_cast,
  simp [-cast_add] with field_simps,
end

theorem integral_sin_pow_even :
  ∫ x in 0..π, sin x ^ (2 * n) = π * ∏ i in range n, (2 * i + 1) / (2 * i + 2) :=
begin
  induction n with k ih, { simp },
  rw [prod_range_succ_comm, mul_left_comm, ← ih, mul_succ, integral_sin_pow],
  norm_cast,
  simp [-cast_add] with field_simps,
end

lemma integral_sin_pow_pos : 0 < ∫ x in 0..π, sin x ^ n :=
begin
  rcases even_or_odd' n with ⟨k, (rfl | rfl)⟩;
  simp only [integral_sin_pow_even, integral_sin_pow_odd];
  refine mul_pos (by norm_num [pi_pos]) (prod_pos (λ n hn, div_pos _ _));
  norm_cast;
  linarith,
end

lemma integral_sin_pow_succ_le : ∫ x in 0..π, sin x ^ (n + 1) ≤ ∫ x in 0..π, sin x ^ n :=
let H := λ x h, pow_le_pow_of_le_one (sin_nonneg_of_mem_Icc h) (sin_le_one x) (n.le_add_right 1) in
by refine integral_mono_on pi_pos.le _ _ H; exact (continuous_sin.pow _).interval_integrable 0 π

lemma integral_sin_pow_antitone : antitone (λ n : ℕ, ∫ x in 0..π, sin x ^ n) :=
antitone_nat_of_succ_le integral_sin_pow_succ_le

/-! ### Integral of `cos x ^ n` -/

lemma integral_cos_pow_aux :
  ∫ x in a..b, cos x ^ (n + 2) = cos b ^ (n + 1) * sin b - cos a ^ (n + 1) * sin a
    + (n + 1) * (∫ x in a..b, cos x ^ n) - (n + 1) * ∫ x in a..b, cos x ^ (n + 2) :=
begin
  let C := cos b ^ (n + 1) * sin b - cos a ^ (n + 1) * sin a,
  have h : ∀ α β γ : ℝ, (β * α * γ) * α = β * (α * α * γ) := λ α β γ, by ring,
  have hu : ∀ x ∈ _, has_deriv_at (λ y, cos y ^ (n + 1)) (-(n + 1 : ℕ) * sin x * cos x ^ n) x :=
    λ x hx, by simpa only [mul_right_comm, neg_mul, mul_neg]
      using (has_deriv_at_cos x).pow (n+1),
  have hv : ∀ x ∈ [a, b], has_deriv_at sin (cos x) x := λ x hx, has_deriv_at_sin x,
  have H := integral_mul_deriv_eq_deriv_mul hu hv _ _,
  calc  ∫ x in a..b, cos x ^ (n + 2)
      = ∫ x in a..b, cos x ^ (n + 1) * cos x                   : by simp only [pow_succ']
  ... = C + (n + 1) * ∫ x in a..b, sin x ^ 2 * cos x ^ n       : by simp [H, h, sq, -neg_add_rev]
  ... = C + (n + 1) * ∫ x in a..b, cos x ^ n - cos x ^ (n + 2) : by simp [sin_sq, sub_mul,
                                                                          ← pow_add, add_comm]
  ... = C + (n + 1) * (∫ x in a..b, cos x ^ n) - (n + 1) * ∫ x in a..b, cos x ^ (n + 2) :
    by rw [integral_sub, mul_sub, add_sub_assoc]; apply continuous.interval_integrable; continuity,
  all_goals { apply continuous.interval_integrable, continuity },
end

/-- The reduction formula for the integral of `cos x ^ n` for any natural `n ≥ 2`. -/
lemma integral_cos_pow :
  ∫ x in a..b, cos x ^ (n + 2) = (cos b ^ (n + 1) * sin b - cos a ^ (n + 1) * sin a) / (n + 2)
    + (n + 1) / (n + 2) * ∫ x in a..b, cos x ^ n :=
begin
  have : (n : ℝ) + 2 ≠ 0 := by exact_mod_cast succ_ne_zero n.succ,
  field_simp,
  convert eq_sub_iff_add_eq.mp (integral_cos_pow_aux n),
  ring,
end

@[simp]
lemma integral_cos_sq : ∫ x in a..b, cos x ^ 2 = (cos b * sin b - cos a * sin a + b - a) / 2 :=
by field_simp [integral_cos_pow, add_sub_assoc]

/-! ### Integral of `sin x ^ m * cos x ^ n` -/

/-- Simplification of the integral of `sin x ^ m * cos x ^ n`, case `n` is odd. -/
lemma integral_sin_pow_mul_cos_pow_odd (m n : ℕ) :
  ∫ x in a..b, sin x ^ m * cos x ^ (2 * n + 1) = ∫ u in sin a..sin b, u ^ m * (1 - u ^ 2) ^ n :=
have hc : continuous (λ u : ℝ, u ^ m * (1 - u ^ 2) ^ n), by continuity,
calc  ∫ x in a..b, sin x ^ m * cos x ^ (2 * n + 1)
    = ∫ x in a..b, sin x ^ m * (1 - sin x ^ 2) ^ n * cos x : by simp only [pow_succ', ← mul_assoc,
                                                                           pow_mul, cos_sq']
... = ∫ u in sin a..sin b, u ^ m * (1 - u ^ 2) ^ n         : integral_comp_mul_deriv
                                                              (λ x hx, has_deriv_at_sin x)
                                                                continuous_on_cos hc

/-- The integral of `sin x * cos x`, given in terms of sin².
  See `integral_sin_mul_cos₂` below for the integral given in terms of cos². -/
@[simp]
lemma integral_sin_mul_cos₁ :
  ∫ x in a..b, sin x * cos x = (sin b ^ 2 - sin a ^ 2) / 2 :=
by simpa using integral_sin_pow_mul_cos_pow_odd 1 0

@[simp]
lemma integral_sin_sq_mul_cos :
  ∫ x in a..b, sin x ^ 2 * cos x = (sin b ^ 3 - sin a ^ 3) / 3 :=
by simpa using integral_sin_pow_mul_cos_pow_odd 2 0

@[simp]
lemma integral_cos_pow_three :
  ∫ x in a..b, cos x ^ 3 = sin b - sin a - (sin b ^ 3 - sin a ^ 3) / 3 :=
by simpa using integral_sin_pow_mul_cos_pow_odd 0 1

/-- Simplification of the integral of `sin x ^ m * cos x ^ n`, case `m` is odd. -/
lemma integral_sin_pow_odd_mul_cos_pow (m n : ℕ) :
  ∫ x in a..b, sin x ^ (2 * m + 1) * cos x ^ n = ∫ u in cos b..cos a, u ^ n * (1 - u ^ 2) ^ m :=
have hc : continuous (λ u : ℝ, u ^ n * (1 - u ^ 2) ^ m), by continuity,
calc   ∫ x in a..b, sin x ^ (2 * m + 1) * cos x ^ n
    = -∫ x in b..a, sin x ^ (2 * m + 1) * cos x ^ n          : by rw integral_symm
... =  ∫ x in b..a, (1 - cos x ^ 2) ^ m * -sin x * cos x ^ n : by simp [pow_succ', pow_mul, sin_sq]
... =  ∫ x in b..a, cos x ^ n * (1 - cos x ^ 2) ^ m * -sin x : by { congr, ext, ring }
... =  ∫ u in cos b..cos a, u ^ n * (1 - u ^ 2) ^ m          : integral_comp_mul_deriv
                                                                (λ x hx, has_deriv_at_cos x)
                                                                  continuous_on_sin.neg hc

/-- The integral of `sin x * cos x`, given in terms of cos².
See `integral_sin_mul_cos₁` above for the integral given in terms of sin². -/
lemma integral_sin_mul_cos₂  :
  ∫ x in a..b, sin x * cos x = (cos a ^ 2 - cos b ^ 2) / 2 :=
by simpa using integral_sin_pow_odd_mul_cos_pow 0 1

@[simp]
lemma integral_sin_mul_cos_sq :
  ∫ x in a..b, sin x * cos x ^ 2 = (cos a ^ 3 - cos b ^ 3) / 3 :=
by simpa using integral_sin_pow_odd_mul_cos_pow 0 2

@[simp]
lemma integral_sin_pow_three :
  ∫ x in a..b, sin x ^ 3 = cos a - cos b - (cos a ^ 3 - cos b ^ 3) / 3 :=
by simpa using integral_sin_pow_odd_mul_cos_pow 1 0

/-- Simplification of the integral of `sin x ^ m * cos x ^ n`, case `m` and `n` are both even. -/
lemma integral_sin_pow_even_mul_cos_pow_even (m n : ℕ) :
    ∫ x in a..b, sin x ^ (2 * m) * cos x ^ (2 * n)
  = ∫ x in a..b, ((1 - cos (2 * x)) / 2) ^ m * ((1 + cos (2 * x)) / 2) ^ n :=
by field_simp [pow_mul, sin_sq, cos_sq, ← sub_sub, (by ring : (2:ℝ) - 1 = 1)]

@[simp]
lemma integral_sin_sq_mul_cos_sq :
  ∫ x in a..b, sin x ^ 2 * cos x ^ 2 = (b - a) / 8 - (sin (4 * b) - sin (4 * a)) / 32 :=
begin
  convert integral_sin_pow_even_mul_cos_pow_even 1 1 using 1,
  have h1 : ∀ c : ℝ, (1 - c) / 2 * ((1 + c) / 2) = (1 - c ^ 2) / 4 := λ c, by ring,
  have h2 : continuous (λ x, cos (2 * x) ^ 2) := by continuity,
  have h3 : ∀ x, cos x * sin x = sin (2 * x) / 2, { intro, rw sin_two_mul, ring },
  have h4 : ∀ d : ℝ, 2 * (2 * d) = 4 * d := λ d, by ring,
  simp [h1, h2.interval_integrable, integral_comp_mul_left (λ x, cos x ^ 2), h3, h4],
  ring,
end


/-
Copyright (c) 2021 Yakov Pechersky. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yakov Pechersky

! This file was ported from Lean 3 source module logic.equiv.fintype
! leanprover-community/mathlib commit ee05e9ce1322178f0c12004eb93c00d2c8c00ed2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fintype.Basic
import Mathbin.GroupTheory.Perm.Sign
import Mathbin.Logic.Equiv.Defs

/-! # Equivalence between fintypes

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

This file contains some basic results on equivalences where one or both
sides of the equivalence are `fintype`s.

# Main definitions

 - `function.embedding.to_equiv_range`: computably turn an embedding of a
   fintype into an `equiv` of the domain to its range
 - `equiv.perm.via_fintype_embedding : perm α → (α ↪ β) → perm β` extends the domain of
   a permutation, fixing everything outside the range of the embedding

# Implementation details

 - `function.embedding.to_equiv_range` uses a computable inverse, but one that has poor
   computational performance, since it operates by exhaustive search over the input `fintype`s.
-/

variable {α β : Type _} [Fintype α] [DecidableEq β] (e : Equiv.Perm α) (f : α ↪ β)

#print Function.Embedding.toEquivRange /-
/-- Computably turn an embedding `f : α ↪ β` into an equiv `α ≃ set.range f`,
if `α` is a `fintype`. Has poor computational performance, due to exhaustive searching in
constructed inverse. When a better inverse is known, use `equiv.of_left_inverse'` or
`equiv.of_left_inverse` instead. This is the computable version of `equiv.of_injective`.
-/
def Function.Embedding.toEquivRange : α ≃ Set.range f :=
  ⟨fun a => ⟨f a, Set.mem_range_self a⟩, f.invOfMemRange, fun _ => by simp, fun _ => by simp⟩
#align function.embedding.to_equiv_range Function.Embedding.toEquivRange
-/

#print Function.Embedding.toEquivRange_apply /-
@[simp]
theorem Function.Embedding.toEquivRange_apply (a : α) :
    f.toEquivRange a = ⟨f a, Set.mem_range_self a⟩ :=
  rfl
#align function.embedding.to_equiv_range_apply Function.Embedding.toEquivRange_apply
-/

/- warning: function.embedding.to_equiv_range_symm_apply_self -> Function.Embedding.toEquivRange_symm_apply_self is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Fintype.{u1} α] [_inst_2 : DecidableEq.{succ u2} β] (f : Function.Embedding.{succ u1, succ u2} α β) (a : α), Eq.{succ u1} α (coeFn.{max 1 (max (succ u2) (succ u1)) (succ u1) (succ u2), max (succ u2) (succ u1)} (Equiv.{succ u2, succ u1} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (Set.range.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f))) α) (fun (_x : Equiv.{succ u2, succ u1} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (Set.range.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f))) α) => (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (Set.range.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f))) -> α) (Equiv.hasCoeToFun.{succ u2, succ u1} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (Set.range.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f))) α) (Equiv.symm.{succ u1, succ u2} α (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (Set.range.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f))) (Function.Embedding.toEquivRange.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f)) (Subtype.mk.{succ u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x (Set.range.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f))) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f a) (Set.mem_range_self.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f) a))) a
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Fintype.{u2} α] [_inst_2 : DecidableEq.{succ u1} β] (f : Function.Embedding.{succ u2, succ u1} α β) (a : α), Eq.{succ u2} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) => α) (Subtype.mk.{succ u1} β (fun (x : β) => Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f a) (Set.mem_range_self.{succ u2, u1} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f) a))) (FunLike.coe.{max (succ u2) (succ u1), succ u1, succ u2} (Equiv.{succ u1, succ u2} (Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) α) (Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) (fun (_x : Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) => α) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u2} (Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) α) (Equiv.symm.{succ u2, succ u1} α (Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) (Function.Embedding.toEquivRange.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f)) (Subtype.mk.{succ u1} β (fun (x : β) => Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f))) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f a) (Set.mem_range_self.{succ u2, u1} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f) a))) a
Case conversion may be inaccurate. Consider using '#align function.embedding.to_equiv_range_symm_apply_self Function.Embedding.toEquivRange_symm_apply_selfₓ'. -/
@[simp]
theorem Function.Embedding.toEquivRange_symm_apply_self (a : α) :
    f.toEquivRange.symm ⟨f a, Set.mem_range_self a⟩ = a := by simp [Equiv.symm_apply_eq]
#align function.embedding.to_equiv_range_symm_apply_self Function.Embedding.toEquivRange_symm_apply_self

/- warning: function.embedding.to_equiv_range_eq_of_injective -> Function.Embedding.toEquivRange_eq_ofInjective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Fintype.{u1} α] [_inst_2 : DecidableEq.{succ u2} β] (f : Function.Embedding.{succ u1, succ u2} α β), Eq.{max 1 (max (succ u1) (succ u2)) (succ u2) (succ u1)} (Equiv.{succ u1, succ u2} α (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (Set.range.{u2, succ u1} β α (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f)))) (Function.Embedding.toEquivRange.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f) (Equiv.ofInjective.{succ u1, u2} α β (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f) (Function.Embedding.injective.{succ u1, succ u2} α β f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Fintype.{u2} α] [_inst_2 : DecidableEq.{succ u1} β] (f : Function.Embedding.{succ u2, succ u1} α β), Eq.{max (succ u2) (succ u1)} (Equiv.{succ u2, succ u1} α (Set.Elem.{u1} β (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f)))) (Function.Embedding.toEquivRange.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f) (Equiv.ofInjective.{succ u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f) (Function.Embedding.injective.{succ u1, succ u2} α β f))
Case conversion may be inaccurate. Consider using '#align function.embedding.to_equiv_range_eq_of_injective Function.Embedding.toEquivRange_eq_ofInjectiveₓ'. -/
theorem Function.Embedding.toEquivRange_eq_ofInjective :
    f.toEquivRange = Equiv.ofInjective f f.Injective :=
  by
  ext
  simp
#align function.embedding.to_equiv_range_eq_of_injective Function.Embedding.toEquivRange_eq_ofInjective

#print Equiv.Perm.viaFintypeEmbedding /-
/-- Extend the domain of `e : equiv.perm α`, mapping it through `f : α ↪ β`.
Everything outside of `set.range f` is kept fixed. Has poor computational performance,
due to exhaustive searching in constructed inverse due to using `function.embedding.to_equiv_range`.
When a better `α ≃ set.range f` is known, use `equiv.perm.via_set_range`.
When `[fintype α]` is not available, a noncomputable version is available as
`equiv.perm.via_embedding`.
-/
def Equiv.Perm.viaFintypeEmbedding : Equiv.Perm β :=
  e.extendDomain f.toEquivRange
#align equiv.perm.via_fintype_embedding Equiv.Perm.viaFintypeEmbedding
-/

#print Equiv.Perm.viaFintypeEmbedding_apply_image /-
@[simp]
theorem Equiv.Perm.viaFintypeEmbedding_apply_image (a : α) :
    e.viaFintypeEmbedding f (f a) = f (e a) :=
  by
  rw [Equiv.Perm.viaFintypeEmbedding]
  convert Equiv.Perm.extendDomain_apply_image e _ _
#align equiv.perm.via_fintype_embedding_apply_image Equiv.Perm.viaFintypeEmbedding_apply_image
-/

#print Equiv.Perm.viaFintypeEmbedding_apply_mem_range /-
theorem Equiv.Perm.viaFintypeEmbedding_apply_mem_range {b : β} (h : b ∈ Set.range f) :
    e.viaFintypeEmbedding f b = f (e (f.invOfMemRange ⟨b, h⟩)) := by
  simpa [Equiv.Perm.viaFintypeEmbedding, Equiv.Perm.extendDomain_apply_subtype, h]
#align equiv.perm.via_fintype_embedding_apply_mem_range Equiv.Perm.viaFintypeEmbedding_apply_mem_range
-/

#print Equiv.Perm.viaFintypeEmbedding_apply_not_mem_range /-
theorem Equiv.Perm.viaFintypeEmbedding_apply_not_mem_range {b : β} (h : b ∉ Set.range f) :
    e.viaFintypeEmbedding f b = b := by
  rwa [Equiv.Perm.viaFintypeEmbedding, Equiv.Perm.extendDomain_apply_not_subtype]
#align equiv.perm.via_fintype_embedding_apply_not_mem_range Equiv.Perm.viaFintypeEmbedding_apply_not_mem_range
-/

/- warning: equiv.perm.via_fintype_embedding_sign -> Equiv.Perm.viaFintypeEmbedding_sign is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Fintype.{u1} α] [_inst_2 : DecidableEq.{succ u2} β] (e : Equiv.Perm.{succ u1} α) (f : Function.Embedding.{succ u1, succ u2} α β) [_inst_3 : DecidableEq.{succ u1} α] [_inst_4 : Fintype.{u2} β], Eq.{1} (Units.{0} Int Int.monoid) (coeFn.{succ u2, succ u2} (MonoidHom.{u2, 0} (Equiv.Perm.{succ u2} β) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} β) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} β) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} β) (Equiv.Perm.permGroup.{u2} β)))) (Units.mulOneClass.{0} Int Int.monoid)) (fun (_x : MonoidHom.{u2, 0} (Equiv.Perm.{succ u2} β) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} β) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} β) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} β) (Equiv.Perm.permGroup.{u2} β)))) (Units.mulOneClass.{0} Int Int.monoid)) => (Equiv.Perm.{succ u2} β) -> (Units.{0} Int Int.monoid)) (MonoidHom.hasCoeToFun.{u2, 0} (Equiv.Perm.{succ u2} β) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} β) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} β) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} β) (Equiv.Perm.permGroup.{u2} β)))) (Units.mulOneClass.{0} Int Int.monoid)) (Equiv.Perm.sign.{u2} β (fun (a : β) (b : β) => _inst_2 a b) _inst_4) (Equiv.Perm.viaFintypeEmbedding.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) e f)) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) (fun (_x : MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) => (Equiv.Perm.{succ u1} α) -> (Units.{0} Int Int.monoid)) (MonoidHom.hasCoeToFun.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) (Equiv.Perm.sign.{u1} α (fun (a : α) (b : α) => _inst_3 a b) _inst_1) e)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Fintype.{u2} α] [_inst_2 : DecidableEq.{succ u1} β] (e : Equiv.Perm.{succ u2} α) (f : Function.Embedding.{succ u2, succ u1} α β) [_inst_3 : DecidableEq.{succ u2} α] [_inst_4 : Fintype.{u1} β], Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} β) => Units.{0} Int Int.instMonoidInt) (Equiv.Perm.viaFintypeEmbedding.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) e f)) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} β) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} β) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} β) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} β) (Equiv.Perm.permGroup.{u1} β)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} β) (fun (_x : Equiv.Perm.{succ u1} β) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} β) => Units.{0} Int Int.instMonoidInt) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} β) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} β) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} β) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} β) (Equiv.Perm.permGroup.{u1} β)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} β) (Units.{0} Int Int.instMonoidInt) (MulOneClass.toMul.{u1} (Equiv.Perm.{succ u1} β) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} β) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} β) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} β) (Equiv.Perm.permGroup.{u1} β))))) (MulOneClass.toMul.{0} (Units.{0} Int Int.instMonoidInt) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} β) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} β) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} β) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} β) (Equiv.Perm.permGroup.{u1} β)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} β) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} β) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} β) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} β) (Equiv.Perm.permGroup.{u1} β)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt) (MonoidHom.monoidHomClass.{u1, 0} (Equiv.Perm.{succ u1} β) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} β) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} β) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} β) (Equiv.Perm.permGroup.{u1} β)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)))) (Equiv.Perm.sign.{u1} β (fun (a : β) (b : β) => _inst_2 a b) _inst_4) (Equiv.Perm.viaFintypeEmbedding.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) e f)) (FunLike.coe.{succ u2, succ u2, 1} (MonoidHom.{u2, 0} (Equiv.Perm.{succ u2} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} α) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} α) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} α) (Equiv.Perm.permGroup.{u2} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u2} α) (fun (_x : Equiv.Perm.{succ u2} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u2} α) => Units.{0} Int Int.instMonoidInt) _x) (MulHomClass.toFunLike.{u2, u2, 0} (MonoidHom.{u2, 0} (Equiv.Perm.{succ u2} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} α) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} α) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} α) (Equiv.Perm.permGroup.{u2} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u2} α) (Units.{0} Int Int.instMonoidInt) (MulOneClass.toMul.{u2} (Equiv.Perm.{succ u2} α) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} α) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} α) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} α) (Equiv.Perm.permGroup.{u2} α))))) (MulOneClass.toMul.{0} (Units.{0} Int Int.instMonoidInt) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (MonoidHomClass.toMulHomClass.{u2, u2, 0} (MonoidHom.{u2, 0} (Equiv.Perm.{succ u2} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} α) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} α) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} α) (Equiv.Perm.permGroup.{u2} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u2} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} α) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} α) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} α) (Equiv.Perm.permGroup.{u2} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt) (MonoidHom.monoidHomClass.{u2, 0} (Equiv.Perm.{succ u2} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} α) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} α) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} α) (Equiv.Perm.permGroup.{u2} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)))) (Equiv.Perm.sign.{u2} α (fun (a : α) (b : α) => _inst_3 a b) _inst_1) e)
Case conversion may be inaccurate. Consider using '#align equiv.perm.via_fintype_embedding_sign Equiv.Perm.viaFintypeEmbedding_signₓ'. -/
@[simp]
theorem Equiv.Perm.viaFintypeEmbedding_sign [DecidableEq α] [Fintype β] :
    Equiv.Perm.sign (e.viaFintypeEmbedding f) = Equiv.Perm.sign e := by
  simp [Equiv.Perm.viaFintypeEmbedding]
#align equiv.perm.via_fintype_embedding_sign Equiv.Perm.viaFintypeEmbedding_sign

namespace Equiv

variable {p q : α → Prop} [DecidablePred p] [DecidablePred q]

#print Equiv.toCompl /-
/-- If `e` is an equivalence between two subtypes of a fintype `α`, `e.to_compl`
is an equivalence between the complement of those subtypes.

See also `equiv.compl`, for a computable version when a term of type
`{e' : α ≃ α // ∀ x : {x // p x}, e' x = e x}` is known. -/
noncomputable def toCompl (e : { x // p x } ≃ { x // q x }) : { x // ¬p x } ≃ { x // ¬q x } :=
  Classical.choice
    (Fintype.card_eq.mp (Fintype.card_compl_eq_card_compl _ _ (Fintype.card_congr e)))
#align equiv.to_compl Equiv.toCompl
-/

#print Equiv.extendSubtype /-
/-- If `e` is an equivalence between two subtypes of a fintype `α`, `e.extend_subtype`
is a permutation of `α` acting like `e` on the subtypes and doing something arbitrary outside.

Note that when `p = q`, `equiv.perm.subtype_congr e (equiv.refl _)` can be used instead. -/
noncomputable abbrev extendSubtype (e : { x // p x } ≃ { x // q x }) : Perm α :=
  subtypeCongr e e.toCompl
#align equiv.extend_subtype Equiv.extendSubtype
-/

#print Equiv.extendSubtype_apply_of_mem /-
theorem extendSubtype_apply_of_mem (e : { x // p x } ≃ { x // q x }) (x) (hx : p x) :
    e.extendSubtype x = e ⟨x, hx⟩ :=
  by
  dsimp only [extend_subtype]
  simp only [subtype_congr, Equiv.trans_apply, Equiv.sumCongr_apply]
  rw [sum_compl_apply_symm_of_pos _ _ hx, Sum.map_inl, sum_compl_apply_inl]
#align equiv.extend_subtype_apply_of_mem Equiv.extendSubtype_apply_of_mem
-/

#print Equiv.extendSubtype_mem /-
theorem extendSubtype_mem (e : { x // p x } ≃ { x // q x }) (x) (hx : p x) :
    q (e.extendSubtype x) := by
  convert(e ⟨x, hx⟩).2
  rw [e.extend_subtype_apply_of_mem _ hx, Subtype.val_eq_coe]
#align equiv.extend_subtype_mem Equiv.extendSubtype_mem
-/

#print Equiv.extendSubtype_apply_of_not_mem /-
theorem extendSubtype_apply_of_not_mem (e : { x // p x } ≃ { x // q x }) (x) (hx : ¬p x) :
    e.extendSubtype x = e.toCompl ⟨x, hx⟩ :=
  by
  dsimp only [extend_subtype]
  simp only [subtype_congr, Equiv.trans_apply, Equiv.sumCongr_apply]
  rw [sum_compl_apply_symm_of_neg _ _ hx, Sum.map_inr, sum_compl_apply_inr]
#align equiv.extend_subtype_apply_of_not_mem Equiv.extendSubtype_apply_of_not_mem
-/

#print Equiv.extendSubtype_not_mem /-
theorem extendSubtype_not_mem (e : { x // p x } ≃ { x // q x }) (x) (hx : ¬p x) :
    ¬q (e.extendSubtype x) := by
  convert(e.to_compl ⟨x, hx⟩).2
  rw [e.extend_subtype_apply_of_not_mem _ hx, Subtype.val_eq_coe]
#align equiv.extend_subtype_not_mem Equiv.extendSubtype_not_mem
-/

end Equiv


import tactic
import order.filter.partial
import algebra.support
import kent_convergence_space.basic
import data.fin.tuple

noncomputable theory
open set filter classical kent_convergence_space convergence_space
open_locale classical filter

variables {α β : Type*}

/-!
### Definition
-/

@[ext] class limit_space (α : Type*) extends convergence_space α :=
(sup_converges : ∀ {f g x}, converges f x → converges g x → converges (f ⊔ g) x)

open limit_space

namespace limit_space

instance has_coe : has_coe (limit_space α) (convergence_space α) :=
{ coe := λ p, p.to_convergence_space }

@[simp, norm_cast] theorem coe_inj {p q : limit_space α} :
  (↑p : convergence_space α)= ↑q ↔ p = q :=
by { rw limit_space.ext_iff, tauto }

lemma coe_injective : function.injective (coe : limit_space α → convergence_space α) :=
λ s t, coe_inj.1

end limit_space

@[simp] def sup_converges_ (p : limit_space α) {f g : filter α} {x : α}
  (hf : converges_ ↑p f x) (hg : converges_ ↑p g x) : converges_ ↑p (f ⊔ g) x
:= @sup_converges _ p _ _ _ hf hg

instance limit_space.to_kent_convergence_space
  [limit_space α] : kent_convergence_space α :=
{ converges := converges,
  pure_converges := pure_converges,
  le_converges := λ f g hle hconv, le_converges hle,
  kent_converges := λ f x hconv, sup_converges hconv (pure_converges x) }

lemma Sup_converges [limit_space α] {fs : set (filter α)} (hfin : fs.finite)
  {x : α} (hconv : ∀ f ∈ fs, converges f x) : converges (Sup fs) x :=
begin
  refine set.finite.induction_on' hfin _ _,
  { simp, exact bot_converges x },
  { intros g gs hmem hsub hnmem hSup,
    rw [set.insert_eq, Sup_union], simp,
    have hg : converges g x := hconv g hmem,
    exact sup_converges hg hSup}
end

lemma Sup_converges_ (p : limit_space α) {fs : set (filter α)} (hfin : fs.finite)
  {x : α} (hconv : ∀ f ∈ fs, converges_ ↑p f x) : converges_ ↑p (Sup fs) x :=
@Sup_converges _ p fs hfin x hconv

/-!
### Ordering
-/

/-- The ordering is the one inherited from convergence spaces. -/

instance : has_le (limit_space α) := ⟨λ p q, ↑p ≤ (↑q : convergence_space α)⟩

instance : partial_order (limit_space α) :=
  partial_order.lift coe limit_space.coe_injective

/-!
### Lattice of limit structures
-/

/-- Limit structures form a complete lattice. Infimums are formed like in convergence
  spaces. Supremums are different: `h` converges to `x` with respect to `p ⊔ q` when there
  exists filters `g, h` such that `g` converges to `x` with respect to `p`, `h` converges
  to `x` with respect to `q` and `f ≤ g ⊔ h`. -/

instance : has_top (limit_space α) :=
{ top := { sup_converges := by tauto, ..convergence_space.has_top.top }}

instance : has_bot (limit_space α) :=
{ bot :=
  { sup_converges :=
    begin
      intros f g x hf hg,
      calc f ⊔ g ≤ pure x ⊔ pure x : sup_le_sup hf hg
      ... = pure x : sup_idem,
    end,
    ..convergence_space.has_bot.bot }}

instance : has_inf (limit_space α) :=
{ inf := λ p q, let super : convergence_space α := ↑p ⊓ ↑q in
  { converges := converges_ super,
    pure_converges := pure_converges_ super,
    le_converges := le_converges_ super,
    sup_converges := λ f g x hf hg,
     ⟨sup_converges_ p hf.1 hg.1, sup_converges_ q hf.2 hg.2⟩ }}

instance : has_sup (limit_space α) :=
{ sup := λ p q,
  { converges := λ f x, ∃ g₁ g₂, f ≤ g₁ ⊔ g₂ ∧ converges_ ↑p g₁ x ∧ converges_ ↑q g₂ x,
    pure_converges := λ x, by refine ⟨pure x, pure x, _, _, _⟩; simp,
    le_converges :=
    begin
      rintros f g hle x ⟨g₁, g₂, hle', hg₁, hg₂⟩,
      exact ⟨g₁, g₂, trans hle hle', hg₁, hg₂⟩
    end,
    sup_converges :=
    begin
      rintros f f' x ⟨g₁, g₂, hle, hg₁, hg₂⟩ ⟨g₁', g₂', hle', hg₁', hg₂'⟩,
      refine ⟨g₁ ⊔ g₁', g₂ ⊔ g₂', _, sup_converges_ p hg₁ hg₁', sup_converges_ q hg₂ hg₂'⟩,
      calc f ⊔ f' ≤ (g₁ ⊔ g₂) ⊔ (g₁' ⊔ g₂') : sup_le_sup hle hle'
      ... = (g₁ ⊔ g₁') ⊔ (g₂ ⊔ g₂') : sup_sup_sup_comm g₁ g₂ g₁' g₂'
    end }}

instance : has_Inf (limit_space α) :=
  { Inf := λ ps, let super : convergence_space α := Inf (coe '' ps) in
    { converges := converges_ super,
      pure_converges := pure_converges_ super,
      le_converges := le_converges_ super,
      sup_converges :=
      begin
        intros f g x hf hg p hp,
        obtain ⟨q, hq, heq⟩ := mem_image_iff_bex.mp hp,
        rw ← heq at *,
        exact sup_converges_ q (hf hp) (hg hp)
      end }}

instance : has_Sup (limit_space α) :=
{ Sup := λ ps, let super : convergence_space α := Sup (coe '' ps) in
  { converges := λ f x, ∃ gs : set (filter α), gs.nonempty ∧ gs.finite ∧
      f ≤ Sup gs ∧ ∀ g, g ∈ gs → converges_ super g x,
    pure_converges := λ x,
      ⟨{ pure x }, set.singleton_nonempty (pure x), set.finite_singleton (pure x), by simp, by simp⟩,
    le_converges :=
    begin
      intros f f' hle x hconv,
      obtain ⟨gs, hne, hfin, hle', hconv'⟩ := hconv,
      exact ⟨gs, hne, hfin, le_trans hle hle', hconv'⟩
    end,
    sup_converges :=
    begin
      rintros f₁ f₂ x hf₁ hf₂,
      obtain ⟨gs₁, hne₁, hfin₁, hle₁, hconv₁⟩ := hf₁,
      obtain ⟨gs₂, hne₂, hfin₂, hle₂, hconv₂⟩ := hf₂,
      refine ⟨gs₁ ∪ gs₂, set.nonempty.inl hne₁, set.finite_union.mpr ⟨hfin₁, hfin₂⟩, _, _⟩,
      rw Sup_union,
      exact sup_le_sup hle₁ hle₂,
      intros g hmem, cases hmem,
      { exact hconv₁ g hmem },
      { exact hconv₂ g hmem }
    end }}

instance : semilattice_inf (limit_space α) :=
by { refine function.injective.semilattice_inf coe limit_space.coe_injective _, tauto }

instance : semilattice_sup (limit_space α) :=
{ le_sup_left := λ p q f x hconv, ⟨f, pure x, le_sup_left, hconv, pure_converges_ ↑q x⟩,
  le_sup_right := λ p q f x hconv, ⟨pure x, f, le_sup_right, pure_converges_ ↑p x, hconv⟩,
  sup_le := λ p q r hp hq f x ⟨g₁, g₂, hle, hg₁, hg₂⟩,
    le_converges_ r hle (sup_converges_ r (hp hg₁) (hq hg₂)),
  ..limit_space.partial_order,
  ..limit_space.has_sup }

lemma limit_space.coe_Inf (ps : set (limit_space α)) :
  (↑(Inf ps) : convergence_space α) = Inf (coe '' ps) :=
by { ext, tauto }

instance : complete_semilattice_Inf (limit_space α) :=
{ Inf_le :=
  begin
    intros ps p hmem f x hconv,
    rw limit_space.coe_Inf at hconv,
    exact hconv (mem_image_of_mem coe hmem),
  end,
  le_Inf :=
  begin
    intros ps q hle,
    change ↑q ≤ ↑(Inf ps),
    rw limit_space.coe_Inf,
    intros f x hconv p hp,
    obtain ⟨r, hr, heq⟩ := mem_image_iff_bex.mp hp,
    rw ← heq,
    exact hle r hr hconv,
  end,
  ..limit_space.partial_order,
  ..limit_space.has_Inf }

instance : complete_semilattice_Sup (limit_space α) :=
{ le_Sup :=
  begin
    intros ps p hp f x hconv,
    refine ⟨{ f }, set.singleton_nonempty f, set.finite_singleton f, by simp, _⟩,
    intros g hmem,
    rw set.mem_singleton_iff.mp hmem,
    exact or.inr ⟨p, mem_image_of_mem coe hp, hconv⟩,
  end,
  Sup_le :=
  begin
    rintros qs p hle f x ⟨gs, hne, hfin, hle', hconv⟩,
    have hg : ∀ g ∈ gs, converges_ ↑p g x,
    begin
      intros g hmem,
      let hconv' : g ≤ pure x ∨
        ∃ q : convergence_space α, q ∈ coe '' qs ∧ converges_ q g x := hconv g hmem,
      exact hconv'.elim
        (λ hg : g ≤ pure x, le_converges_ ↑p hg (pure_converges_ ↑p x))
        (λ hexists : (∃ q : convergence_space α, q ∈ coe '' qs ∧ converges_ q g x),
        begin
          obtain ⟨q, hq, hg⟩ := hexists,
          rw set.mem_image_iff_bex at hq,
          obtain ⟨q', hq', heq⟩ := hq,
          rw ← heq at hg,
          exact (hle q' hq') hg,
        end),
    end,
    have hSup : converges_ ↑p (Sup gs) x, from Sup_converges_ p hfin hg,
    exact le_converges_ p hle' hSup,
  end,
  ..limit_space.partial_order,
  ..limit_space.has_Sup }

instance : lattice (limit_space α) :=
{ ..limit_space.semilattice_sup,
  ..limit_space.semilattice_inf }

instance : complete_lattice (limit_space α) :=
{ bot_le := λ p f x hconv, le_converges_ p hconv (pure_converges_ p x),
  le_top := by intros; tauto,
  ..limit_space.has_bot,
  ..limit_space.has_top,
  ..limit_space.lattice,
  ..limit_space.complete_semilattice_Inf,
  ..limit_space.complete_semilattice_Sup }