Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  tietze.gi   Sprache: unbekannt

 
Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

############################################################################
##
##  fp/tietze.gi
##  Copyright (C) 2021-2022                               Tom Conti-Leslie
##                                                              Ben Spiers
##
##  Licensing information can be found in the README file of this package.
##
############################################################################
##

########################################################################
# This file is organised as follows:
#
# 1.  Definition of the Semigroup Tietze (IsStzPresentation) object
# 2.  Viewing methods for IsStzPresentation objects
# 3.  Internal Tietze transformation functions
# 4.  User-accessible Tietze transformation functions (wrappers)
# 5.  Internal helper functions for word/relation manipulation etc.
# 6.  Internal auto-checkers and appliers for presentation simplifying
# 7.  SimplifyPresentation etc.
# 8.  Other
########################################################################

########################################################################
# 1. Definition of the Semigroup Tietze (IsStzPresentation) object
########################################################################

InstallMethod(StzPresentation, "for a finitely presented semigroup",
[IsFpSemigroup],
function(S)
  local gens, out, rels, type;

  type := NewType(NewFamily("StzFamily", IsStzPresentation),
                  IsStzPresentation and IsComponentObjectRep);

  rels := List(RelationsOfFpSemigroup(S),
              x -> [LetterRepAssocWord(x[1]), LetterRepAssocWord(x[2])]);
  gens := List(GeneratorsOfSemigroup(S), ViewString);

  out := rec(GeneratorsOfStzPresentation := gens,
             RelationsOfStzPresentation  := rels,
             UnreducedFpSemigroup        := S,
             TietzeForwardMap            := List(
               [1 .. Length(GeneratorsOfSemigroup(S))], x -> [x]),
             TietzeBackwardMap           := List(
               [1 .. Length(GeneratorsOfSemigroup(S))], x -> [x]),
             usedGens                    := Set(gens));

  return Objectify(type, out);
end);

InstallMethod(SetRelationsOfStzPresentation,
[IsStzPresentation, IsList],
function(stz, arg)
  if not ForAll(arg, IsList) or
      not ForAll(arg, x -> Length(x) = 2 and ForAll(x, IsList)) or
      not ForAll(arg, x -> ForAll(x, y -> ForAll(y, IsPosInt))) then
        ErrorNoReturn("parameter <arg> must be a list of pairs of words in ",
                      "LetterRep format");
  fi;
  stz!.RelationsOfStzPresentation := arg;
end);

InstallMethod(RelationsOfStzPresentation, [IsStzPresentation],
stz -> stz!.RelationsOfStzPresentation);

InstallMethod(UnreducedFpSemigroup, [IsStzPresentation],
stz -> stz!.UnreducedFpSemigroup);

InstallMethod(TietzeForwardMap, [IsStzPresentation],
stz -> stz!.TietzeForwardMap);

InstallMethod(TietzeBackwardMap, [IsStzPresentation],
stz -> stz!.TietzeBackwardMap);

InstallMethod(GeneratorsOfStzPresentation, [IsStzPresentation],
stz -> stz!.GeneratorsOfStzPresentation);

InstallMethod(SetGeneratorsOfStzPresentation, [IsStzPresentation, IsList],
function(stz, newGens)
  stz!.GeneratorsOfStzPresentation := newGens;
end);

InstallMethod(StzIsomorphism,
[IsStzPresentation],
function(stz)
  local source, range, forward_dict, forward_map, backward_dict, backward_map;
  source := UnreducedFpSemigroup(stz);
  range  := SEMIGROUPS.StzConvertObjToFpSemigroup(stz);

  # build forward map
  forward_dict := TietzeForwardMap(stz);
  forward_map  := function(word)
    local new_word;
    new_word := SEMIGROUPS.StzExpandWord(
                LetterRepAssocWord(UnderlyingElement(word)), forward_dict);
    return Product(new_word, x -> GeneratorsOfSemigroup(range)[x]);
  end;

  # build backward map
  backward_dict := TietzeBackwardMap(stz);
  backward_map  := function(word)
    local new_word;
    new_word := SEMIGROUPS.StzExpandWord(
                LetterRepAssocWord(UnderlyingElement(word)), backward_dict);
    return Product(new_word, x -> GeneratorsOfSemigroup(source)[x]);
  end;

  # TODO(later) are we okay to assume this is necessarily an isomorphism?
  return SemigroupIsomorphismByFunctionNC(source,
                                          range,
                                          forward_map,
                                          backward_map);
end);

InstallMethod(SetTietzeForwardMap,
"for an stz presentation and list",
[IsStzPresentation, IsList],
function(stz, newMaps)
    if not ForAll(newMaps, x -> IsList(x) and ForAll(x, IsPosInt)) then
      ErrorNoReturn("the 2nd argument <newMaps> must ",
                    "be a list of lists of positive integers");
    fi;
    stz!.TietzeForwardMap := newMaps;
end);

InstallMethod(SetTietzeBackwardMap,
"for an stz presentation and list",
[IsStzPresentation, IsList],
function(stz, newMaps)
    if not ForAll(newMaps, x -> IsList(x) and ForAll(x, IsPosInt)) then
      ErrorNoReturn("the 2nd argument <newMaps> must ",
                    "be a list of lists of positive integers");
    fi;
    stz!.TietzeBackwardMap := newMaps;
end);

InstallMethod(TietzeForwardMapReplaceSubword,
"for an stz presentation, list, and list",
[IsStzPresentation, IsList, IsList],
function(stz, subWord, newSubWord)
    local newMaps;
    newMaps := List(stz!.TietzeForwardMap,
                    x -> SEMIGROUPS.StzReplaceSubwordRel(x,
                                                         subWord,
                                                         newSubWord));
    stz!.TietzeForwardMap := newMaps;
end);

# Length of an StzPresentation is defined as the number of generators plus the
# length of every word in the defining relations
InstallMethod(Length, "for an stz presentation", [IsStzPresentation],
function(stz)
  local out, rel;
  out := Length(stz!.GeneratorsOfStzPresentation);
  for rel in RelationsOfStzPresentation(stz) do
    out := out + Length(rel[1]) + Length(rel[2]);
  od;
  return out;
end);

InstallMethod(\<, "for stz presentations",
[IsStzPresentation, IsStzPresentation],
{stz1, stz2} -> Length(stz1) < Length(stz2));

########################################################################
# 2. Viewing methods for IsStzPresentation objects
########################################################################

InstallMethod(ViewString,
[IsStzPresentation],
function(stz)
  local num_gens, num_rels;
  num_gens := Length(stz!.GeneratorsOfStzPresentation);
  num_rels := Length(stz!.RelationsOfStzPresentation);
  return Concatenation(
    StringFormatted("<fp semigroup presentation with {} and {}",
                    Pluralize(num_gens, "generator"),
                    Pluralize(num_rels, "relation")),
    StringFormatted("\< with length {}>", Length(stz)));
end);

SEMIGROUPS.StzRelationDisplayString := function(stz, i)
  local rels, f, gens, w1, w2;
  rels := RelationsOfStzPresentation(stz);
  if i > Length(rels) then
    return fail;
  else
    # We'd like patterns to be grouped, i.e. abab=(ab)^2 when displayed. To
    # do this we sneakily piggyback off display methods for the free semigroup.
    f    := FreeSemigroup(GeneratorsOfStzPresentation(stz));
    gens := GeneratorsOfSemigroup(f);
    w1   := Product(rels[i][1], x -> gens[x]);
    w2   := Product(rels[i][2], x -> gens[x]);
    return Concatenation(PrintString(i),
                         ". ",
                         PrintString(w1),
                         " = ",
                         PrintString(w2));
  fi;
