/-
Copyright (c) 2021 Anatole Dedecker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anatole Dedecker

! This file was ported from Lean 3 source module topology.algebra.order.left_right
! leanprover-community/mathlib commit e46da4e335b8671848ac711ccb34b42538c0d800
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.ContinuousOn

/-!
# Left and right continuity

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

In this file we prove a few lemmas about left and right continuous functions:

* `continuous_within_at_Ioi_iff_Ici`: two definitions of right continuity
  (with `(a, ∞)` and with `[a, ∞)`) are equivalent;
* `continuous_within_at_Iio_iff_Iic`: two definitions of left continuity
  (with `(-∞, a)` and with `(-∞, a]`) are equivalent;
* `continuous_at_iff_continuous_left_right`, `continuous_at_iff_continuous_left'_right'` :
  a function is continuous at `a` if and only if it is left and right continuous at `a`.

## Tags

left continuous, right continuous
-/

open Set Filter

open Topology

section PartialOrder

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

/- warning: continuous_within_at_Ioi_iff_Ici -> continuousWithinAt_Ioi_iff_Ici is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : PartialOrder.{u1} α] [_inst_3 : TopologicalSpace.{u2} β] {a : α} {f : α -> β}, Iff (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a) a) (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Ici.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a) a)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : PartialOrder.{u2} α] [_inst_3 : TopologicalSpace.{u1} β] {a : α} {f : α -> β}, Iff (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Ioi.{u2} α (PartialOrder.toPreorder.{u2} α _inst_2) a) a) (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Ici.{u2} α (PartialOrder.toPreorder.{u2} α _inst_2) a) a)
Case conversion may be inaccurate. Consider using '#align continuous_within_at_Ioi_iff_Ici continuousWithinAt_Ioi_iff_Iciₓ'. -/
theorem continuousWithinAt_Ioi_iff_Ici {a : α} {f : α → β} :
    ContinuousWithinAt f (Ioi a) a ↔ ContinuousWithinAt f (Ici a) a := by
  simp only [← Ici_diff_left, continuousWithinAt_diff_self]
#align continuous_within_at_Ioi_iff_Ici continuousWithinAt_Ioi_iff_Ici

/- warning: continuous_within_at_Iio_iff_Iic -> continuousWithinAt_Iio_iff_Iic is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : PartialOrder.{u1} α] [_inst_3 : TopologicalSpace.{u2} β] {a : α} {f : α -> β}, Iff (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a) a) (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Iic.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a) a)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : PartialOrder.{u2} α] [_inst_3 : TopologicalSpace.{u1} β] {a : α} {f : α -> β}, Iff (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Iio.{u2} α (PartialOrder.toPreorder.{u2} α _inst_2) a) a) (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Iic.{u2} α (PartialOrder.toPreorder.{u2} α _inst_2) a) a)
Case conversion may be inaccurate. Consider using '#align continuous_within_at_Iio_iff_Iic continuousWithinAt_Iio_iff_Iicₓ'. -/
theorem continuousWithinAt_Iio_iff_Iic {a : α} {f : α → β} :
    ContinuousWithinAt f (Iio a) a ↔ ContinuousWithinAt f (Iic a) a :=
  @continuousWithinAt_Ioi_iff_Ici αᵒᵈ _ ‹TopologicalSpace α› _ _ _ f
#align continuous_within_at_Iio_iff_Iic continuousWithinAt_Iio_iff_Iic

/- warning: nhds_left'_le_nhds_ne -> nhds_left'_le_nhds_ne is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : PartialOrder.{u1} α] (a : α), LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (nhdsWithin.{u1} α _inst_1 a (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a)) (nhdsWithin.{u1} α _inst_1 a (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : PartialOrder.{u1} α] (a : α), LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (nhdsWithin.{u1} α _inst_1 a (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a)) (nhdsWithin.{u1} α _inst_1 a (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) a)))
Case conversion may be inaccurate. Consider using '#align nhds_left'_le_nhds_ne nhds_left'_le_nhds_neₓ'. -/
theorem nhds_left'_le_nhds_ne (a : α) : 𝓝[<] a ≤ 𝓝[≠] a :=
  nhdsWithin_mono a fun y hy => ne_of_lt hy
#align nhds_left'_le_nhds_ne nhds_left'_le_nhds_ne

/- warning: nhds_right'_le_nhds_ne -> nhds_right'_le_nhds_ne is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : PartialOrder.{u1} α] (a : α), LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (nhdsWithin.{u1} α _inst_1 a (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a)) (nhdsWithin.{u1} α _inst_1 a (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : PartialOrder.{u1} α] (a : α), LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (nhdsWithin.{u1} α _inst_1 a (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2) a)) (nhdsWithin.{u1} α _inst_1 a (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) a)))
Case conversion may be inaccurate. Consider using '#align nhds_right'_le_nhds_ne nhds_right'_le_nhds_neₓ'. -/
theorem nhds_right'_le_nhds_ne (a : α) : 𝓝[>] a ≤ 𝓝[≠] a :=
  nhdsWithin_mono a fun y hy => ne_of_gt hy
#align nhds_right'_le_nhds_ne nhds_right'_le_nhds_ne

end PartialOrder

section TopologicalSpace

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

/- warning: nhds_left_sup_nhds_right -> nhds_left_sup_nhds_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toHasSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iic.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ici.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a))) (nhds.{u1} α _inst_1 a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iic.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ici.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a))) (nhds.{u1} α _inst_1 a)
Case conversion may be inaccurate. Consider using '#align nhds_left_sup_nhds_right nhds_left_sup_nhds_rightₓ'. -/
theorem nhds_left_sup_nhds_right (a : α) : 𝓝[≤] a ⊔ 𝓝[≥] a = 𝓝 a := by
  rw [← nhdsWithin_union, Iic_union_Ici, nhdsWithin_univ]
#align nhds_left_sup_nhds_right nhds_left_sup_nhds_right

/- warning: nhds_left'_sup_nhds_right -> nhds_left'_sup_nhds_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toHasSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ici.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a))) (nhds.{u1} α _inst_1 a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ici.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a))) (nhds.{u1} α _inst_1 a)
Case conversion may be inaccurate. Consider using '#align nhds_left'_sup_nhds_right nhds_left'_sup_nhds_rightₓ'. -/
theorem nhds_left'_sup_nhds_right (a : α) : 𝓝[<] a ⊔ 𝓝[≥] a = 𝓝 a := by
  rw [← nhdsWithin_union, Iio_union_Ici, nhdsWithin_univ]
#align nhds_left'_sup_nhds_right nhds_left'_sup_nhds_right

/- warning: nhds_left_sup_nhds_right' -> nhds_left_sup_nhds_right' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toHasSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iic.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a))) (nhds.{u1} α _inst_1 a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iic.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a))) (nhds.{u1} α _inst_1 a)
Case conversion may be inaccurate. Consider using '#align nhds_left_sup_nhds_right' nhds_left_sup_nhds_right'ₓ'. -/
theorem nhds_left_sup_nhds_right' (a : α) : 𝓝[≤] a ⊔ 𝓝[>] a = 𝓝 a := by
  rw [← nhdsWithin_union, Iic_union_Ioi, nhdsWithin_univ]
#align nhds_left_sup_nhds_right' nhds_left_sup_nhds_right'

/- warning: nhds_left'_sup_nhds_right' -> nhds_left'_sup_nhds_right' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toHasSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a))) (nhdsWithin.{u1} α _inst_1 a (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] (a : α), Eq.{succ u1} (Filter.{u1} α) (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))))) (nhdsWithin.{u1} α _inst_1 a (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a)) (nhdsWithin.{u1} α _inst_1 a (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_2))))) a))) (nhdsWithin.{u1} α _inst_1 a (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) a)))
Case conversion may be inaccurate. Consider using '#align nhds_left'_sup_nhds_right' nhds_left'_sup_nhds_right'ₓ'. -/
theorem nhds_left'_sup_nhds_right' (a : α) : 𝓝[<] a ⊔ 𝓝[>] a = 𝓝[≠] a := by
  rw [← nhdsWithin_union, Iio_union_Ioi]
#align nhds_left'_sup_nhds_right' nhds_left'_sup_nhds_right'

/- warning: continuous_at_iff_continuous_left_right -> continuousAt_iff_continuous_left_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] [_inst_3 : TopologicalSpace.{u2} β] {a : α} {f : α -> β}, Iff (ContinuousAt.{u1, u2} α β _inst_1 _inst_3 f a) (And (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Iic.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a) a) (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Ici.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a) a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : TopologicalSpace.{u1} β] {a : α} {f : α -> β}, Iff (ContinuousAt.{u2, u1} α β _inst_1 _inst_3 f a) (And (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Iic.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_2))))) a) a) (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Ici.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_2))))) a) a))
Case conversion may be inaccurate. Consider using '#align continuous_at_iff_continuous_left_right continuousAt_iff_continuous_left_rightₓ'. -/
theorem continuousAt_iff_continuous_left_right {a : α} {f : α → β} :
    ContinuousAt f a ↔ ContinuousWithinAt f (Iic a) a ∧ ContinuousWithinAt f (Ici a) a := by
  simp only [ContinuousWithinAt, ContinuousAt, ← tendsto_sup, nhds_left_sup_nhds_right]
#align continuous_at_iff_continuous_left_right continuousAt_iff_continuous_left_right

/- warning: continuous_at_iff_continuous_left'_right' -> continuousAt_iff_continuous_left'_right' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : LinearOrder.{u1} α] [_inst_3 : TopologicalSpace.{u2} β] {a : α} {f : α -> β}, Iff (ContinuousAt.{u1, u2} α β _inst_1 _inst_3 f a) (And (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Iio.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a) a) (ContinuousWithinAt.{u1, u2} α β _inst_1 _inst_3 f (Set.Ioi.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_2)))) a) a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : TopologicalSpace.{u1} β] {a : α} {f : α -> β}, Iff (ContinuousAt.{u2, u1} α β _inst_1 _inst_3 f a) (And (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Iio.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_2))))) a) a) (ContinuousWithinAt.{u2, u1} α β _inst_1 _inst_3 f (Set.Ioi.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_2))))) a) a))
Case conversion may be inaccurate. Consider using '#align continuous_at_iff_continuous_left'_right' continuousAt_iff_continuous_left'_right'ₓ'. -/
theorem continuousAt_iff_continuous_left'_right' {a : α} {f : α → β} :
    ContinuousAt f a ↔ ContinuousWithinAt f (Iio a) a ∧ ContinuousWithinAt f (Ioi a) a := by
  rw [continuousWithinAt_Ioi_iff_Ici, continuousWithinAt_Iio_iff_Iic,
    continuousAt_iff_continuous_left_right]
#align continuous_at_iff_continuous_left'_right' continuousAt_iff_continuous_left'_right'

end TopologicalSpace


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baumann, Stephen Morgan, Scott Morrison, Floris van Doorn
-/
import category_theory.functor

/-!
# Natural transformations

Defines natural transformations between functors.

A natural transformation `α : nat_trans F G` consists of morphisms `α.app X : F.obj X ⟶ G.obj X`,
and the naturality squares `α.naturality f : F.map f ≫ α.app Y = α.app X ≫ G.map f`,
where `f : X ⟶ Y`.

Note that we make `nat_trans.naturality` a simp lemma, with the preferred simp normal form
pushing components of natural transformations to the left.

See also `category_theory.functor_category`, where we provide the category structure on
functors and natural transformations.

Introduces notations
* `τ.app X` for the components of natural transformations,
* `F ⟶ G` for the type of natural transformations between functors `F` and `G`
  (this and the next require `category_theory.functor_category`),
* `σ ≫ τ` for vertical compositions, and
* `σ ◫ τ` for horizontal compositions.

-/

namespace category_theory

-- declare the `v`'s first; see `category_theory.category` for an explanation
universes v₁ v₂ v₃ v₄ u₁ u₂ u₃ u₄

variables {C : Type u₁} [category.{v₁} C] {D : Type u₂} [category.{v₂} D]

/--
`nat_trans F G` represents a natural transformation between functors `F` and `G`.

The field `app` provides the components of the natural transformation.

