import data.list.basic

variable {α : Type*}

open list

example (xs : list ℕ) :
  reverse (xs ++ [1, 2, 3]) = [3, 2, 1] ++ reverse xs :=
by simp

example (xs ys : list α) :
  length (reverse (xs ++ ys)) = length xs + length ys :=
by simp [add_comm]

variables (x y z : ℕ) (p : ℕ → Prop)

example (h : p ((x + 0) * (0 + y * 1 + z * 0))) :
  p (x * y) :=
by { simp at h, assumption }


/-
Copyright (c) 2021 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov, Heather Macbeth

! This file was ported from Lean 3 source module topology.algebra.order.monotone_continuity
! leanprover-community/mathlib commit 4c19a16e4b705bf135cf9a80ac18fcc99c438514
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Topology.Order.Basic
import Mathlib.Topology.Homeomorph

/-!
# Continuity of monotone functions

In this file we prove the following fact: if `f` is a monotone function on a neighborhood of `a`
and the image of this neighborhood is a neighborhood of `f a`, then `f` is continuous at `a`, see
`continuousWithinAt_of_monotoneOn_of_image_mem_nhds`, as well as several similar facts.

We also prove that an `OrderIso` is continuous.

## Tags

continuous, monotone
-/

open Set Filter

open Topology

section LinearOrder

variable {α β : Type _} [LinearOrder α] [TopologicalSpace α] [OrderTopology α]

variable [LinearOrder β] [TopologicalSpace β] [OrderTopology β]

/-- If `f` is a function strictly monotone on a right neighborhood of `a` and the
image of this neighborhood under `f` meets every interval `(f a, b]`, `b > f a`, then `f` is
continuous at `a` from the right.

The assumption `hfs : ∀ b > f a, ∃ c ∈ s, f c ∈ Ioc (f a) b` is required because otherwise the
function `f : ℝ → ℝ` given by `f x = if x ≤ 0 then x else x + 1` would be a counter-example at
`a = 0`. -/
theorem StrictMonoOn.continuousWithinAt_right_of_exists_between {f : α → β} {s : Set α} {a : α}
    (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≥] a) (hfs : ∀ b > f a, ∃ c ∈ s, f c ∈ Ioc (f a) b) :
    ContinuousWithinAt f (Ici a) a := by
  have ha : a ∈ Ici a := left_mem_Ici
  have has : a ∈ s := mem_of_mem_nhdsWithin ha hs
  refine' tendsto_order.2 ⟨fun b hb => _, fun b hb => _⟩
  · filter_upwards [hs, @self_mem_nhdsWithin _ _ a (Ici a)] with _ hxs hxa using hb.trans_le
      ((h_mono.le_iff_le has hxs).2 hxa)
  · rcases hfs b hb with ⟨c, hcs, hac, hcb⟩
    rw [h_mono.lt_iff_lt has hcs] at hac
    filter_upwards [hs, Ico_mem_nhdsWithin_Ici (left_mem_Ico.2 hac)]
    rintro x hx ⟨_, hxc⟩
    exact ((h_mono.lt_iff_lt hx hcs).2 hxc).trans_le hcb
#align strict_mono_on.continuous_at_right_of_exists_between StrictMonoOn.continuousWithinAt_right_of_exists_between

/-- If `f` is a monotone function on a right neighborhood of `a` and the image of this neighborhood
under `f` meets every interval `(f a, b)`, `b > f a`, then `f` is continuous at `a` from the right.

The assumption `hfs : ∀ b > f a, ∃ c ∈ s, f c ∈ Ioo (f a) b` cannot be replaced by the weaker
assumption `hfs : ∀ b > f a, ∃ c ∈ s, f c ∈ Ioc (f a) b` we use for strictly monotone functions
because otherwise the function `ceil : ℝ → ℤ` would be a counter-example at `a = 0`. -/
theorem continuousWithinAt_right_of_monotoneOn_of_exists_between {f : α → β} {s : Set α} {a : α}
    (h_mono : MonotoneOn f s) (hs : s ∈ 𝓝[≥] a) (hfs : ∀ b > f a, ∃ c ∈ s, f c ∈ Ioo (f a) b) :
    ContinuousWithinAt f (Ici a) a := by
  have ha : a ∈ Ici a := left_mem_Ici
  have has : a ∈ s := mem_of_mem_nhdsWithin ha hs
  refine' tendsto_order.2 ⟨fun b hb => _, fun b hb => _⟩
  · filter_upwards [hs, @self_mem_nhdsWithin _ _ a (Ici a)] with _ hxs hxa using hb.trans_le
      (h_mono has hxs hxa)
  · rcases hfs b hb with ⟨c, hcs, hac, hcb⟩
    have : a < c := not_le.1 fun h => hac.not_le <| h_mono hcs has h
    filter_upwards [hs, Ico_mem_nhdsWithin_Ici (left_mem_Ico.2 this)]
    rintro x hx ⟨_, hxc⟩
    exact (h_mono hx hcs hxc.le).trans_lt hcb
#align continuous_at_right_of_monotone_on_of_exists_between continuousWithinAt_right_of_monotoneOn_of_exists_between

/-- If a function `f` with a densely ordered codomain is monotone on a right neighborhood of `a` and
the closure of the image of this neighborhood under `f` is a right neighborhood of `f a`, then `f`
is continuous at `a` from the right. -/
theorem continuousWithinAt_right_of_monotoneOn_of_closure_image_mem_nhdsWithin [DenselyOrdered β]
    {f : α → β} {s : Set α} {a : α} (h_mono : MonotoneOn f s) (hs : s ∈ 𝓝[≥] a)
    (hfs : closure (f '' s) ∈ 𝓝[≥] f a) : ContinuousWithinAt f (Ici a) a := by
  refine' continuousWithinAt_right_of_monotoneOn_of_exists_between h_mono hs fun b hb => _
  rcases(mem_nhdsWithin_Ici_iff_exists_mem_Ioc_Ico_subset hb).1 hfs with ⟨b', ⟨hab', hbb'⟩, hb'⟩
  rcases exists_between hab' with ⟨c', hc'⟩
  rcases mem_closure_iff.1 (hb' ⟨hc'.1.le, hc'.2⟩) (Ioo (f a) b') isOpen_Ioo hc' with
    ⟨_, hc, ⟨c, hcs, rfl⟩⟩
  exact ⟨c, hcs, hc.1, hc.2.trans_le hbb'⟩
#align continuous_at_right_of_monotone_on_of_closure_image_mem_nhds_within continuousWithinAt_right_of_monotoneOn_of_closure_image_mem_nhdsWithin

/-- If a function `f` with a densely ordered codomain is monotone on a right neighborhood of `a` and
the image of this neighborhood under `f` is a right neighborhood of `f a`, then `f` is continuous at
`a` from the right. -/
theorem continuousWithinAt_right_of_monotoneOn_of_image_mem_nhdsWithin [DenselyOrdered β]
    {f : α → β} {s : Set α} {a : α} (h_mono : MonotoneOn f s) (hs : s ∈ 𝓝[≥] a)
    (hfs : f '' s ∈ 𝓝[≥] f a) : ContinuousWithinAt f (Ici a) a :=
  continuousWithinAt_right_of_monotoneOn_of_closure_image_mem_nhdsWithin h_mono hs <|
    mem_of_superset hfs subset_closure
#align continuous_at_right_of_monotone_on_of_image_mem_nhds_within continuousWithinAt_right_of_monotoneOn_of_image_mem_nhdsWithin

/-- If a function `f` with a densely ordered codomain is strictly monotone on a right neighborhood
of `a` and the closure of the image of this neighborhood under `f` is a right neighborhood of `f a`,
then `f` is continuous at `a` from the right. -/
theorem StrictMonoOn.continuousWithinAt_right_of_closure_image_mem_nhdsWithin [DenselyOrdered β]
    {f : α → β} {s : Set α} {a : α} (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≥] a)
    (hfs : closure (f '' s) ∈ 𝓝[≥] f a) : ContinuousWithinAt f (Ici a) a :=
  continuousWithinAt_right_of_monotoneOn_of_closure_image_mem_nhdsWithin
    (fun _ hx _ hy => (h_mono.le_iff_le hx hy).2) hs hfs
#align strict_mono_on.continuous_at_right_of_closure_image_mem_nhds_within StrictMonoOn.continuousWithinAt_right_of_closure_image_mem_nhdsWithin

/-- If a function `f` with a densely ordered codomain is strictly monotone on a right neighborhood
of `a` and the image of this neighborhood under `f` is a right neighborhood of `f a`, then `f` is
continuous at `a` from the right. -/
theorem StrictMonoOn.continuousWithinAt_right_of_image_mem_nhdsWithin [DenselyOrdered β] {f : α → β}
    {s : Set α} {a : α} (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≥] a) (hfs : f '' s ∈ 𝓝[≥] f a) :
    ContinuousWithinAt f (Ici a) a :=
  h_mono.continuousWithinAt_right_of_closure_image_mem_nhdsWithin hs
    (mem_of_superset hfs subset_closure)
#align strict_mono_on.continuous_at_right_of_image_mem_nhds_within StrictMonoOn.continuousWithinAt_right_of_image_mem_nhdsWithin

/-- If a function `f` is strictly monotone on a right neighborhood of `a` and the image of this
neighborhood under `f` includes `Ioi (f a)`, then `f` is continuous at `a` from the right. -/
theorem StrictMonoOn.continuousWithinAt_right_of_surjOn {f : α → β} {s : Set α} {a : α}
    (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≥] a) (hfs : SurjOn f s (Ioi (f a))) :
    ContinuousWithinAt f (Ici a) a :=
  h_mono.continuousWithinAt_right_of_exists_between hs fun _ hb =>
    let ⟨c, hcs, hcb⟩ := hfs hb
    ⟨c, hcs, hcb.symm ▸ hb, hcb.le⟩
#align strict_mono_on.continuous_at_right_of_surj_on StrictMonoOn.continuousWithinAt_right_of_surjOn

/-- If `f` is a strictly monotone function on a left neighborhood of `a` and the image of this
neighborhood under `f` meets every interval `[b, f a)`, `b < f a`, then `f` is continuous at `a`
from the left.

The assumption `hfs : ∀ b < f a, ∃ c ∈ s, f c ∈ Ico b (f a)` is required because otherwise the
function `f : ℝ → ℝ` given by `f x = if x < 0 then x else x + 1` would be a counter-example at
`a = 0`. -/
theorem StrictMonoOn.continuousWithinAt_left_of_exists_between {f : α → β} {s : Set α} {a : α}
    (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≤] a) (hfs : ∀ b < f a, ∃ c ∈ s, f c ∈ Ico b (f a)) :
    ContinuousWithinAt f (Iic a) a :=
  h_mono.dual.continuousWithinAt_right_of_exists_between hs fun b hb =>
    let ⟨c, hcs, hcb, hca⟩ := hfs b hb
    ⟨c, hcs, hca, hcb⟩
#align strict_mono_on.continuous_at_left_of_exists_between StrictMonoOn.continuousWithinAt_left_of_exists_between

/-- If `f` is a monotone function on a left neighborhood of `a` and the image of this neighborhood
under `f` meets every interval `(b, f a)`, `b < f a`, then `f` is continuous at `a` from the left.

The assumption `hfs : ∀ b < f a, ∃ c ∈ s, f c ∈ Ioo b (f a)` cannot be replaced by the weaker
assumption `hfs : ∀ b < f a, ∃ c ∈ s, f c ∈ Ico b (f a)` we use for strictly monotone functions
because otherwise the function `floor : ℝ → ℤ` would be a counter-example at `a = 0`. -/
theorem continuousWithinAt_left_of_monotoneOn_of_exists_between {f : α → β} {s : Set α} {a : α}
    (hf : MonotoneOn f s) (hs : s ∈ 𝓝[≤] a) (hfs : ∀ b < f a, ∃ c ∈ s, f c ∈ Ioo b (f a)) :
    ContinuousWithinAt f (Iic a) a :=
  @continuousWithinAt_right_of_monotoneOn_of_exists_between αᵒᵈ βᵒᵈ _ _ _ _ _ _ f s a hf.dual hs
    fun b hb =>
    let ⟨c, hcs, hcb, hca⟩ := hfs b hb
    ⟨c, hcs, hca, hcb⟩
#align continuous_at_left_of_monotone_on_of_exists_between continuousWithinAt_left_of_monotoneOn_of_exists_between

/-- If a function `f` with a densely ordered codomain is monotone on a left neighborhood of `a` and
the closure of the image of this neighborhood under `f` is a left neighborhood of `f a`, then `f` is
continuous at `a` from the left -/
theorem continuousWithinAt_left_of_monotoneOn_of_closure_image_mem_nhdsWithin [DenselyOrdered β]
    {f : α → β} {s : Set α} {a : α} (hf : MonotoneOn f s) (hs : s ∈ 𝓝[≤] a)
    (hfs : closure (f '' s) ∈ 𝓝[≤] f a) : ContinuousWithinAt f (Iic a) a :=
  @continuousWithinAt_right_of_monotoneOn_of_closure_image_mem_nhdsWithin αᵒᵈ βᵒᵈ _ _ _ _ _ _ _ f s
    a hf.dual hs hfs
#align continuous_at_left_of_monotone_on_of_closure_image_mem_nhds_within continuousWithinAt_left_of_monotoneOn_of_closure_image_mem_nhdsWithin

/-- If a function `f` with a densely ordered codomain is monotone on a left neighborhood of `a` and
the image of this neighborhood under `f` is a left neighborhood of `f a`, then `f` is continuous at
`a` from the left. -/
theorem continuousWithinAt_left_of_monotoneOn_of_image_mem_nhdsWithin [DenselyOrdered β] {f : α → β}
    {s : Set α} {a : α} (h_mono : MonotoneOn f s) (hs : s ∈ 𝓝[≤] a) (hfs : f '' s ∈ 𝓝[≤] f a) :
    ContinuousWithinAt f (Iic a) a :=
  continuousWithinAt_left_of_monotoneOn_of_closure_image_mem_nhdsWithin h_mono hs
    (mem_of_superset hfs subset_closure)
#align continuous_at_left_of_monotone_on_of_image_mem_nhds_within continuousWithinAt_left_of_monotoneOn_of_image_mem_nhdsWithin

/-- If a function `f` with a densely ordered codomain is strictly monotone on a left neighborhood of
`a` and the closure of the image of this neighborhood under `f` is a left neighborhood of `f a`,
then `f` is continuous at `a` from the left. -/
theorem StrictMonoOn.continuousWithinAt_left_of_closure_image_mem_nhdsWithin [DenselyOrdered β]
    {f : α → β} {s : Set α} {a : α} (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≤] a)
    (hfs : closure (f '' s) ∈ 𝓝[≤] f a) : ContinuousWithinAt f (Iic a) a :=
  h_mono.dual.continuousWithinAt_right_of_closure_image_mem_nhdsWithin hs hfs
#align strict_mono_on.continuous_at_left_of_closure_image_mem_nhds_within StrictMonoOn.continuousWithinAt_left_of_closure_image_mem_nhdsWithin

/-- If a function `f` with a densely ordered codomain is strictly monotone on a left neighborhood of
`a` and the image of this neighborhood under `f` is a left neighborhood of `f a`, then `f` is
continuous at `a` from the left. -/
theorem StrictMonoOn.continuousWithinAt_left_of_image_mem_nhdsWithin [DenselyOrdered β] {f : α → β}
    {s : Set α} {a : α} (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≤] a) (hfs : f '' s ∈ 𝓝[≤] f a) :
    ContinuousWithinAt f (Iic a) a :=
  h_mono.dual.continuousWithinAt_right_of_image_mem_nhdsWithin hs hfs
