Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/src/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 18.9.2025 mit Größe 90 kB image not shown  

Quelle  permutat.cc   Sprache: C

 
/****************************************************************************
**
**  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.
*/


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"


/****************************************************************************
**
*V  IdentityPerm  . . . . . . . . . . . . . . . . . . .  identity permutation
**
**  'IdentityPerm' is an identity permutation.
*/

Obj             IdentityPerm;


static const UInt MAX_DEG_PERM4 = ((Int)1 << (sizeof(UInt) == 8 ? 32 : 28)) - 1;

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(<size>) 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 <typename T>
static inline 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;

static Obj TYPE_PERM4;

static Obj TypePerm2(Obj perm)
{
    return TYPE_PERM2;
}

static Obj TypePerm4(Obj perm)
{
    return TYPE_PERM4;
}

// Forward declarations
template <typename T>
static inline UInt LargestMovedPointPerm_(Obj perm);

template <typename T>
static Obj InvPerm(Obj perm);


/****************************************************************************
**
*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>
static void 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?
    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_<T>(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%<"; }

    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>
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<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>
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<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)
{
    typedef typename 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

    degL = DEG_PERM<TL>(opL);
    degR = DEG_PERM<TR>(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<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)
{
    typedef typename 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

    degL = DEG_PERM<TL>(opL);
    degR = DEG_PERM<TR>(opR);

    // 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);

    deg = DEG_PERM<T>(opL);

    // handle trivial permutations
    if (deg == 0) {
        return IdentityPerm;
    }

    // allocate a result bag
    pow = NEW_PERM<T>(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<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
    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<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
    else if ( 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
    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<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
    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<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
    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<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)

    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<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

    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<T>(opR) <= INT_INTOBJ(PERM_INVERSE_THRESHOLD))
        inv = InvPerm<T>(opR);

    if (inv != 0)
        return INTOBJ_INT(
            IMAGE(img - 1, CONST_ADDR_PERM<T>(inv), DEG_PERM<T>(inv)) + 1);

    // 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)
{
    typedef typename 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

    degL = DEG_PERM<TL>(opL);
    degR = DEG_PERM<TR>(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<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)
{
    typedef typename 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

    degL = DEG_PERM<TL>(opL);
    degR = DEG_PERM<TR>(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<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 );
    }

    return com;
}


/****************************************************************************
**
*F  OnePerm( <perm> )
*/

static Obj OnePerm(Obj op)
{
    return IdentityPerm;
}



/****************************************************************************
**
*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 ) {
        return True;
    }
    else if ( TNUM_OBJ(val) < FIRST_EXTERNAL_TNUM ) {
        return False;
    }
    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>
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

    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;
    }

    // return the permutation
    return perm;
}

static Obj FuncPermList(Obj self, Obj list)
{
    RequireSmallList(SELF_NAME, list);

    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);
    }
    else if (len <= MAX_DEG_PERM4) {
        return PermList<UInt4>(list);
    }
    else {
        ErrorMayQuit("PermList: list length %d exceeds maximum permutation degree",
             len, 0);
    }
}

template <typename T>
static inline 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>
static inline UInt LargestMovedPointPerm_(Obj perm)
{
    UInt      sup;
    const T * ptPerm;

    ptPerm = CONST_ADDR_PERM<T>(perm);
    sup = DEG_PERM<T>(perm);
    while (sup-- > 0) {
        if (ptPerm[sup] != sup)
            return sup + 1;
    }
    return 0;
}

UInt LargestMovedPointPerm(Obj perm)
{
    GAP_ASSERT(TNUM_OBJ(perm) == T_PERM2 || TNUM_OBJ(perm) == T_PERM4);

    if (TNUM_OBJ(perm) == T_PERM2)
        return LargestMovedPointPerm_<UInt2>(perm);
    else
        return LargestMovedPointPerm_<UInt4>(perm);
}

/****************************************************************************
**
*F  SmallestMovedPointPerm( <perm> ) smallest point moved by perm
**
**  'SmallestMovedPointPerm' implements 'SmallestMovedPoint' for internal
**  permutations.
*/


// Import 'Infinity', as a return value for the identity permutation
static Obj Infinity;

