## structure.gi RAQ Implementation of definitiions, reps, and elt operations ## Create structures with generators InstallGlobalFunction(CloneOfTypeByGenerators, function(cat, fam, gens, genAttrib, tableCstr) local M; 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); return M; end); ## Functions for each of the magma categories here InstallGlobalFunction(LeftQuasigroup, function(arg) local fam; if Length(arg) = 0 then Error("usage: LeftQuasigroup([], )"); fi; # Extract the family if IsFamily(arg[1]) then fam := arg[1]; Remove(arg, 1); arg := Flat(arg); else arg := Flat(arg); fam := FamilyObj(arg[1]); fi; return CloneOfTypeByGenerators(IsLeftQuasigroup, fam, arg, GeneratorsOfLeftQuasigroup, LeftQuasigroupByMultiplicationTable); end); InstallGlobalFunction(LeftRack, function(arg) local fam; if Length(arg) = 0 then Error("usage: LeftRack([], )"); fi; # Extract the family if IsFamily(arg[1]) then fam := arg[1]; Remove(arg, 1); arg := Flat(arg); else arg := Flat(arg); fam := FamilyObj(arg[1]); fi; return CloneOfTypeByGenerators(IsLeftRack, fam, arg, GeneratorsOfLeftQuasigroup, LeftRackByMultiplicationTableNC); end); InstallGlobalFunction(RightQuasigroup, function(arg) local fam; if Length(arg) = 0 then Error("usage: RightQuasigroup([], )"); fi; # Extract the family if IsFamily(arg[1]) then fam := arg[1]; Remove(arg, 1); arg := Flat(arg); else arg := Flat(arg); fam := FamilyObj(arg[1]); fi; return CloneOfTypeByGenerators(IsRightQuasigroup, fam, arg, GeneratorsOfRightQuasigroup, RightQuasigroupByMultiplicationTable); end); InstallGlobalFunction(RightRack, function(arg) local fam; if Length(arg) = 0 then Error("usage: RightRack([], )"); fi; # Extract the family if IsFamily(arg[1]) then fam := arg[1]; Remove(arg, 1); arg := Flat(arg); else arg := Flat(arg); fam := FamilyObj(arg[1]); fi; return CloneOfTypeByGenerators(IsRightRack, fam, arg, GeneratorsOfRightQuasigroup, RightRackByMultiplicationTableNC); end); ## Predicates to check tables for distributivity InstallMethod(IsRightSelfDistributiveTable, "for matrix", [ IsMatrix ], T -> IsLeftSelfDistributiveTable(TransposedMat(T)) ); InstallMethod(IsLeftSelfDistributiveTable, "for matrix", [ IsMatrix ], function(T) # Everybody else does it by checking all of the cases, so why not me, too? # Is there a better way? local n,i,j,k; n := Length(T); for i in [1..n] do for j in [1..n] do for k in [1..n] do if T[i, T[j,k]] <> T[T[i,j], T[i,k]] then return false; fi; od; od; od; return true; end); ## And now create them from multiplication tables InstallGlobalFunction(LeftQuasigroupByMultiplicationTable, function(T) if not IsLeftQuasigroupTable(T) then Error("Multiplication table must have each row a permutation of ", "the same entries."); fi; return MagmaByMultiplicationTableCreatorNC( CanonicalCayleyTableOfLeftQuasigroupTable(T), LeftQuasigroup, IsLeftQuotientElement and IsMagmaByMultiplicationTableObj ); end); InstallGlobalFunction(RightQuasigroupByMultiplicationTable, function(T) if not IsRightQuasigroupTable(T) then Error("Multiplication table must have each column a permutation of ", "the same entries."); fi; return MagmaByMultiplicationTableCreatorNC( CanonicalCayleyTable(T), RightQuasigroup, IsRightQuotientElement and IsMagmaByMultiplicationTableObj ); end); InstallGlobalFunction(LeftRackByMultiplicationTable, function(T) if not IsLeftQuasigroupTable(T) then Error("Multiplication table must have each row a permutation of ", "the same entries."); fi; T := CanonicalCayleyTableOfLeftQuasigroupTable(T); if not IsLeftSelfDistributiveTable(T) then Error("Multiplication table must be left self distributive."); fi; return LeftRackByMultiplicationTableNC(T); end); InstallGlobalFunction(LeftRackByMultiplicationTableNC, T -> MagmaByMultiplicationTableCreatorNC(T, LeftRack, IsLeftQuotientElement and IsLSelfDistElement and IsMagmaByMultiplicationTableObj ) ); InstallGlobalFunction(RightRackByMultiplicationTable, function(T) if not IsRightQuasigroupTable(T) then Error("Multiplication table must have each column a permutation of ", "the same entries."); fi; T := CanonicalCayleyTable(T); if not IsRightSelfDistributiveTable(T) then Error("Multiplication table must be right self distributive."); fi; return RightRackByMultiplicationTableNC(T); end); InstallGlobalFunction(RightRackByMultiplicationTableNC, T -> MagmaByMultiplicationTableCreatorNC(T, RightRack, IsRightQuotientElement and IsRSelfDistElement and IsMagmaByMultiplicationTableObj ) ); ## And define the operations InstallOtherMethod(LeftQuotient, "for two elts in magma by mult table, when left has left quotients", IsIdenticalObj, [IsLeftQuotientElement, IsMagmaByMultiplicationTableObj], function (l,r) local fam, ix; fam := FamilyObj(l); ix := LeftDivisionTable(fam)[l![1],r![1]]; return fam!.set[ix]; end); InstallOtherMethod(\/, "for two elts in magma by mult table, when right has right quotients", IsIdenticalObj, [IsMagmaByMultiplicationTableObj, IsRightQuotientElement], function (l,r) local fam, ix; fam := FamilyObj(r); ix := RightDivisionTable(fam)[l![1],r![1]]; return fam!.set[ix]; end); ## Create division tables as needed InstallMethod(LeftDivisionTable, "for an object with a multiplication table", [HasMultiplicationTable], function(fam) local LS, n; LS := LeftPerms(fam); n := Size(LS); return List(LS, x->ListPerm(Inverse(x), n)); end); InstallMethod(RightDivisionTable, "for an object with a multiplication table", [HasMultiplicationTable], function (obj) local RS, n; RS := RightPerms(obj); n := Size(RS); return TransposedMat(List(RS, x->ListPerm(Inverse(x), n))); end); ## Create perm lists as needed InstallMethod(LeftPerms, "for an object with a multiplication table", [HasMultiplicationTable], function(fam) return List(MultiplicationTable(fam), x->PermList(x)); end); InstallMethod(RightPerms, "for an object with a muliplication table", [HasMultiplicationTable], function(fam) return List(TransposedMat(MultiplicationTable(fam)), x->PermList(x)); end); ## Distributivity checkers for when need be InstallMethod(IsLSelfDistributive, "for magma", [IsMagma], M -> IsLeftSelfDistributiveTable(MultiplicationTable(M)) ); InstallMethod(IsRSelfDistributive, "for magma", [IsMagma], M -> IsRightSelfDistributiveTable(MultiplicationTable(M)) ); ## View and print and such LeftObjString@ := function(Q) if IsLeftRack(Q) then return "LeftRack"; fi; return "LeftQuasigroup"; end; RightObjString@ := function(Q) if IsRightRack(Q) then 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(DisplayString, "for a one-sided quasigroup", [IsLeftQuasigroup or IsRightQuasigroup], Q -> String(Q)); InstallMethod(Display, "for a left quasigroup with multiplication table", [IsLeftQuasigroup and HasMultiplicationTable], function(Q) Print(LeftObjString@, " 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@, " with ", Size(Q), " elements, generated by ", GeneratorsOfRightQuasigroup(Q), ", with table\n"); Display(MultiplicationTable(Q)); end); LeftObjView@ := function(Q) if HasIsLeftRack(Q) and IsLeftRack(Q) then return " Concatenation(LeftObjView@(Q), ">"); InstallMethod(ViewString, "for a left quasigroup with generators", [IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup], Q -> Concatenation(LeftObjView@(Q), " with ", Size(GeneratorsOfLeftQuasigroup), " 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 ", Size(GeneratorsOfRightQuasigroup), " generators>")); # Patch View/Print/Display for magma by mult objects InstallMethod(String, "for an element of magma by multiplication table", [IsMagmaByMultiplicationTableObj], function(obj) local fam; fam := FamilyObj(obj); if IsBound(fam!.elmNamePrefix) then return Concatenation(fam!.elmNamePrefix, String(obj![1])); fi; return Concatenation("m", String(obj![1])); end); InstallMethod(ViewString, "for an element of magma by multiplication table", [IsMagmaByMultiplicationTableObj], obj -> String(obj)); InstallMethod(DisplayString, "for an element of magma by multiplication table", [IsMagmaByMultiplicationTableObj], obj -> String(obj)); InstallMethod(PrintObj, "for an element of magma by multiplication table", [IsMagmaByMultiplicationTableObj], function(obj) Print(String(obj)); end); ## Special case the Opposite function from LOOPS package, since the opposite ## of a left quasigroup is a right quasigroup and vice versa # Is there a way to do this just once for each direction? InstallMethod(Opposite, "for left quasigroup", [ IsLeftQuasigroup ], L -> RightQuasigroupByMultiplicationTable( TransposedMat(MultiplicationTable(L)) ) ); InstallMethod(Opposite, "for left rack", [ IsLeftRack ], L -> RightRackByMultiplicationTableNC(TransposedMat(MultiplicationTable(L))) ); InstallMethod(Opposite, "for right quasigroup", [ IsRightQuasigroup ], L -> LeftQuasigroupByMultiplicationTable( TransposedMat(MultiplicationTable(L)) ) ); InstallMethod(Opposite, "for right rack", [ IsRightRack ], L -> LeftRackByMultiplicationTableNC(TransposedMat(MultiplicationTable(L))) );