diff --git a/cmd/primitiveRoot.go b/cmd/primitiveRoot.go new file mode 100644 index 0000000..dbd26fd --- /dev/null +++ b/cmd/primitiveRoot.go @@ -0,0 +1,112 @@ +/* +Copyright © 2025 filifa + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package cmd + +import ( + "errors" + "fmt" + "log" + "math/big" + + "github.com/spf13/cobra" +) + +var modulus string +var tpf []string + +func computeNaive(modulus *big.Int) (*big.Int, error) { + for g := big.NewInt(2); g.Cmp(modulus) == -1; g.Add(g, big.NewInt(1)) { + e := new(big.Int).Set(g) + exps := make(map[string]big.Int) + for k := big.NewInt(1); k.Cmp(modulus) == -1; k.Add(k, big.NewInt(1)) { + e.Mul(e, g) + e.Mod(e, modulus) + exps[e.Text(10)] = *k + } + + isPrimitive := true + for a := big.NewInt(1); a.Cmp(modulus) == -1; a.Add(a, big.NewInt(1)) { + gcd := new(big.Int).GCD(nil, nil, a, modulus) + if gcd.Cmp(big.NewInt(1)) != 0 { + continue + } + + _, ok := exps[a.Text(10)] + if !ok { + isPrimitive = false + break + } + } + + if isPrimitive { + return g, nil + } + } + + return nil, errors.New("no primitive root") +} + +func computeFromFactors(modulus *big.Int, tpf []string) *big.Int { + return nil +} + +func primitiveRoot(cmd *cobra.Command, args []string) { + m, ok := new(big.Int).SetString(modulus, 10) + if !ok { + log.Fatal("invalid input " + modulus) + } + + root := new(big.Int) + var err error + if len(tpf) == 0 { + root, err = computeNaive(m) + if err != nil { + log.Fatal(err) + } + } else { + root = computeFromFactors(m, tpf) + } + + fmt.Println(root) +} + +// primitiveRootCmd represents the primitiveRoot command +var primitiveRootCmd = &cobra.Command{ + Use: "primitive-root -m M", + Short: "Compute a primitive root modulo n", + Long: `Compute a primitive root modulo n.`, + Run: primitiveRoot, +} + +func init() { + rootCmd.AddCommand(primitiveRootCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // primitiveRootCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // primitiveRootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + + primitiveRootCmd.Flags().StringVarP(&modulus, "modulus", "m", "", "modulus") + primitiveRootCmd.MarkFlagRequired("modulus") + + primitiveRootCmd.Flags().StringSliceVarP(&tpf, "totient-factorization", "t", make([]string, 0), "prime factorization of the totient of the modulus") +}