template <typename T>
static inline Obj SmallestMovedPointPerm_(Obj perm)
{
    const T * ptPerm = CONST_ADDR_PERM<T>(perm);
    UInt      deg = DEG_PERM<T>(perm);

    for (UInt i = 0; i < deg; i++) {
        if (ptPerm[i] != i)
            return INTOBJ_INT(i + 1);
    }
    return Infinity;
}

static Obj SmallestMovedPointPerm(Obj perm)
{
    GAP_ASSERT(TNUM_OBJ(perm) == T_PERM2 || TNUM_OBJ(perm) == T_PERM4);

    if (TNUM_OBJ(perm) == T_PERM2)
        return SmallestMovedPointPerm_<UInt2>(perm);
    else
        return SmallestMovedPointPerm_<UInt4>(perm);
}


/****************************************************************************
**
*F  FuncLARGEST_MOVED_POINT_PERM( <self>, <perm> ) largest point moved by perm
**
**  GAP-level wrapper for 'LargestMovedPointPerm'.
*/

static Obj FuncLARGEST_MOVED_POINT_PERM(Obj self, Obj perm)
{
    RequirePermutation(SELF_NAME, perm);

    return INTOBJ_INT(LargestMovedPointPerm(perm));
}

/****************************************************************************
**
*F  FuncSMALLEST_MOVED_POINT_PERM( <self>, <perm> )
**
**  GAP-level wrapper for 'SmallestMovedPointPerm'.
*/

static Obj FuncSMALLEST_MOVED_POINT_PERM(Obj self, Obj perm)
{
    RequirePermutation(SELF_NAME, perm);

    return SmallestMovedPointPerm(perm);
}

/****************************************************************************
**
*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>
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<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

    RequirePermutation(SELF_NAME, perm);
    pnt = GetPositiveSmallInt("CycleLengthPermInt", point) - 1;

    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return CYCLE_LENGTH_PERM_INT<UInt2>(perm, pnt);
    }
    else {
        return CYCLE_LENGTH_PERM_INT<UInt4>(perm, pnt);
    }
}


/****************************************************************************
**
*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>
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<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

    RequirePermutation(SELF_NAME, perm);
    pnt = GetPositiveSmallInt("CyclePermInt", point) - 1;

    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return CYCLE_PERM_INT<UInt2>(perm, pnt);
    }
    else {
        return CYCLE_PERM_INT<UInt4>(perm, pnt);
    }
}

/****************************************************************************
**
*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>
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
    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));

    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)
{
    RequirePermutation(SELF_NAME, perm);

    if (TNUM_OBJ(perm) == T_PERM2) {
        return CYCLE_STRUCT_PERM<UInt2>(perm);
    }
    else {
        return CYCLE_STRUCT_PERM<UInt4>(perm);
    }
}

/****************************************************************************
**
*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>
static inline 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>();

        }

    }

    // return the order
    return ord;
}

static Obj FuncORDER_PERM(Obj self, Obj perm)
{
    RequirePermutation(SELF_NAME, perm);

    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return ORDER_PERM<UInt2>(perm);
    }
    else {
        return ORDER_PERM<UInt4>(perm);
    }
}


/****************************************************************************
**
*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>
static inline 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;

        }

    }

    // return the sign
    return INTOBJ_INT( sign );
}

static Obj FuncSIGN_PERM(Obj self, Obj perm)
{
    RequirePermutation(SELF_NAME, perm);

    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return SIGN_PERM<UInt2>(perm);
    }
    else {
        return SIGN_PERM<UInt4>(perm);
    }
}


/****************************************************************************
**
*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>
static inline 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 ) );

        }

    }

    // return the smallest generator
    return small;
}

static Obj FuncSMALLEST_GENERATOR_PERM(Obj self, Obj perm)
{
    RequirePermutation(SELF_NAME, perm);

    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return SMALLEST_GENERATOR_PERM<UInt2>(perm);
    }
    else {
        return SMALLEST_GENERATOR_PERM<UInt4>(perm);
    }
}

/****************************************************************************
**
*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>
static inline 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
      }

    }

    // return the restriction
    return rest;
}

static Obj FuncRESTRICTED_PERM(Obj self, Obj perm, Obj dom, Obj test)
{
    RequirePermutation(SELF_NAME, perm);

    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return RESTRICTED_PERM<UInt2>(perm, dom, test);
    }
    else {
        return RESTRICTED_PERM<UInt4>(perm, dom, test);
    }
}

/****************************************************************************
**
*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>
static inline 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;


  a=INT_INTOBJ(ELM_PLIST(lst,1))-1;
  b=INT_INTOBJ(ELM_PLIST(lst,2))+1;
  max=INT_INTOBJ(ELM_PLIST(lst,3));
  blim=b-max-1;

    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);

  return INTOBJ_INT(b+1);
}

static Obj
FuncSPLIT_PARTITION(Obj self, Obj Ppoints, Obj Qnum, Obj jval, Obj g, Obj lst)
{
  if (TNUM_OBJ(g)==T_PERM2) {
    return SPLIT_PARTITION<UInt2>(Ppoints, Qnum, jval, g, lst);
  }
  else {
    return SPLIT_PARTITION<UInt4>(Ppoints, Qnum, jval, g, lst);
  }
}

/*****************************************************************************
**
*F  FuncDISTANCE_PERMS( <perm1>, <perm2> )
**
**  'DistancePerms' returns the number of points moved by <perm1>/<perm2>
**
*/

