diff options
| author | Adrien Hopkins <adrien.p.hopkins@gmail.com> | 2023-08-30 13:53:53 -0500 |
|---|---|---|
| committer | Adrien Hopkins <adrien.p.hopkins@gmail.com> | 2023-08-30 14:05:09 -0500 |
| commit | 778220b2e3ce662e733727fb9a560fcfe85c19eb (patch) | |
| tree | 38d18d3db70943b42a62f1f786777022e954bd87 | |
| parent | b9164fb5b41136a63391f5675a848ec4a7711cb6 (diff) | |
Add digit map calculation
This is not in the output yet, but it will be soon - printing it is
another task since I want colours in my output.
| -rw-r--r-- | factors/digit_map.go | 140 | ||||
| -rw-r--r-- | factors/factorize.go | 4 | ||||
| -rw-r--r-- | factors/factors_test.go | 95 | ||||
| -rw-r--r-- | factors/mtc.go | 4 | ||||
| -rw-r--r-- | factors/type.go | 14 | ||||
| -rw-r--r-- | factors/uintpow.go | 9 | ||||
| -rw-r--r-- | radix_info.go | 2 |
7 files changed, 249 insertions, 19 deletions
diff --git a/factors/digit_map.go b/factors/digit_map.go new file mode 100644 index 0000000..e78cce0 --- /dev/null +++ b/factors/digit_map.go @@ -0,0 +1,140 @@ +package factors + +import "fmt" + +type DigitType struct { + regularity uint8 + totativeType TotativeType +} + +type TotativeType uint8 + +const ( + // This number does not have any totative factors + Regular TotativeType = iota + // This number's totative part is divisible by (r^2 - 1) + // - this makes it easier to work with + Neighbour + // This number's totative part is not divisible by (r^2 - 1) + // - it will not be nice to work with + Opaque + // This number is zero, and doesn't have a true totative type. + Zero +) + +// Zero and one will always have these types. +var ( + zeroType = DigitType{regularity: 0, totativeType: Zero} + oneType = DigitType{regularity: 0, totativeType: Regular} +) + +// Above this number, regularity indices will be shown as '+' +// instead of the number; this is to keep the code 2 characters long +const maxDisplayRegularity = 7 + +// The regularity index of a number in a radix, the smallest power of the +// radix that is divisible by the number's regular part. +func (dt DigitType) Regularity() uint8 { return dt.regularity } + +// The type of a number's totative part in a radix +func (dt DigitType) TotativeType() TotativeType { return dt.totativeType } + +// String returns a string representation of the digit type +// as a two-character code - the first representing regularity, +// the second totative type +func (dt DigitType) String() string { + var rString string + if dt.regularity > maxDisplayRegularity { + rString = "+" + } else { + rString = fmt.Sprint(dt.regularity) + } + + var tString string + switch dt.totativeType { + case Zero: + tString = "0" + case Regular: + tString = "R" + case Neighbour: + tString = "N" + case Opaque: + tString = "P" + } + + return rString + tString +} + +// Splits a digit in a radix into its regular and totative parts +func splitPF(digit, radix PrimeFactorization) (regular, totative uint) { + regular, totative = 1, 1 + for p, e := range digit.exponents { + if radix.exponents[p] != 0 { + regular *= uintpow(p, e) + } else { + totative *= uintpow(p, e) + } + } + return regular, totative +} + +// Splits a digit in a radix into its regular and totative parts +func Split(digit, radix uint) (regular, totative uint) { + return splitPF(PrimeFactorize(digit), PrimeFactorize(radix)) +} + +// Calculates the regularity index of a number's regular part +func calcRegularity(regular, radix uint) uint8 { + regularity, radixPower := uint8(0), uint(1) + for radixPower%regular != 0 { + regularity++ + radixPower *= radix + } + return regularity +} + +// Calculates the totative type of a number's totative part +func calcTotativeType(totative, radix uint) TotativeType { + switch true { + case totative == 0: + return Zero + case totative == 1: + return Regular + case (radix*radix-1)%totative == 0: + return Neighbour + default: + return Opaque + } +} + +// Gets the regularity and totative type of one digit in a radix +func GetDigitType(digit, radix uint) DigitType { + if radix < 2 { + panic("Radices cannot be less than 2!") + } + radixPF := PrimeFactorize(radix) + digitPF := PrimeFactorize(digit) + regular, totative := splitPF(digitPF, radixPF) + regularity := calcRegularity(regular, radix) + totativeType := calcTotativeType(totative, radix) + return DigitType{regularity, totativeType} +} + +// Gets the regularity and totative type of each digit in a radix +func DigitMap(radix uint) []DigitType { + if radix < 2 { + panic("Radices cannot be less than 2!") + } + radixPF := PrimeFactorize(radix) + types := make([]DigitType, radix, radix) + types[0] = zeroType + types[1] = oneType + for d := uint(2); d < radix; d++ { + digitPF := PrimeFactorize(d) + regular, totative := splitPF(digitPF, radixPF) + regularity := calcRegularity(regular, radix) + totativeType := calcTotativeType(totative, radix) + types[d] = DigitType{regularity, totativeType} + } + return types +} diff --git a/factors/factorize.go b/factors/factorize.go index 2f7368a..8e82b16 100644 --- a/factors/factorize.go +++ b/factors/factorize.go @@ -11,14 +11,14 @@ func Factors(n uint) []uint { factors := []uint{1} for p, e := range primeFactorization.exponents { - next_factors := make([]uint, 0, len(factors) * int(e)) + next_factors := make([]uint, 0, len(factors)*int(e)) for _, factor := range factors { for i := uint(0); i <= e; i++ { multiplier := uint(1) for j := uint(0); j < i; j++ { multiplier *= p } - next_factors = append(next_factors, factor * multiplier) + next_factors = append(next_factors, factor*multiplier) } } factors = next_factors diff --git a/factors/factors_test.go b/factors/factors_test.go index b7f48b9..8373ede 100644 --- a/factors/factors_test.go +++ b/factors/factors_test.go @@ -3,6 +3,7 @@ package factors import ( "fmt" "maps" + "slices" "testing" ) @@ -19,6 +20,8 @@ func tableTest[IN comparable, OUT any](t *testing.T, toTest func(IN) OUT, } } +type uintPair struct{ a, b uint } + var primeFactorCases = map[uint]PrimeFactorization{ 0: PrimeFactorization{map[uint]uint{0: 1}}, 1: PrimeFactorization{map[uint]uint{}}, @@ -58,8 +61,8 @@ 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, + 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, } @@ -163,7 +166,7 @@ func TestPractical(t *testing.T) { tableTest(t, practical, practicalCases, stdEquals, "practical") } -var typeCases = map[uint32]NumberType { +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, @@ -178,6 +181,92 @@ func TestType(t *testing.T) { tableTest(t, Type, typeCases, stdEquals, "Type") } +// ====== DIGIT MAP TESTS ====== +var ( + zt = zeroType // 00 - zero + ot = oneType // 0R - one + ft = DigitType{1, Regular} // 1R - factors + rt = DigitType{2, Regular} // 2R - regulars (index 2) + nt = DigitType{0, Neighbour} // 0N - neighbours + pt = DigitType{0, Opaque} // 0P - opaque totatives +) +var digitMapCases = map[uint][]DigitType{ + 2: []DigitType{zt, ot}, + 3: []DigitType{zt, ot, nt}, + 4: []DigitType{zt, ot, ft, nt}, + 5: []DigitType{zt, ot, nt, nt, nt}, + 6: []DigitType{zt, ot, ft, ft, rt, nt}, + 8: []DigitType{zt, ot, ft, nt, ft, pt, DigitType{1, Neighbour}, nt}, + 10: []DigitType{zt, ot, ft, nt, rt, ft, DigitType{1, Neighbour}, pt, + DigitType{3, Regular}, nt}, + 12: []DigitType{zt, ot, ft, ft, ft, pt, ft, pt, rt, rt, + DigitType{1, Opaque}, nt}, + 16: []DigitType{zt, ot, ft, nt, ft, nt, DigitType{1, Neighbour}, pt, + ft, pt, DigitType{1, Neighbour}, pt, DigitType{1, Neighbour}, + pt, DigitType{1, Opaque}, nt}, +} + +func TestDigitMap(t *testing.T) { + tableTest(t, DigitMap, digitMapCases, slices.Equal, "DigitMap") +} + +// ensures GetDigitType(d, r) == DigitMap(r)[d]; +// does not ensure it has the correct value (that's TestDigitMap's job) +func TestGetDigitType(t *testing.T) { + for radix := range digitMapCases { + digitMap := DigitMap(radix) + for digit := range digitMap { + actual := GetDigitType(uint(digit), radix) + if actual != digitMap[digit] { + t.Logf("GetDigitType(%d, %d) = %d", digit, radix, actual) + t.Errorf("GetDigitType(%d, %d) != DigitMap(%d)[%d]", + digit, radix, radix, digit) + } + } + } +} + +var splitCases = map[uintPair]uintPair{ + // digit, radix, regular part, totative part + uintPair{0, 0}: uintPair{0, 1}, + uintPair{0, 2}: uintPair{1, 0}, + uintPair{0, 12}: uintPair{1, 0}, + uintPair{1, 0}: uintPair{1, 1}, + uintPair{20, 0}: uintPair{1, 20}, + uintPair{360, 0}: uintPair{1, 360}, + uintPair{1, 2}: uintPair{1, 1}, + uintPair{1, 3}: uintPair{1, 1}, + uintPair{1, 7}: uintPair{1, 1}, + uintPair{1, 12}: uintPair{1, 1}, + uintPair{1, 39}: uintPair{1, 1}, + uintPair{1, 2948}: uintPair{1, 1}, + uintPair{5, 6}: uintPair{1, 5}, + uintPair{10, 6}: uintPair{2, 5}, + uintPair{12, 6}: uintPair{12, 1}, + uintPair{15, 6}: uintPair{3, 5}, + uintPair{35, 6}: uintPair{1, 35}, + uintPair{56, 6}: uintPair{8, 7}, + uintPair{5, 12}: uintPair{1, 5}, + uintPair{10, 12}: uintPair{2, 5}, + uintPair{12, 12}: uintPair{12, 1}, + uintPair{15, 12}: uintPair{3, 5}, + uintPair{35, 12}: uintPair{1, 35}, + uintPair{56, 12}: uintPair{8, 7}, + uintPair{12, 7}: uintPair{1, 12}, + uintPair{56, 7}: uintPair{7, 8}, + uintPair{70, 10}: uintPair{10, 7}, +} + +// testing version of Split +func splitTest(a uintPair) uintPair { + regular, totative := Split(a.a, a.b) + return uintPair{regular, totative} +} + +func TestSplit(t *testing.T) { + tableTest(t, splitTest, splitCases, stdEquals, "Split") +} + // to be used as the equal paramater for tableTest func stdEquals[T comparable](a, b T) bool { return a == b } diff --git a/factors/mtc.go b/factors/mtc.go index 522a35c..655124c 100644 --- a/factors/mtc.go +++ b/factors/mtc.go @@ -5,7 +5,7 @@ package factors // multiplication table. func MTC(n uint64) uint64 { mtc := uint64(0) - for i := uint64(2); i <= n - 2; i++ { + for i := uint64(2); i <= n-2; i++ { mtc += n / gcd(i, n) } return mtc @@ -13,7 +13,7 @@ func MTC(n uint64) uint64 { func gcd(a, b uint64) uint64 { for b > 0 { - a, b = b, a % b + a, b = b, a%b } return a } diff --git a/factors/type.go b/factors/type.go index e587ebb..39aab9b 100644 --- a/factors/type.go +++ b/factors/type.go @@ -52,7 +52,7 @@ func Type(n uint32) NumberType { func isSAN(n uint32) bool { if n <= 2 { return true - } else if n % 4 != 0 && n % 6 != 0 { + } else if n%4 != 0 && n%6 != 0 { return false } @@ -77,7 +77,7 @@ func isSAN(n uint32) bool { func exponentsOrdered(n uint32) bool { if n <= 2 { return true - } else if n % 4 != 0 && n % 6 != 0 { + } else if n%4 != 0 && n%6 != 0 { return false } @@ -102,7 +102,7 @@ func exponentsOrdered(n uint32) bool { func practical(n uint32) bool { if n <= 2 { return true - } else if n % 4 != 0 && n % 6 != 0 { + } else if n%4 != 0 && n%6 != 0 { return false } @@ -126,11 +126,3 @@ func practical(n uint32) bool { 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/factors/uintpow.go b/factors/uintpow.go new file mode 100644 index 0000000..85db67f --- /dev/null +++ b/factors/uintpow.go @@ -0,0 +1,9 @@ +package factors + +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 daf62df..1a8cccb 100644 --- a/radix_info.go +++ b/radix_info.go @@ -29,7 +29,7 @@ func main() { factors.MTC(uint64(n))) } else { fmt.Printf("Multiplication Table Complexity ≤ %.4g\n", - float32(n) * float32(n - 2)) + float32(n)*float32(n-2)) } fmt.Printf("Natural Logarithm: %.2f\n", math.Log(float64(n))) } else { |
