Repository: semigroups/Semigroups
Pull request #1054: PermuteMultiplicationTable, refactor OnMultiplicationTable, add docs
Author: login: pramothragavan
Labels: name: waiting-for-creator-input
description: A label for issues or PR that are waiting for input from their creator
Created: 2025-03-26T16:49:49Z | Merged: 2025-04-11T08:33:45Z

This PR
- introduces a new function ``PermuteMultiplicationTable(temp, table, p)``, implemented in C, which permutes a multiplication table ``table`` and stores the result by changing ``temp`` in-place.
- refactors ``OnMultiplicationTable`` to call ``PermuteMultiplicationTable``.

I wasn't sure where to put the function, so I added a new file.


Diff:
diff --git a/GNUmakefile.in b/GNUmakefile.in
index 7940cee7a..d55c7e4ec 100644
--- a/GNUmakefile.in
+++ b/GNUmakefile.in
@@ -28,6 +28,7 @@ KEXT_SOURCES += src/froidure-pin-min-plus-mat.cpp
 KEXT_SOURCES += src/froidure-pin-pbr.cpp
 KEXT_SOURCES += src/froidure-pin-pperm.cpp
 KEXT_SOURCES += src/froidure-pin-transf.cpp
+KEXT_SOURCES += src/isomorph.cpp
 KEXT_SOURCES += src/pkg.cpp
 KEXT_SOURCES += src/to_gap.cpp
 KEXT_SOURCES += gapbind14/src/gapbind14.cpp
diff --git a/doc/isomorph.xml b/doc/isomorph.xml
index 501706668..9a440a04e 100644
--- a/doc/isomorph.xml
+++ b/doc/isomorph.xml
@@ -11,16 +11,22 @@
 <#GAPDoc Label="OnMultiplicationTable">
 <ManSection>
   <Oper Name = "OnMultiplicationTable" Arg = "table, p"/>
-  <Returns>
-    A multiplication table.
-  </Returns>
+  <Oper Name = "PermuteMultiplicationTable" Arg = "output, table, p"/>
+  <Oper Name = "PermuteMultiplicationTableNC" Arg = "output, table, p"/>
   <Description>
-    If <A>table</A> is a multiplication table of a semigroup and the second
-    argument <A>p</A> is a permutation of
-    <C>[1 .. Length(<A>table</A>)]</C>, then this operation returns a
-    multiplication table of a semigroup isomorphic to that defined by
-    <A>table</A> where the elements <C>[1 .. Length(<A>table</A>)]</C> are
-    relabelled according to <A>p</A>.
+    If <A>table</A> is a multiplication table of a semigroup and <A>p</A> is a permutation of
+    <C>[1 .. Length(<A>table</A>)]</C>, then <C>OnMultiplicationTable</C> returns the
+    multiplication table obtained from <A>table</A> where the elements <C>[1 .. Length(<A>table</A>)]</C> are
+    relabelled according to <A>p</A>.<P/>
+
+    <C>PermuteMultiplicationTable</C> works similarly but takes an additional argument <A>output</A>, 
+    which should be a list of mutable lists of the same dimensions as <A>table</A>.
+    The argument <A>output</A> is modified in-place to store the multiplication table that would be given by 
+    <C>OnMultiplicationTable(<A>table</A>, <A>p</A>)</C>, while the argument <A>table</A> remains unchanged.
+    This approach is preferred over <C>OnMultiplicationTable</C> when the user does not need to create a separate result variable.<P/>
+
+    <C>PermuteMultiplicationTableNC</C> is the operation called by <C>PermuteMultiplicationTable</C>. It assumes that the 
+    arguments <A>output</A>, <A>table</A> and <A>p</A> are well-formed and can therefore lead to unexpected results. <P/>
 
     <Example><![CDATA[
 gap> table := [[1, 1, 3, 4, 5, 6, 7, 8],
@@ -36,7 +42,12 @@ gap> OnMultiplicationTable(table, p);
 [ [ 1, 2, 4, 4, 5, 6, 7, 8 ], [ 1, 2, 2, 4, 5, 6, 7, 8 ],
   [ 1, 2, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 2, 4, 5, 6, 7, 8 ],
   [ 7, 5, 5, 6, 5, 6, 7, 8 ], [ 7, 5, 5, 6, 5, 6, 7, 8 ],
-  [ 7, 5, 6, 6, 5, 6, 7, 8 ], [ 7, 5, 6, 6, 5, 6, 7, 8 ] ]]]></Example>
+  [ 7, 5, 6, 6, 5, 6, 7, 8 ], [ 7, 5, 6, 6, 5, 6, 7, 8 ] ]
+gap> temp := List([1 .. Size(table)], x -> [1 .. Size(table)]);;
+gap> PermuteMultiplicationTable(temp, table, p);;
+gap> temp = OnMultiplicationTable(table, p);
+true]]>
+  </Example>
   </Description>
 </ManSection>
 <#/GAPDoc>
diff --git a/gap/attributes/isomorph.gi b/gap/attributes/isomorph.gi
index 83016606d..f0e92622e 100644
--- a/gap/attributes/isomorph.gi
+++ b/gap/attributes/isomorph.gi
@@ -19,9 +19,8 @@ InstallMethod(OnMultiplicationTable, "for a multiplication table, and perm",
 [IsRectangularTable, IsPerm],
 function(table, p)
   local out;
-  out := Permuted(table, p);
-  Apply(out, x -> OnTuples(x, p));
-  Apply(out, x -> Permuted(x, p));
+  out := List(table, ShallowCopy);
+  PermuteMultiplicationTable(out, table, p);
   return out;
 end);
 
