Skip to content

Commit

Permalink
Add argument validation to ListPerm, and turn it into a kernel func…
Browse files Browse the repository at this point in the history
…tion to make it faster (#5566)
  • Loading branch information
fingolfin authored Jan 10, 2024
1 parent a34ee92 commit c4482be
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 36 deletions.
27 changes: 7 additions & 20 deletions lib/permutat.g
Original file line number Diff line number Diff line change
Expand Up @@ -444,39 +444,26 @@ SetOne( PermutationsFamily, () );

#############################################################################
##
#F ListPerm( <perm>[, <length>] ) . . . . . . . . . . . . . list of images
#F ListPerm( <perm>[, <n>] ) . . . . . . . . . . . . . list of images
##
## <#GAPDoc Label="ListPerm">
## <ManSection>
## <Func Name="ListPerm" Arg='perm[, length]'/>
## <Func Name="ListPerm" Arg='perm[, n]'/>
##
## <Description>
## is a list <M>l</M> that contains the images of the positive integers
## under the permutation <A>perm</A>.
## from 1 to <A>n</A> under the permutation <A>perm</A>.
## That means that
## <M>l</M><C>[</C><M>i</M><C>]</C> <M>= i</M><C>^</C><A>perm</A>,
## where <M>i</M> lies between 1
## and the largest point moved by <A>perm</A>
## (see&nbsp;<Ref Attr="LargestMovedPoint" Label="for a permutation"/>).
## where <M>i</M> lies between 1 and <A>n</A>.
## <P/>
## An optional second argument specifies the length of the desired list.
## If the optional second argument <A>n</A> is omitted then the largest
## point moved by <A>perm</A> is used
## (see&nbsp;<Ref Attr="LargestMovedPoint" Label="for a permutation"/>).
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BIND_GLOBAL( "ListPerm", function( arg )
local n;
if Length(arg)=2 then
n := arg[2];
else
n := LargestMovedPoint(arg[1]);
fi;
if IsOne(arg[1]) then
return [1..n];
else
return OnTuples( [1..n], arg[1] );
fi;
end );


#############################################################################
Expand Down
10 changes: 4 additions & 6 deletions src/listfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1528,15 +1528,13 @@ static Int InitKernel (
StructInitInfo * module )
{
// init filters and functions
/* ADD_LIST needs special consideration because we want distinct kernel
handlers for 2 and 3 arguments */
InitHandlerFunc( FuncADD_LIST, "src/listfunc.c:FuncADD_LIST" );
InitHandlerFunc( FuncADD_LIST3, "src/listfunc.c:FuncADD_LIST3" );

InitHdlrOpersFromTable( GVarOpers );
InitHdlrFuncsFromTable( GVarFuncs );


// ADD_LIST needs special consideration because we want distinct kernel
// handlers for 2 and 3 arguments
InitHandlerFunc( FuncADD_LIST, "src/listfunc.c:FuncADD_LIST" );
InitHandlerFunc( FuncADD_LIST3, "src/listfunc.c:FuncADD_LIST3" );

return 0;
}
Expand Down
104 changes: 94 additions & 10 deletions src/permutat.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,63 @@ static Obj FuncPermList(Obj self, Obj list)
}
}

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
Expand Down Expand Up @@ -1951,22 +2008,22 @@ 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
const T * ptPrm; // pointer to the permutation
UInt tmp; // temporary handle
UInt lmp; // largest moved point
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);
lmp = DEG_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 <= lmp )
if ( k <= deg )
tmp = ptPrm[k-1] + 1;
else
tmp = k;
Expand Down Expand Up @@ -2004,7 +2061,7 @@ static inline Obj OnTuplesPerm_(Obj tup, Obj perm)
Obj * ptRes; // pointer to the result
const T * ptPrm; // pointer to the permutation
Obj tmp; // temporary handle
UInt lmp; // largest moved point
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
Expand All @@ -2016,14 +2073,14 @@ static inline Obj OnTuplesPerm_(Obj tup, Obj perm)
// get the pointer
ptRes = ADDR_OBJ(res) + 1;
ptPrm = CONST_ADDR_PERM<T>(perm);
lmp = DEG_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 <= lmp) {
if (k <= deg) {
*ptRes = INTOBJ_INT(ptPrm[k - 1] + 1);
}
}
Expand Down Expand Up @@ -2073,7 +2130,7 @@ static inline Obj OnSetsPerm_(Obj set, Obj perm)
Obj * ptRes; // pointer to the result
const T * ptPrm; // pointer to the permutation
Obj tmp; // temporary handle
UInt lmp; // largest moved point
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
Expand All @@ -2083,15 +2140,15 @@ static inline Obj OnSetsPerm_(Obj set, Obj perm)
// get the pointer
ptRes = ADDR_OBJ(res) + 1;
ptPrm = CONST_ADDR_PERM<T>(perm);
lmp = DEG_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++) {
tmp = *ptRes;
if (IS_POS_INTOBJ(tmp)) {
k = INT_INTOBJ(tmp);
if (k <= lmp) {
if (k <= deg) {
*ptRes = INTOBJ_INT(ptPrm[k - 1] + 1);
}
}
Expand Down Expand Up @@ -2744,6 +2801,22 @@ static StructGVarFilt GVarFilts[] = {
};



/****************************************************************************
**
*V GVarOpers . . . . . . . . . . . . . . . . . list of operations to export
*/
static StructGVarOper GVarOpers [] = {

// ListPerm can take 1 or 2 arguments; since NewOperation ignores the
// handler for variadic operations, use DoOperation0Args as a placeholder.
{ "ListPerm", -1, "perm[, n]", &ListPermOper,
(ObjFunc)DoOperation0Args, "src/permutat.cc:ListPerm" },

{ 0, 0, 0, 0, 0, 0 }

};

/****************************************************************************
**
*V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
Expand Down Expand Up @@ -2817,8 +2890,14 @@ static Int InitKernel (

// init filters and functions
InitHdlrFiltsFromTable( GVarFilts );
InitHdlrOpersFromTable( GVarOpers );
InitHdlrFuncsFromTable( GVarFuncs );

// ListPerm needs special consideration because we want distinct kernel
// handlers for 1 and 2 arguments
InitHandlerFunc( (ObjFunc)FuncListPerm1, "src/permutat.cc:FuncListPerm1" );
InitHandlerFunc( (ObjFunc)FuncListPerm2, "src/permutat.cc:FuncListPerm2" );

// make the buffer bag
#ifndef HPCGAP
InitGlobalBag( &TmpPerm, "src/permutat.cc:TmpPerm" );
Expand Down Expand Up @@ -2924,8 +3003,13 @@ static Int InitLibrary (
{
// init filters and functions
InitGVarFiltsFromTable( GVarFilts );
InitGVarOpersFromTable( GVarOpers );
InitGVarFuncsFromTable( GVarFuncs );

// make and install the 'ListPerm' operation
SET_HDLR_FUNC( ListPermOper, 1, (ObjFunc)FuncListPerm1);
SET_HDLR_FUNC( ListPermOper, 2, (ObjFunc)FuncListPerm2);

// make the identity permutation
IdentityPerm = NEW_PERM2(0);

Expand Down
48 changes: 48 additions & 0 deletions tst/testinstall/perm.tst
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,54 @@ gap> SetX(permAll, permAll, {a,b} -> Comm(a,b) = LeftQuotient((b*a), a*b));
# gap> SetX(permAll, permAll, {a,b} -> a * Comm(a,b) = a^b);
# [ true ]

#
# ListPerm
#
gap> p := ();
()
gap> ListPerm( p );
[ ]
gap> List([-1,0,1,5], n -> ListPerm( p, n ));
[ [ ], [ ], [ 1 ], [ 1, 2, 3, 4, 5 ] ]

#
gap> p := (1,100) / (1,100);
()
gap> ListPerm( p );
[ ]
gap> List([-1,0,1,5], n -> ListPerm( p, n ));
[ [ ], [ ], [ 1 ], [ 1, 2, 3, 4, 5 ] ]

#
gap> p := (1,2^17) / (1,2^17);
()
gap> ListPerm( p );
[ ]
gap> List([-1,0,1,5], n -> ListPerm( p, n ));
[ [ ], [ ], [ 1 ], [ 1, 2, 3, 4, 5 ] ]

#
gap> p := (1,2,3);
(1,2,3)
gap> ListPerm( p );
[ 2, 3, 1 ]
gap> List([-1,0,1,5], n -> ListPerm( p, n ));
[ [ ], [ ], [ 2 ], [ 2, 3, 1, 4, 5 ] ]

#
gap> p := (1,2,3,10000)*(1,10000);
(1,2,3)
gap> ListPerm( p );
[ 2, 3, 1 ]
gap> List([-1,0,1,5], n -> ListPerm( p, n ));
[ [ ], [ ], [ 2 ], [ 2, 3, 1, 4, 5 ] ]

#
gap> ListPerm( 1 );
Error, ListPerm: <perm> must be a permutation (not the integer 1)
gap> ListPerm( (1,2,3), "bla" );
Error, ListPerm: <n> must be a small integer (not a list (string))

#
# PermList
#
Expand Down

0 comments on commit c4482be

Please sign in to comment.