end;

InstallMethod(StzPrintRelations,
"for an stz presentation and a list of pos ints",
[IsStzPresentation, IsList],
function(stz, list)
  local i;
  # This function displays the current relations in terms of the current
  # generators for a semigroup Tietze presentation.
  if IsEmpty(RelationsOfStzPresentation(stz)) then
    Info(InfoFpSemigroup,
         1,
         "There are no relations in the presentation <stz>");
  fi;

  # Print relations at each index of the list, unless incorrect index,
  # in which case skip.
  for i in list do
    if IsPosInt(i) and i <= Length(RelationsOfStzPresentation(stz)) then
      Info(InfoFpSemigroup, 1, SEMIGROUPS.StzRelationDisplayString(stz, i));
    fi;
  od;
end);

InstallMethod(StzPrintRelations, "for an stz presentation",
[IsStzPresentation],
function(stz)
  StzPrintRelations(stz, [1 .. Length(RelationsOfStzPresentation(stz))]);
end);

InstallMethod(StzPrintRelation, "for an stz presentation and a pos int",
[IsStzPresentation, IsPosInt],
function(stz, int)
  StzPrintRelations(stz, [int]);
end);

InstallMethod(StzPrintGenerators,
"for an stz presentation and a list of pos ints",
[IsStzPresentation, IsList],
function(stz, list)
  local flat, gens, out, rel, i;
  # This function displays a list of generators and number of occurrences
  # of each

  # warn if there are no generators in the list (not sure this could happen)
  if IsEmpty(GeneratorsOfStzPresentation(stz)) then
    Info(InfoFpSemigroup, 1,
         "There are no generators in the presentation <stz>");
  fi;

  # create flat flat list of relations to count occurrences
  flat := [];
  for rel in RelationsOfStzPresentation(stz) do
    Append(flat, rel[1]);
    Append(flat, rel[2]);
  od;

  # enumerate and count generators
  gens := GeneratorsOfStzPresentation(stz);
  for i in list do
    # only print if requested index is valid
    if IsPosInt(i) and i <= Length(gens) then
      out := Concatenation(PrintString(i),
                           ".  ",
                           gens[i],
                           "  ",
                           PrintString(Number(flat, x -> x = i)),
                           " occurrences");
      Info(InfoFpSemigroup, 1, out);
    fi;
  od;
end);

InstallMethod(StzPrintGenerators, "for an stz presentation",
[IsStzPresentation],
function(stz)
  StzPrintGenerators(stz, [1 .. Length(GeneratorsOfStzPresentation(stz))]);
end);

InstallMethod(StzPrintPresentation, "for an stz presentation",
[IsStzPresentation],
function(stz)
  local status, oldgens, oldfree, oldelms, newgens, newfree, newelms, w, i;
  # prints everything that is known about the presentation.

  # current generators
  Info(InfoFpSemigroup, 1, "Current generators:");
  StzPrintGenerators(stz);

  # current relations
  Info(InfoFpSemigroup, 1, "");
  Info(InfoFpSemigroup, 1, "Current relations:");
  StzPrintRelations(stz);

  # total number and total length
  Info(InfoFpSemigroup, 1, "");
  status := "There ";
  if Length(stz!.GeneratorsOfStzPresentation) = 1 then
    status := Concatenation(status, "is 1 generator");
  else
    status := Concatenation(status,
                            "are ",
                            PrintString(
                            Length(GeneratorsOfStzPresentation(stz))),
                            " generators");
  fi;
  status := Concatenation(status, " and ");
  if Length(stz!.RelationsOfStzPresentation) = 1 then
    status := Concatenation(status, "1 relation");
  else
    status := Concatenation(status,
               PrintString(Length(stz!.RelationsOfStzPresentation)),
               " relations");
  fi;
  status := Concatenation(status,
                          " of total length ",
                          PrintString(Length(stz)),
                          ".");
  Info(InfoFpSemigroup, 1, status);

  # build free semigroup of old gens for display
  oldgens := GeneratorsOfSemigroup(UnreducedFpSemigroup(stz));
  oldgens := List(oldgens, ViewString);
  oldfree := FreeSemigroup(oldgens);
  oldelms := GeneratorsOfSemigroup(oldfree);

  # build free semigroup of new gens for display
  newgens := GeneratorsOfStzPresentation(stz);
  newfree := FreeSemigroup(newgens);
  newelms := GeneratorsOfSemigroup(newfree);

  # old generators expressed using current ones
  Info(InfoFpSemigroup, 1, "");
  Info(InfoFpSemigroup, 1, "Generators of original fp semigroup expressed as");
  Info(InfoFpSemigroup, 1,
       "combinations of generators in current presentation:");
  for i in [1 .. Length(oldgens)] do
    w := Product(TietzeForwardMap(stz)[i], x -> newelms[x]);
    Info(InfoFpSemigroup, 1, Concatenation(PrintString(i),
                                       ". ",
                                       oldgens[i],
                                       " = ",
                                       PrintString(w)));
  od;

  # new generators expressed using old ones
  Info(InfoFpSemigroup, 1, "");
  Info(InfoFpSemigroup, 1, "Generators of current presentation expressed as");
  Info(InfoFpSemigroup, 1,
       "combinations of generators of original fp semigroup:");
  for i in [1 .. Length(newgens)] do
    w := Product(TietzeBackwardMap(stz)[i], x -> oldelms[x]);
    Info(InfoFpSemigroup, 1, Concatenation(PrintString(i),
                                       ". ",
                                       newgens[i],
                                       " = ",
                                       PrintString(w)));
  od;
end);

########################################################################
# 3. Internal Tietze transformation functions
########################################################################

# TIETZE TRANSFORMATION 1: INTERNAL: ADD REDUNDANT RELATION - NO CHECK
SEMIGROUPS.TietzeTransformation1 := function(stz, pair)
  local rels_copy;
  # Arguments:
  # - <stz> should be a Semigroup Tietze (IsStzPresentation) object.
  # - <pair> should be a list containing two LetterRep words.
  #
  # This function returns nothing, and modifies <stz> in place by adding a new
  # relation given by <pair>. This relation is assumed to be redundant based
  # on the relations already present in RelationsOfStzPresentation(<stz>)
  # (although this is not checked).
  #
  # WARNING: this is an internal function and performs only minimal argument
  # checks. Entering arguments in the wrong format may result in errors that
  # are difficult to interpret. Argument checks are carried out in the
  # analogous, documented function: StzAddRelation. However, these checks
  # may not terminate in some cases.

  # Add relation
  rels_copy := ShallowCopy(RelationsOfStzPresentation(stz));
  Add(rels_copy, pair);
  stz!.RelationsOfStzPresentation := rels_copy;
  return;
end;

# TIETZE TRANSFORMATION 2: INTERNAL: REMOVE REDUNDANT RELATION - NO CHECK
SEMIGROUPS.TietzeTransformation2 := function(stz, index)
  local rels_copy;
  # Arguments:
  # - <stz> should be a Semigroup Tietze (IsStzPresentation) object.
  # - <index> should be the index of the relation in
  #   RelationsOfStzPresentation(<stz>) that needs to be removed.
  #
  # This function returns nothing, and modifies <stz> in place by removing
  # relation number <index>, assumed to be redundant (though redundancy of
  # that relation is not checked).
  #
  # WARNING: this is an internal function and performs only minimal argument
  # checks. Entering arguments in the wrong format may result in errors that
  # are difficult to interpret. Argument checks are carried out in the
  # analogous, documented function: StzRemoveRelation. However, these checks
  # may not terminate in some cases.

  # Remove relation
  rels_copy := ShallowCopy(RelationsOfStzPresentation(stz));
  Remove(rels_copy, index);
  stz!.RelationsOfStzPresentation := rels_copy;
