feat: Rename Std.List.Zipper to List.Cursor (#9911)

This PR renames `Std.List.Zipper` to `List.Cursor`, with slight changes
to the implementation (no `reverse`) and use in loop specification
lemmas.
This commit is contained in:
Sebastian Graf 2025-08-14 11:17:54 +02:00 committed by GitHub
parent 535435955b
commit 0c39a50337
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 164 additions and 144 deletions

View file

@ -1084,6 +1084,12 @@ theorem getLast?_tail {l : List α} : (tail l).getLast? = if l.length = 1 then n
rw [if_neg]
rintro ⟨⟩
@[simp, grind =]
theorem cons_head_tail (h : l ≠ []) : l.head h :: l.tail = l := by
induction l with
| nil => contradiction
| cons ih => simp_all
/-! ## Basic operations -/
/-! ### map -/

View file

@ -31,6 +31,35 @@ end Std.Range
namespace List
@[ext]
structure Cursor {α : Type u} (l : List α) : Type u where
«prefix» : List α
suffix : List α
property : «prefix» ++ suffix = l
def Cursor.at (l : List α) (n : Nat) : Cursor l := ⟨l.take n, l.drop n, by simp⟩
abbrev Cursor.begin (l : List α) : Cursor l := .at l 0
abbrev Cursor.end (l : List α) : Cursor l := .at l l.length
def Cursor.current {α} {l : List α} (c : Cursor l) (h : 0 < c.suffix.length := by get_elem_tactic) : α :=
c.suffix[0]'(by simp [h])
def Cursor.tail (s : Cursor l) (h : 0 < s.suffix.length := by get_elem_tactic) : Cursor l :=
{ «prefix» := s.prefix ++ [s.current]
, suffix := s.suffix.tail
, property := by
have : s.suffix ≠ [] := by simp only [List.ne_nil_iff_length_pos, h]
simp [current, ←List.head_eq_getElem this, s.property] }
@[simp, grind =] theorem Cursor.prefix_at (l : List α) : (Cursor.at l n).prefix = l.take n := rfl
@[simp, grind =] theorem Cursor.suffix_at (l : List α) : (Cursor.at l n).suffix = l.drop n := rfl
@[simp, grind =] theorem Cursor.current_at (l : List α) (h : n < l.length) :
(Cursor.at l n).current (by simpa using Nat.sub_lt_sub_right (Nat.le_refl n) h) = l[n] := by
induction n with simp_all [Cursor.current]
@[simp, grind =] theorem Cursor.tail_at (l : List α) (h : n < l.length) :
(Cursor.at l n).tail (by simpa using Nat.sub_lt_sub_right (Nat.le_refl n) h) = Cursor.at l (n + 1) := by
simp [Cursor.tail, Cursor.at, Cursor.current]
@[grind →]
theorem eq_of_range'_eq_append_cons (h : range' s n step = xs ++ cur :: ys) :
cur = s + step * xs.length := by
@ -71,24 +100,6 @@ theorem lt_of_range'_eq_append_cons (h : range' s n step = xs ++ i :: ys) (hstep
end List
namespace Std.List
@[ext]
structure Zipper {α : Type u} (l : List α) : Type u where
rpref : List α
suff : List α
property : rpref.reverse ++ suff = l
@[simp]
abbrev Zipper.pref {α} {l : List α} (s : List.Zipper l) : List α := s.rpref.reverse
abbrev Zipper.begin (l : List α) : Zipper l := ⟨[],l,rfl⟩
abbrev Zipper.end (l : List α) : Zipper l := ⟨l.reverse,[],by simp⟩
abbrev Zipper.tail (s : Zipper l) (h : s.suff = hd::tl) : Zipper l :=
{ rpref := hd::s.rpref, suff := tl, property := by simp [s.property, ←h] }
end Std.List
namespace Std.Do
-- We override the `Triple` notation in `Std.Do.Triple.Basic` just in this module.
@ -339,7 +350,7 @@ theorem Spec.tryCatch_ExceptT_lift [WP m ps] [Monad m] [MonadExceptOf ε m] (Q :
The type of loop invariants used by the specifications of `for ... in ...` loops.
A loop invariant is a `PostCond` that takes as parameters
* A `List.Zipper xs` representing the iteration state of the loop. It is parameterized by the list
* A `List.Cursor xs` representing the iteration state of the loop. It is parameterized by the list
of elements `xs` that the `for` loop iterates over.
* A state tuple of type `β`, which will be a nesting of `MProd`s representing the elaboration of
`let mut` variables and early return.
@ -351,7 +362,7 @@ During the induction step, the invariant holds for a suffix with head element `x
After running the loop body, the invariant then holds after shifting `x` to the prefix.
-/
abbrev Invariant {α : Type u} (xs : List α) (β : Type u) (ps : PostShape) :=
PostCond (List.Zipper xs × β) ps
PostCond (List.Cursor xs × β) ps
/--
Helper definition for specifying loop invariants for loops with early return.
@ -365,20 +376,20 @@ It is `none` as long as there was no early return and `some r` if the loop retur
This function allows to specify different invariants for the loop body depending on whether the loop
terminated early or not. When there was an early return, the loop has effectively finished, which is
encoded by the additional `⌜xs.suff = []⌝` assertion in the invariant. This assertion is vital for
encoded by the additional `⌜xs.suffix = []⌝` assertion in the invariant. This assertion is vital for
successfully proving the induction step, as it contradicts with the assumption that
`xs.suff = x::rest` of the inductive hypothesis at the start of the loop body, meaning that users
`xs.suffix = x::rest` of the inductive hypothesis at the start of the loop body, meaning that users
won't need to prove anything about the bogus case where the loop has returned early yet takes
another iteration of the loop body.
-/
abbrev Invariant.withEarlyReturn
(onContinue : List.Zipper xs → β → Assertion ps)
(onContinue : List.Cursor xs → β → Assertion ps)
(onReturn : γ → β → Assertion ps)
(onExcept : ExceptConds ps := ExceptConds.false) :
Invariant xs (MProd (Option γ) β) ps :=
⟨fun ⟨xs, x, b⟩ => spred(
(⌜x = none⌝ ∧ onContinue xs b)
(∃ r, ⌜x = some r⌝ ∧ ⌜xs.suff = []⌝ ∧ onReturn r b)),
(∃ r, ⌜x = some r⌝ ∧ ⌜xs.suffix = []⌝ ∧ onReturn r b)),
onExcept⟩
@[spec]
@ -386,32 +397,32 @@ theorem Spec.forIn'_list {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : List α} {init : β} {f : (a : α) → a ∈ xs → β → m (ForInStep β)}
(inv : Invariant xs β ps)
(step : ∀ b rpref x (hx : x ∈ xs) suff (h : xs = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x hx b
(step : ∀ pref cur suff (h : xs = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur (by simp [h]) b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs, by simp⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs.reverse, [], by simp⟩, b), inv.2)} := by
suffices h : ∀ rpref suff (h : xs = rpref.reverse ++ suff),
⦃inv.1 (⟨rpref, suff, by simp [h]⟩, init)}
forIn' (m:=m) suff init (fun a ha => f a (by simp[h,ha]))
⦃(fun b => inv.1 (⟨xs.reverse, [], by simp [h]⟩, b), inv.2)}
from h [] xs rfl
intro rpref suff h
induction suff generalizing rpref init
case nil => apply Triple.pure; simp [h]
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs, rfl⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs, [], by simp⟩, b), inv.2)} := by
suffices h : ∀ c,
⦃inv.1 (c, init)}
forIn' (m:=m) c.suffix init (fun a ha => f a (by simp [←c.property, ha]))
⦃(fun b => inv.1 (⟨xs, [], by simp⟩, b), inv.2)}
from h [], xs, rfl
rintro ⟨pref, suff, h⟩
induction suff generalizing pref init
case nil => apply Triple.pure; simp [h]
case cons x suff ih =>
simp only [List.forIn'_cons]
apply Triple.bind
case hx => exact step init rpref x (by simp[h]) suff h
case hx => exact step _ _ _ h.symm init
case hf =>
intro r
split
next => apply Triple.pure; simp [h]
next => apply Triple.pure; simp
next b =>
simp
have := @ih b (x::rpref) (by simp [h])
have := @ih b (pref ++ [x]) (by simp [h])
simp at this
exact this
@ -425,22 +436,22 @@ theorem Spec.forIn'_list_const_inv {α β : Type u}
f x hx b
⦃(fun r => match r with | .yield b' => inv.1 b' | .done b' => inv.1 b', inv.2)}) :
⦃inv.1 init} forIn' xs init f ⦃inv} :=
Spec.forIn'_list (fun p => inv.1 p.2, inv.2) (fun b _ x hx _ _ => step x hx b)
Spec.forIn'_list (fun p => inv.1 p.2, inv.2) (fun _p c _s h b => step c (by simp [h]) b)
@[spec]
theorem Spec.forIn_list {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : List α} {init : β} {f : α → β → m (ForInStep β)}
(inv : Invariant xs β ps)
(step : ∀ b rpref x suff (h : xs = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x b
(step : ∀ pref cur suff (h : xs = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs, by simp⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs.reverse, [], by simp⟩, b), inv.2)} := by
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs, rfl⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs, [], by simp⟩, b), inv.2)} := by
simp only [← forIn'_eq_forIn]
exact Spec.forIn'_list inv (fun b rpref x _ suff h => step b rpref x suff h)
exact Spec.forIn'_list inv step
-- using the postcondition as a constant invariant:
theorem Spec.forIn_list_const_inv {α β : Type u}
@ -452,18 +463,18 @@ theorem Spec.forIn_list_const_inv {α β : Type u}
f hd b
⦃(fun r => match r with | .yield b' => inv.1 b' | .done b' => inv.1 b', inv.2)}) :
⦃inv.1 init} forIn xs init f ⦃inv} :=
Spec.forIn_list (fun p => inv.1 p.2, inv.2) (fun b _ hd _ _ => step hd b)
Spec.forIn_list (fun p => inv.1 p.2, inv.2) (fun _p c _s _h b => step c b)
@[spec]
theorem Spec.foldlM_list {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : List α} {init : β} {f : β → α → m β}
(inv : Invariant xs β ps)
(step : ∀ b rpref x suff (h : xs = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f b x
⦃(fun b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs, by simp⟩, init)} List.foldlM f init xs ⦃(fun b => inv.1 (⟨xs.reverse, [], by simp⟩, b), inv.2)} := by
(step : ∀ pref cur suff (h : xs = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f b cur
⦃(fun b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs, rfl⟩, init)} List.foldlM f init xs ⦃(fun b => inv.1 (⟨xs, [], by simp⟩, b), inv.2)} := by
have : xs.foldlM f init = forIn xs init (fun a b => .yield <$> f b a) := by
simp only [List.forIn_yield_eq_foldlM, id_map']
rw[this]
@ -481,35 +492,35 @@ theorem Spec.foldlM_list_const_inv {α β : Type u}
f b hd
⦃(fun b' => inv.1 b', inv.2)}) :
⦃inv.1 init} List.foldlM f init xs ⦃inv} :=
Spec.foldlM_list (fun p => inv.1 p.2, inv.2) (fun b _ hd _ _ => step hd b)
Spec.foldlM_list (fun p => inv.1 p.2, inv.2) (fun _p c _s _h b => step c b)
@[spec]
theorem Spec.forIn'_range {β : Type} {m : Type → Type v} {ps : PostShape}
[Monad m] [WPMonad m ps]
{xs : Std.Range} {init : β} {f : (a : Nat) → a ∈ xs → β → m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : ∀ b rpref x (hx : x ∈ xs) suff (h : xs.toList = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x hx b
(step : ∀ pref cur suff (h : xs.toList = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur (by simp [Std.Range.mem_of_mem_range', h]) b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, by simp⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b), inv.2)} := by
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, rfl⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs.toList, [], by simp⟩, b), inv.2)} := by
simp only [Std.Range.forIn'_eq_forIn'_range', Std.Range.size, Std.Range.size.eq_1]
apply Spec.forIn'_list inv (fun b rpref x hx suff h => step b rpref x (Std.Range.mem_of_mem_range' hx) suff h)
apply Spec.forIn'_list inv (fun c hcur b => step c hcur b)
@[spec]
theorem Spec.forIn_range {β : Type} {m : Type → Type v} {ps : PostShape}
[Monad m] [WPMonad m ps]
{xs : Std.Range} {init : β} {f : Nat → β → m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : ∀ b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x b
(step : ∀ pref cur suff (h : xs.toList = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, by simp⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b), inv.2)} := by
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, rfl⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs.toList, [], by simp⟩, b), inv.2)} := by
simp only [Std.Range.forIn_eq_forIn_range', Std.Range.size]
apply Spec.forIn_list inv step
@ -523,15 +534,15 @@ theorem Spec.forIn'_prange {α β : Type u}
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
{xs : PRange ⟨sl, su⟩ α} {init : β} {f : (a : α) → a ∈ xs → β → m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : ∀ b rpref x (hx : x ∈ xs) suff (h : xs.toList = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x hx b
(step : ∀ pref cur suff (h : xs.toList = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur (by simp [←mem_toList_iff_mem, h]) b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, by simp⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b), inv.2)} := by
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, rfl⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs.toList, [], by simp⟩, b), inv.2)} := by
simp only [forIn'_eq_forIn'_toList]
apply Spec.forIn'_list inv (fun b rpref x hx suff h => step b rpref x (mem_toList_iff_mem.mp hx) suff h)
apply Spec.forIn'_list inv step
open Std.PRange in
@[spec]
@ -543,44 +554,44 @@ theorem Spec.forIn_prange {α β : Type u}
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
{xs : PRange ⟨sl, su⟩ α} {init : β} {f : α → β → m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : ∀ b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x b
(step : ∀ pref cur suff (h : xs.toList = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, by simp⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b), inv.2)} := by
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, rfl⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs.toList, [], by simp⟩, b), inv.2)} := by
simp only [forIn]
apply Spec.forIn'_prange inv (fun b rpref x _hx suff h => step b rpref x suff h)
apply Spec.forIn'_prange inv step
@[spec]
theorem Spec.forIn'_array {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : Array α} {init : β} {f : (a : α) → a ∈ xs → β → m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : ∀ b rpref x (hx : x ∈ xs) suff (h : xs.toList = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x hx b
(step : ∀ pref cur suff (h : xs.toList = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur (by simp [←Array.mem_toList_iff, h]) b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, by simp⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b), inv.2)} := by
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, rfl⟩, init)} forIn' xs init f ⦃(fun b => inv.1 (⟨xs.toList, [], by simp⟩, b), inv.2)} := by
cases xs
simp
apply Spec.forIn'_list inv (fun b rpref x hx suff h => step b rpref x (by simp[hx]) suff h)
apply Spec.forIn'_list inv step
@[spec]
theorem Spec.forIn_array {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : Array α} {init : β} {f : α → β → m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : ∀ b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f x b
(step : ∀ pref cur suff (h : xs.toList = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f cur b
⦃(fun r => match r with
| .yield b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, by simp⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b), inv.2)} := by
| .yield b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b')
| .done b' => inv.1 (⟨xs.toList, [], by simp⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, rfl⟩, init)} forIn xs init f ⦃(fun b => inv.1 (⟨xs.toList, [], by simp⟩, b), inv.2)} := by
cases xs
simp
apply Spec.forIn_list inv step
@ -590,11 +601,11 @@ theorem Spec.foldlM_array {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : Array α} {init : β} {f : β → α → m β}
(inv : Invariant xs.toList β ps)
(step : ∀ b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
⦃inv.1 (⟨rpref, x::suff, by simp [h]⟩, b)}
f b x
⦃(fun b' => inv.1 (⟨x::rpref, suff, by simp [h]⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, by simp⟩, init)} Array.foldlM f init xs ⦃(fun b => inv.1 (⟨xs.toList.reverse, [], by simp⟩, b), inv.2)} := by
(step : ∀ pref cur suff (h : xs.toList = pref ++ cur :: suff) b,
⦃inv.1 (⟨pref, cur::suff, h.symm⟩, b)}
f b cur
⦃(fun b' => inv.1 (⟨pref ++ [cur], suff, by simp [h]⟩, b'), inv.2)}) :
⦃inv.1 (⟨[], xs.toList, rfl⟩, init)} Array.foldlM f init xs ⦃(fun b => inv.1 (⟨xs.toList, [], by simp⟩, b), inv.2)} := by
cases xs
simp
apply Spec.foldlM_list inv step

View file

@ -160,7 +160,10 @@ macro (name := mleave) "mleave" : tactic =>
$(mkIdent ``Std.Do.ExceptConds.entails_false):term,
$(mkIdent ``ULift.down_ite):term,
$(mkIdent ``ULift.down_dite):term,
$(mkIdent ``Std.List.Zipper.pref):term,
$(mkIdent ``List.Cursor.prefix_at):term,
$(mkIdent ``List.Cursor.suffix_at):term,
$(mkIdent ``List.Cursor.current_at):term,
$(mkIdent ``List.Cursor.tail_at):term,
$(mkIdent ``and_imp):term,
$(mkIdent ``and_true):term,
$(mkIdent ``dite_eq_ite):term,

View file

@ -152,16 +152,16 @@ theorem sampler_correct {m : Type → Type u} {k h} [Monad m] [WPMonad m ps] :
sampler (m:=m) n k h
⦃⇓ xs => ⌜xs.toList.Nodup⌝⦄ := by
mvcgen -leave [sampler]
case inv1 => exact (⇓ (xs, midway) => ⌜Midway.valid midway xs.rpref.length⌝)
case vc1 rpref x _ _ _ _ _ _ r _ _ _ =>
case inv1 => exact (⇓ (xs, midway) => ⌜Midway.valid midway xs.prefix.length⌝)
case vc1 pref cur _ _ _ _ _ _ r _ _ _ =>
dsimp
mframe
rename_i hinv
mpure_intro
change Midway.valid (next _ x _ r.val) (rpref.length + 1)
have : x = rpref.length := sorry -- by grind -- wishful thinking :(
simp only [List.length_append, List.length_cons, List.length_nil, Nat.zero_add]
have : cur = pref.length := sorry -- by grind -- wishful thinking :(
subst this
apply Midway.valid_next _ rpref.length _ r.val r.property.1 r.property.2 hinv
apply Midway.valid_next _ pref.length _ r.val r.property.1 r.property.2 hinv
case vc2 =>
mpure_intro
exact valid_init

View file

@ -87,7 +87,7 @@ theorem sum_loop_spec :
mintro -
unfold sum_loop
mspec
case inv => exact (⇓ (xs, r) => ⌜(∀ x, x ∈ xs.suff → x ≤ 5) ∧ r + xs.suff.length * 5 ≤ 25⌝)
case inv => exact (⇓ (xs, r) => ⌜(∀ x ∈ xs.suffix, x ≤ 5) ∧ r + xs.suffix.length * 5 ≤ 25⌝)
all_goals simp_all +decide; try omega
intros
mintro _
@ -136,11 +136,11 @@ theorem mkFreshPair_spec_no_eta :
mspec
intro _; simp_all
example : PostCond (Nat × Std.List.Zipper (List.range' 1 3 1)) (PostShape.except Nat (PostShape.arg Nat PostShape.pure)) :=
⟨fun (r, xs) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suff.sum > 4⌝, fun e s => ⌜e = 42 ∧ s = 4⌝, ()⟩
example : PostCond (Nat × List.Cursor (List.range' 1 3 1)) (PostShape.except Nat (PostShape.arg Nat PostShape.pure)) :=
⟨fun (r, xs) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suffix.sum > 4⌝, fun e s => ⌜e = 42 ∧ s = 4⌝, ()⟩
example : PostCond (Nat × Std.List.Zipper (List.range' 1 3 1)) (PostShape.except Nat (PostShape.arg Nat PostShape.pure)) :=
post⟨fun (r, xs) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suff.sum > 4⌝, fun e s => ⌜e = 42 ∧ s = 4⌝⟩
example : PostCond (Nat × List.Cursor (List.range' 1 3 1)) (PostShape.except Nat (PostShape.arg Nat PostShape.pure)) :=
post⟨fun (r, xs) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suffix.sum > 4⌝, fun e s => ⌜e = 42 ∧ s = 4⌝⟩
theorem throwing_loop_spec :
⦃fun s => ⌜s = 4⌝⦄
@ -152,7 +152,7 @@ theorem throwing_loop_spec :
mspec
mspec
mspec
case inv => exact post⟨fun (xs, r) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suff.sum > 4⌝, fun e s => ⌜e = 42 ∧ s = 4⌝⟩
case inv => exact post⟨fun (xs, r) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suffix.sum > 4⌝, fun e s => ⌜e = 42 ∧ s = 4⌝⟩
case post.success =>
mspec
mspec
@ -178,7 +178,7 @@ theorem beaking_loop_spec :
dsimp only [breaking_loop, get, getThe, instMonadStateOfOfMonadLift, liftM, monadLift]
mspec
mspec
case inv => exact (⇓ (xs, r) s => ⌜(r ≤ 4 ∧ r = xs.rpref.sum r > 4) ∧ s = 42⌝)
case inv => exact (⇓ (xs, r) s => ⌜(r ≤ 4 ∧ r = xs.prefix.sum r > 4) ∧ s = 42⌝)
all_goals simp_all
case post => grind
case step =>
@ -196,7 +196,7 @@ theorem returning_loop_spec :
dsimp only [returning_loop, get, getThe, instMonadStateOfOfMonadLift, liftM, monadLift]
mspec
mspec
case inv => exact (⇓ (xs, r) s => ⌜(r.1 = none ∧ r.2 = xs.rpref.sum ∧ r.2 ≤ 4 r.1 = some 42 ∧ r.2 > 4) ∧ s = 4⌝)
case inv => exact (⇓ (xs, r) s => ⌜(r.1 = none ∧ r.2 = xs.prefix.sum ∧ r.2 ≤ 4 r.1 = some 42 ∧ r.2 > 4) ∧ s = 4⌝)
all_goals simp_all [-SPred.entails_1]
case post =>
split
@ -235,8 +235,8 @@ theorem fib_triple : ⦃⌜True⌝⦄ fib_impl n ⦃⇓ r => ⌜r = fib_spec n
if h : n = 0 then simp [h] else
simp only [h, reduceIte]
mspec -- Spec.pure
mspec Spec.forIn_range (⇓ ⟨xs, a, b⟩ => ⌜a = fib_spec xs.rpref.length ∧ b = fib_spec (xs.rpref.length + 1)⌝) ?step
case step => dsimp; intros; mintro _; simp_all
mspec Spec.forIn_range (⇓ ⟨xs, a, b⟩ => ⌜a = fib_spec xs.prefix.length ∧ b = fib_spec (xs.prefix.length + 1)⌝) ?step
case step => intros; mintro _; simp_all
simp_all [Nat.sub_one_add_one]
theorem fib_triple_cases : ⦃⌜True⌝⦄ fib_impl n ⦃⇓ r => ⌜r = fib_spec n⌝⦄ := by
@ -246,8 +246,8 @@ theorem fib_triple_cases : ⦃⌜True⌝⦄ fib_impl n ⦃⇓ r => ⌜r = fib_sp
mintro -
simp only [fib_impl, h, reduceIte]
mspec
mspec Spec.forIn_range (⇓ ⟨xs, a, b⟩ => ⌜a = fib_spec xs.rpref.length ∧ b = fib_spec (xs.rpref.length + 1)⌝) ?step
case step => dsimp; intros; mintro _; mspec; mspec; simp_all
mspec Spec.forIn_range (⇓ ⟨xs, a, b⟩ => ⌜a = fib_spec xs.prefix.length ∧ b = fib_spec (xs.prefix.length + 1)⌝) ?step
case step => intros; mintro _; mspec; mspec; simp_all
simp_all [Nat.sub_one_add_one]
theorem fib_impl_vcs
@ -255,10 +255,10 @@ theorem fib_impl_vcs
(I : (n : Nat) → (_ : ¬n = 0) →
Invariant [1:n].toList (MProd Nat Nat) PostShape.pure)
(ret : ⊢ₛ (Q 0).1 0)
(loop_pre : ∀ n (hn : ¬n = 0), ⊢ₛ (I n hn).1 ⟨Std.List.Zipper.begin _, 0, 1⟩)
(loop_post : ∀ n (hn : ¬n = 0) r, (I n hn).1 ⟨Std.List.Zipper.end _, r⟩ ⊢ₛ (Q n).1 r.2)
(loop_step : ∀ n (hn : ¬n = 0) r rpref x suff (h : [1:n].toList = rpref.reverse ++ x :: suff),
(I n hn).1 ⟨⟨rpref, x::suff, by simp[h]⟩, r⟩ ⊢ₛ (I n hn).1 ⟨⟨x::rpref, suff, by simp[h]⟩, r.2, r.1+r.2⟩)
(loop_pre : ∀ n (hn : ¬n = 0), ⊢ₛ (I n hn).1 ⟨⟨[], [1:n].toList, rfl⟩, 0, 1⟩)
(loop_post : ∀ n (hn : ¬n = 0) r, (I n hn).1 ⟨⟨[1:n].toList, [], by simp⟩, r⟩ ⊢ₛ (Q n).1 r.2)
(loop_step : ∀ n (hn : ¬n = 0) r pref cur suff (h : [1:n].toList = pref ++ cur :: suff),
(I n hn).1 ⟨⟨pref, cur::suff, by simp[h]⟩, r⟩ ⊢ₛ (I n hn).1 ⟨⟨pref ++ [cur], suff, by simp[h]⟩, r.2, r.1+r.2⟩)
: ⊢ₛ wp⟦fib_impl n⟧ (Q n) := by
apply fib_impl.fun_cases n _ ?case1 ?case2
case case1 => intro h; simp only [fib_impl, h, ↓reduceIte]; mstart; mspec
@ -279,7 +279,7 @@ theorem fib_impl_vcs
theorem fib_triple_vcs : ⦃⌜True⌝⦄ fib_impl n ⦃⇓ r => ⌜r = fib_spec n⌝⦄ := by
apply fib_impl_vcs
case I => intro n hn; exact (⇓ ⟨xs, a, b⟩ => ⌜a = fib_spec xs.rpref.length ∧ b = fib_spec (xs.rpref.length + 1)⌝)
case I => intro n hn; exact (⇓ ⟨xs, a, b⟩ => ⌜a = fib_spec xs.prefix.length ∧ b = fib_spec (xs.prefix.length + 1)⌝)
case ret => mpure_intro; rfl
case loop_pre => intros; mpure_intro; trivial
case loop_post => simp_all [Nat.sub_one_add_one]
@ -394,14 +394,14 @@ theorem fib_triple : ⦃⌜True⌝⦄ fib_impl n ⦃⇓ r => ⌜r = fib_spec n
unfold fib_impl
mvcgen
case inv1 => exact ⇓ (xs, ⟨a, b⟩) =>
⌜a = fib_spec xs.rpref.length ∧ b = fib_spec (xs.rpref.length + 1)⌝
⌜a = fib_spec xs.prefix.length ∧ b = fib_spec (xs.prefix.length + 1)⌝
all_goals simp_all +zetaDelta [Nat.sub_one_add_one]
theorem fib_triple_step : ⦃⌜True⌝⦄ fib_impl n ⦃⇓ r => ⌜r = fib_spec n⌝⦄ := by
unfold fib_impl
mvcgen (stepLimit := some 14) -- 13 still has a wp⟦·⟧
case inv1 => exact ⇓ ⟨xs, a, b⟩ =>
⌜a = fib_spec xs.rpref.length ∧ b = fib_spec (xs.rpref.length + 1)⌝
⌜a = fib_spec xs.prefix.length ∧ b = fib_spec (xs.prefix.length + 1)⌝
all_goals simp_all +zetaDelta [Nat.sub_one_add_one]
attribute [local spec] fib_triple in
@ -419,10 +419,10 @@ theorem fib_impl_vcs
(I : (n : Nat) → (_ : ¬n = 0) →
Invariant [1:n].toList (MProd Nat Nat) PostShape.pure)
(ret : ⊢ₛ (Q 0).1 0)
(loop_pre : ∀ n (hn : ¬n = 0), ⊢ₛ (I n hn).1 ⟨Std.List.Zipper.begin _, 0, 1⟩)
(loop_post : ∀ n (hn : ¬n = 0) r, (I n hn).1 ⟨Std.List.Zipper.end _, r⟩ ⊢ₛ (Q n).1 r.2)
(loop_step : ∀ n (hn : ¬n = 0) r rpref x suff (h : [1:n].toList = rpref.reverse ++ x :: suff),
(I n hn).1 ⟨⟨rpref, x::suff, by simp[h]⟩, r⟩ ⊢ₛ (I n hn).1 ⟨⟨x::rpref, suff, by simp[h]⟩, r.2, r.1+r.2⟩)
(loop_pre : ∀ n (hn : ¬n = 0), ⊢ₛ (I n hn).1 ⟨⟨[], [1:n].toList, rfl⟩, 0, 1⟩)
(loop_post : ∀ n (hn : ¬n = 0) r, (I n hn).1 ⟨⟨[1:n].toList, [], by simp⟩, r⟩ ⊢ₛ (Q n).1 r.2)
(loop_step : ∀ n (hn : ¬n = 0) r pref cur suff (h : [1:n].toList = pref ++ cur :: suff),
(I n hn).1 ⟨⟨pref, cur::suff, by simp[h]⟩, r⟩ ⊢ₛ (I n hn).1 ⟨⟨pref ++ [cur], suff, by simp[h]⟩, r.2, r.1+r.2⟩)
: ⊢ₛ wp⟦fib_impl n⟧ (Q n) := by
unfold fib_impl
mvcgen
@ -467,7 +467,7 @@ theorem sum_loop_spec :
-- cf. `ByHand.sum_loop_spec`
mintro -
mvcgen [sum_loop]
case inv1 => exact (⇓ (xs, r) => ⌜(∀ x, x ∈ xs.suff → x ≤ 5) ∧ r + xs.suff.length * 5 ≤ 25⌝)
case inv1 => exact (⇓ (xs, r) => ⌜r + xs.suffix.length * 5 ≤ 25⌝)
all_goals simp_all; try grind
theorem throwing_loop_spec :
@ -476,7 +476,7 @@ theorem throwing_loop_spec :
⦃post⟨fun _ _ => ⌜False⌝,
fun e s => ⌜e = 42 ∧ s = 4⌝⟩⦄ := by
mvcgen [throwing_loop]
case inv1 => exact post⟨fun (xs, r) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suff.sum > 4⌝,
case inv1 => exact post⟨fun (xs, r) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suffix.sum > 4⌝,
fun e s => ⌜e = 42 ∧ s = 4⌝⟩
all_goals mleave; try (subst_vars; grind)
@ -485,7 +485,7 @@ theorem test_loop_break :
breaking_loop
⦃⇓ r s => ⌜r > 4 ∧ s = 1⌝⦄ := by
mvcgen [breaking_loop]
case inv1 => exact (⇓ (xs, r) s => ⌜(r ≤ 4 ∧ r = xs.rpref.sum r > 4) ∧ s = 42⌝)
case inv1 => exact (⇓ (xs, r) s => ⌜(r ≤ 4 ∧ r = xs.prefix.sum r > 4) ∧ s = 42⌝)
all_goals mleave; try grind
theorem test_loop_early_return :
@ -493,7 +493,7 @@ theorem test_loop_early_return :
returning_loop
⦃⇓ r s => ⌜r = 42 ∧ s = 4⌝⦄ := by
mvcgen [returning_loop]
case inv1 => exact (⇓ (xs, r) s => ⌜(r.1 = none ∧ r.2 = xs.rpref.sum ∧ r.2 ≤ 4 r.1 = some 42 ∧ r.2 > 4) ∧ s = 4⌝)
case inv1 => exact (⇓ (xs, r) s => ⌜(r.1 = none ∧ r.2 = xs.prefix.sum ∧ r.2 ≤ 4 r.1 = some 42 ∧ r.2 > 4) ∧ s = 4⌝)
all_goals simp_all; try grind
theorem unfold_to_expose_match_spec :
@ -521,7 +521,7 @@ theorem test_sum :
pure (f := Id) x
⦃⇓r => ⌜r < 30⌝⦄ := by
mvcgen
case inv1 => exact (⇓ (xs, r) => ⌜(∀ x, x ∈ xs.suff → x ≤ 5) ∧ r + xs.suff.length * 5 ≤ 25⌝)
case inv1 => exact (⇓ (xs, r) => ⌜r + xs.suffix.length * 5 ≤ 25⌝)
all_goals simp_all; try grind
/--
@ -555,7 +555,7 @@ example (p : Nat → Prop) [DecidablePred p] (n : Nat) :
case inv1 =>
exact Invariant.withEarlyReturn
(onReturn := fun ret _ => ⌜ret = false ∧ ¬ ∀ i < n, p i⌝)
(onContinue := fun xs _ => ⌜∀ i, i ∈ xs.pref → p i⌝)
(onContinue := fun xs _ => ⌜∀ i, i ∈ xs.prefix → p i⌝)
all_goals simp_all [-Classical.not_forall]; try grind
end Automated
@ -704,7 +704,7 @@ def max_and_sum (xs : Array Nat) : Id (Nat × Nat) := do
theorem max_and_sum_spec (xs : Array Nat) :
⦃⌜∀ i, (h : i < xs.size) → xs[i] ≥ 0⌝⦄ max_and_sum xs ⦃⇓ (m, s) => ⌜s ≤ m * xs.size⌝⦄ := by
mvcgen [max_and_sum]
case inv1 => exact (⇓ ⟨xs, m, s⟩ => ⌜s ≤ m * xs.rpref.length⌝)
case inv1 => exact (⇓ ⟨xs, m, s⟩ => ⌜s ≤ m * xs.prefix.length⌝)
all_goals simp_all
· rw [Nat.left_distrib]
simp +zetaDelta only [Nat.mul_one, Nat.add_le_add_iff_right]
@ -851,20 +851,20 @@ theorem naive_expo_correct (x n : Nat) : naive_expo x n = x^n := by
generalize h : naive_expo x n = r
apply Id.of_wp_run_eq h
mvcgen
case inv1 => exact ⇓⟨xs, r⟩ => ⌜r = x^xs.rpref.length⌝
case inv1 => exact ⇓⟨xs, r⟩ => ⌜r = x^xs.prefix.length⌝
all_goals simp_all [Nat.pow_add_one]
theorem fast_expo_correct (x n : Nat) : fast_expo x n = x^n := by
generalize h : fast_expo x n = r
apply Id.of_wp_run_eq h
mvcgen
case inv1 => exact ⇓⟨xs, e, x', y⟩ => ⌜x' ^ e * y = x ^ n ∧ e ≤ n - xs.rpref.length⌝
case inv1 => exact ⇓⟨xs, e, x', y⟩ => ⌜x' ^ e * y = x ^ n ∧ e ≤ n - xs.prefix.length⌝
all_goals simp_all
case vc1 b _ _ _ _ _ _ _ _ _ _ ih =>
case vc1 b _ _ _ _ _ _ ih =>
obtain ⟨e, y, x'⟩ := b
subst_vars
grind
case vc2 b _ _ _ _ _ _ _ _ _ _ ih _ =>
case vc2 b _ _ _ _ _ _ ih _ =>
obtain ⟨e, y, x'⟩ := b
simp at *
constructor
@ -872,7 +872,7 @@ theorem fast_expo_correct (x n : Nat) : fast_expo x n = x^n := by
have : e - 1 + 1 = e := by grind
rw [this]
· grind
case vc3 b _ _ _ _ _ _ _ _ _ _ ih _ =>
case vc3 b _ _ _ _ _ _ ih _ =>
obtain ⟨e, y, x'⟩ := b
simp at *
constructor
@ -909,7 +909,7 @@ theorem nodup_correct (l : List Int) : nodup l ↔ l.Nodup := by
exact Invariant.withEarlyReturn
(onReturn := fun ret seen => ⌜ret = false ∧ ¬l.Nodup⌝)
(onContinue := fun traversalState seen =>
⌜(∀ x, x ∈ seen ↔ x ∈ traversalState.pref) ∧ traversalState.pref.Nodup⌝)
⌜(∀ x, x ∈ seen ↔ x ∈ traversalState.prefix) ∧ traversalState.prefix.Nodup⌝)
all_goals mleave; grind
theorem nodup_correct_directly (l : List Int) : nodup l ↔ l.Nodup := by

View file

@ -75,7 +75,7 @@ theorem pairsSumToZero_correct (l : List Int) : pairsSumToZero l ↔ l.ExistsPai
exact Invariant.withEarlyReturn
(onReturn := fun r b => ⌜r = true ∧ l.ExistsPair (fun a b => a + b = 0)⌝)
(onContinue := fun traversalState seen =>
⌜(∀ x, x ∈ seen ↔ x ∈ traversalState.rpref) ∧ ¬traversalState.pref.ExistsPair (fun a b => a + b = 0)⌝)
⌜(∀ x, x ∈ seen ↔ x ∈ traversalState.prefix) ∧ ¬traversalState.prefix.ExistsPair (fun a b => a + b = 0)⌝)
all_goals simp_all <;> grind