/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later
*/
/***************************************************************************** * * A transformation <f> has internal representation as follows: * * [Obj* image set, Obj* flat kernel, Obj* external degree, * entries image list] * * The <internal degree> of <f> is just the length of <entries image * list>, this is accessed here using <DEG_TRANS2> and <DEG_TRANS4>. * * Transformations must always have internal degree greater than or equal * to the largest point in <entries image list>. * * An element of <entries image list> of a transformation in T_TRANS2 * must be at most 65535 and be UInt2. Hence the internal and external * degrees of a T_TRANS2 are at most 65536. If <f> is T_TRANS4, then the * elements of <entries image list> must be UInt4. * * The <image set> and <flat kernel> are found relative to the internal * degree of the transformation, and must not be changed after they are * first found. * * The <external degree> is the largest non-negative integer <n> such * that n ^ f != n or i ^ f = n for some i != n, or equivalently the * degree of a transformation is the least non-negative <n> such that [n * + 1, n + 2, .. ] is fixed pointwise by <f>. This value is an invariant * of <f>, in the sense that it does not depend on the internal * representation. In GAP, DegreeOfTransformation(f) returns the external * degree, so that if f = g, then DegreeOfTransformation(f) = * DegreeOfTransformation(g). * * In this file, the external degree of a transformation is accessed * using FuncDegreeOfTransformation(self, f), and the internal degree is * accessed using DEG_TRANS2/4(f). *
*****************************************************************************/
#define MIN(a, b) (a < b ? a : b) #define MAX(a, b) (a < b ? b : a)
#define RequireTransformation(funcname, op) \
RequireArgumentCondition(funcname, op, IS_TRANS(op), \ "must be a transformation")
/**************************************************************************** ** *F GetPositiveListEntryEx, GetPositiveListEntry ** ** Extract list[idx] and check that it is a positive small integer; if so, ** return that integer; otherwise raise an error.
*/ staticInt GetPositiveListEntryEx(constchar * funcname,
Obj list, Int idx, constchar * argname)
{
Obj value = ELM_LIST(list, idx); if (!IS_POS_INTOBJ(value)) { char buf[1024];
snprintf(buf, sizeof(buf), "%s[%d]", argname, (int)idx);
buf[sizeof(buf) - 1] = '\0';
RequireArgumentEx(funcname, value, buf, "must be a positive small integer");
} return INT_INTOBJ(value);
}
// The only transformation created within this file that is of type // T_TRANS4 and that does not have (internal) degree 65537 or greater // is ID_TRANS4.
if (0 < len) {
data = ADDR_OBJ(res);
tmp = data[1];
k = 1; for (i = 2; i <= len; i++) { if (tmp != data[i]) {
k++;
tmp = data[i];
data[k] = tmp;
}
} if (k < len) {
ResizeBag(res, (k + 1) * sizeof(Obj));
SET_LEN_PLIST(res, k);
}
}
}
/******************************************************************************* ** GAP level functions for creating transformations
*******************************************************************************/
// Returns a transformation with list of images <list>, this does not check // that <list> is really a list or that its entries define a transformation.
deg = 0; for (i = LEN_LIST(src); 1 <= i; i--) {
s = GetPositiveListEntry("TransformationListListNC", src, i);
r = GetPositiveListEntry("TransformationListListNC", ran, i);
if (s != r) { if (s > deg) {
deg = s;
} if (r > deg) {
deg = r;
}
}
}
if (deg <= 65536) {
f = NEW_TRANS2(deg);
ptf2 = ADDR_TRANS2(f); for (i = 0; i < deg; i++) {
ptf2[i] = i;
} for (i = LEN_LIST(src); 1 <= i; i--) {
s = INT_INTOBJ(ELM_LIST(src, i));
r = INT_INTOBJ(ELM_LIST(ran, i)); // deg may be smaller than s if s = r if (s != r) {
ptf2[s - 1] = r - 1;
}
}
} else {
f = NEW_TRANS4(deg);
ptf4 = ADDR_TRANS4(f); for (i = 0; i < deg; i++) {
ptf4[i] = i;
} for (i = LEN_LIST(src); 1 <= i; i--) {
s = INT_INTOBJ(ELM_LIST(src, i));
r = INT_INTOBJ(ELM_LIST(ran, i)); if (s != r) {
ptf4[s - 1] = r - 1;
}
}
} return f;
}
// Returns a transformation with image <img> and flat kernel <ker> under the // (unchecked) assumption that the arguments are valid and that there is such // a transformation, i.e. that the maximum value in <ker> equals the length // of <img>.
// Returns an idempotent transformation with image <img> and flat kernel <ker> // under the (unchecked) assumption that the arguments are valid and that // there // is such a transformation, i.e. that the maximum value in <ker> equals the // length of <img>. // // Note that this does not return the same transformation as TRANS_IMG_KER_NC // with the same arguments.
deg = DEG_TRANS(f);
img = FuncIMAGE_SET_TRANS(self, f);
ker = NEW_PLIST(T_PLIST_CYC, deg);
SET_LEN_PLIST(ker, deg);
len = LEN_PLIST(img);
j = 1;
n = 0;
for (i = 0; i < deg; i++) { if (j < len && i + 1 == (UInt)INT_INTOBJ(ELM_PLIST(img, j + 1))) {
j++;
}
SET_ELM_PLIST(ker, ++n, INTOBJ_INT(j));
} return FuncIDEM_IMG_KER_NC(self, img, ker);
}
/******************************************************************************* ** GAP level functions for degree and rank of transformations
*******************************************************************************/
// Returns the degree of the transformation <f>, i.e. the least value <n> such // that <f> fixes [n + 1, n + 2, .. ].
static Obj FuncDegreeOfTransformation(Obj self, Obj f)
{
UInt n, i, deg; const UInt2 * ptf2; const UInt4 * ptf4;
RequireTransformation(SELF_NAME, f);
if (EXT_TRANS(f) == NULL) {
n = DEG_TRANS(f); if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f); if (ptf2[n - 1] != n - 1) {
SET_EXT_TRANS(f, INTOBJ_INT(n));
} else {
deg = 0; for (i = 0; i < n; i++) { if (ptf2[i] > i && ptf2[i] + 1 > deg) {
deg = ptf2[i] + 1;
} elseif (ptf2[i] < i && i + 1 > deg) {
deg = i + 1;
}
}
SET_EXT_TRANS(f, INTOBJ_INT(deg));
}
} else {
ptf4 = CONST_ADDR_TRANS4(f); if (ptf4[n - 1] != n - 1) {
SET_EXT_TRANS(f, INTOBJ_INT(n));
} else {
deg = 0; for (i = 0; i < n; i++) { if (ptf4[i] > i && ptf4[i] + 1 > deg) {
deg = ptf4[i] + 1;
} elseif (ptf4[i] < i && i + 1 > deg) {
deg = i + 1;
}
}
SET_EXT_TRANS(f, INTOBJ_INT(deg));
}
}
} return EXT_TRANS(f);
}
// Returns the rank of transformation, i.e. number of distinct values in // [(1)f .. (n)f] where n = DegreeOfTransformation(f).
m = INT_INTOBJ(n);
deg = DEG_TRANS(f); if (m >= deg) {
rank = RANK_TRANS(f) - deg + m;
} elseif (TNUM_OBJ(f) == T_TRANS2) {
pttmp = ResizeInitTmpTrans(deg);
ptf2 = CONST_ADDR_TRANS2(f);
rank = 0; for (i = 0; i < m; i++) { if (pttmp[ptf2[i]] == 0) {
rank++;
pttmp[ptf2[i]] = 1;
}
}
} else {
pttmp = ResizeInitTmpTrans(deg);
ptf4 = CONST_ADDR_TRANS4(f);
rank = 0; for (i = 0; i < m; i++) { if (pttmp[ptf4[i]] == 0) {
rank++;
pttmp[ptf4[i]] = 1;
}
}
} return INTOBJ_INT(rank);
}
// Returns the rank of the transformation <f> on the <list>, i.e. the number // of // distinct values in [(list[1])f .. (list[n])f], where <list> consists of // positive ints.
len = LEN_LIST(list);
rank = 0; if (TNUM_OBJ(f) == T_TRANS2) {
def = DEG_TRANS2(f);
pttmp = ResizeInitTmpTrans(def);
ptf2 = CONST_ADDR_TRANS2(f); for (i = 1; i <= len; i++) {
j = GetPositiveListEntry("RANK_TRANS_LIST", list, i) - 1; if (j < def) {
j = ptf2[j]; if (pttmp[j] == 0) {
rank++;
pttmp[j] = 1;
}
} else {
rank++;
}
}
} else {
def = DEG_TRANS4(f);
pttmp = ResizeInitTmpTrans(def);
ptf4 = CONST_ADDR_TRANS4(f); for (i = 1; i <= len; i++) {
j = GetPositiveListEntry("RANK_TRANS_LIST", list, i) - 1; if (j < def) {
j = ptf4[j]; if (pttmp[j] == 0) {
rank++;
pttmp[j] = 1;
}
} else {
rank++;
}
}
}
return INTOBJ_INT(rank);
}
/******************************************************************************* ** GAP level functions for the kernel and preimages of a transformation
*******************************************************************************/
// Returns the flat kernel of transformation on // [1 .. DegreeOfTransformation(f)].
// copy the kernel set up to minimum of m, deg if (m < deg) { for (i = 0; i < m; i++) {
*ptnew++ = *ptker++;
}
} else { // m > deg for (i = 0; i < deg; i++) {
*ptnew++ = *ptker++;
} // we must now add another (m - deg) points, // starting with the class number (rank + 1) for (i = 1; i <= m - deg; i++) {
*ptnew++ = INTOBJ_INT(i + RANK_TRANS(f));
}
} return newObj;
}
// Returns the kernel of a transformation <f> as a partition of [1 .. n].
if (nr == 0) {
RetypeBag(out, T_PLIST_EMPTY);
SET_LEN_PLIST(out, 0);
}
return out;
}
/******************************************************************************* ** GAP level functions for the image sets and lists of a transformation
*******************************************************************************/
// Returns the duplicate free list of images of the transformation f on // [1 .. n] where n = DEG_TRANS(f). Note that this might not be sorted.
// copy the image set for (i = 0; i < len; i++) {
*ptnew++ = *ptim++;
} // add newObj points for (i = deg + 1; i <= m; i++) {
*ptnew++ = INTOBJ_INT(i);
}
} return newObj;
}
// Returns the image list [(1)f .. (n)f] of the transformation f.
if (m == 0) {
out = NewImmutableEmptyPlist(); return out;
}
out = NEW_PLIST_IMM(T_PLIST_CYC, m);
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f);
deg = MIN(DEG_TRANS2(f), m); for (i = 0; i < deg; i++) {
SET_ELM_PLIST(out, i + 1, INTOBJ_INT(ptf2[i] + 1));
}
} else {
ptf4 = CONST_ADDR_TRANS4(f);
deg = MIN(DEG_TRANS4(f), m); for (i = 0; i < deg; i++) {
SET_ELM_PLIST(out, i + 1, INTOBJ_INT(ptf4[i] + 1));
}
} for (; i < m; i++)
SET_ELM_PLIST(out, i + 1, INTOBJ_INT(i + 1));
SET_LEN_PLIST(out, (Int)m); return out;
}
/******************************************************************************* ** GAP level functions for properties of transformations
*******************************************************************************/
if (TNUM_OBJ(f) == T_TRANS2) {
deg = DEG_TRANS2(f);
ptf2 = CONST_ADDR_TRANS2(f); for (i = 0; i < deg; i++) { if (ptf2[ptf2[i]] != ptf2[i]) { returnFalse;
}
}
} else {
deg = DEG_TRANS4(f);
ptf4 = CONST_ADDR_TRANS4(f); for (i = 0; i < deg; i++) { if (ptf4[ptf4[i]] != ptf4[i]) { returnFalse;
}
}
} returnTrue;
}
/******************************************************************************* ** GAP level functions for attributes of transformations
*******************************************************************************/
// Returns the least m and r such that f ^ (m + r) = f ^ m, where f is a // transformation.
if (deg == 0) { return NewPlistFromArgs(INTOBJ_INT(1), INTOBJ_INT(1));
}
// seen[pt] = 0 -> haven't seen pt before // // seen[pt] = d where (1 <= d <= deg) // -> pt belongs to a component we've seen before and (pt)f ^ (d - 1) // belongs to a cycle // // seen[pt] = deg + 1 -> pt belongs to a component not seen before
seen = ResizeInitTmpTrans(deg);
pow = 2;
ord = INTOBJ_INT(1);
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f); for (i = 0; i < deg; i++) { if (seen[i] == 0) {
len = 0; // repeatedly apply f to pt until we see something we've seen // already for (pt = i; seen[pt] == 0; pt = ptf2[pt], len++) {
seen[pt] = deg + 1;
}
last_pt = pt; if (seen[pt] <= deg) { // pt belongs to a component we've seen before
dist = seen[pt] + len; // the distance of i from the cycle in its component + 1
} else { // pt belongs to a component we've not seen before
for (cyc = 0; seen[pt] == deg + 1; pt = ptf2[pt], cyc++) { // go around the cycle again and set the value of // seen, // and get the length of the cycle
seen[pt] = 1;
}
ord = LcmInt(ord, INTOBJ_INT(cyc));
// the distance of i from the cycle in its component + 1
dist = len - cyc + 1;
// update bag pointers, in case a garbage collection happened
ptf2 = CONST_ADDR_TRANS2(f);
seen = AddrTmpTrans();
} if (dist > pow) {
pow = dist;
} // record the distances of the points from the cycle for (pt = i; pt != last_pt; pt = ptf2[pt]) {
seen[pt] = dist--;
}
}
}
} else {
ptf4 = CONST_ADDR_TRANS4(f); for (i = 0; i < deg; i++) { if (seen[i] == 0) {
len = 0; // repeatedly apply f to pt until we see something we've seen // already for (pt = i; seen[pt] == 0; pt = ptf4[pt], len++) {
seen[pt] = deg + 1;
}
last_pt = pt; if (seen[pt] <= deg) { // pt belongs to a component we've seen before
dist = seen[pt] + len; // the distance of i from the cycle in its component + 1
} else { // pt belongs to a component we've not seen before
for (cyc = 0; seen[pt] == deg + 1; pt = ptf4[pt], cyc++) { // go around the cycle again and set the value of // seen, // and get the length of the cycle
seen[pt] = 1;
}
ord = LcmInt(ord, INTOBJ_INT(cyc));
// the distance of i from the cycle in its component + 1
dist = len - cyc + 1;
// update bag pointers, in case a garbage collection happened
ptf4 = CONST_ADDR_TRANS4(f);
seen = AddrTmpTrans();
} if (dist > pow) {
pow = dist;
} // record the distances of the points from the cycle for (pt = i; pt != last_pt; pt = ptf4[pt]) {
seen[pt] = dist--;
}
}
}
}
x = FuncIndexPeriodOfTransformation(self, f);
ind = ELM_PLIST(x, 1);
per = ELM_PLIST(x, 2);
pow = per; while (LtInt(pow, ind)) {
pow = SumInt(pow, per);
} return pow;
}
/******************************************************************************* ** GAP level functions for regularity of transformations
*******************************************************************************/
// Returns True if the transformation or list <obj> is injective on the list // <list>.
RequireSmallList(SELF_NAME, list); if (!IS_TRANS(obj) && !IS_LIST(obj)) {
RequireArgument(SELF_NAME, obj, "must be a transformation or a list");
} // init buffer
n = (IS_TRANS(obj) ? DEG_TRANS(obj) : LEN_LIST(obj));
pttmp = ResizeInitTmpTrans(n);
if (TNUM_OBJ(obj) == T_TRANS2) {
ptt2 = CONST_ADDR_TRANS2(obj); for (i = LEN_LIST(list); i >= 1; i--) {
j = GetPositiveListEntry("IsInjectiveListTrans", list, i); if (j <= n) { if (pttmp[ptt2[j - 1]] != 0) { returnFalse;
}
pttmp[ptt2[j - 1]] = 1;
}
}
} elseif (TNUM_OBJ(obj) == T_TRANS4) {
ptt4 = CONST_ADDR_TRANS4(obj); for (i = LEN_LIST(list); i >= 1; i--) {
j = GetPositiveListEntry("IsInjectiveListTrans", list, i); if (j <= n) { if (pttmp[ptt4[j - 1]] != 0) { returnFalse;
}
pttmp[ptt4[j - 1]] = 1;
}
}
} else { // obj is a list, first we check it describes a transformation for (i = 1; i <= n; i++) {
j = GetPositiveListEntry("IsInjectiveListTrans", obj, i); if (j > n) {
ErrorQuit( "<obj> must be a list of positive small integers " "in the range [1 .. %d]",
(Int)n, 0);
}
} for (i = LEN_LIST(list); i >= 1; i--) {
j = GetPositiveListEntry("IsInjectiveListTrans", list, i); if (j <= n) { if (pttmp[INT_INTOBJ(ELM_LIST(obj, j)) - 1] != 0) { returnFalse;
}
pttmp[INT_INTOBJ(ELM_LIST(obj, j)) - 1] = 1;
}
}
} returnTrue;
}
// Returns a transformation g such that transformation f * g * f = f and // g * f * g = g, where f is a transformation.
if (TNUM_OBJ(f) == T_TRANS2) {
deg = DEG_TRANS2(f);
g = NEW_TRANS2(deg);
ptf2 = CONST_ADDR_TRANS2(f);
ptg2 = ADDR_TRANS2(g); for (i = 0; i < deg; i++) {
ptg2[i] = 0;
} for (i = deg - 1; i > 0; i--) {
ptg2[ptf2[i]] = i;
} // to ensure that 1 is in the image and so rank of g equals that of f
ptg2[ptf2[0]] = 0;
} else {
deg = DEG_TRANS4(f);
g = NEW_TRANS4(deg);
ptf4 = CONST_ADDR_TRANS4(f);
ptg4 = ADDR_TRANS4(g); for (i = 0; i < deg; i++) {
ptg4[i] = 0;
} for (i = deg - 1; i > 0; i--) {
ptg4[ptf4[i]] = i;
} // to ensure that 1 is in the image and so rank of g equals that of f
ptg4[ptf4[0]] = 0;
} return g;
}
/******************************************************************************* ** GAP level functions for actions of transformations
*******************************************************************************/
// Returns the flat kernel of a transformation obtained by multiplying <f> by // any transformation with kernel equal to <ker>. If the argument <ker> = // [0], then the flat kernel of <f> on [1 .. <n>] is returned. Otherwise, the // argument <n> is redundant.
// FIXME this should just always return a flat kernel of length <n>, the // special case should be removed, and [0] should be replaced by [1 .. n] in // the Semigroup package.
len = LEN_LIST(ker); if (len == 1 && ELM_LIST(ker, 1) == INTOBJ_INT(0)) { return FuncFLAT_KERNEL_TRANS_INT(self, f, n);
}
rank = 1;
deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f)); if (len < deg) {
ErrorQuit("ON_KERNEL_ANTI_ACTION: the length of <ker> " "must be at least %d",
(Int)deg, 0);
}
if (len == 0) {
out = NewImmutableEmptyPlist(); return out;
}
out = NEW_PLIST_IMM(T_PLIST_CYC, len);
SET_LEN_PLIST(out, len);
pttmp = ResizeInitTmpTrans(len);
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f); for (i = 0; i < deg; i++) { // <f> then <g> with ker(<g>) = <ker>
j = INT_INTOBJ(ELM_LIST(ker, ptf2[i] + 1)) - 1; // f first! if (pttmp[j] == 0) {
pttmp[j] = rank++;
}
SET_ELM_PLIST(out, i + 1, INTOBJ_INT(pttmp[j]));
}
} else {
ptf4 = CONST_ADDR_TRANS4(f); for (i = 0; i < deg; i++) { // <f> then <g> with ker(<g>) = <ker>
j = INT_INTOBJ(ELM_LIST(ker, ptf4[i] + 1)) - 1; // f first! if (pttmp[j] == 0) {
pttmp[j] = rank++;
}
SET_ELM_PLIST(out, i + 1, INTOBJ_INT(pttmp[j]));
}
}
i++; for (; i <= len; i++) { // just <ker>
j = INT_INTOBJ(ELM_LIST(ker, i)) - 1; if (pttmp[j] == 0) {
pttmp[j] = rank++;
}
SET_ELM_PLIST(out, i, INTOBJ_INT(pttmp[j]));
} return out;
}
/******************************************************************************* ** GAP level functions for changing representation of a permutation to a ** transformation
*******************************************************************************/
// Returns a transformation <f> such that (i)f = (i)p for all i <= n where <p> // is a permutation <p> and <n> is a positive integer. Note that the returned // transformation is not necessarily a permutation (mathematically), when n is // less than the largest moved point of p.
// find largest moved point if (TNUM_OBJ(p) == T_PERM2) {
ptPerm2 = CONST_ADDR_PERM2(p); for (sup = DEG_PERM2(p); 1 <= sup; sup--) { if (ptPerm2[sup - 1] != sup - 1) { break;
}
} return FuncAS_TRANS_PERM_INT(self, p, INTOBJ_INT(sup));
} else {
ptPerm4 = CONST_ADDR_PERM4(p); for (sup = DEG_PERM4(p); 1 <= sup; sup--) { if (ptPerm4[sup - 1] != sup - 1) { break;
}
} return FuncAS_TRANS_PERM_INT(self, p, INTOBJ_INT(sup));
}
}
/******************************************************************************* ** GAP level functions for changing representation of a transformation to a ** permutation
*******************************************************************************/
// Returns a permutation mathematically equal to the transformation <f> if // possible, and returns Fail if it is not possible
for (i = 0; i < max; i++) {
ptp[i] = i;
} for (i = 0; i < min; i++) {
ptp[ptf2[i]] = ptg2[i];
} for (; i < deg; i++) {
ptp[i] = ptg2[i];
} for (; i < def; i++) {
ptp[ptf2[i]] = i;
}
} elseif (TNUM_OBJ(f) == T_TRANS2 && TNUM_OBJ(g) == T_TRANS4) {
ptf2 = CONST_ADDR_TRANS2(f);
ptg4 = CONST_ADDR_TRANS4(g);
for (i = 0; i < max; i++) {
ptp[i] = i;
} for (i = 0; i < min; i++) {
ptp[ptf2[i]] = ptg4[i];
} for (; i < deg; i++) {
ptp[i] = ptg4[i];
} for (; i < def; i++) { // The only transformation created within this file that is of // type // T_TRANS4 and that does not have (internal) degree 65537 or // greater // is ID_TRANS4.
ptp[ptf2[i]] = i;
}
} elseif (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS2) {
ptf4 = CONST_ADDR_TRANS4(f);
ptg2 = CONST_ADDR_TRANS2(g);
for (i = 0; i < max; i++) {
ptp[i] = i;
} for (i = 0; i < min; i++) {
ptp[ptf4[i]] = ptg2[i];
} for (; i < deg; i++) { // The only transformation created within this file that is of // type // T_TRANS4 and that does not have (internal) degree 65537 or // greater // is ID_TRANS4.
ptp[i] = ptg2[i];
} for (; i < def; i++) {
ptp[ptf4[i]] = i;
}
} elseif (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS4) {
ptf4 = CONST_ADDR_TRANS4(f);
ptg4 = CONST_ADDR_TRANS4(g);
for (i = 0; i < max; i++) {
ptp[i] = i;
} for (i = 0; i < min; i++) {
ptp[ptf4[i]] = ptg4[i];
} for (; i < deg; i++) {
ptp[i] = ptg4[i];
} for (; i < def; i++) {
ptp[ptf4[i]] = i;
}
} return perm;
}
/******************************************************************************* ** GAP level functions for changing representation of a transformation to a ** transformation
*******************************************************************************/
// Returns a transformation g such that (i)g = (i)f for all i in list, and // where (i)g = i for every other value of i.
// g fixes every point for (i = 0; i < deg; i++) {
ptg2[i] = i;
}
// g acts like f on list * / for (i = 0; i < len; i++) {
k = GetPositiveListEntry("RestrictedTransformation", list, i + 1) - 1; if (k < deg) {
ptg2[k] = ptf2[k];
}
}
} else {
deg = DEG_TRANS4(f);
g = NEW_TRANS4(deg);
// g fixes every point for (i = 0; i < deg; i++) {
ptg4[i] = i;
}
// g acts like f on list for (i = 0; i < len; i++) {
k = GetPositiveListEntry("RestrictedTransformation", list, i + 1) - 1; if (k < deg) {
ptg4[k] = ptf4[k];
}
}
} return g;
}
// AsTransformation for a transformation <f> and a pos int <m> either // restricts // <f> to [1 .. m] or returns <f> depending on whether m is less than or equal // DegreeOfTransformation(f) or not.
// In the first form, this is similar to TRIM_TRANS except that a new // transformation is returned.
static Obj FuncAS_TRANS_TRANS(Obj self, Obj f, Obj m)
{ const UInt2 *ptf2; const UInt4 *ptf4;
UInt2 *ptg2;
UInt4 *ptg4;
UInt i, n, def;
Obj g;
if (TNUM_OBJ(f) == T_TRANS2) {
g = NEW_TRANS2(n);
ptf2 = CONST_ADDR_TRANS2(f);
ptg2 = ADDR_TRANS2(g); for (i = 0; i < n; i++) { if (ptf2[i] > n - 1) { return Fail;
}
ptg2[i] = ptf2[i];
}
} else { if (n > 65536) { // g is T_TRANS4
g = NEW_TRANS4(n);
ptf4 = CONST_ADDR_TRANS4(f);
ptg4 = ADDR_TRANS4(g); for (i = 0; i < n; i++) { if (ptf4[i] > n - 1) { return Fail;
}
ptg4[i] = ptf4[i];
}
} else { // f is T_TRANS4 but n <= 65536 < def and so g will be T_TRANS2
g = NEW_TRANS2(n);
ptf4 = CONST_ADDR_TRANS4(f);
ptg2 = ADDR_TRANS2(g); for (i = 0; i < n; i++) { if (ptf4[i] > n - 1) { return Fail;
}
ptg2[i] = (UInt2)ptf4[i];
}
}
} return g;
}
// Changes the transformation <f> in-place to reduce the degree to <m>. It is // assumed that f is actually a transformation of [1 .. m], i.e. that i ^ f <= // m for all i in [1 .. m].
/******************************************************************************* ** GAP level functions for hashing transformations
*******************************************************************************/
/******************************************************************************* ** GAP level functions for moved points (and related) of a transformation
*******************************************************************************/
// Returns the largest value i such that (i)f <> i or 0 if no such i exists.
RequireTransformation(SELF_NAME, f);
max = 0; if (TNUM_OBJ(f) == T_TRANS2) {
def = DEG_TRANS2(f);
ptf2 = CONST_ADDR_TRANS2(f); for (i = DEG_TRANS2(f); 1 <= i; i--) { if (ptf2[i - 1] != i - 1) { break;
}
} for (; 1 <= i; i--) { if (ptf2[i - 1] + 1 > max) {
max = ptf2[i - 1] + 1; if (max == def) { break;
}
}
}
} else {
def = DEG_TRANS4(f);
ptf4 = CONST_ADDR_TRANS4(f); for (i = DEG_TRANS4(f); 1 <= i; i--) { if (ptf4[i - 1] != i - 1) { break;
}
} for (; 1 <= i; i--) { if (ptf4[i - 1] + 1 > max) {
max = ptf4[i - 1] + 1; if (max == def) break;
}
}
} return INTOBJ_INT(max);
}
// Returns the smallest value <i> such that (i)f <> i if it exists, and Fail // if // not. Note that this differs from the GAP level function which returns // infinity if (i)f = i for all i.
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f);
deg = DEG_TRANS2(f); for (i = 1; i <= deg; i++) { if (ptf2[i - 1] != i - 1) { break;
}
}
} else {
ptf4 = CONST_ADDR_TRANS4(f);
deg = DEG_TRANS4(f); for (i = 1; i <= deg; i++) { if (ptf4[i - 1] != i - 1) { break;
}
}
} return INTOBJ_INT(i);
}
// Returns the smallest value in [SmallestMovedPoint(f) .. // LargestMovedPoint(f)] ^ f if it exists and Fail if it does not. Note that // this differs from the GAP level function which returns infinity if (i)f = i // for all i.
len = 0; if (TNUM_OBJ(f) == T_TRANS2) {
deg = DEG_TRANS2(f);
out = NEW_PLIST(T_PLIST_CYC_SSORT, 0);
ptf2 = CONST_ADDR_TRANS2(f); for (i = 0; i < deg; i++) { if (ptf2[i] != i) {
AssPlist(out, ++len, INTOBJ_INT(i + 1));
ptf2 = CONST_ADDR_TRANS2(f);
}
}
} else {
deg = DEG_TRANS4(f);
out = NEW_PLIST(T_PLIST_CYC_SSORT, 0);
ptf4 = CONST_ADDR_TRANS4(f); for (i = 0; i < deg; i++) { if (ptf4[i] != i) {
AssPlist(out, ++len, INTOBJ_INT(i + 1));
ptf4 = CONST_ADDR_TRANS4(f);
}
}
}
if (LEN_PLIST(out) == 0) {
RetypeBag(out, T_PLIST_EMPTY);
}
return out;
}
/******************************************************************************* ** GAP level functions for connected components of a transformation
*******************************************************************************/
// Returns the representatives, in the following sense, of the components of // the transformation <f>. For every i in [1 .. DegreeOfTransformation(<f>)] // there exists a representative j and a positive integer k such that i ^ (<f> // ^ k) = j. The least number of representatives is returned and these // representatives are partitioned according to the component they belong to.
if (deg == 0) {
out = NewEmptyPlist(); return out;
}
img = FuncUNSORTED_IMAGE_SET_TRANS(self, f);
out = NEW_PLIST(T_PLIST, 1);
seen = ResizeInitTmpTrans(deg);
for (i = 1; i <= (UInt)LEN_PLIST(img); i++) {
seen[INT_INTOBJ(ELM_PLIST(img, i)) - 1] = 1;
}
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f);
nr = 1; for (i = 0; i < deg; i++) { if (seen[i] == 0) { // i belongs to dom(f) but not im(f) // repeatedly apply f to pt until we see something we've seen // already
pt = i; do {
seen[pt] = nr + 1;
pt = ptf2[pt];
} while (seen[pt] == 1);
index = seen[pt];
if (index != nr + 1) { // pt belongs to a component we've seen before
ptf2 = CONST_ADDR_TRANS2(f);
pt = i; do {
seen[pt] = index;
pt = ptf2[pt];
} while (seen[pt] == nr + 1);
comp = ELM_PLIST(out, seen[pt] - 1);
AssPlist(comp, LEN_PLIST(comp) + 1, INTOBJ_INT(i + 1));
} else { // pt belongs to a component we've not seen before
comp = NEW_PLIST(T_PLIST_CYC, 1);
SET_LEN_PLIST(comp, 1);
SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
AssPlist(out, nr++, comp);
}
ptf2 = CONST_ADDR_TRANS2(f);
seen = AddrTmpTrans();
}
} for (i = 0; i < deg; i++) { if (seen[i] == 1) { // i belongs to a cycle for (pt = i; seen[pt] == 1; pt = ptf2[pt]) {
seen[pt] = 0;
}
comp = NEW_PLIST(T_PLIST_CYC, 1);
SET_LEN_PLIST(comp, 1);
SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
AssPlist(out, nr++, comp);
ptf2 = CONST_ADDR_TRANS2(f);
seen = AddrTmpTrans();
}
}
} else {
ptf4 = CONST_ADDR_TRANS4(f);
nr = 1; for (i = 0; i < deg; i++) { if (seen[i] == 0) { // i belongs to dom(f) but not im(f) // repeatedly apply f to pt until we see something we've seen // already
pt = i; do {
seen[pt] = nr + 1;
pt = ptf4[pt];
} while (seen[pt] == 1);
index = seen[pt];
if (index != nr + 1) { // pt belongs to a component we've seen before
pt = i; do {
seen[pt] = index;
pt = ptf4[pt];
} while (seen[pt] == nr + 1);
comp = ELM_PLIST(out, seen[pt] - 1);
AssPlist(comp, LEN_PLIST(comp) + 1, INTOBJ_INT(i + 1));
} else { // pt belongs to a component we've not seen before
comp = NEW_PLIST(T_PLIST_CYC, 1);
SET_LEN_PLIST(comp, 1);
SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
AssPlist(out, nr++, comp);
}
ptf4 = CONST_ADDR_TRANS4(f);
seen = AddrTmpTrans();
}
} for (i = 0; i < deg; i++) { if (seen[i] == 1) { // i belongs to a cycle for (pt = i; seen[pt] == 1; pt = ptf4[pt]) {
seen[pt] = 0;
}
comp = NEW_PLIST(T_PLIST_CYC, 1);
SET_LEN_PLIST(comp, 1);
SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
AssPlist(out, nr++, comp);
ptf4 = CONST_ADDR_TRANS4(f);
seen = AddrTmpTrans();
}
}
} return out;
}
// Returns the number of connected components of the transformation <f>, // thought of as a functional digraph with DegreeOfTransformation(f) vertices.
if (deg == 0) {
out = NewEmptyPlist(); return out;
}
out = NEW_PLIST(T_PLIST, 1);
seen = ResizeInitTmpTrans(deg);
nr = 0;
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f); for (i = 0; i < deg; i++) { if (seen[i] == 0) { // repeatedly apply f to pt until we see something we've seen // already
csize = 0;
pt = i; do {
csize++;
seen[pt] = deg + 1;
pt = ptf2[pt];
} while (seen[pt] == 0);
if (seen[pt] <= deg) { // pt belongs to a component we've seen before
index = seen[pt];
comp = ELM_PLIST(out, index);
pos = LEN_PLIST(comp) + 1;
GROW_PLIST(comp, LEN_PLIST(comp) + csize);
SET_LEN_PLIST(comp, LEN_PLIST(comp) + csize);
} else { // pt belongs to a component we've not seen before
index = ++nr;
pos = 1;
pt = i; while (seen[pt] == deg + 1) {
SET_ELM_PLIST(comp, pos++, INTOBJ_INT(pt + 1));
seen[pt] = index;
pt = ptf2[pt];
}
CHANGED_BAG(out);
}
}
} else {
ptf4 = CONST_ADDR_TRANS4(f); for (i = 0; i < deg; i++) { if (seen[i] == 0) { // repeatedly apply f to pt until we see something we've seen // already
csize = 0;
pt = i; do {
csize++;
seen[pt] = deg + 1;
pt = ptf4[pt];
} while (seen[pt] == 0);
if (seen[pt] <= deg) { // pt belongs to a component we've seen before
index = seen[pt];
comp = ELM_PLIST(out, index);
pos = LEN_PLIST(comp) + 1;
GROW_PLIST(comp, LEN_PLIST(comp) + csize);
SET_LEN_PLIST(comp, LEN_PLIST(comp) + csize);
} else { // pt belongs to a component we've not seen before
index = ++nr;
pos = 1;
/******************************************************************************* ** GAP level functions for cycles of a transformation
*******************************************************************************/
// Returns the cycle contained in the component of the transformation <f> // containing the positive integer <pt>.
if (deg == 0) {
out = NewEmptyPlist(); return out;
}
out = NEW_PLIST(T_PLIST, 0);
nr = 0;
seen = ResizeInitTmpTrans(deg);
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f); for (i = 0; i < deg; i++) { if (seen[i] == 0) { // repeatedly apply f to pt until we see something we've seen // already for (pt = i; seen[pt] == 0; pt = ptf2[pt]) {
seen[pt] = 1;
} if (seen[pt] == 1) { // pt belongs to a component we've not seen before
/******************************************************************************* ** GAP level functions for the Semigroups package
*******************************************************************************/
// Returns a transformation g such that ((i)f)g = i for all i in list, // it is assumed (and not checked) that the transformation f is injective on // list.
if (TNUM_OBJ(f) == T_TRANS2) {
deg = DEG_TRANS2(f);
g = NEW_TRANS2(deg);
ptf2 = CONST_ADDR_TRANS2(f);
ptg2 = ADDR_TRANS2(g);
for (j = 0; j < deg; j++) {
ptg2[j] = j;
} for (j = 1; j <= (UInt)LEN_LIST(list); j++) {
i = GetPositiveListEntry("INV_LIST_TRANS", list, j) - 1; if (i < deg) {
ptg2[ptf2[i]] = i;
}
}
} else {
deg = DEG_TRANS4(f);
g = NEW_TRANS4(deg);
ptf4 = CONST_ADDR_TRANS4(f);
ptg4 = ADDR_TRANS4(g);
for (j = 0; j < deg; j++) {
ptg4[j] = j;
} for (j = 1; j <= (UInt)LEN_LIST(list); j++) {
i = GetPositiveListEntry("INV_LIST_TRANS", list, j) - 1; if (i < deg) {
ptg4[ptf4[i]] = i;
}
}
} return g;
}
// If ker(f) = ker(g), then TRANS_IMG_CONJ returns the permutation p // such that i ^ (f ^ -1 * g) = i ^ p for all i in im(f). // // The permutation returned is the same as: // // MappingPermListList(ImageListOfTransformation(f, n), // ImageListOfTransformation(g, n)); // // where n = max{DegreeOfTransformation(f), DegreeOfTransformation(g)}. // However, this is included to avoid the overhead of producing the image // lists // of f and g in the above.
for (i = 0; i < min; i++) {
ptsrc[ptf2[i]] = 1;
ptdst[ptg2[i]] = 1;
ptp[ptf2[i]] = ptg2[i];
}
// if deg = min, then this isn't executed for (; i < deg; i++) { // ptsrc[i] = 1;
ptdst[ptg2[i]] = 1;
ptp[i] = ptg2[i];
}
// if def = min, then this isn't executed for (; i < def; i++) {
ptsrc[ptf2[i]] = 1;
ptdst[i] = 1;
ptp[ptf2[i]] = i;
}
} elseif (TNUM_OBJ(f) == T_TRANS2 && TNUM_OBJ(g) == T_TRANS4) {
ptf2 = CONST_ADDR_TRANS2(f);
ptg4 = CONST_ADDR_TRANS4(g);
for (i = 0; i < min; i++) {
ptsrc[ptf2[i]] = 1;
ptdst[ptg4[i]] = 1;
ptp[ptf2[i]] = ptg4[i];
}
// if deg = min, then this isn't executed for (; i < deg; i++) { // ptsrc[i] = 1;
ptdst[ptg4[i]] = 1;
ptp[i] = ptg4[i];
}
// if def = min, then this isn't executed for (; i < def; i++) { // The only transformation created within this file that is of // type // T_TRANS4 and that does not have (internal) degree 65537 or // greater // is ID_TRANS4.
ptsrc[ptf2[i]] = 1;
ptdst[i] = 1;
ptp[ptf2[i]] = i;
}
} elseif (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS2) {
ptf4 = CONST_ADDR_TRANS4(f);
ptg2 = CONST_ADDR_TRANS2(g);
for (i = 0; i < min; i++) {
ptsrc[ptf4[i]] = 1;
ptdst[ptg2[i]] = 1;
ptp[ptf4[i]] = ptg2[i];
}
// if deg = min, then this isn't executed for (; i < deg; i++) { // The only transformation created within this file that is of // type // T_TRANS4 and that does not have (internal) degree 65537 or // greater // is ID_TRANS4. // ptsrc[i] = 1;
ptdst[ptg2[i]] = 1;
ptp[i] = ptg2[i];
}
// if def = min, then this isn't executed for (; i < def; i++) {
ptsrc[ptf4[i]] = 1;
ptdst[i] = 1;
ptp[ptf4[i]] = i;
}
} elseif (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS4) {
ptf4 = CONST_ADDR_TRANS4(f);
ptg4 = CONST_ADDR_TRANS4(g);
for (i = 0; i < min; i++) {
ptsrc[ptf4[i]] = 1;
ptdst[ptg4[i]] = 1;
ptp[ptf4[i]] = ptg4[i];
}
// if deg = min, then this isn't executed for (; i < deg; i++) { // ptsrc[i] = 1;
ptdst[ptg4[i]] = 1;
ptp[i] = ptg4[i];
}
// if def = min, then this isn't executed for (; i < def; i++) {
ptsrc[ptf4[i]] = 1;
ptdst[i] = 1;
ptp[ptf4[i]] = i;
}
}
// complete the permutation
j = 0; for (i = 0; i < def; i++) { if (ptsrc[i] == 0) { while (ptdst[j] != 0) {
j++;
}
ptp[i] = j;
j++;
}
} return perm;
}
// Returns the flat kernel of <p> ^ -1 * f * <p> where f is any transformation // such that ker(f) = <ker>, <p> is a permutation and <ker> is itself a flat // kernel of transformation. This assumes (but doesn't check) that <p> is a // permutation of [1 .. Length(<ker>)] regardless of its degree.
if (len == 0) {
out = NEW_PLIST_IMM(T_PLIST_EMPTY, len);
SET_LEN_PLIST(out, len); return out;
}
out = NEW_PLIST_IMM(T_PLIST_CYC, len);
SET_LEN_PLIST(out, len);
ResizeTmpTrans(2 * len);
ptcnj = AddrTmpTrans();
rank = 1;
ptlkp = ptcnj + len;
if (TNUM_OBJ(p) == T_PERM2) {
dep = DEG_PERM2(p);
ptp2 = CONST_ADDR_PERM2(p);
if (dep <= len) { // form the conjugate in ptcnj and init the lookup for (i = 0; i < dep; i++) { // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
ptcnj[ptp2[i]] = ptp2[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
ptlkp[i] = 0;
} for (; i < len; i++) {
ptcnj[i] = IMAGE((UInt)INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1,
ptp2, dep);
ptlkp[i] = 0;
}
} else { // dep > len but p fixes [1..len] setwise
// form the conjugate in ptcnj and init the lookup for (i = 0; i < len; i++) { // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
ptcnj[ptp2[i]] = ptp2[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
ptlkp[i] = 0;
}
}
} else {
dep = DEG_PERM4(p);
ptp4 = CONST_ADDR_PERM4(p);
if (dep <= len) { // form the conjugate in ptcnj and init the lookup for (i = 0; i < dep; i++) { // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
ptcnj[ptp4[i]] = ptp4[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
ptlkp[i] = 0;
} for (; i < len; i++) {
ptcnj[i] = IMAGE((UInt)INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1,
ptp4, dep);
ptlkp[i] = 0;
}
} else { // dep > len but p fixes [1..len] setwise
// form the conjugate in ptcnj and init the lookup for (i = 0; i < len; i++) { // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
ptcnj[ptp4[i]] = ptp4[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
ptlkp[i] = 0;
}
}
}
// form the flat kernel for (i = 0; i < len; i++) { if (ptlkp[ptcnj[i]] == 0) {
ptlkp[ptcnj[i]] = rank++;
}
SET_ELM_PLIST(out, i + 1, INTOBJ_INT(ptlkp[ptcnj[i]]));
} return out;
}
// If <f> is a transformation and <X> is a flat kernel of a transformation, // then we denote OnKernelAntiAction(X, f) by f ^ X. Suppose that x is a // transformation with ker(x) = <X> and ker(<f>x) = f ^ ker(x) has the same // number of classes as ker(x). Then INV_KER_TRANS(X, f) returns a // transformation g such that g<f> ^ ker(x) = ker(x) = ker(gfx) and the action // of g<f> on ker(x) is the identity. template <typename TF, typename TG> static Obj INV_KER_TRANS(Obj X, Obj f)
{
Obj g; const TF * ptf;
TG * ptg;
UInt4 * pttmp;
UInt deg, i, len;
len = LEN_LIST(X);
ResizeTmpTrans(len);
deg = DEG_TRANS<TF>(f);
g = NEW_TRANS<TG>(len);
pttmp = AddrTmpTrans();
ptf = CONST_ADDR_TRANS<TF>(f);
ptg = ADDR_TRANS<TG>(g); if (deg >= len) { // calculate a transversal of f ^ ker(x) = ker(fx) for (i = 0; i < len; i++) {
pttmp[INT_INTOBJ(ELM_LIST(X, ptf[i] + 1)) - 1] = i;
}
} else { for (i = 0; i < deg; i++) {
pttmp[INT_INTOBJ(ELM_LIST(X, ptf[i] + 1)) - 1] = i;
} for (; i < len; i++) {
pttmp[INT_INTOBJ(ELM_LIST(X, i + 1)) - 1] = i;
}
} for (i = len; i >= 1; i--) {
ptg[i - 1] = pttmp[INT_INTOBJ(ELM_LIST(X, i)) - 1];
} return g;
}
// Returns the same value as OnSets(set, f) except if set = [0], when the // image // set of <f> on [1 .. n] is returned instead. If the argument <set> is not // [0], then the third argument is ignored.
if (IS_PLIST(set)) {
res = NEW_PLIST_WITH_MUTABILITY(IS_PLIST_MUTABLE(set), T_PLIST_CYC_SSORT, len);
SET_LEN_PLIST(res, len);
} else { // input is not a plain list, so we make a copy of it, and then also reuse // that copy for our output
res = PLAIN_LIST_COPY(set); if (!IS_MUTABLE_OBJ(set))
MakeImmutableNoRecurse(res);
set = res;
}
if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f);
deg = DEG_TRANS2(f); for (i = len; 1 <= i; i--, ptset--, ptres--) {
k = INT_INTOBJ(*ptset); if (k <= deg) {
k = ptf2[k - 1] + 1;
}
*ptres = INTOBJ_INT(k);
}
} else {
ptf4 = CONST_ADDR_TRANS4(f);
deg = DEG_TRANS4(f); for (i = len; 1 <= i; i--, ptset--, ptres--) {
k = INT_INTOBJ(*ptset); if (k <= deg) {
k = ptf4[k - 1] + 1;
}
*ptres = INTOBJ_INT(k);
}
}
SortPlistByRawObj(res);
REMOVE_DUPS_PLIST_INTOBJ(res);
RetypeBagSM(res, T_PLIST_CYC_SSORT); return res;
}
/******************************************************************************* ******************************************************************************* * GAP kernel functions for transformations *******************************************************************************
*******************************************************************************/
if (def <= deg) { for (i = 0; i < def; i++) { if (*(ptf++) != *(ptg++)) { return 0;
}
} for (; i < deg; i++) { if (*(ptg++) != i) { return 0;
}
}
} else { // The only transformation created within this file that is of type // T_TRANS4 and that does not have (internal) degree 65537 or greater // is ID_TRANS4.
for (i = 0; i < deg; i++) { if (*(ptf++) != *(ptg++)) { return 0;
}
} for (; i < def; i++) { if (*(ptf++) != i) { return 0;
}
}
}
/******************************************************************************* ** Less than for transformations
*******************************************************************************/
if (def <= deg) { for (i = 0; i < def; i++) {
*ptfg++ = ptg[*ptf++];
} for (; i < deg; i++) {
*ptfg++ = ptg[i];
}
} else { for (i = 0; i < def; i++) {
*ptfg++ = IMAGE(ptf[i], ptg, deg);
}
}
return fg;
}
/******************************************************************************* ** Products for a transformation and permutation
*******************************************************************************/
if (def <= dep) { for (i = 0; i < def; i++) {
*ptfp++ = ptp[*ptf++];
} for (; i < dep; i++) {
*ptfp++ = ptp[i];
}
} else { for (i = 0; i < def; i++) {
*ptfp++ = IMAGE(ptf[i], ptp, dep);
}
} return fp;
}
/******************************************************************************* ** Products for a permutation and transformation
*******************************************************************************/
if (dep <= def) { for (i = 0; i < dep; i++) {
*ptpf++ = ptf[*ptp++];
} for (; i < def; i++) {
*ptpf++ = ptf[i];
}
} else { for (i = 0; i < dep; i++) {
*ptpf++ = IMAGE(ptp[i], ptf, def);
}
} return pf;
}
/******************************************************************************* ** Conjugate a transformation f by a permutation p: p ^ -1 * f * p
*******************************************************************************/
if (def == dep) { for (i = 0; i < decnj; i++) {
ptcnj[ptp[i]] = ptp[ptf[i]];
}
} else { for (i = 0; i < decnj; i++) {
ptcnj[IMAGE(i, ptp, dep)] = IMAGE(IMAGE(i, ptf, def), ptp, dep);
}
} return cnj;
}
/******************************************************************************* ** Left quotient a transformation f by a permutation p: p ^ -1 * f
*******************************************************************************/
if (degL <= degR) { for (p = 0; p < degL; p++) {
ptM[*(ptL++)] = *(ptR++);
} for (p = degL; p < degR; p++) {
ptM[p] = *(ptR++);
}
} else { for (p = 0; p < degR; p++) {
ptM[*(ptL++)] = *(ptR++);
} for (p = degR; p < degL; p++) {
ptM[*(ptL++)] = p;
}
}
return mod;
}
/******************************************************************************* ** Apply a transformation to a point
*******************************************************************************/
static Obj PowIntTrans2(Obj point, Obj f)
{ Int img;
if (TNUM_OBJ(point) == T_INTPOS) { return point;
}
/**************************************************************************** ** *F OnSetsTrans( <set>, <f> ) . . . . . . . . . operations on sets of points ** ** 'OnSetsTrans' returns the image of the tuple <set> under the ** transformation <f>. It is called from 'FuncOnSets'. ** ** The input <set> must be a non-empty set, i.e., plain, dense and strictly ** sorted. This is not verified.
*/
Obj OnSetsTrans(Obj set, Obj f)
{ const UInt2 * ptf2; const UInt4 * ptf4;
UInt deg;
Obj * ptres, tmp, res;
UInt i, isint, k;
// copy the list into a mutable plist, which we will then modify in place
res = PLAIN_LIST_COPY(set); const UInt len = LEN_PLIST(res);
ptres = ADDR_OBJ(res) + 1; if (TNUM_OBJ(f) == T_TRANS2) {
ptf2 = CONST_ADDR_TRANS2(f);
deg = DEG_TRANS2(f); // loop over the entries of the tuple
isint = 1; for (i = 1; i <= len; i++, ptres++) {
tmp = *ptres; if (IS_POS_INTOBJ(tmp)) {
k = INT_INTOBJ(tmp); if (k <= deg) {
*ptres = INTOBJ_INT(ptf2[k - 1] + 1);
}
} else {
isint = 0;
tmp = POW(tmp, f);
ptres = ADDR_OBJ(res) + i;
ptf2 = CONST_ADDR_TRANS2(f);
*ptres = tmp;
CHANGED_BAG(res);
}
}
} else {
ptf4 = CONST_ADDR_TRANS4(f);
deg = DEG_TRANS4(f);
// loop over the entries of the tuple
isint = 1; for (i = 1; i <= len; i++, ptres++) {
tmp = *ptres; if (IS_POS_INTOBJ(tmp)) {
k = INT_INTOBJ(tmp); if (k <= deg) {
*ptres = INTOBJ_INT(ptf4[k - 1] + 1);
}
} else {
isint = 0;
tmp = POW(tmp, f);
ptres = ADDR_OBJ(res) + i;
ptf4 = CONST_ADDR_TRANS4(f);
*ptres = tmp;
CHANGED_BAG(res);
}
}
}
// sort the result and remove dups if (isint) {
SortPlistByRawObj(res);
REMOVE_DUPS_PLIST_INTOBJ(res);
RetypeBagSM(res, T_PLIST_CYC_SSORT);
} else {
SortDensePlist(res);
RemoveDupsDensePlist(res);
RESET_FILT_LIST(res, FN_IS_SSORT);
}
return res;
}
/**************************************************************************** ** *F OnTuplesTrans( <tup>, <f> ) . . . . . . . operations on tuples of points ** ** 'OnTuplesTrans' returns the image of the tuple <tup> under the ** transformation <f>. It is called from 'FuncOnTuples'. ** ** The input <tup> must be a non-empty and dense plain list. This is not ** verified.
*/
Obj OnTuplesTrans(Obj tup, Obj f)
{ const UInt2 * ptf2; const UInt4 * ptf4;
UInt deg, i, k;
Obj * ptres, res, tmp;
// copy the list into a mutable plist, which we will then modify in place
res = PLAIN_LIST_COPY(tup);
RESET_FILT_LIST(res, FN_IS_NSORT); const UInt len = LEN_PLIST(res);
// loop over the entries of the tuple for (i = 1; i <= len; i++, ptres++) {
tmp = *ptres; if (IS_POS_INTOBJ(tmp)) {
k = INT_INTOBJ(tmp); if (k <= deg) {
k = ptf2[k - 1] + 1;
}
*ptres = INTOBJ_INT(k);
} elseif (tmp == NULL) {
ErrorQuit("OnTuples: <tup> must not contain holes", 0, 0);
} else {
tmp = POW(tmp, f);
ptres = ADDR_OBJ(res) + i;
ptf2 = CONST_ADDR_TRANS2(f);
*ptres = tmp;
CHANGED_BAG(res);
}
}
} else {
ptf4 = CONST_ADDR_TRANS4(f);
deg = DEG_TRANS4(f);
// loop over the entries of the tuple for (i = 1; i <= len; i++, ptres++) {
tmp = *ptres; if (IS_POS_INTOBJ(tmp)) {
k = INT_INTOBJ(tmp); if (k <= deg) {
k = ptf4[k - 1] + 1;
}
*ptres = INTOBJ_INT(k);
} elseif (tmp == NULL) {
ErrorQuit("OnTuples: <tup> must not contain holes", 0, 0);
} else {
tmp = POW(tmp, f);
ptres = ADDR_OBJ(res) + i;
ptf4 = CONST_ADDR_TRANS4(f);
*ptres = tmp;
CHANGED_BAG(res);
}
}
} return res;
}
/******************************************************************************* ** Save and load workspace, garbage collection, IS_TRANS
*******************************************************************************/
#ifdef GAP_ENABLE_SAVELOAD // Save and load staticvoid SaveTrans2(Obj f)
{ const UInt2 * ptr;
UInt len, i;
ptr = CONST_ADDR_TRANS2(f); // save the image list
len = DEG_TRANS2(f); for (i = 0; i < len; i++) {
SaveUInt2(*ptr++);
}
}
staticvoid LoadTrans2(Obj f)
{
UInt2 * ptr;
UInt len, i;
len = DEG_TRANS2(f);
ptr = ADDR_TRANS2(f); for (i = 0; i < len; i++) {
*ptr++ = LoadUInt2();
}
}
staticvoid SaveTrans4(Obj f)
{ const UInt4 * ptr;
UInt len, i;
ptr = CONST_ADDR_TRANS4(f); // save the image list
len = DEG_TRANS4(f); for (i = 0; i < len; i++) {
SaveUInt4(*ptr++);
}
}
staticvoid LoadTrans4(Obj f)
{
UInt4 * ptr;
UInt len, i;
len = DEG_TRANS4(f);
ptr = ADDR_TRANS4(f); for (i = 0; i < len; i++) {
*ptr++ = LoadUInt4();
}
} #endif
// init filters and functions
InitHdlrFiltsFromTable(GVarFilts);
InitHdlrFuncsFromTable(GVarFuncs);
// register global bags with the garbage collector
InitGlobalBag(&MODULE_STATE(Trans).TmpTrans, "src/trans.c:TmpTrans");
InitGlobalBag(&IdentityTrans, "src/trans.c:IdentityTrans");
// We make the next transformation to allow testing of some parts of the // code which would not otherwise be accessible, since no other // transformation created in this file is a T_TRANS4 unless its internal // degree is > 65536. Such transformation can be created by packages with // a // kernel module, and so we introduce the next transformation for testing // purposes.
Obj ID_TRANS4 = NEW_TRANS4(0);
AssReadOnlyGVar(GVarName("ID_TRANS4"), ID_TRANS4);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.