end;

# TIETZE TRANSFORMATION 3: INTERNAL: ADD NEW GENERATOR - NO CHECK
SEMIGROUPS.TietzeTransformation3 := function(stz, word, name)
  local new_gens, new_rels, back_word, new_maps, letter;
  # Arguments:
  # - <stz> should be a Semigroup Tietze (IsStzPresentation) object.
  # - <word> should be a LetterRep word.
  # - <name> should be a string, or fail.
  #
  # This function returns nothing, and modifies <stz> in place by adding a new
  # generator and the relation gen = <word>.
  # The name for the new generator is <name>, unless this argument is fail,
  # in which case the name is automatically generated using
  # SEMIGROUPS.NewGeneratorName.
  # This function also updates the Tietze backwards map so that the new
  # generator can be expressed as a word on the generators of the original
  # semigroup.
  #
  # WARNING: this is an internal function and performs only minimal argument
  # checks. Entering arguments in the wrong format may result in errors that
  # are difficult to interpret. Argument checks are carried out in the
  # analogous, documented function: StzAddGenerator.

  # Add new generator string to the list of gens in similar format
  new_gens := ShallowCopy(stz!.GeneratorsOfStzPresentation);
  new_rels := ShallowCopy(stz!.RelationsOfStzPresentation);
  if name = fail or name in stz!.GeneratorsOfStzPresentation then
    # either name was not specified, or it was but is already a generator.
    # generate a new generator name, as of yet unused.
    Add(new_gens, SEMIGROUPS.NewGeneratorName(List(stz!.usedGens)));
  else
    # this must mean the argument is a string as of yet unused, so add that
    Add(new_gens, ShallowCopy(name));
  fi;

  # in either case we have added a new generator: for cosmetic reasons,
  # prohibit that generator name from being auto-used again (in case we delete
  # it)
  UniteSet(stz!.usedGens, new_gens);

  # Add relation setting new gen equal to <word>
  Add(new_rels, [word, [Length(stz!.GeneratorsOfStzPresentation) + 1]]);

  # Update internal representation of the stz object
  SetGeneratorsOfStzPresentation(stz, new_gens);
  SetRelationsOfStzPresentation(stz, new_rels);

  # Now we need to update the backwards map to express the new generator in
  # terms of the original generators.
  back_word := [];
  new_maps  := ShallowCopy(TietzeBackwardMap(stz));
  for letter in word do
    Append(back_word, new_maps[letter]);
  od;
  Add(new_maps, back_word);
  SetTietzeBackwardMap(stz, new_maps);
end;

# TIETZE TRANSFORMATION 4: INTERNAL: REMOVE GENERATOR - MINIMAL CHECKS
SEMIGROUPS.TietzeTransformation4 := function(stz, gen, index)
  local found_expr, expr, decrement, tempMaps, tempRels, tempGens;
  # Arguments:
  # - <stz> should be a Semigroup Tietze (IsStzPresentation) object.
  # - <gen> should be a pos int.
  # - <index> should be a pos int.
  #
  # This function returns nothing, and modifies <stz> in place by removing
  # generator number <gen> using relation number <index>.
  # If relation number <index> of <stz> is not of the form
  # [<gen>] = *some word not including <gen>* (or that relation flipped),
  # then this function throws ErrorNoReturn.
  # This function also updates the Tietze forward map, so that any generator
  # in the original semigroup expressed as a combination of generators
  # including <gen> is now represented without using <gen> (since <gen>
  # has been removed).
  #
  # WARNING: this is an internal function and performs only minimal argument
  # checks. Entering arguments in the wrong format may result in errors that
  # are difficult to interpret. Argument checks are carried out in the
  # analogous, documented function: StzRemoveGenerator.

  # check we can express <gen> using only other generators with relation
  # number <index>
  found_expr := false;
  if RelationsOfStzPresentation(stz)[index][1] = [gen] and
      not gen in RelationsOfStzPresentation(stz)[index][2] then
    expr := RelationsOfStzPresentation(stz)[index][2];
    found_expr := true;
  elif RelationsOfStzPresentation(stz)[index][2] = [gen] and
      not gen in RelationsOfStzPresentation(stz)[index][1] then
    expr := RelationsOfStzPresentation(stz)[index][1];
    found_expr := true;
  fi;

  # check we found an expression for <gen>. If not, nothing can be done.
  if not found_expr then
    ErrorNoReturn("TietzeTransformation4, internal function: third argument\n",
                  "<index> does not point to a relation expressing second\n",
                  "argument <gen> as a combination of other generators");
  fi;

  # Define decrement function to bump down generator numbers past the one
  # we're going to remove
  decrement := function(z)
    if z <= gen then  # shouldn't be equal but just in case
      return z;
    else
      return z - 1;
    fi;
  end;

  # update forward mapping component
  # TODO(later) do we really need TietzeForwardMapReplaceSubword?
  TietzeForwardMapReplaceSubword(stz, [gen], expr);
  tempMaps := ShallowCopy(TietzeForwardMap(stz));
  Apply(tempMaps, x -> List(x, decrement));
  SetTietzeForwardMap(stz, tempMaps);

  # remove generator from backward mapping component
  tempMaps := ShallowCopy(TietzeBackwardMap(stz));
  Remove(tempMaps, gen);
  SetTietzeBackwardMap(stz, tempMaps);

  # sub in expression we found and remove relation we used for gen
  tempRels := ShallowCopy(RelationsOfStzPresentation(stz));
  Remove(tempRels, index);
  tempRels := SEMIGROUPS.StzReplaceSubword(tempRels, [gen], expr);
  SetRelationsOfStzPresentation(stz, tempRels);
  Apply(stz!.RelationsOfStzPresentation, x -> List(x, y -> List(y, decrement)));

  # remove generator.
  tempGens := ShallowCopy(GeneratorsOfStzPresentation(stz));
  Remove(tempGens, gen);
  SetGeneratorsOfStzPresentation(stz, tempGens);
end;

########################################################################
# 4. User-accessible Tietze transformation functions (wrappers)
########################################################################

