44package main
55
66import (
7+ "bufio"
78 "encoding/binary"
89 "flag"
910 "fmt"
@@ -15,6 +16,7 @@ import (
1516 "runtime"
1617 "runtime/debug"
1718 "runtime/pprof"
19+ "strconv"
1820 "strings"
1921 "sync"
2022 "time"
@@ -25,29 +27,32 @@ import (
2527
2628var (
2729 // Flags
28- cpuprofile string // write CPU profile to this file
29- pslqVerbose bool // verbose flag for PSLQ
30- pslqIterations int // iterations for PSLQ
31- pslqPrecision uint // precision to do PSLQ in
32- pslqLogMaxCoeff uint // log(max coefficient size) for PSLQ
33- pslqTargetPrecision float64 // PSLQ target precision of the result as a fraction of prec
34- workers int // Number of workers for PSLQ and search
35- pslqAlgorithm int // PSLQ algorithm
36- pslqMaxItems int // output no more than this many entries per PSLQ file
37- alpha uint32 // Don't output arctan terms bigger than 1/alpha
38- maxPrime uint32 // maximum prime factor to consider for groups
39- pslqMinItems int // minimum number of items required for a PSLQ file
40- minGroupLength int // only find groups with this many members, min
41- maxGroupLength int // only find groups with this many members, max
42- maxX uint32 // max value of x when in arctan 1/x term
43- arctanCacheSize int // cache this many arctan values
44- groupCacheSize int // cache this many group values
45- pslqMaxFails int // stop PSLQ with this many failures in a row
46- pslqMaxNoNew int // stop PSLQ if no new formulae for this long
47- pslqTrimStart bool // if set, trim PSLQ norms from the start (larger fractions)
48- maxLehmerMeasure float64 // don't print if worse than this
49- searchX uint32 // if set, linear search formulae from 1/searchX up
50- pslqSkipZeros bool // if set skip PSLQ on any relations which contain zero relations
30+ cpuprofile string // write CPU profile to this file
31+ pslqVerbose bool // verbose flag for PSLQ
32+ pslqIterations int // iterations for PSLQ
33+ pslqPrecision uint // precision to do PSLQ in
34+ pslqLogMaxCoeff uint // log(max coefficient size) for PSLQ
35+ pslqTargetPrecision float64 // PSLQ target precision of the result as a fraction of prec
36+ workers int // Number of workers for PSLQ and search
37+ pslqAlgorithm int // PSLQ algorithm
38+ pslqMaxItems int // output no more than this many entries per PSLQ file
39+ alpha uint32 // Don't output arctan terms bigger than 1/alpha
40+ maxPrime uint32 // maximum prime factor to consider for groups
41+ pslqMinItems int // minimum number of items required for a PSLQ file
42+ minGroupLength int // only find groups with this many members, min
43+ maxGroupLength int // only find groups with this many members, max
44+ maxX uint32 // max value of x when in arctan 1/x term
45+ arctanCacheSize int // cache this many arctan values
46+ groupCacheSize int // cache this many group values
47+ pslqMaxFails int // stop PSLQ with this many failures in a row
48+ pslqMaxNoNew int // stop PSLQ if no new formulae for this long
49+ pslqTrimStart bool // if set, trim PSLQ norms from the start (larger fractions)
50+ maxLehmerMeasure float64 // don't print if worse than this
51+ searchX uint32 // if set, linear search formulae from 1/searchX up
52+ pslqSkipZeros bool // if set skip PSLQ on any relations which contain zero relations
53+ fixedGroupString string // a comma or space separated string for a prime group to use
54+ groupsFile string // a file of comma or spare seperated groups, one per line
55+ fixedGroups [][]uint32 // parsed version of the above
5156
5257 // Globals
5358 arctanCache * lru.Cache [uint32 , * big.Float ]
@@ -94,6 +99,8 @@ func init() {
9499 flag .BoolVar (& pslqSkipZeros , "skip-zeros" , false , "if set skip PSLQ on any relations which contain previously found zero relations" )
95100 flag .Float64Var (& maxLehmerMeasure , "max-lehmer-measure" , 0.0 , "Don't print formulae with a Lehmer measure greater than this" )
96101 Uint32Var (& searchX , "search-x" , 0 , "If set do a linear search of terms from this value up" )
102+ flag .StringVar (& fixedGroupString , "group" , "" , "A comma or space separated list of primes to use as the prime group" )
103+ flag .StringVar (& groupsFile , "groups-file" , "" , "a file of comma or spare seperated groups, one per line" )
97104}
98105
99106func showParameters () {
@@ -575,7 +582,15 @@ func processPrimeGroups(norms []*Norm, primeMap map[uint32][]*Norm, primesToProc
575582 go processPrimeGroupsWorker (primeMap , ch , & wg )
576583 }
577584
578- if searchX == 0 {
585+ if len (fixedGroups ) != 0 {
586+ for _ , group := range fixedGroups {
587+ statsMu .Lock ()
588+ currentGroup = group
589+ groupsChecked ++
590+ statsMu .Unlock ()
591+ ch <- group
592+ }
593+ } else if searchX == 0 {
579594 for length := minGroupLength ; length <= maxGroupLength ; length ++ {
580595 Printf ("\n --- Processing groups of length %d ---\n " , length )
581596 for group := range Combinations (primesToProcess , length ) {
@@ -638,13 +653,99 @@ func run() error {
638653 return processPrimeGroups (norms , primeMap , primesToProcess )
639654}
640655
656+ // parseUint32List parses a string containing space or comma or both
657+ // separated integers into a slice of uint32.
658+ // It returns the slice of parsed integers and any error encountered during parsing.
659+ func parseUint32List (input string ) ([]uint32 , error ) {
660+ // Replace commas with spaces to handle both delimiters consistently.
661+ replaced := strings .ReplaceAll (input , "," , " " )
662+
663+ // Split the string by spaces.
664+ parts := strings .Fields (replaced )
665+
666+ result := make ([]uint32 , 0 , len (parts ))
667+
668+ for _ , part := range parts {
669+ if part == "" {
670+ continue // Skip empty strings resulting from multiple spaces or commas.
671+ }
672+ val , err := strconv .ParseUint (part , 10 , 32 )
673+ if err != nil {
674+ return nil , err
675+ }
676+ result = append (result , uint32 (val ))
677+ }
678+
679+ return result , nil
680+ }
681+
682+ // loadGroups reads a file of prime groups, parses each line as a space or comma
683+ // separated list of integers, and returns a slice of slices of uint32.
684+ func loadGroups (fileName string ) ([][]uint32 , error ) {
685+ file , err := os .Open (fileName )
686+ if err != nil {
687+ return nil , fmt .Errorf ("failed to open file %q: %w" , fileName , err )
688+ }
689+ defer file .Close ()
690+
691+ var groups [][]uint32
692+ scanner := bufio .NewScanner (file )
693+ for scanner .Scan () {
694+ line := scanner .Text ()
695+ if line == "" {
696+ continue // Skip empty lines
697+ }
698+ group , err := parseUint32List (line )
699+ if err != nil {
700+ return nil , fmt .Errorf ("failed to parse line %q: %w" , line , err )
701+ }
702+ groups = append (groups , group )
703+ }
704+
705+ if err := scanner .Err (); err != nil {
706+ return nil , fmt .Errorf ("failed to read file %q: %w" , fileName , err )
707+ }
708+
709+ return groups , nil
710+ }
711+
641712func main () {
642713 var err error
643714 flag .Parse ()
644715
645716 Printf ("Starting %s with parameters %v\n \n " , os .Args [0 ], os .Args [1 :])
646717 showParameters ()
647718
719+ if fixedGroupString != "" {
720+ fixedGroup , err := parseUint32List (fixedGroupString )
721+ if err != nil {
722+ log .Fatalf ("Failed to parse -group %q: %v" , fixedGroupString , err )
723+ }
724+ fixedGroups = [][]uint32 {fixedGroup }
725+ Printf ("Using fixed prime group %v\n " , fixedGroup )
726+ } else if groupsFile != "" {
727+ fixedGroups , err = loadGroups (groupsFile )
728+ if err != nil {
729+ log .Fatalf ("Failed to parse -groups-file %q: %v" , groupsFile , err )
730+ }
731+ Printf ("Using %d fixed prime groups from file %q\n " , len (fixedGroups ), groupsFile )
732+ }
733+ // Filter out lines bigger than maxPrime
734+ if len (fixedGroups ) != 0 {
735+ newFixedGroups := fixedGroups [:0 ]
736+ GROUPS:
737+ for _ , group := range fixedGroups {
738+ for _ , p := range group {
739+ if p > maxPrime {
740+ Printf ("Warning: dropping %v from fixed group as %d > %d (-max-prime)\n " , group , p , maxPrime )
741+ continue GROUPS
742+ }
743+ }
744+ newFixedGroups = append (newFixedGroups , group )
745+ }
746+ fixedGroups = newFixedGroups
747+ }
748+
648749 // Do CPU profiling if requested
649750 if cpuprofile != "" {
650751 f , err := os .Create (cpuprofile )
0 commit comments