RAQ, a GAP System package for Racks And Quandles.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

740 lines 27 KiB Raw Blame History

 `## structure.gi RAQ Implementation of definitiions, reps, and elt operations` ``` ``` `## Testing properties of collections the hard way if we have to` ``` ``` `InstallMethod(IsElementwiseIdempotent, "for finite collections",` ` [IsMultiplicativeElementCollection and IsFinite],` ` M -> ForAll(Elements(M), m->IsIdempotent(m))` `);` ``` ``` `InstallMethod(IsLSelfDistributive,` ` "for arbitrary multiplicative collections, the hard way",` ` [IsMultiplicativeElementCollection],` ` function (C)` ` local a,b,d;` ` for a in C do for b in C do for d in C do` ` if d*(a*b) <> (d*a)*(d*b) then return false; fi;` ` od; od; od;` ` return true;` `end);` ``` ``` `InstallMethod(IsRSelfDistributive,` ` "for arbitrary multiplicative collections, the hard way",` ` [IsMultiplicativeElementCollection],` ` function (C)` ` local a,b,d;` ` for a in C do for b in C do for d in C do` ` if (a*b)*d <> (a*d)*(b*d) then return false; fi;` ` od; od; od;` ` return true;` `end);` ` ` `## Create structures with generators` ``` ``` `InstallMethod(GeneratorsOfMagma,` ` "finite left quasigroups",` ` [IsLeftQuasigroup and IsFinite],` ` q -> GeneratorsOfLeftQuasigroup(q)` `);` `InstallMethod(GeneratorsOfMagma,` ` "finite right quasigroups",` ` [IsRightQuasigroup and IsFinite],` ` q -> GeneratorsOfRightQuasigroup(q)` `);` ``` ``` `InstallGlobalFunction(CloneOfTypeByGenerators,` ` function(cat, fam, gens, genAttrib, tableCstr)` ` local M, elf;` ` if not(IsEmpty(gens) or IsIdenticalObj(FamilyObj(gens), fam)) then` ` Error(" and family of do not match");` ` fi;` ` M := Objectify(NewType( fam, cat and IsAttributeStoringRep), rec());` ` Setter(genAttrib)(M, AsList(gens));` ` SetConstructorFromTable(M, tableCstr);` ` elf := ElementsFamily(fam);` ` # Since there doesn't seem to be a way to make the IsFinite method based` ` # on the family being finite into an immediate method:` ` if HasIsFinite(elf) and IsFinite(elf) then SetIsFinite(M, true); fi;` ` return M;` `end);` ``` ``` `## Helpers for the constructors below:` ``` ``` `ArgHelper@ := function(parmlist)` ` local fam;` ` # returns a list of the family and the list of elements of parmlist` ` if Length(parmlist) = 0 then` ` Error("usage: RAQ constructors take an optional family, followed by gens");` ` fi;` ` fam := fail;` ` if IsFamily(parmlist[1]) then fam := Remove(parmlist,1); fi;` ` if Length(parmlist) = 1 and IsList(parmlist[1])` ` and not IsDirectProductElement(parmlist[1]) then` ` parmlist := parmlist[1];` ` fi;` ` if fam = fail then fam := FamilyObj(parmlist); fi;` ` return [fam, parmlist];` `end;` ``` ``` `CheckLQGprop@ := function(gens)` ` local g, h;` ` # Make sure all elements in gens have left quotient property pairwise` ` for g in gens do for h in gens do` ` if g*LeftQuotient(g,h) <> h or LeftQuotient(g,g*h) <> h then` ` Error("left quasigroup property of left quotients violated");` ` fi;` ` od; od;` ` return;` `end;` ``` ``` `CheckRQGprop@ := function(gens)` ` local g, h;` ` # Make sure all elements in gens have right quotient property pairwise` ` for g in gens do for h in gens do` ` if (h*g)/g <> h or (h/g)*g <> h then` ` Error("right quasigroup property of / violated");` ` fi;` ` od; od;` ` return;` `end;` ``` ``` `## Functions for each of the magma categories here` `InstallGlobalFunction(LeftQuasigroup, function(arg)` ` arg := ArgHelper@(arg);` ` CheckLQGprop@(arg[2]);` ` return LeftQuasigroupNC(arg[1], arg[2]);` `end);` ``` ``` `InstallGlobalFunction(LeftQuasigroupNC, function(fam, gens) ` ` return CloneOfTypeByGenerators(IsLeftQuasigroup, fam, gens,` ` GeneratorsOfLeftQuasigroup, ` ` LeftQuasigroupByMultiplicationTableNC);` `end);` ``` ``` `InstallGlobalFunction(LeftRack, function(arg)` ` arg := ArgHelper@(arg);` ` CheckLQGprop@(arg[2]);` ` if not IsLSelfDistributive(arg[2]) then` ` Error("Left rack must have left distributive generators");` ` fi;` ` return LeftRackNC(arg[1], arg[2]);` `end);` ``` ``` `InstallGlobalFunction(LeftRackNC, function(fam, gens)` ` return CloneOfTypeByGenerators(IsLeftRack, fam, gens,` ` GeneratorsOfLeftQuasigroup,` ` LeftRackByMultiplicationTableNC);` `end);` ``` ``` `InstallGlobalFunction(LeftQuandle, function(arg)` ` arg := ArgHelper@(arg);` ` CheckLQGprop@(arg[2]);` ` if not IsLSelfDistributive(arg[2]) then` ` Error("Left quandle must have left distributive generators");` ` fi;` ` if not IsElementwiseIdempotent(arg[2]) then` ` Error("Quandles must contain only idempotent elements");` ` fi;` ` return LeftQuandleNC(arg[1], arg[2]);` `end);` ``` ``` `InstallGlobalFunction(LeftQuandleNC, function(fam, gens)` ` return CloneOfTypeByGenerators(IsLeftQuandle, fam, gens,` ` GeneratorsOfLeftQuasigroup,` ` LeftQuandleByMultiplicationTableNC);` `end);` ``` ``` `InstallGlobalFunction(RightQuasigroup, function(arg)` ` arg := ArgHelper@(arg);` ` CheckRQGprop@(arg[2]);` ` return RightQuasigroupNC(arg[1], arg[2]);` `end);` ``` ``` `InstallGlobalFunction(RightQuasigroupNC, function(fam, gens)` ` return CloneOfTypeByGenerators(IsRightQuasigroup, fam, gens,` ` GeneratorsOfRightQuasigroup, ` ` RightQuasigroupByMultiplicationTableNC);` `end);` ``` ``` `InstallGlobalFunction(RightRack, function(arg)` ` arg := ArgHelper@(arg);` ` CheckRQGprop@(arg[2]);` ` if not IsRSelfDistributive(arg[2]) then` ` Error("Right rack must have right distributive generators");` ` fi;` ` return RightRackNC(arg[1], arg[2]);` `end);` ``` ``` `InstallGlobalFunction(RightRackNC, function(fam, gens)` ` return CloneOfTypeByGenerators(IsRightRack, fam, gens,` ` GeneratorsOfRightQuasigroup,` ` RightRackByMultiplicationTableNC);` `end);` ``` ``` `InstallGlobalFunction(RightQuandle, function(arg)` ` arg := ArgHelper@(arg);` ` CheckRQGprop@(arg[2]);` ` if not IsRSelfDistributive(arg[2]) then` ` Error("Right quandle must have right distributive generators");` ` fi;` ` if not IsElementwiseIdempotent(arg[2]) then` ` Error("Quandles must contain only idempotent elements");` ` fi;` ` return RightQuandleNC(arg[1], arg[2]);` `end);` ``` ``` `InstallGlobalFunction(RightQuandleNC, function(fam, gens)` ` return CloneOfTypeByGenerators(IsRightQuandle, fam, gens,` ` GeneratorsOfRightQuasigroup,` ` RightQuandleByMultiplicationTableNC);` `end);` ``` ``` `## NOTE: If it is the case that the magma generated by the` `## GeneratorsOf[Left|Right]Quasigroup is finite, then since the [left|right]` `## multiplication is injective, it is also surjective on that set and in fact` `## it is the whole quasigroup. However, it is not yet clear to me how best to` `## capture this fact in GAP in a computationally useful way.` ``` ``` `## View and print and such` `LeftObjString@ := function(Q)` ` # Don't test distributivity if we haven't already` ` if HasIsLSelfDistributive(Q) and IsLeftRack(Q) then ` ` if HasIsElementwiseIdempotent(Q) and IsElementwiseIdempotent(Q) then` ` return "LeftQuandle";` ` fi;` ` return "LeftRack";` ` fi;` ` return "LeftQuasigroup";` `end;` ``` ``` `RightObjString@ := function(Q)` ` # Don't test distributivity if we haven't already` ` if HasIsRSelfDistributive(Q) and IsRightRack(Q) then ` ` if HasIsElementwiseIdempotent(Q) and IsElementwiseIdempotent(Q) then` ` return "RightQuandle";` ` fi;` ` return "RightRack";` ` fi;` ` return "RightQuasigroup";` `end;` ``` ``` `InstallMethod(String, "for a left quasigroup",` ` [IsLeftQuasigroup],` ` Q -> Concatenation(LeftObjString@(Q), "(...)"));` ``` ``` `InstallMethod(String, "for a left quasigroup with generators",` ` [IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup],` ` Q -> Concatenation(LeftObjString@(Q), "( ", ` ` String(GeneratorsOfLeftQuasigroup(Q)), " )"));` ``` ``` `InstallMethod(String, "for a left quasigroup with multiplication table",` ` [IsLeftQuasigroup and HasMultiplicationTable],` ` Q -> Concatenation(LeftObjString@(Q), ` ` "ByMultiplicationTableNC( ",` ` String(MultiplicationTable(Q)), " )"));` ``` ``` `InstallMethod(String, "for a right quasigroup",` ` [IsRightQuasigroup],` ` Q -> Concatenation(RightObjString@(Q), "(...)"));` ``` ``` `InstallMethod(String, "for a right quasigroup with generators",` ` [IsRightQuasigroup and HasGeneratorsOfRightQuasigroup],` ` Q -> Concatenation(RightObjString@(Q), "( ", ` ` String(GeneratorsOfRightQuasigroup(Q)), " )"));` ``` ``` `InstallMethod(String, "for a right quasigroup with multiplication table",` ` [IsRightQuasigroup and HasMultiplicationTable],` ` Q -> Concatenation(RightObjString@(Q), ` ` "ByMultiplicationTableNC( ",` ` String(MultiplicationTable(Q)), " )"));` ``` ``` `InstallMethod(PrintString, "for a left quasigroup",` ` [IsLeftQuasigroup], Q -> String(Q));` ``` ``` `InstallMethod(PrintString, "for a right quasigroup",` ` [IsRightQuasigroup], Q -> String(Q));` ``` ``` `InstallMethod(DisplayString, "for a left quasigroup",` ` [IsLeftQuasigroup], Q -> String(Q));` ``` ``` `InstallMethod(DisplayString, "for a right quasigroup",` ` [IsRightQuasigroup], Q -> String(Q));` ``` ``` `InstallMethod(Display, "for a left quasigroup with multiplication table",` ` [IsLeftQuasigroup and HasMultiplicationTable],` ` function(Q)` ` Print(LeftObjString@(Q), " with ", Size(Q), ` ` " elements, generated by ", ` ` GeneratorsOfLeftQuasigroup(Q), ", with table\n");` ` Display(MultiplicationTable(Q));` `end);` ``` ``` `InstallMethod(Display, "for a right quasigroup with multiplication table",` ` [IsRightQuasigroup and HasMultiplicationTable],` ` function(Q)` ` Print(RightObjString@(Q), " with ", Size(Q), ` ` " elements, generated by ", ` ` GeneratorsOfRightQuasigroup(Q), ", with table\n");` ` Display(MultiplicationTable(Q));` `end);` ``` ``` `LeftObjView@ := function(Q)` ` # Don't test distributivity if we haven't already` ` if HasIsLSelfDistributive(Q) and IsLeftRack(Q) then ` ` if HasIsElementwiseIdempotent(Q) and IsElementwiseIdempotent(Q) then` ` return " Concatenation(LeftObjView@(Q), ">"));` ``` ``` `InstallMethod(ViewString, "for a left quasigroup with generators",` ` [IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup], ` ` Q -> Concatenation(LeftObjView@(Q), " with ",` ` String(Size(GeneratorsOfLeftQuasigroup(Q))), ` ` " generators>"));` ``` ``` `InstallMethod(ViewString, "for a right quasigroup",` ` [IsRightQuasigroup], ` ` Q -> Concatenation(RightObjView@(Q), ">"));` ``` ``` `InstallMethod(ViewString, "for a right quasigroup with generators",` ` [IsRightQuasigroup and HasGeneratorsOfRightQuasigroup], ` ` Q -> Concatenation(RightObjView@(Q), " with ",` ` String(Size(GeneratorsOfRightQuasigroup(Q))), ` ` " generators>"));` ``` ``` `## Opposite structures` `OFDir@ := NewDictionary("strings", true);` `# What property does the Opposite family have for each property of the` `# underlying elements? Hopefully we have gotten all of the relevant` `# properties. What we really want is the implied (and maybe the required)` `# properties of the family of the elements that we are taking the Opposite of,` `# but there is not any documented way of obtaining those.` `AddDictionary(OFDir@, "CanEasilyCompareElements", CanEasilyCompareElements);` `AddDictionary(OFDir@, "CanEasilySortElements", CanEasilySortElements);` `AddDictionary(OFDir@, "IsAssociativeElement", IsAssociativeElement);` `AddDictionary(OFDir@, "IsCommutativeElement", IsCommutativeElement);` `AddDictionary(OFDir@, "IsExtLElement", IsExtRElement);` `AddDictionary(OFDir@, "IsExtRElement", IsExtLElement);` `AddDictionary(OFDir@, "IsFiniteOrderElement", IsFiniteOrderElement);` `AddDictionary(OFDir@, "IsLeftQuotientElement", IsRightQuotientElement);` `AddDictionary(OFDir@, "IsLSelfDistElement", IsRSelfDistElement);` `AddDictionary(OFDir@, "IsMultiplicativeElementWithInverse",` ` IsMultiplicativeElementWithInverse);` `AddDictionary(OFDir@, "IsMultiplicativeElementWithOne",` ` IsMultiplicativeElementWithOne);` `AddDictionary(OFDir@, "IsRightQuotientElement", IsLeftQuotientElement);` `AddDictionary(OFDir@, "IsRSelfDistElement", IsLSelfDistElement);` ``` ``` `InstallMethod(OppositeFamily, "for a family",` ` [IsFamily],` ` function(fam)` ` local F, elt, elt_props, opp_props, prop, opp_filt, filt;` ` elt := Representative(fam);` ` elt_props := CategoriesOfObject(elt);` ` Append(elt_props, KnownTruePropertiesOfObject(elt));` ` opp_props := [];` ` for prop in elt_props do` ` if KnowsDictionary(OFDir@, prop) then` ` Add(opp_props, LookupDictionary(OFDir@, prop));` ` fi;` ` od;` ` opp_filt := IsMultiplicativeElement;` ` for filt in opp_props do opp_filt := opp_filt and filt; od;` ` return NewFamily(Concatenation("OppositeFamily(", fam!.NAME, ")"),` ` IsOppositeObject, opp_filt);` `end);` ` ` `InstallMethod(OppositeType, "for a family",` ` [IsFamily],` ` fam -> NewType(OppositeFamily(fam), IsDefaultOppositeObject)` `);` ` ` `InstallMethod(OppositeObj, "for a multiplicative element",` ` [IsMultiplicativeElement],` ` function (obj)` ` local fam;` ` fam := FamilyObj(obj);` ` SetRepresentative(fam, obj);` ` return Objectify(OppositeType(fam), [Immutable(obj)]);` `end);` ``` ``` `InstallMethod(OppositeObj, "for a commutative element",` ` [IsMultiplicativeElement and IsCommutativeElement],` ` IdFunc` `);` ``` ``` `InstallMethod(UnderlyingMultiplicativeElement, "for a default opposite obj",` ` [IsDefaultOppositeObject],` ` obj -> obj![1]` `);` ``` ``` `## Printing and viewing opposite objects` `InstallMethod(String, "for opposite objects",` ` [IsDefaultOppositeObject],` ` obj -> Concatenation("OppositeObj( ", String(obj![1]), " )")` `);` ``` ``` `InstallMethod(ViewString, "for opposite objects",` ` [IsDefaultOppositeObject],` ` obj -> Concatenation("o", ViewString(obj![1]))` `);` ``` ``` `InstallMethod(\=, "for two opposite objects",` ` IsIdenticalObj,` ` [IsDefaultOppositeObject, IsDefaultOppositeObject],` ` function(l,r) return l![1] = r![1]; end` `);` ``` ``` `InstallMethod(\<, "for two opposite objects",` ` IsIdenticalObj,` ` [IsDefaultOppositeObject, IsDefaultOppositeObject],` ` function(l,r) return l![1] < r![1]; end` `);` ``` ``` `InstallMethod(\*, "for two opposite objects",` ` IsIdenticalObj,` ` [IsDefaultOppositeObject, IsDefaultOppositeObject],` ` function(l,r) return OppositeObj(r![1]*l![1]); end` `);` ``` ``` `InstallOtherMethod(LeftQuotient, "for two opposite objects",` ` IsIdenticalObj,` ` [IsDefaultOppositeObject and IsLeftQuotientElement, IsDefaultOppositeObject],` ` function(l,r) return OppositeObj(r![1]/l![1]); end` `);` ``` ``` `InstallOtherMethod(\/, "for two opposite objects",` ` IsIdenticalObj,` ` [IsDefaultOppositeObject, IsDefaultOppositeObject and IsRightQuotientElement],` ` function(l,r) return OppositeObj(LeftQuotient(r![1], l![1])); end` `);` ``` ``` `InstallMethod(OneOp, "for an opposite object",` ` [IsDefaultOppositeObject and IsMultiplicativeElementWithOne],` ` ob -> OppositeObj(One(ob![1]))` `);` ``` ``` `InstallMethod(InverseOp, "for an opposite object",` ` [IsDefaultOppositeObject and IsMultiplicativeElementWithInverse],` ` ob -> OppositeObj(Inverse(ob![1]))` `);` ``` ``` `# Now the many Opposite implementations. How can we cut this down?` ``` ``` `InstallMethod(Opposite, "for a commutative magma",` ` [IsMagma and IsCommutative],` ` IdFunc` `);` ``` ``` `InstallMethod(Opposite, "for a finitely generated magma",` ` [IsMagma and HasGeneratorsOfMagma],` ` function(M)` ` local fam, elts;` ` # Get elements first to prime the representative of the family` ` elts := List(GeneratorsOfMagma(M), m -> OppositeObj(m));` ` fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(M))));` ` return Magma(fam, elts);` `end); ` ``` ``` `InstallMethod(Opposite, "for a finitely generated magma with one",` ` [IsMagmaWithOne and HasGeneratorsOfMagmaWithOne],` ` function(M)` ` local fam, elts;` ` # Get elements first to prime the representative of the family` ` elts := List(GeneratorsOfMagmaWithOne(M), m -> OppositeObj(m));` ` fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(M))));` ` return MagmaWithOne(fam, elts);` `end); ` ``` ``` `InstallMethod(Opposite, "for a finitely generated magma with inverse",` ` [IsMagmaWithInverses and HasGeneratorsOfMagmaWithInverses],` ` function(M)` ` local fam, elts;` ` # Get elements first to prime the representative of the family` ` elts := List(GeneratorsOfMagmaWithInverses(M), m -> OppositeObj(m));` ` fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(M))));` ` return MagmaWithInverses(fam, elts);` `end); ` ``` ``` `# Do we need Opposite() methods for Semigroups, Monoids, and Groups? Or have` `# we copied enough properties that those filters will simply carry over? The` `# couple of small tests I did the Opposite of a Group was again reported as a` `# Group, so let's leave those methods out for now, and move on to quasigroups,` `# racks, and quandles.` ``` ``` `OppHelper@ := function(Q, whichgens, cnstr)` ` local fam, elts, opp;` ` elts := List(whichgens(Q), q -> OppositeObj(q));` ` fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(Q))));` ` opp := cnstr(fam, elts);` ` if HasIsFinite(Q) then SetIsFinite(opp, IsFinite(Q)); fi;` ` return opp;` `end;` ``` ``` `#! @Chapter construct` `#! @Section from_quasigroups` `#! @Arguments magma` `#! @ItemType Attr` `#! @Label for various finitely-generated magmas` `#! @Description Given `q`, one of the structures covered in &RAQ;,` `#! `Opposite(q)` returns a structure which is just the same except the order` `#! of the operation is exactly reversed: `a*b` in the new structure means` `#! exactly what `b*a` did in the original structure. (This is the same` `#! operation as transposing the Cayley table.) Actually, this operation is` `#! originally defined in &LOOPS;, and it is extended in &RAQ; to one-sided` `#! quasigroups, racks, and quandles. Moreover, since Opposite actually makes` `#! sense for an arbitrary magma, &RAQ; makes an effort to extend it to as` `#! wide a class of arguments as are easily implemented. Note for example` `#! Opposite has no effect on a commutative magma, and &RAQ;` `#! recognizes this fact.` `#! @Returns a magma of the same category` `InstallMethod(Opposite, "for a left quasigroup",` ` [IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup],` ` Q -> OppHelper@(Q, GeneratorsOfLeftQuasigroup, RightQuasigroupNC)` `);` ``` ``` `InstallMethod(Opposite, "for a left rack",` ` [IsLeftRack and HasGeneratorsOfLeftQuasigroup],` ` Q -> OppHelper@(Q, GeneratorsOfLeftQuasigroup, RightRackNC)` `);` ``` ``` `InstallMethod(Opposite, "for a left quandle",` ` [IsLeftQuandle and HasGeneratorsOfLeftQuasigroup],` ` Q -> OppHelper@(Q, GeneratorsOfLeftQuasigroup, RightQuandleNC)` `);` ``` ``` `InstallMethod(Opposite, "for a right quasigroup",` ` [IsRightQuasigroup and HasGeneratorsOfRightQuasigroup],` ` Q -> OppHelper@(Q, GeneratorsOfRightQuasigroup, LeftQuasigroupNC)` `);` ``` ``` `InstallMethod(Opposite, "for a right rack",` ` [IsRightRack and HasGeneratorsOfRightQuasigroup],` ` Q -> OppHelper@(Q, GeneratorsOfRightQuasigroup, LeftRackNC)` `);` ``` ``` `InstallMethod(Opposite, "for a right quandle",` ` [IsRightQuandle and HasGeneratorsOfRightQuasigroup],` ` Q -> OppHelper@(Q, GeneratorsOfRightQuasigroup, LeftQuandleNC)` `);` ``` ``` `# Direct Products` `# We actually try to handle here the general cases that are not otherwise` `# handled by the groups functions in GAP as a whole, or the LOOPS package` ``` ``` `# Helper for roughly creating the join of the filters of a bunch of objects.` `FiltersOfObj@ := function(obj)` ` local f;` ` f := CategoriesOfObject(obj);` ` Append(f,KnownTruePropertiesOfObject(obj));` ` Append(f,List(KnownAttributesOfObject(obj), x -> Concatenation("Has", x)));` ` return f;` `end;` ``` ``` `# From the implementation of DirectProductOp for groups and quasigroups, the` `# below will only be called with a second-argument collection which is not a` `# group or quasigroup.` ``` ``` `# This is a helper that creates a rough join of the filters of a list of` `# objects, using the same arguments as directproduct op (the redundant "first"` `# argument, which is handy for seeding the filter list)` `RoughJoinOfFilters@ := function(list, first)` ` local item, jof;` ` jof := Set(FiltersOfObj@(first));` ` for item in list{[2..Length(list)]} do` ` IntersectSet(jof, FiltersOfObj@(item));` ` od;` ` return jof;` `end;` ``` ``` `#! @ItemType Meth` `#! @Arguments list-of-factors, distinguished-factor` `#! @Returns a magma with only the structure common to all factors` `#! @Description Extends the `DirectProduct` operation to allow factors that` `#! are not even quasigroups, such as the one-sided quasigroups, racks, and` `#! quandles with which &RAQ; is concerned. This direct product operation` `#! makes its best effort to find the most structured category for the result` `#! that it can, given that every factor in the product must lie in that` `#! category.` `InstallOtherMethod(DirectProductOp, "for a list and non-quasigroup magma",` ` [IsList, IsMagma],` ` function (list, first)` ` local item, i, jof, ids, gens, g, current, genfunc, genlists;` ` # Simple checks` ` if IsEmpty(list) then` ` Error("Usage: Cannot take DirectProduct of zero items.");` ` elif Length(list) = 1 then` ` return list[1];` ` fi;` ` jof := RoughJoinOfFilters@(list, first);` ` ` ` # The dispatch below is somewhat ad-hoc, but covers the major existing cases` ` ` ` # First, if we don't have generators for all of the items, we're kinda out` ` # of luck here:` ` if not ForAny(jof, nm -> StartsWith(nm,"HasGenerators")) then` ` TryNextMethod();` ` fi;` ` # Next, if anything is not even a magma, recommend cartesian product` ` if not "IsMagma" in jof then` ` Info(InfoRAQ, 1, "Try Cartesian() for products of collections.");` ` TryNextMethod();` ` fi;` ` ` ` # The primary division now is between entities with identities, for which` ` # the product will have a smaller generating set, and those which do` ` # not, for which the generators is the full cartesian product of generators.` ` if "IsMagmaWithOne" in jof then` ` # Code basically copied from DirectProductOp() for general groups;` ` # would be better if this could be moved into the GAP core` ` ids := List(list, One);` ` gens := [];` ` for i in [1..Length(list)] do` ` for g in GeneratorsOfMagmaWithOne(list[i]) do` ` current := ShallowCopy(ids);` ` current[i] := g;` ` Add(gens, DirectProductElement(current));` ` od;` ` od;` ` # Now, we want the most specific structure we know about here.` ` # Only aware of MagmaWithOne and Monoid, as direct products of ` ` # loops are already covered by the LOOPS package:` ` if "IsMonoid" in jof then` ` return MonoidByGenerators(gens);` ` fi;` ` return MagmaWithOneByGenerators(gens);` ` fi;` ` # OK, here we know not all structures have inverses. Therefore, we must` ` # resort to the full cartesian product of generators.` ` # But we need to figure out what function to use to obtain the generators.` ` if "HasGeneratorsOfLeftQuasigroup" in jof then` ` genfunc := GeneratorsOfLeftQuasigroup;` ` elif "HasGeneratorsOfRightQuasigroup" in jof then` ` genfunc := GeneratorsOfRightQuasigroup;` ` elif "HasGeneratorsOfMagma" in jof then` ` genfunc := GeneratorsOfMagma;` ` elif "HasGeneratorsOfDomain" in jof then` ` genfunc := GeneratorsOfDomain;` ` else` ` Info(InfoRAQ,1, "RAQ: Unusual product, each of ", list,` ` " has generators, but not sure what kind; trying next method.");` ` TryNextMethod();` ` fi;` ` ` ` genlists := List(list, genfunc);` ` gens := Cartesian(genlists);` ` Apply(gens, DirectProductElement);` ` # Again, we need to figure out what sort of structure we might have` ` if "IsAssociative" in jof then` ` return SemigroupByGenerators(gens);` ` elif "IsLeftQuasigroup" in jof then` ` if "IsLSelfDistributive" in jof then` ` if "IsElementwiseIdempotent" in jof then` ` return LeftQuandleNC(FamilyObj(gens), gens);` ` fi;` ` return LeftRackNC(FamilyObj(gens), gens);` ` fi;` ` return LeftQuasigroupNC(FamilyObj(gens), gens);` ` elif "IsRightQuasigroup" in jof then` ` if "IsRSelfDistributive" in jof then` ` if "IsElementwiseIdempotent" in jof then` ` return RightQuandleNC(FamilyObj(gens), gens);` ` fi;` ` return RightRackNC(FamilyObj(gens), gens);` ` fi;` ` return RightQuasigroupNC(FamilyObj(gens), gens);` ` fi;` ` # Not seeing any additional structure; did I miss anything?` ` return MagmaByGenerators(gens);` `end);` ``` ``` `# Make sure that we can take quotients properly in the direct product` `InstallOtherMethod(LeftQuotient,` ` IsIdenticalObj,` ` [IsDirectProductElement, IsDirectProductElement],` ` function (l,r) ` ` local i, comps;` ` comps := [];` ` for i in [1..Length(l)] do` ` Add(comps, LeftQuotient(l![i],r![i]));` ` od;` ` return DirectProductElement(comps);` `end);` ``` ``` `InstallOtherMethod(\/,` ` IsIdenticalObj,` ` [IsDirectProductElement, IsDirectProductElement],` ` function (l,r) ` ` local i, comps;` ` comps := [];` ` for i in [1..Length(l)] do` ` Add(comps, l![i] / r![i]);` ` od;` ` return DirectProductElement(comps);` `end);` ``` ``` ``` ``` ``` ``` `## Coversions among the structure types` `InstallMethod(AsLeftQuasigroup, "for a left quasigroup",` ` [IsLeftQuasigroup], IdFunc);` `InstallMethod(AsLeftRack, "for a left rack",` ` [IsLeftRack], IdFunc);` `InstallMethod(AsLeftQuandle, "for a left quandle",` ` [IsLeftQuandle], IdFunc);` `InstallMethod(AsRightQuasigroup, "for a right quasigroup",` ` [IsRightQuasigroup], IdFunc);` `InstallMethod(AsRightRack, "for a right rack",` ` [IsRightRack], IdFunc);` `InstallMethod(AsRightQuandle, "for a right quandle",` ` [IsRightQuandle], IdFunc);` ``` ``` `AsAStructure@ := function(struc, D)` ` local T,S;` ` D := AsSSortedList(D);` ` # Check if D is closed under *` ` T := MultiplicationTable(D);` ` if ForAny(T, l -> fail in l) then` ` return fail;` ` fi; ` ` # it is, so check if it satisfies the properties of struc` ` S := struc(D);` ` # OK, good, so we are there, just record what we know` ` SetAsSSortedList(S, D);` ` SetMultiplicationTable(S, T);` ` SetIsFinite(S, true);` ` SetSize(S, Length(D));` ` return S;` `end;` ``` ``` `InstallMethod(AsLeftQuasigroup, "for an arbitrary collection",` ` [IsCollection], D -> AsAStructure@(LeftQuasigroup, D));` `InstallMethod(AsLeftRack, "for an arbitrary collection",` ` [IsCollection], D -> AsAStructure@(LeftRack, D));` `InstallMethod(AsLeftQuandle, "for an arbitrary collection",` ` [IsCollection], D -> AsAStructure@(LeftQuandle, D));` ``` ``` `InstallMethod(AsRightQuasigroup, "for an arbitrary collection",` ` [IsCollection], D -> AsAStructure@(RightQuasigroup, D));` `InstallMethod(AsRightRack, "for an arbitrary collection",` ` [IsCollection], D -> AsAStructure@(RightRack, D));` `InstallMethod(AsRightQuandle, "for an arbitrary collection",` ``` [IsCollection], D -> AsAStructure@(RightQuandle, D)); ``` ``` ```