#align strict_mono_on.continuous_at_left_of_image_mem_nhds_within StrictMonoOn.continuousWithinAt_left_of_image_mem_nhdsWithin

/-- If a function `f` is strictly monotone on a left neighborhood of `a` and the image of this
neighborhood under `f` includes `Iio (f a)`, then `f` is continuous at `a` from the left. -/
theorem StrictMonoOn.continuousWithinAt_left_of_surjOn {f : α → β} {s : Set α} {a : α}
    (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝[≤] a) (hfs : SurjOn f s (Iio (f a))) :
    ContinuousWithinAt f (Iic a) a :=
  h_mono.dual.continuousWithinAt_right_of_surjOn hs hfs
#align strict_mono_on.continuous_at_left_of_surj_on StrictMonoOn.continuousWithinAt_left_of_surjOn

/-- If a function `f` is strictly monotone on a neighborhood of `a` and the image of this
neighborhood under `f` meets every interval `[b, f a)`, `b < f a`, and every interval
`(f a, b]`, `b > f a`, then `f` is continuous at `a`. -/
theorem StrictMonoOn.continuousAt_of_exists_between {f : α → β} {s : Set α} {a : α}
    (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝 a) (hfs_l : ∀ b < f a, ∃ c ∈ s, f c ∈ Ico b (f a))
    (hfs_r : ∀ b > f a, ∃ c ∈ s, f c ∈ Ioc (f a) b) : ContinuousAt f a :=
  continuousAt_iff_continuous_left_right.2
    ⟨h_mono.continuousWithinAt_left_of_exists_between (mem_nhdsWithin_of_mem_nhds hs) hfs_l,
      h_mono.continuousWithinAt_right_of_exists_between (mem_nhdsWithin_of_mem_nhds hs) hfs_r⟩
#align strict_mono_on.continuous_at_of_exists_between StrictMonoOn.continuousAt_of_exists_between

/-- If a function `f` with a densely ordered codomain is strictly monotone on a neighborhood of `a`
and the closure of the image of this neighborhood under `f` is a neighborhood of `f a`, then `f` is
continuous at `a`. -/
theorem StrictMonoOn.continuousAt_of_closure_image_mem_nhds [DenselyOrdered β] {f : α → β}
    {s : Set α} {a : α} (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝 a)
    (hfs : closure (f '' s) ∈ 𝓝 (f a)) : ContinuousAt f a :=
  continuousAt_iff_continuous_left_right.2
    ⟨h_mono.continuousWithinAt_left_of_closure_image_mem_nhdsWithin (mem_nhdsWithin_of_mem_nhds hs)
        (mem_nhdsWithin_of_mem_nhds hfs),
      h_mono.continuousWithinAt_right_of_closure_image_mem_nhdsWithin
        (mem_nhdsWithin_of_mem_nhds hs) (mem_nhdsWithin_of_mem_nhds hfs)⟩
#align strict_mono_on.continuous_at_of_closure_image_mem_nhds StrictMonoOn.continuousAt_of_closure_image_mem_nhds

/-- If a function `f` with a densely ordered codomain is strictly monotone on a neighborhood of `a`
and the image of this set under `f` is a neighborhood of `f a`, then `f` is continuous at `a`. -/
theorem StrictMonoOn.continuousAt_of_image_mem_nhds [DenselyOrdered β] {f : α → β} {s : Set α}
    {a : α} (h_mono : StrictMonoOn f s) (hs : s ∈ 𝓝 a) (hfs : f '' s ∈ 𝓝 (f a)) :
    ContinuousAt f a :=
  h_mono.continuousAt_of_closure_image_mem_nhds hs (mem_of_superset hfs subset_closure)
#align strict_mono_on.continuous_at_of_image_mem_nhds StrictMonoOn.continuousAt_of_image_mem_nhds

/-- If `f` is a monotone function on a neighborhood of `a` and the image of this neighborhood under
`f` meets every interval `(b, f a)`, `b < f a`, and every interval `(f a, b)`, `b > f a`, then `f`
is continuous at `a`. -/
theorem continuousAt_of_monotoneOn_of_exists_between {f : α → β} {s : Set α} {a : α}
    (h_mono : MonotoneOn f s) (hs : s ∈ 𝓝 a) (hfs_l : ∀ b < f a, ∃ c ∈ s, f c ∈ Ioo b (f a))
    (hfs_r : ∀ b > f a, ∃ c ∈ s, f c ∈ Ioo (f a) b) : ContinuousAt f a :=
  continuousAt_iff_continuous_left_right.2
    ⟨continuousWithinAt_left_of_monotoneOn_of_exists_between h_mono (mem_nhdsWithin_of_mem_nhds hs)
        hfs_l,
      continuousWithinAt_right_of_monotoneOn_of_exists_between h_mono
        (mem_nhdsWithin_of_mem_nhds hs) hfs_r⟩
#align continuous_at_of_monotone_on_of_exists_between continuousAt_of_monotoneOn_of_exists_between

/-- If a function `f` with a densely ordered codomain is monotone on a neighborhood of `a` and the
closure of the image of this neighborhood under `f` is a neighborhood of `f a`, then `f` is
continuous at `a`. -/
theorem continuousAt_of_monotoneOn_of_closure_image_mem_nhds [DenselyOrdered β] {f : α → β}
    {s : Set α} {a : α} (h_mono : MonotoneOn f s) (hs : s ∈ 𝓝 a)
    (hfs : closure (f '' s) ∈ 𝓝 (f a)) : ContinuousAt f a :=
  continuousAt_iff_continuous_left_right.2
    ⟨continuousWithinAt_left_of_monotoneOn_of_closure_image_mem_nhdsWithin h_mono
        (mem_nhdsWithin_of_mem_nhds hs) (mem_nhdsWithin_of_mem_nhds hfs),
      continuousWithinAt_right_of_monotoneOn_of_closure_image_mem_nhdsWithin h_mono
        (mem_nhdsWithin_of_mem_nhds hs) (mem_nhdsWithin_of_mem_nhds hfs)⟩
#align continuous_at_of_monotone_on_of_closure_image_mem_nhds continuousAt_of_monotoneOn_of_closure_image_mem_nhds

/-- If a function `f` with a densely ordered codomain is monotone on a neighborhood of `a` and the
image of this neighborhood under `f` is a neighborhood of `f a`, then `f` is continuous at `a`. -/
theorem continuousAt_of_monotoneOn_of_image_mem_nhds [DenselyOrdered β] {f : α → β} {s : Set α}
    {a : α} (h_mono : MonotoneOn f s) (hs : s ∈ 𝓝 a) (hfs : f '' s ∈ 𝓝 (f a)) : ContinuousAt f a :=
  continuousAt_of_monotoneOn_of_closure_image_mem_nhds h_mono hs
    (mem_of_superset hfs subset_closure)
#align continuous_at_of_monotone_on_of_image_mem_nhds continuousAt_of_monotoneOn_of_image_mem_nhds

/-- A monotone function with densely ordered codomain and a dense range is continuous. -/
theorem Monotone.continuous_of_denseRange [DenselyOrdered β] {f : α → β} (h_mono : Monotone f)
    (h_dense : DenseRange f) : Continuous f :=
  continuous_iff_continuousAt.mpr fun a =>
    continuousAt_of_monotoneOn_of_closure_image_mem_nhds (fun x _ y _ hxy => h_mono hxy)
        univ_mem <|
      by simp only [image_univ, h_dense.closure_eq, univ_mem]
#align monotone.continuous_of_dense_range Monotone.continuous_of_denseRange

/-- A monotone surjective function with a densely ordered codomain is continuous. -/
theorem Monotone.continuous_of_surjective [DenselyOrdered β] {f : α → β} (h_mono : Monotone f)
    (h_surj : Function.Surjective f) : Continuous f :=
  h_mono.continuous_of_denseRange h_surj.denseRange
#align monotone.continuous_of_surjective Monotone.continuous_of_surjective

end LinearOrder

/-!
### Continuity of order isomorphisms

In this section we prove that an `OrderIso` is continuous, hence it is a `Homeomorph`. We prove
this for an `OrderIso` between to partial orders with order topology.
-/

namespace OrderIso

variable {α β : Type _} [PartialOrder α] [PartialOrder β] [TopologicalSpace α] [TopologicalSpace β]
  [OrderTopology α] [OrderTopology β]

protected theorem continuous (e : α ≃o β) : Continuous e := by
  rw [‹OrderTopology β›.topology_eq_generate_intervals]
  refine' continuous_generateFrom fun s hs => _
  rcases hs with ⟨a, rfl | rfl⟩
  · rw [e.preimage_Ioi]
    apply isOpen_lt'
  · rw [e.preimage_Iio]
    apply isOpen_gt'
#align order_iso.continuous OrderIso.continuous

/-- An order isomorphism between two linear order `OrderTopology` spaces is a homeomorphism. -/
def toHomeomorph (e : α ≃o β) : α ≃ₜ β :=
  { e with
    continuous_toFun := e.continuous
    continuous_invFun := e.symm.continuous }
#align order_iso.to_homeomorph OrderIso.toHomeomorph

@[simp]
theorem coe_toHomeomorph (e : α ≃o β) : ⇑e.toHomeomorph = e :=
  rfl
#align order_iso.coe_to_homeomorph OrderIso.coe_toHomeomorph

@[simp]
theorem coe_toHomeomorph_symm (e : α ≃o β) : ⇑e.toHomeomorph.symm = e.symm :=
  rfl
#align order_iso.coe_to_homeomorph_symm OrderIso.coe_toHomeomorph_symm

end OrderIso


import algebra.category.Group.abelian
import category_theory.abelian.homology

import for_mathlib.homology_exact
import for_mathlib.homology_map
import for_mathlib.has_homology
import for_mathlib.AddCommGroup.exact

noncomputable theory

universes v u

open category_theory

@[to_additive]
lemma subgroup.comap_eq_iff {A B : Type*} [comm_group A] [comm_group B] (f : A ≃* B)
  (C : subgroup A) (D : subgroup B) :
  D.comap f.to_monoid_hom = C ↔ C.comap f.symm.to_monoid_hom = D :=
begin
  split; rintro rfl; ext,
  { simp only [subgroup.mem_comap, mul_equiv.coe_to_monoid_hom, mul_equiv.apply_symm_apply], },
  { simp only [subgroup.mem_comap, mul_equiv.coe_to_monoid_hom, mul_equiv.symm_apply_apply], }
end

@[to_additive] noncomputable
def mul_equiv.surjective_congr {A B C D : Type*}
  [comm_group A] [comm_group B] [comm_group C] [comm_group D]
  (e : A ≃* B) (f : A →* C) (g : B →* D)
  (hf : function.surjective f) (hg : function.surjective g)
  (he : g.ker.comap e.to_monoid_hom = f.ker) :
