{-# OPTIONS --safe --lossy-unification #-}
module Cubical.Algebra.AbGroup.Instances.FreeAbGroup where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.HLevels

open import Cubical.Data.Sigma
open import Cubical.Data.Nat hiding (_·_) renaming (_+_ to _+ℕ_)
open import Cubical.Data.Nat.Order
open import Cubical.Data.Nat.Order.Inductive
open import Cubical.Data.Int renaming (_·_ to _·ℤ_ ; -_ to -ℤ_)
open import Cubical.Data.Fin.Inductive
open import Cubical.Data.Empty as 

open import Cubical.HITs.FreeAbGroup

open import Cubical.Algebra.AbGroup
open import Cubical.Algebra.AbGroup.Instances.Pi
open import Cubical.Algebra.AbGroup.Instances.Int
open import Cubical.Algebra.Group
open import Cubical.Algebra.Group.Morphisms
open import Cubical.Algebra.Group.MorphismProperties


private variable
   : Level

module _ {A : Type } where

  FAGAbGroup : AbGroup 
  FAGAbGroup = makeAbGroup {G = FreeAbGroup A} ε _·_ _⁻¹ trunc assoc identityᵣ invᵣ comm

-- Alternative definition of case when A = Fin n
ℤ[Fin_] : (n : )  AbGroup ℓ-zero
ℤ[Fin n ] = ΠℤAbGroup (Fin n)


--  generator of ℤ[Fin_]
ℤFinGenerator : {n : } (k : Fin n)  ℤ[Fin n ] .fst
ℤFinGenerator {n = n} k s with (fst k ≟ᵗ fst s)
... | lt x = 0
... | eq x = 1
... | gt x = 0

ℤFinGeneratorComm : {n : } (x y : Fin n)  ℤFinGenerator x y  ℤFinGenerator y x
ℤFinGeneratorComm x y with (fst x ≟ᵗ fst y) | (fst y ≟ᵗ fst x)
... | lt x₁ | lt x₂ = refl
... | lt x₁ | eq x₂ = ⊥.rec (¬m<ᵗm (subst (_<ᵗ fst y) (sym x₂) x₁))
... | lt x₁ | gt x₂ = refl
... | eq x₁ | lt x₂ = ⊥.rec (¬m<ᵗm (subst (fst y <ᵗ_) x₁ x₂))
... | eq x₁ | eq x₂ = refl
... | eq x₁ | gt x₂ = ⊥.rec (¬m<ᵗm (subst (_<ᵗ fst y) x₁ x₂))
... | gt x₁ | lt x₂ = refl
... | gt x₁ | eq x₂ = ⊥.rec (¬m<ᵗm (subst (_<ᵗ fst x) x₂ x₁))
... | gt x₁ | gt x₂ = refl

isGeneratorℤFinGenerator : {n : } (f : ℤ[Fin n ] .fst) (a : _)
   f a  sumFinℤ {n = n} λ s  f s ·ℤ (ℤFinGenerator s a)
isGeneratorℤFinGenerator {n = suc n} f =
  elimFin basec
          λ s  cong f (Σ≡Prop  _  isProp<ᵗ) refl)
  ∙∙ isGeneratorℤFinGenerator {n = n} (f  injectSuc) s
  ∙∙  i  sumFinℤ  x  f (injectSuc x) ·ℤ lem₁ s x i))
  ∙∙ +Comm (F s) 0
  ∙∙ λ i  (sym (·Comm (f flast) 0)
     (cong (f flast ·ℤ_) (sym (lem₂ s flast refl)))) i
     + F s
  where
  basec : f flast  sumFinℤ  s  f s ·ℤ ℤFinGenerator s flast)
  basec with (n ≟ᵗ n)
  ... | lt x = ⊥.rec (¬m<ᵗm x)
  ... | eq x = λ i  (·Comm (f flast) (pos 1) (~ i)) + lem₂ (~ i)
    where
    lem₁ : (s : _)  ℤFinGenerator (injectSuc {n = n} s) flast  0
    lem₁ s with (fst s ≟ᵗ n)
    ... | lt x = refl
    ... | eq w = ⊥.rec (¬m<ᵗm (subst (fst s <ᵗ_) (sym w) (snd s)))
    ... | gt x = refl

    lem₂ : sumFinℤ  s  f (injectSuc s) ·ℤ ℤFinGenerator (injectSuc s) flast)  0
    lem₂ =  i  sumFinℤ λ s  (cong (f (injectSuc s) ·ℤ_) (lem₁ s)
                               ·Comm (f (injectSuc s)) 0) i)
        (sumFinℤ0 n)
  ... | gt x = ⊥.rec (¬m<ᵗm x)

  module _ (a : Fin n) where
    F = sumFinGen _+_ (pos 0)  x  f (injectSuc x)
                      ·ℤ (ℤFinGenerator (injectSuc x) (injectSuc a)))

    lem₁ : (x : _)
       ℤFinGenerator {n = n} x a -- (a , diff , cong predℕ p)
        ℤFinGenerator {n = suc n} (injectSuc x) (injectSuc a) -- (a , suc diff , p)
    lem₁ x with (fst x ≟ᵗ fst a)
    ... | lt x₁ = refl
    ... | eq x₁ = refl
    ... | gt x₁ = refl

    lem₂ : (k : Fin (suc n))  fst k  n
       ℤFinGenerator {n = suc n} k (injectSuc a)  0
    lem₂ k q with (fst k ≟ᵗ fst a)
    ... | lt _ = refl
    ... | eq x = ⊥.rec (¬m<ᵗm (subst (_<ᵗ n) (sym x  q) (snd a)))
    ... | gt _ = refl