# Tietze Transformation 1: Add relation
InstallMethod(StzAddRelation, "for an stz presentation and a pair of words",
[IsStzPresentation, IsList],
1,  # bump ahead of next implementation and do the list size arg check here
function(stz, pair)
  local n, f, free_fam, r, s, fp_fam, w1, w2, p1, p2, word, letter;
  # <pair> should be a pair of LetterRep words.

  # argument checks:
  if Length(pair) <> 2 then
    ErrorNoReturn("StzAddRelation: second argument <pair> should be a list\n",
                  "of length 2");
  fi;
  n := Length(stz!.GeneratorsOfStzPresentation);
  for word in pair do
    if not IsList(word) then
      TryNextMethod();  # pass this on to the case where the list may be a pair
                        # of words in OG semigroup
    elif IsEmpty(word) then
      ErrorNoReturn("StzAddRelation: words in second argument <pair> should\n",
                    "be non-empty");
    else
      for letter in word do
        if not (IsPosInt(letter) and letter <= n) then
          ErrorNoReturn("StzAddRelation: words in second argument <pair>\n",
                        "should be lists of pos ints no greater than the\n",
                        "number of generators of first argument <stz>");
        fi;
      od;
    fi;
  od;

  # check that the pair can be deduced from the other relations, by
  # creating fp semigroup with current relations.

  # base free semigroup
  f        := FreeSemigroup(stz!.GeneratorsOfStzPresentation);
  free_fam := FamilyObj(f.1);            # marrow for creating free semigp words
  r        := List(stz!.RelationsOfStzPresentation,
                   x -> List(x, y -> AssocWordByLetterRep(free_fam, y)));
  s        := f / r;                    # fp semigroup
  fp_fam   := FamilyObj(s.1);           # marrow for creating fp words
  # words first in free semigroup, then map to fp semigroup:
  w1       := AssocWordByLetterRep(free_fam, pair[1]);
  w2       := AssocWordByLetterRep(free_fam, pair[2]);
  p1       := ElementOfFpSemigroup(fp_fam, w1);
  p2       := ElementOfFpSemigroup(fp_fam, w2);
  # check if words are equal in the fp semigroup
  # WARNING: may run forever if undecidable
  if p1 = p2 then
    SEMIGROUPS.TietzeTransformation1(stz, pair);
    return;
  else
    ErrorNoReturn("StzAddRelation: second argument <pair> must list two\n",
                  "words that are equal in the presentation <stz>");
  fi;
end);

InstallMethod(StzAddRelation,
"for an stz presentation and a pair of semigroup elements",
[IsStzPresentation, IsList],
function(stz, pair)
  local s, pairwords, word;
  # retrieve original semigroup
  s := UnreducedFpSemigroup(stz);
  for word in pair do
    if not word in s then
      TryNextMethod();
    fi;
  od;

  # convert into words in original semigroup
  pairwords := List(pair, UnderlyingElement);
  Apply(pairwords, LetterRepAssocWord);
  # map to words in new semigroup
  Apply(pairwords, x -> SEMIGROUPS.StzExpandWord(x, TietzeForwardMap(stz)));

  # apply tietze1, with argument checks
  StzAddRelation(stz, pairwords);
  return;
end);

# Tietze Transformation 1: Add relation (NO REDUNDANCY CHECK)
InstallMethod(StzAddRelationNC, "for an stz presentation and a pair of words",
[IsStzPresentation, IsList],
1,  # bump priority, do list size check here
function(stz, pair)
  local n, word, letter;
  # <pair> should be a pair of LetterRep words.

  # argument checks:
  if Length(pair) <> 2 then
    ErrorNoReturn("StzAddRelationNC: second argument <pair> should be a list\n",
                  "of length 2");
  fi;
  n := Length(stz!.GeneratorsOfStzPresentation);
  for word in pair do
    if not IsList(word) then
      TryNextMethod();  # pass this on to the case where the list may be a pair
                        # of words in OG semigroup
    elif IsEmpty(word) then
      ErrorNoReturn("StzAddRelationNC: words in second argument <pair>\n",
                    "should be non-empty");
    else
      for letter in word do
        if not (IsPosInt(letter) and letter <= n) then
          ErrorNoReturn("StzAddRelationNC: words in second argument <pair>\n",
                        "should be lists of pos ints no greater than the\n",
                        "number of generators of first argument <stz>");
        fi;
      od;
    fi;
  od;

  # WARNING: no checks are run to verify that the pair is redundant. This
  # may result in an output semigroup which is non-isomorphic to the
  # starting semigroup.
  SEMIGROUPS.TietzeTransformation1(stz, pair);
  return;
end);

InstallMethod(StzAddRelationNC,
"for an stz presentation and a pair of semigroup elements",
[IsStzPresentation, IsList],
function(stz, pair)
  local s, pairwords, word;
  # retrieve original semigroup
  s := UnreducedFpSemigroup(stz);
  for word in pair do
    if not word in s then
      TryNextMethod();
    fi;
  od;

  # convert into words in original semigroup
  pairwords := List(pair, UnderlyingElement);
  Apply(pairwords, LetterRepAssocWord);
  # map to words in new semigroup
  Apply(pairwords, x -> SEMIGROUPS.StzExpandWord(x, TietzeForwardMap(stz)));

  # apply tietze1, without argument checks
  # WARNING: no checks are run to verify that the pair is redundant. This
  # may result in an output semigroup which is non-isomorphic to the
  # starting semigroup.
  SEMIGROUPS.TietzeTransformation1(stz, pairwords);
  return;
end);

# Tietze Transformation 2: Remove relation
InstallMethod(StzRemoveRelation,
"for an stz presentation and a pos int",
[IsStzPresentation, IsPosInt],
function(stz, index)
  local rels, pair, new_f, new_gens, new_s, free_fam, w1, w2, fp_fam, p1, p2;
  # <index> should be the index of the relation needing removing in the
  # overall list of relations.

  # argument check: valid index
  rels := ShallowCopy(stz!.RelationsOfStzPresentation);
  if index > Length(rels) then
    ErrorNoReturn("StzRemoveRelation: second argument <index> must be less\n",
                  "than or equal to the number of relations of the first\n",
                  "argument <stz>");
  fi;

  # create hypothetical fp semigroup that would be the result of removing
  # the requested pair
  pair := rels[index];
  Remove(rels, index);
  new_f    := FreeSemigroup(stz!.GeneratorsOfStzPresentation);
  new_gens := GeneratorsOfSemigroup(new_f);
  new_s    := new_f / List(rels,
                           x -> List(x,
                                     y -> Product(List(y,
                                                       z -> new_gens[z]))));

  # create two associative words
  free_fam := FamilyObj(new_f.1);
  w1       := AssocWordByLetterRep(free_fam, pair[1]);
  w2       := AssocWordByLetterRep(free_fam, pair[2]);

  # map these words to hypothetical fp semigroup words and check equality
  fp_fam := FamilyObj(new_s.1);
  p1     := ElementOfFpSemigroup(fp_fam, w1);
  p2     := ElementOfFpSemigroup(fp_fam, w2);

  # WARNING: may run forever if undecidable
  if p1 = p2 then
    SEMIGROUPS.TietzeTransformation2(stz, index);
    return;
  else
    ErrorNoReturn("StzRemoveRelation: second argument <index> must point to\n",
                  "a relation that is redundant in the presentation <stz>");
  fi;
end);

# Tietze Transformation 2: Remove relation (NO REDUNDANCY CHECK)
InstallMethod(StzRemoveRelationNC,
"for an stz presentation and a pos int",
[IsStzPresentation, IsPosInt],
function(stz, index)
  if index > Length(RelationsOfStzPresentation(stz)) then
    ErrorNoReturn("StzRemoveRelationNC: second argument <index> must be less\n",
                  "than or equal to the number of relations of the first\n",
                  "argument <stz>");
  fi;

  # WARNING: no checks are run to verify that the pair is redundant. This
  # may result in an output semigroup which is non-isomorphic to the
  # starting semigroup.
  SEMIGROUPS.TietzeTransformation2(stz, index);
  return;
end);

# Tietze Transformation 3: Add generator
InstallMethod(StzAddGenerator,
"for an stz presentation and a LetterRep word",
[IsStzPresentation, IsList],
function(stz, word)
  local n, letter;
  # argument checks
  if IsEmpty(word) then
    ErrorNoReturn("StzAddGenerator: cannot add generator equal to the empty\n",
                  "word");
  fi;
  n := Length(GeneratorsOfStzPresentation(stz));
  for letter in word do
    if not (IsPosInt(letter) and letter <= n) then
      # the argument has not been entered as a list of pos ints. Pass off to
      # potential future methods for Tietze3, but this is likely to be a
      # mistake.
      ErrorNoReturn("StzAddGenerator: second argument <word> is not a\n",
                    "list of pos ints at most equal to the number of\n",
                    "generators of the first argument <stz>");
    fi;
  od;

  # at this point all is good, so request new generator to be added with
  # a new generator name auto-created.
  SEMIGROUPS.TietzeTransformation3(stz, word, fail);
end);

