Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ This file describes changes in the smallgrp package.

# Unreleased

- Optimized `NumberSmallGroups` to be much faster in certain cases, for
- Added `SmallGroupsAddLayer` as a clean interface for extending the
Small Groups Library by a further layer.
- Optimized `NumberSmallGroups` to be much faster in certain cases. For
example `NumberSmallGroups(1536, IsSolvableGroup, true)` is now instant
instead of running for 90 seconds.

Expand Down
5 changes: 4 additions & 1 deletion doc/overview.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ For more information, refer to <Cite Key="Burrell2021"/>.
<Heading>Related packages</Heading>

Several other &GAP; packages give access to groups of orders which are not
covered by this library, or which are covered only in part.
covered by this library, or which are covered only in part. A package can
make its groups available through the functions of this chapter by calling
<Ref Func="SmallGroupsAddLayer"/>.

<P/>
<Index Key="SglPPow package"><Package>SglPPow</Package> package</Index>
Expand Down Expand Up @@ -137,6 +139,7 @@ the one used here. See <URL>https://gap-packages.github.io/sotgrps/</URL>.
<#Include Label="IdsOfAllSmallGroups">
<#Include Label="IdGap3SolvableGroup">
<#Include Label="SmallGroupsInformation">
<#Include Label="SmallGroupsAddLayer">
<#Include Label="UnloadSmallGroupsData">
<#Include Label="SMALL_GROUPS_OLD_ORDER">
</Section>
Expand Down
241 changes: 241 additions & 0 deletions gap/addlayer.gi
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
#############################################################################
##
#W addlayer.gi GAP group library Max Horn
##
## The layers of the library, and the API for adding more.
##

#############################################################################
##
#V SMALL_GROUPS_LAYERS
##
## every layer, keyed by name. 'SMALL_GROUPS_LAYER_LIST' holds the same
## records in the order they are consulted.
SMALL_GROUPS_LAYERS := rec();

#############################################################################
##
#V SMALL_GROUPS_LAYERS.SmallGrp
##
## one layer standing for all those installed the old way, by filling
## 'SMALL_AVAILABLE_FUNCS' and the other global arrays it reads below: the
## layers built into this package, and any a package adds that way.
SMALL_GROUPS_LAYERS.SmallGrp := rec(
name := "SmallGrp",
before := [ ],
after := [ ],

available := function( size )
local l, r;
for l in [ 1 .. Length( SMALL_AVAILABLE_FUNCS ) ] do
if IsBound( SMALL_AVAILABLE_FUNCS[ l ] ) then
r := SMALL_AVAILABLE_FUNCS[ l ]( size );
if r <> fail then
return r;
fi;
fi;
od;
return fail;
end,

idAvailable := function( size )
local l, r;
for l in [ 1 .. Length( ID_AVAILABLE_FUNCS ) ] do
if IsBound( ID_AVAILABLE_FUNCS[ l ] ) then
r := ID_AVAILABLE_FUNCS[ l ]( size );
if r <> fail then
return r;
fi;
fi;
od;
return fail;
end,

group := function( size, i, inforec )
return SMALL_GROUP_FUNCS[ inforec.func ]( size, i, inforec );
end,

id := function( G, inforec )
return ID_GROUP_FUNCS[ inforec.func ]( G, inforec );
end,

numberOf := function( size, inforec )
return NUMBER_SMALL_GROUPS_FUNCS[ inforec.func ]( size, inforec );
end,

properties := function( size, inforec )
if not IsBound( SMALL_GROUPS_PROPERTIES_FUNCS[ inforec.func ] ) then
return fail;
fi;
return SMALL_GROUPS_PROPERTIES_FUNCS[ inforec.func ]( size, inforec );
end,

select := function( size, funcs, vals, inforec, all, id, idList )
return SELECT_SMALL_GROUPS_FUNCS[ inforec.func ]
( size, funcs, vals, inforec, all, id, idList );
end,

count := function( size, funcs, vals, inforec, idList )
if IsBound( COUNT_SMALL_GROUPS_FUNCS[ inforec.func ] ) then
return COUNT_SMALL_GROUPS_FUNCS[ inforec.func ]
( size, funcs, vals, inforec, idList );
fi;
return Length( SELECT_SMALL_GROUPS_FUNCS[ inforec.func ]
( size, funcs, vals, inforec, true, true, idList ) );
end,

information := function( size, inforec, num )
SMALL_GROUPS_INFORMATION[ inforec.func ]( size, inforec, num );
end,
);

SMALL_GROUPS_LAYER_LIST[ 1 ] := SMALL_GROUPS_LAYERS.SmallGrp;