C ≃* D :=
{ to_fun := f.lift_of_surjective hf ⟨g.comp e.to_monoid_hom, λ x hx,
  by { rw ← he at hx, exact hx }⟩,
  inv_fun := g.lift_of_surjective hg ⟨f.comp e.symm.to_monoid_hom, λ x hx,
  by { rw subgroup.comap_eq_iff at he, rw ← he at hx, exact hx, }⟩,
  left_inv := λ x, begin
    obtain ⟨x, rfl⟩ := hf x,
    delta monoid_hom.lift_of_surjective,
    simp only [monoid_hom.lift_of_right_inverse_comp_apply, subtype.coe_mk, monoid_hom.comp_apply],
    convert monoid_hom.lift_of_right_inverse_comp_apply _ _ _ _ (e.to_monoid_hom x) using 1,
    exact f.congr_arg (e.symm_apply_apply x).symm,
  end,
  right_inv := λ x, begin
    obtain ⟨x, rfl⟩ := hg x,
    delta monoid_hom.lift_of_surjective,
    simp only [monoid_hom.lift_of_right_inverse_comp_apply, subtype.coe_mk, monoid_hom.comp_apply],
    convert monoid_hom.lift_of_right_inverse_comp_apply _ _ _ _ (e.symm.to_monoid_hom x) using 1,
    exact g.congr_arg (e.apply_symm_apply x).symm,
  end,
  map_mul' := λ x y, monoid_hom.map_mul _ _ _ }

@[to_additive]
def mul_equiv.quotient_congr {A B : Type*} [comm_group A] [comm_group B] (f : A ≃* B)
  (C : subgroup A) (D : subgroup B) (h : D.comap f.to_monoid_hom = C) :
A ⧸ C ≃* B ⧸ D :=
{ to_fun := quotient_group.map _ _ f.to_monoid_hom h.ge,
  inv_fun := quotient_group.map _ _ f.symm.to_monoid_hom
  begin
    refine le_of_eq _, subst h, ext,
    simp only [subgroup.mem_comap, mul_equiv.coe_to_monoid_hom, mul_equiv.apply_symm_apply],
  end,
  left_inv := λ x, begin
    induction x using quotient_group.induction_on,
    erw [quotient_group.map_coe, f.symm_apply_apply],
    refl,
  end,
  right_inv := λ x, begin
    induction x using quotient_group.induction_on,
    erw [quotient_group.map_coe, f.apply_symm_apply],
    refl,
  end,
  map_mul' := λ x y, monoid_hom.map_mul _ _ _ }
.

namespace AddCommGroup

open category_theory.limits

-- move this
attribute [elementwise] AddCommGroup.kernel_iso_ker_hom_comp_subtype AddCommGroup.kernel_iso_ker_inv_comp_ι

variables {A B C : AddCommGroup.{u}} (f : A ⟶ B) (g : B ⟶ C) (w : f ≫ g = 0)

protected noncomputable
def homology {A B C : AddCommGroup.{u}} (f : A ⟶ B) (g : B ⟶ C) : AddCommGroup :=
AddCommGroup.of (g.ker ⧸ (f.range.comap g.ker.subtype))

def has_homology : has_homology f g (AddCommGroup.homology f g) :=
{ w := w,
  π := (AddCommGroup.kernel_iso_ker _).hom ≫ of_hom (quotient_add_group.mk' _),
  ι := quotient_add_group.lift _ ((cokernel.π f).comp $ add_subgroup.subtype _) begin
    rintro ⟨y, hx : g y = 0⟩ ⟨x, rfl : f x = y⟩,
    dsimp only [add_monoid_hom.comp_apply, quotient_add_group.mk'_apply, subtype.coe_mk,
      add_subgroup.coe_subtype],
    rw [← comp_apply, cokernel.condition, zero_apply],
  end,
  π_ι := by { rw [← kernel_iso_ker_hom_comp_subtype], refl },
  ex_π := begin
    rw [← exact_comp_hom_inv_comp_iff (kernel_iso_ker g), iso.inv_hom_id_assoc],
    rw AddCommGroup.exact_iff',
    split,
    { ext, simp, },
    { rintros ⟨x, hx : _ = _⟩ (hh : _ = _),
      dsimp at hh,
      rw quotient_add_group.eq_zero_iff at hh,
      obtain ⟨t,ht⟩ := hh,
      use t,
      ext,
      simp_rw comp_apply,
      dsimp [kernel_iso_ker],
      rw [← comp_apply, kernel.lift_ι, ht],
      refl }
  end,
  ι_ex := begin
    rw AddCommGroup.exact_iff',
    split,
    { ext ⟨t⟩,
      simpa only [comp_apply, add_monoid_hom.coe_comp, quotient_add_group.coe_mk',
        function.comp_app, quotient_add_group.lift_mk, add_subgroup.coe_subtype,
        add_subgroup.coe_mk, cokernel.π_desc_apply, add_monoid_hom.zero_comp,
        add_monoid_hom.zero_apply] },
    { rintros x (hx : _ = _),
      change ∃ e, _,
      have : function.surjective (cokernel.π f) := surjective_of_epi (cokernel.π f),
      obtain ⟨y,rfl⟩ := this x,
      rw [← comp_apply, cokernel.π_desc] at hx,
      let yy : g.ker := ⟨y,hx⟩,
      use quotient_add_group.mk yy,
      simp only [quotient_add_group.lift_mk', add_monoid_hom.coe_comp, add_subgroup.coe_subtype,
        function.comp_app, subtype.coe_mk] }
  end,
  epi_π := begin
    apply_with epi_comp {instances:=ff}, { apply_instance },
    rw AddCommGroup.epi_iff_surjective, exact quotient_add_group.mk'_surjective _
  end,
  mono_ι := begin
    rw [AddCommGroup.mono_iff_injective, injective_iff_map_eq_zero],
    intros y hy,
    obtain ⟨⟨y, hy'⟩, rfl⟩ := quotient_add_group.mk'_surjective _ y,
    rw [quotient_add_group.mk'_apply, quotient_add_group.eq_zero_iff],
    rw [quotient_add_group.mk'_apply, quotient_add_group.lift_mk] at hy,
    rw add_subgroup.mem_comap,
    dsimp at hy ⊢,
    have : exact f (cokernel.π f) := abelian.exact_cokernel f,
    rw AddCommGroup.exact_iff' at this,
    exact this.2 hy,
  end }

protected noncomputable
def homology_iso {A B C : AddCommGroup.{u}} (f : A ⟶ B) (g : B ⟶ C) (w : f ≫ g = 0) :
  homology f g w ≅ AddCommGroup.of (g.ker ⧸ (f.range.comap g.ker.subtype)) :=
(homology.has f g w).iso (has_homology f g w)
-- begin
--   refine homology_iso_cokernel_lift f g w ≪≫
--     AddCommGroup.cokernel_iso_quotient _ ≪≫
--     add_equiv_iso_AddCommGroup_iso.hom _,
--   refine add_equiv.quotient_congr _ _ _ _,
--   { exact add_equiv_iso_AddCommGroup_iso.inv (AddCommGroup.kernel_iso_ker _) },
--   rw add_subgroup.comap_eq_iff,
--   show add_subgroup.comap (AddCommGroup.kernel_iso_ker g).inv
--     (add_monoid_hom.range (limits.kernel.lift g f w)) =
--     add_subgroup.comap (add_monoid_hom.ker g).subtype (add_monoid_hom.range f),
--   dsimp only [AddCommGroup.kernel_iso_ker],
--   have : function.injective (λ x, limits.kernel.ι g x),
--   { rw [← AddCommGroup.kernel_iso_ker_hom_comp_subtype, coe_comp],
--     have : function.injective (g.ker.subtype) := subtype.val_injective,
--     refine this.comp _,
--     refine function.has_left_inverse.injective _,
--     refine ⟨(AddCommGroup.kernel_iso_ker _).inv, _⟩,
--     intro y, refine category_theory.iso.hom_inv_id_apply (AddCommGroup.kernel_iso_ker g) y, },
--   ext ⟨x, hx⟩,
--   simp only [add_subgroup.mem_comap, add_monoid_hom.mem_range, add_subgroup.coe_subtype,
--     subtype.coe_mk, ← this.eq_iff, category_theory.limits.kernel.lift_ι_apply],
-- end
.

-- lemma homology_iso_hom_eq {A B C : AddCommGroup.{u}} (f : A ⟶ B) (g : B ⟶ C) (w : f ≫ g = 0) :
--   (AddCommGroup.homology_iso f g w).hom =
--   homology.desc' _ _ _ ((AddCommGroup.kernel_iso_ker _).hom ≫ quotient_add_group.mk' _)
--   (by { ext x, simp only [comp_apply, quotient_add_group.mk'_apply, zero_apply, quotient_add_group.eq_zero_iff], refine ⟨x, _⟩, rw [kernel_iso_ker_hom_comp_subtype_apply, ← comp_apply, limits.kernel.lift_ι], }) :=
-- begin
--   ext1,
--   dsimp only [AddCommGroup.homology_iso, iso.trans_hom],
--   rw [homology.π'_desc'],
--   admit
-- end

variables {A₁ B₁ C₁ A₂ B₂ C₂ : AddCommGroup.{u}}
variables {f₁ : A₁ ⟶ B₁} {g₁ : B₁ ⟶ C₁} (w₁ : f₁ ≫ g₁ = 0)
variables {f₂ : A₂ ⟶ B₂} {g₂ : B₂ ⟶ C₂} (w₂ : f₂ ≫ g₂ = 0)
variables {α : A₁ ⟶ A₂} {β : B₁ ⟶ B₂} {γ : C₁ ⟶ C₂}
variables (sq1 : commsq f₁ α β f₂) (sq2 : commsq g₁ β γ g₂)

include sq1

@[simps apply]
def ker_map : ↥(add_monoid_hom.ker f₁) →+ ↥(add_monoid_hom.ker f₂) :=
add_monoid_hom.mk' (λ x, ⟨α x, by { rw [f₂.mem_ker, ← comp_apply, ← sq1.w, comp_apply], convert β.map_zero, exact x.2 }⟩) $
by { rintro ⟨a, _⟩ ⟨b, _⟩, ext, apply α.map_add, }

include sq2

noncomputable
def homology_map : AddCommGroup.homology f₁ g₁ ⟶ AddCommGroup.homology f₂ g₂ :=
(has_homology f₁ g₁ w₁).map (has_homology f₂ g₂ w₂) sq1 sq2

@[simp]
lemma homology_map_apply_mk (t) :
  homology_map w₁ w₂ sq1 sq2 (quotient_add_group.mk t) =
  quotient_add_group.mk (ker_map sq2 t) :=
begin
  dsimp [homology_map, has_homology.map, has_homology.desc],
  generalize_proofs _ h _ _ h1 h2,
  have := h.comp_epi_desc
    ((has_homology f₂ g₂ w₂).lift (kernel.ι g₁ ≫ β ≫ cokernel.π f₂) h1) h2,
  let e : kernel g₁ ≅ AddCommGroup.of (add_monoid_hom.ker g₁) := AddCommGroup.kernel_iso_ker _,
  let s := e.inv t,
  let p := (has_homology f₁ g₁ w₁).π,
  have ht : quotient_add_group.mk t = p s,
  { dsimp [s, p], rw ← comp_apply,
    let pp : AddCommGroup.of g₁.ker ⟶ AddCommGroup.homology f₁ g₁ :=
      quotient_add_group.mk' _,
    suffices : pp = e.inv ≫ p, rw ← this, refl,
    rw iso.eq_inv_comp,
    ext t, refl },
  rw [ht, ← comp_apply, this],
  dsimp [s],
  rw ← comp_apply,
  dsimp [e, has_homology.desc, has_homology.lift],
  generalize_proofs hh,
  let q := (kernel_iso_ker g₁).inv ≫ hh.mono_lift (kernel.ι g₁ ≫ β ≫ cokernel.π f₂) h1,
  let pp : AddCommGroup.of g₂.ker ⟶ AddCommGroup.homology f₂ g₂ :=
    quotient_add_group.mk' _,
  let tt : AddCommGroup.of g₁.ker ⟶ AddCommGroup.of g₂.ker :=
    ker_map sq2,
  have hq : (kernel_iso_ker g₁).inv ≫ hh.mono_lift (kernel.ι g₁ ≫ β ≫ cokernel.π f₂) h1 =
    tt ≫ pp,
  { rw iso.inv_comp_eq,
    have := hh.mono_lift_comp (kernel.ι g₁ ≫ β ≫ cokernel.π f₂) h1,
    rw [← cancel_mono (has_homology f₂ g₂ w₂).ι, this],
    ext, refl } ,
  rw hq, refl,
end

noncomputable
def homology_iso_hom_homology_map :
  (AddCommGroup.homology_iso f₁ g₁ w₁).hom ≫ homology_map w₁ w₂ sq1 sq2 =
  homology.map' w₁ w₂ sq1 sq2 ≫ (AddCommGroup.homology_iso f₂ g₂ w₂).hom :=
begin
  erw has_homology.map_comp_map,
  erw has_homology.map_comp_map,
  refl,
end

noncomputable
def homology_iso_inv_homology_map :
  (AddCommGroup.homology_iso f₁ g₁ w₁).inv ≫ homology.map' w₁ w₂ sq1 sq2 =
  homology_map w₁ w₂ sq1 sq2 ≫ (AddCommGroup.homology_iso f₂ g₂ w₂).inv :=
by rw [iso.inv_comp_eq, ← category.assoc, iso.eq_comp_inv, homology_iso_hom_homology_map]

end AddCommGroup


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.algebra.module
import Mathlib.topology.instances.real
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Continuous additive maps are `ℝ`-linear

In this file we prove that a continuous map `f : E →+ F` between two topological vector spaces
over `ℝ` is `ℝ`-linear
-/

namespace add_monoid_hom

/-- A continuous additive map between two vector spaces over `ℝ` is `ℝ`-linear. -/
theorem map_real_smul {E : Type u_1} [add_comm_group E] [vector_space ℝ E] [topological_space E] [topological_vector_space ℝ E] {F : Type u_2} [add_comm_group F] [vector_space ℝ F] [topological_space F] [topological_vector_space ℝ F] [t2_space F] (f : E →+ F) (hf : continuous ⇑f) (c : ℝ) (x : E) : coe_fn f (c • x) = c • coe_fn f x := sorry

/-- Reinterpret a continuous additive homomorphism between two real vector spaces
as a continuous real-linear map. -/
def to_real_linear_map {E : Type u_1} [add_comm_group E] [vector_space ℝ E] [topological_space E] [topological_vector_space ℝ E] {F : Type u_2} [add_comm_group F] [vector_space ℝ F] [topological_space F] [topological_vector_space ℝ F] [t2_space F] (f : E →+ F) (hf : continuous ⇑f) : continuous_linear_map ℝ E F :=
  continuous_linear_map.mk (linear_map.mk ⇑f sorry (map_real_smul f hf))

@[simp] theorem coe_to_real_linear_map {E : Type u_1} [add_comm_group E] [vector_space ℝ E] [topological_space E] [topological_vector_space ℝ E] {F : Type u_2} [add_comm_group F] [vector_space ℝ F] [topological_space F] [topological_vector_space ℝ F] [t2_space F] (f : E →+ F) (hf : continuous ⇑f) : ⇑(to_real_linear_map f hf) = ⇑f :=
  rfl


import data.real.basic

open nat

#check add_succ

example (n : ℕ) : 0 + n = n :=
begin
  induction n with n ih,
    norm_num,
  rw add_succ,
  rw ih,
end


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

Cofinality on ordinals, regular cardinals.
-/
import set_theory.ordinal
noncomputable theory

open function cardinal
local attribute [instance] classical.prop_decidable

universes u v w
variables {α : Type*} {r : α → α → Prop}

/-- Cofinality of a reflexive order `≼`. This is the smallest cardinality
  of a subset `S : set α` such that `∀ a, ∃ b ∈ S, a ≼ b`. -/
def order.cof (r : α → α → Prop) [is_refl α r] : cardinal :=
@cardinal.min {S : set α // ∀ a, ∃ b ∈ S, r a b}
  ⟨⟨set.univ, λ a, ⟨a, ⟨⟩, refl _⟩⟩⟩
  (λ S, mk S)

theorem order_iso.cof.aux {α : Type u} {β : Type v} {r s}
  [is_refl α r] [is_refl β s] (f : r ≃o s) :
  cardinal.lift.{u (max u v)} (order.cof r) ≤
  cardinal.lift.{v (max u v)} (order.cof s) :=
begin
  rw [order.cof, order.cof, lift_min, lift_min, cardinal.le_min],
  intro S, cases S with S H, simp [(∘)],
  refine le_trans (min_le _ _) _,
  { exact ⟨f ⁻¹' S, λ a,
    let ⟨b, bS, h⟩ := H (f a) in ⟨f.symm b, by simp [bS, f.ord', h,
      -coe_fn_coe_base, -coe_fn_coe_trans, principal_seg.coe_coe_fn', initial_seg.coe_coe_fn]⟩⟩ },
  { exact lift_mk_le.{u v (max u v)}.2
    ⟨⟨λ ⟨x, h⟩, ⟨f x, h⟩, λ ⟨x, h₁⟩ ⟨y, h₂⟩ h₃,
      by congr; injection h₃ with h'; exact f.to_equiv.injective h'⟩⟩ }
end

theorem order_iso.cof {α : Type u} {β : Type v} {r s}
  [is_refl α r] [is_refl β s] (f : r ≃o s) :
  cardinal.lift.{u (max u v)} (order.cof r) =
  cardinal.lift.{v (max u v)} (order.cof s) :=
le_antisymm (order_iso.cof.aux f) (order_iso.cof.aux f.symm)

namespace ordinal

/-- Cofinality of an ordinal. This is the smallest cardinal of a
  subset `S` of the ordinal which is unbounded, in the sense
  `∀ a, ∃ b ∈ S, a ≤ b`. It is defined for all ordinals, but
  `cof 0 = 0` and `cof (succ o) = 1`, so it is only really
  interesting on limit ordinals (when it is an infinite cardinal). -/
def cof (o : ordinal.{u}) : cardinal.{u} :=
quot.lift_on o (λ ⟨α, r, _⟩,
  @order.cof α (λ x y, ¬ r y x) ⟨λ a, by resetI; apply irrefl⟩) $
λ ⟨α, r, _⟩ ⟨β, s, _⟩ ⟨⟨f, hf⟩⟩, begin
  show @order.cof α (λ x y, ¬ r y x) ⟨_⟩ = @order.cof β (λ x y, ¬ s y x) ⟨_⟩,
  refine cardinal.lift_inj.1 (@order_iso.cof _ _ _ _ ⟨_⟩ ⟨_⟩ _),
  exact ⟨f, λ a b, not_congr hf⟩,
end

theorem le_cof_type [is_well_order α r] {c} : c ≤ cof (type r) ↔
  ∀ S : set α, (∀ a, ∃ b ∈ S, ¬ r b a) → c ≤ mk S :=
by dsimp [cof, order.cof, type, quotient.mk, quot.lift_on];
   rw [cardinal.le_min, subtype.forall]; refl

theorem cof_type_le [is_well_order α r] (S : set α) (h : ∀ a, ∃ b ∈ S, ¬ r b a) :
  cof (type r) ≤ mk S :=
le_cof_type.1 (le_refl _) S h

theorem lt_cof_type [is_well_order α r] (S : set α) (hl : mk S < cof (type r)) :
  ∃ a, ∀ b ∈ S, r b a :=
not_forall_not.1 $ λ h, not_le_of_lt hl $ cof_type_le S (λ a, not_ball.1 (h a))

theorem cof_eq (r : α → α → Prop) [is_well_order α r] :
  ∃ S : set α, (∀ a, ∃ b ∈ S, ¬ r b a) ∧ mk S = cof (type r) :=
begin
  have : ∃ i, cof (type r) = _,
  { dsimp [cof, order.cof, type, quotient.mk, quot.lift_on],
    apply cardinal.min_eq },
  exact let ⟨⟨S, hl⟩, e⟩ := this in ⟨S, hl, e.symm⟩,
end

theorem ord_cof_eq (r : α → α → Prop) [is_well_order α r] :
  ∃ S : set α, (∀ a, ∃ b ∈ S, ¬ r b a) ∧ type (subrel r S) = (cof (type r)).ord :=
let ⟨S, hS, e⟩ := cof_eq r, ⟨s, _, e'⟩ := cardinal.ord_eq S,
    T : set α := {a | ∃ aS : a ∈ S, ∀ b : S, s b ⟨_, aS⟩ → r b a} in
begin
  resetI, suffices,
  { refine ⟨T, this,
      le_antisymm _ (cardinal.ord_le.2 $ cof_type_le T this)⟩,
    rw [← e, e'],
    refine type_le'.2 ⟨order_embedding.of_monotone
      (λ a, ⟨a, let ⟨aS, _⟩ := a.2 in aS⟩) (λ a b h, _)⟩,
    rcases a with ⟨a, aS, ha⟩, rcases b with ⟨b, bS, hb⟩,
    change s ⟨a, _⟩ ⟨b, _⟩,
    refine ((trichotomous_of s _ _).resolve_left (λ hn, _)).resolve_left _,
    { exact asymm h (ha _ hn) },
    { intro e, injection e with e, subst b,
      exact irrefl _ h } },
  { intro a,
    have : {b : S | ¬ r b a} ≠ ∅ := let ⟨b, bS, ba⟩ := hS a in
      @set.ne_empty_of_mem S {b | ¬ r b a} ⟨b, bS⟩ ba,
    let b := (is_well_order.wf s).min _ this,
    have ba : ¬r b a := (is_well_order.wf s).min_mem _ this,
    refine ⟨b, ⟨b.2, λ c, not_imp_not.1 $ λ h, _⟩, ba⟩,
    rw [show ∀b:S, (⟨b, b.2⟩:S) = b, by intro b; cases b; refl],
    exact (is_well_order.wf s).not_lt_min _ this
      (is_order_connected.neg_trans h ba) }
end

theorem lift_cof (o) : (cof o).lift = cof o.lift :=
induction_on o $ begin introsI α r _,
  cases lift_type r with _ e, rw e,
  apply le_antisymm,
  { refine le_cof_type.2 (λ S H, _),
    have : (mk (ulift.up ⁻¹' S)).lift ≤ mk S :=
     ⟨⟨λ ⟨⟨x, h⟩⟩, ⟨⟨x⟩, h⟩,
       λ ⟨⟨x, h₁⟩⟩ ⟨⟨y, h₂⟩⟩ e, by simp at e; congr; injection e⟩⟩,
    refine le_trans (cardinal.lift_le.2 $ cof_type_le _ _) this,
    exact λ a, let ⟨⟨b⟩, bs, br⟩ := H ⟨a⟩ in ⟨b, bs, br⟩ },
  { rcases cof_eq r with ⟨S, H, e'⟩,
    have : mk (ulift.down ⁻¹' S) ≤ (mk S).lift :=
     ⟨⟨λ ⟨⟨x⟩, h⟩, ⟨⟨x, h⟩⟩,
       λ ⟨⟨x⟩, h₁⟩ ⟨⟨y⟩, h₂⟩ e, by simp at e; congr; injections⟩⟩,
    rw e' at this,
    refine le_trans (cof_type_le _ _) this,
    exact λ ⟨a⟩, let ⟨b, bs, br⟩ := H a in ⟨⟨b⟩, bs, br⟩ }
end

theorem cof_le_card (o) : cof o ≤ card o :=
induction_on o $ λ α r _, begin
  resetI,
  have : mk (@set.univ α) = card (type r) :=
    quotient.sound ⟨equiv.set.univ _⟩,
  rw ← this, exact cof_type_le set.univ (λ a, ⟨a, ⟨⟩, irrefl a⟩)
end

theorem cof_ord_le (c : cardinal) : cof c.ord ≤ c :=
by simpa using cof_le_card c.ord

@[simp] theorem cof_zero : cof 0 = 0 :=
le_antisymm (by simpa using cof_le_card 0) (cardinal.zero_le _)

@[simp] theorem cof_eq_zero {o} : cof o = 0 ↔ o = 0 :=
⟨induction_on o $ λ α r _ z, by exactI
  let ⟨S, hl, e⟩ := cof_eq r in type_eq_zero_iff_empty.2 $
  λ ⟨a⟩, let ⟨b, h, _⟩ := hl a in
  ne_zero_iff_nonempty.2 (by exact ⟨⟨_, h⟩⟩) (e.trans z),
λ e, by simp [e]⟩

@[simp] theorem cof_succ (o) : cof (succ o) = 1 :=
begin
  apply le_antisymm,
  { refine induction_on o (λ α r _, _),
    change cof (type _) ≤ _,
    rw [← (_ : mk _ = 1)], apply cof_type_le,
    { refine λ a, ⟨sum.inr punit.star, set.mem_singleton _, _⟩,
      rcases a with a|⟨⟨⟨⟩⟩⟩; simp [empty_relation] },
    { rw [cardinal.fintype_card, set.card_singleton], simp } },
  { rw [← cardinal.succ_zero, cardinal.succ_le],
    simpa [lt_iff_le_and_ne, cardinal.zero_le] using
      λ h, succ_ne_zero o (cof_eq_zero.1 (eq.symm h)) }
end

@[simp] theorem cof_eq_one_iff_is_succ {o} : cof.{u} o = 1 ↔ ∃ a, o = succ a :=
⟨induction_on o $ λ α r _ z, begin
  resetI,
  rcases cof_eq r with ⟨S, hl, e⟩, rw z at e,
  cases ne_zero_iff_nonempty.1 (by rw e; exact one_ne_zero) with a,
  refine ⟨typein r a, eq.symm $ quotient.sound
    ⟨order_iso.of_surjective (order_embedding.of_monotone _
      (λ x y, _)) (λ x, _)⟩⟩,
  { apply sum.rec; [exact subtype.val, exact λ _, a] },
  { rcases x with x|⟨⟨⟨⟩⟩⟩; rcases y with y|⟨⟨⟨⟩⟩⟩;
      simp [subrel, order.preimage, empty_relation],
    exact x.2 },
  { suffices : r x a ∨ ∃ (b : punit), ↑a = x, {simpa},
    rcases trichotomous_of r x a with h|h|h,
    { exact or.inl h },
    { exact or.inr ⟨punit.star, h.symm⟩ },
    { rcases hl x with ⟨a', aS, hn⟩,
      rw (_ : ↑a = a') at h, {exact absurd h hn},
      refine congr_arg subtype.val (_ : a = ⟨a', aS⟩),
      haveI := le_one_iff_subsingleton.1 (le_of_eq e),
      apply subsingleton.elim } }
end, λ ⟨a, e⟩, by simp [e]⟩

@[simp] theorem cof_add (a b : ordinal) : b ≠ 0 → cof (a + b) = cof b :=
induction_on a $ λ α r _, induction_on b $ λ β s _ b0, begin
  resetI,
  change cof (type _) = _,
  refine eq_of_forall_le_iff (λ c, _),
  rw [le_cof_type, le_cof_type],
  split; intros H S hS,
  { refine le_trans (H {a | sum.rec_on a (∅:set α) S} (λ a, _)) ⟨⟨_, _⟩⟩,
    { cases a with a b,
      { cases type_ne_zero_iff_nonempty.1 b0 with b,
        rcases hS b with ⟨b', bs, _⟩,
        exact ⟨sum.inr b', bs, by simp⟩ },
      { rcases hS b with ⟨b', bs, h⟩,
        exact ⟨sum.inr b', bs, by simp [h]⟩ } },
    { exact λ a, match a with ⟨sum.inr b, h⟩ := ⟨b, h⟩ end },
    { exact λ a b, match a, b with
        ⟨sum.inr a, h₁⟩, ⟨sum.inr b, h₂⟩, h := by congr; injection h
      end } },
  { refine le_trans (H (sum.inr ⁻¹' S) (λ a, _)) ⟨⟨_, _⟩⟩,
    { rcases hS (sum.inr a) with ⟨a'|b', bs, h⟩; simp at h,
      { cases h }, { exact ⟨b', bs, h⟩ } },
    { exact λ ⟨a, h⟩, ⟨_, h⟩ },
    { exact λ ⟨a, h₁⟩ ⟨b, h₂⟩ h,
        by injection h with h; congr; injection h } }
end

@[simp] theorem cof_cof (o : ordinal) : cof (cof o).ord = cof o :=
le_antisymm (le_trans (cof_le_card _) (by simp)) $
induction_on o $ λ α r _, by exactI
let ⟨S, hS, e₁⟩ := ord_cof_eq r,
    ⟨T, hT, e₂⟩ := cof_eq (subrel r S) in begin
  rw e₁ at e₂, rw ← e₂,
  refine le_trans (cof_type_le {a | ∃ h, (subtype.mk a h : S) ∈ T} (λ a, _)) ⟨⟨_, _⟩⟩,
  { rcases hS a with ⟨b, bS, br⟩,
    rcases hT ⟨b, bS⟩ with ⟨⟨c, cS⟩, cT, cs⟩,
    exact ⟨c, ⟨cS, cT⟩, is_order_connected.neg_trans cs br⟩ },
  { exact λ ⟨a, h⟩, ⟨⟨a, h.fst⟩, h.snd⟩ },
  { exact λ ⟨a, ha⟩ ⟨b, hb⟩ h,
      by injection h with h; congr; injection h },
end

theorem omega_le_cof {o} : cardinal.omega ≤ cof o ↔ is_limit o :=
begin
  rcases zero_or_succ_or_limit o with rfl|⟨o,rfl⟩|l,
  { simp [not_zero_is_limit, cardinal.omega_ne_zero] },
  { simp [not_succ_is_limit, cardinal.one_lt_omega] },
  { simp [l], refine le_of_not_lt (λ h, _),
    cases cardinal.lt_omega.1 h with n e,
    have := cof_cof o,
    rw [e, ord_nat] at this,
    cases n,
    { simp at e, simpa [e, not_zero_is_limit] using l },
    { rw [← nat_cast_succ, cof_succ] at this,
      rw [← this, cof_eq_one_iff_is_succ] at e,
      rcases e with ⟨a, rfl⟩,
      exact not_succ_is_limit _ l } }
end

@[simp] theorem cof_omega : cof omega = cardinal.omega :=
le_antisymm
  (by rw ← card_omega; apply cof_le_card)
  (omega_le_cof.2 omega_is_limit)

theorem cof_eq' (r : α → α → Prop) [is_well_order α r] (h : is_limit (type r)) :
  ∃ S : set α, (∀ a, ∃ b ∈ S, r a b) ∧ mk S = cof (type r) :=
let ⟨S, H, e⟩ := cof_eq r in
⟨S, λ a,
  let a' := enum r _ (h.2 _ (typein_lt_type r a)) in
  let ⟨b, h, ab⟩ := H a' in
  ⟨b, h, (is_order_connected.conn a b a' $ (typein_lt_typein r).1
    (by rw typein_enum; apply ordinal.lt_succ_self)).resolve_right ab⟩,
e⟩

theorem cof_sup_le_lift {ι} (f : ι → ordinal) (H : ∀ i, f i < sup f) :
  cof (sup f) ≤ (mk ι).lift :=
begin
  generalize e : sup f = o,
  refine ordinal.induction_on o _ e, introsI α r _ e',
  rw e' at H,
  refine le_trans (cof_type_le (set.range (λ i, enum r _ (H i))) _)
    ⟨embedding.of_surjective _⟩,
  { intro a, by_contra h,
    apply not_le_of_lt (typein_lt_type r a),
    rw [← e', sup_le],
    intro i,
    simp [set.range] at h,
    simpa using le_of_lt ((typein_lt_typein r).2 (h _ i rfl)) },
  { exact λ i, ⟨_, set.mem_range_self i.1⟩ },
  { intro a, rcases a with ⟨_, i, rfl⟩, exact ⟨⟨i⟩, by simp⟩ }
end

theorem cof_sup_le {ι} (f : ι → ordinal) (H : ∀ i, f i < sup.{u u} f) :
  cof (sup.{u u} f) ≤ mk ι :=
by simpa using cof_sup_le_lift.{u u} f H

theorem cof_bsup_le_lift {o : ordinal} : ∀ (f : Π a < o, ordinal), (∀ i h, f i h < bsup o f) →
  cof (bsup o f) ≤ o.card.lift :=
induction_on o $ λ α r _ f H,
by rw bsup_type; refine cof_sup_le_lift _ _;
   rw ← bsup_type; intro a; apply H

theorem cof_bsup_le {o : ordinal} : ∀ (f : Π a < o, ordinal), (∀ i h, f i h < bsup.{u u} o f) →
  cof (bsup.{u u} o f) ≤ o.card :=
induction_on o $ λ α r _ f H,
by simpa using cof_bsup_le_lift.{u u} f H

@[simp] theorem cof_univ : cof univ.{u v} = cardinal.univ :=
le_antisymm (cof_le_card _) begin
  refine le_of_forall_lt (λ c h, _),
  rcases lt_univ'.1 h with ⟨c, rfl⟩,
  rcases @cof_eq ordinal.{u} (<) _ with ⟨S, H, Se⟩,
  rw [univ, ← lift_cof, ← cardinal.lift_lift, cardinal.lift_lt, ← Se],
  refine lt_of_not_ge (λ h, _),
  cases cardinal.lift_down h with a e,
  refine quotient.induction_on a (λ α e, _) e,
  cases quotient.exact e with f,
  have f := equiv.ulift.symm.trans f,
  let g := λ a, (f a).1,
  let o := succ (sup.{u u} g),
  rcases H o with ⟨b, h, l⟩,
  refine l (lt_succ.2 _),
  rw ← show g (f.symm ⟨b, h⟩) = b, by dsimp [g]; simp,
  apply le_sup
end

end ordinal

namespace cardinal
open ordinal

local infixr ^ := @pow cardinal.{u} cardinal cardinal.has_pow

/-- A cardinal is a limit if it is not zero or a successor
  cardinal. Note that `ω` is a limit cardinal by this definition. -/
def is_limit (c : cardinal) : Prop :=
c ≠ 0 ∧ ∀ x < c, succ x < c

/-- A cardinal is a strong limit if it is not zero and it is
  closed under powersets. Note that `ω` is a strong limit by this definition. -/
def is_strong_limit (c : cardinal) : Prop :=
c ≠ 0 ∧ ∀ x < c, 2 ^ x < c

theorem is_strong_limit.is_limit {c} (H : is_strong_limit c) : is_limit c :=
⟨H.1, λ x h, lt_of_le_of_lt (succ_le.2 $ cantor _) (H.2 _ h)⟩

/-- A cardinal is regular if it is infinite and it equals its own cofinality. -/
def is_regular (c : cardinal) : Prop :=
omega ≤ c ∧ c.ord.cof = c

theorem cof_is_regular {o : ordinal} (h : o.is_limit) : is_regular o.cof :=
⟨omega_le_cof.2 h, cof_cof _⟩

theorem omega_is_regular : is_regular omega :=
⟨le_refl _, by simp⟩

theorem succ_is_regular {c : cardinal.{u}} (h : omega ≤ c) : is_regular (succ c) :=
⟨le_trans h (le_of_lt $ lt_succ_self _), begin
  refine le_antisymm (cof_ord_le _) (succ_le.2 _),
  cases quotient.exists_rep (succ c) with α αe, simp at αe,
  rcases ord_eq α with ⟨r, wo, re⟩, resetI,
  have := ord_is_limit (le_trans h $ le_of_lt $ lt_succ_self _),
  rw [← αe, re] at this ⊢,
  rcases cof_eq' r this with ⟨S, H, Se⟩,
  rw [← Se],
  apply lt_imp_lt_of_le_imp_le (mul_le_mul_right c),
  rw [mul_eq_self h, ← succ_le, ← αe, ← sum_const],
  refine le_trans _ (sum_le_sum (λ x:S, card (typein r x)) _ _),
  { simp [typein, sum_mk (λ x:S, {a//r a x})],
    refine ⟨embedding.of_surjective _⟩,
    { exact λ x, x.2.1 },
    { exact λ a, let ⟨b, h, ab⟩ := H a in ⟨⟨⟨_, h⟩, _, ab⟩, rfl⟩ } },
  { intro i,
    rw [← lt_succ, ← lt_ord, ← αe, re],
    apply typein_lt_type }
end⟩

theorem sup_lt_of_is_regular {ι} (f : ι → cardinal)
  {c} (hc : is_regular c) (H1 : cardinal.mk ι < c)
  (H2 : ∀ i, f i < c) : sup.{u u} f < c :=
begin
  refine lt_of_le_of_ne (sup_le.2 (λ i, le_of_lt $ H2 i)) _,
  rintro rfl, apply not_le_of_lt H1,
  simpa [sup_ord, H2, hc.2] using cof_sup_le.{u} (λ i, (f i).ord)
end

theorem sum_lt_of_is_regular {ι} (f : ι → cardinal)
  {c} (hc : is_regular c) (H1 : cardinal.mk ι < c)
  (H2 : ∀ i, f i < c) : sum.{u u} f < c :=
lt_of_le_of_lt (sum_le_sup _) $ mul_lt_of_lt hc.1 H1 $
sup_lt_of_is_regular f hc H1 H2

/-- A cardinal is inaccessible if it is an
  uncountable regular strong limit cardinal. -/
def is_inaccessible (c : cardinal) :=
omega < c ∧ is_regular c ∧ is_strong_limit c

theorem is_inaccessible.mk {c}
 (h₁ : omega < c) (h₂ : c ≤ c.ord.cof) (h₃ : ∀ x < c, 2 ^ x < c) :
 is_inaccessible c :=
⟨h₁, ⟨le_of_lt h₁, le_antisymm (cof_ord_le _) h₂⟩,
  ne_of_gt (lt_trans omega_pos h₁), h₃⟩

/- Lean's foundations prove the existence of ω many inaccessible
   cardinals -/
theorem univ_inaccessible : is_inaccessible (univ.{u v}) :=
is_inaccessible.mk
  (by simpa using lift_lt_univ' omega)
  (by simp)
  (λ c h, begin
    rcases lt_univ'.1 h with ⟨c, rfl⟩,
    rw ← lift_two_power.{u (max (u+1) v)},
    apply lift_lt_univ'
  end)

theorem lt_power_cof {c : cardinal.{u}} : omega ≤ c → c < c ^ cof c.ord :=
quotient.induction_on c $ λ α h, begin
  rcases ord_eq α with ⟨r, wo, re⟩, resetI,
  have := ord_is_limit h,
  rw [mk_def, re] at this ⊢,
  rcases cof_eq' r this with ⟨S, H, Se⟩,
  have := sum_lt_prod (λ a:S, mk {x // r x a}) (λ _, mk α) (λ i, _),
  { simp [Se.symm] at this ⊢,
    refine lt_of_le_of_lt _ this,
    refine ⟨embedding.of_surjective _⟩,
    { exact λ x, x.2.1 },
    { exact λ a, let ⟨b, h, ab⟩ := H a in ⟨⟨⟨_, h⟩, _, ab⟩, rfl⟩ } },
  { have := typein_lt_type r i,
    rwa [← re, lt_ord] at this }
end

theorem lt_cof_power {a b : cardinal} (ha : omega ≤ a) (b1 : 1 < b) :
  a < cof (b ^ a).ord :=
begin
  have b0 : b ≠ 0 := ne_of_gt (lt_trans zero_lt_one b1),
  apply lt_imp_lt_of_le_imp_le (power_le_power_left $ power_ne_zero a b0),
  rw [power_mul, mul_eq_self ha],
  exact lt_power_cof (le_trans ha $ le_of_lt $ cantor' _ b1),
end

end cardinal


import algebraic_geometry.prime_spectrum.basic
import ring_theory.ideal.minimal_prime
import for_mathlib.specializing

namespace prime_spectrum
variables {R S : Type*} [comm_ring R] [comm_ring S] (f : R →+* S)

lemma image_is_closed_iff_is_stable_under_specialization :
  is_closed (set.range $ prime_spectrum.comap f) ↔
    stable_under_specialization (set.range $ prime_spectrum.comap f) :=
begin
  refine ⟨is_closed.stable_under_specialization, λ h, _⟩,
  rw prime_spectrum.is_closed_iff_zero_locus_ideal,
  refine ⟨f.ker, _⟩,
  apply le_antisymm,
  { rintro _ ⟨p, rfl⟩ x (hx : f x = 0), show f x ∈ p.1, rw hx, exact zero_mem _ },
  rintros p (hp : f.ker ≤ p.as_ideal),
  obtain ⟨q, hq₁, hq₂⟩ := ideal.exists_minimal_primes_le hp,
  obtain ⟨p', hp', hp'', rfl⟩ := ideal.exists_comap_eq_of_mem_minimal_primes f _ hq₁,
  let p'' : prime_spectrum S := ⟨p', hp'⟩,
  change prime_spectrum.comap f p'' ≤ p at hq₂,
  apply h ((prime_spectrum.le_iff_specializes _ _).mp hq₂),
  exact set.mem_range_self _
end

lemma closure_image_comap_zero_locus (I : ideal S) :
  closure (prime_spectrum.comap f '' prime_spectrum.zero_locus I) =
    prime_spectrum.zero_locus (I.comap f) :=
begin
  apply subset_antisymm,
  { rw [is_closed.closure_subset_iff, set.image_subset_iff,
      prime_spectrum.preimage_comap_zero_locus],
    exacts [prime_spectrum.zero_locus_anti_mono (set.image_preimage_subset _ _),
      prime_spectrum.is_closed_zero_locus _] },
  { rintro x (hx : I.comap f ≤ x.as_ideal),
    obtain ⟨q, hq₁, hq₂⟩ := ideal.exists_minimal_primes_le hx,
    obtain ⟨p', hp', hp'', rfl⟩ := ideal.exists_comap_eq_of_mem_minimal_primes f _ hq₁,
    let p'' : prime_spectrum S := ⟨p', hp'⟩,
    apply is_closed_closure.stable_under_specialization
      ((prime_spectrum.le_iff_specializes (prime_spectrum.comap f p'') x).mp hq₂),
    apply subset_closure,
    exact ⟨p'', hp'', rfl⟩ },
end

lemma _root_.ideal.quotient.ker_mk {R : Type*} [comm_ring R] (I : ideal R) :
  I^.quotient.mk^.ker = I :=
by { ext1, exact submodule.quotient.mk_eq_zero _ }

lemma is_closed_map_of_is_integral (H : f.is_integral) :
  is_closed_map (prime_spectrum.comap f) :=
begin
  intros S hS,
  obtain ⟨I, rfl⟩ := (prime_spectrum.is_closed_iff_zero_locus_ideal _).mp hS,
  rw [← I^.quotient.ker_mk, ← prime_spectrum.range_comap_of_surjective _ _
    ideal.quotient.mk_surjective, ← set.range_comp, ← continuous_map.coe_comp,
    ← prime_spectrum.comap_comp, prime_spectrum.image_is_closed_iff_is_stable_under_specialization],
  rintros _ q h ⟨p, rfl⟩,
  rw ← prime_spectrum.le_iff_specializes at h,
  let f' := (ideal.quotient.mk I).comp f,
  have : f'.is_integral := ring_hom.is_integral_trans _ _ H
    (ring_hom.is_integral_of_surjective _ ideal.quotient.mk_surjective),
  letI := f'.to_algebra,
  obtain ⟨q', h₁, h₂, e⟩ :=
    ideal.exists_ideal_over_prime_of_is_integral this q.as_ideal p.as_ideal h,
  exact ⟨⟨q', h₂⟩, prime_spectrum.ext.mpr e⟩
end

lemma surjective_of_is_integral_of_injective (H : f.is_integral) (H' : function.injective f) :
  function.surjective (prime_spectrum.comap f) :=
begin
  rw [← set.range_iff_surjective, ← (is_closed_map_of_is_integral f H).closed_range.closure_eq,
    ← set.image_univ, ← zero_locus_bot, closure_image_comap_zero_locus,
    ← ring_hom.ker_eq_comap_bot, f.injective_iff_ker_eq_bot.mp H', zero_locus_bot]
end

end prime_spectrum


/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.data.nat.basic
import Mathlib.Lean3Lib.init.data.nat.div
import Mathlib.Lean3Lib.init.meta.default
import Mathlib.Lean3Lib.init.algebra.functions

universes u

namespace Mathlib

namespace nat

protected theorem add_comm (n : ℕ) (m : ℕ) : n + m = m + n := sorry

protected theorem add_assoc (n : ℕ) (m : ℕ) (k : ℕ) : n + m + k = n + (m + k) := sorry

protected theorem add_left_comm (n : ℕ) (m : ℕ) (k : ℕ) : n + (m + k) = m + (n + k) :=
  left_comm Nat.add nat.add_comm nat.add_assoc

protected theorem add_left_cancel {n : ℕ} {m : ℕ} {k : ℕ} : n + m = n + k → m = k := sorry

protected theorem add_right_cancel {n : ℕ} {m : ℕ} {k : ℕ} (h : n + m = k + m) : n = k :=
  (fun (this : m + n = m + k) => nat.add_left_cancel this)
    (eq.mp (Eq._oldrec (Eq.refl (m + n = k + m)) (nat.add_comm k m))
      (eq.mp (Eq._oldrec (Eq.refl (n + m = k + m)) (nat.add_comm n m)) h))

theorem succ_ne_zero (n : ℕ) : Nat.succ n ≠ 0 := fun (h : Nat.succ n = 0) => nat.no_confusion h

theorem succ_ne_self (n : ℕ) : Nat.succ n ≠ n := sorry

protected theorem one_ne_zero : 1 ≠ 0 := fun (h : 1 = 0) => nat.no_confusion h

protected theorem zero_ne_one : 0 ≠ 1 := fun (h : 0 = 1) => nat.no_confusion h

theorem eq_zero_of_add_eq_zero_right {n : ℕ} {m : ℕ} : n + m = 0 → n = 0 := sorry

theorem eq_zero_of_add_eq_zero_left {n : ℕ} {m : ℕ} (h : n + m = 0) : m = 0 :=
  eq_zero_of_add_eq_zero_right (nat.add_comm n m ▸ h)

@[simp] theorem pred_zero : Nat.pred 0 = 0 := rfl

@[simp] theorem pred_succ (n : ℕ) : Nat.pred (Nat.succ n) = n := rfl

protected theorem mul_zero (n : ℕ) : n * 0 = 0 := rfl

theorem mul_succ (n : ℕ) (m : ℕ) : n * Nat.succ m = n * m + n := rfl

protected theorem zero_mul (n : ℕ) : 0 * n = 0 := sorry

theorem succ_mul (n : ℕ) (m : ℕ) : Nat.succ n * m = n * m + m := sorry

protected theorem right_distrib (n : ℕ) (m : ℕ) (k : ℕ) : (n + m) * k = n * k + m * k := sorry

protected theorem left_distrib (n : ℕ) (m : ℕ) (k : ℕ) : n * (m + k) = n * m + n * k := sorry

protected theorem mul_comm (n : ℕ) (m : ℕ) : n * m = m * n := sorry

protected theorem mul_assoc (n : ℕ) (m : ℕ) (k : ℕ) : n * m * k = n * (m * k) := sorry

protected theorem mul_one (n : ℕ) : n * 1 = n := nat.zero_add

protected theorem one_mul (n : ℕ) : 1 * n = n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 * n = n)) (nat.mul_comm 1 n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (n * 1 = n)) (nat.mul_one n))) (Eq.refl n))

/- properties of inequality -/

protected theorem le_of_eq {n : ℕ} {m : ℕ} (p : n = m) : n ≤ m := p ▸ less_than_or_equal.refl

theorem le_succ_of_le {n : ℕ} {m : ℕ} (h : n ≤ m) : n ≤ Nat.succ m := nat.le_trans h (le_succ m)

theorem le_of_succ_le {n : ℕ} {m : ℕ} (h : Nat.succ n ≤ m) : n ≤ m := nat.le_trans (le_succ n) h

protected theorem le_of_lt {n : ℕ} {m : ℕ} (h : n < m) : n ≤ m := le_of_succ_le h

def lt.step {n : ℕ} {m : ℕ} : n < m → n < Nat.succ m := less_than_or_equal.step

theorem eq_zero_or_pos (n : ℕ) : n = 0 ∨ n > 0 :=
  nat.cases_on n (Or.inl rfl) fun (n : ℕ) => Or.inr (succ_pos n)

protected theorem pos_of_ne_zero {n : ℕ} : n ≠ 0 → n > 0 := or.resolve_left (eq_zero_or_pos n)

protected theorem lt_trans {n : ℕ} {m : ℕ} {k : ℕ} (h₁ : n < m) : m < k → n < k :=
  nat.le_trans (less_than_or_equal.step h₁)

protected theorem lt_of_le_of_lt {n : ℕ} {m : ℕ} {k : ℕ} (h₁ : n ≤ m) : m < k → n < k :=
  nat.le_trans (succ_le_succ h₁)

def lt.base (n : ℕ) : n < Nat.succ n := nat.le_refl (Nat.succ n)

theorem lt_succ_self (n : ℕ) : n < Nat.succ n := lt.base n

protected theorem le_antisymm {n : ℕ} {m : ℕ} (h₁ : n ≤ m) : m ≤ n → n = m :=
  less_than_or_equal.cases_on h₁ (fun (a : n ≤ n) => rfl)
    fun (a : ℕ) (b : less_than_or_equal n a) (c : Nat.succ a ≤ n) =>
      absurd (nat.lt_of_le_of_lt b c) (nat.lt_irrefl n)

protected theorem lt_or_ge (a : ℕ) (b : ℕ) : a < b ∨ a ≥ b := sorry

protected theorem le_total {m : ℕ} {n : ℕ} : m ≤ n ∨ n ≤ m :=
  or.imp_left nat.le_of_lt (nat.lt_or_ge m n)

protected theorem lt_of_le_and_ne {m : ℕ} {n : ℕ} (h1 : m ≤ n) : m ≠ n → m < n :=
  or.resolve_right (or.swap (nat.eq_or_lt_of_le h1))

protected theorem lt_iff_le_not_le {m : ℕ} {n : ℕ} : m < n ↔ m ≤ n ∧ ¬n ≤ m := sorry

protected instance linear_order : linear_order ℕ :=
  linear_order.mk less_than_or_equal nat.lt nat.le_refl nat.le_trans nat.le_antisymm nat.le_total
    nat.decidable_le nat.decidable_eq nat.decidable_lt

theorem eq_zero_of_le_zero {n : ℕ} (h : n ≤ 0) : n = 0 := le_antisymm h (zero_le n)

theorem succ_lt_succ {a : ℕ} {b : ℕ} : a < b → Nat.succ a < Nat.succ b := succ_le_succ

theorem lt_of_succ_lt {a : ℕ} {b : ℕ} : Nat.succ a < b → a < b := le_of_succ_le

theorem lt_of_succ_lt_succ {a : ℕ} {b : ℕ} : Nat.succ a < Nat.succ b → a < b := le_of_succ_le_succ

theorem pred_lt_pred {n : ℕ} {m : ℕ} : n ≠ 0 → n < m → Nat.pred n < Nat.pred m := sorry

theorem lt_of_succ_le {a : ℕ} {b : ℕ} (h : Nat.succ a ≤ b) : a < b := h

theorem succ_le_of_lt {a : ℕ} {b : ℕ} (h : a < b) : Nat.succ a ≤ b := h

theorem le_add_right (n : ℕ) (k : ℕ) : n ≤ n + k := sorry

theorem le_add_left (n : ℕ) (m : ℕ) : n ≤ m + n := nat.add_comm n m ▸ le_add_right n m

theorem le.dest {n : ℕ} {m : ℕ} : n ≤ m → ∃ (k : ℕ), n + k = m := sorry

theorem le.intro {n : ℕ} {m : ℕ} {k : ℕ} (h : n + k = m) : n ≤ m := h ▸ le_add_right n k

protected theorem add_le_add_left {n : ℕ} {m : ℕ} (h : n ≤ m) (k : ℕ) : k + n ≤ k + m := sorry

protected theorem add_le_add_right {n : ℕ} {m : ℕ} (h : n ≤ m) (k : ℕ) : n + k ≤ m + k :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n + k ≤ m + k)) (nat.add_comm n k)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (k + n ≤ m + k)) (nat.add_comm m k)))
      (nat.add_le_add_left h k))

protected theorem le_of_add_le_add_left {k : ℕ} {n : ℕ} {m : ℕ} (h : k + n ≤ k + m) : n ≤ m := sorry

protected theorem le_of_add_le_add_right {k : ℕ} {n : ℕ} {m : ℕ} : n + k ≤ m + k → n ≤ m :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n + k ≤ m + k → n ≤ m)) (nat.add_comm n k)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (k + n ≤ m + k → n ≤ m)) (nat.add_comm m k)))
      nat.le_of_add_le_add_left)

protected theorem add_le_add_iff_le_right (k : ℕ) (n : ℕ) (m : ℕ) : n + k ≤ m + k ↔ n ≤ m :=
  { mp := nat.le_of_add_le_add_right, mpr := fun (h : n ≤ m) => nat.add_le_add_right h k }

protected theorem lt_of_add_lt_add_left {k : ℕ} {n : ℕ} {m : ℕ} (h : k + n < k + m) : n < m :=
  let h' : k + n ≤ k + m := nat.le_of_lt h;
  nat.lt_of_le_and_ne (nat.le_of_add_le_add_left h')
    fun (heq : n = m) => nat.lt_irrefl (k + m) (eq.mp (Eq._oldrec (Eq.refl (k + n < k + m)) heq) h)

protected theorem lt_of_add_lt_add_right {a : ℕ} {b : ℕ} {c : ℕ} (h : a + b < c + b) : a < c :=
  sorry

protected theorem add_lt_add_left {n : ℕ} {m : ℕ} (h : n < m) (k : ℕ) : k + n < k + m :=
  lt_of_succ_le (add_succ k n ▸ nat.add_le_add_left (succ_le_of_lt h) k)

protected theorem add_lt_add_right {n : ℕ} {m : ℕ} (h : n < m) (k : ℕ) : n + k < m + k :=
  nat.add_comm k m ▸ nat.add_comm k n ▸ nat.add_lt_add_left h k

protected theorem lt_add_of_pos_right {n : ℕ} {k : ℕ} (h : k > 0) : n < n + k :=
  nat.add_lt_add_left h n

protected theorem lt_add_of_pos_left {n : ℕ} {k : ℕ} (h : k > 0) : n < k + n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n < k + n)) (nat.add_comm k n))) (nat.lt_add_of_pos_right h)

protected theorem add_lt_add {a : ℕ} {b : ℕ} {c : ℕ} {d : ℕ} (h₁ : a < b) (h₂ : c < d) :
    a + c < b + d :=
  lt_trans (nat.add_lt_add_right h₁ c) (nat.add_lt_add_left h₂ b)

protected theorem add_le_add {a : ℕ} {b : ℕ} {c : ℕ} {d : ℕ} (h₁ : a ≤ b) (h₂ : c ≤ d) :
    a + c ≤ b + d :=
  le_trans (nat.add_le_add_right h₁ c) (nat.add_le_add_left h₂ b)

protected theorem zero_lt_one : 0 < 1 := zero_lt_succ 0

theorem mul_le_mul_left {n : ℕ} {m : ℕ} (k : ℕ) (h : n ≤ m) : k * n ≤ k * m := sorry

theorem mul_le_mul_right {n : ℕ} {m : ℕ} (k : ℕ) (h : n ≤ m) : n * k ≤ m * k :=
  nat.mul_comm k m ▸ nat.mul_comm k n ▸ mul_le_mul_left k h

protected theorem mul_lt_mul_of_pos_left {n : ℕ} {m : ℕ} {k : ℕ} (h : n < m) (hk : k > 0) :
    k * n < k * m :=
  nat.lt_of_lt_of_le (nat.lt_add_of_pos_right hk)
    (mul_succ k n ▸ mul_le_mul_left k (succ_le_of_lt h))

protected theorem mul_lt_mul_of_pos_right {n : ℕ} {m : ℕ} {k : ℕ} (h : n < m) (hk : k > 0) :
    n * k < m * k :=
  nat.mul_comm k m ▸ nat.mul_comm k n ▸ nat.mul_lt_mul_of_pos_left h hk

protected theorem le_of_mul_le_mul_left {a : ℕ} {b : ℕ} {c : ℕ} (h : c * a ≤ c * b) (hc : c > 0) :
    a ≤ b :=
  iff.mp decidable.not_lt
    fun (h1 : b < a) =>
      (fun (h2 : c * b < c * a) => not_le_of_gt h2 h) (nat.mul_lt_mul_of_pos_left h1 hc)

theorem le_of_lt_succ {m : ℕ} {n : ℕ} : m < Nat.succ n → m ≤ n := le_of_succ_le_succ

theorem eq_of_mul_eq_mul_left {m : ℕ} {k : ℕ} {n : ℕ} (Hn : n > 0) (H : n * m = n * k) : m = k :=
  le_antisymm (nat.le_of_mul_le_mul_left (le_of_eq H) Hn)
    (nat.le_of_mul_le_mul_left (le_of_eq (Eq.symm H)) Hn)

/- sub properties -/

@[simp] protected theorem zero_sub (a : ℕ) : 0 - a = 0 := sorry

theorem sub_lt_succ (a : ℕ) (b : ℕ) : a - b < Nat.succ a := lt_succ_of_le (sub_le a b)

protected theorem sub_le_sub_right {n : ℕ} {m : ℕ} (h : n ≤ m) (k : ℕ) : n - k ≤ m - k := sorry

/- bit0/bit1 properties -/

protected theorem bit1_eq_succ_bit0 (n : ℕ) : bit1 n = Nat.succ (bit0 n) := rfl

protected theorem bit1_succ_eq (n : ℕ) : bit1 (Nat.succ n) = Nat.succ (Nat.succ (bit1 n)) :=
  Eq.trans (nat.bit1_eq_succ_bit0 (Nat.succ n)) (congr_arg Nat.succ (nat.bit0_succ_eq n))

protected theorem bit1_ne_one {n : ℕ} : n ≠ 0 → bit1 n ≠ 1 := sorry

protected theorem bit0_ne_one (n : ℕ) : bit0 n ≠ 1 := sorry

protected theorem add_self_ne_one (n : ℕ) : n + n ≠ 1 := sorry

protected theorem bit1_ne_bit0 (n : ℕ) (m : ℕ) : bit1 n ≠ bit0 m := sorry

protected theorem bit0_ne_bit1 (n : ℕ) (m : ℕ) : bit0 n ≠ bit1 m := ne.symm (nat.bit1_ne_bit0 m n)

protected theorem bit0_inj {n : ℕ} {m : ℕ} : bit0 n = bit0 m → n = m := sorry

protected theorem bit1_inj {n : ℕ} {m : ℕ} : bit1 n = bit1 m → n = m := sorry

protected theorem bit0_ne {n : ℕ} {m : ℕ} : n ≠ m → bit0 n ≠ bit0 m :=
  fun (h₁ : n ≠ m) (h₂ : bit0 n = bit0 m) => absurd (nat.bit0_inj h₂) h₁

protected theorem bit1_ne {n : ℕ} {m : ℕ} : n ≠ m → bit1 n ≠ bit1 m :=
  fun (h₁ : n ≠ m) (h₂ : bit1 n = bit1 m) => absurd (nat.bit1_inj h₂) h₁

protected theorem zero_ne_bit0 {n : ℕ} : n ≠ 0 → 0 ≠ bit0 n :=
  fun (h : n ≠ 0) => ne.symm (nat.bit0_ne_zero h)

protected theorem zero_ne_bit1 (n : ℕ) : 0 ≠ bit1 n := ne.symm (nat.bit1_ne_zero n)

protected theorem one_ne_bit0 (n : ℕ) : 1 ≠ bit0 n := ne.symm (nat.bit0_ne_one n)

protected theorem one_ne_bit1 {n : ℕ} : n ≠ 0 → 1 ≠ bit1 n :=
  fun (h : n ≠ 0) => ne.symm (nat.bit1_ne_one h)

protected theorem one_lt_bit1 {n : ℕ} : n ≠ 0 → 1 < bit1 n := sorry

protected theorem one_lt_bit0 {n : ℕ} : n ≠ 0 → 1 < bit0 n := sorry

protected theorem bit0_lt {n : ℕ} {m : ℕ} (h : n < m) : bit0 n < bit0 m := nat.add_lt_add h h

protected theorem bit1_lt {n : ℕ} {m : ℕ} (h : n < m) : bit1 n < bit1 m :=
  succ_lt_succ (nat.add_lt_add h h)

protected theorem bit0_lt_bit1 {n : ℕ} {m : ℕ} (h : n ≤ m) : bit0 n < bit1 m :=
  lt_succ_of_le (nat.add_le_add h h)

protected theorem bit1_lt_bit0 {n : ℕ} {m : ℕ} : n < m → bit1 n < bit0 m := sorry

protected theorem one_le_bit1 (n : ℕ) : 1 ≤ bit1 n :=
  (fun (this : 1 ≤ Nat.succ (bit0 n)) => this) (succ_le_succ (zero_le (bit0 n)))

protected theorem one_le_bit0 (n : ℕ) : n ≠ 0 → 1 ≤ bit0 n := sorry

/- subtraction -/

@[simp] protected theorem sub_zero (n : ℕ) : n - 0 = n := rfl

theorem sub_succ (n : ℕ) (m : ℕ) : n - Nat.succ m = Nat.pred (n - m) := rfl

theorem succ_sub_succ (n : ℕ) (m : ℕ) : Nat.succ n - Nat.succ m = n - m := succ_sub_succ_eq_sub n m

protected theorem sub_self (n : ℕ) : n - n = 0 := sorry

/- TODO(Leo): remove the following ematch annotations as soon as we have
   arithmetic theory in the smt_stactic -/

protected theorem add_sub_add_right (n : ℕ) (k : ℕ) (m : ℕ) : n + k - (m + k) = n - m := sorry

protected theorem add_sub_add_left (k : ℕ) (n : ℕ) (m : ℕ) : k + n - (k + m) = n - m :=
  eq.mpr (id (Eq._oldrec (Eq.refl (k + n - (k + m) = n - m)) (nat.add_comm k n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (n + k - (k + m) = n - m)) (nat.add_comm k m)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (n + k - (m + k) = n - m)) (nat.add_sub_add_right n k m)))
        (Eq.refl (n - m))))

protected theorem add_sub_cancel (n : ℕ) (m : ℕ) : n + m - m = n :=
  (fun (this : n + m - (0 + m) = n) =>
      eq.mp (Eq._oldrec (Eq.refl (n + m - (0 + m) = n)) (nat.zero_add m)) this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (n + m - (0 + m) = n)) (nat.add_sub_add_right n m 0)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (n - 0 = n)) (nat.sub_zero n))) (Eq.refl n)))

protected theorem add_sub_cancel_left (n : ℕ) (m : ℕ) : n + m - n = m :=
  (fun (this : n + m - (n + 0) = m) => this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (n + m - (n + 0) = m)) (nat.add_sub_add_left n m 0)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (m - 0 = m)) (nat.sub_zero m))) (Eq.refl m)))

