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 }