diff --git a/src/isomorph.cpp b/src/isomorph.cpp
new file mode 100644
index 000000000..cddb56b65
--- /dev/null
+++ b/src/isomorph.cpp
@@ -0,0 +1,152 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2025 Pramoth Ragavan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTableNC(Obj self, Obj output, Obj table, Obj p) {
+  SEMIGROUPS_ASSERT(IS_LIST(table));
+  UInt n = LEN_LIST(table);
+  SEMIGROUPS_ASSERT(IS_LIST(output));
+  SEMIGROUPS_ASSERT(IS_PERM(p));
+  SEMIGROUPS_ASSERT(LEN_LIST(output) == n);
+
+  for (UInt i = 1; i <= n; i++) {
+    SEMIGROUPS_ASSERT(IS_LIST(ELM_LIST(table, i)));
+    SEMIGROUPS_ASSERT(IS_LIST(ELM_LIST(output, i)));
+    SEMIGROUPS_ASSERT(LEN_LIST(ELM_LIST(table, i)) == n);
+    SEMIGROUPS_ASSERT(LEN_LIST(ELM_LIST(output, i)) == n);
+
+    for (UInt j = 1; j <= n; j++) {
+      Obj elem = ELM_LIST(ELM_LIST(table, i), j);
+      SEMIGROUPS_ASSERT(IS_INTOBJ(elem));
+      UInt val = INT_INTOBJ(elem);
+      SEMIGROUPS_ASSERT(val >= 1 && val <= n);
+    }
+  }
+
+  if (TNUM_OBJ(p) == T_PERM2) {
+    UInt deg_p = DEG_PERM2(p);
+    Obj  q     = STOREDINV_PERM(p);
+    if (q == 0) {
+      q = INV(p);
+      SET_STOREDINV_PERM(p, q);
+    }
+    SEMIGROUPS_ASSERT(TNUM_OBJ(q) == T_PERM2);
+    UInt deg_q = DEG_PERM2(q);
+    for (UInt i = 1; i <= n; i++) {
+      Obj  row = ELM_LIST(output, i);
+      UInt ii  = IMAGE(i - 1, CONST_ADDR_PERM2(q), deg_q) + 1;
+
+      for (UInt j = 1; j <= n; j++) {
+        UInt home    = INT_INTOBJ(ELM_LIST(
+            ELM_LIST(table, ii), IMAGE(j - 1, CONST_ADDR_PERM2(q), deg_q) + 1));
+        UInt new_val = IMAGE(home - 1, CONST_ADDR_PERM2(p), deg_p) + 1;
+        ASS_LIST(row, j, INTOBJ_INT(new_val));
+      }
+    }
+  } else {
+    UInt deg_p = DEG_PERM4(p);
+    Obj  q     = STOREDINV_PERM(p);
+    if (q == 0) {
+      q = INV(p);
+      SET_STOREDINV_PERM(p, q);
+    }
+    SEMIGROUPS_ASSERT(TNUM_OBJ(q) == T_PERM4);
+    UInt deg_q = DEG_PERM4(q);
+    for (UInt i = 1; i <= n; i++) {
+      Obj  row = ELM_LIST(output, i);
+      UInt ii  = IMAGE(i - 1, CONST_ADDR_PERM4(q), deg_q) + 1;
+
+      for (UInt j = 1; j <= n; j++) {
+        UInt home    = INT_INTOBJ(ELM_LIST(
+            ELM_LIST(table, ii), IMAGE(j - 1, CONST_ADDR_PERM4(q), deg_q) + 1));
+        UInt new_val = IMAGE(home - 1, CONST_ADDR_PERM4(p), deg_p) + 1;
+        ASS_LIST(row, j, INTOBJ_INT(new_val));
+      }
+    }
+  }
+  CHANGED_BAG(output);
+  return 0L;
+}
+
+Obj PermuteMultiplicationTable(Obj self, Obj output, Obj table, Obj p) {
+  if (!IS_LIST(output) || !IS_LIST(table)) {
+    ErrorMayQuit("the arguments <output> and <table> must be lists but found "
+                 "types '%s' and '%s' respectively",
+                 (Int) TNAM_OBJ(output),
+                 (Int) TNAM_OBJ(table));
+  }
+
+  UInt n = LEN_LIST(table);
+
+  if (LEN_LIST(output) != n) {
+    ErrorMayQuit("the arguments <output> and <table> must have the same "
+                 "dimensions but found lengths %d and %d, respectively",
+                 LEN_LIST(output),
+                 LEN_LIST(table));
+  }
+
+  for (UInt i = 1; i <= n; i++) {
+    if (!IS_LIST(ELM_LIST(output, i))) {
+      ErrorMayQuit("the elements of the first argument <output> must be lists, "
+                   "but found an element of type '%s' at position %d",
+                   (Int) TNAM_OBJ(ELM_LIST(output, i)),
+                   i);
+    }
+    if (!IS_LIST(ELM_LIST(table, i))) {
+      ErrorMayQuit("the elements of the second argument <table> must be lists, "
+                   "but found an element of type '%s' at position %d",
+                   (Int) TNAM_OBJ(ELM_LIST(table, i)),
+                   i);
+    }
+    if (LEN_LIST(ELM_LIST(output, i)) != n) {
+      ErrorMayQuit("the first argument <output> must be a square table, "
+                   "(expected %d elements per row, but "
+                   "found a row of length %d)",
+                   n,
+                   LEN_LIST(ELM_LIST(output, i)));
+    }
+    if (LEN_LIST(ELM_LIST(table, i)) != n) {
+      ErrorMayQuit("the second argument <table> must be a square table, "
+                   "(expected %d elements per row, but "
+                   "found a row of length %d)",
+                   n,
+                   LEN_LIST(ELM_LIST(output, i)));
+    }
+    for (UInt j = 1; j <= n; j++) {
+      Obj elem = ELM_LIST(ELM_LIST(table, i), j);
+      if (!IS_INTOBJ(elem) || INT_INTOBJ(elem) < 1 || INT_INTOBJ(elem) > n) {
+        ErrorMayQuit("all entries in the second argument <table> must be "
+                     "positive integers from 1 to "
+                     "Size(<table>) = %d",
+                     LEN_LIST(table),
+                     0L);
+      }
+    }
+  }
+  if (!IS_PERM(p)) {
+    ErrorMayQuit("the argument <p> must be a permutation but found type %s",
+                 (Int) TNAM_OBJ(p),
+                 0L);
+  }
+
+  PermuteMultiplicationTableNC(self, output, table, p);
+  return 0L;
+}
diff --git a/src/isomorph.hpp b/src/isomorph.hpp
new file mode 100644
index 000000000..6e71648ac
--- /dev/null
+++ b/src/isomorph.hpp
@@ -0,0 +1,36 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2025 Pramoth Ragavan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#ifndef SEMIGROUPS_SRC_ISOMORPH_HPP_
+#define SEMIGROUPS_SRC_ISOMORPH_HPP_
+
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Obj PermuteMultiplicationTableNC(Obj self, Obj temp, Obj M, Obj p);
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj M, Obj p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // SEMIGROUPS_SRC_ISOMORPH_HPP_
diff --git a/src/pkg.cpp b/src/pkg.cpp
index ae8317854..5f9e9f6e0 100644
--- a/src/pkg.cpp
+++ b/src/pkg.cpp
@@ -41,6 +41,7 @@
 #include "conglatt.hpp"
 #include "froidure-pin-fallback.hpp"  // for RUN_FROIDURE_PIN
 #include "froidure-pin.hpp"           // for init_froidure_pin
