/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel

! This file was ported from Lean 3 source module topology.metric_space.completion
! leanprover-community/mathlib commit 69c6a5a12d8a2b159f20933e60115a4f2de62b58
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.UniformSpace.Completion
import Mathbin.Topology.MetricSpace.Isometry
import Mathbin.Topology.Instances.Real

/-!
# The completion of a metric space

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

Completion of uniform spaces are already defined in `topology.uniform_space.completion`. We show
here that the uniform space completion of a metric space inherits a metric space structure,
by extending the distance to the completion and checking that it is indeed a distance, and that
it defines the same uniformity as the already defined uniform structure on the completion
-/

open Set Filter UniformSpace Metric

open Filter Topology uniformity

noncomputable section

universe u v

variable {α : Type u} {β : Type v} [PseudoMetricSpace α]

namespace UniformSpace.Completion

/-- The distance on the completion is obtained by extending the distance on the original space,
by uniform continuity. -/
instance : Dist (Completion α) :=
  ⟨Completion.extension₂ dist⟩

/- warning: uniform_space.completion.uniform_continuous_dist -> UniformSpace.Completion.uniformContinuous_dist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], UniformContinuous.{u1, 0} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) Real (Prod.uniformSpace.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace) (fun (p : Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) => Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) (Prod.fst.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p) (Prod.snd.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], UniformContinuous.{u1, 0} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) Real (instUniformSpaceProd.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace) (fun (p : Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) => Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) (Prod.fst.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p) (Prod.snd.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p))
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.uniform_continuous_dist UniformSpace.Completion.uniformContinuous_distₓ'. -/
/-- The new distance is uniformly continuous. -/
protected theorem uniformContinuous_dist :
    UniformContinuous fun p : Completion α × Completion α => dist p.1 p.2 :=
  uniformContinuous_extension₂ dist
#align uniform_space.completion.uniform_continuous_dist UniformSpace.Completion.uniformContinuous_dist

#print UniformSpace.Completion.continuous_dist /-
/-- The new distance is continuous. -/
protected theorem continuous_dist [TopologicalSpace β] {f g : β → Completion α} (hf : Continuous f)
    (hg : Continuous g) : Continuous fun x => dist (f x) (g x) :=
  Completion.uniformContinuous_dist.Continuous.comp (hf.prod_mk hg : _)
#align uniform_space.completion.continuous_dist UniformSpace.Completion.continuous_dist
-/

/- warning: uniform_space.completion.dist_eq -> UniformSpace.Completion.dist_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : α) (y : α), Eq.{1} Real (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (HasLiftT.mk.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (CoeTCₓ.coe.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasCoeT.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (HasLiftT.mk.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (CoeTCₓ.coe.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasCoeT.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) y)) (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) x y)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : α) (y : α), Eq.{1} Real (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) (UniformSpace.Completion.coe'.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1) x) (UniformSpace.Completion.coe'.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1) y)) (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) x y)
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.dist_eq UniformSpace.Completion.dist_eqₓ'. -/
/-- The new distance is an extension of the original distance. -/
@[simp]
protected theorem dist_eq (x y : α) : dist (x : Completion α) y = dist x y :=
  Completion.extension₂_coe_coe uniformContinuous_dist _ _
#align uniform_space.completion.dist_eq UniformSpace.Completion.dist_eq

/- warning: uniform_space.completion.dist_self -> UniformSpace.Completion.dist_self is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)), Eq.{1} Real (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) x x) (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)), Eq.{1} Real (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) x x) (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal))
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.dist_self UniformSpace.Completion.dist_selfₓ'. -/
/- Let us check that the new distance satisfies the axioms of a distance, by starting from the
properties on α and extending them to `completion α` by continuity. -/
protected theorem dist_self (x : Completion α) : dist x x = 0 :=
  by
  apply induction_on x
  · refine' isClosed_eq _ continuous_const
    exact completion.continuous_dist continuous_id continuous_id
  · intro a
    rw [completion.dist_eq, dist_self]
#align uniform_space.completion.dist_self UniformSpace.Completion.dist_self

#print UniformSpace.Completion.dist_comm /-
protected theorem dist_comm (x y : Completion α) : dist x y = dist y x :=
  by
  apply induction_on₂ x y
  ·
    exact
      isClosed_eq (completion.continuous_dist continuous_fst continuous_snd)
        (completion.continuous_dist continuous_snd continuous_fst)
  · intro a b
    rw [completion.dist_eq, completion.dist_eq, dist_comm]
#align uniform_space.completion.dist_comm UniformSpace.Completion.dist_comm
-/

/- warning: uniform_space.completion.dist_triangle -> UniformSpace.Completion.dist_triangle is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (y : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (z : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) x z) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.hasAdd) (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) x y) (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) y z))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (y : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (z : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)), LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) x z) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.instAddReal) (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) x y) (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) y z))
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.dist_triangle UniformSpace.Completion.dist_triangleₓ'. -/
protected theorem dist_triangle (x y z : Completion α) : dist x z ≤ dist x y + dist y z :=
  by
  apply induction_on₃ x y z
  ·
    refine' isClosed_le _ (Continuous.add _ _) <;>
      apply_rules [completion.continuous_dist, Continuous.fst, Continuous.snd, continuous_id]
  · intro a b c
    rw [completion.dist_eq, completion.dist_eq, completion.dist_eq]
    exact dist_triangle a b c
#align uniform_space.completion.dist_triangle UniformSpace.Completion.dist_triangle

/- warning: uniform_space.completion.mem_uniformity_dist -> UniformSpace.Completion.mem_uniformity_dist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (s : Set.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))), Iff (Membership.Mem.{u1, u1} (Set.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.hasMem.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) s (uniformity.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Exists.{1} Real (fun (ε : Real) => Exists.{0} (GT.gt.{0} Real Real.hasLt ε (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) (fun (H : GT.gt.{0} Real Real.hasLt ε (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) => forall {a : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)} {b : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)}, (LT.lt.{0} Real Real.hasLt (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) a b) ε) -> (Membership.Mem.{u1, u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (Set.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Set.hasMem.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Prod.mk.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a b) s))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (s : Set.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))), Iff (Membership.mem.{u1, u1} (Set.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (instMembershipSetFilter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) s (uniformity.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Exists.{1} Real (fun (ε : Real) => And (GT.gt.{0} Real Real.instLTReal ε (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal))) (forall {a : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)} {b : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)}, (LT.lt.{0} Real Real.instLTReal (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) a b) ε) -> (Membership.mem.{u1, u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (Set.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Set.instMembershipSet.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Prod.mk.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a b) s))))
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.mem_uniformity_dist UniformSpace.Completion.mem_uniformity_distₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- Elements of the uniformity (defined generally for completions) can be characterized in terms
of the distance. -/
protected theorem mem_uniformity_dist (s : Set (Completion α × Completion α)) :
    s ∈ 𝓤 (Completion α) ↔ ∃ ε > 0, ∀ {a b}, dist a b < ε → (a, b) ∈ s :=
  by
  constructor
  · /- Start from an entourage `s`. It contains a closed entourage `t`. Its pullback in `α` is an
        entourage, so it contains an `ε`-neighborhood of the diagonal by definition of the entourages
        in metric spaces. Then `t` contains an `ε`-neighborhood of the diagonal in `completion α`, as
        closed properties pass to the completion. -/
    intro hs
    rcases mem_uniformity_isClosed hs with ⟨t, ht, ⟨tclosed, ts⟩⟩
    have A : { x : α × α | (coe x.1, coe x.2) ∈ t } ∈ uniformity α :=
      uniformContinuous_def.1 (uniform_continuous_coe α) t ht
    rcases mem_uniformity_dist.1 A with ⟨ε, εpos, hε⟩
    refine' ⟨ε, εpos, fun x y hxy => _⟩
    have : ε ≤ dist x y ∨ (x, y) ∈ t := by
      apply induction_on₂ x y
      · have :
          { x : completion α × completion α | ε ≤ dist x.fst x.snd ∨ (x.fst, x.snd) ∈ t } =
            { p : completion α × completion α | ε ≤ dist p.1 p.2 } ∪ t :=
          by ext <;> simp
        rw [this]
        apply IsClosed.union _ tclosed
        exact isClosed_le continuous_const completion.uniform_continuous_dist.continuous
      · intro x y
        rw [completion.dist_eq]
        by_cases h : ε ≤ dist x y
        · exact Or.inl h
        · have Z := hε (not_le.1 h)
          simp only [Set.mem_setOf_eq] at Z
          exact Or.inr Z
    simp only [not_le.mpr hxy, false_or_iff, not_le] at this
    exact ts this
  · /- Start from a set `s` containing an ε-neighborhood of the diagonal in `completion α`. To show
        that it is an entourage, we use the fact that `dist` is uniformly continuous on
        `completion α × completion α` (this is a general property of the extension of uniformly
        continuous functions). Therefore, the preimage of the ε-neighborhood of the diagonal in ℝ
        is an entourage in `completion α × completion α`. Massaging this property, it follows that
        the ε-neighborhood of the diagonal is an entourage in `completion α`, and therefore this is
        also the case of `s`. -/
    rintro ⟨ε, εpos, hε⟩
    let r : Set (ℝ × ℝ) := { p | dist p.1 p.2 < ε }
    have : r ∈ uniformity ℝ := Metric.dist_mem_uniformity εpos
    have T := uniformContinuous_def.1 (@completion.uniform_continuous_dist α _) r this
    simp only [uniformity_prod_eq_prod, mem_prod_iff, exists_prop, Filter.mem_map,
      Set.mem_setOf_eq] at T
    rcases T with ⟨t1, ht1, t2, ht2, ht⟩
    refine' mem_of_superset ht1 _
    have A : ∀ a b : completion α, (a, b) ∈ t1 → dist a b < ε :=
      by
      intro a b hab
      have : ((a, b), (a, a)) ∈ t1 ×ˢ t2 := ⟨hab, refl_mem_uniformity ht2⟩
      have I := ht this
      simp [completion.dist_self, Real.dist_eq, completion.dist_comm] at I
      exact lt_of_le_of_lt (le_abs_self _) I
    show t1 ⊆ s
    · rintro ⟨a, b⟩ hp
      have : dist a b < ε := A a b hp
      exact hε this
#align uniform_space.completion.mem_uniformity_dist UniformSpace.Completion.mem_uniformity_dist

/- warning: uniform_space.completion.eq_of_dist_eq_zero -> UniformSpace.Completion.eq_of_dist_eq_zero is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (y : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)), (Eq.{1} Real (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) x y) (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) -> (Eq.{succ u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) x y)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (y : UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)), (Eq.{1} Real (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) x y) (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal))) -> (Eq.{succ u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) x y)
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.eq_of_dist_eq_zero UniformSpace.Completion.eq_of_dist_eq_zeroₓ'. -/
/-- If two points are at distance 0, then they coincide. -/
protected theorem eq_of_dist_eq_zero (x y : Completion α) (h : dist x y = 0) : x = y :=
  by
  /- This follows from the separation of `completion α` and from the description of
    entourages in terms of the distance. -/
  have : SeparatedSpace (completion α) := by infer_instance
  refine' separated_def.1 this x y fun s hs => _
  rcases(completion.mem_uniformity_dist s).1 hs with ⟨ε, εpos, hε⟩
  rw [← h] at εpos
  exact hε εpos
#align uniform_space.completion.eq_of_dist_eq_zero UniformSpace.Completion.eq_of_dist_eq_zero