isGeneratorℤFinGenerator' : {n : } (f : ℤ[Fin n ] .fst) (a : _)
   f a  sumFinℤ {n = n} λ s  (ℤFinGenerator a s) ·ℤ f s
isGeneratorℤFinGenerator' {n = n} f a =
  isGeneratorℤFinGenerator f a
   sumFinℤId n λ x  ·Comm (f x) (ℤFinGenerator x a)
                      cong (_·ℤ f x) (ℤFinGeneratorComm x a)

ℤFinGeneratorVanish : (n : ) (x : _)  ℤFinGenerator {n = suc n} flast (injectSuc x)  0
ℤFinGeneratorVanish n x with (n ≟ᵗ (fst x))
... | lt x₁ = refl
... | eq x₁ = ⊥.rec (¬m<ᵗm (subst (_<ᵗ n) (sym x₁) (snd x)))
... | gt x₁ = refl

-- elimination principle
elimℤFin :  {} {n : }
   (P : ℤ[Fin (suc n) ] .fst  Type )
   ((x : _)  P (ℤFinGenerator x))
   ((f : _)  P f  P  x  -ℤ (f x)))
   ((f g : _)  P f  P g  P  x  f x + g x))
   (x : _)  P x
elimℤFin {n = n} P gen d ind f =
  subst P (sym (funExt (isGeneratorℤFinGenerator f)))
    (P-resp-sumFinℤ n P gen d ind (suc n)  x y  f y ·ℤ ℤFinGenerator y x)
      λ t  P-resp· n P gen d ind (f t) (ℤFinGenerator t) (gen t))
  where
  module _ {} (n : ) (P : ℤ[Fin (suc n) ] .fst  Type )
           (gens : (x : _)  P (ℤFinGenerator x))
           (ind- : ((f : _)  P f  P  x  -ℤ (f x))))
           (ind+ : (f g : _)  P f  P g  P  x  f x + g x)) where
    P-resp·pos : (x : ) (f : ℤ[Fin (suc n) ] .fst)  P f  P  x₁  pos x ·ℤ f x₁)
    P-resp·pos zero f d =
      subst P  (funExt  x  -Cancel (ℤFinGenerator fzero x)))
      (ind+ (ℤFinGenerator fzero)
         x  -ℤ (ℤFinGenerator fzero x))
          (gens fzero) (ind- _ (gens fzero)))
    P-resp·pos (suc zero) f d = d
    P-resp·pos (suc (suc x)) f d =
      ind+ f  a  pos (suc x) ·ℤ f a) d (P-resp·pos (suc x) f d)

    P-resp· : (x : _) (f : ℤ[Fin (suc n) ] .fst)  P f  P  x₁  x ·ℤ f x₁)
    P-resp· (pos n) f d = P-resp·pos n f d
    P-resp· (negsuc n) f d =
      subst P (funExt  r  -DistL· (pos (suc n)) (f r)))
       (ind-  x  pos (suc n) ·ℤ f x) (P-resp·pos (suc n) _ d))

    P-resp-sumFinℤ : (m : ) (f : Fin (suc n)  Fin m  )
                   ((t : _)  P  x  f x t))
                   P  t  sumFinℤ {n = m} (f t))
    P-resp-sumFinℤ zero f r = P-resp·pos zero (ℤFinGenerator fzero) (gens fzero)
    P-resp-sumFinℤ (suc m) f r =
      ind+  t  f t flast)  t  sumFinℤ  x  f t (injectSuc x)))
        (r flast)
        (P-resp-sumFinℤ m  x y  f x (injectSuc y)) (r  injectSuc))


