package factors import ( "fmt" "maps" "math" "testing" ) // Run a test with a table of cases func tableTest[IN comparable, OUT any](t *testing.T, toTest func(IN) OUT, cases map[IN]OUT, equal func(OUT, OUT) bool, name string) { for input, expected := range cases { t.Run(fmt.Sprintf("%v", input), func(t *testing.T) { actual := toTest(input) if !equal(expected, actual) { t.Errorf("%s(%v) = %v, expect %v", name, input, actual, expected) } }) } } var primeFactorCases = map[uint]PrimeFactorization{ 0: PrimeFactorization{map[uint]uint{0: 1}}, 1: PrimeFactorization{map[uint]uint{}}, 2: PrimeFactorization{map[uint]uint{2: 1}}, 3: PrimeFactorization{map[uint]uint{3: 1}}, 4: PrimeFactorization{map[uint]uint{2: 2}}, 6: PrimeFactorization{map[uint]uint{2: 1, 3: 1}}, 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}}, 86400: PrimeFactorization{map[uint]uint{2: 7, 3: 3, 5: 2}}, } func TestPrimeFactorize(t *testing.T) { equal := func(a, b PrimeFactorization) bool { return maps.Equal(a.exponents, b.exponents) } tableTest(t, PrimeFactorize, primeFactorCases, equal, "PrimeFactorize") } var factorCases = map[uint][]uint{ 1: []uint{1}, 2: []uint{1, 2}, 4: []uint{1, 2, 4}, 6: []uint{1, 2, 3, 6}, 10: []uint{1, 2, 5, 10}, 12: []uint{1, 2, 3, 4, 6, 12}, 13: []uint{1, 13}, 15: []uint{1, 3, 5, 15}, 18: []uint{1, 2, 3, 6, 9, 18}, 60: []uint{1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60}, } func TestFactors(t *testing.T) { tableTest(t, Factors, factorCases, setEquals, "Factors") } var totativeRatioCases = map[uint]float64{ 1: 1.0, 2: 0.5, 3: 2.0 / 3.0, 4: 0.5, 6: 1.0 / 3.0, 8: 0.5, 12: 1.0 / 3.0, } func TestTotativeRatio(t *testing.T) { equals := func(a, b float64) bool { return floatEquals(a, b, 1e-15) } tableTest(t, TotativeRatio, totativeRatioCases, equals, "TotativeRatio") } var factorScoreCases = map[uint]float64{ 1: 1.0, 2: 1.5, 3: 4.0 / 3.0, 4: 1.75, 6: 2.0, 8: 1.875, 10: 1.8, 12: 7.0 / 3.0, 120: 3.0, } func TestFactorScore(t *testing.T) { // factors.Score is accurate enough that we can test for exact floats! tableTest(t, Score, factorScoreCases, stdEquals, "Score") } 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+", } func TestBasicRank(t *testing.T) { tableTest(t, BasicRank, basicRankCases, stdEquals, "BasicRank") } var gcdCases = map[struct{ a, b uint64 }]uint64{ {0, 0}: 0, {1, 1}: 1, {6, 10}: 2, {10, 6}: 2, {12, 20}: 4, {20, 12}: 4, {20, 55}: 5, {55, 20}: 5, {60, 120}: 60, {120, 60}: 60, } // a version of gcd() for testing purposes func gcdTest(c struct{ a, b uint64 }) uint64 { return gcd(c.a, c.b) } func TestGCD(t *testing.T) { tableTest(t, gcdTest, gcdCases, stdEquals, "gcd") } var mtcCases = map[uint64]uint64{ 2: 0, 3: 0, 4: 2, 5: 10, 6: 8, 7: 28, 8: 26, 9: 42, 10: 42, 11: 88, 12: 52, 13: 130, 14: 100, 15: 116, 16: 138, 18: 146, 20: 190, 24: 252, 28: 416, 30: 380, 32: 618, 36: 598, 48: 1100, 60: 1496, 64: 2602, 90: 3662, 120: 6080, 210: 18542, 360: 54362, 840: 270122, 2520: 2363528, 5040: 9409112, } func TestMTC(t *testing.T) { 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 func stdEquals[T comparable](a, b T) bool { return a == b } func setEquals[E comparable](a, b []E) bool { // use maps to simulate sets // aSet[a] == true means set contains a, false means not aSet := make(map[E]bool) bSet := make(map[E]bool) for _, i := range a { aSet[i] = true } for _, j := range b { bSet[j] = true } return maps.Equal(aSet, bSet) } func floatEquals(a, b, maxDelta float64) bool { return math.Abs(a-b) <= maxDelta*math.Max(math.Abs(a), math.Abs(b)) }