/-
Copyright (c) 2020 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Mario Carneiro, Yury G. Kudryashov

! This file was ported from Lean 3 source module order.rel_classes
! leanprover-community/mathlib commit bc7d81beddb3d6c66f71449c5bc76c38cb77cf9e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Order.Basic
import Mathlib.Logic.IsEmpty
import Mathlib.Tactic.MkIffOfInductiveProp

/-!
# Unbundled relation classes

In this file we prove some properties of `Is*` classes defined in `Init.Algebra.Classes`. The main
difference between these classes and the usual order classes (`Preorder` etc) is that usual classes
extend `LE` and/or `LT` while these classes take a relation as an explicit argument.

-/

universe u v

variable {α : Type u} {β : Type v} {r : α → α → Prop} {s : β → β → Prop}

open Function

theorem of_eq [IsRefl α r] : ∀ {a b}, a = b → r a b
  | _, _, .refl _ => refl _
#align of_eq of_eq

theorem comm [IsSymm α r] {a b : α} : r a b ↔ r b a :=
  ⟨symm, symm⟩
#align comm comm

theorem antisymm' [IsAntisymm α r] {a b : α} : r a b → r b a → b = a := fun h h' => antisymm h' h
#align antisymm' antisymm'

theorem antisymm_iff [IsRefl α r] [IsAntisymm α r] {a b : α} : r a b ∧ r b a ↔ a = b :=
  ⟨fun h => antisymm h.1 h.2, by
    rintro rfl
    exact ⟨refl _, refl _⟩⟩
#align antisymm_iff antisymm_iff

/-- A version of `antisymm` with `r` explicit.

This lemma matches the lemmas from lean core in `Init.Algebra.Classes`, but is missing there.  -/
@[elab_without_expected_type]
theorem antisymm_of (r : α → α → Prop) [IsAntisymm α r] {a b : α} : r a b → r b a → a = b :=
  antisymm
#align antisymm_of antisymm_of

/-- A version of `antisymm'` with `r` explicit.

This lemma matches the lemmas from lean core in `Init.Algebra.Classes`, but is missing there.  -/
@[elab_without_expected_type]
theorem antisymm_of' (r : α → α → Prop) [IsAntisymm α r] {a b : α} : r a b → r b a → b = a :=
  antisymm'
#align antisymm_of' antisymm_of'

/-- A version of `comm` with `r` explicit.

This lemma matches the lemmas from lean core in `Init.Algebra.Classes`, but is missing there.  -/
theorem comm_of (r : α → α → Prop) [IsSymm α r] {a b : α} : r a b ↔ r b a :=
  comm
#align comm_of comm_of

theorem IsRefl.swap (r) [IsRefl α r] : IsRefl α (swap r) :=
  ⟨refl_of r⟩
#align is_refl.swap IsRefl.swap

theorem IsIrrefl.swap (r) [IsIrrefl α r] : IsIrrefl α (swap r) :=
  ⟨irrefl_of r⟩
#align is_irrefl.swap IsIrrefl.swap

theorem IsTrans.swap (r) [IsTrans α r] : IsTrans α (swap r) :=
  ⟨fun _ _ _ h₁ h₂ => trans_of r h₂ h₁⟩
#align is_trans.swap IsTrans.swap

theorem IsAntisymm.swap (r) [IsAntisymm α r] : IsAntisymm α (swap r) :=
  ⟨fun _ _ h₁ h₂ => _root_.antisymm h₂ h₁⟩
#align is_antisymm.swap IsAntisymm.swap

theorem IsAsymm.swap (r) [IsAsymm α r] : IsAsymm α (swap r) :=
  ⟨fun _ _ h₁ h₂ => asymm_of r h₂ h₁⟩
#align is_asymm.swap IsAsymm.swap

theorem IsTotal.swap (r) [IsTotal α r] : IsTotal α (swap r) :=
  ⟨fun a b => (total_of r a b).symm⟩
#align is_total.swap IsTotal.swap

theorem IsTrichotomous.swap (r) [IsTrichotomous α r] : IsTrichotomous α (swap r) :=
  ⟨fun a b => by simpa [Function.swap, or_comm, or_left_comm] using trichotomous_of r a b⟩
#align is_trichotomous.swap IsTrichotomous.swap

theorem IsPreorder.swap (r) [IsPreorder α r] : IsPreorder α (swap r) :=
  { @IsRefl.swap α r _, @IsTrans.swap α r _ with }
#align is_preorder.swap IsPreorder.swap

theorem IsStrictOrder.swap (r) [IsStrictOrder α r] : IsStrictOrder α (swap r) :=
  { @IsIrrefl.swap α r _, @IsTrans.swap α r _ with }
#align is_strict_order.swap IsStrictOrder.swap

theorem IsPartialOrder.swap (r) [IsPartialOrder α r] : IsPartialOrder α (swap r) :=
  { @IsPreorder.swap α r _, @IsAntisymm.swap α r _ with }
#align is_partial_order.swap IsPartialOrder.swap

theorem IsTotalPreorder.swap (r) [IsTotalPreorder α r] : IsTotalPreorder α (swap r) :=
  { @IsPreorder.swap α r _, @IsTotal.swap α r _ with }
#align is_total_preorder.swap IsTotalPreorder.swap

theorem IsLinearOrder.swap (r) [IsLinearOrder α r] : IsLinearOrder α (swap r) :=
  { @IsPartialOrder.swap α r _, @IsTotal.swap α r _ with }
#align is_linear_order.swap IsLinearOrder.swap

protected theorem IsAsymm.isAntisymm (r) [IsAsymm α r] : IsAntisymm α r :=
  ⟨fun _ _ h₁ h₂ => (_root_.asymm h₁ h₂).elim⟩
#align is_asymm.is_antisymm IsAsymm.isAntisymm

protected theorem IsAsymm.isIrrefl [IsAsymm α r] : IsIrrefl α r :=
  ⟨fun _ h => _root_.asymm h h⟩
#align is_asymm.is_irrefl IsAsymm.isIrrefl

protected theorem IsTotal.isTrichotomous (r) [IsTotal α r] : IsTrichotomous α r :=
  ⟨fun a b => or_left_comm.1 (Or.inr <| total_of r a b)⟩
#align is_total.is_trichotomous IsTotal.isTrichotomous

-- see Note [lower instance priority]
instance (priority := 100) IsTotal.to_isRefl (r) [IsTotal α r] : IsRefl α r :=
  ⟨fun a => (or_self_iff _).1 <| total_of r a a⟩

theorem ne_of_irrefl {r} [IsIrrefl α r] : ∀ {x y : α}, r x y → x ≠ y
  | _, _, h, rfl => irrefl _ h
#align ne_of_irrefl ne_of_irrefl

theorem ne_of_irrefl' {r} [IsIrrefl α r] : ∀ {x y : α}, r x y → y ≠ x
  | _, _, h, rfl => irrefl _ h
#align ne_of_irrefl' ne_of_irrefl'

theorem not_rel_of_subsingleton (r) [IsIrrefl α r] [Subsingleton α] (x y) : ¬r x y :=
  Subsingleton.elim x y ▸ irrefl x
#align not_rel_of_subsingleton not_rel_of_subsingleton

theorem rel_of_subsingleton (r) [IsRefl α r] [Subsingleton α] (x y) : r x y :=
  Subsingleton.elim x y ▸ refl x
#align rel_of_subsingleton rel_of_subsingleton

@[simp]
theorem empty_relation_apply (a b : α) : EmptyRelation a b ↔ False :=
  Iff.rfl
#align empty_relation_apply empty_relation_apply

theorem eq_empty_relation (r) [IsIrrefl α r] [Subsingleton α] : r = EmptyRelation :=
  funext₂ <| by simpa using not_rel_of_subsingleton r
#align eq_empty_relation eq_empty_relation

instance : IsIrrefl α EmptyRelation :=
  ⟨fun _ => id⟩

theorem trans_trichotomous_left [IsTrans α r] [IsTrichotomous α r] {a b c : α} :
    ¬r b a → r b c → r a c := by
  intro h₁ h₂
  rcases trichotomous_of r a b with (h₃ | rfl | h₃)
  exacts [_root_.trans h₃ h₂, h₂, absurd h₃ h₁]
#align trans_trichotomous_left trans_trichotomous_left

theorem trans_trichotomous_right [IsTrans α r] [IsTrichotomous α r] {a b c : α} :
    r a b → ¬r c b → r a c := by
  intro h₁ h₂
  rcases trichotomous_of r b c with (h₃ | rfl | h₃)
  exacts [_root_.trans h₁ h₃, h₁, absurd h₃ h₂]
#align trans_trichotomous_right trans_trichotomous_right

theorem transitive_of_trans (r : α → α → Prop) [IsTrans α r] : Transitive r := IsTrans.trans
#align transitive_of_trans transitive_of_trans

/-- In a trichotomous irreflexive order, every element is determined by the set of predecessors. -/
theorem extensional_of_trichotomous_of_irrefl (r : α → α → Prop) [IsTrichotomous α r] [IsIrrefl α r]
    {a b : α} (H : ∀ x, r x a ↔ r x b) : a = b :=
  ((@trichotomous _ r _ a b).resolve_left <| mt (H _).2 <| irrefl a).resolve_right <| mt (H _).1
    <| irrefl b
#align extensional_of_trichotomous_of_irrefl extensional_of_trichotomous_of_irrefl

/-- Construct a partial order from a `isStrictOrder` relation.

See note [reducible non-instances]. -/
@[reducible]
def partialOrderOfSO (r) [IsStrictOrder α r] : PartialOrder α where
  le x y := x = y ∨ r x y
  lt := r
  le_refl x := Or.inl rfl
  le_trans x y z h₁ h₂ :=
    match y, z, h₁, h₂ with
    | _, _, Or.inl rfl, h₂ => h₂
    | _, _, h₁, Or.inl rfl => h₁
    | _, _, Or.inr h₁, Or.inr h₂ => Or.inr (_root_.trans h₁ h₂)
  le_antisymm x y h₁ h₂ :=
    match y, h₁, h₂ with
    | _, Or.inl rfl, _ => rfl
    | _, _, Or.inl rfl => rfl
    | _, Or.inr h₁, Or.inr h₂ => (asymm h₁ h₂).elim
  lt_iff_le_not_le x y :=
    ⟨fun h => ⟨Or.inr h, not_or_of_not (fun e => by rw [e] at h; exact irrefl _ h) (asymm h)⟩,
      fun ⟨h₁, h₂⟩ => h₁.resolve_left fun e => h₂ <| e ▸ Or.inl rfl⟩
set_option linter.uppercaseLean3 false in
#align partial_order_of_SO partialOrderOfSO

/-- Construct a linear order from an `IsStrictTotalOrder` relation.

See note [reducible non-instances]. -/
@[reducible]
def linearOrderOfSTO (r) [IsStrictTotalOrder α r] [∀ x y, Decidable ¬r x y] : LinearOrder α :=
  let hD : DecidableRel (fun x y => x = y ∨ r x y) := fun x y =>
      decidable_of_iff (¬r y x)
        ⟨fun h => ((trichotomous_of r y x).resolve_left h).imp Eq.symm id, fun h =>
          h.elim (fun h => h ▸ irrefl_of _ _) (asymm_of r)⟩
  { partialOrderOfSO r with
    le_total := fun x y =>
      match y, trichotomous_of r x y with
      | y, Or.inl h => Or.inl (Or.inr h)
      | _, Or.inr (Or.inl rfl) => Or.inl (Or.inl rfl)
      | _, Or.inr (Or.inr h) => Or.inr (Or.inr h),
    toMin := minOfLe,
    toMax := maxOfLe,
    decidable_le := hD }
set_option linter.uppercaseLean3 false in
#align linear_order_of_STO linearOrderOfSTO

theorem IsStrictTotalOrder.swap (r) [IsStrictTotalOrder α r] : IsStrictTotalOrder α (swap r) :=
  { IsTrichotomous.swap r, IsStrictOrder.swap r with }
#align is_strict_total_order.swap IsStrictTotalOrder.swap

/-! ### Order connection -/

/-- A connected order is one satisfying the condition `a < c → a < b ∨ b < c`.
  This is recognizable as an intuitionistic substitute for `a ≤ b ∨ b ≤ a` on
  the constructive reals, and is also known as negative transitivity,
  since the contrapositive asserts transitivity of the relation `¬ a < b`.  -/
class IsOrderConnected (α : Type u) (lt : α → α → Prop) : Prop where
  /-- A connected order is one satisfying the condition `a < c → a < b ∨ b < c`. -/
  conn : ∀ a b c, lt a c → lt a b ∨ lt b c
#align is_order_connected IsOrderConnected

theorem IsOrderConnected.neg_trans {r : α → α → Prop} [IsOrderConnected α r] {a b c}
    (h₁ : ¬r a b) (h₂ : ¬r b c) : ¬r a c :=
  mt (IsOrderConnected.conn a b c) <| by simp [h₁, h₂]
#align is_order_connected.neg_trans IsOrderConnected.neg_trans

theorem isStrictWeakOrder_of_isOrderConnected [IsAsymm α r] [IsOrderConnected α r] :
    IsStrictWeakOrder α r :=
  { @IsAsymm.isIrrefl α r _ with
    trans := fun _ _ c h₁ h₂ => (IsOrderConnected.conn _ c _ h₁).resolve_right (asymm h₂),
    incomp_trans := fun _ _ _ ⟨h₁, h₂⟩ ⟨h₃, h₄⟩ =>
      ⟨IsOrderConnected.neg_trans h₁ h₃, IsOrderConnected.neg_trans h₄ h₂⟩ }
#align is_strict_weak_order_of_is_order_connected isStrictWeakOrder_of_isOrderConnected

-- see Note [lower instance priority]
instance (priority := 100) isStrictOrderConnected_of_isStrictTotalOrder [IsStrictTotalOrder α r] :
    IsOrderConnected α r :=
  ⟨λ _ _ _ h => (trichotomous _ _).imp_right
    fun o => o.elim (fun e => e ▸ h) fun h' => _root_.trans h' h⟩
#align is_order_connected_of_is_strict_total_order isStrictOrderConnected_of_isStrictTotalOrder

-- see Note [lower instance priority]
instance (priority := 100) isStrictTotalOrder_of_isStrictTotalOrder [IsStrictTotalOrder α r] :
    IsStrictWeakOrder α r :=
  { isStrictWeakOrder_of_isOrderConnected with }
#align is_strict_weak_order_of_is_strict_total_order isStrictTotalOrder_of_isStrictTotalOrder

/-! ### Well-order -/

/-- A well-founded relation. Not to be confused with `isWellOrder`. -/
@[mk_iff] class IsWellFounded (α : Type u) (r : α → α → Prop) : Prop where
  /-- The relation is `WellFounded`, as a proposition. -/
  wf : WellFounded r
#align is_well_founded IsWellFounded
#align is_well_founded_iff IsWellFounded_iff

#align has_well_founded WellFoundedRelation
set_option linter.uppercaseLean3 false in
#align has_well_founded.R WellFoundedRelation.rel
instance [h : WellFoundedRelation α] : IsWellFounded α WellFoundedRelation.rel :=
  { h with }

theorem WellFoundedRelation.asymmetric {α : Sort _} [WellFoundedRelation α] {a b : α} :
    WellFoundedRelation.rel a b → ¬ WellFoundedRelation.rel b a :=
  fun hab hba => WellFoundedRelation.asymmetric hba hab
termination_by _ => a

lemma WellFounded.prod_lex {ra : α → α → Prop} {rb : β → β → Prop} (ha : WellFounded ra)
    (hb : WellFounded rb) : WellFounded (Prod.Lex ra rb) :=
  (Prod.lex ⟨_, ha⟩ ⟨_, hb⟩).wf
#align prod.lex_wf WellFounded.prod_lex

namespace IsWellFounded

variable (r) [IsWellFounded α r]

/-- Induction on a well-founded relation. -/
theorem induction {C : α → Prop} : ∀ a, (∀ x, (∀ y, r y x → C y) → C x) → C a :=
  wf.induction
#align is_well_founded.induction IsWellFounded.induction

/-- All values are accessible under the well-founded relation. -/
theorem apply : ∀ a, Acc r a :=
  wf.apply
#align is_well_founded.apply IsWellFounded.apply

-- Porting note: WellFounded.fix is noncomputable, at 2022-10-29 lean
/-- Creates data, given a way to generate a value from all that compare as less under a well-founded
relation. See also `IsWellFounded.fix_eq`. -/
noncomputable
def fix {C : α → Sort _} : (∀ x : α, (∀ y : α, r y x → C y) → C x) → ∀ x : α, C x :=
  wf.fix
#align is_well_founded.fix IsWellFounded.fix

/-- The value from `IsWellFounded.fix` is built from the previous ones as specified. -/
theorem fix_eq {C : α → Sort _} (F : ∀ x : α, (∀ y : α, r y x → C y) → C x) :
    ∀ x, fix r F x = F x fun y _ => fix r F y :=
  wf.fix_eq F
#align is_well_founded.fix_eq IsWellFounded.fix_eq

/-- Derive a `WellFoundedRelation` instance from an `isWellFounded` instance. -/
def toWellFoundedRelation : WellFoundedRelation α :=
  ⟨r, IsWellFounded.wf⟩

end IsWellFounded

theorem WellFounded.asymmetric {α : Sort _} {r : α → α → Prop} (h : WellFounded r) (a b) :
    r a b → ¬r b a :=
  @WellFoundedRelation.asymmetric _ ⟨_, h⟩ _ _
#align well_founded.asymmetric WellFounded.asymmetric

-- see Note [lower instance priority]
instance (priority := 100) (r : α → α → Prop) [IsWellFounded α r] : IsAsymm α r :=
  ⟨IsWellFounded.wf.asymmetric⟩

-- see Note [lower instance priority]
instance (priority := 100) (r : α → α → Prop) [IsWellFounded α r] : IsIrrefl α r :=
  IsAsymm.isIrrefl

/-- A class for a well founded relation `<`. -/
@[reducible]
def WellFoundedLT (α : Type _) [LT α] : Prop :=
  IsWellFounded α (· < ·)
#align well_founded_lt WellFoundedLT

/-- A class for a well founded relation `>`. -/
@[reducible]
def WellFoundedGT (α : Type _) [LT α] : Prop :=
  IsWellFounded α (· > ·)
#align well_founded_gt WellFoundedGT

-- See note [lower instance priority]
instance (priority := 100) (α : Type _) [LT α] [h : WellFoundedLT α] : WellFoundedGT αᵒᵈ :=
  h

-- See note [lower instance priority]
instance (priority := 100) (α : Type _) [LT α] [h : WellFoundedGT α] : WellFoundedLT αᵒᵈ :=
  h

theorem wellFoundedGT_dual_iff (α : Type _) [LT α] : WellFoundedGT αᵒᵈ ↔ WellFoundedLT α :=
  ⟨fun h => ⟨h.wf⟩, fun h => ⟨h.wf⟩⟩
#align well_founded_gt_dual_iff wellFoundedGT_dual_iff

theorem wellFoundedLT_dual_iff (α : Type _) [LT α] : WellFoundedLT αᵒᵈ ↔ WellFoundedGT α :=
  ⟨fun h => ⟨h.wf⟩, fun h => ⟨h.wf⟩⟩
#align well_founded_lt_dual_iff wellFoundedLT_dual_iff

/-- A well order is a well-founded linear order. -/
class IsWellOrder (α : Type u) (r : α → α → Prop) extends
  IsTrichotomous α r, IsTrans α r, IsWellFounded α r : Prop
#align is_well_order IsWellOrder

-- see Note [lower instance priority]
instance (priority := 100) {α} (r : α → α → Prop) [IsWellOrder α r] :
    IsStrictTotalOrder α r where

-- see Note [lower instance priority]
instance (priority := 100) {α} (r : α → α → Prop) [IsWellOrder α r] : IsTrichotomous α r :=
  by infer_instance

-- see Note [lower instance priority]
instance (priority := 100) {α} (r : α → α → Prop) [IsWellOrder α r] : IsTrans α r := by
  infer_instance

-- see Note [lower instance priority]
instance (priority := 100) {α} (r : α → α → Prop) [IsWellOrder α r] : IsIrrefl α r := by
  infer_instance

-- see Note [lower instance priority]
instance (priority := 100) {α} (r : α → α → Prop) [IsWellOrder α r] : IsAsymm α r := by
  infer_instance

namespace WellFoundedLT

variable [LT α] [WellFoundedLT α]

/-- Inducts on a well-founded `<` relation. -/
theorem induction {C : α → Prop} : ∀ a, (∀ x, (∀ y, y < x → C y) → C x) → C a :=
  IsWellFounded.induction _
#align well_founded_lt.induction WellFoundedLT.induction

/-- All values are accessible under the well-founded `<`. -/
theorem apply : ∀ a : α, Acc (· < ·) a :=
  IsWellFounded.apply _
#align well_founded_lt.apply WellFoundedLT.apply

-- Porting note: WellFounded.fix is noncomputable, at 2022-10-29 lean
/-- Creates data, given a way to generate a value from all that compare as lesser. See also
`WellFoundedLT.fix_eq`. -/
noncomputable
def fix {C : α → Sort _} : (∀ x : α, (∀ y : α, y < x → C y) → C x) → ∀ x : α, C x :=
  IsWellFounded.fix (· < ·)
#align well_founded_lt.fix WellFoundedLT.fix

/-- The value from `WellFoundedLT.fix` is built from the previous ones as specified. -/
theorem fix_eq {C : α → Sort _} (F : ∀ x : α, (∀ y : α, y < x → C y) → C x) :
    ∀ x, fix F x = F x fun y _ => fix F y :=
  IsWellFounded.fix_eq _ F
#align well_founded_lt.fix_eq WellFoundedLT.fix_eq

/-- Derive a `WellFoundedRelation` instance from a `WellFoundedLT` instance. -/
def toWellFoundedRelation : WellFoundedRelation α :=
  IsWellFounded.toWellFoundedRelation (· < ·)

end WellFoundedLT

namespace WellFoundedGT

variable [LT α] [WellFoundedGT α]

/-- Inducts on a well-founded `>` relation. -/
theorem induction {C : α → Prop} : ∀ a, (∀ x, (∀ y, x < y → C y) → C x) → C a :=
  IsWellFounded.induction _
#align well_founded_gt.induction WellFoundedGT.induction

/-- All values are accessible under the well-founded `>`. -/
theorem apply : ∀ a : α, Acc (· > ·) a :=
  IsWellFounded.apply _
#align well_founded_gt.apply WellFoundedGT.apply

-- Porting note: WellFounded.fix is noncomputable, at 2022-10-29 lean
/-- Creates data, given a way to generate a value from all that compare as greater. See also
`WellFoundedGT.fix_eq`. -/
noncomputable
def fix {C : α → Sort _} : (∀ x : α, (∀ y : α, x < y → C y) → C x) → ∀ x : α, C x :=
  IsWellFounded.fix (· > ·)
#align well_founded_gt.fix WellFoundedGT.fix

/-- The value from `WellFoundedGT.fix` is built from the successive ones as specified. -/
theorem fix_eq {C : α → Sort _} (F : ∀ x : α, (∀ y : α, x < y → C y) → C x) :
    ∀ x, fix F x = F x fun y _ => fix F y :=
  IsWellFounded.fix_eq _ F
#align well_founded_gt.fix_eq WellFoundedGT.fix_eq

/-- Derive a `WellFoundedRelation` instance from a `WellFoundedGT` instance. -/
def toWellFoundedRelation : WellFoundedRelation α :=
  IsWellFounded.toWellFoundedRelation (· > ·)

end WellFoundedGT

/-- Construct a decidable linear order from a well-founded linear order. -/
noncomputable def IsWellOrder.linearOrder (r : α → α → Prop) [IsWellOrder α r] : LinearOrder α :=
  letI := fun x y => Classical.dec ¬r x y
  linearOrderOfSTO r
#align is_well_order.linear_order IsWellOrder.linearOrder

/-- Derive a `WellFoundedRelation` instance from a `IsWellOrder` instance. -/
def IsWellOrder.toHasWellFounded [LT α] [hwo : IsWellOrder α (· < ·)] : WellFoundedRelation α where
  rel := (· < ·)
  wf := hwo.wf
#align is_well_order.to_has_well_founded IsWellOrder.toHasWellFounded

-- This isn't made into an instance as it loops with `IsIrrefl α r`.
theorem Subsingleton.isWellOrder [Subsingleton α] (r : α → α → Prop) [hr : IsIrrefl α r] :
    IsWellOrder α r :=
  { hr with
    trichotomous := fun a b => Or.inr <| Or.inl <| Subsingleton.elim a b,
    trans := fun a b _ h => (not_rel_of_subsingleton r a b h).elim,
    wf := ⟨fun a => ⟨_, fun y h => (not_rel_of_subsingleton r y a h).elim⟩⟩ }
#align subsingleton.is_well_order Subsingleton.isWellOrder

instance [Subsingleton α] : IsWellOrder α EmptyRelation :=
  Subsingleton.isWellOrder _

instance (priority := 100) [IsEmpty α] (r : α → α → Prop) : IsWellOrder α r where
  trichotomous := isEmptyElim
  trans := isEmptyElim
  wf := wellFounded_of_isEmpty r

instance [IsWellFounded α r] [IsWellFounded β s] : IsWellFounded (α × β) (Prod.Lex r s) :=
  ⟨IsWellFounded.wf.prod_lex IsWellFounded.wf⟩

instance [IsWellOrder α r] [IsWellOrder β s] : IsWellOrder (α × β) (Prod.Lex r s) where
  trichotomous := fun ⟨a₁, a₂⟩ ⟨b₁, b₂⟩ =>
    match @trichotomous _ r _ a₁ b₁ with
    | Or.inl h₁ => Or.inl <| Prod.Lex.left _ _ h₁
    | Or.inr (Or.inr h₁) => Or.inr <| Or.inr <| Prod.Lex.left _ _ h₁
    | Or.inr (Or.inl (.refl _)) =>
        match @trichotomous _ s _ a₂ b₂ with
        | Or.inl h => Or.inl <| Prod.Lex.right _ h
        | Or.inr (Or.inr h) => Or.inr <| Or.inr <| Prod.Lex.right _ h
        | Or.inr (Or.inl (.refl _)) => Or.inr <| Or.inl rfl
  trans a b c h₁ h₂ := by
    cases' h₁ with a₁ a₂ b₁ b₂ ab a₁ b₁ b₂ ab <;> cases' h₂ with _ _ c₁ c₂ bc _ _ c₂ bc
    exacts [.left _ _ (_root_.trans ab bc), .left _ _ ab, .left _ _ bc,
      .right _ (_root_.trans ab bc)]

instance (r : α → α → Prop) [IsWellFounded α r] (f : β → α) : IsWellFounded _ (InvImage r f) :=
  ⟨InvImage.wf f IsWellFounded.wf⟩

instance (f : α → ℕ) : IsWellFounded _ (Measure f) :=
  ⟨(measure f).wf⟩

theorem Subrelation.isWellFounded (r : α → α → Prop) [IsWellFounded α r] {s : α → α → Prop}
    (h : Subrelation s r) : IsWellFounded α s :=
  ⟨h.wf IsWellFounded.wf⟩
#align subrelation.is_well_founded Subrelation.isWellFounded

namespace Set

/-- An unbounded or cofinal set. -/
def Unbounded (r : α → α → Prop) (s : Set α) : Prop :=
  ∀ a, ∃ b ∈ s, ¬r b a
#align set.unbounded Set.Unbounded

/-- A bounded or final set. Not to be confused with `Metric.bounded`. -/
def Bounded (r : α → α → Prop) (s : Set α) : Prop :=
  ∃ a, ∀ b ∈ s, r b a
#align set.bounded Set.Bounded

@[simp]
theorem not_bounded_iff {r : α → α → Prop} (s : Set α) : ¬Bounded r s ↔ Unbounded r s := by
  simp only [Bounded, Unbounded, not_forall, not_exists, exists_prop, not_and, not_not]
#align set.not_bounded_iff Set.not_bounded_iff

@[simp]
theorem not_unbounded_iff {r : α → α → Prop} (s : Set α) : ¬Unbounded r s ↔ Bounded r s := by
  rw [not_iff_comm, not_bounded_iff]
#align set.not_unbounded_iff Set.not_unbounded_iff

theorem unbounded_of_isEmpty [IsEmpty α] {r : α → α → Prop} (s : Set α) : Unbounded r s :=
  isEmptyElim
#align set.unbounded_of_is_empty Set.unbounded_of_isEmpty

end Set

namespace Prod

