package factors import ( "fmt" "math" "testing" ) 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) { for i, expected := range primeFactorCases { testname := fmt.Sprintf("%d", i) t.Run(testname, func(t *testing.T) { actual := PrimeFactorize(i) if !mapEquals(expected.exponents, actual.exponents) { t.Errorf("PrimeFactorize(%d) = %s, want %s", i, actual, expected) } }) } } 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) { for i, expected := range factorCases { testname := fmt.Sprintf("%d", i) t.Run(testname, func(t *testing.T) { actual := Factors(i) if !setEquals(expected, actual) { t.Errorf("Factors(%d) = %v, want %v", i, actual, expected) } }) } } 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) { for i, expected := range totativeRatioCases { testname := fmt.Sprintf("%d", i) t.Run(testname, func(t *testing.T) { actual := TotativeRatio(i) if !floatEquals(expected, actual, 1e-15) { t.Errorf("TotativeRatio(%d) = %v, want %v", i, actual, expected) } }) } } 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) { for i, expected := range factorScoreCases { testname := fmt.Sprintf("%d", i) t.Run(testname, func(t *testing.T) { actual := Score(i) // factors.Score is accurate enough that we can test // for exact float values! if expected != actual { t.Errorf("Score(%d) = %v, want %v", i, actual, expected) } }) } } func mapEquals[K comparable, V comparable](a, b map[K]V) bool { for k := range a { if a[k] != b[k] { return false } } for k := range b { if a[k] != b[k] { return false } } return true } func setEquals(a, b []uint) bool { // use maps to simulate sets // aSet[a] == true means set contains a, false means not aSet := make(map[uint]bool) bSet := make(map[uint]bool) for _, i := range a { aSet[i] = true } for _, j := range b { bSet[j] = true } return mapEquals(aSet, bSet) } func floatEquals(a, b, maxDelta float64) bool { return math.Abs(a-b) <= maxDelta*math.Max(math.Abs(a), math.Abs(b)) }