template <typename TL, typename TR>
static inline Obj DISTANCE_PERMS(Obj opL, Obj opR)
{
    UInt       dist = 0;
    const TL * ptL = CONST_ADDR_PERM<TL>(opL);
    const TR * ptR = CONST_ADDR_PERM<TR>(opR);
    UInt       degL = DEG_PERM<TL>(opL);
    UInt       degR = DEG_PERM<TR>(opR);
    UInt       i;
    if (degL < degR) {
        for (i = 0; i < degL; i++)
            if (ptL[i] != ptR[i])
                dist++;
        for (; i < degR; i++)
            if (ptR[i] != i)
                dist++;
    }
    else {
        for (i = 0; i < degR; i++)
            if (ptL[i] != ptR[i])
                dist++;
        for (; i < degL; i++)
            if (ptL[i] != i)
                dist++;
    }

    return INTOBJ_INT(dist);
}

static Obj FuncDISTANCE_PERMS(Obj self, Obj opL, Obj opR)
{
    UInt type = (TNUM_OBJ(opL) == T_PERM2 ? 20 : 40) + (TNUM_OBJ(opR) == T_PERM2 ? 2 : 4);
    switch (type) {
    case 22:
        return DISTANCE_PERMS<UInt2, UInt2>(opL, opR);
    case 24:
        return DISTANCE_PERMS<UInt2, UInt4>(opL, opR);
    case 42:
        return DISTANCE_PERMS<UInt4, UInt2>(opL, opR);
    case 44:
        return DISTANCE_PERMS<UInt4, UInt4>(opL, opR);
    }
    return Fail;
}

/****************************************************************************
**
*F  FuncSMALLEST_IMG_TUP_PERM( <tup>, <perm> )
**
**  `SmallestImgTuplePerm' returns the smallest image of the  tuple  <tup>
**  under  the permutation <perm>.
*/

template <typename T>
static inline 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;
    }

    return INTOBJ_INT(res);

}

static Obj FuncSMALLEST_IMG_TUP_PERM(Obj self, Obj tup, Obj perm)
{
    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return SMALLEST_IMG_TUP_PERM<UInt2>(tup, perm);
    }
    else {
        return SMALLEST_IMG_TUP_PERM<UInt4>(tup, perm);
    }
}

/****************************************************************************
**
*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>
static inline 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);
            }
        }
        else if (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 );
        }
    }

    return res;
}

Obj             OnTuplesPerm (
    Obj                 tup,
    Obj                 perm )
{
    if ( TNUM_OBJ(perm) == T_PERM2 ) {
        return OnTuplesPerm_<UInt2>(tup, perm);
    }
    else {
        return OnTuplesPerm_<UInt4>(tup, perm);
    }
}


/****************************************************************************
**
*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>
static inline 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

--> --------------------

92%


¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Versionsinformation zu Columbo

Bemerkung:

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders