summaryrefslogtreecommitdiff
path: root/factors
diff options
context:
space:
mode:
authorAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-10-09 13:35:04 -0500
committerAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-10-09 13:35:04 -0500
commiteeff1f3c61fad805bb5ce0170e98378c3b706c18 (patch)
tree0cffbefc58b6c9196d779abb249c3c82bce4ffc4 /factors
parent194004a9f99096ab724758ba39f39c50c71a21ed (diff)
Add more tests & fix found bugs
- TotativeDigits was defined incorrectly, previously counted totatives from 0 to r-1, now counts digits from 1 to r. This ensures that len(TotativeDigits(r)) = Totient(r).
Diffstat (limited to 'factors')
-rw-r--r--factors/factors_test.go80
-rw-r--r--factors/score.go2
-rw-r--r--factors/totative.go10
3 files changed, 69 insertions, 23 deletions
diff --git a/factors/factors_test.go b/factors/factors_test.go
index 0952e8e..ab06aba 100644
--- a/factors/factors_test.go
+++ b/factors/factors_test.go
@@ -29,11 +29,13 @@ var primeFactorCases = map[uint]PrimeFactorization{
3: PrimeFactorization{map[uint]uint{3: 1}},
4: PrimeFactorization{map[uint]uint{2: 2}},
6: PrimeFactorization{map[uint]uint{2: 1, 3: 1}},
+ 9: PrimeFactorization{map[uint]uint{3: 2}},
10: PrimeFactorization{map[uint]uint{2: 1, 5: 1}},
12: PrimeFactorization{map[uint]uint{2: 2, 3: 1}},
33: PrimeFactorization{map[uint]uint{3: 1, 11: 1}},
60: PrimeFactorization{map[uint]uint{2: 2, 3: 1, 5: 1}},
71: PrimeFactorization{map[uint]uint{71: 1}},
+ 1024: PrimeFactorization{map[uint]uint{2: 10}},
86400: PrimeFactorization{map[uint]uint{2: 7, 3: 3, 5: 2}},
}
@@ -49,6 +51,7 @@ var factorCases = map[uint][]uint{
2: []uint{1, 2},
4: []uint{1, 2, 4},
6: []uint{1, 2, 3, 6},
+ 9: []uint{1, 3, 9},
10: []uint{1, 2, 5, 10},
12: []uint{1, 2, 3, 4, 6, 12},
13: []uint{1, 13},
@@ -56,6 +59,8 @@ var factorCases = map[uint][]uint{
16: []uint{1, 2, 4, 8, 16},
18: []uint{1, 2, 3, 6, 9, 18},
30: []uint{1, 2, 3, 5, 6, 10, 15, 30},
+ 32: []uint{1, 2, 4, 8, 16, 32},
+ 33: []uint{1, 3, 11, 33},
60: []uint{1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60},
71: []uint{1, 71},
}
@@ -65,10 +70,10 @@ func TestFactors(t *testing.T) {
}
var totativeRatioCases = map[uint]float64{
- 1: 1.0, 2: 0.5, 3: 2.0 / 3.0, 4: 0.5,
- 6: 1.0 / 3.0, 7: 6.0 / 7.0, 8: 0.5,
- 12: 1.0 / 3.0, 14: 3.0 / 7.0, 15: 8.0 / 15.0,
- 30: 4.0 / 15.0, 60: 4.0 / 15.0, 120: 4.0 / 15.0,
+ 1: 1.0, 2: 0.5, 3: 2.0 / 3.0, 4: 0.5, 6: 1.0 / 3.0, 7: 6.0 / 7.0, 8: 0.5,
+ 10: 0.4, 12: 1.0 / 3.0, 14: 3.0 / 7.0, 15: 8.0 / 15.0, 16: 0.5, 20: 0.4,
+ 24: 1.0 / 3.0, 30: 4.0 / 15.0, 60: 4.0 / 15.0, 71: 70.0 / 71.0,
+ 120: 4.0 / 15.0,
}
func totativeRatio(n uint) float64 {
@@ -80,14 +85,46 @@ func TestTotativeRatio(t *testing.T) {
}
var totientCases = map[uint]uint{
- 0: 0, 1: 1, 2: 1, 3: 2, 4: 2, 6: 2, 7: 6, 8: 4, 12: 4,
- 14: 6, 15: 8, 30: 8, 60: 16, 71: 70, 120: 32,
+ 0: 0, 1: 1, 2: 1, 3: 2, 4: 2, 6: 2, 7: 6, 8: 4, 10: 4, 12: 4,
+ 14: 6, 15: 8, 16: 8, 20: 8, 24: 8, 30: 8, 60: 16, 71: 70, 120: 32,
}
func TestTotativeCount(t *testing.T) {
tableTest(t, Totient, totientCases, stdEquals, "Totient")
}
+var totativeDigitCases = map[uint32][]uint32{
+ 0: []uint32{},
+ 1: []uint32{1},
+ 2: []uint32{1},
+ 3: []uint32{1, 2},
+ 4: []uint32{1, 3},
+ 6: []uint32{1, 5},
+ 7: []uint32{1, 2, 3, 4, 5, 6},
+ 8: []uint32{1, 3, 5, 7},
+ 10: []uint32{1, 3, 7, 9},
+ 12: []uint32{1, 5, 7, 11},
+ 14: []uint32{1, 3, 5, 9, 11, 13},
+ 15: []uint32{1, 2, 4, 7, 8, 11, 13, 14},
+ 16: []uint32{1, 3, 5, 7, 9, 11, 13, 15},
+ 20: []uint32{1, 3, 7, 9, 11, 13, 17, 19},
+ 24: []uint32{1, 5, 7, 11, 13, 17, 19, 23},
+ 30: []uint32{1, 7, 11, 13, 17, 19, 23, 29},
+ 60: []uint32{1, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 49, 53, 59},
+ 71: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70},
+ 120: []uint32{1, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 49, 53, 59,
+ 61, 67, 71, 73, 77, 79, 83, 89, 91, 97, 101, 103, 107, 109, 113, 119},
+}
+
+func TestTotativeDigits(t *testing.T) {
+ tableTest(t, TotativeDigits, totativeDigitCases, slices.Equal,
+ "TotativeDigits")
+}
+
var factorScoreCases = map[uint]float64{
1: 1.0,
2: 1.5,
@@ -109,13 +146,17 @@ func TestFactorScore(t *testing.T) {
}
var basicRankCases = map[uint]string{
- 2: "D-", 3: "E-", 4: "C~", 5: "F+", 6: "B~",
- 7: "F-", 8: "C-", 9: "E~", 10: "D+", 11: "F~", 12: "A-",
- 14: "D~", 15: "E+", 18: "B-", 20: "C+", 24: "A~", 30: "B+", 60: "A+",
+ // one test per rank, plus three extra (22, 25, 44) to test that
+ // n%3 = 1 vs n%3 = 2 doesn't matter
+ 2: "D-", 3: "E-", 4: "C~", 5: "F+", 6: "B~", 7: "F-", 8: "C-",
+ 9: "E~", 10: "D+", 11: "F~", 12: "A-", 14: "D~", 15: "E+", 18: "B-",
+ 20: "C+", 22: "D-", 24: "A~", 25: "F+", 30: "B+", 44: "C~", 60: "A+",
// test that it only depends on n%60
- 62: "D-", 63: "E-", 64: "C~", 65: "F+", 66: "B~",
- 67: "F-", 68: "C-", 69: "E~", 70: "D+", 71: "F~", 72: "A-",
- 74: "D~", 75: "E+", 78: "B-", 80: "C+", 84: "A~", 90: "B+", 120: "A+",
+ 62: "D-", 63: "E-", 64: "C~", 65: "F+", 66: "B~", 67: "F-", 68: "C-",
+ 69: "E~", 70: "D+", 71: "F~", 72: "A-", 74: "D~", 75: "E+", 78: "B-",
+ 80: "C+", 82: "D-", 84: "A~", 85: "F+", 90: "B+", 104: "C~", 120: "A+",
+ // test that it can extend to 0 and 1 as well
+ 0: "A+", 1: "F~",
}
func TestBasicRank(t *testing.T) {
@@ -124,6 +165,9 @@ func TestBasicRank(t *testing.T) {
var gcdCases = map[struct{ a, b uint32 }]uint32{
{0, 0}: 0, {1, 1}: 1,
+ {0, 1}: 1, {1, 0}: 1,
+ {0, 12}: 12, {12, 0}: 12,
+ {1, 12}: 1, {12, 1}: 1,
{6, 10}: 2, {10, 6}: 2,
{12, 20}: 4, {20, 12}: 4,
{20, 55}: 5, {55, 20}: 5,
@@ -172,9 +216,10 @@ func TestSAN(t *testing.T) {
}
var expOrdCases = map[uint]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,
+ 0: true, 1: true, 2: true, 4: true, 5: false, 6: true, 8: true, 12: true,
+ 16: true, 18: false, 20: false, 27: false, 30: true, 36: true, 44: false,
+ 60: true, 70: false, 90: false, 96: true, 101: false, 120: true, 144: true,
+ 180: true, 770: false, 900: true, 6912: true, 10010: false,
}
func TestExponentsOrdered(t *testing.T) {
@@ -201,8 +246,9 @@ var typeCases = map[uint]NumberType{
12: ColossallyAbundant, 18: Practical, 20: Practical, 24: Superabundant,
28: Practical, 30: OrderedExponent, 31: NotPractical, 34: NotPractical,
60: ColossallyAbundant, 70: NotPractical, 90: Practical, 100: Practical,
- 120: ColossallyAbundant, 144: OrderedExponent, 240: Superabundant,
- 360: ColossallyAbundant, 720: Superabundant, 770: NotPractical,
+ 120: ColossallyAbundant, 144: OrderedExponent, 180: Superabundant,
+ 240: Superabundant, 360: ColossallyAbundant, 720: Superabundant,
+ 770: NotPractical, 900: OrderedExponent, 1680: Superabundant,
1729: NotPractical, 6912: OrderedExponent, 10010: NotPractical,
10080: Superabundant, 15120: Superabundant, 25200: Superabundant,
27720: Superabundant, 55440: ColossallyAbundant,
diff --git a/factors/score.go b/factors/score.go
index c777d11..00988c9 100644
--- a/factors/score.go
+++ b/factors/score.go
@@ -34,7 +34,7 @@ func Score(n uint) float64 {
return float64(factorSum) / float64(n)
}
-const maxSmall = 1 << 28 - 1
+const maxSmall = 1<<28 - 1
func bigScore(n uint) float64 {
factorSum := new(big.Int)
diff --git a/factors/totative.go b/factors/totative.go
index abbbe89..d1947b6 100644
--- a/factors/totative.go
+++ b/factors/totative.go
@@ -1,7 +1,7 @@
package factors
-// Totient calculates the number of numbers less than n that
-// are totatives of n (share no factors with n)
+// Totient calculates the number of numbers between 1 and n inclusive
+// that are totatives of n (share no factors with n)
func Totient(n uint) uint {
if n == 0 {
return 0
@@ -18,12 +18,12 @@ 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).
+// TotativeDigits returns a slice containing every number between 1 and r
+// inclusive 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++ {
+ for d := uint32(1); d <= r; d++ {
if gcd(d, r) == 1 {
digits[totativesFound] = d
totativesFound++