/* File permgrp.c. Contains miscellaneous short functions for computing with
permutation groups, as follows:
isNontrivialGroup: Returns true if a permutation group has order at
least 2. A base/sgs are not needed.
levelIn: Returns the level of a permutation relative to the
base of a given permutation group.
isIdentityElt: Returns true is an element of a group with known base
is an involution (fast test).
isInvolutoryElt: Returns true is an element of a group with known base
is an involution (fast test). */
#include <stddef.h>
#include <ctype.h>
#include <string.h>
#include "group.h"
#include "copy.h"
#include "errmesg.h"
#include "essentia.h"
#include "new.h"
#include "permut.h"
#include "storage.h"
extern GroupOptions options;
CHECK( permgr)
/*-------------------------- genCount -------------------------------------*/
/* This function may be used to determine the total number of generators, and
the number of known involutory generators, in a permutation group. The
function returns the total number of generators and sets the second
parameter to the number of involutory generators. Note the function assumes
inverse generators are present as permutations on the linked list of
generators, unless involCount == NULL. */
Unsigned genCount(
const PermGroup *const G, /* The permutation group. */
Unsigned *involCount) /* If nonnull, set to number of
involutory generators. */
{
Unsigned totalCount = 0, tempInvolCount = 0;
Permutation *gen;
for ( gen = G->generator ; gen ; gen = gen->next ) {
++totalCount;
if ( isInvolution( gen) )
++tempInvolCount;
}
if ( involCount )
*involCount = tempInvolCount;
return tempInvolCount + (totalCount - tempInvolCount) / 2;
}
/*-------------------------- isFixedPointOf -------------------------------*/
/* The function isFixedPointOf( G, level, point) returns true if the point
point is a fixed point of G^(level), and it returns false otherwise. */
BOOLEAN isFixedPointOf(
const PermGroup *const G, /* A group with known base and sgs. */
const Unsigned level, /* The level mentioned above. */
const Unsigned point) /* Check if this point is a fixed point. */
{
Permutation *gen;
for ( gen = G->generator ; gen ; gen = gen->next )
if ( gen->level >= level && gen->image[point] != point )
return FALSE;
return TRUE;
}
/*-------------------------- isNontrivialGroup ----------------------------*/
/* This function returns true if a permutation group has order at least 2 and
false if it has order 1. A base/sgs are not needed. */
BOOLEAN isNontrivialGroup(
PermGroup *G) /* The permutation group to test. */
{
Permutation *gen;
for ( gen = G->generator ; gen ; gen = gen->next )
if ( !isIdentity( gen) )
return TRUE;
return FALSE;
}
/*-------------------------- levelIn --------------------------------------*/
/* This function returns the level of a permutation relative to the
sequence of points in the array base of a permutation group. (The array
need not actually be a base.) If the permutation fixes the "base", the
value returned is 1+baseSize. */
Unsigned levelIn(
PermGroup *G, /* The perm group (base and baseSize filled in). */
Permutation *perm) /* The permutation whose level is returned. */
{
Unsigned level;
for ( level = 1 ; level <= G->baseSize && perm->image[G->base[level]] ==
G->base[level] ; ++level )
;
return level;
}
/*-------------------------- isIdentityElt --------------------------------*/
/* This function returns true if a given permutation known to lie in a group
with known base is the identity, and false otherwise. The function is
fast, as the permutation need be applied only to the base. */
BOOLEAN isIdentityElt(
PermGroup *G, /* The permutation group (base known). */
Permutation *perm) /* The permutation known to lie in G. */
{
Unsigned i;
if ( !IS_SYMMETRIC(G) ) {
for ( i = 1 ; i <= G->baseSize && perm->image[G->base[i]] ==
G->base[i] ; ++i )
;
return i > G->baseSize;
}
else {
for ( i = 1 ; i <= G->degree && perm->image[i] == i ; ++i )
;
return i > G->degree;
}
}
/*-------------------------- isInvolutoryElt ------------------------------*/
/* This function returns true if a given permutation known to lie in a group
with known base is an involution, and false otherwise. The function is
fast, as the permutation need be applied only to the base. */
BOOLEAN isInvolutoryElt(
PermGroup *G, /* The permutation group (base known). */
Permutation *perm) /* The permutation known to lie in G. */
{
Unsigned i;
for ( i = 1 ; i <= G->baseSize && perm->image[perm->image[G->base[i]]] ==
G->base[i] ; ++i )
;
return i > G->baseSize;
}
/*-------------------------- fixesBasicOrbit ------------------------------*/
/* The function fixesBasicOrbit( G, level, perm) returns true if permutation
perm fixes (setwise) the level'th basic orbit or permutation group G and
returns false otherwise. Note: The basic orbit need not be correct. */
BOOLEAN fixesBasicOrbit(
const PermGroup *const G,
const Unsigned level,
const Permutation *const perm)
{
Unsigned i;
UnsignedS *basicOrbit = G->basicOrbit[level];
Permutation **svec = G->schreierVec[level];
for ( i = 1 ; i <= G->basicOrbLen[level] ; ++i )
if ( ! svec[ perm->image[basicOrbit[i]] ] )
return FALSE;
return TRUE;
}
/*-------------------------- linkEssentialGens ----------------------------*/
/* This function constructs a singly linked list of all the generators of a
permutation group essential at a specified level, using the xNext field
of the generating permutations. It returns a pointer to the head
permutation on the list. The field essential of each generator must be
filled in. */
Permutation *linkEssentialGens(
PermGroup *G, /* The permutation group. */
Unsigned level) /* Generators essential at this level are
linked. */
{
Permutation *listHeader = NULL, *previousGen = NULL, *gen;
for ( gen = G->generator ; gen ; gen = gen->next )
if ( ESSENTIAL_AT_LEVEL(gen,level) ) {
if ( previousGen )
previousGen->xNext = gen;
else
listHeader = gen;
gen->xNext = NULL;
previousGen = gen;
}
return listHeader;
}
/*-------------------------- assignGenName -------------------------------*/
/* This function assigns a name to a generator for a permutation group. The
name chosen is <options.genNamePrefix>zz, where zz is the 2-digit ASCII
representation of the smallest positive integer not currently in use for
a generator name. However, if <options.genNamePrefix> is *, then the name
will be a single letter, the first not currently in use, or two identical
letters, if all single letters are in use. The function terminates with
an error message if all possible names are already in use. */
void assignGenName(
PermGroup *const G,
Permutation *const gen)
{
char inUse[256];
char *letter = "abcdefghijklmnopqrstuvwxyz";
Unsigned i, prefixLen;
char let1, let2;
Permutation *oldGen;
if ( strcmp(options.genNamePrefix,"*") == 0 ) {
for ( i = 0 ; i < 26 ; ++i ) {
inUse[letter[i]] = FALSE;
inUse[letter[i]-64] = FALSE; /* OK for ASCII and EBCDIC. */
}
for ( oldGen = G->generator ; oldGen ; oldGen = oldGen->next ) {
let1 = tolower( oldGen->name[0]);
let2 = tolower( oldGen->name[1]);
if ( strlen(oldGen->name) == 1 )
inUse[let1] = TRUE;
else if ( strlen(oldGen->name) == 2 && let1 == let2 & let1 >= 64 )
inUse[let1-64] = TRUE;
}
for ( i = 0 ; i < 26 ; ++i )
if ( !inUse[letter[i]] ) {
gen->name[0] = letter[i];
gen->name[1] = '\0';
return ;
}
for ( i = 0 ; i < 26 ; ++i )
if ( !inUse[letter[i]-64] ) {
gen->name[0] = letter[i];
gen->name[1] = letter[i];
gen->name[2] = '\0';
return ;
}
ERROR( "assignGenName", "No more generator names are available.")
}
else{
for ( i = 1 ; i <= 99 ; ++i )
inUse[i] = 0;
prefixLen = strlen( options.genNamePrefix);
for ( oldGen = G->generator ; oldGen ; oldGen = oldGen->next )
if ( strncmp( oldGen->name, options.genNamePrefix, prefixLen) == 0 &&
oldGen->name[prefixLen] == i / 10 + '0' &&
oldGen->name[prefixLen+1] == i % 10 + '0' )
inUse[i] = TRUE;
for ( i = 1 ; i <= 99 ; ++i )
if ( !inUse[i] ) {
strcpy( gen->name, options.genNamePrefix);
gen->name[prefixLen] = i / 10 + '0';
gen->name[prefixLen+1] = i % 10 + '0';
gen->name[prefixLen+2] = '\0';
return;
}
ERROR( "assignGenName", "No more generator names are available.")
}
}
/*-------------------------- adjoinInverseGen ----------------------------*/
void adjoinInverseGen(
PermGroup *const G,
Permutation *gen) /* Must be a generator of G, and must have invImage. */
{
Permutation *invGen;
if ( !gen->invPermutation ) {
if ( gen->image == gen->invImage )
gen->invPermutation = gen;
else {
invGen = allocPermutation();
gen->invPermutation = invGen;
invGen->invPermutation = gen;
strcpy( invGen->name, "*");
invGen->degree = gen->degree;
invGen->level = gen->level;
MAKE_NOT_ESSENTIAL_ALL( invGen);
invGen->image = gen->invImage;
invGen->invImage = gen->image;
invGen->last = NULL;
invGen->next = G->generator;
G->generator->last = invGen;
G->generator = invGen;
}
}
}
/*-------------------------- depthGreaterThan ----------------------------*/
/* This function tests rather the "depth" of a group G exceeds a specified
integral value comparisonDepth. Here the depth of G is defined to be
log(|G|) / log(degree(G)). The function returns true if the depth of
G exceeds comparisonDepth and false otherwise. Overflow should occur
only if (degree(G))^comparisonDepth is out of bounds. If the NOFLOAT
option is given, the computation is approximate. */
BOOLEAN depthGreaterThan(
const PermGroup *const G,
const Unsigned comparisonDepth)
{
int i, j;
#ifndef NOFLOAT
double ratio = 1.0;
/* Handle symmetric group. (Always return true -- tecnically wrong.) */
if ( IS_SYMMETRIC(G) )
return TRUE;
for ( i = 1 ; i <= comparisonDepth ; ++i )
ratio /= (double) G->degree;
for ( i = 0 ; i < G->order->noOfFactors ; ++i )
for ( j = 1 ; j <= G->order->exponent[i] ; ++j ) {
ratio *= (double) G->order->prime[i];
if ( ratio > 1.0 )
return TRUE;
}
#endif
#ifdef NOFLOAT
int cDepth = comparisonDepth;
unsigned long product = 1;
/* Handle symmetric group. (Always return true -- tecnically wrong.) */
if ( IS_SYMMETRIC(G) )
return TRUE;
for ( i = 0 ; i < G->order->noOfFactors ; ++i )
for ( j = 1 ; j <= G->order->exponent[i] ; ++j ) {
product *= G->order->prime[i];
if ( product >= G->degree ) {
product = (product + G->degree / 2) / G->degree;
--cDepth;
if ( cDepth <= 0 )
return TRUE;
}
}
#endif
return FALSE;
}
/*-------------------------- isDoublyTransitive --------------------------*/
/* This function tests whether a group G is doubly transitive. A base and
strong generating set for G must be available (not checked). */
BOOLEAN isDoublyTransitive(
const PermGroup *const G)
{
return G->baseSize >= 2 && G->basicOrbLen[2] == G->degree - 1;
}
/*-------------------------- conjugatePermByPerm -------------------------*/
/* This function may be used to conjugate one permutation by another. The
permutation perm is replaced by conjPerm^-1 * perm * conjPerm. It is
assumed that both permutations have the same degree. */
void conjugatePermByPerm(
Permutation *const perm,
const Permutation *const conjPerm)
{
Unsigned pt;
Unsigned *tempPerm = allocIntArrayDegree();
for ( pt = 1 ; pt <= perm->degree ; ++pt )
tempPerm[pt] = perm->image[pt];
for ( pt = 1 ; pt <= perm->degree ; ++pt )
perm->image[conjPerm->image[pt]] = conjPerm->image[tempPerm[pt]];
if ( perm->invImage )
for ( pt = 1 ; pt <= perm->degree ; ++pt )
perm->invImage[perm->image[pt]] = pt;
freeIntArrayDegree( tempPerm);
}
/*-------------------------- conjugateGroupByPerm ------------------------*/
/* This function may be used to conjugate a permutation group by a permutation.
The permutation group G is replaced by conjPerm^-1 * G * conjPerm. It is
assumed that the group and the permutation have the same degree. The
complete The completeOrbit, orbNumberOfPt, and startOfOrbitNo fields are
not currently handled, nor are omega and invOmega. */
void conjugateGroupByPerm(
PermGroup *const G,
const Permutation *const conjPerm)
{
Unsigned level, i, pt;
Permutation *gen;
Permutation **tempSVec, **temp;
/* Conjugate the base, basic orbits, and Schreier vectors. */
if ( G->base ) {
tempSVec = (Permutation **) allocPtrArrayDegree();
for ( level = 1 ; level <= G->baseSize ; ++level ) {
G->base[level] = conjPerm->image[G->base[level]];
for ( i = 1 ; i <= G->basicOrbLen[level] ; ++i )
G->basicOrbit[level][i] = conjPerm->image[G->basicOrbit[level][i]];
for ( pt = 1 ; pt <= G->degree ; ++pt )
tempSVec[conjPerm->image[pt]] = G->schreierVec[level][pt];
EXCHANGE( G->schreierVec[level], tempSVec, temp);
}
freePtrArrayDegree( tempSVec);
}
/* Conjugate generators. */
for ( gen = G->generator ; gen ; gen = gen->next )
conjugatePermByPerm( gen, conjPerm);
}
/*-------------------------- checkConjugacyInGroup ------------------------------*/
/* The function checkConjugacyInGroup( G, e, f, conjPerm) returns true if
permutation conjPerm in G conjugates permutation e in group G to permutation
f in G, that is, if e ^ conjPerm = f, or equivalently,
e * conjPerm = conjPerm * f. It is assumed G has a base and strong
generating set. */
BOOLEAN checkConjugacyInGroup(
const PermGroup *const G,
const Permutation *const e,
const Permutation *const f,
const Permutation *const conjPerm)
{
int i;
for ( i = 1 ; i <= G->baseSize ; ++i )
if ( conjPerm->image[e->image[G->base[i]]] !=
f->image[conjPerm->image[G->base[i]]] )
return FALSE;
return TRUE;
}
/*-------------------------- isElementOf -----------------------------------------*/
/* The function isElementOf( perm, group) returns true is the permutation
perm lies in the group G and false otherwise. The group must already
have a base and strong generating set. This procedure is not particularly
efficient: It does a great deal of unnecessary multiplications
if containment turns out not to hold. */
BOOLEAN isElementOf(
const Permutation *const perm,
const PermGroup *const group)
{
Permutation *gen, *perm1;
Unsigned level, gamma;
BOOLEAN returnValue;
/* Check that the group has a base. If not, give error message. */
if ( group->base == NULL)
ERROR1s( "isElementOf", "Group ", group->name, " must have a base.")
/* Make a copy of permutation perm. */
perm1 = copyOfPermutation( perm);
/* Now attempt to factor perm1. If process breaks down because the
appropriate point is not in the required basic orbit, return false
immediately. */
for ( level = 1 ; level <= group->baseSize ; ++level) {
gamma = perm1->image[group->base[level]];
while ( (gen = group->schreierVec[level][gamma]) != NULL &&
gen != FIRST_IN_ORBIT ) {
rightMultiplyInv( perm1, gen);
gamma = gen->invImage[gamma];
}
if ( gen == NULL ) {
deletePermutation( perm1);
return FALSE;
}
}
/* If all the appropriate points lie in the correct basic orbits, return
true if and only if the remaining permutation is the identity. */
returnValue = isIdentity( perm1);
deletePermutation( perm1);
return returnValue;
}
/*-------------------------- isSubgroupOf ----------------------------------------*/
/* The function isSubgroupOf( subGroup, group) returns true if subGroup
is a subgroup of group and returns false otherwise. The degrees of the
groups must be equal. It is not too efficient because it checks all
(possibly strong) generators of subGroup. Note group must have a
base and strong generating set, but subGroup need not. */
BOOLEAN isSubgroupOf(
const PermGroup *const subGroup,
const PermGroup *const group)
{
Permutation *gen;
/* Check that the group has a base. If not, give error message. */
if ( group->base == NULL)
ERROR1s( "isElementOf", "Group ", group->name, " must have a base.")
/* Now check each generator of subGroup for containment in group. */
for ( gen = subGroup->generator ; gen ; gen = gen->next )
if ( !isElementOf(gen,group) )
return FALSE;
return TRUE;
}
/*-------------------------- isNormalizedBy --------------------------------------*/
/* The function isNormalizedBy( group, nGroup) returns true if group
is normalized by nGroup and returns false otherwise. The degrees of the
groups must be equal. It is inefficient because it checks all
(possibly strong) generators of group conjugated by all (possibly strong)
generators of nGroup and, more importantly, it does NOT take advantage of
any prior knowledge that group is a subgroup of nGroup. Note group must
have a base and strong generating set, but nGroup need not. */
BOOLEAN isNormalizedBy(
const PermGroup *const group,
const PermGroup *const nGroup)
{
Permutation *gen, *nGen, *perm = newIdentityPerm( group->degree);
BOOLEAN involFlag;
Unsigned pt;
/* Check that the group has a base. If not, give error message. */
if ( group->base == NULL)
ERROR1s( "isElementOf", "Group ", group->name, " must have a base.")
/* Now check each conjugage of each generator of group for containment in
nGroup. */
for ( gen = group->generator ; gen ; gen = gen->next ) {
involFlag = isInvolution( gen);
if ( involFlag && perm->invImage != perm->image ) {
freeIntArrayDegree( perm->invImage);
perm->invImage = perm->image;
}
else if ( !involFlag && perm->invImage == perm->image )
perm->invImage = allocIntArrayDegree();
for ( nGen = nGroup->generator ; nGen ; nGen = nGen->next ) {
for ( pt = 1 ; pt <= group->degree ; ++pt )
perm->image[nGen->image[pt]] = nGen->image[gen->image[pt]];
if ( !involFlag )
for ( pt = 1 ; pt <= group->degree ; ++pt )
perm->invImage[perm->image[pt]] = pt;
if ( !isElementOf(perm,group) ) {
freePermutation( perm);
return FALSE;
}
}
}
freePermutation( perm);
return TRUE;
}
/*-------------------------- isCentralizedBy --------------------------------*/
/* The function isCentralizedBy( group, cGroup) returns true if group
is centralized by cGroup and returns false otherwise. The degrees of the
groups must be equal. It is inefficient because it checks all pairs of
(possibly strong) generators from the two groups and, more importantly, it
does NOT take advantage of any prior knowledge of the bases of the two
groups (as would be possible when one is contained in the other). */
BOOLEAN isCentralizedBy(
const PermGroup *const group,
const PermGroup *const cGroup)
{
Permutation *gen, *cGen;
Unsigned pt;
/* Check equality of degrees. */
if ( group->degree != cGroup->degree )
ERROR( "isCentralizedBy", "Groups do not have same degree.")
/* Check each generator of group commutes with each generator of cGroup. */
for ( gen = group->generator ; gen ; gen = gen->next )
for ( cGen = cGroup->generator ; cGen ; cGen = cGen->next )
for ( pt = 1 ; pt <= group->degree ; ++pt )
if ( cGen->image[gen->image[pt]] != gen->image[cGen->image[pt]] )
return FALSE;
return TRUE;
}
/*-------------------------- isBaseImage -----------------------------------------*/
/* The function isBaseImage( G, image), where G is a permutation group having
a base and strong generating set and where image is an array of points of
length G->baseSize, returns true if there exists a permutation in G mapping
the base to the sequence image, and returns false otherwise. */
BOOLEAN isBaseImage(
const PermGroup *const G,
const Unsigned image[])
{
Unsigned level, i, t;
UnsignedS *img = allocIntArrayBaseSize();
for ( level = 1 ; level <= G->baseSize ; ++level )
img[level] = image[level];
for ( level = 1 ; level <= G->baseSize ; ++level ) {
if ( G->schreierVec[level][img[level]] == NULL ) {
freeIntArrayBaseSize( img);
return FALSE;
}
while ( img[level] != G->base[level] ) {
t = img[level];
for ( i = level ; i <= G->baseSize ; ++i )
img[i] = G->schreierVec[level][t]->invImage[img[i]];
}
}
freeIntArrayBaseSize( img);
return TRUE;
}
/*-------------------------- reduceWrtGroup --------------------------------*/
/* The function reduceWrtGroup( G, h, reductionLevel), where G is a
permutation group with base and strong generating set and where h is a
permutation (with inverse image) replaces h by
h u_1[d_1]^-1 u_2[d_2]^-1 u_j-1[d_j-1]^-1,
where the new h fixes the first j-1 base points of G, and where the
new h maps the j'th base point outside the j'th basic orbit, or where
j = G->baseSize+1. Unless reductionLevel = NULL, it sets *reductionLevel
to j. */
void reduceWrtGroup(
const PermGroup *const G,
Permutation *const h,
Unsigned *reductionLevel)
{
int level;
for ( level = 1 ; level <= G->baseSize &&
G->schreierVec[level][h->image[G->base[level]]] != NULL ; ++level )
while ( h->image[G->base[level]] != G->base[level] )
rightMultiplyInv( h, G->schreierVec[level][h->image[G->base[level]]]);
if ( reductionLevel )
*reductionLevel = level;
}
¤ Dauer der Verarbeitung: 0.11 Sekunden
¤
*© Formatika GbR, Deutschland
|
|