#############################################################################
##
#W  standard/homomorph.tst
#Y  Copyright (C) 2022                               Artemis Konstantinidi
##                                                         Chinmaya Nagpal
##
##  Licensing information can be found in the README file of this package.
##
#############################################################################
##

## We don't use local variables in this test file because it doesn't play nice
## with something in this test file.

#
gap> START_TEST("Semigroups package: standard/attributes/homomorph.tst");
gap> LoadPackage("semigroups", false);;

#
gap> SEMIGROUPS.StartTest();

# helper functions
gap> BruteForceHomoCheck := function(homo)
>   local x, y;
>   for x in Generators(Source(homo)) do
>     for y in Generators(Source(homo)) do
>       if x ^ homo * y ^ homo <> (x * y) ^ homo then
>         return false;
>       fi;
>     od;
>   od;
>   return true;
> end;;
gap> BruteForceIsoCheck := function(iso)
>   local x, y;
>   if not IsInjective(iso) or not IsSurjective(iso) then
>     return false;
>   fi;
>   for x in Generators(Source(iso)) do
>     for y in Generators(Source(iso)) do
>       if x ^ iso * y ^ iso <> (x * y) ^ iso then
>         return false;
>       fi;
>     od;
>   od;
>   return true;
> end;;
gap> BruteForceInverseCheck := function(map)
> local inv;
>   inv := InverseGeneralMapping(map);
>   return ForAll(Source(map), x -> x = (x ^ map) ^ inv)
>     and ForAll(Range(map), x -> x = (x ^ inv) ^ map);
> end;;

# Test for every generator in the first list is in the first semigroup
gap> S := FullTransformationMonoid(3);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> y := [IdentityTransformation, Transformation([2, 3, 1]),
>       Transformation([2, 1]), Transformation([1, 1, 1, 1])];;
gap> SemigroupHomomorphismByImages(S, S, y, gens);
Error, the 3rd argument (a list) must consist of elements of the 1st argument \
(a semigroup)

# Test for every element in the second list in the second semigroup
gap> S := FullTransformationMonoid(3);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> y := [IdentityTransformation, Transformation([2, 3, 1]),
>       Transformation([2, 1]), Transformation([1, 1, 1, 1])];;
gap> SemigroupHomomorphismByImages(S, S, gens, y);
Error, the 4th argument (a list) must consist of elements of the 2nd argument \
(a semigroup)

# Test for the first list generating the first semigroup
gap> S := FullTransformationMonoid(3);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> z := [Transformation([2, 3, 1]), Transformation([2, 3, 1]),
>  Transformation([2, 1]), Transformation([2, 1])];;
gap> SemigroupHomomorphismByImages(S, S, z, gens);
Error, the 1st argument (a semigroup) is not generated by the 3rd argument (a \
list)

# Test for the two lists being the same size
gap> S := FullTransformationMonoid(3);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> j := [IdentityTransformation, Transformation([2, 3, 1]),
>  Transformation([2, 1]), Transformation([1, 2, 1]),
>  Transformation([1, 1])];;
gap> SemigroupHomomorphismByImages(S, S, j, gens);
Error, the 3rd argument (a list) and the 4th argument (a list) are not the sam\
e size

