## 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 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) # returns a list of the family and the flat list of elements of parmlist if Length(parmlist) = 0 then Error("usage: RAQ constructors take an optional family, followed by gens"); fi; if IsFamily(parmlist[1]) then return [Remove(parmlist,1), Flat(parmlist)]; fi; parmlist := Flat(parmlist); return [FamilyObj(parmlist), 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>"));