Naturality is expressed by `α.naturality_lemma`.
-/
@[ext]
structure nat_trans (F G : C ⥤ D) : Type (max u₁ v₂) :=
(app : Π X : C, F.obj X ⟶ G.obj X)
(naturality' : ∀ ⦃X Y : C⦄ (f : X ⟶ Y), F.map f ≫ app Y = app X ≫ G.map f . obviously)

restate_axiom nat_trans.naturality'
-- Rather arbitrarily, we say that the 'simpler' form is
-- components of natural transfomations moving earlier.
attribute [simp, reassoc] nat_trans.naturality

lemma congr_app {F G : C ⥤ D} {α β : nat_trans F G} (h : α = β) (X : C) : α.app X = β.app X :=
congr_fun (congr_arg nat_trans.app h) X

namespace nat_trans

/-- `nat_trans.id F` is the identity natural transformation on a functor `F`. -/
protected def id (F : C ⥤ D) : nat_trans F F :=
{ app := λ X, 𝟙 (F.obj X) }

@[simp] lemma id_app' (F : C ⥤ D) (X : C) : (nat_trans.id F).app X = 𝟙 (F.obj X) := rfl

instance (F : C ⥤ D) : inhabited (nat_trans F F) := ⟨nat_trans.id F⟩

open category
open category_theory.functor

section
variables {F G H I : C ⥤ D}

/-- `vcomp α β` is the vertical compositions of natural transformations. -/
def vcomp (α : nat_trans F G) (β : nat_trans G H) : nat_trans F H :=
{ app := λ X, (α.app X) ≫ (β.app X) }

-- functor_category will rewrite (vcomp α β) to (α ≫ β), so this is not a
-- suitable simp lemma.  We will declare the variant vcomp_app' there.
lemma vcomp_app (α : nat_trans F G) (β : nat_trans G H) (X : C) :
  (vcomp α β).app X = (α.app X) ≫ (β.app X) := rfl

end

/--
The diagram
    F(f)      F(g)      F(h)
F X ----> F Y ----> F U ----> F U
 |         |         |         |
 | α(X)    | α(Y)    | α(U)    | α(V)
 v         v         v         v
G X ----> G Y ----> G U ----> G V
    G(f)      G(g)      G(h)
commutes.
-/
example {F G : C ⥤ D} (α : nat_trans F G) {X Y U V : C} (f : X ⟶ Y) (g : Y ⟶ U) (h : U ⟶ V) :
  α.app X ≫ G.map f ≫ G.map g ≫ G.map h =
    F.map f ≫ F.map g ≫ F.map h ≫ α.app V :=
by simp

end nat_trans

end category_theory


import Init.Data.Nat.Div

/-!
This tests that `0 : Fin (n + 1)` unfolds to `0 : Nat`, which is a property kept for backwards
compatibility with mathlib in Lean 3.

Some Zulip discussion is available
[here](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Nat.2Emod.200.20n.20.3D.200.20no.20longer.20true.20by.20rfl/near/319683008).
-/

example (n : Nat) : 0 % n = 0 := rfl
example (n : Nat) : (0 : Fin (n + 1)).val = 0 := rfl


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

This contains some additional lemmas for working with heterogenous
equality.  It is far from clear that the approach taken here
(or in places where these results are used) is optimal.

-/

import data.fintype.basic

lemma heq_rec {B : Type*} {X : B → Type*} {b0 b1 : B} (eb : b0 = b1) (x : X b0) :
 x == (@eq.rec_on B b0 X b1 eb x) :=
begin
 exact eq.dcases_on eb (heq.refl x)
end

lemma cast_eq_heq {B : Type*} {X : B → Type*} {b0 b1 : B} (eb : b0 = b1)
 (x0 : X b0) (x1 : X b1) :
  (x0 == x1) ↔ (@eq.rec_on B b0 X b1 eb x0 = x1) :=
begin
 let e0 := (@heq_rec B X b0 b1 eb x0),
 split,
 {intro e1, exact eq_of_heq (e0.symm.trans e1)},
 {intro e2, exact (e0.trans (heq_of_eq e2))}
end

lemma eq_mp_heq {B : Type*} {X : B → Type*} {b0 b1 : B} (eb : b0 = b1)
 (x0 : X b0) (x1 : X b1) :
  (x0 == x1) ↔ (eq.mp (congr_arg X eb) x0 == x1) :=
begin
 let eX := congr_arg X eb,
 cases eb,
 have h : eX = rfl := rfl,
 have : eq.mp eX x0 = x0 := rfl,
 rw[this],
end

lemma eq_mpr_a {α β : Type*} (e : β = α) (a : α) :
 e.mpr a == a :=
begin
 cases e; simp[eq.mpr],
end

lemma heq_snd {B : Type*} {X : B → Type*} {x0 x1 : sigma X} (ex : x0 = x1) :
 x0.2 == x1.2 :=
begin
 let C : ∀ (x : sigma X) (e : x0 = x), Prop := (λ x _,x0.2 == x.2),
 let c : C x0 rfl := heq_of_eq rfl,
 exact @eq.dcases_on (sigma X) x0 C x1 ex c,
end

lemma heq_subst {B C : Type*} {X : B → Type*} {Y : C → Type*}
 (f : B → C) (g : ∀ b : B, (X b) → (Y (f b)))
 {b0 b1 : B} {x0 : X b0} {x1 : X b1}
 (eb : b0 = b1) (ex : x0 == x1) :
  (g b0 x0) == (g b1 x1) :=
begin
 have e0 : (@eq.rec_on B b0 X b1 eb x0 = x1) :=
  (@cast_eq_heq B X b0 b1 eb x0 x1).mp ex,
 let e1 := congr_arg (g b1) e0,
 let e2 := @eq.dcases_on B b0
            (λ b e,g b0 x0 == g b (@eq.rec_on B b0 X b e x0))
              b1 eb (heq.refl (g b0 x0)),
 exact heq_of_heq_of_eq e2 e1
end

lemma heq_section_subst {B : Sort*} {X : B → Type*}
 (s : ∀ b : B, X b) {b0 b1 : B} (eb : b0 = b1) : (s b0) == (s b1) :=
begin
 let F : ∀ (b : B) (e : b0 = b), Prop :=
  λ b e, s b0 == s b,
 let e0 : F b0 rfl := heq.refl (s b0),
 exact @eq.dcases_on B b0 F b1 eb e0,
end

lemma heq_sigma {B : Type*} {X : B → Type*} {Y : Type*} (f : ∀ (b : B) (x : X b), Y)
 {b0 b1 : B} {x0 : X b0} {x1 : X b1} (eb : b0 = b1) (ex : x0 == x1) : (f b0 x0) = (f b1 x1) :=
begin
 let F : ∀ (b : B) (e : b0 = b), Prop :=
  λ b e , (∀ (x : X b), x0 == x → (f b0 x0) = (f b x)),
 let e0 : F b0 rfl := λ x e1,congr_arg (f b0) (eq_of_heq e1),
 exact (@eq.dcases_on B b0 F b1 eb e0) x1 ex
end

lemma heq_prod {A0 A1 B0 B1 : Type*} {a0 : A0} {a1 : A1} {b0 : B0} {b1 : B1}
 (ea : a0 == a1) (eb : b0 == b1) : prod.mk a0 b0 == prod.mk a1 b1 :=
begin
 let e0 : prod.mk a0 b0 == prod.mk a1 b0 :=
  @heq.cases_on A0 a0 (λ X x,prod.mk a0 b0 == prod.mk x b0) A1 a1 ea (heq.refl _),
 let e1 : prod.mk a1 b0 == prod.mk a1 b1 :=
  @heq.cases_on B0 b0 (λ X x,prod.mk a1 b0 == prod.mk a1 x) B1 b1 eb (heq.refl _),
 exact e0.trans e1,
end

lemma subtype_heq_of_iff {α : Type*} {p q : (α → Prop)}
 (epq : ∀ a, p a ↔ q a) (x : subtype p) (y : subtype q)
 (exy : x.val = y.val) : x == y :=
begin
 let s : ∀ (p0 : α → Prop) (e0 : p = p0), subtype p0 :=
  λ p0 e0, ⟨x.val,eq.subst e0 x.property⟩,
 let C : ∀ (p0 : α → Prop) (e0 : p = p0), Prop :=
  λ p0 e0, x == s p0 e0,
 let c : C p rfl := by simp[C,s],
 have epq1 : ∀ a, p a = q a := λ a, propext (epq a),
 have epq2 : p = q := funext epq1,
 let y0 : subtype q := s q epq2,
 have x_heq_y0 : x == y0 := @eq.dcases_on (α → Prop) p C q epq2 c,
 have x_eq_y0_val : x.val = y0.val := by simp[s],
 exact x_heq_y0.trans (heq_of_eq (subtype.eq (x_eq_y0_val.symm.trans exy))),
end


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

! This file was ported from Lean 3 source module number_theory.fermat4
! leanprover-community/mathlib commit cb3ceec8485239a61ed51d944cb9a95b68c6bafc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.NumberTheory.PythagoreanTriples
import Mathbin.RingTheory.Coprime.Lemmas
import Mathbin.Tactic.LinearCombination

/-!
# Fermat's Last Theorem for the case n = 4

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.
There are no non-zero integers `a`, `b` and `c` such that `a ^ 4 + b ^ 4 = c ^ 4`.
-/

noncomputable section

open Classical

#print Fermat42 /-
/-- Shorthand for three non-zero integers `a`, `b`, and `c` satisfying `a ^ 4 + b ^ 4 = c ^ 2`.
We will show that no integers satisfy this equation. Clearly Fermat's Last theorem for n = 4
follows. -/
def Fermat42 (a b c : ℤ) : Prop :=
  a ≠ 0 ∧ b ≠ 0 ∧ a ^ 4 + b ^ 4 = c ^ 2
#align fermat_42 Fermat42
-/

namespace Fermat42

#print Fermat42.comm /-
theorem comm {a b c : ℤ} : Fermat42 a b c ↔ Fermat42 b a c :=
  by
  delta Fermat42
  rw [add_comm]
  tauto
#align fermat_42.comm Fermat42.comm
-/

/- warning: fermat_42.mul -> Fermat42.mul is a dubious translation:
lean 3 declaration is
  forall {a : Int} {b : Int} {c : Int} {k : Int}, (Ne.{1} Int k (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) -> (Iff (Fermat42 a b c) (Fermat42 (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) k a) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) k b) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) k (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) c)))
but is expected to have type
  forall {a : Int} {b : Int} {c : Int} {k : Int}, (Ne.{1} Int k (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) -> (Iff (Fermat42 a b c) (Fermat42 (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) k a) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) k b) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) k (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) c)))
