/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad

The integers, with addition, multiplication, and subtraction.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.nat.basic
import Mathlib.algebra.order_functions
import Mathlib.PostPort

universes u_1 u

namespace Mathlib

namespace int

protected instance inhabited : Inhabited ℤ := { default := int.zero }

protected instance nontrivial : nontrivial ℤ :=
  nontrivial.mk (Exists.intro 0 (Exists.intro 1 int.zero_ne_one))

protected instance comm_ring : comm_ring ℤ :=
  comm_ring.mk int.add int.add_assoc int.zero int.zero_add int.add_zero int.neg int.sub
    int.add_left_neg int.add_comm int.mul int.mul_assoc int.one int.one_mul int.mul_one
    int.distrib_left int.distrib_right int.mul_comm

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

-- instance : has_sub int            := by apply_instance -- This is in core

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 monoid : monoid ℤ := ring.to_monoid ℤ

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

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

protected instance semigroup : semigroup ℤ := monoid.to_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_semiring : comm_semiring ℤ := comm_ring.to_comm_semiring

protected instance semiring : semiring ℤ := ring.to_semiring

protected instance ring : ring ℤ := comm_ring.to_ring ℤ

protected instance distrib : distrib ℤ := ring.to_distrib ℤ

protected instance linear_ordered_comm_ring : linear_ordered_comm_ring ℤ :=
  linear_ordered_comm_ring.mk comm_ring.add comm_ring.add_assoc comm_ring.zero comm_ring.zero_add
    comm_ring.add_zero comm_ring.neg comm_ring.sub comm_ring.add_left_neg comm_ring.add_comm
    comm_ring.mul comm_ring.mul_assoc comm_ring.one comm_ring.one_mul comm_ring.mul_one
    comm_ring.left_distrib comm_ring.right_distrib linear_order.le linear_order.lt
    linear_order.le_refl linear_order.le_trans linear_order.le_antisymm int.add_le_add_left sorry
    int.mul_pos linear_order.le_total linear_order.decidable_le linear_order.decidable_eq
    linear_order.decidable_lt nontrivial.exists_pair_ne comm_ring.mul_comm

protected instance linear_ordered_add_comm_group : linear_ordered_add_comm_group ℤ :=
  linear_ordered_ring.to_linear_ordered_add_comm_group

theorem abs_eq_nat_abs (a : ℤ) : abs a = ↑(nat_abs a) :=
  int.cases_on a (fun (a : ℕ) => idRhs (abs ↑a = ↑a) (abs_of_nonneg (coe_zero_le a)))
    fun (a : ℕ) =>
      idRhs (abs (Int.negSucc a) = -Int.negSucc a) (abs_of_nonpos (le_of_lt (neg_succ_lt_zero a)))

theorem nat_abs_abs (a : ℤ) : nat_abs (abs a) = nat_abs a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs (abs a) = nat_abs a)) (abs_eq_nat_abs a)))
    (Eq.refl (nat_abs ↑(nat_abs a)))

theorem sign_mul_abs (a : ℤ) : sign a * abs a = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (sign a * abs a = a)) (abs_eq_nat_abs a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (sign a * ↑(nat_abs a) = a)) (sign_mul_nat_abs a)))
      (Eq.refl a))

@[simp] theorem default_eq_zero : Inhabited.default = 0 := rfl

@[simp] theorem add_def {a : ℤ} {b : ℤ} : int.add a b = a + b := rfl

@[simp] theorem mul_def {a : ℤ} {b : ℤ} : int.mul a b = a * b := rfl

@[simp] theorem coe_nat_mul_neg_succ (m : ℕ) (n : ℕ) : ↑m * Int.negSucc n = -(↑m * ↑(Nat.succ n)) :=
  rfl

@[simp] theorem neg_succ_mul_coe_nat (m : ℕ) (n : ℕ) : Int.negSucc m * ↑n = -(↑(Nat.succ m) * ↑n) :=
  rfl

@[simp] theorem neg_succ_mul_neg_succ (m : ℕ) (n : ℕ) :
    Int.negSucc m * Int.negSucc n = ↑(Nat.succ m) * ↑(Nat.succ n) :=
  rfl

@[simp] theorem coe_nat_le {m : ℕ} {n : ℕ} : ↑m ≤ ↑n ↔ m ≤ n := coe_nat_le_coe_nat_iff m n

@[simp] theorem coe_nat_lt {m : ℕ} {n : ℕ} : ↑m < ↑n ↔ m < n := coe_nat_lt_coe_nat_iff m n

@[simp] theorem coe_nat_inj' {m : ℕ} {n : ℕ} : ↑m = ↑n ↔ m = n := int.coe_nat_eq_coe_nat_iff m n

@[simp] theorem coe_nat_pos {n : ℕ} : 0 < ↑n ↔ 0 < n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 < ↑n ↔ 0 < n)) (Eq.symm int.coe_nat_zero)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑0 < ↑n ↔ 0 < n)) (propext coe_nat_lt))) (iff.refl (0 < n)))

@[simp] theorem coe_nat_eq_zero {n : ℕ} : ↑n = 0 ↔ n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑n = 0 ↔ n = 0)) (Eq.symm int.coe_nat_zero)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑n = ↑0 ↔ n = 0)) (propext coe_nat_inj'))) (iff.refl (n = 0)))

theorem coe_nat_ne_zero {n : ℕ} : ↑n ≠ 0 ↔ n ≠ 0 := not_congr coe_nat_eq_zero

@[simp] theorem coe_nat_nonneg (n : ℕ) : 0 ≤ ↑n := iff.mpr coe_nat_le (nat.zero_le n)

theorem coe_nat_ne_zero_iff_pos {n : ℕ} : ↑n ≠ 0 ↔ 0 < n :=
  { mp := fun (h : ↑n ≠ 0) => nat.pos_of_ne_zero (iff.mp coe_nat_ne_zero h),
    mpr := fun (h : 0 < n) => ne.symm (ne_of_lt (iff.mpr coe_nat_lt h)) }

theorem coe_nat_succ_pos (n : ℕ) : 0 < ↑(Nat.succ n) := iff.mpr coe_nat_pos (nat.succ_pos n)

@[simp] theorem coe_nat_abs (n : ℕ) : abs ↑n = ↑n := abs_of_nonneg (coe_nat_nonneg n)

/-! ### succ and pred -/

/-- Immediate successor of an integer: `succ n = n + 1` -/
def succ (a : ℤ) : ℤ := a + 1

/-- Immediate predecessor of an integer: `pred n = n - 1` -/
def pred (a : ℤ) : ℤ := a - 1

theorem nat_succ_eq_int_succ (n : ℕ) : ↑(Nat.succ n) = succ ↑n := rfl

theorem pred_succ (a : ℤ) : pred (succ a) = a := add_sub_cancel a 1

theorem succ_pred (a : ℤ) : succ (pred a) = a := sub_add_cancel a 1

theorem neg_succ (a : ℤ) : -succ a = pred (-a) := neg_add a 1

theorem succ_neg_succ (a : ℤ) : succ (-succ a) = -a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (succ (-succ a) = -a)) (neg_succ a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (succ (pred (-a)) = -a)) (succ_pred (-a)))) (Eq.refl (-a)))

theorem neg_pred (a : ℤ) : -pred a = succ (-a) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (-pred a = succ (-a))) (eq_neg_of_eq_neg (Eq.symm (neg_succ (-a))))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (-pred a = -pred ( --a))) (neg_neg a))) (Eq.refl (-pred a)))

theorem pred_neg_pred (a : ℤ) : pred (-pred a) = -a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (pred (-pred a) = -a)) (neg_pred a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (pred (succ (-a)) = -a)) (pred_succ (-a)))) (Eq.refl (-a)))

theorem pred_nat_succ (n : ℕ) : pred ↑(Nat.succ n) = ↑n := pred_succ ↑n

theorem neg_nat_succ (n : ℕ) : -↑(Nat.succ n) = pred (-↑n) := neg_succ ↑n

theorem succ_neg_nat_succ (n : ℕ) : succ (-↑(Nat.succ n)) = -↑n := succ_neg_succ ↑n

theorem lt_succ_self (a : ℤ) : a < succ a := lt_add_of_pos_right a zero_lt_one

theorem pred_self_lt (a : ℤ) : pred a < a := sub_lt_self a zero_lt_one

theorem add_one_le_iff {a : ℤ} {b : ℤ} : a + 1 ≤ b ↔ a < b := iff.rfl

theorem lt_add_one_iff {a : ℤ} {b : ℤ} : a < b + 1 ↔ a ≤ b := add_le_add_iff_right 1

theorem le_add_one {a : ℤ} {b : ℤ} (h : a ≤ b) : a ≤ b + 1 := le_of_lt (iff.mpr lt_add_one_iff h)

theorem sub_one_lt_iff {a : ℤ} {b : ℤ} : a - 1 < b ↔ a ≤ b :=
  iff.trans sub_lt_iff_lt_add lt_add_one_iff

theorem le_sub_one_iff {a : ℤ} {b : ℤ} : a ≤ b - 1 ↔ a < b := le_sub_iff_add_le

protected theorem induction_on {p : ℤ → Prop} (i : ℤ) (hz : p 0) (hp : ∀ (i : ℕ), p ↑i → p (↑i + 1))
    (hn : ∀ (i : ℕ), p (-↑i) → p (-↑i - 1)) : p i :=
  sorry

/-- Inductively define a function on `ℤ` by defining it at `b`, for the `succ` of a number greater
  than `b`, and the `pred` of a number less than `b`. -/
protected def induction_on' {C : ℤ → Sort u_1} (z : ℤ) (b : ℤ) :
    C b → ((k : ℤ) → b ≤ k → C k → C (k + 1)) → ((k : ℤ) → k ≤ b → C k → C (k - 1)) → C z :=
  fun (H0 : C b) (Hs : (k : ℤ) → b ≤ k → C k → C (k + 1))
    (Hp : (k : ℤ) → k ≤ b → C k → C (k - 1)) =>
    eq.mpr sorry
      (Int.rec
        (fun (n : ℕ) =>
          Nat.rec (eq.mpr sorry (eq.mpr sorry H0))
            (fun (n : ℕ) (ih : C (Int.ofNat n + b)) =>
              eq.mpr sorry
                (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (Hs (Int.ofNat n + b) sorry ih)))))
            n)
        (fun (n : ℕ) =>
          Nat.rec
            (eq.mpr sorry
              (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (Hp b sorry H0))))))
            (fun (n : ℕ) (ih : C (Int.negSucc n + b)) =>
              eq.mpr sorry
                (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (Hp (Int.negSucc n + b) sorry ih)))))
            n)
        (z - b))

/-! ### nat abs -/

theorem nat_abs_add_le (a : ℤ) (b : ℤ) : nat_abs (a + b) ≤ nat_abs a + nat_abs b := sorry

theorem nat_abs_neg_of_nat (n : ℕ) : nat_abs (neg_of_nat n) = n :=
  nat.cases_on n (Eq.refl (nat_abs (neg_of_nat 0)))
    fun (n : ℕ) => Eq.refl (nat_abs (neg_of_nat (Nat.succ n)))

theorem nat_abs_mul (a : ℤ) (b : ℤ) : nat_abs (a * b) = nat_abs a * nat_abs b := sorry

theorem nat_abs_mul_nat_abs_eq {a : ℤ} {b : ℤ} {c : ℕ} (h : a * b = ↑c) :
    nat_abs a * nat_abs b = c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a * nat_abs b = c)) (Eq.symm (nat_abs_mul a b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs (a * b) = c)) h))
      (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs ↑c = c)) (nat_abs_of_nat c))) (Eq.refl c)))

@[simp] theorem nat_abs_mul_self' (a : ℤ) : ↑(nat_abs a) * ↑(nat_abs a) = a * a :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (↑(nat_abs a) * ↑(nat_abs a) = a * a))
        (Eq.symm (int.coe_nat_mul (nat_abs a) (nat_abs a)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(nat_abs a * nat_abs a) = a * a)) nat_abs_mul_self))
      (Eq.refl (a * a)))

theorem neg_succ_of_nat_eq' (m : ℕ) : Int.negSucc m = -↑m - 1 := sorry

theorem nat_abs_ne_zero_of_ne_zero {z : ℤ} (hz : z ≠ 0) : nat_abs z ≠ 0 :=
  fun (h : nat_abs z = 0) => hz (eq_zero_of_nat_abs_eq_zero h)

@[simp] theorem nat_abs_eq_zero {a : ℤ} : nat_abs a = 0 ↔ a = 0 :=
  { mp := eq_zero_of_nat_abs_eq_zero, mpr := fun (h : a = 0) => Eq.symm h ▸ rfl }

theorem nat_abs_lt_nat_abs_of_nonneg_of_lt {a : ℤ} {b : ℤ} (w₁ : 0 ≤ a) (w₂ : a < b) :
    nat_abs a < nat_abs b :=
  sorry

theorem nat_abs_eq_iff_mul_self_eq {a : ℤ} {b : ℤ} : nat_abs a = nat_abs b ↔ a * a = b * b := sorry

theorem nat_abs_lt_iff_mul_self_lt {a : ℤ} {b : ℤ} : nat_abs a < nat_abs b ↔ a * a < b * b := sorry

theorem nat_abs_le_iff_mul_self_le {a : ℤ} {b : ℤ} : nat_abs a ≤ nat_abs b ↔ a * a ≤ b * b := sorry

theorem nat_abs_eq_iff_sq_eq {a : ℤ} {b : ℤ} : nat_abs a = nat_abs b ↔ a ^ bit0 1 = b ^ bit0 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a = nat_abs b ↔ a ^ bit0 1 = b ^ bit0 1)) (pow_two a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a = nat_abs b ↔ a * a = b ^ bit0 1)) (pow_two b)))
      nat_abs_eq_iff_mul_self_eq)

theorem nat_abs_lt_iff_sq_lt {a : ℤ} {b : ℤ} : nat_abs a < nat_abs b ↔ a ^ bit0 1 < b ^ bit0 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a < nat_abs b ↔ a ^ bit0 1 < b ^ bit0 1)) (pow_two a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a < nat_abs b ↔ a * a < b ^ bit0 1)) (pow_two b)))
      nat_abs_lt_iff_mul_self_lt)

theorem nat_abs_le_iff_sq_le {a : ℤ} {b : ℤ} : nat_abs a ≤ nat_abs b ↔ a ^ bit0 1 ≤ b ^ bit0 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a ≤ nat_abs b ↔ a ^ bit0 1 ≤ b ^ bit0 1)) (pow_two a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a ≤ nat_abs b ↔ a * a ≤ b ^ bit0 1)) (pow_two b)))
      nat_abs_le_iff_mul_self_le)

/-! ### `/`  -/

@[simp] theorem of_nat_div (m : ℕ) (n : ℕ) : Int.ofNat (m / n) = Int.ofNat m / Int.ofNat n := rfl

@[simp] theorem coe_nat_div (m : ℕ) (n : ℕ) : ↑(m / n) = ↑m / ↑n := rfl

theorem neg_succ_of_nat_div (m : ℕ) {b : ℤ} (H : 0 < b) : Int.negSucc m / b = -(↑m / b + 1) := sorry

@[simp] protected theorem div_neg (a : ℤ) (b : ℤ) : a / -b = -(a / b) := sorry