protected theorem sub_sub (n : ℕ) (m : ℕ) (k : ℕ) : n - m - k = n - (m + k) := sorry

theorem le_of_le_of_sub_le_sub_right {n : ℕ} {m : ℕ} {k : ℕ} (h₀ : k ≤ m) (h₁ : n - k ≤ m - k) :
    n ≤ m :=
  sorry

protected theorem sub_le_sub_right_iff (n : ℕ) (m : ℕ) (k : ℕ) (h : k ≤ m) :
    n - k ≤ m - k ↔ n ≤ m :=
  { mp := le_of_le_of_sub_le_sub_right h, mpr := fun (h : n ≤ m) => nat.sub_le_sub_right h k }

theorem sub_self_add (n : ℕ) (m : ℕ) : n - (n + m) = 0 :=
  (fun (this : n + 0 - (n + m) = 0) => this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (n + 0 - (n + m) = 0)) (nat.add_sub_add_left n 0 m)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 - m = 0)) (nat.zero_sub m))) (Eq.refl 0)))

theorem add_le_to_le_sub (x : ℕ) {y : ℕ} {k : ℕ} (h : k ≤ y) : x + k ≤ y ↔ x ≤ y - k := sorry

theorem sub_lt_of_pos_le (a : ℕ) (b : ℕ) (h₀ : 0 < a) (h₁ : a ≤ b) : b - a < b :=
  sub_lt (lt_of_lt_of_le h₀ h₁) h₀

