summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--factors/factors_test.go61
-rw-r--r--factors/prime_factorization.go13
-rw-r--r--factors/type.go136
-rw-r--r--radix_info.go16
4 files changed, 221 insertions, 5 deletions
diff --git a/factors/factors_test.go b/factors/factors_test.go
index db6e098..12d4384 100644
--- a/factors/factors_test.go
+++ b/factors/factors_test.go
@@ -87,7 +87,7 @@ var factorScoreCases = map[uint]float64{
func TestFactorScore(t *testing.T) {
// factors.Score is accurate enough that we can test for exact floats!
- tableTest(t, Score, factorScoreCases, stdEquals[float64], "Score")
+ tableTest(t, Score, factorScoreCases, stdEquals, "Score")
}
var basicRankCases = map[uint]string{
@@ -97,7 +97,7 @@ var basicRankCases = map[uint]string{
}
func TestBasicRank(t *testing.T) {
- tableTest(t, BasicRank, basicRankCases, stdEquals[string], "BasicRank")
+ tableTest(t, BasicRank, basicRankCases, stdEquals, "BasicRank")
}
var gcdCases = map[struct{ a, b uint }]uint{
@@ -114,7 +114,7 @@ func gcdTest(c struct{ a, b uint }) uint {
}
func TestGCD(t *testing.T) {
- tableTest(t, gcdTest, gcdCases, stdEquals[uint], "gcd")
+ tableTest(t, gcdTest, gcdCases, stdEquals, "gcd")
}
var mtcCases = map[uint]uint{
@@ -127,7 +127,60 @@ var mtcCases = map[uint]uint{
}
func TestMTC(t *testing.T) {
- tableTest(t, MTC, mtcCases, stdEquals[uint], "MTC")
+ tableTest(t, MTC, mtcCases, stdEquals, "MTC")
+}
+
+var sanCases = map[uint32]bool{
+ 1: true, 2: true, 4: true, 6: true, 12: true, 24: true, 36: true,
+ 48: true, 60: true, 120: true, 180: true, 240: true, 360: true,
+ 720: true, 840: true, 1260: true, 1680: true, 2520: true, 5040: true,
+ 10080: true, 15120: true, 25200: true, 27720: true, 55440: true,
+ 3: false, 8: false, 13: false, 18: false, 20: false, 30: false,
+ 31: false, 72: false, 107: false, 135: false, 144: false, 300: false,
+ 1728: false, 4000: false, 6912: false, 7560: false,
+}
+
+func TestSAN(t *testing.T) {
+ tableTest(t, isSAN, sanCases, stdEquals, "isSAN")
+}
+
+var expOrdCases = map[uint32]bool{
+ 1: true, 2: true, 4: true, 5: false, 6: true, 12: true, 18: false,
+ 20: false, 30: true, 44: false, 60: true, 70: false, 96: true,
+ 101: false, 120: true, 144: true, 770: false, 6912: true, 10010: false,
+}
+
+func TestExponentsOrdered(t *testing.T) {
+ tableTest(t, exponentsOrdered, expOrdCases, stdEquals, "exponentsOrdered")
+}
+
+var practicalCases = map[uint32]bool{
+ 1: true, 2: true, 4: true, 6: true, 8: true, 12: true, 16: true,
+ 18: true, 20: true, 24: true, 28: true, 30: true, 32: true, 36: true,
+ 48: true, 60: true, 120: true, 180: true, 240: true, 360: true,
+ 720: true, 840: true, 1260: true, 1680: true, 2520: true, 5040: true,
+ 10080: true, 15120: true, 25200: true, 27720: true, 55440: true,
+ 3: false, 10: false, 27: false, 44: false, 45: false, 68: false,
+ 70: false, 114: false, 121: false, 770: false, 1729: false, 10010: false,
+}
+
+func TestPractical(t *testing.T) {
+ tableTest(t, practical, practicalCases, stdEquals, "practical")
+}
+
+var typeCases = map[uint32]NumberType {
+ 2: ColossallyAbundant, 3: NotPractical, 4: Superabundant,
+ 6: ColossallyAbundant, 8: OrderedExponent, 10: NotPractical,
+ 12: ColossallyAbundant, 18: Practical, 20: Practical, 24: Superabundant,
+ 28: Practical, 30: OrderedExponent, 31: NotPractical, 34: NotPractical,
+ 60: ColossallyAbundant, 70: NotPractical, 90: Practical,
+ 120: ColossallyAbundant, 240: Superabundant, 720: Superabundant,
+ 770: NotPractical, 1729: NotPractical, 6912: OrderedExponent,
+ 10010: NotPractical, 10080: Superabundant,
+}
+
+func TestType(t *testing.T) {
+ tableTest(t, Type, typeCases, stdEquals, "Type")
}
// to be used as the equal paramater for tableTest
diff --git a/factors/prime_factorization.go b/factors/prime_factorization.go
index 69d0e08..f91a2cf 100644
--- a/factors/prime_factorization.go
+++ b/factors/prime_factorization.go
@@ -45,7 +45,7 @@ func (factors PrimeFactorization) Exponent(p uint) uint {
return factors.exponents[p]
}
-// ExponentMap creates and returns a map mapping primes to their exponents.
+// ExponentMap returns a map mapping primes to their exponents.
func (factors PrimeFactorization) ExponentMap() map[uint]uint {
exponentMap := make(map[uint]uint, len(factors.exponents))
for p, e := range factors.exponents {
@@ -54,6 +54,17 @@ func (factors PrimeFactorization) ExponentMap() map[uint]uint {
return exponentMap
}
+// Primes returns a slice containing all of the primes with nonzero exponents
+func (factors PrimeFactorization) Primes() []uint {
+ primes := make([]uint, 0, len(factors.exponents))
+ for p := range factors.exponents {
+ if factors.exponents[p] > 0 {
+ primes = append(primes, p)
+ }
+ }
+ return primes
+}
+
// Size returns how many primes in this factorization have nonzero exponents.
func (factors PrimeFactorization) Size() int {
return len(factors.exponents)
diff --git a/factors/type.go b/factors/type.go
new file mode 100644
index 0000000..e587ebb
--- /dev/null
+++ b/factors/type.go
@@ -0,0 +1,136 @@
+package factors
+
+import "slices"
+
+// The set of all colossaly abundant numbers that are small enough
+// to be stored in a uint32.
+// These numbers are also the first 14 superior highly composites.
+var colossallyAbundantNums = [...]uint32{
+ 2, 6, 12, 60, 120, 360, 2520, 5040, 55440,
+ 720720, 1441440, 4324320, 21621600, 367567200}
+
+// A cache containing all superabundant numbers up to a certain value
+var sanCache = []uint32{1}
+
+// A type of number (as determined by its factors)
+type NumberType byte
+
+const (
+ // A number whose factor score is higher than any other,
+ // if you adjust for size by dividing by some power of the number
+ // (different powers yield different best numbers).
+ // All colossally abundant numbers are also superabundant.
+ ColossallyAbundant NumberType = 0x84
+ // A number whose factor score is higher than any smaller number.
+ // All superabundant numbers have ordered exponents.
+ Superabundant NumberType = 0x83
+ // A number whose prime factorization exponents stay the same or decrease
+ // as you go from smaller to larger primes.
+ // All of these numbers are also practical.
+ OrderedExponent NumberType = 0x82
+ // A number whose factors can sum to any smaller number without duplication.
+ // All practical numbers besides 1 and 2 are divisible by 4 or 6.
+ Practical NumberType = 0x81
+ // None of the above types
+ NotPractical NumberType = 0x80
+)
+
+func Type(n uint32) NumberType {
+ if slices.Contains(colossallyAbundantNums[:], n) {
+ return ColossallyAbundant
+ } else if isSAN(n) {
+ return Superabundant
+ } else if exponentsOrdered(n) {
+ return OrderedExponent
+ } else if practical(n) {
+ return Practical
+ } else {
+ return NotPractical
+ }
+}
+
+func isSAN(n uint32) bool {
+ if n <= 2 {
+ return true
+ } else if n % 4 != 0 && n % 6 != 0 {
+ return false
+ }
+
+ cachedMax := sanCache[len(sanCache)-1]
+ maxScore := Score(uint(cachedMax))
+ nScore := Score(uint(n))
+ for i := cachedMax + 1; i <= n; i++ {
+ iScore := Score(uint(i))
+ if iScore > maxScore {
+ sanCache = append(sanCache, i)
+ maxScore = iScore
+ if maxScore > nScore {
+ return false
+ }
+ }
+ }
+
+ _, found := slices.BinarySearch(sanCache, n)
+ return found
+}
+
+func exponentsOrdered(n uint32) bool {
+ if n <= 2 {
+ return true
+ } else if n % 4 != 0 && n % 6 != 0 {
+ return false
+ }
+
+ pf := PrimeFactorize(uint(n))
+ maxPrime := slices.Max(pf.Primes())
+
+ lastPrime := uint(2)
+ for p := uint(3); p <= maxPrime; p += 2 {
+ if PrimeFactorize(p).Exponent(p) == 1 {
+ if pf.Exponent(p) > pf.Exponent(lastPrime) {
+ return false
+ } else if pf.Exponent(p) == 0 && p < maxPrime {
+ return false
+ }
+ lastPrime = p
+ }
+ }
+
+ return true
+}
+
+func practical(n uint32) bool {
+ if n <= 2 {
+ return true
+ } else if n % 4 != 0 && n % 6 != 0 {
+ return false
+ }
+
+ pf := PrimeFactorize(uint(n))
+ primes := pf.Primes()
+ slices.Sort(primes)
+
+ // algorithm from Wikipedia
+ for i := 0; i < pf.Size()-1; i++ {
+ factorSumUptoP := uint(1)
+ for j := 0; j <= i; j++ {
+ pj := primes[j]
+ ej := pf.Exponent(pj)
+ factorSumUptoP *= (uintpow(pj, ej+1) - 1) / (pj - 1)
+ }
+
+ if primes[i+1] > 1+factorSumUptoP {
+ return false
+ }
+ }
+
+ return true
+}
+
+func uintpow(a, b uint) uint {
+ result := uint(1)
+ for i := uint(0); i < b; i++ {
+ result *= a
+ }
+ return result
+}
diff --git a/radix_info.go b/radix_info.go
index 8bdfa5b..f3aecce 100644
--- a/radix_info.go
+++ b/radix_info.go
@@ -22,6 +22,9 @@ func main() {
fmt.Printf("Totative Ratio: %03.1f%%\n",
factors.TotativeRatio(n)*100.0)
fmt.Println("2345 Rank:", factors.BasicRank(n))
+ if n < 1<<32 {
+ printTypeMessage(factors.Type(uint32(n)))
+ }
fmt.Println("Multiplication Table Complexity:", factors.MTC(n))
fmt.Printf("Natural Logarithm: %.2f\n", math.Log(float64(n)))
} else {
@@ -34,3 +37,16 @@ func main() {
fmt.Println("Please provide an argument (radix to study).")
}
}
+
+func printTypeMessage(t factors.NumberType) {
+ switch t {
+ case factors.ColossallyAbundant:
+ fmt.Println("This radix is colossally abundant!")
+ case factors.Superabundant:
+ fmt.Println("This radix is superabundant.")
+ case factors.OrderedExponent:
+ fmt.Println("This radix has ordered exponents.")
+ case factors.Practical:
+ fmt.Println("This radix is practical.")
+ }
+}