Imports

CLRS Section 8.3 - Radix sort

This file proves the pure correctness spine for radix sort from the stable counting-sort theorem in Section 8.2.

The model is intentionally abstract. A list of digit functions is supplied in least-significant to most-significant order, and each pass is a stable countingSortBy over the current digit. The final theorems say that the result is ordered by the corresponding most-significant-first lexicographic relation, preserves membership, preserves the input order inside each complete digit signature, and hence captures the CLRS stable-pass proof spine.

The final layer instantiates the abstract digit interface with concrete base-b digits for natural-number keys, packages ordinary key ordering behind a named digit-order bridge, and discharges that bridge for bounded fixed-width keys. The final concrete theorem therefore says radix sort returns a list ordered by the ordinary natural-number key when every key is represented inside the supplied digit window.

namespace CLRSnamespace Chapter08universe uvariable {α : Type u}

Relation-ordered lists and digit lexicographic order

Pairwise list ordering by a relation. This is stronger than adjacent ordering and is convenient for stable bucket proofs, since filtering a pairwise-ordered list preserves the order relation.

abbrev OrderedRel (rel : α α Prop) (xs : List α) : Prop := xs.Pairwise rel

A single higher-priority digit extends an existing lower-priority order.

def LexWith (digit : α Nat) (rel : α α Prop) (x y : α) : Prop := digit x < digit y (digit x = digit y rel x y)

Accumulate a radix lexicographic relation from digits supplied least-significant first. Each later digit becomes higher priority.

def RadixRel : List (α Nat) (α α Prop) α α Prop | [], rel => rel | digit :: digits, rel => RadixRel digits (LexWith digit rel)

The public lexicographic relation induced by low-to-high radix digits.

def RadixLex (digitsLow : List (α Nat)) : α α Prop := RadixRel digitsLow (fun _ _ => True)
theorem orderedRel_trivial (xs : List α) : OrderedRel (fun _ _ => True) xs := induction xs with exact List.Pairwise.cons ( ) ihtheorem orderedRel_append_of_rel {rel : α α Prop} {xs ys : List α} (hxs : OrderedRel rel xs) (hys : OrderedRel rel ys) (hrel : x xs, y ys, rel x y) : OrderedRel rel (xs ++ ys) :=

Turn a pairwise relation order into the adjacent key-order predicate used by the counting-sort section, provided the relation implies key monotonicity on the same list.

theorem orderedBy_of_orderedRel_on {rel : α α Prop} {key : α Nat} {xs : List α} (hxs : OrderedRel rel xs) (hrel : x xs, y xs, rel x y key x key y) : OrderedBy key xs := induction xs with cases xs with cases hxs with exact hrel x ( ) y ( ) (hhead y ( )) exact hrel a ( ) b ( ) hab
theorem orderedRel_bucket {rel : α α Prop} {key : α Nat} {xs : List α} (k : Nat) (hxs : OrderedRel rel xs) : OrderedRel rel (bucket key xs k) := , hhead y hy exact hall y ( )

One stable radix pass

A stable counting-sort pass by a new digit upgrades a lower-priority relation to a lexicographic relation with the new digit as the most significant criterion.

exact Or.inl (Nat.lt_of_le_of_lt hxle ( ))

Radix sort

Radix sort by stable counting-sort passes. Digits are supplied in least-significant to most-significant order.

def radixSortBy (maxDigit : Nat) : List (α Nat) List α List α | [], xs => xs | digit :: digits, xs => radixSortBy maxDigit digits (countingSortBy maxDigit digit xs)

All digit functions are bounded by the declared maximum digit.

def AllDigitsLe (digitsLow : List (α Nat)) (xs : List α) (maxDigit : Nat) : Prop := digit digitsLow, AllKeysLe digit xs maxDigit

Stability by complete digit signatures

The subsequence of elements whose values match sample on every radix digit, preserving the ambient list order.

Equality of this list before and after radix sort is the direct stability statement: items with the same complete digit signature appear in their original relative order.

def digitClass (digitsLow : List (α Nat)) (sample : α) (xs : List α) : List α := xs.filter fun x => digitsLow.all fun digit => digit x == digit sample
theorem digitClass_cons_filter (digit : α Nat) (digitsLow : List (α Nat)) (sample : α) (xs : List α) : digitClass (digit :: digitsLow) sample xs = (digitClass digitsLow sample xs).filter (fun x => digit x == digit sample) := theorem digitClass_cons_bucket (digit : α Nat) (digitsLow : List (α Nat)) (sample : α) (xs : List α) : digitClass (digit :: digitsLow) sample xs = digitClass digitsLow sample (bucket digit xs (digit sample)) := theorem allDigitsLe_of_mem_iff {digitsLow : List (α Nat)} {xs ys : List α} {maxDigit : Nat} (h : AllDigitsLe digitsLow xs maxDigit) (hmem : x, x ys x xs) : AllDigitsLe digitsLow ys maxDigit := theorem radixSortBy_ordered_aux (maxDigit : Nat) (digitsLow : List (α Nat)) (rel : α α Prop) (xs : List α) (hxs : OrderedRel rel xs) : OrderedRel (RadixRel digitsLow rel) (radixSortBy maxDigit digitsLow xs) := induction digitsLow generalizing rel xs with