theorem div_of_neg_of_pos {a : ℤ} {b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b = -((-a - 1) / b + 1) :=
  sorry

protected theorem div_nonneg {a : ℤ} {b : ℤ} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a / b := sorry

protected theorem div_nonpos {a : ℤ} {b : ℤ} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a / b ≤ 0 :=
  nonpos_of_neg_nonneg
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ -(a / b))) (Eq.symm (int.div_neg a b))))
      (int.div_nonneg Ha (neg_nonneg_of_nonpos Hb)))

theorem div_neg' {a : ℤ} {b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 := sorry

-- Will be generalized to Euclidean domains.

protected theorem zero_div (b : ℤ) : 0 / b = 0 :=
  int.cases_on b
    (fun (b : ℕ) =>
      nat.cases_on b (idRhs (0 / 0 = 0 / 0) rfl)
        fun (b : ℕ) => idRhs (0 / ↑(b + 1) = 0 / ↑(b + 1)) rfl)
    fun (b : ℕ) => idRhs (0 / Int.negSucc b = 0 / Int.negSucc b) rfl

protected theorem div_zero (a : ℤ) : a / 0 = 0 :=
  int.cases_on a
    (fun (a : ℕ) =>
      nat.cases_on a (idRhs (0 / 0 = 0 / 0) rfl)
        fun (a : ℕ) => idRhs (↑(a + 1) / 0 = ↑(a + 1) / 0) rfl)
    fun (a : ℕ) => idRhs (Int.negSucc a / 0 = Int.negSucc a / 0) rfl

@[simp] protected theorem div_one (a : ℤ) : a / 1 = a := sorry

theorem div_eq_zero_of_lt {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a / b = 0 := sorry

theorem div_eq_zero_of_lt_abs {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : a < abs b) : a / b = 0 := sorry

protected theorem add_mul_div_right (a : ℤ) (b : ℤ) {c : ℤ} (H : c ≠ 0) :
    (a + b * c) / c = a / c + b :=
  sorry

protected theorem add_mul_div_left (a : ℤ) {b : ℤ} (c : ℤ) (H : b ≠ 0) :
    (a + b * c) / b = a / b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b * c) / b = a / b + c)) (mul_comm b c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a + c * b) / b = a / b + c)) (int.add_mul_div_right a c H)))
      (Eq.refl (a / b + c)))

protected theorem add_div_of_dvd_right {a : ℤ} {b : ℤ} {c : ℤ} (H : c ∣ b) :
    (a + b) / c = a / c + b / c :=
  sorry

protected theorem add_div_of_dvd_left {a : ℤ} {b : ℤ} {c : ℤ} (H : c ∣ a) :
    (a + b) / c = a / c + b / c :=
  sorry

@[simp] protected theorem mul_div_cancel (a : ℤ) {b : ℤ} (H : b ≠ 0) : a * b / b = a :=
  eq.mp (Eq._oldrec (Eq.refl (a * b / b = 0 + a)) (zero_add a))
    (eq.mp (Eq._oldrec (Eq.refl (a * b / b = 0 / b + a)) (int.zero_div b))
      (eq.mp (Eq._oldrec (Eq.refl ((0 + a * b) / b = 0 / b + a)) (zero_add (a * b)))
        (int.add_mul_div_right 0 a H)))

@[simp] protected theorem mul_div_cancel_left {a : ℤ} (b : ℤ) (H : a ≠ 0) : a * b / a = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b / a = b)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / a = b)) (int.mul_div_cancel b H))) (Eq.refl b))

@[simp] protected theorem div_self {a : ℤ} (H : a ≠ 0) : a / a = 1 :=
  eq.mp (Eq._oldrec (Eq.refl (1 * a / a = 1)) (one_mul a)) (int.mul_div_cancel 1 H)

/-! ### mod -/

theorem of_nat_mod (m : ℕ) (n : ℕ) : ↑m % ↑n = Int.ofNat (m % n) := rfl

@[simp] theorem coe_nat_mod (m : ℕ) (n : ℕ) : ↑(m % n) = ↑m % ↑n := rfl

theorem neg_succ_of_nat_mod (m : ℕ) {b : ℤ} (bpos : 0 < b) : Int.negSucc m % b = b - 1 - ↑m % b :=
  sorry

@[simp] theorem mod_neg (a : ℤ) (b : ℤ) : a % -b = a % b := sorry

@[simp] theorem mod_abs (a : ℤ) (b : ℤ) : a % abs b = a % b :=
  abs_by_cases (fun (i : ℤ) => a % i = a % b) rfl (mod_neg a b)

theorem zero_mod (b : ℤ) : 0 % b = 0 := congr_arg Int.ofNat (nat.zero_mod (nat_abs b))

theorem mod_zero (a : ℤ) : a % 0 = a :=
  int.cases_on a
    (fun (a : ℕ) => idRhs (Int.ofNat (a % 0) = Int.ofNat a) (congr_arg Int.ofNat (nat.mod_zero a)))
    fun (a : ℕ) =>
      idRhs (Int.negSucc (a % 0) = Int.negSucc a) (congr_arg Int.negSucc (nat.mod_zero a))

theorem mod_one (a : ℤ) : a % 1 = 0 := sorry

theorem mod_eq_of_lt {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a % b = a := sorry

theorem mod_nonneg (a : ℤ) {b : ℤ} : b ≠ 0 → 0 ≤ a % b := sorry

theorem mod_lt_of_pos (a : ℤ) {b : ℤ} (H : 0 < b) : a % b < b := sorry

theorem mod_lt (a : ℤ) {b : ℤ} (H : b ≠ 0) : a % b < abs b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a % b < abs b)) (Eq.symm (mod_abs a b))))
    (mod_lt_of_pos a (iff.mpr abs_pos H))

theorem mod_add_div_aux (m : ℕ) (n : ℕ) :
    ↑n - (↑m % ↑n + 1) - (↑n * (↑m / ↑n) + ↑n) = Int.negSucc m :=
  sorry

theorem mod_add_div (a : ℤ) (b : ℤ) : a % b + b * (a / b) = a := sorry

theorem div_add_mod (a : ℤ) (b : ℤ) : b * (a / b) + a % b = a :=
  Eq.trans (add_comm (b * (a / b)) (a % b)) (mod_add_div a b)

theorem mod_def (a : ℤ) (b : ℤ) : a % b = a - b * (a / b) := eq_sub_of_add_eq (mod_add_div a b)

@[simp] theorem add_mul_mod_self {a : ℤ} {b : ℤ} {c : ℤ} : (a + b * c) % c = a % c := sorry

@[simp] theorem add_mul_mod_self_left (a : ℤ) (b : ℤ) (c : ℤ) : (a + b * c) % b = a % b :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b * c) % b = a % b)) (mul_comm b c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a + c * b) % b = a % b)) add_mul_mod_self))
      (Eq.refl (a % b)))

@[simp] theorem add_mod_self {a : ℤ} {b : ℤ} : (a + b) % b = a % b :=
  eq.mp (Eq._oldrec (Eq.refl ((a + b * 1) % b = a % b)) (mul_one b)) (add_mul_mod_self_left a b 1)

@[simp] theorem add_mod_self_left {a : ℤ} {b : ℤ} : (a + b) % a = b % a :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b) % a = b % a)) (add_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((b + a) % a = b % a)) add_mod_self)) (Eq.refl (b % a)))

@[simp] theorem mod_add_mod (m : ℤ) (n : ℤ) (k : ℤ) : (m % n + k) % n = (m + k) % n := sorry

@[simp] theorem add_mod_mod (m : ℤ) (n : ℤ) (k : ℤ) : (m + n % k) % k = (m + n) % k :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((m + n % k) % k = (m + n) % k)) (add_comm m (n % k))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((n % k + m) % k = (m + n) % k)) (mod_add_mod n k m)))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((n + m) % k = (m + n) % k)) (add_comm n m)))
        (Eq.refl ((m + n) % k))))

theorem add_mod (a : ℤ) (b : ℤ) (n : ℤ) : (a + b) % n = (a % n + b % n) % n :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b) % n = (a % n + b % n) % n)) (add_mod_mod (a % n) b n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a + b) % n = (a % n + b) % n)) (mod_add_mod a n b)))
      (Eq.refl ((a + b) % n)))

theorem add_mod_eq_add_mod_right {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) (H : m % n = k % n) :
    (m + i) % n = (k + i) % n :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((m + i) % n = (k + i) % n)) (Eq.symm (mod_add_mod m n i))))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl ((m % n + i) % n = (k + i) % n)) (Eq.symm (mod_add_mod k n i))))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((m % n + i) % n = (k % n + i) % n)) H))
        (Eq.refl ((k % n + i) % n))))

theorem add_mod_eq_add_mod_left {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) (H : m % n = k % n) :
    (i + m) % n = (i + k) % n :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((i + m) % n = (i + k) % n)) (add_comm i m)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((m + i) % n = (i + k) % n)) (add_mod_eq_add_mod_right i H)))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((k + i) % n = (i + k) % n)) (add_comm k i)))
        (Eq.refl ((i + k) % n))))

theorem mod_add_cancel_right {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) :
    (m + i) % n = (k + i) % n ↔ m % n = k % n :=
  sorry

theorem mod_add_cancel_left {m : ℤ} {n : ℤ} {k : ℤ} {i : ℤ} :
    (i + m) % n = (i + k) % n ↔ m % n = k % n :=
  sorry

theorem mod_sub_cancel_right {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) :
    (m - i) % n = (k - i) % n ↔ m % n = k % n :=
  mod_add_cancel_right (-i)

theorem mod_eq_mod_iff_mod_sub_eq_zero {m : ℤ} {n : ℤ} {k : ℤ} : m % n = k % n ↔ (m - k) % n = 0 :=
  sorry

@[simp] theorem mul_mod_left (a : ℤ) (b : ℤ) : a * b % b = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b % b = 0)) (Eq.symm (zero_add (a * b)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((0 + a * b) % b = 0)) add_mul_mod_self))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 % b = 0)) (zero_mod b))) (Eq.refl 0)))

@[simp] theorem mul_mod_right (a : ℤ) (b : ℤ) : a * b % a = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b % a = 0)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a % a = 0)) (mul_mod_left b a))) (Eq.refl 0))

theorem mul_mod (a : ℤ) (b : ℤ) (n : ℤ) : a * b % n = a % n * (b % n) % n := sorry

@[simp] theorem neg_mod_two (i : ℤ) : -i % bit0 1 = i % bit0 1 := sorry

theorem mod_self {a : ℤ} : a % a = 0 :=
  eq.mp (Eq._oldrec (Eq.refl (1 * a % a = 0)) (one_mul a)) (mul_mod_left 1 a)

@[simp] theorem mod_mod_of_dvd (n : ℤ) {m : ℤ} {k : ℤ} (h : m ∣ k) : n % k % m = n % m := sorry

@[simp] theorem mod_mod (a : ℤ) (b : ℤ) : a % b % b = a % b := sorry

theorem sub_mod (a : ℤ) (b : ℤ) (n : ℤ) : (a - b) % n = (a % n - b % n) % n := sorry

/-! ### properties of `/` and `%` -/

@[simp] theorem mul_div_mul_of_pos {a : ℤ} (b : ℤ) (c : ℤ) (H : 0 < a) : a * b / (a * c) = b / c :=
  sorry

@[simp] theorem mul_div_mul_of_pos_left (a : ℤ) {b : ℤ} (c : ℤ) (H : 0 < b) :
    a * b / (c * b) = a / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b / (c * b) = a / c)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / (c * b) = a / c)) (mul_comm c b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / (b * c) = a / c)) (mul_div_mul_of_pos a c H)))
        (Eq.refl (a / c))))

@[simp] theorem mul_mod_mul_of_pos {a : ℤ} (b : ℤ) (c : ℤ) (H : 0 < a) :
    a * b % (a * c) = a * (b % c) :=
  sorry

theorem lt_div_add_one_mul_self (a : ℤ) {b : ℤ} (H : 0 < b) : a < (a / b + 1) * b := sorry

theorem abs_div_le_abs (a : ℤ) (b : ℤ) : abs (a / b) ≤ abs a := sorry

theorem div_le_self {a : ℤ} (b : ℤ) (Ha : 0 ≤ a) : a / b ≤ a :=
  eq.mp (Eq._oldrec (Eq.refl (a / b ≤ abs a)) (abs_of_nonneg Ha))
    (le_trans (le_abs_self (a / b)) (abs_div_le_abs a b))

theorem mul_div_cancel_of_mod_eq_zero {a : ℤ} {b : ℤ} (H : a % b = 0) : b * (a / b) = a :=
  eq.mp (Eq._oldrec (Eq.refl (0 + b * (a / b) = a)) (zero_add (b * (a / b))))
    (eq.mp (Eq._oldrec (Eq.refl (a % b + b * (a / b) = a)) H) (mod_add_div a b))

theorem div_mul_cancel_of_mod_eq_zero {a : ℤ} {b : ℤ} (H : a % b = 0) : a / b * b = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b * b = a)) (mul_comm (a / b) b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * (a / b) = a)) (mul_div_cancel_of_mod_eq_zero H)))
      (Eq.refl a))

theorem mod_two_eq_zero_or_one (n : ℤ) : n % bit0 1 = 0 ∨ n % bit0 1 = 1 := sorry

/-! ### dvd -/

theorem coe_nat_dvd {m : ℕ} {n : ℕ} : ↑m ∣ ↑n ↔ m ∣ n := sorry

theorem coe_nat_dvd_left {n : ℕ} {z : ℤ} : ↑n ∣ z ↔ n ∣ nat_abs z := sorry

theorem coe_nat_dvd_right {n : ℕ} {z : ℤ} : z ∣ ↑n ↔ nat_abs z ∣ n := sorry

theorem dvd_antisymm {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : 0 ≤ b) : a ∣ b → b ∣ a → a = b := sorry

theorem dvd_of_mod_eq_zero {a : ℤ} {b : ℤ} (H : b % a = 0) : a ∣ b :=
  Exists.intro (b / a) (Eq.symm (mul_div_cancel_of_mod_eq_zero H))

theorem mod_eq_zero_of_dvd {a : ℤ} {b : ℤ} : a ∣ b → b % a = 0 := sorry

theorem dvd_iff_mod_eq_zero (a : ℤ) (b : ℤ) : a ∣ b ↔ b % a = 0 :=
  { mp := mod_eq_zero_of_dvd, mpr := dvd_of_mod_eq_zero }

/-- If `a % b = c` then `b` divides `a - c`. -/
theorem dvd_sub_of_mod_eq {a : ℤ} {b : ℤ} {c : ℤ} (h : a % b = c) : b ∣ a - c := sorry

theorem nat_abs_dvd {a : ℤ} {b : ℤ} : ↑(nat_abs a) ∣ b ↔ a ∣ b := sorry

theorem dvd_nat_abs {a : ℤ} {b : ℤ} : a ∣ ↑(nat_abs b) ↔ a ∣ b := sorry

protected instance decidable_dvd : DecidableRel has_dvd.dvd :=
  fun (a n : ℤ) => decidable_of_decidable_of_iff (int.decidable_eq (n % a) 0) sorry

protected theorem div_mul_cancel {a : ℤ} {b : ℤ} (H : b ∣ a) : a / b * b = a :=
  div_mul_cancel_of_mod_eq_zero (mod_eq_zero_of_dvd H)

protected theorem mul_div_cancel' {a : ℤ} {b : ℤ} (H : a ∣ b) : a * (b / a) = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * (b / a) = b)) (mul_comm a (b / a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b / a * a = b)) (int.div_mul_cancel H))) (Eq.refl b))

protected theorem mul_div_assoc (a : ℤ) {b : ℤ} {c : ℤ} : c ∣ b → a * b / c = a * (b / c) := sorry

protected theorem mul_div_assoc' (b : ℤ) {a : ℤ} {c : ℤ} (h : c ∣ a) : a * b / c = a / c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b / c = a / c * b)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / c = a / c * b)) (int.mul_div_assoc b h)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b * (a / c) = a / c * b)) (mul_comm b (a / c))))
        (Eq.refl (a / c * b))))

theorem div_dvd_div {a : ℤ} {b : ℤ} {c : ℤ} (H1 : a ∣ b) (H2 : b ∣ c) : b / a ∣ c / a := sorry

protected theorem eq_mul_of_div_eq_right {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ∣ a) (H2 : a / b = c) :
    a = b * c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a = b * c)) (Eq.symm H2)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * (a / b))) (int.mul_div_cancel' H1))) (Eq.refl a))

protected theorem div_eq_of_eq_mul_right {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ≠ 0) (H2 : a = b * c) :
    a / b = c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b = c)) H2))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * c / b = c)) (int.mul_div_cancel_left c H1))) (Eq.refl c))

protected theorem eq_div_of_mul_eq_right {a : ℤ} {b : ℤ} {c : ℤ} (H1 : a ≠ 0) (H2 : a * b = c) :
    b = c / a :=
  Eq.symm (int.div_eq_of_eq_mul_right H1 (Eq.symm H2))

protected theorem div_eq_iff_eq_mul_right {a : ℤ} {b : ℤ} {c : ℤ} (H : b ≠ 0) (H' : b ∣ a) :
    a / b = c ↔ a = b * c :=
  { mp := int.eq_mul_of_div_eq_right H', mpr := int.div_eq_of_eq_mul_right H }

protected theorem div_eq_iff_eq_mul_left {a : ℤ} {b : ℤ} {c : ℤ} (H : b ≠ 0) (H' : b ∣ a) :
    a / b = c ↔ a = c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b = c ↔ a = c * b)) (mul_comm c b)))
    (int.div_eq_iff_eq_mul_right H H')

protected theorem eq_mul_of_div_eq_left {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ∣ a) (H2 : a / b = c) :
    a = c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a = c * b)) (mul_comm c b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * c)) (int.eq_mul_of_div_eq_right H1 H2)))
      (Eq.refl (b * c)))

protected theorem div_eq_of_eq_mul_left {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ≠ 0) (H2 : a = c * b) :
    a / b = c :=
  int.div_eq_of_eq_mul_right H1
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * c)) (mul_comm b c)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a = c * b)) H2)) (Eq.refl (c * b))))

theorem neg_div_of_dvd {a : ℤ} {b : ℤ} (H : b ∣ a) : -a / b = -(a / b) := sorry

theorem sub_div_of_dvd {a : ℤ} {b : ℤ} {c : ℤ} (hcb : c ∣ b) : (a - b) / c = a / c - b / c := sorry

theorem sub_div_of_dvd_sub {a : ℤ} {b : ℤ} {c : ℤ} (hcab : c ∣ a - b) :
    (a - b) / c = a / c - b / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a - b) / c = a / c - b / c)) (propext eq_sub_iff_add_eq)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl ((a - b) / c + b / c = a / c))
          (Eq.symm (int.add_div_of_dvd_left hcab))))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((a - b + b) / c = a / c)) (sub_add_cancel a b)))
        (Eq.refl (a / c))))

theorem div_sign (a : ℤ) (b : ℤ) : a / sign b = a * sign b := sorry

@[simp] theorem sign_mul (a : ℤ) (b : ℤ) : sign (a * b) = sign a * sign b := sorry

protected theorem sign_eq_div_abs (a : ℤ) : sign a = a / abs a := sorry

theorem mul_sign (i : ℤ) : i * sign i = ↑(nat_abs i) := sorry

theorem le_of_dvd {a : ℤ} {b : ℤ} (bpos : 0 < b) (H : a ∣ b) : a ≤ b := sorry

theorem eq_one_of_dvd_one {a : ℤ} (H : 0 ≤ a) (H' : a ∣ 1) : a = 1 := sorry

theorem eq_one_of_mul_eq_one_right {a : ℤ} {b : ℤ} (H : 0 ≤ a) (H' : a * b = 1) : a = 1 :=
  eq_one_of_dvd_one H (Exists.intro b (Eq.symm H'))

theorem eq_one_of_mul_eq_one_left {a : ℤ} {b : ℤ} (H : 0 ≤ b) (H' : a * b = 1) : b = 1 :=
  eq_one_of_mul_eq_one_right H
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a = 1)) (mul_comm b a)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a * b = 1)) H')) (Eq.refl 1)))

theorem of_nat_dvd_of_dvd_nat_abs {a : ℕ} {z : ℤ} (haz : a ∣ nat_abs z) : ↑a ∣ z := sorry

theorem dvd_nat_abs_of_of_nat_dvd {a : ℕ} {z : ℤ} (haz : ↑a ∣ z) : a ∣ nat_abs z := sorry

theorem pow_dvd_of_le_of_pow_dvd {p : ℕ} {m : ℕ} {n : ℕ} {k : ℤ} (hmn : m ≤ n)
    (hdiv : ↑(p ^ n) ∣ k) : ↑(p ^ m) ∣ k :=
  sorry

theorem dvd_of_pow_dvd {p : ℕ} {k : ℕ} {m : ℤ} (hk : 1 ≤ k) (hpk : ↑(p ^ k) ∣ m) : ↑p ∣ m :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑p ∣ m)) (Eq.symm (pow_one p))))
    (pow_dvd_of_le_of_pow_dvd hk hpk)

/-- If `n > 0` then `m` is not divisible by `n` iff it is between `n * k` and `n * (k + 1)`
  for some `k`. -/
theorem exists_lt_and_lt_iff_not_dvd (m : ℤ) {n : ℤ} (hn : 0 < n) :
    (∃ (k : ℤ), n * k < m ∧ m < n * (k + 1)) ↔ ¬n ∣ m :=
  sorry

/-! ### `/` and ordering -/

protected theorem div_mul_le (a : ℤ) {b : ℤ} (H : b ≠ 0) : a / b * b ≤ a :=
  le_of_sub_nonneg
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ a - a / b * b)) (mul_comm (a / b) b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ a - b * (a / b))) (Eq.symm (mod_def a b))))
        (mod_nonneg a H)))

protected theorem div_le_of_le_mul {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H' : a ≤ b * c) :
    a / c ≤ b :=
  le_of_mul_le_mul_right (le_trans (int.div_mul_le a (ne_of_gt H)) H') H

protected theorem mul_lt_of_lt_div {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H3 : a < b / c) :
    a * c < b :=
  lt_of_not_ge (mt (int.div_le_of_le_mul H) (not_le_of_gt H3))

protected theorem mul_le_of_le_div {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 < c) (H2 : a ≤ b / c) :
    a * c ≤ b :=
  le_trans (mul_le_mul_of_nonneg_right H2 (le_of_lt H1)) (int.div_mul_le b (ne_of_gt H1))

protected theorem le_div_of_mul_le {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 < c) (H2 : a * c ≤ b) :
    a ≤ b / c :=
  le_of_lt_add_one
    (lt_of_mul_lt_mul_right (lt_of_le_of_lt H2 (lt_div_add_one_mul_self b H1)) (le_of_lt H1))

protected theorem le_div_iff_mul_le {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) : a ≤ b / c ↔ a * c ≤ b :=
  { mp := int.mul_le_of_le_div H, mpr := int.le_div_of_mul_le H }

protected theorem div_le_div {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H' : a ≤ b) : a / c ≤ b / c :=
  int.le_div_of_mul_le H (le_trans (int.div_mul_le a (ne_of_gt H)) H')

protected theorem div_lt_of_lt_mul {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H' : a < b * c) :
    a / c < b :=
  lt_of_not_ge (mt (int.mul_le_of_le_div H) (not_le_of_gt H'))

protected theorem lt_mul_of_div_lt {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 < c) (H2 : a / c < b) :
    a < b * c :=
  lt_of_not_ge (mt (int.le_div_of_mul_le H1) (not_le_of_gt H2))

protected theorem div_lt_iff_lt_mul {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) : a / c < b ↔ a < b * c :=
  { mp := int.lt_mul_of_div_lt H, mpr := int.div_lt_of_lt_mul H }

protected theorem le_mul_of_div_le {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 ≤ b) (H2 : b ∣ a)
    (H3 : a / b ≤ c) : a ≤ c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ c * b)) (Eq.symm (int.div_mul_cancel H2))))
    (mul_le_mul_of_nonneg_right H3 H1)

protected theorem lt_div_of_mul_lt {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 ≤ b) (H2 : b ∣ c)
    (H3 : a * b < c) : a < c / b :=
  lt_of_not_ge (mt (int.le_mul_of_div_le H1 H2) (not_le_of_gt H3))

protected theorem lt_div_iff_mul_lt {a : ℤ} {b : ℤ} (c : ℤ) (H : 0 < c) (H' : c ∣ b) :
    a < b / c ↔ a * c < b :=
  { mp := int.mul_lt_of_lt_div H, mpr := int.lt_div_of_mul_lt (le_of_lt H) H' }

theorem div_pos_of_pos_of_dvd {a : ℤ} {b : ℤ} (H1 : 0 < a) (H2 : 0 ≤ b) (H3 : b ∣ a) : 0 < a / b :=
  int.lt_div_of_mul_lt H2 H3 (eq.mpr (id (Eq._oldrec (Eq.refl (0 * b < a)) (zero_mul b))) H1)

theorem div_eq_div_of_mul_eq_mul {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (H2 : d ∣ c) (H3 : b ≠ 0)
    (H4 : d ≠ 0) (H5 : a * d = b * c) : a / b = c / d :=
  int.div_eq_of_eq_mul_right H3
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * (c / d))) (Eq.symm (int.mul_div_assoc b H2))))
      (Eq.symm (int.div_eq_of_eq_mul_left H4 (Eq.symm H5))))

theorem eq_mul_div_of_mul_eq_mul_of_dvd_left {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hb : b ≠ 0)
    (hbc : b ∣ c) (h : b * a = c * d) : a = c / b * d :=
  sorry

/-- If an integer with larger absolute value divides an integer, it is
zero. -/
theorem eq_zero_of_dvd_of_nat_abs_lt_nat_abs {a : ℤ} {b : ℤ} (w : a ∣ b)
    (h : nat_abs b < nat_abs a) : b = 0 :=
  sorry

theorem eq_zero_of_dvd_of_nonneg_of_lt {a : ℤ} {b : ℤ} (w₁ : 0 ≤ a) (w₂ : a < b) (h : b ∣ a) :
    a = 0 :=
  eq_zero_of_dvd_of_nat_abs_lt_nat_abs h (nat_abs_lt_nat_abs_of_nonneg_of_lt w₁ w₂)

/-- If two integers are congruent to a sufficiently large modulus,
they are equal. -/
theorem eq_of_mod_eq_of_nat_abs_sub_lt_nat_abs {a : ℤ} {b : ℤ} {c : ℤ} (h1 : a % b = c)
    (h2 : nat_abs (a - c) < nat_abs b) : a = c :=
  eq_of_sub_eq_zero (eq_zero_of_dvd_of_nat_abs_lt_nat_abs (dvd_sub_of_mod_eq h1) h2)

theorem of_nat_add_neg_succ_of_nat_of_lt {m : ℕ} {n : ℕ} (h : m < Nat.succ n) :
    Int.ofNat m + Int.negSucc n = Int.negSucc (n - m) :=
  sorry

theorem of_nat_add_neg_succ_of_nat_of_ge {m : ℕ} {n : ℕ} (h : Nat.succ n ≤ m) :
    Int.ofNat m + Int.negSucc n = Int.ofNat (m - Nat.succ n) :=
  sorry

@[simp] theorem neg_add_neg (m : ℕ) (n : ℕ) :
    Int.negSucc m + Int.negSucc n = Int.negSucc (Nat.succ (m + n)) :=
  rfl

/-! ### to_nat -/

theorem to_nat_eq_max (a : ℤ) : ↑(to_nat a) = max a 0 :=
  int.cases_on a (fun (a : ℕ) => idRhs (↑a = max (↑a) 0) (Eq.symm (max_eq_left (coe_zero_le a))))
    fun (a : ℕ) =>
      idRhs (0 = max (Int.negSucc a) 0) (Eq.symm (max_eq_right (le_of_lt (neg_succ_lt_zero a))))

@[simp] theorem to_nat_zero : to_nat 0 = 0 := rfl

@[simp] theorem to_nat_one : to_nat 1 = 1 := rfl

@[simp] theorem to_nat_of_nonneg {a : ℤ} (h : 0 ≤ a) : ↑(to_nat a) = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(to_nat a) = a)) (to_nat_eq_max a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (max a 0 = a)) (max_eq_left h))) (Eq.refl a))

@[simp] theorem to_nat_sub_of_le (a : ℤ) (b : ℤ) (h : b ≤ a) : ↑(to_nat (a + -b)) = a + -b :=
  to_nat_of_nonneg (sub_nonneg_of_le h)

@[simp] theorem to_nat_coe_nat (n : ℕ) : to_nat ↑n = n := rfl

@[simp] theorem to_nat_coe_nat_add_one {n : ℕ} : to_nat (↑n + 1) = n + 1 := rfl

theorem le_to_nat (a : ℤ) : a ≤ ↑(to_nat a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ ↑(to_nat a))) (to_nat_eq_max a))) (le_max_left a 0)

@[simp] theorem to_nat_le {a : ℤ} {n : ℕ} : to_nat a ≤ n ↔ a ≤ ↑n :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (to_nat a ≤ n ↔ a ≤ ↑n))
        (propext (iff.symm (coe_nat_le_coe_nat_iff (to_nat a) n)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(to_nat a) ≤ ↑n ↔ a ≤ ↑n)) (to_nat_eq_max a)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (max a 0 ≤ ↑n ↔ a ≤ ↑n)) (propext max_le_iff)))
        (and_iff_left (coe_zero_le n))))

@[simp] theorem lt_to_nat {n : ℕ} {a : ℤ} : n < to_nat a ↔ ↑n < a :=
  iff.mp le_iff_le_iff_lt_iff_lt to_nat_le

theorem to_nat_le_to_nat {a : ℤ} {b : ℤ} (h : a ≤ b) : to_nat a ≤ to_nat b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (to_nat a ≤ to_nat b)) (propext to_nat_le)))
    (le_trans h (le_to_nat b))

theorem to_nat_lt_to_nat {a : ℤ} {b : ℤ} (hb : 0 < b) : to_nat a < to_nat b ↔ a < b := sorry

theorem lt_of_to_nat_lt {a : ℤ} {b : ℤ} (h : to_nat a < to_nat b) : a < b :=
  iff.mp (to_nat_lt_to_nat (iff.mp lt_to_nat (lt_of_le_of_lt (nat.zero_le (to_nat a)) h))) h

theorem to_nat_add {a : ℤ} {b : ℤ} (ha : 0 ≤ a) (hb : 0 ≤ b) :
    to_nat (a + b) = to_nat a + to_nat b :=
  sorry

theorem to_nat_add_one {a : ℤ} (h : 0 ≤ a) : to_nat (a + 1) = to_nat a + 1 :=
  to_nat_add h zero_le_one

