diff --git a/doc/dual.xml b/doc/dual.xml
new file mode 100644
index 0000000000..7ca5ef39da
--- /dev/null
+++ b/doc/dual.xml
@@ -0,0 +1,135 @@
+#############################################################################
+##
+#W dual.xml
+#Y Copyright (C) 2018 Finn Smith
+##
+## Licensing information can be found in the README file of this package.
+##
+#############################################################################
+##
+
+<#GAPDoc Label="DualSemigroup">
+
+
+ The dual semigroup of the given semigroup.
+
+ The dual semigroup of a semigroup S is the
+ anti-isomorphic semigroup with the same underlying set as S,
+ where multiplication is reversed. This attribute returns a semigroup
+ isomorphic to the dual semigroup of S.
+
+ S := Semigroup([Transformation([1, 4, 3, 2, 2]),
+> Transformation([5, 4, 4, 1, 2])]);;
+gap> D := DualSemigroup(S);
+>
+gap> Size(S) = Size(D);
+true
+gap> NrDClasses(S) = NrDClasses(D);
+true]]>
+<#/GAPDoc>
+
+<#GAPDoc Label="AntiIsomorphismDualSemigroup">
+
+
+
+ An anti-isomorphism from S to the corresponding dual semigroup.
+
+
+ The dual semigroup of S mathematically has the same underlying
+ set as S, but is represented with a different set of elements in
+ &Semigroups;. This function returns a mapping which is an anti-isomorphism from
+ S to its dual.
+
+ S := PartitionMonoid(3);
+
+gap> map := AntiIsomorphismDualSemigroup(S);
+MappingByFunction( ,
+ >, function( x ) ... end, function( x ) ... end )
+gap> inv := InverseGeneralMapping(map);;
+gap> x := Bipartition([[1, -2], [2, -3], [3, -1]]);
+
+gap> y := Bipartition([[1], [2, -2], [3, -3], [-1]]);
+
+gap> (x ^ map) * (y ^ map) = (y * x) ^ map;
+true
+gap> x ^ map;
+<
+ in the dual semigroup>]]>
+<#/GAPDoc>
+
+<#GAPDoc Label="IsDualSemigroupElement">
+
+
+ Returns true if elt has the representation of a dual
+ semigroup element.
+
+ Elements of a dual semigroup obtained using
+ normally lie in this
+ category. The exception is elements obtained by applying
+ the map to elements already
+ in this category. That is, the elements of a semigroup lie in the
+ category if and only if the
+ elements of the corresponding dual semigroup do not.
+
+ S := SingularPartitionMonoid(4);;
+gap> D := DualSemigroup(S);;
+gap> s := GeneratorsOfSemigroup(S)[1];;
+gap> map := AntiIsomorphismDualSemigroup(S);;
+gap> t := s ^ map;
+<
+ in the dual semigroup>
+gap> IsDualSemigroupElement(t);
+true
+gap> inv := InverseGeneralMapping(map);;
+gap> x := t ^ inv;
+
+gap> IsDualSemigroupElement(x);
+false]]>
+<#/GAPDoc>
+
+<#GAPDoc Label="IsDualSemigroupRep">
+
+
+ Returns true if sgrp is represented as
+ a dual semigroup.
+
+ Semigroups created using
+ normally have this representation. The exception is semigroups
+ which are the dual of semigroups already lying in this category.
+ That is, a semigroup has the representation
+ if and only if the corresponding
+ dual semigroup does not.
+
+ S := Semigroup([Transformation([3, 5, 1, 1, 2]),
+> Transformation([1, 2, 4, 4, 3])]);
+
+gap> D := DualSemigroup(S);
+>
+gap> IsDualSemigroupRep(D);
+true
+gap> R := DualSemigroup(D);
+
+gap> IsDualSemigroupRep(R);
+false
+gap> R = S;
+true
+gap> T := Range(IsomorphismTransformationSemigroup(D));
+
+gap> IsDualSemigroupRep(T);
+false
+gap> x := Representative(D);
+
+gap> V := Semigroup(x);
+>
+gap> IsDualSemigroupRep(V);
+true]]>
+<#/GAPDoc>
diff --git a/doc/z-chap06.xml b/doc/z-chap06.xml
index e2d3f81fd4..4dab708e2e 100644
--- a/doc/z-chap06.xml
+++ b/doc/z-chap06.xml
@@ -360,6 +360,37 @@ true]]>
<#Include Label = "InverseSubsemigroupByProperty">
<#Include Label = "DirectProduct">
<#Include Label = "WreathProduct">
+
+
+
+
+ Dual semigroups
+ Given a semigroup (S, \cdot), we can consider the anti-isomorphic
+ semigroup (S, \ast) where we define:
+
+ s \ast t = t \cdot s for all s, t \in S.
+
+ This is called the dual semigroup of (S, \cdot).
+
+ The Green's relations of the dual semigroup D of S
+ are, appropriately, dual to those of S. Given &x; and &y; in S:
+
+
+ - &x; and &y; are &L;-related in D if and only if
+ they are &R;-related in S, and vice-versa,
+ - &x; and &y; are &H;-, &D;-, or &J;-related in D if and
+ only if they are &H;-, &D;-, or &J;-related in S respectively.
+
+
+
+ While mathematically a semigroup and its dual have the same underlying set,
+ in &Semigroups; the underlying sets are disjoint. This makes it reasonable
+ to speak of the dual semigroup D of S.
+
+ <#Include Label = "DualSemigroup">
+ <#Include Label = "IsDualSemigroupRep">
+ <#Include Label = "IsDualSemigroupElement">
+ <#Include Label = "AntiIsomorphismDualSemigroup">
diff --git a/gap/attributes/dual.gd b/gap/attributes/dual.gd
new file mode 100644
index 0000000000..4c2585a6bf
--- /dev/null
+++ b/gap/attributes/dual.gd
@@ -0,0 +1,28 @@
+#############################################################################
+##
+## dual.gd
+## Copyright (C) 2018 James D. Mitchell
+## Finn Smith
+##
+## Licensing information can be found in the README file of this package.
+##
+#############################################################################
+##
+
+DeclareCategory("IsDualSemigroupElement", IsAssociativeElement);
+DeclareCategoryCollections("IsDualSemigroupElement");
+DeclareAttribute("DualSemigroup", IsSemigroup);
+
+# Every semigroup is mathematically a dual semigroup
+# What we care about is whether it is represented as one
+DeclareRepresentation("IsDualSemigroupRep",
+ IsEnumerableSemigroupRep and
+ IsDualSemigroupElementCollection,
+ []);
+
+DeclareAttribute("DualSemigroupOfFamily", IsFamily);
+DeclareAttribute("AntiIsomorphismDualSemigroup", IsSemigroup);
+DeclareGlobalFunction("UnderlyingElementOfDualSemigroupElement");
+
+InstallTrueMethod(IsDualSemigroupRep,
+ IsSemigroup and IsDualSemigroupElementCollection);
diff --git a/gap/attributes/dual.gi b/gap/attributes/dual.gi
new file mode 100644
index 0000000000..95314f52e5
--- /dev/null
+++ b/gap/attributes/dual.gi
@@ -0,0 +1,210 @@
+#############################################################################
+##
+## dual.gi
+## Copyright (C) 2018 James D. Mitchell
+## Finn Smith
+##
+## Licensing information can be found in the README file of this package.
+##
+#############################################################################
+##
+## This file contains an implementation of dual semigroups. We only provide
+## enough functionality to allow dual semigroups to work as enumerable
+## semigroups. This is to avoid having to install versions of every function
+## in Semigroups specially for dual semigroup representations. In some cases
+## special functions would be faster.
+
+InstallMethod(DualSemigroup, "for a semigroup",
+[IsSemigroup],
+function(S)
+ local dual, fam, filts, map, type;
+
+ if IsDualSemigroupRep(S) then
+ if HasGeneratorsOfSemigroup(S) then
+ return Semigroup(List(GeneratorsOfSemigroup(S),
+ x -> UnderlyingElementOfDualSemigroupElement(x)));
+ fi;
+ ErrorNoReturn("Semigroups: DualSemigroup: \n",
+ "this dual semigroup cannot be constructed ",
+ "without knowing generators,");
+ fi;
+
+ fam := NewFamily("DualSemigroupElementsFamily", IsDualSemigroupElement);
+ dual := Objectify(NewType(CollectionsFamily(fam),
+ IsWholeFamily and
+ IsDualSemigroupRep and
+ IsAttributeStoringRep),
+ rec());
+
+ filts := IsDualSemigroupElement;
+ if IsMultiplicativeElementWithOne(Representative(S)) then
+ filts := filts and IsMultiplicativeElementWithOne;
+ fi;
+
+ type := NewType(fam, filts);
+ fam!.type := type;
+
+ SetDualSemigroupOfFamily(fam, dual);
+
+ SetElementsFamily(FamilyObj(dual), fam);
+ SetDualSemigroup(dual, S);
+
+ if HasIsFinite(S) then
+ SetIsFinite(dual, IsFinite(S));
+ fi;
+
+ if IsTransformationSemigroup(S) then
+ map := AntiIsomorphismDualSemigroup(dual);
+ SetAntiIsomorphismTransformationSemigroup(dual, map);
+ fi;
+
+ if HasGeneratorsOfSemigroup(S) then
+ SetGeneratorsOfSemigroup(dual,
+ List(GeneratorsOfSemigroup(S),
+ x -> SEMIGROUPS.DualSemigroupElementNC(dual,
+ x)));
+ fi;
+
+ if HasGeneratorsOfMonoid(S) then
+ SetGeneratorsOfMonoid(dual,
+ List(GeneratorsOfMonoid(S),
+ x -> SEMIGROUPS.DualSemigroupElementNC(dual,
+ x)));
+ fi;
+ return dual;
+end);
+
+SEMIGROUPS.DualSemigroupElementNC := function(S, s)
+ if not IsDualSemigroupElement(s) then
+ return Objectify(ElementsFamily(FamilyObj(S))!.type, [s]);
+ fi;
+ return s![1];
+end;
+
+InstallMethod(AntiIsomorphismDualSemigroup, "for a semigroup",
+[IsSemigroup],
+function(S)
+ local dual, inv, iso;
+
+ dual := DualSemigroup(S);
+ iso := function(x)
+ return SEMIGROUPS.DualSemigroupElementNC(dual, x);
+ end;
+
+ inv := function(x)
+ return SEMIGROUPS.DualSemigroupElementNC(S, x);
+ end;
+ return MappingByFunction(S, dual, iso, inv);
+end);
+
+InstallGlobalFunction(UnderlyingElementOfDualSemigroupElement,
+"for a dual semigroup element",
+function(s)
+ if not IsDualSemigroupElement(s) then
+ ErrorNoReturn("Semigroups: UnderlyingElementOfDualSemigroupElement: \n",
+ "the argument must be an element represented as a dual ",
+ "semigroup element,");
+ fi;
+ return s![1];
+end);
+
+################################################################################
+## Technical methods
+################################################################################
+
+InstallMethod(OneMutable, "for a dual semigroup element",
+[IsDualSemigroupElement and IsMultiplicativeElementWithOne],
+function(s)
+ local S, x;
+ S := DualSemigroupOfFamily(FamilyObj(s));
+ x := SEMIGROUPS.DualSemigroupElementNC(DualSemigroup(S), s);
+ return SEMIGROUPS.DualSemigroupElementNC(S, OneMutable(x));
+end);
+
+InstallMethod(MultiplicativeNeutralElement, "for a dual semigroup",
+[IsDualSemigroupRep],
+10, # add rank to beat enumeration methods
+function(S)
+ local m;
+ m := MultiplicativeNeutralElement(DualSemigroup(S));
+ if m <> fail then
+ return SEMIGROUPS.DualSemigroupElementNC(S, m);
+ fi;
+ return fail;
+end);
+
+InstallMethod(Representative, "for a dual semigroup",
+[IsDualSemigroupRep],
+function(S)
+ if HasGeneratorsOfSemigroup(S) then
+ return GeneratorsOfSemigroup(S)[1];
+ fi;
+ return SEMIGROUPS.DualSemigroupElementNC(S, Representative(DualSemigroup(S)));
+end);
+
+InstallMethod(Size, "for a dual semigroup",
+[IsDualSemigroupRep],
+10, # add rank to beat enumeration methods
+function(S)
+ return Size(DualSemigroup(S));
+end);
+
+InstallMethod(AsList, "for a dual semigroup",
+[IsDualSemigroupRep],
+10, # add rank to beat enumeration methods
+function(S)
+ return List(DualSemigroup(S), s -> SEMIGROUPS.DualSemigroupElementNC(S, s));
+end);
+
+InstallMethod(\*, "for dual semigroup elements",
+IsIdenticalObj,
+[IsDualSemigroupElement, IsDualSemigroupElement],
+function(x, y)
+ return Objectify(FamilyObj(x)!.type, [y![1] * x![1]]);
+end);
+
+InstallMethod(\=, "for dual semigroup elements",
+IsIdenticalObj,
+[IsDualSemigroupElement, IsDualSemigroupElement],
+function(x, y)
+ return x![1] = y![1];
+end);
+
+InstallMethod(\<, "for dual semigroup elements",
+IsIdenticalObj,
+[IsDualSemigroupElement, IsDualSemigroupElement],
+function(x, y)
+ return x![1] < y![1];
+end);
+
+InstallMethod(ViewObj, "for dual semigroup elements",
+[IsDualSemigroupElement], PrintObj);
+
+InstallMethod(PrintObj, "for dual semigroup elements",
+[IsDualSemigroupElement],
+function(x)
+ Print("<", ViewString(x![1]), " in the dual semigroup>");
+end);
+
+InstallMethod(ViewObj, "for a dual semigroup",
+[IsDualSemigroupRep], PrintObj);
+
+InstallMethod(PrintObj, "for a dual semigroup",
+[IsDualSemigroupRep],
+function(S)
+ Print("");
+end);
+
+InstallMethod(ChooseHashFunction, "for a dual semigroup element and int",
+[IsDualSemigroupElement, IsInt],
+function(x, data)
+ local H, hashfunc;
+
+ H := ChooseHashFunction(x![1], data);
+ hashfunc := function(a, b)
+ return H.func(a![1], b);
+ end;
+ return rec(func := hashfunc, data := H.data);
+end);
diff --git a/gap/tools/utils.gi b/gap/tools/utils.gi
index f80efd6815..1f800245a9 100644
--- a/gap/tools/utils.gi
+++ b/gap/tools/utils.gi
@@ -242,6 +242,7 @@ SEMIGROUPS.DocXMLFiles := ["../PackageInfo.g",
"congrees.xml",
"congrms.xml",
"conguniv.xml",
+ "dual.xml",
"display.xml",
"elements.xml",
"factor.xml",
diff --git a/init.g b/init.g
index 484b020db4..afd68288ad 100644
--- a/init.g
+++ b/init.g
@@ -97,12 +97,13 @@ ReadPackage("semigroups", "gap/tools/iterators.gd");
ReadPackage("semigroups", "gap/attributes/attr.gd");
ReadPackage("semigroups", "gap/attributes/attract.gd");
ReadPackage("semigroups", "gap/attributes/attrinv.gd");
+ReadPackage("semigroups", "gap/attributes/dual.gd");
ReadPackage("semigroups", "gap/attributes/factor.gd");
ReadPackage("semigroups", "gap/attributes/isomorph.gd");
+ReadPackage("semigroups", "gap/attributes/isorms.gd");
ReadPackage("semigroups", "gap/attributes/maximal.gd");
ReadPackage("semigroups", "gap/attributes/normalizer.gd");
ReadPackage("semigroups", "gap/attributes/properties.gd");
-ReadPackage("semigroups", "gap/attributes/isorms.gd");
ReadPackage("semigroups", "gap/congruences/congpairs.gd");
ReadPackage("semigroups", "gap/congruences/congrms.gd");
diff --git a/read.g b/read.g
index fc3c00a9a0..9a49a3b69e 100644
--- a/read.g
+++ b/read.g
@@ -73,12 +73,13 @@ ReadPackage("semigroups", "gap/attributes/attr.gi");
ReadPackage("semigroups", "gap/attributes/attract.gi");
ReadPackage("semigroups", "gap/attributes/attrfp.gi");
ReadPackage("semigroups", "gap/attributes/attrinv.gi");
+ReadPackage("semigroups", "gap/attributes/dual.gi");
ReadPackage("semigroups", "gap/attributes/factor.gi");
ReadPackage("semigroups", "gap/attributes/isomorph.gi");
+ReadPackage("semigroups", "gap/attributes/isorms.gi");
ReadPackage("semigroups", "gap/attributes/maximal.gi");
ReadPackage("semigroups", "gap/attributes/normalizer.gi");
ReadPackage("semigroups", "gap/attributes/properties.gi");
-ReadPackage("semigroups", "gap/attributes/isorms.gi");
ReadPackage("semigroups", "gap/congruences/congpairs.gi");
ReadPackage("semigroups", "gap/congruences/congrms.gi");
diff --git a/tst/standard/dual.tst b/tst/standard/dual.tst
new file mode 100644
index 0000000000..13fc01dc33
--- /dev/null
+++ b/tst/standard/dual.tst
@@ -0,0 +1,313 @@
+############################################################################
+##
+#W standard/dual.tst
+#Y Copyright (C) 2018 Finn Smith
+##
+## Licensing information can be found in the README file of this package.
+##
+#############################################################################
+##
+gap> START_TEST("Semigroups package: standard/dual.tst");
+gap> LoadPackage("semigroups", false);;
+
+#
+gap> SEMIGROUPS.StartTest();;
+
+# helper functions
+gap> BruteForceAntiIsoCheck := function(antiiso)
+> local x, y;
+> if not IsInjective(antiiso) or not IsSurjective(antiiso) then
+> return false;
+> fi;
+> for x in Generators(Source(antiiso)) do
+> for y in Generators(Source(antiiso)) do
+> if x ^ antiiso * y ^ antiiso <> (y * x) ^ antiiso 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;;
+
+#T# Creation of dual semigroups and elements - 1
+gap> S := Semigroup([Transformation([1, 3, 2]), Transformation([1, 4, 4, 2])]);
+
+gap> T := DualSemigroup(S);
+>
+gap> S := Semigroup([Transformation([1, 3, 2]), Transformation([1, 4, 4, 2])]);;
+gap> T := DualSemigroup(S);
+>
+gap> AsSSortedList(T);
+[ ,
+ ,
+ ,
+ ,
+ ,
+ ]
+gap> Size(T);
+6
+
+#T# Creation of dual semigroups and elements - 2
+gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]),
+> Transformation([5, 5, 6, 1, 4, 5]),
+> Transformation([5, 3, 1, 6, 4, 5])]);;
+gap> T := DualSemigroup(S);
+>
+gap> Size(T);
+385
+gap> Size(T) = Size(S);
+true
+gap> AsSortedList(T) = AsSortedList(List(S,
+> s -> SEMIGROUPS.DualSemigroupElementNC(T, s)));
+true
+gap> iso := AntiIsomorphismDualSemigroup(S);;
+gap> AsSortedList(T) = AsSortedList(List(S,
+> s -> s ^ iso));
+true
+
+#T# Creation of dual semigroups and elements - 3
+gap> S := FullTransformationMonoid(20);;
+gap> T := DualSemigroup(S);;
+gap> HasGeneratorsOfSemigroup(T);
+true
+gap> HasGeneratorsOfMonoid(T);
+true
+
+#T# DClasses of dual semigroups - 1
+gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]),
+> Transformation([5, 5, 6, 1, 4, 5]),
+> Transformation([5, 3, 1, 6, 4, 5])]);;
+gap> T := DualSemigroup(S);;
+gap> DClasses(T);
+[ >, >,
+ >, >,
+ >, >,
+ >, >,
+ >, >,
+ >, >,
+ >, >,
+ > ]
+gap> DS := AsSortedList(DClasses(T));;
+gap> for i in [1 .. Size(DS) - 1] do
+> if not DS[i] < DS[i + 1] then
+> Print("comparison failure");
+> fi;
+> od;
+
+#T# Green's classes of dual semigroups
+gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]),
+> Transformation([5, 5, 6, 1, 4, 5])]);;
+gap> T := DualSemigroup(S);;
+gap> iso := AntiIsomorphismDualSemigroup(T);;
+gap> ForAll(DClasses(T),
+> x -> AsSortedList(List(x, y -> y ^ iso)) =
+> AsSortedList(GreensDClassOfElement(S,
+> Representative(x) ^ iso)));
+true
+gap> ForAll(LClasses(T),
+> x -> AsSortedList(List(x, y -> y ^ iso)) =
+> AsSortedList(GreensRClassOfElement(S,
+> Representative(x) ^ iso)));
+true
+gap> ForAll(RClasses(T),
+> x -> AsSortedList(List(x, y -> y ^ iso)) =
+> AsSortedList(GreensLClassOfElement(S,
+> Representative(x) ^ iso)));
+true
+gap> ForAll(HClasses(T),
+> x -> AsSortedList(List(x, y -> y ^ iso)) =
+> AsSortedList(GreensHClassOfElement(S,
+> Representative(x) ^ iso)));
+true
+
+#T# Representatives
+gap> S := FullTransformationMonoid(20);;
+gap> T := DualSemigroup(S);;
+gap> Representative(T);
+
+
+#T# Size
+gap> S := FullTransformationMonoid(20);;
+gap> T := DualSemigroup(S);;
+gap> Size(T);
+104857600000000000000000000
+
+#T# AsList
+gap> S := FullTransformationMonoid(6);;
+gap> T := DualSemigroup(S);;
+gap> AsList(T);;
+
+#T# One and MultiplicativeNeutralElement - 1
+gap> S := FullBooleanMatMonoid(5);;
+gap> T := DualSemigroup(S);;
+gap> One(Representative(T));
+
+gap> One(Representative(T)) = MultiplicativeNeutralElement(T);
+true
+
+#T# One and MultiplicativeNeutralElement - 2
+gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]),
+> Transformation([5, 5, 6, 1, 4, 5])]);;
+gap> IsMonoidAsSemigroup(S);
+false
+gap> T := DualSemigroup(S);;
+gap> One(Representative(T));
+
+gap> MultiplicativeNeutralElement(T);
+fail
+
+#T# AntiIsomorphisms
+gap> S := FullTransformationMonoid(5);;
+gap> T := DualSemigroup(S);;
+gap> HasAntiIsomorphismTransformationSemigroup(T);
+true
+gap> Range(AntiIsomorphismTransformationSemigroup(T)) = S;
+true
+gap> antiso := AntiIsomorphismDualSemigroup(S);
+MappingByFunction( , >, function( x ) ... end, function( \
+x ) ... end )
+gap> inv := AntiIsomorphismTransformationSemigroup(T);
+MappingByFunction( \
+>, , function( x ) ... end, function( \
+x ) ... end )
+gap> ForAll(S, x -> (x ^ antiso) ^ inv = x);
+true
+gap> invantiso := InverseGeneralMapping(antiso);
+MappingByFunction( \
+>, , function( x ) ... end, function( \
+x ) ... end )
+gap> ForAll(S, x -> (x ^ antiso) ^ invantiso = x);
+true
+
+#T# AntiIsomorphism brute force checking - 1
+gap> S := FullTropicalMaxPlusMonoid(2, 4);;
+gap> antiiso := AntiIsomorphismDualSemigroup(S);;
+gap> BruteForceAntiIsoCheck(antiiso);
+true
+gap> BruteForceInverseCheck(antiiso);
+true
+
+#T# AntiIsomorphism brute force checking - 2
+gap> S := PlanarPartitionMonoid(3);;
+gap> antiiso := AntiIsomorphismDualSemigroup(S);;
+gap> BruteForceAntiIsoCheck(antiiso);
+true
+gap> BruteForceInverseCheck(antiiso);
+true
+
+#T# AntiIsomorphism brute force checking - 3
+gap> S := OrderEndomorphisms(6);;
+gap> antiiso := AntiIsomorphismDualSemigroup(S);;
+gap> BruteForceAntiIsoCheck(antiiso);
+true
+gap> BruteForceInverseCheck(antiiso);
+true
+
+#T# Subsemigroups of a dual semigroup
+gap> S := FullBooleanMatMonoid(4);
+
+gap> T := DualSemigroup(S);
+>
+gap> U := Semigroup(GeneratorsOfSemigroup(T){[3 .. 5]});
+>
+gap> AsList(U);
+[ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ]
+gap> Size(last);
+26
+gap> Size(DualSemigroup(U));
+26
+gap> V := DualSemigroup(U);
+
+gap> V = Semigroup(GeneratorsOfSemigroup(S){[3 .. 5]});
+true
+
+#T# UnderlyingElementOfDualSemigroupElement
+gap> S := SingularPartitionMonoid(4);;
+gap> D := DualSemigroup(S);;
+gap> d := Representative(D);
+<
+ in the dual semigroup>
+gap> UnderlyingElementOfDualSemigroupElement(d);
+
+gap> Representative(S);
+
+gap> UnderlyingElementOfDualSemigroupElement(S);
+Error, Semigroups: UnderlyingElementOfDualSemigroupElement:
+the argument must be an element represented as a dual semigroup element,
+gap> T := Semigroup(GeneratorsOfSemigroup(D){[1000 .. 2000]});
+>
+gap> UnderlyingElementOfDualSemigroupElement(Representative(T));
+
+
+#T# UnbindVariables
+gap> Unbind(antiso);
+gap> Unbind(inv);
+gap> Unbind(invantiso);
+gap> Unbind(i);
+gap> Unbind(U);
+gap> Unbind(V);
+gap> Unbind(S);
+gap> Unbind(T);
+
+#
+gap> SEMIGROUPS.StopTest();
+gap> STOP_TEST("Semigroups package: standard/dual.tst");