/* This script is part of radix_info. Copyright (C) 2023-2025 Adrien Hopkins This program is free software: you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. 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 main import ( "aphopkins/radix_info/factors" "fmt" "io" "math" "sort" ) // FactorInfo contains all of the information this program // calculates about a radix. type factorInfo struct { // The radix this info is about Radix uint // A representation of this radix as a product of prime numbers. // Most of the important info about radices is determined through this. PrimeFactorization factors.PrimeFactorization // The radix's factors, in sorted order. Factors []uint // An estimate of the utility of the radix's factors. Score float64 // The number of digits that are totatives (numbers that share no // factors with the radix - they are the worst kind of digits) Totient uint // 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 // Whether or not this radix is part of any special factor-related classes. // This is not calculated if the radix is too large - in this case // this field will be nil. Type factors.CompositenessType // An estimate of the complexity of the radix's multiplication table. // This is not calculated if the radix is too large - in this case // this field will be nil. MTC *uint64 // The radix's base-2 logarithm. // One digit in this radix is equivalent to this many bits. Log2 float64 // Information about each digit's compatibility with the radix. // This determines what kind of decimal expansion the digit's // reciprocoal has and what patterns are in its row of the multiplication // table. DigitMap []factors.DigitType // The complexity of each prime factor. // The amount of numbers to memorize for testing p^e is around RC(p)^e. // The memorization for the product of prime powers // is less than the memorization of worst part of the product. PrimeRegularComplexities map[uint]float64 // If true, the above field is the log2 of its true value. LogRegularComplexities bool } const ( // The maximum radix that will always be treated as normal // (i.e. radix type and exact MTC will be calculated) maxNormal = 1 << 16 // The max radix that will be treated as normal with -l // above this, MTC can exceed the size of a uint64 maxExtended = 1 << 32 ) func getFactorInfo(a args) *factorInfo { radix := a.Radix r_factors := factors.Factors(radix) sort.Slice(r_factors, func(i, j int) bool { return r_factors[i] < r_factors[j] }) var totativeDigits []uint32 = nil if a.TotativeDigits && radix < maxExtended { totativeDigits = factors.TotativeDigits(uint32(radix)) } var mtc_ptr *uint64 = nil if radix < maxNormal || (a.ExactMTCLarge && radix < maxExtended) { mtc := factors.MTC(uint32(radix)) mtc_ptr = &mtc } var digitMap []factors.DigitType if a.FullMap { digitMap = make([]factors.DigitType, maxSmallRadix) for d := 0; d < maxSmallRadix; d++ { digitMap[d] = factors.GetDigitType(uint(d), radix) } } else if radix <= maxSmallRadix { digitMap = factors.DigitMap(radix) } else { digitMap = []factors.DigitType{} } totativeCount := factors.Totient(radix) totativeRatio := float64(totativeCount) / float64(radix) var prcs = factors.PrimeRegularComplexities(radix) if a.LogRegularComplexities { for regular := range prcs { prcs[regular] = math.Log2(prcs[regular]) } } return &factorInfo{radix, factors.PrimeFactorize(radix), r_factors, factors.Score(radix), totativeCount, totativeRatio, totativeDigits, factors.BasicRank(radix), factors.Type(radix), mtc_ptr, math.Log2(float64(radix)), digitMap, prcs, a.LogRegularComplexities} } func (fi *factorInfo) writeTo(w io.Writer) { fmt.Fprintln(w, fi.Radix, "=", fi.PrimeFactorization) fmt.Fprintln(w, "Factors:", fi.Factors) fmt.Fprintf(w, "Factor Utility Score: %.4f\n", fi.Score) fmt.Fprintf(w, "Regular Utility Score: %.4f\n", float64(fi.Radix)/float64(fi.Totient)) fmt.Fprintln(w, "2345 Rank:", fi.BasicRank) 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) } else { low_mtc_est := float64(fi.Radix) * float64(fi.Totient-2) high_mtc_est := float64(fi.Radix) * float64(fi.Radix-2) fmt.Fprintf(w, "Multiplication Table Complexity is between %.6g and %.6g.\n", low_mtc_est, high_mtc_est) } fmt.Fprintf(w, "Base-2 Logarithm: %.3f\n", fi.Log2) fmt.Fprintf(w, "Number Length: %.4f×Decimal\n", 1/math.Log10(float64(fi.Radix))) if fi.LogRegularComplexities { fmt.Fprint(w, "log2(Prime Regular Complexities): ") } else { fmt.Fprint(w, "Prime Regular Complexities: ") } writePRCMap(w, fi.PrimeRegularComplexities, 4) fmt.Fprintln(w) if len(fi.DigitMap) > 0 { writeDigitMap(w, fi.DigitMap) } } func (fi *factorInfo) writeToCompact(w io.Writer) { fmt.Fprintf(w, "%d = %s | σ(r)/r: %.2f | r/φ(r): %.2f | %s\n", fi.Radix, fi.PrimeFactorization, fi.Score, 1/fi.TotativeRatio, typeAbbrev(fi.Type)) if fi.LogRegularComplexities { fmt.Fprint(w, "logRC: ") } else { fmt.Fprint(w, "RC: ") } writePRCMap(w, fi.PrimeRegularComplexities, 2) if fi.MTC != nil { fmt.Fprintf(w, " | MTC: %d | ", *fi.MTC) } else { low_mtc_est := float32(fi.Radix) * float32(fi.Totient-2) high_mtc_est := float32(fi.Radix) * float32(fi.Radix-2) fmt.Fprintf(w, " | %.4g ≤ MTC ≤ %.4g | ", low_mtc_est, high_mtc_est) } fmt.Fprintf(w, "log2: %.2f", fi.Log2) fmt.Fprintln(w) if len(fi.DigitMap) > 0 { writeDigitMapCompact(w, fi.DigitMap) } } func writeTypeMessage(w io.Writer, t factors.CompositenessType) { switch t { case factors.ColossallyAbundant: fmt.Fprintln(w, "This radix is colossally abundant!") case factors.Superabundant: fmt.Fprintln(w, "This radix is superabundant.") case factors.OrderedExponent: fmt.Fprintln(w, "This radix has ordered exponents.") case factors.Practical: fmt.Fprintln(w, "This radix is practical.") } } func typeAbbrev(t factors.CompositenessType) string { switch t { case factors.ColossallyAbundant: return "Colossally Abundant" case factors.Superabundant: return "Superabundant" case factors.OrderedExponent: return "Ordered Exponents" case factors.Practical: return "Practical" case factors.None: return "Not Practical" default: panic("Should not be possible to get here.") } } func writePRCMap(w io.Writer, prcs map[uint]float64, precision int) { var first = true var primeFactors = make([]uint, 0, len(prcs)) for p := range prcs { primeFactors = append(primeFactors, p) } sort.Slice(primeFactors, func(i, j int) bool { return primeFactors[i] < primeFactors[j] }) for _, p := range primeFactors { if !first { fmt.Fprintf(w, ", ") } first = false fmt.Fprintf(w, "%d: %.*f", p, precision, prcs[p]) } }