From 0fdb60275f64ae2ef5471cf1e4b72d91799a9f3a Mon Sep 17 00:00:00 2001 From: filifa Date: Thu, 21 Aug 2025 19:20:36 -0400 Subject: [PATCH] move primitive root functions to lib --- cmd/primitiveRoot.go | 110 ++----------------------------------------- internal/lib/lib.go | 104 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 107 deletions(-) diff --git a/cmd/primitiveRoot.go b/cmd/primitiveRoot.go index a721f39..2a799c7 100644 --- a/cmd/primitiveRoot.go +++ b/cmd/primitiveRoot.go @@ -17,121 +17,17 @@ along with this program. If not, see . package cmd import ( - "errors" "fmt" "log" "math/big" "github.com/spf13/cobra" + "scm.dairydemon.net/filifa/mathtools/internal/lib" ) var modulus string var tpf []string -func totient(n *big.Int) *big.Int { - N := new(big.Int).Set(n) - - phi := new(big.Int).Set(N) - - sqrtn := new(big.Int).Sqrt(N) - for i := big.NewInt(2); i.Cmp(sqrtn) != 1; i.Add(i, big.NewInt(1)) { - mod := new(big.Int).Mod(N, i) - if mod.Cmp(big.NewInt(0)) != 0 { - continue - } - - // phi -= phi // i - tmp := new(big.Int).Div(phi, i) - phi.Sub(phi, tmp) - - for mod.Cmp(big.NewInt(0)) == 0 { - N.Div(N, i) - mod.Mod(N, i) - } - } - - if N.Cmp(big.NewInt(1)) == 1 { - // phi -= phi // N - tmp := new(big.Int).Div(phi, N) - phi.Sub(phi, tmp) - } - - return phi -} - -func multiplicativeOrder(g *big.Int, modulus *big.Int) *big.Int { - e := new(big.Int).Set(g) - var k *big.Int - for k = big.NewInt(1); e.Cmp(big.NewInt(1)) != 0; k.Add(k, big.NewInt(1)) { - e.Mul(e, g) - e.Mod(e, modulus) - } - - return k -} - -func computeNaive(modulus *big.Int) (*big.Int, error) { - if modulus.Cmp(big.NewInt(1)) == 0 { - return big.NewInt(0), nil - } - - phi := totient(modulus) - - for g := big.NewInt(1); g.Cmp(modulus) == -1; g.Add(g, big.NewInt(1)) { - gcd := new(big.Int).GCD(nil, nil, g, modulus) - if gcd.Cmp(big.NewInt(1)) != 0 { - continue - } - - order := multiplicativeOrder(g, modulus) - if order.Cmp(phi) == 0 { - return g, nil - } - } - - return nil, errors.New("no primitive root") -} - -func computeFromFactors(modulus *big.Int, tpf []string) (*big.Int, error) { - phi := big.NewInt(1) - factors := make(map[string]bool) - for _, s := range tpf { - p, ok := new(big.Int).SetString(s, 10) - if !ok { - return nil, errors.New("invalid input " + s) - } - - phi.Mul(phi, p) - factors[p.Text(10)] = true - } - - for g := big.NewInt(1); g.Cmp(modulus) == -1; g.Add(g, big.NewInt(1)) { - gcd := new(big.Int).GCD(nil, nil, g, modulus) - if gcd.Cmp(big.NewInt(1)) != 0 { - continue - } - - isPrimitive := true - for p := range factors { - e := new(big.Int) - f, _ := new(big.Int).SetString(p, 10) - k := new(big.Int).Div(phi, f) - e.Exp(g, k, modulus) - - if e.Cmp(big.NewInt(1)) == 0 { - isPrimitive = false - break - } - } - - if isPrimitive { - return g, nil - } - } - - return nil, errors.New("no primitive root") -} - func primitiveRoot(cmd *cobra.Command, args []string) { m, ok := new(big.Int).SetString(modulus, 10) if !ok { @@ -141,12 +37,12 @@ func primitiveRoot(cmd *cobra.Command, args []string) { root := new(big.Int) var err error if len(tpf) == 0 { - root, err = computeNaive(m) + root, err = lib.PrimitiveRoot(m) if err != nil { log.Fatal(err) } } else { - root, err = computeFromFactors(m, tpf) + root, err = lib.PrimitiveRootFast(m, tpf) if err != nil { log.Fatal(err) } diff --git a/internal/lib/lib.go b/internal/lib/lib.go index c7f5159..9718f7f 100644 --- a/internal/lib/lib.go +++ b/internal/lib/lib.go @@ -76,3 +76,107 @@ func ArePairwiseCoprime(moduli []*big.Int) bool { return true } + +func Totient(n *big.Int) *big.Int { + N := new(big.Int).Set(n) + + phi := new(big.Int).Set(N) + + sqrtn := new(big.Int).Sqrt(N) + for i := big.NewInt(2); i.Cmp(sqrtn) != 1; i.Add(i, big.NewInt(1)) { + mod := new(big.Int).Mod(N, i) + if mod.Cmp(big.NewInt(0)) != 0 { + continue + } + + // phi -= phi // i + tmp := new(big.Int).Div(phi, i) + phi.Sub(phi, tmp) + + for mod.Cmp(big.NewInt(0)) == 0 { + N.Div(N, i) + mod.Mod(N, i) + } + } + + if N.Cmp(big.NewInt(1)) == 1 { + // phi -= phi // N + tmp := new(big.Int).Div(phi, N) + phi.Sub(phi, tmp) + } + + return phi +} + +func MultiplicativeOrder(g *big.Int, modulus *big.Int) *big.Int { + e := new(big.Int).Set(g) + var k *big.Int + for k = big.NewInt(1); e.Cmp(big.NewInt(1)) != 0; k.Add(k, big.NewInt(1)) { + e.Mul(e, g) + e.Mod(e, modulus) + } + + return k +} + +func PrimitiveRoot(modulus *big.Int) (*big.Int, error) { + if modulus.Cmp(big.NewInt(1)) == 0 { + return big.NewInt(0), nil + } + + phi := Totient(modulus) + + for g := big.NewInt(1); g.Cmp(modulus) == -1; g.Add(g, big.NewInt(1)) { + gcd := new(big.Int).GCD(nil, nil, g, modulus) + if gcd.Cmp(big.NewInt(1)) != 0 { + continue + } + + order := MultiplicativeOrder(g, modulus) + if order.Cmp(phi) == 0 { + return g, nil + } + } + + return nil, errors.New("no primitive root") +} + +func PrimitiveRootFast(modulus *big.Int, tpf []string) (*big.Int, error) { + phi := big.NewInt(1) + factors := make(map[string]bool) + for _, s := range tpf { + p, ok := new(big.Int).SetString(s, 10) + if !ok { + return nil, errors.New("invalid input " + s) + } + + phi.Mul(phi, p) + factors[p.Text(10)] = true + } + + for g := big.NewInt(1); g.Cmp(modulus) == -1; g.Add(g, big.NewInt(1)) { + gcd := new(big.Int).GCD(nil, nil, g, modulus) + if gcd.Cmp(big.NewInt(1)) != 0 { + continue + } + + isPrimitive := true + for p := range factors { + e := new(big.Int) + f, _ := new(big.Int).SetString(p, 10) + k := new(big.Int).Div(phi, f) + e.Exp(g, k, modulus) + + if e.Cmp(big.NewInt(1)) == 0 { + isPrimitive = false + break + } + } + + if isPrimitive { + return g, nil + } + } + + return nil, errors.New("no primitive root") +}