-- multiplication
·posFree : {n : } (a : ) (x : Fin n)  FreeAbGroup (Fin n)
·posFree {n = n} zero x = ε
·posFree {n = n} (suc a) x =  x  · (·posFree {n = n} a x)

·Free : {n : } (a : ) (x : Fin n)  FreeAbGroup (Fin n)
·Free (pos n) x = ·posFree n x
·Free (negsuc n) x = ·posFree (suc n) x ⁻¹

·Free⁻¹ : {n : } (a : ) (x : Fin n)  ·Free (-ℤ a) x  (·Free a x) ⁻¹
·Free⁻¹ {n = n} (pos zero) x = sym (GroupTheory.inv1g (AbGroup→Group FAGAbGroup))
·Free⁻¹ {n = n} (pos (suc n₁)) x = refl
·Free⁻¹ {n = n} (negsuc n₁) x = sym (GroupTheory.invInv (AbGroup→Group FAGAbGroup) _)

·FreeSuc : {n : } (a : ) (x : Fin n)
   ·Free (sucℤ a) x   x  · ·Free a x
·FreeSuc (pos n) x = refl
·FreeSuc (negsuc zero) x =
  sym (cong ( x  ·_) (cong (_⁻¹) (identityᵣ  x ))  invᵣ  x )
·FreeSuc (negsuc (suc n)) x =
  sym (cong ( x  ·_)
           (GroupTheory.invDistr (AbGroup→Group FAGAbGroup)  x  ( x  · ·posFree n x)
           comm _ _)
  ∙∙ assoc _ _ _
  ∙∙ (cong ( ( x  · ·posFree n x) ⁻¹) (invᵣ  x )
   comm _ _
   identityᵣ (( x  · ·posFree n x) ⁻¹)))

·FreeHomPos : {n : } (a : ) (b : ) (x : Fin n)
              ·Free (pos a) x · ·Free b x  ·Free (pos a + b) x
·FreeHomPos zero b x = comm _ _  identityᵣ (·Free b x)  cong  y  ·Free y x) (+Comm b 0)
·FreeHomPos (suc n) b x =
    sym (assoc  x  (·Free (pos n) x) (·Free b x))
   cong ( x  ·_) (·FreeHomPos n b x)
   l b
  where
  l : (b : )   x  · ·Free (pos n + b) x  ·Free (pos (suc n) + b) x
  l (pos m) = cong ( x  ·_)  i  ·Free (pos+ n m (~ i)) x)
                              λ i  ·Free (pos+ (suc n) m i) x
  l (negsuc m) = sym (·FreeSuc (pos n +negsuc m) x)
                λ j  ·Free (sucℤ+negsuc m (pos n) j) x

·FreeHom : {n : } (a b : ) (x : Fin n)  ·Free a x · ·Free b x  ·Free (a + b) x
·FreeHom (pos n) b x = ·FreeHomPos n b x
·FreeHom (negsuc n) b x =
     cong (( x  · ·posFree n x) ⁻¹ ·_)
       (sym (cong (_⁻¹) (·Free⁻¹ b x)
           GroupTheory.invInv (AbGroup→Group FAGAbGroup) (·Free b x)))
    comm _ _
  ∙∙ sym (GroupTheory.invDistr (AbGroup→Group FAGAbGroup) (·Free (pos (suc n)) x) (·Free (-ℤ b) x))
  ∙∙ cong (_⁻¹) (·FreeHomPos (suc n) (-ℤ b) x)
  ∙∙ sym (·Free⁻¹ (pos (suc n) + -ℤ b) x)
  ∙∙ λ i  ·Free (help (~ i)) x
  where
  help : negsuc n + b  -ℤ (pos (suc n) + -ℤ b)
  help = cong (negsuc n +_) (sym (-Involutive b))
        sym (-Dist+ (pos (suc n)) (-ℤ b))

