------------------------------------------------------------------------
-- The Agda standard library
--
-- Substituting equalities for binary relations
------------------------------------------------------------------------

-- For more general transformations between algebraic structures see
-- `Algebra.Morphisms`.

{-# OPTIONS --without-K --safe #-}

open import Data.Product as Prod
open import Relation.Binary.Core

module Algebra.Construct.Subst.Equality
  {a ℓ₁ ℓ₂} {A : Set a} {≈₁ : Rel A ℓ₁} {≈₂ : Rel A ℓ₂}
  (equiv@(to , from) : ≈₁  ≈₂)
  where

open import Algebra.Definitions
open import Algebra.Structures
import Data.Sum as Sum
open import Function.Base
open import Relation.Binary.Construct.Subst.Equality equiv

------------------------------------------------------------------------
-- Definitions

cong₁ :  {⁻¹}  Congruent₁ ≈₁ ⁻¹  Congruent₁ ≈₂ ⁻¹
cong₁ cong x≈y = to (cong (from x≈y))

cong₂ :  {}  Congruent₂ ≈₁   Congruent₂ ≈₂ 
cong₂ cong u≈v x≈y = to (cong (from u≈v) (from x≈y))

assoc :  {}  Associative ≈₁   Associative ≈₂ 
assoc assoc x y z = to (assoc x y z)

comm :  {}  Commutative ≈₁   Commutative ≈₂ 
comm comm x y = to (comm x y)

idem :  {}  Idempotent ≈₁   Idempotent ≈₂ 
idem idem x = to (idem x)

sel :  {}  Selective ≈₁   Selective ≈₂ 
sel sel x y = Sum.map to to (sel x y)

identity :  { e}  Identity ≈₁ e   Identity ≈₂ e 
identity = Prod.map (to ∘_) (to ∘_)

inverse :  { e ⁻¹}  Inverse ≈₁ ⁻¹  e  Inverse ≈₂ ⁻¹  e
inverse = Prod.map (to ∘_) (to ∘_)

absorptive :  { }  Absorptive ≈₁    Absorptive ≈₂  
absorptive = Prod.map  f x y  to (f x y))  f x y  to (f x y))

distribˡ :  { }  _DistributesOverˡ_ ≈₁    _DistributesOverˡ_ ≈₂  
distribˡ distribˡ x y z = to (distribˡ x y z)

distribʳ :  { }  _DistributesOverʳ_ ≈₁    _DistributesOverʳ_ ≈₂  
distribʳ distribʳ x y z = to (distribʳ x y z)

distrib :  { }  _DistributesOver_ ≈₁    _DistributesOver_ ≈₂  
distrib {} {} = Prod.map (distribˡ {} {}) (distribʳ {} {})

------------------------------------------------------------------------
-- Structures

isMagma :  {}  IsMagma ≈₁   IsMagma ≈₂ 
isMagma S = record
  { isEquivalence = isEquivalence S.isEquivalence
  ; ∙-cong        = cong₂ S.∙-cong
  } where module S = IsMagma S

isSemigroup :  {}  IsSemigroup ≈₁   IsSemigroup ≈₂ 
isSemigroup {} S = record
  { isMagma = isMagma S.isMagma
  ; assoc   = assoc {} S.assoc
  } where module S = IsSemigroup S

isBand :  {}  IsBand ≈₁   IsBand ≈₂ 
isBand {} S = record
  { isSemigroup = isSemigroup S.isSemigroup
  ; idem        = idem {} S.idem
  } where module S = IsBand S

isSelectiveMagma :  {}  IsSelectiveMagma ≈₁   IsSelectiveMagma ≈₂ 
isSelectiveMagma S = record
  { isMagma = isMagma S.isMagma
  ; sel     = sel S.sel
  } where module S = IsSelectiveMagma S

isMonoid :  { ε}  IsMonoid ≈₁  ε  IsMonoid ≈₂  ε
isMonoid S = record
  { isSemigroup = isSemigroup S.isSemigroup
  ; identity    = Prod.map (to ∘_) (to ∘_) S.identity
  } where module S = IsMonoid S

isCommutativeMonoid :  { ε} 
  IsCommutativeMonoid ≈₁  ε  IsCommutativeMonoid ≈₂  ε
isCommutativeMonoid S = record
  { isMonoid = isMonoid S.isMonoid
  ; comm     = comm S.comm
  } where module S = IsCommutativeMonoid S

isIdempotentCommutativeMonoid :  { ε} 
  IsIdempotentCommutativeMonoid ≈₁  ε 
  IsIdempotentCommutativeMonoid ≈₂  ε
isIdempotentCommutativeMonoid {} S = record
  { isCommutativeMonoid = isCommutativeMonoid S.isCommutativeMonoid
  ; idem                = to  S.idem
  } where module S = IsIdempotentCommutativeMonoid S

isGroup :  { ε ⁻¹}  IsGroup ≈₁  ε ⁻¹  IsGroup ≈₂  ε ⁻¹
isGroup S = record
  { isMonoid = isMonoid S.isMonoid
  ; inverse  = Prod.map (to ∘_) (to ∘_) S.inverse
  ; ⁻¹-cong  = cong₁ S.⁻¹-cong
  } where module S = IsGroup S

isAbelianGroup :  { ε ⁻¹} 
  IsAbelianGroup ≈₁  ε ⁻¹  IsAbelianGroup ≈₂  ε ⁻¹
isAbelianGroup S = record
  { isGroup = isGroup S.isGroup
  ; comm    = comm S.comm
  } where module S = IsAbelianGroup S

isNearSemiring :  {+ * 0#} 
  IsNearSemiring ≈₁ + * 0#  IsNearSemiring ≈₂ + * 0#
isNearSemiring {* = *} S = record
  { +-isMonoid    = isMonoid S.+-isMonoid
  ; *-cong        = cong₂ S.*-cong
  ; *-assoc       = assoc {*} S.*-assoc
  ; distribʳ      = λ x y z  to (S.distribʳ x y z)
  ; zeroˡ         = to  S.zeroˡ
  } where module S = IsNearSemiring S

isSemiringWithoutOne :  {+ * 0#} 
  IsSemiringWithoutOne ≈₁ + * 0#  IsSemiringWithoutOne ≈₂ + * 0#
isSemiringWithoutOne {+} {*} S = record
  { +-isCommutativeMonoid = isCommutativeMonoid S.+-isCommutativeMonoid
  ; *-cong                = cong₂ S.*-cong
  ; *-assoc               = assoc {*} S.*-assoc
  ; distrib               = distrib {*} {+} S.distrib
  ; zero                  = Prod.map (to ∘_) (to ∘_) S.zero
  } where module S = IsSemiringWithoutOne S

isCommutativeSemiringWithoutOne :  {+ * 0#} 
  IsCommutativeSemiringWithoutOne ≈₁ + * 0# 
  IsCommutativeSemiringWithoutOne ≈₂ + * 0#
isCommutativeSemiringWithoutOne S = record
  { isSemiringWithoutOne = isSemiringWithoutOne S.isSemiringWithoutOne
  ; *-comm               = comm S.*-comm
  } where module S = IsCommutativeSemiringWithoutOne S