InstallMethod(StzAddGenerator,
"for an stz presentation and a fp semigroup element",
[IsStzPresentation, IsElementOfFpSemigroup],
function(stz, word)
  local letterrepword;
  # argument check: word should be an element of the unreduced semigroup
  # (that way we can express it as a word on the current tietze generators)
  if not word in UnreducedFpSemigroup(stz) then
    TryNextMethod();
  fi;

  # if we do have an element of s, use the forward map to express it as a word
  # on the current generators, then run the original implementation of Tietze 3.
  letterrepword := SEMIGROUPS.StzExpandWord(
                     LetterRepAssocWord(UnderlyingElement(word)),
                     TietzeForwardMap(stz));
  StzAddGenerator(stz, letterrepword);
end);

# Tietze Transformation 3: Add generator (with specified new generator name)
InstallMethod(StzAddGenerator,
"for an stz presentation, a LetterRep word and a string",
[IsStzPresentation, IsList, IsString],
function(stz, word, name)
  local n, letter;
  # argument check 0: new word is non-empty
  if IsEmpty(word) then
    ErrorNoReturn("StzAddGenerator: cannot add generator equal to the empty\n",
                  "word");
  fi;
  # argument check 1: word is a valid word
  n := Length(GeneratorsOfStzPresentation(stz));
  for letter in word do
    if not (IsPosInt(letter) and letter <= n) then
      # the argument has not been entered as a list of pos ints. Pass off to
      # potential future methods for Tietze3, but this is likely to be a
      # mistake.
      ErrorNoReturn("StzAddGenerator: second argument <word> is not a\n",
                    "list of pos ints at most equal to the number of\n",
                    "generators of the first argument <stz>");
    fi;
  od;

  # argument check 2: requested generator name not yet used
  if name in GeneratorsOfStzPresentation(stz) then
    ErrorNoReturn("StzAddGenerator: third argument <name> should not be the\n",
                  "name of a pre-existing generator");
  fi;

  # otherwise we are all good
  SEMIGROUPS.TietzeTransformation3(stz, word, name);
end);

InstallMethod(StzAddGenerator,
"for an stz presentation, a fp semigroup element and a string",
[IsStzPresentation, IsElementOfFpSemigroup, IsString],
function(stz, word, name)
  local letterrepword;
  # argument check: word should be an element of the unreduced semigroup
  # (that way we can express it as a word on the current tietze generators)
  if not word in UnreducedFpSemigroup(stz) then
    TryNextMethod();
  fi;

  # if we do have an element of s, use the forward map to express it as a word
  # on the current generators, then run the original implementation of Tietze 3.
  letterrepword := SEMIGROUPS.StzExpandWord(
                     LetterRepAssocWord(UnderlyingElement(word)),
                     TietzeForwardMap(stz));
  StzAddGenerator(stz, letterrepword, name);
end);

# Tietze Transformation 4: Remove generator
InstallMethod(StzRemoveGenerator,
"for an stz presentation and a positive integer",
[IsStzPresentation, IsPosInt],
function(stz, gen)
  local found, index, i, rels;
  # argument check 1: requested removal is potentially possible
  if Length(GeneratorsOfStzPresentation(stz)) = 1 then
    ErrorNoReturn("StzRemoveGenerator: cannot remove only remaining\n",
                  "generator \"",
                  GeneratorsOfStzPresentation(stz)[1],
                  "\"");
  elif gen > Length(GeneratorsOfStzPresentation(stz)) then
    ErrorNoReturn("StzRemoveGenerator: second argument <gen> must be no\n",
                  "greater than the total number of generators");
  fi;

  # argument check 2: generator can be expressed as a product of others.
  # this check has to be repeated inside Tietze4, but we include it here to
  # ensure that an incorrect input is clearly reported to the user.
  found := false;
  rels  := RelationsOfStzPresentation(stz);
  for i in [1 .. Length(rels)] do
    if  (rels[i][1] = [gen] and not gen in rels[i][2]) or
        (rels[i][2] = [gen] and not gen in rels[i][1]) then
      found := true;
      index := i;
      continue;
    fi;
  od;
  if not found then
    ErrorNoReturn("StzRemoveGenerator: there is no relation in first\n",
                  "argument <stz> expressing second argument <gen> as a\n",
                  "product of other generators");
  fi;

  # otherwise all good; apply internal Tietze 4 function (it will check
  # whether any relation can actually express that generator as a combination
  # of others)
  SEMIGROUPS.TietzeTransformation4(stz, gen, index);
end);

InstallMethod(StzRemoveGenerator,
"for an stz presentation and a generator name",
[IsStzPresentation, IsString],
function(stz, genname)
  local gen;
  # find index of genname in stz gens
  gen := Position(GeneratorsOfStzPresentation(stz), genname);
  if gen = fail then
    ErrorNoReturn("StzRemoveGenerator: second argument <gen> does not\n",
                  "correspond to a generator name in first argument <stz>");
  else
    StzRemoveGenerator(stz, gen);
  fi;
end);

# Tietze transformation 4 with specified index (e.g. if there are several
# relations allowing the generator to be removed and the user wants a specific
# one)
InstallMethod(StzRemoveGenerator,
"for an stz presentation and two pos ints",
[IsStzPresentation, IsPosInt, IsPosInt],
function(stz, gen, index)
  # first argument check: requested generator exists
  if Length(GeneratorsOfStzPresentation(stz)) = 1 then
    ErrorNoReturn("StzRemoveGenerator: cannot remove only remaining\n",
                  "generator \"",
                  GeneratorsOfStzPresentation(stz)[1],
                  "\"");
  elif gen > Length(GeneratorsOfStzPresentation(stz)) then
    ErrorNoReturn("StzRemoveGenerator: second argument <gen> must be no\n",
                  "greater than the total number of generators");
  fi;

  # second argument check: index exists
  if index > Length(RelationsOfStzPresentation(stz)) then
    ErrorNoReturn("StzRemoveGenerator: third argument <index> must be no\n",
                  "greater than the total number of relations in first\n",
                  "argument <stz>");
  fi;

  # third argument check: a reasonable relation number has been supplied
  if (RelationsOfStzPresentation(stz)[index][1] <> [gen]
      or gen in RelationsOfStzPresentation(stz)[index][2])
      and (RelationsOfStzPresentation(stz)[index][2] <> [gen]
      or gen in RelationsOfStzPresentation(stz)[index][1]) then
    ErrorNoReturn("StzRemoveGenerator: third argument <index> does not point\n",
                  "to a relation expressing second argument <gen> as a\n",
                  "combination of other generators in first argument <stz>");
  fi;

  # if we made it this far then the substitution can be made
  SEMIGROUPS.TietzeTransformation4(stz, gen, index);
end);

InstallMethod(StzRemoveGenerator,
"for an stz presentation, a generator name and a pos int",
[IsStzPresentation, IsString, IsPosInt],
function(stz, genname, index)
  local gen;
  # find index of genname in stz gens
  gen := Position(GeneratorsOfStzPresentation(stz), genname);
  if gen = fail then
    ErrorNoReturn("StzRemoveGenerator: second argument <gen> does not\n",
                  "correspond to a generator name in first argument <stz>");
  else
    StzRemoveGenerator(stz, gen, index);
  fi;
end);