sumFinℤFinGenerator≡1 : (n : ) (f : Fin n)
   sumFinGen _·_ ε  x  ·Free (ℤFinGenerator f x) x)   f 
sumFinℤFinGenerator≡1 (suc n) =
  elimFin (basec n)
          indstep
  where
  basec : (n : )  sumFinGen _·_ ε  x  ·Free (ℤFinGenerator (flast {m = n}) x) x)   flast 
  basec n with (n ≟ᵗ n)
  ... | lt x = ⊥.rec (¬m<ᵗm x)
  ... | eq x = ((λ i  identityᵣ  flast  i
            · sumFinGen _·_ ε  x  ·Free (ℤFinGenerator flast (injectSuc x))
                                            (injectSuc x)))
               cong ( flast  ·_) (sumFinGenId _ _ _ _
                  (funExt  s  cong₂ ·Free (ℤFinGeneratorVanish n s) refl))
                 sumFinGen0 _·_ ε identityᵣ n
                    x₁  ·Free (pos 0) (injectSuc x₁))  _  refl)) )
               identityᵣ _
  ... | gt x = ⊥.rec (¬m<ᵗm x)
  module _ (x : Fin n) where
    FR = Free↑
    indstep :
      ·Free (ℤFinGenerator (injectSuc x) flast) flast · sumFinGen _·_ ε
             x₁  ·Free (ℤFinGenerator (injectSuc x) (injectSuc x₁)) (injectSuc x₁))
        injectSuc x 
    indstep with (fst x ≟ᵗ n)
    ... | lt a = comm _ _
                identityᵣ _
                 i  sumFinGen _·_ ε  x  lem x i))
                sym (Free↑sumFinℤ n n  x₁  ·Free (ℤFinGenerator x x₁) x₁))
                cong (Free↑ n) (sumFinℤFinGenerator≡1 n x)
     where
     lem : (x₁ : Fin n)
        ·Free (ℤFinGenerator (injectSuc x) (injectSuc x₁)) (injectSuc x₁)
         Free↑ n (·Free (ℤFinGenerator x x₁) x₁)
     lem x₁ with (fst x ≟ᵗ fst x₁)
     ... | lt x = refl
     ... | eq x = refl
     ... | gt x = refl
    ... | eq a = ⊥.rec (¬m<ᵗm (subst (_<ᵗ n) a (snd x)))
    ... | gt a = ⊥.rec (¬m<ᵗm (<ᵗ-trans a (snd x)))


-- equivalence between two versions of free ab group
ℤFin→Free : (n : )  ℤ[Fin n ] .fst  FreeAbGroup (Fin n)
ℤFin→Free n f = sumFinGen {A = FreeAbGroup (Fin n)} _·_ ε  x  ·Free (f x) x)

Free→ℤFin : (n : )  FreeAbGroup (Fin n)  ℤ[Fin n ] .fst
Free→ℤFin n  x  = ℤFinGenerator x
Free→ℤFin n ε _ = pos 0
Free→ℤFin n (a · a₁) x = Free→ℤFin n a x + Free→ℤFin n a₁ x
Free→ℤFin n (a ⁻¹) x = -ℤ Free→ℤFin n a x
Free→ℤFin n (assoc a a₁ a₂ i) x = +Assoc (Free→ℤFin n a x) (Free→ℤFin n a₁ x) (Free→ℤFin n a₂ x) i
Free→ℤFin n (comm a a₁ i) x = +Comm (Free→ℤFin n a x) (Free→ℤFin n a₁ x) i
Free→ℤFin n (identityᵣ a i) = Free→ℤFin n a
Free→ℤFin n (invᵣ a i) x = -Cancel (Free→ℤFin n a x) i
Free→ℤFin n (trunc a a₁ x y i j) k =
  isSet→isSet' isSetℤ
     _  Free→ℤFin n a k)  _  Free→ℤFin n a₁ k)
     j  Free→ℤFin n (x j) k)  j  Free→ℤFin n (y j) k) j i