Radix sort returns a list ordered by the induced digit lexicographic order.

theorem radixSortBy_ordered (maxDigit : Nat) (digitsLow : List (α Nat)) (xs : List α) : OrderedRel (RadixLex digitsLow) (radixSortBy maxDigit digitsLow xs) :=
_ = digitClass (digit :: digits) sample xs := countingSortBy_digitClass_cons_eq maxDigit digit digits sample xs hdigit

Radix sort is stable with respect to complete digit signatures: filtering the output to all elements matching a fixed sample on every digit gives exactly the same ordered subsequence as filtering the input.

theorem radixSortBy_stable (maxDigit : Nat) (digitsLow : List (α Nat)) (xs : List α) (hdigits : AllDigitsLe digitsLow xs maxDigit) (sample : α) : digitClass digitsLow sample (radixSortBy maxDigit digitsLow xs) = digitClass digitsLow sample xs := radixSortBy_digitClass_eq maxDigit digitsLow xs hdigits sample

Reader-facing correctness theorem for abstract radix sort.

theorem radixSortBy_correct [DecidableEq α] (maxDigit : Nat) (digitsLow : List (α Nat)) (xs : List α) (hdigits : AllDigitsLe digitsLow xs maxDigit) : OrderedRel (RadixLex digitsLow) (radixSortBy maxDigit digitsLow xs) ( x, x radixSortBy maxDigit digitsLow xs x xs) (radixSortBy maxDigit digitsLow xs).Perm xs := radixSortBy_ordered maxDigit digitsLow xs, radixSortBy_mem_iff maxDigit digitsLow xs hdigits, radixSortBy_perm maxDigit digitsLow xs hdigits

Reader-facing correctness theorem including the explicit stability clause.

theorem radixSortBy_correct_stable [DecidableEq α] (maxDigit : Nat) (digitsLow : List (α Nat)) (xs : List α) (hdigits : AllDigitsLe digitsLow xs maxDigit) : OrderedRel (RadixLex digitsLow) (radixSortBy maxDigit digitsLow xs) ( sample, digitClass digitsLow sample (radixSortBy maxDigit digitsLow xs) = digitClass digitsLow sample xs) ( x, x radixSortBy maxDigit digitsLow xs x xs) (radixSortBy maxDigit digitsLow xs).Perm xs := radixSortBy_ordered maxDigit digitsLow xs, radixSortBy_stable maxDigit digitsLow xs hdigits, radixSortBy_mem_iff maxDigit digitsLow xs hdigits, radixSortBy_perm maxDigit digitsLow xs hdigits

Concrete base-b digit extraction

The ith least-significant base-base digit of a natural number.

For example, digit 0 is the units digit and digit 1 is the next more significant digit. The definition is intentionally small so the abstract radix-sort theorem above can be reused without changing its proof.

def baseDigit (base i n : Nat) : Nat := (n / base ^ i) % base

The low-to-high list of concrete base-base digit functions.

def baseDigitsLow (base digitCount : Nat) (key : α Nat) : List (α Nat) := (List.range digitCount).map fun i x => baseDigit base i (key x)

Every extracted base digit is bounded by base - 1.

theorem baseDigit_le_max (base i n : Nat) (hbase : 0 < base) : baseDigit base i n base - 1 :=

For a one-digit key smaller than the base, the extracted digit is the key.

theorem baseDigit_zero_eq_self_of_lt {base n : Nat} (hn : n < base) : baseDigit base 0 n = n :=

The concrete digit list satisfies the abstract bounded-digit hypothesis.

Reinterpreting the first digitCount extracted low-to-high digits gives the corresponding residue modulo base ^ digitCount.

If a key fits into the declared fixed-width digit window, the extracted digits reconstruct the key exactly.

Accumulator form of the radix lexicographic value lemma. The accumulator low stores lower-priority digits and is assumed to be smaller than the current place-value scale.

Radix lexicographic order on bounded digits is monotone for the natural number obtained by reinterpreting the low-to-high digit list.

Concrete base digits satisfy the bounded-digit side condition needed by radixLex_value_le.

