From fac54ec438795ba098e83d205d6b297002127186 Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:25:27 +0000 Subject: [PATCH 1/8] move factor_with_ecm into util --- rsa/factor_with_ecm.go | 50 +++------------------------------- util/factor_with_ecm.go | 59 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 46 deletions(-) create mode 100644 util/factor_with_ecm.go diff --git a/rsa/factor_with_ecm.go b/rsa/factor_with_ecm.go index 144bd51..cb78860 100644 --- a/rsa/factor_with_ecm.go +++ b/rsa/factor_with_ecm.go @@ -1,13 +1,12 @@ package rsa import ( - "bufio" "fmt" "log" "math/big" "os/exec" "ret/theme" - "strings" + "ret/util" "sync" ) @@ -71,54 +70,13 @@ func scriptFactorECMManyFactors(cmd string, factors []*big.Int, n *big.Int, e *b fmt.Print(script) } func factorWithECM(strategy *Strategy, n *big.Int) { - cmd := exec.Command("/usr/bin/ecm", "-c", "1000000000", "-one", "2000") + factors, cmdStr, err := util.FactorWithECM(n) - stdin, err := cmd.StdinPipe() if err != nil { log.Printf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) return } - stdout, err := cmd.StdoutPipe() - if err != nil { - log.Printf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) - return - } - - factors := make([]*big.Int, 0) - - cofactor := new(big.Int).Set(n) - - stdin.Write([]byte(fmt.Sprintf("%s\n", cofactor))) - - cmd.Start() - - scanner := bufio.NewScanner(stdout) - - for scanner.Scan() { - line := scanner.Text() - - splits := strings.Split(line, " ") - - if strings.Contains(line, "Found prime factor of ") { - factor, _ := new(big.Int).SetString(splits[len(splits)-1], 10) - factors = append(factors, factor) - continue - } - - if strings.Contains(line, "Composite cofactor") { - cofactor.SetString(splits[2], 10) - stdin.Write([]byte(fmt.Sprintf("%s\n", cofactor))) - continue - } - - if strings.Contains(line, "Prime cofactor") { - factor, _ := new(big.Int).SetString(splits[2], 10) - factors = append(factors, factor) - break - } - } - if len(factors) == 2 { // special case for N = p * q where p and q and two distinct primes p := factors[0] @@ -135,7 +93,7 @@ func factorWithECM(strategy *Strategy, n *big.Int) { continue } - scriptFactorECM(cmd.String(), p, q, n, e, c, mBytes) + scriptFactorECM(cmdStr, p, q, n, e, c, mBytes) } } } else { @@ -156,7 +114,7 @@ func factorWithECM(strategy *Strategy, n *big.Int) { continue } - scriptFactorECMManyFactors(cmd.String(), factors, n, e, c, mBytes) + scriptFactorECMManyFactors(cmdStr, factors, n, e, c, mBytes) } } } diff --git a/util/factor_with_ecm.go b/util/factor_with_ecm.go new file mode 100644 index 0000000..7743a1b --- /dev/null +++ b/util/factor_with_ecm.go @@ -0,0 +1,59 @@ +package util + +import ( + "bufio" + "fmt" + "math/big" + "os/exec" + "strings" +) + +func FactorWithECM(n *big.Int) ([]*big.Int, string, error) { + cmd := exec.Command("/usr/bin/ecm", "-c", "1000000000", "-one", "2000") + + stdin, err := cmd.StdinPipe() + if err != nil { + return nil, cmd.String(), err + } + + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, cmd.String(), err + } + + factors := make([]*big.Int, 0) + + cofactor := new(big.Int).Set(n) + + stdin.Write([]byte(fmt.Sprintf("%s\n", cofactor))) + + cmd.Start() + + scanner := bufio.NewScanner(stdout) + + for scanner.Scan() { + line := scanner.Text() + + splits := strings.Split(line, " ") + + if strings.Contains(line, "Found prime factor of ") { + factor, _ := new(big.Int).SetString(splits[len(splits)-1], 10) + factors = append(factors, factor) + continue + } + + if strings.Contains(line, "Composite cofactor") { + cofactor.SetString(splits[2], 10) + stdin.Write([]byte(fmt.Sprintf("%s\n", cofactor))) + continue + } + + if strings.Contains(line, "Prime cofactor") { + factor, _ := new(big.Int).SetString(splits[2], 10) + factors = append(factors, factor) + break + } + } + + return factors, cmd.String(), nil +} From ddd7df17afbc79f16022bfa26c4ff61b4e509c1c Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:34:46 +0000 Subject: [PATCH 2/8] use util.FactorWithECM in the factor command --- commands/factor.go | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/commands/factor.go b/commands/factor.go index 6e49784..1f5608e 100644 --- a/commands/factor.go +++ b/commands/factor.go @@ -8,6 +8,7 @@ import ( "ret/util" "strings" "sync" + "time" ) var ( @@ -63,15 +64,18 @@ func parseFactorArgs(args []string) { } func Factor(args []string) { + startTime := time.Now() + parseFactorArgs(args) var wg sync.WaitGroup for _, n := range N { - wg.Add(1) + wg.Add(2) go func() { defer wg.Done() + factors, url, err := util.FactorDB(n) if err != nil { log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) @@ -85,7 +89,34 @@ func Factor(args []string) { return } - fmt.Printf("%v\n%v\n", factors, url) + diff := time.Now().Sub(startTime) + + fmt.Printf(theme.ColorGreen+"[factordb]"+theme.ColorReset+" 🪓 in "+ + theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", + diff, url, factors) + }() + + go func() { + defer wg.Done() + + factors, cmdStr, err := util.FactorWithECM(n) + if err != nil { + log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) + } + + if factors == nil { + return + } + + if len(factors) == 0 { + return + } + + diff := time.Now().Sub(startTime) + + fmt.Printf(theme.ColorGreen+"[ecm]"+theme.ColorReset+" 🪓 in "+ + theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", + diff, cmdStr, factors) }() } From 11f4e0ab29383daeb2808ca17738c0a826827d12 Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:40:57 +0000 Subject: [PATCH 3/8] move factor_with_pari to util --- rsa/factor_with_pari.go | 47 +++------------------------------ util/factor_with_pari.go | 57 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 43 deletions(-) create mode 100644 util/factor_with_pari.go diff --git a/rsa/factor_with_pari.go b/rsa/factor_with_pari.go index ecfc10a..482a3c0 100644 --- a/rsa/factor_with_pari.go +++ b/rsa/factor_with_pari.go @@ -1,15 +1,12 @@ package rsa import ( - "bufio" "fmt" "log" "math/big" - "os" "os/exec" "ret/theme" - "strconv" - "strings" + "ret/util" "sync" ) @@ -73,49 +70,13 @@ func scriptFactorPariManyFactors(cmd string, factors []*big.Int, n *big.Int, e * fmt.Print(script) } func factorWithPari(strategy *Strategy, n *big.Int) { - file, err := os.CreateTemp("", "ret_rsa_factorme") + factors, cmdStr, err := util.FactorWithPari(n) - fmt.Fprintf(file, "print(factorint(%s))\n", n) - - file.Close() - - cmd := exec.Command("/usr/bin/gp", "--stacksize", "1073741824", "--fast", "--quiet", file.Name()) - - stdout, err := cmd.StdoutPipe() if err != nil { log.Printf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) return } - factors := make([]*big.Int, 0) - - cmd.Start() - - scanner := bufio.NewScanner(stdout) - - if !scanner.Scan() { - return - } - - line := scanner.Text() - splits := strings.Split(line[1:len(line)-1], ";") - - for _, split := range splits { - nums := strings.Split(split, ",") - - count, err := strconv.Atoi(strings.TrimSpace(nums[1])) - if err != nil { - log.Printf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) - return - } - - factor, _ := new(big.Int).SetString(strings.TrimSpace(nums[0]), 10) - - for range count { - factors = append(factors, factor) - } - } - if len(factors) == 2 { // special case for N = p * q where p and q and two distinct primes p := factors[0] @@ -132,7 +93,7 @@ func factorWithPari(strategy *Strategy, n *big.Int) { continue } - scriptFactorPari(cmd.String(), p, q, n, e, c, mBytes) + scriptFactorPari(cmdStr, p, q, n, e, c, mBytes) } } } else { @@ -153,7 +114,7 @@ func factorWithPari(strategy *Strategy, n *big.Int) { continue } - scriptFactorPariManyFactors(cmd.String(), factors, n, e, c, mBytes) + scriptFactorPariManyFactors(cmdStr, factors, n, e, c, mBytes) } } } diff --git a/util/factor_with_pari.go b/util/factor_with_pari.go new file mode 100644 index 0000000..81f9000 --- /dev/null +++ b/util/factor_with_pari.go @@ -0,0 +1,57 @@ +package util + +import ( + "bufio" + "fmt" + "math/big" + "os" + "os/exec" + "strconv" + "strings" +) + +func FactorWithPari(n *big.Int) ([]*big.Int, string, error) { + + file, err := os.CreateTemp("", "ret_rsa_factorme") + + fmt.Fprintf(file, "print(factorint(%s))\n", n) + + file.Close() + + cmd := exec.Command("/usr/bin/gp", "--stacksize", "1073741824", "--fast", "--quiet", file.Name()) + + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, cmd.String(), err + } + + factors := make([]*big.Int, 0) + + cmd.Start() + + scanner := bufio.NewScanner(stdout) + + if !scanner.Scan() { + return nil, cmd.String(), err + } + + line := scanner.Text() + splits := strings.Split(line[1:len(line)-1], ";") + + for _, split := range splits { + nums := strings.Split(split, ",") + + count, err := strconv.Atoi(strings.TrimSpace(nums[1])) + if err != nil { + return nil, cmd.String(), err + } + + factor, _ := new(big.Int).SetString(strings.TrimSpace(nums[0]), 10) + + for range count { + factors = append(factors, factor) + } + } + + return factors, cmd.String(), nil +} From 8217df339e49aa049cbf9e9dcae379161ceac2f5 Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:43:57 +0000 Subject: [PATCH 4/8] use util.FactorWithPari with the factor command --- commands/factor.go | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/commands/factor.go b/commands/factor.go index 1f5608e..96a9283 100644 --- a/commands/factor.go +++ b/commands/factor.go @@ -71,7 +71,7 @@ func Factor(args []string) { var wg sync.WaitGroup for _, n := range N { - wg.Add(2) + wg.Add(1) go func() { defer wg.Done() @@ -91,10 +91,14 @@ func Factor(args []string) { diff := time.Now().Sub(startTime) - fmt.Printf(theme.ColorGreen+"[factordb]"+theme.ColorReset+" 🪓 in "+ + fmt.Printf(theme.ColorGreen+"🪓 [factordb]"+theme.ColorReset+" in "+ theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", diff, url, factors) }() + } + + for _, n := range N { + wg.Add(1) go func() { defer wg.Done() @@ -114,7 +118,34 @@ func Factor(args []string) { diff := time.Now().Sub(startTime) - fmt.Printf(theme.ColorGreen+"[ecm]"+theme.ColorReset+" 🪓 in "+ + fmt.Printf(theme.ColorGreen+"🪓 [ecm]"+theme.ColorReset+" in "+ + theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", + diff, cmdStr, factors) + }() + } + + for _, n := range N { + wg.Add(1) + + go func() { + defer wg.Done() + + factors, cmdStr, err := util.FactorWithPari(n) + if err != nil { + log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) + } + + if factors == nil { + return + } + + if len(factors) == 0 { + return + } + + diff := time.Now().Sub(startTime) + + fmt.Printf(theme.ColorGreen+"🪓 [gp-pari]"+theme.ColorReset+" in "+ theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", diff, cmdStr, factors) }() From bc790fe02ba766e01cff7cda4fd6babb3f72e0b0 Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:48:56 +0000 Subject: [PATCH 5/8] move installation checks to util --- rsa/factor_with_ecm.go | 10 ++-------- rsa/factor_with_pari.go | 10 ++-------- util/factor_with_ecm.go | 14 ++++++++++++++ util/factor_with_pari.go | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/rsa/factor_with_ecm.go b/rsa/factor_with_ecm.go index cb78860..ae70c4c 100644 --- a/rsa/factor_with_ecm.go +++ b/rsa/factor_with_ecm.go @@ -4,7 +4,6 @@ import ( "fmt" "log" "math/big" - "os/exec" "ret/theme" "ret/util" "sync" @@ -121,13 +120,8 @@ func factorWithECM(strategy *Strategy, n *big.Int) { } func StrategyFactorWithECM(strategy *Strategy) { - // check that ecm is installed - cmd := exec.Command("/usr/bin/ecm", "--help") - - err := cmd.Run() - if err != nil { - fmt.Printf("😰"+theme.ColorGray+" \""+theme.ColorReset+"%v"+theme.ColorGray+"\""+theme.ColorYellow+ - " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"gmp-ecm"+theme.ColorReset+"\n", cmd.String()) + installed := util.CheckIfECMInstalled() + if installed != true { return } diff --git a/rsa/factor_with_pari.go b/rsa/factor_with_pari.go index 482a3c0..62587f5 100644 --- a/rsa/factor_with_pari.go +++ b/rsa/factor_with_pari.go @@ -4,7 +4,6 @@ import ( "fmt" "log" "math/big" - "os/exec" "ret/theme" "ret/util" "sync" @@ -121,13 +120,8 @@ func factorWithPari(strategy *Strategy, n *big.Int) { } func StrategyFactorWithPari(strategy *Strategy) { - // check that pari-gp is installed - cmd := exec.Command("/usr/bin/gp", "-v") - - err := cmd.Run() - if err != nil { - fmt.Printf("😰"+theme.ColorGray+" \""+theme.ColorReset+"%v"+theme.ColorGray+"\""+theme.ColorYellow+ - " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"pari-gp"+theme.ColorReset+"\n", cmd.String()) + installed := util.CheckIfPariInstalled() + if installed != true { return } diff --git a/util/factor_with_ecm.go b/util/factor_with_ecm.go index 7743a1b..fd5cd79 100644 --- a/util/factor_with_ecm.go +++ b/util/factor_with_ecm.go @@ -5,9 +5,23 @@ import ( "fmt" "math/big" "os/exec" + "ret/theme" "strings" ) +func CheckIfECMInstalled() bool { + cmd := exec.Command("/usr/bin/ecm", "--help") + + err := cmd.Run() + if err != nil { + fmt.Printf("😰"+theme.ColorGray+" \""+theme.ColorReset+"%v"+theme.ColorGray+"\""+theme.ColorYellow+ + " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"gmp-ecm"+theme.ColorReset+"\n", cmd.String()) + return false + } + + return true +} + func FactorWithECM(n *big.Int) ([]*big.Int, string, error) { cmd := exec.Command("/usr/bin/ecm", "-c", "1000000000", "-one", "2000") diff --git a/util/factor_with_pari.go b/util/factor_with_pari.go index 81f9000..91bbbd9 100644 --- a/util/factor_with_pari.go +++ b/util/factor_with_pari.go @@ -6,10 +6,24 @@ import ( "math/big" "os" "os/exec" + "ret/theme" "strconv" "strings" ) +func CheckIfPariInstalled() bool { + cmd := exec.Command("/usr/bin/gp", "-v") + + err := cmd.Run() + if err != nil { + fmt.Printf("😰"+theme.ColorGray+" \""+theme.ColorReset+"%v"+theme.ColorGray+"\""+theme.ColorYellow+ + " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"pari-gp"+theme.ColorReset+"\n", cmd.String()) + return false + } + + return true +} + func FactorWithPari(n *big.Int) ([]*big.Int, string, error) { file, err := os.CreateTemp("", "ret_rsa_factorme") From a9ac2d92b6447fd6570495e990abe958dce03730 Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:51:12 +0000 Subject: [PATCH 6/8] extra newlines on warning output --- util/factor_with_ecm.go | 2 +- util/factor_with_pari.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/util/factor_with_ecm.go b/util/factor_with_ecm.go index fd5cd79..6eb84c3 100644 --- a/util/factor_with_ecm.go +++ b/util/factor_with_ecm.go @@ -15,7 +15,7 @@ func CheckIfECMInstalled() bool { err := cmd.Run() if err != nil { fmt.Printf("😰"+theme.ColorGray+" \""+theme.ColorReset+"%v"+theme.ColorGray+"\""+theme.ColorYellow+ - " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"gmp-ecm"+theme.ColorReset+"\n", cmd.String()) + " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"gmp-ecm"+theme.ColorReset+"\n\n", cmd.String()) return false } diff --git a/util/factor_with_pari.go b/util/factor_with_pari.go index 91bbbd9..329085c 100644 --- a/util/factor_with_pari.go +++ b/util/factor_with_pari.go @@ -17,7 +17,7 @@ func CheckIfPariInstalled() bool { err := cmd.Run() if err != nil { fmt.Printf("😰"+theme.ColorGray+" \""+theme.ColorReset+"%v"+theme.ColorGray+"\""+theme.ColorYellow+ - " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"pari-gp"+theme.ColorReset+"\n", cmd.String()) + " failed"+theme.ColorReset+"! consider installing "+theme.ColorCyan+"pari-gp"+theme.ColorReset+"\n\n", cmd.String()) return false } From 5cb125e031d3ee4a943dd36fabdc754508670562 Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:51:49 +0000 Subject: [PATCH 7/8] check if ecm / pari are installed before trying to use them --- commands/factor.go | 83 +++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/commands/factor.go b/commands/factor.go index 96a9283..8c1444c 100644 --- a/commands/factor.go +++ b/commands/factor.go @@ -97,58 +97,65 @@ func Factor(args []string) { }() } - for _, n := range N { - wg.Add(1) + if util.CheckIfECMInstalled() { - go func() { - defer wg.Done() + for _, n := range N { + wg.Add(1) - factors, cmdStr, err := util.FactorWithECM(n) - if err != nil { - log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) - } + go func() { + defer wg.Done() - if factors == nil { - return - } + factors, cmdStr, err := util.FactorWithECM(n) + if err != nil { + log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) + } - if len(factors) == 0 { - return - } + if factors == nil { + return + } - diff := time.Now().Sub(startTime) + if len(factors) == 0 { + return + } - fmt.Printf(theme.ColorGreen+"🪓 [ecm]"+theme.ColorReset+" in "+ - theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", - diff, cmdStr, factors) - }() + diff := time.Now().Sub(startTime) + + fmt.Printf(theme.ColorGreen+"🪓 [ecm]"+theme.ColorReset+" in "+ + theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", + diff, cmdStr, factors) + }() + } } - for _, n := range N { - wg.Add(1) + if util.CheckIfPariInstalled() { - go func() { - defer wg.Done() + for _, n := range N { + wg.Add(1) - factors, cmdStr, err := util.FactorWithPari(n) - if err != nil { - log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) - } + go func() { + defer wg.Done() - if factors == nil { - return - } + factors, cmdStr, err := util.FactorWithPari(n) + if err != nil { + log.Fatalf("💥 "+theme.ColorRed+" error"+theme.ColorReset+": %v\n", err) + } - if len(factors) == 0 { - return - } + if factors == nil { + return + } - diff := time.Now().Sub(startTime) + if len(factors) == 0 { + return + } + + diff := time.Now().Sub(startTime) + + fmt.Printf(theme.ColorGreen+"🪓 [gp-pari]"+theme.ColorReset+" in "+ + theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", + diff, cmdStr, factors) + }() + } - fmt.Printf(theme.ColorGreen+"🪓 [gp-pari]"+theme.ColorReset+" in "+ - theme.ColorYellow+"%v"+theme.ColorGray+" %v"+theme.ColorReset+"\n"+"%v\n\n", - diff, cmdStr, factors) - }() } wg.Wait() From cd1de516bea2f406f7861f3833a9b666aefa6758 Mon Sep 17 00:00:00 2001 From: dmur1 <93072266+dmur1@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:54:19 +0000 Subject: [PATCH 8/8] update factor command help string and readme --- README.md | 5 +++++ commands/factor.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/README.md b/README.md index 166f533..75375e8 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,11 @@ you can supply arguments the most common prefixes i.e. n= -n= --n= multiple values can be supplied as a list or with multiple argument prefixes e.g. -n=1,2,3 or -n=1 -n=2 -n=3 +this command opportunistically makes use of the following tools to perform factorization: + + - gmp-ecm + - pari-gp + for example: ```bash $ ret factor -n=1807415580361109435231633835400969 diff --git a/commands/factor.go b/commands/factor.go index 8c1444c..ad24b77 100644 --- a/commands/factor.go +++ b/commands/factor.go @@ -38,6 +38,10 @@ func FactorHelp() string { "you can supply arguments the most common prefixes i.e. " + theme.ColorBlue + "n= -n= --n= " + theme.ColorReset + "\n\n" + "multiple values can be supplied as a list or with multiple argument prefixes e.g. " + theme.ColorBlue + "-n=1,2,3 or -n=1 -n=2 -n=3" + theme.ColorReset + "\n\n" + + "this command opportunistically makes use of the following tools to perform factorization:\n\n" + + " - gmp-ecm\n" + + " - pari-gp\n\n" + + "for example:\n" + "```bash\n" + theme.ColorGray + "$ " + theme.ColorBlue + "ret factor -n=1807415580361109435231633835400969\n" + theme.ColorReset +