theorem sub_one (n : ℕ) : n - 1 = Nat.pred n := rfl

theorem succ_sub_one (n : ℕ) : Nat.succ n - 1 = n := rfl

theorem succ_pred_eq_of_pos {n : ℕ} : n > 0 → Nat.succ (Nat.pred n) = n := sorry

theorem sub_eq_zero_of_le {n : ℕ} {m : ℕ} (h : n ≤ m) : n - m = 0 := sorry

protected theorem le_of_sub_eq_zero {n : ℕ} {m : ℕ} : n - m = 0 → n ≤ m := sorry

protected theorem sub_eq_zero_iff_le {n : ℕ} {m : ℕ} : n - m = 0 ↔ n ≤ m :=
  { mp := nat.le_of_sub_eq_zero, mpr := sub_eq_zero_of_le }

theorem add_sub_of_le {n : ℕ} {m : ℕ} (h : n ≤ m) : n + (m - n) = m := sorry

protected theorem sub_add_cancel {n : ℕ} {m : ℕ} (h : n ≥ m) : n - m + m = n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n - m + m = n)) (nat.add_comm (n - m) m)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (m + (n - m) = n)) (add_sub_of_le h))) (Eq.refl n))

protected theorem add_sub_assoc {m : ℕ} {k : ℕ} (h : k ≤ m) (n : ℕ) : n + m - k = n + (m - k) :=
  sorry