The arithmetic bridge needed to turn digit-lexicographic order into ordinary key order on a concrete input domain.

def RadixDigitOrderRespectsKey (base digitCount : Nat) (key : α Nat) (domain : List α) : Prop := x domain, y domain, RadixLex (baseDigitsLow base digitCount key) x y key x key y

For fixed-width bounded keys, the concrete radix digit order respects ordinary natural-number key order.

Concrete radix sort for natural-number keys, using the low-to-high base digits of key.

def radixSortNatBy (base digitCount : Nat) (key : α Nat) (xs : List α) : List α := radixSortBy (base - 1) (baseDigitsLow base digitCount key) xs

Reader-facing concrete radix-sort correctness theorem.

The ordering clause is still the digit-lexicographic relation induced by the chosen concrete digits. A later arithmetic refinement can identify this relation with ordinary natural-number ordering under a bounded-key hypothesis.

theorem radixSortNatBy_correct_stable [DecidableEq α] (base digitCount : Nat) (key : α Nat) (xs : List α) (hbase : 0 < base) : OrderedRel (RadixLex (baseDigitsLow base digitCount key)) (radixSortNatBy base digitCount key xs) ( sample, digitClass (baseDigitsLow base digitCount key) sample (radixSortNatBy base digitCount key xs) = digitClass (baseDigitsLow base digitCount key) sample xs) ( x, x radixSortNatBy base digitCount key xs x xs) (radixSortNatBy base digitCount key xs).Perm xs :=

If every key in the input fits in one base-base digit, then the concrete radix digit order is already ordinary key order.

If the concrete digit lexicographic relation respects the natural key order on the input domain, concrete radix sort returns a list ordered by that key.

theorem radixSortNatBy_keyOrdered_of_digitOrder [DecidableEq α] (base digitCount : Nat) (key : α Nat) (xs : List α) (hbase : 0 < base) (hdigit_order : RadixDigitOrderRespectsKey base digitCount key xs) : OrderedBy key (radixSortNatBy base digitCount key xs) := exact orderedBy_of_orderedRel_on hcorrect.1 ( )

Concrete radix-sort correctness theorem with ordinary key ordering separated from the remaining base-b arithmetic obligation.

theorem radixSortNatBy_correct_keyOrdered_of_digitOrder [DecidableEq α] (base digitCount : Nat) (key : α Nat) (xs : List α) (hbase : 0 < base) (hdigit_order : RadixDigitOrderRespectsKey base digitCount key xs) : OrderedBy key (radixSortNatBy base digitCount key xs) ( sample, digitClass (baseDigitsLow base digitCount key) sample (radixSortNatBy base digitCount key xs) = digitClass (baseDigitsLow base digitCount key) sample xs) ( x, x radixSortNatBy base digitCount key xs x xs) (radixSortNatBy base digitCount key xs).Perm xs :=

Concrete one-pass radix-sort correctness when all keys are already below the base. This is the smallest arithmetic discharge of RadixDigitOrderRespectsKey.

theorem radixSortNatBy_correct_keyOrdered_singleDigit [DecidableEq α] (base : Nat) (key : α Nat) (xs : List α) (hbase : 0 < base) (hkeys : x xs, key x < base) : OrderedBy key (radixSortNatBy base 1 key xs) ( sample, digitClass (baseDigitsLow base 1 key) sample (radixSortNatBy base 1 key xs) = digitClass (baseDigitsLow base 1 key) sample xs) ( x, x radixSortNatBy base 1 key xs x xs) (radixSortNatBy base 1 key xs).Perm xs := radixSortNatBy_correct_keyOrdered_of_digitOrder base 1 key xs hbase (radixDigitOrderRespectsKey_singleDigit base key xs hkeys)

Concrete fixed-width radix-sort correctness for bounded natural-number keys. The bound key x < base ^ digitCount says the supplied digit window is wide enough to represent every input key.

theorem radixSortNatBy_correct_keyOrdered_of_bounded [DecidableEq α] (base digitCount : Nat) (key : α Nat) (xs : List α) (hbase : 0 < base) (hkeys : x xs, key x < base ^ digitCount) : OrderedBy key (radixSortNatBy base digitCount key xs) ( sample, digitClass (baseDigitsLow base digitCount key) sample (radixSortNatBy base digitCount key xs) = digitClass (baseDigitsLow base digitCount key) sample xs) ( x, x radixSortNatBy base digitCount key xs x xs) (radixSortNatBy base digitCount key xs).Perm xs := radixSortNatBy_correct_keyOrdered_of_digitOrder base digitCount key xs hbase (radixDigitOrderRespectsKey_of_bounded base digitCount key xs hbase hkeys)
end Chapter08end CLRS