# Tietze Transformation 1/2: substitute relation
InstallMethod(StzSubstituteRelation,
"for an stz presentation and two positive integers",
[IsStzPresentation, IsPosInt, IsPosInt],
function(stz, index, side)
  local oldword, newword, new_rel, i;
  # argument check
  if index > Length(RelationsOfStzPresentation(stz)) then
    ErrorNoReturn("StzSubstituteRelation: second argument <index> must be no\n",
                  "greater than the number of relations in first argument\n",
                  "<stz>");
  fi;
  if not side in [1, 2] then
    ErrorNoReturn("StzSubstituteRelation: third argument <side> must be\n",
                  "either 1 or 2");
  fi;

  oldword := ShallowCopy(RelationsOfStzPresentation(stz)[index][side]);
  newword := ShallowCopy(RelationsOfStzPresentation(stz)[index][[2, 1][side]]);

  # Push relations onto the end of the stack of relations, each time replacing
  # the relation at the front by that relation, with oldword -> newword.
  # Each time, immediately delete the replaced relation at the front of the
  # stack, and do this as many times as there are relations, so that order
  # is preserved.
  # Special case for relation number <index> itself: it should not be changed.
  for i in [1 .. Length(RelationsOfStzPresentation(stz))] do
    if i = index then
      new_rel := [oldword, newword];
    else
      new_rel := List(stz!.RelationsOfStzPresentation[1],
                      x -> SEMIGROUPS.StzReplaceSubwordRel(x,
                                                           oldword,
                                                           newword));
    fi;
    SEMIGROUPS.TietzeTransformation1(stz, new_rel);
    SEMIGROUPS.TietzeTransformation2(stz, 1);
  od;
end);

########################################################################
# 5. Internal helper functions for word/relation manipulation etc.
########################################################################

SEMIGROUPS.StzReplaceSubword := function(rels, subword, newWord)
  local newRels, rel1, rel2, i;

  newRels := List([1 .. Length(rels)], x -> []);
  for i in [1 .. Length(rels)] do
    rel1 := SEMIGROUPS.StzReplaceSubwordRel(rels[i][1], subword, newWord);
    rel2 := SEMIGROUPS.StzReplaceSubwordRel(rels[i][2], subword, newWord);
    newRels[i] := [rel1, rel2];
  od;
  return newRels;
end;

SEMIGROUPS.StzReplaceSubwordRel := function(word, subword, newWord)
  local out, k, l, i;
  # Searches a single LetterRepAssocWord list and replaces instances of
  # subword with newWord.

  # build word up as we read through the old word.
  out := [];
  k   := Length(subword);
  l   := Length(word);
  i   := 1;  # current index that we are looking at when trying to see subword
  while i <= l do
    if word{[i .. Minimum(i + k - 1, l)]} = subword then
      # in this, case the word starting at i needs to be substituted out.
      # (the minimum is there to make sure we don't fall off the end of word)
      Append(out, newWord);
      # jump to end of occurrence
      i := i + k;
    else
      # move over by one and append the original letter, since the word is not
      # seen here.
      Add(out, word[i]);
      i := i + 1;
    fi;
  od;
  return out;
end;

# takes in a letterrep word and replaces every letter with its expression in
# dict.
# NOTE: does not check arguments. Assumes in good faith that every integer
# in word has an entry in the list <dict>.
SEMIGROUPS.StzExpandWord := function(word, dict)
  local out, letter;
  out := [];
  for letter in word do
    Append(out, dict[letter]);
  od;
  return out;
end;

SEMIGROUPS.NewGeneratorName := function(names_immut)
  local alph, Alph, na, nA, names_prefx, names_suffx, int_positions, prefixes,
  prefixes_collected, p, ints, i, name, names;
  names := [];
  for name in names_immut do
    Add(names, ShallowCopy(name));
  od;

  # useful helper variables
  alph := "abcdefghijklmnopqrstuvwxyz";
  Alph := "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  # SPECIAL CASE 0: empty list
  if IsEmpty(names) then
    return "a";
  fi;

  # SPECIAL CASE 1: only one generator
  if Length(names) = 1 then
    if Length(names[1]) = 1 then
      if names[1][1] in Alph then
        return [First(Alph, x -> not [x] in names)];
      elif names[1][1] in alph then
        return [First(alph, x -> not [x] in names)];
      else
        return "a";
      fi;
    else
      return "a";
    fi;
  fi;

  # SPECIAL CASE 2: single letter names are present. Add an unused letter
  # with the most common capitalisation
  na := Length(Filtered(names, x -> Length(x) = 1 and x[1] in alph));
  nA := Length(Filtered(names, x -> Length(x) = 1 and x[1] in Alph));
  if 2 <= na and na < 26 then
    if na <= nA and nA < 26 then
      return [First(Alph, x -> not [x] in names)];
    else
      return [First(alph, x -> not [x] in names)];
    fi;
  elif 2 <= nA and nA < 26 then
    return [First(Alph, x -> not [x] in names)];
  fi;

  # SPECIAL CASE 3: there are names like s1, s3, s23, etc or x12, etc
  names_prefx := [];
  names_suffx := [];
  for name in names do
    Add(names_prefx, [name[1]]);
    Add(names_suffx, name{[2 .. Length(name)]});
  od;
  int_positions := PositionsProperty(names_suffx, x -> Int(x) <> fail
                                              and x <> ""
                                              and x[1] <> '-');
  if Length(int_positions) >= 2 then
    prefixes           := names_prefx{int_positions};
    prefixes_collected := Collected(prefixes);
    # look for highest frequency in collected list
    p := prefixes_collected[PositionMaximum(prefixes_collected, x -> x[2])][1];
    # find maximum suffix int, even amongst those with prefix not p
    ints := List(names_suffx{int_positions}, Int);
    i    := Maximum(ints) + 1;
    return Concatenation(p, String(i));
  fi;

  # if none of the special cases are covered, just try s1, s2,... until good
  for i in [1 .. Length(names) + 1] do
    if not Concatenation("s", String(i)) in names then
      return Concatenation("s", String(i));
    fi;
  od;
end;

# Counts the number of times a subword appears in the relations of an stz
# presentation, ensuring that the subwords do not overlap
# (Eg, for relations [[1,1,1,1],[1,1]], the subword [1,1] would return 3 and not
# 4)
SEMIGROUPS.StzCountRelationSubwords := function(stz, subWord)
  local count, relSide, rel, rels, pos, len, relSideCopy;
  rels := RelationsOfStzPresentation(stz);
  len := Length(subWord);
  count := 0;
  for rel in rels do
    for relSide in rel do
      pos := PositionSublist(relSide, subWord);
      relSideCopy := ShallowCopy(relSide);
      while pos <> fail do
        count := count + 1;
        relSideCopy := List([(pos + len) .. Length(relSideCopy)],
                            x -> relSideCopy[x]);
        pos := PositionSublist(relSideCopy, subWord);
      od;
    od;
  od;
  return count;
end;

# Converts an Stz presentation into an fp semigroup.
SEMIGROUPS.StzConvertObjToFpSemigroup := function(stz)
  local F, rels, gens;
  F    := FreeSemigroup(stz!.GeneratorsOfStzPresentation);
  rels := RelationsOfStzPresentation(stz);
  gens := GeneratorsOfSemigroup(F);
  return F / List(rels, x -> List(x, y -> Product(List(y, z -> gens[z]))));
end;