protected theorem sub_eq_iff_eq_add {a : ℕ} {b : ℕ} {c : ℕ} (ab : b ≤ a) : a - b = c ↔ a = c + b :=
  sorry

protected theorem lt_of_sub_eq_succ {m : ℕ} {n : ℕ} {l : ℕ} (H : m - n = Nat.succ l) : n < m :=
  sorry

@[simp] theorem zero_min (a : ℕ) : min 0 a = 0 := min_eq_left (zero_le a)

@[simp] theorem min_zero (a : ℕ) : min a 0 = 0 := min_eq_right (zero_le a)

-- Distribute succ over min

theorem min_succ_succ (x : ℕ) (y : ℕ) : min (Nat.succ x) (Nat.succ y) = Nat.succ (min x y) := sorry

theorem sub_eq_sub_min (n : ℕ) (m : ℕ) : n - m = n - min n m := sorry

@[simp] theorem sub_add_min_cancel (n : ℕ) (m : ℕ) : n - m + min n m = n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n - m + min n m = n)) (sub_eq_sub_min n m)))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (n - min n m + min n m = n)) (nat.sub_add_cancel (min_le_left n m))))
      (Eq.refl n))

/- TODO(Leo): sub + inequalities -/

protected def strong_rec_on {p : ℕ → Sort u} (n : ℕ) (h : (n : ℕ) → ((m : ℕ) → m < n → p m) → p n) :
    p n :=
  (fun (this : (n m : ℕ) → m < n → p m) => this (Nat.succ n) n (lt_succ_self n))
    fun (n : ℕ) =>
      Nat.rec (fun (m : ℕ) (h₁ : m < 0) => absurd h₁ (not_lt_zero m))
        (fun (n : ℕ) (ih : (m : ℕ) → m < n → p m) (m : ℕ) (h₁ : m < Nat.succ n) =>
          or.by_cases sorry (fun (ᾰ : m < n) => ih m ᾰ)
            fun (ᾰ : m = n) => Eq._oldrec (fun (h₁ : n < Nat.succ n) => h n ih) (Eq.symm ᾰ) h₁)
        n

protected theorem strong_induction_on {p : ℕ → Prop} (n : ℕ)
    (h : ∀ (n : ℕ), (∀ (m : ℕ), m < n → p m) → p n) : p n :=
  nat.strong_rec_on n h

protected theorem case_strong_induction_on {p : ℕ → Prop} (a : ℕ) (hz : p 0)
    (hi : ∀ (n : ℕ), (∀ (m : ℕ), m ≤ n → p m) → p (Nat.succ n)) : p a :=
  sorry

/- mod -/

theorem mod_def (x : ℕ) (y : ℕ) : x % y = ite (0 < y ∧ y ≤ x) ((x - y) % y) x := sorry

@[simp] theorem mod_zero (a : ℕ) : a % 0 = a := sorry

theorem mod_eq_of_lt {a : ℕ} {b : ℕ} (h : a < b) : a % b = a := sorry

@[simp] theorem zero_mod (b : ℕ) : 0 % b = 0 := sorry

theorem mod_eq_sub_mod {a : ℕ} {b : ℕ} (h : a ≥ b) : a % b = (a - b) % b := sorry

theorem mod_lt (x : ℕ) {y : ℕ} (h : y > 0) : x % y < y := sorry

@[simp] theorem mod_self (n : ℕ) : n % n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n % n = 0)) (mod_eq_sub_mod (le_refl n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((n - n) % n = 0)) (nat.sub_self n)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 % n = 0)) (zero_mod n))) (Eq.refl 0)))

@[simp] theorem mod_one (n : ℕ) : n % 1 = 0 :=
  (fun (this : n % 1 < 1) => eq_zero_of_le_zero (le_of_lt_succ this)) (mod_lt n (succ_pos 0))

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

/- div & mod -/

theorem div_def (x : ℕ) (y : ℕ) : x / y = ite (0 < y ∧ y ≤ x) ((x - y) / y + 1) 0 := sorry

theorem mod_add_div (m : ℕ) (k : ℕ) : m % k + k * (m / k) = m := sorry

/- div -/

@[simp] protected theorem div_one (n : ℕ) : n / 1 = n := sorry

@[simp] protected theorem div_zero (n : ℕ) : n / 0 = 0 := sorry

@[simp] protected theorem zero_div (b : ℕ) : 0 / b = 0 :=
  Eq.trans (div_def 0 b) (if_neg (And._oldrec not_le_of_gt))

protected theorem div_le_of_le_mul {m : ℕ} {n : ℕ} {k : ℕ} : m ≤ k * n → m / k ≤ n := sorry

protected theorem div_le_self (m : ℕ) (n : ℕ) : m / n ≤ m := sorry

theorem div_eq_sub_div {a : ℕ} {b : ℕ} (h₁ : b > 0) (h₂ : a ≥ b) : a / b = (a - b) / b + 1 := sorry

theorem div_eq_of_lt {a : ℕ} {b : ℕ} (h₀ : a < b) : a / b = 0 := sorry

-- this is a Galois connection

--   f x ≤ y ↔ x ≤ g y

-- with

--   f x = x * k

--   g y = y / k

theorem le_div_iff_mul_le (x : ℕ) (y : ℕ) {k : ℕ} (Hk : k > 0) : x ≤ y / k ↔ x * k ≤ y := sorry

theorem div_lt_iff_lt_mul (x : ℕ) (y : ℕ) {k : ℕ} (Hk : k > 0) : x / k < y ↔ x < y * k := sorry

def iterate {α : Sort u} (op : α → α) : ℕ → α → α := sorry

/- successor and predecessor -/

theorem add_one_ne_zero (n : ℕ) : n + 1 ≠ 0 := succ_ne_zero n

theorem eq_zero_or_eq_succ_pred (n : ℕ) : n = 0 ∨ n = Nat.succ (Nat.pred n) := sorry

theorem exists_eq_succ_of_ne_zero {n : ℕ} (H : n ≠ 0) : ∃ (k : ℕ), n = Nat.succ k :=
  Exists.intro (Nat.pred n) (or.resolve_left (eq_zero_or_eq_succ_pred n) H)

theorem discriminate {B : Sort u} {n : ℕ} (H1 : n = 0 → B) (H2 : (m : ℕ) → n = Nat.succ m → B) :
    B :=
  (fun (_x : ℕ) (h : n = _x) =>
      Nat.rec (fun (h : n = 0) => H1 h)
        (fun (n_1 : ℕ) (ih : n = n_1 → B) (h : n = Nat.succ n_1) => H2 n_1 h) _x h)
    n rfl

theorem one_succ_zero : 1 = Nat.succ 0 := rfl

theorem two_step_induction {P : ℕ → Sort u} (H1 : P 0) (H2 : P 1)
    (H3 : (n : ℕ) → P n → P (Nat.succ n) → P (Nat.succ (Nat.succ n))) (a : ℕ) : P a :=
  sorry

theorem sub_induction {P : ℕ → ℕ → Sort u} (H1 : (m : ℕ) → P 0 m) (H2 : (n : ℕ) → P (Nat.succ n) 0)
    (H3 : (n m : ℕ) → P n m → P (Nat.succ n) (Nat.succ m)) (n : ℕ) (m : ℕ) : P n m :=
  sorry

/- addition -/

theorem succ_add_eq_succ_add (n : ℕ) (m : ℕ) : Nat.succ n + m = n + Nat.succ m := sorry

-- theorem one_add (n : ℕ) : 1 + n = succ n := by simp [add_comm]

protected theorem add_right_comm (n : ℕ) (m : ℕ) (k : ℕ) : n + m + k = n + k + m :=
  right_comm Nat.add nat.add_comm nat.add_assoc

theorem eq_zero_of_add_eq_zero {n : ℕ} {m : ℕ} (H : n + m = 0) : n = 0 ∧ m = 0 :=
  { left := eq_zero_of_add_eq_zero_right H, right := eq_zero_of_add_eq_zero_left H }

theorem eq_zero_of_mul_eq_zero {n : ℕ} {m : ℕ} : n * m = 0 → n = 0 ∨ m = 0 := sorry

/- properties of inequality -/

theorem le_succ_of_pred_le {n : ℕ} {m : ℕ} : Nat.pred n ≤ m → n ≤ Nat.succ m :=
  nat.cases_on n less_than_or_equal.step fun (a : ℕ) => succ_le_succ

theorem le_lt_antisymm {n : ℕ} {m : ℕ} (h₁ : n ≤ m) (h₂ : m < n) : False :=
  nat.lt_irrefl n (nat.lt_of_le_of_lt h₁ h₂)

theorem lt_le_antisymm {n : ℕ} {m : ℕ} (h₁ : n < m) (h₂ : m ≤ n) : False := le_lt_antisymm h₂ h₁

protected theorem lt_asymm {n : ℕ} {m : ℕ} (h₁ : n < m) : ¬m < n := le_lt_antisymm (nat.le_of_lt h₁)

protected def lt_ge_by_cases {a : ℕ} {b : ℕ} {C : Sort u} (h₁ : a < b → C) (h₂ : a ≥ b → C) : C :=
  decidable.by_cases h₁ fun (h : ¬a < b) => h₂ sorry

protected def lt_by_cases {a : ℕ} {b : ℕ} {C : Sort u} (h₁ : a < b → C) (h₂ : a = b → C)
    (h₃ : b < a → C) : C :=
  nat.lt_ge_by_cases h₁
    fun (h₁ : a ≥ b) => nat.lt_ge_by_cases h₃ fun (h : b ≥ a) => h₂ (nat.le_antisymm h h₁)

protected theorem lt_trichotomy (a : ℕ) (b : ℕ) : a < b ∨ a = b ∨ b < a :=
  nat.lt_by_cases (fun (h : a < b) => Or.inl h) (fun (h : a = b) => Or.inr (Or.inl h))
    fun (h : b < a) => Or.inr (Or.inr h)

protected theorem eq_or_lt_of_not_lt {a : ℕ} {b : ℕ} (hnlt : ¬a < b) : a = b ∨ b < a :=
  or.resolve_left (nat.lt_trichotomy a b) hnlt

theorem lt_succ_of_lt {a : ℕ} {b : ℕ} (h : a < b) : a < Nat.succ b := le_succ_of_le h

def one_pos : 0 < 1 := nat.zero_lt_one

/- subtraction -/

protected theorem sub_le_sub_left {n : ℕ} {m : ℕ} (k : ℕ) (h : n ≤ m) : k - m ≤ k - n :=
  less_than_or_equal.drec (le_refl (k - n))
    (fun {h_b : ℕ} (h_ᾰ : less_than_or_equal n h_b) (h_ih : k - h_b ≤ k - n) =>
      le_trans (pred_le (Nat.sub k h_b)) h_ih)
    h

theorem succ_sub_sub_succ (n : ℕ) (m : ℕ) (k : ℕ) : Nat.succ n - m - Nat.succ k = n - m - k := sorry

protected theorem sub.right_comm (m : ℕ) (n : ℕ) (k : ℕ) : m - n - k = m - k - n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m - n - k = m - k - n)) (nat.sub_sub m n k)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (m - (n + k) = m - k - n)) (nat.sub_sub m k n)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (m - (n + k) = m - (k + n))) (nat.add_comm n k)))
        (Eq.refl (m - (k + n)))))

theorem mul_pred_left (n : ℕ) (m : ℕ) : Nat.pred n * m = n * m - m := sorry

theorem mul_pred_right (n : ℕ) (m : ℕ) : n * Nat.pred m = n * m - n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n * Nat.pred m = n * m - n)) (nat.mul_comm n (Nat.pred m))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Nat.pred m * n = n * m - n)) (mul_pred_left m n)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (m * n - n = n * m - n)) (nat.mul_comm m n)))
        (Eq.refl (n * m - n))))

protected theorem mul_sub_right_distrib (n : ℕ) (m : ℕ) (k : ℕ) : (n - m) * k = n * k - m * k :=
  sorry

protected theorem mul_sub_left_distrib (n : ℕ) (m : ℕ) (k : ℕ) : n * (m - k) = n * m - n * k :=
  sorry

protected theorem mul_self_sub_mul_self_eq (a : ℕ) (b : ℕ) : a * a - b * b = (a + b) * (a - b) :=
  sorry

theorem succ_mul_succ_eq (a : ℕ) (b : ℕ) : Nat.succ a * Nat.succ b = a * b + a + b + 1 := sorry

theorem succ_sub {m : ℕ} {n : ℕ} (h : m ≥ n) : Nat.succ m - n = Nat.succ (m - n) := sorry

protected theorem sub_pos_of_lt {m : ℕ} {n : ℕ} (h : m < n) : n - m > 0 :=
  (fun (this : 0 + m < n - m + m) => nat.lt_of_add_lt_add_right this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 + m < n - m + m)) (nat.zero_add m)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (m < n - m + m)) (nat.sub_add_cancel (le_of_lt h)))) h))

protected theorem sub_sub_self {n : ℕ} {m : ℕ} (h : m ≤ n) : n - (n - m) = m :=
  iff.mpr (nat.sub_eq_iff_eq_add (sub_le n m)) (Eq.symm (add_sub_of_le h))

protected theorem sub_add_comm {n : ℕ} {m : ℕ} {k : ℕ} (h : k ≤ n) : n + m - k = n - k + m :=
  iff.mpr (nat.sub_eq_iff_eq_add (nat.le_trans h (le_add_right n m)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (n + m = n - k + m + k)) (nat.add_right_comm (n - k) m k)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (n + m = n - k + k + m)) (nat.sub_add_cancel h)))
        (Eq.refl (n + m))))

theorem sub_one_sub_lt {n : ℕ} {i : ℕ} (h : i < n) : n - 1 - i < n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n - 1 - i < n)) (nat.sub_sub n 1 i)))
    (sub_lt (lt_of_lt_of_le (zero_lt_succ i) h)
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 < 1 + i)) (nat.add_comm 1 i))) (zero_lt_succ i)))

theorem pred_inj {a : ℕ} {b : ℕ} : a > 0 → b > 0 → Nat.pred a = Nat.pred b → a = b := sorry

/- find -/

protected def find_x {p : ℕ → Prop} [decidable_pred p] (H : ∃ (n : ℕ), p n) :
    Subtype fun (n : ℕ) => p n ∧ ∀ (m : ℕ), m < n → ¬p m :=
  well_founded.fix (wf_lbp H)
    (fun (m : ℕ)
      (IH :
      (y : ℕ) →
        lbp y m →
          (fun (k : ℕ) =>
              (∀ (n : ℕ), n < k → ¬p n) → Subtype fun (n : ℕ) => p n ∧ ∀ (m : ℕ), m < n → ¬p m)
            y)
      (al : ∀ (n : ℕ), n < m → ¬p n) =>
      dite (p m) (fun (pm : p m) => { val := m, property := sorry })
        fun (pm : ¬p m) => (fun (this : ∀ (n : ℕ), n ≤ m → ¬p n) => IH (m + 1) sorry sorry) sorry)
    0 sorry

