From 857e552fcfa9ba54ea3d48268f49aca6cf4ad428 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sat, 15 Aug 2026 18:46:55 +0200 Subject: [PATCH 1/3] Fix `SimpleGroupsIterator` for start values just below 10^18 The orders of the non-PSL(2,q) simple groups come in two lists, the second loaded on demand. The iterator asked for it once `start` reached 10^18, but the first list ends already at 911215823217986880, so for a start value between the two nothing was loaded, the search for the first relevant entry returned `fail`, and building the iterator broke on indexing the list with it. Load the second list when the search comes up empty instead, which is what `NextIterator_SimGp` already does, and report an order beyond the documented range rather than indexing with `fail` -- that case failed the same way above the second list. Assistance from Claude Code (Claude Opus 5): diagnosis from a stack trace, the fix, and the regression test. Co-authored-by: Claude Opus 5 --- grp/simple.gi | 15 +++++++++++++-- .../2026-08-15-SimpleGroupsIterator.tst | 12 ++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst diff --git a/grp/simple.gi b/grp/simple.gi index babad7ffd6..7915e8b7ba 100644 --- a/grp/simple.gi +++ b/grp/simple.gi @@ -796,8 +796,19 @@ InstallGlobalFunction(SimpleGroupsIterator,function(arg) stack:=a[3]; a:=a[2]; until SizeL2Q(b)>=start; - if start>=10^18 then LOADSIMPLE2(); fi; - pos:=First([1..Length(SIMPLEGPSNONL2)],x->SIMPLEGPSNONL2[x][1]>=start); + # Running off the end of the first list is what says the second one is + # needed. Testing `start' against a fixed bound instead left a window between + # the largest order in the first list and 10^18 in which nothing was loaded + # and `pos' stayed `fail'. + pos:=PositionProperty(SIMPLEGPSNONL2,x->x[1]>=start); + if pos=fail then + LOADSIMPLE2(); + pos:=PositionProperty(SIMPLEGPSNONL2,x->x[1]>=start); + if pos=fail then + Error("List of simple groups only available up to order ", + SIMPLE_GROUPS_ITERATOR_RANGE); + fi; + fi; return IteratorByFunctions(rec( IsDoneIterator:=IsDoneIterator_SimGp, NextIterator:=NextIterator_SimGp, diff --git a/tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst b/tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst new file mode 100644 index 0000000000..87c0c6e8e5 --- /dev/null +++ b/tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst @@ -0,0 +1,12 @@ +# The orders of the non-PSL(2,q) simple groups come in two lists, the second +# loaded on demand. `SimpleGroupsIterator' asked for it when `start' reached +# 10^18, but the first list ends at 911215823217986880; in between nothing was +# loaded, `pos' stayed `fail', and building the iterator broke on indexing the +# list with it. +gap> it := SimpleGroupsIterator(10^18-1, 10^18-1);; +gap> IsDoneIterator(it); +true +gap> it := SimpleGroupsIterator(10^18-1, 11*10^17 : NOPSL2);; +gap> l := [];; for g in it do Add(l, g); od; +gap> List(l, Size); +[ 1053927211015007280 ] From c9e6341391add92edff7721c69c9047e63ad7365 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 16 Aug 2026 13:03:30 +0200 Subject: [PATCH 2/3] Address review: comment, early range check, documentation Say what the code does rather than what changed about it. Look the order up before searching for the PSL(2,q) order to start from, so that one beyond the range is rejected at once instead of after a search that takes minutes -- which is what makes it cheap to test. Shorten the message accordingly: with the 28-digit bound the old one wrapped, and matching that in a test needs a trailing space, which `Test` does not normalise away. The wording now matches what `lib/grp.gi` says for the same condition. The documentation claimed the iteration runs up to 10^27 or to `end`, which was wrong for an `end` beyond 10^27. Assistance from Claude Code (Claude Opus 5). Co-authored-by: Claude Opus 5 --- grp/simple.gd | 6 +++-- grp/simple.gi | 26 +++++++++---------- .../2026-08-15-SimpleGroupsIterator.tst | 5 ++++ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/grp/simple.gd b/grp/simple.gd index c6b1c556a7..3191addabe 100644 --- a/grp/simple.gd +++ b/grp/simple.gd @@ -76,8 +76,10 @@ DeclareGlobalFunction("EpimorphismFromClassical"); ## ## ## This function returns an iterator that will run over all nonabelian simple groups, starting -## at order start if specified, up to order 10^{27} (or -- if specified -## -- order end). If the option NOPSL2 is given, groups of type +## at order start if specified, and stopping at order end if specified. +## Only orders up to 10^{27} are available, and asking for more is an error, +## whether through start or by iterating that far. +## If the option NOPSL2 is given, groups of type ## PSL_2(q) are omitted. ## it:=SimpleGroupsIterator(20000); diff --git a/grp/simple.gi b/grp/simple.gi index 7915e8b7ba..d1f9fab036 100644 --- a/grp/simple.gi +++ b/grp/simple.gi @@ -787,6 +787,19 @@ InstallGlobalFunction(SimpleGroupsIterator,function(arg) fi; nopsl2:=ValueOption("NOPSL2")=true or ValueOption("nopsl2")=true; + # The non-L2 orders come in two lists, the second loaded on demand. + # We do so early, so that a `start' beyond the data is rejected at + # once rather than after searching for it. + pos:=PositionProperty(SIMPLEGPSNONL2,x->x[1]>=start); + if pos=fail then + LOADSIMPLE2(); + pos:=PositionProperty(SIMPLEGPSNONL2,x->x[1]>=start); + if pos=fail then + Error("simple groups of order > ",SIMPLE_GROUPS_ITERATOR_RANGE, + " are not available"); + fi; + fi; + # find relevant L2 order a:=RootInt(start,3)-1; stack:=fail; @@ -796,19 +809,6 @@ InstallGlobalFunction(SimpleGroupsIterator,function(arg) stack:=a[3]; a:=a[2]; until SizeL2Q(b)>=start; - # Running off the end of the first list is what says the second one is - # needed. Testing `start' against a fixed bound instead left a window between - # the largest order in the first list and 10^18 in which nothing was loaded - # and `pos' stayed `fail'. - pos:=PositionProperty(SIMPLEGPSNONL2,x->x[1]>=start); - if pos=fail then - LOADSIMPLE2(); - pos:=PositionProperty(SIMPLEGPSNONL2,x->x[1]>=start); - if pos=fail then - Error("List of simple groups only available up to order ", - SIMPLE_GROUPS_ITERATOR_RANGE); - fi; - fi; return IteratorByFunctions(rec( IsDoneIterator:=IsDoneIterator_SimGp, NextIterator:=NextIterator_SimGp, diff --git a/tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst b/tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst index 87c0c6e8e5..37d5cf4362 100644 --- a/tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst +++ b/tst/testbugfix/2026-08-15-SimpleGroupsIterator.tst @@ -10,3 +10,8 @@ gap> it := SimpleGroupsIterator(10^18-1, 11*10^17 : NOPSL2);; gap> l := [];; for g in it do Add(l, g); od; gap> List(l, Size); [ 1053927211015007280 ] + +# An order beyond the data is rejected when the iterator is built, before the +# search for the PSL(2,q) order to start from. +gap> SimpleGroupsIterator(10^28, 10^28); +Error, simple groups of order > 1000000000000000000000000000 are not available From 99594810615f786662cfb9c7d4013b49459878d2 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 16 Aug 2026 14:18:59 +0200 Subject: [PATCH 3/3] tweak --- grp/simple.gd | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/grp/simple.gd b/grp/simple.gd index 3191addabe..0dc0bd1008 100644 --- a/grp/simple.gd +++ b/grp/simple.gd @@ -77,9 +77,8 @@ DeclareGlobalFunction("EpimorphismFromClassical"); ## ## This function returns an iterator that will run over all nonabelian simple groups, starting ## at order start if specified, and stopping at order end if specified. -## Only orders up to 10^{27} are available, and asking for more is an error, -## whether through start or by iterating that far. -## If the option NOPSL2 is given, groups of type +## Only orders up to 10^{27} are available. Attempting to iterate beyond that +## results in an error. If the option NOPSL2 is given, groups of type ## PSL_2(q) are omitted. ## it:=SimpleGroupsIterator(20000);