summaryrefslogtreecommitdiff
path: root/factors/digit_map.go
blob: db562db042cb65aba7aae883b7ca482b861c71b7 (plain)
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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 - 1)
	// - this gives it the simplest possible decimal expansion
	// for a non-regular (1 digit repeating) and a simple divisibility
	// test (sum digits, like 3 or 9 in decimal)
	Omega
	// This number's totative part is divisible by (r + 1)
	// - this makes it slightly more complicated than omega
	Alpha
	// This number's totative part is divisible by (r^2 - 1)
	// but not (r + 1) or (r - 1)
	// - these totatives straddle the line between simple and complex
	Pseudoneighbour
	// 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 Omega:
		tString = "ω"
	case Alpha:
		tString = "α"
	case Pseudoneighbour:
		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-1)%totative == 0:
		return Omega
	case (radix+1)%totative == 0:
		return Alpha
	case (radix*radix-1)%totative == 0:
		return Pseudoneighbour
	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
}