/**************************************************************************** ** ** 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 ** ** This file contains the functions for permutations (small and large). ** ** Mathematically a permutation is a bijective mapping of a finite set onto ** itself. In \GAP\ this subset must always be of the form [ 1, 2, .., N ], ** where N is at most $2^32$. ** ** Internally a permutation <p> is viewed as a mapping of [ 0, 1, .., N-1 ], ** because in C indexing of arrays is done with the origin 0 instead of 1. ** A permutation is represented by a bag of type 'T_PERM2' or 'T_PERM4' of ** the form ** ** (CONST_)ADDR_OBJ(p) ** | ** v ** +------+-------+-------+-------+-------+- - - -+-------+-------+ ** | inv. | image | image | image | image | | image | image | ** | perm | of 0 | of 1 | of 2 | of 3 | | of N-2| of N-1| ** +------+-------+-------+-------+-------+- - - -+-------+-------+ ** ^ ** | ** (CONST_)ADDR_PERM2(p) resp. (CONST_)ADDR_PERM4(p) ** ** The first entry of the bag <p> is either zero, or a reference to another ** permutation representing the inverse of <p>. The remaining entries of the ** bag form an array describing the permutation. For bags of type 'T_PERM2', ** the entries are of type 'UInt2' (defined in 'common.h' as an 16 bit wide ** unsigned integer type), for type 'T_PERM4' the entries are of type ** 'UInt4' (defined as a 32bit wide unsigned integer type). The first of ** these entries is the image of 0, the second is the image of 1, and so on. ** Thus, the entry at C index <i> is the image of <i>, if we view the ** permutation as mapping of [ 0, 1, 2, .., N-1 ] as described above. ** ** Permutations are never shortened. For example, if the product of two ** permutations of degree 100 is the identity, it is nevertheless stored as ** array of length 100, in which the <i>-th entry is of course simply <i>. ** Testing whether a product has trailing fixpoints would be pretty costly, ** and permutations of equal degree can be handled by the functions faster.
*/
/**************************************************************************** ** *V TmpPerm . . . . . . . handle of the buffer bag of the permutation package ** ** 'TmpPerm' is the handle of a bag of type 'T_PERM4', which is created at ** initialization time of this package. Functions in this package can use ** this bag for whatever purpose they want. They have to make sure of ** course that it is large enough. ** The buffer is *not* guaranteed to have any particular value, routines ** that require a zero-initialization need to do this at the start. ** This buffer is only constructed once it is needed, to avoid startup ** costs (particularly when starting new threads). ** Use the UseTmpPerm(<size>) utility function to ensure it is constructed!
*/
Obj TmpPerm;
template <typename T> staticinline T * ADDR_TMP_PERM()
{ // no GAP_ASSERT here on purpose return (T *)(ADDR_OBJ(TmpPerm) + 1);
}
/**************************************************************************** ** *F TypePerm( <perm> ) . . . . . . . . . . . . . . . . type of a permutation ** ** 'TypePerm' returns the type of permutations. ** ** 'TypePerm' is the function in 'TypeObjFuncs' for permutations.
*/ static Obj TYPE_PERM2;
/**************************************************************************** ** *F PrintPerm( <perm> ) . . . . . . . . . . . . . . . . . print a permutation ** ** 'PrintPerm' prints the permutation <perm> in the usual cycle notation. It ** uses the degree to print all points with same width, which looks nicer. ** Linebreaks are preferred most after cycles and next most after commas. ** ** It remembers which points have already been printed, to avoid O(n^2) ** behaviour.
*/ template <typename T> staticvoid PrintPerm(Obj perm)
{
UInt degPerm; // degree of the permutation const T * ptPerm; // pointer to the permutation
UInt p, q; // loop variables BOOL isId; // permutation is the identity? constchar * fmt1; // common formats to print points constchar * fmt2; // common formats to print points
// set up the formats used, so all points are printed with equal width // Use LargestMovedPoint so printing is consistent
degPerm = LargestMovedPointPerm_<T>(perm); if ( degPerm < 10 ) { fmt1 = "%>(%>%1d%<"; fmt2 = ",%>%1d%<"; } elseif ( degPerm < 100 ) { fmt1 = "%>(%>%2d%<"; fmt2 = ",%>%2d%<"; } elseif ( degPerm < 1000 ) { fmt1 = "%>(%>%3d%<"; fmt2 = ",%>%3d%<"; } elseif ( degPerm < 10000 ) { fmt1 = "%>(%>%4d%<"; fmt2 = ",%>%4d%<"; } else { fmt1 = "%>(%>%5d%<"; fmt2 = ",%>%5d%<"; }
UseTmpPerm(SIZE_OBJ(perm));
T * ptSeen = ADDR_TMP_PERM<T>();
// clear the buffer bag
memset(ptSeen, 0, DEG_PERM<T>(perm) * sizeof(T));
// run through all points
isId = TRUE;
ptPerm = CONST_ADDR_PERM<T>(perm); for ( p = 0; p < degPerm; p++ ) { // if the smallest is the one we started with lets print the cycle if (!ptSeen[p] && ptPerm[p] != p) {
ptSeen[p] = 1;
isId = FALSE;
Pr(fmt1,(Int)(p+1), 0); // restore pointer, in case Pr caused a garbage collection
ptPerm = CONST_ADDR_PERM<T>(perm);
ptSeen = ADDR_TMP_PERM<T>(); for ( q = ptPerm[p]; q != p; q = ptPerm[q] ) {
ptSeen[q] = 1;
Pr(fmt2,(Int)(q+1), 0);
ptPerm = CONST_ADDR_PERM<T>(perm);
ptSeen = ADDR_TMP_PERM<T>();
}
Pr("%<)", 0, 0); // restore pointer, in case Pr caused a garbage collection
ptPerm = CONST_ADDR_PERM<T>(perm);
ptSeen = ADDR_TMP_PERM<T>();
}
}
// special case for the identity if ( isId ) Pr("()", 0, 0);
}
/**************************************************************************** ** *F EqPerm( <opL>, <opR> ) . . . . . . . test if two permutations are equal ** ** 'EqPerm' returns 'true' if the two permutations <opL> and <opR> are equal ** and 'false' otherwise. ** ** Two permutations may be equal, even if the two sequences do not have the ** same length, if the larger permutation fixes the exceeding points. **
*/ template <typename TL, typename TR> staticInt EqPerm(Obj opL, Obj opR)
{
UInt degL; // degree of the left operand const TL * ptL; // pointer to the left operand
UInt degR; // degree of the right operand const TR * ptR; // pointer to the right operand
UInt p; // loop variable
// get the degrees
degL = DEG_PERM<TL>(opL);
degR = DEG_PERM<TR>(opR);
// set up the pointers
ptL = CONST_ADDR_PERM<TL>(opL);
ptR = CONST_ADDR_PERM<TR>(opR);
// search for a difference and return False if you find one if ( degL <= degR ) { for ( p = 0; p < degL; p++ ) if ( *(ptL++) != *(ptR++) ) return 0; for ( p = degL; p < degR; p++ ) if ( p != *(ptR++) ) return 0;
} else { for ( p = 0; p < degR; p++ ) if ( *(ptL++) != *(ptR++) ) return 0; for ( p = degR; p < degL; p++ ) if ( *(ptL++) != p ) return 0;
}
// otherwise they must be equal return 1;
}
/**************************************************************************** ** *F LtPerm( <opL>, <opR> ) . test if one permutation is smaller than another ** ** 'LtPerm' returns 'true' if the permutation <opL> is strictly less than ** the permutation <opR>. Permutations are ordered lexicographically with ** respect to the images of 1,2,.., etc.
*/ template <typename TL, typename TR> staticInt LtPerm(Obj opL, Obj opR)
{
UInt degL; // degree of the left operand const TL * ptL; // pointer to the left operand
UInt degR; // degree of the right operand const TR * ptR; // pointer to the right operand
UInt p; // loop variable
// get the degrees of the permutations
degL = DEG_PERM<TL>(opL);
degR = DEG_PERM<TR>(opR);
// set up the pointers
ptL = CONST_ADDR_PERM<TL>(opL);
ptR = CONST_ADDR_PERM<TR>(opR);
// search for a difference and return if you find one if ( degL <= degR ) {
for (p = 0; p < degL; p++, ptL++, ptR++) if (*ptL != *ptR) { return *ptL < *ptR;
} for (p = degL; p < degR; p++, ptR++) if (p != *ptR) { return p < *ptR;
}
} else { for (p = 0; p < degR; p++, ptL++, ptR++) if (*ptL != *ptR) { return *ptL < *ptR;
} for (p = degR; p < degL; p++, ptL++) if (*ptL != p) { return *ptL < p;
}
}
// otherwise they must be equal return 0;
}
/**************************************************************************** ** *F ProdPerm( <opL>, <opR> ) . . . . . . . . . . . . product of permutations ** ** 'ProdPerm' returns the product of the two permutations <opL> and <opR>. ** ** This is a little bit tuned but should be sufficiently easy to understand.
*/ template <typename TL, typename TR> static Obj ProdPerm(Obj opL, Obj opR)
{ typedeftypename ResultType<TL,TR>::type Res;
Obj prd; // handle of the product (result)
UInt degP; // degree of the product
Res * ptP; // pointer to the product
UInt degL; // degree of the left operand const TL * ptL; // pointer to the left operand
UInt degR; // degree of the right operand const TR * ptR; // pointer to the right operand
UInt p; // loop variable
// handle trivial cases if (degL == 0) { return opR;
} if (degR == 0) { return opL;
}
// compute the size of the result and allocate a bag
degP = degL < degR ? degR : degL;
prd = NEW_PERM<Res>( degP );
// set up the pointers
ptL = CONST_ADDR_PERM<TL>(opL);
ptR = CONST_ADDR_PERM<TR>(opR);
ptP = ADDR_PERM<Res>(prd);
// if the left (inner) permutation has smaller degree, it is very easy if ( degL <= degR ) { for ( p = 0; p < degL; p++ )
*(ptP++) = ptR[ *(ptL++) ]; for ( p = degL; p < degR; p++ )
*(ptP++) = ptR[ p ];
}
// otherwise we have to use the macro 'IMAGE' else { for ( p = 0; p < degL; p++ )
*(ptP++) = IMAGE( ptL[ p ], ptR, degR );
}
return prd;
}
/**************************************************************************** ** *F QuoPerm( <opL>, <opR> ) . . . . . . . . . . . . quotient of permutations ** ** 'QuoPerm' returns the quotient of the permutations <opL> and <opR>, i.e., ** the product '<opL>\*<opR>\^-1'. ** ** Unfortunately this cannot be done in <degree> steps, we need 2 * <degree> ** steps.
*/ static Obj QuoPerm(Obj opL, Obj opR)
{ return PROD(opL, INV(opR));
}
/**************************************************************************** ** *F LQuoPerm( <opL>, <opR> ) . . . . . . . . . left quotient of permutations ** ** 'LQuoPerm' returns the left quotient of the two permutations <opL> and ** <opR>, i.e., the value of '<opL>\^-1*<opR>', which sometimes comes handy. ** ** This can be done as fast as a single multiplication or inversion.
*/ template <typename TL, typename TR> static Obj LQuoPerm(Obj opL, Obj opR)
{ typedeftypename ResultType<TL,TR>::type Res;
Obj mod; // handle of the quotient (result)
UInt degM; // degree of the quotient
Res * ptM; // pointer to the quotient
UInt degL; // degree of the left operand const TL * ptL; // pointer to the left operand
UInt degR; // degree of the right operand const TR * ptR; // pointer to the right operand
UInt p; // loop variable
// handle trivial cases if (degL == 0) { return opR;
} if (degR == 0) { return InvPerm<TL>(opL);
}
// compute the size of the result and allocate a bag
degM = degL < degR ? degR : degL;
mod = NEW_PERM<Res>( degM );
// set up the pointers
ptL = CONST_ADDR_PERM<TL>(opL);
ptR = CONST_ADDR_PERM<TR>(opR);
ptM = ADDR_PERM<Res>(mod);
// it is one thing if the left (inner) permutation is smaller if ( degL <= degR ) { for ( p = 0; p < degL; p++ )
ptM[ *(ptL++) ] = *(ptR++); for ( p = degL; p < degR; p++ )
ptM[ p ] = *(ptR++);
}
// and another if the right (outer) permutation is smaller else { for ( p = 0; p < degR; p++ )
ptM[ *(ptL++) ] = *(ptR++); for ( p = degR; p < degL; p++ )
ptM[ *(ptL++) ] = p;
}
return mod;
}
/**************************************************************************** ** *F InvPerm( <perm> ) . . . . . . . . . . . . . . . inverse of a permutation
*/ template <typename T> static Obj InvPerm(Obj perm)
{
Obj inv; // handle of the inverse (result)
T * ptInv; // pointer to the inverse const T * ptPerm; // pointer to the permutation
UInt deg; // degree of the permutation
UInt p; // loop variables
inv = STOREDINV_PERM(perm); if (inv != 0) return inv;
deg = DEG_PERM<T>(perm);
inv = NEW_PERM<T>(deg);
// get pointer to the permutation and the inverse
ptPerm = CONST_ADDR_PERM<T>(perm);
ptInv = ADDR_PERM<T>(inv);
// invert the permutation for ( p = 0; p < deg; p++ )
ptInv[ *ptPerm++ ] = p;
// store and return the inverse
SET_STOREDINV_PERM(perm, inv); return inv;
}
/**************************************************************************** ** *F PowPermInt( <opL>, <opR> ) . . . . . . . integer power of a permutation ** ** 'PowPermInt' returns the <opR>-th power of the permutation <opL>. <opR> ** must be a small integer. ** ** This repeatedly applies the permutation <opR> to all points which seems ** to be faster than binary powering, and does not need temporary storage.
*/ template <typename T> static Obj PowPermInt(Obj opL, Obj opR)
{
Obj pow; // handle of the power (result)
T * ptP; // pointer to the power const T * ptL; // pointer to the permutation
UInt1 * ptKnown; // pointer to temporary bag
UInt deg; // degree of the permutation Int exp, e; // exponent (right operand)
UInt len; // length of cycle (result)
UInt p, q, r; // loop variables
// handle zeroth and first powers and stored inverses separately if ( opR == INTOBJ_INT(0)) return IdentityPerm; if ( opR == INTOBJ_INT(1)) return opL; if (opR == INTOBJ_INT(-1)) return InvPerm<T>(opL);
// compute the power by repeated mapping for small positive exponents if ( IS_INTOBJ(opR)
&& 2 <= INT_INTOBJ(opR) && INT_INTOBJ(opR) < 8 ) {
// get pointer to the permutation and the power
exp = INT_INTOBJ(opR);
ptL = CONST_ADDR_PERM<T>(opL);
ptP = ADDR_PERM<T>(pow);
// loop over the points of the permutation for ( p = 0; p < deg; p++ ) {
q = p; for ( e = 0; e < exp; e++ )
q = ptL[q];
ptP[p] = q;
}
}
// compute the power by raising the cycles individually for large exps elseif ( IS_INTOBJ(opR) && 8 <= INT_INTOBJ(opR) ) {
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(opL));
ptKnown = ADDR_TMP_PERM<UInt1>();
// clear the buffer bag
memset(ptKnown, 0, DEG_PERM<T>(opL));
// get pointer to the permutation and the power
exp = INT_INTOBJ(opR);
ptL = CONST_ADDR_PERM<T>(opL);
ptP = ADDR_PERM<T>(pow);
// loop over all cycles for ( p = 0; p < deg; p++ ) {
// if we haven't looked at this cycle so far if ( ptKnown[p] == 0 ) {
// find the length of this cycle
len = 1; for ( q = ptL[p]; q != p; q = ptL[q] ) {
len++; ptKnown[q] = 1;
}
// raise this cycle to the power <exp> mod <len>
r = p; for ( e = 0; e < exp % len; e++ )
r = ptL[r];
ptP[p] = r;
r = ptL[r]; for ( q = ptL[p]; q != p; q = ptL[q] ) {
ptP[q] = r;
r = ptL[r];
}
}
}
}
// compute the power by raising the cycles individually for large exps elseif ( TNUM_OBJ(opR) == T_INTPOS ) {
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(opL));
ptKnown = ADDR_TMP_PERM<UInt1>();
// clear the buffer bag
memset(ptKnown, 0, DEG_PERM<T>(opL));
// get pointer to the permutation and the power
ptL = CONST_ADDR_PERM<T>(opL);
ptP = ADDR_PERM<T>(pow);
// loop over all cycles for ( p = 0; p < deg; p++ ) {
// if we haven't looked at this cycle so far if ( ptKnown[p] == 0 ) {
// find the length of this cycle
len = 1; for ( q = ptL[p]; q != p; q = ptL[q] ) {
len++; ptKnown[q] = 1;
}
// raise this cycle to the power <exp> mod <len>
r = p;
exp = INT_INTOBJ( ModInt( opR, INTOBJ_INT(len) ) ); for ( e = 0; e < exp; e++ )
r = ptL[r];
ptP[p] = r;
r = ptL[r]; for ( q = ptL[p]; q != p; q = ptL[q] ) {
ptP[q] = r;
r = ptL[r];
}
}
}
}
// compute the power by repeated mapping for small negative exponents elseif ( IS_INTOBJ(opR)
&& -8 < INT_INTOBJ(opR) && INT_INTOBJ(opR) < 0 ) {
// get pointer to the permutation and the power
exp = -INT_INTOBJ(opR);
ptL = CONST_ADDR_PERM<T>(opL);
ptP = ADDR_PERM<T>(pow);
// loop over the points for ( p = 0; p < deg; p++ ) {
q = p; for ( e = 0; e < exp; e++ )
q = ptL[q];
ptP[q] = p;
}
}
// compute the power by raising the cycles individually for large exps elseif ( IS_INTOBJ(opR) && INT_INTOBJ(opR) <= -8 ) {
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(opL));
ptKnown = ADDR_TMP_PERM<UInt1>();
// clear the buffer bag
memset(ptKnown, 0, DEG_PERM<T>(opL));
// get pointer to the permutation and the power
exp = -INT_INTOBJ(opR);
ptL = CONST_ADDR_PERM<T>(opL);
ptP = ADDR_PERM<T>(pow);
// loop over all cycles for ( p = 0; p < deg; p++ ) {
// if we haven't looked at this cycle so far if ( ptKnown[p] == 0 ) {
// find the length of this cycle
len = 1; for ( q = ptL[p]; q != p; q = ptL[q] ) {
len++; ptKnown[q] = 1;
}
// raise this cycle to the power <exp> mod <len>
r = p; for ( e = 0; e < exp % len; e++ )
r = ptL[r];
ptP[r] = p;
r = ptL[r]; for ( q = ptL[p]; q != p; q = ptL[q] ) {
ptP[r] = q;
r = ptL[r];
}
}
}
}
// compute the power by raising the cycles individually for large exps elseif ( TNUM_OBJ(opR) == T_INTNEG ) { // do negation first as it can cause a garbage collection
opR = AInvInt(opR);
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(opL));
ptKnown = ADDR_TMP_PERM<UInt1>();
// clear the buffer bag
memset(ptKnown, 0, DEG_PERM<T>(opL));
// get pointer to the permutation and the power
ptL = CONST_ADDR_PERM<T>(opL);
ptP = ADDR_PERM<T>(pow);
// loop over all cycles for ( p = 0; p < deg; p++ ) {
// if we haven't looked at this cycle so far if ( ptKnown[p] == 0 ) {
// find the length of this cycle
len = 1; for ( q = ptL[p]; q != p; q = ptL[q] ) {
len++; ptKnown[q] = 1;
}
// raise this cycle to the power <exp> mod <len>
r = p;
exp = INT_INTOBJ( ModInt( opR, INTOBJ_INT(len) ) ); for ( e = 0; e < exp % len; e++ )
r = ptL[r];
ptP[r] = p;
r = ptL[r]; for ( q = ptL[p]; q != p; q = ptL[q] ) {
ptP[r] = q;
r = ptL[r];
}
}
}
}
return pow;
}
/**************************************************************************** ** *F PowIntPerm( <opL>, <opR> ) . . . image of an integer under a permutation ** ** 'PowIntPerm' returns the image of the positive integer <opL> under the ** permutation <opR>. If <opL> is larger than the degree of <opR> it is a ** fixpoint of the permutation and thus simply returned.
*/ template <typename T> static Obj PowIntPerm(Obj opL, Obj opR)
{ Int img; // image (result)
// large positive integers (> 2^28-1) are fixed by any permutation if ( TNUM_OBJ(opL) == T_INTPOS ) return opL;
img = INT_INTOBJ(opL);
RequireArgumentConditionEx("PowIntPerm", opL, "", img > 0, "must be a positive integer");
// compute the image if ( img <= DEG_PERM<T>(opR) ) {
img = (CONST_ADDR_PERM<T>(opR))[img-1] + 1;
}
// return it return INTOBJ_INT(img);
}
/**************************************************************************** ** *F QuoIntPerm( <opL>, <opR> ) . preimage of an integer under a permutation ** ** 'QuoIntPerm' returns the preimage of the preimage integer <opL> under the ** permutation <opR>. If <opL> is larger than the degree of <opR> it is a ** fixpoint, and thus simply returned. ** ** There are basically two ways to find the preimage. One is to run through ** <opR> and look for <opL>. The index where it's found is the preimage. ** The other is to find the image of <opL> under <opR>, the image of that ** point and so on, until we come back to <opL>. The last point is the ** preimage of <opL>. This is faster because the cycles are usually short.
*/ static Obj PERM_INVERSE_THRESHOLD;
template <typename T> static Obj QuoIntPerm(Obj opL, Obj opR)
{
T pre; // preimage (result) Int img; // image (left operand) const T * ptR; // pointer to the permutation
// compute the preimage if ( img <= DEG_PERM<T>(opR) ) {
pre = T(img - 1);
ptR = CONST_ADDR_PERM<T>(opR); while (ptR[pre] != T(img - 1))
pre = ptR[pre]; // return it return INTOBJ_INT(pre + 1);
} else return INTOBJ_INT(img);
}
/**************************************************************************** ** *F PowPerm( <opL>, <opR> ) . . . . . . . . . . . conjugation of permutations ** ** 'PowPerm' returns the conjugation of the two permutations <opL> and ** <opR>, that s defined as the following product '<opR>\^-1 \*\ <opL> \*\ ** <opR>'.
*/ template <typename TL, typename TR> static Obj PowPerm(Obj opL, Obj opR)
{ typedeftypename ResultType<TL,TR>::type Res;
Obj cnj; // handle of the conjugation (res)
UInt degC; // degree of the conjugation
Res * ptC; // pointer to the conjugation
UInt degL; // degree of the left operand const TL * ptL; // pointer to the left operand
UInt degR; // degree of the right operand const TR * ptR; // pointer to the right operand
UInt p; // loop variable
// compute the size of the result and allocate a bag
degC = degL < degR ? degR : degL;
cnj = NEW_PERM<Res>( degC );
// set up the pointers
ptL = CONST_ADDR_PERM<TL>(opL);
ptR = CONST_ADDR_PERM<TR>(opR);
ptC = ADDR_PERM<Res>(cnj);
// it is faster if the both permutations have the same size if ( degL == degR ) { for ( p = 0; p < degC; p++ )
ptC[ ptR[p] ] = ptR[ ptL[p] ];
}
// otherwise we have to use the macro 'IMAGE' three times else { for ( p = 0; p < degC; p++ )
ptC[ IMAGE(p,ptR,degR) ] = IMAGE( IMAGE(p,ptL,degL), ptR, degR );
}
return cnj;
}
/**************************************************************************** ** *F CommPerm( <opL>, <opR> ) . . . . . . . . commutator of two permutations ** ** 'CommPerm' returns the commutator of the two permutations <opL> and ** <opR>, that is defined as '<hd>\^-1 \*\ <opR>\^-1 \*\ <opL> \*\ <opR>'.
*/ template <typename TL, typename TR> static Obj CommPerm(Obj opL, Obj opR)
{ typedeftypename ResultType<TL,TR>::type Res;
Obj com; // handle of the commutator (res)
UInt degC; // degree of the commutator
Res * ptC; // pointer to the commutator
UInt degL; // degree of the left operand const TL * ptL; // pointer to the left operand
UInt degR; // degree of the right operand const TR * ptR; // pointer to the right operand
UInt p; // loop variable
// compute the size of the result and allocate a bag
degC = degL < degR ? degR : degL;
com = NEW_PERM<Res>( degC );
// set up the pointers
ptL = CONST_ADDR_PERM<TL>(opL);
ptR = CONST_ADDR_PERM<TR>(opR);
ptC = ADDR_PERM<Res>(com);
// it is faster if the both permutations have the same size if ( degL == degR ) { for ( p = 0; p < degC; p++ )
ptC[ ptL[ ptR[ p ] ] ] = ptR[ ptL[ p ] ];
}
// otherwise we have to use the macro 'IMAGE' four times else { for ( p = 0; p < degC; p++ )
ptC[ IMAGE( IMAGE(p,ptR,degR), ptL, degL ) ]
= IMAGE( IMAGE(p,ptL,degL), ptR, degR );
}
/**************************************************************************** ** *F FiltIS_PERM( <self>, <val> ) . . . . . . test if a value is a permutation ** ** 'FiltIS_PERM' implements the internal function 'IsPerm'. ** ** 'IsPerm( <val> )' ** ** 'IsPerm' returns 'true' if the value <val> is a permutation and 'false' ** otherwise.
*/ static Obj IsPermFilt;
static Obj FiltIS_PERM(Obj self, Obj val)
{ // return 'true' if <val> is a permutation and 'false' otherwise if ( TNUM_OBJ(val) == T_PERM2 || TNUM_OBJ(val) == T_PERM4 ) { returnTrue;
} elseif ( TNUM_OBJ(val) < FIRST_EXTERNAL_TNUM ) { returnFalse;
} else { return DoFilter( self, val );
}
}
/**************************************************************************** ** *F FuncPermList( <self>, <list> ) . . . . . convert a list to a permutation ** ** 'FuncPermList' implements the internal function 'PermList' ** ** 'PermList( <list> )' ** ** Converts the list <list> into a permutation, which is then returned. ** ** 'FuncPermList' simply copies the list pointwise into a permutation bag. ** It also does some checks to make sure that the list is a permutation.
*/ template <typename T> staticinline Obj PermList(Obj list)
{
Obj perm; // handle of the permutation
T * ptPerm; // pointer to the permutation
UInt degPerm; // degree of the permutation const Obj * ptList; // pointer to the list
T * ptTmp; // pointer to the buffer bag Int i, k; // loop variables
GAP_ASSERT(IS_PLIST(list));
degPerm = LEN_PLIST( list );
/* make sure that the global buffer bag is large enough for checkin*/
UseTmpPerm(SIZEBAG_PERM<T>(degPerm));
// allocate the bag for the permutation and get pointer
perm = NEW_PERM<T>(degPerm);
ptPerm = ADDR_PERM<T>(perm);
ptList = CONST_ADDR_OBJ(list);
ptTmp = ADDR_TMP_PERM<T>();
// make the buffer bag clean
memset(ptTmp, 0, degPerm * sizeof(T));
// run through all entries of the list for ( i = 1; i <= degPerm; i++ ) {
// get the <i>th entry of the list if ( !IS_INTOBJ(ptList[i]) ) { return Fail;
}
k = INT_INTOBJ(ptList[i]); if ( k <= 0 || degPerm < k ) { return Fail;
}
// make sure we haven't seen this entry yet if ( ptTmp[k-1] != 0 ) { return Fail;
}
ptTmp[k-1] = 1;
// and finally copy it into the permutation
ptPerm[i-1] = k-1;
}
UInt len = LEN_LIST( list ); if (len == 0) return IdentityPerm; if (!IS_PLIST(list)) { if (!IS_POSS_LIST(list)) return Fail; if (IS_RANGE(list)) { if (GET_LOW_RANGE(list) == 1 && GET_INC_RANGE(list) == 1) return IdentityPerm;
}
list = PLAIN_LIST_COPY(list);
}
if ( len <= 65536 ) { return PermList<UInt2>(list);
} elseif (len <= MAX_DEG_PERM4) { return PermList<UInt4>(list);
} else {
ErrorMayQuit("PermList: list length %d exceeds maximum permutation degree",
len, 0);
}
}
template <typename T> staticinline Obj ListPerm_(Obj perm, Int len)
{
Obj res; // handle of the image, result
Obj * ptRes; // pointer to the result const T * ptPrm; // pointer to the permutation
UInt deg; // degree of the permutation
UInt i; // loop variable
if (len <= 0) return NewEmptyPlist();
// copy the list into a mutable plist, which we will then modify in place
res = NEW_PLIST(T_PLIST_CYC, len);
SET_LEN_PLIST(res, len);
// get the pointer
ptRes = ADDR_OBJ(res) + 1;
ptPrm = CONST_ADDR_PERM<T>(perm);
// loop over the entries of the permutation
deg = DEG_PERM<T>(perm); if (deg > len)
deg = len; for (i = 1; i <= deg; i++, ptRes++) {
*ptRes = INTOBJ_INT(ptPrm[i - 1] + 1);
} // add extras if requested for (; i <= len; i++, ptRes++) {
*ptRes = INTOBJ_INT(i);
}
return res;
}
static Obj ListPermOper;
static Obj FuncListPerm1(Obj self, Obj perm)
{
RequirePermutation(SELF_NAME, perm); Int nn = LargestMovedPointPerm(perm); if (TNUM_OBJ(perm) == T_PERM2) return ListPerm_<UInt2>(perm, nn); else return ListPerm_<UInt4>(perm, nn);
}
static Obj FuncListPerm2(Obj self, Obj perm, Obj n)
{
RequirePermutation(SELF_NAME, perm); Int nn = GetSmallInt(SELF_NAME, n); if (TNUM_OBJ(perm) == T_PERM2) return ListPerm_<UInt2>(perm, nn); else return ListPerm_<UInt4>(perm, nn);
}
/**************************************************************************** ** *F LargestMovedPointPerm( <perm> ) largest point moved by perm ** ** 'LargestMovedPointPerm' returns the largest positive integer that is ** moved by the permutation <perm>. ** ** This is easy, except that permutations may contain trailing fixpoints.
*/ template <typename T> staticinline UInt LargestMovedPointPerm_(Obj perm)
{
UInt sup; const T * ptPerm;
/**************************************************************************** ** *F FuncCYCLE_LENGTH_PERM_INT( <self>, <perm>, <point> ) . . . . . . . . . . *F . . . . . . . . . . . . . . . . . . length of a cycle under a permutation ** ** 'FuncCycleLengthInt' implements the internal function ** 'CycleLengthPermInt' ** ** 'CycleLengthPermInt( <perm>, <point> )' ** ** 'CycleLengthPermInt' returns the length of the cycle of <point>, which ** must be a positive integer, under the permutation <perm>. ** ** Note that the order of the arguments to this function has been reversed.
*/ template <typename T> staticinline Obj CYCLE_LENGTH_PERM_INT(Obj perm, UInt pnt)
{ const T * ptPerm; // pointer to the permutation
UInt deg; // degree of the permutation
UInt len; // length of cycle (result)
UInt p; // loop variable
// get pointer to the permutation, the degree, and the point
ptPerm = CONST_ADDR_PERM<T>(perm);
deg = DEG_PERM<T>(perm);
// now compute the length by looping over the cycle
len = 1; if ( pnt < deg ) { for ( p = ptPerm[pnt]; p != pnt; p = ptPerm[p] )
len++;
}
return INTOBJ_INT(len);
}
static Obj FuncCYCLE_LENGTH_PERM_INT(Obj self, Obj perm, Obj point)
{
UInt pnt; // value of the point
/**************************************************************************** ** *F FuncCYCLE_PERM_INT( <self>, <perm>, <point> ) . . cycle of a permutation * ** 'FuncCYCLE_PERM_INT' implements the internal function 'CyclePermInt'. ** ** 'CyclePermInt( <perm>, <point> )' ** ** 'CyclePermInt' returns the cycle of <point>, which must be a positive ** integer, under the permutation <perm> as a list.
*/ template <typename T> staticinline Obj CYCLE_PERM_INT(Obj perm, UInt pnt)
{
Obj list; // handle of the list (result)
Obj * ptList; // pointer to the list const T * ptPerm; // pointer to the permutation
UInt deg; // degree of the permutation
UInt len; // length of the cycle
UInt p; // loop variable
// get pointer to the permutation, the degree, and the point
ptPerm = CONST_ADDR_PERM<T>(perm);
deg = DEG_PERM<T>(perm);
// now compute the length by looping over the cycle
len = 1; if ( pnt < deg ) { for ( p = ptPerm[pnt]; p != pnt; p = ptPerm[p] )
len++;
}
// allocate the list
list = NEW_PLIST( T_PLIST, len );
SET_LEN_PLIST( list, len );
ptList = ADDR_OBJ(list);
ptPerm = CONST_ADDR_PERM<T>(perm);
// copy the points into the list
len = 1;
ptList[len++] = INTOBJ_INT( pnt+1 ); if ( pnt < deg ) { for ( p = ptPerm[pnt]; p != pnt; p = ptPerm[p] )
ptList[len++] = INTOBJ_INT( p+1 );
}
return list;
}
static Obj FuncCYCLE_PERM_INT(Obj self, Obj perm, Obj point)
{
UInt pnt; // value of the point
/**************************************************************************** ** *F FuncCYCLE_STRUCT_PERM( <self>, <perm> ) . . . . . cycle structure of perm * ** 'FuncCYCLE_STRUCT_PERM' implements the internal function ** `CycleStructPerm'. ** ** `CycleStructPerm( <perm> )' ** ** 'CycleStructPerm' returns a list of the form as described under ** `CycleStructure'.
*/ template <typename T> staticinline Obj CYCLE_STRUCT_PERM(Obj perm)
{
Obj list; // handle of the list (result)
Obj * ptList; // pointer to the list const T * ptPerm; // pointer to the permutation
T * scratch;
T * offset;
UInt deg; // degree of the permutation
UInt pnt; // value of the point
UInt len; // length of the cycle
UInt p; // loop variable
UInt max; // maximal cycle length
UInt cnt;
UInt ende;
UInt bytes;
UInt1 * clr;
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(perm) + 8);
// find the largest moved point
deg = LargestMovedPointPerm_<T>(perm); if (deg == 0) { // special treatment of identity return NewEmptyPlist();
}
scratch = ADDR_TMP_PERM<T>();
// the first deg bytes of TmpPerm hold a bit list of points done // so far. The remaining bytes will form the lengths of nontrivial // cycles (as numbers of type T). As every nontrivial cycle requires // at least 2 points, this is guaranteed to fit.
bytes = ((deg / sizeof(T)) + 1) * sizeof(T); // ensure alignment
offset = (T *)((UInt)scratch + (bytes));
// clear out the bits
memset(scratch, 0, bytes);
cnt = 0;
clr = (UInt1 *)scratch;
max = 0;
ptPerm = CONST_ADDR_PERM<T>(perm); for (pnt = 0; pnt < deg; pnt++) { if (clr[pnt] == 0) {
len = 0;
clr[pnt] = 1; for (p = ptPerm[pnt]; p != pnt; p = ptPerm[p]) {
clr[p] = 1;
len++;
}
if (len > 0) {
offset[cnt] = (T)len;
cnt++; if (len > max) {
max = len;
}
}
}
}
ende = cnt;
// create the list
list = NEW_PLIST(T_PLIST, max);
SET_LEN_PLIST(list, max);
ptList = ADDR_OBJ(list);
// Recalculate after possible GC
scratch = ADDR_TMP_PERM<T>();
offset = (T *)((UInt)scratch + (bytes));
/**************************************************************************** ** *F FuncORDER_PERM( <self>, <perm> ) . . . . . . . . . order of a permutation ** ** 'FuncORDER_PERM' implements the internal function 'OrderPerm'. ** ** 'OrderPerm( <perm> )' ** ** 'OrderPerm' returns the order of the permutation <perm>, i.e., the ** smallest positive integer <n> such that '<perm>\^<n>' is the identity. ** ** Since the largest element in S(65536) has order greater than 10^382 this ** computation may easily overflow. So we have to use arbitrary precision.
*/ template <typename T> staticinline Obj ORDER_PERM(Obj perm)
{ const T * ptPerm; // pointer to the permutation
Obj ord; // order (result), may be huge
T * ptKnown; // pointer to temporary bag
UInt len; // length of one cycle
UInt p, q; // loop variables
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(perm));
// get the pointer to the bags
ptPerm = CONST_ADDR_PERM<T>(perm);
ptKnown = ADDR_TMP_PERM<T>();
// clear the buffer bag for ( p = 0; p < DEG_PERM<T>(perm); p++ )
ptKnown[p] = 0;
// start with order 1
ord = INTOBJ_INT(1);
// loop over all cycles for ( p = 0; p < DEG_PERM<T>(perm); p++ ) {
// if we haven't looked at this cycle so far if ( ptKnown[p] == 0 && ptPerm[p] != p ) {
// find the length of this cycle
len = 1; for ( q = ptPerm[p]; q != p; q = ptPerm[q] ) {
len++; ptKnown[q] = 1;
}
ord = LcmInt( ord, INTOBJ_INT( len ) );
// update bag pointers, in case a garbage collection happened
ptPerm = CONST_ADDR_PERM<T>(perm);
ptKnown = ADDR_TMP_PERM<T>();
/**************************************************************************** ** *F FuncSIGN_PERM( <self>, <perm> ) . . . . . . . . . . sign of a permutation ** ** 'FuncSIGN_PERM' implements the internal function 'SignPerm'. ** ** 'SignPerm( <perm> )' ** ** 'SignPerm' returns the sign of the permutation <perm>. The sign is +1 if ** <perm> is the product of an *even* number of transpositions, and -1 if ** <perm> is the product of an *odd* number of transpositions. The sign ** is a homomorphism from the symmetric group onto the multiplicative group ** $\{ +1, -1 \}$, the kernel of which is the alternating group.
*/ template <typename T> staticinline Obj SIGN_PERM(Obj perm)
{ const T * ptPerm; // pointer to the permutation Int sign; // sign (result)
T * ptKnown; // pointer to temporary bag
UInt len; // length of one cycle
UInt p, q; // loop variables
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(perm));
// get the pointer to the bags
ptPerm = CONST_ADDR_PERM<T>(perm);
ptKnown = ADDR_TMP_PERM<T>();
// clear the buffer bag for ( p = 0; p < DEG_PERM<T>(perm); p++ )
ptKnown[p] = 0;
// start with sign 1
sign = 1;
// loop over all cycles for ( p = 0; p < DEG_PERM<T>(perm); p++ ) {
// if we haven't looked at this cycle so far if ( ptKnown[p] == 0 && ptPerm[p] != p ) {
// find the length of this cycle
len = 1; for ( q = ptPerm[p]; q != p; q = ptPerm[q] ) {
len++; ptKnown[q] = 1;
}
// if the length is even invert the sign if ( len % 2 == 0 )
sign = -sign;
/**************************************************************************** ** *F FuncSMALLEST_GENERATOR_PERM( <self>, <perm> ) . . . . . . . . . . . . . . *F . . . . . . . smallest generator of cyclic group generated by permutation ** ** 'FuncSMALLEST_GENERATOR_PERM' implements the internal function ** 'SmallestGeneratorPerm'. ** ** 'SmallestGeneratorPerm( <perm> )' ** ** 'SmallestGeneratorPerm' returns the smallest generator of the cyclic ** group generated by the permutation <perm>. That is the result is a ** permutation that generates the same cyclic group as <perm> and is with ** respect to the lexicographical order defined by '\<' the smallest such ** permutation.
*/ template <typename T> staticinline Obj SMALLEST_GENERATOR_PERM(Obj perm)
{
Obj small; // handle of the smallest gen
T * ptSmall; // pointer to the smallest gen const T * ptPerm; // pointer to the permutation
T * ptKnown; // pointer to temporary bag
Obj ord; // order, may be huge
Obj pow; // power, may also be huge
UInt len; // length of one cycle
UInt gcd, s, t; // gcd( len, ord ), temporaries
UInt min; // minimal element in a cycle
UInt p, q; // loop variables
UInt l, n, x, gcd2; // loop variable
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(perm));
// allocate the result bag
small = NEW_PERM<T>( DEG_PERM<T>(perm) );
// get the pointer to the bags
ptPerm = CONST_ADDR_PERM<T>(perm);
ptKnown = ADDR_TMP_PERM<T>();
ptSmall = ADDR_PERM<T>(small);
// clear the buffer bag for ( p = 0; p < DEG_PERM<T>(perm); p++ )
ptKnown[p] = 0;
// we only know that we must raise <perm> to a power = 0 mod 1
ord = INTOBJ_INT(1); pow = INTOBJ_INT(0);
// loop over all cycles for ( p = 0; p < DEG_PERM<T>(perm); p++ ) {
// if we haven't looked at this cycle so far if ( ptKnown[p] == 0 ) {
// find the length of this cycle
len = 1; for ( q = ptPerm[p]; q != p; q = ptPerm[q] ) {
len++; ptKnown[q] = 1;
}
// compute the gcd with the previously order ord /* Note that since len is single precision, ord % len is to*/
gcd = len; s = INT_INTOBJ( ModInt( ord, INTOBJ_INT(len) ) ); while ( s != 0 ) {
t = s; s = gcd % s; gcd = t;
}
// we must raise the cycle into a power = pow mod gcd
x = INT_INTOBJ( ModInt( pow, INTOBJ_INT( gcd ) ) );
/* find the smallest element in the cycle at such a positio*/
min = DEG_PERM<T>(perm)-1;
n = 0; for ( q = p, l = 0; l < len; l++ ) {
gcd2 = len; s = l; while ( s != 0 ) { t = s; s = gcd2 % s; gcd2 = t; } if ( l % gcd == x && gcd2 == 1 && q <= min ) {
min = q;
n = l;
}
q = ptPerm[q];
}
// raise the cycle to that power and put it in the result
ptSmall[p] = min; for ( q = ptPerm[p]; q != p; q = ptPerm[q] ) {
min = ptPerm[min]; ptSmall[q] = min;
}
// compute the new order and the new power while ( INT_INTOBJ( ModInt( pow, INTOBJ_INT(len) ) ) != n )
pow = SumInt( pow, ord );
ord = ProdInt( ord, INTOBJ_INT( len / gcd ) );
/**************************************************************************** ** *F FuncRESTRICTED_PERM( <self>, <perm>, <dom>, <test> ) . . RestrictedPerm ** ** 'FuncRESTRICTED_PERM' implements the internal function ** 'RESTRICTED_PERM'. ** ** 'RESTRICTED_PERM( <perm>, <dom>, <test> )' ** ** 'RESTRICTED_PERM' returns the restriction of <perm> to <dom>. If <test> ** is set to `true' it is verified that <dom> is the union of cycles of ** <perm>.
*/ template <typename T> staticinline Obj RESTRICTED_PERM(Obj perm, Obj dom, Obj test)
{
Obj rest;
T * ptRest; const T * ptPerm; const Obj * ptDom; Int i,inc,len,p,deg;
// make sure that the buffer bag is large enough
UseTmpPerm(SIZE_OBJ(perm));
// allocate the result bag
deg = DEG_PERM<T>(perm);
rest = NEW_PERM<T>(deg);
// get the pointer to the bags
ptPerm = CONST_ADDR_PERM<T>(perm);
ptRest = ADDR_PERM<T>(rest);
// create identity everywhere for ( p = 0; p < deg; p++ ) {
ptRest[p]=(T)p;
}
if ( ! IS_RANGE(dom) ) { if ( ! IS_PLIST( dom ) ) { return Fail;
} // domain is list
ptPerm = CONST_ADDR_PERM<T>(perm);
ptRest = ADDR_PERM<T>(rest);
ptDom = CONST_ADDR_OBJ(dom);
len = LEN_LIST(dom); for (i = 1; i <= len; i++) { if (IS_POS_INTOBJ(ptDom[i])) {
p = INT_INTOBJ(ptDom[i]); if (p <= deg) {
p -= 1;
ptRest[p] = ptPerm[p];
}
} else { return Fail;
}
}
} else {
len = GET_LEN_RANGE(dom);
p = GET_LOW_RANGE(dom);
inc = GET_INC_RANGE(dom); if (p < 1 || p + inc * (len - 1) < 1) { return Fail;
} for (i = p; i != p + inc * len; i += inc) { if (i <= deg) {
ptRest[i - 1] = ptPerm[i - 1];
}
}
}
if (test==True) {
T * ptTmp = ADDR_TMP_PERM<T>();
// cleanout for ( p = 0; p < deg; p++ ) {
ptTmp[p]=0;
}
// check whether the result is a permutation for (p=0;p<deg;p++) {
inc=ptRest[p]; if (ptTmp[inc]==1) return Fail; // point was known else ptTmp[inc]=1; // now point is known
}
/**************************************************************************** ** *F FuncTRIM_PERM( <self>, <perm>, <n> ) . . . . . . . . . trim a permutation ** ** 'TRIM_PERM' trims a permutation to the first <n> points. This can be ## useful to save memory
*/ static Obj FuncTRIM_PERM(Obj self, Obj perm, Obj n)
{
RequirePermutation(SELF_NAME, perm);
RequireNonnegativeSmallInt(SELF_NAME, n);
UInt newDeg = INT_INTOBJ(n);
UInt oldDeg =
(TNUM_OBJ(perm) == T_PERM2) ? DEG_PERM2(perm) : DEG_PERM4(perm); if (newDeg > oldDeg)
newDeg = oldDeg;
TrimPerm(perm, newDeg); return 0;
}
/**************************************************************************** ** *F FuncSPLIT_PARTITION( <Ppoints>, <Qnum>,<j>,<g>,<l>) ** <l> is a list [<a>,<b>,<max>] -- needed because of large parameter number ** ** This function is used in the partition backtrack to split a partition. ** The points <i> in the list Ppoints between a (start) and b (end) such ** that Qnum[i^g]=j will be moved at the end. ** At most <max> points will be moved. ** The function returns the start point of the new partition (or -1 if too ** many are moved). ** Ppoints and Qnum must be plain lists of small integers.
*/ template <typename T> staticinline Obj
SPLIT_PARTITION(Obj Ppoints, Obj Qnum, Obj jval, Obj g, Obj lst)
{ Int a; Int b; Int max; Int blim;
UInt deg; const T * ptPerm;
Obj tmp;
deg=DEG_PERM<T>(g);
ptPerm=CONST_ADDR_PERM<T>(g); while ( (a<b)) { do {
b--; if (b<blim) { // too many points got moved out return INTOBJ_INT(-1);
}
} while (ELM_PLIST(Qnum,
IMAGE(INT_INTOBJ(ELM_PLIST(Ppoints,b))-1,ptPerm,deg)+1)==jval); do {
a++;
} while ((a<b)
&&(!(ELM_PLIST(Qnum,
IMAGE(INT_INTOBJ(ELM_PLIST(Ppoints,a))-1,ptPerm,deg)+1)==jval))); // swap if (a<b) {
tmp=ELM_PLIST(Ppoints,a);
SET_ELM_PLIST(Ppoints,a,ELM_PLIST(Ppoints,b));
SET_ELM_PLIST(Ppoints,b,tmp);
}
}
// list is not necc. sorted wrt. \< (any longer)
RESET_FILT_LIST(Ppoints, FN_IS_SSORT);
RESET_FILT_LIST(Ppoints, FN_IS_NSORT);
/**************************************************************************** ** *F FuncSMALLEST_IMG_TUP_PERM( <tup>, <perm> ) ** ** `SmallestImgTuplePerm' returns the smallest image of the tuple <tup> ** under the permutation <perm>.
*/ template <typename T> staticinline Obj SMALLEST_IMG_TUP_PERM(Obj tup, Obj perm)
{
UInt res; // handle of the image, result const Obj * ptTup; // pointer to the tuple const T * ptPrm; // pointer to the permutation
UInt tmp; // temporary handle
UInt deg; // degree of the permutation
UInt i, k; // loop variables
res = MAX_DEG_PERM4; // ``infty''.
// get the pointer
ptTup = CONST_ADDR_OBJ(tup) + LEN_LIST(tup);
ptPrm = CONST_ADDR_PERM<T>(perm);
deg = DEG_PERM<T>(perm);
// loop over the entries of the tuple for ( i = LEN_LIST(tup); 1 <= i; i--, ptTup-- ) {
k = INT_INTOBJ( *ptTup ); if ( k <= deg )
tmp = ptPrm[k-1] + 1; else
tmp = k; if (tmp<res) res = tmp;
}
/**************************************************************************** ** *F OnTuplesPerm( <tup>, <perm> ) . . . . operations on tuples of points ** ** 'OnTuplesPerm' returns the image of the tuple <tup> under the ** permutation <perm>. It is called from 'FuncOnTuples'. ** ** The input <tup> must be a non-empty and dense plain list. This is not ** verified.
*/ template <typename T> staticinline Obj OnTuplesPerm_(Obj tup, Obj perm)
{
Obj res; // handle of the image, result
Obj * ptRes; // pointer to the result const T * ptPrm; // pointer to the permutation
Obj tmp; // temporary handle
UInt deg; // degree of the permutation
UInt i, k; // loop variables
// 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_SSORT);
RESET_FILT_LIST(res, FN_IS_NSORT); const UInt len = LEN_PLIST(res);
// get the pointer
ptRes = ADDR_OBJ(res) + 1;
ptPrm = CONST_ADDR_PERM<T>(perm);
deg = DEG_PERM<T>(perm);
// 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) {
*ptRes = INTOBJ_INT(ptPrm[k - 1] + 1);
}
} elseif (tmp == NULL) {
ErrorQuit("OnTuples: must not contain holes", 0, 0);
} else {
tmp = POW(tmp, perm); // POW may trigger a garbage collection, so update pointers
ptRes = ADDR_OBJ(res) + i;
ptPrm = CONST_ADDR_PERM<T>(perm);
*ptRes = tmp;
CHANGED_BAG( res );
}
}
/**************************************************************************** ** *F OnSetsPerm( <set>, <perm> ) . . . . . . . . operations on sets of points ** ** 'OnSetsPerm' returns the image of the tuple <set> under the permutation ** <perm>. 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.
*/ template <typename T> staticinline Obj OnSetsPerm_(Obj set, Obj perm)
{
Obj res; // handle of the image, result
Obj * ptRes; // pointer to the result const T * ptPrm; // pointer to the permutation
Obj tmp; // temporary handle
UInt deg; // degree of the permutation
UInt i, k; // loop variables
// 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);
// get the pointer
ptRes = ADDR_OBJ(res) + 1;
ptPrm = CONST_ADDR_PERM<T>(perm);
deg = DEG_PERM<T>(perm);
// loop over the entries of the tuple BOOL isSmallIntList = TRUE; for (i = 1; i <= len; i++, ptRes++) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.83 Sekunden
(vorverarbeitet)
¤
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 ist noch experimentell.