/- warning: uniform_space.completion.uniformity_dist' -> UniformSpace.Completion.uniformity_dist' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], Eq.{succ u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (uniformity.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (infᵢ.{u1, 1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.completeLattice.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))))) (Subtype.{1} Real (fun (ε : Real) => LT.lt.{0} Real Real.hasLt (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero))) ε)) (fun (ε : Subtype.{1} Real (fun (ε : Real) => LT.lt.{0} Real Real.hasLt (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero))) ε)) => Filter.principal.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (setOf.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (fun (p : Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) => LT.lt.{0} Real Real.hasLt (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) (Prod.fst.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p) (Prod.snd.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p)) (Subtype.val.{1} Real (fun (ε : Real) => LT.lt.{0} Real Real.hasLt (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero))) ε) ε)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], Eq.{succ u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (uniformity.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (infᵢ.{u1, 1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.instCompleteLatticeFilter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))))) (Subtype.{1} Real (fun (ε : Real) => LT.lt.{0} Real Real.instLTReal (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal)) ε)) (fun (ε : Subtype.{1} Real (fun (ε : Real) => LT.lt.{0} Real Real.instLTReal (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal)) ε)) => Filter.principal.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (setOf.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (fun (p : Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) => LT.lt.{0} Real Real.instLTReal (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) (Prod.fst.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p) (Prod.snd.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p)) (Subtype.val.{1} Real (fun (ε : Real) => LT.lt.{0} Real Real.instLTReal (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal)) ε) ε)))))
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.uniformity_dist' UniformSpace.Completion.uniformity_dist'ₓ'. -/
/-- Reformulate `completion.mem_uniformity_dist` in terms that are suitable for the definition
of the metric space structure. -/
protected theorem uniformity_dist' :
    𝓤 (Completion α) = ⨅ ε : { ε : ℝ // 0 < ε }, 𝓟 { p | dist p.1 p.2 < ε.val } :=
  by
  ext s; rw [mem_infi_of_directed]
  · simp [completion.mem_uniformity_dist, subset_def]
  · rintro ⟨r, hr⟩ ⟨p, hp⟩
    use ⟨min r p, lt_min hr hp⟩
    simp (config := { contextual := true }) [lt_min_iff, (· ≥ ·)]
#align uniform_space.completion.uniformity_dist' UniformSpace.Completion.uniformity_dist'

/- warning: uniform_space.completion.uniformity_dist -> UniformSpace.Completion.uniformity_dist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], Eq.{succ u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (uniformity.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (infᵢ.{u1, 1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.completeLattice.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))))) Real (fun (ε : Real) => infᵢ.{u1, 0} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.completeLattice.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))))) (GT.gt.{0} Real Real.hasLt ε (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) (fun (H : GT.gt.{0} Real Real.hasLt ε (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) => Filter.principal.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (setOf.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (fun (p : Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) => LT.lt.{0} Real Real.hasLt (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasDist.{u1} α _inst_1) (Prod.fst.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p) (Prod.snd.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p)) ε)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], Eq.{succ u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (uniformity.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.uniformSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (infᵢ.{u1, 1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.instCompleteLatticeFilter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))))) Real (fun (ε : Real) => infᵢ.{u1, 0} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) (Filter.instCompleteLatticeFilter.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))))) (GT.gt.{0} Real Real.instLTReal ε (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal))) (fun (H : GT.gt.{0} Real Real.instLTReal ε (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal))) => Filter.principal.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (setOf.{u1} (Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) (fun (p : Prod.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))) => LT.lt.{0} Real Real.instLTReal (Dist.dist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instDistCompletionToUniformSpace.{u1} α _inst_1) (Prod.fst.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p) (Prod.snd.{u1, u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) p)) ε)))))
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.uniformity_dist UniformSpace.Completion.uniformity_distₓ'. -/
protected theorem uniformity_dist : 𝓤 (Completion α) = ⨅ ε > 0, 𝓟 { p | dist p.1 p.2 < ε } := by
  simpa [infᵢ_subtype] using @completion.uniformity_dist' α _
#align uniform_space.completion.uniformity_dist UniformSpace.Completion.uniformity_dist

/-- Metric space structure on the completion of a pseudo_metric space. -/
instance : MetricSpace (Completion α)
    where
  dist_self := Completion.dist_self
  eq_of_dist_eq_zero := Completion.eq_of_dist_eq_zero
  dist_comm := Completion.dist_comm
  dist_triangle := Completion.dist_triangle
  dist := dist
  toUniformSpace := by infer_instance
  uniformity_dist := Completion.uniformity_dist

/- warning: uniform_space.completion.coe_isometry -> UniformSpace.Completion.coe_isometry is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], Isometry.{u1, u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (MetricSpace.toPseudoMetricSpace.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.metricSpace.{u1} α _inst_1))) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (HasLiftT.mk.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (CoeTCₓ.coe.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasCoeT.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α], Isometry.{u1, u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (EMetricSpace.toPseudoEMetricSpace.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (MetricSpace.toEMetricSpace.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instMetricSpaceCompletionToUniformSpace.{u1} α _inst_1))) (UniformSpace.Completion.coe'.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1))
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.coe_isometry UniformSpace.Completion.coe_isometryₓ'. -/
/-- The embedding of a metric space in its completion is an isometry. -/
theorem coe_isometry : Isometry (coe : α → Completion α) :=
  Isometry.of_dist_eq Completion.dist_eq
#align uniform_space.completion.coe_isometry UniformSpace.Completion.coe_isometry

/- warning: uniform_space.completion.edist_eq -> UniformSpace.Completion.edist_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : α) (y : α), Eq.{1} ENNReal (EDist.edist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (PseudoMetricSpace.toEDist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (MetricSpace.toPseudoMetricSpace.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.metricSpace.{u1} α _inst_1))) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (HasLiftT.mk.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (CoeTCₓ.coe.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasCoeT.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (HasLiftT.mk.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (CoeTCₓ.coe.{succ u1, succ u1} α (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.hasCoeT.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)))) y)) (EDist.edist.{u1} α (PseudoMetricSpace.toEDist.{u1} α _inst_1) x y)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] (x : α) (y : α), Eq.{1} ENNReal (EDist.edist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (PseudoEMetricSpace.toEDist.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (EMetricSpace.toPseudoEMetricSpace.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (MetricSpace.toEMetricSpace.{u1} (UniformSpace.Completion.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.Completion.instMetricSpaceCompletionToUniformSpace.{u1} α _inst_1)))) (UniformSpace.Completion.coe'.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1) x) (UniformSpace.Completion.coe'.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1) y)) (EDist.edist.{u1} α (PseudoEMetricSpace.toEDist.{u1} α (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1)) x y)
Case conversion may be inaccurate. Consider using '#align uniform_space.completion.edist_eq UniformSpace.Completion.edist_eqₓ'. -/
@[simp]
protected theorem edist_eq (x y : α) : edist (x : Completion α) y = edist x y :=
  coe_isometry x y
#align uniform_space.completion.edist_eq UniformSpace.Completion.edist_eq

end UniformSpace.Completion


theorem t (p q r:Prop) : (p ∨ q) ∨ r ↔ p ∨ (q ∨ r) :=
iff.intro
    ( assume Hpqr : (p ∨ q) ∨ r,
        or.elim Hpqr
            ( assume Hpq : p ∨ q,
                or.elim Hpq
                    ( assume Hp : p,
                        or.intro_left (q ∨ r) Hp)
                    ( assume Hq : q,
                        or.intro_right p (or.intro_left r Hq))
            )
            ( assume Hr : r,
                or.intro_right p (or.intro_right q Hr)
            )
    )
    ( assume Hpqr : p ∨ (q ∨ r),
        or.elim Hpqr
            ( assume Hp : p,
                or.intro_left r (or.intro_left q Hp)
            )
            ( assume Hqr : q ∨ r,
                or.elim Hqr
                    ( assume Hq : q,
                        or.intro_left r (or.intro_right p Hq))
                    ( assume Hr : r,
                        or.intro_right (p ∨ q) Hr)
            )
    )
check t  -- t : ∀ p q r, (p ∨ q) ∨ r ↔ p ∨ (q ∨ r)


/-
  Definitions
-/

def square (m : ℕ) : ℕ :=
m * m

-- We can write the same function as a lambda
def square' : ℕ → ℕ :=
λ (m : ℕ), m * m

-- ... or using pattern matching
def square'' : ℕ → ℕ
| 0     := 0
| (m+1) := square'' m + 2 * m + 1

/-
  Interactive commands
-/

#print square'

#eval square' 12

#check square
#check λ x, square (string.length x)

example (x : ℕ) : ℕ := square'' (2*x)

#check ℕ → ℕ
#check ∀ x : ℕ, x = x
#check Π n : ℕ, array n ℕ

/-
  Polymorphic definitions
-/

#check ℕ
#check Type
#check Type 1
#check Type 2

universes u v
#check Type u

-- polymorphic functions take the type as an extra argument
def injective (α : Type u) (β : Type v) (f : α → β) : Prop :=
∀ x y, f x = f y → x = y

#check injective ℕ ℕ nat.succ

-- using curly braces, Lean will fill in the type automatically ("implicit argument")
def injective' {α : Type u} {β : Type v} (f : α → β) : Prop :=
injective α β f

#check injective ℕ ℕ nat.succ
#check injective' nat.succ

/-
  Inductive types (≃ free algebras)
-/

-- "Let mynat be the smallest set containing zero and succ x for every x ∈ mynat"
inductive mynat : Type
| zero : mynat
| succ (x : mynat) : mynat

#check mynat
#check mynat.zero
#check mynat.succ
#check mynat.rec

/-
  Inductive predicates
-/

-- The same mechanism can also define predicates

-- "Let le be the smallest relation containg ..."
inductive mynat.le : mynat → mynat → Prop
| refl {a} : mynat.le a a
| step {a b} : mynat.le a b → mynat.le a (mynat.succ b)

/-
  Recursion
-/

def mynat.add (m : mynat) : mynat → mynat
-- pattern matching only applies to the arguments after the colon
| mynat.zero := m
| (mynat.succ x) := mynat.succ (mynat.add x)

/-
  Namespaces
-/

-- names in lean are separated by . (like / for file names)
#check mynat.add

-- after open, we no longer need to write the prefix
open mynat
#check add
#eval add (succ zero) (succ zero)

-- adds the prefix to all definitions
namespace mynat.foo
namespace bar
def baz := succ zero
end bar
end mynat.foo
#check mynat.foo.bar.baz

#print prefix mynat

/-
  Structures
-/

structure point (α : Type u) :=
(x : α)
(y : α)

def pt : point ℕ := { y := 1, x := 2 }

#eval pt.y
#eval point.y pt

example : point ℕ := point.mk 1 2
example : point ℕ := ⟨1, 2⟩
example := { point . x := 1, y := 2 }
example : point ℕ := { x := 1, y := 2 }

example := { pt with x := 5 }

structure point3 (α : Type u) extends point α :=
(z : α)

example : point3 ℕ := { pt with z := 3 }

/-
  Type classes
-/

-- type classes allow us to define 0, 1, +, etc. for arbitrary objects
#check (0 : mynat)
#check (1 : mynat)
#check zero + zero

-- type class instances are just structures:
instance : has_zero mynat :=
{ zero := mynat.zero }

#print instances has_zero

#check (0 : mynat)

instance : has_add mynat :=
{ add := mynat.add }

