\import Algebra.Group
\import Algebra.Meta
\import Algebra.Monoid
\import Algebra.Monoid.Sub
\import Algebra.Pointed
\import Function.Meta
\import Logic
\import Logic.Meta
\import Order.PartialOrder
\import Paths
\import Relation.Equivalence
\import Set

\type LocType {M : CMonoid} (S : SubMonoid M) => Quotient (~)
  \where {
    \func SType {M : CMonoid} (S : SubMonoid M) => \Sigma (x y : M) (S y)

    \protected \func \infix 4 ~ (a b : SType S) => a.1 * b.2 = b.1 * a.2

    \func inl~ (a : SType S) : LocType S => in~ a

    \lemma ~-lequiv {a b : SType S} (p : a ~ b) : inl~ a = inl~ b
      => path \lam i => ~-equiv a b p i

    \lemma ~-lequiv-right {a b : SType S} {c : M} (Sc : S c) (p : a.1 * b.2 * c = b.1 * a.2 * c) : inl~ a = inl~ b
      => \let a' : SType S => (a.1 * c, a.2 * c, contains_* a.3 Sc)
         \in ~-lequiv equation.cMonoid *> ~-lequiv {_} {_} {a'} (equation.cMonoid {p})
  }

\instance LocMonoid {M : CMonoid} (S : SubMonoid M) : CMonoid (LocType S) \cowith
  | ide => in~ (1, 1, S.contains_ide)
  | * (x y : LocType S) : LocType S \with {
    | in~ a, in~ b => in~ (a.1 M.* b.1, a.2 M.* b.2, S.contains_* a.3 b.3)
    | in~ a, ~-equiv x y r => ~-lequiv $ equation.cMonoid {r}
    | ~-equiv x y r, in~ a => ~-lequiv $ equation.cMonoid {r}
  }
  | ide-left {in~ a} => ~-lequiv equation.cMonoid
  | *-assoc {in~ a} {in~ b} {in~ c} => ~-lequiv equation.cMonoid
  | *-comm {in~ a} {in~ b} => ~-lequiv equation.cMonoid
  \where \open LocType

\type LocAbType {M : AbMonoid} (S : SubAddMonoid M) => Quotient (~)
  \where {
    \func SType {M : AbMonoid} (S : SubAddMonoid M) => \Sigma (x y : M) (S y)

    \protected \func \infix 4 ~ (a b : SType S) => a.1 + b.2 = b.1 + a.2

    \func inl~ (a : SType S) : LocAbType S => in~ a

    \lemma ~-lequiv {a b : SType S} (p : a ~ b) : inl~ a = inl~ b
      => path \lam i => ~-equiv a b p i

    \lemma ~-lequiv-right {a b : SType S} {c : M} (Sc : S c) (p : a.1 + b.2 + c = b.1 + a.2 + c) : inl~ a = inl~ b
      => \let a' : SType S => (a.1 + c, a.2 + c, contains_+ a.3 Sc)
         \in ~-lequiv equation.abMonoid *> ~-lequiv {_} {_} {a'} (equation.abMonoid {p})
  }

\instance LocAbMonoid {M : AbMonoid} (S : SubAddMonoid M) : AbMonoid (LocAbType S) \cowith
  | zro => in~ (0, 0, S.contains_zro)
  | + (x y : LocAbType S) : LocAbType S \with {
    | in~ a, in~ b => in~ (a.1 M.+ b.1, a.2 M.+ b.2, S.contains_+ a.3 b.3)
    | in~ a, ~-equiv x y r => ~-lequiv $ equation.abMonoid {r}
    | ~-equiv x y r, in~ a => ~-lequiv $ equation.abMonoid {r}
  }
  | zro-left {in~ a} => ~-lequiv equation.abMonoid
  | +-assoc {in~ a} {in~ b} {in~ c} => ~-lequiv equation.abMonoid
  | +-comm {in~ a} {in~ b} => ~-lequiv equation.abMonoid
  \where \open LocAbType

\func MaxLocAbType (M : AbMonoid) => LocAbType {M} SubAddMonoid.max

\instance GrothendieckAbGroup (M : AbMonoid) : AbGroup (MaxLocAbType M)
  | AbMonoid => LocAbMonoid SubAddMonoid.max
  | negative (x : MaxLocAbType M) : MaxLocAbType M \with {
    | in~ a => in~ (a.2, a.1, ())
    | ~-equiv x y r => LocAbType.~-lequiv $ equation.abMonoid {r}
  }
  | negative-left {in~ a} => LocAbType.~-lequiv equation.abMonoid