+#include "isomorph.hpp"               // for permuting multiplication tables
 #include "semigroups-debug.hpp"       // for SEMIGROUPS_ASSERT
 #include "to_cpp.hpp"                 // for to_cpp
 #include "to_gap.hpp"                 // for to_gap
@@ -481,6 +482,8 @@ static StructGVarFunc GVarFuncs[] = {
                BIPART_NR_IDEMPOTENTS,
                4,
                "o, scc, lookup, nr_threads"),
+    GVAR_ENTRY("isomorph.cpp", PermuteMultiplicationTableNC, 3, "temp, M, p"),
+    GVAR_ENTRY("isomorph.cpp", PermuteMultiplicationTable, 3, "temp, M, p"),
 
     {0, 0, 0, 0, 0} /* Finish with an empty entry */
 };
diff --git a/tst/standard/attributes/isomorph.tst b/tst/standard/attributes/isomorph.tst
index 428e3dd2d..b45c18c88 100644
--- a/tst/standard/attributes/isomorph.tst
+++ b/tst/standard/attributes/isomorph.tst
@@ -9,7 +9,7 @@
 ##
 
 #@local A, BruteForceInverseCheck, BruteForceIsoCheck, F, G, S, T, U, V, inv
-#@local map, x, y, M, N, R, L
+#@local map, x, y, M, N, R, L, OldOnMultiplicationTable, p, out
 gap> START_TEST("Semigroups package: standard/attributes/isomorph.tst");
 gap> LoadPackage("semigroups", false);;
 
@@ -430,6 +430,55 @@ gap> AutomorphismGroup(S);
 Error, no method found! For debugging hints type ?Recovery from NoMethodFound
 Error, no 2nd choice method found for `AutomorphismGroup' on 1 arguments
 
+# OnMultiplicationTable/PermuteMultiplicationTable
+gap> OldOnMultiplicationTable := function(table, p)
+>  local out;
+>  out := Permuted(table, p);
+>  Apply(out, x -> OnTuples(x, p));
+>  Apply(out, x -> Permuted(x, p));
+>  return out;
+> end;;
+gap> S := MultiplicationTable(DualSymmetricInverseMonoid(2));;
+gap> p := (1, 2);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (1, 3);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (2, 3);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (1, 2, 3);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (1, 2, 3, 4);;
+gap> OnMultiplicationTable(S, p);
+Error, List Element: <list>[4] must have an assigned value
+gap> PermuteMultiplicationTable(List([1 .. 3], x -> [1 .. 3]), S, 1);
+Error, the argument <p> must be a permutation but found type integer
+gap> PermuteMultiplicationTable(List([1 .. 4], x -> [1 .. 4]), S, (1, 2));
+Error, the arguments <output> and <table> must have the same dimensions but fo\
+und lengths 4 and 3, respectively
+gap> M := [[1 .. 4], [1 .. 4]];;
+gap> PermuteMultiplicationTable(M, S, (1, 2));
+Error, the arguments <output> and <table> must have the same dimensions but fo\
+und lengths 2 and 3, respectively
+gap> S := M;;
+gap> PermuteMultiplicationTable(M, S, (1, 2));
+Error, the first argument <output> must be a square table, (expected 2 element\
+s per row, but found a row of length 4)
+gap> S := MultiplicationTable(SymmetricInverseMonoid(2));;
+gap> M := List([1 .. 7], x -> [1 .. 7]);;
+gap> p := (1, 4, 2, 5, 7);;
+gap> OnMultiplicationTable(S, p) = M;
+false
+gap> PermuteMultiplicationTable(M, S, p);
+gap> OnMultiplicationTable(S, p) = M;
+true
+gap> OnMultiplicationTable([[1, 3], [1, 2]], (1, 2));
+Error, all entries in the second argument <table> must be positive integers fr\
+om 1 to Size(<table>) = 2
+
 #
 gap> SEMIGROUPS.StopTest();
 gap> STOP_TEST("Semigroups package: standard/attributes/isomorph.tst");


Reviews:
user: login: james-d-mitchell
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T08:55:49Z
user: login: james-d-mitchell
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T08:56:03Z
user: login: james-d-mitchell
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T08:57:20Z
user: login: james-d-mitchell
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T08:59:00Z
user: login: james-d-mitchell
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T09:00:01Z
user: login: james-d-mitchell
body: Looks generally great, a number of minor things to adjust, and then one major one: I'd suggest implementing `PermuteMultiplicationTableNC` in the C code which would not do any of the checks at the start of the current `PermuteMultiplicationTable`, just assume that they hold. Then modify `PermuteMultiplicationTable` to call that function after doing the checks. You could add assertions to `PermuteMultiplicationTableNC` to check that the requirements are met, when compiled without `--enable-debug` these checks will not be performed.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-10T07:35:49Z
user: login: pramothragavan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T12:36:26Z

Review comments:
diff_hunk: @@ -0,0 +1,61 @@
+#include "gap_all.h"
+#include "isomorph.hpp"
+
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj M, Obj p) {
+    int n = LEN_LIST(M);
path: src/isomorph.cpp
user: login: james-d-mitchell
body: What if `temp` isn't a list? I think you need to check `IS_LIST` and give an error if not. 

created_at: 2025-03-27T08:55:48Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,61 @@
+#include "gap_all.h"
+#include "isomorph.hpp"
+
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj M, Obj p) {
+    int n = LEN_LIST(M);
+
+    if (LEN_LIST(temp) != n) {
+        ErrorQuit("the arguments <temp> and <table> must have the same dimensions", 0L, 0L);
+    }
+    for (int i = 1; i <= n; i++) {
+        if (LEN_LIST(ELM_LIST(temp, i)) != n) {
+            ErrorQuit("the arguments <temp> and <table> must have the same dimensions", 0L, 0L);
+        }
+    }
+
+    if (IS_PERM(p) == 0) {
+        ErrorQuit("the argument <p> must be a permutation", 0L, 0L);
+    }
+  
+    int p_type = TNUM_OBJ(p);
+    if (p_type == T_PERM2) {
+        int deg_p = DEG_PERM2(p);
+        Obj q = STOREDINV_PERM(p);
+        if (q == 0) {
+            q = INV(p);
+            SET_STOREDINV_PERM(p, q);
+          }
path: src/isomorph.cpp
user: login: james-d-mitchell
body: Weird formatting
created_at: 2025-03-27T08:56:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,61 @@
+#include "gap_all.h"
+#include "isomorph.hpp"
+
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj M, Obj p) {
+    int n = LEN_LIST(M);
+
+    if (LEN_LIST(temp) != n) {
+        ErrorQuit("the arguments <temp> and <table> must have the same dimensions", 0L, 0L);
+    }
+    for (int i = 1; i <= n; i++) {
+        if (LEN_LIST(ELM_LIST(temp, i)) != n) {
+            ErrorQuit("the arguments <temp> and <table> must have the same dimensions", 0L, 0L);
+        }
+    }
+
+    if (IS_PERM(p) == 0) {
+        ErrorQuit("the argument <p> must be a permutation", 0L, 0L);
+    }
+  
+    int p_type = TNUM_OBJ(p);
+    if (p_type == T_PERM2) {
+        int deg_p = DEG_PERM2(p);
+        Obj q = STOREDINV_PERM(p);
+        if (q == 0) {
+            q = INV(p);
+            SET_STOREDINV_PERM(p, q);
+          }
+        int deg_q = DEG_PERM2(q);
+        for (int i = 1; i <= n; i++) {
+          Obj row = ELM_LIST(temp, i);
+          int ii = IMAGE(i - 1, CONST_ADDR_PERM2(q), deg_q) + 1;
+      
+          for (int j = 1; j <= n; j++) {
+            int home = Int_ObjInt(ELM_LIST(ELM_LIST(M, ii), IMAGE(j - 1, CONST_ADDR_PERM2(q), deg_q) + 1));
+            int new_val = IMAGE(home - 1, CONST_ADDR_PERM2(p), deg_p) + 1;
+            ASS_LIST(row, j, INTOBJ_INT(new_val));
+          }
+        }
+      } else {
+        int deg_p = DEG_PERM4(p);
+        Obj q = STOREDINV_PERM(p);
+        if (q == 0) {
+            q = INV(p);
+            SET_STOREDINV_PERM(p, q);
+          }
+        int deg_q = DEG_PERM4(q);
path: src/isomorph.cpp
user: login: james-d-mitchell
body: Can you maybe add an assertion here that checks that `q` is a `PERM4`?
created_at: 2025-03-27T08:57:20Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,61 @@
+#include "gap_all.h"
+#include "isomorph.hpp"
+
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj M, Obj p) {
+    int n = LEN_LIST(M);
+
+    if (LEN_LIST(temp) != n) {
+        ErrorQuit("the arguments <temp> and <table> must have the same dimensions", 0L, 0L);
+    }
+    for (int i = 1; i <= n; i++) {
path: src/isomorph.cpp
user: login: james-d-mitchell
body: Also need to check that each item in `temp` and `M` are satisfy `IS_LIST`
created_at: 2025-03-27T08:59:00Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,61 @@
+#include "gap_all.h"
+#include "isomorph.hpp"
+
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj M, Obj p) {
+    int n = LEN_LIST(M);
+
+    if (LEN_LIST(temp) != n) {
+        ErrorQuit("the arguments <temp> and <table> must have the same dimensions", 0L, 0L);
+    }
+    for (int i = 1; i <= n; i++) {
+        if (LEN_LIST(ELM_LIST(temp, i)) != n) {
+            ErrorQuit("the arguments <temp> and <table> must have the same dimensions", 0L, 0L);
+        }
+    }
+
+    if (IS_PERM(p) == 0) {
+        ErrorQuit("the argument <p> must be a permutation", 0L, 0L);
+    }
+  
+    int p_type = TNUM_OBJ(p);
+    if (p_type == T_PERM2) {
+        int deg_p = DEG_PERM2(p);
+        Obj q = STOREDINV_PERM(p);
+        if (q == 0) {
+            q = INV(p);
+            SET_STOREDINV_PERM(p, q);
+          }
+        int deg_q = DEG_PERM2(q);
+        for (int i = 1; i <= n; i++) {
+          Obj row = ELM_LIST(temp, i);
+          int ii = IMAGE(i - 1, CONST_ADDR_PERM2(q), deg_q) + 1;
+      
+          for (int j = 1; j <= n; j++) {
path: src/isomorph.cpp
user: login: james-d-mitchell
body: You probably need to check that the elements in the lists are integers too
created_at: 2025-03-27T09:00:01Z
author_association: COLLABORATOR
diff_hunk: @@ -11,16 +11,20 @@
 <#GAPDoc Label="OnMultiplicationTable">
 <ManSection>
   <Oper Name = "OnMultiplicationTable" Arg = "table, p"/>
-  <Returns>
-    A multiplication table.
-  </Returns>
+  <Oper Name = "PermuteMultiplicationTable" Arg = "temp, table, p"/>
path: doc/isomorph.xml
user: login: james-d-mitchell
body: ```suggestion
  <Oper Name = "PermuteMultiplicationTable" Arg = "output, input, p"/>
