## bytable.gi RAQ Implementation of racks etc. by multiplication tables. ## 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); InstallMethod(IsElementwiseIdempotentTable, "for matrix", [ IsMatrix ], T -> ForAll([1..Length(T)], i->(T[i,i]=i)) ); ## And a general principle: collections from finite families are finite. InstallMethod(IsFinite, "for any collection (with a finite element family)", [IsCollection], function(C) local ef; ef := ElementsFamily(FamilyObj(C)); if HasIsFinite(ef) and IsFinite(ef) then return true; fi; TryNextMethod(); return fail; end); ## And now create them from multiplication tables # First a helper function MagmaNumber@ := 1; MagmaLetters@ := "VABCDEFGHJKMNPSTU"; MagmaBase@ := Length(MagmaLetters@); NextMagmaString@ := function(filts) local str, n; str := "l"; if not "IsLeftQuotientElement" in NamesFilter(filts) then str := "r"; fi; n := MagmaNumber@; MagmaNumber@ := MagmaNumber@ + 1; while n > 0 do Add(str, MagmaLetters@[RemInt(n, MagmaBase@) + 1]); n := QuoInt(n, MagmaBase@); od; return str; end; FiniteMagmaCreator@ := function(tbl, cnstr, filts) local M; M := MagmaByMultiplicationTableCreatorNC( tbl, cnstr, filts and IsMagmaByMultiplicationTableObj); # Is there such a thing as a non-finite table in GAP? Anyhow... SetIsFinite(ElementsFamily(FamilyObj(M)), IsFinite(tbl)); SpecifyElmNamePrefix(M, NextMagmaString@(filts)); return M; end; InstallGlobalFunction(LeftQuasigroupByMultiplicationTable, function(T) if not IsLeftQuasigroupTable(T) then Error("Multiplication table must have each row a permutation of ", "the same entries."); fi; return LeftQuasigroupByMultiplicationTableNC( CanonicalCayleyTableOfLeftQuasigroupTable(T)); end); InstallGlobalFunction(LeftQuasigroupByMultiplicationTableNC, T -> FiniteMagmaCreator@(T, LeftQuasigroupNC, IsLeftQuotientElement) ); InstallGlobalFunction(RightQuasigroupByMultiplicationTable, function(T) if not IsRightQuasigroupTable(T) then Error("Multiplication table must have each row a permutation of ", "the same entries."); fi; return RightQuasigroupByMultiplicationTableNC(CanonicalCayleyTable(T)); end); InstallGlobalFunction(RightQuasigroupByMultiplicationTableNC, T -> FiniteMagmaCreator@(T, RightQuasigroupNC, IsRightQuotientElement) ); 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 -> FiniteMagmaCreator@(T, LeftRackNC, IsLeftQuotientElement and IsLSelfDistElement) ); InstallGlobalFunction(LeftQuandleByMultiplicationTable, 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) and IsElementwiseIdempotentTable(T)) then Error("Multiplication table must be left self-dist and idempotent."); fi; return LeftQuandleByMultiplicationTableNC(T); end); InstallGlobalFunction(LeftQuandleByMultiplicationTableNC, T -> FiniteMagmaCreator@(T, LeftQuandleNC, IsLeftQuotientElement and IsLSelfDistElement and IsIdempotent) ); 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 -> FiniteMagmaCreator@(T, RightRackNC, IsRightQuotientElement and IsRSelfDistElement) ); InstallGlobalFunction(RightQuandleByMultiplicationTable, 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) and IsElementwiseIdempotentTable(T)) then Error("Multiplication table must be right self-dist and idempotent."); fi; return RightQuandleByMultiplicationTableNC(T); end); InstallGlobalFunction(RightQuandleByMultiplicationTableNC, T -> FiniteMagmaCreator@(T, RightQuandleNC, IsRightQuotientElement and IsRSelfDistElement and IsIdempotent) ); ## Creators from permutations InstallGlobalFunction(LeftQuasigroupByPerms, function(perms) local Q; Q := LeftQuasigroupByMultiplicationTableNC( CayleyTableByPerms(perms, [1..Length(perms)])); SetLeftPerms(Q, perms); return Q; end); InstallGlobalFunction(LeftRackByPerms, function(perms) local Q; Q := LeftRackByMultiplicationTable( CayleyTableByPerms(perms, [1..Length(perms)])); SetLeftPerms(Q, perms); return Q; end); InstallGlobalFunction(LeftRackByPermsNC, function(perms) local Q; Q := LeftRackByMultiplicationTableNC( CayleyTableByPerms(perms, [1..Length(perms)])); SetLeftPerms(Q, perms); return Q; end); InstallGlobalFunction(LeftQuandleByPerms, function(perms) local Q; Q := LeftQuandleByMultiplicationTable( CayleyTableByPerms(perms, [1..Length(perms)])); SetLeftPerms(Q, perms); return Q; end); InstallGlobalFunction(LeftQuandleByPermsNC, function(perms) local Q; Q := LeftQuandleByMultiplicationTableNC( CayleyTableByPerms(perms, [1..Length(perms)])); SetLeftPerms(Q, perms); return Q; end); InstallGlobalFunction(RightQuasigroupByPerms, function(perms) local Q; Q := RightQuasigroupByMultiplicationTableNC( TransposedMat( CayleyTableByPerms(perms, [1..Length(perms)]))); SetRightPerms(Q, perms); return Q; end); InstallGlobalFunction(RightRackByPerms, function(perms) local Q; Q := RightRackByMultiplicationTable( TransposedMat( CayleyTableByPerms(perms, [1..Length(perms)]))); SetRightPerms(Q, perms); return Q; end); InstallGlobalFunction(RightRackByPermsNC, function(perms) local Q; Q := RightRackByMultiplicationTableNC( TransposedMat( CayleyTableByPerms(perms, [1..Length(perms)]))); SetRightPerms(Q, perms); return Q; end); InstallGlobalFunction(RightQuandleByPerms, function(perms) local Q; Q := RightQuandleByMultiplicationTable( TransposedMat( CayleyTableByPerms(perms, [1..Length(perms)]))); SetRightPerms(Q, perms); return Q; end); InstallGlobalFunction(RightQuandleByPermsNC, function(perms) local Q; Q := RightQuandleByMultiplicationTableNC( TransposedMat( CayleyTableByPerms(perms, [1..Length(perms)]))); SetRightPerms(Q, perms); return Q; end); ## 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/idempotence checkers for when need be InstallMethod(IsLSelfDistributive, "for collections with multiplication tables", [IsMultiplicativeElementCollection and HasMultiplicationTable], M -> IsLeftSelfDistributiveTable(MultiplicationTable(M)) ); InstallMethod(IsRSelfDistributive, "for collections with multiplication table", [IsMultiplicativeElementCollection and HasMultiplicationTable], M -> IsRightSelfDistributiveTable(MultiplicationTable(M)) ); ## 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); ## Property of a collection that its elements know their multiplication table InstallImmediateMethod(IsBuiltFromMultiplicationTable, IsCollection, 1, C -> HasMultiplicationTable(ElementsFamily(FamilyObj(C))) ); ## 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 and IsBuiltFromMultiplicationTable], L -> RightQuasigroupByMultiplicationTable( TransposedMat(MultiplicationTable(L)) ) ); InstallMethod(Opposite, "for left rack", [ IsLeftRack and IsBuiltFromMultiplicationTable], L -> RightRackByMultiplicationTableNC(TransposedMat(MultiplicationTable(L))) ); InstallMethod(Opposite, "for right quasigroup", [ IsRightQuasigroup and IsBuiltFromMultiplicationTable], L -> LeftQuasigroupByMultiplicationTable( TransposedMat(MultiplicationTable(L)) ) ); InstallMethod(Opposite, "for right rack", [ IsRightRack and IsBuiltFromMultiplicationTable], L -> LeftRackByMultiplicationTableNC(TransposedMat(MultiplicationTable(L))) ); # Note that opposite of quandles seems to come for free from above, which is # good. ## Direct products # As in the case of general one-sided quasigroups, this implementation should # only be called with a second-argument collection which is not a group or # quasigroup. InstallOtherMethod(DirectProductOp, "for a list and non-quasigroup magma built from multiplication table", [IsList, IsMagma and IsBuiltFromMultiplicationTable], function (list, first) local n, item, i, jof, bigtable; n := Length(list); # Simple checks if n < 1 then Error("Usage: Cannot take DirectProduct of zero items."); elif n < 2 then return list[1]; fi; # See if we can handle all objects for i in [2..n] do if not HasMultiplicationTable(list[i]) then return DirectProductOp(Permuted(list, (1,i)), list[i]); fi; od; # OK, safe to take everyone's multiplication table. # So go ahead and make the big thing bigtable := ProductTableOfCanonicalCayleyTables( List(list, MultiplicationTable)); # But we have to figure out what to do with it. jof := RoughJoinOfFilters@(list, first); # Dispatch modeled after the general one if "IsMagmaWithOne" in jof then if "IsMonoid" in jof then return MonoidByMultiplicationTable(bigtable); fi; return MagmaWithOneByMultiplicationTable(bigtable); fi; if "IsAssociative" in jof then return SemigroupByMultiplicationTable(bigtable); elif "IsLeftQuasigroup" in jof then if "IsLSelfDistributive" in jof then if "IsElementwiseIdempotent" in jof then return LeftQuandleByMultiplicationTableNC(bigtable); fi; return LeftRackByMultiplicationTableNC(bigtable); fi; return LeftQuasigroupByMultiplicationTableNC(bigtable); elif "IsRightQuasigroup" in jof then if "IsRSelfDistributive" in jof then if "IsElementwiseIdempotent" in jof then return RightQuandleByMultiplicationTableNC(bigtable); fi; return RightRackByMultiplicationTableNC(bigtable); fi; return RightQuasigroupByMultiplicationTableNC(bigtable); fi; return MagmaByMultiplicationTable(bigtable); end);