ℤFin→Free-ℤFinGenerator : (n : ) (x : _)
   ℤFin→Free n (ℤFinGenerator x)   x 
ℤFin→Free-ℤFinGenerator zero x = isContr→isProp isContr-FreeFin0 _ _
ℤFin→Free-ℤFinGenerator (suc n) f = sumFinℤFinGenerator≡1 _ f

ℤFin→FreeHom : (n : )  (f g : ℤ[Fin n ] .fst)
   ℤFin→Free n  x  f x + g x)  ℤFin→Free n f · ℤFin→Free n g
ℤFin→FreeHom n f g =
   i  sumFinGen _·_ ε  x  ·FreeHom  (f x) (g x) x (~ i)))
   sumFinGenHom _·_ ε identityᵣ comm assoc n
       x  ·Free (f x) x) λ x  ·Free (g x) x

ℤFin→FreeInv : (n : ) (f : ℤ[Fin n ] .fst)
   ℤFin→Free n  x  -ℤ (f x))  (ℤFin→Free n f) ⁻¹
ℤFin→FreeInv n f =
     i  sumFinGen _·_ ε  x  ·Free⁻¹ (f x) x i))
   sumFinG-neg n {A = FAGAbGroup}  x  ·Free (f x) x)

ℤFin→Free→ℤFin : (n : ) (x : FreeAbGroup (Fin n))  ℤFin→Free n (Free→ℤFin n x)  x
ℤFin→Free→ℤFin zero x = isContr→isProp (subst (isContr  FreeAbGroup) (sym lem) isContr-Free⊥) _ _
  where
  lem : Fin 0  
  lem = isoToPath (iso ¬Fin0 (λ{()}) (λ{()}) λ p  ⊥.rec (¬Fin0 p))
ℤFin→Free→ℤFin (suc n) =
  ElimProp.f (trunc _ _)
    (ℤFin→Free-ℤFinGenerator (suc n))
    (comm _ _
    ∙∙ identityᵣ _
    ∙∙ sumFinGen0 _·_ ε identityᵣ n  _  ε)  _  refl))
     {x = x} {y = y} p q
       ℤFin→FreeHom (suc n) (Free→ℤFin (suc n) x) (Free→ℤFin (suc n) y)  cong₂ _·_ p q)
    λ {x} p  ℤFin→FreeInv (suc n) (Free→ℤFin (suc n) x)  cong (_⁻¹) p

Free→ℤFin→Free : (n : ) (x : _)  Free→ℤFin n (ℤFin→Free n x)  x
Free→ℤFin→Free zero f = funExt λ x  ⊥.rec (¬Fin0 x)
Free→ℤFin→Free (suc n) =
  elimℤFin _
    x  cong (Free→ℤFin (suc n)) (ℤFin→Free-ℤFinGenerator (suc n) x))
    f p  cong (Free→ℤFin (suc n))
              (ℤFin→FreeInv (suc n) f)  λ i r  -ℤ (p i r))
   λ f g p q  cong (Free→ℤFin (suc n))
                 (ℤFin→FreeHom (suc n) f g)  λ i r  p i r + q i r

Iso-ℤFin-FreeAbGroup : (n : )  Iso (ℤ[Fin n ] .fst) (FAGAbGroup {A = Fin n} .fst)
Iso.fun (Iso-ℤFin-FreeAbGroup n) = ℤFin→Free n
Iso.inv (Iso-ℤFin-FreeAbGroup n) = Free→ℤFin n
Iso.rightInv (Iso-ℤFin-FreeAbGroup n) = ℤFin→Free→ℤFin n
Iso.leftInv (Iso-ℤFin-FreeAbGroup n) = Free→ℤFin→Free n

ℤFin≅FreeAbGroup : (n : )  AbGroupIso (ℤ[Fin n ]) (FAGAbGroup {A = Fin n})
fst (ℤFin≅FreeAbGroup n) = Iso-ℤFin-FreeAbGroup n
snd (ℤFin≅FreeAbGroup n) = makeIsGroupHom (ℤFin→FreeHom n)

-- this iso implies that ℤFin inherits the prop elimination principle of FAGAbGroup
elimPropℤFin :  {} (n : )
  (A : ℤ[Fin n ] .fst  Type )
   ((x : _)  isProp (A x))
   (A  _  0))
   ((x : _)  A (ℤFinGenerator x))
   ((f g : _)  A f  A g  A λ x  f x + g x)
   ((f : _)  A f  A (-ℤ_  f))
   (x : _)  A x
elimPropℤFin n A pr z t s u w =
  subst A (Iso.leftInv (Iso-ℤFin-FreeAbGroup n) w) (help (ℤFin→Free n w))
  where
  help : (x : _)  A (Free→ℤFin n x)
  help = ElimProp.f (pr _) t z
     {x} {y}  s (Free→ℤFin n x) (Free→ℤFin n y))
    λ {x}  u (Free→ℤFin n x)

-- functoriality of ℤFin in n

ℤFinFunctGenerator : {n m : } (f : Fin n  Fin m) (g : ℤ[Fin n ] .fst)
  (x : Fin n)  ℤ[Fin m ] .fst
ℤFinFunctGenerator {n = n} {m} f g x y with ((f x .fst) ≟ᵗ y .fst)
... | lt _ = 0
... | eq _ = g x
... | gt _ = 0

ℤFinFunctGenerator≡ : {n m : } (f : Fin n  Fin m)
  (t : Fin n)  (y : Fin m)
   ℤFinFunctGenerator f (ℤFinGenerator t) t y
    ℤFinGenerator (f t) y
ℤFinFunctGenerator≡ f t y with (f t .fst ≟ᵗ y .fst)
... | lt _ = refl
... | eq _ = lem
  where
  lem : _
  lem with (fst t ≟ᵗ fst t)
  ... | lt q = ⊥.rec (¬m<ᵗm q)
  ... | eq _ = refl
  ... | gt q = ⊥.rec (¬m<ᵗm q)
... | gt _ = refl

ℤFinFunctFun : {n m : } (f : Fin n  Fin m)
   ℤ[Fin n ] .fst  ℤ[Fin m ] .fst
ℤFinFunctFun {n = n} {m} f g x =
  sumFinℤ {n = n} λ y  ℤFinFunctGenerator f g y x

ℤFinFunct : {n m : } (f : Fin n  Fin m)
   AbGroupHom (ℤ[Fin n ]) (ℤ[Fin m ])
fst (ℤFinFunct {n = n} {m} f) = ℤFinFunctFun f
snd (ℤFinFunct {n = n} {m} f) =
  makeIsGroupHom λ g h
    funExt λ x  sumFinGenId _+_ 0
               y  ℤFinFunctGenerator f  x  g x + h x) y x)
               y  ℤFinFunctGenerator f g y x + ℤFinFunctGenerator f h y x)
              (funExt (lem g h x))
           sumFinGenHom _+_ (pos 0)  _  refl) +Comm +Assoc n _ _
  where
  lem : (g h : _) (x : _) (y : Fin n)
     ℤFinFunctGenerator f  x  g x + h x) y x
      ℤFinFunctGenerator f g y x + ℤFinFunctGenerator f h y x
  lem g h x y with (f y . fst ≟ᵗ x .fst)
  ... | lt _ = refl
  ... | eq _ = refl
  ... | gt _ = refl

-- Homs are equal if they agree on generators
agreeOnℤFinGenerator→≡ :  {n m : }
   {ϕ ψ : AbGroupHom (ℤ[Fin n ]) (ℤ[Fin m ])}
   ((x : _)  fst ϕ (ℤFinGenerator x)  fst ψ (ℤFinGenerator x))
   ϕ  ψ
agreeOnℤFinGenerator→≡ {n} {m} {ϕ} {ψ} idr =
  Σ≡Prop  _  isPropIsGroupHom _ _)
   (funExt
    (elimPropℤFin _ _  _  isOfHLevelPath' 1 (isSetΠ  _  isSetℤ)) _ _)
      (IsGroupHom.pres1 (snd ϕ)  sym (IsGroupHom.pres1 (snd ψ)))
      idr
       f g p q  IsGroupHom.pres· (snd ϕ) f g
                 ∙∙  i x  p i x + q i x)
                 ∙∙ sym (IsGroupHom.pres· (snd ψ) f g ))
      λ f p  IsGroupHom.presinv (snd ϕ) f
           ∙∙  i x  -ℤ (p i x))
           ∙∙ sym (IsGroupHom.presinv (snd ψ) f)))