```
Just wondering if these might be slightly clearer variable names?
created_at: 2025-04-10T06:45:17Z
author_association: COLLABORATOR
diff_hunk: @@ -11,16 +11,20 @@
 <#GAPDoc Label="OnMultiplicationTable">
 <ManSection>
   <Oper Name = "OnMultiplicationTable" Arg = "table, p"/>
-  <Returns>
-    A multiplication table.
-  </Returns>
+  <Oper Name = "PermuteMultiplicationTable" Arg = "temp, table, p"/>
   <Description>
     If <A>table</A> is a multiplication table of a semigroup and the second
path: doc/isomorph.xml
user: login: james-d-mitchell
body: ```suggestion
    If <A>table</A> is a multiplication table of a semigroup and the
```
created_at: 2025-04-10T06:45:39Z
author_association: COLLABORATOR
diff_hunk: @@ -11,16 +11,20 @@
 <#GAPDoc Label="OnMultiplicationTable">
 <ManSection>
   <Oper Name = "OnMultiplicationTable" Arg = "table, p"/>
-  <Returns>
-    A multiplication table.
-  </Returns>
+  <Oper Name = "PermuteMultiplicationTable" Arg = "temp, table, p"/>
   <Description>
     If <A>table</A> is a multiplication table of a semigroup and the second
     argument <A>p</A> is a permutation of
-    <C>[1 .. Length(<A>table</A>)]</C>, then this operation returns a
+    <C>[1 .. Length(<A>table</A>)]</C>, then <C>OnMultiplicationTable</C> returns a
path: doc/isomorph.xml
user: login: james-d-mitchell
body: ```suggestion
    <C>[1 .. Length(<A>table</A>)]</C>, then <C>OnMultiplicationTable</C> returns the
```
created_at: 2025-04-10T06:46:22Z
author_association: COLLABORATOR
diff_hunk: @@ -11,16 +11,20 @@
 <#GAPDoc Label="OnMultiplicationTable">
 <ManSection>
   <Oper Name = "OnMultiplicationTable" Arg = "table, p"/>
-  <Returns>
-    A multiplication table.
-  </Returns>
+  <Oper Name = "PermuteMultiplicationTable" Arg = "temp, table, p"/>
   <Description>
     If <A>table</A> is a multiplication table of a semigroup and the second
     argument <A>p</A> is a permutation of
-    <C>[1 .. Length(<A>table</A>)]</C>, then this operation returns a
+    <C>[1 .. Length(<A>table</A>)]</C>, then <C>OnMultiplicationTable</C> returns a
     multiplication table of a semigroup isomorphic to that defined by
path: doc/isomorph.xml
user: login: james-d-mitchell
body: ```suggestion
    multiplication table obtained from <A>table</A> by
