Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
Every integer is the same mathematical object regardless of how we write it, but how we represent it determines the cost of every operation we perform. Computers use base 2 internally; we read base 10; cryptographers serialise large integers in base 16 or base 64; binary representations make multiplication by powers of 2 free; modular reduction by is just digit summing in base . The point of this task is to internalise the division-algorithm view of base- representation: every integer has a unique expansion with , and that uniqueness is just the division algorithm applied recursively. Once you see that, you'll see why every fast modular-arithmetic algorithm — square-and-multiply for exponentiation, Montgomery reduction, Barrett reduction — is fundamentally about choosing the right base.
For any integer and any base , there is a unique sequence of digits with , (unless ), and . The digits are computed by repeatedly dividing by and recording remainders — exactly the division algorithm, applied recursively.
to_base(1729, b) and against Python's bin/oct/hex.from_base(to_base(n, b), b) == n for several bases . This is the round-trip that ratifies uniqueness.Use these three in order. Each builds on the one before.
In one paragraph, explain what 'base $b$' means and how the division algorithm gives you the digits of any integer in any base. Why is the resulting digit sequence unique?
Walk me through *proving* that the base-$b$ representation of an integer is unique. The argument is a near-direct application of the division algorithm — show exactly how, by induction on the number of digits.
Why does choosing $b = 2^{32}$ or $b = 2^{64}$ as the working base for a big-integer library (instead of base 10 or base 2) make multiplication and modular reduction much faster? Tie this to how Montgomery reduction and Barrett reduction work in real cryptographic libraries.
to_base(123, 4)hex(305419896).// main.go
package main
import "fmt"
// toBase returns the digits of n in base b, most significant first.
func toBase(n, b int) []int {
if n == 0 {
return []int{0}
}
var digits []int
for n > 0 {
digits = append(digits, n%b)
n /= b
}
// reverse
for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {
digits[i], digits[j] = digits[j], digits[i]
}
return digits
}
// fromBase reconstructs the integer from digits in base b.
func fromBase(digits []int, b int) int {
n := 0
for _, d := range digits {
n = n*b + d
}
return n
}
func main() {
fmt.Println(toBase(255, 2)) // [1 1 1 1 1 1 1 1]
fmt.Println(toBase(255, 16)) // [15 15] -> ff
fmt.Println(toBase(2024, 7)) // [5 6 6 1 1] -> 5*7^4 + 6*7^3 + 6*7^2 + 1*7 + 1
fmt.Println(fromBase([]int{5, 6, 6, 1, 1}, 7)) // 2024
// Go fmt verbs for common bases
fmt.Printf("%b %x %o\n", 255, 255, 255) // 11111111 ff 377
fmt.Println(255) // decimal
}go run main.go