This PR sets up the new integrated test/bench suite. It then migrates all benchmarks and some related tests to the new suite. There's also some documentation and some linting. For now, a lot of the old tests are left alone so this PR doesn't become even larger than it already is. Eventually, all tests should be migrated to the new suite though so there isn't a confusing mix of two systems.
75 lines
2.4 KiB
Text
75 lines
2.4 KiB
Text
module
|
|
import Lean
|
|
/-
|
|
Use `grind` as one of the tactics for array-element access and termination proofs.
|
|
-/
|
|
macro_rules | `(tactic| get_elem_tactic_extensible) => `(tactic| grind)
|
|
/-
|
|
Note: We disable model-based theory combination (-mbtc) here because `grind` can
|
|
exhaust heartbeats when exploring certain "bad" termination checker scenarios.
|
|
For example, in `heapsort.go`, the termination checker attempts `termination_by a.size` first,
|
|
and `grind` will perform excessive case splits, consuming heartbeats too quickly.
|
|
|
|
BTW, the error message must be improved. It just says heartbeats exhausted :(
|
|
-/
|
|
macro_rules | `(tactic| decreasing_trivial) => `(tactic| grind -mbtc)
|
|
-- TODO: annotate the library
|
|
attribute [grind] Array.size_swap
|
|
|
|
abbrev leftChild (i : Nat) := 2*i + 1
|
|
abbrev parent (i : Nat) := (i - 1) / 2
|
|
|
|
-- TODO: Generalize `siftDown` to arbitrary element types (not just `Int`)
|
|
-- once other issues are resolved.
|
|
|
|
def siftDown (a : Array Int) (root : Nat) (e : Nat) (h : e ≤ a.size := by grind) : Array Int :=
|
|
-- Remark: It is annoying to have to write `if _ : ...` to make sure the hypothesis is available
|
|
-- while type-checking the if-then-else body.
|
|
if _ : leftChild root < e then
|
|
let child := leftChild root
|
|
-- Remark: it would be nice to have a `p ∧ q` where we can assume `p` while type checking `q`
|
|
-- I simulated it using a nested `if-then-else`
|
|
let child := if _ : child+1 < e then
|
|
if a[child] < a[child + 1] then
|
|
child + 1
|
|
else
|
|
child
|
|
else
|
|
child
|
|
if a[root] < a[child] then
|
|
let a := a.swap root child
|
|
siftDown a child e
|
|
else
|
|
a
|
|
else
|
|
a
|
|
termination_by e - root
|
|
|
|
@[grind] theorem siftDown_size : (siftDown a root e h).size = a.size := by
|
|
fun_induction siftDown <;> grind
|
|
|
|
def heapify (a : Array Int) : Array Int :=
|
|
let start := parent (a.size - 1) + 1
|
|
go a start
|
|
where
|
|
go (a : Array Int) (start : Nat) : Array Int :=
|
|
match start with
|
|
| 0 => a
|
|
| start+1 => go (siftDown a start a.size) start
|
|
|
|
def heapsort (a : Array Int) : Array Int :=
|
|
let a := heapify a
|
|
go a a.size
|
|
where
|
|
go (a : Array Int) (e : Nat) (h : e ≤ a.size := by grind) : Array Int :=
|
|
-- Another annoying `_ :`
|
|
if _ : e > 1 then
|
|
let e := e - 1
|
|
let a := a.swap e 0
|
|
go (siftDown a 0 e) e
|
|
else
|
|
a
|
|
|
|
/-- info: #[0, 1, 2, 4, 5, 7, 8, 10] -/
|
|
#guard_msgs (info) in
|
|
#eval heapsort #[4, 1, 0, 5, 7, 10, 8, 2]
|