########################################################################
# 6.  Internal auto-checkers and appliers for presentation simplifying
########################################################################

## Format to add a new reduction check:
## StzCheck: function that takes an stz presentation as input and checks all
##           possible instances of the desired reduction, and outputs as a
##           record the least length of the possible reductions together with
##           the arguments required to achieve that length
## StzApply: function that takes an stz presentation and the above record as
##           input and applies the arguments from the record to achieve the
##           desired reduction
## Add the above two functions to the list results in StzSimplifyOnce as the
## pair [StzApply, StzCheck(stz)]

SEMIGROUPS.StzFrequentSubwordCheck := function(stz)
  local best_gain, best_word, flat, count_occurrences, n, c, gain, word,
        pair, i, j;
  # SUPER INEFFICIENT (~n^3), do something about this eventually (@Reinis?)
  # Look at every subword, count how many times it appears, and see whether
  # introducing a new generator equal to a given subword would make the
  # whole presentation shorter
  best_gain := 0;   # best reduction seen so far
  best_word := [];  # word currently holding record

  # flat list of words (don't care about which one is related to which)
  flat := [];
  for pair in stz!.RelationsOfStzPresentation do
    Append(flat, ShallowCopy(pair));  # TODO(later) might not need shallow copy
  od;

  # function to count occurrences of subword in list of lists
  count_occurrences := function(list, subword)
    local count, k, l, i, word;
    count := 0;
    k     := Length(subword);
    for word in list do
      l := Length(word);
      i := 1;  # index at which to start counting
      while i <= l - k + 1 do
        if word{[i .. i + k - 1]} = subword then
          count := count + 1;
          # jump to end of occurrence
          i := i + k;
        else
          # move over by one
          i := i + 1;
        fi;
      od;
    od;
    return count;
  end;

  # now check for every subword, how many times it appears
  for word in flat do
    # N.B. ASSUMES WORDS NON-EMPTY
    n := Length(word);
    for i in [1 .. n - 1] do
      for j in [i + 1 .. n] do
        c := count_occurrences(flat, word{[i .. j]});
        # now calculate what a gain that would be:
        # subbing out every instance of word{[i .. j]} for a word of length 1
        # makes us gain j - i characters per substitution
        # BUT, we also have a new generator cost of 1
        # AND a new relation cost of 3 + j - i
        gain := (c - 1) * (j - i) - 3;
        if gain > best_gain then
          best_gain := gain;
          best_word := word{[i .. j]};
        fi;
      od;
    od;
  od;
  return rec(reduction := Length(stz) - best_gain,
             word := best_word);
end;

SEMIGROUPS.StzFrequentSubwordApply := function(stz, metadata)
  local word, rels, n, gens, k, shortened_rels, new_rel, i, str, f, aWord;

  # have received instruction to sub out metadata.word for something shorter.
  word := metadata.word;
  rels := ShallowCopy(RelationsOfStzPresentation(stz));
  n    := Length(rels);
  gens := ShallowCopy(GeneratorsOfStzPresentation(stz));
  k    := Length(gens);

  # Build message
  str := "<Creating new generator to replace instances of word: ";
  f := FreeSemigroup(gens);
  aWord := AssocWordByLetterRep(FamilyObj(f.1), metadata.word);
  Append(str, PrintString(aWord));
  Append(str, ">");
  Info(InfoFpSemigroup, 2, PRINT_STRINGIFY(str));

  # first, add new generator.
  SEMIGROUPS.TietzeTransformation3(stz, word, fail);

  # then, go through and add loads of relations which are the old ones but
  # with the old word subbed out.
  shortened_rels := SEMIGROUPS.StzReplaceSubword(rels, word, [k + 1]);
  for new_rel in shortened_rels do
    SEMIGROUPS.TietzeTransformation1(stz, new_rel);
  od;

  # finally, remove the original relations.
  for i in [1 .. n] do
    SEMIGROUPS.TietzeTransformation2(stz, 1);
  od;
  return;
end;

# Checks each relation in the stz presentation in turn and determines if
# replacing the longer side by the shorter side reduces the length of the
# presentation
SEMIGROUPS.StzRelsSubCheck := function(stz)
  local rels, currentMin, currentRel, tempRel, len, relLenDiff, numInstances,
        newLen, i;
  rels := RelationsOfStzPresentation(stz);
  currentMin := Length(stz);
  currentRel := 0;
  for i in [1 .. Length(rels)] do
    tempRel := ShallowCopy(rels[i]);
    SortBy(tempRel, Length);
    len := Length(stz);
    relLenDiff := Length(tempRel[2]) - Length(tempRel[1]);
    numInstances := SEMIGROUPS.StzCountRelationSubwords(stz, tempRel[2]) - 1;
    newLen := len - numInstances * relLenDiff;
    if newLen < currentMin then
      currentMin := newLen;
      currentRel := i;
    fi;
  od;
  return rec(reduction := currentMin,
              argument := currentRel);
end;

# Checks each relation of the stz presentation to see if any are of the form
# a = w, where a is a generator and w is a word not containing a, then
# determines the length if the generator and relation were removed
SEMIGROUPS.StzRedundantGeneratorCheck := function(stz)
  local rel, rels, numInstances, relPos, tempPositions, r, out, redLen,
        genToRemove, wordToReplace, foundRedundant, currentMin, currentGen,
        currentRel;
  rels := RelationsOfStzPresentation(stz);
  out := [];
  currentMin := Length(stz);
  currentGen := 0;
  currentRel := 0;
  for rel in rels do
    foundRedundant := false;
    if Length(rel[1]) = 1 and Length(rel[2]) = 1 and rel[1] <> rel[2] then
      genToRemove := rel[1][1];
      wordToReplace := rel[2];
      Append(out, [Length(stz) - 3]);
      if Length(stz) - 3 < currentMin then
        currentMin := Length(stz) - 3;
        currentGen := genToRemove;
        currentRel := Position(rels, rel);
      fi;
      continue;
    elif Length(rel[1]) = 1 and Length(rel[2]) > 1 and
          (not (rel[1][1] in rel[2])) then
      genToRemove := rel[1][1];
      wordToReplace := rel[2];
      foundRedundant := true;
    elif Length(rel[2]) = 1 and Length(rel[1]) > 1 and
          (not (rel[2][1] in rel[1])) then
      genToRemove := rel[2][1];
      wordToReplace := rel[1];
      foundRedundant := true;
    fi;
    if foundRedundant then
      relPos := Position(rels, rel);
      numInstances := 0;
      for r in Concatenation([1 .. relPos - 1], [relPos + 1 .. Length(rels)]) do
        tempPositions := Length(Positions(Concatenation(rels[r][1], rels[r][2]),
                                          genToRemove));
        numInstances  := numInstances + tempPositions;
      od;
      redLen := Length(stz) + (numInstances * (Length(wordToReplace) - 1)) -
                2 - Length(wordToReplace);
      if redLen < currentMin then
        currentMin := redLen;
        currentGen := genToRemove;
        currentRel := Position(rels, rel);
      fi;
    fi;
  od;
  return rec(reduction := currentMin,
              argument := currentGen,
              infoRel := currentRel);
end;