instance isRefl_preimage_fst {r : α → α → Prop} [IsRefl α r] : IsRefl (α × α) (Prod.fst ⁻¹'o r) :=
  ⟨fun a => refl_of r a.1⟩

instance isRefl_preimage_snd {r : α → α → Prop} [IsRefl α r] : IsRefl (α × α) (Prod.snd ⁻¹'o r) :=
  ⟨fun a => refl_of r a.2⟩

instance isTrans_preimage_fst {r : α → α → Prop} [IsTrans α r] :
    IsTrans (α × α) (Prod.fst ⁻¹'o r) :=
  ⟨fun _ _ _ => trans_of r⟩

instance isTrans_preimage_snd {r : α → α → Prop} [IsTrans α r] :
    IsTrans (α × α) (Prod.snd ⁻¹'o r) :=
  ⟨fun _ _ _ => trans_of r⟩

end Prod

/-! ### Strict-non strict relations -/

/-- An unbundled relation class stating that `r` is the nonstrict relation corresponding to the
strict relation `s`. Compare `Preorder.lt_iff_le_not_le`. This is mostly meant to provide dot
notation on `(⊆)` and `(⊂)`. -/
class IsNonstrictStrictOrder (α : Type _) (r s : α → α → Prop) where
  /-- The relation `r` is the nonstrict relation corresponding to the strict relation `s`. -/
  right_iff_left_not_left (a b : α) : s a b ↔ r a b ∧ ¬r b a
#align is_nonstrict_strict_order IsNonstrictStrictOrder

theorem right_iff_left_not_left {r s : α → α → Prop} [IsNonstrictStrictOrder α r s] {a b : α} :
    s a b ↔ r a b ∧ ¬r b a :=
  IsNonstrictStrictOrder.right_iff_left_not_left _ _
#align right_iff_left_not_left right_iff_left_not_left

/-- A version of `right_iff_left_not_left` with explicit `r` and `s`. -/
theorem right_iff_left_not_left_of (r s : α → α → Prop) [IsNonstrictStrictOrder α r s] {a b : α} :
    s a b ↔ r a b ∧ ¬r b a :=
  right_iff_left_not_left
#align right_iff_left_not_left_of right_iff_left_not_left_of

-- The free parameter `r` is strictly speaking not uniquely determined by `s`, but in practice it
-- always has a unique instance, so this is not dangerous.
@[nolint dangerousInstance]
instance {s : α → α → Prop} [IsNonstrictStrictOrder α r s] : IsIrrefl α s :=
  ⟨fun _ h => ((right_iff_left_not_left_of r s).1 h).2 ((right_iff_left_not_left_of r s).1 h).1⟩

/-! #### `⊆` and `⊂` -/

section Subset
variable [HasSubset α] {a b c : α}

lemma subset_of_eq_of_subset (hab : a = b) (hbc : b ⊆ c) : a ⊆ c := by rwa [hab]
#align subset_of_eq_of_subset subset_of_eq_of_subset

lemma subset_of_subset_of_eq (hab : a ⊆ b) (hbc : b = c) : a ⊆ c := by rwa [←hbc]
#align subset_of_subset_of_eq subset_of_subset_of_eq

@[refl]
lemma subset_refl [IsRefl α (· ⊆ ·)] (a : α) : a ⊆ a := refl _
#align subset_refl subset_refl

lemma subset_rfl [IsRefl α (· ⊆ ·)] : a ⊆ a := refl _
#align subset_rfl subset_rfl

lemma subset_of_eq [IsRefl α (· ⊆ ·)] : a = b → a ⊆ b := fun h => h ▸ subset_rfl
#align subset_of_eq subset_of_eq

lemma superset_of_eq [IsRefl α (· ⊆ ·)] : a = b → b ⊆ a := fun h => h ▸ subset_rfl
#align superset_of_eq superset_of_eq

lemma ne_of_not_subset [IsRefl α (· ⊆ ·)] : ¬a ⊆ b → a ≠ b := mt subset_of_eq
#align ne_of_not_subset ne_of_not_subset

lemma ne_of_not_superset [IsRefl α (· ⊆ ·)] : ¬a ⊆ b → b ≠ a := mt superset_of_eq
#align ne_of_not_superset ne_of_not_superset

@[trans]
lemma subset_trans [IsTrans α (· ⊆ ·)] {a b c : α} : a ⊆ b → b ⊆ c → a ⊆ c := _root_.trans
#align subset_trans subset_trans

lemma subset_antisymm [IsAntisymm α (· ⊆ ·)] : a ⊆ b → b ⊆ a → a = b := antisymm
#align subset_antisymm subset_antisymm

lemma superset_antisymm [IsAntisymm α (· ⊆ ·)] : a ⊆ b → b ⊆ a → b = a := antisymm'
#align superset_antisymm superset_antisymm

alias subset_of_eq_of_subset ← Eq.trans_subset
#align eq.trans_subset Eq.trans_subset

alias subset_of_subset_of_eq ← HasSubset.subset.trans_eq
#align has_subset.subset.trans_eq HasSubset.subset.trans_eq

alias subset_of_eq ← Eq.subset' --TODO: Fix it and kill `Eq.subset`
#align eq.subset' Eq.subset'

alias superset_of_eq ← Eq.superset
#align eq.superset Eq.superset

alias subset_trans ← HasSubset.Subset.trans
#align has_subset.subset.trans HasSubset.Subset.trans

alias subset_antisymm ← HasSubset.Subset.antisymm
#align has_subset.subset.antisymm HasSubset.Subset.antisymm

alias superset_antisymm ← HasSubset.Subset.antisymm'
#align has_subset.subset.antisymm' HasSubset.Subset.antisymm'

theorem subset_antisymm_iff [IsRefl α (· ⊆ ·)] [IsAntisymm α (· ⊆ ·)] : a = b ↔ a ⊆ b ∧ b ⊆ a :=
  ⟨fun h => ⟨h.subset', h.superset⟩, fun h => h.1.antisymm h.2⟩
#align subset_antisymm_iff subset_antisymm_iff

theorem superset_antisymm_iff [IsRefl α (· ⊆ ·)] [IsAntisymm α (· ⊆ ·)] : a = b ↔ b ⊆ a ∧ a ⊆ b :=
  ⟨fun h => ⟨h.superset, h.subset'⟩, fun h => h.1.antisymm' h.2⟩
#align superset_antisymm_iff superset_antisymm_iff

end Subset

section Ssubset
variable [HasSSubset α] {a b c : α}

lemma ssubset_of_eq_of_ssubset (hab : a = b) (hbc : b ⊂ c) : a ⊂ c := by rwa [hab]
#align ssubset_of_eq_of_ssubset ssubset_of_eq_of_ssubset

lemma ssubset_of_ssubset_of_eq (hab : a ⊂ b) (hbc : b = c) : a ⊂ c := by rwa [←hbc]
#align ssubset_of_ssubset_of_eq ssubset_of_ssubset_of_eq

lemma ssubset_irrefl [IsIrrefl α (· ⊂ ·)] (a : α) : ¬a ⊂ a := irrefl _
#align ssubset_irrefl ssubset_irrefl

lemma ssubset_irrfl [IsIrrefl α (· ⊂ ·)] {a : α} : ¬a ⊂ a := irrefl _
#align ssubset_irrfl ssubset_irrfl

lemma ne_of_ssubset [IsIrrefl α (· ⊂ ·)] {a b : α} : a ⊂ b → a ≠ b := ne_of_irrefl
#align ne_of_ssubset ne_of_ssubset

lemma ne_of_ssuperset [IsIrrefl α (· ⊂ ·)] {a b : α} : a ⊂ b → b ≠ a := ne_of_irrefl'
#align ne_of_ssuperset ne_of_ssuperset

@[trans]
lemma ssubset_trans [IsTrans α (· ⊂ ·)] {a b c : α} : a ⊂ b → b ⊂ c → a ⊂ c := _root_.trans
#align ssubset_trans ssubset_trans

lemma ssubset_asymm [IsAsymm α (· ⊂ ·)] {a b : α} : a ⊂ b → ¬b ⊂ a := asymm
#align ssubset_asymm ssubset_asymm

alias ssubset_of_eq_of_ssubset ← Eq.trans_ssubset
#align eq.trans_ssubset Eq.trans_ssubset

alias ssubset_of_ssubset_of_eq ← HasSSubset.SSubset.trans_eq
#align has_ssubset.ssubset.trans_eq HasSSubset.SSubset.trans_eq

alias ssubset_irrfl ← HasSSubset.SSubset.false
#align has_ssubset.ssubset.false HasSSubset.SSubset.false

alias ne_of_ssubset ← HasSSubset.SSubset.ne
#align has_ssubset.ssubset.ne HasSSubset.SSubset.ne

alias ne_of_ssuperset ← HasSSubset.SSubset.ne'
#align has_ssubset.ssubset.ne' HasSSubset.SSubset.ne'

alias ssubset_trans ← HasSSubset.SSubset.trans
#align has_ssubset.ssubset.trans HasSSubset.SSubset.trans

alias ssubset_asymm ← HasSSubset.SSubset.asymm
#align has_ssubset.ssubset.asymm HasSSubset.SSubset.asymm

end Ssubset

section SubsetSsubset

variable [HasSubset α] [HasSSubset α] [IsNonstrictStrictOrder α (· ⊆ ·) (· ⊂ ·)] {a b c : α}

theorem ssubset_iff_subset_not_subset : a ⊂ b ↔ a ⊆ b ∧ ¬b ⊆ a :=
  right_iff_left_not_left
#align ssubset_iff_subset_not_subset ssubset_iff_subset_not_subset

theorem subset_of_ssubset (h : a ⊂ b) : a ⊆ b :=
  (ssubset_iff_subset_not_subset.1 h).1
#align subset_of_ssubset subset_of_ssubset

theorem not_subset_of_ssubset (h : a ⊂ b) : ¬b ⊆ a :=
  (ssubset_iff_subset_not_subset.1 h).2
#align not_subset_of_ssubset not_subset_of_ssubset

theorem not_ssubset_of_subset (h : a ⊆ b) : ¬b ⊂ a := fun h' => not_subset_of_ssubset h' h
#align not_ssubset_of_subset not_ssubset_of_subset

theorem ssubset_of_subset_not_subset (h₁ : a ⊆ b) (h₂ : ¬b ⊆ a) : a ⊂ b :=
  ssubset_iff_subset_not_subset.2 ⟨h₁, h₂⟩
#align ssubset_of_subset_not_subset ssubset_of_subset_not_subset

alias subset_of_ssubset ← HasSSubset.SSubset.subset
#align has_ssubset.ssubset.subset HasSSubset.SSubset.subset

alias not_subset_of_ssubset ← HasSSubset.SSubset.not_subset
#align has_ssubset.ssubset.not_subset HasSSubset.SSubset.not_subset

alias not_ssubset_of_subset ← HasSubset.Subset.not_ssubset
#align has_subset.subset.not_ssubset HasSubset.Subset.not_ssubset

alias ssubset_of_subset_not_subset ← HasSubset.Subset.ssubset_of_not_subset
#align has_subset.subset.ssubset_of_not_subset HasSubset.Subset.ssubset_of_not_subset

theorem ssubset_of_subset_of_ssubset [IsTrans α (· ⊆ ·)] (h₁ : a ⊆ b) (h₂ : b ⊂ c) : a ⊂ c :=
  (h₁.trans h₂.subset).ssubset_of_not_subset fun h => h₂.not_subset <| h.trans h₁
#align ssubset_of_subset_of_ssubset ssubset_of_subset_of_ssubset

theorem ssubset_of_ssubset_of_subset [IsTrans α (· ⊆ ·)] (h₁ : a ⊂ b) (h₂ : b ⊆ c) : a ⊂ c :=
  (h₁.subset.trans h₂).ssubset_of_not_subset fun h => h₁.not_subset <| h₂.trans h
#align ssubset_of_ssubset_of_subset ssubset_of_ssubset_of_subset

theorem ssubset_of_subset_of_ne [IsAntisymm α (· ⊆ ·)] (h₁ : a ⊆ b) (h₂ : a ≠ b) : a ⊂ b :=
  h₁.ssubset_of_not_subset <| mt h₁.antisymm h₂
#align ssubset_of_subset_of_ne ssubset_of_subset_of_ne

theorem ssubset_of_ne_of_subset [IsAntisymm α (· ⊆ ·)] (h₁ : a ≠ b) (h₂ : a ⊆ b) : a ⊂ b :=
  ssubset_of_subset_of_ne h₂ h₁
#align ssubset_of_ne_of_subset ssubset_of_ne_of_subset

theorem eq_or_ssubset_of_subset [IsAntisymm α (· ⊆ ·)] (h : a ⊆ b) : a = b ∨ a ⊂ b :=
  (em (b ⊆ a)).imp h.antisymm h.ssubset_of_not_subset
#align eq_or_ssubset_of_subset eq_or_ssubset_of_subset

theorem ssubset_or_eq_of_subset [IsAntisymm α (· ⊆ ·)] (h : a ⊆ b) : a ⊂ b ∨ a = b :=
  (eq_or_ssubset_of_subset h).symm
#align ssubset_or_eq_of_subset ssubset_or_eq_of_subset

alias ssubset_of_subset_of_ssubset ← HasSubset.Subset.trans_ssubset
#align has_subset.subset.trans_ssubset HasSubset.Subset.trans_ssubset

alias ssubset_of_ssubset_of_subset ← HasSSubset.SSubset.trans_subset
#align has_ssubset.ssubset.trans_subset HasSSubset.SSubset.trans_subset

alias ssubset_of_subset_of_ne ← HasSubset.Subset.ssubset_of_ne
#align has_subset.subset.ssubset_of_ne HasSubset.Subset.ssubset_of_ne

alias ssubset_of_ne_of_subset ← Ne.ssubset_of_subset
#align ne.ssubset_of_subset Ne.ssubset_of_subset

alias eq_or_ssubset_of_subset ← HasSubset.Subset.eq_or_ssubset
#align has_subset.subset.eq_or_ssubset HasSubset.Subset.eq_or_ssubset

alias ssubset_or_eq_of_subset ← HasSubset.Subset.ssubset_or_eq
#align has_subset.subset.ssubset_or_eq HasSubset.Subset.ssubset_or_eq

theorem ssubset_iff_subset_ne [IsAntisymm α (· ⊆ ·)] : a ⊂ b ↔ a ⊆ b ∧ a ≠ b :=
  ⟨fun h => ⟨h.subset, h.ne⟩, fun h => h.1.ssubset_of_ne h.2⟩
#align ssubset_iff_subset_ne ssubset_iff_subset_ne

theorem subset_iff_ssubset_or_eq [IsRefl α (· ⊆ ·)] [IsAntisymm α (· ⊆ ·)] :
    a ⊆ b ↔ a ⊂ b ∨ a = b :=
  ⟨fun h => h.ssubset_or_eq, fun h => h.elim subset_of_ssubset subset_of_eq⟩
#align subset_iff_ssubset_or_eq subset_iff_ssubset_or_eq

end SubsetSsubset

/-! ### Conversion of bundled order typeclasses to unbundled relation typeclasses -/

instance [Preorder α] : IsRefl α (· ≤ ·) :=
  ⟨le_refl⟩

instance [Preorder α] : IsRefl α (· ≥ ·) :=
  IsRefl.swap _

instance [Preorder α] : IsTrans α (· ≤ ·) :=
  ⟨@le_trans _ _⟩

instance [Preorder α] : IsTrans α (· ≥ ·) :=
  IsTrans.swap _

instance [Preorder α] : IsPreorder α (· ≤ ·) where

instance [Preorder α] : IsPreorder α (· ≥ ·) where

instance [Preorder α] : IsIrrefl α (· < ·) :=
  ⟨lt_irrefl⟩

instance [Preorder α] : IsIrrefl α (· > ·) :=
  IsIrrefl.swap _

instance [Preorder α] : IsTrans α (· < ·) :=
  ⟨@lt_trans _ _⟩

instance [Preorder α] : IsTrans α (· > ·) :=
  IsTrans.swap _

instance [Preorder α] : IsAsymm α (· < ·) :=
  ⟨@lt_asymm _ _⟩

instance [Preorder α] : IsAsymm α (· > ·) :=
  IsAsymm.swap _

instance [Preorder α] : IsAntisymm α (· < ·) :=
  IsAsymm.isAntisymm _

instance [Preorder α] : IsAntisymm α (· > ·) :=
  IsAsymm.isAntisymm _

instance [Preorder α] : IsStrictOrder α (· < ·) where

instance [Preorder α] : IsStrictOrder α (· > ·) where

instance [Preorder α] : IsNonstrictStrictOrder α (· ≤ ·) (· < ·) :=
  ⟨@lt_iff_le_not_le _ _⟩

instance [PartialOrder α] : IsAntisymm α (· ≤ ·) :=
  ⟨@le_antisymm _ _⟩

instance [PartialOrder α] : IsAntisymm α (· ≥ ·) :=
  IsAntisymm.swap _

instance [PartialOrder α] : IsPartialOrder α (· ≤ ·) where

instance [PartialOrder α] : IsPartialOrder α (· ≥ ·) where

instance [LinearOrder α] : IsTotal α (· ≤ ·) :=
  ⟨le_total⟩

instance [LinearOrder α] : IsTotal α (· ≥ ·) :=
  IsTotal.swap _

-- Porting note: this was `by infer_instance` before
instance [LinearOrder α] : IsTotalPreorder α (· ≤ ·) where

instance [LinearOrder α] : IsTotalPreorder α (· ≥ ·) where

instance [LinearOrder α] : IsLinearOrder α (· ≤ ·) where

instance [LinearOrder α] : IsLinearOrder α (· ≥ ·) where

instance [LinearOrder α] : IsTrichotomous α (· < ·) :=
  ⟨lt_trichotomy⟩

instance [LinearOrder α] : IsTrichotomous α (· > ·) :=
  IsTrichotomous.swap _

instance [LinearOrder α] : IsTrichotomous α (· ≤ ·) :=
  IsTotal.isTrichotomous _

instance [LinearOrder α] : IsTrichotomous α (· ≥ ·) :=
  IsTotal.isTrichotomous _

instance [LinearOrder α] : IsStrictTotalOrder α (· < ·) where

instance [LinearOrder α] : IsOrderConnected α (· < ·) := by infer_instance

instance [LinearOrder α] : IsIncompTrans α (· < ·) := by infer_instance

instance [LinearOrder α] : IsStrictWeakOrder α (· < ·) := by infer_instance

theorem transitive_le [Preorder α] : Transitive (@LE.le α _) :=
  transitive_of_trans _
#align transitive_le transitive_le

theorem transitive_lt [Preorder α] : Transitive (@LT.lt α _) :=
  transitive_of_trans _
#align transitive_lt transitive_lt

theorem transitive_ge [Preorder α] : Transitive (@GE.ge α _) :=
  transitive_of_trans _
#align transitive_ge transitive_ge

theorem transitive_gt [Preorder α] : Transitive (@GT.gt α _) :=
  transitive_of_trans _
#align transitive_gt transitive_gt

instance OrderDual.isTotal_le [LE α] [h : IsTotal α (· ≤ ·)] : IsTotal αᵒᵈ (· ≤ ·) :=
  @IsTotal.swap α _ h

instance : WellFoundedLT ℕ :=
  ⟨Nat.lt_wfRel.wf⟩
#align nat.lt_wf Nat.lt_wfRel

instance Nat.lt.isWellOrder : IsWellOrder ℕ (· < ·) where
#align nat.lt.is_well_order Nat.lt.isWellOrder

instance [LinearOrder α] [h : IsWellOrder α (· < ·)] : IsWellOrder αᵒᵈ (· > ·) := h

instance [LinearOrder α] [h : IsWellOrder α (· > ·)] : IsWellOrder αᵒᵈ (· < ·) := h


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

! This file was ported from Lean 3 source module data.int.least_greatest
! leanprover-community/mathlib commit 3342d1b2178381196f818146ff79bc0e7ccd9e2d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Int.Order.Basic

/-! # Least upper bound and greatest lower bound properties for integers

In this file we prove that a bounded above nonempty set of integers has the greatest element, and a
counterpart of this statement for the least element.

## Main definitions

* `Int.leastOfBdd`: if `P : ℤ → Prop` is a decidable predicate, `b` is a lower bound of the set
  `{m | P m}`, and there exists `m : ℤ` such that `P m` (this time, no witness is required), then
  `Int.leastOfBdd` returns the least number `m` such that `P m`, together with proofs of `P m` and
  of the minimality. This definition is computable and does not rely on the axiom of choice.
* `Int.greatestOfBdd`: a similar definition with all inequalities reversed.

## Main statements

* `Int.exists_least_of_bdd`: if `P : ℤ → Prop` is a predicate such that the set `{m : P m}` is
  bounded below and nonempty, then this set has the least element. This lemma uses classical logic
  to avoid assumption `[DecidablePred P]`. See `Int.leastOfBdd` for a constructive counterpart.

* `Int.coe_leastOfBdd_eq`: `(Int.leastOfBdd b Hb Hinh : ℤ)` does not depend on `b`.

* `Int.exists_greatest_of_bdd`, `Int.coe_greatest_of_bdd_eq`: versions of the above lemmas with all
  inequalities reversed.

## Tags

integer numbers, least element, greatest element
-/

namespace Int

/-- A computable version of `exists_least_of_bdd`: given a decidable predicate on the
integers, with an explicit lower bound and a proof that it is somewhere true, return
the least value for which the predicate is true. -/
def leastOfBdd {P : ℤ → Prop} [DecidablePred P] (b : ℤ) (Hb : ∀ z : ℤ, P z → b ≤ z)
    (Hinh : ∃ z : ℤ, P z) : { lb : ℤ // P lb ∧ ∀ z : ℤ, P z → lb ≤ z } :=
  have EX : ∃ n : ℕ, P (b + n) :=
    let ⟨elt, Helt⟩ := Hinh
    match elt, le.dest (Hb _ Helt), Helt with
    | _, ⟨n, rfl⟩, Hn => ⟨n, Hn⟩
  ⟨b + (Nat.find EX : ℤ), Nat.find_spec EX, fun z h =>
    match z, le.dest (Hb _ h), h with
    | _, ⟨_, rfl⟩, h => add_le_add_left (Int.ofNat_le.2 <| Nat.find_min' _ h) _⟩
#align int.least_of_bdd Int.leastOfBdd

/--
    If `P : ℤ → Prop` is a predicate such that the set `{m : P m}` is bounded below and nonempty,
    then this set has the least element. This lemma uses classical logic to avoid assumption
    `[DecidablePred P]`. See `Int.leastOfBdd` for a constructive counterpart. -/
theorem exists_least_of_bdd
    {P : ℤ → Prop}
    [DecidablePred P]
    (Hbdd : ∃ b : ℤ , ∀ z : ℤ , P z → b ≤ z)
    (Hinh : ∃ z : ℤ , P z) : ∃ lb : ℤ , P lb ∧ ∀ z : ℤ , P z → lb ≤ z := by
  let ⟨b , Hb⟩ := Hbdd
  let ⟨lb , H⟩ := leastOfBdd b Hb Hinh
  exact ⟨lb , H⟩
#align int.exists_least_of_bdd Int.exists_least_of_bdd

theorem coe_leastOfBdd_eq {P : ℤ → Prop} [DecidablePred P] {b b' : ℤ} (Hb : ∀ z : ℤ, P z → b ≤ z)
    (Hb' : ∀ z : ℤ, P z → b' ≤ z) (Hinh : ∃ z : ℤ, P z) :
    (leastOfBdd b Hb Hinh : ℤ) = leastOfBdd b' Hb' Hinh := by
  rcases leastOfBdd b Hb Hinh with ⟨n, hn, h2n⟩
  rcases leastOfBdd b' Hb' Hinh with ⟨n', hn', h2n'⟩
  exact le_antisymm (h2n _ hn') (h2n' _ hn)
#align int.coe_least_of_bdd_eq Int.coe_leastOfBdd_eq

/-- A computable version of `exists_greatest_of_bdd`: given a decidable predicate on the
integers, with an explicit upper bound and a proof that it is somewhere true, return
the greatest value for which the predicate is true. -/
def greatestOfBdd {P : ℤ → Prop} [DecidablePred P] (b : ℤ) (Hb : ∀ z : ℤ, P z → z ≤ b)
    (Hinh : ∃ z : ℤ, P z) : { ub : ℤ // P ub ∧ ∀ z : ℤ, P z → z ≤ ub } :=
  have Hbdd' : ∀ z : ℤ, P (-z) → -b ≤ z := fun z h => neg_le.1 (Hb _ h)
  have Hinh' : ∃ z : ℤ, P (-z) :=
    let ⟨elt, Helt⟩ := Hinh
    ⟨-elt, by rw [neg_neg]; exact Helt⟩
  let ⟨lb, Plb, al⟩ := leastOfBdd (-b) Hbdd' Hinh'
  ⟨-lb, Plb, fun z h => le_neg.1 <| al _ <| by rwa [neg_neg]⟩
#align int.greatest_of_bdd Int.greatestOfBdd

/--
    If `P : ℤ → Prop` is a predicate such that the set `{m : P m}` is bounded above and nonempty,
    then this set has the greatest element. This lemma uses classical logic to avoid assumption
    `[DecidablePred P]`. See `Int.greatestOfBdd` for a constructive counterpart. -/
theorem exists_greatest_of_bdd
    {P : ℤ → Prop}
    [DecidablePred P]
    (Hbdd : ∃ b : ℤ , ∀ z : ℤ , P z → z ≤ b)
    (Hinh : ∃ z : ℤ , P z) : ∃ ub : ℤ , P ub ∧ ∀ z : ℤ , P z → z ≤ ub := by
  let ⟨ b , Hb ⟩ := Hbdd
  let ⟨ lb , H ⟩ := greatestOfBdd b Hb Hinh
  exact ⟨ lb , H ⟩
#align int.exists_greatest_of_bdd Int.exists_greatest_of_bdd

theorem coe_greatestOfBdd_eq {P : ℤ → Prop} [DecidablePred P] {b b' : ℤ}
    (Hb : ∀ z : ℤ, P z → z ≤ b) (Hb' : ∀ z : ℤ, P z → z ≤ b') (Hinh : ∃ z : ℤ, P z) :
    (greatestOfBdd b Hb Hinh : ℤ) = greatestOfBdd b' Hb' Hinh := by
  rcases greatestOfBdd b Hb Hinh with ⟨n, hn, h2n⟩
  rcases greatestOfBdd b' Hb' Hinh with ⟨n', hn', h2n'⟩
  exact le_antisymm (h2n' _ hn) (h2n _ hn')
#align int.coe_greatest_of_bdd_eq Int.coe_greatestOfBdd_eq

end Int


import category_theory.comma
import algebra.category.CommRing
import .global
import .ideals
import .Omega
import data.opposite
import ring_theory.ideals
import  .src.sieve
import  .src.grothendieck
import ring_theory.localization
open category_theory
open localization  ---362
open opposite
universes u
local notation `Ring` := CommRing.{u}
local notation `Aff`  := Ringᵒᵖ
local notation `Set` :=  Type u

namespace ideals
--- Starting we ideal lemma
/-
     f (S) ⊆ f <S>
-/
lemma set_image_S_subset_set_image_span_S (A B : Ring )(f : A ⟶ B) (S : set A) :
        set.image f S ⊆  set.image f (ideal.span S) :=
begin
    apply set.image_subset,
    exact ideal.subset_span,
end
/-
    < f⁻¹ S > ⊆ f⁻¹ < S >
-/
lemma inverse_image_ideal (A B : Ring)(f : A ⟶ B)(S : set B) :
    ideal.span (set.preimage f S) ≤  ideal.comap f (ideal.span S) :=
    begin
        rw ideal.span_le,   -- span S \leq I ↔  S ≤ I
        apply set.preimage_mono ideal.subset_span,
    end
/-
    < f (S) > = < f<S> >
-/
lemma ideal.map_span_eq_span_image(A B : Ring )(f : A ⟶ B) (S : set A):
    ideal.span (set.image f S) = ideal.map f (ideal.span S) :=
begin
    apply le_antisymm,
    exact ideal.span_mono (set_image_S_subset_set_image_span_S A B f S),
    exact ideal.map_le_iff_le_comap.2 (ideal.span_le.2 (set.image_subset_iff.1 (ideal.subset_span))),
end
/-
  < S >   = ⊤ →  <f S > = ⊤
-/
lemma top_ideal_map {A B : Ring}(f : A ⟶ B)(S : set A) :
ideal.span S = ⊤ → ideal.span (set.image f S) = ⊤ := λ certif,
begin
    rw ideal.map_span_eq_span_image,
    rw certif,
    exact ideal.map_top f,
end

end ideals
open ideals
namespace Pre_sieves
--
-- on va definir un famille de sous-ensemble d'un anneau qui va nous donner des covering_familly
structure covering_familly (X : Aff) :=
-- idée base ring (base_ring := X.unop)
-- (S : set base_ring)
-- (I:= ideal.span S)   ----refaire !!!! tout !!!
-- (co_max : I = ⊤)
(S : set (X.unop))
(co_maximality : ideal.span S = ⊤)

/-
Accessor
-/
def to_ring (X : Aff) : Ring := X.unop
def to_set{X :Aff} (P : covering_familly X) : set (to_ring X) := P.S
lemma to_set_ext{X :Aff} (P : covering_familly X) : to_set P = P.S := rfl
def base_ring{X : Aff}(P : covering_familly X) : Ring := to_ring X
def Ideal{X : Aff}(P : covering_familly X) : ideal $ base_ring P := ideal.span $ to_set P

@[ext] lemma ext (X : Aff) (P Q : covering_familly X) : P.S = Q.S → P = Q  :=
    begin
        intros h1,cases P,cases Q,
        congr ; try { assumption },
    end
lemma exten {X : Aff} (P Q : covering_familly X) : P = Q ↔ P.S =Q.S := begin
     split,intro,congr; try {assumption},
     exact ext X P Q,
end
def pull_back{X Y : Aff}(f : Y ⟶ X) (P : covering_familly X) : covering_familly(Y)  := begin
 exact {S := set.image f.unop P.S, co_maximality := top_ideal_map f.unop P.S P.co_maximality},
end
infix  `#`   := pull_back
-- local notation prefix  f♯P := (pull_back f P)
lemma pull_back_ext {X Y : Aff}(f : Y ⟶ X)(P : covering_familly X):
          ( f # P).S  = set.image f.unop P.S := rfl
/-
Test for a covering_familly to be eq to pull-back.
-/
theorem pull_back_eq {X Y : Aff}(f : Y ⟶ X)(P : covering_familly X)(Q_Y : covering_familly Y) :
        f # P = Q_Y ↔ set.image f.unop P.S = Q_Y.S :=
    begin
          split,
               intros, subst a, rw pull_back_ext,
               intros, apply ext, rw ← a,rw ← to_set_ext,exact pull_back_ext f P,
    end
end  Pre_sieves

open Pre_sieves

def sieves_of (X : Aff) (P : covering_familly X) :=
       { f : over X | ∃ s : P.S, ideal.map (f.hom.unop) (ideal.span ({s} : set X.unop)) = ⊤ }

lemma simply {A B :Ring}(f  : A ⟶ B)(s : A) : ideal.map (f)(ideal.span({s} : set A)) = ideal.span( {f s} : set B) :=
begin
     have hyp : set.image f ({s} : set A) = ({f s} : set B),
     exact set.image_singleton,
     rw ← hyp,
     exact eq.symm ( ideal.map_span_eq_span_image A B f {s}),
end
-- La définition est alors :
-- def sieves_of' (X : Aff) (P : covering_familly X) :=
--      { f : over X | ∃ s : P.S, ideal.span ({f.hom s.val} : set (f.left.unop))   = ⊤ }
lemma mem_sieves (X : Aff) (P : covering_familly X)(f : over X) : (
∃ s : P.S, ideal.map (f.hom.unop) (ideal.span ({s} : set X.unop)) = ⊤) →   f ∈ (sieves_of X P) := λ hyp, hyp

lemma sieves_mem {X : Aff} {P : covering_familly X}{f : over X} : f ∈ (sieves_of X P) →
(∃ s : P.S, ideal.map (f.hom.unop) (ideal.span ({s} : set X.unop)) = ⊤) := λ hyp,hyp

def subs (X : Aff) (P : covering_familly X) :
∀ (f : over X) (hyp : f ∈ (sieves_of X P)) (Z : Aff) (g : Z ⟶ f.left),
 (over.mk (g ≫ f.hom)) ∈ (sieves_of X P) :=
begin
     intros, apply mem_sieves, rw over.mk_hom,
     rcases hyp with ⟨s, hyp_su⟩, use s,
     show ideal.map (f.hom.unop ≫ g.unop) (ideal.span ({s} : set X.unop)) = ⊤,
          rw [ideal.ideal_comp,hyp_su],
          exact ideal.map_top g.unop,
end

instance (X : Aff) : has_coe (covering_familly X)  (sieve X) := ⟨λ P, {arrows := sieves_of X P,subs := subs X P}⟩

lemma arrows_comp {X :Aff}(P : covering_familly X) : sieves_of X P = (P : sieve X).arrows := rfl
theorem  has_coe_is_equiv {X : Aff} (P Q : covering_familly X)  : P = Q → (P : sieve X) = (Q : sieve X) :=
---  si S(P) = S(Q) alors les sieves sont égaux : évident sieve X est défini unniquement à l'aide de S. La
---  réciproque est fausse !
begin
     intros,
     rw exten at a,
     apply sieve.extensionality,
     iterate 2{ rw ← arrows_comp},
     ext f,
     split,
     intros hyp, apply mem_sieves,rcases hyp with ⟨ s, hyp_s ⟩,use s.val, rw ← a, exact s.property,exact hyp_s,
     intros hyp, apply mem_sieves,rcases hyp with ⟨ s, hyp_s ⟩,use s.val, rw  a, exact s.property,exact hyp_s,
end

def Aff_sieves {X : Aff }(P : covering_familly X) : sieve(X) := {arrows := sieves_of X P,subs := subs X P}
def Aff_sieves_arrow {X : Aff}(P : covering_familly X) : (Aff_sieves  P).arrows = sieves_of X P := rfl
def mem_Aff_sieves {X : Aff}(P : covering_familly X)( f : over X) : f ∈ sieves_of X P   →  f ∈ Aff_sieves P :=
begin
     intros,
     show f ∈ (Aff_sieves P).arrows,
          rw Aff_sieves_arrow,
          assumption,
end
/-
Fondamental theorem  : le faire mieux avec Aff_sieves !
-/

-- trop complexe niveau écriture ce n'est pas fluide ! c'est un peu mieux mais pas encore !
-- Niveau mathmatique :
--   Soit X Y deux affines. h : Y ⟶ X un morphisme. P un recouvrement de X donné par la famille
--    S := P.S de l'anneau X.unop. Soit maintenant f / Y i.e f : Z ⟶ Y dans Aff.
--    alors (Z ⟶ Y ⟶ X) ∈ (P : sieve X) ssi f ∈ (h # P)
-- Il sz'agit du petit calcul.
-- Soit s ∈ X.unop, alors f h (s) engendre l'idéal plein de X.unop alors f s'  := f (h(s)) engendre le plein

theorem pull_back_stability {X Y: Aff}(h : Y ⟶ X) (P : covering_familly X)(f : over Y) :
 (over.mk (f.hom ≫ h)) ∈  (P : sieve X)  →  f ∈ ( (h # P) : sieve Y)  :=
λ fh_hyp, begin
     apply mem_sieves,
     rcases  sieves_mem  fh_hyp with ⟨s,hyp_s⟩,
     rw [over.mk_hom,unop_comp,ideal.ideal_comp,simply] at hyp_s,
     use h.unop s.val,
        rw pull_back_ext,
        exact set.mem_image_of_mem h.unop s.property,
     exact hyp_s,
end
theorem pull_back_stability'{X Y: Aff}(h : Y ⟶ X) (P : covering_familly X)(f : over Y) :
  f ∈ ( ( h#P) : sieve Y) → (over.mk (f.hom ≫ h)) ∈  (P  : sieve X) := begin
     intros hyp,
     apply mem_sieves,
     rw [over.mk_hom,unop_comp],
     rcases hyp with ⟨ ⟨ s,prop⟩,certif⟩,
     rcases  set.mem_image_iff_bex.1 prop with ⟨w,⟨h_p,hu⟩⟩,  -- s = h(w)  w ∈ S
     use w,exact h_p,
     rw ideal.ideal_comp,
     rw simply,
     show ideal.map ( f.hom.unop) (ideal.span {h.unop w}) = ⊤,
     rw hu,exact certif,
end

--- changement de fichier  !!!!
/-
Zariski
-/

axiom Localisation_axiom_1 (X : Aff)(s : unop X) :
∃ f : over X, ideal.map f.hom.unop (ideal.span({s})) = ⊤

def Zariski_topology (X : Aff)  :=  {S : sieve X | ∃ P : covering_familly X, S = P}

lemma mem_Zariski ( X : Aff) (S : sieve X) :
S ∈ Zariski_topology( X)  ↔  ∃  P : covering_familly X, S =  P :=  iff.rfl

lemma Zariski_construct {X : Aff} (S : sieve X) :  (∃  P : covering_familly X, S =  P) → S ∈ Zariski_topology X :=
 (mem_Zariski X S).2
lemma Zariski {X : Aff}(S : sieve X) :
     S ∈ Zariski_topology( X)  →  ∃  P : covering_familly X, S =  P  := (mem_Zariski X S).1

lemma Zariski_cover {X : Aff}(P : covering_familly X) : (P : sieve X) ∈ Zariski_topology(X):= begin
use P,
end

-- constructor
def Zariski_cover_of  {X : Aff} (P : covering_familly X) : Zariski_topology(X) :=
{val := (P : sieve X), property := begin use P, end }

lemma Zariski_cover_val{X :Aff} (P : covering_familly X) : (Zariski_cover_of P).val = P := rfl

def max_P (X : Aff) : covering_familly X := {S := {(1 : unop X)}, co_maximality := ideal.span_singleton_one}
lemma map_P_S (X : Aff) :  (max_P X).S = {(1 : unop X)} := rfl

lemma id_in_max (X : Aff) : over.mk (𝟙 X) ∈ (max_P X : sieve X) :=
begin
     -- apply mem_sieves,
     use 1,exact set.mem_singleton 1,
     show ideal.map (𝟙 X.unop) (ideal.span {1})  = ⊤,
          rw ideal.ideal_id, exact ideal.span_singleton_one,
end

/-
Idée mise en place :
     id : X → X in max + sub
-/
lemma eqmax {X : Aff}(S : sieve X) : S = ⊤ ↔ (over.mk (𝟙 X) ∈ S) :=
begin
     split, intro,rw a,trivial,
     exact sieve.top_of_has_id,
 end

lemma max_is_max(X : Aff) :   (max_P(X) : sieve X) = ⊤ := begin
    exact (eqmax _).2 (id_in_max X),
end

lemma pull_back_comp {X Y : Aff} (P : covering_familly X) (h : Y ⟶ X) :
    sieve.pullback (P : sieve X) h =  ( h # P : sieve Y ) :=
begin
    ext f,  --- ici l'histoire des arrows !!
    show f ∈ (sieve.pullback (P : sieve X) h) ↔ f ∈ (h#P : sieve Y),
        split,
        intros hyp,apply pull_back_stability, rw ← sieve.pullback_def2 h,exact hyp,
        intros,rw sieve.pullback_def2 h,apply pull_back_stability',exact a,
end
lemma pull_back_of_max (X Y :Aff) (f : Y ⟶ X) :  (f # (max_P X) : sieve Y) = (max_P Y : sieve Y) := begin
     rw max_is_max,
     apply (eqmax _).2,
     apply pull_back_stability,rw max_is_max,trivial,
end
-- definir l'appartenant a grothendieck topology
#print grothendieck
def stab'  (X Y : Aff) (S ∈ Zariski_topology X) (h : Y ⟶ X) :
            sieve.pullback S h ∈ Zariski_topology(Y) :=
    begin
        apply Zariski_construct,
        cases H with P Hyp,
        use pull_back h P,
        subst Hyp ,
        exact pull_back_comp P h,
    end
-- il nous faut étudier un peu les choses !
-- Par exemple l'hypothèse :  (f : over X),  sieve.pullback R f.fom ∈ Zariski(f.left)
--    f :  Z    →  X, il existe  (S(Z) set Z) tel que sieve.pullback R f.hom = Ring sieve S(Z)
--     g Z' → Z, g f ∈ R ↔  ∃ s_Z  g s_Z inversible
--
--

-- le premier lemme ca être le suivant :

def trans' (X : Aff)(S ∈ Zariski_topology X)(R : sieve X) : (∀(f : over X), f ∈ S →
sieve.pullback R f.hom ∈  Zariski_topology(f.left)) → (R ∈ Zariski_topology(X)) :=
begin
     intros hyp,
     apply Zariski_construct,
     rcases H with ⟨P,hyp_P⟩,

 sorry,
end


-- Producto de tipos finitos
-- =========================

import data.fintype.card

example
  (A B : Type*)
  [fintype A]
  [fintype B]
  : fintype (A × B) :=
by apply_instance

universe u

def is_example (a : Type u) : Prop :=
∃ (Q : Type u) [fintype Q], Q = a

variables (a b : Type u)

lemma is_example_prod :
  is_example a → is_example b → is_example (a × b) :=
begin
  rintros ⟨Q, hQ, f⟩,
  rintros ⟨Q', hQ', f'⟩,
  resetI,
  refine ⟨Q × Q', infer_instance, _⟩,
  congr; assumption,
end

lemma is_example_prod2 :
  is_example a → is_example b → is_example (a × b) :=
begin
  rintros ⟨Q, hQ, f⟩,
  rintros ⟨Q', hQ', f'⟩,
  resetI,
  refine ⟨Q × Q', _, _⟩,
  apply_instance,
  congr; assumption,
end


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_148
  (c : ℝ)
  (f : ℝ → ℝ)
  (h₀ : ∀ x, f x = c * x^3 - 9 * x + 3)
  (h₁ : f 2 = 9) :
  c = 3 :=
begin
  rw h₀ at h₁,
  linarith,
end


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

! This file was ported from Lean 3 source module data.nat.order.basic
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.Ring.Canonical
import Mathbin.Data.Nat.Basic

/-!
# The natural numbers as a linearly ordered commutative semiring

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

We also have a variety of lemmas which have been deferred from `data.nat.basic` because it is
easier to prove them with this ordered semiring instance available.

You may find that some theorems can be moved back to `data.nat.basic` by modifying their proofs.
-/

universe u v

/-! ### instances -/

#print Nat.orderBot /-
instance Nat.orderBot : OrderBot ℕ where
  bot := 0
  bot_le := Nat.zero_le
#align nat.order_bot Nat.orderBot
-/

instance : LinearOrderedCommSemiring ℕ :=
  { Nat.commSemiring, Nat.linearOrder with
    lt := Nat.lt
    add_le_add_left := @Nat.add_le_add_left
    le_of_add_le_add_left := @Nat.le_of_add_le_add_left
    zero_le_one := Nat.le_of_lt (Nat.zero_lt_succ 0)
    mul_lt_mul_of_pos_left := @Nat.mul_lt_mul_of_pos_left
    mul_lt_mul_of_pos_right := @Nat.mul_lt_mul_of_pos_right
    DecidableEq := Nat.decidableEq
    exists_pair_ne := ⟨0, 1, ne_of_lt Nat.zero_lt_one⟩ }

instance : LinearOrderedCommMonoidWithZero ℕ :=
  { Nat.linearOrderedCommSemiring, (inferInstance : CommMonoidWithZero ℕ) with
    mul_le_mul_left := fun a b h c => Nat.mul_le_mul_left c h }

/-! Extra instances to short-circuit type class resolution and ensure computability -/

-- Not using `infer_instance` avoids `classical.choice` in the following two
instance : LinearOrderedSemiring ℕ :=
  inferInstance

instance : StrictOrderedSemiring ℕ :=
  inferInstance

instance : StrictOrderedCommSemiring ℕ :=
  inferInstance

instance : OrderedSemiring ℕ :=
  StrictOrderedSemiring.toOrderedSemiring'

instance : OrderedCommSemiring ℕ :=
  StrictOrderedCommSemiring.toOrderedCommSemiring'

instance : LinearOrderedCancelAddCommMonoid ℕ :=
  inferInstance

instance : CanonicallyOrderedCommSemiring ℕ :=
  { Nat.nontrivial, Nat.orderBot, (inferInstance : OrderedAddCommMonoid ℕ),
    (inferInstance : LinearOrderedSemiring ℕ),
    (inferInstance :
      CommSemiring
        ℕ) with
    exists_add_of_le := fun a b h => (Nat.le.dest h).imp fun _ => Eq.symm
    le_self_add := Nat.le_add_right
    eq_zero_or_eq_zero_of_mul_eq_zero := fun a b => Nat.eq_zero_of_mul_eq_zero }

instance : CanonicallyLinearOrderedAddMonoid ℕ :=
  { (inferInstance : CanonicallyOrderedAddMonoid ℕ), Nat.linearOrder with }

variable {m n k l : ℕ}

namespace Nat

/-! ### Equalities and inequalities involving zero and one -/

#print Nat.one_le_iff_ne_zero /-
theorem one_le_iff_ne_zero : 1 ≤ n ↔ n ≠ 0 :=
  (show 1 ≤ n ↔ 0 < n from Iff.rfl).trans pos_iff_ne_zero
#align nat.one_le_iff_ne_zero Nat.one_le_iff_ne_zero
-/

#print Nat.one_lt_iff_ne_zero_and_ne_one /-
theorem one_lt_iff_ne_zero_and_ne_one : ∀ {n : ℕ}, 1 < n ↔ n ≠ 0 ∧ n ≠ 1
  | 0 => by decide
  | 1 => by decide
  | n + 2 => by decide
#align nat.one_lt_iff_ne_zero_and_ne_one Nat.one_lt_iff_ne_zero_and_ne_one
-/

/- warning: nat.mul_ne_zero -> Nat.mul_ne_zero is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Ne.{1} Nat m (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Ne.{1} Nat (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) n m) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))))
but is expected to have type
  forall {m : Nat} {n : Nat}, (Ne.{1} Nat m (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Ne.{1} Nat (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) m n) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))
Case conversion may be inaccurate. Consider using '#align nat.mul_ne_zero Nat.mul_ne_zeroₓ'. -/
protected theorem mul_ne_zero (n0 : n ≠ 0) (m0 : m ≠ 0) : n * m ≠ 0
  | nm => (eq_zero_of_mul_eq_zero nm).elim n0 m0
#align nat.mul_ne_zero Nat.mul_ne_zero

#print Nat.mul_eq_zero /-
@[simp]
protected theorem mul_eq_zero : m * n = 0 ↔ m = 0 ∨ n = 0 :=
  Iff.intro eq_zero_of_mul_eq_zero (by simp (config := { contextual := true }) [or_imp])
#align nat.mul_eq_zero Nat.mul_eq_zero
-/

#print Nat.zero_eq_mul /-
@[simp]
protected theorem zero_eq_mul : 0 = m * n ↔ m = 0 ∨ n = 0 := by rw [eq_comm, Nat.mul_eq_zero]
#align nat.zero_eq_mul Nat.zero_eq_mul
-/

#print Nat.eq_zero_of_double_le /-
theorem eq_zero_of_double_le (h : 2 * n ≤ n) : n = 0 :=
  add_right_eq_self.mp <| le_antisymm ((two_mul n).symm.trans_le h) le_add_self
#align nat.eq_zero_of_double_le Nat.eq_zero_of_double_le
-/

#print Nat.eq_zero_of_mul_le /-
theorem eq_zero_of_mul_le (hb : 2 ≤ n) (h : n * m ≤ m) : m = 0 :=
  eq_zero_of_double_le <| le_trans (Nat.mul_le_mul_right _ hb) h
#align nat.eq_zero_of_mul_le Nat.eq_zero_of_mul_le
-/

/- warning: nat.zero_max -> Nat.zero_max is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, Eq.{1} Nat (LinearOrder.max.{0} Nat Nat.linearOrder (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n) n
but is expected to have type
  forall {n : Nat}, Eq.{1} Nat (Max.max.{0} Nat Nat.instMaxNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) n
Case conversion may be inaccurate. Consider using '#align nat.zero_max Nat.zero_maxₓ'. -/
theorem zero_max : max 0 n = n :=
  max_eq_right (zero_le _)
#align nat.zero_max Nat.zero_max

/- warning: nat.min_eq_zero_iff -> Nat.min_eq_zero_iff is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (LinearOrder.min.{0} Nat Nat.linearOrder m n) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Or (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))))
but is expected to have type
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (Min.min.{0} Nat instMinNat m n) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Or (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))
Case conversion may be inaccurate. Consider using '#align nat.min_eq_zero_iff Nat.min_eq_zero_iffₓ'. -/
@[simp]
theorem min_eq_zero_iff : min m n = 0 ↔ m = 0 ∨ n = 0 :=
  by
  constructor
  · intro h
    cases' le_total m n with H H
    · simpa [H] using Or.inl h
    · simpa [H] using Or.inr h
  · rintro (rfl | rfl) <;> simp
#align nat.min_eq_zero_iff Nat.min_eq_zero_iff

/- warning: nat.max_eq_zero_iff -> Nat.max_eq_zero_iff is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (LinearOrder.max.{0} Nat Nat.linearOrder m n) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (And (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))))
but is expected to have type
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (Max.max.{0} Nat Nat.instMaxNat m n) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (And (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))
Case conversion may be inaccurate. Consider using '#align nat.max_eq_zero_iff Nat.max_eq_zero_iffₓ'. -/
@[simp]
theorem max_eq_zero_iff : max m n = 0 ↔ m = 0 ∧ n = 0 :=
  by
  constructor
  · intro h
    cases' le_total m n with H H
    · simp only [H, max_eq_right] at h
      exact ⟨le_antisymm (H.trans h.le) (zero_le _), h⟩
    · simp only [H, max_eq_left] at h
      exact ⟨h, le_antisymm (H.trans h.le) (zero_le _)⟩
  · rintro ⟨rfl, rfl⟩
    simp
#align nat.max_eq_zero_iff Nat.max_eq_zero_iff

/- warning: nat.add_eq_max_iff -> Nat.add_eq_max_iff is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m n) (LinearOrder.max.{0} Nat Nat.linearOrder m n)) (Or (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))))
but is expected to have type
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m n) (Max.max.{0} Nat Nat.instMaxNat m n)) (Or (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))
Case conversion may be inaccurate. Consider using '#align nat.add_eq_max_iff Nat.add_eq_max_iffₓ'. -/
theorem add_eq_max_iff : m + n = max m n ↔ m = 0 ∨ n = 0 :=
  by
  rw [← min_eq_zero_iff]
  cases' le_total m n with H H <;> simp [H]
#align nat.add_eq_max_iff Nat.add_eq_max_iff

/- warning: nat.add_eq_min_iff -> Nat.add_eq_min_iff is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m n) (LinearOrder.min.{0} Nat Nat.linearOrder m n)) (And (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))))
but is expected to have type
  forall {m : Nat} {n : Nat}, Iff (Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m n) (Min.min.{0} Nat instMinNat m n)) (And (Eq.{1} Nat m (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))
Case conversion may be inaccurate. Consider using '#align nat.add_eq_min_iff Nat.add_eq_min_iffₓ'. -/
theorem add_eq_min_iff : m + n = min m n ↔ m = 0 ∧ n = 0 :=
  by
  rw [← max_eq_zero_iff]
  cases' le_total m n with H H <;> simp [H]
#align nat.add_eq_min_iff Nat.add_eq_min_iff

#print Nat.one_le_of_lt /-
theorem one_le_of_lt (h : n < m) : 1 ≤ m :=
  lt_of_le_of_lt (Nat.zero_le _) h
#align nat.one_le_of_lt Nat.one_le_of_lt
-/

#print Nat.eq_one_of_mul_eq_one_right /-
theorem eq_one_of_mul_eq_one_right (H : m * n = 1) : m = 1 :=
  eq_one_of_dvd_one ⟨n, H.symm⟩
#align nat.eq_one_of_mul_eq_one_right Nat.eq_one_of_mul_eq_one_right
-/

#print Nat.eq_one_of_mul_eq_one_left /-
theorem eq_one_of_mul_eq_one_left (H : m * n = 1) : n = 1 :=
  eq_one_of_mul_eq_one_right (by rwa [mul_comm])
#align nat.eq_one_of_mul_eq_one_left Nat.eq_one_of_mul_eq_one_left
-/

/-! ### `succ` -/

#print Nat.two_le_iff /-
theorem two_le_iff : ∀ n, 2 ≤ n ↔ n ≠ 0 ∧ n ≠ 1
  | 0 => by simp
  | 1 => by simp
  | n + 2 => by simp
#align nat.two_le_iff Nat.two_le_iff
-/

#print Nat.lt_one_iff /-
@[simp]
theorem lt_one_iff {n : ℕ} : n < 1 ↔ n = 0 :=
  lt_succ_iff.trans nonpos_iff_eq_zero
#align nat.lt_one_iff Nat.lt_one_iff
-/

/-! ### `add` -/

#print Nat.add_pos_left /-
theorem add_pos_left {m : ℕ} (h : 0 < m) (n : ℕ) : 0 < m + n :=
  calc
    m + n > 0 + n := Nat.add_lt_add_right h n
    _ = n := (Nat.zero_add n)
    _ ≥ 0 := zero_le n

#align nat.add_pos_left Nat.add_pos_left
-/

#print Nat.add_pos_right /-
theorem add_pos_right (m : ℕ) {n : ℕ} (h : 0 < n) : 0 < m + n := by rw [add_comm];
  exact add_pos_left h m
#align nat.add_pos_right Nat.add_pos_right
-/

#print Nat.add_pos_iff_pos_or_pos /-
theorem add_pos_iff_pos_or_pos (m n : ℕ) : 0 < m + n ↔ 0 < m ∨ 0 < n :=
  Iff.intro
    (by
      intro h
      cases' m with m
      · simp [zero_add] at h
        exact Or.inr h
      exact Or.inl (succ_pos _))
    (by
      intro h; cases' h with mpos npos
      · apply add_pos_left mpos
      apply add_pos_right _ npos)
#align nat.add_pos_iff_pos_or_pos Nat.add_pos_iff_pos_or_pos
-/

#print Nat.add_eq_one_iff /-
theorem add_eq_one_iff : m + n = 1 ↔ m = 0 ∧ n = 1 ∨ m = 1 ∧ n = 0 := by
  cases n <;> simp [succ_eq_add_one, ← add_assoc, succ_inj']
#align nat.add_eq_one_iff Nat.add_eq_one_iff
-/

#print Nat.add_eq_two_iff /-
theorem add_eq_two_iff : m + n = 2 ↔ m = 0 ∧ n = 2 ∨ m = 1 ∧ n = 1 ∨ m = 2 ∧ n = 0 := by
  cases n <;> simp [(succ_ne_zero 1).symm, succ_eq_add_one, ← add_assoc, succ_inj', add_eq_one_iff]
#align nat.add_eq_two_iff Nat.add_eq_two_iff
-/

#print Nat.add_eq_three_iff /-
theorem add_eq_three_iff :
    m + n = 3 ↔ m = 0 ∧ n = 3 ∨ m = 1 ∧ n = 2 ∨ m = 2 ∧ n = 1 ∨ m = 3 ∧ n = 0 := by
  cases n <;> simp [(succ_ne_zero 1).symm, succ_eq_add_one, ← add_assoc, succ_inj', add_eq_two_iff]
#align nat.add_eq_three_iff Nat.add_eq_three_iff
-/

#print Nat.le_add_one_iff /-
theorem le_add_one_iff : m ≤ n + 1 ↔ m ≤ n ∨ m = n + 1 :=
  ⟨fun h =>
    match Nat.eq_or_lt_of_le h with
    | Or.inl h => Or.inr h
    | Or.inr h => Or.inl <| Nat.le_of_succ_le_succ h,
    Or.ndrec (fun h => le_trans h <| Nat.le_add_right _ _) le_of_eq⟩
#align nat.le_add_one_iff Nat.le_add_one_iff
-/

#print Nat.le_and_le_add_one_iff /-
theorem le_and_le_add_one_iff : n ≤ m ∧ m ≤ n + 1 ↔ m = n ∨ m = n + 1 :=
  by
  rw [le_add_one_iff, and_or_left, ← le_antisymm_iff, eq_comm, and_iff_right_of_imp]
  rintro rfl
  exact n.le_succ
#align nat.le_and_le_add_one_iff Nat.le_and_le_add_one_iff
-/

#print Nat.add_succ_lt_add /-
theorem add_succ_lt_add (hab : m < n) (hcd : k < l) : m + k + 1 < n + l :=
  by
  rw [add_assoc]
  exact add_lt_add_of_lt_of_le hab (Nat.succ_le_iff.2 hcd)
#align nat.add_succ_lt_add Nat.add_succ_lt_add
-/

/-! ### `pred` -/

#print Nat.pred_le_iff /-
theorem pred_le_iff : pred m ≤ n ↔ m ≤ succ n :=
  ⟨le_succ_of_pred_le, by
    cases m
    · exact fun _ => zero_le n
    exact le_of_succ_le_succ⟩
#align nat.pred_le_iff Nat.pred_le_iff
-/

/-! ### `sub`

Most lemmas come from the `has_ordered_sub` instance on `ℕ`. -/

instance : OrderedSub ℕ := by
  constructor
  intro m n k
  induction' n with n ih generalizing k
  · simp
  · simp only [sub_succ, add_succ, succ_add, ih, pred_le_iff]

#print Nat.lt_pred_iff /-
theorem lt_pred_iff : n < pred m ↔ succ n < m :=
  show n < m - 1 ↔ n + 1 < m from lt_tsub_iff_right
#align nat.lt_pred_iff Nat.lt_pred_iff
-/

#print Nat.lt_of_lt_pred /-
theorem lt_of_lt_pred (h : m < n - 1) : m < n :=
  lt_of_succ_lt (lt_pred_iff.1 h)
#align nat.lt_of_lt_pred Nat.lt_of_lt_pred
-/

#print Nat.le_or_le_of_add_eq_add_pred /-
theorem le_or_le_of_add_eq_add_pred (h : k + l = m + n - 1) : m ≤ k ∨ n ≤ l :=
  by
  cases' le_or_lt m k with h' h' <;> [left, right]
  · exact h'
  · replace h' := add_lt_add_right h' l
    rw [h] at h'
    cases' n.eq_zero_or_pos with hn hn
    · rw [hn]
      exact zero_le l
    rw [m.add_sub_assoc hn, add_lt_add_iff_left] at h'
    exact Nat.le_of_pred_lt h'
#align nat.le_or_le_of_add_eq_add_pred Nat.le_or_le_of_add_eq_add_pred
-/

#print Nat.sub_succ' /-
/-- A version of `nat.sub_succ` in the form `_ - 1` instead of `nat.pred _`. -/
theorem sub_succ' (m n : ℕ) : m - n.succ = m - n - 1 :=
  rfl
#align nat.sub_succ' Nat.sub_succ'
-/

/-! ### `mul` -/

#print Nat.mul_eq_one_iff /-
theorem mul_eq_one_iff : ∀ {m n : ℕ}, m * n = 1 ↔ m = 1 ∧ n = 1
  | 0, 0 => by decide
  | 0, 1 => by decide
  | 1, 0 => by decide
  | m + 2, 0 => by simp
  | 0, n + 2 => by simp
  | m + 1, n + 1 =>
    ⟨fun h => by
      simp only [add_mul, mul_add, mul_add, one_mul, mul_one, (add_assoc _ _ _).symm, Nat.succ_inj',
          add_eq_zero_iff] at h <;>
        simp [h.1.2, h.2],
      fun h => by simp only [h, mul_one]⟩
#align nat.mul_eq_one_iff Nat.mul_eq_one_iff
-/

#print Nat.succ_mul_pos /-
theorem succ_mul_pos (m : ℕ) (hn : 0 < n) : 0 < succ m * n :=
  mul_pos (succ_pos m) hn
#align nat.succ_mul_pos Nat.succ_mul_pos
-/

#print Nat.mul_self_le_mul_self /-
theorem mul_self_le_mul_self (h : m ≤ n) : m * m ≤ n * n :=
  mul_le_mul h h (zero_le _) (zero_le _)
#align nat.mul_self_le_mul_self Nat.mul_self_le_mul_self
-/

#print Nat.mul_self_lt_mul_self /-
theorem mul_self_lt_mul_self : ∀ {m n : ℕ}, m < n → m * m < n * n
  | 0, n, h => mul_pos h h
  | succ m, n, h => mul_lt_mul h (le_of_lt h) (succ_pos _) (zero_le _)
#align nat.mul_self_lt_mul_self Nat.mul_self_lt_mul_self
-/

#print Nat.mul_self_le_mul_self_iff /-
theorem mul_self_le_mul_self_iff : m ≤ n ↔ m * m ≤ n * n :=
  ⟨mul_self_le_mul_self, le_imp_le_of_lt_imp_lt mul_self_lt_mul_self⟩
#align nat.mul_self_le_mul_self_iff Nat.mul_self_le_mul_self_iff
-/

#print Nat.mul_self_lt_mul_self_iff /-
theorem mul_self_lt_mul_self_iff : m < n ↔ m * m < n * n :=
  le_iff_le_iff_lt_iff_lt.1 mul_self_le_mul_self_iff
#align nat.mul_self_lt_mul_self_iff Nat.mul_self_lt_mul_self_iff
-/

#print Nat.le_mul_self /-
theorem le_mul_self : ∀ n : ℕ, n ≤ n * n
  | 0 => le_rfl
  | n + 1 => by simp
#align nat.le_mul_self Nat.le_mul_self
-/

#print Nat.le_mul_of_pos_left /-
theorem le_mul_of_pos_left (h : 0 < n) : m ≤ n * m :=
  by
  conv =>
    lhs
    rw [← one_mul m]
  exact mul_le_mul_of_nonneg_right h.nat_succ_le (by decide)
#align nat.le_mul_of_pos_left Nat.le_mul_of_pos_left
-/

#print Nat.le_mul_of_pos_right /-
theorem le_mul_of_pos_right (h : 0 < n) : m ≤ m * n :=
  by
  conv =>
    lhs
    rw [← mul_one m]
  exact mul_le_mul_of_nonneg_left h.nat_succ_le (by decide)
#align nat.le_mul_of_pos_right Nat.le_mul_of_pos_right
-/

#print Nat.mul_self_inj /-
theorem mul_self_inj : m * m = n * n ↔ m = n :=
  le_antisymm_iff.trans
    (le_antisymm_iff.trans (and_congr mul_self_le_mul_self_iff mul_self_le_mul_self_iff)).symm
#align nat.mul_self_inj Nat.mul_self_inj
-/

#print Nat.le_add_pred_of_pos /-
theorem le_add_pred_of_pos (n : ℕ) {i : ℕ} (hi : i ≠ 0) : n ≤ i + (n - 1) :=
  by
  refine' le_trans _ add_tsub_le_assoc
  simp [add_comm, Nat.add_sub_assoc, one_le_iff_ne_zero.2 hi]
#align nat.le_add_pred_of_pos Nat.le_add_pred_of_pos
-/

#print Nat.lt_mul_self_iff /-
@[simp]
theorem lt_mul_self_iff : ∀ {n : ℕ}, n < n * n ↔ 1 < n
  | 0 => iff_of_false (lt_irrefl _) zero_le_one.not_lt
  | n + 1 => lt_mul_iff_one_lt_left n.succ_pos
#align nat.lt_mul_self_iff Nat.lt_mul_self_iff
-/

/-!
### Recursion and induction principles

This section is here due to dependencies -- the lemmas here require some of the lemmas
proved above, and some of the results in later sections depend on the definitions in this section.
-/

#print Nat.diag_induction /-
/-- Given a predicate on two naturals `P : ℕ → ℕ → Prop`, `P a b` is true for all `a < b` if
`P (a + 1) (a + 1)` is true for all `a`, `P 0 (b + 1)` is true for all `b` and for all
`a < b`, `P (a + 1) b` is true and `P a (b + 1)` is true implies `P (a + 1) (b + 1)` is true. -/
@[elab_as_elim]
theorem diag_induction (P : ℕ → ℕ → Prop) (ha : ∀ a, P (a + 1) (a + 1)) (hb : ∀ b, P 0 (b + 1))
    (hd : ∀ a b, a < b → P (a + 1) b → P a (b + 1) → P (a + 1) (b + 1)) : ∀ a b, a < b → P a b
  | 0, b + 1, h => hb _
  | a + 1, b + 1, h => by
    apply hd _ _ ((add_lt_add_iff_right _).1 h)
    · have : a + 1 = b ∨ a + 1 < b := by rwa [← le_iff_eq_or_lt, ← Nat.lt_succ_iff]
      rcases this with (rfl | _)
      · exact ha _
      apply diag_induction (a + 1) b this
    apply diag_induction a (b + 1)
    apply lt_of_le_of_lt (Nat.le_succ _) h termination_by' ⟨_, measure_wf fun p => p.1 + p.2.1⟩
#align nat.diag_induction Nat.diag_induction
-/

#print Nat.set_induction_bounded /-
/-- A subset of `ℕ` containing `k : ℕ` and closed under `nat.succ` contains every `n ≥ k`. -/
theorem set_induction_bounded {S : Set ℕ} (hk : k ∈ S) (h_ind : ∀ k : ℕ, k ∈ S → k + 1 ∈ S)
    (hnk : k ≤ n) : n ∈ S :=
  @leRecOn (fun n => n ∈ S) k n hnk h_ind hk
#align nat.set_induction_bounded Nat.set_induction_bounded
-/

#print Nat.set_induction /-
/-- A subset of `ℕ` containing zero and closed under `nat.succ` contains all of `ℕ`. -/
theorem set_induction {S : Set ℕ} (hb : 0 ∈ S) (h_ind : ∀ k : ℕ, k ∈ S → k + 1 ∈ S) (n : ℕ) :
    n ∈ S :=
  set_induction_bounded hb h_ind (zero_le n)
#align nat.set_induction Nat.set_induction
-/

/-! ### `div` -/

#print Nat.div_le_of_le_mul' /-
protected theorem div_le_of_le_mul' (h : m ≤ k * n) : m / k ≤ n :=
  (Nat.eq_zero_or_pos k).elim (fun k0 => by rw [k0, Nat.div_zero] <;> apply zero_le) fun k0 =>
    (mul_le_mul_left k0).1 <|
      calc
        k * (m / k) ≤ m % k + k * (m / k) := Nat.le_add_left _ _
        _ = m := (mod_add_div _ _)
        _ ≤ k * n := h

#align nat.div_le_of_le_mul' Nat.div_le_of_le_mul'
-/

#print Nat.div_le_self' /-
protected theorem div_le_self' (m n : ℕ) : m / n ≤ m :=
  (Nat.eq_zero_or_pos n).elim (fun n0 => by rw [n0, Nat.div_zero] <;> apply zero_le) fun n0 =>
    Nat.div_le_of_le_mul' <|
      calc
        m = 1 * m := (one_mul _).symm
        _ ≤ n * m := Nat.mul_le_mul_right _ n0

#align nat.div_le_self' Nat.div_le_self'
-/

#print Nat.div_lt_of_lt_mul /-
protected theorem div_lt_of_lt_mul (h : m < n * k) : m / n < k :=
  lt_of_mul_lt_mul_left
    (calc
      n * (m / n) ≤ m % n + n * (m / n) := Nat.le_add_left _ _
      _ = m := (mod_add_div _ _)
      _ < n * k := h
      )
    (Nat.zero_le n)
#align nat.div_lt_of_lt_mul Nat.div_lt_of_lt_mul
-/

#print Nat.eq_zero_of_le_div /-
theorem eq_zero_of_le_div (hn : 2 ≤ n) (h : m ≤ m / n) : m = 0 :=
  eq_zero_of_mul_le hn <| by
    rw [mul_comm] <;> exact (Nat.le_div_iff_mul_le' (lt_of_lt_of_le (by decide) hn)).1 h
#align nat.eq_zero_of_le_div Nat.eq_zero_of_le_div
-/

#print Nat.div_mul_div_le_div /-
theorem div_mul_div_le_div (m n k : ℕ) : m / k * n / m ≤ n / k :=
  if hm0 : m = 0 then by simp [hm0]
  else
    calc
      m / k * n / m ≤ n * m / k / m :=
        Nat.div_le_div_right (by rw [mul_comm] <;> exact mul_div_le_mul_div_assoc _ _ _)
      _ = n / k := by
        rw [Nat.div_div_eq_div_mul, mul_comm n, mul_comm k,
          Nat.mul_div_mul _ _ (Nat.pos_of_ne_zero hm0)]

#align nat.div_mul_div_le_div Nat.div_mul_div_le_div
-/

#print Nat.eq_zero_of_le_half /-
theorem eq_zero_of_le_half (h : n ≤ n / 2) : n = 0 :=
  eq_zero_of_le_div le_rfl h
#align nat.eq_zero_of_le_half Nat.eq_zero_of_le_half
-/

#print Nat.mul_div_mul_comm_of_dvd_dvd /-
theorem mul_div_mul_comm_of_dvd_dvd (hmk : k ∣ m) (hnl : l ∣ n) :
    m * n / (k * l) = m / k * (n / l) :=
  by
  rcases k.eq_zero_or_pos with (rfl | hk0); · simp
  rcases l.eq_zero_or_pos with (rfl | hl0); · simp
  obtain ⟨_, rfl⟩ := hmk
  obtain ⟨_, rfl⟩ := hnl
  rw [mul_mul_mul_comm, Nat.mul_div_cancel_left _ hk0, Nat.mul_div_cancel_left _ hl0,
    Nat.mul_div_cancel_left _ (mul_pos hk0 hl0)]
#align nat.mul_div_mul_comm_of_dvd_dvd Nat.mul_div_mul_comm_of_dvd_dvd
-/

#print Nat.le_half_of_half_lt_sub /-
theorem le_half_of_half_lt_sub {a b : ℕ} (h : a / 2 < a - b) : b ≤ a / 2 :=
  by
  rw [Nat.le_div_iff_mul_le two_pos]
  rw [Nat.div_lt_iff_lt_mul two_pos, Nat.mul_sub_right_distrib, lt_tsub_iff_right, mul_two a] at h
  exact le_of_lt (Nat.lt_of_add_lt_add_left h)
#align nat.le_half_of_half_lt_sub Nat.le_half_of_half_lt_sub
-/

#print Nat.half_le_of_sub_le_half /-
theorem half_le_of_sub_le_half {a b : ℕ} (h : a - b ≤ a / 2) : a / 2 ≤ b :=
  by
  rw [Nat.le_div_iff_mul_le two_pos, Nat.mul_sub_right_distrib, tsub_le_iff_right, mul_two,
    add_le_add_iff_left] at h
  rw [← Nat.mul_div_left b two_pos]
  exact Nat.div_le_div_right h
#align nat.half_le_of_sub_le_half Nat.half_le_of_sub_le_half
-/

/-! ### `mod`, `dvd` -/

#print Nat.two_mul_odd_div_two /-
theorem two_mul_odd_div_two (hn : n % 2 = 1) : 2 * (n / 2) = n - 1 := by
  conv =>
    rhs
    rw [← Nat.mod_add_div n 2, hn, add_tsub_cancel_left]
#align nat.two_mul_odd_div_two Nat.two_mul_odd_div_two
-/

#print Nat.div_dvd_of_dvd /-
theorem div_dvd_of_dvd (h : n ∣ m) : m / n ∣ m :=
  ⟨n, (Nat.div_mul_cancel h).symm⟩
#align nat.div_dvd_of_dvd Nat.div_dvd_of_dvd
-/

#print Nat.div_div_self /-
protected theorem div_div_self (h : n ∣ m) (hm : m ≠ 0) : m / (m / n) = n :=
  by
  rcases h with ⟨_, rfl⟩
  rw [mul_ne_zero_iff] at hm
  rw [mul_div_right _ (Nat.pos_of_ne_zero hm.1), mul_div_left _ (Nat.pos_of_ne_zero hm.2)]
#align nat.div_div_self Nat.div_div_self
-/

#print Nat.mod_mul_right_div_self /-
theorem mod_mul_right_div_self (m n k : ℕ) : m % (n * k) / n = m / n % k :=
  by
  rcases Nat.eq_zero_or_pos n with (rfl | hn); · simp
  rcases Nat.eq_zero_or_pos k with (rfl | hk); · simp
  conv_rhs => rw [← mod_add_div m (n * k)]
  rw [mul_assoc, add_mul_div_left _ _ hn, add_mul_mod_self_left,
    mod_eq_of_lt (Nat.div_lt_of_lt_mul (mod_lt _ (mul_pos hn hk)))]
#align nat.mod_mul_right_div_self Nat.mod_mul_right_div_self
-/

#print Nat.mod_mul_left_div_self /-
theorem mod_mul_left_div_self (m n k : ℕ) : m % (k * n) / n = m / n % k := by
  rw [mul_comm k, mod_mul_right_div_self]
#align nat.mod_mul_left_div_self Nat.mod_mul_left_div_self
-/

#print Nat.not_dvd_of_pos_of_lt /-
theorem not_dvd_of_pos_of_lt (h1 : 0 < n) (h2 : n < m) : ¬m ∣ n :=
  by
  rintro ⟨k, rfl⟩
  rcases Nat.eq_zero_or_pos k with (rfl | hk)
  · exact lt_irrefl 0 h1
  · exact not_lt.2 (le_mul_of_pos_right hk) h2
#align nat.not_dvd_of_pos_of_lt Nat.not_dvd_of_pos_of_lt
-/

#print Nat.sub_mod_eq_zero_of_mod_eq /-
/-- If `m` and `n` are equal mod `k`, `m - n` is zero mod `k`. -/
theorem sub_mod_eq_zero_of_mod_eq (h : m % k = n % k) : (m - n) % k = 0 := by
  rw [← Nat.mod_add_div m k, ← Nat.mod_add_div n k, ← h, tsub_add_eq_tsub_tsub,
    add_tsub_cancel_left, ← mul_tsub, Nat.mul_mod_right]
#align nat.sub_mod_eq_zero_of_mod_eq Nat.sub_mod_eq_zero_of_mod_eq
-/

#print Nat.one_mod /-
@[simp]
theorem one_mod (n : ℕ) : 1 % (n + 2) = 1 :=
  Nat.mod_eq_of_lt (add_lt_add_right n.succ_pos 1)
#align nat.one_mod Nat.one_mod
-/

#print Nat.dvd_sub_mod /-
theorem dvd_sub_mod (k : ℕ) : n ∣ k - k % n :=
  ⟨k / n, tsub_eq_of_eq_add_rev (Nat.mod_add_div k n).symm⟩
#align nat.dvd_sub_mod Nat.dvd_sub_mod
-/

/- warning: nat.add_mod_eq_ite -> Nat.add_mod_eq_ite is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat} {k : Nat}, Eq.{1} Nat (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m n) k) (ite.{1} Nat (LE.le.{0} Nat Nat.hasLe k (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) n k))) (Nat.decidableLe k (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) n k))) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) n k)) k) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.hasMod) n k)))
but is expected to have type
  forall {m : Nat} {n : Nat} {k : Nat}, Eq.{1} Nat (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m n) k) (ite.{1} Nat (LE.le.{0} Nat instLENat k (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) n k))) (Nat.decLe k (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) n k))) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) n k)) k) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) m k) (HMod.hMod.{0, 0, 0} Nat Nat Nat (instHMod.{0} Nat Nat.instModNat) n k)))
Case conversion may be inaccurate. Consider using '#align nat.add_mod_eq_ite Nat.add_mod_eq_iteₓ'. -/
theorem add_mod_eq_ite :
    (m + n) % k = if k ≤ m % k + n % k then m % k + n % k - k else m % k + n % k :=
  by
  cases k; · simp
  rw [Nat.add_mod]
  split_ifs with h
  · rw [Nat.mod_eq_sub_mod h, Nat.mod_eq_of_lt]
    exact
      (tsub_lt_iff_right h).mpr (Nat.add_lt_add (m.mod_lt k.zero_lt_succ) (n.mod_lt k.zero_lt_succ))
  · exact Nat.mod_eq_of_lt (lt_of_not_ge h)
#align nat.add_mod_eq_ite Nat.add_mod_eq_ite

#print Nat.div_mul_div_comm /-
theorem div_mul_div_comm (hmn : n ∣ m) (hkl : l ∣ k) : m / n * (k / l) = m * k / (n * l) :=
  have exi1 : ∃ x, m = n * x := hmn
  have exi2 : ∃ y, k = l * y := hkl
  if hn : n = 0 then by simp [hn]
  else
    have : 0 < n := Nat.pos_of_ne_zero hn
    if hl : l = 0 then by simp [hl]
    else by
      have : 0 < l := Nat.pos_of_ne_zero hl
      cases' exi1 with x hx
      cases' exi2 with y hy
      rw [hx, hy, Nat.mul_div_cancel_left, Nat.mul_div_cancel_left]
      symm
      apply Nat.div_eq_of_eq_mul_left
      apply mul_pos
      repeat' assumption
      cc