Case conversion may be inaccurate. Consider using '#align fermat_42.mul Fermat42.mulₓ'. -/
theorem mul {a b c k : ℤ} (hk0 : k ≠ 0) : Fermat42 a b c ↔ Fermat42 (k * a) (k * b) (k ^ 2 * c) :=
  by
  delta Fermat42
  constructor
  · intro f42
    constructor
    · exact mul_ne_zero hk0 f42.1
    constructor
    · exact mul_ne_zero hk0 f42.2.1
    · have H : a ^ 4 + b ^ 4 = c ^ 2 := f42.2.2
      linear_combination k ^ 4 * H
  · intro f42
    constructor
    · exact right_ne_zero_of_mul f42.1
    constructor
    · exact right_ne_zero_of_mul f42.2.1
    apply (mul_right_inj' (pow_ne_zero 4 hk0)).mp
    linear_combination f42.2.2
#align fermat_42.mul Fermat42.mul

#print Fermat42.ne_zero /-
theorem ne_zero {a b c : ℤ} (h : Fermat42 a b c) : c ≠ 0 :=
  by
  apply ne_zero_pow two_ne_zero _; apply ne_of_gt
  rw [← h.2.2, (by ring : a ^ 4 + b ^ 4 = (a ^ 2) ^ 2 + (b ^ 2) ^ 2)]
  exact
    add_pos (sq_pos_of_ne_zero _ (pow_ne_zero 2 h.1)) (sq_pos_of_ne_zero _ (pow_ne_zero 2 h.2.1))
#align fermat_42.ne_zero Fermat42.ne_zero
-/

#print Fermat42.Minimal /-
/-- We say a solution to `a ^ 4 + b ^ 4 = c ^ 2` is minimal if there is no other solution with
a smaller `c` (in absolute value). -/
def Minimal (a b c : ℤ) : Prop :=
  Fermat42 a b c ∧ ∀ a1 b1 c1 : ℤ, Fermat42 a1 b1 c1 → Int.natAbs c ≤ Int.natAbs c1
#align fermat_42.minimal Fermat42.Minimal
-/

#print Fermat42.exists_minimal /-
/-- if we have a solution to `a ^ 4 + b ^ 4 = c ^ 2` then there must be a minimal one. -/
theorem exists_minimal {a b c : ℤ} (h : Fermat42 a b c) : ∃ a0 b0 c0, Minimal a0 b0 c0 :=
  by
  let S : Set ℕ := { n | ∃ s : ℤ × ℤ × ℤ, Fermat42 s.1 s.2.1 s.2.2 ∧ n = Int.natAbs s.2.2 }
  have S_nonempty : S.nonempty := by
    use Int.natAbs c
    rw [Set.mem_setOf_eq]
    use ⟨a, ⟨b, c⟩⟩
    tauto
  let m : ℕ := Nat.find S_nonempty
  have m_mem : m ∈ S := Nat.find_spec S_nonempty
  rcases m_mem with ⟨s0, hs0, hs1⟩
  use s0.1, s0.2.1, s0.2.2, hs0
  intro a1 b1 c1 h1
  rw [← hs1]
  apply Nat.find_min'
  use ⟨a1, ⟨b1, c1⟩⟩
  tauto
#align fermat_42.exists_minimal Fermat42.exists_minimal
-/

/- warning: fermat_42.coprime_of_minimal -> Fermat42.coprime_of_minimal is a dubious translation:
lean 3 declaration is
  forall {a : Int} {b : Int} {c : Int}, (Fermat42.Minimal a b c) -> (IsCoprime.{0} Int Int.commSemiring a b)
but is expected to have type
  forall {a : Int} {b : Int} {c : Int}, (Fermat42.Minimal a b c) -> (IsCoprime.{0} Int Int.instCommSemiringInt a b)
Case conversion may be inaccurate. Consider using '#align fermat_42.coprime_of_minimal Fermat42.coprime_of_minimalₓ'. -/
/-- a minimal solution to `a ^ 4 + b ^ 4 = c ^ 2` must have `a` and `b` coprime. -/
theorem coprime_of_minimal {a b c : ℤ} (h : Minimal a b c) : IsCoprime a b :=
  by
  apply int.gcd_eq_one_iff_coprime.mp
  by_contra hab
  obtain ⟨p, hp, hpa, hpb⟩ := nat.prime.not_coprime_iff_dvd.mp hab
  obtain ⟨a1, rfl⟩ := int.coe_nat_dvd_left.mpr hpa
  obtain ⟨b1, rfl⟩ := int.coe_nat_dvd_left.mpr hpb
  have hpc : (p : ℤ) ^ 2 ∣ c :=
    by
    rw [← Int.pow_dvd_pow_iff zero_lt_two, ← h.1.2.2]
    apply Dvd.intro (a1 ^ 4 + b1 ^ 4)
    ring
  obtain ⟨c1, rfl⟩ := hpc
  have hf : Fermat42 a1 b1 c1 :=
    (Fermat42.mul (int.coe_nat_ne_zero.mpr (Nat.Prime.ne_zero hp))).mpr h.1
  apply Nat.le_lt_antisymm (h.2 _ _ _ hf)
  rw [Int.natAbs_mul, lt_mul_iff_one_lt_left, Int.natAbs_pow, Int.natAbs_ofNat]
  · exact Nat.one_lt_pow _ _ zero_lt_two (Nat.Prime.one_lt hp)
  · exact Nat.pos_of_ne_zero (Int.natAbs_ne_zero_of_ne_zero (NeZero hf))
#align fermat_42.coprime_of_minimal Fermat42.coprime_of_minimal

#print Fermat42.minimal_comm /-
/-- We can swap `a` and `b` in a minimal solution to `a ^ 4 + b ^ 4 = c ^ 2`. -/
theorem minimal_comm {a b c : ℤ} : Minimal a b c → Minimal b a c := fun ⟨h1, h2⟩ =>
  ⟨Fermat42.comm.mp h1, h2⟩
#align fermat_42.minimal_comm Fermat42.minimal_comm
-/

#print Fermat42.neg_of_minimal /-
/-- We can assume that a minimal solution to `a ^ 4 + b ^ 4 = c ^ 2` has positive `c`. -/
theorem neg_of_minimal {a b c : ℤ} : Minimal a b c → Minimal a b (-c) :=
  by
  rintro ⟨⟨ha, hb, heq⟩, h2⟩
  constructor
  · apply And.intro ha (And.intro hb _)
    rw [HEq]
    exact (neg_sq c).symm
  rwa [Int.natAbs_neg c]
#align fermat_42.neg_of_minimal Fermat42.neg_of_minimal
-/

#print Fermat42.exists_odd_minimal /-
/-- We can assume that a minimal solution to `a ^ 4 + b ^ 4 = c ^ 2` has `a` odd. -/
theorem exists_odd_minimal {a b c : ℤ} (h : Fermat42 a b c) :
    ∃ a0 b0 c0, Minimal a0 b0 c0 ∧ a0 % 2 = 1 :=
  by
  obtain ⟨a0, b0, c0, hf⟩ := exists_minimal h
  cases' Int.emod_two_eq_zero_or_one a0 with hap hap
  · cases' Int.emod_two_eq_zero_or_one b0 with hbp hbp
    · exfalso
      have h1 : 2 ∣ (Int.gcd a0 b0 : ℤ) :=
        Int.dvd_gcd (Int.dvd_of_emod_eq_zero hap) (Int.dvd_of_emod_eq_zero hbp)
      rw [int.gcd_eq_one_iff_coprime.mpr (coprime_of_minimal hf)] at h1
      revert h1
      norm_num
    · exact ⟨b0, ⟨a0, ⟨c0, minimal_comm hf, hbp⟩⟩⟩
  exact ⟨a0, ⟨b0, ⟨c0, hf, hap⟩⟩⟩
#align fermat_42.exists_odd_minimal Fermat42.exists_odd_minimal
-/

#print Fermat42.exists_pos_odd_minimal /-
/-- We can assume that a minimal solution to `a ^ 4 + b ^ 4 = c ^ 2` has
`a` odd and `c` positive. -/
theorem exists_pos_odd_minimal {a b c : ℤ} (h : Fermat42 a b c) :
    ∃ a0 b0 c0, Minimal a0 b0 c0 ∧ a0 % 2 = 1 ∧ 0 < c0 :=
  by
  obtain ⟨a0, b0, c0, hf, hc⟩ := exists_odd_minimal h
  rcases lt_trichotomy 0 c0 with (h1 | rfl | h1)
  · use a0, b0, c0
    tauto
  · exfalso
    exact NeZero hf.1 rfl
  · use a0, b0, -c0, neg_of_minimal hf, hc
    exact neg_pos.mpr h1
#align fermat_42.exists_pos_odd_minimal Fermat42.exists_pos_odd_minimal
-/

end Fermat42

/- warning: int.coprime_of_sq_sum -> Int.coprime_of_sq_sum is a dubious translation:
lean 3 declaration is
  forall {r : Int} {s : Int}, (IsCoprime.{0} Int Int.commSemiring s r) -> (IsCoprime.{0} Int Int.commSemiring (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) r (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) s (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))) r)
but is expected to have type
  forall {r : Int} {s : Int}, (IsCoprime.{0} Int Int.instCommSemiringInt s r) -> (IsCoprime.{0} Int Int.instCommSemiringInt (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) r (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) s (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))) r)
Case conversion may be inaccurate. Consider using '#align int.coprime_of_sq_sum Int.coprime_of_sq_sumₓ'. -/
theorem Int.coprime_of_sq_sum {r s : ℤ} (h2 : IsCoprime s r) : IsCoprime (r ^ 2 + s ^ 2) r :=
  by
  rw [sq, sq]
  exact (IsCoprime.mul_left h2 h2).mul_add_left_left r
#align int.coprime_of_sq_sum Int.coprime_of_sq_sum

/- warning: int.coprime_of_sq_sum' -> Int.coprime_of_sq_sum' is a dubious translation:
lean 3 declaration is
  forall {r : Int} {s : Int}, (IsCoprime.{0} Int Int.commSemiring r s) -> (IsCoprime.{0} Int Int.commSemiring (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) r (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) s (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) r s))
but is expected to have type
  forall {r : Int} {s : Int}, (IsCoprime.{0} Int Int.instCommSemiringInt r s) -> (IsCoprime.{0} Int Int.instCommSemiringInt (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) r (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) s (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) r s))
Case conversion may be inaccurate. Consider using '#align int.coprime_of_sq_sum' Int.coprime_of_sq_sum'ₓ'. -/
theorem Int.coprime_of_sq_sum' {r s : ℤ} (h : IsCoprime r s) : IsCoprime (r ^ 2 + s ^ 2) (r * s) :=
  by
  apply IsCoprime.mul_right (Int.coprime_of_sq_sum (is_coprime_comm.mp h))
  rw [add_comm]; apply Int.coprime_of_sq_sum h
#align int.coprime_of_sq_sum' Int.coprime_of_sq_sum'

namespace Fermat42