# Checks each relation to determine if it is a direct duplicate of another
# (ie does not check equivalence in terms of other relations, just whether they
# are literally the same relation)
SEMIGROUPS.StzDuplicateRelsCheck := function(stz)
  local rel, rels, i, tempRel, j, currentMin, currentRel, len;
  rels := RelationsOfStzPresentation(stz);
  currentMin := Length(stz);
  currentRel := 0;
  if Length(rels) < 2 then
    return rec(reduction := Length(stz),
                argument := 1);
  else
    for j in [1 .. Length(rels)] do
      rel := rels[j];
      for i in Concatenation([1 .. j - 1],
                              [j + 1 .. Length(rels)]) do
        tempRel := rels[i];
        if (tempRel[1] = rel[1] and tempRel[2] = rel[2]) or
            (tempRel[1] = rel[2] and tempRel[2] = rel[1]) then
          len := Length(stz) - Length(rel[1]) - Length(rel[2]);
          if len < currentMin then
            currentMin := len;
            currentRel := j;
          fi;
        fi;
      od;
    od;
    return rec(reduction := currentMin,
                argument := currentRel);
  fi;
end;

# Checks each relation to determine if any are of the form w = w, where w is a
# word over the generators
SEMIGROUPS.StzTrivialRelationCheck := function(stz)
  local rels, len, i, currentMin, currentRel, newLen;
  rels := RelationsOfStzPresentation(stz);
  len := Length(stz);
  currentMin := len;
  currentRel := 0;
  for i in [1 .. Length(rels)] do
    if rels[i][1] = rels[i][2] then
      newLen := len - Length(rels[i][1]) - Length(rels[i][2]);
      if newLen < currentMin then
        currentMin := newLen;
        currentRel := i;
      fi;
    fi;
  od;
  return rec(reduction := currentMin,
              argument := currentRel);
end;

# Removes a trivial relation
SEMIGROUPS.StzTrivialRelationApply := function(stz, args)
  local str;
  str := "<Removing trivial relation: ";
  Append(str, SEMIGROUPS.StzRelationDisplayString(stz, args.argument));
  Append(str, ">");
  Info(InfoFpSemigroup, 2, PRINT_STRINGIFY(str));
  SEMIGROUPS.TietzeTransformation2(stz, args.argument);
end;

# Removes a duplicated relation
SEMIGROUPS.StzDuplicateRelsApply := function(stz, args)
  local str;
  str := "<Removing duplicate relation: ";
  Append(str, SEMIGROUPS.StzRelationDisplayString(stz, args.argument));
  Append(str, ">");
  Info(InfoFpSemigroup, 2, PRINT_STRINGIFY(str));
  SEMIGROUPS.TietzeTransformation2(stz, args.argument);
end;

# Removes a redundant generator
SEMIGROUPS.StzGensRedundantApply := function(stz, args)
  local str;
  str := "<Removing redundant generator ";
  Append(str, GeneratorsOfStzPresentation(stz)[args.argument]);
  Append(str, " using relation : ");
  Append(str, SEMIGROUPS.StzRelationDisplayString(stz, args.infoRel));
  Append(str, ">");
  Info(InfoFpSemigroup, 2, PRINT_STRINGIFY(str));
  SEMIGROUPS.TietzeTransformation4(stz, args.argument, args.infoRel);
end;

# Replaces all instances of one side of a relation with the other inside each
# other relation
SEMIGROUPS.StzRelsSubApply := function(stz, args)
  local str, relIndex, rels, rel, subword, replaceWord, containsRel, newRel, i,
        j;
  str := "<Replacing all instances in other relations of relation: ";
  Append(str, SEMIGROUPS.StzRelationDisplayString(stz, args.argument));
  Append(str, ">");
  Info(InfoFpSemigroup, 2, PRINT_STRINGIFY(str));

  relIndex := args.argument;
  rels := RelationsOfStzPresentation(stz);
  rel := ShallowCopy(rels[relIndex]);
  SortBy(rel, Length);
  # TODO(later) line above: potential edge cases where we are not substituting
  # the longer for the shorter, but rather two words of equal length? I guess
  # not, since the checker function would only suggest this applier function if
  # there was an actual reduction? In that case, careful to use correctly

  # record words to sub out (one we are searching for) and to replace with
  subword := rel[2];
  replaceWord := rel[1];

  # keep track of relation indices where we substituted
  containsRel := [];
  for i in [1 .. Length(rels)] do
    if  i <> relIndex and
        (PositionSublist(rels[i][1], subword) <> fail or
         PositionSublist(rels[i][2], subword) <> fail) then
      Add(containsRel, i);
    fi;
  od;

  for i in containsRel do
    newRel := List([1 .. 2], x -> ShallowCopy(rels[i][x]));
    newRel := List(newRel, x -> SEMIGROUPS.StzReplaceSubwordRel(x, subword,
                                                                replaceWord));
    SEMIGROUPS.TietzeTransformation1(stz, newRel);
  od;
  for j in [1 .. Length(containsRel)] do
    SEMIGROUPS.TietzeTransformation2(stz, containsRel[j]);
    containsRel := containsRel - 1;
  od;
end;

########################################################################
# 7. SimplifyPresentation etc.
########################################################################

InstallMethod(StzSimplifyOnce,
[IsStzPresentation],
function(stz)
  local rels, results, len, mins, result, func, args;
  rels := RelationsOfStzPresentation(stz);
  if IsEmpty(rels) then
    return false;
  else
    results := [[SEMIGROUPS.StzGensRedundantApply,
                  SEMIGROUPS.StzRedundantGeneratorCheck(stz)],
                [SEMIGROUPS.StzDuplicateRelsApply,
                  SEMIGROUPS.StzDuplicateRelsCheck(stz)],
                [SEMIGROUPS.StzRelsSubApply,
                  SEMIGROUPS.StzRelsSubCheck(stz)],
                [SEMIGROUPS.StzFrequentSubwordApply,
                  SEMIGROUPS.StzFrequentSubwordCheck(stz)],
                [SEMIGROUPS.StzTrivialRelationApply,
                  SEMIGROUPS.StzTrivialRelationCheck(stz)]];
    len := Length(stz);
    mins := List(results, x -> x[2].reduction);
    if Minimum(mins) < len then
      result := results[Position(mins, Minimum(mins))];
      func := result[1];
      args := result[2];
      func(stz, args);
      return true;
    fi;
    return false;
  fi;
end);

InstallMethod(StzSimplifyPresentation,
[IsStzPresentation],
function(stz)
  local transformApplied;
  transformApplied := true;
  Info(InfoFpSemigroup, 2, "Applying StzSimplifyPresentation...");
  Info(InfoFpSemigroup, 2, "StzSimplifyPresentation is verbose by default. ",
                           "Use SetInfoLevel(InfoFpSemigroup, 1) to hide");
  Info(InfoFpSemigroup, 2, "output while maintaining ability to use ",
                           "StzPrintRelations, StzPrintGenerators, etc.");
  Info(InfoFpSemigroup, 2, Concatenation("Current: ", ViewString(stz)));
  while transformApplied do
    transformApplied := StzSimplifyOnce(stz);
    if transformApplied then
      Info(InfoFpSemigroup, 2, Concatenation("Current: ", ViewString(stz)));
    fi;
  od;
end);

InstallMethod(SimplifiedFpSemigroup,
[IsFpSemigroup],
function(S)
  local T, map;
  map := SimplifyFpSemigroup(S);
  T := Range(map);
  SetUnreducedFpSemigroup(T, S);
  SetFpTietzeIsomorphism(T, map);
  return T;
end);

InstallMethod(SimplifyFpSemigroup, "for an f.p. semigroup", [IsFpSemigroup],
function(S)
  local stz;
  stz := StzPresentation(S);
  StzSimplifyPresentation(stz);
  return StzIsomorphism(stz);
end);

[ zur Elbe Produktseite wechseln0.73Quellennavigators  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge