/-
Copyright (c) 2020 Kenji Nakagawa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenji Nakagawa, Anne Baanen, Filippo A. E. Nuccio
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.discrete_valuation_ring
import Mathlib.ring_theory.fractional_ideal
import Mathlib.ring_theory.ideal.over
import Mathlib.PostPort

universes u_1 u_2 l u_3 u_4 u_5

namespace Mathlib

/-!
# Dedekind domains

This file defines the notion of a Dedekind domain (or Dedekind ring),
giving three equivalent definitions (TODO: and shows that they are equivalent).

## Main definitions

 - `is_dedekind_domain` defines a Dedekind domain as a commutative ring that is not a field,
   Noetherian, integrally closed in its field of fractions and has Krull dimension exactly one.
   `is_dedekind_domain_iff` shows that this does not depend on the choice of field of fractions.
 - `is_dedekind_domain_dvr` alternatively defines a Dedekind domain as an integral domain that
   is not a field, Noetherian, and the localization at every nonzero prime ideal is a DVR.
 - `is_dedekind_domain_inv` alternatively defines a Dedekind domain as an integral domain that
   is not a field, and every nonzero fractional ideal is invertible.
 - `is_dedekind_domain_inv_iff` shows that this does note depend on the choice of field of fractions.

## Implementation notes

The definitions that involve a field of fractions choose a canonical field of fractions,
but are independent of that choice. The `..._iff` lemmas express this independence.

## References

* [D. Marcus, *Number Fields*][marcus1977number]
* [J.W.S. Cassels, A. Frölich, *Algebraic Number Theory*][cassels1967algebraic]

## Tags

dedekind domain, dedekind ring
-/

/-- A ring `R` has Krull dimension at most one if all nonzero prime ideals are maximal. -/
def ring.dimension_le_one (R : Type u_1) [comm_ring R] :=
  ∀ (p : ideal R), p ≠ ⊥ → ideal.is_prime p → ideal.is_maximal p

namespace ring

theorem dimension_le_one.principal_ideal_ring (A : Type u_2) [integral_domain A] [is_principal_ideal_ring A] : dimension_le_one A :=
  fun (p : ideal A) (nonzero : p ≠ ⊥) (prime : ideal.is_prime p) => is_prime.to_maximal_ideal nonzero

theorem dimension_le_one.integral_closure (R : Type u_1) (A : Type u_2) [comm_ring R] [integral_domain A] [nontrivial R] [algebra R A] (h : dimension_le_one R) : dimension_le_one ↥(integral_closure R A) := sorry

end ring

/--
A Dedekind domain is an integral domain that is Noetherian, integrally closed, and
has Krull dimension exactly one (`not_is_field` and `dimension_le_one`).

The integral closure condition is independent of the choice of field of fractions:
use `is_dedekind_domain_iff` to prove `is_dedekind_domain` for a given `fraction_map`.

This is the default implementation, but there are equivalent definitions,
`is_dedekind_domain_dvr` and `is_dedekind_domain_inv`.
TODO: Prove that these are actually equivalent definitions.
-/
class is_dedekind_domain (A : Type u_2) [integral_domain A]
where
  not_is_field : ¬is_field A
  is_noetherian_ring : is_noetherian_ring A
  dimension_le_one : ring.dimension_le_one A
  is_integrally_closed : integral_closure A (fraction_ring A) = ⊥

/-- An integral domain is a Dedekind domain iff and only if it is not a field, is
Noetherian, has dimension ≤ 1, and is integrally closed in a given fraction field.
In particular, this definition does not depend on the choice of this fraction field. -/
theorem is_dedekind_domain_iff (A : Type u_2) (K : Type u_3) [integral_domain A] [field K] (f : fraction_map A K) : is_dedekind_domain A ↔
  ¬is_field A ∧ is_noetherian_ring A ∧ ring.dimension_le_one A ∧ integral_closure A (localization_map.codomain f) = ⊥ := sorry

/--
A Dedekind domain is an integral domain that is not a field, is Noetherian, and the
localization at every nonzero prime is a discrete valuation ring.

