Imports
import Mathlib

CLRS Section 9.2 - Selection by rank

This file gives the first Chapter 9 correctness interface: a selector returns an element with the requested zero-based order statistic. The implementation is the simple specification program obtained by sorting and indexing; randomized or deterministic linear-time SELECT can later refine to this interface.

The public certificate is phrased by counts, so duplicates are handled in the usual order-statistic way: if the selected value is x, then at most k elements are strictly smaller than x, and more than k elements are at most x.

namespace CLRSnamespace Chapter09

Specification selector and rank certificate

A sorted copy of the input, used as the executable specification.

def sortedCopy (xs : List Nat) : List Nat := xs.mergeSort (fun x y => decide (x y))

Number of input elements strictly smaller than x.

def ltCount (x : Nat) (xs : List Nat) : Nat := (xs.filter (fun y => decide (y < x))).length

Number of input elements at most x.

def leCount (x : Nat) (xs : List Nat) : Nat := (xs.filter (fun y => decide (y x))).length

Number of input elements at least x.

def geCount (x : Nat) (xs : List Nat) : Nat := (xs.filter (fun y => decide (x y))).length

Number of input elements strictly greater than x.

def gtCount (x : Nat) (xs : List Nat) : Nat := (xs.filter (fun y => decide (x < y))).length

Select the zero-based rank k, if the input has that many elements.

def selectByRank? (k : Nat) (xs : List Nat) : Option Nat := (sortedCopy xs)[k]?

Rank certificate for order statistics with duplicates. The selected value x is present in the input, the number of values below it is at most k, and the number of values at most it is greater than k.

def RankCertificate (xs : List Nat) (k x : Nat) : Prop := x xs ltCount x xs k k < leCount x xs
theorem sortedCopy_perm (xs : List Nat) : (sortedCopy xs).Perm xs := theorem sortedCopy_pairwise (xs : List Nat) : (sortedCopy xs).Pairwise (fun x y => x y) :=

List lemmas for the rank proof

theorem pairwise_split_bounds {lo hi : List Nat} {x : Nat} (h : (lo ++ x :: hi).Pairwise (fun x y => x y)) : ( y lo, y x) ( y hi, x y) := exact hcross y hy x ( ) cases hxhi with theorem ltCount_eq_of_perm {xs ys : List Nat} {x : Nat} (h : xs.Perm ys) : ltCount x xs = ltCount x ys := theorem leCount_eq_of_perm {xs ys : List Nat} {x : Nat} (h : xs.Perm ys) : leCount x xs = leCount x ys := theorem geCount_eq_length_sub_ltCount (x : Nat) : xs : List Nat, geCount x xs = xs.length - ltCount x xs := induction xs with theorem gtCount_eq_length_sub_leCount (x : Nat) : xs : List Nat, gtCount x xs = xs.length - leCount x xs := induction xs with theorem leCount_le_of_sublist {x : Nat} {xs ys : List Nat} (hsub : xs.Sublist ys) : leCount x xs leCount x ys := theorem geCount_le_of_sublist {x : Nat} {xs ys : List Nat} (hsub : xs.Sublist ys) : geCount x xs geCount x ys := theorem leCount_cons_of_le {x y : Nat} {ys : List Nat} (h : y x) : leCount x (y :: ys) = leCount x ys + 1 := theorem leCount_cons_of_not_le {x y : Nat} {ys : List Nat} (h : ¬ y x) : leCount x (y :: ys) = leCount x ys := theorem geCount_cons_of_le {x y : Nat} {ys : List Nat} (h : x y) : geCount x (y :: ys) = geCount x ys + 1 := theorem geCount_cons_of_not_le {x y : Nat} {ys : List Nat} (h : ¬ x y) : geCount x (y :: ys) = geCount x ys := theorem leCount_mono_of_le {low high : Nat} (h : low high) : xs : List Nat, leCount low xs leCount high xs := induction xs with theorem geCount_anti_mono_of_le {low high : Nat} (h : low high) : xs : List Nat, geCount high xs geCount low xs := induction xs with

Pivot-style selection

Fuelled quickselect over lists of natural numbers.

The first element is used as the pivot. The recursive calls keep only the values strictly below or strictly above the pivot; the middle pivot block is represented by the count interval ltCount pivot xs ≤ k < leCount pivot xs.

def quickSelectFuel? : Nat Nat List Nat Option Nat | 0, _, _ => none | _ + 1, _, [] => none | fuel + 1, k, pivot :: tail => let xs := pivot :: tail if k < ltCount pivot xs then quickSelectFuel? fuel k (xs.filter fun y => decide (y < pivot)) else if k < leCount pivot xs then some pivot else quickSelectFuel? fuel (k - leCount pivot xs) (xs.filter fun y => decide (pivot < y))

Public quickselect wrapper with exactly one unit of fuel per input element.

def quickSelect? (k : Nat) (xs : List Nat) : Option Nat := quickSelectFuel? xs.length k xs
theorem rankCertificate_pivot {xs : List Nat} {pivot k : Nat} (hpivot : pivot xs) (hlo : ¬ k < ltCount pivot xs) (hle : k < leCount pivot xs) : RankCertificate xs k pivot := hpivot, Nat.le_of_not_gt hlo, hle

Selection correctness

theorem selectByRank?_mem {k : Nat} {xs : List Nat} {x : Nat} (hsel : selectByRank? k xs = some x) : x xs := (selectByRank?_rankCorrect hsel).1

The specification selector succeeds whenever the requested rank is in range.

exact (sortedCopy xs)[k],

Reader-facing correctness wrapper for the specification selector.

theorem selectByRank?_correct {k : Nat} {xs : List Nat} {x : Nat} (hsel : selectByRank? k xs = some x) : RankCertificate xs k x := selectByRank?_rankCorrect hsel
theorem quickSelect?_rankCorrect {k : Nat} {xs : List Nat} {x : Nat} (hsel : quickSelect? k xs = some x) : RankCertificate xs k x := theorem quickSelect?_mem {k : Nat} {xs : List Nat} {x : Nat} (hsel : quickSelect? k xs = some x) : x xs := (quickSelect?_rankCorrect hsel).1

Reader-facing correctness wrapper for the pivot-style quickselect model.

theorem quickSelect?_correct {k : Nat} {xs : List Nat} {x : Nat} (hsel : quickSelect? k xs = some x) : RankCertificate xs k x := quickSelect?_rankCorrect hsel
end Chapter09end CLRS