#print Fermat42.not_minimal /-
-- If we have a solution to a ^ 4 + b ^ 4 = c ^ 2, we can construct a smaller one. This
-- implies there can't be a smallest solution.
theorem not_minimal {a b c : ℤ} (h : Minimal a b c) (ha2 : a % 2 = 1) (hc : 0 < c) : False :=
  by
  -- Use the fact that a ^ 2, b ^ 2, c form a pythagorean triple to obtain m and n such that
  -- a ^ 2 = m ^ 2 - n ^ 2, b ^ 2 = 2 * m * n and c = m ^ 2 + n ^ 2
  -- first the formula:
  have ht : PythagoreanTriple (a ^ 2) (b ^ 2) c :=
    by
    delta PythagoreanTriple
    linear_combination h.1.2.2
  -- coprime requirement:
  have h2 : Int.gcd (a ^ 2) (b ^ 2) = 1 := int.gcd_eq_one_iff_coprime.mpr (coprime_of_minimal h).pow
  -- in order to reduce the possibilities we get from the classification of pythagorean triples
  -- it helps if we know the parity of a ^ 2 (and the sign of c):
  have ha22 : a ^ 2 % 2 = 1 := by
    rw [sq, Int.mul_emod, ha2]
    norm_num
  obtain ⟨m, n, ht1, ht2, ht3, ht4, ht5, ht6⟩ := ht.coprime_classification' h2 ha22 hc
  -- Now a, n, m form a pythagorean triple and so we can obtain r and s such that
  -- a = r ^ 2 - s ^ 2, n = 2 * r * s and m = r ^ 2 + s ^ 2
  -- formula:
  have htt : PythagoreanTriple a n m :=
    by
    delta PythagoreanTriple
    linear_combination ht1
  -- a and n are coprime, because a ^ 2 = m ^ 2 - n ^ 2 and m and n are coprime.
  have h3 : Int.gcd a n = 1 := by
    apply int.gcd_eq_one_iff_coprime.mpr
    apply @IsCoprime.of_mul_left_left _ _ _ a
    rw [← sq, ht1, (by ring : m ^ 2 - n ^ 2 = m ^ 2 + -n * n)]
    exact (int.gcd_eq_one_iff_coprime.mp ht4).pow_leftₓ.add_mul_right_left (-n)
  -- m is positive because b is non-zero and b ^ 2 = 2 * m * n and we already have 0 ≤ m.
  have hb20 : b ^ 2 ≠ 0 := mt pow_eq_zero h.1.2.1
  have h4 : 0 < m := by
    apply lt_of_le_of_ne ht6
    rintro rfl
    revert hb20
    rw [ht2]
    simp
  obtain ⟨r, s, htt1, htt2, htt3, htt4, htt5, htt6⟩ := htt.coprime_classification' h3 ha2 h4
  -- Now use the fact that (b / 2) ^ 2 = m * r * s, and m, r and s are pairwise coprime to obtain
  -- i, j and k such that m = i ^ 2, r = j ^ 2 and s = k ^ 2.
  -- m and r * s are coprime because m = r ^ 2 + s ^ 2 and r and s are coprime.
  have hcp : Int.gcd m (r * s) = 1 := by
    rw [htt3]
    exact
      int.gcd_eq_one_iff_coprime.mpr (Int.coprime_of_sq_sum' (int.gcd_eq_one_iff_coprime.mp htt4))
  -- b is even because b ^ 2 = 2 * m * n.
  have hb2 : 2 ∣ b := by
    apply @Int.Prime.dvd_pow' _ 2 _ Nat.prime_two
    rw [ht2, mul_assoc]
    exact dvd_mul_right 2 (m * n)
  cases' hb2 with b' hb2'
  have hs : b' ^ 2 = m * (r * s) :=
    by
    apply (mul_right_inj' (by norm_num : (4 : ℤ) ≠ 0)).mp
    linear_combination (-b - 2 * b') * hb2' + ht2 + 2 * m * htt2
  have hrsz : r * s ≠ 0 :=
    by
    -- because b ^ 2 is not zero and (b / 2) ^ 2 = m * (r * s)
    by_contra hrsz
    revert hb20
    rw [ht2, htt2, mul_assoc, @mul_assoc _ _ _ r s, hrsz]
    simp
  have h2b0 : b' ≠ 0 := by
    apply ne_zero_pow two_ne_zero
    rw [hs]
    apply mul_ne_zero
    · exact ne_of_gt h4
    · exact hrsz
  obtain ⟨i, hi⟩ := Int.sq_of_gcd_eq_one hcp hs.symm
  -- use m is positive to exclude m = - i ^ 2
  have hi' : ¬m = -i ^ 2 := by
    by_contra h1
    have hit : -i ^ 2 ≤ 0
    apply neg_nonpos.mpr (sq_nonneg i)
    rw [← h1] at hit
    apply absurd h4 (not_lt.mpr hit)
  replace hi : m = i ^ 2
  · apply Or.resolve_right hi hi'
  rw [mul_comm] at hs
  rw [Int.gcd_comm] at hcp
  -- obtain d such that r * s = d ^ 2
  obtain ⟨d, hd⟩ := Int.sq_of_gcd_eq_one hcp hs.symm
  -- (b / 2) ^ 2 and m are positive so r * s is positive
  have hd' : ¬r * s = -d ^ 2 := by
    by_contra h1
    rw [h1] at hs
    have h2 : b' ^ 2 ≤ 0 := by
      rw [hs, (by ring : -d ^ 2 * m = -(d ^ 2 * m))]
      exact neg_nonpos.mpr ((zero_le_mul_right h4).mpr (sq_nonneg d))
    have h2' : 0 ≤ b' ^ 2 := by apply sq_nonneg b'
    exact absurd (lt_of_le_of_ne h2' (Ne.symm (pow_ne_zero _ h2b0))) (not_lt.mpr h2)
  replace hd : r * s = d ^ 2
  · apply Or.resolve_right hd hd'
  -- r = +/- j ^ 2
  obtain ⟨j, hj⟩ := Int.sq_of_gcd_eq_one htt4 hd
  have hj0 : j ≠ 0 := by
    intro h0
    rw [h0, zero_pow zero_lt_two, neg_zero, or_self_iff] at hj
    apply left_ne_zero_of_mul hrsz hj
  rw [mul_comm] at hd
  rw [Int.gcd_comm] at htt4
  -- s = +/- k ^ 2
  obtain ⟨k, hk⟩ := Int.sq_of_gcd_eq_one htt4 hd
  have hk0 : k ≠ 0 := by
    intro h0
    rw [h0, zero_pow zero_lt_two, neg_zero, or_self_iff] at hk
    apply right_ne_zero_of_mul hrsz hk
  have hj2 : r ^ 2 = j ^ 4 := by
    cases' hj with hjp hjp <;>
      · rw [hjp]
        ring
  have hk2 : s ^ 2 = k ^ 4 := by
    cases' hk with hkp hkp <;>
      · rw [hkp]
        ring
  -- from m = r ^ 2 + s ^ 2 we now get a new solution to a ^ 4 + b ^ 4 = c ^ 2:
  have hh : i ^ 2 = j ^ 4 + k ^ 4 := by rw [← hi, htt3, hj2, hk2]
  have hn : n ≠ 0 := by
    rw [ht2] at hb20
    apply right_ne_zero_of_mul hb20
  -- and it has a smaller c: from c = m ^ 2 + n ^ 2 we see that m is smaller than c, and i ^ 2 = m.
  have hic : Int.natAbs i < Int.natAbs c :=
    by
    apply int.coe_nat_lt.mp
    rw [← Int.eq_natAbs_of_zero_le (le_of_lt hc)]
    apply gt_of_gt_of_ge _ (Int.natAbs_le_self_sq i)
    rw [← hi, ht3]
    apply gt_of_gt_of_ge _ (Int.le_self_sq m)
    exact lt_add_of_pos_right (m ^ 2) (sq_pos_of_ne_zero n hn)
  have hic' : Int.natAbs c ≤ Int.natAbs i :=
    by
    apply h.2 j k i
    exact ⟨hj0, hk0, hh.symm⟩
  apply absurd (not_le_of_lt hic) (not_not.mpr hic')
#align fermat_42.not_minimal Fermat42.not_minimal
-/

end Fermat42

/- warning: not_fermat_42 -> not_fermat_42 is a dubious translation:
lean 3 declaration is
  forall {a : Int} {b : Int} {c : Int}, (Ne.{1} Int a (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) -> (Ne.{1} Int b (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) -> (Ne.{1} Int (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) a (OfNat.ofNat.{0} Nat 4 (OfNat.mk.{0} Nat 4 (bit0.{0} Nat Nat.hasAdd (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) b (OfNat.ofNat.{0} Nat 4 (OfNat.mk.{0} Nat 4 (bit0.{0} Nat Nat.hasAdd (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) c (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall {a : Int} {b : Int} {c : Int}, (Ne.{1} Int a (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) -> (Ne.{1} Int b (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) -> (Ne.{1} Int (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) a (OfNat.ofNat.{0} Nat 4 (instOfNatNat 4))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) b (OfNat.ofNat.{0} Nat 4 (instOfNatNat 4)))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) c (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))))
Case conversion may be inaccurate. Consider using '#align not_fermat_42 not_fermat_42ₓ'. -/
theorem not_fermat_42 {a b c : ℤ} (ha : a ≠ 0) (hb : b ≠ 0) : a ^ 4 + b ^ 4 ≠ c ^ 2 :=
  by
  intro h
  obtain ⟨a0, b0, c0, ⟨hf, h2, hp⟩⟩ :=
    Fermat42.exists_pos_odd_minimal (And.intro ha (And.intro hb h))
  apply Fermat42.not_minimal hf h2 hp
#align not_fermat_42 not_fermat_42

/- warning: not_fermat_4 -> not_fermat_4 is a dubious translation:
lean 3 declaration is
  forall {a : Int} {b : Int} {c : Int}, (Ne.{1} Int a (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) -> (Ne.{1} Int b (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) -> (Ne.{1} Int (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) a (OfNat.ofNat.{0} Nat 4 (OfNat.mk.{0} Nat 4 (bit0.{0} Nat Nat.hasAdd (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) b (OfNat.ofNat.{0} Nat 4 (OfNat.mk.{0} Nat 4 (bit0.{0} Nat Nat.hasAdd (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.monoid)) c (OfNat.ofNat.{0} Nat 4 (OfNat.mk.{0} Nat 4 (bit0.{0} Nat Nat.hasAdd (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))))
but is expected to have type
  forall {a : Int} {b : Int} {c : Int}, (Ne.{1} Int a (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) -> (Ne.{1} Int b (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) -> (Ne.{1} Int (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) a (OfNat.ofNat.{0} Nat 4 (instOfNatNat 4))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) b (OfNat.ofNat.{0} Nat 4 (instOfNatNat 4)))) (HPow.hPow.{0, 0, 0} Int Nat Int (instHPow.{0, 0} Int Nat (Monoid.Pow.{0} Int Int.instMonoidInt)) c (OfNat.ofNat.{0} Nat 4 (instOfNatNat 4))))
Case conversion may be inaccurate. Consider using '#align not_fermat_4 not_fermat_4ₓ'. -/
theorem not_fermat_4 {a b c : ℤ} (ha : a ≠ 0) (hb : b ≠ 0) : a ^ 4 + b ^ 4 ≠ c ^ 4 :=
  by
  intro heq
  apply @not_fermat_42 _ _ (c ^ 2) ha hb
  rw [HEq]; ring
#align not_fermat_4 not_fermat_4


open classical

theorem Ex008(a b : Prop): ¬(a ∧ b) → (¬a ∨ ¬b) :=
assume H1:¬(a ∧ b),
have tmp:¬¬(¬a ∨ ¬b),from not.intro
  (
    assume H2:¬(¬a ∨ ¬b),
    have ¬a,from not.intro
      (
        assume H3:a,
        have A:¬b,from not.intro
          (
            assume H4:b,
            have tmp2:(a ∧ b), from and.intro H3 H4,
            show false, from H1 tmp2
          ),
        have B:¬a ∨ ¬b, from or.inr A,
        show false, from H2 B
      ),
    have ¬a ∨ ¬b, from  or.inl this,
    show false, from H2 this
  ),
by_contradiction
  (
    assume H5:¬(¬a ∨ ¬b),
    show false, from tmp H5
  )


import algebra.field
import tactic

noncomputable theory

/--
The set of R-points of affine plane is `Hom(Spec(R),A_Z^2)`,
which is just `R^2`
-/
@[ext]
structure affine_plane_point (K : Type*) [comm_ring K] :=
mk :: (x y : K)

def affine_plane_point.base_change {K : Type*} [comm_ring K]
(P : affine_plane_point K) {L : Type*} [comm_ring L] (f : ring_hom K L)
: affine_plane_point L := ⟨ f P.x, f P.y ⟩

/--
A point in `A^3` whose coordinates are not all-zero
-/
@[ext]
structure projective_plane_point' (K : Type*) [comm_ring K] :=
mk :: (X Y Z : K) (h : ¬ (X = 0 ∧ Y = 0 ∧ Z = 0))

def projective_plane_point'.base_change' {K : Type*} [field K]
(P : projective_plane_point' K) {L : Type*} [field L] (f : ring_hom K L)
: projective_plane_point' L := ⟨ f P.X, f P.Y, f P.Z, (by simp [ring_hom.map_eq_zero, P.h]) ⟩

def projective_plane_point'.is_equal {K : Type*} [field K]
(P P' : projective_plane_point' K) :=
∃ a : K, a ≠ 0 ∧ P'.X = a * P.X ∧ P'.Y = a * P.Y ∧ P'.Z = a * P.Z

@[simp]
lemma projective_plane_point'.is_equal.refl {K : Type*} [field K]
(P : projective_plane_point' K) : P.is_equal P :=
begin
  use 1,
  simp,
end

@[simp]
lemma projective_plane_point'.is_equal.symm {K : Type*} [field K]
(P P' : projective_plane_point' K) (h : P.is_equal P') : P'.is_equal P :=
begin
  rcases h with ⟨ a, h1, h2, h3, h4 ⟩,
  use 1/a,
  simp [h2, h3, h4, h1],
end

@[simp]
lemma projective_plane_point'.is_equal.trans {K : Type*} [field K]
(P P' P'' : projective_plane_point' K) (h : P.is_equal P') (h' : P'.is_equal P'') : P.is_equal P'' :=
begin
  rcases h with ⟨ a, h1, h2, h3, h4 ⟩,
  rcases h' with ⟨ a', h'1, h'2, h'3, h'4 ⟩,
  use a*a',
  rw [h'2, h'3, h'4, h2, h3, h4],
  split, { simp [h1, h'1], },
  split, { ring, },
  split, { ring, },
  ring,
end

lemma projective_plane_point'.is_equal.is_equivalence (K : Type*) [h : field K]
: equivalence (@projective_plane_point'.is_equal K h) :=
mk_equivalence (@projective_plane_point'.is_equal K h)
(@projective_plane_point'.is_equal.refl K h)
(@projective_plane_point'.is_equal.symm K h)
(@projective_plane_point'.is_equal.trans K h)

instance projective_plane_point.setoid (K : Type*) [h : field K]
: setoid (projective_plane_point' K) :=
setoid.mk (@projective_plane_point'.is_equal K h)
(projective_plane_point'.is_equal.is_equivalence K)

/--
The set of R-points of projective plane is `Hom(Spec(R),P_Z^2)`,
for simplicity we restrict ourself to field case, namely
the equivalent class of points in `A^3`
-/
def projective_plane_point (K : Type*) [field K] : Type* :=
quotient (projective_plane_point.setoid K)

def projective_plane_point'.base_change {K : Type*} [field K]
(P : projective_plane_point' K) {L : Type*} [field L] (f : ring_hom K L)
: projective_plane_point L := quotient.mk (P.base_change' f)

lemma projective_plane_point'.base_change.sound {K : Type*} [field K]
{L : Type*} [field L] (f : ring_hom K L)
(P P' : projective_plane_point' K) (h : P.is_equal P')
: P.base_change f = P'.base_change f :=
begin
  simp [projective_plane_point'.base_change, quotient.sound],
  rcases h with ⟨ a, h1, h2, h3, h4 ⟩,
  use (f a),
  simp [projective_plane_point'.base_change', h1, h2, h3, h4],
end

def projective_plane_point.base_change {K : Type*} [field K]
(P : projective_plane_point K) {L : Type*} [field L] (f : ring_hom K L)
: projective_plane_point L := quotient.lift (λ (P : projective_plane_point' K),
  projective_plane_point'.base_change P f)
(projective_plane_point'.base_change.sound f) P

def projective_plane_point'.is_finite {K : Type*} [field K]
(P : projective_plane_point' K) := P.Z ≠ 0

lemma projective_plane_point'.is_finite.sound {K : Type*} [field K]
(P P' : projective_plane_point' K) (h : P.is_equal P') : P.is_finite = P'.is_finite :=
begin
  rcases h with ⟨ a, h1, h2, h3, h4 ⟩,
  simp [projective_plane_point'.is_finite, h4, h1],
end

def projective_plane_point.is_finite {K : Type*} [h : field K]
(P : projective_plane_point K) := quotient.lift (@projective_plane_point'.is_finite K h)
(@projective_plane_point'.is_finite.sound K h) P

def projective_plane_point'.is_infinite {K : Type*} [field K]
(P : projective_plane_point' K) := P.Z = 0

lemma projective_plane_point'.is_infinite.sound {K : Type*} [field K]
(P P' : projective_plane_point' K) (h : P.is_equal P') : P.is_infinite = P'.is_infinite :=
begin
  rcases h with ⟨ a, h1, h2, h3, h4 ⟩,
  simp [projective_plane_point'.is_infinite, h4, h1],
end

def projective_plane_point.is_infinite {K : Type*} [h : field K]
(P : projective_plane_point K) := quotient.lift (@projective_plane_point'.is_infinite K h)
(@projective_plane_point'.is_infinite.sound K h) P

def projective_plane_point'.infinite_point_X (K : Type*) [field K]
: projective_plane_point' K := ⟨ 1, 0, 0, (by simp) ⟩

def projective_plane_point'.infinite_point_Y (K : Type*) [field K]
: projective_plane_point' K := ⟨ 0, 1, 0, (by simp) ⟩

def projective_plane_point'.infinite_point_slope (K : Type*) [field K]
(k : K) : projective_plane_point' K := ⟨ 1, k, 0, (by simp) ⟩

def projective_plane_point.infinite_point_X (K : Type*) [field K]
: projective_plane_point K := quotient.mk (projective_plane_point'.infinite_point_X K)

def projective_plane_point.infinite_point_Y (K : Type*) [field K]
: projective_plane_point K := quotient.mk (projective_plane_point'.infinite_point_Y K)

def projective_plane_point.infinite_point_slope (K : Type*) [field K]
(k : K) : projective_plane_point K := quotient.mk (projective_plane_point'.infinite_point_slope K k)

def affine_plane_point.to_projective_plane' {K : Type*} [field K]
(P : affine_plane_point K) : projective_plane_point' K := ⟨ P.x, P.y, 1, (by simp) ⟩

def affine_plane_point.to_projective_plane {K : Type*} [field K]
(P : affine_plane_point K) : projective_plane_point K := quotient.mk P.to_projective_plane'

def projective_plane_point'.to_affine_plane {K : Type*} [field K]
(P : projective_plane_point' K) : affine_plane_point K :=
affine_plane_point.mk (P.X/P.Z) (P.Y/P.Z)

def projective_plane_point'.to_affine_plane.sound {K : Type*} [field K]
(P P' : projective_plane_point' K) (h : P.is_equal P')
: P.to_affine_plane = P'.to_affine_plane :=
begin
  rcases h with ⟨ a, h1, h2, h3, h4 ⟩,
  by_cases hZ : P.Z ≠ 0, {
    simp [projective_plane_point'.to_affine_plane, h2, h3, h4],
    split, {
      symmetry,
      field_simp [h1, hZ],
      ring,
    },
    symmetry,
    field_simp [h1, hZ],
    ring,
  },
  simp at hZ,
  simp [projective_plane_point'.to_affine_plane, hZ, h4],
end

def projective_plane_point.to_affine_plane {K : Type*} [h : field K]
(P : projective_plane_point K) : affine_plane_point K :=
quotient.lift (@projective_plane_point'.to_affine_plane K h)
(@projective_plane_point'.to_affine_plane.sound K h) P

@[simp]
lemma affine_plane_point.embed_invertible {K : Type*} [field K]
(P : affine_plane_point K) : P.to_projective_plane.to_affine_plane = P :=
begin
  unfold projective_plane_point.to_affine_plane
  affine_plane_point.to_projective_plane
  affine_plane_point.to_projective_plane',
  simp [projective_plane_point'.to_affine_plane, affine_plane_point.ext_iff],
end

@[simp]
lemma projective_plane_point.embed_invertible {K : Type*} [field K]
(P : projective_plane_point K) (h : P.is_finite) : P.to_affine_plane.to_projective_plane = P :=
begin
  revert h P,
  refine quotient.ind _,
  intros P h,
  simp [projective_plane_point.is_finite, projective_plane_point'.is_finite] at h,
  refine quotient.sound _,
  simp [affine_plane_point.to_projective_plane',
    projective_plane_point.to_affine_plane, projective_plane_point'.to_affine_plane],
  use [P.Z, h],
  split, { field_simp [mul_comm], },
  split, { field_simp [mul_comm], },
  simp,
end


/-
Copyright (c) 2021 David Wärn,. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: David Wärn, Scott Morrison

! This file was ported from Lean 3 source module combinatorics.quiver.path
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Combinatorics.Quiver.Basic
import Mathbin.Logic.Lemmas

/-!
# Paths in quivers

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

Given a quiver `V`, we define the type of paths from `a : V` to `b : V` as an inductive
family. We define composition of paths and the action of prefunctors on paths.
-/

open Function

universe v v₁ v₂ u u₁ u₂

namespace Quiver

#print Quiver.Path /-
/-- `G.path a b` is the type of paths from `a` to `b` through the arrows of `G`. -/
inductive Path {V : Type u} [Quiver.{v} V] (a : V) : V → Sort max (u + 1) v
  | nil : Path a
  | cons : ∀ {b c : V}, Path b → (b ⟶ c) → Path c
#align quiver.path Quiver.Path
-/

#print Quiver.Hom.toPath /-
/-- An arrow viewed as a path of length one. -/
def Hom.toPath {V} [Quiver V] {a b : V} (e : a ⟶ b) : Path a b :=
  Path.nil.cons e
#align quiver.hom.to_path Quiver.Hom.toPath
-/

namespace Path

variable {V : Type u} [Quiver V] {a b c d : V}

/- warning: quiver.path.nil_ne_cons -> Quiver.Path.nil_ne_cons is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b) (e : Quiver.Hom.{u2, u1} V _inst_1 b a), Ne.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a a) (Quiver.Path.nil.{u2, u1} V _inst_1 a) (Quiver.Path.cons.{u2, u1} V _inst_1 a b a p e)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b) (e : Quiver.Hom.{u1, u2} V _inst_1 b a), Ne.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a a) (Quiver.Path.nil.{u1, u2} V _inst_1 a) (Quiver.Path.cons.{u1, u2} V _inst_1 a b a p e)
Case conversion may be inaccurate. Consider using '#align quiver.path.nil_ne_cons Quiver.Path.nil_ne_consₓ'. -/
theorem nil_ne_cons (p : Path a b) (e : b ⟶ a) : Path.nil ≠ p.cons e :=
  fun.
#align quiver.path.nil_ne_cons Quiver.Path.nil_ne_cons

/- warning: quiver.path.cons_ne_nil -> Quiver.Path.cons_ne_nil is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b) (e : Quiver.Hom.{u2, u1} V _inst_1 b a), Ne.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a a) (Quiver.Path.cons.{u2, u1} V _inst_1 a b a p e) (Quiver.Path.nil.{u2, u1} V _inst_1 a)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b) (e : Quiver.Hom.{u1, u2} V _inst_1 b a), Ne.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a a) (Quiver.Path.cons.{u1, u2} V _inst_1 a b a p e) (Quiver.Path.nil.{u1, u2} V _inst_1 a)
Case conversion may be inaccurate. Consider using '#align quiver.path.cons_ne_nil Quiver.Path.cons_ne_nilₓ'. -/
theorem cons_ne_nil (p : Path a b) (e : b ⟶ a) : p.cons e ≠ Path.nil :=
  fun.
#align quiver.path.cons_ne_nil Quiver.Path.cons_ne_nil

/- warning: quiver.path.obj_eq_of_cons_eq_cons -> Quiver.Path.obj_eq_of_cons_eq_cons is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {d : V} {p : Quiver.Path.{u2, u1} V _inst_1 a b} {p' : Quiver.Path.{u2, u1} V _inst_1 a c} {e : Quiver.Hom.{u2, u1} V _inst_1 b d} {e' : Quiver.Hom.{u2, u1} V _inst_1 c d}, (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a d) (Quiver.Path.cons.{u2, u1} V _inst_1 a b d p e) (Quiver.Path.cons.{u2, u1} V _inst_1 a c d p' e')) -> (Eq.{succ u1} V b c)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {d : V} {p : Quiver.Path.{u1, u2} V _inst_1 a b} {p' : Quiver.Path.{u1, u2} V _inst_1 a c} {e : Quiver.Hom.{u1, u2} V _inst_1 b d} {e' : Quiver.Hom.{u1, u2} V _inst_1 c d}, (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a d) (Quiver.Path.cons.{u1, u2} V _inst_1 a b d p e) (Quiver.Path.cons.{u1, u2} V _inst_1 a c d p' e')) -> (Eq.{succ u2} V b c)
Case conversion may be inaccurate. Consider using '#align quiver.path.obj_eq_of_cons_eq_cons Quiver.Path.obj_eq_of_cons_eq_consₓ'. -/
theorem obj_eq_of_cons_eq_cons {p : Path a b} {p' : Path a c} {e : b ⟶ d} {e' : c ⟶ d}
    (h : p.cons e = p'.cons e') : b = c := by injection h
#align quiver.path.obj_eq_of_cons_eq_cons Quiver.Path.obj_eq_of_cons_eq_cons

/- warning: quiver.path.heq_of_cons_eq_cons -> Quiver.Path.heq_of_cons_eq_cons is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {d : V} {p : Quiver.Path.{u2, u1} V _inst_1 a b} {p' : Quiver.Path.{u2, u1} V _inst_1 a c} {e : Quiver.Hom.{u2, u1} V _inst_1 b d} {e' : Quiver.Hom.{u2, u1} V _inst_1 c d}, (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a d) (Quiver.Path.cons.{u2, u1} V _inst_1 a b d p e) (Quiver.Path.cons.{u2, u1} V _inst_1 a c d p' e')) -> (HEq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) p (Quiver.Path.{u2, u1} V _inst_1 a c) p')
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {d : V} {p : Quiver.Path.{u1, u2} V _inst_1 a b} {p' : Quiver.Path.{u1, u2} V _inst_1 a c} {e : Quiver.Hom.{u1, u2} V _inst_1 b d} {e' : Quiver.Hom.{u1, u2} V _inst_1 c d}, (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a d) (Quiver.Path.cons.{u1, u2} V _inst_1 a b d p e) (Quiver.Path.cons.{u1, u2} V _inst_1 a c d p' e')) -> (HEq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) p (Quiver.Path.{u1, u2} V _inst_1 a c) p')
Case conversion may be inaccurate. Consider using '#align quiver.path.heq_of_cons_eq_cons Quiver.Path.heq_of_cons_eq_consₓ'. -/
theorem heq_of_cons_eq_cons {p : Path a b} {p' : Path a c} {e : b ⟶ d} {e' : c ⟶ d}
    (h : p.cons e = p'.cons e') : HEq p p' := by injection h
#align quiver.path.heq_of_cons_eq_cons Quiver.Path.heq_of_cons_eq_cons

/- warning: quiver.path.hom_heq_of_cons_eq_cons -> Quiver.Path.hom_heq_of_cons_eq_cons is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {d : V} {p : Quiver.Path.{u2, u1} V _inst_1 a b} {p' : Quiver.Path.{u2, u1} V _inst_1 a c} {e : Quiver.Hom.{u2, u1} V _inst_1 b d} {e' : Quiver.Hom.{u2, u1} V _inst_1 c d}, (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a d) (Quiver.Path.cons.{u2, u1} V _inst_1 a b d p e) (Quiver.Path.cons.{u2, u1} V _inst_1 a c d p' e')) -> (HEq.{u2} (Quiver.Hom.{u2, u1} V _inst_1 b d) e (Quiver.Hom.{u2, u1} V _inst_1 c d) e')
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {d : V} {p : Quiver.Path.{u1, u2} V _inst_1 a b} {p' : Quiver.Path.{u1, u2} V _inst_1 a c} {e : Quiver.Hom.{u1, u2} V _inst_1 b d} {e' : Quiver.Hom.{u1, u2} V _inst_1 c d}, (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a d) (Quiver.Path.cons.{u1, u2} V _inst_1 a b d p e) (Quiver.Path.cons.{u1, u2} V _inst_1 a c d p' e')) -> (HEq.{u1} (Quiver.Hom.{u1, u2} V _inst_1 b d) e (Quiver.Hom.{u1, u2} V _inst_1 c d) e')
Case conversion may be inaccurate. Consider using '#align quiver.path.hom_heq_of_cons_eq_cons Quiver.Path.hom_heq_of_cons_eq_consₓ'. -/
theorem hom_heq_of_cons_eq_cons {p : Path a b} {p' : Path a c} {e : b ⟶ d} {e' : c ⟶ d}
    (h : p.cons e = p'.cons e') : HEq e e' := by injection h
#align quiver.path.hom_heq_of_cons_eq_cons Quiver.Path.hom_heq_of_cons_eq_cons

#print Quiver.Path.length /-
/-- The length of a path is the number of arrows it uses. -/
def length {a : V} : ∀ {b : V}, Path a b → ℕ
  | _, path.nil => 0
  | _, path.cons p _ => p.length + 1
#align quiver.path.length Quiver.Path.length
-/

instance {a : V} : Inhabited (Path a a) :=
  ⟨Path.nil⟩

/- warning: quiver.path.length_nil -> Quiver.Path.length_nil is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V}, Eq.{1} Nat (Quiver.Path.length.{u1, u2} V _inst_1 a a (Quiver.Path.nil.{u2, u1} V _inst_1 a)) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V}, Eq.{1} Nat (Quiver.Path.length.{u2, u1} V _inst_1 a a (Quiver.Path.nil.{u1, u2} V _inst_1 a)) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))
Case conversion may be inaccurate. Consider using '#align quiver.path.length_nil Quiver.Path.length_nilₓ'. -/
@[simp]
theorem length_nil {a : V} : (Path.nil : Path a a).length = 0 :=
  rfl
#align quiver.path.length_nil Quiver.Path.length_nil

/- warning: quiver.path.length_cons -> Quiver.Path.length_cons is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] (a : V) (b : V) (c : V) (p : Quiver.Path.{u2, u1} V _inst_1 a b) (e : Quiver.Hom.{u2, u1} V _inst_1 b c), Eq.{1} Nat (Quiver.Path.length.{u1, u2} V _inst_1 a c (Quiver.Path.cons.{u2, u1} V _inst_1 a b c p e)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (Quiver.Path.length.{u1, u2} V _inst_1 a b p) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] (a : V) (b : V) (c : V) (p : Quiver.Path.{u1, u2} V _inst_1 a b) (e : Quiver.Hom.{u1, u2} V _inst_1 b c), Eq.{1} Nat (Quiver.Path.length.{u2, u1} V _inst_1 a c (Quiver.Path.cons.{u1, u2} V _inst_1 a b c p e)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Quiver.Path.length.{u2, u1} V _inst_1 a b p) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align quiver.path.length_cons Quiver.Path.length_consₓ'. -/
@[simp]
theorem length_cons (a b c : V) (p : Path a b) (e : b ⟶ c) : (p.cons e).length = p.length + 1 :=
  rfl
#align quiver.path.length_cons Quiver.Path.length_cons

/- warning: quiver.path.eq_of_length_zero -> Quiver.Path.eq_of_length_zero is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b), (Eq.{1} Nat (Quiver.Path.length.{u1, u2} V _inst_1 a b p) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{succ u1} V a b)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b), (Eq.{1} Nat (Quiver.Path.length.{u2, u1} V _inst_1 a b p) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{succ u2} V a b)
Case conversion may be inaccurate. Consider using '#align quiver.path.eq_of_length_zero Quiver.Path.eq_of_length_zeroₓ'. -/
theorem eq_of_length_zero (p : Path a b) (hzero : p.length = 0) : a = b :=
  by
  cases p
  · rfl
  · cases Nat.succ_ne_zero _ hzero
#align quiver.path.eq_of_length_zero Quiver.Path.eq_of_length_zero

#print Quiver.Path.comp /-
/-- Composition of paths. -/
def comp {a b : V} : ∀ {c}, Path a b → Path b c → Path a c
  | _, p, path.nil => p
  | _, p, path.cons q e => (p.comp q).cons e
#align quiver.path.comp Quiver.Path.comp
-/

/- warning: quiver.path.comp_cons -> Quiver.Path.comp_cons is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {d : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b) (q : Quiver.Path.{u2, u1} V _inst_1 b c) (e : Quiver.Hom.{u2, u1} V _inst_1 c d), Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a d) (Quiver.Path.comp.{u1, u2} V _inst_1 a b d p (Quiver.Path.cons.{u2, u1} V _inst_1 b c d q e)) (Quiver.Path.cons.{u2, u1} V _inst_1 a c d (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p q) e)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {d : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b) (q : Quiver.Path.{u1, u2} V _inst_1 b c) (e : Quiver.Hom.{u1, u2} V _inst_1 c d), Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a d) (Quiver.Path.comp.{u2, u1} V _inst_1 a b d p (Quiver.Path.cons.{u1, u2} V _inst_1 b c d q e)) (Quiver.Path.cons.{u1, u2} V _inst_1 a c d (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p q) e)
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_cons Quiver.Path.comp_consₓ'. -/
@[simp]
theorem comp_cons {a b c d : V} (p : Path a b) (q : Path b c) (e : c ⟶ d) :
    p.comp (q.cons e) = (p.comp q).cons e :=
  rfl
#align quiver.path.comp_cons Quiver.Path.comp_cons

/- warning: quiver.path.comp_nil -> Quiver.Path.comp_nil is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b), Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) (Quiver.Path.comp.{u1, u2} V _inst_1 a b b p (Quiver.Path.nil.{u2, u1} V _inst_1 b)) p
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b), Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) (Quiver.Path.comp.{u2, u1} V _inst_1 a b b p (Quiver.Path.nil.{u1, u2} V _inst_1 b)) p
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_nil Quiver.Path.comp_nilₓ'. -/
@[simp]
theorem comp_nil {a b : V} (p : Path a b) : p.comp Path.nil = p :=
  rfl
#align quiver.path.comp_nil Quiver.Path.comp_nil

/- warning: quiver.path.nil_comp -> Quiver.Path.nil_comp is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b), Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) (Quiver.Path.comp.{u1, u2} V _inst_1 a a b (Quiver.Path.nil.{u2, u1} V _inst_1 a) p) p
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b), Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) (Quiver.Path.comp.{u2, u1} V _inst_1 a a b (Quiver.Path.nil.{u1, u2} V _inst_1 a) p) p
Case conversion may be inaccurate. Consider using '#align quiver.path.nil_comp Quiver.Path.nil_compₓ'. -/
@[simp]
theorem nil_comp {a : V} : ∀ {b} (p : Path a b), Path.nil.comp p = p
  | a, path.nil => rfl
  | b, path.cons p e => by rw [comp_cons, nil_comp]
#align quiver.path.nil_comp Quiver.Path.nil_comp

/- warning: quiver.path.comp_assoc -> Quiver.Path.comp_assoc is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {d : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b) (q : Quiver.Path.{u2, u1} V _inst_1 b c) (r : Quiver.Path.{u2, u1} V _inst_1 c d), Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a d) (Quiver.Path.comp.{u1, u2} V _inst_1 a c d (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p q) r) (Quiver.Path.comp.{u1, u2} V _inst_1 a b d p (Quiver.Path.comp.{u1, u2} V _inst_1 b c d q r))
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {d : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b) (q : Quiver.Path.{u1, u2} V _inst_1 b c) (r : Quiver.Path.{u1, u2} V _inst_1 c d), Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a d) (Quiver.Path.comp.{u2, u1} V _inst_1 a c d (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p q) r) (Quiver.Path.comp.{u2, u1} V _inst_1 a b d p (Quiver.Path.comp.{u2, u1} V _inst_1 b c d q r))
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_assoc Quiver.Path.comp_assocₓ'. -/
@[simp]
theorem comp_assoc {a b c : V} :
    ∀ {d} (p : Path a b) (q : Path b c) (r : Path c d), (p.comp q).comp r = p.comp (q.comp r)
  | c, p, q, path.nil => rfl
  | d, p, q, path.cons r e => by rw [comp_cons, comp_cons, comp_cons, comp_assoc]
#align quiver.path.comp_assoc Quiver.Path.comp_assoc

/- warning: quiver.path.length_comp -> Quiver.Path.length_comp is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b) {c : V} (q : Quiver.Path.{u2, u1} V _inst_1 b c), Eq.{1} Nat (Quiver.Path.length.{u1, u2} V _inst_1 a c (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p q)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (Quiver.Path.length.{u1, u2} V _inst_1 a b p) (Quiver.Path.length.{u1, u2} V _inst_1 b c q))
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b) {c : V} (q : Quiver.Path.{u1, u2} V _inst_1 b c), Eq.{1} Nat (Quiver.Path.length.{u2, u1} V _inst_1 a c (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p q)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Quiver.Path.length.{u2, u1} V _inst_1 a b p) (Quiver.Path.length.{u2, u1} V _inst_1 b c q))
Case conversion may be inaccurate. Consider using '#align quiver.path.length_comp Quiver.Path.length_compₓ'. -/
@[simp]
theorem length_comp (p : Path a b) : ∀ {c} (q : Path b c), (p.comp q).length = p.length + q.length
  | c, nil => rfl
  | c, cons q h => congr_arg Nat.succ q.length_comp
#align quiver.path.length_comp Quiver.Path.length_comp

/- warning: quiver.path.comp_inj -> Quiver.Path.comp_inj is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {p₁ : Quiver.Path.{u2, u1} V _inst_1 a b} {p₂ : Quiver.Path.{u2, u1} V _inst_1 a b} {q₁ : Quiver.Path.{u2, u1} V _inst_1 b c} {q₂ : Quiver.Path.{u2, u1} V _inst_1 b c}, (Eq.{1} Nat (Quiver.Path.length.{u1, u2} V _inst_1 b c q₁) (Quiver.Path.length.{u1, u2} V _inst_1 b c q₂)) -> (Iff (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a c) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p₁ q₁) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p₂ q₂)) (And (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) p₁ p₂) (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 b c) q₁ q₂)))
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {p₁ : Quiver.Path.{u1, u2} V _inst_1 a b} {p₂ : Quiver.Path.{u1, u2} V _inst_1 a b} {q₁ : Quiver.Path.{u1, u2} V _inst_1 b c} {q₂ : Quiver.Path.{u1, u2} V _inst_1 b c}, (Eq.{1} Nat (Quiver.Path.length.{u2, u1} V _inst_1 b c q₁) (Quiver.Path.length.{u2, u1} V _inst_1 b c q₂)) -> (Iff (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a c) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p₁ q₁) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p₂ q₂)) (And (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) p₁ p₂) (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 b c) q₁ q₂)))
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_inj Quiver.Path.comp_injₓ'. -/
theorem comp_inj {p₁ p₂ : Path a b} {q₁ q₂ : Path b c} (hq : q₁.length = q₂.length) :
    p₁.comp q₁ = p₂.comp q₂ ↔ p₁ = p₂ ∧ q₁ = q₂ :=
  by
  refine'
    ⟨fun h => _, by
      rintro ⟨rfl, rfl⟩
      rfl⟩
  induction' q₁ with d₁ e₁ q₁ f₁ ih generalizing q₂ <;> obtain _ | ⟨q₂, f₂⟩ := q₂
  · exact ⟨h, rfl⟩
  · cases hq
  · cases hq
  simp only [comp_cons] at h
  obtain rfl := h.1
  obtain ⟨rfl, rfl⟩ := ih (Nat.succ.inj hq) h.2.1.Eq
  rw [h.2.2.Eq]
  exact ⟨rfl, rfl⟩
#align quiver.path.comp_inj Quiver.Path.comp_inj

/- warning: quiver.path.comp_inj' -> Quiver.Path.comp_inj' is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {p₁ : Quiver.Path.{u2, u1} V _inst_1 a b} {p₂ : Quiver.Path.{u2, u1} V _inst_1 a b} {q₁ : Quiver.Path.{u2, u1} V _inst_1 b c} {q₂ : Quiver.Path.{u2, u1} V _inst_1 b c}, (Eq.{1} Nat (Quiver.Path.length.{u1, u2} V _inst_1 a b p₁) (Quiver.Path.length.{u1, u2} V _inst_1 a b p₂)) -> (Iff (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a c) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p₁ q₁) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p₂ q₂)) (And (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) p₁ p₂) (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 b c) q₁ q₂)))
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {p₁ : Quiver.Path.{u1, u2} V _inst_1 a b} {p₂ : Quiver.Path.{u1, u2} V _inst_1 a b} {q₁ : Quiver.Path.{u1, u2} V _inst_1 b c} {q₂ : Quiver.Path.{u1, u2} V _inst_1 b c}, (Eq.{1} Nat (Quiver.Path.length.{u2, u1} V _inst_1 a b p₁) (Quiver.Path.length.{u2, u1} V _inst_1 a b p₂)) -> (Iff (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a c) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p₁ q₁) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p₂ q₂)) (And (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) p₁ p₂) (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 b c) q₁ q₂)))
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_inj' Quiver.Path.comp_inj'ₓ'. -/
theorem comp_inj' {p₁ p₂ : Path a b} {q₁ q₂ : Path b c} (h : p₁.length = p₂.length) :
    p₁.comp q₁ = p₂.comp q₂ ↔ p₁ = p₂ ∧ q₁ = q₂ :=
  ⟨fun h_eq => (comp_inj <| Nat.add_left_cancel <| by simpa [h] using congr_arg length h_eq).1 h_eq,
    by
    rintro ⟨rfl, rfl⟩
    rfl⟩
#align quiver.path.comp_inj' Quiver.Path.comp_inj'

/- warning: quiver.path.comp_injective_left -> Quiver.Path.comp_injective_left is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} (q : Quiver.Path.{u2, u1} V _inst_1 b c), Function.Injective.{max (succ u1) u2, max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) (Quiver.Path.{u2, u1} V _inst_1 a c) (fun (p : Quiver.Path.{u2, u1} V _inst_1 a b) => Quiver.Path.comp.{u1, u2} V _inst_1 a b c p q)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} (q : Quiver.Path.{u1, u2} V _inst_1 b c), Function.Injective.{max (succ u2) u1, max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) (Quiver.Path.{u1, u2} V _inst_1 a c) (fun (p : Quiver.Path.{u1, u2} V _inst_1 a b) => Quiver.Path.comp.{u2, u1} V _inst_1 a b c p q)
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_injective_left Quiver.Path.comp_injective_leftₓ'. -/
theorem comp_injective_left (q : Path b c) : Injective fun p : Path a b => p.comp q :=
  fun p₁ p₂ h => ((comp_inj rfl).1 h).1
#align quiver.path.comp_injective_left Quiver.Path.comp_injective_left

/- warning: quiver.path.comp_injective_right -> Quiver.Path.comp_injective_right is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b), Function.Injective.{max (succ u1) u2, max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 b c) (Quiver.Path.{u2, u1} V _inst_1 a c) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b), Function.Injective.{max (succ u2) u1, max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 b c) (Quiver.Path.{u1, u2} V _inst_1 a c) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p)
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_injective_right Quiver.Path.comp_injective_rightₓ'. -/
theorem comp_injective_right (p : Path a b) : Injective (p.comp : Path b c → Path a c) :=
  fun q₁ q₂ h => ((comp_inj' rfl).1 h).2
#align quiver.path.comp_injective_right Quiver.Path.comp_injective_right

/- warning: quiver.path.comp_inj_left -> Quiver.Path.comp_inj_left is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {p₁ : Quiver.Path.{u2, u1} V _inst_1 a b} {p₂ : Quiver.Path.{u2, u1} V _inst_1 a b} {q : Quiver.Path.{u2, u1} V _inst_1 b c}, Iff (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a c) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p₁ q) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p₂ q)) (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) p₁ p₂)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {p₁ : Quiver.Path.{u1, u2} V _inst_1 a b} {p₂ : Quiver.Path.{u1, u2} V _inst_1 a b} {q : Quiver.Path.{u1, u2} V _inst_1 b c}, Iff (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a c) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p₁ q) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p₂ q)) (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) p₁ p₂)
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_inj_left Quiver.Path.comp_inj_leftₓ'. -/
@[simp]
theorem comp_inj_left {p₁ p₂ : Path a b} {q : Path b c} : p₁.comp q = p₂.comp q ↔ p₁ = p₂ :=
  q.comp_injective_left.eq_iff