/-- If `n : ℕ`, then `int.to_nat' n = some n`, if `n : ℤ` is negative, then `int.to_nat' n = none`.
-/
def to_nat' : ℤ → Option ℕ := sorry

theorem mem_to_nat' (a : ℤ) (n : ℕ) : n ∈ to_nat' a ↔ a = ↑n := sorry

theorem to_nat_zero_of_neg {z : ℤ} : z < 0 → to_nat z = 0 := sorry

/-! ### units -/

@[simp] theorem units_nat_abs (u : units ℤ) : nat_abs ↑u = 1 := sorry

theorem units_eq_one_or (u : units ℤ) : u = 1 ∨ u = -1 := sorry

theorem units_inv_eq_self (u : units ℤ) : u⁻¹ = u :=
  or.elim (units_eq_one_or u) (fun (h : u = 1) => Eq.symm h ▸ rfl)
    fun (h : u = -1) => Eq.symm h ▸ rfl

@[simp] theorem units_mul_self (u : units ℤ) : u * u = 1 :=
  or.elim (units_eq_one_or u) (fun (h : u = 1) => Eq.symm h ▸ rfl)
    fun (h : u = -1) => Eq.symm h ▸ rfl

-- `units.coe_mul` is a "wrong turn" for the simplifier, this undoes it and simplifies further

@[simp] theorem units_coe_mul_self (u : units ℤ) : ↑u * ↑u = 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑u * ↑u = 1)) (Eq.symm (units.coe_mul u u))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(u * u) = 1)) (units_mul_self u)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (↑1 = 1)) units.coe_one)) (Eq.refl 1)))

/-! ### bitwise ops -/

@[simp] theorem bodd_zero : bodd 0 = false := rfl

@[simp] theorem bodd_one : bodd 1 = tt := rfl

theorem bodd_two : bodd (bit0 1) = false := rfl

@[simp] theorem bodd_coe (n : ℕ) : bodd ↑n = nat.bodd n := rfl

@[simp] theorem bodd_sub_nat_nat (m : ℕ) (n : ℕ) :
    bodd (sub_nat_nat m n) = bxor (nat.bodd m) (nat.bodd n) :=
  sorry

@[simp] theorem bodd_neg_of_nat (n : ℕ) : bodd (neg_of_nat n) = nat.bodd n := sorry

@[simp] theorem bodd_neg (n : ℤ) : bodd (-n) = bodd n := sorry

@[simp] theorem bodd_add (m : ℤ) (n : ℤ) : bodd (m + n) = bxor (bodd m) (bodd n) := sorry

@[simp] theorem bodd_mul (m : ℤ) (n : ℤ) : bodd (m * n) = bodd m && bodd n := sorry

theorem bodd_add_div2 (n : ℤ) : cond (bodd n) 1 0 + bit0 1 * div2 n = n := sorry

theorem div2_val (n : ℤ) : div2 n = n / bit0 1 :=
  int.cases_on n
    (fun (n : ℕ) =>
      idRhs (Int.ofNat (nat.div2 n) = Int.ofNat (n / bit0 1))
        (congr_arg Int.ofNat (nat.div2_val n)))
    fun (n : ℕ) =>
      idRhs (Int.negSucc (nat.div2 n) = Int.negSucc (n / bit0 1))
        (congr_arg Int.negSucc (nat.div2_val n))

theorem bit0_val (n : ℤ) : bit0 n = bit0 1 * n := Eq.symm (two_mul n)

theorem bit1_val (n : ℤ) : bit1 n = bit0 1 * n + 1 :=
  congr_arg (fun (_x : ℤ) => _x + 1) (bit0_val n)

theorem bit_val (b : Bool) (n : ℤ) : bit b n = bit0 1 * n + cond b 1 0 :=
  bool.cases_on b (Eq.trans (bit0_val n) (Eq.symm (add_zero (bit0 1 * n)))) (bit1_val n)

theorem bit_decomp (n : ℤ) : bit (bodd n) (div2 n) = n :=
  Eq.trans (bit_val (bodd n) (div2 n))
    (Eq.trans (add_comm (bit0 1 * div2 n) (cond (bodd n) 1 0)) (bodd_add_div2 n))

/-- Defines a function from `ℤ` conditionally, if it is defined for odd and even integers separately
  using `bit`. -/
def bit_cases_on {C : ℤ → Sort u} (n : ℤ) (h : (b : Bool) → (n : ℤ) → C (bit b n)) : C n :=
  eq.mpr sorry (h (bodd n) (div2 n))

@[simp] theorem bit_zero : bit false 0 = 0 := rfl

@[simp] theorem bit_coe_nat (b : Bool) (n : ℕ) : bit b ↑n = ↑(nat.bit b n) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (bit b ↑n = ↑(nat.bit b n))) (bit_val b ↑n)))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (bit0 1 * ↑n + cond b 1 0 = ↑(nat.bit b n))) (nat.bit_val b n)))
      (bool.cases_on b (Eq.refl (bit0 1 * ↑n + cond false 1 0))
        (Eq.refl (bit0 1 * ↑n + cond tt 1 0))))

@[simp] theorem bit_neg_succ (b : Bool) (n : ℕ) :
    bit b (Int.negSucc n) = Int.negSucc (nat.bit (!b) n) :=
  sorry

@[simp] theorem bodd_bit (b : Bool) (n : ℤ) : bodd (bit b n) = b := sorry

@[simp] theorem bodd_bit0 (n : ℤ) : bodd (bit0 n) = false := bodd_bit false n

@[simp] theorem bodd_bit1 (n : ℤ) : bodd (bit1 n) = tt := bodd_bit tt n

@[simp] theorem div2_bit (b : Bool) (n : ℤ) : div2 (bit b n) = n := sorry

theorem bit0_ne_bit1 (m : ℤ) (n : ℤ) : bit0 m ≠ bit1 n := sorry

theorem bit1_ne_bit0 (m : ℤ) (n : ℤ) : bit1 m ≠ bit0 n := ne.symm (bit0_ne_bit1 n m)

theorem bit1_ne_zero (m : ℤ) : bit1 m ≠ 0 := sorry

@[simp] theorem test_bit_zero (b : Bool) (n : ℤ) : test_bit (bit b n) 0 = b := sorry

@[simp] theorem test_bit_succ (m : ℕ) (b : Bool) (n : ℤ) :
    test_bit (bit b n) (Nat.succ m) = test_bit n m :=
  sorry

theorem bitwise_or : bitwise bor = lor := sorry

theorem bitwise_and : bitwise band = land := sorry

theorem bitwise_diff : (bitwise fun (a b : Bool) => a && !b) = ldiff := sorry

theorem bitwise_xor : bitwise bxor = lxor := sorry

@[simp] theorem bitwise_bit (f : Bool → Bool → Bool) (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) :
    bitwise f (bit a m) (bit b n) = bit (f a b) (bitwise f m n) :=
  sorry

@[simp] theorem lor_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) :
    lor (bit a m) (bit b n) = bit (a || b) (lor m n) :=
  sorry

@[simp] theorem land_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) :
    land (bit a m) (bit b n) = bit (a && b) (land m n) :=
  sorry

@[simp] theorem ldiff_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) :
    ldiff (bit a m) (bit b n) = bit (a && !b) (ldiff m n) :=
  sorry

@[simp] theorem lxor_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) :
    lxor (bit a m) (bit b n) = bit (bxor a b) (lxor m n) :=
  sorry

@[simp] theorem lnot_bit (b : Bool) (n : ℤ) : lnot (bit b n) = bit (!b) (lnot n) := sorry

@[simp] theorem test_bit_bitwise (f : Bool → Bool → Bool) (m : ℤ) (n : ℤ) (k : ℕ) :
    test_bit (bitwise f m n) k = f (test_bit m k) (test_bit n k) :=
  sorry

@[simp] theorem test_bit_lor (m : ℤ) (n : ℤ) (k : ℕ) :
    test_bit (lor m n) k = test_bit m k || test_bit n k :=
  sorry

@[simp] theorem test_bit_land (m : ℤ) (n : ℤ) (k : ℕ) :
    test_bit (land m n) k = test_bit m k && test_bit n k :=
  sorry

@[simp] theorem test_bit_ldiff (m : ℤ) (n : ℤ) (k : ℕ) :
    test_bit (ldiff m n) k = test_bit m k && !test_bit n k :=
  sorry

@[simp] theorem test_bit_lxor (m : ℤ) (n : ℤ) (k : ℕ) :
    test_bit (lxor m n) k = bxor (test_bit m k) (test_bit n k) :=
  sorry

@[simp] theorem test_bit_lnot (n : ℤ) (k : ℕ) : test_bit (lnot n) k = !test_bit n k := sorry

theorem shiftl_add (m : ℤ) (n : ℕ) (k : ℤ) : shiftl m (↑n + k) = shiftl (shiftl m ↑n) k := sorry

theorem shiftl_sub (m : ℤ) (n : ℕ) (k : ℤ) : shiftl m (↑n - k) = shiftr (shiftl m ↑n) k :=
  shiftl_add m n (-k)

@[simp] theorem shiftl_neg (m : ℤ) (n : ℤ) : shiftl m (-n) = shiftr m n := rfl

@[simp] theorem shiftr_neg (m : ℤ) (n : ℤ) : shiftr m (-n) = shiftl m n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (shiftr m (-n) = shiftl m n)) (Eq.symm (shiftl_neg m (-n)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (shiftl m ( --n) = shiftl m n)) (neg_neg n)))
      (Eq.refl (shiftl m n)))

@[simp] theorem shiftl_coe_nat (m : ℕ) (n : ℕ) : shiftl ↑m ↑n = ↑(nat.shiftl m n) := rfl

@[simp] theorem shiftr_coe_nat (m : ℕ) (n : ℕ) : shiftr ↑m ↑n = ↑(nat.shiftr m n) :=
  nat.cases_on n (Eq.refl (shiftr ↑m ↑0)) fun (n : ℕ) => Eq.refl (shiftr ↑m ↑(Nat.succ n))

@[simp] theorem shiftl_neg_succ (m : ℕ) (n : ℕ) :
    shiftl (Int.negSucc m) ↑n = Int.negSucc (nat.shiftl' tt m n) :=
  rfl

@[simp] theorem shiftr_neg_succ (m : ℕ) (n : ℕ) :
    shiftr (Int.negSucc m) ↑n = Int.negSucc (nat.shiftr m n) :=
  nat.cases_on n (Eq.refl (shiftr (Int.negSucc m) ↑0))
    fun (n : ℕ) => Eq.refl (shiftr (Int.negSucc m) ↑(Nat.succ n))

theorem shiftr_add (m : ℤ) (n : ℕ) (k : ℕ) : shiftr m (↑n + ↑k) = shiftr (shiftr m ↑n) ↑k := sorry

theorem shiftl_eq_mul_pow (m : ℤ) (n : ℕ) : shiftl m ↑n = m * ↑(bit0 1 ^ n) := sorry

theorem shiftr_eq_div_pow (m : ℤ) (n : ℕ) : shiftr m ↑n = m / ↑(bit0 1 ^ n) := sorry

theorem one_shiftl (n : ℕ) : shiftl 1 ↑n = ↑(bit0 1 ^ n) := congr_arg coe (nat.one_shiftl n)

@[simp] theorem zero_shiftl (n : ℤ) : shiftl 0 n = 0 :=
  int.cases_on n (fun (n : ℕ) => idRhs (↑(nat.shiftl 0 n) = ↑0) (congr_arg coe (nat.zero_shiftl n)))
    fun (n : ℕ) =>
      idRhs (↑(nat.shiftr 0 (Nat.succ n)) = ↑0) (congr_arg coe (nat.zero_shiftr (Nat.succ n)))

@[simp] theorem zero_shiftr (n : ℤ) : shiftr 0 n = 0 := zero_shiftl (-n)

/-! ### Least upper bound property for integers -/

theorem exists_least_of_bdd {P : ℤ → Prop} (Hbdd : ∃ (b : ℤ), ∀ (z : ℤ), P z → b ≤ z)
    (Hinh : ∃ (z : ℤ), P z) : ∃ (lb : ℤ), P lb ∧ ∀ (z : ℤ), P z → lb ≤ z :=
  sorry

theorem exists_greatest_of_bdd {P : ℤ → Prop} (Hbdd : ∃ (b : ℤ), ∀ (z : ℤ), P z → z ≤ b)
    (Hinh : ∃ (z : ℤ), P z) : ∃ (ub : ℤ), P ub ∧ ∀ (z : ℤ), P z → z ≤ ub :=
  sorry

end Mathlib


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebra.homology.homotopy
import algebra.category.Module.abelian
import algebra.category.Module.subobject
import category_theory.limits.concrete_category

/-!
# Complexes of modules

We provide some additional API to work with homological complexes in `Module R`.
-/

universes v u

open_locale classical
noncomputable theory

open category_theory category_theory.limits homological_complex

variables {R : Type v} [ring R]
variables {ι : Type*} {c : complex_shape ι} {C D : homological_complex (Module.{u} R) c}

namespace Module

/--
To prove that two maps out of a homology group are equal,
it suffices to check they are equal on the images of cycles.
-/
lemma homology_ext {L M N K : Module R} {f : L ⟶ M} {g : M ⟶ N} (w : f ≫ g = 0)
  {h k : homology f g w ⟶ K}
  (w : ∀ (x : linear_map.ker g),
    h (cokernel.π (image_to_kernel _ _ w) (to_kernel_subobject x)) =
      k (cokernel.π (image_to_kernel _ _ w) (to_kernel_subobject x))) : h = k :=
begin
  refine cokernel_funext (λ n, _),
  -- Gosh it would be nice if `equiv_rw` could directly use an isomorphism, or an enriched `≃`.
  equiv_rw (kernel_subobject_iso g ≪≫ Module.kernel_iso_ker g).to_linear_equiv.to_equiv at n,
  convert w n; simp [to_kernel_subobject],
end

/-- Bundle an element `C.X i` such that `C.d_from i x = 0` as a term of `C.cycles i`. -/
abbreviation to_cycles {C : homological_complex (Module.{u} R) c}
  {i : ι} (x : linear_map.ker (C.d_from i)) : C.cycles i :=
to_kernel_subobject x

@[ext]
lemma cycles_ext {C : homological_complex (Module.{u} R) c} {i : ι}
  {x y : C.cycles i} (w : (C.cycles i).arrow x = (C.cycles i).arrow y) : x = y :=
begin
  apply_fun (C.cycles i).arrow using (Module.mono_iff_injective _).mp (cycles C i).arrow_mono,
  exact w,
end

local attribute [instance] concrete_category.has_coe_to_sort

@[simp] lemma cycles_map_to_cycles (f : C ⟶ D) {i : ι} (x : linear_map.ker (C.d_from i)) :
  (cycles_map f i) (to_cycles x) = to_cycles ⟨f.f i x.1, by simp [x.2]⟩ :=
by { ext, simp, }

/-- Build a term of `C.homology i` from an element `C.X i` such that `C.d_from i x = 0`. -/
abbreviation to_homology
  {C : homological_complex (Module.{u} R) c} {i : ι} (x : linear_map.ker (C.d_from i)) :
  C.homology i :=
homology.π (C.d_to i) (C.d_from i) _ (to_cycles x)

@[ext]
lemma homology_ext' {M : Module R} (i : ι) {h k : C.homology i ⟶ M}
  (w : ∀ (x : linear_map.ker (C.d_from i)), h (to_homology x) = k (to_homology x)) :
  h = k :=
homology_ext _ w

/-- We give an alternative proof of `homology_map_eq_of_homotopy`,
specialized to the setting of `V = Module R`,
to demonstrate the use of extensionality lemmas for homology in `Module R`. -/
example (f g : C ⟶ D) (h : homotopy f g) (i : ι) :
  (homology_functor (Module.{u} R) c i).map f = (homology_functor (Module.{u} R) c i).map g :=
begin
  -- To check that two morphisms out of a homology group agree, it suffices to check on cycles:
  ext,
  simp only [homology_functor_map, homology.π_map_apply],
  -- To check that two elements are equal mod boundaries, it suffices to exhibit a boundary:
  ext1,
  swap, exact (to_prev i h.hom) x.1,
  -- Moreover, to check that two cycles are equal, it suffices to check their underlying elements:
  ext1,
  simp only [map_add, image_to_kernel_arrow_apply, homological_complex.hom.sq_from_left,
      Module.to_kernel_subobject_arrow, category_theory.limits.kernel_subobject_map_arrow_apply,
      d_next_eq_d_from_from_next, function.comp_app, zero_add, Module.coe_comp,
      linear_map.add_apply, map_zero, subtype.val_eq_coe,
      category_theory.limits.image_subobject_arrow_comp_apply, linear_map.map_coe_ker,
      prev_d_eq_to_prev_d_to, h.comm i, x.2],
  abel
end

end Module


import solutions.world2_multiplication

import mynat.le
/- Here's what you get from the import:

1) The following data:
  * a binary relation called mynat.le, and notation a ≤ b for this relation.

  The definition is: a ≤ b ↔ ∃ c : mynat, b = a + c

2) The following axiom:

  * `le_def (a b : mynat) : a ≤ b ↔ ∃ (c : mynat), b = a + c`

You can rewrite `le_def`.

If a goal is of the form `∃ c, ...` then to make progress you can use the `use` tactic.
For example `use 7` will replace all c's in the goal with 7's.
-/

namespace mynat

-- example
theorem le_refl (a : mynat) : a ≤ a :=
begin
  rw le_def,
  use 0,
  rw add_zero,
end

example : one ≤ one := le_refl one

-- ignore this; it's making the "refl" tactic work with goals of the form a ≤ a
attribute [_refl_lemma] le_refl

theorem le_succ {a b : mynat} (h : a ≤ b) : a ≤ (succ b) :=
begin
  sorry
end

lemma zero_le (a : mynat) : 0 ≤ a :=
begin
  sorry
end

lemma le_zero {a : mynat} : a ≤ 0 → a = 0 :=
begin
  sorry
end

theorem le_trans ⦃a b c : mynat⦄ (hab : a ≤ b) (hbc : b ≤ c) : a ≤ c :=
begin
  sorry
end

instance : preorder mynat := by structure_helper

-- ignore this, it's the definition.
theorem lt_iff_le_not_le {a b : mynat} : a < b ↔ a ≤ b ∧ ¬ b ≤ a := iff.rfl

theorem le_antisymm : ∀ {{a b : mynat}}, a ≤ b → b ≤ a → a = b :=
begin
  sorry
end

instance : partial_order mynat := by structure_helper

theorem lt_iff_le_and_ne ⦃a b : mynat⦄ : a < b ↔ a ≤ b ∧ a ≠ b :=
begin
  sorry
end

lemma succ_le_succ {a b : mynat} (h : a ≤ b) : succ a ≤ succ b :=
begin
  sorry
end

theorem le_total (a b : mynat) : a ≤ b ∨ b ≤ a :=
begin
  sorry
end

instance : linear_order mynat := by structure_helper

theorem add_le_add_right (a b : mynat) : a ≤ b → ∀ t, (a + t) ≤ (b + t) :=
begin
  sorry
end

theorem le_succ_self (a : mynat) : a ≤ succ a :=
begin
  sorry
end

theorem le_of_succ_le_succ {a b : mynat} : succ a ≤ succ b → a ≤ b :=
begin
  sorry
end

theorem not_succ_le_self {{d : mynat}} (h : succ d ≤ d) : false :=
begin
  sorry
end

theorem add_le_add_left : ∀ (a b : mynat), a ≤ b → ∀ (c : mynat), c + a ≤ c + b :=
begin
  sorry
end

def succ_le_succ_iff (a b : mynat) : succ a ≤ succ b ↔ a ≤ b :=
begin
  sorry
end

def succ_lt_succ_iff (a b : mynat) : succ a < succ b ↔ a < b :=
begin
  sorry
end

theorem lt_of_add_lt_add_left : ∀ {{a b c : mynat}}, a + b < a + c → b < c :=
begin
  sorry
end

theorem le_iff_exists_add : ∀ (a b : mynat), a ≤ b ↔ ∃ (c : mynat), b = a + c :=
begin
  sorry
end

theorem zero_ne_one : (0 : mynat) ≠ 1 :=
begin
  sorry
end

instance : ordered_comm_monoid mynat := by structure_helper

theorem le_of_add_le_add_left ⦃ a b c : mynat⦄ : a + b ≤ a + c → b ≤ c :=
begin
  sorry
end

instance : ordered_cancel_comm_monoid mynat := by structure_helper

theorem mul_le_mul_of_nonneg_left ⦃a b c : mynat⦄ : a ≤ b → 0 ≤ c → c * a ≤ c * b :=
begin
  sorry
end

theorem mul_le_mul_of_nonneg_right ⦃a b c : mynat⦄ : a ≤ b → 0 ≤ c → a * c ≤ b * c :=
begin
  sorry
end

theorem ne_zero_of_pos ⦃a : mynat⦄ : 0 < a → a ≠ 0 :=
begin
  sorry
end

theorem mul_lt_mul_of_pos_left ⦃a b c : mynat⦄ : a < b → 0 < c → c * a < c * b :=
begin
  sorry
end

theorem mul_lt_mul_of_pos_right ⦃a b c : mynat⦄ : a < b → 0 < c → a * c < b * c :=
begin
  sorry
end

instance : ordered_semiring mynat := by structure_helper

lemma lt_irrefl (a : mynat) : ¬ (a < a) :=
begin
  sorry
end

end mynat


import ..lectures.love03_forward_proofs_demo

/-! # LoVe Exercise 4: Functional Programming -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1: Reverse of a List

We define a new accumulator-based version of `reverse`. The first argument,
`as`, serves as the accumulator. This definition is __tail-recursive__, meaning
that compilers and interpreters can easily optimize the recursion away,
resulting in more efficient code. -/

def accurev {α : Type} : list α → list α → list α
| as []        := as
| as (x :: xs) := accurev (x :: as) xs

/-! 1.1. Our intention is that `accurev [] xs` should be equal to `reverse xs`.
But if we start an induction, we quickly see that the induction hypothesis is
not strong enough. Start by proving the following generalization (using the
`induction'` tactic or pattern matching): -/

lemma accurev_eq_reverse_append {α : Type} :
  ∀as xs : list α, accurev as xs = reverse xs ++ as
:= sorry

/-! 1.2. Derive the desired equation. -/

lemma accurev_eq_reverse {α : Type} (xs : list α) :
  accurev [] xs = reverse xs :=
sorry

/-! 1.3. Prove the following property.

Hint: A one-line inductionless proof is possible. -/

lemma accurev_accurev {α : Type} (xs : list α) :
  accurev [] (accurev [] xs) = xs :=
sorry

/-! 1.4. Prove the following lemma by structural induction, as a "paper" proof.
This is a good exercise to develop a deeper understanding of how structural
induction works.

    lemma accurev_eq_reverse_append {α : Type} :
      ∀as xs : list α, accurev as xs = reverse xs ++ as

Guidelines for paper proofs:

We expect detailed, rigorous, mathematical proofs. You are welcome to use
standard mathematical notation or Lean structured commands (e.g., `assume`,
`have`, `show`, `calc`). You can also use tactical proofs (e.g., `intro`,
`apply`), but then please indicate some of the intermediate goals, so that we
can follow the chain of reasoning.

Major proof steps, including applications of induction and invocation of the
induction hypothesis, must be stated explicitly. For each case of a proof by
induction, you must list the inductive hypotheses assumed (if any) and the goal
to be proved. Minor proof steps corresponding to `refl`, `simp`, or `cc` need
not be justified if you think they are obvious (to humans), but you should say
which key lemmas they depend on. You should be explicit whenever you use a
function definition or an introduction rule for an inductive predicate. -/

-- enter your paper proof here

/-! ## Question 2: Drop and Take

The `drop` function removes the first `n` elements from the front of a list. -/

def drop {α : Type} : ℕ → list α → list α
| 0       xs        := xs
| (_ + 1) []        := []
| (m + 1) (x :: xs) := drop m xs

/-! 2.1. Define the `take` function, which returns a list consisting of the the
first `n` elements at the front of a list.

To avoid unpleasant surprises in the proofs, we recommend that you follow the
same recursion pattern as for `drop` above. -/

def take {α : Type} : ℕ → list α → list α
:= sorry

#eval take 0 [3, 7, 11]   -- expected: []
#eval take 1 [3, 7, 11]   -- expected: [3]
#eval take 2 [3, 7, 11]   -- expected: [3, 7]
#eval take 3 [3, 7, 11]   -- expected: [3, 7, 11]
#eval take 4 [3, 7, 11]   -- expected: [3, 7, 11]

#eval take 2 ["a", "b", "c"]   -- expected: ["a", "b"]

/-! 2.2. Prove the following lemmas, using `induction'` or pattern matching.
Notice that they are registered as simplification rules thanks to the `@[simp]`
attribute. -/

@[simp] lemma drop_nil {α : Type} :
  ∀n : ℕ, drop n ([] : list α) = []
:= sorry

@[simp] lemma take_nil {α : Type} :
  ∀n : ℕ, take n ([] : list α) = []
:= sorry

/-! 2.3. Follow the recursion pattern of `drop` and `take` to prove the
following lemmas. In other words, for each lemma, there should be three cases,
and the third case will need to invoke the induction hypothesis.

Hint: Note that there are three variables in the `drop_drop` lemma (but only two
arguments to `drop`). For the third case, `←add_assoc` might be useful. -/

lemma drop_drop {α : Type} :
  ∀(m n : ℕ) (xs : list α), drop n (drop m xs) = drop (n + m) xs
| 0       n xs        := by refl
-- supply the two missing cases here

lemma take_take {α : Type} :
  ∀(m : ℕ) (xs : list α), take m (take m xs) = take m xs
:= sorry

lemma take_drop {α : Type} :
  ∀(n : ℕ) (xs : list α), take n xs ++ drop n xs = xs
:= sorry

/-! ## Question 3: A Type of λ-Terms

3.1. Define an inductive type corresponding to the untyped λ-terms, as given
by the following context-free grammar:

    term ::= 'var' string        -- variable (e.g., `x`)
           | 'lam' string term   -- λ-expression (e.g., `λx, t`)
           | 'app' term term     -- application (e.g., `t u`) -/

-- enter your definition here

/-! 3.2. Register a textual representation of the type `term` as an instance of
the `has_repr` type class. Make sure to supply enough parentheses to guarantee
that the output is unambiguous. -/

def term.repr : term → string
-- enter your answer here

@[instance] def term.has_repr : has_repr term :=
{ repr := term.repr }

/-! 3.3. Test your textual representation. The following command should print
something like `(λx, ((y x) x))`. -/

#eval (term.lam "x" (term.app (term.app (term.var "y") (term.var "x"))
    (term.var "x")))

/-! ## Question 4: Vectors and Matrices

(Note: this question is long, but good preparation for the last HW problem!)

Recall the type constructor `vec : Type → nat → Type` that represents a sequence
of values of a particular length. -/

inductive vec (α : Type) : ℕ → Type
| nil {}                           : vec 0
| cons (a : α) {n : ℕ} (v : vec n) : vec (n + 1)

-- We define convenience notation for writing vectors. Note that this notation
-- cannot be used when pattern-matching.
local notation `V[]` := vec.nil
local notation `V[` l:(foldr `,` (h t, vec.cons h t) vec.nil) `]` := l

def sample_vector_1 : vec string 0 := V[]
def sample_vector_2 : vec ℕ 4 := V[1, 9, 5, 1]

namespace vec

/-! We can define functions on vectors just as we do on lists and trees. For
instance, here's an `append` function on vectors analogous to its counterpart
for lists: -/

