From 632028bbcfb89188f6d7034decf65ff75386a761 Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Mon, 12 Oct 2026 22:43:30 +0200 Subject: [PATCH 1/2] Use smaller exponents when norming and sifting Choose residues in (-r/2, r/2] at finite depths. Assisted-by: Claude Code (Fable 5.1) --- doc/defins.xml | 4 ++-- gap/basic/pcpelms.gi | 15 +++++++++++++-- gap/basic/pcppara.gi | 8 +++++--- gap/basic/pcppcps.gi | 12 +++++++----- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/doc/defins.xml b/doc/defins.xml index df937d22..0bd9c9ca 100644 --- a/doc/defins.xml +++ b/doc/defins.xml @@ -247,8 +247,8 @@ its relative index. For each pcp-element g there exists an integer - returns an integer e, either positive or -1, such that the pcp-element - g raised to the power of e is normed. + returns an integer e such that the pcp-element g raised to the + power of e is normed. diff --git a/gap/basic/pcpelms.gi b/gap/basic/pcpelms.gi index 8f6e4bad..3ee0f56f 100644 --- a/gap/basic/pcpelms.gi +++ b/gap/basic/pcpelms.gi @@ -217,6 +217,17 @@ function( g ) return o; end ); +############################################################################# +## +#F SmallestResidue( q, r ) . . . . . . . q mod r in (-r/2, r/2], q if r = 0 +## +BindGlobal( "SmallestResidue", function( q, r ) + if r = 0 then return q; fi; + q := q mod r; + if 2*q > r then q := q - r; fi; + return q; +end ); + ############################################################################# ## #F NormingExponent( g ) . . . . . . . . .returns f such that g^f is normed @@ -234,10 +245,10 @@ BindGlobal( "NormingExponent", function( g ) elif r = 0 then return 1; elif IsPrime( r ) then - return l^-1 mod r; + return SmallestResidue( l^-1, r ); else e := Gcdex( r, l ); # = RelativeIndex - return e.coeff2 mod r; # l * c2 = e mod r + return SmallestResidue( e.coeff2, r ); # l * c2 = e mod r fi; end ); diff --git a/gap/basic/pcppara.gi b/gap/basic/pcppara.gi index 33518d8a..5c6c48c7 100644 --- a/gap/basic/pcppara.gi +++ b/gap/basic/pcppara.gi @@ -28,10 +28,11 @@ end ); ## Apply GcdPcp( g, h ), and apply the same operations on i and j. ## BindGlobal( "GcdPcpPara", function(g, h, i, j) - local x, y, a, b, q, r, t, z, w, u; + local x, y, a, b, q, r, t, z, w, u, rel; x := g; y := h; + rel := FactorOrder(g); a := LeadingExponent(x); b := LeadingExponent(y); @@ -52,6 +53,8 @@ BindGlobal( "GcdPcpPara", function(g, h, i, j) while b <> 0 do q := QuoInt(a, b); + r := a - q * b; + q := SmallestResidue(q, rel); t := x * y ^ -q; x := y; @@ -61,7 +64,6 @@ BindGlobal( "GcdPcpPara", function(g, h, i, j) z := w; w := u; - r := a - q * b; a := b; b := r; od; @@ -183,7 +185,7 @@ function( pcs, gens, ppcs, pgens ) b := LeadingExponent(h); if a > 0 and b > 0 and a mod b = 0 then # GcdPcpPara would leave h and hh unchanged. - q := a/b; + q := SmallestResidue(a/b, rels[d]); g := g * h^-q; gg := gg * hh^-q; else diff --git a/gap/basic/pcppcps.gi b/gap/basic/pcppcps.gi index 7520fda8..794011ef 100644 --- a/gap/basic/pcppcps.gi +++ b/gap/basic/pcppcps.gi @@ -101,13 +101,15 @@ IGSValFun := IGSValFun4; ## ## For two non-trivial PcpElements g and h with the same depth, apply the ## Euclidean algorithm to the leading exponents, and do the same operations -## on the elements themselves. +## on the elements themselves. At a depth of finite relative order the +## quotients are taken modulo that order. ## BindGlobal( "GcdPcp", function(g, h) - local x, y, a, b, q, r, t; + local x, y, a, b, q, r, t, rel; x := g; y := h; + rel := FactorOrder(g); a := LeadingExponent(x); b := LeadingExponent(y); @@ -123,12 +125,12 @@ BindGlobal( "GcdPcp", function(g, h) while b <> 0 do q := QuoInt(a, b); + r := a - q * b; - t := x * y ^ -q; + t := x * y ^ -SmallestResidue(q, rel); x := y; y := t; - r := a - q * b; a := b; b := r; od; @@ -190,7 +192,7 @@ InstallGlobalFunction(AddToIgs, function(igs, gens) b := LeadingExponent(h); if a > 0 and b > 0 and a mod b = 0 then # GcdPcp would leave h unchanged. - g := g * h^(-a/b); + g := g * h^-SmallestResidue(a/b, rels[d]); else pair := GcdPcp(g, h); h := pair[1]; From 43db182a27b93b7ccc6cf2699c57780de695be8a Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Sat, 17 Oct 2026 13:50:26 +0200 Subject: [PATCH 2/2] Update docs & changelog --- CHANGES.md | 3 +++ doc/defins.xml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index cc42fb15..360f48a8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -40,6 +40,9 @@ This file describes changes in the GAP package 'polycyclic'. - Allow a finite group fallback for `RepresentativeAction` and `IsConjugate` for subgroups - Optimise `NormalIntersection` +- Avoid needlessly large powers when norming and sifting elements at a depth + of finite relative order; this also changes which power `NormedPcpElement` + returns - Make more use of `NC` versions of functions, and avoid the use of generic `PreImage` and `Image` dispatchers to improve speed and memory usage - Implement a method for sorting pcp-subgroups, which prevents hangs when creating diff --git a/doc/defins.xml b/doc/defins.xml index 0bd9c9ca..7bdca757 100644 --- a/doc/defins.xml +++ b/doc/defins.xml @@ -249,6 +249,9 @@ its relative index. For each pcp-element g there exists an integer returns an integer e such that the pcp-element g raised to the power of e is normed. + For non-trivial g with factor order r, we have + -r/2 < e \leq r/2 if r > 0, and + e \in \{-1,1\} if r = 0.