protected def find {p : ℕ → Prop} [decidable_pred p] (H : ∃ (n : ℕ), p n) : ℕ :=
  subtype.val (nat.find_x H)

protected theorem find_spec {p : ℕ → Prop} [decidable_pred p] (H : ∃ (n : ℕ), p n) :
    p (nat.find H) :=
  and.left (subtype.property (nat.find_x H))

protected theorem find_min {p : ℕ → Prop} [decidable_pred p] (H : ∃ (n : ℕ), p n) {m : ℕ} :
    m < nat.find H → ¬p m :=
  and.right (subtype.property (nat.find_x H))

protected theorem find_min' {p : ℕ → Prop} [decidable_pred p] (H : ∃ (n : ℕ), p n) {m : ℕ}
    (h : p m) : nat.find H ≤ m :=
  decidable.le_of_not_lt fun (l : m < nat.find H) => nat.find_min H l h

/- mod -/

theorem mod_le (x : ℕ) (y : ℕ) : x % y ≤ x := sorry

@[simp] theorem add_mod_right (x : ℕ) (z : ℕ) : (x + z) % z = x % z :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((x + z) % z = x % z)) (mod_eq_sub_mod (le_add_left z x))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((x + z - z) % z = x % z)) (nat.add_sub_cancel x z)))
      (Eq.refl (x % z)))

@[simp] theorem add_mod_left (x : ℕ) (z : ℕ) : (x + z) % x = z % x :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((x + z) % x = z % x)) (nat.add_comm x z)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((z + x) % x = z % x)) (add_mod_right z x))) (Eq.refl (z % x)))

@[simp] theorem add_mul_mod_self_left (x : ℕ) (y : ℕ) (z : ℕ) : (x + y * z) % y = x % y := sorry

@[simp] theorem add_mul_mod_self_right (x : ℕ) (y : ℕ) (z : ℕ) : (x + y * z) % z = x % z :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((x + y * z) % z = x % z)) (nat.mul_comm y z)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((x + z * y) % z = x % z)) (add_mul_mod_self_left x z y)))
      (Eq.refl (x % z)))

@[simp] theorem mul_mod_right (m : ℕ) (n : ℕ) : m * n % m = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m * n % m = 0)) (Eq.symm (nat.zero_add (m * n)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((0 + m * n) % m = 0)) (add_mul_mod_self_left 0 m n)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 % m = 0)) (zero_mod m))) (Eq.refl 0)))

@[simp] theorem mul_mod_left (m : ℕ) (n : ℕ) : m * n % n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m * n % n = 0)) (nat.mul_comm m n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (n * m % n = 0)) (mul_mod_right n m))) (Eq.refl 0))

theorem mul_mod_mul_left (z : ℕ) (x : ℕ) (y : ℕ) : z * x % (z * y) = z * (x % y) := sorry

theorem mul_mod_mul_right (z : ℕ) (x : ℕ) (y : ℕ) : x * z % (y * z) = x % y * z := sorry

theorem cond_to_bool_mod_two (x : ℕ) [d : Decidable (x % bit0 1 = 1)] :
    cond (to_bool (x % bit0 1 = 1)) 1 0 = x % bit0 1 :=
  sorry

theorem sub_mul_mod (x : ℕ) (k : ℕ) (n : ℕ) (h₁ : n * k ≤ x) : (x - n * k) % n = x % n := sorry

/- div -/

theorem sub_mul_div (x : ℕ) (n : ℕ) (p : ℕ) (h₁ : n * p ≤ x) : (x - n * p) / n = x / n - p := sorry

theorem div_mul_le_self (m : ℕ) (n : ℕ) : m / n * n ≤ m := sorry

@[simp] theorem add_div_right (x : ℕ) {z : ℕ} (H : z > 0) : (x + z) / z = Nat.succ (x / z) :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl ((x + z) / z = Nat.succ (x / z))) (div_eq_sub_div H (le_add_left z x))))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl ((x + z - z) / z + 1 = Nat.succ (x / z))) (nat.add_sub_cancel x z)))
      (Eq.refl (x / z + 1)))

@[simp] theorem add_div_left (x : ℕ) {z : ℕ} (H : z > 0) : (z + x) / z = Nat.succ (x / z) :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((z + x) / z = Nat.succ (x / z))) (nat.add_comm z x)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((x + z) / z = Nat.succ (x / z))) (add_div_right x H)))
      (Eq.refl (Nat.succ (x / z))))

@[simp] theorem mul_div_right (n : ℕ) {m : ℕ} (H : m > 0) : m * n / m = n := sorry

@[simp] theorem mul_div_left (m : ℕ) {n : ℕ} (H : n > 0) : m * n / n = m :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m * n / n = m)) (nat.mul_comm m n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (n * m / n = m)) (mul_div_right m H))) (Eq.refl m))

protected theorem div_self {n : ℕ} (H : n > 0) : n / n = 1 :=
  let t : (0 + n) / n = Nat.succ (0 / n) := add_div_right 0 H;
  eq.mp (Eq._oldrec (Eq.refl (n / n = Nat.succ (0 / n))) (nat.zero_div n))
    (eq.mp (Eq._oldrec (Eq.refl ((0 + n) / n = Nat.succ (0 / n))) (nat.zero_add n)) t)

theorem add_mul_div_left (x : ℕ) (z : ℕ) {y : ℕ} (H : y > 0) : (x + y * z) / y = x / y + z := sorry

theorem add_mul_div_right (x : ℕ) (y : ℕ) {z : ℕ} (H : z > 0) : (x + y * z) / z = x / z + y :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((x + y * z) / z = x / z + y)) (nat.mul_comm y z)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((x + z * y) / z = x / z + y)) (add_mul_div_left x y H)))
      (Eq.refl (x / z + y)))

protected theorem mul_div_cancel (m : ℕ) {n : ℕ} (H : n > 0) : m * n / n = m := sorry

protected theorem mul_div_cancel_left (m : ℕ) {n : ℕ} (H : n > 0) : n * m / n = m :=
  eq.mpr (id (Eq._oldrec (Eq.refl (n * m / n = m)) (nat.mul_comm n m)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (m * n / n = m)) (nat.mul_div_cancel m H))) (Eq.refl m))

protected theorem div_eq_of_eq_mul_left {m : ℕ} {n : ℕ} {k : ℕ} (H1 : n > 0) (H2 : m = k * n) :
    m / n = k :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m / n = k)) H2))
    (eq.mpr (id (Eq._oldrec (Eq.refl (k * n / n = k)) (nat.mul_div_cancel k H1))) (Eq.refl k))

protected theorem div_eq_of_eq_mul_right {m : ℕ} {n : ℕ} {k : ℕ} (H1 : n > 0) (H2 : m = n * k) :
    m / n = k :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m / n = k)) H2))
    (eq.mpr (id (Eq._oldrec (Eq.refl (n * k / n = k)) (nat.mul_div_cancel_left k H1))) (Eq.refl k))

protected theorem div_eq_of_lt_le {m : ℕ} {n : ℕ} {k : ℕ} (lo : k * n ≤ m)
    (hi : m < Nat.succ k * n) : m / n = k :=
  sorry

theorem mul_sub_div (x : ℕ) (n : ℕ) (p : ℕ) (h₁ : x < n * p) :
    (n * p - Nat.succ x) / n = p - Nat.succ (x / n) :=
  sorry

protected theorem mul_pos {a : ℕ} {b : ℕ} (ha : a > 0) (hb : b > 0) : a * b > 0 :=
  (fun (h : 0 * b < a * b) => eq.mp (Eq._oldrec (Eq.refl (0 * b < a * b)) (nat.zero_mul b)) h)
    (nat.mul_lt_mul_of_pos_right ha hb)

protected theorem div_div_eq_div_mul (m : ℕ) (n : ℕ) (k : ℕ) : m / n / k = m / (n * k) := sorry

protected theorem mul_div_mul {m : ℕ} (n : ℕ) (k : ℕ) (H : m > 0) : m * n / (m * k) = n / k :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (m * n / (m * k) = n / k))
        (Eq.symm (nat.div_div_eq_div_mul (m * n) m k))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (m * n / m / k = n / k)) (nat.mul_div_cancel_left n H)))
      (Eq.refl (n / k)))

/- dvd -/

@[simp] protected theorem dvd_mul_right (a : ℕ) (b : ℕ) : a ∣ a * b := Exists.intro b rfl

protected theorem dvd_trans {a : ℕ} {b : ℕ} {c : ℕ} (h₁ : a ∣ b) (h₂ : b ∣ c) : a ∣ c := sorry

protected theorem eq_zero_of_zero_dvd {a : ℕ} (h : 0 ∣ a) : a = 0 :=
  exists.elim h fun (c : ℕ) (H' : a = 0 * c) => Eq.trans H' (nat.zero_mul c)

protected theorem dvd_add {a : ℕ} {b : ℕ} {c : ℕ} (h₁ : a ∣ b) (h₂ : a ∣ c) : a ∣ b + c := sorry

protected theorem dvd_add_iff_right {k : ℕ} {m : ℕ} {n : ℕ} (h : k ∣ m) : k ∣ n ↔ k ∣ m + n := sorry

protected theorem dvd_add_iff_left {k : ℕ} {m : ℕ} {n : ℕ} (h : k ∣ n) : k ∣ m ↔ k ∣ m + n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (k ∣ m ↔ k ∣ m + n)) (nat.add_comm m n)))
    (nat.dvd_add_iff_right h)

theorem dvd_sub {k : ℕ} {m : ℕ} {n : ℕ} (H : n ≤ m) (h₁ : k ∣ m) (h₂ : k ∣ n) : k ∣ m - n :=
  iff.mpr (nat.dvd_add_iff_left h₂)
    (eq.mpr (id (Eq._oldrec (Eq.refl (k ∣ m - n + n)) (nat.sub_add_cancel H))) h₁)

theorem dvd_mod_iff {k : ℕ} {m : ℕ} {n : ℕ} (h : k ∣ n) : k ∣ m % n ↔ k ∣ m :=
  let t : k ∣ m % n ↔ k ∣ m % n + n * (m / n) :=
    nat.dvd_add_iff_left (nat.dvd_trans h (nat.dvd_mul_right n (m / n)));
  eq.mp (Eq._oldrec (Eq.refl (k ∣ m % n ↔ k ∣ m % n + n * (m / n))) (mod_add_div m n)) t

theorem le_of_dvd {m : ℕ} {n : ℕ} (h : n > 0) : m ∣ n → m ≤ n := sorry

theorem dvd_antisymm {m : ℕ} {n : ℕ} : m ∣ n → n ∣ m → m = n := sorry

theorem pos_of_dvd_of_pos {m : ℕ} {n : ℕ} (H1 : m ∣ n) (H2 : n > 0) : m > 0 := sorry

theorem eq_one_of_dvd_one {n : ℕ} (H : n ∣ 1) : n = 1 :=
  le_antisymm (le_of_dvd (of_as_true trivial) H) (pos_of_dvd_of_pos H (of_as_true trivial))

theorem dvd_of_mod_eq_zero {m : ℕ} {n : ℕ} (H : n % m = 0) : m ∣ n :=
  Exists.intro (n / m)
    (eq.mp (Eq._oldrec (Eq.refl (n = 0 + m * (n / m))) (nat.zero_add (m * (n / m))))
      (eq.mp (Eq._oldrec (Eq.refl (n = n % m + m * (n / m))) H) (Eq.symm (mod_add_div n m))))

theorem mod_eq_zero_of_dvd {m : ℕ} {n : ℕ} (H : m ∣ n) : n % m = 0 := sorry

theorem dvd_iff_mod_eq_zero (m : ℕ) (n : ℕ) : m ∣ n ↔ n % m = 0 :=
  { mp := mod_eq_zero_of_dvd, mpr := dvd_of_mod_eq_zero }

protected instance decidable_dvd : DecidableRel has_dvd.dvd :=
  fun (m n : ℕ) => decidable_of_decidable_of_iff (nat.decidable_eq (n % m) 0) sorry

protected theorem mul_div_cancel' {m : ℕ} {n : ℕ} (H : n ∣ m) : n * (m / n) = m :=
  let t : m % n + n * (m / n) = m := mod_add_div m n;
  eq.mp (Eq._oldrec (Eq.refl (0 + n * (m / n) = m)) (nat.zero_add (n * (m / n))))
    (eq.mp (Eq._oldrec (Eq.refl (m % n + n * (m / n) = m)) (mod_eq_zero_of_dvd H)) t)

protected theorem div_mul_cancel {m : ℕ} {n : ℕ} (H : n ∣ m) : m / n * n = m :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m / n * n = m)) (nat.mul_comm (m / n) n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (n * (m / n) = m)) (nat.mul_div_cancel' H))) (Eq.refl m))

protected theorem mul_div_assoc (m : ℕ) {n : ℕ} {k : ℕ} (H : k ∣ n) : m * n / k = m * (n / k) :=
  sorry

theorem dvd_of_mul_dvd_mul_left {m : ℕ} {n : ℕ} {k : ℕ} (kpos : k > 0) (H : k * m ∣ k * n) :
    m ∣ n :=
  sorry

theorem dvd_of_mul_dvd_mul_right {m : ℕ} {n : ℕ} {k : ℕ} (kpos : k > 0) (H : m * k ∣ n * k) :
    m ∣ n :=
  dvd_of_mul_dvd_mul_left kpos
    (eq.mp (Eq._oldrec (Eq.refl (k * m ∣ n * k)) (nat.mul_comm n k))
      (eq.mp (Eq._oldrec (Eq.refl (m * k ∣ n * k)) (nat.mul_comm m k)) H))

/- --- -/

protected theorem mul_le_mul_of_nonneg_left {a : ℕ} {b : ℕ} {c : ℕ} (h₁ : a ≤ b) : c * a ≤ c * b :=
  sorry

protected theorem mul_le_mul_of_nonneg_right {a : ℕ} {b : ℕ} {c : ℕ} (h₁ : a ≤ b) : a * c ≤ b * c :=
  sorry

protected theorem mul_lt_mul {a : ℕ} {b : ℕ} {c : ℕ} {d : ℕ} (hac : a < c) (hbd : b ≤ d)
    (pos_b : 0 < b) : a * b < c * d :=
  lt_of_lt_of_le (nat.mul_lt_mul_of_pos_right hac pos_b) (nat.mul_le_mul_of_nonneg_left hbd)

protected theorem mul_lt_mul' {a : ℕ} {b : ℕ} {c : ℕ} {d : ℕ} (h1 : a ≤ c) (h2 : b < d)
    (h3 : 0 < c) : a * b < c * d :=
  lt_of_le_of_lt (nat.mul_le_mul_of_nonneg_right h1) (nat.mul_lt_mul_of_pos_left h2 h3)

-- TODO: there are four variations, depending on which variables we assume to be nonneg

protected theorem mul_le_mul {a : ℕ} {b : ℕ} {c : ℕ} {d : ℕ} (hac : a ≤ c) (hbd : b ≤ d) :
    a * b ≤ c * d :=
  le_trans (nat.mul_le_mul_of_nonneg_right hac) (nat.mul_le_mul_of_nonneg_left hbd)

theorem div_lt_self {n : ℕ} {m : ℕ} : n > 0 → m > 1 → n / m < n := sorry

end Mathlib