## 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 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 LeftQuasigroupByMultiplicationTableNC( CanonicalCayleyTableOfLeftQuasigroupTable(T)); end); InstallGlobalFunction(LeftQuasigroupByMultiplicationTableNC, T -> MagmaByMultiplicationTableCreatorNC(T, LeftQuasigroup, IsLeftQuotientElement and IsMagmaByMultiplicationTableObj) ); 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 -> MagmaByMultiplicationTableCreatorNC(T, RightQuasigroup, IsRightQuotientElement and IsMagmaByMultiplicationTableObj) ); 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(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 -> MagmaByMultiplicationTableCreatorNC(T, LeftQuandle, IsLeftQuotientElement and IsLSelfDistElement and IsIdempotent 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 ) ); 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 -> MagmaByMultiplicationTableCreatorNC(T, RightQuandle, IsRightQuotientElement and IsRSelfDistElement and IsIdempotent 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/idempotence checkers for when need be InstallMethod(IsLSelfDistributive, "for magma", [IsMagma and IsFinite], M -> IsLeftSelfDistributiveTable(MultiplicationTable(M)) ); InstallMethod(IsRSelfDistributive, "for magma", [IsMagma and IsFinite], M -> IsRightSelfDistributiveTable(MultiplicationTable(M)) ); InstallMethod(IsElementwiseIdempotent, "for magma", [IsMagma and IsFinite], M -> ForAll(Elements(M), m->IsIdempotent(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 InstallMethod(IsBuiltFromMultiplicationTable, "for a collection", [IsCollection], 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))) );