#align nat.div_mul_div_comm Nat.div_mul_div_comm
-/

#print Nat.div_eq_self /-
theorem div_eq_self : m / n = m ↔ m = 0 ∨ n = 1 :=
  by
  constructor
  · intro
    cases n
    · simp_all
    · cases n
      · right
        rfl
      · left
        have : m / (n + 2) ≤ m / 2 := div_le_div_left (by simp) (by decide)
        refine' eq_zero_of_le_half _
        simp_all
  · rintro (rfl | rfl) <;> simp
#align nat.div_eq_self Nat.div_eq_self
-/

/- ./././Mathport/Syntax/Translate/Tactic/Lean3.lean:132:4: warning: unsupported: rw with cfg: { occs := occurrences.pos[occurrences.pos] «expr[ ,]»([2]) } -/
#print Nat.div_eq_sub_mod_div /-
theorem div_eq_sub_mod_div : m / n = (m - m % n) / n :=
  by
  by_cases n0 : n = 0
  · rw [n0, Nat.div_zero, Nat.div_zero]
  · rw [← mod_add_div m n]
    rw [add_tsub_cancel_left, mul_div_right _ (Nat.pos_of_ne_zero n0)]
#align nat.div_eq_sub_mod_div Nat.div_eq_sub_mod_div
-/

