/****************************************************************************
**
** 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
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
is either zero, or a reference to another
** permutation representing the inverse of
. 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 'system.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 is the image of , 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 -th entry is of course simply .
** Testing whether a product has trailing fixpoints would be pretty costly,
** and permutations of equal degree can be handled by the functions faster.
*/
extern "C" {
#include "permutat.h"
#include "ariths.h"
#include "bool.h"
#include "error.h"
#include "gapstate.h"
#include "integer.h"
#include "io.h"
#include "listfunc.h"
#include "lists.h"
#include "modules.h"
#include "opers.h"
#include "plist.h"
#include "precord.h"
#include "range.h"
#include "records.h"
#include "saveload.h"
#include "sysfiles.h"
#include "trans.h"
} // extern "C"
#include "permutat_intern.hh"
/****************************************************************************
**
*F IMAGE(,,) . . . . . . image of under of degree
**
** 'IMAGE' returns the image of the point under the permutation of
** degree pointed to by . If the point is greater than or
** equal to the image is itself.
**
** 'IMAGE' is implemented as a macro so do not use it with arguments that
** have side effects.
*/
#define IMAGE(i,pt,dg) (((i) < (dg)) ? (pt)[(i)] : (i))
/****************************************************************************
**
*V IdentityPerm . . . . . . . . . . . . . . . . . . . identity permutation
**
** 'IdentityPerm' is an identity permutation.
*/
Obj IdentityPerm;
static ModuleStateOffset PermutatStateOffset = -1;
typedef struct {
/****************************************************************************
**
*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() utility function to ensure it is constructed!
*/
Obj TmpPerm;
} PermutatModuleState;
#define TmpPerm MODULE_STATE(Permutat).TmpPerm
static UInt1 * UseTmpPerm(UInt size)
{
if (TmpPerm == (Obj)0)
TmpPerm = NewBag(T_PERM4, size);
else if (SIZE_BAG(TmpPerm) < size)
ResizeBag(TmpPerm, size);
return (UInt1 *)(ADDR_OBJ(TmpPerm) + 1);
}
template
static inline T * ADDR_TMP_PERM()
{
// no GAP_ASSERT here on purpose
return (T *)(ADDR_OBJ(TmpPerm) + 1);
}
/****************************************************************************
**
*F TypePerm( ) . . . . . . . . . . . . . . . . type of a permutation
**
** 'TypePerm' returns the type of permutations.
**
** 'TypePerm' is the function in 'TypeObjFuncs' for permutations.
*/
static Obj TYPE_PERM2;
static Obj TYPE_PERM4;
static Obj TypePerm2(Obj perm)
{
return TYPE_PERM2;
}
static Obj TypePerm4(Obj perm)
{
return TYPE_PERM4;
}
// Forward declarations
template
static inline UInt LargestMovedPointPerm_(Obj perm);
template
static Obj InvPerm(Obj perm);
/****************************************************************************
**
*F PrintPerm( ) . . . . . . . . . . . . . . . . . print a permutation
**
** 'PrintPerm' prints the permutation in the usual cycle notation. It
** uses the degree to print all points with same width, which looks nicer.
** Linebreaks are prefered most after cycles and next most after commas.
**
** It does not remember which points have already been printed. To avoid
** printing a cycle twice each is printed with the smallest element first.
** This may in the worst case, for (1,2,..,n), take n^2/2 steps, but is fast
** enough to keep a terminal at 9600 baud busy for all but the extrem cases.
*/
template
static void PrintPerm(Obj perm)
{
UInt degPerm; /* degree of the permutation */
const T * ptPerm; /* pointer to the permutation */
UInt p, q; /* loop variables */
UInt isId; /* permutation is the identity? */
const char * fmt1; /* common formats to print points */
const char * 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_(perm);
if ( degPerm < 10 ) { fmt1 = "%>(%>%1d%<"; fmt2 = ",%>%1d%<"; }
else if ( degPerm < 100 ) { fmt1 = "%>(%>%2d%<"; fmt2 = ",%>%2d%<"; }
else if ( degPerm < 1000 ) { fmt1 = "%>(%>%3d%<"; fmt2 = ",%>%3d%<"; }
else if ( degPerm < 10000 ) { fmt1 = "%>(%>%4d%<"; fmt2 = ",%>%4d%<"; }
else { fmt1 = "%>(%>%5d%<"; fmt2 = ",%>%5d%<"; }
/* run through all points */
isId = 1;
ptPerm = CONST_ADDR_PERM(perm);
for ( p = 0; p < degPerm; p++ ) {
/* find the smallest element in this cycle */
q = ptPerm[p];
while ( p < q ) q = ptPerm[q];
/* if the smallest is the one we started with lets print the cycle */
if ( p == q && ptPerm[p] != p ) {
isId = 0;
Pr(fmt1,(Int)(p+1),0L);
ptPerm = CONST_ADDR_PERM(perm);
for ( q = ptPerm[p]; q != p; q = ptPerm[q] ) {
Pr(fmt2,(Int)(q+1),0L);
ptPerm = CONST_ADDR_PERM(perm);
}
Pr("%<)",0L,0L);
/* restore pointer, in case Pr caused a garbage collection */
ptPerm = CONST_ADDR_PERM(perm);
}
}
/* special case for the identity */
if ( isId ) Pr("()",0L,0L);
}
/****************************************************************************
**
*F EqPerm( , ) . . . . . . . test if two permutations are equal
**
** 'EqPerm' returns 'true' if the two permutations and 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
static Int 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(opL);
degR = DEG_PERM(opR);
/* set up the pointers */
ptL = CONST_ADDR_PERM(opL);
ptR = CONST_ADDR_PERM(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 0L;
for ( p = degL; p < degR; p++ )
if ( p != *(ptR++) )
return 0L;
}
else {
for ( p = 0; p < degR; p++ )
if ( *(ptL++) != *(ptR++) )
return 0L;
for ( p = degR; p < degL; p++ )
if ( *(ptL++) != p )
return 0L;
}
/* otherwise they must be equal */
return 1L;
}
/****************************************************************************
**
*F LtPerm( , ) . test if one permutation is smaller than another
**
** 'LtPerm' returns 'true' if the permutation is strictly less than
** the permutation . Permutations are ordered lexicographically with
** respect to the images of 1,2,.., etc.
*/
template
static Int 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(opL);
degR = DEG_PERM(opR);
/* set up the pointers */
ptL = CONST_ADDR_PERM(opL);
ptR = CONST_ADDR_PERM(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( , ) . . . . . . . . . . . . product of permutations
**
** 'ProdPerm' returns the product of the two permutations and .
**
** This is a little bit tuned but should be sufficiently easy to understand.
*/
template
static Obj ProdPerm(Obj opL, Obj opR)
{
typedef typename ResultType::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 */
degL = DEG_PERM(opL);
degR = DEG_PERM(opR);
// 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( degP );
/* set up the pointers */
ptL = CONST_ADDR_PERM(opL);
ptR = CONST_ADDR_PERM(opR);
ptP = ADDR_PERM(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 the result */
return prd;
}
/****************************************************************************
**
*F QuoPerm( , ) . . . . . . . . . . . . quotient of permutations
**
** 'QuoPerm' returns the quotient of the permutations and , i.e.,
** the product '\*\^-1'.
**
** Unfortunatly this can not be done in steps, we need 2 *
** steps.
*/
static Obj QuoPerm(Obj opL, Obj opR)
{
return PROD(opL, INV(opR));
}
/****************************************************************************
**
*F LQuoPerm( , ) . . . . . . . . . left quotient of permutations
**
** 'LQuoPerm' returns the left quotient of the two permutations and
** , i.e., the value of '\^-1*', which sometimes comes handy.
**
** This can be done as fast as a single multiplication or inversion.
*/
template
static Obj LQuoPerm(Obj opL, Obj opR)
{
typedef typename ResultType::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 */
degL = DEG_PERM(opL);
degR = DEG_PERM(opR);
// handle trivial cases
if (degL == 0) {
return opR;
}
if (degR == 0) {
return InvPerm(opL);
}
// compute the size of the result and allocate a bag
degM = degL < degR ? degR : degL;
mod = NEW_PERM( degM );
/* set up the pointers */
ptL = CONST_ADDR_PERM(opL);
ptR = CONST_ADDR_PERM(opR);
ptM = ADDR_PERM(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 the result */
return mod;
}
/****************************************************************************
**
*F InvPerm( ) . . . . . . . . . . . . . . . inverse of a permutation
*/
template
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(perm);
inv = NEW_PERM(deg);
// get pointer to the permutation and the inverse
ptPerm = CONST_ADDR_PERM(perm);
ptInv = ADDR_PERM(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( , ) . . . . . . . integer power of a permutation
**
** 'PowPermInt' returns the -th power of the permutation .
** must be a small integer.
**
** This repeatedly applies the permutation to all points which seems
** to be faster than binary powering, and does not need temporary storage.
*/
template
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(opL);
deg = DEG_PERM(opL);
// handle trivial permutations
if (deg == 0) {
return IdentityPerm;
}
// allocate a result bag
pow = NEW_PERM(deg);
/* 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(opL);
ptP = ADDR_PERM(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 */
else if ( IS_INTOBJ(opR) && 8 <= INT_INTOBJ(opR) ) {
/* make sure that the buffer bag is large enough */
UseTmpPerm(SIZE_OBJ(opL));
ptKnown = ADDR_TMP_PERM();
/* clear the buffer bag */
memset(ptKnown, 0, DEG_PERM(opL));
/* get pointer to the permutation and the power */
exp = INT_INTOBJ(opR);
ptL = CONST_ADDR_PERM(opL);
ptP = ADDR_PERM(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 mod */
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 */
else if ( TNUM_OBJ(opR) == T_INTPOS ) {
/* make sure that the buffer bag is large enough */
UseTmpPerm(SIZE_OBJ(opL));
ptKnown = ADDR_TMP_PERM();
/* clear the buffer bag */
memset(ptKnown, 0, DEG_PERM(opL));
/* get pointer to the permutation and the power */
ptL = CONST_ADDR_PERM(opL);
ptP = ADDR_PERM(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 mod */
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 */
else if ( 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(opL);
ptP = ADDR_PERM(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 */
else if ( IS_INTOBJ(opR) && INT_INTOBJ(opR) <= -8 ) {
/* make sure that the buffer bag is large enough */
UseTmpPerm(SIZE_OBJ(opL));
ptKnown = ADDR_TMP_PERM();
/* clear the buffer bag */
memset(ptKnown, 0, DEG_PERM(opL));
/* get pointer to the permutation and the power */
exp = -INT_INTOBJ(opR);
ptL = CONST_ADDR_PERM(opL);
ptP = ADDR_PERM(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 mod */
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 */
else if ( 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();
/* clear the buffer bag */
memset(ptKnown, 0, DEG_PERM(opL));
/* get pointer to the permutation and the power */
ptL = CONST_ADDR_PERM(opL);
ptP = ADDR_PERM(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 mod */
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 the result */
return pow;
}
/****************************************************************************
**
*F PowIntPerm( , ) . . . image of an integer under a permutation
**
** 'PowIntPerm' returns the image of the positive integer under the
** permutation . If is larger than the degree of it is a
** fixpoint of the permutation and thus simply returned.
*/
template
static Obj PowIntPerm(Obj opL, Obj opR)
{
Int img; /* image (result) */
GAP_ASSERT(TNUM_OBJ(opL) == T_INTPOS || TNUM_OBJ(opL) == T_INT);
/* 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(opR) ) {
img = (CONST_ADDR_PERM(opR))[img-1] + 1;
}
/* return it */
return INTOBJ_INT(img);
}
/****************************************************************************
**
*F QuoIntPerm( , ) . preimage of an integer under a permutation
**
** 'QuoIntPerm' returns the preimage of the preimage integer under the
** permutation . If is larger than the degree of is is a
** fixpoint, and thus simply returned.
**
** There are basically two ways to find the preimage. One is to run through
** and look for . The index where it's found is the preimage.
** The other is to find the image of under , the image of that
** point and so on, until we come back to . The last point is the
** preimage of . This is faster because the cycles are usually short.
*/
static Obj PERM_INVERSE_THRESHOLD;
template
static Obj QuoIntPerm(Obj opL, Obj opR)
{
T pre; /* preimage (result) */
Int img; /* image (left operand) */
const T * ptR; /* pointer to the permutation */
GAP_ASSERT(TNUM_OBJ(opL) == T_INTPOS || TNUM_OBJ(opL) == T_INT);
/* large positive integers (> 2^28-1) are fixed by any permutation */
if ( TNUM_OBJ(opL) == T_INTPOS )
return opL;
img = INT_INTOBJ(opL);
RequireArgumentConditionEx("QuoIntPerm", opL, "", img > 0,
"must be a positive integer");
Obj inv = STOREDINV_PERM(opR);
if (inv == 0 && PERM_INVERSE_THRESHOLD != 0 &&
IS_INTOBJ(PERM_INVERSE_THRESHOLD) &&
DEG_PERM(opR) <= INT_INTOBJ(PERM_INVERSE_THRESHOLD))
inv = InvPerm(opR);
if (inv != 0)
return INTOBJ_INT(
IMAGE(img - 1, CONST_ADDR_PERM(inv), DEG_PERM(inv)) + 1);
/* compute the preimage */
if ( img <= DEG_PERM(opR) ) {
pre = T(img - 1);
ptR = CONST_ADDR_PERM(opR);
while (ptR[pre] != T(img - 1))
pre = ptR[pre];
/* return it */
return INTOBJ_INT(pre + 1);
}
else
return INTOBJ_INT(img);
}
/****************************************************************************
**
*F PowPerm( , ) . . . . . . . . . . . conjugation of permutations
**
** 'PowPerm' returns the conjugation of the two permutations and
** , that s defined as the following product '\^-1 \*\ \*\
** '.
*/
template
static Obj PowPerm(Obj opL, Obj opR)
{
typedef typename ResultType::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 */
degL = DEG_PERM(opL);
degR = DEG_PERM(opR);
// handle trivial cases
if (degL == 0) {
return IdentityPerm;
}
if (degR == 0) {
return opL;
}
// compute the size of the result and allocate a bag
degC = degL < degR ? degR : degL;
cnj = NEW_PERM( degC );
/* set up the pointers */
ptL = CONST_ADDR_PERM(opL);
ptR = CONST_ADDR_PERM(opR);
ptC = ADDR_PERM(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 the result */
return cnj;
}
/****************************************************************************
**
*F CommPerm( , ) . . . . . . . . commutator of two permutations
**
** 'CommPerm' returns the commutator of the two permutations and
** , that is defined as '\^-1 \*\ \^-1 \*\ \*\ '.
*/
template
static Obj CommPerm(Obj opL, Obj opR)
{
typedef typename ResultType::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 */
degL = DEG_PERM(opL);
degR = DEG_PERM(opR);
// handle trivial cases
if (degL == 0 || degR == 0) {
return IdentityPerm;
}
// compute the size of the result and allocate a bag
degC = degL < degR ? degR : degL;
com = NEW_PERM( degC );
/* set up the pointers */
ptL = CONST_ADDR_PERM(opL);
ptR = CONST_ADDR_PERM(opR);
ptC = ADDR_PERM(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 );
}
/* return the result */
return com;
}
/****************************************************************************
**
*F OnePerm( )
*/
static Obj OnePerm(Obj op)
{
return IdentityPerm;
}
/****************************************************************************
**
*F FiltIS_PERM( , ) . . . . . . test if a value is a permutation
**
** 'FiltIS_PERM' implements the internal function 'IsPerm'.
**
** 'IsPerm( )'
**
** 'IsPerm' returns 'true' if the value is a permutation and 'false'
** otherwise.
*/
static Obj IsPermFilt;
static Obj FiltIS_PERM(Obj self, Obj val)
{
/* return 'true' if is a permutation and 'false' otherwise */
if ( TNUM_OBJ(val) == T_PERM2 || TNUM_OBJ(val) == T_PERM4 ) {
return True;
}
else if ( TNUM_OBJ(val) < FIRST_EXTERNAL_TNUM ) {
return False;
}
else {
return DoFilter( self, val );
}
}
/****************************************************************************
**
*F FuncPermList( , ) . . . . . convert a list to a permutation
**
** 'FuncPermList' implements the internal function 'PermList'
**
** 'PermList( )'
**
** Converts the 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
static inline 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 */
PLAIN_LIST( list );
degPerm = LEN_LIST( list );
/* make sure that the global buffer bag is large enough for checkin*/
UseTmpPerm(SIZEBAG_PERM(degPerm));
/* allocate the bag for the permutation and get pointer */
perm = NEW_PERM(degPerm);
ptPerm = ADDR_PERM(perm);
ptList = CONST_ADDR_OBJ(list);
ptTmp = ADDR_TMP_PERM();
/* make the buffer bag clean */
for ( i = 1; i <= degPerm; i++ )
ptTmp[i-1] = 0;
/* run through all entries of the list */
for ( i = 1; i <= degPerm; i++ ) {
/* get the th entry of the list */
if ( ptList[i] == 0 ) {
return Fail;
}
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;
}
/* return the permutation */
return perm;
}
static Obj FuncPermList(Obj self, Obj list)
{
/* check the arguments */
RequireSmallList("PermList", list);
UInt len = LEN_LIST( list );
if ( len <= 65536 ) {
return PermList(list);
}
else if (len <= MAX_DEG_PERM4) {
return PermList(list);
}
else {
ErrorMayQuit("PermList: list length %i exceeds maximum permutation degree\n",
len, 0);
}
}
/****************************************************************************
**
*F LargestMovedPointPerm( ) largest point moved by perm
**
** 'LargestMovedPointPerm' returns the largest positive integer that is
** moved by the permutation .
**
** This is easy, except that permutations may contain trailing fixpoints.
*/
template
static inline UInt LargestMovedPointPerm_(Obj perm)
{
UInt sup;
const T * ptPerm;
ptPerm = CONST_ADDR_PERM(perm);
for (sup = DEG_PERM(perm); 1 <= sup; sup--) {
if (ptPerm[sup - 1] != sup - 1)
break;
}
return sup;
}
UInt LargestMovedPointPerm(Obj perm)
{
GAP_ASSERT(TNUM_OBJ(perm) == T_PERM2 || TNUM_OBJ(perm) == T_PERM4);
if (TNUM_OBJ(perm) == T_PERM2)
return LargestMovedPointPerm_(perm);
else
return LargestMovedPointPerm_(perm);
}
/****************************************************************************
**
*F FuncLARGEST_MOVED_POINT_PERM( , ) largest point moved by perm
**
** GAP-level wrapper for 'LargestMovedPointPerm'.
*/
static Obj FuncLARGEST_MOVED_POINT_PERM(Obj self, Obj perm)
{
/* check the argument */
RequirePermutation("LargestMovedPointPerm", perm);
return INTOBJ_INT(LargestMovedPointPerm(perm));
}
/****************************************************************************
**
*F FuncCYCLE_LENGTH_PERM_INT( , , ) . . . . . . . . . .
*F . . . . . . . . . . . . . . . . . . length of a cycle under a permutation
**
** 'FuncCycleLengthInt' implements the internal function
** 'CycleLengthPermInt'
**
** 'CycleLengthPermInt( , )'
**
** 'CycleLengthPermInt' returns the length of the cycle of , which
** must be a positive integer, under the permutation .
**
** Note that the order of the arguments to this function has been reversed.
*/
template
static inline 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(perm);
deg = DEG_PERM(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 */
/* evaluate and check the arguments */
RequirePermutation("CycleLengthPermInt", perm);
pnt = GetPositiveSmallInt("CycleLengthPermInt", point) - 1;
if ( TNUM_OBJ(perm) == T_PERM2 ) {
return CYCLE_LENGTH_PERM_INT(perm, pnt);
}
else {
return CYCLE_LENGTH_PERM_INT(perm, pnt);
}
}
/****************************************************************************
**
*F FuncCYCLE_PERM_INT( , , ) . . cycle of a permutation
*
** 'FuncCYCLE_PERM_INT' implements the internal function 'CyclePermInt'.
**
** 'CyclePermInt( , )'
**
** 'CyclePermInt' returns the cycle of , which must be a positive
** integer, under the permutation as a list.
*/
template
static inline 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(perm);
deg = DEG_PERM(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(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 */
/* evaluate and check the arguments */
RequirePermutation("CyclePermInt", perm);
pnt = GetPositiveSmallInt("CyclePermInt", point) - 1;
if ( TNUM_OBJ(perm) == T_PERM2 ) {
return CYCLE_PERM_INT(perm, pnt);
}
else {
return CYCLE_PERM_INT(perm, pnt);
}
}
/****************************************************************************
**
*F FuncCYCLE_STRUCT_PERM( , ) . . . . . cycle structure of perm
*
** 'FuncCYCLE_STRUCT_PERM' implements the internal function
** `CycleStructPerm'.
**
** `CycleStructPerm( )'
**
** 'CycleStructPerm' returns a list of the form as described under
** `CycleStructure'.
*/
template
static inline 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 */
ptPerm = CONST_ADDR_PERM(perm);
for (deg = DEG_PERM(perm); 1 <= deg; deg--) {
if (ptPerm[deg - 1] != deg - 1)
break;
}
if (deg == 0) {
/* special treatment of identity */
list = NEW_PLIST(T_PLIST, 0);
return list;
}
scratch = ADDR_TMP_PERM();
/* 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));
clr = (UInt1 *)scratch;
/* clear out the bits */
for (cnt = 0; cnt < bytes; cnt++) {
clr[cnt] = (UInt1)0;
}
cnt = 0;
clr = (UInt1 *)scratch;
max = 0;
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();
offset = (T *)((UInt)scratch + (bytes));
for (cnt = 0; cnt < ende; cnt++) {
pnt = (UInt)offset[cnt];
if (ptList[pnt] == 0) {
ptList[pnt] = INTOBJ_INT(1);
}
else {
ptList[pnt] = INTOBJ_INT(INT_INTOBJ(ptList[pnt]) + 1);
}
}
return list;
}
static Obj FuncCYCLE_STRUCT_PERM(Obj self, Obj perm)
{
/* evaluate and check the arguments */
RequirePermutation("CycleStructPerm", perm);
if (TNUM_OBJ(perm) == T_PERM2) {
return CYCLE_STRUCT_PERM(perm);
}
else {
return CYCLE_STRUCT_PERM(perm);
}
}
/****************************************************************************
**
*F FuncORDER_PERM( , ) . . . . . . . . . order of a permutation
**
** 'FuncORDER_PERM' implements the internal function 'OrderPerm'.
**
** 'OrderPerm( )'
**
** 'OrderPerm' returns the order of the permutation