/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro

! This file was ported from Lean 3 source module data.nat.prime
! leanprover-community/mathlib commit 8631e2d5ea77f6c13054d9151d82b83069680cb1
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Associated
import Mathlib.Algebra.Parity
import Mathlib.Data.Int.Dvd.Basic
import Mathlib.Data.Int.Units
import Mathlib.Data.Nat.Factorial.Basic
import Mathlib.Data.Nat.GCD.Basic
import Mathlib.Data.Nat.Sqrt
import Mathlib.Order.Bounds.Basic
import Mathlib.Tactic.ByContra

/-!
# Prime numbers

This file deals with prime numbers: natural numbers `p ≥ 2` whose only divisors are `p` and `1`.

## Important declarations

- `Nat.Prime`: the predicate that expresses that a natural number `p` is prime
- `Nat.Primes`: the subtype of natural numbers that are prime
- `Nat.minFac n`: the minimal prime factor of a natural number `n ≠ 1`
- `Nat.exists_infinite_primes`: Euclid's theorem that there exist infinitely many prime numbers.
  This also appears as `Nat.not_bddAbove_setOf_prime` and `Nat.infinite_setOf_prime` (the latter
  in `Data.Nat.PrimeFin`).
- `Nat.prime_iff`: `Nat.Prime` coincides with the general definition of `Prime`
- `Nat.irreducible_iff_nat_prime`: a non-unit natural number is
                                  only divisible by `1` iff it is prime

-/

open Bool Subtype

open Nat

namespace Nat

/-- `Nat.Prime p` means that `p` is a prime number, that is, a natural number
  at least 2 whose only divisors are `p` and `1`. -/
-- Porting note: removed @[pp_nodot]
def Prime (p : ℕ) :=
  Irreducible p
#align nat.prime Nat.Prime

theorem irreducible_iff_nat_prime (a : ℕ) : Irreducible a ↔ Nat.Prime a :=
  Iff.rfl
#align irreducible_iff_nat_prime Nat.irreducible_iff_nat_prime

theorem not_prime_zero : ¬Prime 0
  | h => h.ne_zero rfl
#align nat.not_prime_zero Nat.not_prime_zero

theorem not_prime_one : ¬Prime 1
  | h => h.ne_one rfl
#align nat.not_prime_one Nat.not_prime_one

theorem Prime.ne_zero {n : ℕ} (h : Prime n) : n ≠ 0 :=
  Irreducible.ne_zero h
#align nat.prime.ne_zero Nat.Prime.ne_zero

theorem Prime.pos {p : ℕ} (pp : Prime p) : 0 < p :=
  Nat.pos_of_ne_zero pp.ne_zero
#align nat.prime.pos Nat.Prime.pos

theorem Prime.two_le : ∀ {p : ℕ}, Prime p → 2 ≤ p
  | 0, h => (not_prime_zero h).elim
  | 1, h => (not_prime_one h).elim
  | _ + 2, _ => le_add_self
#align nat.prime.two_le Nat.Prime.two_le

theorem Prime.one_lt {p : ℕ} : Prime p → 1 < p :=
  Prime.two_le
#align nat.prime.one_lt Nat.Prime.one_lt

instance Prime.one_lt' (p : ℕ) [hp : Fact p.Prime] : Fact (1 < p) :=
  ⟨hp.1.one_lt⟩
#align nat.prime.one_lt' Nat.Prime.one_lt'

theorem Prime.ne_one {p : ℕ} (hp : p.Prime) : p ≠ 1 :=
  hp.one_lt.ne'
#align nat.prime.ne_one Nat.Prime.ne_one

theorem Prime.eq_one_or_self_of_dvd {p : ℕ} (pp : p.Prime) (m : ℕ) (hm : m ∣ p) :
    m = 1 ∨ m = p := by
  obtain ⟨n, hn⟩ := hm
  have := pp.isUnit_or_isUnit hn
  rw [Nat.isUnit_iff, Nat.isUnit_iff] at this
  apply Or.imp_right _ this
  rintro rfl
  rw [hn, mul_one]
#align nat.prime.eq_one_or_self_of_dvd Nat.Prime.eq_one_or_self_of_dvd

theorem prime_def_lt'' {p : ℕ} : Prime p ↔ 2 ≤ p ∧ ∀ (m) (_ : m ∣ p), m = 1 ∨ m = p := by
  refine' ⟨fun h => ⟨h.two_le, h.eq_one_or_self_of_dvd⟩, fun h => _⟩
  -- Porting note: needed to make ℕ explicit
  have h1 := (@one_lt_two ℕ ..).trans_le h.1
  refine' ⟨mt Nat.isUnit_iff.mp h1.ne', fun a b hab => _⟩
  simp only [Nat.isUnit_iff]
  apply Or.imp_right _ (h.2 a _)
  · rintro rfl
    rw [← mul_right_inj' (pos_of_gt h1).ne', ← hab, mul_one]
  · rw [hab]
    exact dvd_mul_right _ _
#align nat.prime_def_lt'' Nat.prime_def_lt''

theorem prime_def_lt {p : ℕ} : Prime p ↔ 2 ≤ p ∧ ∀ m < p, m ∣ p → m = 1 :=
  prime_def_lt''.trans <|
    and_congr_right fun p2 =>
      forall_congr' fun _ =>
        ⟨fun h l d => (h d).resolve_right (ne_of_lt l), fun h d =>
          (le_of_dvd (le_of_succ_le p2) d).lt_or_eq_dec.imp_left fun l => h l d⟩
#align nat.prime_def_lt Nat.prime_def_lt

theorem prime_def_lt' {p : ℕ} : Prime p ↔ 2 ≤ p ∧ ∀ m, 2 ≤ m → m < p → ¬m ∣ p :=
  prime_def_lt.trans <|
    and_congr_right fun p2 =>
      forall_congr' fun m =>
        ⟨fun h m2 l d => not_lt_of_ge m2 ((h l d).symm ▸ by decide), fun h l d => by
          rcases m with (_ | _ | m)
          · rw [eq_zero_of_zero_dvd d] at p2
            revert p2
            decide
          · rfl
          · exact (h le_add_self l).elim d⟩
#align nat.prime_def_lt' Nat.prime_def_lt'

theorem prime_def_le_sqrt {p : ℕ} : Prime p ↔ 2 ≤ p ∧ ∀ m, 2 ≤ m → m ≤ sqrt p → ¬m ∣ p :=
  prime_def_lt'.trans <|
    and_congr_right fun p2 =>
      ⟨fun a m m2 l => a m m2 <| lt_of_le_of_lt l <| sqrt_lt_self p2, fun a =>
        have : ∀ {m k : ℕ}, m ≤ k → 1 < m → p ≠ m * k := fun {m k} mk m1 e =>
          a m m1 (le_sqrt.2 (e.symm ▸ Nat.mul_le_mul_left m mk)) ⟨k, e⟩
        fun m m2 l ⟨k, e⟩ => by
        cases' le_total m k with mk km
        · exact this mk m2 e
        · rw [mul_comm] at e
          refine' this km (lt_of_mul_lt_mul_right _ (zero_le m)) e
          rwa [one_mul, ← e]⟩
#align nat.prime_def_le_sqrt Nat.prime_def_le_sqrt

theorem prime_of_coprime (n : ℕ) (h1 : 1 < n) (h : ∀ m < n, m ≠ 0 → n.coprime m) : Prime n := by
  refine' prime_def_lt.mpr ⟨h1, fun m mlt mdvd => _⟩
  have hm : m ≠ 0 := by
    rintro rfl
    rw [zero_dvd_iff] at mdvd
    exact mlt.ne' mdvd
  exact (h m mlt hm).symm.eq_one_of_dvd mdvd
#align nat.prime_of_coprime Nat.prime_of_coprime

section

/-- This instance is slower than the instance `decidablePrime` defined below,
  but has the advantage that it works in the kernel for small values.

  If you need to prove that a particular number is prime, in any case
  you should not use `by decide`, but rather `by norm_num`, which is
  much faster.
  -/
@[local instance]
def decidablePrime1 (p : ℕ) : Decidable (Prime p) :=
  decidable_of_iff' _ prime_def_lt'
#align nat.decidable_prime_1 Nat.decidablePrime1

theorem prime_two : Prime 2 := by decide
#align nat.prime_two Nat.prime_two

theorem prime_three : Prime 3 := by decide
#align nat.prime_three Nat.prime_three

theorem Prime.five_le_of_ne_two_of_ne_three {p : ℕ} (hp : p.Prime) (h_two : p ≠ 2)
    (h_three : p ≠ 3) : 5 ≤ p := by
  by_contra' h
  revert h_two h_three hp
  -- Porting note: was `decide!`
  match p with
  | 0 => decide
  | 1 => decide
  | 2 => decide
  | 3 => decide
  | 4 => decide
  | n + 5 => exact (h.not_le le_add_self).elim
#align nat.prime.five_le_of_ne_two_of_ne_three Nat.Prime.five_le_of_ne_two_of_ne_three

end

theorem Prime.pred_pos {p : ℕ} (pp : Prime p) : 0 < pred p :=
  lt_pred_iff.2 pp.one_lt
#align nat.prime.pred_pos Nat.Prime.pred_pos

theorem succ_pred_prime {p : ℕ} (pp : Prime p) : succ (pred p) = p :=
  succ_pred_eq_of_pos pp.pos
#align nat.succ_pred_prime Nat.succ_pred_prime

theorem dvd_prime {p m : ℕ} (pp : Prime p) : m ∣ p ↔ m = 1 ∨ m = p :=
  ⟨fun d => pp.eq_one_or_self_of_dvd m d, fun h =>
    h.elim (fun e => e.symm ▸ one_dvd _) fun e => e.symm ▸ dvd_rfl⟩
#align nat.dvd_prime Nat.dvd_prime

theorem dvd_prime_two_le {p m : ℕ} (pp : Prime p) (H : 2 ≤ m) : m ∣ p ↔ m = p :=
  (dvd_prime pp).trans <| or_iff_right_of_imp <| Not.elim <| ne_of_gt H
#align nat.dvd_prime_two_le Nat.dvd_prime_two_le

theorem prime_dvd_prime_iff_eq {p q : ℕ} (pp : p.Prime) (qp : q.Prime) : p ∣ q ↔ p = q :=
  dvd_prime_two_le qp (Prime.two_le pp)
#align nat.prime_dvd_prime_iff_eq Nat.prime_dvd_prime_iff_eq

theorem Prime.not_dvd_one {p : ℕ} (pp : Prime p) : ¬p ∣ 1 :=
  Irreducible.not_dvd_one pp
#align nat.prime.not_dvd_one Nat.Prime.not_dvd_one

theorem not_prime_mul {a b : ℕ} (a1 : 1 < a) (b1 : 1 < b) : ¬Prime (a * b) := fun h =>
  ne_of_lt (Nat.mul_lt_mul_of_pos_left b1 (lt_of_succ_lt a1)) <| by
    simpa using (dvd_prime_two_le h a1).1 (dvd_mul_right _ _)
#align nat.not_prime_mul Nat.not_prime_mul

theorem not_prime_mul' {a b n : ℕ} (h : a * b = n) (h₁ : 1 < a) (h₂ : 1 < b) : ¬Prime n := by
  rw [← h]
  exact not_prime_mul h₁ h₂
#align nat.not_prime_mul' Nat.not_prime_mul'

theorem prime_mul_iff {a b : ℕ} : Nat.Prime (a * b) ↔ a.Prime ∧ b = 1 ∨ b.Prime ∧ a = 1 := by
  simp only [iff_self_iff, irreducible_mul_iff, ← irreducible_iff_nat_prime, Nat.isUnit_iff]
#align nat.prime_mul_iff Nat.prime_mul_iff

theorem Prime.dvd_iff_eq {p a : ℕ} (hp : p.Prime) (a1 : a ≠ 1) : a ∣ p ↔ p = a := by
  refine'
    ⟨_, by
      rintro rfl
      rfl⟩
  rintro ⟨j, rfl⟩
  rcases prime_mul_iff.mp hp with (⟨_, rfl⟩ | ⟨_, rfl⟩)
  · exact mul_one _
  · exact (a1 rfl).elim
#align nat.prime.dvd_iff_eq Nat.Prime.dvd_iff_eq

section MinFac

theorem minFac_lemma (n k : ℕ) (h : ¬n < k * k) : sqrt n - k < sqrt n + 2 - k :=
  (tsub_lt_tsub_iff_right <| le_sqrt.2 <| le_of_not_gt h).2 <| Nat.lt_add_of_pos_right (by decide)
#align nat.min_fac_lemma Nat.minFac_lemma

/-- If `n < k * k`, then `minFacAux n k = n`, if `k | n`, then `minFacAux n k = k`.
  Otherwise, `minFacAux n k = minFacAux n (k+2)` using well-founded recursion.
  If `n` is odd and `1 < n`, then then `minFacAux n 3` is the smallest prime factor of `n`. -/
def minFacAux (n : ℕ) : ℕ → ℕ
  | k =>
    if h : n < k * k then n
    else
      if k ∣ n then k
      else
        have := minFac_lemma n k h
        minFacAux n (k + 2)
termination_by _ n k => sqrt n + 2 - k
#align nat.min_fac_aux Nat.minFacAux

/-- Returns the smallest prime factor of `n ≠ 1`. -/
def minFac : ℕ → ℕ
  | 0 => 2
  | 1 => 1
  | n + 2 => if 2 ∣ n then 2 else minFacAux (n + 2) 3
#align nat.min_fac Nat.minFac

@[simp]
theorem minFac_zero : minFac 0 = 2 :=
  rfl
#align nat.min_fac_zero Nat.minFac_zero

@[simp]
theorem minFac_one : minFac 1 = 1 :=
  rfl
#align nat.min_fac_one Nat.minFac_one

theorem minFac_eq : ∀ n, minFac n = if 2 ∣ n then 2 else minFacAux n 3
  | 0 => by simp
  | 1 => by simp [show 2 ≠ 1 by decide]
  | n + 2 => by
    simp [minFac]
#align nat.min_fac_eq Nat.minFac_eq

private def minFacProp (n k : ℕ) :=
  2 ≤ k ∧ k ∣ n ∧ ∀ m, 2 ≤ m → m ∣ n → k ≤ m

theorem minFacAux_has_prop {n : ℕ} (n2 : 2 ≤ n) :
    ∀ k i, k = 2 * i + 3 → (∀ m, 2 ≤ m → m ∣ n → k ≤ m) → minFacProp n (minFacAux n k)
  | k => fun i e a => by
    rw [minFacAux]
    by_cases h : n < k * k <;> simp [h]
    · have pp : Prime n :=
        prime_def_le_sqrt.2
          ⟨n2, fun m m2 l d => not_lt_of_ge l <| lt_of_lt_of_le (sqrt_lt.2 h) (a m m2 d)⟩
      exact ⟨n2, dvd_rfl, fun m m2 d => le_of_eq ((dvd_prime_two_le pp m2).1 d).symm⟩
    have k2 : 2 ≤ k := by
      subst e
      apply Nat.le_add_left
    by_cases dk : k ∣ n <;> simp [dk]
    · exact ⟨k2, dk, a⟩
    · refine'
        have := minFac_lemma n k h
        minFacAux_has_prop n2 (k + 2) (i + 1) (by simp [e, left_distrib]) fun m m2 d => _
      cases' Nat.eq_or_lt_of_le (a m m2 d) with me ml
      · subst me
        contradiction
      apply (Nat.eq_or_lt_of_le ml).resolve_left
      intro me
      rw [← me, e] at d
      have d' : 2 * (i + 2) ∣ n := d
      have := a _ le_rfl (dvd_of_mul_right_dvd d')
      rw [e] at this
      exact absurd this (by contradiction)
  termination_by _ n _ k => sqrt n + 2 - k
#align nat.min_fac_aux_has_prop Nat.minFacAux_has_prop

theorem minFac_has_prop {n : ℕ} (n1 : n ≠ 1) : minFacProp n (minFac n) := by
  by_cases n0 : n = 0
  · simp [n0, minFacProp, GE.ge]
  have n2 : 2 ≤ n := by
    revert n0 n1
    rcases n with (_ | _ | _) <;> simp [succ_le_succ]
  simp [minFac_eq]
  by_cases d2 : 2 ∣ n <;> simp [d2]
  · exact ⟨le_rfl, d2, fun k k2 _ => k2⟩
  · refine'
      minFacAux_has_prop n2 3 0 rfl fun m m2 d => (Nat.eq_or_lt_of_le m2).resolve_left (mt _ d2)
    exact fun e => e.symm ▸ d
#align nat.min_fac_has_prop Nat.minFac_has_prop

theorem minFac_dvd (n : ℕ) : minFac n ∣ n :=
  if n1 : n = 1 then by simp [n1] else (minFac_has_prop n1).2.1
#align nat.min_fac_dvd Nat.minFac_dvd

theorem minFac_prime {n : ℕ} (n1 : n ≠ 1) : Prime (minFac n) :=
  let ⟨f2, fd, a⟩ := minFac_has_prop n1
  prime_def_lt'.2 ⟨f2, fun m m2 l d => not_le_of_gt l (a m m2 (d.trans fd))⟩
#align nat.min_fac_prime Nat.minFac_prime

theorem minFac_le_of_dvd {n : ℕ} : ∀ {m : ℕ}, 2 ≤ m → m ∣ n → minFac n ≤ m := by
  by_cases n1 : n = 1 <;> [exact fun m2 _ => n1.symm ▸ le_trans (by decide) m2,
    apply (minFac_has_prop n1).2.2]
#align nat.min_fac_le_of_dvd Nat.minFac_le_of_dvd

theorem minFac_pos (n : ℕ) : 0 < minFac n := by
  by_cases n1 : n = 1 <;> [exact n1.symm ▸ by decide, exact (minFac_prime n1).pos]
#align nat.min_fac_pos Nat.minFac_pos

theorem minFac_le {n : ℕ} (H : 0 < n) : minFac n ≤ n :=
  le_of_dvd H (minFac_dvd n)
#align nat.min_fac_le Nat.minFac_le

theorem le_minFac {m n : ℕ} : n = 1 ∨ m ≤ minFac n ↔ ∀ p, Prime p → p ∣ n → m ≤ p :=
  ⟨fun h p pp d =>
    h.elim (by rintro rfl; cases pp.not_dvd_one d) fun h =>
      le_trans h <| minFac_le_of_dvd pp.two_le d,
    fun H => or_iff_not_imp_left.2 fun n1 => H _ (minFac_prime n1) (minFac_dvd _)⟩
#align nat.le_min_fac Nat.le_minFac

theorem le_minFac' {m n : ℕ} : n = 1 ∨ m ≤ minFac n ↔ ∀ p, 2 ≤ p → p ∣ n → m ≤ p :=
  ⟨fun h p (pp : 1 < p) d =>
    h.elim (by rintro rfl ; cases not_le_of_lt pp (le_of_dvd (by decide) d)) fun h =>
      le_trans h <| minFac_le_of_dvd pp d,
    fun H => le_minFac.2 fun p pp d => H p pp.two_le d⟩
#align nat.le_min_fac' Nat.le_minFac'

theorem prime_def_minFac {p : ℕ} : Prime p ↔ 2 ≤ p ∧ minFac p = p :=
  ⟨fun pp =>
    ⟨pp.two_le,
      let ⟨f2, fd, _⟩ := minFac_has_prop <| ne_of_gt pp.one_lt
      ((dvd_prime pp).1 fd).resolve_left (ne_of_gt f2)⟩,
    fun ⟨p2, e⟩ => e ▸ minFac_prime (ne_of_gt p2)⟩
#align nat.prime_def_min_fac Nat.prime_def_minFac

@[simp]
theorem Prime.minFac_eq {p : ℕ} (hp : Prime p) : minFac p = p :=
  (prime_def_minFac.1 hp).2
#align nat.prime.min_fac_eq Nat.Prime.minFac_eq

/-- This instance is faster in the virtual machine than `decidablePrime1`,
but slower in the kernel.

If you need to prove that a particular number is prime, in any case
you should not use `by decide`, but rather `by norm_num`, which is
much faster.
-/
instance decidablePrime (p : ℕ) : Decidable (Prime p) :=
  decidable_of_iff' _ prime_def_minFac
#align nat.decidable_prime Nat.decidablePrime

theorem not_prime_iff_minFac_lt {n : ℕ} (n2 : 2 ≤ n) : ¬Prime n ↔ minFac n < n :=
  (not_congr <| prime_def_minFac.trans <| and_iff_right n2).trans <|
    (lt_iff_le_and_ne.trans <| and_iff_right <| minFac_le <| le_of_succ_le n2).symm
#align nat.not_prime_iff_min_fac_lt Nat.not_prime_iff_minFac_lt

theorem minFac_le_div {n : ℕ} (pos : 0 < n) (np : ¬Prime n) : minFac n ≤ n / minFac n :=
  match minFac_dvd n with
  | ⟨0, h0⟩ => absurd pos <| by rw [h0, mul_zero]; exact by decide
  | ⟨1, h1⟩ => by
    rw [mul_one] at h1
    rw [prime_def_minFac, not_and_or, ← h1, eq_self_iff_true, _root_.not_true, or_false_iff,
      not_le] at np
    rw [le_antisymm (le_of_lt_succ np) (succ_le_of_lt pos), minFac_one, Nat.div_one]
  | ⟨x + 2, hx⟩ => by
    conv_rhs =>
      congr
      rw [hx]
    rw [Nat.mul_div_cancel_left _ (minFac_pos _)]
    exact minFac_le_of_dvd (le_add_left 2 x) ⟨minFac n, by rwa [mul_comm]⟩
#align nat.min_fac_le_div Nat.minFac_le_div

/-- The square of the smallest prime factor of a composite number `n` is at most `n`.
-/
theorem minFac_sq_le_self {n : ℕ} (w : 0 < n) (h : ¬Prime n) : minFac n ^ 2 ≤ n :=
  have t : minFac n ≤ n / minFac n := minFac_le_div w h
  calc
    minFac n ^ 2 = minFac n * minFac n := sq (minFac n)
    _ ≤ n / minFac n * minFac n := Nat.mul_le_mul_right (minFac n) t
    _ ≤ n := div_mul_le_self n (minFac n)

#align nat.min_fac_sq_le_self Nat.minFac_sq_le_self

@[simp]
theorem minFac_eq_one_iff {n : ℕ} : minFac n = 1 ↔ n = 1 := by
  constructor
  · intro h
    by_contra hn
    have := minFac_prime hn
    rw [h] at this
    exact not_prime_one this
  · rintro rfl
    rfl
#align nat.min_fac_eq_one_iff Nat.minFac_eq_one_iff

@[simp]
theorem minFac_eq_two_iff (n : ℕ) : minFac n = 2 ↔ 2 ∣ n := by
  constructor
  · intro h
    rw [←h]
    exact minFac_dvd n
  · intro h
    have ub := minFac_le_of_dvd (le_refl 2) h
    have lb := minFac_pos n
    refine ub.eq_or_lt.resolve_right fun h' => ?_
    have := le_antisymm (Nat.succ_le_of_lt lb) (lt_succ_iff.mp h')
    rw [eq_comm, Nat.minFac_eq_one_iff] at this
    subst this
    exact not_lt_of_le (le_of_dvd zero_lt_one h) one_lt_two
#align nat.min_fac_eq_two_iff Nat.minFac_eq_two_iff

end MinFac

theorem exists_dvd_of_not_prime {n : ℕ} (n2 : 2 ≤ n) (np : ¬Prime n) : ∃ m, m ∣ n ∧ m ≠ 1 ∧ m ≠ n :=
  ⟨minFac n, minFac_dvd _, ne_of_gt (minFac_prime (ne_of_gt n2)).one_lt,
    ne_of_lt <| (not_prime_iff_minFac_lt n2).1 np⟩
#align nat.exists_dvd_of_not_prime Nat.exists_dvd_of_not_prime

theorem exists_dvd_of_not_prime2 {n : ℕ} (n2 : 2 ≤ n) (np : ¬Prime n) :
    ∃ m, m ∣ n ∧ 2 ≤ m ∧ m < n :=
  ⟨minFac n, minFac_dvd _, (minFac_prime (ne_of_gt n2)).two_le,
    (not_prime_iff_minFac_lt n2).1 np⟩
#align nat.exists_dvd_of_not_prime2 Nat.exists_dvd_of_not_prime2

theorem exists_prime_and_dvd {n : ℕ} (hn : n ≠ 1) : ∃ p, Prime p ∧ p ∣ n :=
  ⟨minFac n, minFac_prime hn, minFac_dvd _⟩
#align nat.exists_prime_and_dvd Nat.exists_prime_and_dvd

theorem dvd_of_forall_prime_mul_dvd {a b : ℕ}
    (hdvd : ∀ p : ℕ, p.Prime → p ∣ a → p * a ∣ b) : a ∣ b := by
  obtain rfl | ha := eq_or_ne a 1
  · apply one_dvd
  obtain ⟨p, hp⟩ := exists_prime_and_dvd ha
  exact _root_.trans (dvd_mul_left a p) (hdvd p hp.1 hp.2)
#align nat.dvd_of_forall_prime_mul_dvd Nat.dvd_of_forall_prime_mul_dvd

/-- Euclid's theorem on the **infinitude of primes**.
Here given in the form: for every `n`, there exists a prime number `p ≥ n`. -/
theorem exists_infinite_primes (n : ℕ) : ∃ p, n ≤ p ∧ Prime p :=
  let p := minFac (n ! + 1)
  have f1 : n ! + 1 ≠ 1 := ne_of_gt <| succ_lt_succ <| factorial_pos _
  have pp : Prime p := minFac_prime f1
  have np : n ≤ p :=
    le_of_not_ge fun h =>
      have h₁ : p ∣ n ! := dvd_factorial (minFac_pos _) h
      have h₂ : p ∣ 1 := (Nat.dvd_add_iff_right h₁).2 (minFac_dvd _)
      pp.not_dvd_one h₂
  ⟨p, np, pp⟩
#align nat.exists_infinite_primes Nat.exists_infinite_primes

/-- A version of `Nat.exists_infinite_primes` using the `BddAbove` predicate. -/
theorem not_bddAbove_setOf_prime : ¬BddAbove { p | Prime p } := by
  rw [not_bddAbove_iff]
  intro n
  obtain ⟨p, hi, hp⟩ := exists_infinite_primes n.succ
  exact ⟨p, hp, hi⟩
#align nat.not_bdd_above_set_of_prime Nat.not_bddAbove_setOf_prime

theorem Prime.eq_two_or_odd {p : ℕ} (hp : Prime p) : p = 2 ∨ p % 2 = 1 :=
  p.mod_two_eq_zero_or_one.imp_left fun h =>
    ((hp.eq_one_or_self_of_dvd 2 (dvd_of_mod_eq_zero h)).resolve_left (by decide)).symm
#align nat.prime.eq_two_or_odd Nat.Prime.eq_two_or_odd

theorem Prime.eq_two_or_odd' {p : ℕ} (hp : Prime p) : p = 2 ∨ Odd p :=
  Or.imp_right (fun h => ⟨p / 2, (div_add_mod p 2).symm.trans (congr_arg _ h)⟩) hp.eq_two_or_odd
#align nat.prime.eq_two_or_odd' Nat.Prime.eq_two_or_odd'

theorem Prime.even_iff {p : ℕ} (hp : Prime p) : Even p ↔ p = 2 := by
  rw [even_iff_two_dvd, prime_dvd_prime_iff_eq prime_two hp, eq_comm]
#align nat.prime.even_iff Nat.Prime.even_iff

theorem Prime.odd_of_ne_two {p : ℕ} (hp : p.Prime) (h_two : p ≠ 2) : Odd p :=
  hp.eq_two_or_odd'.resolve_left h_two
#align nat.prime.odd_of_ne_two Nat.Prime.odd_of_ne_two

/-- A prime `p` satisfies `p % 2 = 1` if and only if `p ≠ 2`. -/
theorem Prime.mod_two_eq_one_iff_ne_two {p : ℕ} [Fact p.Prime] : p % 2 = 1 ↔ p ≠ 2 := by
  refine' ⟨fun h hf => _, (Nat.Prime.eq_two_or_odd <| @Fact.out p.Prime _).resolve_left⟩
  rw [hf] at h
  simp at h
#align nat.prime.mod_two_eq_one_iff_ne_two Nat.Prime.mod_two_eq_one_iff_ne_two

theorem coprime_of_dvd {m n : ℕ} (H : ∀ k, Prime k → k ∣ m → ¬k ∣ n) : coprime m n := by
  rw [coprime_iff_gcd_eq_one]
  by_contra g2
  obtain ⟨p, hp, hpdvd⟩ := exists_prime_and_dvd g2
  apply H p hp <;> apply dvd_trans hpdvd
  · exact gcd_dvd_left _ _
  · exact gcd_dvd_right _ _
#align nat.coprime_of_dvd Nat.coprime_of_dvd

theorem coprime_of_dvd' {m n : ℕ} (H : ∀ k, Prime k → k ∣ m → k ∣ n → k ∣ 1) : coprime m n :=
  coprime_of_dvd fun k kp km kn => not_le_of_gt kp.one_lt <| le_of_dvd zero_lt_one <| H k kp km kn
#align nat.coprime_of_dvd' Nat.coprime_of_dvd'

theorem factors_lemma {k} : (k + 2) / minFac (k + 2) < k + 2 :=
  div_lt_self (Nat.zero_lt_succ _) (minFac_prime (by
      apply Nat.ne_of_gt
      apply Nat.succ_lt_succ
      apply Nat.zero_lt_succ
      )).one_lt
#align nat.factors_lemma Nat.factors_lemma

theorem Prime.coprime_iff_not_dvd {p n : ℕ} (pp : Prime p) : coprime p n ↔ ¬p ∣ n :=
  ⟨fun co d => pp.not_dvd_one <| co.dvd_of_dvd_mul_left (by simp [d]), fun nd =>
    coprime_of_dvd fun m m2 mp => ((prime_dvd_prime_iff_eq m2 pp).1 mp).symm ▸ nd⟩
#align nat.prime.coprime_iff_not_dvd Nat.Prime.coprime_iff_not_dvd

theorem Prime.dvd_iff_not_coprime {p n : ℕ} (pp : Prime p) : p ∣ n ↔ ¬coprime p n :=
  iff_not_comm.2 pp.coprime_iff_not_dvd
#align nat.prime.dvd_iff_not_coprime Nat.Prime.dvd_iff_not_coprime

theorem Prime.not_coprime_iff_dvd {m n : ℕ} : ¬coprime m n ↔ ∃ p, Prime p ∧ p ∣ m ∧ p ∣ n := by
  apply Iff.intro
  · intro h
    exact
      ⟨minFac (gcd m n), minFac_prime h, (minFac_dvd (gcd m n)).trans (gcd_dvd_left m n),
        (minFac_dvd (gcd m n)).trans (gcd_dvd_right m n)⟩
  · intro h
    cases' h with p hp
    apply Nat.not_coprime_of_dvd_of_dvd (Prime.one_lt hp.1) hp.2.1 hp.2.2
#align nat.prime.not_coprime_iff_dvd Nat.Prime.not_coprime_iff_dvd

theorem Prime.dvd_mul {p m n : ℕ} (pp : Prime p) : p ∣ m * n ↔ p ∣ m ∨ p ∣ n :=
  ⟨fun H => or_iff_not_imp_left.2 fun h => (pp.coprime_iff_not_dvd.2 h).dvd_of_dvd_mul_left H,
    Or.rec (fun h : p ∣ m => h.mul_right _) fun h : p ∣ n => h.mul_left _⟩
#align nat.prime.dvd_mul Nat.Prime.dvd_mul

theorem Prime.not_dvd_mul {p m n : ℕ} (pp : Prime p) (Hm : ¬p ∣ m) (Hn : ¬p ∣ n) : ¬p ∣ m * n :=
  mt pp.dvd_mul.1 <| by simp [Hm, Hn]
#align nat.prime.not_dvd_mul Nat.Prime.not_dvd_mul

theorem prime_iff {p : ℕ} : p.Prime ↔ _root_.Prime p :=
  ⟨fun h => ⟨h.ne_zero, h.not_unit, fun _ _ => h.dvd_mul.mp⟩, Prime.irreducible⟩
#align nat.prime_iff Nat.prime_iff

alias prime_iff ↔ Prime.prime _root_.Prime.nat_prime
#align nat.prime.prime Nat.Prime.prime
#align prime.nat_prime Prime.nat_prime

-- Porting note: attributes `protected`, `nolint dup_namespace` removed

theorem irreducible_iff_prime {p : ℕ} : Irreducible p ↔ _root_.Prime p :=
  prime_iff
#align nat.irreducible_iff_prime Nat.irreducible_iff_prime

theorem Prime.dvd_of_dvd_pow {p m n : ℕ} (pp : Prime p) (h : p ∣ m ^ n) : p ∣ m := by
  induction' n with n IH
  · exact pp.not_dvd_one.elim h
  · rw [pow_succ] at h
    exact (pp.dvd_mul.1 h).elim IH id
#align nat.prime.dvd_of_dvd_pow Nat.Prime.dvd_of_dvd_pow

theorem Prime.pow_not_prime {x n : ℕ} (hn : 2 ≤ n) : ¬(x ^ n).Prime := fun hp =>
  (hp.eq_one_or_self_of_dvd x <| dvd_trans ⟨x, sq _⟩ (pow_dvd_pow _ hn)).elim
    (fun hx1 => hp.ne_one <| hx1.symm ▸ one_pow _) fun hxn =>
    lt_irrefl x <|
      calc
        x = x ^ 1 := (pow_one _).symm
        _ < x ^ n := Nat.pow_right_strictMono (hxn.symm ▸ hp.two_le) hn
        _ = x := hxn.symm

#align nat.prime.pow_not_prime Nat.Prime.pow_not_prime

theorem Prime.pow_not_prime' {x : ℕ} : ∀ {n : ℕ}, n ≠ 1 → ¬(x ^ n).Prime
  | 0 => fun _ => not_prime_one
  | 1 => fun h => (h rfl).elim
  | _ + 2 => fun _ => Prime.pow_not_prime le_add_self
#align nat.prime.pow_not_prime' Nat.Prime.pow_not_prime'

theorem Prime.eq_one_of_pow {x n : ℕ} (h : (x ^ n).Prime) : n = 1 :=
  not_imp_not.mp Prime.pow_not_prime' h
#align nat.prime.eq_one_of_pow Nat.Prime.eq_one_of_pow

theorem Prime.pow_eq_iff {p a k : ℕ} (hp : p.Prime) : a ^ k = p ↔ a = p ∧ k = 1 := by
  refine' ⟨fun h => _, fun h => by rw [h.1, h.2, pow_one]⟩
  rw [← h] at hp
  rw [← h, hp.eq_one_of_pow, eq_self_iff_true, and_true_iff, pow_one]
#align nat.prime.pow_eq_iff Nat.Prime.pow_eq_iff

theorem pow_minFac {n k : ℕ} (hk : k ≠ 0) : (n ^ k).minFac = n.minFac := by
  rcases eq_or_ne n 1 with (rfl | hn)
  · simp
  have hnk : n ^ k ≠ 1 := fun hk' => hn ((pow_eq_one_iff hk).1 hk')
  apply (minFac_le_of_dvd (minFac_prime hn).two_le ((minFac_dvd n).pow hk)).antisymm
  apply
    minFac_le_of_dvd (minFac_prime hnk).two_le
      ((minFac_prime hnk).dvd_of_dvd_pow (minFac_dvd _))
#align nat.pow_min_fac Nat.pow_minFac

theorem Prime.pow_minFac {p k : ℕ} (hp : p.Prime) (hk : k ≠ 0) : (p ^ k).minFac = p := by
  rw [Nat.pow_minFac hk, hp.minFac_eq]
#align nat.prime.pow_min_fac Nat.Prime.pow_minFac

theorem Prime.mul_eq_prime_sq_iff {x y p : ℕ} (hp : p.Prime) (hx : x ≠ 1) (hy : y ≠ 1) :
    x * y = p ^ 2 ↔ x = p ∧ y = p := by
    refine' ⟨fun h => _, fun ⟨h₁, h₂⟩ => h₁.symm ▸ h₂.symm ▸ (sq _).symm⟩
    have pdvdxy : p ∣ x * y := by rw [h]; simp [sq]
    -- Could be `wlog := hp.dvd_mul.1 pdvdxy using x y`, but that imports more than we want.
    suffices ∀ x' y' : ℕ, x' ≠ 1 → y' ≠ 1 → x' * y' = p ^ 2 → p ∣ x' → x' = p ∧ y' = p by
      obtain hx | hy := hp.dvd_mul.1 pdvdxy <;>
        [skip, rw [And.comm]] <;>
        [skip, rw [mul_comm] at h pdvdxy] <;>
        apply this <;>
        assumption
    rintro x y hx hy h ⟨a, ha⟩
    have : a ∣ p := ⟨y, by rwa [ha, sq, mul_assoc, mul_right_inj' hp.ne_zero, eq_comm] at h⟩
    obtain ha1 | hap := (Nat.dvd_prime hp).mp ‹a ∣ p›
    · subst ha1
      rw [mul_one] at ha
      subst ha
      simp only [sq, mul_right_inj' hp.ne_zero] at h
      subst h
      exact ⟨rfl, rfl⟩
    · refine' (hy ?_).elim
      subst hap
      subst ha
      rw [sq, Nat.mul_right_eq_self_iff (Nat.mul_pos hp.pos hp.pos : 0 < a * a)] at h
      exact h

#align nat.prime.mul_eq_prime_sq_iff Nat.Prime.mul_eq_prime_sq_iff

theorem Prime.dvd_factorial : ∀ {n p : ℕ} (_ : Prime p), p ∣ n ! ↔ p ≤ n
  | 0, p, hp => iff_of_false hp.not_dvd_one (not_le_of_lt hp.pos)
  | n + 1, p, hp => by
    rw [factorial_succ, hp.dvd_mul, Prime.dvd_factorial hp]
    exact
      ⟨fun h => h.elim (le_of_dvd (succ_pos _)) le_succ_of_le, fun h =>
        (_root_.lt_or_eq_of_le h).elim (Or.inr ∘ le_of_lt_succ) fun h => Or.inl <| by rw [h]⟩
#align nat.prime.dvd_factorial Nat.Prime.dvd_factorial

theorem Prime.coprime_pow_of_not_dvd {p m a : ℕ} (pp : Prime p) (h : ¬p ∣ a) : coprime a (p ^ m) :=
  (pp.coprime_iff_not_dvd.2 h).symm.pow_right _
#align nat.prime.coprime_pow_of_not_dvd Nat.Prime.coprime_pow_of_not_dvd

theorem coprime_primes {p q : ℕ} (pp : Prime p) (pq : Prime q) : coprime p q ↔ p ≠ q :=
  pp.coprime_iff_not_dvd.trans <| not_congr <| dvd_prime_two_le pq pp.two_le
#align nat.coprime_primes Nat.coprime_primes

theorem coprime_pow_primes {p q : ℕ} (n m : ℕ) (pp : Prime p) (pq : Prime q) (h : p ≠ q) :
    coprime (p ^ n) (q ^ m) :=
  ((coprime_primes pp pq).2 h).pow _ _
#align nat.coprime_pow_primes Nat.coprime_pow_primes

theorem coprime_or_dvd_of_prime {p} (pp : Prime p) (i : ℕ) : coprime p i ∨ p ∣ i := by
  rw [pp.dvd_iff_not_coprime] ; apply em
#align nat.coprime_or_dvd_of_prime Nat.coprime_or_dvd_of_prime

theorem coprime_of_lt_prime {n p} (n_pos : 0 < n) (hlt : n < p) (pp : Prime p) : coprime p n :=
  (coprime_or_dvd_of_prime pp n).resolve_right fun h => lt_le_antisymm hlt (le_of_dvd n_pos h)
#align nat.coprime_of_lt_prime Nat.coprime_of_lt_prime

theorem eq_or_coprime_of_le_prime {n p} (n_pos : 0 < n) (hle : n ≤ p) (pp : Prime p) :
    p = n ∨ coprime p n :=
  hle.eq_or_lt.imp Eq.symm fun h => coprime_of_lt_prime n_pos h pp
#align nat.eq_or_coprime_of_le_prime Nat.eq_or_coprime_of_le_prime

theorem dvd_prime_pow {p : ℕ} (pp : Prime p) {m i : ℕ} : i ∣ p ^ m ↔ ∃ k ≤ m, i = p ^ k := by
  simp_rw [_root_.dvd_prime_pow  (prime_iff.mp pp)  m, associated_eq_eq]
#align nat.dvd_prime_pow Nat.dvd_prime_pow

theorem Prime.dvd_mul_of_dvd_ne {p1 p2 n : ℕ} (h_neq : p1 ≠ p2) (pp1 : Prime p1) (pp2 : Prime p2)
    (h1 : p1 ∣ n) (h2 : p2 ∣ n) : p1 * p2 ∣ n :=
  coprime.mul_dvd_of_dvd_of_dvd ((coprime_primes pp1 pp2).mpr h_neq) h1 h2
#align nat.prime.dvd_mul_of_dvd_ne Nat.Prime.dvd_mul_of_dvd_ne

/-- If `p` is prime,
and `a` doesn't divide `p^k`, but `a` does divide `p^(k+1)`
then `a = p^(k+1)`.
-/
theorem eq_prime_pow_of_dvd_least_prime_pow {a p k : ℕ} (pp : Prime p) (h₁ : ¬a ∣ p ^ k)
    (h₂ : a ∣ p ^ (k + 1)) : a = p ^ (k + 1) := by
  obtain ⟨l, ⟨h, rfl⟩⟩ := (dvd_prime_pow pp).1 h₂
  congr
  exact le_antisymm h (not_le.1 ((not_congr (pow_dvd_pow_iff_le_right (Prime.one_lt pp))).1 h₁))
#align nat.eq_prime_pow_of_dvd_least_prime_pow Nat.eq_prime_pow_of_dvd_least_prime_pow

theorem ne_one_iff_exists_prime_dvd : ∀ {n}, n ≠ 1 ↔ ∃ p : ℕ, p.Prime ∧ p ∣ n
  | 0 => by simpa using Exists.intro 2 Nat.prime_two
  | 1 => by simp [Nat.not_prime_one]
  | n + 2 => by
    let a := n + 2
    let ha : a ≠ 1 := Nat.succ_succ_ne_one n
    simp only [true_iff_iff, Ne.def, not_false_iff, ha]
    exact ⟨a.minFac, Nat.minFac_prime ha, a.minFac_dvd⟩
#align nat.ne_one_iff_exists_prime_dvd Nat.ne_one_iff_exists_prime_dvd

theorem eq_one_iff_not_exists_prime_dvd {n : ℕ} : n = 1 ↔ ∀ p : ℕ, p.Prime → ¬p ∣ n := by
  simpa using not_iff_not.mpr ne_one_iff_exists_prime_dvd
#align nat.eq_one_iff_not_exists_prime_dvd Nat.eq_one_iff_not_exists_prime_dvd

theorem succ_dvd_or_succ_dvd_of_succ_sum_dvd_mul {p : ℕ} (p_prime : Prime p) {m n k l : ℕ}
    (hpm : p ^ k ∣ m) (hpn : p ^ l ∣ n) (hpmn : p ^ (k + l + 1) ∣ m * n) :
    p ^ (k + 1) ∣ m ∨ p ^ (l + 1) ∣ n := by
  have hpd : p ^ (k + l) * p ∣ m * n := by
      let hpmn' : p ^ (succ (k + l)) ∣ m * n := hpmn
      rwa [pow_succ'] at hpmn'
  have hpd2 : p ∣ m * n / p ^ (k + l) := dvd_div_of_mul_dvd hpd
  have hpd3 : p ∣ m * n / (p ^ k * p ^ l) := by simpa [pow_add] using hpd2
  have hpd4 : p ∣ m / p ^ k * (n / p ^ l) := by simpa [Nat.div_mul_div_comm hpm hpn] using hpd3
  have hpd5 : p ∣ m / p ^ k ∨ p ∣ n / p ^ l :=
    (Prime.dvd_mul p_prime).1 hpd4
  suffices p ^ k * p ∣ m ∨ p ^ l * p ∣ n by rwa [_root_.pow_succ', _root_.pow_succ']
  exact hpd5.elim (fun h : p ∣ m / p ^ k => Or.inl <| mul_dvd_of_dvd_div hpm h)
    fun h : p ∣ n / p ^ l => Or.inr <| mul_dvd_of_dvd_div hpn h
#align nat.succ_dvd_or_succ_dvd_of_succ_sum_dvd_mul Nat.succ_dvd_or_succ_dvd_of_succ_sum_dvd_mul

theorem prime_iff_prime_int {p : ℕ} : p.Prime ↔ _root_.Prime (p : ℤ) :=
  ⟨fun hp =>
    ⟨Int.coe_nat_ne_zero_iff_pos.2 hp.pos, mt Int.isUnit_iff_natAbs_eq.1 hp.ne_one, fun a b h => by
      rw [← Int.dvd_natAbs, Int.coe_nat_dvd, Int.natAbs_mul, hp.dvd_mul] at h ;
        rwa [← Int.dvd_natAbs, Int.coe_nat_dvd, ← Int.dvd_natAbs, Int.coe_nat_dvd]⟩,
    fun hp =>
    Nat.prime_iff.2
      ⟨Int.coe_nat_ne_zero.1 hp.1,
        (mt Nat.isUnit_iff.1) fun h => by simp [h, not_prime_one] at hp, fun a b => by
        simpa only [Int.coe_nat_dvd, (Int.ofNat_mul _ _).symm] using hp.2.2 a b⟩⟩
#align nat.prime_iff_prime_int Nat.prime_iff_prime_int

/-- The type of prime numbers -/
def Primes :=
  { p : ℕ // p.Prime }
  deriving DecidableEq
#align nat.primes Nat.Primes

namespace Primes

instance : Repr Nat.Primes :=
  ⟨fun p _ => repr p.val⟩

instance inhabitedPrimes : Inhabited Primes :=
  ⟨⟨2, prime_two⟩⟩
#align nat.primes.inhabited_primes Nat.Primes.inhabitedPrimes

instance coeNat : Coe Nat.Primes ℕ :=
  ⟨Subtype.val⟩
#align nat.primes.coe_nat Nat.Primes.coeNat

-- Porting note: change in signature to match change in coercion
theorem coe_nat_injective : Function.Injective (fun (a : Nat.Primes) ↦ (a : ℕ)) :=
  Subtype.coe_injective
#align nat.primes.coe_nat_injective Nat.Primes.coe_nat_injective

theorem coe_nat_inj (p q : Nat.Primes) : (p : ℕ) = (q : ℕ) ↔ p = q :=
  Subtype.ext_iff.symm
#align nat.primes.coe_nat_inj Nat.Primes.coe_nat_inj

end Primes

instance monoid.primePow {α : Type _} [Monoid α] : Pow α Primes :=
  ⟨fun x p => x ^ (p : ℕ)⟩
#align nat.monoid.prime_pow Nat.monoid.primePow

end Nat

namespace Nat

instance fact_prime_two : Fact (Prime 2) :=
  ⟨prime_two⟩
#align nat.fact_prime_two Nat.fact_prime_two

instance fact_prime_three : Fact (Prime 3) :=
  ⟨prime_three⟩
#align nat.fact_prime_three Nat.fact_prime_three

end Nat

namespace Int

theorem prime_two : Prime (2 : ℤ) :=
  Nat.prime_iff_prime_int.mp Nat.prime_two
#align int.prime_two Int.prime_two

theorem prime_three : Prime (3 : ℤ) :=
  Nat.prime_iff_prime_int.mp Nat.prime_three
#align int.prime_three Int.prime_three

end Int


import group_theory.group_action
import algebra.group
import category_theory.category.Groupoid
/--
## Let G be a group and X a G-set (i.e a set with an G action), we make the so called 'action groupoid'
## This is a category with obj  : X and for x y ∈ X, hom(x,y) = Transporteur (x,y)  := { g ∈ G | g • x = y}
##  The composition is given by the group law.
-/

def Transporteur (G : Type )[group G](X : Type )[mul_action  G X] (x y : X) : set G  := { g : G | g • x = y}
lemma mem_transporteur (G : Type )[group G](X : Type )[mul_action  G X] (x y : X)(g : G) :
        (g ∈ Transporteur G X x y) ↔ g • x = y :=
        begin
            split,
            intro,
            cases a,
            exact rfl, ---------- Grrrrouhhhhhhhhh :  how to simplify
            intro,
            cases a,
            exact rfl,
        end
section Transposteur
parameters (G : Type )[group G](X : Type )[mul_action  G X]
def one_in_transporteur : ∀ x y : X, (1 : G) ∈ (Transporteur G X x y) ↔ (x = y) :=
    begin
        intros x y,
        rw mem_transporteur  (G) (X) (x) (y) (1),
        split,
        intro,
        cases a,
        exact eq.symm (one_smul G x ),
        intro,
        cases a,
        exact one_smul G x,
    end
lemma transporteur_comp (x y z : X) : (Transporteur G X  x y) → (Transporteur G X y z ) → (Transporteur G X x z) :=
    λ ⟨g,proof_g⟩ ⟨h, proof_h⟩,  begin
        have H : (h * g) • x = z,
            rw mul_smul,
            rw mem_transporteur at proof_g,
            rw proof_g,
            rw mem_transporteur at proof_h,
            rw proof_h,
        use (h * g),
        exact H,
    end
lemma transporteur_inv (x y : X) : (Transporteur G X x y) → (Transporteur G X y x) :=
λ ⟨g,proof_g⟩, begin
    rw mem_transporteur at proof_g,
    have H : x = g⁻¹ • y,
        rw ← proof_g,
        rw ←  mul_smul,
        rw  inv_mul_self,
        rw one_smul,
    use g⁻¹,
    exact eq.symm H,
end
end Transposteur
open category_theory
section
variables (G : Type )[group G](X : Type)[mul_action  G X]

end


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import analysis.box_integral.partition.basic

/-!
# Split a box along one or more hyperplanes

## Main definitions

A hyperplane `{x : ι → ℝ | x i = a}` splits a rectangular box `I : box_integral.box ι` into two
smaller boxes. If `a ∉ Ioo (I.lower i, I.upper i)`, then one of these boxes is empty, so it is not a
box in the sense of `box_integral.box`.

We introduce the following definitions.

* `box_integral.box.split_lower I i a` and `box_integral.box.split_upper I i a` are these boxes (as
  `with_bot (box_integral.box ι)`);
* `box_integral.prepartition.split I i a` is the partition of `I` made of these two boxes (or of one
   box `I` if one of these boxes is empty);
* `box_integral.prepartition.split_many I s`, where `s : finset (ι × ℝ)` is a finite set of
  hyperplanes `{x : ι → ℝ | x i = a}` encoded as pairs `(i, a)`, is the partition of `I` made by
  cutting it along all the hyperplanes in `s`.

## Main results

The main result `box_integral.prepartition.exists_Union_eq_diff` says that any prepartition `π` of
`I` admits a prepartition `π'` of `I` that covers exactly `I \ π.Union`. One of these prepartitions
is available as `box_integral.prepartition.compl`.

## Tags

rectangular box, partition, hyperplane
-/

noncomputable theory
open_locale classical big_operators filter
open function set filter

namespace box_integral

variables {ι M : Type*} {n : ℕ}

namespace box

variables {I : box ι} {i : ι} {x : ℝ} {y : ι → ℝ}

/-- Given a box `I` and `x ∈ (I.lower i, I.upper i)`, the hyperplane `{y : ι → ℝ | y i = x}` splits
`I` into two boxes. `box_integral.box.split_lower I i x` is the box `I ∩ {y | y i ≤ x}`
(if it is nonempty). As usual, we represent a box that may be empty as
`with_bot (box_integral.box ι)`. -/
def split_lower (I : box ι) (i : ι) (x : ℝ) : with_bot (box ι) :=
mk' I.lower (update I.upper i (min x (I.upper i)))

@[simp] lemma coe_split_lower : (split_lower I i x : set (ι → ℝ)) = I ∩ {y | y i ≤ x} :=
begin
  rw [split_lower, coe_mk'],
  ext y,
  simp only [mem_univ_pi, mem_Ioc, mem_inter_iff, mem_coe, mem_set_of_eq, forall_and_distrib,
    ← pi.le_def, le_update_iff, le_min_iff, and_assoc, and_forall_ne i, mem_def],
  rw [and_comm (y i ≤ x), pi.le_def]
end

lemma split_lower_le : I.split_lower i x ≤ I := with_bot_coe_subset_iff.1 $ by simp

@[simp] lemma split_lower_eq_bot {i x} : I.split_lower i x = ⊥ ↔ x ≤ I.lower i :=
begin
  rw [split_lower, mk'_eq_bot, exists_update_iff I.upper (λ j y, y ≤ I.lower j)],
  simp [(I.lower_lt_upper _).not_le]
end

@[simp] lemma split_lower_eq_self : I.split_lower i x = I ↔ I.upper i ≤ x :=
by simp [split_lower, update_eq_iff]

lemma split_lower_def [decidable_eq ι] {i x} (h : x ∈ Ioo (I.lower i) (I.upper i))
  (h' : ∀ j, I.lower j < update I.upper i x j :=
    (forall_update_iff I.upper (λ j y, I.lower j < y)).2 ⟨h.1, λ j hne, I.lower_lt_upper _⟩) :
  I.split_lower i x = (⟨I.lower, update I.upper i x, h'⟩ : box ι) :=
by { simp only [split_lower, mk'_eq_coe, min_eq_left h.2.le], use rfl, congr }

/-- Given a box `I` and `x ∈ (I.lower i, I.upper i)`, the hyperplane `{y : ι → ℝ | y i = x}` splits
`I` into two boxes. `box_integral.box.split_upper I i x` is the box `I ∩ {y | x < y i}`
(if it is nonempty). As usual, we represent a box that may be empty as
`with_bot (box_integral.box ι)`. -/
def split_upper (I : box ι) (i : ι) (x : ℝ) : with_bot (box ι) :=
mk' (update I.lower i (max x (I.lower i))) I.upper

@[simp] lemma coe_split_upper : (split_upper I i x : set (ι → ℝ)) = I ∩ {y | x < y i} :=
begin
  rw [split_upper, coe_mk'],
  ext y,
  simp only [mem_univ_pi, mem_Ioc, mem_inter_iff, mem_coe, mem_set_of_eq, forall_and_distrib,
    forall_update_iff I.lower (λ j z, z < y j), max_lt_iff, and_assoc (x < y i),
    and_forall_ne i, mem_def],
  exact and_comm _ _
end

lemma split_upper_le : I.split_upper i x ≤ I := with_bot_coe_subset_iff.1 $ by simp

@[simp] lemma split_upper_eq_bot {i x} : I.split_upper i x = ⊥ ↔ I.upper i ≤ x :=
begin
  rw [split_upper, mk'_eq_bot, exists_update_iff I.lower (λ j y, I.upper j ≤ y)],
  simp [(I.lower_lt_upper _).not_le]
end

@[simp] lemma split_upper_eq_self : I.split_upper i x = I ↔ x ≤ I.lower i :=
by simp [split_upper, update_eq_iff]

lemma split_upper_def [decidable_eq ι] {i x} (h : x ∈ Ioo (I.lower i) (I.upper i))
  (h' : ∀ j, update I.lower i x j < I.upper j :=
    (forall_update_iff I.lower (λ j y, y < I.upper j)).2 ⟨h.2, λ j hne, I.lower_lt_upper _⟩) :
  I.split_upper i x = (⟨update I.lower i x, I.upper, h'⟩ : box ι) :=
by { simp only [split_upper, mk'_eq_coe, max_eq_left h.1.le], refine ⟨_, rfl⟩, congr }

lemma disjoint_split_lower_split_upper (I : box ι) (i : ι) (x : ℝ) :
  disjoint (I.split_lower i x) (I.split_upper i x) :=
begin
  rw [← disjoint_with_bot_coe, coe_split_lower, coe_split_upper],
  refine (disjoint.inf_left' _ _).inf_right' _,
  rw set.disjoint_left,
  exact λ y (hle : y i ≤ x) hlt, not_lt_of_le hle hlt
end

lemma split_lower_ne_split_upper (I : box ι) (i : ι) (x : ℝ) :
  I.split_lower i x ≠ I.split_upper i x :=
begin
  cases le_or_lt x (I.lower i),
  { rw [split_upper_eq_self.2 h, split_lower_eq_bot.2 h], exact with_bot.bot_ne_coe },
  { refine (disjoint_split_lower_split_upper I i x).ne _,
    rwa [ne.def, split_lower_eq_bot, not_le] }
end
end box

namespace prepartition

variables {I J : box ι} {i : ι} {x : ℝ}

/-- The partition of `I : box ι` into the boxes `I ∩ {y | y ≤ x i}` and `I ∩ {y | x i < y}`.
One of these boxes can be empty, then this partition is just the single-box partition `⊤`. -/
def split (I : box ι) (i : ι) (x : ℝ) : prepartition I :=
of_with_bot {I.split_lower i x, I.split_upper i x}
  begin
    simp only [finset.mem_insert, finset.mem_singleton],
    rintro J (rfl|rfl),
    exacts [box.split_lower_le, box.split_upper_le]
  end
  begin
    simp only [finset.coe_insert, finset.coe_singleton, true_and, set.mem_singleton_iff,
      pairwise_insert_of_symmetric symmetric_disjoint, pairwise_singleton],
    rintro J rfl -,
    exact I.disjoint_split_lower_split_upper i x
  end

@[simp] lemma mem_split_iff : J ∈ split I i x ↔ ↑J = I.split_lower i x ∨ ↑J = I.split_upper i x :=
by simp [split]

lemma mem_split_iff' : J ∈ split I i x ↔
  (J : set (ι → ℝ)) = I ∩ {y | y i ≤ x} ∨ (J : set (ι → ℝ)) = I ∩ {y | x < y i} :=
by simp [mem_split_iff, ← box.with_bot_coe_inj]

@[simp] lemma Union_split (I : box ι) (i : ι) (x : ℝ) : (split I i x).Union = I :=
by simp [split, ← inter_union_distrib_left, ← set_of_or, le_or_lt]

lemma is_partition_split (I : box ι) (i : ι) (x : ℝ) : is_partition (split I i x) :=
is_partition_iff_Union_eq.2 $ Union_split I i x

lemma sum_split_boxes {M : Type*} [add_comm_monoid M] (I : box ι) (i : ι) (x : ℝ) (f : box ι → M) :
  ∑ J in (split I i x).boxes, f J = (I.split_lower i x).elim 0 f + (I.split_upper i x).elim 0 f :=
by rw [split, sum_of_with_bot, finset.sum_pair (I.split_lower_ne_split_upper i x)]

/-- If `x ∉ (I.lower i, I.upper i)`, then the hyperplane `{y | y i = x}` does not split `I`. -/
lemma split_of_not_mem_Ioo (h : x ∉ Ioo (I.lower i) (I.upper i)) : split I i x = ⊤ :=
begin
  refine ((is_partition_top I).eq_of_boxes_subset (λ J hJ, _)).symm,
  rcases mem_top.1 hJ with rfl, clear hJ,
  rw [mem_boxes, mem_split_iff],
  rw [mem_Ioo, not_and_distrib, not_lt, not_lt] at h,
  cases h; [right, left],
  { rwa [eq_comm, box.split_upper_eq_self] },
  { rwa [eq_comm, box.split_lower_eq_self] }
end

lemma coe_eq_of_mem_split_of_mem_le {y : ι → ℝ} (h₁ : J ∈ split I i x) (h₂ : y ∈ J) (h₃ : y i ≤ x) :
  (J : set (ι → ℝ)) = I ∩ {y | y i ≤ x} :=
(mem_split_iff'.1 h₁).resolve_right $ λ H,
  by { rw [← box.mem_coe, H] at h₂, exact h₃.not_lt h₂.2 }

lemma coe_eq_of_mem_split_of_lt_mem {y : ι → ℝ} (h₁ : J ∈ split I i x) (h₂ : y ∈ J) (h₃ : x < y i) :
  (J : set (ι → ℝ)) = I ∩ {y | x < y i} :=
(mem_split_iff'.1 h₁).resolve_left $ λ H,
  by { rw [← box.mem_coe, H] at h₂, exact h₃.not_le h₂.2 }

@[simp] lemma restrict_split (h : I ≤ J) (i : ι) (x : ℝ) : (split J i x).restrict I = split I i x :=
begin
  refine ((is_partition_split J i x).restrict h).eq_of_boxes_subset _,
  simp only [finset.subset_iff, mem_boxes, mem_restrict', exists_prop, mem_split_iff'],
  have : ∀ s, (I ∩ s : set (ι → ℝ)) ⊆ J, from λ s, (inter_subset_left _ _).trans h,
  rintro J₁ ⟨J₂, (H₂|H₂), H₁⟩; [left, right]; simp [H₁, H₂, inter_left_comm ↑I, this],
end

lemma inf_split (π : prepartition I) (i : ι) (x : ℝ) :
  π ⊓ split I i x = π.bUnion (λ J, split J i x) :=
bUnion_congr_of_le rfl $ λ J hJ, restrict_split hJ i x

/-- Split a box along many hyperplanes `{y | y i = x}`; each hyperplane is given by the pair
`(i x)`. -/
def split_many (I : box ι) (s : finset (ι × ℝ)) : prepartition I :=
s.inf (λ p, split I p.1 p.2)

@[simp]

@[simp] lemma split_many_insert (I : box ι) (s : finset (ι × ℝ)) (p : ι × ℝ) :
  split_many I (insert p s) = split_many I s ⊓ split I p.1 p.2 :=
by rw [split_many, finset.inf_insert, inf_comm, split_many]

lemma split_many_le_split (I : box ι) {s : finset (ι × ℝ)} {p : ι × ℝ} (hp : p ∈ s) :
  split_many I s ≤ split I p.1 p.2 :=
finset.inf_le hp

lemma is_partition_split_many (I : box ι) (s : finset (ι × ℝ)) :
  is_partition (split_many I s) :=
finset.induction_on s (by simp only [split_many_empty, is_partition_top]) $
  λ a s ha hs, by simpa only [split_many_insert, inf_split]
    using hs.bUnion (λ J hJ, is_partition_split _ _ _)

@[simp] lemma Union_split_many (I : box ι) (s : finset (ι × ℝ)) : (split_many I s).Union = I :=
(is_partition_split_many I s).Union_eq

lemma inf_split_many {I : box ι} (π : prepartition I) (s : finset (ι × ℝ)) :
  π ⊓ split_many I s = π.bUnion (λ J, split_many J s) :=
begin
  induction s using finset.induction_on with p s hp ihp,
  { simp },
  { simp_rw [split_many_insert, ← inf_assoc, ihp, inf_split, bUnion_assoc] }
end

/-- Let `s : finset (ι × ℝ)` be a set of hyperplanes `{x : ι → ℝ | x i = r}` in `ι → ℝ` encoded as
pairs `(i, r)`. Suppose that this set contains all faces of a box `J`. The hyperplanes of `s` split
a box `I` into subboxes. Let `Js` be one of them. If `J` and `Js` have nonempty intersection, then
`Js` is a subbox of `J`.  -/
lemma not_disjoint_imp_le_of_subset_of_mem_split_many {I J Js : box ι} {s : finset (ι × ℝ)}
  (H : ∀ i, {(i, J.lower i), (i, J.upper i)} ⊆ s) (HJs : Js ∈ split_many I s)
  (Hn : ¬disjoint (J : with_bot (box ι)) Js) : Js ≤ J :=
begin
  simp only [finset.insert_subset, finset.singleton_subset_iff] at H,
  rcases box.not_disjoint_coe_iff_nonempty_inter.mp Hn with ⟨x, hx, hxs⟩,
  refine λ y hy i, ⟨_, _⟩,
  { rcases split_many_le_split I (H i).1 HJs with ⟨Jl, Hmem : Jl ∈ split I i (J.lower i), Hle⟩,
    have := Hle hxs,
    rw [← box.coe_subset_coe, coe_eq_of_mem_split_of_lt_mem Hmem this (hx i).1] at Hle,
    exact (Hle hy).2 },
  { rcases split_many_le_split I (H i).2 HJs with ⟨Jl, Hmem : Jl ∈ split I i (J.upper i), Hle⟩,
    have := Hle hxs,
    rw [← box.coe_subset_coe, coe_eq_of_mem_split_of_mem_le Hmem this (hx i).2] at Hle,
    exact (Hle hy).2 }
end

section fintype

variable [finite ι]

/-- Let `s` be a finite set of boxes in `ℝⁿ = ι → ℝ`. Then there exists a finite set `t₀` of
hyperplanes (namely, the set of all hyperfaces of boxes in `s`) such that for any `t ⊇ t₀`
and any box `I` in `ℝⁿ` the following holds. The hyperplanes from `t` split `I` into subboxes.
Let `J'` be one of them, and let `J` be one of the boxes in `s`. If these boxes have a nonempty
intersection, then `J' ≤ J`. -/
lemma eventually_not_disjoint_imp_le_of_mem_split_many (s : finset (box ι)) :
  ∀ᶠ t : finset (ι × ℝ) in at_top, ∀ (I : box ι) (J ∈ s) (J' ∈ split_many I t),
    ¬disjoint (J : with_bot (box ι)) J' → J' ≤ J :=
begin
  casesI nonempty_fintype ι,
  refine eventually_at_top.2
    ⟨s.bUnion (λ J, finset.univ.bUnion (λ i, {(i, J.lower i), (i, J.upper i)})),
      λ t ht I J hJ J' hJ', not_disjoint_imp_le_of_subset_of_mem_split_many (λ i, _) hJ'⟩,
  exact λ p hp, ht (finset.mem_bUnion.2 ⟨J, hJ, finset.mem_bUnion.2 ⟨i, finset.mem_univ _, hp⟩⟩)
end

lemma eventually_split_many_inf_eq_filter (π : prepartition I) :
  ∀ᶠ t : finset (ι × ℝ) in at_top,
    π ⊓ (split_many I t) = (split_many I t).filter (λ J, ↑J ⊆ π.Union) :=
begin
  refine (eventually_not_disjoint_imp_le_of_mem_split_many π.boxes).mono (λ t ht, _),
  refine le_antisymm ((bUnion_le_iff _).2 $ λ J hJ, _) (le_inf (λ J hJ, _) (filter_le _ _)),
  { refine of_with_bot_mono _,
    simp only [finset.mem_image, exists_prop, mem_boxes, mem_filter],
    rintro _ ⟨J₁, h₁, rfl⟩ hne,
    refine ⟨_, ⟨J₁, ⟨h₁, subset.trans _ (π.subset_Union hJ)⟩, rfl⟩, le_rfl⟩,
    exact ht I J hJ J₁ h₁ (mt disjoint_iff.1 hne) },
  { rw mem_filter at hJ,
    rcases set.mem_Union₂.1 (hJ.2 J.upper_mem) with ⟨J', hJ', hmem⟩,
    refine ⟨J', hJ', ht I _ hJ' _ hJ.1 $ box.not_disjoint_coe_iff_nonempty_inter.2 _⟩,
    exact ⟨J.upper, hmem, J.upper_mem⟩  }
end

lemma exists_split_many_inf_eq_filter_of_finite (s : set (prepartition I)) (hs : s.finite) :
  ∃ t : finset (ι × ℝ), ∀ π ∈ s,
    π ⊓ (split_many I t) = (split_many I t).filter (λ J, ↑J ⊆ π.Union) :=
begin
  have := λ π (hπ : π ∈ s), eventually_split_many_inf_eq_filter π,
  exact (hs.eventually_all.2 this).exists
end

/-- If `π` is a partition of `I`, then there exists a finite set `s` of hyperplanes such that
`split_many I s ≤ π`. -/
lemma is_partition.exists_split_many_le {I : box ι} {π : prepartition I}
  (h : is_partition π) : ∃ s, split_many I s ≤ π :=
(eventually_split_many_inf_eq_filter π).exists.imp $ λ s hs,
  by { rwa [h.Union_eq, filter_of_true, inf_eq_right] at hs, exact λ J hJ, le_of_mem _ hJ }

/-- For every prepartition `π` of `I` there exists a prepartition that covers exactly
`I \ π.Union`. -/
lemma exists_Union_eq_diff (π : prepartition I) :
  ∃ π' : prepartition I, π'.Union = I \ π.Union :=
begin
  rcases π.eventually_split_many_inf_eq_filter.exists with ⟨s, hs⟩,
  use (split_many I s).filter (λ J, ¬(J : set (ι → ℝ)) ⊆ π.Union),
  simp [← hs]
end

/-- If `π` is a prepartition of `I`, then `π.compl` is a prepartition of `I`
such that `π.compl.Union = I \ π.Union`. -/
def compl (π : prepartition I) : prepartition I := π.exists_Union_eq_diff.some

@[simp] lemma Union_compl (π : prepartition I) : π.compl.Union = I \ π.Union :=
π.exists_Union_eq_diff.some_spec

/-- Since the definition of `box_integral.prepartition.compl` uses `Exists.some`,
the result depends only on `π.Union`. -/
lemma compl_congr {π₁ π₂ : prepartition I} (h : π₁.Union = π₂.Union) :
  π₁.compl = π₂.compl :=
by { dunfold compl, congr' 1, rw h }

lemma is_partition.compl_eq_bot {π : prepartition I} (h : is_partition π) : π.compl = ⊥ :=
by rw [← Union_eq_empty, Union_compl, h.Union_eq, diff_self]

@[simp] lemma compl_top : (⊤ : prepartition I).compl = ⊥ := (is_partition_top I).compl_eq_bot

end fintype

end prepartition

end box_integral


import separation_world.level3 -- hide

/-
# Level 4: Characterisation of Frechet spaces
-/

variables {X : Type} -- hide
variables [topological_space X] -- hide

namespace topological_space -- hide
open set -- hide

/- Lemma
Let τ be a topological space. τ is a frechet space if only if for all the points in the topology, their singletons are closed sets.
-/
lemma T1_characterisation : T1_space X ↔ (∀ (x : X), is_closed ({x} : set X)) :=
begin
  split,
  { introsI t1 x,
    rw [is_closed, ← union_disjont_open_sets],
    exact topological_space.union {U : set X | (x ∉ U) ∧ (is_open U)} (λ B hB, hB.2)},
  { intro h,
    exact ⟨λ x y hxy, ⟨{y}ᶜ,h y, mem_compl_singleton_iff.mpr (ne.symm hxy), not_not.mpr rfl⟩⟩}
end

end topological_space -- hide


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import data.finset.sort
import data.fintype.basic

/-!
# Sorting a finite type

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

This file provides two equivalences for linearly ordered fintypes:
* `mono_equiv_of_fin`: Order isomorphism between `α` and `fin (card α)`.
* `fin_sum_equiv_of_finset`: Equivalence between `α` and `fin m ⊕ fin n` where `m` and `n` are
  respectively the cardinalities of some `finset α` and its complement.
-/

open finset

/-- Given a linearly ordered fintype `α` of cardinal `k`, the order isomorphism
`mono_equiv_of_fin α h` is the increasing bijection between `fin k` and `α`. Here, `h` is a proof
that the cardinality of `α` is `k`. We use this instead of an isomorphism `fin (card α) ≃o α` to
avoid casting issues in further uses of this function. -/
def mono_equiv_of_fin (α : Type*) [fintype α] [linear_order α] {k : ℕ} (h : fintype.card α = k) :
  fin k ≃o α :=
(univ.order_iso_of_fin h).trans $ (order_iso.set_congr _ _ coe_univ).trans order_iso.set.univ

variables {α : Type*} [decidable_eq α] [fintype α] [linear_order α] {m n : ℕ} {s : finset α}

/-- If `α` is a linearly ordered fintype, `s : finset α` has cardinality `m` and its complement has
cardinality `n`, then `fin m ⊕ fin n ≃ α`. The equivalence sends elements of `fin m` to
elements of `s` and elements of `fin n` to elements of `sᶜ` while preserving order on each
"half" of `fin m ⊕ fin n` (using `set.order_iso_of_fin`). -/
def fin_sum_equiv_of_finset (hm : s.card = m) (hn : sᶜ.card = n) : fin m ⊕ fin n ≃ α :=
calc fin m ⊕ fin n ≃ (s : set α) ⊕ (sᶜ : set α) :
  equiv.sum_congr (s.order_iso_of_fin hm).to_equiv $
    (sᶜ.order_iso_of_fin hn).to_equiv.trans $ equiv.set.of_eq s.coe_compl
... ≃ α : equiv.set.sum_compl _

@[simp] lemma fin_sum_equiv_of_finset_inl (hm : s.card = m) (hn : sᶜ.card = n) (i : fin m) :
  fin_sum_equiv_of_finset hm hn (sum.inl i) = s.order_emb_of_fin hm i :=
rfl

@[simp] lemma fin_sum_equiv_of_finset_inr (hm : s.card = m) (hn : sᶜ.card = n) (i : fin n) :
  fin_sum_equiv_of_finset hm hn (sum.inr i) = sᶜ.order_emb_of_fin hn i :=
rfl


import ..library.src_ordered_field

namespace mth1001

namespace myreal

section ordered

variables {R : Type} [myordered_field R]

open_locale classical

open myordered_field

/-
The three basic axiom of an orderd field are:

1. `trichotomy`,
2. `pos_add_of_pos_of_pos`, and
3. `pos_mul_of_pos_of_pos`,

as exemplified below
-/

example (x : R) : pos x ∧ ¬x = 0 ∧ ¬pos (-x)
               ∨ ¬pos x ∧ x = 0  ∧ ¬pos (-x)
               ∨ ¬pos x ∧ x ≠ 0 ∧ pos (-x) := trichotomy x

example (x y : R) : pos x → pos y → pos (x + y) := pos_add_of_pos_of_pos x y

example (x y : R) : pos x → pos y → pos (x * y) := pos_mul_of_pos_of_pos x y

-- In the example below, we see that the square of non-zero positive number is positive.
example (x : R) (h : x ≠ (0 : R)) : pos (x ^ 2) :=
begin
  rw pow_two,
  rcases trichotomy x with ⟨hpx, _, _⟩ | ⟨_, rfl, _⟩ | ⟨_, _, hpnx⟩,
  { exact pos_mul_of_pos_of_pos _ _ hpx hpx, },
  { rw mul_zero, contradiction, },
  { rw [←neg_mul_neg_self],
    exact pos_mul_of_pos_of_pos _ _ hpnx hpnx,},
end

-- Exercise 033:
-- Use trichotomy on `(1 : R)`, as in the example above.
lemma pos_one : pos (1 : R) :=
begin
  sorry
end

-- Below, we see that every non-zero natural number (seen as a term of type `R`) is positive.
lemma pos_nat (n : ℕ) : n ≠ 0 → pos (n : R) :=
begin
  induction n with k hk,
  { intro _, contradiction, },
  { intro _,
    rw coe_nat_succ,
    by_cases h₁ : k = 0,
    { rw h₁,
      change pos((0 : R) + (1 : R)),
      rw zero_add,
      exact pos_one, },
    { exact pos_add_of_pos_of_pos _ _ (hk h₁) pos_one }, },
end

-- The lemmas below are used to work with the definition of `<`.

lemma lt_iff_pos_sub (x y : R) : x < y ↔ pos (y -x) := by refl

lemma lt_iff_pos_neg (x y : R) : x < y ↔ pos (y + -x) := by refl

lemma pos_iff_gt_zero (x : R) : pos x ↔ 0 < x :=
by rw [lt_iff_pos_sub, sub_zero]

lemma gt_zero_of_ne_zero_nat (n : ℕ) (h : n ≠ 0) : (0 : R) < n :=
begin
  rw [←pos_iff_gt_zero],
  exact pos_nat n h,
end

-- Exercise 034:
lemma mul_pos {a b : R} (h₁ : 0 < a) (h₂ : 0 < b) : 0 < a * b :=
begin
  sorry
end

-- Exercise 035:
lemma neg_pos {x : R} : 0 < -x ↔ x < 0:=
begin
  repeat {rw lt_iff_pos_neg},
  sorry  end

-- Exercise 036:
lemma trichotomy' (x y: R) : x < y ∧ ¬x = y ∧ ¬y < x ∨
                               ¬x < y ∧ x = y ∧ ¬y < x ∨
                               ¬x < y ∧ ¬x = y ∧ y < x :=
begin
  repeat {rw lt_iff_pos_sub},
  have : x - y = -(y - x),
  { sorry, },
  sorry
end

-- Exercise 037:
lemma lt_trans {x y z : R} : x < y → y < z → x < z :=
begin
  repeat {rw lt_iff_pos_sub},
  sorry
end

-- Exercise 038:
lemma add_lt_add_iff_right_mpr {x y : R} (z : R) : x < y → x + z < y + z :=
begin
  sorry
end

-- Exercise 039:
lemma add_lt_add_iff_right_mp {x y : R} (z : R) : x + z < y + z → x < y :=
begin
  sorry
end

-- Exercise 040:
lemma add_lt_add_iff_right {x y : R} (z : R) : x + z < y + z ↔ x < y :=
begin
  sorry
end

-- Exercise 041:
theorem neg_lt_neg_iff  {a b : R} : -a < -b ↔ b < a :=
begin
  sorry
end

-- Exercise 042:
lemma mul_lt_mul_left_mpr {x y z : R} : 0 < z → x < y → z * x < z * y :=
begin
  sorry
end

-- Exercise 043:
theorem add_lt_add {a b c d : R} : a < b → c < d → a + c < b + d :=
begin
  sorry
end

-- Exercise 044:
lemma lt_irrefl {x : R} : ¬x < x :=
begin
  sorry
end

-- Exercise 045:
lemma non_zero_of_pos {x : R} (h : 0 < x) : x ≠ 0 :=
begin
  sorry
end

-- Exercise 046:
-- Use `lt_irrefl` to prove the following.
theorem ne_of_gt {a b : R} (h : a > b) : a ≠ b :=
begin
  sorry  end

-- The following lemma is used to work with the definition of `≤`.

lemma le_iff_lt_or_eq {x y : R} : x ≤ y ↔ ((x < y) ∨ x = y) := by refl

-- Exercise 047:
lemma le_refl (x : R) : x ≤ x :=
sorry

-- Exercise 048:
-- Though it looks complicated, you can fill in the `sorry` below using only the
-- `split`, `intro`, and `exact` tactics (with and introduction and hypotheses).
lemma not_le_iff_lt (x y : R) : ¬(x ≤ y) ↔ (y < x) :=
begin
  rw le_iff_lt_or_eq,
  push_neg,
  rcases trichotomy' x y with ⟨hxlty, _, _⟩ | ⟨_, hxy, hnyltx ⟩  | ⟨hnxlty, hnxy, hxlty ⟩ ,
  { split,
    { rintro ⟨hnxy, _⟩,
      contradiction, },
    { intros hyltx, exfalso,
      exact lt_irrefl (lt_trans hxlty hyltx), }, },
  { split,
    { rintro ⟨_, hnxy⟩,
      contradiction, },
    { intro hyltx, contradiction, }, },
  { sorry, },
end

-- Exercise 049:
-- Use `neg_le_iff_lt` to prove the result below.
lemma not_lt_iff_le (x y : R) : ¬(x < y) ↔ (y ≤ x) :=
sorry

-- Exercise 050:
lemma neg_nonneg {x : R} : 0 ≤ -x ↔ x ≤ 0 :=
begin
  repeat {rw le_iff_lt_or_eq},
  have k : 0 < -x ↔ x < 0, from neg_pos,
  sorry
end

-- Exercise 051:
lemma le_trans (x y z : R) : x ≤ y → y ≤ z → x ≤ z :=
begin
  rintro (h₁ | rfl) (h₂ | rfl),
  { sorry, },
  { sorry, },
  { sorry, },
  { sorry, },
end

-- Exercise 052:
lemma lt_of_le_of_lt {a b c : R} (h₁ : a ≤ b) (h₂ : b < c) : a < c :=
begin
  sorry
end

-- Exercise 053:
-- Use `rcases trichotomy' x y` (see examples above) to prove the following.
lemma le_total (x y : R) : x ≤ y ∨ y ≤ x :=
begin
  sorry
end

-- Exercise 054:
lemma anti_symm {x y : R} : x ≤ y → y ≤ x → x = y :=
begin
  sorry
end

-- Exercise 055:
theorem neg_le_neg_iff {a b : R} : -a ≤ -b ↔ b ≤ a :=
begin
  repeat {rw le_iff_lt_or_eq},
  split,
  { rintro (hlt | heq),
    { left, rwa ←neg_lt_neg_iff, },
    { right, rw [←neg_neg a, heq, neg_neg], }, },
  { sorry, },
end

-- Exercise 056:
theorem add_le_add {a b c d : R} : a ≤ b → c ≤ d → a + c ≤ b + d :=
begin
  sorry
end

-- Exercise 057:
theorem mul_self_non_neg (a : R) : 0 ≤ a * a:=
begin
  rcases trichotomy' 0 a with ⟨posa, _⟩ | ⟨_, eq0, _⟩ | ⟨_, _, nega⟩,
  { sorry, },
  { sorry, },
  { sorry, },
end

-- Exercise 058:
lemma non_neg_mul_of_non_neg_of_non_neg {a b : R} (h₁ : 0 ≤ a) (h₂ : 0 ≤ b) : 0 ≤ a * b :=
begin
  cases h₁ with apos aeq0,
  { cases h₂ with bpos beq0,
    { sorry, },
    { right, rw [←beq0, mul_zero], }, },
  { sorry, },
end

-- Exercise 059:
lemma non_neg_of_non_neg_mul_of_pos {x y : R} (h₁ : 0 ≤ x * y) (h₂ : 0 < x) : 0 ≤ y :=
begin
  sorry
end

lemma non_neg_mul_iff_non_neg_and_non_neg_or_non_pos_and_non_pos (a b : R)
  : 0 ≤ a * b ↔ (0 ≤ a ∧ 0 ≤ b) ∨ (a ≤ 0 ∧ b ≤ 0) :=
begin
  split,
  { intro h₁,
    by_cases h₂ : 0 ≤ a,
    { by_cases h₃ : a = 0,
      { rw h₃,
        exact or.elim (le_total b 0) (λ h₄, or.inr ⟨le_refl 0, h₄⟩) (λ h₄, or.inl ⟨le_refl 0, h₄⟩), },
      { have h₄ : 0 < a, from or.elim h₂ id (λ aeq0, absurd aeq0.symm h₃),
        have h₅ : 0 ≤ b, from non_neg_of_non_neg_mul_of_pos h₁ h₄,
        exact or.inl ⟨or.inl h₄, h₅⟩, }, },
    { rw not_le_iff_lt at h₂,
      right,
      have k : b ≤ 0,
      { by_contra h₃,
        rw not_le_iff_lt at h₃,
        rw ←neg_pos at h₂,
        have h₄ : 0 < b * -a, from mul_pos h₃ h₂,
        rw [←neg_mul_eq_mul_neg, mul_comm, neg_pos] at h₄,
        exact lt_irrefl (lt_of_le_of_lt h₁ h₄), },
      exact ⟨or.inl h₂, k⟩, }, },
  { rintro (⟨h₁, h₂⟩ | ⟨h₁, h₂⟩),
    { exact non_neg_mul_of_non_neg_of_non_neg h₁ h₂, },
    { rw ←neg_mul_neg a b,
      rw ←neg_nonneg at h₁ h₂,
      exact non_neg_mul_of_non_neg_of_non_neg h₁ h₂, }, },
end

-- Exercise 060:
-- Modify the proof of the second case to prove the first case.
theorem inv_pos {a : R}  (h : a ≠ 0) : 0 < a⁻¹ ↔ 0 < a :=
begin
  split,
  { sorry, },
  { intro k,
    have h₂ : 0 ≤ (a⁻¹ * a⁻¹), from mul_self_non_neg a⁻¹,
    rw le_iff_lt_or_eq at h₂,
    cases h₂ with posainvsq eq0,
    { convert mul_lt_mul_left_mpr k posainvsq,
      { rw mul_zero, },
      { rw [←mul_assoc, mul_inv a h, one_mul], }, },
    { have h₃ : a⁻¹ = 0,
      { cases eq_zero_or_eq_zero_of_mul_eq_zero _ _ eq0.symm;
        assumption, },
      exact absurd h₃ (inv_ne_zero h), }, },
end

-- Exercise 061:
theorem inv_lt_inv {a b : R} (h₁ : 0  < a) (h₂ : 0 < b) : a⁻¹ < b⁻¹ ↔ b < a :=
begin
  split,
  { sorry, },
  { intro h₃,
    have h₅ : a ≠ 0, from ne_of_gt h₁,
    have k₁ : a⁻¹ > 0, from (inv_pos h₅).mpr h₁,
    have h₄ : a⁻¹ * b < a⁻¹ * a, from mul_lt_mul_left_mpr k₁ h₃,
    rw inv_mul a h₅ at h₄,
    have h₇ : b ≠ 0, from ne_of_gt h₂,
    have k₂ : b⁻¹ > 0, from (inv_pos h₇).mpr h₂,
    have h₆ :  b⁻¹ * (a⁻¹ * b) < b⁻¹ * 1, from mul_lt_mul_left_mpr k₂ h₄,
    rw [mul_comm, mul_assoc, mul_inv b h₇, mul_one, mul_one] at h₆,
    exact h₆, },
end

end ordered

section max_abs

variables {R : Type} [myordered_field R]

open_locale classical

open myordered_field

/-
The absolute value `abs a` of `a : R` is defined to be by maximum of
`a` and `-a`.
-/

example (a : R) : abs a = max a (-a) := rfl

/-
By definition, `max a b` is `a` if `b ≤ a`, otherwise it is `b`.

Note the use of `if_pos` and `if_neg` below to distiguish between the cases where
`b ≤ a` and `¬(b ≤ a)` in the definition of `max`.
-/

lemma le_max_left (a b : R) : a ≤ max a b :=
begin
  unfold max,
  by_cases h : b ≤ a,
  { rw (if_pos h),
    exact le_refl a, },
  { rw (if_neg h),
    rw not_le_iff_lt at h,
    left, exact h, },
end

-- Exercise 062:
lemma le_max_right (a b : R) : b ≤ max a b :=
begin
  unfold max,
  by_cases h : b ≤ a,
  { rw (if_pos h),
    sorry, },
  { rw (if_neg h),
    sorry, },
end

-- Exercise 063:
-- Prove this using the template of the above two results.
lemma max_choice (a b : R) : max a b = a ∨ max a b = b :=
begin
  sorry
end

lemma neg_le_abs (a : R) : -a ≤ abs a :=
begin
  unfold abs max,
  by_cases h : -a ≤ a,
  { rw (if_pos h), exact h, },
  { rw (if_neg h), exact le_refl (-a), },
end

-- Exercise 064:
lemma le_abs_self (a : R) : a ≤ abs a :=
begin
  sorry
end

-- Exercise 065:
theorem triangle_inequality (x y : R) : abs (x + y) ≤ abs x + abs y :=
begin
  by_cases h : -(x+y) ≤ x+y,
  { have : abs (x+y) = x + y,
    { unfold abs max,
      rw (if_pos h), },
    rw this,
    have h₁ : x ≤ abs x, from le_abs_self x,
    have h₂ : y ≤ abs y, from le_abs_self y,
    exact add_le_add h₁ h₂, },
  { sorry, },
end

end max_abs

section upper_bounds_lower_bounds_sup

open myordered_field

variables {R : Type} [myordered_field R]

/-
Here are archetypal applications of the definitions of `upper_bound`, `lower_bound`,
`bounded_above`, `bounded_below`, `bounded`, and `is_sup`.
-/

example (u : R) (S : set R) (h : ∀ s ∈ S, s ≤ u) : upper_bound u S := h
example (v : R) (S : set R) (h : ∀ s ∈ S, v ≤ s) : lower_bound v S := h
example (S : set R) (h : ∃ u : R, upper_bound u S) : bounded_above S := h
example (S : set R) (h : ∃ v : R, lower_bound v S) : bounded_below S := h
example (S : set R) (h₁ : bounded_above S) (h₂ : bounded_below S) : bounded S := and.intro h₁ h₂
example (u : R) (S : set R) (h₁ : upper_bound u S) (h₂ : ∀ v : R, upper_bound v S → u ≤ v)
: is_sup u S := and.intro h₁ h₂

-- Exercise 066:
theorem sup_uniqueness (S : set R) (a b : R) (h₁ : is_sup a S) (h₂ : is_sup b S) : a = b :=
begin
  cases h₁ with h₃ h₄,
  cases h₂ with h₅ h₆,
  apply anti_symm,
  { sorry, },
  { sorry, },
end

-- Exercise 067:
-- In this example, we show _every_ real number `u` is an upper bound of the empty set.
theorem empty_set_upper_bound (u : R) : upper_bound u ∅ :=
begin
  sorry
end

-- Exercise 068:
theorem empty_set_lower_bound (v : R) : lower_bound v ∅ :=
begin
  sorry
end

-- Exercise 069:
-- Given `S` a set of real numbers, given `s ∈ S`, given `u` and `v` are upper and lower bounds
-- of `S`, respectively, then `v ≤ u`.
example (S : set R) (u v : R) (h₁ : upper_bound u S) (h₂ : lower_bound v S) (s : R) (h₃ : s ∈ S)
  : v ≤ u :=
begin
  sorry
end

-- Exercise 070:
-- However, it's *not true* that for every set `S` of real numbers, for all real numbers `u` and `v`,
-- if `u` and `v` are upper and lower bounds of `S`, respectively, then `v ≤ u`.
-- Hint: start with `push_neg` and think of the results above.
example : ¬(∀ (S : set R), ∀ u v : R, upper_bound u S → lower_bound v S → v ≤ u) :=
begin
  sorry
end

end upper_bounds_lower_bounds_sup

section boo

open myordered_field

variables {R : Type} [myordered_field R]

lemma zero_of_non_neg_of_lt_pos (a : R) (h : 0 ≤ a) (h₂ : ∀ ε > 0, a < ε) : a = 0 :=
begin
  rw le_iff_lt_or_eq at h,
  cases h with hpos heq0,
  { exfalso,
    specialize h₂ (a * (↑2)⁻¹),
    have h₃ : (0 : R) < ↑2, from gt_zero_of_ne_zero_nat 2 (by norm_num),
    have k₁ : ↑2 ≠ (0 : R), from non_zero_of_pos h₃,
    have h₄ : (0 : R) < (↑2)⁻¹, { rwa inv_pos k₁, },
    have h₅ : 0 < a * (↑2)⁻¹, from mul_pos hpos h₄,
    have h₆ : a < a * (↑2)⁻¹, from h₂ h₅,
    have h₇ : ↑2 * a < a,
    calc ↑2 * a < ↑2 * (a * (↑2)⁻¹) : mul_lt_mul_left_mpr h₃ h₆
           ...  = a                 : by {rw [mul_comm, mul_assoc, inv_mul _ k₁, mul_one] },
    have k₂ : ↑2 = (1 : R) + (1 : R),
    { rw [coe_nat_succ, coe_nat_succ, (show ↑0 = (0 : R), by refl), zero_add], },
    have k₃ : a + a < a, { rwa [k₂, add_mul, one_mul] at h₇, },
    have k₄ : a < (0 : R),
    { rwa [←add_lt_add_iff_right a, zero_add], },
    exact lt_irrefl (lt_trans hpos k₄), },
  { exact heq0.symm, }
end

end boo

end myreal

end mth1001


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module data.nat.modeq
! leanprover-community/mathlib commit 2ed7e4aec72395b6a7c3ac4ac7873a7a43ead17c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Int.GCD
import Mathlib.Data.Int.Order.Lemmas

/-!
# Congruences modulo a natural number

This file defines the equivalence relation `a ≡ b [MOD n]` on the natural numbers,
and proves basic properties about it such as the Chinese Remainder Theorem
`modEq_and_modEq_iff_modEq_mul`.

## Notations

`a ≡ b [MOD n]` is notation for `nat.ModEq n a b`, which is defined to mean `a % n = b % n`.

## Tags

ModEq, congruence, mod, MOD, modulo
-/

namespace Nat

/-- Modular equality. `n.ModEq a b`, or `a ≡ b [MOD n]`, means that `a - b` is a multiple of `n`. -/
def ModEq (n a b : ℕ) :=
  a % n = b % n
#align nat.modeq Nat.ModEq

@[inherit_doc]
notation:50 a " ≡ " b " [MOD " n "]" => ModEq n a b

variable {m n a b c d : ℕ}

-- Porting note: This instance should be derivable automatically
instance : Decidable (ModEq n a b) := decEq (a % n) (b % n)

namespace ModEq

@[refl]
protected theorem refl (a : ℕ) : a ≡ a [MOD n] := rfl
#align nat.modeq.refl Nat.ModEq.refl

protected theorem rfl : a ≡ a [MOD n] :=
  ModEq.refl _
#align nat.modeq.rfl Nat.ModEq.rfl

instance : IsRefl _ (ModEq n) :=
  ⟨ModEq.refl⟩

@[symm]
protected theorem symm : a ≡ b [MOD n] → b ≡ a [MOD n] :=
  Eq.symm
#align nat.modeq.symm Nat.ModEq.symm

@[trans]
protected theorem trans : a ≡ b [MOD n] → b ≡ c [MOD n] → a ≡ c [MOD n] :=
  Eq.trans
#align nat.modeq.trans Nat.ModEq.trans

instance : Trans (ModEq n) (ModEq n) (ModEq n) where
  trans := Nat.ModEq.trans

protected theorem comm : a ≡ b [MOD n] ↔ b ≡ a [MOD n] :=
  ⟨ModEq.symm, ModEq.symm⟩
#align nat.modeq.comm Nat.ModEq.comm

end ModEq

theorem modEq_zero_iff_dvd : a ≡ 0 [MOD n] ↔ n ∣ a := by rw [ModEq, zero_mod, dvd_iff_mod_eq_zero]
#align nat.modeq_zero_iff_dvd Nat.modEq_zero_iff_dvd

theorem _root_.Dvd.dvd.modEq_zero_nat (h : n ∣ a) : a ≡ 0 [MOD n] :=
  modEq_zero_iff_dvd.2 h
#align has_dvd.dvd.modeq_zero_nat Dvd.dvd.modEq_zero_nat

theorem _root_.Dvd.dvd.zero_modEq_nat (h : n ∣ a) : 0 ≡ a [MOD n] :=
  h.modEq_zero_nat.symm
#align has_dvd.dvd.zero_modeq_nat Dvd.dvd.zero_modEq_nat

theorem modEq_iff_dvd : a ≡ b [MOD n] ↔ (n : ℤ) ∣ b - a := by
  rw [ModEq, eq_comm, ← Int.coe_nat_inj', Int.coe_nat_mod, Int.coe_nat_mod,
    Int.emod_eq_emod_iff_emod_sub_eq_zero, Int.dvd_iff_emod_eq_zero]
#align nat.modeq_iff_dvd Nat.modEq_iff_dvd

alias modEq_iff_dvd ↔ ModEq.dvd modEq_of_dvd
#align nat.modeq.dvd Nat.ModEq.dvd
#align nat.modeq_of_dvd Nat.modEq_of_dvd

/-- A variant of `modEq_iff_dvd` with `nat` divisibility -/
theorem modEq_iff_dvd' (h : a ≤ b) : a ≡ b [MOD n] ↔ n ∣ b - a := by
  rw [modEq_iff_dvd, ← Int.coe_nat_dvd, Int.ofNat_sub h]
#align nat.modeq_iff_dvd' Nat.modEq_iff_dvd'

theorem mod_modEq (a n) : a % n ≡ a [MOD n] :=
  mod_mod _ _
#align nat.mod_modeq Nat.mod_modEq

namespace ModEq

lemma of_dvd (d : m ∣ n) (h : a ≡ b [MOD n]) : a ≡ b [MOD m] := modEq_of_dvd $ d.natCast.trans h.dvd
#align nat.modeq.of_dvd Nat.ModEq.of_dvd

protected theorem mul_left' (c : ℕ) (h : a ≡ b [MOD n]) : c * a ≡ c * b [MOD c * n] := by
  unfold ModEq at *; rw [mul_mod_mul_left, mul_mod_mul_left, h]
#align nat.modeq.mul_left' Nat.ModEq.mul_left'

protected theorem mul_left (c : ℕ) (h : a ≡ b [MOD n]) : c * a ≡ c * b [MOD n] :=
  (h.mul_left' _).of_dvd (dvd_mul_left _ _)
#align nat.modeq.mul_left Nat.ModEq.mul_left

protected theorem mul_right' (c : ℕ) (h : a ≡ b [MOD n]) : a * c ≡ b * c [MOD n * c] := by
  rw [mul_comm a, mul_comm b, mul_comm n]; exact h.mul_left' c
#align nat.modeq.mul_right' Nat.ModEq.mul_right'

protected theorem mul_right (c : ℕ) (h : a ≡ b [MOD n]) : a * c ≡ b * c [MOD n] := by
  rw [mul_comm a, mul_comm b]; exact h.mul_left c
#align nat.modeq.mul_right Nat.ModEq.mul_right

protected theorem mul (h₁ : a ≡ b [MOD n]) (h₂ : c ≡ d [MOD n]) : a * c ≡ b * d [MOD n] :=
  (h₂.mul_left _).trans (h₁.mul_right _)
#align nat.modeq.mul Nat.ModEq.mul

protected theorem pow (m : ℕ) (h : a ≡ b [MOD n]) : a ^ m ≡ b ^ m [MOD n] := by
  induction m with
  | zero => rfl
  | succ d hd =>
    rw[pow_succ, pow_succ]
    exact hd.mul h
#align nat.modeq.pow Nat.ModEq.pow

protected

protected theorem add_left (c : ℕ) (h : a ≡ b [MOD n]) : c + a ≡ c + b [MOD n] :=
  ModEq.rfl.add h
#align nat.modeq.add_left Nat.ModEq.add_left

protected theorem add_right (c : ℕ) (h : a ≡ b [MOD n]) : a + c ≡ b + c [MOD n] :=
  h.add ModEq.rfl
#align nat.modeq.add_right Nat.ModEq.add_right

protected theorem add_left_cancel (h₁ : a ≡ b [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    c ≡ d [MOD n] := by
  simp only [modEq_iff_dvd, Int.ofNat_add] at *
  rw [add_sub_add_comm] at h₂
  convert _root_.dvd_sub h₂ h₁ using 1
  rw [add_sub_cancel']
#align nat.modeq.add_left_cancel Nat.ModEq.add_left_cancel

protected theorem add_left_cancel' (c : ℕ) (h : c + a ≡ c + b [MOD n]) : a ≡ b [MOD n] :=
  ModEq.rfl.add_left_cancel h
#align nat.modeq.add_left_cancel' Nat.ModEq.add_left_cancel'

protected theorem add_right_cancel (h₁ : c ≡ d [MOD n]) (h₂ : a + c ≡ b + d [MOD n]) :
    a ≡ b [MOD n] := by
  rw [add_comm a, add_comm b] at h₂
  exact h₁.add_left_cancel h₂
#align nat.modeq.add_right_cancel Nat.ModEq.add_right_cancel

protected theorem add_right_cancel' (c : ℕ) (h : a + c ≡ b + c [MOD n]) : a ≡ b [MOD n] :=
  ModEq.rfl.add_right_cancel h
#align nat.modeq.add_right_cancel' Nat.ModEq.add_right_cancel'

/-- Cancel left multiplication on both sides of the `≡` and in the modulus.

For cancelling left multiplication in the modulus, see `Nat.ModEq.of_mul_left`. -/
protected theorem mul_left_cancel' {a b c m : ℕ} (hc : c ≠ 0) :
    c * a ≡ c * b [MOD c * m] → a ≡ b [MOD m] := by
  simp [modEq_iff_dvd, ← mul_sub, mul_dvd_mul_iff_left (by simp [hc] : (c : ℤ) ≠ 0)]
#align nat.modeq.mul_left_cancel' Nat.ModEq.mul_left_cancel'

protected theorem mul_left_cancel_iff' {a b c m : ℕ} (hc : c ≠ 0) :
    c * a ≡ c * b [MOD c * m] ↔ a ≡ b [MOD m] :=
  ⟨ModEq.mul_left_cancel' hc, ModEq.mul_left' _⟩
#align nat.modeq.mul_left_cancel_iff' Nat.ModEq.mul_left_cancel_iff'

/-- Cancel right multiplication on both sides of the `≡` and in the modulus.

For cancelling right multiplication in the modulus, see `Nat.ModEq.of_mul_right`. -/
protected theorem mul_right_cancel' {a b c m : ℕ} (hc : c ≠ 0) :
    a * c ≡ b * c [MOD m * c] → a ≡ b [MOD m] := by
  simp [modEq_iff_dvd, ← sub_mul, mul_dvd_mul_iff_right (by simp [hc] : (c : ℤ) ≠ 0)]
#align nat.modeq.mul_right_cancel' Nat.ModEq.mul_right_cancel'

protected theorem mul_right_cancel_iff' {a b c m : ℕ} (hc : c ≠ 0) :
    a * c ≡ b * c [MOD m * c] ↔ a ≡ b [MOD m] :=
  ⟨ModEq.mul_right_cancel' hc, ModEq.mul_right' _⟩
#align nat.modeq.mul_right_cancel_iff' Nat.ModEq.mul_right_cancel_iff'

/-- Cancel left multiplication in the modulus.

For cancelling left multiplication on both sides of the `≡`, see `nat.modeq.mul_left_cancel'`. -/
lemma of_mul_left (m : ℕ) (h : a ≡ b [MOD m * n]) : a ≡ b [MOD n] := by
  rw [modEq_iff_dvd] at *
  exact (dvd_mul_left (n : ℤ) (m : ℤ)).trans h
#align nat.modeq.of_mul_left Nat.ModEq.of_mul_left

/-- Cancel right multiplication in the modulus.

For cancelling right multiplication on both sides of the `≡`, see `nat.modeq.mul_right_cancel'`. -/
lemma of_mul_right (m : ℕ) : a ≡ b [MOD n * m] → a ≡ b [MOD n] := mul_comm m n ▸ of_mul_left _
#align nat.modeq.of_mul_right Nat.ModEq.of_mul_right

end ModEq

lemma modEq_sub (h : b ≤ a) : a ≡ b [MOD a - b] := (modEq_of_dvd $ by rw [Int.ofNat_sub h]).symm
#align nat.modeq_sub Nat.modEq_sub

lemma modEq_one : a ≡ b [MOD 1] := modEq_of_dvd $ one_dvd _
#align nat.modeq_one Nat.modEq_one

@[simp] lemma modEq_zero_iff : a ≡ b [MOD 0] ↔ a = b := by rw [ModEq, mod_zero, mod_zero]
#align nat.modeq_zero_iff Nat.modEq_zero_iff

@[simp] lemma add_modEq_left : n + a ≡ a [MOD n] := by rw [ModEq, add_mod_left]
#align nat.add_modeq_left Nat.add_modEq_left

@[simp] lemma add_modEq_right : a + n ≡ a [MOD n] := by rw [ModEq, add_mod_right]
#align nat.add_modeq_right Nat.add_modEq_right

namespace ModEq

theorem le_of_lt_add (h1 : a ≡ b [MOD m]) (h2 : a < b + m) : a ≤ b :=
  (le_total a b).elim id fun h3 =>
    Nat.le_of_sub_eq_zero
      (eq_zero_of_dvd_of_lt ((modEq_iff_dvd' h3).mp h1.symm) ((tsub_lt_iff_left h3).mpr h2))
#align nat.modeq.le_of_lt_add Nat.ModEq.le_of_lt_add

theorem add_le_of_lt (h1 : a ≡ b [MOD m]) (h2 : a < b) : a + m ≤ b :=
  le_of_lt_add (add_modEq_right.trans h1) (add_lt_add_right h2 m)
#align nat.modeq.add_le_of_lt Nat.ModEq.add_le_of_lt

theorem dvd_iff (h : a ≡ b [MOD m]) (hdm : d ∣ m) : d ∣ a ↔ d ∣ b := by
  simp only [← modEq_zero_iff_dvd]
  replace h := h.of_dvd hdm
  exact ⟨h.symm.trans, h.trans⟩
#align nat.modeq.dvd_iff Nat.ModEq.dvd_iff

theorem gcd_eq (h : a ≡ b [MOD m]) : gcd a m = gcd b m := by
  have h1 := gcd_dvd_right a m
  have h2 := gcd_dvd_right b m
  exact
    dvd_antisymm (dvd_gcd ((h.dvd_iff h1).mp (gcd_dvd_left a m)) h1)
      (dvd_gcd ((h.dvd_iff h2).mpr (gcd_dvd_left b m)) h2)
#align nat.modeq.gcd_eq Nat.ModEq.gcd_eq

lemma eq_of_abs_lt (h : a ≡ b [MOD m]) (h2 : |(b : ℤ) - a| < m) : a = b := by
  apply Int.ofNat.inj
  rw [eq_comm, ← sub_eq_zero]
  exact Int.eq_zero_of_abs_lt_dvd h.dvd h2
#align nat.modeq.eq_of_abs_lt Nat.ModEq.eq_of_abs_lt

lemma eq_of_lt_of_lt (h : a ≡ b [MOD m]) (ha : a < m) (hb : b < m) : a = b :=
h.eq_of_abs_lt $ abs_sub_lt_iff.2
  ⟨(sub_le_self _ $ Int.coe_nat_nonneg _).trans_lt $ Int.ofNat_lt.2 hb,
   (sub_le_self _ $ Int.coe_nat_nonneg _).trans_lt $ Int.ofNat_lt.2 ha⟩
#align nat.modeq.eq_of_lt_of_lt Nat.ModEq.eq_of_lt_of_lt

/-- To cancel a common factor `c` from a `ModEq` we must divide the modulus `m` by `gcd m c` -/
lemma cancel_left_div_gcd (hm : 0 < m) (h : c * a ≡ c * b [MOD m]) :  a ≡ b [MOD m / gcd m c] := by
  let d := gcd m c
  have hmd := gcd_dvd_left m c
  have hcd := gcd_dvd_right m c
  rw [modEq_iff_dvd]
  refine' @Int.dvd_of_dvd_mul_right_of_gcd_one (m / d) (c / d) (b - a) _ _
  show (m / d : ℤ) ∣ c / d * (b - a)
  · rw [mul_comm, ← Int.mul_ediv_assoc (b - a) (Int.coe_nat_dvd.mpr hcd), mul_comm]
    apply Int.ediv_dvd_ediv (Int.coe_nat_dvd.mpr hmd)
    rw [mul_sub]
    exact modEq_iff_dvd.mp h
  show Int.gcd (m / d) (c / d) = 1
  ·
    simp only [← Int.coe_nat_div, Int.coe_nat_gcd (m / d) (c / d), gcd_div hmd hcd,
      Nat.div_self (gcd_pos_of_pos_left c hm)]
#align nat.modeq.cancel_left_div_gcd Nat.ModEq.cancel_left_div_gcd

/-- To cancel a common factor `c` from a `ModEq` we must divide the modulus `m` by `gcd m c` -/
lemma cancel_right_div_gcd (hm : 0 < m) (h : a * c ≡ b * c [MOD m]) : a ≡ b [MOD m / gcd m c] := by
  apply cancel_left_div_gcd hm
  simpa [mul_comm] using h
#align nat.modeq.cancel_right_div_gcd Nat.ModEq.cancel_right_div_gcd

lemma cancel_left_div_gcd' (hm : 0 < m) (hcd : c ≡ d [MOD m]) (h : c * a ≡ d * b [MOD m]) :
  a ≡ b [MOD m / gcd m c] :=
(h.trans $ hcd.symm.mul_right b).cancel_left_div_gcd hm
#align nat.modeq.cancel_left_div_gcd' Nat.ModEq.cancel_left_div_gcd'

lemma cancel_right_div_gcd' (hm : 0 < m) (hcd : c ≡ d [MOD m]) (h : a * c ≡ b * d [MOD m]) :
  a ≡ b [MOD m / gcd m c] :=
(h.trans $ hcd.symm.mul_left b).cancel_right_div_gcd hm
#align nat.modeq.cancel_right_div_gcd' Nat.ModEq.cancel_right_div_gcd'

/-- A common factor that's coprime with the modulus can be cancelled from a `ModEq` -/
lemma cancel_left_of_coprime (hmc : gcd m c = 1) (h : c * a ≡ c * b [MOD m]) : a ≡ b [MOD m] := by
  rcases m.eq_zero_or_pos with (rfl | hm)
  · simp only [gcd_zero_left] at hmc
    simp only [gcd_zero_left, hmc, one_mul, modEq_zero_iff] at h
    subst h
    rfl
  simpa [hmc] using h.cancel_left_div_gcd hm
#align nat.modeq.cancel_left_of_coprime Nat.ModEq.cancel_left_of_coprime

/-- A common factor that's coprime with the modulus can be cancelled from a `ModEq` -/
lemma cancel_right_of_coprime (hmc : gcd m c = 1) (h : a * c ≡ b * c [MOD m]) : a ≡ b [MOD m] :=
cancel_left_of_coprime hmc $ by simpa [mul_comm] using h
#align nat.modeq.cancel_right_of_coprime Nat.ModEq.cancel_right_of_coprime

end ModEq

/-- The natural number less than `lcm n m` congruent to `a` mod `n` and `b` mod `m` -/
def chineseRemainder' (h : a ≡ b [MOD gcd n m]) : { k // k ≡ a [MOD n] ∧ k ≡ b [MOD m] } :=
  if hn : n = 0 then ⟨a, by rw [hn, gcd_zero_left] at h; constructor; rfl; exact h⟩
  else
    if hm : m = 0 then ⟨b, by rw [hm, gcd_zero_right] at h; constructor; exact h.symm; rfl⟩
    else
      ⟨let (c, d) := xgcd n m
       Int.toNat ((n * c * b + m * d * a) / gcd n m % lcm n m),
       by
        rw [xgcd_val]
        dsimp
        rw [modEq_iff_dvd, modEq_iff_dvd,
          Int.toNat_of_nonneg (Int.emod_nonneg _ (Int.coe_nat_ne_zero.2 (lcm_ne_zero hn hm)))]
        have hnonzero : (gcd n m : ℤ) ≠ 0 := by
          norm_cast
          rw [Nat.gcd_eq_zero_iff, not_and]
          exact fun _ => hm
        have hcoedvd : ∀ t, (gcd n m : ℤ) ∣ t * (b - a) := fun t => h.dvd.mul_left _
        have := gcd_eq_gcd_ab n m

        constructor <;> rw [Int.emod_def, ← sub_add] <;>
            refine' dvd_add _ (dvd_mul_of_dvd_left _ _) <;>
          try norm_cast
        · rw [← sub_eq_iff_eq_add'] at this
          rw [← this, sub_mul, ← add_sub_assoc, add_comm, add_sub_assoc, ← mul_sub,
            Int.add_ediv_of_dvd_left, Int.mul_ediv_cancel_left _ hnonzero,
            Int.mul_ediv_assoc _ h.dvd, ← sub_sub, sub_self, zero_sub, dvd_neg, mul_assoc]
          exact dvd_mul_right _ _
          norm_cast
          exact dvd_mul_right _ _
        · exact dvd_lcm_left n m
        · rw [← sub_eq_iff_eq_add] at this
          rw [← this, sub_mul, sub_add, ← mul_sub, Int.sub_ediv_of_dvd,
            Int.mul_ediv_cancel_left _ hnonzero, Int.mul_ediv_assoc _ h.dvd, ← sub_add, sub_self,
            zero_add, mul_assoc]
          exact dvd_mul_right _ _
          exact hcoedvd _
        · exact dvd_lcm_right n m⟩
#align nat.chinese_remainder' Nat.chineseRemainder'

/-- The natural number less than `n*m` congruent to `a` mod `n` and `b` mod `m` -/
def chineseRemainder (co : n.coprime m) (a b : ℕ) : { k // k ≡ a [MOD n] ∧ k ≡ b [MOD m] } :=
  chineseRemainder' (by convert @modEq_one a b)
#align nat.chinese_remainder Nat.chineseRemainder

theorem chineseRemainder'_lt_lcm (h : a ≡ b [MOD gcd n m]) (hn : n ≠ 0) (hm : m ≠ 0) :
    ↑(chineseRemainder' h) < lcm n m := by
  dsimp only [chineseRemainder']
  rw [dif_neg hn, dif_neg hm, Subtype.coe_mk, xgcd_val, ← Int.toNat_coe_nat (lcm n m)]
  have lcm_pos := Int.coe_nat_pos.mpr (Nat.pos_of_ne_zero (lcm_ne_zero hn hm))
  exact (Int.toNat_lt_toNat lcm_pos).mpr (Int.emod_lt_of_pos _ lcm_pos)
#align nat.chinese_remainder'_lt_lcm Nat.chineseRemainder'_lt_lcm

theorem chineseRemainder_lt_mul (co : n.coprime m) (a b : ℕ) (hn : n ≠ 0) (hm : m ≠ 0) :
    ↑(chineseRemainder co a b) < n * m :=
  lt_of_lt_of_le (chineseRemainder'_lt_lcm _ hn hm) (le_of_eq co.lcm_eq_mul)
#align nat.chinese_remainder_lt_mul Nat.chineseRemainder_lt_mul

theorem modEq_and_modEq_iff_modEq_mul {a b m n : ℕ} (hmn : m.coprime n) :
    a ≡ b [MOD m] ∧ a ≡ b [MOD n] ↔ a ≡ b [MOD m * n] :=
  ⟨fun h => by
    rw [Nat.modEq_iff_dvd, Nat.modEq_iff_dvd, ← Int.dvd_natAbs, Int.coe_nat_dvd, ← Int.dvd_natAbs,
      Int.coe_nat_dvd] at h
    rw [Nat.modEq_iff_dvd, ← Int.dvd_natAbs, Int.coe_nat_dvd]
    exact hmn.mul_dvd_of_dvd_of_dvd h.1 h.2, fun h =>
    ⟨h.of_mul_right _, h.of_mul_left _⟩⟩
#align nat.modeq_and_modeq_iff_modeq_mul Nat.modEq_and_modEq_iff_modEq_mul

theorem coprime_of_mul_modEq_one (b : ℕ) {a n : ℕ} (h : a * b ≡ 1 [MOD n]) : a.coprime n := by
  obtain ⟨g, hh⟩ := Nat.gcd_dvd_right a n
  rw [Nat.coprime_iff_gcd_eq_one, ← Nat.dvd_one, ← Nat.modEq_zero_iff_dvd]
  calc
    1 ≡ a * b [MOD a.gcd n] := (hh ▸ h).symm.of_mul_right g
    _ ≡ 0 * b [MOD a.gcd n] := (Nat.modEq_zero_iff_dvd.mpr (Nat.gcd_dvd_left _ _)).mul_right b
    _ = 0 := by rw [zero_mul]

#align nat.coprime_of_mul_modeq_one Nat.coprime_of_mul_modEq_one

@[simp 1100]
theorem mod_mul_right_mod (a b c : ℕ) : a % (b * c) % b = a % b :=
  (mod_modEq _ _).of_mul_right _
#align nat.mod_mul_right_mod Nat.mod_mul_right_mod

@[simp 1100]
theorem mod_mul_left_mod (a b c : ℕ) : a % (b * c) % c = a % c :=
  (mod_modEq _ _).of_mul_left _
#align nat.mod_mul_left_mod Nat.mod_mul_left_mod

theorem div_mod_eq_mod_mul_div (a b c : ℕ) : a / b % c = a % (b * c) / b :=
  if hb0 : b = 0 then by simp [hb0]
  else by
    rw [← @add_right_cancel_iff _ _ _ (c * (a / b / c)), mod_add_div, Nat.div_div_eq_div_mul, ←
      mul_right_inj' hb0, ← @add_left_cancel_iff _ _ _ (a % b), mod_add_div, mul_add, ←
      @add_left_cancel_iff _ _ _ (a % (b * c) % b), add_left_comm, ← add_assoc (a % (b * c) % b),
      mod_add_div, ← mul_assoc, mod_add_div, mod_mul_right_mod]
#align nat.div_mod_eq_mod_mul_div Nat.div_mod_eq_mod_mul_div

theorem add_mod_add_ite (a b c : ℕ) :
    ((a + b) % c + if c ≤ a % c + b % c then c else 0) = a % c + b % c :=
  have : (a + b) % c = (a % c + b % c) % c := ((mod_modEq _ _).add <| mod_modEq _ _).symm
  if hc0 : c = 0 then by simp [hc0, Nat.mod_zero]
  else by
    rw [this]
    split_ifs with h
    · have h2 : (a % c + b % c) / c < 2 :=
        Nat.div_lt_of_lt_mul
          (by
            rw [mul_two]
            exact
              add_lt_add (Nat.mod_lt _ (Nat.pos_of_ne_zero hc0))
                (Nat.mod_lt _ (Nat.pos_of_ne_zero hc0)))
      have h0 : 0 < (a % c + b % c) / c := Nat.div_pos h (Nat.pos_of_ne_zero hc0)
      rw [← @add_right_cancel_iff _ _ _ (c * ((a % c + b % c) / c)), add_comm _ c, add_assoc,
        mod_add_div, le_antisymm (le_of_lt_succ h2) h0, mul_one, add_comm]
    · rw [Nat.mod_eq_of_lt (lt_of_not_ge h), add_zero]
#align nat.add_mod_add_ite Nat.add_mod_add_ite

theorem add_mod_of_add_mod_lt {a b c : ℕ} (hc : a % c + b % c < c) : (a + b) % c = a % c + b % c :=
  by rw [← add_mod_add_ite, if_neg (not_le_of_lt hc), add_zero]
#align nat.add_mod_of_add_mod_lt Nat.add_mod_of_add_mod_lt

theorem add_mod_add_of_le_add_mod {a b c : ℕ} (hc : c ≤ a % c + b % c) :
    (a + b) % c + c = a % c + b % c := by rw [← add_mod_add_ite, if_pos hc]
#align nat.add_mod_add_of_le_add_mod Nat.add_mod_add_of_le_add_mod

theorem add_div {a b c : ℕ} (hc0 : 0 < c) :
    (a + b) / c = a / c + b / c + if c ≤ a % c + b % c then 1 else 0 := by
  rw [← mul_right_inj' hc0.ne', ← @add_left_cancel_iff _ _ _ ((a + b) % c + a % c + b % c)]
  suffices
    (a + b) % c + c * ((a + b) / c) + a % c + b % c =
      (a % c + c * (a / c) + (b % c + c * (b / c)) + c * if c ≤ a % c + b % c then 1 else 0) +
        (a + b) % c
    by simpa only [mul_add, add_comm, add_left_comm, add_assoc]
  rw [mod_add_div, mod_add_div, mod_add_div, mul_ite, add_assoc, add_assoc]
  conv_lhs => rw [← add_mod_add_ite]
  simp
  ac_rfl
#align nat.add_div Nat.add_div

theorem add_div_eq_of_add_mod_lt {a b c : ℕ} (hc : a % c + b % c < c) :
    (a + b) / c = a / c + b / c :=
  if hc0 : c = 0 then by simp [hc0]
  else by rw [add_div (Nat.pos_of_ne_zero hc0), if_neg (not_le_of_lt hc), add_zero]
#align nat.add_div_eq_of_add_mod_lt Nat.add_div_eq_of_add_mod_lt

protected theorem add_div_of_dvd_right {a b c : ℕ} (hca : c ∣ a) : (a + b) / c = a / c + b / c :=
  if h : c = 0 then by simp [h]
  else
    add_div_eq_of_add_mod_lt
      (by
        rw [Nat.mod_eq_zero_of_dvd hca, zero_add]
        exact Nat.mod_lt _ (pos_iff_ne_zero.mpr h))
#align nat.add_div_of_dvd_right Nat.add_div_of_dvd_right

protected theorem add_div_of_dvd_left {a b c : ℕ} (hca : c ∣ b) : (a + b) / c = a / c + b / c := by
  rwa [add_comm, Nat.add_div_of_dvd_right, add_comm]
#align nat.add_div_of_dvd_left Nat.add_div_of_dvd_left

theorem add_div_eq_of_le_mod_add_mod {a b c : ℕ} (hc : c ≤ a % c + b % c) (hc0 : 0 < c) :
    (a + b) / c = a / c + b / c + 1 := by rw [add_div hc0, if_pos hc]
#align nat.add_div_eq_of_le_mod_add_mod Nat.add_div_eq_of_le_mod_add_mod

theorem add_div_le_add_div (a b c : ℕ) : a / c + b / c ≤ (a + b) / c :=
  if hc0 : c = 0 then by simp [hc0]
  else by rw [Nat.add_div (Nat.pos_of_ne_zero hc0)]; exact Nat.le_add_right _ _
#align nat.add_div_le_add_div Nat.add_div_le_add_div

theorem le_mod_add_mod_of_dvd_add_of_not_dvd {a b c : ℕ} (h : c ∣ a + b) (ha : ¬c ∣ a) :
    c ≤ a % c + b % c :=
  by_contradiction fun hc => by
    have : (a + b) % c = a % c + b % c := add_mod_of_add_mod_lt (lt_of_not_ge hc)
    simp_all [dvd_iff_mod_eq_zero]
#align nat.le_mod_add_mod_of_dvd_add_of_not_dvd Nat.le_mod_add_mod_of_dvd_add_of_not_dvd

theorem odd_mul_odd {n m : ℕ} : n % 2 = 1 → m % 2 = 1 → n * m % 2 = 1 := by
  simpa [Nat.ModEq] using @ModEq.mul 2 n 1 m 1
#align nat.odd_mul_odd Nat.odd_mul_odd

theorem odd_mul_odd_div_two {m n : ℕ} (hm1 : m % 2 = 1) (hn1 : n % 2 = 1) :
    m * n / 2 = m * (n / 2) + m / 2 :=
  have hm0 : 0 < m := Nat.pos_of_ne_zero fun h => by simp_all
  have hn0 : 0 < n := Nat.pos_of_ne_zero fun h => by simp_all
  mul_right_injective₀ two_ne_zero <| by
    dsimp
    rw [mul_add, two_mul_odd_div_two hm1, mul_left_comm, two_mul_odd_div_two hn1,
      two_mul_odd_div_two (Nat.odd_mul_odd hm1 hn1), mul_tsub, mul_one, ←
      add_tsub_assoc_of_le (succ_le_of_lt hm0),
      tsub_add_cancel_of_le (le_mul_of_one_le_right (Nat.zero_le _) hn0)]
#align nat.odd_mul_odd_div_two Nat.odd_mul_odd_div_two

theorem odd_of_mod_four_eq_one {n : ℕ} : n % 4 = 1 → n % 2 = 1 := by
  simpa [ModEq, show 2 * 2 = 4 by norm_num] using @ModEq.of_mul_left 2 n 1 2
#align nat.odd_of_mod_four_eq_one Nat.odd_of_mod_four_eq_one

theorem odd_of_mod_four_eq_three {n : ℕ} : n % 4 = 3 → n % 2 = 1 := by
  simpa [ModEq, show 2 * 2 = 4 by norm_num, show 3 % 4 = 3 by norm_num] using
    @ModEq.of_mul_left 2 n 3 2
#align nat.odd_of_mod_four_eq_three Nat.odd_of_mod_four_eq_three

/-- A natural number is odd iff it has residue `1` or `3` mod `4`-/
theorem odd_mod_four_iff {n : ℕ} : n % 2 = 1 ↔ n % 4 = 1 ∨ n % 4 = 3 :=
  have help : ∀ m : ℕ, m < 4 → m % 2 = 1 → m = 1 ∨ m = 3 := by decide
  ⟨fun hn =>
    help (n % 4) (mod_lt n (by norm_num)) <| (mod_mod_of_dvd n (by norm_num : 2 ∣ 4)).trans hn,
    fun h => Or.elim h odd_of_mod_four_eq_one odd_of_mod_four_eq_three⟩
#align nat.odd_mod_four_iff Nat.odd_mod_four_iff

end Nat