#print Nat.not_dvd_of_between_consec_multiples /-
/-- `m` is not divisible by `n` if it is between `n * k` and `n * (k + 1)` for some `k`. -/
theorem not_dvd_of_between_consec_multiples (h1 : n * k < m) (h2 : m < n * (k + 1)) : ¬n ∣ m :=
  by
  rintro ⟨d, rfl⟩
  exact Monotone.ne_of_lt_of_lt_nat (Covariant.monotone_of_const n) k h1 h2 d rfl
#align nat.not_dvd_of_between_consec_multiples Nat.not_dvd_of_between_consec_multiples
-/

/-! ### `find` -/

section Find

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

#print Nat.find_pos /-
@[simp]
theorem find_pos (h : ∃ n : ℕ, p n) : 0 < Nat.find h ↔ ¬p 0 := by
  rw [pos_iff_ne_zero, Ne, Nat.find_eq_zero]
#align nat.find_pos Nat.find_pos
-/

#print Nat.find_add /-
theorem find_add {hₘ : ∃ m, p (m + n)} {hₙ : ∃ n, p n} (hn : n ≤ Nat.find hₙ) :
    Nat.find hₘ + n = Nat.find hₙ :=
  by
  refine' ((le_find_iff _ _).2 fun m hm hpm => hm.not_le _).antisymm _
  · have hnm : n ≤ m := hn.trans (find_le hpm)
    refine' add_le_of_le_tsub_right_of_le hnm (find_le _)
    rwa [tsub_add_cancel_of_le hnm]
  · rw [← tsub_le_iff_right]
    refine' (le_find_iff _ _).2 fun m hm hpm => hm.not_le _
    rw [tsub_le_iff_right]
    exact find_le hpm
#align nat.find_add Nat.find_add
-/

end Find

/-! ### `find_greatest` -/

section FindGreatest

variable {P Q : ℕ → Prop} [DecidablePred P]

#print Nat.findGreatest_eq_iff /-
theorem findGreatest_eq_iff :
    Nat.findGreatest P k = m ↔ m ≤ k ∧ (m ≠ 0 → P m) ∧ ∀ ⦃n⦄, m < n → n ≤ k → ¬P n :=
  by
  induction' k with k ihk generalizing m
  · rw [eq_comm, Iff.comm]
    simp only [nonpos_iff_eq_zero, Ne.def, and_iff_left_iff_imp, find_greatest_zero]
    rintro rfl
    exact ⟨fun h => (h rfl).elim, fun n hlt heq => (hlt.Ne HEq.symm).elim⟩
  · by_cases hk : P (k + 1)
    · rw [find_greatest_eq hk]
      constructor
      · rintro rfl
        exact ⟨le_rfl, fun _ => hk, fun n hlt hle => (hlt.not_le hle).elim⟩
      · rintro ⟨hle, h0, hm⟩
        rcases Decidable.eq_or_lt_of_le hle with (rfl | hlt)
        exacts[rfl, (hm hlt le_rfl hk).elim]
    · rw [find_greatest_of_not hk, ihk]
      constructor
      · rintro ⟨hle, hP, hm⟩
        refine' ⟨hle.trans k.le_succ, hP, fun n hlt hle => _⟩
        rcases Decidable.eq_or_lt_of_le hle with (rfl | hlt')
        exacts[hk, hm hlt <| lt_succ_iff.1 hlt']
      · rintro ⟨hle, hP, hm⟩
        refine' ⟨lt_succ_iff.1 (hle.lt_of_ne _), hP, fun n hlt hle => hm hlt (hle.trans k.le_succ)⟩
        rintro rfl
        exact hk (hP k.succ_ne_zero)
#align nat.find_greatest_eq_iff Nat.findGreatest_eq_iff
-/

#print Nat.findGreatest_eq_zero_iff /-
theorem findGreatest_eq_zero_iff : Nat.findGreatest P k = 0 ↔ ∀ ⦃n⦄, 0 < n → n ≤ k → ¬P n := by
  simp [find_greatest_eq_iff]
#align nat.find_greatest_eq_zero_iff Nat.findGreatest_eq_zero_iff
-/

#print Nat.findGreatest_spec /-
theorem findGreatest_spec (hmb : m ≤ n) (hm : P m) : P (Nat.findGreatest P n) :=
  by
  by_cases h : Nat.findGreatest P n = 0
  · cases m
    · rwa [h]
    exact ((find_greatest_eq_zero_iff.1 h) m.zero_lt_succ hmb hm).elim
  · exact (find_greatest_eq_iff.1 rfl).2.1 h
#align nat.find_greatest_spec Nat.findGreatest_spec
-/

#print Nat.findGreatest_le /-
theorem findGreatest_le (n : ℕ) : Nat.findGreatest P n ≤ n :=
  (findGreatest_eq_iff.1 rfl).1
#align nat.find_greatest_le Nat.findGreatest_le
-/

#print Nat.le_findGreatest /-
theorem le_findGreatest (hmb : m ≤ n) (hm : P m) : m ≤ Nat.findGreatest P n :=
  le_of_not_lt fun hlt => (findGreatest_eq_iff.1 rfl).2.2 hlt hmb hm
#align nat.le_find_greatest Nat.le_findGreatest
-/

#print Nat.findGreatest_mono_right /-
theorem findGreatest_mono_right (P : ℕ → Prop) [DecidablePred P] : Monotone (Nat.findGreatest P) :=
  by
  refine' monotone_nat_of_le_succ fun n => _
  rw [find_greatest_succ]
  split_ifs
  · exact (find_greatest_le n).trans (le_succ _)
  · rfl
#align nat.find_greatest_mono_right Nat.findGreatest_mono_right
-/

#print Nat.findGreatest_mono_left /-
theorem findGreatest_mono_left [DecidablePred Q] (hPQ : P ≤ Q) :
    Nat.findGreatest P ≤ Nat.findGreatest Q :=
  by
  intro n
  induction' n with n hn
  · rfl
  by_cases P (n + 1)
  · rw [find_greatest_eq h, find_greatest_eq (hPQ _ h)]
  · rw [find_greatest_of_not h]
    exact hn.trans (Nat.findGreatest_mono_right _ <| le_succ _)
#align nat.find_greatest_mono_left Nat.findGreatest_mono_left
-/

#print Nat.findGreatest_mono /-
theorem findGreatest_mono [DecidablePred Q] (hPQ : P ≤ Q) (hmn : m ≤ n) :
    Nat.findGreatest P m ≤ Nat.findGreatest Q n :=
  (Nat.findGreatest_mono_right _ hmn).trans <| findGreatest_mono_left hPQ _
#align nat.find_greatest_mono Nat.findGreatest_mono
-/

#print Nat.findGreatest_is_greatest /-
theorem findGreatest_is_greatest (hk : Nat.findGreatest P n < k) (hkb : k ≤ n) : ¬P k :=
  (findGreatest_eq_iff.1 rfl).2.2 hk hkb
#align nat.find_greatest_is_greatest Nat.findGreatest_is_greatest
-/

#print Nat.findGreatest_of_ne_zero /-
theorem findGreatest_of_ne_zero (h : Nat.findGreatest P n = m) (h0 : m ≠ 0) : P m :=
  (findGreatest_eq_iff.1 h).2.1 h0
#align nat.find_greatest_of_ne_zero Nat.findGreatest_of_ne_zero
-/

end FindGreatest

/-! ### `bit0` and `bit1` -/

#print Nat.bit0_le /-
protected theorem bit0_le {n m : ℕ} (h : n ≤ m) : bit0 n ≤ bit0 m :=
  add_le_add h h
#align nat.bit0_le Nat.bit0_le
-/

#print Nat.bit1_le /-
protected theorem bit1_le {n m : ℕ} (h : n ≤ m) : bit1 n ≤ bit1 m :=
  succ_le_succ (add_le_add h h)
#align nat.bit1_le Nat.bit1_le
-/

#print Nat.bit_le /-
theorem bit_le : ∀ (b : Bool) {m n : ℕ}, m ≤ n → bit b m ≤ bit b n
  | tt, _, _, h => Nat.bit1_le h
  | ff, _, _, h => Nat.bit0_le h
#align nat.bit_le Nat.bit_le
-/

#print Nat.bit0_le_bit /-
theorem bit0_le_bit : ∀ (b) {m n : ℕ}, m ≤ n → bit0 m ≤ bit b n
  | tt, _, _, h => le_of_lt <| Nat.bit0_lt_bit1 h
  | ff, _, _, h => Nat.bit0_le h
#align nat.bit0_le_bit Nat.bit0_le_bit
-/

#print Nat.bit_le_bit1 /-
theorem bit_le_bit1 : ∀ (b) {m n : ℕ}, m ≤ n → bit b m ≤ bit1 n
  | ff, _, _, h => le_of_lt <| Nat.bit0_lt_bit1 h
  | tt, _, _, h => Nat.bit1_le h
#align nat.bit_le_bit1 Nat.bit_le_bit1
-/

#print Nat.bit_lt_bit0 /-
theorem bit_lt_bit0 : ∀ (b) {m n : ℕ}, m < n → bit b m < bit0 n
  | tt, _, _, h => Nat.bit1_lt_bit0 h
  | ff, _, _, h => Nat.bit0_lt h
#align nat.bit_lt_bit0 Nat.bit_lt_bit0
-/

#print Nat.bit_lt_bit /-
theorem bit_lt_bit (a b) (h : m < n) : bit a m < bit b n :=
  lt_of_lt_of_le (bit_lt_bit0 _ h) (bit0_le_bit _ le_rfl)
#align nat.bit_lt_bit Nat.bit_lt_bit
-/

#print Nat.bit0_le_bit1_iff /-
@[simp]
theorem bit0_le_bit1_iff : bit0 m ≤ bit1 n ↔ m ≤ n :=
  ⟨fun h => by
    rwa [← Nat.lt_succ_iff, n.bit1_eq_succ_bit0, ← n.bit0_succ_eq, bit0_lt_bit0, Nat.lt_succ_iff] at
      h,
    fun h => le_of_lt (Nat.bit0_lt_bit1 h)⟩
#align nat.bit0_le_bit1_iff Nat.bit0_le_bit1_iff
-/

#print Nat.bit0_lt_bit1_iff /-
@[simp]
theorem bit0_lt_bit1_iff : bit0 m < bit1 n ↔ m ≤ n :=
  ⟨fun h => bit0_le_bit1_iff.1 (le_of_lt h), Nat.bit0_lt_bit1⟩
#align nat.bit0_lt_bit1_iff Nat.bit0_lt_bit1_iff
-/

#print Nat.bit1_le_bit0_iff /-
@[simp]
theorem bit1_le_bit0_iff : bit1 m ≤ bit0 n ↔ m < n :=
  ⟨fun h => by rwa [m.bit1_eq_succ_bit0, succ_le_iff, bit0_lt_bit0] at h, fun h =>
    le_of_lt (Nat.bit1_lt_bit0 h)⟩
#align nat.bit1_le_bit0_iff Nat.bit1_le_bit0_iff
-/

#print Nat.bit1_lt_bit0_iff /-
@[simp]
theorem bit1_lt_bit0_iff : bit1 m < bit0 n ↔ m < n :=
  ⟨fun h => bit1_le_bit0_iff.1 (le_of_lt h), Nat.bit1_lt_bit0⟩
#align nat.bit1_lt_bit0_iff Nat.bit1_lt_bit0_iff
-/

@[simp]
theorem one_le_bit0_iff : 1 ≤ bit0 n ↔ 0 < n :=
  by
  convert bit1_le_bit0_iff
  rfl
#align nat.one_le_bit0_iff Nat.one_le_bit0_iff

@[simp]
theorem one_lt_bit0_iff : 1 < bit0 n ↔ 1 ≤ n :=
  by
  convert bit1_lt_bit0_iff
  rfl
#align nat.one_lt_bit0_iff Nat.one_lt_bit0_iff

@[simp]
theorem bit_le_bit_iff : ∀ {b : Bool}, bit b m ≤ bit b n ↔ m ≤ n
  | ff => bit0_le_bit0
  | tt => bit1_le_bit1
#align nat.bit_le_bit_iff Nat.bit_le_bit_iff

@[simp]
theorem bit_lt_bit_iff : ∀ {b : Bool}, bit b m < bit b n ↔ m < n
  | ff => bit0_lt_bit0
  | tt => bit1_lt_bit1
#align nat.bit_lt_bit_iff Nat.bit_lt_bit_iff

@[simp]
theorem bit_le_bit1_iff : ∀ {b : Bool}, bit b m ≤ bit1 n ↔ m ≤ n
  | ff => bit0_le_bit1_iff
  | tt => bit1_le_bit1
#align nat.bit_le_bit1_iff Nat.bit_le_bit1_iff

/-! ### decidability of predicates -/

#print Nat.decidableLoHi /-
instance decidableLoHi (lo hi : ℕ) (P : ℕ → Prop) [H : DecidablePred P] :
    Decidable (∀ x, lo ≤ x → x < hi → P x) :=
  decidable_of_iff (∀ x < hi - lo, P (lo + x))
    ⟨fun al x hl hh => by
      have := al (x - lo) ((tsub_lt_tsub_iff_right hl).mpr hh)
      rwa [add_tsub_cancel_of_le hl] at this, fun al x h =>
      al _ (Nat.le_add_right _ _) (lt_tsub_iff_left.mp h)⟩
#align nat.decidable_lo_hi Nat.decidableLoHi
-/

#print Nat.decidableLoHiLe /-
instance decidableLoHiLe (lo hi : ℕ) (P : ℕ → Prop) [H : DecidablePred P] :
    Decidable (∀ x, lo ≤ x → x ≤ hi → P x) :=
  decidable_of_iff (∀ x, lo ≤ x → x < hi + 1 → P x) <|
    ball_congr fun x hl => imp_congr lt_succ_iff Iff.rfl
#align nat.decidable_lo_hi_le Nat.decidableLoHiLe
-/

end Nat