///|
/// Associative, commutative accumulation and an additive-identity test.
pub(open) trait Semigroup {
  fn plus(Self, Self) -> Self
  fn is_zero(Self) -> Bool
}

///|
pub(open) trait Monoid: Semigroup {
  fn zero() -> Self
}

///|
pub(open) trait Abelian: Monoid {
  fn negate(Self) -> Self
}

///|
pub impl Semigroup for Int64 with fn plus(self, other) {
  self + other
}

///|
pub impl Semigroup for Int64 with fn is_zero(self) {
  self == 0
}

///|
pub impl Monoid for Int64 with fn zero() {
  0
}

///|
pub impl Abelian for Int64 with fn negate(self) {
  -self
}

///|
pub impl Semigroup for Int32 with fn plus(self, other) {
  self + other
}

///|
pub impl Semigroup for Int32 with fn is_zero(self) {
  self == 0
}

///|
pub impl Monoid for Int32 with fn zero() {
  0
}

///|
pub impl Abelian for Int32 with fn negate(self) {
  -self
}

///|
/// Presence-only semigroup: there is no zero or retraction.
pub(all) enum Present {
  Present
} derive(Eq, Debug)

///|
pub impl Semigroup for Present with fn plus(self, _other) {
  self
}

///|
pub impl Semigroup for Present with fn is_zero(_self) {
  false
}

///|
pub extend Present with Eq::{equal, not_equal}

///|
pub extend Present with Debug::{to_repr}

///|
pub extend Present with Semigroup::{plus, is_zero}

///|
pub impl[A : Semigroup, B : Semigroup] Semigroup for (A, B) with fn plus(
  self,
  other,
) {
  (self.0.plus(other.0), self.1.plus(other.1))
}

///|
pub impl[A : Semigroup, B : Semigroup] Semigroup for (A, B) with fn is_zero(
  self,
) {
  self.0.is_zero() && self.1.is_zero()
}

///|
pub impl[A : Monoid, B : Monoid] Monoid for (A, B) with fn zero() {
  (A::zero(), B::zero())
}

///|
pub impl[A : Abelian, B : Abelian] Abelian for (A, B) with fn negate(self) {
  (self.0.negate(), self.1.negate())
}