summaryrefslogtreecommitdiff
path: root/factors
diff options
context:
space:
mode:
authorAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-10-09 16:15:48 -0500
committerAdrien Hopkins <adrien.p.hopkins@gmail.com>2023-10-09 16:16:29 -0500
commitc50ece109cdb29ab5d3fa7444040b546da7e360c (patch)
tree33fbf5678f2030a85f6c4b4f20ff5fc302516c53 /factors
parentdbd02c8437ae634f4ece3c2979afeb19a3979f61 (diff)
factors: Give all exported members proper godoc
Diffstat (limited to 'factors')
-rw-r--r--factors/digit_map.go57
-rw-r--r--factors/factorize.go2
-rw-r--r--factors/mtc.go17
-rw-r--r--factors/score.go86
-rw-r--r--factors/totative.go4
-rw-r--r--factors/type.go6
6 files changed, 132 insertions, 40 deletions
diff --git a/factors/digit_map.go b/factors/digit_map.go
index a16f018..7c56a1e 100644
--- a/factors/digit_map.go
+++ b/factors/digit_map.go
@@ -2,11 +2,25 @@ package factors
import "fmt"
+/*
+A DigitType represents the number-theoretical type of a digit in a radix.
+It is composed of two parts:
+ - The regularity index, which is the exponent of the smallest power
+ of the radix that the digit's regular part (as defined by [Split])
+ is a factor of.
+ - The totative type, which is the [TotativeType] of
+ the radix's totative part (as defined by [Split]).
+
+The zero value of DigitType is the type 00,
+the type that the digit zero has in every nonzero radix.
+*/
type DigitType struct {
regularity uint8
totativeType TotativeType
}
+// TotativeType classifies the totative parts (as determined by [Split])
+// of digits in radices.
type TotativeType byte
const (
@@ -42,16 +56,28 @@ var (
// 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
+// The regularity index of a number in a radix - the smallest power of the
// radix that is divisible by the number's regular part.
+//
+// If this is zero, and the digit isn't, this digit is a totative.
func (dt DigitType) Regularity() uint8 { return dt.regularity }
-// The type of a number's totative part in a radix
+// TotativeType returns the type of a number's totative part in a radix.
+// This is one of the possible values of [TotativeType].
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
+// String returns a string representation of the digit type.
+//
+// This representation is a two-character code.
+// The first character is the type's [DigitType.Regularity],
+// or "+" if it is above 7.
+// The second character is a character representing the [TotativeType]:
+// - [Regular] is 'R'
+// - [Omega] is 'ω'
+// - [Alpha] is 'α'
+// - [Pseudoneighbour] is 'N'
+// - [Opaque] is 'P' (to avoid confusing it with [Zero])
+// - [Zero] is '0'
func (dt DigitType) String() string {
var rString string
if dt.regularity > maxDisplayRegularity {
@@ -92,7 +118,19 @@ func splitPF(digit, radix PrimeFactorization) (regular, totative uint) {
return regular, totative
}
-// Splits a digit in a radix into its regular and totative parts
+/*
+Split splits a digit in a radix into its regular and totative parts.
+The regular part will be a divisor of some power of the radix.
+The totative part will be coprime to the radix,
+except if the digit or radix are zero.
+The product of the regular and totative parts will be equal to the digit.
+
+Special cases (follow from PrimeFactorize(0) = 0^1):
+
+ Split(0, x) = 1, 0
+ Split(x, 0) = 1, x
+ Split(0, 0) = 0, 1
+*/
func Split(digit, radix uint) (regular, totative uint) {
return splitPF(PrimeFactorize(digit), PrimeFactorize(radix))
}
@@ -129,7 +167,8 @@ func calcTotativeType(totative, radix uint) TotativeType {
}
}
-// Gets the regularity and totative type of one digit in a radix
+// Gets the regularity and totative type of one digit in a radix.
+//
// In radix zero, zero is type 1R, one is type 0R, and everything else is 0P.
func GetDigitType(digit, radix uint) DigitType {
radixPF := PrimeFactorize(radix)
@@ -140,7 +179,9 @@ func GetDigitType(digit, radix uint) DigitType {
return DigitType{regularity, totativeType}
}
-// Gets the regularity and totative type of each digit in a radix
+// DigitMap gets the [DigitType] of every digit in a radix.
+//
+// DigitMap(0) returns an empty slice.
func DigitMap(radix uint) []DigitType {
radixPF := PrimeFactorize(radix)
types := make([]DigitType, radix, radix)
diff --git a/factors/factorize.go b/factors/factorize.go
index 9aa43a7..16cd810 100644
--- a/factors/factorize.go
+++ b/factors/factorize.go
@@ -2,6 +2,8 @@ package factors
// Factors returns a number's factors as a slice.
// The slice is not guaranteed to be in sorted order.
+//
+// Because every number is a factor of zero, Factors(0) panics.
func Factors(n uint) []uint {
if n == 0 {
panic("Cannot get factors of 0!")
diff --git a/factors/mtc.go b/factors/mtc.go
index 024391e..4d40396 100644
--- a/factors/mtc.go
+++ b/factors/mtc.go
@@ -3,10 +3,21 @@ package factors
// MTC returns the multiplication table complexity of a radix n.
// This is an estimate of how difficult it is to learn a radix's
// multiplication table.
-func MTC(n uint32) uint64 {
+//
+// The MTC is a sum of each digit's row complexity,
+// which is the length of the pattern of the product's last digit
+// (equal to digit / gcd(digit, radix)).
+// Digits 1 and radix - 1 are excluded because they have simpler patterns
+// than the other digits.
+//
+// uint32 is used as the input type and uint64 as the output to prevent
+// integer overflow (and because the MTC algorithm is slow).
+//
+// The MTC of 0 and 1 are both zero.
+func MTC(radix uint32) uint64 {
mtc := uint64(0)
- for i := uint32(2); i <= n-2; i++ {
- mtc += uint64(n / gcd(i, n))
+ for digit := uint32(2); digit <= radix-2; digit++ {
+ mtc += uint64(radix / gcd(digit, radix))
}
return mtc
}
diff --git a/factors/score.go b/factors/score.go
index 00988c9..b1da963 100644
--- a/factors/score.go
+++ b/factors/score.go
@@ -5,21 +5,29 @@ import (
"math/big"
)
-// Score returns a "factor score" equal to the sum of the reciprocoals
-// of the number n's factors.
-// Rationale:
-// A number's factors are one of the most important things that determines
-// how well it functions as a number base. An easy way to determine how
-// good these factors are is to simply count them, but that comes with
-// the problem that all factors are considered equal when they really aren't
-// - divisibility by 2 (which ensures 1/2 is a terminating fraction and
-// you can tell whether a number is even or odd by looking at the last digit)
-// is more important than divisibility by 23. The most obvious way of
-// accounting for this is by giving each factor a score and adding those
-// scores to get the overall score. I chose score(f) = 1/f because it is
-// the simplest function that captures the intuition that smaller factors
-// are more valuable than larger ones. It also gives the score a meaning:
-// a factor's score is the probability a random number is divisible by it.
+/*
+Score returns a "factor score" equal to the sum of the reciprocoals
+of the number n's factors.
+
+Rationale:
+A number's factors are one of the most important things that determines
+how well it functions as a number radix. An easy way to determine how
+good these factors are is to simply count them, but that comes with
+the problem that all factors are considered equal when they really aren't
+- divisibility by 2 (which ensures 1/2 is a terminating fraction and
+you can tell whether a number is even or odd by looking at the last digit)
+is more important than divisibility by 23. The most obvious way of
+accounting for this is by giving each factor a score and adding those
+scores to get the overall score. I chose score(f) = 1/f because it is
+the simplest function that captures the intuition that smaller factors
+are more valuable than larger ones. It also gives the score a meaning:
+a factor's score is the probability a random number is divisible by it.
+
+Special cases:
+
+ Score(0) = NaN
+ Score(1) = 1.0
+*/
func Score(n uint) float64 {
if n == 0 {
return math.NaN()
@@ -54,30 +62,54 @@ func bigScore(n uint) float64 {
return score64
}
-// BasicRank returns a rank describing how well a radix handles the simplest
-// fractions (1/2, 1/3, 1/4 and 1/5). Zero and one are not true radices,
-// but because this rank otherwise only depends on a radix's remainder
-// mod 60, they have the same ranks as 60 and 61 (A+, F~).
-// Also known as 2345 Rank.
-func BasicRank(n uint) string {
+/*
+BasicRank returns a rank describing how well a radix handles the simplest
+fractions (1/2, 1/3, 1/4, 1/5). It consists of two parts: a letter (A-F)
+and a sign (+, -, ~). It is also known as the 2345 Rank.
+
+The letter determines how well the first three fractions are handled
+(specifically, what their decimal expansions look like):
+ - A means all three terminate with one digit.
+ - B means 1/2 and 1/3 terminate with one digit,
+ and 1/4 terminates with two digits.
+ - C means 1/2 and 1/4 terminate with one digit,
+ but 1/3 is infinitely repeating (pattern length 1-2 digits).
+ - D means 1/2 terminates with one digit,
+ but 1/3 is infinitely repeating (pattern length 1-2 digits),
+ and 1/4 terminates with two digits.
+ - E means 1/3 terminates with one digit,
+ but 1/2 and 1/4 are infinitely repeating
+ (pattern lengths 1 and 1-2 respectively).
+ - F means all three are infinitely repeating
+ (pattern lengths 1, 1-2 and 1-2 respectively).
+
+The sign determines how well 1/5 is handled:
+ - A plus means it terminates with one digit.
+ - A tilde means it repeats with a short (1-2 digit) pattern.
+ - A minus means it repeats with a long (4-digit) pattern.
+
+Because the rank only depends on radix % 60, 0 and 1 are given the same
+ranks as 60 and 61 (A+, F~).
+*/
+func BasicRank(radix uint) string {
var firstRank, secondRank string
switch uint(0) {
- case n % 12:
+ case radix % 12:
firstRank = "A"
- case n % 6:
+ case radix % 6:
firstRank = "B"
- case n % 4:
+ case radix % 4:
firstRank = "C"
- case n % 2:
+ case radix % 2:
firstRank = "D"
- case n % 3:
+ case radix % 3:
firstRank = "E"
default:
firstRank = "F"
}
- switch n % 5 {
+ switch radix % 5 {
case 0:
secondRank = "+"
case 1, 4:
diff --git a/factors/totative.go b/factors/totative.go
index d1947b6..9dde4b0 100644
--- a/factors/totative.go
+++ b/factors/totative.go
@@ -1,7 +1,7 @@
package factors
// Totient calculates the number of numbers between 1 and n inclusive
-// that are totatives of n (share no factors with n)
+// that are totatives of n (share no factors with n).
func Totient(n uint) uint {
if n == 0 {
return 0
@@ -20,6 +20,8 @@ func Totient(n uint) uint {
// TotativeDigits returns a slice containing every number between 1 and r
// inclusive that is a totative of r (shares no factors with r).
+//
+// The returned slice's length is [Totient](r).
func TotativeDigits(r uint32) []uint32 {
digits := make([]uint32, Totient(uint(r)))
totativesFound := 0
diff --git a/factors/type.go b/factors/type.go
index 33c48bd..91eb94e 100644
--- a/factors/type.go
+++ b/factors/type.go
@@ -41,7 +41,10 @@ var superabundantNums = [...]uint64{
6133685312708553600, 9200527969062830400, 18401055938125660800,
}
-// A type of number (as determined by its factors)
+// A classification of numbers, based on how many factors they have.
+// Each type is a subset of the next (except [Practical] and [NotPractical]),
+// so a number is only counted as its most exclusive type.
+// The zero value of this type is invalid.
type NumberType byte
const (
@@ -64,6 +67,7 @@ const (
NotPractical NumberType = 0x40
)
+// Type determines the [NumberType] of a number.
func Type(n uint) NumberType {
if slices.Contains(colossallyAbundantNums[:], uint64(n)) {
return ColossallyAbundant