summaryrefslogtreecommitdiff
path: root/factors
diff options
context:
space:
mode:
authorAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-08-30 13:53:53 -0500
committerAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-08-30 14:05:09 -0500
commit778220b2e3ce662e733727fb9a560fcfe85c19eb (patch)
tree38d18d3db70943b42a62f1f786777022e954bd87 /factors
parentb9164fb5b41136a63391f5675a848ec4a7711cb6 (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.
Diffstat (limited to 'factors')
-rw-r--r--factors/digit_map.go140
-rw-r--r--factors/factorize.go4
-rw-r--r--factors/factors_test.go95
-rw-r--r--factors/mtc.go4
-rw-r--r--factors/type.go14
-rw-r--r--factors/uintpow.go9
6 files changed, 248 insertions, 18 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
+}