#align quiver.path.comp_inj_left Quiver.Path.comp_inj_left

/- warning: quiver.path.comp_inj_right -> Quiver.Path.comp_inj_right is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} {c : V} {p : Quiver.Path.{u2, u1} V _inst_1 a b} {q₁ : Quiver.Path.{u2, u1} V _inst_1 b c} {q₂ : Quiver.Path.{u2, u1} V _inst_1 b c}, Iff (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a c) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p q₁) (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p q₂)) (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 b c) q₁ q₂)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} {c : V} {p : Quiver.Path.{u1, u2} V _inst_1 a b} {q₁ : Quiver.Path.{u1, u2} V _inst_1 b c} {q₂ : Quiver.Path.{u1, u2} V _inst_1 b c}, Iff (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a c) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p q₁) (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p q₂)) (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 b c) q₁ q₂)
Case conversion may be inaccurate. Consider using '#align quiver.path.comp_inj_right Quiver.Path.comp_inj_rightₓ'. -/
@[simp]
theorem comp_inj_right {p : Path a b} {q₁ q₂ : Path b c} : p.comp q₁ = p.comp q₂ ↔ q₁ = q₂ :=
  p.comp_injective_right.eq_iff
#align quiver.path.comp_inj_right Quiver.Path.comp_inj_right