#check (0 + 0 : mynat)

@[instance] -- instances are just definitions with the [instance] "attribute"
def mynat.has_one : has_one mynat :=
⟨succ zero⟩ -- any structure instance syntax works

-- now numerals work!
#check (7 : mynat)

-- type class arguments are written in square brackets
def double {α : Type u} [has_add α] (x : α) :=
x + x

#check double (7 : mynat)

/-
  Logical connectives
-/

section
-- Variables in a section are implicitly added
-- to all definitions/theorems/etc. (as needed)
variables (p q : Prop) (r : mynat → Prop)

#check p → q
#check ∀ x, r x

#check true
#check false
#check 0 = 1
#check 0 = (1 : mynat)
#check p ∨ q
#check ¬q
#check p ∧ q
#check p ↔ q
#check ∃ x, r x

#check ∀ x < 100, ∃ y > 20, ∃ z ∈ {0,1,2,3}, ∃ w, x+y = z+w

end

/-
  Tactics produce either a single new tactic state, or fail.

  A tactic state is essentially a list of goals.
-/

lemma p1_1 {a b : Prop} : a → b → b ∧ a :=
begin
  intros ha hb,
  apply and.intro,
  assumption,
  assumption,
end

-- we can freely mix tactics and expressions
#check fin.mk -- (fin n) are the natural numbers less than n
example (n : ℕ) : fin (2*n + 7) :=
fin.mk n (begin end)  -- failing tactics are underlined in red

lemma p1_2 {a b : Prop} : a → b → b ∧ a :=
begin
  intros ha hb,
  apply and.intro,
  -- repeats a tactic until it fails
  repeat { assumption },
end

lemma p1_6 {a b : Prop} : a → b → b ∧ a :=
-- ; executes the right tactic on all new goals
by intros ha hb; apply and.intro; assumption

lemma p2 (x : ℕ) : true ∧ x = x :=
-- the orelse (<|>) operator allows backtracking
by constructor; trivial <|> refl

/-
  Rewriting
-/

lemma p3 (f : ℕ → ℕ) (a b : ℕ) (h : f (1 * (0 + a)) = f b) : f a = f (0 + b) :=
begin
-- The `rw` tactic takes a (quantified) equation and rewrites the goal using it
  rw zero_add,
-- You can also pass it multiple equations, and/or rewrite hypotheses
  rw [zero_add, one_mul] at h,
  assumption,
end

/-
  Induction
-/

lemma mynat.zero_add {a : mynat} : 0 + a = a :=
begin
  induction a,
end

/-
  Theorems
-/

lemma mynat.add_comm (a b : mynat) : a + b = b + a :=
begin
end

lemma mynat.add_assoc (a b c : mynat) : a + b + c = a + (b + c) :=
begin
end

-- It's boring to write (a b c : mynat) every time.
section
variables (a b c : mynat)

lemma mynat.eq_iff_succ_eq_succ : succ a = succ b → a = b :=
begin
end

lemma mynat.add_cancel_right : a + c = b + c → a = b :=
begin
end

end

/-
  Notations
-/

notation `ℕ'` := mynat

#check (5 : ℕ')

-- supports various fancy stuff, e.g. mixfix operators and binders
def mysum {α} [has_add α] : mynat → (mynat → α) → α
| 0 f := f 0
| (succ a) f := mysum a f + f (succ a)

local notation `sum` binder `until` b `of` f:(scoped x, x) :=
mysum b f

#check sum i until 10 of i+20
#eval  sum i until 10 of i+20

/-
  There are two different kinds of "truth values":

   - Prop: types, erased at runtime
     ^^^^ this is what you use to state theorems

   - bool: inductive type with values tt/ff, computable
-/

#check true ∧ false ∨ true
#check tt && ff || tt

-- Lean automatically converts ("coerces") bools into Props
-- x  is coerced into  x = tt
example : (tt : Prop) := rfl

-- We can convert a Prop p into a bool if it is "decidable",
-- i.e. there is a function that computes whether p is true or not
#eval (∀ x ∈ [1,2,3,4], x^2 < x + 10 : bool)

-- decidable is implemented as a typeclass, and you can add your own instances
#print instances decidable

-- Many definitions in Lean use decidable propositions instead of bool:
#eval if ∀ x ∈ [1,2,3,4], x^2 < x + 10 then "ok" else "ko"
#check guard $ ∀ x ∈ [1,2,3,4], x^2 < x + 10
#check list.filter (λ x, x^2 < x + 10)

-- Choice implies that all propositions are decidable.
-- If we mark `classical.prop_decidable` as a type-class instance,
-- then if-then-else works on all propositions.
local attribute [instance, priority 0] classical.prop_decidable

-- However we cannot execute definitions that use choice to
-- construct data, such definitions are then marked as "noncomputable".
noncomputable def find_zero (f : ℕ → ℕ) : ℕ :=
if h : ∃ i, f i = 0 then classical.some h else 0

#eval find_zero (λ x, 1)

/-
  Definitional equality
-/

-- the free monoid generated by ℕ'
#check list mynat
#check ([1,4,3] : list mynat)

-- the free monoid generated by the carrier of the free monoid generated by ℕ'
#check list (list mynat)

def nested_list (α : Type u) : ℕ → Type u
| 0     := α
-- recursive calls only have arguments from after the colon
| (n+1) := list (nested_list n)

#reduce nested_list ℕ' 5

-- Why does this work?
example (n : ℕ) (xs : nested_list ℕ' (n+1)) :=
list.length xs -- xs needs to be a (list _) here, but it's a nested_list!

-- (nested_list _ (n+1)) reduces to a (list _):
#reduce λ n, nested_list ℕ' (n+1)
-- This is called "definitional equality".
-- In the underlying logic of Lean, both terms can be used interchangeably*.

#reduce λ n, n+1
#reduce λ n, 1+n

-- We can of course write functions that return nested_list
def n_singleton {α : Type u} : ∀ n, α → nested_list α n
-- in the base case, the return type is (nested_list α 0) =def= α
| 0     a := a
-- in the step case, the return type is
-- (nested_list α (n+1)) =def= (list (nested_list α n))
| (n+1) a := [n_singleton n a]


/-
Copyright (c) 2022 Yakov Pechersky. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yakov Pechersky, Floris van Doorn
-/
import data.pnat.basic

/-!
# Explicit least witnesses to existentials on positive natural numbers

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

Implemented via calling out to `nat.find`.

-/

namespace pnat

variables {p q : ℕ+ → Prop} [decidable_pred p] [decidable_pred q] (h : ∃ n, p n)

instance decidable_pred_exists_nat :
  decidable_pred (λ n' : ℕ, ∃ (n : ℕ+) (hn : n' = n), p n) := λ n',
