diff --git a/CHANGES.md b/CHANGES.md index 2322e5e..4ce5419 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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. diff --git a/doc/overview.xml b/doc/overview.xml index 4c3c9b1..4d8be7a 100644 --- a/doc/overview.xml +++ b/doc/overview.xml @@ -97,7 +97,9 @@ For more information, refer to . Related packages Several other ⪆ 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 +.

SglPPow package @@ -137,6 +139,7 @@ the one used here. See https://gap-packages.github.io/sotgrps/. <#Include Label="IdsOfAllSmallGroups"> <#Include Label="IdGap3SolvableGroup"> <#Include Label="SmallGroupsInformation"> + <#Include Label="SmallGroupsAddLayer"> <#Include Label="UnloadSmallGroupsData"> <#Include Label="SMALL_GROUPS_OLD_ORDER"> diff --git a/gap/addlayer.gi b/gap/addlayer.gi new file mode 100644 index 0000000..1ce8727 --- /dev/null +++ b/gap/addlayer.gi @@ -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 ) +## +## 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( " 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 .", comp ); + fi; + od; + for comp in [ "name", "available", "group" ] do + if not IsBound( desc.( comp ) ) then + Error( ".", comp, " must be given" ); + fi; + od; + if not IsString( desc.name ) or IsEmpty( desc.name ) then + Error( ".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( ".", 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( ".", comp, " must be a list of layer names" ); + fi; + od; + if IsBound( desc.idAvailable ) and not IsBound( desc.id ) then + Error( ".idAvailable is of no use without .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 ); diff --git a/gap/small.gd b/gap/small.gd index c8b8a24..c351d54 100644 --- a/gap/small.gd +++ b/gap/small.gd @@ -977,6 +977,110 @@ DeclareGlobalFunction( "IdStandardPresented512Group" ); ## DeclareGlobalFunction( "SmallGroupsInformation" ); +############################################################################# +## +#F SmallGroupsAddLayer( ) +## +## <#GAPDoc Label="SmallGroupsAddLayer"> +## +## +## +## +## adds a further layer to the library, described by the record +## desc. Its groups then become available through +## , +## , and the +## other functions of this chapter. The components of desc are: +## +## name +## +## a string naming the layer. Must be unique. Can be used by other +## layers to refer to this one. +## +## available +## +## a function taking an order and either returning fail, to indicate +## this layer does not handle the given order; or else a record +## which is handed to the functions below as inforec. The component +## number of the returned record, if present, is the number of groups +## of that order. +## +## group +## +## a function ( order, i, inforec ) returning +## the i-th group of that order. +## +## idAvailable +## +## optional, a function taking an order and either returning fail, +## to indicate this layer does not handle identification of groups of the +## given order; or else a record which is handed to the function id +## below as idrec. Defaults to available, so it is only +## needed where the identification covers other orders than the +## construction, or wants a record of its own. If idAvailable is +## defined then id must also be provided. +## +## id +## +## optional, a function ( G, idrec ) returning the +## index of G among the groups of its order. Without it +## stays unavailable for these orders. +## +## number +## +## optional, a function ( order, inforec ) returning +## the number of groups of that order. Only needed if available +## does not report this count. +## +## information +## +## optional, a function +## ( order, inforec, number ) printing what +## should say about that order +## beyond the number of groups. +## +## properties +## +## optional, a function ( order, inforec ) reporting +## which selection criteria the select and count functions +## of this layer can deduce from the position of the groups alone, so that +## and +## need not construct the groups to decide these criteria. +## +## select, count +## +## optional, how and +## are to work through this layer. By +## default the groups of the order are constructed one by one and tested. +## +## before, after +## +## 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 +## "SmallGrp", which without a wish to the contrary comes first. +## +## +## Where two layers cover an order, the one consulted first wins. +## SOTGroup( order, i ), +## id := { G, inforec } -> IdSOTGroup( G )[2] ) ); +## ]]> +## +## +## <#/GAPDoc> +## +DeclareGlobalFunction( "SmallGroupsAddLayer" ); + ############################################################################# ## #A IdGap3SolvableGroup( ) diff --git a/gap/small.gi b/gap/small.gi index 9230498..ed191b3 100644 --- a/gap/small.gi +++ b/gap/small.gi @@ -7,13 +7,23 @@ ## groups and the group identification routines. ## +############################################################################# +## +#V SMALL_GROUPS_LAYER_LIST +## +## every layer, in the order they are consulted. 'gap/addlayer.gi' puts the +## layers of this package into the first entry and appends those registered +## by 'SmallGroupsAddLayer'. +SMALL_GROUPS_LAYER_LIST := [ ]; + ############################################################################# ## #F SMALL_AVAILABLE_FUNCS ## ## On every level of the small groups library one function is written into ## this list. It will detect those sizes, which are contained in this -## library level. +## library level. Layers registered by 'SmallGroupsAddLayer' do not appear +## here; the layer 'SmallGrp' is what reads this list. SMALL_AVAILABLE_FUNCS := [ ]; ############################################################################# @@ -22,20 +32,20 @@ SMALL_AVAILABLE_FUNCS := [ ]; ## ## returns fail if the library of groups of is not installed. ## Otherwise a record with some information about the construction of the -## groups of is returned. +## groups of is returned. Its component 'layer' is the layer that +## answered, which is how the functions below reach the rest of its methods. InstallGlobalFunction( SMALL_AVAILABLE, function( size ) - local l, r; + local layer, r; if not IsPosInt( size ) then Error( " must be a positive integer"); fi; - 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; + for layer in SMALL_GROUPS_LAYER_LIST do + r := layer.available( size ); + if r <> fail then + r.layer := layer; + return r; fi; od; return fail; @@ -548,11 +558,14 @@ SMALL_GROUPS_PROPERTY_IDS := function( size, inforec, funcs, vals ) od; ids := [ [ 1 .. inforec.number ] ]; - if ForAll( hits, x -> x = fail ) or - not IsBound( SMALL_GROUPS_PROPERTIES_FUNCS[ inforec.func ] ) then + if ForAll( hits, x -> x = fail ) + or not IsBound( inforec.layer.properties ) then + return rec( ids := ids, funcs := funcs, vals := vals ); + fi; + props := inforec.layer.properties( size, inforec ); + if props = fail then return rec( ids := ids, funcs := funcs, vals := vals ); fi; - props := SMALL_GROUPS_PROPERTIES_FUNCS[ inforec.func ]( size, inforec ); evalfuncs := [ ]; evalvals := [ ]; @@ -613,7 +626,7 @@ SMALL_GROUPS_SELECT_GENERIC := function( size, funcs, vals, inforec, local result, i, g, ok, j, sel, range; if not IsBound( inforec.number ) then - inforec := NUMBER_SMALL_GROUPS_FUNCS[ inforec.func ]( size, inforec); + inforec := inforec.layer.numberOf( size, inforec ); fi; # narrow down the candidates using properties which can be decided from @@ -640,7 +653,7 @@ SMALL_GROUPS_SELECT_GENERIC := function( size, funcs, vals, inforec, result := [ ]; for i in range do - g := SMALL_GROUP_FUNCS[ inforec.func ]( + g := inforec.layer.group( size, SMALL_GROUPS_LIBRARY_NUMBER( size, i ), inforec ); SetIdGroup( g, [ size, i ] ); ok := true; @@ -673,7 +686,7 @@ SMALL_GROUPS_COUNT_GENERIC := function( size, funcs, vals, inforec, idList ) local sel, range, n, i, g; if not IsBound( inforec.number ) then - inforec := NUMBER_SMALL_GROUPS_FUNCS[ inforec.func ]( size, inforec ); + inforec := inforec.layer.numberOf( size, inforec ); fi; sel := SMALL_GROUPS_PROPERTY_IDS( size, inforec, funcs, vals ); @@ -692,7 +705,7 @@ SMALL_GROUPS_COUNT_GENERIC := function( size, funcs, vals, inforec, idList ) n := 0; for i in range do - g := SMALL_GROUP_FUNCS[ inforec.func ]( + g := inforec.layer.group( size, SMALL_GROUPS_LIBRARY_NUMBER( size, i ), inforec ); SetIdGroup( g, [ size, i ] ); if ForAll( [ 1 .. Length( sel.funcs ) ], @@ -719,11 +732,11 @@ SMALL_GROUPS_COUNT := function( argl ) Error( "NumberSmallGroups: groups of order ", size, " not available" ); fi; - if IsBound( COUNT_SMALL_GROUPS_FUNCS[ inforec.func ] ) then - n := n + COUNT_SMALL_GROUPS_FUNCS[ inforec.func ] + if IsBound( inforec.layer.count ) then + n := n + inforec.layer.count ( size, query.funcs, query.vals, inforec, query.idList ); else - n := n + Length( SELECT_SMALL_GROUPS_FUNCS[ inforec.func ] + n := n + Length( inforec.layer.select ( size, query.funcs, query.vals, inforec, true, true, query.idList ) ); fi; @@ -776,7 +789,7 @@ InstallGlobalFunction( SmallGroup, function( arg ) if inforec = fail then Error( "the library of groups of size ", size, " is not available" ); fi; - g := SMALL_GROUP_FUNCS[ inforec.func ]( + g := inforec.layer.group( size, SMALL_GROUPS_LIBRARY_NUMBER( size, i ), inforec ); SetIdGroup( g, [ size, i ] ); IsPGroup( g ); @@ -802,7 +815,7 @@ SMALL_GROUPS_NUMBER := function( size ) if IsBound( inforec.number ) then return inforec.number; fi; - return NUMBER_SMALL_GROUPS_FUNCS[ inforec.func ]( size, inforec ).number; + return inforec.layer.numberOf( size, inforec ).number; end; ############################################################################# @@ -866,7 +879,7 @@ InstallGlobalFunction( SelectSmallGroups, function( argl, all, id ) Error( "AllSmallGroups / OneSmallGroup: groups of order ", size, " not available" ); fi; - gs := SELECT_SMALL_GROUPS_FUNCS[ inforec.func ] + gs := inforec.layer.select ( size, funcs, vals, inforec, all, id, idList ); if all then Append( result, gs ); @@ -893,14 +906,15 @@ ID_AVAILABLE_FUNCS := [ ]; #F ID_AVAILABLE ## InstallGlobalFunction( ID_AVAILABLE, function( size ) - local l, r; + local layer, r; if not IsInt( size ) then return fail; fi; - 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 + for layer in SMALL_GROUPS_LAYER_LIST do + if IsBound( layer.idAvailable ) then + r := layer.idAvailable( size ); + if r <> fail then + r.layer := layer; return r; fi; fi; @@ -929,13 +943,13 @@ InstallMethod( IdGroup, [ IsGroup ], 0, function( G ) - local inforec, size, id; + local idrec, size, id; size := Size( G ); if size = 1 then return [ 1, 1 ]; fi; - inforec := ID_AVAILABLE( size ); - if inforec = fail then + idrec := ID_AVAILABLE( size ); + if idrec = fail then Error( "the group identification for groups of size ", size, " is not available" ); fi; @@ -958,7 +972,7 @@ function( G ) G := PcGroupCode( CodePcGroup( G ), Size( G ) ); fi; - id := ID_GROUP_FUNCS[ inforec.func ]( G, inforec ); + id := idrec.layer.id( G, idrec ); if not SMALL_GROUPS_OLD_ORDER then if size = 3^7 then diff --git a/gap/smlinfo.gi b/gap/smlinfo.gi index 33bb401..4d9ba0f 100644 --- a/gap/smlinfo.gi +++ b/gap/smlinfo.gi @@ -61,28 +61,21 @@ InstallGlobalFunction( SmallGroupsInformation, function( size ) ErrorNoReturn("usage: SmallGroupsInformation( size )"); fi; - smav := SMALL_AVAILABLE( size ); - idav := ID_AVAILABLE( size ); - if size = 1024 then Print( "The groups of size 1024 are not available. \n"); return; fi; + smav := SMALL_AVAILABLE( size ); if smav = fail then Print( "The groups of size ", size, " are not available. \n"); return; fi; - lib := 1; - if IsBound( smav.lib ) then - lib := smav.lib; - fi; - if IsBound( smav.number ) then num := smav.number; else - num := NUMBER_SMALL_GROUPS_FUNCS[ smav.func ]( size, smav ).number; + num := smav.layer.numberOf( size, smav ).number; fi; if num = 1 then Print("\n There is 1 group of order ",size,".\n"); @@ -90,26 +83,39 @@ InstallGlobalFunction( SmallGroupsInformation, function( size ) Print("\n There are ",num," groups of order ",size,".\n" ); fi; - SMALL_GROUPS_INFORMATION[ smav.func ]( size, smav, num ); + smav.layer.information( size, smav, num ); # report those properties whose value the selection functions can read # off from the position a group has in this list - if IsBound( SMALL_GROUPS_PROPERTIES_FUNCS[ smav.func ] ) then + if IsBound( smav.layer.properties ) then if not IsBound( smav.number ) then smav.number := num; fi; - props := SMALL_GROUPS_PROPERTIES_FUNCS[ smav.func ]( size, smav ); - names := List( Filtered( Concatenation( - SMALL_GROUPS_INDEXED_PROPERTIES, - SMALL_GROUPS_INDEXED_ATTRIBUTES ), - t -> IsBound( props.( t[ 1 ] ) ) ), - t -> t[ 3 ] ); - SMALL_GROUPS_PRINT_INDEXED( names ); + props := smav.layer.properties( size, smav ); + if props <> fail then + names := List( Filtered( Concatenation( + SMALL_GROUPS_INDEXED_PROPERTIES, + SMALL_GROUPS_INDEXED_ATTRIBUTES ), + t -> IsBound( props.( t[ 1 ] ) ) ), + t -> t[ 3 ] ); + SMALL_GROUPS_PRINT_INDEXED( names ); + fi; fi; - Print("\n This size belongs to layer ",lib, - " of the SmallGroups library. \n"); + if IsBound( smav.lib ) then + lib := smav.lib; + else + lib := 1; + fi; + if smav.layer.name = "SmallGrp" then + Print("\n This size belongs to layer ",lib, + " of the SmallGroups library. \n"); + else + Print("\n This size belongs to the layer \"",smav.layer.name, + "\". \n"); + fi; + idav := ID_AVAILABLE( size ); if idav <> fail then Print(" IdSmallGroup is available for this size. \n \n"); else diff --git a/read.g b/read.g index d6b9e1f..5919a58 100644 --- a/read.g +++ b/read.g @@ -10,6 +10,9 @@ ReadPackage( "smallgrp", "gap/idgrp1.g" ); # read the information function ReadPackage( "smallgrp", "gap/smlinfo.gi" ); +# read the API for adding further layers +ReadPackage( "smallgrp", "gap/addlayer.gi" ); + # read the function-files of the small groups library READ_SMALL_LIB := function() local i, s, LoadFunc; diff --git a/tst/addlayer.tst b/tst/addlayer.tst new file mode 100644 index 0000000..a455e7a --- /dev/null +++ b/tst/addlayer.tst @@ -0,0 +1,307 @@ +gap> START_TEST("addlayer.tst"); +gap> saved := rec( layers := ShallowCopy( SMALL_GROUPS_LAYER_LIST ), +> avail := ShallowCopy( SMALL_AVAILABLE_FUNCS ) );; + +# +# The layers below are stand-ins. None of the orders they claim -- 2016, +# 2025, 2040 -- has a layer in this library, and none of them hands out all +# the groups of its orders; they hand out enough to watch the plumbing. +# +gap> SmallGroupsAddLayer( rec( +> name := "two of them", +> available := function( order ) +> if order <> 2016 then +> return fail; +> fi; +> return rec( number := 2 ); +> end, +> group := function( order, i, inforec ) +> if i = 1 then +> return CyclicGroup( order ); +> fi; +> return DihedralGroup( order ); +> end, +> id := function( G, inforec ) +> if IsAbelian( G ) then +> return 1; +> fi; +> return 2; +> end, +> information := function( order, inforec, num ) +> Print( "\n Namely the cyclic and the dihedral one.\n" ); +> end ) ); +gap> SmallGroupsAvailable( 2016 ); +true +gap> IdGroupsAvailable( 2016 ); +true +gap> NumberSmallGroups( 2016 ); +2 +gap> List( [ 1, 2 ], i -> IdGroup( SmallGroup( 2016, i ) ) ); +[ [ 2016, 1 ], [ 2016, 2 ] ] +gap> IdsOfAllSmallGroups( 2016, IsAbelian, true ); +[ [ 2016, 1 ] ] +gap> NumberSmallGroups( 2016, IsAbelian, false ); +1 +gap> SmallGroupsInformation( 2016 ); + + There are 2 groups of order 2016. + + Namely the cyclic and the dihedral one. + + This size belongs to the layer "two of them". + IdSmallGroup is available for this size. + + +# +# The layer is registered by name, behind the layers of this library. +# +gap> Position( SMALL_GROUPS_LAYER_LIST, SMALL_GROUPS_LAYERS.("two of them") ); +2 +gap> SmallGroupsAddLayer( rec( name := "two of them", +> available := ReturnFail, +> group := ReturnFail ) ); +Error, a layer named "two of them" is already registered +gap> SmallGroupsAddLayer( rec( name := "typo", available := ReturnFail, +> group := ReturnFail, ids := ReturnFail ) ); +Error, unknown component .ids +gap> SmallGroupsAddLayer( rec( name := "sparse", available := ReturnFail ) ); +Error, .group must be given +gap> SmallGroupsAddLayer( 42 ); +Error, must be a record +gap> SmallGroupsAddLayer( rec( name := 42, available := ReturnFail, +> group := ReturnFail ) ); +Error, .name must be a non-empty string +gap> SmallGroupsAddLayer( rec( name := "", available := ReturnFail, +> group := ReturnFail ) ); +Error, .name must be a non-empty string +gap> SmallGroupsAddLayer( rec( name := "odd", available := 42, +> group := ReturnFail ) ); +Error, .available must be a function +gap> SmallGroupsAddLayer( rec( name := "odd", available := ReturnFail, +> group := ReturnFail, before := [ 42 ] ) ); +Error, .before must be a list of layer names + +# +# Layers all claiming order 2025, to watch the order they are consulted in. +# What they hand out is beside the point; only which of them answers is. +# +gap> claim := function( name, number, wishes ) +> local desc; +> desc := ShallowCopy( wishes ); +> desc.name := name; +> desc.available := function( order ) +> if order <> 2025 then +> return fail; +> fi; +> return rec( number := number ); +> end; +> desc.group := { order, i, inforec } -> CyclicGroup( order ); +> SmallGroupsAddLayer( desc ); +> end;; +gap> claim( "alpha", 3, rec() ); +gap> NumberSmallGroups( 2025 ); +3 +gap> claim( "beta", 4, rec( before := [ "alpha" ] ) ); +gap> NumberSmallGroups( 2025 ); +4 +gap> claim( "gamma", 5, rec( after := [ "beta" ], before := [ "alpha" ] ) ); +gap> NumberSmallGroups( 2025 ); +4 +gap> List( SMALL_GROUPS_LAYER_LIST, l -> l.name ); +[ "SmallGrp", "two of them", "beta", "gamma", "alpha" ] + +# a wish about a layer that is not registered is ignored +gap> claim( "delta", 6, rec( after := [ "not loaded" ] ) ); +gap> NumberSmallGroups( 2025 ); +4 + +# wishes that cannot all be met are refused, and nothing is registered +gap> claim( "loop", 7, rec( before := [ "alpha" ], after := [ "alpha" ] ) ); +Error, SmallGroupsAddLayer: the layers alpha, loop ask for an order that canno\ +t be met +gap> IsBound( SMALL_GROUPS_LAYERS.loop ); +false +gap> NumberSmallGroups( 2025 ); +4 + +# without an 'id' function the identification stays unavailable +gap> IdGroupsAvailable( 2025 ); +false + +# +# 'number' where 'available' does not report the count, and 'count' where the +# layer knows better than to construct the groups. +# +gap> SmallGroupsAddLayer( rec( +> name := "counted", +> available := function( order ) +> if order = 2040 then +> return rec(); +> fi; +> return fail; +> end, +> group := { order, i, inforec } -> CyclicGroup( order ), +> number := { order, inforec } -> 7, +> count := { order, funcs, vals, inforec, idList } -> 42 ) ); +gap> NumberSmallGroups( 2040 ); +7 +gap> NumberSmallGroups( 2040, IsAbelian, true ); +42 + +# a layer has to report the number one way or the other +gap> SmallGroupsAddLayer( rec( +> name := "countless", +> available := function( order ) +> if order = 2064 then +> return rec(); +> fi; +> return fail; +> end, +> group := ReturnFail ) ); +gap> NumberSmallGroups( 2064 ); +Error, layer countless reports no number of groups of order 2064 + +# +# A layer that selects for itself: no generic count is installed over it, so +# the counting goes through its own selection. +# +gap> SmallGroupsAddLayer( rec( +> name := "selective", +> available := function( order ) +> if order = 2052 then +> return rec( number := 2 ); +> fi; +> return fail; +> end, +> group := { order, i, inforec } -> CyclicGroup( order ), +> select := function( order, funcs, vals, inforec, all, id, idList ) +> if not all then +> return CyclicGroup( order ); +> elif id then +> return [ [ order, 1 ], [ order, 2 ] ]; +> fi; +> return [ CyclicGroup( order ), CyclicGroup( order ) ]; +> end ) ); +gap> IsBound( SMALL_GROUPS_LAYERS.selective.count ); +false +gap> IdsOfAllSmallGroups( 2052, IsAbelian, true ); +[ [ 2052, 1 ], [ 2052, 2 ] ] +gap> NumberSmallGroups( 2052, IsAbelian, true ); +2 +gap> IsCyclic( OneSmallGroup( 2052, IsAbelian, true ) ); +true + +# +# 'available' has to return 'fail' or a record +# +gap> SmallGroupsAddLayer( rec( +> name := "confused", +> available := function( order ) +> if order = 2058 then +> return 42; +> fi; +> return fail; +> end, +> group := ReturnFail ) ); +gap> SmallGroupsAvailable( 2058 ); +Error, the 'available' function of layer confused must return 'fail' or a reco\ +rd + +# +# A layer added the old way, by filling the arrays directly, is picked up by +# the layer "SmallGrp": it needs no slot of its own here, and registering a +# further layer leaves it where it is. +# +gap> SMALL_AVAILABLE_FUNCS[ Length( SMALL_AVAILABLE_FUNCS ) + 1 ] := +> function( order ) +> if order <> 2079 then +> return fail; +> fi; +> return rec( lib := 12, func := 1000, number := 9 ); +> end;; +gap> NumberSmallGroups( 2079 ); +9 +gap> SMALL_AVAILABLE( 2079 ).layer.name; +"SmallGrp" +gap> claim( "epsilon", 8, rec() ); +gap> NumberSmallGroups( 2079 ); +9 +gap> NumberSmallGroups( 2025 ); +4 + +# a wish is met wherever the layers sit +gap> claim( "zeta", 9, rec( before := [ "beta" ] ) ); +gap> List( SMALL_GROUPS_LAYER_LIST, l -> l.name ); +[ "SmallGrp", "two of them", "delta", "counted", "countless", "selective", + "confused", "epsilon", "zeta", "beta", "gamma", "alpha" ] +gap> NumberSmallGroups( 2079 ); +9 + +# +# a layer may put itself in front of this library, and then answers for an +# order this library covers +# +gap> NumberSmallGroups( 96 ); +231 +gap> SmallGroupsAddLayer( rec( +> name := "in front", +> before := [ "SmallGrp" ], +> available := function( order ) +> if order <> 96 then +> return fail; +> fi; +> return rec( number := 1 ); +> end, +> group := { order, i, inforec } -> CyclicGroup( order ) ) ); +gap> Position( SMALL_GROUPS_LAYER_LIST, SMALL_GROUPS_LAYERS.("in front") ) +> < Position( SMALL_GROUPS_LAYER_LIST, SMALL_GROUPS_LAYERS.SmallGrp ); +true +gap> NumberSmallGroups( 96 ); +1 + +# +# 'idAvailable' where the identification covers fewer orders than the groups +# +gap> SmallGroupsAddLayer( rec( +> name := "half identified", +> available := function( order ) +> if order in [ 2072, 2080 ] then +> return rec( number := 1 ); +> fi; +> return fail; +> end, +> group := { order, i, inforec } -> CyclicGroup( order ), +> idAvailable := function( order ) +> if order = 2072 then +> return rec(); +> fi; +> return fail; +> end, +> id := { G, idrec } -> 1 ) ); +gap> List( [ 2072, 2080 ], SmallGroupsAvailable ); +[ true, true ] +gap> List( [ 2072, 2080 ], IdGroupsAvailable ); +[ true, false ] +gap> IdGroup( CyclicGroup( 2072 ) ); +[ 2072, 1 ] + +# on its own it identifies nothing +gap> SmallGroupsAddLayer( rec( name := "no id", available := ReturnFail, +> group := ReturnFail, +> idAvailable := ReturnFail ) ); +Error, .idAvailable is of no use without .id + +# +# put the library back as it was, so the stand-ins do not follow the rest of +# the tests around +# +gap> SMALL_GROUPS_LAYERS := rec( SmallGrp := saved.layers[1] );; +gap> SMALL_GROUPS_LAYER_LIST := saved.layers;; +gap> SMALL_AVAILABLE_FUNCS := saved.avail;; +gap> List( [ 2016, 2025, 2040, 2052, 2064, 2072 ], SmallGroupsAvailable ); +[ false, false, false, false, false, false ] +gap> NumberSmallGroups( 96 ); +231 + +# +gap> STOP_TEST( "addlayer.tst", 1);