# Check that isomorphism work
gap> S := Semigroup([
>  Matrix(IsNTPMatrix, [[0, 1, 2], [4, 3, 0], [0, 2, 0]], 9, 4),
>  Matrix(IsNTPMatrix, [[1, 1, 0], [4, 1, 1], [0, 0, 0]], 9, 4)]);
<semigroup of 3x3 ntp matrices with 2 generators>
gap> T := AsSemigroup(IsTransformationSemigroup, S);
<transformation semigroup of size 46, degree 47 with 2 generators>
gap> SemigroupHomomorphismByImages(S, T, [2], GeneratorsOfSemigroup(T));
Error, the 3rd argument (a list) must consist of elements of the 1st argument \
(a semigroup)
gap> gens := [Matrix(IsNTPMatrix, [[0, 1, 2], [4, 3, 0], [0, 2, 0]], 9, 4)];;
gap> SemigroupHomomorphismByImages(S, T, gens, GeneratorsOfSemigroup(T));
Error, the 1st argument (a semigroup) is not generated by the 3rd argument (a \
list)
gap> gens := GeneratorsOfSemigroup(S);;
gap> SemigroupHomomorphismByImages(S, T, gens, [2]);
Error, the 4th argument (a list) must consist of elements of the 2nd argument \
(a semigroup)
gap> imgs := GeneratorsOfSemigroup(T);;
gap> SemigroupHomomorphismByImages(S, T, gens, [imgs[2]]);
Error, the 3rd argument (a list) and the 4th argument (a list) are not the sam\
e size
gap> gens := [gens[2], gens[1]];;
gap> imgs := [imgs[2], imgs[1]];;
gap> hom := SemigroupHomomorphismByImages(S, T, gens, imgs);
<semigroup of size 46, 3x3 ntp matrices with 2 generators> -> 
<transformation semigroup of size 46, degree 47 with 2 generators>
gap> ImageElm(hom, 2);
Error, the 2nd argument (a mult. elt.) is not an element of the source of the \
1st argument (semigroup homom. by images)
gap> PreImagesRepresentative(hom, 2);
Error, the 2nd argument is not an element of the range of the 1st argument (se\
migroup homom. by images)
gap> PreImagesElm(hom, 2);
Error, the 2nd argument is not an element of the range of the 1st argument (se\
migroup homom. by images)
gap> PreImagesSet(hom, [2]);
Error, no method found! For debugging hints type ?Recovery from NoMethodFound
Error, no 1st choice method found for `PreImagesSet' on 2 arguments
gap> IsSurjective(hom);
true
gap> IsInjective(hom);
true
gap> iso := SemigroupIsomorphismByImages(S, T);;
gap> BruteForceHomoCheck(iso);
true
gap> BruteForceInverseCheck(iso);
true
gap> Source(iso) = S;
true
gap> Range(iso) = T;
true
gap> S := Semigroup(IdentityTransformation);
<trivial transformation group of degree 0 with 1 generator>
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := Semigroup(PartialPerm([]));
<trivial partial perm group of rank 0 with 1 generator>
gap> imgs := GeneratorsOfSemigroup(T);;
gap> iso := SemigroupIsomorphismByImages(S, T, gens, imgs);;
gap> BruteForceHomoCheck(iso);
true
gap> BruteForceInverseCheck(iso);
true
gap> IsSurjective(iso);
true
gap> IsInjective(iso);
true
gap> Print(iso, "\n");
SemigroupIsomorphismByImages( Monoid( [ IdentityTransformation ] ), Monoid( [ \
PartialPerm( [  ], [  ] ) ] ), [ IdentityTransformation ], [ PartialPerm( [  ]\
, [  ] ) ] )
gap> EvalString(String(hom)) = hom;
true
gap> S := TrivialSemigroup();
<trivial transformation group of degree 0 with 1 generator>
gap> T := FullTransformationSemigroup(2);
<full transformation monoid of degree 2>
gap> SemigroupIsomorphismByImages(S, T, [S.1, S.1], [T.1, T.2]);
fail

# homomorph: SemigroupHomomorphismByImages, for infinite semigroup(s)
gap> S := FreeSemigroup(1);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := TrivialSemigroup();;
gap> imgs := GeneratorsOfSemigroup(T);;
gap> hom := SemigroupHomomorphismByImages(S, T, gens, imgs);
<free semigroup on the generators [ s1 ]> -> <trivial transformation group of 
  degree 0 with 1 generator>

# homomorph: SemigroupHomomorphismByImages, for trivial semigroups
gap> S := TrivialSemigroup(IsTransformationSemigroup);
<trivial transformation group of degree 0 with 1 generator>
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := TrivialSemigroup(IsBipartitionSemigroup);
<trivial block bijection group of degree 1 with 1 generator>
gap> imgs := GeneratorsOfSemigroup(T);;
gap> hom := SemigroupHomomorphismByImages(S, T, gens, imgs);;
gap> BruteForceHomoCheck(hom);
true
gap> Print(hom, "\n");
SemigroupHomomorphismByImages( Monoid( [ IdentityTransformation ] ), Monoid( [\
 Bipartition([ [ 1, -1 ] ]) ] ), [ IdentityTransformation ], [ Bipartition([ [\
 1, -1 ] ]) ] )
gap> EvalString(String(hom)) = hom;
true
gap> KernelOfSemigroupHomomorphism(hom);
<universal semigroup congruence over <trivial transformation group of 
 degree 0 with 1 generator>>

# homomorph: SemigroupHomomorphismByImages, for monogenic semigroups
gap> S := MonogenicSemigroup(IsTransformationSemigroup, 3, 2);
<commutative non-regular transformation semigroup of size 4, degree 5 with 1 
 generator>
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := MonogenicSemigroup(IsBipartitionSemigroup, 3, 2);
<commutative non-regular block bijection semigroup of size 4, degree 6 with 1 
 generator>
gap> imgs := GeneratorsOfSemigroup(T);;
gap> hom := SemigroupHomomorphismByImages(S, T);
<commutative non-regular transformation semigroup of size 4, degree 5 with 1 
  generator> -> <commutative non-regular block bijection semigroup of size 4, 
  degree 6 with 1 generator>
gap> BruteForceHomoCheck(hom);
true
gap> iso := SemigroupIsomorphismByImages(S, gens, imgs);
<commutative non-regular transformation semigroup of size 4, degree 5 with 1 
  generator> -> <commutative block bijection semigroup of size 4, degree 6 
  with 1 generator>
gap> BruteForceIsoCheck(iso);
true
gap> BruteForceInverseCheck(iso);
true
gap> map := x -> T.1 ^ Length(Factorization(S, x));;
gap> inv := x -> S.1 ^ Length(Factorization(T, x));;
gap> iso := SemigroupIsomorphismByFunction(S, T, map, inv);;
gap> Print(iso, "\n");
SemigroupIsomorphismByFunction( Semigroup( [ Transformation( [ 2, 1, 2, 3, 4 ]\
 ) ] ), Semigroup( [ Bipartition([ [ 1, -2 ], [ 2, -1 ], [ 3, 4, -3, -6 ], [ 5\
, -4 ], [ 6, -5 ] ]) ] ), function ( x ) return T.1 ^ Length( Factorization( S\
, x ) ); end, function ( x ) return S.1 ^ Length( Factorization( T, x ) ); end\
 )
gap> EvalString(String(iso)) = iso;
true

# homomorph: SemigroupHomomorphismByImages, for simple semigroups
gap> S := ReesMatrixSemigroup(SymmetricGroup(3), [[(), (1, 3, 2)],
>                                                 [(2, 3), (1, 2)],
>                                                 [(), (2, 3, 1)]]);
<Rees matrix semigroup 2x3 over Sym( [ 1 .. 3 ] )>
gap> gensS := GeneratorsOfSemigroup(S);;
gap> U := AsSemigroup(IsBipartitionSemigroup, S);
<bipartition semigroup of size 36, degree 37 with 4 generators>
gap> gensU := GeneratorsOfSemigroup(U);;
gap> V := AsSemigroup(IsTransformationSemigroup, S);
<transformation semigroup of size 36, degree 37 with 4 generators>
gap> gensV := GeneratorsOfSemigroup(V);;
gap> hom1 := SemigroupHomomorphismByImages(S, U, gensS, gensU);;
gap> BruteForceHomoCheck(hom);
true
gap> hom2 := SemigroupHomomorphismByImages(U, S, gensU, gensS);;
gap> BruteForceHomoCheck(hom);
true
gap> hom3 := SemigroupHomomorphismByImages(U, V, gensU, gensV);;
gap> BruteForceHomoCheck(hom);
true
gap> hom1 = hom2;
false
gap> hom1 = hom3;
false
gap> hom3 = hom2;
false
gap> iso := SemigroupIsomorphismByImages(U, V, gensV);;
gap> BruteForceIsoCheck(iso);
true
gap> BruteForceInverseCheck(iso);
true

# homomorph: SemigroupHomomorphismByImages, for 0-simple semigroups
gap> S := ReesZeroMatrixSemigroup(SymmetricGroup(3), [[(), (1, 3, 2)],
>                                                     [0, (1, 2)],
>                                                     [(), (2, 3, 1)]]);
<Rees 0-matrix semigroup 2x3 over Sym( [ 1 .. 3 ] )>
gap> gensS := GeneratorsOfSemigroup(S);;
gap> T := ReesZeroMatrixSemigroup(SymmetricGroup(3), [[(), ()],
>                                                     [(), ()],
>                                                     [(), 0]]);
<Rees 0-matrix semigroup 2x3 over Sym( [ 1 .. 3 ] )>
gap> gensT := GeneratorsOfSemigroup(T);;
gap> U := AsSemigroup(IsBipartitionSemigroup, S);
<bipartition semigroup of size 37, degree 38 with 5 generators>
gap> gensU := GeneratorsOfSemigroup(U);;
gap> V := AsSemigroup(IsTransformationSemigroup, S);
<transformation semigroup of size 37, degree 38 with 5 generators>
gap> gensV := GeneratorsOfSemigroup(V);;
gap> hom := SemigroupHomomorphismByImages(S, U, gensS, gensU);;
gap> BruteForceHomoCheck(hom);
true
gap> hom := SemigroupHomomorphismByImages(U, S, gensU, gensS);;
gap> BruteForceHomoCheck(hom);
true
gap> hom := SemigroupHomomorphismByImages(U, V, gensU, gensV);;
gap> BruteForceHomoCheck(hom);
true
gap> SemigroupHomomorphismByImages(U, T, gensU, gensT);
fail
gap> F := FreeSemigroup(1);;
gap> F := F / [[F.1 ^ 4, F.1]];;
gap> S := ReesZeroMatrixSemigroup(F, [[F.1]]);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := ReesZeroMatrixSemigroup(F, [[F.1 ^ 2]]);;
gap> map := IsomorphismSemigroups(S, T);;
gap> imgs := List(gens, x -> x ^ map);;
gap> hom := SemigroupHomomorphismByImages(S, T, gens, imgs);;
gap> BruteForceHomoCheck(hom);
true

# for monogenic semigroups
gap> S := MonogenicSemigroup(4, 5);;
gap> T := MonogenicSemigroup(20, 1);;
gap> imgs := GeneratorsOfSemigroup(T);;
gap> SemigroupHomomorphismByImages(S, T, imgs);
fail
gap> S := MonogenicSemigroup(1, 4);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := MonogenicSemigroup(2, 3);;
gap> imgs := GeneratorsOfSemigroup(T);;
gap> SemigroupHomomorphismByImages(S, gens, imgs);
fail
gap> S := MonogenicSemigroup(1, 4);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := Semigroup(Generators(S) ^ (1, 2));;
gap> imgs := GeneratorsOfSemigroup(T);;
gap> SemigroupHomomorphismByImages(S, T, imgs) <> fail;
true

# SemigroupHomomorphismByImages
gap> S := FullTransformationMonoid(3);
<full transformation monoid of degree 3>
gap> gens := GeneratorsOfSemigroup(S);;
gap> T := AsMonoid(IsPBRMonoid, S);
<pbr monoid of size 27, degree 3 with 3 generators>
gap> imgs := GeneratorsOfSemigroup(T);;
gap> hom := SemigroupHomomorphismByImages(S, gens, imgs);
<full transformation monoid of degree 3> -> 
<pbr monoid of degree 3 with 3 generators>
gap> BruteForceHomoCheck(hom);
true
gap> Print(hom, "\n");
SemigroupHomomorphismByImages( Monoid( [ Transformation( [ 2, 3, 1 ] ), Transf\
ormation( [ 2, 1 ] ), Transformation( [ 1, 2, 1 ] ) ] ), Monoid( [ PBR([ [ -2 \
], [ -3 ], [ -1 ] ], [ [ 3 ], [ 1 ], [ 2 ] ]), PBR([ [ -2 ], [ -1 ], [ -3 ] ],\
 [ [ 2 ], [ 1 ], [ 3 ] ]), PBR([ [ -1 ], [ -2 ], [ -1 ] ], [ [ 1, 3 ], [ 2 ], \
[  ] ]) ] ), [ IdentityTransformation, Transformation( [ 2, 3, 1 ] ), Transfor\
mation( [ 2, 1 ] ), Transformation( [ 1, 2, 1 ] ) ], [ PBR([ [ -1 ], [ -2 ], [\
 -3 ] ], [ [ 1 ], [ 2 ], [ 3 ] ]), PBR([ [ -2 ], [ -3 ], [ -1 ] ], [ [ 3 ], [ \
1 ], [ 2 ] ]), PBR([ [ -2 ], [ -1 ], [ -3 ] ], [ [ 2 ], [ 1 ], [ 3 ] ]), PBR([\
 [ -1 ], [ -2 ], [ -1 ] ], [ [ 1, 3 ], [ 2 ], [  ] ]) ] )
gap> EvalString(String(hom)) = hom;
true

# Tests with the same group
gap> S := FullTransformationMonoid(3);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> imgs := ListWithIdenticalEntries(4, ConstantTransformation(3, 1));;
gap> hom1 := SemigroupHomomorphismByImages(S, S, gens, imgs);;
gap> BruteForceHomoCheck(hom1);
true
gap> Source(hom1) = S;
true
gap> Range(hom1) = S;
true
gap> hom2 := SemigroupHomomorphismByImages(S, S, gens, gens);;
gap> BruteForceHomoCheck(hom2);
true
gap> Range(hom2) = S;
true
gap> Source(hom2) = S;
true
gap> ImagesSource(hom2) = S;
true
gap> map := hom2;;
gap> ForAll(S, x -> ForAll(S, y -> (x * y) ^ map = x ^ map * y ^ map));
true
gap> IsSurjective(hom2);
true
gap> IsInjective(hom2);
true
gap> IsBijective(hom2);
true
gap> AsSemigroupIsomorphismByFunction(hom2);
<full transformation monoid of degree 3> -> 
<full transformation monoid of degree 3>
gap> x := ConstantTransformation(2, 1);
Transformation( [ 1, 1 ] )
gap> x ^ map;
Transformation( [ 1, 1 ] )
gap> imgs2 := [gens[2], gens[2], gens[1], gens[3]];;
gap> hom3 := SemigroupHomomorphismByImages(S, S, gens, imgs2);
fail

# Tests with semigroups of different sizes, testing every single function
gap> S := FullTransformationMonoid(3);;
gap> gens := GeneratorsOfSemigroup(S);;
gap> J := FullTransformationMonoid(4);;
gap> imgs := ListWithIdenticalEntries(4, ConstantTransformation(3, 1));;
gap> hom := SemigroupHomomorphismByImages(S, J, gens, imgs);
<full transformation monoid of degree 3> -> 
<full transformation monoid of degree 4>
gap> BruteForceHomoCheck(hom);
true
gap> ImagesSource(hom);
<transformation semigroup of degree 3 with 4 generators>
gap> PreImagesElm(hom, Transformation([1, 1, 1]));
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImagesSet(hom, [Transformation([1, 1, 1])]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImagesRepresentative(hom, Transformation([1, 1, 1]));
IdentityTransformation
gap> PreImagesRepresentative(hom, Transformation([3, 3, 4, 3]));
fail
gap> PreImagesElm(hom, Transformation([3, 3, 4, 3]));
Error, the 2nd argument is not mapped to by the 1st argument (semigroup homom.\
 by images)
gap> IsSurjective(hom);
false
gap> IsInjective(hom);
false
gap> IsBijective(hom);
false
gap> IsTotal(hom);
true
gap> IsSingleValued(hom);
true
gap> IsMapping(hom);
true
gap> Range(hom);
<full transformation monoid of degree 4>
gap> Source(hom);
<full transformation monoid of degree 3>
gap> UnderlyingRelation(hom);
<object>
gap> ImagesSource(hom);
<trivial transformation group of degree 3 with 4 generators>
gap> ImagesElm(hom, gens[1]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> ImagesSet(hom, [gens[1]]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Image(hom);
<trivial transformation group of degree 3 with 4 generators>
gap> Image(hom, gens[1]);
Transformation( [ 1, 1, 1 ] )
gap> Image(hom, [gens[1]]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Images(hom);
<trivial transformation group of degree 3 with 4 generators>
gap> Images(hom, gens[1]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Images(hom, [gens[1]]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> PreImagesRange(hom);
<full transformation monoid of degree 3>

#
gap> PreImageElm(hom, imgs[1]);
Error, <map> must be injective and surjective
gap> PreImagesRepresentative(hom, imgs[1]);
IdentityTransformation
gap> PreImagesSet(hom, [imgs[1]]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImagesSet(hom, [Transformation([3, 3, 4, 3])]);
Error, the 2nd argument is not mapped to by the 1st argument (semigroup homom.\
 by images)
gap> PreImage(hom);
<full transformation monoid of degree 3>

#
gap> PreImage(hom, imgs[1]);
Error, <map> must be an injective and surjective mapping
gap> PreImage(hom, [imgs[1]]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImages(hom);
<full transformation monoid of degree 3>
gap> PreImages(hom, imgs[1]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImages(hom, [imgs[1]]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> hom := AsSemigroupHomomorphismByFunction(hom);
<full transformation monoid of degree 3> -> 
<full transformation monoid of degree 4>
gap> KernelOfSemigroupHomomorphism(hom);
<universal semigroup congruence over <full transformation monoid of degree 3>>
gap> Print(hom, "\n");
SemigroupHomomorphismByFunction( Monoid( [ Transformation( [ 2, 3, 1 ] ), Tran\
sformation( [ 2, 1 ] ), Transformation( [ 1, 2, 1 ] ) ] ), Monoid( [ Transform\
ation( [ 2, 3, 4, 1 ] ), Transformation( [ 2, 1 ] ), Transformation( [ 1, 2, 3\
, 1 ] ) ] ), function ( x ) return ImageElm( hom, x ); end )
gap> EvalString(String(hom)) = hom;
true
gap> IsSurjective(hom);
false
gap> IsInjective(hom);
false
gap> IsBijective(hom);
false
gap> IsTotal(hom);
true
gap> IsSingleValued(hom);
true
gap> IsMapping(hom);
true
gap> Range(hom);
<full transformation monoid of degree 4>
gap> Source(hom);
<full transformation monoid of degree 3>
gap> UnderlyingRelation(hom);
<object>
gap> ImagesSource(hom);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> ImagesElm(hom, gens[1]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> ImagesSet(hom, [gens[1]]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Image(hom);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Image(hom, gens[1]);
Transformation( [ 1, 1, 1 ] )
gap> Image(hom, [gens[1]]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Images(hom);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Images(hom, gens[1]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> Images(hom, [gens[1]]);
[ Transformation( [ 1, 1, 1 ] ) ]
gap> PreImagesRange(hom);
<full transformation monoid of degree 3>

# 
gap> PreImageElm(hom, imgs[1]);
Error, <map> must be injective and surjective
gap> PreImagesSet(hom, [imgs[1]]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImage(hom);
<full transformation monoid of degree 3>

# 
gap> PreImage(hom, imgs[1]);
Error, <map> must be an injective and surjective mapping
gap> PreImage(hom, [imgs[1]]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImages(hom);
<full transformation monoid of degree 3>
gap> PreImages(hom, imgs[1]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]
gap> PreImages(hom, [imgs[1]]);
[ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 1, 2 ] ), 
  Transformation( [ 1, 1 ] ), Transformation( [ 1, 2, 1 ] ), 
  Transformation( [ 1, 2, 2 ] ), IdentityTransformation, 
  Transformation( [ 1, 3, 1 ] ), Transformation( [ 1, 3, 2 ] ), 
  Transformation( [ 1, 3, 3 ] ), Transformation( [ 2, 1, 1 ] ), 
  Transformation( [ 2, 1, 2 ] ), Transformation( [ 2, 1 ] ), 
  Transformation( [ 2, 2, 1 ] ), Transformation( [ 2, 2, 2 ] ), 
  Transformation( [ 2, 2 ] ), Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 3, 2 ] ), Transformation( [ 2, 3, 3 ] ), 
  Transformation( [ 3, 1, 1 ] ), Transformation( [ 3, 1, 2 ] ), 
  Transformation( [ 3, 1, 3 ] ), Transformation( [ 3, 2, 1 ] ), 
  Transformation( [ 3, 2, 2 ] ), Transformation( [ 3, 2, 3 ] ), 
  Transformation( [ 3, 3, 1 ] ), Transformation( [ 3, 3, 2 ] ), 
  Transformation( [ 3, 3, 3 ] ) ]

# Tests with semigroups to the trivial semigroup
gap> T := TrivialSemigroup();;
gap> S := GLM(2, 2);;
gap> gens := GeneratorsOfSemigroup(S);
[ <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2> ]
gap> imgs := ListX(gens, x -> IdentityTransformation);
[ IdentityTransformation, IdentityTransformation, IdentityTransformation, 
  IdentityTransformation ]
gap> hom := SemigroupHomomorphismByImages(S, T, gens, imgs);
<general linear monoid 2x2 over GF(2)> -> <trivial transformation group of 
  degree 0 with 1 generator>
gap> BruteForceHomoCheck(hom);
true
gap> PreImagesElm(hom, IdentityTransformation);
[ <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2>, 
  <an immutable 2x2 matrix over GF2>, <an immutable 2x2 matrix over GF2> ]
gap> IsSurjective(hom);
true
gap> IsInjective(hom);
false
gap> S := FullTransformationMonoid(3);
<full transformation monoid of degree 3>
gap> gens := GeneratorsOfSemigroup(S);
[ IdentityTransformation, Transformation( [ 2, 3, 1 ] ), 
  Transformation( [ 2, 1 ] ), Transformation( [ 1, 2, 1 ] ) ]
gap> imgs := ListX(gens, x -> IdentityTransformation);
[ IdentityTransformation, IdentityTransformation, IdentityTransformation, 
  IdentityTransformation ]
gap> hom2 := SemigroupHomomorphismByImages(S, T, gens, imgs);
<full transformation monoid of degree 3> -> <trivial transformation group of 
  degree 0 with 1 generator>
gap> hom = hom2;
false

# Test with quotient semigroup
gap> S := Semigroup([Transformation([2, 1, 5, 1, 5]),
> Transformation([1, 1, 1, 5, 3]), Transformation([2, 5, 3, 5, 3])]);;
gap> cong := SemigroupCongruence(S, [[Transformation([1, 1, 1, 1, 1]), Transformation([1, 1, 1, 3, 3])]]);
<2-sided semigroup congruence over <transformation semigroup of degree 5 with 
 3 generators> with 1 generating pairs>
gap> T := S / cong;;
gap> gens := GeneratorsOfSemigroup(S);;
gap> images := List(gens, gen -> EquivalenceClassOfElement(cong, gen));;
gap> hom1 := SemigroupHomomorphismByImages_NC(S, T, gens, images);;
gap> BruteForceHomoCheck(hom1);
true
gap> gens[1] ^ hom1 = images[1];
true
gap> ImageElm(hom1, gens[1]) = images[1];
true
gap> IsSurjective(hom1);
true
gap> IsInjective(hom1);
false
gap> hom2 := hom1;;
gap> hom2 = hom1;
true
gap> gens2 := [gens[3], gens[1], gens[2]];;
gap> images2 := [images[3], images[1], images[2]];;
gap> hom2 := SemigroupHomomorphismByImages(Semigroup(gens2),
>       Semigroup(images2), gens2, images2);;
gap> BruteForceHomoCheck(hom2);
true
gap> hom1 = hom2;
true
gap> cong = KernelOfSemigroupHomomorphism(hom1);
true

# Test with transformation semigroup isomorphic to quotient semigroup above
gap> S := Semigroup([Transformation([2, 1, 5, 1, 5]),
>       Transformation([1, 1, 1, 5, 3]), Transformation([2, 5, 3, 5, 3])]);;
gap> congs := CongruencesOfSemigroup(S);;
gap> cong := congs[4];;
gap> T := S / cong;;
gap> gens := GeneratorsOfSemigroup(S);;
gap> images := List(gens, gen -> EquivalenceClassOfElement(cong, gen));;
gap> hom1 := SemigroupHomomorphismByImages_NC(S, T, gens, images);;
gap> BruteForceHomoCheck(hom1);
true
gap> map := IsomorphismTransformationSemigroup(ImagesSource(hom1));;
gap> K := Range(map);;
gap> images2 := GeneratorsOfSemigroup(K);;
gap> hom2 := SemigroupHomomorphismByImages(S, K, gens, images2);;
gap> BruteForceHomoCheck(hom2);
true
gap> ImagesSource(hom2) = K;
true
gap> PreImagesRange(hom2);
<transformation semigroup of size 59, degree 5 with 3 generators>
gap> PreImagesRepresentative(hom2, images2[2]);
Transformation( [ 1, 1, 1, 5, 3 ] )
gap> PreImagesSet(hom2, [images2[1]]) = [gens[1]];
true
gap> ImageElm(hom2, gens[1]) = images2[1];
true
gap> IsSurjective(hom2);
true
gap> IsInjective(hom2);
false
gap> IsBijective(hom2);
false
gap> gens3 := [gens[3], gens[1], gens[2]];;
gap> images3 := [images2[3], images2[1], images2[2]];;
gap> hom3 := SemigroupHomomorphismByImages(Semigroup(gens3),
>       Semigroup(images3), gens3, images3);;
gap> BruteForceHomoCheck(hom3);
true
gap> hom3 = hom2;
true
gap> EvalString(String(hom3)) = hom3;
true
gap> hom1bf := AsSemigroupHomomorphismByFunction(hom1);
<transformation semigroup of size 59, degree 5 with 3 generators> -> 
<quotient of <2-sided semigroup congruence over <transformation semigroup 
  of size 59, degree 5 with 3 generators> with 1 generating pairs>>
gap> KernelOfSemigroupHomomorphism(hom1bf);
<2-sided semigroup congruence over <transformation semigroup of size 59, 
 degree 5 with 3 generators> with 1 generating pairs>
gap> BruteForceHomoCheck(hom1bf);
true
gap> hom1bfbi := AsSemigroupHomomorphismByImages(hom1bf);
<transformation semigroup of size 59, degree 5 with 3 generators> -> 
<quotient of <2-sided semigroup congruence over <transformation semigroup 
  of size 59, degree 5 with 3 generators> with 1 generating pairs>>
gap> hom1bfbi = hom1;
true
gap> IsSemigroupHomomorphismByImages(hom1bf);
false
gap> IsSemigroupHomomorphismByFunction(hom1bf);
true
gap> IsSemigroupHomomorphismByImages(hom1);
true
gap> IsSemigroupHomomorphismByFunction(hom1);
false
gap> IsSurjective(hom1bf);
true

# Simple test for SHBF
gap> g := Semigroup([(1, 2, 3, 4), (1, 2)]);;
gap> h := Semigroup([(1, 2, 3), (1, 2)]);;
gap> hom := SemigroupHomomorphismByFunction(g, h,
> function(x) if SignPerm(x) = -1 then return (1, 2); else return ();fi;end);
<semigroup of size 24, with 2 generators> -> 
<semigroup of size 6, with 2 generators>
gap> ImagesSource(hom);
[ (), (1,2) ]
gap> Image(hom, (1, 2, 3, 4));
(1,2)
gap> hom := SemigroupIsomorphismByFunction(g, h, x -> (), IdFunc);
fail

# Test with quotient semigroup, but this time SHBF
gap> S := Semigroup([Transformation([2, 1, 5, 1, 5]),
>       Transformation([1, 1, 1, 5, 3]), Transformation([2, 5, 3, 5, 3])]);;
gap> congs := CongruencesOfSemigroup(S);;
gap> cong := congs[4];;
gap> T := S / cong;;
gap> gens := GeneratorsOfSemigroup(S);;
gap> imgs := List(gens, gen -> EquivalenceClassOfElement(cong, gen));;
gap> hom1 := SemigroupHomomorphismByFunctionNC(S, T, x ->
> EquivalenceClassOfElement(cong, x));;
gap> BruteForceHomoCheck(hom1);
true
gap> KernelOfSemigroupHomomorphism(hom1);
<2-sided semigroup congruence over <transformation semigroup of size 59, 
 degree 5 with 3 generators> with 1 generating pairs>
gap> gens[1] ^ hom1 = imgs[1];
true
gap> ImageElm(hom1, gens[1]) = imgs[1];
true
gap> IsSurjective(hom1);
true
gap> IsInjective(hom1);
false
gap> hom2 := hom1;;
gap> hom2 = hom1;
true
gap> IsSurjective(hom1);
true
gap> IsInjective(hom1);
false
gap> IsBijective(hom1);
false
gap> IsTotal(hom1);
true
gap> IsSingleValued(hom1);
true
gap> IsMapping(hom1);
true
gap> Range(hom1) = T;
true
gap> Source(hom1) = S;
true
gap> UnderlyingRelation(hom1);
<object>
gap> ImagesSource(hom1) = T;
true
gap> ImagesElm(hom1, gens[1]) = [imgs[1]];
true
gap> ImagesSet(hom1, [gens[1]]) = [imgs[1]];
true
gap> Image(hom1) = T;
true
gap> Image(hom1, gens[1]) = imgs[1];
true
gap> Image(hom1, [gens[1]]) = [imgs[1]];
true
gap> Images(hom1) = T;
true
gap> Images(hom1, gens[1]) = [imgs[1]];
true
gap> Images(hom1, [gens[1]]) = [imgs[1]];
true
gap> PreImagesRange(hom1) = S;
true

#
gap> PreImageElm(hom1, imgs[1]);
Error, <map> must be injective and surjective
gap> PreImagesRepresentative(hom1, imgs[1]);
Error, no default method for s.p. general mapping
gap> PreImagesSet(hom1, [imgs[1]]);
[ Transformation( [ 2, 1, 5, 1, 5 ] ) ]
gap> PreImage(hom1);
<transformation semigroup of size 59, degree 5 with 3 generators>

#
gap> PreImage(hom1, imgs[1]);
Error, <map> must be an injective and surjective mapping
gap> PreImage(hom1, [imgs[1]]);
[ Transformation( [ 2, 1, 5, 1, 5 ] ) ]
gap> PreImages(hom1);
<transformation semigroup of size 59, degree 5 with 3 generators>
gap> PreImages(hom1, imgs[1]);
[ Transformation( [ 2, 1, 5, 1, 5 ] ) ]
gap> PreImages(hom1, [imgs[1]]);
[ Transformation( [ 2, 1, 5, 1, 5 ] ) ]

# Test with jumbled generators
gap> S := Semigroup([Transformation([2, 1, 5, 1, 5]),
>  Transformation([1, 1, 1, 5, 3]),
>  Transformation([2, 5, 3, 5, 3])]);;
gap> gens1 := GeneratorsOfSemigroup(S);;
gap> cong := SemigroupCongruence(S,
> [[Transformation([1, 1, 1, 5, 3]), Transformation([2, 5, 3, 5, 3])],
>  [Transformation([1, 2, 5, 2, 5]), Transformation([2, 1, 5, 1, 5])]]);
<2-sided semigroup congruence over <transformation semigroup of degree 5 with 
 3 generators> with 2 generating pairs>
gap> T := S / cong;;
gap> images1 := List(gens1, gen -> EquivalenceClassOfElement(cong, gen));;
gap> hom1 := SemigroupHomomorphismByImages_NC(S, T, gens1, images1);;
gap> BruteForceHomoCheck(hom1);
true
gap> map := IsomorphismTransformationSemigroup(ImagesSource(hom1));;
gap> K := Range(map);;
gap> images2 := GeneratorsOfSemigroup(K);;
gap> hom2 := SemigroupHomomorphismByImages(S, K, gens1, images2);;
gap> BruteForceHomoCheck(hom1);
true
gap> gens1;
[ Transformation( [ 2, 1, 5, 1, 5 ] ), Transformation( [ 1, 1, 1, 5, 3 ] ), 
  Transformation( [ 2, 5, 3, 5, 3 ] ) ]
gap> gens2 := [gens1[3], gens1[1], gens1[2]];
[ Transformation( [ 2, 5, 3, 5, 3 ] ), Transformation( [ 2, 1, 5, 1, 5 ] ), 
  Transformation( [ 1, 1, 1, 5, 3 ] ) ]
gap> images2 := [images2[3], images2[1], images2[2]];;
gap> hom3 := SemigroupHomomorphismByImages(S, K, gens2, images2);;
gap> BruteForceHomoCheck(hom3);
true
gap> EvalString(String(hom3)) = hom3;
true

# Test with FP Semigroup with relations added
gap> S := AsSemigroup(IsFpSemigroup, Semigroup([Transformation([2, 1, 5, 1, 5]),
>       Transformation([1, 1, 1, 5, 3]), Transformation([2, 5, 3, 5, 3])]));;
gap> gens := GeneratorsOfSemigroup(S);;
gap> relations := [[gens[1], gens[2]]];;
gap> T := S / relations;;
gap> imgs := GeneratorsOfSemigroup(T);;
gap> hom := SemigroupHomomorphismByImages(S, T, gens, imgs);
<fp semigroup with 3 generators and 34 relations of length 225> -> 
<fp semigroup with 3 generators and 35 relations of length 227>
gap> gens[1] ^ hom;
s1
gap> gens[2] ^ hom;
s2
gap> KernelOfSemigroupHomomorphism(hom);
<universal semigroup congruence over <fp semigroup with 3 generators and 
  34 relations of length 225>>

# Test with huge transformation semigroup
gap> S := Semigroup(FullTransformationMonoid(9), rec(acting := true));;
gap> T := Semigroup(FullTransformationMonoid(1), rec(acting := true));;
gap> hom := SemigroupHomomorphismByImages(S, T, GeneratorsOfSemigroup(S), List(GeneratorsOfSemigroup(S), x -> T.1));
<transformation monoid of size 387420489, degree 9 with 3 generators> -> 
<trivial transformation group of degree 0 with 1 generator>

# Test for IsGeneratorsOfInverseSemigroup
gap> S := MonogenicSemigroup(IsTransformationSemigroup, 1, 3);
<transformation group of size 3, degree 3 with 1 generator>
gap> T := InverseSemigroup(MonogenicSemigroup(IsPartialPermSemigroup, 1, 3));
<partial perm group of rank 3 with 1 generator>
gap> hom := SemigroupHomomorphismByImages(S, T, [S.1 ^ 2], [T.1]);
<transformation group of size 3, degree 3 with 1 generator> -> 
<partial perm group of rank 3 with 1 generator>

# SemigroupIsomorphismByImages non-bijective homom.
gap> S := FullTransformationMonoid(3);;
gap> T := FullTransformationSemigroup(2);;
gap> SemigroupIsomorphismByImages(S, T, GeneratorsOfSemigroup(S),
> List([1 .. 4], x -> ConstantTransformation(2, 1)));
fail
gap> SemigroupIsomorphismByImagesNC(S, T, GeneratorsOfSemigroup(S),
> List([1 .. 4], x -> ConstantTransformation(2, 1)));
<full transformation monoid of degree 3> -> 
<full transformation monoid of degree 2>

# SemigroupHomomorphismByFunction non-homomorphism
gap> S := FullTransformationMonoid(3);;
gap> T := FullTransformationSemigroup(3);;
gap> SemigroupHomomorphismByFunction(S, T, x -> AsTransformation((1, 2, 3)));
fail

# SemigroupIsomorphismByFunction inverse not a homomorphism
gap> S := FullTransformationMonoid(3);;
gap> T := FullTransformationSemigroup(3);;
gap> SemigroupIsomorphismByFunction(S,
>                                   T,
>                                   x -> x,
>                                   x -> AsTransformation((1, 2, 3)));
fail

# SemigroupIsomorphismByFunction inverse not inverse
gap> S := FullTransformationMonoid(3);;
gap> T := FullTransformationSemigroup(3);;
gap> SemigroupIsomorphismByFunction(S,
>                                   T,
>                                   x -> x,
>                                   x -> x ^ (1, 2));
fail
gap> S := FullTransformationMonoid(3);;
gap> T := FullTransformationSemigroup(3);;
gap> SemigroupIsomorphismByFunction(S,
>                                   T,
>                                   x -> x ^ (1, 2),
>                                   x -> x);
fail

# InverseGeneralMapping for a semigroup homomorphism that happens to be an
# isomorphism
gap> S := Semigroup(PBR([[-2], [-2]], [[], [1, 2]]));
<commutative pbr semigroup of degree 2 with 1 generator>
gap> map := IsomorphismPartialPermSemigroup(S);
<trivial pbr group of degree 2 with 1 generator> -> 
<trivial partial perm group of rank 1 with 1 generator>
gap> IsBijective(map);
true
gap> BruteForceInverseCheck(map);
true

# ImagesRepresentative for semigroups homomorphism by images
gap> S := MonogenicSemigroup(IsTransformationSemigroup, 1, 3);
<transformation group of size 3, degree 3 with 1 generator>
gap> T := InverseSemigroup(MonogenicSemigroup(IsPartialPermSemigroup, 1, 3));
<partial perm group of rank 3 with 1 generator>
gap> hom := SemigroupHomomorphismByImages(S, T, [S.1 ^ 2], [T.1]);
<transformation group of size 3, degree 3 with 1 generator> -> 
<partial perm group of rank 3 with 1 generator>
gap> ImagesRepresentative(hom, S.1);
(1,3,2)
gap> ImagesRepresentative(hom, ConstantTransformation(3, 1));
Error, the 2nd argument is not an element of the source of the 1st argument (s\
emigroup homom. by images)

# KernelOfSemigroupHomomorphism for non-quotient semigroup
gap> S := Semigroup(Transformation([2, 1, 5, 1, 5]),
>                   Transformation([1, 1, 1, 5, 3]),
>                   Transformation([2, 5, 3, 5, 3]));;
gap> cong := SemigroupCongruence(S,
> [[Transformation([1, 1, 1, 1, 1]), Transformation([1, 1, 1, 3, 3])]]);
<2-sided semigroup congruence over <transformation semigroup of degree 5 with 
 3 generators> with 1 generating pairs>
gap> T := AsSemigroup(IsTransformationSemigroup, S / cong);;
gap> hom := SemigroupHomomorphismByImages(S, T);;
gap> KernelOfSemigroupHomomorphism(hom);
<2-sided semigroup congruence over <transformation semigroup of size 59, 
 degree 5 with 3 generators> with 1 generating pairs>
gap> KernelOfSemigroupHomomorphism(hom) = cong;
true

# Equality for semigroup homomorphisms
gap> S := Semigroup(Transformation([2, 1, 5, 1, 5]),
>                   Transformation([1, 1, 1, 5, 3]),
>                   Transformation([2, 5, 3, 5, 3]));;
gap> hom1 := SemigroupHomomorphismByImages(S, S, GeneratorsOfSemigroup(S),
> List(GeneratorsOfSemigroup(S), x -> Idempotents(S)[1]));;
gap> hom2 := SemigroupHomomorphismByImages(S, S, GeneratorsOfSemigroup(S),
> List(GeneratorsOfSemigroup(S), x -> Idempotents(S)[2]));;
gap> hom1 = hom2;
false

# Unbind local variables, auto-generated by etc/tst-unbind-local-vars.py
gap> Unbind(BruteForceHomoCheck);
gap> Unbind(BruteForceInverseCheck);
gap> Unbind(BruteForceIsoCheck);
gap> Unbind(F);
gap> Unbind(J);
gap> Unbind(K);
gap> Unbind(S);
gap> Unbind(T);
gap> Unbind(U);
gap> Unbind(V);
gap> Unbind(acting);
gap> Unbind(cong);
gap> Unbind(congs);
gap> Unbind(g);
gap> Unbind(gens);
gap> Unbind(gens1);
gap> Unbind(gens2);
gap> Unbind(gens3);
gap> Unbind(gensS);
gap> Unbind(gensT);
gap> Unbind(gensU);
gap> Unbind(gensV);
gap> Unbind(h);
gap> Unbind(hom);
gap> Unbind(hom1);
gap> Unbind(hom1bf);
gap> Unbind(hom1bfbi);
gap> Unbind(hom2);
gap> Unbind(hom3);
gap> Unbind(images);
gap> Unbind(images1);
gap> Unbind(images2);
gap> Unbind(images3);
gap> Unbind(imgs);
gap> Unbind(imgs2);
gap> Unbind(inv);
gap> Unbind(iso);
gap> Unbind(j);
gap> Unbind(map);
gap> Unbind(relations);
gap> Unbind(x);
gap> Unbind(y);
gap> Unbind(z);

#
gap> SEMIGROUPS.StopTest();
gap> STOP_TEST("Semigroups package: standard/attributes/homomorph.tst");
