summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-09-07 13:15:40 -0500
committerAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-09-07 13:15:40 -0500
commit3cfb1fdcbe83c1ee4adf8e370f0922ca41c472c6 (patch)
tree2695b9decec500c1c5137b79e8fd83a95961c2a0
parent7fa8cf9666cd3427dd078bb4a5fdb9fa30c94b09 (diff)
Add ability to display specific totative digits
Instead of just saying how many totative digits there are, the new -t flag allows the user to determine the specific digits. All totatives will end in one of these digits, and all primes are either factors or totatives. This feature is not useful in comparing radices, only learning one you have already chosen. Because there can be so many totatives (p - 1 of them for primes!), this is not displayed by default. The digit map (without -f) gives this information, and beyond its range every number will have more than 8 totatives, so do not use this flag if you only want the count.
-rw-r--r--args.go13
-rw-r--r--factor_info.go28
-rw-r--r--factors/totative.go14
-rw-r--r--radix_info.go2
4 files changed, 45 insertions, 12 deletions
diff --git a/args.go b/args.go
index a211435..d0f5da9 100644
--- a/args.go
+++ b/args.go
@@ -11,10 +11,11 @@ const ProgramVersion = "1.0.0-alpha+dev"
// The arguments to this program
type args struct {
- Radix uint
- Compact bool
- FullMap bool
- ExactMTCLarge bool
+ Radix uint
+ Compact bool
+ FullMap bool
+ ExactMTCLarge bool
+ TotativeDigits bool
// If true, exit the program immediately after parsing args.
Exit bool
}
@@ -28,6 +29,8 @@ func parseArgs() (args, error) {
flag.BoolVar(&a.ExactMTCLarge, "m", false,
fmt.Sprintf("Calculate exact MTC for very large radices (up to %d instead of %d), which may take a while.",
maxExtended, maxNormal))
+ flag.BoolVar(&a.TotativeDigits, "t", false,
+ "Calculate the radix's totative digits instead of just how many there are; incompatible with -c.")
help := flag.Bool("?", false,
"Get information about program usage then exit")
version := flag.Bool("V", false,
@@ -40,6 +43,8 @@ func parseArgs() (args, error) {
} else if *version {
fmt.Println("Radix Info version", ProgramVersion)
return args{Exit: true}, nil
+ } else if a.Compact && a.TotativeDigits {
+ return args{}, errors.New("You cannot use both -t and -c at the same time.")
} else {
if flag.NArg() == 1 {
if radix, err := strconv.ParseUint(flag.Arg(0), 0, 0); err == nil {
diff --git a/factor_info.go b/factor_info.go
index 03c81f3..bcfc3b1 100644
--- a/factor_info.go
+++ b/factor_info.go
@@ -26,6 +26,8 @@ type factorInfo struct {
// The fraction of digits that are totatives (numbers that share no
// factors with the radix - they are the worst kind of digits)
TotativeRatio float64
+ // The totative digits of this radix. May be nil if this was not requested.
+ TotativeDigits []uint32
// A rank measuring how well the radix works with the most elementary
// numbers and ratios
BasicRank string
@@ -58,18 +60,25 @@ const (
maxExtended = 1 << 32
)
-func getFactorInfo(radix uint, fullMap bool, exactMTCLarge bool) *factorInfo {
+func getFactorInfo(a args) *factorInfo {
+ radix := a.Radix
+
r_factors := factors.Factors(radix)
slices.Sort(r_factors)
+ var totativeDigits []uint32 = nil
+ if a.TotativeDigits && radix < maxExtended {
+ totativeDigits = factors.TotativeDigits(uint32(radix))
+ }
+
var mtc_ptr *uint64 = nil
- if radix < maxNormal || (exactMTCLarge && radix < maxExtended) {
+ if radix < maxNormal || (a.ExactMTCLarge && radix < maxExtended) {
mtc := factors.MTC(uint32(radix))
mtc_ptr = &mtc
}
var digitMap []factors.DigitType
- if fullMap {
+ if a.FullMap {
digitMap = make([]factors.DigitType, maxSmallRadix)
for d := 0; d < maxSmallRadix; d++ {
digitMap[d] = factors.GetDigitType(uint(d), radix)
@@ -85,16 +94,21 @@ func getFactorInfo(radix uint, fullMap bool, exactMTCLarge bool) *factorInfo {
return &factorInfo{radix, factors.PrimeFactorize(radix),
r_factors, factors.Score(radix), totativeCount, totativeRatio,
- factors.BasicRank(radix), factors.Type(radix), mtc_ptr,
- math.Log(float64(radix)), digitMap}
+ totativeDigits, factors.BasicRank(radix), factors.Type(radix),
+ mtc_ptr, math.Log(float64(radix)), digitMap}
}
func (fi *factorInfo) writeTo(w io.Writer) {
fmt.Fprintln(w, fi.Radix, "=", fi.PrimeFactorization)
fmt.Fprintf(w, "Factors: %v (Score: %.4f)\n", fi.Factors, fi.Score)
fmt.Fprintln(w, "2345 Rank:", fi.BasicRank)
- fmt.Fprintf(w, "Totative Digit Count: %d (%.3f%%)\n",
- fi.Totient, fi.TotativeRatio*100.0)
+ if fi.TotativeDigits != nil {
+ fmt.Fprintf(w, "Totative Digits: %v (%.3f%%)\n",
+ fi.TotativeDigits, fi.TotativeRatio*100.0)
+ } else {
+ fmt.Fprintf(w, "Totative Digit Count: %d (%.3f%%)\n",
+ fi.Totient, fi.TotativeRatio*100.0)
+ }
writeTypeMessage(w, fi.Type)
if fi.MTC != nil {
fmt.Fprintln(w, "Multiplication Table Complexity:", *fi.MTC)
diff --git a/factors/totative.go b/factors/totative.go
index 680cc26..abbbe89 100644
--- a/factors/totative.go
+++ b/factors/totative.go
@@ -17,3 +17,17 @@ func Totient(n uint) uint {
return num * (n / denom)
}
+
+// TotativeDigits returns a slice containing every number less than r
+// that is a totative of r (shares no factors with r).
+func TotativeDigits(r uint32) []uint32 {
+ digits := make([]uint32, Totient(uint(r)))
+ totativesFound := 0
+ for d := uint32(0); d < r; d++ {
+ if gcd(d, r) == 1 {
+ digits[totativesFound] = d
+ totativesFound++
+ }
+ }
+ return digits
+}
diff --git a/radix_info.go b/radix_info.go
index 9845572..f495905 100644
--- a/radix_info.go
+++ b/radix_info.go
@@ -12,7 +12,7 @@ func main() {
}
if err == nil {
- factorInfo := getFactorInfo(args.Radix, args.FullMap, args.ExactMTCLarge)
+ factorInfo := getFactorInfo(args)
if args.Compact {
factorInfo.writeToCompact(os.Stdout)
} else {