decidable_of_iff' (∃ (h : 0 < n'), p ⟨n', h⟩) $ subtype.exists.trans $
  by simp_rw [subtype.coe_mk, @exists_comm (_ < _) (_ = _), exists_prop, exists_eq_left']

include h

/-- The `pnat` version of `nat.find_x` -/
protected def find_x : {n // p n ∧ ∀ m : ℕ+, m < n → ¬p m} :=
begin
  have : ∃ (n' : ℕ) (n : ℕ+) (hn' : n' = n), p n, from exists.elim h (λ n hn, ⟨n, n, rfl, hn⟩),
  have n := nat.find_x this,
  refine ⟨⟨n, _⟩, _, λ m hm pm, _⟩,
  { obtain ⟨n', hn', -⟩ := n.prop.1,
    rw hn',
    exact n'.prop },
  { obtain ⟨n', hn', pn'⟩ := n.prop.1,
    simpa [hn', subtype.coe_eta] using pn' },
  { exact n.prop.2 m hm ⟨m, rfl, pm⟩ }
end

/--
If `p` is a (decidable) predicate on `ℕ+` and `hp : ∃ (n : ℕ+), p n` is a proof that
there exists some positive natural number satisfying `p`, then `pnat.find hp` is the
smallest positive natural number satisfying `p`. Note that `pnat.find` is protected,
meaning that you can't just write `find`, even if the `pnat` namespace is open.

The API for `pnat.find` is:

* `pnat.find_spec` is the proof that `pnat.find hp` satisfies `p`.
* `pnat.find_min` is the proof that if `m < pnat.find hp` then `m` does not satisfy `p`.
* `pnat.find_min'` is the proof that if `m` does satisfy `p` then `pnat.find hp ≤ m`.
-/
protected def find : ℕ+ :=
pnat.find_x h

protected

protected theorem find_min : ∀ {m : ℕ+}, m < pnat.find h → ¬p m :=
(pnat.find_x h).prop.right

protected theorem find_min' {m : ℕ+} (hm : p m) : pnat.find h ≤ m :=
le_of_not_lt (λ l, pnat.find_min h l hm)

variables {n m : ℕ+}

lemma find_eq_iff : pnat.find h = m ↔ p m ∧ ∀ n < m, ¬ p n :=
begin
  split,
  { rintro rfl, exact ⟨pnat.find_spec h, λ _, pnat.find_min h⟩ },
  { rintro ⟨hm, hlt⟩,
    exact le_antisymm (pnat.find_min' h hm) (not_lt.1 $ imp_not_comm.1 (hlt _) $ pnat.find_spec h) }
end

@[simp] lemma find_lt_iff (n : ℕ+) : pnat.find h < n ↔ ∃ m < n, p m :=
⟨λ h2, ⟨pnat.find h, h2, pnat.find_spec h⟩, λ ⟨m, hmn, hm⟩, (pnat.find_min' h hm).trans_lt hmn⟩

@[simp] lemma find_le_iff (n : ℕ+) : pnat.find h ≤ n ↔ ∃ m ≤ n, p m :=
by simp only [exists_prop, ← lt_add_one_iff, find_lt_iff]

@[simp] lemma le_find_iff (n : ℕ+) : n ≤ pnat.find h ↔ ∀ m < n, ¬ p m :=
by simp_rw [← not_lt, find_lt_iff, not_exists]

@[simp] lemma lt_find_iff (n : ℕ+) : n < pnat.find h ↔ ∀ m ≤ n, ¬ p m :=
by simp only [← add_one_le_iff, le_find_iff, add_le_add_iff_right]

@[simp] lemma find_eq_one : pnat.find h = 1 ↔ p 1 :=
by simp [find_eq_iff]

@[simp] lemma one_le_find : 1 < pnat.find h ↔ ¬ p 1 :=
not_iff_not.mp $ by simp

theorem find_mono (h : ∀ n, q n → p n)
  {hp : ∃ n, p n} {hq : ∃ n, q n} :
  pnat.find hp ≤ pnat.find hq :=
pnat.find_min' _ (h _ (pnat.find_spec hq))

lemma find_le {h : ∃ n, p n} (hn : p n) : pnat.find h ≤ n :=
(pnat.find_le_iff _ _).2 ⟨n, le_rfl, hn⟩

lemma find_comp_succ (h : ∃ n, p n) (h₂ : ∃ n, p (n + 1)) (h1 : ¬ p 1) :
  pnat.find h = pnat.find h₂ + 1 :=
begin
  refine (find_eq_iff _).2 ⟨pnat.find_spec h₂, λ n, pnat.rec_on n _ _⟩,
  { simp [h1] },
  intros m IH hm,
  simp only [add_lt_add_iff_right, lt_find_iff] at hm,
  exact hm _ le_rfl
end

end pnat


import .src_field tactic

namespace mth1001

namespace myreal

section grouplaws

variables {R : Type} [comm_group R]

lemma add_comm (x y : R) : x + y = y + x := comm_group.add_comm x y
lemma add_assoc (x y z : R) : (x + y) + z = x + (y + z) := comm_group.add_assoc x y z
lemma add_zero (x : R) : x + 0 = x := comm_group.add_zero x
lemma zero_add (x : R) : 0 + x = x := by {rw [add_comm, add_zero]}
lemma add_neg' (x : R) : x + (-x) = 0 := comm_group.add_inv x
lemma neg_add (x : R) : (-x) + x = 0 := by {rw [add_comm, add_neg']}

def add_identity (u : R) := ∀ x : R, (x + u = x) ∧ (u + x = x)

lemma add_left_eq_self_mp {x a : R} : x + a = a → x = 0 :=
by { intro h, rw [←(add_zero x), ←(add_neg' a), ←add_assoc, h] }

lemma add_left_eq_self_mpr {x a : R} : x = 0 → x + a = a :=
by { intro h, rw [h, zero_add] }

theorem add_left_eq_self (x a : R) : x + a = a ↔ x = 0:=
iff.intro add_left_eq_self_mp add_left_eq_self_mpr

theorem add_left_inj  (x a b : R) : a + x = b + x ↔ a = b :=
⟨ λ h, by rw [←(add_zero a), ←(add_neg' x), ←add_assoc, h, add_assoc, add_neg' x, add_zero],
  λ h, h ▸ rfl⟩

lemma sub_eq_add_neg' (x y : R) : x - y = x + (-y) := rfl

lemma neg_zero : -(0 : R) = 0 :=
by rw [←add_zero (-(0 : R)), neg_add]

lemma sub_zero (a : R) : a - 0 = a :=
by rw [sub_eq_add_neg', neg_zero, add_zero]

theorem sub_eq_zero_iff_eq (x y : R) : x - y = 0 ↔ x = y :=
by rw [←add_left_inj (y) _ _, zero_add, sub_eq_add_neg', add_assoc, neg_add, add_zero]

theorem neg_neg (x : R) : - - x = x :=
by rw [←(zero_add (- - x)), ←(add_neg' x), add_assoc, add_neg', add_zero]

lemma neg_add_eq_neg_add_neg' (x y : R) : -(x + y) = -y + - x :=
begin
  rw [←add_zero (-(x+y)), ←add_neg' x],
  conv in (x + -x) { congr, rw ←add_zero x, skip },
  rw [←add_neg' y, ←add_assoc x y _, add_assoc (x+y) _ _, ←add_assoc, neg_add, zero_add],
end

end grouplaws

section fieldlaws

variables {R : Type} [myfield R]

lemma mul_comm (x y : R) : x * y = y * x := myfield.mul_comm x y
lemma mul_assoc (x y z : R) : (x * y) * z = x * (y * z) := myfield.mul_assoc x y z
lemma mul_one (x : R) : x * 1 = x := myfield.mul_one x
lemma one_mul (x : R) : 1 * x = x := by { rw [mul_comm, mul_one] }
lemma mul_inv (x : R) : x ≠ 0 → x * x⁻¹  = 1 := myfield.mul_inv x
lemma inv_mul (x : R) : x ≠ 0 → x⁻¹ * x = 1 := by {intro h, rw [mul_comm, mul_inv x h], }
lemma mul_add (x y z : R) : x * (y + z) = x * y + x * z := myfield.mul_add x y z
lemma zero_ne_one : (0 : R) ≠ (1 : R) := myfield.zero_ne_one
lemma exists_pair_ne : ∃ x y : R, x ≠ y := ⟨(0 : R), (1 : R), zero_ne_one⟩

def mul_identity (u : R) := ∀ x : R, (x * u = x) ∧ (u * x = x)

theorem add_mul (x y z : R) : (x + y) * z = x * z + y * z :=
by { rw [mul_comm, mul_add], repeat { rw mul_comm z _ }, }

theorem mul_identity_unique {a b : R} (h₁ : mul_identity a) (h₂ : mul_identity b)  : a = b :=
by rw [(h₂ a).left.symm, (h₁ b).right]

def mul_inverse (y x : R) := (x * y = 1) ∧ (y * x = 1)

theorem mul_inverse_unique {a b x : R} (h₁ : mul_inverse a x) (h₂ : mul_inverse b x) : a = b :=
by rw [←mul_one a, ←h₂.left, ←mul_assoc, h₁.right, one_mul]

theorem mul_zero {x : R} : x * (0 : R) = (0 : R) :=
begin
  conv {to_rhs, rw ←add_neg' (x*0)},
  conv {to_rhs, congr, rw [←add_zero (0 : R), mul_add], skip},
  rw [add_assoc, add_neg', add_zero],
end

theorem zero_mul {x : R} : (0 : R) * x = (0 : R) := (mul_comm x 0) ▸ mul_zero

theorem neg_one_mul (x : R) : (-1) * x = -x :=
begin
  conv in ((-1)* x) { rw ←(add_zero ((-1)*x)) },
  rw [←(add_neg' x), ←add_assoc],
  conv in ((-1)*x + x) { congr, skip, rw ←(one_mul x) },
  rw [←add_mul, neg_add, zero_mul, zero_add],
end

lemma neg_one_mul_neg_one : (-(1 : R)) * (-1) = 1 :=
by rw [neg_one_mul, neg_neg]

lemma neg_mul_eq_mul_neg (x y : R) : -(x * y) = x * (-y) :=
by rw [←add_zero (x * -y), ←add_neg' (x*y), ←add_assoc, ←mul_add, neg_add, mul_zero, zero_add]

lemma neg_mul_neg_self (x : R) : (-x)*(-x) = x * x :=
by rw [←neg_one_mul, mul_assoc, mul_comm x _, ←mul_assoc, ←mul_assoc, neg_one_mul_neg_one, one_mul]

lemma neg_mul_neg (x y : R) : (-x) * (-y) = x * y :=
begin
  rw [←neg_one_mul, mul_assoc, ←neg_mul_eq_mul_neg, ←neg_one_mul (x*y)],
  rw [←mul_assoc, neg_one_mul_neg_one, one_mul],
end

lemma one_inv : (1 : R)⁻¹ = (1 : R) :=
by rw [←(mul_one (1 : R)⁻¹), inv_mul (1 : R) (zero_ne_one.symm)]

theorem mul_sub (x y z : R) : x * (y - z) = x * y - x * z :=
begin
  repeat {rw sub_eq_add_neg'},
  rw [mul_add, neg_mul_eq_mul_neg],
end

theorem mul_left_inj' (x a b : R) (h₁ : x ≠ 0): a * x = b * x ↔ a = b :=
⟨λ h, by rw [←(mul_one a), ←(mul_inv x h₁), ←mul_assoc, h, mul_assoc, mul_inv x h₁, mul_one],
λ h, h ▸ rfl ⟩

open_locale classical

lemma eq_zero_of_not_eq_zero_of_mul_not_eq_zero (x b : R) (h₁ : b ≠ 0) (h₂ : x * b = 0) : x = 0 :=
begin
  by_cases h : x = 0,
  { exact h },
  { rw [←mul_one x, ←mul_inv x h, mul_comm x x⁻¹, ←mul_one (x * (x⁻¹ * x)), ←mul_inv b h₁],
    rw [mul_assoc, mul_assoc, ←mul_assoc x b _, h₂, zero_mul, mul_zero, mul_zero], }
end

lemma eq_zero_or_eq_zero_of_mul_eq_zero (x y : R) (h : x * y = 0) : x = 0 ∨ y = 0 :=
begin
  by_cases k : y = 0,
  { exact or.inr k },
  { exact or.inl (eq_zero_of_not_eq_zero_of_mul_not_eq_zero x y k h), },
end

lemma mul_inv' (x y : R) (h₁ : x ≠ 0) (h₂ : y ≠ 0) : (x * y)⁻¹ = y⁻¹ * x⁻¹ :=
begin
  have h : x * y ≠ 0 := λ h, h₁ (eq_zero_of_not_eq_zero_of_mul_not_eq_zero _ _ h₂ h),
  rw [←(mul_left_inj' _ _ _ h₁), ←(mul_left_inj' _ _ _ h₂)],
  rw [mul_assoc, inv_mul (x * y) h, mul_assoc _ _ x, inv_mul x h₁, mul_one, inv_mul y h₂],
end

theorem inv_ne_zero {a : R} : a ≠ 0 → a⁻¹ ≠ 0 :=
begin
  intros ane0 ainv0,
  have : (1 : R) = (0 : R),
  { rw [←mul_inv a ane0, ainv0, mul_zero], },
  exact zero_ne_one this.symm,
end

theorem inv_inv' (a : R) (h : a ≠ 0 ) : (a⁻¹)⁻¹ = a :=
by rw [←one_mul (a⁻¹)⁻¹, ←mul_inv a h, mul_assoc, mul_inv a⁻¹ (inv_ne_zero h), mul_one]

end fieldlaws

section powers

variables {R : Type} [myfield R]

lemma pow_succ (x : R) (n : ℕ) : x^(n+1) = x * x^n := rfl
lemma pow_zero (x : R) : x ^ 0 = 1 := rfl
lemma pow_one (x : R) : x ^ 1 = x := by rw [pow_succ, pow_zero, mul_one]
lemma pow_succ' (x : R) (n : ℕ) : x^(n+1) = x^n * x := by rw [pow_succ, mul_comm]

theorem pow_two (x : R) : x ^ 2 = x * x := by rw [pow_succ, pow_one]

theorem pow_ne_zero {x : R} (n : ℕ) (h : x ≠ 0) : x ^ n ≠ 0 :=
begin
  induction n with k hk,
  { exact (zero_ne_one.symm), },
  { rw pow_succ',
    intro k,
    apply hk,
    exact eq_zero_of_not_eq_zero_of_mul_not_eq_zero _ _ h k, },
end

theorem pow_add (x : R) (m n : ℕ) : x ^ (m + n) = (x ^ m) * (x ^ n) :=
begin
  induction n with k hk,
  { rw [nat.add_zero, pow_zero, mul_one], },
  { rw [nat.add_succ, pow_succ', pow_succ', hk, ←mul_assoc]},
end

theorem pow_mul (x : R) (m n : ℕ) : x ^ (m*n) = (x ^ m) ^ n :=
begin
  induction n with k hk,
  { rw [nat.mul_zero, pow_zero, pow_zero], },
  { rw [nat.succ_eq_add_one, left_distrib, nat.mul_one, pow_add, hk,  pow_succ'], },
end

theorem pow_sub_mul_pow (x : R) {m n : ℕ} (h : m ≤ n) : x ^ (n - m) * x ^ m = x ^ n :=
by rw [←pow_add, nat.sub_add_cancel h]

theorem pow_sub' (x : R) (m n : ℕ) (h : m ≤ n) (ne0 : x ≠ 0) : x ^ (n - m) = (x ^ n) * (x ^ m)⁻¹ :=
begin
  have h₂ : x^m ≠ 0, from pow_ne_zero m ne0,
  rw [←mul_left_inj' (x^m) _ _ h₂, mul_assoc, inv_mul _ h₂, mul_one, pow_sub_mul_pow x h],
end

end powers

end myreal

end mth1001


/-
Copyright (c) 2019 Neil Strickland. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Neil Strickland

This is about the obvious bijection (X \ {x}) ∐ 1 ≃ X, or
(option (erase a)) ≃ α in Lean notation.
-/

import data.fintype.basic

namespace combinatorics

variables {α : Type*} [fintype α] [decidable_eq α]

def erase (a : α) := {b : α // b ≠ a}

namespace erase

instance (a : α) : decidable_eq (erase a) := by { apply_instance }
instance (a : α) : fintype (erase a) := by { dsimp[erase], apply_instance }

def option_equiv (a : α) : option (erase a) ≃ α := begin
 let to_fun : option (erase a) → α :=
   λ x, @option.cases_on (erase a) (λ _, α) x a (λ b,b.val),
 have to_fun_none : to_fun none = a := rfl,
 have to_fun_some : ∀ b, to_fun (some b) = b.val := λ b,rfl,
 let inv_fun : ∀ b : α, option (erase a) :=
  λ b, (if h : b = a then none else (some ⟨b,h⟩)),
 have inv_fun_a : inv_fun a = none := dif_pos rfl,
 have inv_fun_not_a : ∀ (b : α) (h : b ≠ a), inv_fun b = some ⟨b,h⟩ :=
  λ b h,dif_neg h,
 have left_inv : function.left_inverse inv_fun to_fun :=
  begin
   rintro (_ | ⟨b,b_ne_a⟩),
   {rw[to_fun_none,inv_fun_a],},
   {rw[to_fun_some,inv_fun_not_a],}
  end,
 have right_inv : function.right_inverse inv_fun to_fun :=
  begin
   intro b,
   by_cases h : b = a,
   {rw[h,inv_fun_a]},
   {rw[inv_fun_not_a b h]}
  end,
 exact ⟨to_fun,inv_fun,left_inv,right_inv⟩,
end

lemma card (a : α) : fintype.card (erase a) + 1 = fintype.card α :=
 (@fintype.card_option (erase a) _).symm.trans
   (fintype.card_congr (option_equiv a))

def inc {a : α} (b : erase a) : α := b.val

def inc_inj (a : α) : function.injective (@inc α _ _ a) :=
 λ b₁ b₂ e, subtype.eq e

end erase

end combinatorics


/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Amelia Livingston, Yury Kudryashov,
Neil Strickland, Aaron Anderson
-/

import algebra.group_with_zero.basic

/-!
# Divisibility

This file defines the basics of the divisibility relation in the context of `(comm_)` `monoid`s
`(_with_zero)`.

## Main definitions

 * `monoid.has_dvd`

## Implementation notes

The divisibility relation is defined for all monoids, and as such, depends on the order of
  multiplication if the monoid is not commutative. There are two possible conventions for
  divisibility in the noncommutative context, and this relation follows the convention for ordinals,
  so `a | b` is defined as `∃ c, b = a * c`.

## Tags

divisibility, divides
-/

variables {α : Type*}

section semigroup

variables [semigroup α] {a b c : α}

/-- There are two possible conventions for divisibility, which coincide in a `comm_monoid`.
    This matches the convention for ordinals. -/
@[priority 100]
instance semigroup_has_dvd : has_dvd α :=
has_dvd.mk (λ a b, ∃ c, b = a * c)

-- TODO: this used to not have `c` explicit, but that seems to be important
--       for use with tactics, similar to `exists.intro`
theorem dvd.intro (c : α) (h : a * c = b) : a ∣ b :=
exists.intro c h^.symm

alias dvd.intro ← dvd_of_mul_right_eq

theorem exists_eq_mul_right_of_dvd (h : a ∣ b) : ∃ c, b = a * c := h

theorem dvd.elim {P : Prop} {a b : α} (H₁ : a ∣ b) (H₂ : ∀ c, b = a * c → P) : P :=
exists.elim H₁ H₂

local attribute [simp] mul_assoc mul_comm mul_left_comm

@[trans] theorem dvd_trans : a ∣ b → b ∣ c → a ∣ c
| ⟨d, h₁⟩ ⟨e, h₂⟩ := ⟨d * e, h₁ ▸ h₂.trans $ mul_assoc a d e⟩

alias dvd_trans ← has_dvd.dvd.trans

instance : is_trans α (∣) := ⟨λ a b c, dvd_trans⟩

@[simp] theorem dvd_mul_right (a b : α) : a ∣ a * b := dvd.intro b rfl

theorem dvd_mul_of_dvd_left (h : a ∣ b) (c : α) : a ∣ b * c :=
h.trans (dvd_mul_right b c)

alias dvd_mul_of_dvd_left ← has_dvd.dvd.mul_right

theorem dvd_of_mul_right_dvd (h : a * b ∣ c) : a ∣ c :=
(dvd_mul_right a b).trans h

section map_dvd

variables {M N : Type*} [monoid M] [monoid N]

lemma map_dvd {F : Type*} [mul_hom_class F M N] (f : F) {a b} : a ∣ b → f a ∣ f b
| ⟨c, h⟩ := ⟨f c, h.symm ▸ map_mul f a c⟩

lemma mul_hom.map_dvd (f : M →ₙ* N) {a b} : a ∣ b → f a ∣ f b := map_dvd f

lemma monoid_hom.map_dvd (f : M →* N) {a b} : a ∣ b → f a ∣ f b := map_dvd f

end map_dvd

end semigroup

section monoid

variables [monoid α]

@[refl, simp] theorem dvd_refl (a : α) : a ∣ a := dvd.intro 1 (mul_one a)
theorem dvd_rfl : ∀ {a : α}, a ∣ a := dvd_refl
instance : is_refl α (∣) := ⟨dvd_refl⟩

theorem one_dvd (a : α) : 1 ∣ a := dvd.intro a (one_mul a)

end monoid

section comm_semigroup

variables [comm_semigroup α] {a b c : α}

theorem dvd.intro_left (c : α) (h : c * a = b) : a ∣ b :=
dvd.intro _ (begin rewrite mul_comm at h, apply h end)

alias dvd.intro_left ← dvd_of_mul_left_eq

theorem exists_eq_mul_left_of_dvd (h : a ∣ b) : ∃ c, b = c * a :=
dvd.elim h (assume c, assume H1 : b = a * c, exists.intro c (eq.trans H1 (mul_comm a c)))

lemma dvd_iff_exists_eq_mul_left : a ∣ b ↔ ∃ c, b = c * a :=
⟨exists_eq_mul_left_of_dvd, by { rintro ⟨c, rfl⟩, exact ⟨c, mul_comm _ _⟩, }⟩

theorem dvd.elim_left {P : Prop} (h₁ : a ∣ b) (h₂ : ∀ c, b = c * a → P) : P :=
exists.elim (exists_eq_mul_left_of_dvd h₁) (assume c, assume h₃ : b = c * a, h₂ c h₃)

@[simp] theorem dvd_mul_left (a b : α) : a ∣ b * a := dvd.intro b (mul_comm a b)

theorem dvd_mul_of_dvd_right (h : a ∣ b) (c : α) : a ∣ c * b :=
begin rw mul_comm, exact h.mul_right _ end

alias dvd_mul_of_dvd_right ← has_dvd.dvd.mul_left

local attribute [simp] mul_assoc mul_comm mul_left_comm

theorem mul_dvd_mul : ∀ {a b c d : α}, a ∣ b → c ∣ d → a * c ∣ b * d
| a ._ c ._ ⟨e, rfl⟩ ⟨f, rfl⟩ := ⟨e * f, by simp⟩

theorem dvd_of_mul_left_dvd (h : a * b ∣ c) : b ∣ c :=
dvd.elim h (λ d ceq, dvd.intro (a * d) (by simp [ceq]))

end comm_semigroup

section comm_monoid

variables [comm_monoid α] {a b : α}

theorem mul_dvd_mul_left (a : α) {b c : α} (h : b ∣ c) : a * b ∣ a * c :=
mul_dvd_mul (dvd_refl a) h

theorem mul_dvd_mul_right (h : a ∣ b) (c : α) : a * c ∣ b * c :=
mul_dvd_mul h (dvd_refl c)

end comm_monoid

section semigroup_with_zero

variables [semigroup_with_zero α] {a : α}

theorem eq_zero_of_zero_dvd (h : 0 ∣ a) : a = 0 :=
dvd.elim h (λ c H', H'.trans (zero_mul c))

/-- Given an element `a` of a commutative semigroup with zero, there exists another element whose
    product with zero equals `a` iff `a` equals zero. -/
@[simp] lemma zero_dvd_iff : 0 ∣ a ↔ a = 0 :=
⟨eq_zero_of_zero_dvd, λ h, by { rw h, use 0, simp }⟩

@[simp] theorem dvd_zero (a : α) : a ∣ 0 := dvd.intro 0 (by simp)

end semigroup_with_zero

/-- Given two elements `b`, `c` of a `cancel_monoid_with_zero` and a nonzero element `a`,
 `a*b` divides `a*c` iff `b` divides `c`. -/
theorem mul_dvd_mul_iff_left [cancel_monoid_with_zero α] {a b c : α}
  (ha : a ≠ 0) : a * b ∣ a * c ↔ b ∣ c :=
exists_congr $ λ d, by rw [mul_assoc, mul_right_inj' ha]

/-- Given two elements `a`, `b` of a commutative `cancel_monoid_with_zero` and a nonzero
  element `c`, `a*c` divides `b*c` iff `a` divides `b`. -/
theorem mul_dvd_mul_iff_right [cancel_comm_monoid_with_zero α] {a b c : α} (hc : c ≠ 0) :
  a * c ∣ b * c ↔ a ∣ b :=
exists_congr $ λ d, by rw [mul_right_comm, mul_left_inj' hc]

/-!
### Units in various monoids
-/

namespace units

section monoid
variables [monoid α] {a b : α} {u : αˣ}

/-- Elements of the unit group of a monoid represented as elements of the monoid
    divide any element of the monoid. -/
lemma coe_dvd : ↑u ∣ a := ⟨↑u⁻¹ * a, by simp⟩

/-- In a monoid, an element `a` divides an element `b` iff `a` divides all
    associates of `b`. -/
lemma dvd_mul_right : a ∣ b * u ↔ a ∣ b :=
iff.intro
  (assume ⟨c, eq⟩, ⟨c * ↑u⁻¹, by rw [← mul_assoc, ← eq, units.mul_inv_cancel_right]⟩)
  (assume ⟨c, eq⟩, eq.symm ▸ (dvd_mul_right _ _).mul_right _)

/-- In a monoid, an element `a` divides an element `b` iff all associates of `a` divide `b`. -/
lemma mul_right_dvd : a * u ∣ b ↔ a ∣ b :=
iff.intro
  (λ ⟨c, eq⟩, ⟨↑u * c, eq.trans (mul_assoc _ _ _)⟩)
  (λ h, dvd_trans (dvd.intro ↑u⁻¹ (by rw [mul_assoc, u.mul_inv, mul_one])) h)

end monoid

section comm_monoid
variables [comm_monoid α] {a b : α} {u : αˣ}

/-- In a commutative monoid, an element `a` divides an element `b` iff `a` divides all left
    associates of `b`. -/
lemma dvd_mul_left : a ∣ u * b ↔ a ∣ b := by { rw mul_comm, apply dvd_mul_right }

/-- In a commutative monoid, an element `a` divides an element `b` iff all
  left associates of `a` divide `b`.-/
lemma mul_left_dvd : ↑u * a ∣ b ↔ a ∣ b :=
by { rw mul_comm, apply mul_right_dvd }

end comm_monoid

end units

namespace is_unit

section monoid

variables [monoid α] {a b u : α} (hu : is_unit u)
include hu

/-- Units of a monoid divide any element of the monoid. -/
@[simp] lemma dvd : u ∣ a := by { rcases hu with ⟨u, rfl⟩, apply units.coe_dvd, }

@[simp] lemma dvd_mul_right : a ∣ b * u ↔ a ∣ b :=
by { rcases hu with ⟨u, rfl⟩, apply units.dvd_mul_right, }

/-- In a monoid, an element a divides an element b iff all associates of `a` divide `b`.-/
@[simp] lemma mul_right_dvd : a * u ∣ b ↔ a ∣ b :=
by { rcases hu with ⟨u, rfl⟩, apply units.mul_right_dvd, }

end monoid

section comm_monoid
variables [comm_monoid α] (a b u : α) (hu : is_unit u)
include hu

/-- In a commutative monoid, an element `a` divides an element `b` iff `a` divides all left
    associates of `b`. -/
@[simp] lemma dvd_mul_left : a ∣ u * b ↔ a ∣ b :=
by { rcases hu with ⟨u, rfl⟩, apply units.dvd_mul_left, }

/-- In a commutative monoid, an element `a` divides an element `b` iff all
  left associates of `a` divide `b`.-/
@[simp] lemma mul_left_dvd : u * a ∣ b ↔ a ∣ b :=
by { rcases hu with ⟨u, rfl⟩, apply units.mul_left_dvd, }

end comm_monoid

end is_unit

section comm_monoid
variables [comm_monoid α]

theorem is_unit_iff_dvd_one {x : α} : is_unit x ↔ x ∣ 1 :=
⟨by rintro ⟨u, rfl⟩; exact ⟨_, u.mul_inv.symm⟩,
 λ ⟨y, h⟩, ⟨⟨x, y, h.symm, by rw [h, mul_comm]⟩, rfl⟩⟩

theorem is_unit_iff_forall_dvd {x : α} :
  is_unit x ↔ ∀ y, x ∣ y :=
is_unit_iff_dvd_one.trans ⟨λ h y, h.trans (one_dvd _), λ h, h _⟩

theorem is_unit_of_dvd_unit {x y : α}
  (xy : x ∣ y) (hu : is_unit y) : is_unit x :=
is_unit_iff_dvd_one.2 $ xy.trans $ is_unit_iff_dvd_one.1 hu

lemma is_unit_of_dvd_one : ∀a ∣ 1, is_unit (a:α)
| a ⟨b, eq⟩ := ⟨units.mk_of_mul_eq_one a b eq.symm, rfl⟩

lemma not_is_unit_of_not_is_unit_dvd {a b : α} (ha : ¬is_unit a) (hb : a ∣ b) :
  ¬ is_unit b :=
mt (is_unit_of_dvd_unit hb) ha

end comm_monoid

section comm_monoid_with_zero

variable [comm_monoid_with_zero α]

/-- `dvd_not_unit a b` expresses that `a` divides `b` "strictly", i.e. that `b` divided by `a`
is not a unit. -/
def dvd_not_unit (a b : α) : Prop := a ≠ 0 ∧ ∃ x, ¬is_unit x ∧ b = a * x

lemma dvd_not_unit_of_dvd_of_not_dvd {a b : α} (hd : a ∣ b) (hnd : ¬ b ∣ a) :
  dvd_not_unit a b :=
begin
  split,
  { rintro rfl, exact hnd (dvd_zero _) },
  { rcases hd with ⟨c, rfl⟩,
    refine ⟨c, _, rfl⟩,
    rintro ⟨u, rfl⟩,
    simpa using hnd }
end

end comm_monoid_with_zero

lemma dvd_and_not_dvd_iff [cancel_comm_monoid_with_zero α] {x y : α} :
  x ∣ y ∧ ¬y ∣ x ↔ dvd_not_unit x y :=
⟨λ ⟨⟨d, hd⟩, hyx⟩, ⟨λ hx0, by simpa [hx0] using hyx, ⟨d,
    mt is_unit_iff_dvd_one.1 (λ ⟨e, he⟩, hyx ⟨e, by rw [hd, mul_assoc, ← he, mul_one]⟩), hd⟩⟩,
  λ ⟨hx0, d, hdu, hdx⟩, ⟨⟨d, hdx⟩, λ ⟨e, he⟩, hdu (is_unit_of_dvd_one _
    ⟨e, mul_left_cancel₀ hx0 $ by conv {to_lhs, rw [he, hdx]};simp [mul_assoc]⟩)⟩⟩

section monoid_with_zero

variable [monoid_with_zero α]

theorem ne_zero_of_dvd_ne_zero {p q : α} (h₁ : q ≠ 0)
  (h₂ : p ∣ q) : p ≠ 0 :=
begin
  rcases h₂ with ⟨u, rfl⟩,
  exact left_ne_zero_of_mul h₁,
end

end monoid_with_zero


/-
Above, we used the example vec α n for vectors of elements of type α of length n.
Declare a constant vec_add that could represent a function that adds two vectors of natural numbers of the same length,
and a constant vec_reverse that can represent a function that reverses its argument.
Use implicit arguments for parameters that can be inferred.
Declare some variables and check some expressions involving the constants that you have declared.
-/

universe u
constant vec : Type u → ℕ → Type u

namespace vec
  constant empty : Π α : Type u, vec α 0
  constant cons :
    Π (α : Type u) (n : ℕ), α → vec α n → vec α (n + 1)
  constant append :
    Π (α : Type u) (n m : ℕ),  vec α m → vec α n → vec α (n + m)
  constant add :
    Π (α : Type u) (n : ℕ), vec α n → vec α n → vec α n
  const reverse :
    Π (α : Type u) (n : ℕ), vec α n → vec α n
end vec


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

! This file was ported from Lean 3 source module data.set.intervals.ord_connected_component
! leanprover-community/mathlib commit 92ca63f0fb391a9ca5f22d2409a6080e786d99f7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Set.Intervals.OrdConnected
import Mathlib.Tactic.SwapVar
/-!
# Order connected components of a set

In this file we define `Set.ordConnectedComponent s x` to be the set of `y` such that
`Set.uIcc x y ⊆ s` and prove some basic facts about this definition. At the moment of writing,
this construction is used only to prove that any linear order with order topology is a T₅ space,
so we only add API needed for this lemma.
-/

open Interval Function OrderDual

namespace Set

variable {α : Type _} [LinearOrder α] {s t : Set α} {x y z : α}

/-- Order-connected component of a point `x` in a set `s`. It is defined as the set of `y` such that
`Set.uIcc x y ⊆ s`. Note that it is empty if and only if `x ∉ s`. -/
def ordConnectedComponent (s : Set α) (x : α) : Set α :=
  { y | [[x, y]] ⊆ s }
#align set.ord_connected_component Set.ordConnectedComponent

theorem mem_ordConnectedComponent : y ∈ ordConnectedComponent s x ↔  [[x, y]] ⊆ s :=
  Iff.rfl
#align set.mem_ord_connected_component Set.mem_ordConnectedComponent

theorem dual_ordConnectedComponent :
    ordConnectedComponent (ofDual ⁻¹' s) (toDual x) = ofDual ⁻¹' ordConnectedComponent s x :=
  ext <|
      (Surjective.forall toDual.surjective).2 fun x =>
      by
      rw [mem_ordConnectedComponent, dual_uIcc]
      rfl
#align set.dual_ord_connected_component Set.dual_ordConnectedComponent

theorem ordConnectedComponent_subset : ordConnectedComponent s x ⊆ s := fun _ hy =>
  hy right_mem_uIcc
#align set.ord_connected_component_subset Set.ordConnectedComponent_subset

theorem subset_ordConnectedComponent {t} [h : OrdConnected s] (hs : x ∈ s) (ht : s ⊆ t) :
    s ⊆ ordConnectedComponent t x := fun _ hy => (h.uIcc_subset hs hy).trans ht
#align set.subset_ord_connected_component Set.subset_ordConnectedComponent

@[simp]
theorem self_mem_ordConnectedComponent : x ∈ ordConnectedComponent s x ↔ x ∈ s := by
  rw [mem_ordConnectedComponent, uIcc_self, singleton_subset_iff]
#align set.self_mem_ord_connected_component Set.self_mem_ordConnectedComponent

@[simp]
theorem nonempty_ordConnectedComponent : (ordConnectedComponent s x).Nonempty ↔ x ∈ s :=
  ⟨fun ⟨_, hy⟩ => hy <| left_mem_uIcc, fun h => ⟨x, self_mem_ordConnectedComponent.2 h⟩⟩
#align set.nonempty_ord_connected_component Set.nonempty_ordConnectedComponent

@[simp]

@[simp]
theorem ordConnectedComponent_empty : ordConnectedComponent ∅ x = ∅ :=
  ordConnectedComponent_eq_empty.2 (not_mem_empty x)
#align set.ord_connected_component_empty Set.ordConnectedComponent_empty

@[simp]
theorem ordConnectedComponent_univ : ordConnectedComponent univ x = univ := by
  simp [ordConnectedComponent]
#align set.ord_connected_component_univ Set.ordConnectedComponent_univ

theorem ordConnectedComponent_inter (s t : Set α) (x : α) :
    ordConnectedComponent (s ∩ t) x = ordConnectedComponent s x ∩ ordConnectedComponent t x := by
  simp [ordConnectedComponent, setOf_and]
#align set.ord_connected_component_inter Set.ordConnectedComponent_inter

theorem mem_ordConnectedComponent_comm :
    y ∈ ordConnectedComponent s x ↔ x ∈ ordConnectedComponent s y := by
  rw [mem_ordConnectedComponent, mem_ordConnectedComponent, uIcc_comm]
#align set.mem_ord_connected_component_comm Set.mem_ordConnectedComponent_comm

theorem mem_ordConnectedComponent_trans (hxy : y ∈ ordConnectedComponent s x)
    (hyz : z ∈ ordConnectedComponent s y) : z ∈ ordConnectedComponent s x :=
  calc
    [[x, z]] ⊆ [[x, y]] ∪ [[y, z]] := uIcc_subset_uIcc_union_uIcc
    _ ⊆ s := union_subset hxy hyz

#align set.mem_ord_connected_component_trans Set.mem_ordConnectedComponent_trans

theorem ordConnectedComponent_eq (h : [[x, y]] ⊆ s) :
    ordConnectedComponent s x = ordConnectedComponent s y :=
  ext fun _ =>
    ⟨mem_ordConnectedComponent_trans (mem_ordConnectedComponent_comm.2 h),
      mem_ordConnectedComponent_trans h⟩
#align set.ord_connected_component_eq Set.ordConnectedComponent_eq

instance : OrdConnected (ordConnectedComponent s x) :=
  ordConnected_of_uIcc_subset_left fun _ hy _ hz => (uIcc_subset_uIcc_left hz).trans hy

/-- Projection from `s : Set α` to `α` sending each order connected component of `s` to a single
point of this component. -/
noncomputable def ordConnectedProj (s : Set α) : s → α := fun x : s =>
  (nonempty_ordConnectedComponent.2 x.2).some
#align set.ord_connected_proj Set.ordConnectedProj

theorem ordConnectedProj_mem_ordConnectedComponent (s : Set α) (x : s) :
    ordConnectedProj s x ∈ ordConnectedComponent s x :=
  Nonempty.some_mem _
#align set.ord_connected_proj_mem_ord_connected_component Set.ordConnectedProj_mem_ordConnectedComponent

theorem mem_ordConnectedComponent_ordConnectedProj (s : Set α) (x : s) :
    ↑x ∈ ordConnectedComponent s (ordConnectedProj s x) :=
  mem_ordConnectedComponent_comm.2 <| ordConnectedProj_mem_ordConnectedComponent s x
#align set.mem_ord_connected_component_ord_connected_proj Set.mem_ordConnectedComponent_ordConnectedProj

@[simp]
theorem ordConnectedComponent_ordConnectedProj (s : Set α) (x : s) :
    ordConnectedComponent s (ordConnectedProj s x) = ordConnectedComponent s x :=
  ordConnectedComponent_eq <| mem_ordConnectedComponent_ordConnectedProj _ _
#align set.ord_connected_component_ord_connected_proj Set.ordConnectedComponent_ordConnectedProj

@[simp]
theorem ordConnectedProj_eq {x y : s} :
    ordConnectedProj s x = ordConnectedProj s y ↔ [[(x : α), y]] ⊆ s := by
  constructor <;> intro h
  · rw [← mem_ordConnectedComponent, ← ordConnectedComponent_ordConnectedProj, h,
      ordConnectedComponent_ordConnectedProj, self_mem_ordConnectedComponent]
    exact y.2
  · simp only [ordConnectedProj, ordConnectedComponent_eq h]
#align set.ord_connected_proj_eq Set.ordConnectedProj_eq

/-- A set that intersects each order connected component of a set by a single point. Defined as the
range of `Set.ordConnectedProj s`. -/
def ordConnectedSection (s : Set α) : Set α :=
  range <| ordConnectedProj s
#align set.ord_connected_section Set.ordConnectedSection

theorem dual_ordConnectedSection (s : Set α) :
    ordConnectedSection (ofDual ⁻¹' s) = ofDual ⁻¹' ordConnectedSection s := by
  simp_rw [ordConnectedSection, ordConnectedProj]
  ext x
  simp [dual_ordConnectedComponent]
  tauto

#align set.dual_ord_connected_section Set.dual_ordConnectedSection

theorem ordConnectedSection_subset : ordConnectedSection s ⊆ s :=
  range_subset_iff.2 fun _ => ordConnectedComponent_subset <| Nonempty.some_mem _
#align set.ord_connected_section_subset Set.ordConnectedSection_subset

theorem eq_of_mem_ordConnectedSection_of_uIcc_subset (hx : x ∈ ordConnectedSection s)
    (hy : y ∈ ordConnectedSection s) (h : [[x, y]] ⊆ s) : x = y := by
  rcases hx with ⟨x, rfl⟩; rcases hy with ⟨y, rfl⟩
  exact
    ordConnectedProj_eq.2
      (mem_ordConnectedComponent_trans
        (mem_ordConnectedComponent_trans (ordConnectedProj_mem_ordConnectedComponent _ _) h)
        (mem_ordConnectedComponent_ordConnectedProj _ _))
#align set.eq_of_mem_ord_connected_section_of_uIcc_subset
  Set.eq_of_mem_ordConnectedSection_of_uIcc_subset

/-- Given two sets `s t : Set α`, the set `Set.orderSeparatingSet s t` is the set of points that
belong both to some `Set.ordConnectedComponent tᶜ x`, `x ∈ s`, and to some
`Set.ordConnectedComponent sᶜ x`, `x ∈ t`. In the case of two disjoint closed sets, this is the
union of all open intervals $(a, b)$ such that their endpoints belong to different sets. -/
def ordSeparatingSet (s t : Set α) : Set α :=
  (⋃ x ∈ s, ordConnectedComponent (tᶜ) x) ∩ ⋃ x ∈ t, ordConnectedComponent (sᶜ) x
#align set.ord_separating_set Set.ordSeparatingSet

theorem ordSeparatingSet_comm (s t : Set α) : ordSeparatingSet s t = ordSeparatingSet t s :=
  inter_comm _ _
#align set.ord_separating_set_comm Set.ordSeparatingSet_comm

theorem disjoint_left_ordSeparatingSet : Disjoint s (ordSeparatingSet s t) :=
  Disjoint.inter_right' _ <|
    disjoint_unionᵢ₂_right.2 fun _ _ =>
      disjoint_compl_right.mono_right <| ordConnectedComponent_subset
#align set.disjoint_left_ord_separating_set Set.disjoint_left_ordSeparatingSet

theorem disjoint_right_ordSeparatingSet : Disjoint t (ordSeparatingSet s t) :=
  ordSeparatingSet_comm t s ▸ disjoint_left_ordSeparatingSet
#align set.disjoint_right_ord_separating_set Set.disjoint_right_ordSeparatingSet

theorem dual_ordSeparatingSet :
    ordSeparatingSet (ofDual ⁻¹' s) (ofDual ⁻¹' t) = ofDual ⁻¹' ordSeparatingSet s t := by
  simp only [ordSeparatingSet, mem_preimage, ← toDual.surjective.unionᵢ_comp, ofDual_toDual,
    dual_ordConnectedComponent, ← preimage_compl, preimage_inter, preimage_unionᵢ]
#align set.dual_ord_separating_set Set.dual_ordSeparatingSet

/-- An auxiliary neighborhood that will be used in the proof of `OrderTopology.t5Space`. -/
def ordT5Nhd (s t : Set α) : Set α :=
  ⋃ x ∈ s, ordConnectedComponent (tᶜ ∩ (ordConnectedSection <| ordSeparatingSet s t)ᶜ) x
#align set.ord_t5_nhd Set.ordT5Nhd

theorem disjoint_ordT5Nhd : Disjoint (ordT5Nhd s t) (ordT5Nhd t s) := by
  rw [disjoint_iff_inf_le]
  rintro x ⟨hx₁, hx₂⟩
  rcases mem_unionᵢ₂.1 hx₁ with ⟨a, has, ha⟩
  clear hx₁
  rcases mem_unionᵢ₂.1 hx₂ with ⟨b, hbt, hb⟩
  clear hx₂
  rw [mem_ordConnectedComponent, subset_inter_iff] at ha hb
  cases' le_total a b with hab hab
  on_goal 2 => swap_var a ↔ b, s ↔ t, ha ↔ hb, has ↔ hbt
  all_goals
-- porting note: wlog not implemented yet, the following replaces the three previous lines
-- wlog (discharger := tactic.skip) hab : a ≤ b := le_total a b using a b s t, b a t s
    cases' ha with ha ha'
    cases' hb with hb hb'
    have hsub : [[a, b]] ⊆ (ordSeparatingSet s t).ordConnectedSectionᶜ :=
      by
      rw [ordSeparatingSet_comm, uIcc_comm] at hb'
      calc
        [[a, b]] ⊆ [[a, x]] ∪ [[x, b]] := uIcc_subset_uIcc_union_uIcc
        _ ⊆ (ordSeparatingSet s t).ordConnectedSectionᶜ := union_subset ha' hb'
    clear ha' hb'
    cases' le_total x a with hxa hax
    · exact hb (Icc_subset_uIcc' ⟨hxa, hab⟩) has
    cases' le_total b x with hbx hxb
    · exact ha (Icc_subset_uIcc ⟨hab, hbx⟩) hbt
    have h' : x ∈ ordSeparatingSet s t := ⟨mem_unionᵢ₂.2 ⟨a, has, ha⟩, mem_unionᵢ₂.2 ⟨b, hbt, hb⟩⟩
    -- porting note: lift not implemented yet
    -- lift x to ordSeparatingSet s t using this
    suffices : ordConnectedComponent (ordSeparatingSet s t) x ⊆ [[a, b]]
    exact hsub (this <| ordConnectedProj_mem_ordConnectedComponent _ ⟨x, h'⟩) (mem_range_self _)
    rintro y (hy : [[x, y]] ⊆ ordSeparatingSet s t)
    rw [uIcc_of_le hab, mem_Icc, ← not_lt, ← not_lt]
    have sol1 := fun (hya : y < a) =>
        (disjoint_left (t := ordSeparatingSet s t)).1 disjoint_left_ordSeparatingSet has
          (hy <| Icc_subset_uIcc' ⟨hya.le, hax⟩)
    have sol2 := fun (hby : b < y) =>
        (disjoint_left (t := ordSeparatingSet s t)).1 disjoint_right_ordSeparatingSet hbt
          (hy <| Icc_subset_uIcc ⟨hxb, hby.le⟩)
    exact ⟨sol1, sol2⟩
#align set.disjoint_ord_t5_nhd Set.disjoint_ordT5Nhd


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

import data.int.basic
import tactic.linear_combination
import tactic.linarith

/-
# Quotients in Lean

Upon request, let's try to see how to construct number systems like the integers or the
rational numbers in Lean. Note that this is again some mathematical way to do this, not the
actual way, e.g. integers are defined as the disjoint union of ℕ with itself, where the first
copy is interpreted as the usual natural numbers while the second copy is interpreted as the
numbers `1-n` where `n : ℕ`. Similarly, ℚ is contructed as pairs of coprime integers (p,q).
This makes them computationally a bit better behaved than our quotient way.

## Equivalence relations in Lean

Lean knows what an equivalence relation is. It is a reflexive, symmetric and transitive relation.
A relation on a set `X` is a function `X → X → Prop`, i.e. a function that takes two elements
of a set `X` and outputs a truth value depending whether they are related or not.

```
def reflexive := ∀ x, x ∼ x

def symmetric := ∀ ⦃x y⦄, x ∼ y → y ∼ x

def transitive := ∀ ⦃x y z⦄, x ∼ y → y ∼ m z → x ∼ z

def equivalence := reflexive r ∧ symmetric r ∧ transitive r
```

-/

def R (r s : ℕ × ℕ ) : Prop :=
r.1+s.2=s.1+r.2

lemma R_def (r s : ℕ × ℕ) :
R r s ↔ r.1 + s.2 = s.1 + r.2 := by refl

lemma R_refl : reflexive R :=
λ r, by rw R_def

lemma R_symm : symmetric R :=
begin
  intros r s hrs,
  rw R_def at *,
  exact hrs.symm,
end

lemma R_trans : transitive R :=
begin
  intros r s t hrs hst,
  rw R_def at *,
  rw ← add_right_inj s.snd,
  linarith,
end

lemma R_equiv : equivalence R :=
begin
  exact ⟨R_refl, R_symm, R_trans⟩,
end

/- A setoid on a Type is a relation together with the fact that
  this relation is an equivalence relation. -/
instance s : setoid (ℕ × ℕ) :=
{ r := R,
  iseqv := R_equiv }

structure int_plane_non_zero :=
(fst : ℤ) (snd : ℤ) (non_zero : snd ≠ 0)

def S (r s : int_plane_non_zero) : Prop :=
r.1 * s.2 = s.1 * r.2

lemma S_def (r s : int_plane_non_zero) :
S r s ↔ r.1 * s.2 = s.1 * r.2 := by refl

lemma S_refl : reflexive S :=
λ r, by rw S_def

lemma S_symm : symmetric S :=
begin
  intros r s hrs,
  rw S_def at *,
  exact hrs.symm,
end

lemma S_trans : transitive S :=
begin
  intros r s t hrs hst,
  rw S_def at *,
  rw ← mul_right_inj' (s.non_zero),
  linear_combination t.snd * hrs + r.snd * hst,
end

lemma S_equiv : equivalence S :=
begin
  exact ⟨S_refl, S_symm, S_trans⟩,
end

instance t : setoid (int_plane_non_zero) :=
{ r := S,
  iseqv := S_equiv }


import data.nat.basic
import data.nat.modeq

import ent.basic
import ent.gcd
import ent.modeq
import ent.parity

set_option max_memory 4096

open nat

section pyth
  parameters a b c : ℕ
  parameter py : a^2 + b^2 = c^2
  include a b c py

  lemma not_both_odd : not (odd a ∧ odd b) :=
    begin
      intro oab,
      cases oab with oa ob,
      have ha : a^2 ≡ 1 [MOD 4] := odd_square_mod_four oa,
      have hb : b^2 ≡ 1 [MOD 4] := odd_square_mod_four ob,
      have hc : c^2 ≡ 2 [MOD 4] := begin
        rw ←py, apply modeq.modeq_add; assumption end,
      apply (@square_two_mod_four c), assumption
    end

  lemma at_least_one_even : even a ∨ even b :=
    begin
      cases even_or_odd a,
      { apply or.inl, assumption },
      { cases even_or_odd b,
        { apply or.inr, assumption },
        { have := and.intro ‹odd a› ‹odd b›,
          have := not_both_odd,
          contradiction } }
    end

  parameter a_b : coprime a b
  include a_b

  lemma not_both_even : ¬ (even a ∧ even b) := begin
    intros H,
    cases H with a_even b_even,
    rw even_iff_two_dvd at a_even b_even,
    have := not_coprime_of_dvd_of_dvd dec_trivial a_even b_even,
    contradiction
  end

  -- Need this later, so prove it before assumption "even b"
  lemma c_odd : odd c := begin
    have : odd (a^2 + b^2) := begin
      cases even_or_odd a with a_even a_odd,
      { cases even_or_odd b with b_even b_odd,
        { have := not_both_even,
          have := and.intro a_even b_even,
          contradiction },
        { apply even_plus_odd_is_odd,
          apply even_square_is_even, assumption,
          apply odd_square_is_odd, assumption } },
      { cases even_or_odd b with b_even b_odd,
        { apply odd_plus_even_is_odd,
          apply odd_square_is_odd, assumption,
          apply even_square_is_even, assumption },
        { have := not_both_odd,
          have := and.intro a_odd b_odd,
          contradiction } }
    end,
    rw py at this,
    cases even_or_odd c with c_even c_odd,
    { exact absurd (and.intro (even_square_is_even c_even) this)
                   (not_even_and_odd _) },
    { assumption }
  end

  parameter b_even : even b
  include b_even

  lemma a_odd : odd a := begin
    cases even_or_odd a with a_even _,
    { have := and.intro a_even b_even,
      have := not_both_even,
      contradiction },
    assumption
  end

/-
  lemma c_odd : odd c := begin
    cases even_or_odd c with c_even _,
    { refine absurd (and.intro _ _) (not_even_and_odd (c^2)),
      exact even_square_is_even c_even,
      rw ←py,
      apply odd_plus_even_is_odd,
      exact odd_square_is_odd a_odd,
      exact even_square_is_even b_even
    },
    assumption
  end
-/

  lemma a_c : coprime a c :=
    -- a^2 _|_ b^2 and so a^2 _|_ a^2 + b^2 = c^2 and then a _|_ c by divisibility.
    begin
      have a2_c2 : gcd (a*a) (c*c) = 1 := calc
        gcd (a*a) (c*c) = gcd (a^2) (c^2)           : by rw [nat.pow_two, nat.pow_two]
        ...             = gcd (a^2) (a^2 + b^2)     : by rw py
        ...             = gcd (a^2) (a^2 * 1 + b^2) : by simp
        ...             = gcd (a^2) (b^2)           : by rw gcd.gcd_row_op (a^2) (b^2) 1
        ...             = 1                         : coprime.pow 2 2 a_b,
      exact coprime.coprime_mul_left (coprime.coprime_mul_left_right a2_c2)
    end

  parameter pos : a > 0 ∧ b > 0 ∧ c > 0
  include pos

  lemma a_lt_c : a < c := begin
    cases nat.lt_or_ge a c with _ ge,
    { assumption },
    { have b2pos : b^2 > 0 := begin
        rw nat.pow_two,
        exact nat.mul_self_lt_mul_self_iff.mp pos.2.1
      end,
      have : a^2 < a^2 :=
      calc a^2 < a^2 + b^2  : nat.lt_add_of_pos_right b2pos
           ... = c^2        : by rw py
           ... = c*c        : by rw nat.pow_two
           ... ≤ a*a        : nat.mul_self_le_mul_self_iff.mp ge
           ... = a^2        : by rw nat.pow_two,
      exact absurd this (not_lt_of_ge (le_refl (a^2)))
      }
  end
  lemma a_le_c : a ≤ c := le_of_lt a_lt_c

  lemma cma_cpa_b2 : (c - a) * (c + a) = b^2 :=
    calc (c - a) * (c + a) = c^2 - a^2  : by rw [mul_comm, ←nat.mul_self_sub_mul_self_eq, nat.pow_two, nat.pow_two]
         ...               = a^2 + b^2 - a^2  : by rw py
         ...               = b^2  : by rw nat.add_sub_cancel_left

  -- local attribute [simp] add_comm add_left_comm add_assoc
  local attribute [simp] mul_comm mul_left_comm mul_assoc

  -- XXX: m > n?
  lemma ex : ∃ m n : ℕ, m > 0 ∧ n > 0 ∧ n < m ∧ coprime m n ∧
                        a = m^2 - n^2 ∧ b = 2 * m * n ∧ c = m^2 + n^2 :=
  begin
    have gac : gcd (c - a) (c + a) = 2 :=
      gcd.sum_difference_of_coprime_odd a c a_le_c a_c a_odd c_odd,
/-
    cases @exists_coprime (c - a) (c + a) (gac.symm ▸ (dec_trivial : 2 > 0)) with x H,
    cases H with y H',
    cases H' with x_y H'',
    cases H'' with hx hy,
-/
    rcases @exists_coprime (c - a) (c + a) (gac.symm ▸ (dec_trivial : 2 > 0)) with ⟨x, y, x_y, hx, hy⟩,
    have g := cma_cpa_b2,
    rw gac at hx hy,
    rw [hx, hy] at g,
    cases modeq.rep_of_modeq b_even dec_trivial with half_b Hb,
    simp at Hb,
    have : 2 * 2 * (x * y) = 2 * 2 * half_b^2 :=
      calc 2 * 2 * (x * y) = x * 2 * (y * 2)  : by simp
           ...             = b^2              : g
           ...             = (half_b * 2)^2   : by rw Hb
           ...             = (half_b * 2) * (half_b * 2)  : by rw nat.pow_two
           ...             = 2 * 2 * half_b^2 : by simp [nat.pow_two],
    have xyb : x * y = half_b^2 := nat.eq_of_mul_eq_mul_left dec_trivial this,
    cases coprime_square_product xyb x_y with n H5,
    cases H5 with m H6,
    cases H6 with xn2 ym2,
    existsi [m, n],
    have cma : c - a = 2 * n^2 := by rw [hx, mul_comm, xn2],
    have cpa : c + a = 2 * m^2 := by rw [hy, mul_comm, ym2],
    have twoa : 2 * a = 2 * (m^2 - n^2) :=
    calc 2 * a = (c + a) - (c - a)  : by rw gcd.sum_difference_difference a_le_c
         ...   = 2 * (m^2 - n^2)    : by simp [cma, cpa, nat.mul_sub_left_distrib],
    have twoc : 2 * c = 2 * (m^2 + n^2) :=
    calc 2 * c = (c - a) + (c + a)  : by rw gcd.sum_difference_sum a_le_c
         ...   = 2 * (m^2 + n^2)    : by simp [cma, cpa, left_distrib],
    repeat { split },
    { apply pos_iff_ne_zero.mpr, intro m0,
      have :=
      calc 0 = 0^2  : rfl
         ... = m^2  : by rw m0
         ... = y    : ym2.symm,
      have :=
      calc 0 = x * 2 * (0 * 2)  : by simp
         ... = x * 2 * (y * 2)  : by rw this
         ... = b^2  : g
         ... > 0    : pow_lt_pow_of_lt_left pos.2.1 (dec_trivial : 2 > 0),
      exact absurd this (lt_irrefl _)
    },
    { apply pos_iff_ne_zero.mpr, intro n0,
      have :=
      calc 0 = 0^2  : rfl
         ... = n^2  : by rw n0
         ... = x    : xn2.symm,
      have :=
      calc 0 = 0 * 2 * (y * 2)  : by simp
         ... = x * 2 * (y * 2)  : by rw this
         ... = b^2  : g
         ... > 0    : pow_lt_pow_of_lt_left pos.2.1 (dec_trivial : 2 > 0),
      exact absurd this (lt_irrefl _)
    },
    { apply lt_of_not_ge, intro n_ge_m,
      have :=
      calc c - a = 2 * n^2  : cma
           ...   ≥ 2 * m^2  : mul_le_mul_left 2 (pow_le_pow_of_le_left n_ge_m 2)
           ...   = c + a    : cpa.symm
           ...   > c        : nat.lt_add_of_pos_right pos.1
           ...   ≥ c - a    : nat.sub_le _ _,
      exact absurd this (lt_irrefl _)
    },
    { exact gcd.coprime_iff_squares_coprime.mp (ym2 ▸ xn2 ▸ x_y.symm) },
    { exact eq_of_mul_eq_mul_left dec_trivial twoa },
    {
      rw nat.pow_two at xyb xn2 ym2,
      have : b * b = (2 * m * n) * (2 * m * n) :=
      calc b * b = 2 * 2 * (half_b * half_b)    : by simp [*, Hb]
           ...   = 2 * 2 * (x * y)              : by rw ←xyb
           ...   = 2 * 2 * ((n * n) * (m * m))  : by rw [xn2, ym2]
           ...   = (2 * m * n) * (2 * m * n)    : by simp,
      exact sqrt_eq this
    },
    { exact eq_of_mul_eq_mul_left dec_trivial twoc }
  end
end pyth

/-
#check not_both_odd
#check not_both_even
#check c_odd
#check ex
#print axioms not_both_odd
-/


import .src_13_applications_to_even_and_odd

namespace mth1001

section even_odd_further

/-
We round of this part of the module by proving a further series of results on even and odd numbers.
We begin with a relatively straightforward result.
-/

-- Exercise 093:
example : ∃ m n, odd (m + n) :=
begin
  sorry
end

-- Exercise 094:
theorem even_sub_of_even_of_even : ∀ m n, even m → even n → even (m - n) :=
begin
  sorry
end

/-
The next examples require a bit more work. You may wish to review the section on application
to even and odd numbers. Remmember that the `ring` tactic can simplify some algebraic expressions.
-/

-- Exercise 095:
example : ¬(∀ m n, odd (m + n)) :=
begin
  sorry
end

-- Exercise 096:
example : ∃ m, ∀ n, even n ∨ even (m + n) :=
begin
  sorry
end

-- Exercise 097:
example : ∀ m n, even ( (n + m) * (n - m + 1)) :=
begin
  sorry
end

-- Exercise 098:
example : ∀ m, ∃ n, even (m + n) :=
begin
  sorry
end

-- Exercise 099:
example : ¬(∃ m, ∀ n, even (m + n)) :=
begin
  sorry
end

end even_odd_further

end mth1001