```
created_at: 2025-04-10T06:46:49Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
path: src/isomorph.cpp
user: login: james-d-mitchell
body: ```suggestion
// Copyright (C) 2025 Pramoth Ragavan
```
created_at: 2025-04-10T06:49:51Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
+    ErrorQuit("the arguments <temp> and <table> must be lists", 0L, 0L);
+  }
+
+  int n = LEN_LIST(table);
+
+  if (LEN_LIST(temp) != n) {
+    ErrorQuit("the arguments <temp> and <table> must have the same dimensions",
+              0L,
+              0L);
+  }
+
+  for (int i = 1; i <= n; i++) {
+    if (IS_LIST(ELM_LIST(temp, i)) == 0 || IS_LIST(ELM_LIST(table, i)) == 0) {
+      ErrorQuit("the elements of <temp> and <table> must be lists", 0L, 0L);
+    }
+    if (LEN_LIST(ELM_LIST(temp, i)) != n || LEN_LIST(ELM_LIST(table, i)) != n) {
+      ErrorQuit("the arguments <temp> and <table> must be square tables of the "
+                "same dimensions",
+                0L,
+                0L);
+    }
+    for (int j = 1; j <= n; j++) {
+      Obj elem = ELM_LIST(ELM_LIST(table, i), j);
+      if (!IS_INTOBJ(elem) || Int_ObjInt(elem) < 1 || Int_ObjInt(elem) > n) {
+        ErrorQuit("all entries in <table> must be positive integers from 1 to "
+                  "Size(<table>)",
+                  0L,
+                  0L);
+      }
+    }
+  }
+
+  if (IS_PERM(p) == 0) {
+    ErrorQuit("the argument <p> must be a permutation", 0L, 0L);
+  }
+
+  int p_type = TNUM_OBJ(p);
+  if (p_type == T_PERM2) {
path: src/isomorph.cpp
user: login: james-d-mitchell
body: ```suggestion
  if (TNUM_OBJ(p) == T_PERM2) {
```
created_at: 2025-04-10T06:51:57Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
path: src/isomorph.cpp
user: login: james-d-mitchell
body: ```suggestion
  if (!IS_LIST(temp) || !IS_LIST(table)) {
```
created_at: 2025-04-10T06:52:22Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
+    ErrorQuit("the arguments <temp> and <table> must be lists", 0L, 0L);
+  }
+
+  int n = LEN_LIST(table);
+
+  if (LEN_LIST(temp) != n) {
+    ErrorQuit("the arguments <temp> and <table> must have the same dimensions",
path: src/isomorph.cpp
user: login: james-d-mitchell
body: ```suggestion
    ErrorQuit("the arguments <temp> and <table> must have the same dimensions but found lengths %d and %d, respectively", LEN_LIST(temp), LEN_LIST(table));
```
You might have to cast the `LEN_LIST`s here to `Int` not sure.
created_at: 2025-04-10T06:53:30Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
+    ErrorQuit("the arguments <temp> and <table> must be lists", 0L, 0L);
+  }
+
+  int n = LEN_LIST(table);
+
+  if (LEN_LIST(temp) != n) {
+    ErrorQuit("the arguments <temp> and <table> must have the same dimensions",
+              0L,
+              0L);
+  }
+
+  for (int i = 1; i <= n; i++) {
+    if (IS_LIST(ELM_LIST(temp, i)) == 0 || IS_LIST(ELM_LIST(table, i)) == 0) {
path: src/isomorph.cpp
user: login: james-d-mitchell
body: I'd go for more specificity in the error here: check `temp` first, and report which entry is not a list (and its type using `TNAM_OBJ`. Then do the same with `table`.  I can write more about what I mean, if this is not clear.
created_at: 2025-04-10T06:54:58Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
+    ErrorQuit("the arguments <temp> and <table> must be lists", 0L, 0L);
+  }
+
+  int n = LEN_LIST(table);
+
+  if (LEN_LIST(temp) != n) {
+    ErrorQuit("the arguments <temp> and <table> must have the same dimensions",
+              0L,
+              0L);
+  }
+
+  for (int i = 1; i <= n; i++) {
+    if (IS_LIST(ELM_LIST(temp, i)) == 0 || IS_LIST(ELM_LIST(table, i)) == 0) {
+      ErrorQuit("the elements of <temp> and <table> must be lists", 0L, 0L);
+    }
+    if (LEN_LIST(ELM_LIST(temp, i)) != n || LEN_LIST(ELM_LIST(table, i)) != n) {
+      ErrorQuit("the arguments <temp> and <table> must be square tables of the "
+                "same dimensions",
+                0L,
+                0L);
+    }
+    for (int j = 1; j <= n; j++) {
+      Obj elem = ELM_LIST(ELM_LIST(table, i), j);
+      if (!IS_INTOBJ(elem) || Int_ObjInt(elem) < 1 || Int_ObjInt(elem) > n) {
+        ErrorQuit("all entries in <table> must be positive integers from 1 to "
+                  "Size(<table>)",
path: src/isomorph.cpp
user: login: james-d-mitchell
body: ```suggestion
                  "Size(<table>) = %d", LEN_LIST(table), 0L);
```
created_at: 2025-04-10T06:55:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
+    ErrorQuit("the arguments <temp> and <table> must be lists", 0L, 0L);
+  }
+
+  int n = LEN_LIST(table);
+
+  if (LEN_LIST(temp) != n) {
+    ErrorQuit("the arguments <temp> and <table> must have the same dimensions",
+              0L,
+              0L);
+  }
+
+  for (int i = 1; i <= n; i++) {
+    if (IS_LIST(ELM_LIST(temp, i)) == 0 || IS_LIST(ELM_LIST(table, i)) == 0) {
+      ErrorQuit("the elements of <temp> and <table> must be lists", 0L, 0L);
+    }
+    if (LEN_LIST(ELM_LIST(temp, i)) != n || LEN_LIST(ELM_LIST(table, i)) != n) {
+      ErrorQuit("the arguments <temp> and <table> must be square tables of the "
+                "same dimensions",
+                0L,
+                0L);
+    }
+    for (int j = 1; j <= n; j++) {
+      Obj elem = ELM_LIST(ELM_LIST(table, i), j);
+      if (!IS_INTOBJ(elem) || Int_ObjInt(elem) < 1 || Int_ObjInt(elem) > n) {
+        ErrorQuit("all entries in <table> must be positive integers from 1 to "
+                  "Size(<table>)",
+                  0L,
+                  0L);
+      }
+    }
+  }
+
+  if (IS_PERM(p) == 0) {
path: src/isomorph.cpp
user: login: james-d-mitchell
body: ```suggestion
  if (!IS_PERM(p)) {
```
created_at: 2025-04-10T06:56:22Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
+    ErrorQuit("the arguments <temp> and <table> must be lists", 0L, 0L);
+  }
+
+  int n = LEN_LIST(table);
+
+  if (LEN_LIST(temp) != n) {
+    ErrorQuit("the arguments <temp> and <table> must have the same dimensions",
+              0L,
+              0L);
+  }
+
+  for (int i = 1; i <= n; i++) {
+    if (IS_LIST(ELM_LIST(temp, i)) == 0 || IS_LIST(ELM_LIST(table, i)) == 0) {
+      ErrorQuit("the elements of <temp> and <table> must be lists", 0L, 0L);
+    }
+    if (LEN_LIST(ELM_LIST(temp, i)) != n || LEN_LIST(ELM_LIST(table, i)) != n) {
+      ErrorQuit("the arguments <temp> and <table> must be square tables of the "
+                "same dimensions",
+                0L,
+                0L);
+    }
+    for (int j = 1; j <= n; j++) {
+      Obj elem = ELM_LIST(ELM_LIST(table, i), j);
+      if (!IS_INTOBJ(elem) || Int_ObjInt(elem) < 1 || Int_ObjInt(elem) > n) {
+        ErrorQuit("all entries in <table> must be positive integers from 1 to "
+                  "Size(<table>)",
+                  0L,
+                  0L);
+      }
+    }
+  }
+
+  if (IS_PERM(p) == 0) {
+    ErrorQuit("the argument <p> must be a permutation", 0L, 0L);
+  }
+
+  int p_type = TNUM_OBJ(p);
+  if (p_type == T_PERM2) {
+    int deg_p = DEG_PERM2(p);
+    Obj q     = STOREDINV_PERM(p);
+    if (q == 0) {
+      q = INV(p);
+      SET_STOREDINV_PERM(p, q);
+    }
+    SEMIGROUPS_ASSERT(TNUM_OBJ(q) == T_PERM2);
+    int deg_q = DEG_PERM2(q);
+    for (int i = 1; i <= n; i++) {
path: src/isomorph.cpp
user: login: james-d-mitchell
body: I'm a bit uncomfortable with the use of `int`s here, a quick look in the source code of GAP `permutat.cc` indicates that they are using `UInt` instead for similar variables. Perhaps these resolve to the same thing, but for consistency, can you please change `int` to `UInt` everywhere in this function?
created_at: 2025-04-10T07:00:51Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,105 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+
+#include "isomorph.hpp"
+#include "gap_all.h"
+#include "semigroups-debug.hpp"  // for SEMIGROUPS_ASSERT
+
+Obj PermuteMultiplicationTable(Obj self, Obj temp, Obj table, Obj p) {
+  if (IS_LIST(temp) == 0 || IS_LIST(table) == 0) {
+    ErrorQuit("the arguments <temp> and <table> must be lists", 0L, 0L);
+  }
+
+  int n = LEN_LIST(table);
+
+  if (LEN_LIST(temp) != n) {
+    ErrorQuit("the arguments <temp> and <table> must have the same dimensions",
+              0L,
+              0L);
+  }
+
+  for (int i = 1; i <= n; i++) {
+    if (IS_LIST(ELM_LIST(temp, i)) == 0 || IS_LIST(ELM_LIST(table, i)) == 0) {
+      ErrorQuit("the elements of <temp> and <table> must be lists", 0L, 0L);
+    }
+    if (LEN_LIST(ELM_LIST(temp, i)) != n || LEN_LIST(ELM_LIST(table, i)) != n) {
+      ErrorQuit("the arguments <temp> and <table> must be square tables of the "
+                "same dimensions",
+                0L,
+                0L);
+    }
+    for (int j = 1; j <= n; j++) {
+      Obj elem = ELM_LIST(ELM_LIST(table, i), j);
+      if (!IS_INTOBJ(elem) || Int_ObjInt(elem) < 1 || Int_ObjInt(elem) > n) {
+        ErrorQuit("all entries in <table> must be positive integers from 1 to "
+                  "Size(<table>)",
+                  0L,
+                  0L);
+      }
+    }
+  }
+
+  if (IS_PERM(p) == 0) {
+    ErrorQuit("the argument <p> must be a permutation", 0L, 0L);
+  }
+
+  int p_type = TNUM_OBJ(p);
+  if (p_type == T_PERM2) {
+    int deg_p = DEG_PERM2(p);
+    Obj q     = STOREDINV_PERM(p);
+    if (q == 0) {
+      q = INV(p);
+      SET_STOREDINV_PERM(p, q);
+    }
+    SEMIGROUPS_ASSERT(TNUM_OBJ(q) == T_PERM2);
+    int deg_q = DEG_PERM2(q);
+    for (int i = 1; i <= n; i++) {
+      Obj row = ELM_LIST(temp, i);
+      int ii  = IMAGE(i - 1, CONST_ADDR_PERM2(q), deg_q) + 1;
+
+      for (int j = 1; j <= n; j++) {
+        int home    = Int_ObjInt(ELM_LIST(
path: src/isomorph.cpp
user: login: james-d-mitchell
body: ```suggestion
        int home    = INT_INTOBJ(ELM_LIST(
```
and the same change elsewhere, it seems like there's no point in using `Int_ObjInt` but there is some overhead, so let's not.
created_at: 2025-04-10T07:02:24Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,35 @@
+//
+// Semigroups package for GAP
+// Copyright (C) 2016 James D. Mitchell
path: src/isomorph.hpp
user: login: james-d-mitchell
body: Same change as above
created_at: 2025-04-10T07:03:53Z
author_association: COLLABORATOR
diff_hunk: @@ -430,6 +430,48 @@ gap> AutomorphismGroup(S);
 Error, no method found! For debugging hints type ?Recovery from NoMethodFound
 Error, no 2nd choice method found for `AutomorphismGroup' on 1 arguments
 
-#
+# OnMultiplicationTable/PermuteMultiplicationTable
+gap> OldOnMultiplicationTable := function(table, p)
+>  local out;
+>  out := Permuted(table, p);
+>  Apply(out, x -> OnTuples(x, p));
+>  Apply(out, x -> Permuted(x, p));
+>  return out;
+> end;;
+gap> S := MultiplicationTable(DualSymmetricInverseMonoid(2));;
+gap> p := (1, 2);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (1, 3);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (2, 3);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (1, 2, 3);;
+gap> OldOnMultiplicationTable(S, p) = OnMultiplicationTable(S, p);
+true
+gap> p := (1, 2, 3, 4);;
+gap> OnMultiplicationTable(S, p);
+Error, List Element: <list>[4] must have an assigned value
+gap> PermuteMultiplicationTable(List([1 .. 3], x -> [1 .. 3]), S, 1);
+Error, the argument <p> must be a permutation
+gap> PermuteMultiplicationTable(List([1 .. 4], x -> [1 .. 4]), S, (1, 2));
+Error, the arguments <temp> and <table> must have the same dimensions
+gap> M := [[1 .. 4], [1 .. 4]];;
+gap> PermuteMultiplicationTable(M, S, (1, 2));
+Error, the arguments <temp> and <table> must have the same dimensions
+gap> S := M;;
+gap> PermuteMultiplicationTable(M, S, (1, 2));
+Error, the arguments <temp> and <table> must be square tables of the same dime\
+nsions
+gap> S := MultiplicationTable(SymmetricInverseMonoid(2));;
+gap> M := List([1 .. 7], x -> [1 .. 7]);;
+gap> p := (1, 4, 2, 5, 7);;
+gap> OnMultiplicationTable(S, p) = M;
+false
+gap> PermuteMultiplicationTable(M, S, p);
+gap> OnMultiplicationTable(S, p) = M;
+true
path: tst/standard/attributes/isomorph.tst
user: login: james-d-mitchell
body: ```suggestion
true

#
```
created_at: 2025-04-10T07:04:35Z
author_association: COLLABORATOR
diff_hunk: @@ -11,16 +11,20 @@
 <#GAPDoc Label="OnMultiplicationTable">
 <ManSection>
   <Oper Name = "OnMultiplicationTable" Arg = "table, p"/>
-  <Returns>
-    A multiplication table.
-  </Returns>
+  <Oper Name = "PermuteMultiplicationTable" Arg = "temp, table, p"/>
path: doc/isomorph.xml
user: login: pramothragavan
body: How about `output` and `table` so that it's more consistent with `OnMultiplicationTable`? Feels odd to also change `OnMultiplicationTable` to have variable name `input`

Everything else hopefully addressed
created_at: 2025-04-10T12:36:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: pramothragavan
created_at: 2025-03-27T15:18:22Z
body: Hopefully all addressed now @james-d-mitchell. I've also added some tests.
author_association: CONTRIBUTOR
user: login: pramothragavan
created_at: 2025-04-10T12:59:29Z
body: Not sure I've done exactly what you wanted with the assertions @james-d-mitchell but let me know
author_association: CONTRIBUTOR
user: login: james-d-mitchell
created_at: 2025-04-10T14:29:45Z
body: > Not sure I've done exactly what you wanted with the assertions @james-d-mitchell but let me know

Probably easiest for me to just do it, when you are done, please let me know and I'll add the assertions.
author_association: COLLABORATOR
user: login: pramothragavan
created_at: 2025-04-10T14:31:15Z
body: > > Not sure I've done exactly what you wanted with the assertions @james-d-mitchell but let me know
> 
> Probably easiest for me to just do it, when you are done, please let me know and I'll add the assertions.

Sounds good, all done now @james-d-mitchell 
author_association: CONTRIBUTOR
user: login: james-d-mitchell
created_at: 2025-04-10T14:35:31Z
body: Ah you did exactly what I was suggesting already, thanks!
author_association: COLLABORATOR
user: login: pramothragavan
created_at: 2025-04-10T14:58:12Z
body: Just rebased so hopefully CI is all good now
author_association: CONTRIBUTOR