This is equivalent to `is_dedekind_domain`.
TODO: prove the equivalence.
-/
structure is_dedekind_domain_dvr (A : Type u_2) [integral_domain A]
where
  not_is_field : ¬is_field A
  is_noetherian_ring : is_noetherian_ring A
  is_dvr_at_nonzero_prime : ∀ (P : ideal A), P ≠ ⊥ → ideal.is_prime P → discrete_valuation_ring (localization.at_prime P)

protected instance ring.fractional_ideal.has_inv (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} : has_inv (ring.fractional_ideal g) :=
  has_inv.mk fun (I : ring.fractional_ideal g) => 1 / I

theorem inv_eq (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} {I : ring.fractional_ideal g} : I⁻¹ = 1 / I :=
  rfl

theorem inv_zero' (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} : 0⁻¹ = 0 :=
  ring.fractional_ideal.div_zero

theorem inv_nonzero (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} {J : ring.fractional_ideal g} (h : J ≠ 0) : J⁻¹ = { val := ↑1 / ↑J, property := ring.fractional_ideal.fractional_div_of_nonzero h } :=
  ring.fractional_ideal.div_nonzero h

theorem coe_inv_of_nonzero (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} {J : ring.fractional_ideal g} (h : J ≠ 0) : ↑(J⁻¹) = localization_map.coe_submodule g 1 / ↑J :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(J⁻¹) = localization_map.coe_submodule g 1 / ↑J)) (inv_nonzero K h)))
    (Eq.refl ↑{ val := ↑1 / ↑J, property := ring.fractional_ideal.fractional_div_of_nonzero h })

/-- `I⁻¹` is the inverse of `I` if `I` has an inverse. -/
theorem right_inverse_eq (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} (I : ring.fractional_ideal g) (J : ring.fractional_ideal g) (h : I * J = 1) : J = (I⁻¹) :=
  congr_arg units.inv (units.ext rfl)

theorem mul_inv_cancel_iff (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} {I : ring.fractional_ideal g} : I * (I⁻¹) = 1 ↔ ∃ (J : ring.fractional_ideal g), I * J = 1 := sorry

