This change * moves `termination_by` and `decreasing_by` next to the function they apply to * simplify the syntax of `termination_by` * apply the `decreasing_by` goal to all goals at once, for better interactive use. See the section in `RELEASES.md` for more details and migration advise. This is a hard breaking change, requiring developers to touch every `termination_by` in their code base. We decided to still do it as a hard-breaking change, because supporting both old and new syntax at the same time would be non-trivial, and not save that much. Moreover, this requires changes to some metaprograms that developers might have written, and supporting both syntaxes at the same time would make _their_ migration harder.
150 lines
4.3 KiB
Text
150 lines
4.3 KiB
Text
/-!
|
||
This files tests Lean's ability to guess the right lexicographic order.
|
||
|
||
When writing tests for GuesLex, keep in mind that it doesn't do anything
|
||
when there is only one plausible measure (one function, only one varying argument).
|
||
-/
|
||
|
||
set_option showInferredTerminationBy true
|
||
|
||
def ackermann (n m : Nat) := match n, m with
|
||
| 0, m => m + 1
|
||
| .succ n, 0 => ackermann n 1
|
||
| .succ n, .succ m => ackermann n (ackermann (n + 1) m)
|
||
|
||
def ackermann2 (n m : Nat) := match n, m with
|
||
| m, 0 => m + 1
|
||
| 0, .succ n => ackermann2 1 n
|
||
| .succ m, .succ n => ackermann2 (ackermann2 m (n + 1)) n
|
||
|
||
def ackermannList (n m : List Unit) := match n, m with
|
||
| [], m => () :: m
|
||
| ()::n, [] => ackermannList n [()]
|
||
| ()::n, ()::m => ackermannList n (ackermannList (()::n) m)
|
||
|
||
def foo2 : Nat → Nat → Nat
|
||
| .succ n, 1 => foo2 n 1
|
||
| .succ n, 2 => foo2 (.succ n) 1
|
||
| n, 3 => foo2 (.succ n) 2
|
||
| .succ n, 4 => foo2 (if n > 10 then n else .succ n) 3
|
||
| n, 5 => foo2 (n - 1) 4
|
||
| n, .succ m => foo2 n m
|
||
| _, _ => 0
|
||
|
||
mutual
|
||
def even : Nat → Bool
|
||
| 0 => true
|
||
| .succ n => not (odd n)
|
||
def odd : Nat → Bool
|
||
| 0 => false
|
||
| .succ n => not (even n)
|
||
end
|
||
|
||
mutual
|
||
def evenWithFixed (m : String) : Nat → Bool
|
||
| 0 => true
|
||
| .succ n => not (oddWithFixed m n)
|
||
def oddWithFixed (m : String) : Nat → Bool
|
||
| 0 => false
|
||
| .succ n => not (evenWithFixed m n)
|
||
end
|
||
|
||
def ping (n : Nat) := pong n
|
||
where pong : Nat → Nat
|
||
| 0 => 0
|
||
| .succ n => ping n
|
||
|
||
def hasForbiddenArg (n : Nat) (_h : n = n) (m : Nat) : Nat :=
|
||
match n, m with
|
||
| 0, 0 => 0
|
||
| .succ m, n => hasForbiddenArg m rfl n
|
||
| m, .succ n => hasForbiddenArg (.succ m) rfl n
|
||
|
||
/-!
|
||
Example from “Finding Lexicographic Orders for Termination Proofs in
|
||
Isabelle/HOL” by Lukas Bulwahn, Alexander Krauss, and Tobias Nipkow,
|
||
10.1007/978-3-540-74591-4_5
|
||
-/
|
||
def blowup : Nat → Nat → Nat → Nat → Nat → Nat → Nat → Nat → Nat
|
||
| 0, 0, 0, 0, 0, 0, 0, 0 => 0
|
||
| 0, 0, 0, 0, 0, 0, 0, .succ i => .succ (blowup i i i i i i i i)
|
||
| 0, 0, 0, 0, 0, 0, .succ h, i => .succ (blowup h h h h h h h i)
|
||
| 0, 0, 0, 0, 0, .succ g, h, i => .succ (blowup g g g g g g h i)
|
||
| 0, 0, 0, 0, .succ f, g, h, i => .succ (blowup f f f f f g h i)
|
||
| 0, 0, 0, .succ e, f, g, h, i => .succ (blowup e e e e f g h i)
|
||
| 0, 0, .succ d, e, f, g, h, i => .succ (blowup d d d e f g h i)
|
||
| 0, .succ c, d, e, f, g, h, i => .succ (blowup c c d e f g h i)
|
||
| .succ b, c, d, e, f, g, h, i => .succ (blowup b c d e f g h i)
|
||
|
||
-- Let’s try to confuse the lexicographic guessing function's
|
||
-- unpacking of packed n-ary arguments
|
||
def confuseLex1 : Nat → @PSigma Nat (fun _ => Nat) → Nat
|
||
| 0, _p => 0
|
||
| .succ n, ⟨x,y⟩ => confuseLex1 n ⟨x, .succ y⟩
|
||
|
||
def confuseLex2 : @PSigma Nat (fun _ => Nat) → Nat
|
||
| ⟨_,0⟩ => 0
|
||
| ⟨0,_⟩ => 0
|
||
| ⟨.succ y,.succ n⟩ => confuseLex2 ⟨y,n⟩
|
||
|
||
def dependent : (n : Nat) → (m : Fin n) → Nat
|
||
| 0, i => Fin.elim0 i
|
||
| .succ 0, 0 => 0
|
||
| .succ (.succ n), 0 => dependent (.succ n) ⟨n, n.lt_succ_self⟩
|
||
| .succ (.succ n), ⟨.succ m, h⟩ =>
|
||
dependent (.succ (.succ n)) ⟨m, Nat.lt_of_le_of_lt (Nat.le_succ _) h⟩
|
||
|
||
-- An example based on a real world problem, condensed by Leo
|
||
inductive Expr where
|
||
| add (a b : Expr)
|
||
| val (n : Nat)
|
||
|
||
mutual
|
||
def eval (a : Expr) : Nat :=
|
||
match a with
|
||
| .add x y => eval_add (x, y)
|
||
| .val n => n
|
||
|
||
def eval_add (a : Expr × Expr) : Nat :=
|
||
match a with
|
||
| (x, y) => eval x + eval y
|
||
end
|
||
|
||
namespace VarNames
|
||
|
||
/-! Test that varnames are inferred nicely. -/
|
||
|
||
def shadow1 (x2 : Nat) : Nat → Nat
|
||
| 0 => 0
|
||
| .succ n => shadow1 (x2 + 1) n
|
||
decreasing_by decreasing_tactic
|
||
|
||
|
||
-- This test is a bit moot since #3081, but lets keep it
|
||
def some_n : Nat := 1
|
||
def shadow2 (some_n : Nat) : Nat → Nat
|
||
| 0 => 0
|
||
| .succ n => shadow2 (some_n + 1) n
|
||
decreasing_by decreasing_tactic
|
||
|
||
-- Shadowing `sizeOf`, as a varying paramter
|
||
def shadowSizeOf1 (sizeOf : Nat) : Nat → Nat
|
||
| 0 => 0
|
||
| .succ n => shadowSizeOf1 (sizeOf + 1) n
|
||
decreasing_by decreasing_tactic
|
||
|
||
-- Shadowing `sizeOf`, as a fixed paramter
|
||
def shadowSizeOf2 (sizeOf : Nat) : Nat → Nat → Nat
|
||
| 0, m => m
|
||
| .succ n, m => shadowSizeOf2 sizeOf n m
|
||
decreasing_by decreasing_tactic
|
||
|
||
-- Shadowing `sizeOf`, as something in the environment
|
||
def sizeOf : Nat := 2
|
||
|
||
def qualifiedSizeOf (m : Nat) : Nat → Nat
|
||
| 0 => 0
|
||
| .succ n => qualifiedSizeOf (m + 1) n
|
||
decreasing_by decreasing_tactic
|
||
|
||
end VarNames
|