#print Quiver.Path.toList /-
/-- Turn a path into a list. The list contains `a` at its head, but not `b` a priori. -/
@[simp]
def toList : ∀ {b : V}, Path a b → List V
  | b, nil => []
  | b, @cons _ _ _ c _ p f => c :: p.toList
#align quiver.path.to_list Quiver.Path.toList
-/

/- warning: quiver.path.to_list_comp -> Quiver.Path.toList_comp is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b) {c : V} (q : Quiver.Path.{u2, u1} V _inst_1 b c), Eq.{succ u1} (List.{u1} V) (Quiver.Path.toList.{u1, u2} V _inst_1 a c (Quiver.Path.comp.{u1, u2} V _inst_1 a b c p q)) (Append.append.{u1} (List.{u1} V) (List.hasAppend.{u1} V) (Quiver.Path.toList.{u1, u2} V _inst_1 b c q) (Quiver.Path.toList.{u1, u2} V _inst_1 a b p))
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b) {c : V} (q : Quiver.Path.{u1, u2} V _inst_1 b c), Eq.{succ u2} (List.{u2} V) (Quiver.Path.toList.{u2, u1} V _inst_1 a c (Quiver.Path.comp.{u2, u1} V _inst_1 a b c p q)) (HAppend.hAppend.{u2, u2, u2} (List.{u2} V) (List.{u2} V) (List.{u2} V) (instHAppend.{u2} (List.{u2} V) (List.instAppendList.{u2} V)) (Quiver.Path.toList.{u2, u1} V _inst_1 b c q) (Quiver.Path.toList.{u2, u1} V _inst_1 a b p))
Case conversion may be inaccurate. Consider using '#align quiver.path.to_list_comp Quiver.Path.toList_compₓ'. -/
/-- `quiver.path.to_list` is a contravariant functor. The inversion comes from `quiver.path` and
`list` having different preferred directions for adding elements. -/
@[simp]
theorem toList_comp (p : Path a b) : ∀ {c} (q : Path b c), (p.comp q).toList = q.toList ++ p.toList
  | c, nil => by simp
  | c, @cons _ _ _ d _ q f => by simp [to_list_comp]
#align quiver.path.to_list_comp Quiver.Path.toList_comp

/- warning: quiver.path.to_list_chain_nonempty -> Quiver.Path.toList_chain_nonempty is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} (p : Quiver.Path.{u2, u1} V _inst_1 a b), List.Chain.{u1} V (fun (x : V) (y : V) => Nonempty.{u2} (Quiver.Hom.{u2, u1} V _inst_1 y x)) b (Quiver.Path.toList.{u1, u2} V _inst_1 a b p)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} (p : Quiver.Path.{u1, u2} V _inst_1 a b), List.Chain.{u2} V (fun (x : V) (y : V) => Nonempty.{u1} (Quiver.Hom.{u1, u2} V _inst_1 y x)) b (Quiver.Path.toList.{u2, u1} V _inst_1 a b p)
Case conversion may be inaccurate. Consider using '#align quiver.path.to_list_chain_nonempty Quiver.Path.toList_chain_nonemptyₓ'. -/
theorem toList_chain_nonempty : ∀ {b} (p : Path a b), p.toList.Chain (fun x y => Nonempty (y ⟶ x)) b
  | b, nil => List.Chain.nil
  | b, cons p f => p.toList_chain_nonempty.cons ⟨f⟩
#align quiver.path.to_list_chain_nonempty Quiver.Path.toList_chain_nonempty

variable [∀ a b : V, Subsingleton (a ⟶ b)]

/- warning: quiver.path.to_list_injective -> Quiver.Path.toList_injective is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] [_inst_2 : forall (a : V) (b : V), Subsingleton.{u2} (Quiver.Hom.{u2, u1} V _inst_1 a b)] (a : V) (b : V), Function.Injective.{max (succ u1) u2, succ u1} (Quiver.Path.{u2, u1} V _inst_1 a b) (List.{u1} V) (Quiver.Path.toList.{u1, u2} V _inst_1 a b)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] [_inst_2 : forall (a : V) (b : V), Subsingleton.{u1} (Quiver.Hom.{u1, u2} V _inst_1 a b)] (a : V) (b : V), Function.Injective.{max (succ u2) u1, succ u2} (Quiver.Path.{u1, u2} V _inst_1 a b) (List.{u2} V) (Quiver.Path.toList.{u2, u1} V _inst_1 a b)
Case conversion may be inaccurate. Consider using '#align quiver.path.to_list_injective Quiver.Path.toList_injectiveₓ'. -/
theorem toList_injective (a : V) : ∀ b, Injective (toList : Path a b → List V)
  | b, nil, nil, h => rfl
  | b, nil, @cons _ _ _ c _ p f, h => by cases h
  | b, @cons _ _ _ c _ p f, nil, h => by cases h
  | b, @cons _ _ _ c _ p f, @cons _ _ s t u C D, h =>
    by
    simp only [to_list] at h
    obtain ⟨rfl, hAC⟩ := h
    simp [to_list_injective _ hAC]
#align quiver.path.to_list_injective Quiver.Path.toList_injective

/- warning: quiver.path.to_list_inj -> Quiver.Path.toList_inj is a dubious translation:
lean 3 declaration is
  forall {V : Type.{u1}} [_inst_1 : Quiver.{u2, u1} V] {a : V} {b : V} [_inst_2 : forall (a : V) (b : V), Subsingleton.{u2} (Quiver.Hom.{u2, u1} V _inst_1 a b)] {p : Quiver.Path.{u2, u1} V _inst_1 a b} {q : Quiver.Path.{u2, u1} V _inst_1 a b}, Iff (Eq.{succ u1} (List.{u1} V) (Quiver.Path.toList.{u1, u2} V _inst_1 a b p) (Quiver.Path.toList.{u1, u2} V _inst_1 a b q)) (Eq.{max (succ u1) u2} (Quiver.Path.{u2, u1} V _inst_1 a b) p q)
but is expected to have type
  forall {V : Type.{u2}} [_inst_1 : Quiver.{u1, u2} V] {a : V} {b : V} [_inst_2 : forall (a : V) (b : V), Subsingleton.{u1} (Quiver.Hom.{u1, u2} V _inst_1 a b)] {p : Quiver.Path.{u1, u2} V _inst_1 a b} {q : Quiver.Path.{u1, u2} V _inst_1 a b}, Iff (Eq.{succ u2} (List.{u2} V) (Quiver.Path.toList.{u2, u1} V _inst_1 a b p) (Quiver.Path.toList.{u2, u1} V _inst_1 a b q)) (Eq.{max (succ u2) u1} (Quiver.Path.{u1, u2} V _inst_1 a b) p q)
Case conversion may be inaccurate. Consider using '#align quiver.path.to_list_inj Quiver.Path.toList_injₓ'. -/
@[simp]
theorem toList_inj {p q : Path a b} : p.toList = q.toList ↔ p = q :=
  (toList_injective _ _).eq_iff
#align quiver.path.to_list_inj Quiver.Path.toList_inj

end Path

end Quiver

namespace Prefunctor

open Quiver

variable {V : Type u₁} [Quiver.{v₁} V] {W : Type u₂} [Quiver.{v₂} W] (F : V ⥤q W)

#print Prefunctor.mapPath /-
/-- The image of a path under a prefunctor. -/
def mapPath {a : V} : ∀ {b : V}, Path a b → Path (F.obj a) (F.obj b)
  | _, path.nil => Path.nil
  | _, path.cons p e => Path.cons (map_path p) (F.map e)
#align prefunctor.map_path Prefunctor.mapPath
-/

#print Prefunctor.mapPath_nil /-
@[simp]
theorem mapPath_nil (a : V) : F.mapPath (Path.nil : Path a a) = Path.nil :=
  rfl
#align prefunctor.map_path_nil Prefunctor.mapPath_nil
-/

#print Prefunctor.mapPath_cons /-
@[simp]
theorem mapPath_cons {a b c : V} (p : Path a b) (e : b ⟶ c) :
    F.mapPath (Path.cons p e) = Path.cons (F.mapPath p) (F.map e) :=
  rfl
#align prefunctor.map_path_cons Prefunctor.mapPath_cons
-/

#print Prefunctor.mapPath_comp /-
@[simp]
theorem mapPath_comp {a b : V} (p : Path a b) :
    ∀ {c : V} (q : Path b c), F.mapPath (p.comp q) = (F.mapPath p).comp (F.mapPath q)
  | _, path.nil => rfl
  | _, path.cons p e => by dsimp; rw [map_path_comp]
#align prefunctor.map_path_comp Prefunctor.mapPath_comp
-/

#print Prefunctor.mapPath_toPath /-
@[simp]
theorem mapPath_toPath {a b : V} (f : a ⟶ b) : F.mapPath f.toPath = (F.map f).toPath :=
  rfl
#align prefunctor.map_path_to_path Prefunctor.mapPath_toPath
-/

end Prefunctor


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

-/
import data.dfinsupp
import group_theory.submonoid.operations
import group_theory.subgroup

/-!
# Direct sum

This file defines the direct sum of abelian groups, indexed by a discrete type.

## Notation

`⨁ i, β i` is the n-ary direct sum `direct_sum`.
This notation is in the `direct_sum` locale, accessible after `open_locale direct_sum`.

## References

* https://en.wikipedia.org/wiki/Direct_sum
-/

open_locale big_operators

universes u v w u₁

variables (ι : Type v) [dec_ι : decidable_eq ι] (β : ι → Type w)

/-- `direct_sum β` is the direct sum of a family of additive commutative monoids `β i`.

Note: `open_locale direct_sum` will enable the notation `⨁ i, β i` for `direct_sum β`. -/
@[derive [has_coe_to_fun, add_comm_monoid, inhabited]]
def direct_sum [Π i, add_comm_monoid (β i)] : Type* := Π₀ i, β i

localized "notation `⨁` binders `, ` r:(scoped f, direct_sum _ f) := r" in direct_sum

namespace direct_sum

variables {ι}

section add_comm_group

variables [Π i, add_comm_group (β i)]

instance : add_comm_group (direct_sum ι β) := dfinsupp.add_comm_group

variables {β}
@[simp] lemma sub_apply (g₁ g₂ : ⨁ i, β i) (i : ι) : (g₁ - g₂) i = g₁ i - g₂ i :=
dfinsupp.sub_apply _ _ _

end add_comm_group

variables [Π i, add_comm_monoid (β i)]

@[simp] lemma zero_apply (i : ι) : (0 : ⨁ i, β i) i = 0 := rfl

variables {β}
@[simp] lemma add_apply (g₁ g₂ : ⨁ i, β i) (i : ι) : (g₁ + g₂) i = g₁ i + g₂ i :=
dfinsupp.add_apply _ _ _

variables (β)
include dec_ι

/-- `mk β s x` is the element of `⨁ i, β i` that is zero outside `s`
and has coefficient `x i` for `i` in `s`. -/
def mk (s : finset ι) : (Π i : (↑s : set ι), β i.1) →+ ⨁ i, β i :=
{ to_fun := dfinsupp.mk s,
  map_add' := λ _ _, dfinsupp.mk_add,
  map_zero' := dfinsupp.mk_zero, }

/-- `of i` is the natural inclusion map from `β i` to `⨁ i, β i`. -/
def of (i : ι) : β i →+ ⨁ i, β i :=
dfinsupp.single_add_hom β i

variables {β}

theorem mk_injective (s : finset ι) : function.injective (mk β s) :=
dfinsupp.mk_injective s

theorem of_injective (i : ι) : function.injective (of β i) :=
dfinsupp.single_injective

@[elab_as_eliminator]
protected theorem induction_on {C : (⨁ i, β i) → Prop}
  (x : ⨁ i, β i) (H_zero : C 0)
  (H_basic : ∀ (i : ι) (x : β i), C (of β i x))
  (H_plus : ∀ x y, C x → C y → C (x + y)) : C x :=
begin
  apply dfinsupp.induction x H_zero,
  intros i b f h1 h2 ih,
  solve_by_elim
end

/-- If two additive homomorphisms from `⨁ i, β i` are equal on each `of β i y`,
then they are equal. -/
lemma add_hom_ext {γ : Type*} [add_monoid γ] ⦃f g : (⨁ i, β i) →+ γ⦄
  (H : ∀ (i : ι) (y : β i), f (of _ i y) = g (of _ i y)) : f = g :=
dfinsupp.add_hom_ext H

/-- If two additive homomorphisms from `⨁ i, β i` are equal on each `of β i y`,
then they are equal.

See note [partially-applied ext lemmas]. -/
@[ext] lemma add_hom_ext' {γ : Type*} [add_monoid γ] ⦃f g : (⨁ i, β i) →+ γ⦄
  (H : ∀ (i : ι), f.comp (of _ i) = g.comp (of _ i)) : f = g :=
add_hom_ext $ λ i, add_monoid_hom.congr_fun $ H i

variables {γ : Type u₁} [add_comm_monoid γ]

section to_add_monoid

variables (φ : Π i, β i →+ γ) (ψ : (⨁ i, β i) →+ γ)

/-- `to_add_monoid φ` is the natural homomorphism from `⨁ i, β i` to `γ`
induced by a family `φ` of homomorphisms `β i → γ`. -/
def to_add_monoid : (⨁ i, β i) →+ γ :=
(dfinsupp.lift_add_hom φ)

@[simp] lemma to_add_monoid_of (i) (x : β i) : to_add_monoid φ (of β i x) = φ i x :=
dfinsupp.lift_add_hom_apply_single φ i x

theorem to_add_monoid.unique (f : ⨁ i, β i) :
  ψ f = to_add_monoid (λ i, ψ.comp (of β i)) f :=
by {congr, ext, simp [to_add_monoid, of]}

end to_add_monoid

section from_add_monoid

/-- `from_add_monoid φ` is the natural homomorphism from `γ` to `⨁ i, β i`
induced by a family `φ` of homomorphisms `γ → β i`.

Note that this is not an isomorphism. Not every homomorphism `γ →+ ⨁ i, β i` arises in this way. -/
def from_add_monoid : (⨁ i, γ →+ β i) →+ (γ →+ ⨁ i, β i) :=
to_add_monoid $ λ i, add_monoid_hom.comp_hom (of β i)

@[simp] lemma from_add_monoid_of (i : ι) (f : γ →+ β i) :
  from_add_monoid (of _ i f) = (of _ i).comp f :=
by { rw [from_add_monoid, to_add_monoid_of], refl }

lemma from_add_monoid_of_apply (i : ι) (f : γ →+ β i) (x : γ) :
  from_add_monoid (of _ i f) x = of _ i (f x) :=
by rw [from_add_monoid_of, add_monoid_hom.coe_comp]

end from_add_monoid

variables (β)
/-- `set_to_set β S T h` is the natural homomorphism `⨁ (i : S), β i → ⨁ (i : T), β i`,
where `h : S ⊆ T`. -/
-- TODO: generalize this to remove the assumption `S ⊆ T`.
def set_to_set (S T : set ι) (H : S ⊆ T) :
  (⨁ (i : S), β i) →+ (⨁ (i : T), β i) :=
to_add_monoid $ λ i, of (λ (i : subtype T), β i) ⟨↑i, H i.prop⟩
variables {β}

omit dec_ι

/-- The natural equivalence between `⨁ _ : ι, M` and `M` when `unique ι`. -/
protected def id (M : Type v) (ι : Type* := punit) [add_comm_monoid M] [unique ι] :
  (⨁ (_ : ι), M) ≃+ M :=
{ to_fun := direct_sum.to_add_monoid (λ _, add_monoid_hom.id M),
  inv_fun := of (λ _, M) (default ι),
  left_inv := λ x, direct_sum.induction_on x
    (by rw [add_monoid_hom.map_zero, add_monoid_hom.map_zero])
    (λ p x, by rw [unique.default_eq p, to_add_monoid_of]; refl)
    (λ x y ihx ihy, by rw [add_monoid_hom.map_add, add_monoid_hom.map_add, ihx, ihy]),
  right_inv := λ x, to_add_monoid_of _ _ _,
  ..direct_sum.to_add_monoid (λ _, add_monoid_hom.id M) }

/-- The `direct_sum` formed by a collection of `add_submonoid`s of `M` is said to be internal if the
canonical map `(⨁ i, A i) →+ M` is bijective.

See `direct_sum.add_subgroup_is_internal` for the same statement about `add_subgroup`s. -/
def add_submonoid_is_internal {M : Type*} [decidable_eq ι] [add_comm_monoid M]
  (A : ι → add_submonoid M) : Prop :=
function.bijective (direct_sum.to_add_monoid (λ i, (A i).subtype) : (⨁ i, A i) →+ M)

/-- The `direct_sum` formed by a collection of `add_subgroup`s of `M` is said to be internal if the
canonical map `(⨁ i, A i) →+ M` is bijective.

See `direct_sum.submodule_is_internal` for the same statement about `submodules`s. -/
def add_subgroup_is_internal {M : Type*} [decidable_eq ι] [add_comm_group M]
  (A : ι → add_subgroup M) : Prop :=
function.bijective (direct_sum.to_add_monoid (λ i, (A i).subtype) : (⨁ i, A i) →+ M)

lemma add_subgroup_is_internal.to_add_submonoid
  {M : Type*} [decidable_eq ι] [add_comm_group M] (A : ι → add_subgroup M) :
  add_subgroup_is_internal A ↔
    add_submonoid_is_internal (λ i, (A i).to_add_submonoid) :=
iff.rfl

end direct_sum