#############################################################################
##
#F SMALL_GROUPS_SORT_LAYERS( layers )
##
## <layers> in an order meeting every 'before' and 'after' wish, taking at
## each step the earliest registered of those not waiting on another. A wish
## naming a layer that is not registered is ignored, so wishing about a
## package that is not loaded does no harm.
SMALL_GROUPS_SORT_LAYERS := function( layers )
local names, pred, layer, other, order, done, next;

names := List( layers, l -> l.name );
pred := rec();
for layer in layers do
pred.( layer.name ) := [ ];
od;
for layer in layers do
for other in layer.after do
if other in names then
AddSet( pred.( layer.name ), other );
fi;
od;
for other in layer.before do
if other in names then
AddSet( pred.( other ), layer.name );
fi;
od;
od;

order := [ ];
done := [ ];
while Length( order ) < Length( layers ) do
next := First( layers, l -> not l.name in done and
IsSubset( done, pred.( l.name ) ) );
if next = fail then
Error( "SmallGroupsAddLayer: the layers ",
JoinStringsWithSeparator(
Filtered( names, n -> not n in done ), ", " ),
" ask for an order that cannot be met" );
fi;
Add( order, next );
AddSet( done, next.name );
od;
return order;
end;

#############################################################################
##
#F SmallGroupsAddLayer( desc )
##
InstallGlobalFunction( SmallGroupsAddLayer, function( desc )
local known, comp, layer, order;

if not IsRecord( desc ) then
Error( "<desc> must be a record" );
fi;

known := [ "name", "available", "group", "id", "idAvailable", "number",
"properties", "information", "select", "count",
"before", "after" ];
for comp in RecNames( desc ) do
if not comp in known then
Error( "unknown component <desc>.", comp );
fi;
od;
for comp in [ "name", "available", "group" ] do
if not IsBound( desc.( comp ) ) then
Error( "<desc>.", comp, " must be given" );
fi;
od;
if not IsString( desc.name ) or IsEmpty( desc.name ) then
Error( "<desc>.name must be a non-empty string" );
fi;
if IsBound( SMALL_GROUPS_LAYERS.( desc.name ) ) then
Error( "a layer named \"", desc.name, "\" is already registered" );
fi;
for comp in [ "available", "group", "id", "idAvailable", "number",
"properties", "information", "select", "count" ] do
if IsBound( desc.( comp ) ) and not IsFunction( desc.( comp ) ) then
Error( "<desc>.", comp, " must be a function" );
fi;
od;
for comp in [ "before", "after" ] do
if IsBound( desc.( comp ) ) and
not ( IsList( desc.( comp ) ) and ForAll( desc.( comp ), IsString ) )
then
Error( "<desc>.", comp, " must be a list of layer names" );
fi;
od;
if IsBound( desc.idAvailable ) and not IsBound( desc.id ) then
Error( "<desc>.idAvailable is of no use without <desc>.id" );
fi;

layer := ShallowCopy( desc );
for comp in [ "before", "after" ] do
if not IsBound( layer.( comp ) ) then
layer.( comp ) := [ ];
fi;
od;

# what the layer hands back is checked once, here, rather than wherever
# it later turns out not to be a record
layer.available := function( size )
local r;
r := desc.available( size );
if r <> fail and not IsRecord( r ) then
Error( "the 'available' function of layer ", layer.name,
" must return 'fail' or a record" );
fi;
return r;
end;
if IsBound( layer.id ) and not IsBound( layer.idAvailable ) then
layer.idAvailable := layer.available;
fi;

# a layer reports a number, the library carries it in the record
if IsBound( desc.number ) then
layer.numberOf := function( size, inforec )
inforec := ShallowCopy( inforec );
inforec.number := desc.number( size, inforec );
return inforec;
end;
else
layer.numberOf := function( size, inforec )
Error( "layer ", layer.name, " reports no number of groups of ",
"order ", size );
end;
fi;

# a layer that selects for itself is not counted generically
if not IsBound( layer.select ) then
layer.select := SMALL_GROUPS_SELECT_GENERIC;
if not IsBound( layer.count ) then
layer.count := SMALL_GROUPS_COUNT_GENERIC;
fi;
fi;
if not IsBound( layer.information ) then
layer.information := ReturnTrue;
fi;

# settle the order before anything is written, so a rejected wish leaves
# the library as it was
order := SMALL_GROUPS_SORT_LAYERS(
Concatenation( SMALL_GROUPS_LAYER_LIST, [ layer ] ) );

SMALL_GROUPS_LAYERS.( layer.name ) := layer;
SMALL_GROUPS_LAYER_LIST := order;
end );
104 changes: 104 additions & 0 deletions gap/small.gd
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,110 @@ DeclareGlobalFunction( "IdStandardPresented512Group" );
##
DeclareGlobalFunction( "SmallGroupsInformation" );