@[simp] theorem map_inv (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} {K' : Type u_5} [field K'] {g' : fraction_map R₁ K'} (I : ring.fractional_ideal g) (h : alg_equiv R₁ (localization_map.codomain g) (localization_map.codomain g')) : ring.fractional_ideal.map (↑h) (I⁻¹) = (ring.fractional_ideal.map (↑h) I⁻¹) := sorry

@[simp] theorem span_singleton_inv (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} (x : localization_map.codomain g) : ring.fractional_ideal.span_singleton x⁻¹ = ring.fractional_ideal.span_singleton (x⁻¹) :=
  ring.fractional_ideal.one_div_span_singleton x

theorem mul_generator_self_inv (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} (I : ring.fractional_ideal g) [submodule.is_principal ↑I] (h : I ≠ 0) : I * ring.fractional_ideal.span_singleton (submodule.is_principal.generator ↑I⁻¹) = 1 := sorry

theorem invertible_of_principal (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} (I : ring.fractional_ideal g) [submodule.is_principal ↑I] (h : I ≠ 0) : I * (I⁻¹) = 1 :=
  iff.mpr ring.fractional_ideal.mul_div_self_cancel_iff
    (Exists.intro (ring.fractional_ideal.span_singleton (submodule.is_principal.generator ↑I⁻¹))
      (mul_generator_self_inv K I h))

theorem invertible_iff_generator_nonzero (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} (I : ring.fractional_ideal g) [submodule.is_principal ↑I] : I * (I⁻¹) = 1 ↔ submodule.is_principal.generator ↑I ≠ 0 := sorry

theorem is_principal_inv (K : Type u_3) [field K] {R₁ : Type u_4} [integral_domain R₁] {g : fraction_map R₁ K} (I : ring.fractional_ideal g) [submodule.is_principal ↑I] (h : I ≠ 0) : submodule.is_principal (subtype.val (I⁻¹)) := sorry

/--
A Dedekind domain is an integral domain that is not a field such that every fractional ideal
has an inverse.

This is equivalent to `is_dedekind_domain`.
TODO: prove the equivalence.
-/
structure is_dedekind_domain_inv (A : Type u_2) [integral_domain A]
where
  not_is_field : ¬is_field A
  mul_inv_cancel : ∀ (I : ring.fractional_ideal (fraction_ring.of A)), I ≠ ⊥ → I * (1 / I) = 1

theorem is_dedekind_domain_inv_iff (A : Type u_2) (K : Type u_3) [integral_domain A] [field K] (f : fraction_map A K) : is_dedekind_domain_inv A ↔ ¬is_field A ∧ ∀ (I : ring.fractional_ideal f), I ≠ ⊥ → I * (I⁻¹) = 1 := sorry


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

! This file was ported from Lean 3 source module ring_theory.ring_invo
! leanprover-community/mathlib commit ec2dfcae3677bcdc0d8e906831b1d251dfcbc0f1
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Ring.Equiv
import Mathlib.Algebra.Ring.Opposite

/-!
# Ring involutions

This file defines a ring involution as a structure extending `R ≃+* Rᵐᵒᵖ`,
with the additional fact `f.involution : (f (f x).unop).unop = x`.

## Notations

We provide a coercion to a function `R → Rᵐᵒᵖ`.

## References

* <https://en.wikipedia.org/wiki/Involution_(mathematics)#Ring_theory>

## Tags

Ring involution
-/

variable (R : Type _)

/-- A ring involution -/
structure RingInvo [Semiring R] extends R ≃+* Rᵐᵒᵖ where
/-- The requirement that the ring homomorphism is its own inverse -/
  involution' : ∀ x, (toFun (toFun x).unop).unop = x
#align ring_invo RingInvo

/-- The equivalence of rings underlying a ring involution. -/
add_decl_doc RingInvo.toRingEquiv
#align ring_invo.to_ring_equiv RingInvo.toRingEquiv

/-- `RingInvoClass F R` states that `F` is a type of ring involutions.
You should extend this class when you extend `RingInvo`. -/
class RingInvoClass (F : Type _) (R : outParam (Type _)) [Semiring R] extends
  RingEquivClass F R Rᵐᵒᵖ  where
  /-- Every ring involution must be its own inverse -/
  involution : ∀ (f : F) (x), (f (f x).unop).unop = x
#align ring_invo_class RingInvoClass

/-- Turn an element of a type `F` satisfying `RingInvoClass F R` into an actual
`RingInvo`. This is declared as the default coercion from `F` to `RingInvo R`. -/
@[coe]
def RingInvoClass.toRingInvo {R} [Semiring R] [RingInvoClass F R] (f : F) :
    RingInvo R :=
  { (f : R ≃+* Rᵐᵒᵖ) with involution' := RingInvoClass.involution f }

namespace RingInvo

variable {R} [Semiring R]

/-- Any type satisfying `RingInvoClass` can be cast into `RingInvo` via
`RingInvoClass.toRingInvo`. -/
instance [Semiring R] [RingInvoClass F R] : CoeTC F (RingInvo R) :=
  ⟨RingInvoClass.toRingInvo⟩

instance [Semiring R] : RingInvoClass (RingInvo R) R where
  coe f := f.toFun
  inv f := f.invFun
  coe_injective' e f h₁ h₂ := by
    rcases e with ⟨⟨tE, _⟩, _⟩; rcases f with ⟨⟨tF, _⟩, _⟩
    cases tE
    cases tF
    congr
  map_add f := f.map_add'
  map_mul f := f.map_mul'
  left_inv f := f.left_inv
  right_inv f := f.right_inv
  involution f := f.involution'

/-- Construct a ring involution from a ring homomorphism. -/
def mk' (f : R →+* Rᵐᵒᵖ) (involution : ∀ r, (f (f r).unop).unop = r) : RingInvo R :=
  { f with
    invFun := fun r => (f r.unop).unop
    left_inv := fun r => involution r
    right_inv := fun _ => MulOpposite.unop_injective <| involution _
    involution' := involution }
#align ring_invo.mk' RingInvo.mk'

-- Porting note: removed CoeFun instance, undesired in lean4
-- instance : CoeFun (RingInvo R) fun _ => R → Rᵐᵒᵖ :=
--   ⟨fun f => f.toRingEquiv.toFun⟩
#noalign ring_invo.to_fun_eq_coe

@[simp]
theorem involution (f : RingInvo R) (x : R) : (f (f x).unop).unop = x :=
  f.involution' x
#align ring_invo.involution RingInvo.involution

-- porting note: remove Coe instance, not needed
-- instance hasCoeToRingEquiv : Coe (RingInvo R) (R ≃+* Rᵐᵒᵖ) :=
--   ⟨RingInvo.toRingEquiv⟩
-- #align ring_invo.has_coe_to_ring_equiv RingInvo.hasCoeToRingEquiv

@[norm_cast]
theorem coe_ringEquiv (f : RingInvo R) (a : R) : (f : R ≃+* Rᵐᵒᵖ) a = f a :=
  rfl
#align ring_invo.coe_ring_equiv RingInvo.coe_ringEquiv

-- porting Note: simp can prove this
-- @[simp]
theorem map_eq_zero_iff (f : RingInvo R) {x : R} : f x = 0 ↔ x = 0 :=
  f.toRingEquiv.map_eq_zero_iff
#align ring_invo.map_eq_zero_iff RingInvo.map_eq_zero_iff

end RingInvo

open RingInvo

section CommRing

variable [CommRing R]

/-- The identity function of a `CommRing` is a ring involution. -/
protected def RingInvo.id : RingInvo R :=
  { RingEquiv.toOpposite R with involution' := fun _ => rfl }
#align ring_invo.id RingInvo.id

instance : Inhabited (RingInvo R) :=
  ⟨RingInvo.id _⟩

end CommRing


/-
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

! This file was ported from Lean 3 source module combinatorics.quiver.arborescence
! leanprover-community/mathlib commit fc2ed6f838ce7c9b7c7171e58d78eaf7b438fb0e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Order.WellFounded
import Mathlib.Data.Nat.Basic
import Mathlib.Combinatorics.Quiver.Subquiver
import Mathlib.Combinatorics.Quiver.Path

/-!
# Arborescences

A quiver `V` is an arborescence (or directed rooted tree) when we have a root vertex `root : V` such
that for every `b : V` there is a unique path from `root` to `b`.

## Main definitions

- `Quiver.Arborescence V`: a typeclass asserting that `V` is an arborescence
- `arborescenceMk`: a convenient way of proving that a quiver is an arborescence
- `RootedConnected r`: a typeclass asserting that there is at least one path from `r` to `b` for
every `b`.
- `geodesicSubtree r`: given `[RootedConntected r]`, this is a subquiver of `V` which contains
just enough edges to include a shortest path from `r` to `b` for every `b`.
- `geodesicArborescence : Arborescence (geodesicSubtree r)`: an instance saying that the geodesic
subtree is an arborescence. This proves the directed analogue of 'every connected graph has a
spanning tree'. This proof avoids the use of Zorn's lemma.
-/

open Opposite

universe v u

namespace Quiver

/-- A quiver is an arborescence when there is a unique path from the default vertex
    to every other vertex. -/
class Arborescence (V : Type u) [Quiver.{v} V] : Type max u v where
  /-- The root of the arborescence. -/
  root : V
  /-- There is a unique path from the root to any other vertex. -/
  uniquePath : ∀ b : V, Unique (Path root b)
#align quiver.arborescence Quiver.Arborescence

/-- The root of an arborescence. -/
def root (V : Type u) [Quiver V] [Arborescence V] : V :=
  Arborescence.root
#align quiver.root Quiver.root

instance {V : Type u} [Quiver V] [Arborescence V] (b : V) : Unique (Path (root V) b) :=
  Arborescence.uniquePath b

/-- To show that `[Quiver V]` is an arborescence with root `r : V`, it suffices to
  - provide a height function `V → ℕ` such that every arrow goes from a
    lower vertex to a higher vertex,
  - show that every vertex has at most one arrow to it, and
  - show that every vertex other than `r` has an arrow to it. -/
noncomputable def arborescenceMk {V : Type u} [Quiver V] (r : V) (height : V → ℕ)
    (height_lt : ∀ ⦃a b⦄, (a ⟶ b) → height a < height b)
    (unique_arrow : ∀ ⦃a b c : V⦄ (e : a ⟶ c) (f : b ⟶ c), a = b ∧ HEq e f)
    (root_or_arrow : ∀ b, b = r ∨ ∃ a, Nonempty (a ⟶ b)) :
    Arborescence V where
  root := r
  uniquePath b :=
    ⟨Classical.inhabited_of_nonempty
        (by
          rcases show ∃ n, height b < n from ⟨_, Nat.lt.base _⟩ with ⟨n, hn⟩
          induction' n with n ih generalizing b
          · exact False.elim (Nat.not_lt_zero _ hn)
          rcases root_or_arrow b with (⟨⟨⟩⟩ | ⟨a, ⟨e⟩⟩)
          · exact ⟨Path.nil⟩
          · rcases ih a (lt_of_lt_of_le (height_lt e) (Nat.lt_succ_iff.mp hn)) with ⟨p⟩
            exact ⟨p.cons e⟩),
      by
      have height_le : ∀ {a b}, Path a b → height a ≤ height b := by
        intro a b p
        induction' p with b c _ e ih
        rfl
        exact le_of_lt (lt_of_le_of_lt ih (height_lt e))
      suffices ∀ p q : Path r b, p = q by
        intro p
        apply this
      intro p q
      induction' p with a c p e ih <;> cases' q with b _ q f
      · rfl
      · exact False.elim (lt_irrefl _ (lt_of_le_of_lt (height_le q) (height_lt f)))
      · exact False.elim (lt_irrefl _ (lt_of_le_of_lt (height_le p) (height_lt e)))
      · rcases unique_arrow e f with ⟨⟨⟩, ⟨⟩⟩
        rw [ih]⟩
#align quiver.arborescence_mk Quiver.arborescenceMk

/-- `RootedConnected r` means that there is a path from `r` to any other vertex. -/
class RootedConnected {V : Type u} [Quiver V] (r : V) : Prop where
  nonempty_path : ∀ b : V, Nonempty (Path r b)
#align quiver.rooted_connected Quiver.RootedConnected

attribute [instance] RootedConnected.nonempty_path

section GeodesicSubtree

variable {V : Type u} [Quiver.{v + 1} V] (r : V) [RootedConnected r]

/-- A path from `r` of minimal length. -/
noncomputable def shortestPath (b : V) : Path r b :=
  WellFounded.min (measure Path.length).wf Set.univ Set.univ_nonempty
#align quiver.shortest_path Quiver.shortestPath

/-- The length of a path is at least the length of the shortest path -/
theorem shortest_path_spec {a : V} (p : Path r a) : (shortestPath r a).length ≤ p.length :=
  not_lt.mp (WellFounded.not_lt_min (measure _).wf Set.univ _ trivial)
#align quiver.shortest_path_spec Quiver.shortest_path_spec

/-- A subquiver which by construction is an arborescence. -/
def geodesicSubtree : WideSubquiver V := fun a b =>
  { e | ∃ p : Path r a, shortestPath r b = p.cons e }
#align quiver.geodesic_subtree Quiver.geodesicSubtree

noncomputable instance geodesicArborescence : Arborescence (geodesicSubtree r) :=
  arborescenceMk r (fun a => (shortestPath r a).length)
    (by
      rintro a b ⟨e, p, h⟩
      simp_rw [h, Path.length_cons, Nat.lt_succ_iff]
      apply shortest_path_spec)
    (by
      rintro a b c ⟨e, p, h⟩ ⟨f, q, j⟩
      cases h.symm.trans j
      constructor <;> rfl)
    (by
      intro b
      rcases hp : shortestPath r b with (_ | ⟨p, e⟩)
      · exact Or.inl rfl
      · exact Or.inr ⟨_, ⟨⟨e, p, hp⟩⟩⟩)
#align quiver.geodesic_arborescence Quiver.geodesicArborescence

end GeodesicSubtree

end Quiver