-- NOTE: the length arguments `m` and `n` must go after the colon (i.e., they
-- can't be parameters of `vec_map`). We can then simply ignore those arguments
-- when pattern matching by using `_`, as shown.
def append {α : Type} : ∀ {m n : ℕ}, vec α m → vec α n → vec α (n + m)
| _ _ vec.nil ys := ys
| _ _ (vec.cons x xs) ys := vec.cons x (append xs ys)

/-! 4.1. Implement the function `dot_product` that computes the dot
product of two equal-dimensional (i.e., equal-length in the programmatic sense)
vectors with integer coordinates (i.e., elements). The dot product of two
vectors `V[v₁, v₂, ..., vₙ]` and `V[w₁, w₂, ..., wₙ]` is the number
`v₁ * w₁ + v₂ * w₂ + ... + vₙ * wₙ`, or, more formally, `∑_{i=1}^n (vᵢ * wᵢ)`. -/

def dot_product : ∀ {n : ℕ}, vec ℤ n → vec ℤ n → ℤ
:= sorry

/-! 4.2. We define the *melding* (a coined term) of two equal-length
vectors to be the vector formed by applying a combining function to each
corresponding pair of elements in the two vectors. For instance, the melding of
`V[1, 2, 3]` and `V[4, 5, 6]` using the combining function `(+)` would be the
vector `V[5, 7, 9]`.

Implement a function `meld` that performs this operation. (You'll need to fill
in both the type and body of the function.)
-/

def meld : sorry := sorry

/-! The *product* of two types `α` and `β`, denoted `α × β`, is the type of
pairs `(a, b)` such that `a : α` and `b : β`. (`(a, b)` is syntactic sugar for
`prod.mk a b`, where `prod.mk : ∀ {α β : Type}, α → β → α × β`.) -/

#check (1, 2)
#check prod.mk 1951 "X"

/-! 4.3. The *zipping* of two vectors is the vector formed by pairing
corresponding elements. For instance, zipping `V[1, 2, 3] : vec ℕ 3` and
`["a", "b", "c"] : vec string 3` yields the vector
`[(1, "a"), (2, "b"), (3, "c")] : vec (ℕ × string) 3`.

Use `meld` to implement `zip`. The only modification you may make to the line
below is to replace `sorry` with a *non-recursive* function. -/

def zip {α β : Type} {n : ℕ} : vec α n → vec β n → vec (α × β) n :=
meld sorry

/-! 4.4. Prove the lemma below that says that appending and melding
can be done in either order -- that is, that melding the results of appending
two pairs of vectors is the same as appending the result of melding two pairs of
vectors.

You may prove your base case however you wish. However, for the inductive
case, you must use `calc` mode, and you may only use the following terms in your
proof (i.e., as "justifications" after the `:` in `calc` mode):
* `rfl`
* `eq.symm rfl` (this is used for "stepping backward" through functions)
* `by rw [ih]`

You should, of course, fill in the `ih` declaration with the appropriate
variables. -/

lemma meld_append {α β γ : Type} : ∀ {m n : ℕ}
  (f : α → β → γ) (vs : vec α n) (ws : vec β n) (xs : vec α m) (ys : vec β m),
    meld f (append vs xs) (append ws ys) =
    append (meld f vs ws) (meld f xs ys)
| _ _ f vec.nil vec.nil xs ys :=
  sorry
| _ _ f (vec.cons v vs) (vec.cons w ws) xs ys :=
  have ih : _, from meld_append sorry sorry sorry sorry sorry,
  sorry

end vec

/-! 4.5. Having defined vectors, we now turn our attention to matrices. An
`r`-by-`c` matrix is a "grid" of numerical entries (we'll use the naturals to
make things simple) with `r` rows and `c` columns. We can think of them as
vectors of vectors: either a length-`c` vector of `r`-element column vectors, or
a length-`r` vector of `c`-element row vectors.

For this problem, we'll use the *row-wise* representation: that is, we'll think
of matrices as a vector containing row vectors.

Fill in the type-level function that defines this type below. That is, `mat r c`
should evaluate to the type whose values are row-wise representations of
matrices that store natural numbers as their entries. -/

def mat (r : ℕ) (c : ℕ) : Type :=
sorry

-- The errors below should disappear if you've defined the type correctly!
#reduce (V[] : mat 0 0)
#reduce (V[V[11, 12], V[21, 22], V[31, 32]] : mat 3 2)
#reduce (V[V[1, 3, 4], V[2, 2, 5]] : mat 2 3)

/-! 4.6. The first type of matrix operation we'll implement is extracting
subcomponents. Below, implement the functions `get_row` and `get_col` that,
respectively, extract a row or column at a specified index from a matrix.

Notice an interesting feature of these functions: they each take as an argument
a *proof* that the requested row or column index is legal for the given matrix.
This allows us to enforce the legality of arguments at the type level.

We provide two helper functions below that you will likely find helpful. (Hint:
notice the second explicit argument to `vec.nth`!) -/

def vec.map {α β : Type} : ∀ {n : ℕ}, (α → β) → vec α n → vec β n
| _ _ vec.nil         := vec.nil
| _ f (vec.cons x xs) := vec.cons (f x) (vec.map f xs)

def vec.nth {α : Type} : ∀ {n : ℕ} (i : ℕ), i < n → vec α n → α
| _ 0       _ (vec.cons x _)  := x
| _ (n + 1) h (vec.cons x xs) := vec.nth n (nat.succ_lt_succ_iff.mp h) xs
| 0 _       h vec.nil         := absurd h dec_trivial

namespace mat

def get_row {r c : ℕ} (i : ℕ) (h : i < r) (m : mat r c) : vec ℕ c :=
sorry

def get_col {r c : ℕ} (i : ℕ) (h : i < c) (m : mat r c) : vec ℕ r :=
sorry

/-! 4.7. Our second matrix operation is addition. Matrix addition proceeds
component-wise: e.g.,
┌     ┐    ┌     ┐     ┌         ┐
| a b |  + | e f |  =  | a+e b+f |
| c d |    | g h |     | c+g d+h |
└     ┘    └     ┘     └         ┘

Below, implement a function `add` for adding two matrices.

Hint: you might find your `vec.zip` function from earlier useful. You also
remain free to use any vector helper functions you might need. -/

def add : ∀ {r c}, mat r c → mat r c → mat r c
:= sorry

end mat

end LoVe


/-
Copyright (c) 2020 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Reid Barton

! This file was ported from Lean 3 source module data.set.intervals.infinite
! leanprover-community/mathlib commit 1f0096e6caa61e9c849ec2adbd227e960e9dff58
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Set.Finite

/-!
# Infinitude of intervals

Bounded intervals in dense orders are infinite, as are unbounded intervals
in orders that are unbounded on the appropriate side. We also prove that an unbounded
preorder is an infinite type.
-/

variable {α : Type _} [Preorder α]

/-- A nonempty preorder with no maximal element is infinite. This is not an instance to avoid
a cycle with `Infinite α → Nontrivial α → Nonempty α`. -/
theorem NoMaxOrder.infinite [Nonempty α] [NoMaxOrder α] : Infinite α :=
  let ⟨f, hf⟩ := Nat.exists_strictMono α
  Infinite.of_injective f hf.injective
#align no_max_order.infinite NoMaxOrder.infinite

/-- A nonempty preorder with no minimal element is infinite. This is not an instance to avoid
a cycle with `Infinite α → Nontrivial α → Nonempty α`. -/
theorem NoMinOrder.infinite [Nonempty α] [NoMinOrder α] : Infinite α :=
  @NoMaxOrder.infinite αᵒᵈ _ _ _
#align no_min_order.infinite NoMinOrder.infinite

namespace Set

section DenselyOrdered

variable [DenselyOrdered α] {a b : α} (h : a < b)

theorem Ioo.infinite : Infinite (Ioo a b) :=
  @NoMaxOrder.infinite _ _ (nonempty_Ioo_subtype h) _
#align set.Ioo.infinite Set.Ioo.infinite

theorem Ioo_infinite : (Ioo a b).Infinite :=
  infinite_coe_iff.1 <| Ioo.infinite h
#align set.Ioo_infinite Set.Ioo_infinite

theorem Ico_infinite : (Ico a b).Infinite :=
  (Ioo_infinite h).mono Ioo_subset_Ico_self
#align set.Ico_infinite Set.Ico_infinite

theorem Ico.infinite : Infinite (Ico a b) :=
  infinite_coe_iff.2 <| Ico_infinite h
#align set.Ico.infinite Set.Ico.infinite

theorem Ioc_infinite : (Ioc a b).Infinite :=
  (Ioo_infinite h).mono Ioo_subset_Ioc_self
#align set.Ioc_infinite Set.Ioc_infinite

theorem Ioc.infinite : Infinite (Ioc a b) :=
  infinite_coe_iff.2 <| Ioc_infinite h
#align set.Ioc.infinite Set.Ioc.infinite

theorem Icc_infinite : (Icc a b).Infinite :=
  (Ioo_infinite h).mono Ioo_subset_Icc_self
#align set.Icc_infinite Set.Icc_infinite

theorem Icc.infinite : Infinite (Icc a b) :=
  infinite_coe_iff.2 <| Icc_infinite h
#align set.Icc.infinite Set.Icc.infinite

end DenselyOrdered

instance [NoMinOrder α] {a : α} : Infinite (Iio a) :=
  NoMinOrder.infinite

theorem Iio_infinite [NoMinOrder α] (a : α) : (Iio a).Infinite :=
  infinite_coe_iff.1 inferInstance
#align set.Iio_infinite Set.Iio_infinite

instance [NoMinOrder α] {a : α} : Infinite (Iic a) :=
  NoMinOrder.infinite

theorem Iic_infinite [NoMinOrder α] (a : α) : (Iic a).Infinite :=
  infinite_coe_iff.1 inferInstance
#align set.Iic_infinite Set.Iic_infinite

instance [NoMaxOrder α] {a : α} : Infinite (Ioi a) :=
  NoMaxOrder.infinite

theorem Ioi_infinite [NoMaxOrder α] (a : α) : (Ioi a).Infinite :=
  infinite_coe_iff.1 inferInstance
#align set.Ioi_infinite Set.Ioi_infinite

instance [NoMaxOrder α] {a : α} : Infinite (Ici a) :=
  NoMaxOrder.infinite

theorem Ici_infinite [NoMaxOrder α] (a : α) : (Ici a).Infinite :=
  infinite_coe_iff.1 inferInstance
#align set.Ici_infinite Set.Ici_infinite

end Set


import data.nat.basic
import data.set.basic
import data.real.basic

-- ------------------ EXERCICE 1 ------------------

namespace ex1

theorem ex_1_1 : ∀ P:Prop, P → P := assume P:Prop, assume h:P, show P, from h

theorem ex_1_2 : ∀ P Q:Prop, P ∧ Q → P ∨ Q := assume P Q:Prop, assume h:P ∧ Q, show P ∨ Q, from or.inl h.left

end ex1

-- ------------------ EXERCICE 2 ------------------

namespace ex2

variables {E F G:Type}

definition injective  (f: E → F) : Prop  := ∀ (u:E), ∀ (v:E), f u=f v → u=v

theorem ex_2_1 : ∀ f:E→ F, ∀ g:F→ G, (injective f) ∧ (injective g) → (injective (g ∘ f)) :=
   assume f:E→ F,
     assume g:F→ G,
       assume h1:(injective f) ∧ (injective g),
         assume u v :E,
           assume h2: (g ∘ f) u =(g ∘ f) v,
             have h3 : f u=f v, from h1.right (f u) (f v) h2,
             show u = v, from h1.left u v h3

theorem ex_2_2 : ∀ f:E→ F, ∀ g:F→ G,  injective (g ∘ f) → injective f :=
   assume f:E→ F,
     assume g:F→ G,
       assume h1:injective (g ∘ f),
         assume u v :E,
           assume h2: f u =f v,
             have h3:g (f u) =g (f v), from congr_arg g h2,
             show u=v, from h1 u v h3

end ex2
-- ------------------ EXERCICE 3 ------------------

namespace ex3

definition divisible (n p:ℕ) := ∃k:ℕ , n=k*p

theorem  ex_3_2 : ∀ p m n :ℕ , (divisible m p)∧ (divisible n p) → (divisible (m+n) p) :=
   assume p m n : ℕ,
      assume h: (divisible m p)∧ (divisible n p),
        exists.elim h.left (
          assume (j:ℕ )  (h_m: m = j*p),
          exists.elim h.right (
            assume (k:ℕ ) (h_n : n = k*p),
              exists.intro (j+k) (
                calc
                  m+n = j*p+n      : h_m ▸ (eq.refl (m+n))      -- congr_arg (λ z, z+n)   h_m  --
                  ... = j*p+k*p    : h_n ▸ (eq.refl (j*p+n))    -- congr_arg (λ z, j*p+z) h_n  --
                  ... = (j+k)*p    : (right_distrib j k p).symm
              )
          )
        )

end ex3

-- ------------------ EXERCICE 4 ------------------

namespace ex4

definition est_majorant (A:set ℝ) (m:ℝ)  : Prop := ∀ x:ℝ , x ∈ A → x ≤ m
definition est_majorant' (A:set ℝ) (m:ℝ) : Prop := ∀ x∈ A, x ≤ m
#print  est_majorant'

definition est_pge  (A:set ℝ) (m:ℝ) : Prop := m∈A ∧ (est_majorant A m)

theorem ex_4_3 : ∀ (A:set ℝ) (m:ℝ ) (n:ℝ ), (est_pge A m) ∧ (est_pge A n)→ m=n :=
  assume  (A:set ℝ) (m:ℝ ) (n:ℝ ),
    assume (h1: (est_pge A m) ∧ (est_pge A n)),
      have h2 : m ≤ n, from h1.right.right m h1.left.left,
      have h3 : n ≤ m, from h1.left.right n h1.right.left,
      show m=n, from le_antisymm h2 h3

end ex4

-- ------------------ QCM Open ------------------

namespace qcm_open

  variables {E F G:Type}

  definition injective  (f: E → F) : Prop  := ∀ (u:E), ∀ (v:E), f u = f v  →  u = v
  definition surjective (f: E → F) : Prop  := ∀ (y:F), ∃ (x:E), y = f x

  theorem interessant : ∀  (u: E → F) (v: F → G), surjective (v ∘ u) → (injective v) → (surjective u) :=
  assume  (u: E → F) (v: F → G),
    assume h1 : surjective (v ∘ u),
      assume h2: injective v,
        assume (y:F),
          let z:= v y in
           exists.elim (h1 (z:G))
           (
             assume (x:E) (h3: z = (v ∘ u) x),
             have h4: v y = v (u x), from h3,
             exists.intro x (
               show y = u x, from h2 y (u x) h4
             )
           )
/-
  Soient E, F, G trois ensembles.

  Définition :  Soit f une application de E dans F. On dit que f est injective si et seulement si pour tous u et v dans E,
  tels que f(u) = f(v), on a u=v.

  Définition :  Soit f une application de E dans F. On dit que f est surjective si et seulement si pour tout y dans F,
  il existe x dans E tel que y=f(x).

  Théorème : Pour toutes applications u de E dans F et v de F dans F,
  si v o u est surjective et v est injective alors u est surjective.

  Preuve : Soient u une application de E dans F et v une application de F dans G.
  On suppose que v o u est surjective, et que v est injective.
  Soit y appartenant à F. (pour satisfaire la définition de surjectivité de u, on cherche x dans E tel que y=u(x))
  On pose z:= v(y).
  En appliquant la surjectivité de v o u  à z, qui est bien un élément de G, on obtient l'existence
  d'un élément x de E tel que z = (v o u) (x).  Ainsi, on a v(y) = v(u(x)).
  Mais alors ce x convient, puisque l'injectivité de v donne y=u(x).

-/
end qcm_open


/-
Copyright (c) 2023 María Inés de Frutos-Fernández. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: María Inés de Frutos-Fernández
-/
import number_theory.padics.padic_numbers
import ring_theory.valuation.integers
import topology.metric_space.cau_seq_filter
import topology.algebra.valued_field
import from_mathlib.spectral_norm_unique
import from_mathlib.normed_valued

/-!
# The `p`-adic complex numbers.

In this file we define the field `ℂ_[p]` of `p`-adic complex numbers and we give it both a normed
field and a valued field structure, induced by the unique extension of the `p`-adic norm to `ℂ_[p]`.

## Main Definitions

* `padic_complex` : the type of `p`-adic complex numbers.
* `padic_complex_integers` : the ring of integers of `ℂ_[p]`.

## Main Results

* `padic_complex.norm_extends` : the norm on `ℂ_[p]` extends the norm on `Q_p_alg p`, and hence
  the norm on `ℚ_[p]`.
* `padic_complex.is_nonarchimedean` : The norm on `ℂ_[p]` is nonarchimedean.

## Notation

We introduce the notation `ℂ_[p]` for the `p`-adic complex numbers, and `𝓞_ℂ_[p]` for its ring of
integers.

## Tags

p-adic, p adic, padic, norm, valuation, cauchy, completion, p-adic completion
-/

noncomputable theory

open rank_one_valuation

open_locale nnreal

variables (p : ℕ) [fact (nat.prime p)]

/-- `Q_p_alg p` is the algebraic closure of `ℚ_[p]`. -/
@[reducible] def Q_p_alg : Type* := algebraic_closure ℚ_[p]

/-- `Q_p_alg p` is an algebraic extension of `ℚ_[p]`. -/
lemma Q_p_alg.is_algebraic : algebra.is_algebraic ℚ_[p] (Q_p_alg p) :=
algebraic_closure.is_algebraic _

instance : has_coe ℚ_[p] (Q_p_alg p) := ⟨algebra_map ℚ_[p] (Q_p_alg p)⟩

protected lemma coe_eq : (coe : ℚ_[p] → (Q_p_alg p)) = algebra_map ℚ_[p] (Q_p_alg p) := rfl

namespace Q_p_alg

/-- `Q_p_alg p` is a normed field, where the norm is the `p`-adic norm, that is, the spectral norm
induced by the `p`-adic norm on `ℚ_[p]`. -/
instance normed_field : normed_field (Q_p_alg p) :=
@spectral_norm_to_normed_field ℚ_[p] _ _ _ _ padic.complete_space (Q_p_alg.is_algebraic p)
  padic_norm_e.nonarchimedean

/-- The norm on `Q_p_alg p` is nonarchimedean. -/
lemma is_nonarchimedean : is_nonarchimedean (norm : (Q_p_alg p) → ℝ) :=
spectral_norm_is_nonarchimedean (Q_p_alg.is_algebraic p) (padic_norm_e.nonarchimedean)

/-- The norm on `Q_p_alg p` is the spectral norm induced by the `p`-adic norm on `ℚ_[p]`. -/
lemma norm_def (x : Q_p_alg p) : ‖ x ‖ = spectral_norm ℚ_[p] (Q_p_alg p) x := rfl

/-- The norm on `Q_p_alg p` extends the `p`-adic norm on `ℚ_[p]`. -/
lemma Q_p.norm_extends (x : ℚ_[p]) : ‖ (x : Q_p_alg p) ‖ = ‖ x ‖ :=
spectral_alg_norm_extends (Q_p_alg.is_algebraic p) _ (padic_norm_e.nonarchimedean)

/-- `Q_p_alg p` is a valued field, with the valuation corresponding to the `p`-adic norm. -/
instance valued_field : valued (Q_p_alg p) ℝ≥0 :=
normed_field.to_valued (Q_p_alg.is_nonarchimedean p)

/-- The valuation of `x : Q_p_alg p` agrees with its `ℝ≥0`-valued norm. -/
lemma v_def (x : Q_p_alg p) : valued.v x = ‖ x ‖₊ := rfl

/-- The coercion of the valuation of `x : Q_p_alg p` to `ℝ` agrees with its norm. -/
lemma v_def_coe (x : Q_p_alg p) :
  ((valued.v x : ℝ≥0) : ℝ) = spectral_norm ℚ_[p] (Q_p_alg p) x := rfl

/-- The valuation of `p : Q_p_alg p` is `1/p`. -/
lemma valuation_p (p : ℕ) [fact p.prime] : valued.v (p : Q_p_alg p) = 1/(p : ℝ≥0) :=
begin
  rw [← map_nat_cast (algebra_map  ℚ_[p] (Q_p_alg p)), ← coe_eq],
  ext,
  rw [v_def_coe, coe_eq, spectral_norm_extends,padic_norm_e.norm_p, one_div, nonneg.coe_inv,
    nnreal.coe_nat_cast],
end

end Q_p_alg

/-- `ℂ_[p]` is the field of `p`-adic complex numbers, that is, the completion of `Q_p_alg p` with
respect to the `p`-adic norm. -/
def padic_complex := uniform_space.completion (Q_p_alg p)

notation `ℂ_[`p`]` := padic_complex p

namespace padic_complex

/-- The `p`-adic complex numbers have a field structure. -/
instance : field ℂ_[p] := uniform_space.completion.field

/-- The `p`-adic complex numbers are inhabited. -/
instance : inhabited ℂ_[p] := ⟨0⟩

/-- `ℂ_[p]` is a valued field, where the valuation is the one extending that on `Q_p_alg p`. -/
instance valued_field : valued (ℂ_[p]) ℝ≥0 := valued.valued_completion

/-- `ℂ_[p]` is a complete space. -/
instance complete_space : complete_space (ℂ_[p]) := uniform_space.completion.complete_space _

instance : has_coe_t (Q_p_alg p) ℂ_[p] := uniform_space.completion.has_coe_t _

/-- The valuation on `ℂ_[p]` extends the valuation on `Q_p_alg p`. -/
lemma valuation_extends (x : Q_p_alg p) : valued.v (x : ℂ_[p]) = valued.v x :=
valued.extension_extends _

/-- `ℂ_[p]` is an algebra over `Q_p_alg p`. -/
instance : algebra (Q_p_alg p) ℂ_[p] := uniform_space.completion.algebra' _

lemma coe_eq : (coe : (Q_p_alg p) → ℂ_[p]) = algebra_map (Q_p_alg p) ℂ_[p] := rfl

lemma coe_zero : ((0 : Q_p_alg p) : ℂ_[p]) = 0 := rfl

/-- The valuation of `p : ℂ_[p]` is `1/p`. -/
lemma valuation_p (p : ℕ) [fact p.prime] : valued.v (p : ℂ_[p]) = 1/(p : ℝ≥0) :=
by rw [← map_nat_cast (algebra_map _ _), ← coe_eq, valuation_extends, Q_p_alg.valuation_p]

/-- The valuation on `ℂ_[p]` has rank one. -/
instance : is_rank_one (padic_complex.valued_field p).v :=
{ hom := monoid_with_zero_hom.id ℝ≥0,
  strict_mono := strict_mono_id,
  nontrivial  :=
  begin
    use p,
    haveI hp : nat.prime p := _inst_1.elim,
    simp only [valuation_p, one_div, ne.def, inv_eq_zero, nat.cast_eq_zero,
      inv_eq_one, nat.cast_eq_one],
    exact ⟨hp.ne_zero, hp.ne_one⟩,
  end }

/-- `ℂ_[p]` is a normed field, where the norm corresponds to the extension of the `p`-adic
  valuation.-/
instance : normed_field ℂ_[p] := valued_field.to_normed_field

/-- The norm on `ℂ_[p]` agrees with the valuation. -/
lemma norm_def : (norm : ℂ_[p] → ℝ) = rank_one_valuation.norm_def := rfl

/-- The norm on `ℂ_[p]` extends the norm on `Q_p_alg p`. -/
lemma norm_extends (x : Q_p_alg p) : ‖ (x : ℂ_[p]) ‖ = ‖ x ‖ :=
begin
  by_cases hx : x = 0,
  { rw [hx, coe_zero, norm_zero, norm_zero] },
  { simp only [norm_def, rank_one_valuation.norm_def, valuation_extends,
      monoid_with_zero_hom.coe_mk],
    refl }
end

/-- The `ℝ≥0`-valued norm on `ℂ_[p]` extends that on `Q_p_alg p`. -/
lemma nnnorm_extends (x : Q_p_alg p) : ‖ (x : ℂ_[p]) ‖₊ = ‖ x ‖₊ :=
by { ext, exact norm_extends p x }

/-- The norm on `ℂ_[p]` is nonarchimedean. -/
lemma is_nonarchimedean : is_nonarchimedean (norm : ℂ_[p] → ℝ) :=
begin
  intros x y,
  apply uniform_space.completion.induction_on₂ x y,
  { exact is_closed_le (continuous.comp continuous_norm continuous_add) (continuous.max
      (continuous.comp (@continuous_norm ℂ_[p] _) (continuous.fst continuous_id))
      (continuous.comp (@continuous_norm ℂ_[p] _) (continuous.snd continuous_id))) },
  { intros a b,
    simp only [← uniform_space.completion.coe_add, norm_extends],
    exact Q_p_alg.is_nonarchimedean p a b }
end

end padic_complex

/-- We define `𝓞_ℂ_[p]` as the subring elements of `ℂ_[p]` with valuation `≤ 1`. -/
def padic_complex_integers : subring ℂ_[p] := (padic_complex.valued_field p).v.integer

notation `𝓞_ℂ_[`p`]` := padic_complex_integers p

/-- `𝓞_ℂ_[p]` is the ring of integers of `ℂ_[p]`. -/
lemma padic_complex.integers : valuation.integers (padic_complex.valued_field p).v  𝓞_ℂ_[p] :=
valuation.integer.integers _


import monotone limits

theorem bdd_above_of_is_bounded (f : ℕ → ℝ) (hfb : M1P1.is_bounded f) : bdd_above (set.range f) :=
let ⟨M, hm⟩ := hfb in ⟨M, λ y ⟨n, hn⟩, hn ▸ (abs_le.1 (hm n)).2⟩

theorem bdd_below_of_is_bounded (f : ℕ → ℝ) (hfb : M1P1.is_bounded f) : bdd_below (set.range f) :=
let ⟨M, hm⟩ := hfb in ⟨-M, λ y ⟨n, hn⟩, hn ▸ (abs_le.1 (hm n)).1⟩

theorem M1P1.is_bounded.comp {f : ℕ → ℝ} (hf : M1P1.is_bounded f) (s : ℕ → ℕ) : M1P1.is_bounded (f ∘ s) :=
let ⟨M, hm⟩ := hf in ⟨M, λ y, hm (s y)⟩

theorem increasing_bounded (f : ℕ → ℝ) (hfb : M1P1.is_bounded f) (hfi : increasing f) :
  M1P1.is_limit f (real.Sup $ set.range f) :=
begin
  intros ε Hε,
  have := mt (real.Sup_le_ub (set.range f) ⟨f 0, 0, rfl⟩) (not_le_of_lt $ sub_lt_self _ Hε),
  classical, simp only [not_forall] at this, rcases this with ⟨_, ⟨n, rfl⟩, hn⟩, use n, intros m hnm,
  rw abs_sub_lt_iff, split,
  { rw sub_lt_iff_lt_add', refine lt_of_le_of_lt _ (lt_add_of_pos_right _ Hε),
    exact real.le_Sup _ (bdd_above_of_is_bounded _ hfb) ⟨m, rfl⟩ },
  { rw sub_lt, exact lt_of_lt_of_le (not_le.1 hn) (hfi _ _ hnm) }
end

theorem decreasing_bounded (f : ℕ → ℝ) (hfb : M1P1.is_bounded f) (hfd : decreasing f) :
  M1P1.is_limit f (real.Inf $ set.range f) :=
begin
  intros ε Hε,
  have := mt (real.lb_le_Inf (set.range f) ⟨f 0, 0, rfl⟩) (not_le_of_lt $ lt_add_of_pos_right _ Hε),
  classical, simp only [not_forall] at this, rcases this with ⟨_, ⟨n, rfl⟩, hn⟩, use n, intros m hnm,
  rw abs_sub_lt_iff, split,
  { rw sub_lt_iff_lt_add', exact lt_of_le_of_lt (hfd _ _ hnm) (not_le.1 hn) },
  { rw sub_lt, refine lt_of_lt_of_le (sub_lt_self _ Hε) _,
    exact real.Inf_le _ (bdd_below_of_is_bounded _ hfb) ⟨m, rfl⟩ }
end

theorem bolzano_weierstrass (f : ℕ → ℝ) (hf : M1P1.is_bounded f) : ∃ s : ℕ → ℕ, strictly_increasing s ∧ M1P1.has_limit (f ∘ s) :=
let ⟨s, hs1, hs2⟩ := exists_monotone f in or.cases_on hs2
  (λ hsi, ⟨s, hs1, _, increasing_bounded _ (hf.comp s) (increasing_of_strictly_increasing _ hsi)⟩)
  (λ hsd, ⟨s, hs1, _, decreasing_bounded _ (hf.comp s) hsd⟩)


import Mathlib.Tactic.Ring

/- # Proving challenge -/

/- My summing function -/
def sum : Nat → Nat
  | 0     => 0
  | n + 1 => n + 1 + sum n

/- The actual proof -/
theorem twoTimesSumEqTimesSucc {n : Nat} : 2 * sum n = n * (n + 1) := by
  induction n with
    | zero => rfl
    | succ n hi =>
      simp only [sum, Nat.mul_add, Nat.add_succ, hi]
      ring

/-! # Programming challenge -/

/-
A helper function for the programming challenge.

It carries over two variables for the computation happening on the current streak:
`currStreak` and `currSum`. `currStreak` is the length of the current consecutive
ascending sequence and `currSum` is the sum of that sequence.

It also carries over two variables for the best solution until this point:
`currMaxStreak` and `currMaxSum`, whose names indicate analogous utilities to the
other two.
-/
def longestConsecutiveSumAux : List Nat → Nat → Nat → Nat → Nat → Nat
  | a::b::t, currStreak, currSum, currMaxStreak, currMaxSum =>
    if a + 1 ≠ b then -- breaking the streak
      longestConsecutiveSumAux (b::t) 0 0 currMaxStreak currMaxSum
    else
      if currMaxStreak < currStreak + 1 then -- update highest values
        longestConsecutiveSumAux (b::t) (currStreak + 1) (a + currSum)
          (currStreak + 1) (a + b + currSum)
      else
        longestConsecutiveSumAux (b::t) (currStreak + 1) (a + currSum)
          currMaxStreak currMaxSum
  | _, _, _, _, currMaxSum => currMaxSum

/- Final function for the challenge -/
def longestConsecutiveSum (l : List Nat) : Nat := longestConsecutiveSumAux l 0 0 0 0

#eval longestConsecutiveSum [1, 2, 3, 100, 7, 8, 9, 10] -- 34 = 7 + 8 + 9 + 10

/- # Extra -/

/- This function extracts the greatest sum instead of the sum of the longest sequence -/
def greatestConsecutiveSumAux : List Nat → Nat → Nat → Nat
  | a::b::t, currSum, currMaxSum =>
    if a + 1 ≠ b then -- breaking the streak
      greatestConsecutiveSumAux (b::t) 0 currMaxSum
    else
      greatestConsecutiveSumAux (b::t) (a + currSum)
        (max (a + b + currSum) currMaxSum)
  | _, _, currMax => currMax

def greatestConsecutiveSum (l : List Nat) : Nat := greatestConsecutiveSumAux l 0 0

#eval greatestConsecutiveSum [1, 2, 3, 100, 7, 8] -- 15 = 7 + 8


/-
Copyright (c) 2022 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import tactic -- imports all the Lean tactics
import group_theory.subgroup.basic
/-!

# Quotient groups

-/

/-
subgroup.normal : Π {G : Type u_1} [_inst_1 : group G], subgroup G → Prop
-/

-- let G be a group and let N be a normal subgroup
variables {G : Type} [group G] (N : subgroup G) [hN : subgroup.normal N]

include hN

example (g h : G) (h1 : g ∈ N) : h * g * h⁻¹ ∈ N :=
begin
  apply hN.conj_mem,
  assumption,
end

-- The binary relation on G whose equivalence classes are the cosets of N
def R (a b : G) : Prop := a * b⁻¹ ∈ N

variables (a b : G)

lemma R_def (a b : G) : R N a b ↔ a * b⁻¹ ∈ N := iff.rfl

lemma R_refl : reflexive (R N) :=
begin
  intro x,
  rw R_def,
  simp [N.one_mem],
end

lemma R_symm : symmetric (R N) :=
begin
  intros x y h,
  rw R_def at *,
  rw ← N.inv_mem_iff,
  simp [h],
end

lemma R_trans : transitive (R N) :=
begin
  intros x y z h1 h2,
  rw R_def at *,
  have h := N.mul_mem h1 h2,
  convert h using 1,
  group,
end

lemma R_equiv : equivalence (R N) :=
⟨R_refl N, R_symm N, R_trans N⟩

def s : setoid G :=
{ r := R N,
  iseqv := R_equiv N }

-- Q is quotient of G by N
notation `Q`:10000 N := quotient (s N)

-- things to do
-- (1) make Q a group
-- (2) Define group hom G -> Q
-- (3) prove universal property : if φ : G -> H is a group hom and N is in ker(φ)
--     then we get an induced map Q -> H

namespace quotient_group

def equiv_def (a b : G) : @has_equiv.equiv G (@setoid_has_equiv G (s N)) a b ↔ a * b⁻¹ ∈ N := iff.rfl
--  ↔ a * b⁻¹ ∈ N := sorry
def one : Q N := quotient.mk' 1

def setoid_r_def (a b : G) : @setoid.r G (s N) a b ↔ a * b⁻¹ ∈ N := iff.rfl

instance : has_one (Q N) := ⟨one N⟩

def inv : (Q N) → Q N :=
quotient.map' (λ g, g⁻¹) begin
  intros a b h,
  dsimp,
  rw equiv_def at *,
  rw [hN.mem_comm_iff, ← N.inv_mem_iff] at h,
  convert h using 1,
  group,
end

instance : has_inv (Q N) := ⟨inv N⟩

def mul : (Q N) → (Q N) → Q N :=
quotient.map₂' (λ g h, g * h) begin
  intros a b h1 c d h2,
  dsimp,
  rw equiv_def at *,
  rw (show (b * d)⁻¹ = d⁻¹ * b⁻¹, by group),
  rw hN.mem_comm_iff at h1,
  rw [mul_assoc, hN.mem_comm_iff],
  convert N.mul_mem h2 h1 using 1,
  group,
end

instance : has_mul (Q N) :=
⟨mul N⟩

instance group : group (Q N) :=
{ mul := (*),
  mul_assoc := begin
    intros a b c,
    apply quotient.induction_on₃' a b c, clear a b c,
    intros a b c,
    apply quotient.sound',
    dsimp,
    rw setoid_r_def,
    convert N.one_mem,
    group,
  end,
  one := 1,
  one_mul := begin
    intro a,
    apply quotient.induction_on' a, clear a,
    intro a,
    apply quotient.sound',
    dsimp,
    rw setoid_r_def,
    simp [N.one_mem],
  end,
  mul_one := begin
    intro a,
    apply quotient.induction_on' a, clear a,
    intro a,
    apply quotient.sound',
    dsimp,
    rw setoid_r_def,
    simp [N.one_mem],
  end,
  inv := has_inv.inv,
  mul_left_inv := begin
    intro a,
    apply quotient.induction_on' a, clear a,
    intro a,
    apply quotient.sound',
    dsimp,
    rw setoid_r_def,
    simp [N.one_mem],
  end, }

def canonical : G →* (Q N) :=
{ to_fun := quotient.mk',
  map_one' := rfl,
  map_mul' := λ x y, rfl }

-- (3) prove universal property : if φ : G -> H is a group hom and N is in ker(φ)
--     then we get an induced map Q -> H

variable {N}

def lift {H : Type} [group H] {φ : G →* H} (hφ : N ≤ φ.ker) :
(Q N) →* H :=
{ to_fun := λ q, quotient.lift_on' q φ begin
    clear q,
    intros a b h,
    rw setoid_r_def at h,
    have h2 : φ (a * b⁻¹) = 1,
    { rw ← monoid_hom.mem_ker,
      apply hφ h },
    rw φ.map_mul at h2,
    rw φ.map_inv at h2,
    exact mul_inv_eq_one.mp h2,
  end,
  map_one' := begin
     exact φ.map_one,
  end,
  map_mul' := begin intros x y,
    apply quotient.induction_on₂' x y, clear x y,
    exact φ.map_mul,
  end }

/-

G ----φ---> H
|        /
|mk    /
|    / lift
|   /
\//
Q
-/
example (H : Type) [group H] (g : G) (φ : G →* H) (hφ : N ≤ φ.ker) :
  φ g = lift hφ (quotient.mk' g) := rfl

end quotient_group


/-
Copyright (c) 2022 Jujian Zhang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jujian Zhang
-/
import group_theory.subgroup.pointwise
import group_theory.quotient_group
import algebra.group.pi

/-!
# Divisible Group and rootable group

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

In this file, we define a divisible add monoid and a rootable monoid with some basic properties.

## Main definition

* `divisible_by A α`: An additive monoid `A` is said to be divisible by `α` iff for all `n ≠ 0 ∈ α`
  and `y ∈ A`, there is an `x ∈ A` such that `n • x = y`. In this file, we adopt a constructive
  approach, i.e. we ask for an explicit `div : A → α → A` function such that `div a 0 = 0` and
  `n • div a n = a` for all `n ≠ 0 ∈ α`.
* `rootable_by A α`: A monoid `A` is said to be rootable by `α` iff for all `n ≠ 0 ∈ α` and `y ∈ A`,
  there is an `x ∈ A` such that `x^n = y`. In this file, we adopt a constructive approach, i.e. we
  ask for an explicit `root : A → α → A` function such that `root a 0 = 1` and `(root a n)ⁿ = a` for
  all `n ≠ 0 ∈ α`.

## Main results

For additive monoids and groups:

* `divisible_by_of_smul_right_surj` : the constructive definition of divisiblity is implied by
  the condition that `n • x = a` has solutions for all `n ≠ 0` and `a ∈ A`.
* `smul_right_surj_of_divisible_by` : the constructive definition of divisiblity implies
  the condition that `n • x = a` has solutions for all `n ≠ 0` and `a ∈ A`.
* `prod.divisible_by` : `A × B` is divisible for any two divisible additive monoids.
* `pi.divisible_by` : any product of divisble additive monoids is divisible.
* `add_group.divisible_by_int_of_divisible_by_nat` : for additive groups, int divisiblity is implied
  by nat divisiblity.
* `add_group.divisible_by_nat_of_divisible_by_int` : for additive groups, nat divisiblity is implied
  by int divisiblity.
* `add_comm_group.divisible_by_int_of_smul_top_eq_top`: the constructive definition of divisiblity
  is implied by the condition that `n • A = A` for all `n ≠ 0`.
* `add_comm_group.smul_top_eq_top_of_divisible_by_int`: the constructive definition of divisiblity
  implies the condition that `n • A = A` for all `n ≠ 0`.
* `divisible_by_int_of_char_zero` : any field of characteristic zero is divisible.
* `quotient_add_group.divisible_by` : quotient group of divisible group is divisible.
* `function.surjective.divisible_by` : if `A` is divisible and `A →+ B` is surjective, then `B`
  is divisible.

and their multiplicative counterparts:

* `rootable_by_of_pow_left_surj` : the constructive definition of rootablity is implied by the
  condition that `xⁿ = y` has solutions for all `n ≠ 0` and `a ∈ A`.
* `pow_left_surj_of_rootable_by` : the constructive definition of rootablity implies the
  condition that `xⁿ = y` has solutions for all `n ≠ 0` and `a ∈ A`.
* `prod.rootable_by` : any product of two rootable monoids is rootable.
* `pi.rootable_by` : any product of rootable monoids is rootable.
* `group.rootable_by_int_of_rootable_by_nat` : in groups, int rootablity is implied by nat
  rootablity.
* `group.rootable_by_nat_of_rootable_by_int` : in groups, nat rootablity is implied by int
  rootablity.
* `quotient_group.rootable_by` : quotient group of rootable group is rootable.
* `function.surjective.rootable_by` : if `A` is rootable and `A →* B` is surjective, then `B` is
  rootable.

TODO: Show that divisibility implies injectivity in the category of `AddCommGroup`.
-/

open_locale pointwise

section add_monoid

variables (A α : Type*) [add_monoid A] [has_smul α A] [has_zero α]

/--
An `add_monoid A` is `α`-divisible iff `n • x = a` has a solution for all `n ≠ 0 ∈ α` and `a ∈ A`.
Here we adopt a constructive approach where we ask an explicit `div : A → α → A` function such that
* `div a 0 = 0` for all `a ∈ A`
* `n • div a n = a` for all `n ≠ 0 ∈ α` and `a ∈ A`.
-/
class divisible_by :=
(div : A → α → A)
(div_zero : ∀ a, div a 0 = 0)
(div_cancel : ∀ {n : α} (a : A), n ≠ 0 → n • (div a n) = a)

end add_monoid

section monoid

variables (A α : Type*) [monoid A] [has_pow A α] [has_zero α]

/--
A `monoid A` is `α`-rootable iff `xⁿ = a` has a solution for all `n ≠ 0 ∈ α` and `a ∈ A`.
Here we adopt a constructive approach where we ask an explicit `root : A → α → A` function such that
* `root a 0 = 1` for all `a ∈ A`
* `(root a n)ⁿ = a` for all `n ≠ 0 ∈ α` and `a ∈ A`.
-/
@[to_additive]
class rootable_by :=
(root : A → α → A)
(root_zero : ∀ a, root a 0 = 1)
(root_cancel : ∀ {n : α} (a : A), n ≠ 0 → (root a n)^n = a)

@[to_additive smul_right_surj_of_divisible_by]
lemma pow_left_surj_of_rootable_by [rootable_by A α] {n : α} (hn : n ≠ 0) :
  function.surjective (λ a, pow a n : A → A) :=
λ x, ⟨rootable_by.root x n, rootable_by.root_cancel _ hn⟩

/--
A `monoid A` is `α`-rootable iff the `pow _ n` function is surjective, i.e. the constructive version
implies the textbook approach.
-/
@[to_additive divisible_by_of_smul_right_surj
"An `add_monoid A` is `α`-divisible iff `n • _` is a surjective function, i.e. the constructive
version implies the textbook approach."]
noncomputable def rootable_by_of_pow_left_surj
  (H : ∀ {n : α}, n ≠ 0 → function.surjective (λ a, a^n : A → A)) :
rootable_by A α :=
{ root := λ a n, @dite _ (n = 0) (classical.dec _) (λ _, (1 : A)) (λ hn, (H hn a).some),
  root_zero := λ _, by classical; exact dif_pos rfl,
  root_cancel := λ n a hn, by { classical, rw dif_neg hn, exact (H hn a).some_spec } }

section pi

variables {ι β : Type*} (B : ι → Type*) [Π (i : ι), has_pow (B i) β]
variables [has_zero β] [Π (i : ι), monoid (B i)] [Π i, rootable_by (B i) β]

@[to_additive]
instance pi.rootable_by : rootable_by (Π i, B i) β :=
{ root := λ x n i, rootable_by.root (x i) n,
  root_zero := λ x, funext $ λ i, rootable_by.root_zero _,
  root_cancel := λ n x hn, funext $ λ i, rootable_by.root_cancel _ hn }

end pi

section prod

variables {β B B' : Type*} [has_pow B β] [has_pow B' β]
variables [has_zero β] [monoid B] [monoid B'] [rootable_by B β] [rootable_by B' β]

@[to_additive]
instance prod.rootable_by : rootable_by (B × B') β :=
{ root := λ p n, (rootable_by.root p.1 n, rootable_by.root p.2 n),
  root_zero := λ p, prod.ext (rootable_by.root_zero _) (rootable_by.root_zero _),
  root_cancel := λ n p hn, prod.ext (rootable_by.root_cancel _ hn) (rootable_by.root_cancel _ hn) }

end prod

end monoid

namespace add_comm_group

variables (A : Type*) [add_comm_group A]

lemma smul_top_eq_top_of_divisible_by_int [divisible_by A ℤ] {n : ℤ} (hn : n ≠ 0) :
  n • (⊤ : add_subgroup A) = ⊤ :=
add_subgroup.map_top_of_surjective _ $ λ a, ⟨divisible_by.div a n, divisible_by.div_cancel _ hn⟩

/--
If for all `n ≠ 0 ∈ ℤ`, `n • A = A`, then `A` is divisible.
-/
noncomputable def divisible_by_int_of_smul_top_eq_top
  (H : ∀ {n : ℤ} (hn : n ≠ 0), n • (⊤ : add_subgroup A) = ⊤) :
  divisible_by A ℤ :=
{ div := λ a n, if hn : n = 0 then 0 else
    (show a ∈ n • (⊤ : add_subgroup A), by rw [H hn]; trivial).some,
  div_zero := λ a, dif_pos rfl,
  div_cancel := λ n a hn, begin
    rw [dif_neg hn],
    generalize_proofs h1,
    exact h1.some_spec.2,
  end }

end add_comm_group

@[priority 100]
instance divisible_by_int_of_char_zero {𝕜} [division_ring 𝕜] [char_zero 𝕜] : divisible_by 𝕜 ℤ :=
{ div := λ q n, q / n,
  div_zero := λ q, by norm_num,
  div_cancel := λ n q hn,
    by rw [zsmul_eq_mul, (int.cast_commute n _).eq, div_mul_cancel q (int.cast_ne_zero.mpr hn)] }

namespace group

variables (A : Type*) [group A]

/--
A group is `ℤ`-rootable if it is `ℕ`-rootable.
-/
@[to_additive add_group.divisible_by_int_of_divisible_by_nat
"An additive group is `ℤ`-divisible if it is `ℕ`-divisible."]
def rootable_by_int_of_rootable_by_nat [rootable_by A ℕ] : rootable_by A ℤ :=
{ root := λ a z, match z with
  | (n : ℕ) := rootable_by.root a n
  | -[1+n] := (rootable_by.root a (n + 1))⁻¹
  end,
  root_zero := λ a, rootable_by.root_zero a,
  root_cancel := λ n a hn, begin
    induction n,
    { change (rootable_by.root a _) ^ _ = a,
      norm_num,
      rw [rootable_by.root_cancel],
      rw [int.of_nat_eq_coe] at hn,
      exact_mod_cast hn, },
    { change ((rootable_by.root a _) ⁻¹)^_ = a,
      norm_num,
      rw [rootable_by.root_cancel],
      norm_num, }
  end}

/--A group is `ℕ`-rootable if it is `ℤ`-rootable
-/
@[to_additive add_group.divisible_by_nat_of_divisible_by_int
"An additive group is `ℕ`-divisible if it `ℤ`-divisible."]
def rootable_by_nat_of_rootable_by_int [rootable_by A ℤ] : rootable_by A ℕ :=
{ root := λ a n, rootable_by.root a (n : ℤ),
  root_zero := λ a, rootable_by.root_zero a,
  root_cancel := λ n a hn, begin
    have := rootable_by.root_cancel a (show (n : ℤ) ≠ 0, by exact_mod_cast hn),
    norm_num at this,
    exact this,
  end }

end group

section hom

variables {α A B : Type*}
variables [has_zero α] [monoid A] [monoid B] [has_pow A α] [has_pow B α] [rootable_by A α]
variables (f : A → B)

/--
If `f : A → B` is a surjective homomorphism and `A` is `α`-rootable, then `B` is also `α`-rootable.
-/
@[to_additive "If `f : A → B` is a surjective homomorphism and
`A` is `α`-divisible, then `B` is also `α`-divisible."]
noncomputable def function.surjective.rootable_by (hf : function.surjective f)
  (hpow : ∀ (a : A) (n : α), f (a ^ n) = f a ^ n) : rootable_by B α :=
rootable_by_of_pow_left_surj _ _ $ λ n hn x,
  let ⟨y, hy⟩ := hf x in ⟨f $ rootable_by.root y n, (by rw [←hpow (rootable_by.root y n) n,
    rootable_by.root_cancel _ hn, hy] : _ ^ _ = x)⟩

@[to_additive divisible_by.surjective_smul]
lemma rootable_by.surjective_pow
  (A α : Type*) [monoid A] [has_pow A α] [has_zero α] [rootable_by A α] {n : α} (hn : n ≠ 0) :
  function.surjective (λ (a : A), a^n) :=
λ a, ⟨rootable_by.root a n, rootable_by.root_cancel a hn⟩

end hom

section quotient

variables (α : Type*) {A : Type*} [comm_group A] (B : subgroup A)

/-- Any quotient group of a rootable group is rootable. -/
@[to_additive quotient_add_group.divisible_by
"Any quotient group of a divisible group is divisible"]
noncomputable instance quotient_group.rootable_by [rootable_by A ℕ] : rootable_by (A ⧸ B) ℕ :=
quotient_group.mk_surjective.rootable_by _ $ λ _ _, rfl

end quotient