#############################################################################
##
#F SmallGroupsAddLayer( <desc> )
##
## <#GAPDoc Label="SmallGroupsAddLayer">
## <ManSection>
## <Func Name="SmallGroupsAddLayer" Arg='desc'/>
##
## <Description>
## adds a further layer to the library, described by the record
## <A>desc</A>. Its groups then become available through
## <Ref Func="SmallGroup" Label="for group order and index"/>,
## <Ref Func="NumberSmallGroups"/>, <Ref Func="AllSmallGroups"/> and the
## other functions of this chapter. The components of <A>desc</A> are:
## <List>
## <Mark><C>name</C></Mark>
## <Item>
## a string naming the layer. Must be unique. Can be used by other
## layers to refer to this one.
## </Item>
## <Mark><C>available</C></Mark>
## <Item>
## a function taking an order and either returning <K>fail</K>, to indicate
## this layer does not handle the given order; or else a record
## which is handed to the functions below as <A>inforec</A>. The component
## <C>number</C> of the returned record, if present, is the number of groups
## of that order.
## </Item>
## <Mark><C>group</C></Mark>
## <Item>
## a function <C>( <A>order</A>, <A>i</A>, <A>inforec</A> )</C> returning
## the <A>i</A>-th group of that order.
## </Item>
## <Mark><C>idAvailable</C></Mark>
## <Item>
## optional, a function taking an order and either returning <K>fail</K>,
## to indicate this layer does not handle identification of groups of the
## given order; or else a record which is handed to the function <C>id</C>
## below as <A>idrec</A>. Defaults to <C>available</C>, so it is only
## needed where the identification covers other orders than the
## construction, or wants a record of its own. If <C>idAvailable</C> is
## defined then <C>id</C> must also be provided.
## </Item>
## <Mark><C>id</C></Mark>
## <Item>
## optional, a function <C>( <A>G</A>, <A>idrec</A> )</C> returning the
## index of <A>G</A> among the groups of its order. Without it
## <Ref Attr="IdSmallGroup"/> stays unavailable for these orders.
## </Item>
## <Mark><C>number</C></Mark>
## <Item>
## optional, a function <C>( <A>order</A>, <A>inforec</A> )</C> returning
## the number of groups of that order. Only needed if <C>available</C>
## does not report this count.
## </Item>
## <Mark><C>information</C></Mark>
## <Item>
## optional, a function
## <C>( <A>order</A>, <A>inforec</A>, <A>number</A> )</C> printing what
## <Ref Func="SmallGroupsInformation"/> should say about that order
## beyond the number of groups.
## </Item>
## <Mark><C>properties</C></Mark>
## <Item>
## optional, a function <C>( <A>order</A>, <A>inforec</A> )</C> reporting
## which selection criteria the <C>select</C> and <C>count</C> functions
## of this layer can deduce from the position of the groups alone, so that
## <Ref Func="SelectSmallGroups"/> and <Ref Func="NumberSmallGroups"/>
## need not construct the groups to decide these criteria.
## </Item>
## <Mark><C>select</C>, <C>count</C></Mark>
## <Item>
## optional, how <Ref Func="SelectSmallGroups"/> and
## <Ref Func="NumberSmallGroups"/> are to work through this layer. By
## default the groups of the order are constructed one by one and tested.
## </Item>
## <Mark><C>before</C>, <C>after</C></Mark>
## <Item>
## optional lists of names of other layers, to be consulted after
## respectively before this one. A name that is not registered is
## ignored, so wishing about a package that is not loaded does no harm.
## The layers of this package are themselves one layer, named
## <C>"SmallGrp"</C>, which without a wish to the contrary comes first.
## </Item>
## </List>
## Where two layers cover an order, the one consulted first wins.
## <Log><![CDATA[
## SmallGroupsAddLayer( rec(
## name := "SOTGrps",
## available := function( order )
## if not IsSOTAvailable( order ) then
## return fail;
## fi;
## return rec( number := NumberOfSOTGroups( order ) );
## end,
## group := { order, i, inforec } -> SOTGroup( order, i ),
## id := { G, inforec } -> IdSOTGroup( G )[2] ) );
## ]]></Log>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
DeclareGlobalFunction( "SmallGroupsAddLayer" );

#############################################################################
##
#A IdGap3SolvableGroup( <G> )
Expand Down
Loading