mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-19 11:24:07 +00:00
Compare commits
42 Commits
change_arr
...
floatbits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d0bc1647f | ||
|
|
688ee4c887 | ||
|
|
9a3dd615e0 | ||
|
|
7e6363dc05 | ||
|
|
a074bd9a2b | ||
|
|
498d41633b | ||
|
|
e0d7c3ac79 | ||
|
|
6a5b122b40 | ||
|
|
bf9ddf2c74 | ||
|
|
3f47871e73 | ||
|
|
85f25967ea | ||
|
|
8e1ddbc5aa | ||
|
|
e6e39f502f | ||
|
|
debb82bc20 | ||
|
|
9a85433477 | ||
|
|
4616c0ac3e | ||
|
|
e55b681774 | ||
|
|
63132105ba | ||
|
|
350b36411c | ||
|
|
1c30c76e72 | ||
|
|
d5adadc00e | ||
|
|
f08805e5c4 | ||
|
|
256b49bda9 | ||
|
|
28cf146d00 | ||
|
|
970261b1e1 | ||
|
|
6b811f8c92 | ||
|
|
f721f94045 | ||
|
|
86524d5c23 | ||
|
|
f18d9e04bc | ||
|
|
fa33423c84 | ||
|
|
1315266dd3 | ||
|
|
b1e52f1475 | ||
|
|
985600f448 | ||
|
|
ace6248e20 | ||
|
|
9f42368e1a | ||
|
|
a401368384 | ||
|
|
5e01e628b2 | ||
|
|
3a408e0e54 | ||
|
|
675d2d5a11 | ||
|
|
281c07ca97 | ||
|
|
004430b568 | ||
|
|
61f7dcb36b |
2
.github/workflows/pr-body.yml
vendored
2
.github/workflows/pr-body.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Check PR body for changelog convention
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited, labeled, converted_to_draft, ready_for_review]
|
||||
|
||||
@@ -9,6 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR body
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
|
||||
@@ -170,7 +170,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
ln -sf ${lean-all}/* .
|
||||
'';
|
||||
buildPhase = ''
|
||||
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi' -j$NIX_BUILD_CORES
|
||||
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi|leanruntest_timeIO' -j$NIX_BUILD_CORES
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
|
||||
@@ -1922,12 +1922,12 @@ represents an element of `Squash α` the same as `α` itself
|
||||
`Squash.lift` will extract a value in any subsingleton `β` from a function on `α`,
|
||||
while `Nonempty.rec` can only do the same when `β` is a proposition.
|
||||
-/
|
||||
def Squash (α : Type u) := Quot (fun (_ _ : α) => True)
|
||||
def Squash (α : Sort u) := Quot (fun (_ _ : α) => True)
|
||||
|
||||
/-- The canonical quotient map into `Squash α`. -/
|
||||
def Squash.mk {α : Type u} (x : α) : Squash α := Quot.mk _ x
|
||||
def Squash.mk {α : Sort u} (x : α) : Squash α := Quot.mk _ x
|
||||
|
||||
theorem Squash.ind {α : Type u} {motive : Squash α → Prop} (h : ∀ (a : α), motive (Squash.mk a)) : ∀ (q : Squash α), motive q :=
|
||||
theorem Squash.ind {α : Sort u} {motive : Squash α → Prop} (h : ∀ (a : α), motive (Squash.mk a)) : ∀ (q : Squash α), motive q :=
|
||||
Quot.ind h
|
||||
|
||||
/-- If `β` is a subsingleton, then a function `α → β` lifts to `Squash α → β`. -/
|
||||
|
||||
@@ -42,3 +42,4 @@ import Init.Data.PLift
|
||||
import Init.Data.Zero
|
||||
import Init.Data.NeZero
|
||||
import Init.Data.Function
|
||||
import Init.Data.RArray
|
||||
|
||||
@@ -18,3 +18,4 @@ import Init.Data.Array.Bootstrap
|
||||
import Init.Data.Array.GetLit
|
||||
import Init.Data.Array.MapIdx
|
||||
import Init.Data.Array.Set
|
||||
import Init.Data.Array.Monadic
|
||||
|
||||
@@ -10,6 +10,16 @@ import Init.Data.List.Attach
|
||||
|
||||
namespace Array
|
||||
|
||||
/-- `O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
|
||||
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
|
||||
but is defined only when all members of `l` satisfy `P`, using the proof
|
||||
to apply `f`.
|
||||
|
||||
We replace this at runtime with a more efficient version via
|
||||
-/
|
||||
def pmap {P : α → Prop} (f : ∀ a, P a → β) (l : Array α) (H : ∀ a ∈ l, P a) : Array β :=
|
||||
(l.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
|
||||
|
||||
/--
|
||||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||||
`Array {x // P x}` is the same as the input `Array α`.
|
||||
@@ -35,6 +45,10 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
l.toArray.attach = (l.attachWith (· ∈ l.toArray) (by simp)).toArray := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem _root_.List.pmap_toArray {l : List α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l.toArray, P a} :
|
||||
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp] theorem toList_attachWith {l : Array α} {P : α → Prop} {H : ∀ x ∈ l, P x} :
|
||||
(l.attachWith P H).toList = l.toList.attachWith P (by simpa [mem_toList] using H) := by
|
||||
simp [attachWith]
|
||||
@@ -43,6 +57,29 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
l.attach.toList = l.toList.attachWith (· ∈ l) (by simp [mem_toList]) := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem toList_pmap {l : Array α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l, P a} :
|
||||
(l.pmap f H).toList = l.toList.pmap f (fun a m => H a (mem_def.mpr m)) := by
|
||||
simp [pmap]
|
||||
|
||||
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
|
||||
@[inline] private def pmapImpl {P : α → Prop} (f : ∀ a, P a → β) (l : Array α) (H : ∀ a ∈ l, P a) :
|
||||
Array β := (l.attachWith _ H).map fun ⟨x, h'⟩ => f x h'
|
||||
|
||||
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
|
||||
funext α β p f L h'
|
||||
cases L
|
||||
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith]
|
||||
apply List.pmap_congr_left
|
||||
intro a m h₁ h₂
|
||||
congr
|
||||
|
||||
@[simp] theorem _root_.List.attachWith_mem_toArray {l : List α} :
|
||||
l.attachWith (fun x => x ∈ l.toArray) (fun x h => by simpa using h) =
|
||||
l.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
simp only [List.attachWith, List.attach, List.map_pmap]
|
||||
apply List.pmap_congr_left
|
||||
simp
|
||||
|
||||
/-! ## unattach
|
||||
|
||||
`Array.unattach` is the (one-sided) inverse of `Array.attach`. It is a synonym for `Array.map Subtype.val`.
|
||||
@@ -83,7 +120,7 @@ def unattach {α : Type _} {p : α → Prop} (l : Array { x // p x }) := l.map (
|
||||
|
||||
@[simp] theorem unattach_attach {l : Array α} : l.attach.unattach = l := by
|
||||
cases l
|
||||
simp
|
||||
simp only [List.attach_toArray, List.unattach_toArray, List.unattach_attachWith]
|
||||
|
||||
@[simp] theorem unattach_attachWith {p : α → Prop} {l : Array α}
|
||||
{H : ∀ a ∈ l, p a} :
|
||||
|
||||
@@ -166,13 +166,14 @@ count of 1 when called.
|
||||
-/
|
||||
@[extern "lean_array_fswap"]
|
||||
def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
|
||||
let v₁ := a.get i
|
||||
let v₂ := a.get j
|
||||
let v₁ := a[i]
|
||||
let v₂ := a[j]
|
||||
let a' := a.set i v₂
|
||||
a'.set j v₁ (Nat.lt_of_lt_of_eq j.isLt (size_set a i v₂ _).symm)
|
||||
|
||||
@[simp] theorem size_swap (a : Array α) (i j : Fin a.size) : (a.swap i j).size = a.size := by
|
||||
show ((a.set i (a.get j)).set j (a.get i) (Nat.lt_of_lt_of_eq j.isLt (size_set a i (a.get j) _).symm)).size = a.size
|
||||
show ((a.set i a[j]).set j a[i]
|
||||
(Nat.lt_of_lt_of_eq j.isLt (size_set a i a[j] _).symm)).size = a.size
|
||||
rw [size_set, size_set]
|
||||
|
||||
/--
|
||||
@@ -246,10 +247,10 @@ def get? (a : Array α) (i : Nat) : Option α :=
|
||||
if h : i < a.size then some a[i] else none
|
||||
|
||||
def back? (a : Array α) : Option α :=
|
||||
a.get? (a.size - 1)
|
||||
a[a.size - 1]?
|
||||
|
||||
@[inline] def swapAt (a : Array α) (i : Fin a.size) (v : α) : α × Array α :=
|
||||
let e := a.get i
|
||||
let e := a[i]
|
||||
let a := a.set i v
|
||||
(e, a)
|
||||
|
||||
@@ -273,24 +274,22 @@ def take (a : Array α) (n : Nat) : Array α :=
|
||||
@[inline]
|
||||
unsafe def modifyMUnsafe [Monad m] (a : Array α) (i : Nat) (f : α → m α) : m (Array α) := do
|
||||
if h : i < a.size then
|
||||
let idx : Fin a.size := ⟨i, h⟩
|
||||
let v := a.get idx
|
||||
let v := a[i]
|
||||
-- Replace a[i] by `box(0)`. This ensures that `v` remains unshared if possible.
|
||||
-- Note: we assume that arrays have a uniform representation irrespective
|
||||
-- of the element type, and that it is valid to store `box(0)` in any array.
|
||||
let a' := a.set idx (unsafeCast ())
|
||||
let a' := a.set i (unsafeCast ())
|
||||
let v ← f v
|
||||
pure <| a'.set idx v (Nat.lt_of_lt_of_eq h (size_set a ..).symm)
|
||||
pure <| a'.set i v (Nat.lt_of_lt_of_eq h (size_set a ..).symm)
|
||||
else
|
||||
pure a
|
||||
|
||||
@[implemented_by modifyMUnsafe]
|
||||
def modifyM [Monad m] (a : Array α) (i : Nat) (f : α → m α) : m (Array α) := do
|
||||
if h : i < a.size then
|
||||
let idx := ⟨i, h⟩
|
||||
let v := a.get idx
|
||||
let v := a[i]
|
||||
let v ← f v
|
||||
pure <| a.set idx v
|
||||
pure <| a.set i v
|
||||
else
|
||||
pure a
|
||||
|
||||
@@ -443,6 +442,8 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
map 0 (mkEmpty as.size)
|
||||
|
||||
@[deprecated mapM (since := "2024-11-11")] abbrev sequenceMap := @mapM
|
||||
|
||||
/-- Variant of `mapIdxM` which receives the index as a `Fin as.size`. -/
|
||||
@[inline]
|
||||
def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
@@ -455,15 +456,15 @@ def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
rw [← inv, Nat.add_assoc, Nat.add_comm 1 j, Nat.add_comm]
|
||||
apply Nat.le_add_right
|
||||
have : i + (j + 1) = as.size := by rw [← inv, Nat.add_comm j 1, Nat.add_assoc]
|
||||
map i (j+1) this (bs.push (← f ⟨j, j_lt⟩ (as.get ⟨j, j_lt⟩)))
|
||||
map i (j+1) this (bs.push (← f ⟨j, j_lt⟩ (as.get j j_lt)))
|
||||
map as.size 0 rfl (mkEmpty as.size)
|
||||
|
||||
@[inline]
|
||||
def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : Nat → α → m β) : m (Array β) :=
|
||||
def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : Nat → α → m β) (as : Array α) : m (Array β) :=
|
||||
as.mapFinIdxM fun i a => f i a
|
||||
|
||||
@[inline]
|
||||
def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : α → m (Option β)) : m (Option β) := do
|
||||
def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m (Option β)) (as : Array α) : m (Option β) := do
|
||||
for a in as do
|
||||
match (← f a) with
|
||||
| some b => return b
|
||||
@@ -471,14 +472,14 @@ def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as
|
||||
return none
|
||||
|
||||
@[inline]
|
||||
def findM? {α : Type} {m : Type → Type} [Monad m] (as : Array α) (p : α → m Bool) : m (Option α) := do
|
||||
def findM? {α : Type} {m : Type → Type} [Monad m] (p : α → m Bool) (as : Array α) : m (Option α) := do
|
||||
for a in as do
|
||||
if (← p a) then
|
||||
return a
|
||||
return none
|
||||
|
||||
@[inline]
|
||||
def findIdxM? [Monad m] (as : Array α) (p : α → m Bool) : m (Option Nat) := do
|
||||
def findIdxM? [Monad m] (p : α → m Bool) (as : Array α) : m (Option Nat) := do
|
||||
let mut i := 0
|
||||
for a in as do
|
||||
if (← p a) then
|
||||
@@ -530,7 +531,7 @@ def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as :
|
||||
return !(← as.anyM (start := start) (stop := stop) fun v => return !(← p v))
|
||||
|
||||
@[inline]
|
||||
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : α → m (Option β)) : m (Option β) :=
|
||||
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m (Option β)) (as : Array α) : m (Option β) :=
|
||||
let rec @[specialize] find : (i : Nat) → i ≤ as.size → m (Option β)
|
||||
| 0, _ => pure none
|
||||
| i+1, h => do
|
||||
@@ -544,7 +545,7 @@ def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
find as.size (Nat.le_refl _)
|
||||
|
||||
@[inline]
|
||||
def findRevM? {α : Type} {m : Type → Type w} [Monad m] (as : Array α) (p : α → m Bool) : m (Option α) :=
|
||||
def findRevM? {α : Type} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) : m (Option α) :=
|
||||
as.findSomeRevM? fun a => return if (← p a) then some a else none
|
||||
|
||||
@[inline]
|
||||
@@ -573,7 +574,7 @@ def mapFinIdx {α : Type u} {β : Type v} (as : Array α) (f : Fin as.size →
|
||||
Id.run <| as.mapFinIdxM f
|
||||
|
||||
@[inline]
|
||||
def mapIdx {α : Type u} {β : Type v} (as : Array α) (f : Nat → α → β) : Array β :=
|
||||
def mapIdx {α : Type u} {β : Type v} (f : Nat → α → β) (as : Array α) : Array β :=
|
||||
Id.run <| as.mapIdxM f
|
||||
|
||||
/-- Turns `#[a, b]` into `#[(a, 0), (b, 1)]`. -/
|
||||
@@ -581,29 +582,29 @@ def zipWithIndex (arr : Array α) : Array (α × Nat) :=
|
||||
arr.mapIdx fun i a => (a, i)
|
||||
|
||||
@[inline]
|
||||
def find? {α : Type} (as : Array α) (p : α → Bool) : Option α :=
|
||||
def find? {α : Type} (p : α → Bool) (as : Array α) : Option α :=
|
||||
Id.run <| as.findM? p
|
||||
|
||||
@[inline]
|
||||
def findSome? {α : Type u} {β : Type v} (as : Array α) (f : α → Option β) : Option β :=
|
||||
def findSome? {α : Type u} {β : Type v} (f : α → Option β) (as : Array α) : Option β :=
|
||||
Id.run <| as.findSomeM? f
|
||||
|
||||
@[inline]
|
||||
def findSome! {α : Type u} {β : Type v} [Inhabited β] (a : Array α) (f : α → Option β) : β :=
|
||||
match findSome? a f with
|
||||
def findSome! {α : Type u} {β : Type v} [Inhabited β] (f : α → Option β) (a : Array α) : β :=
|
||||
match a.findSome? f with
|
||||
| some b => b
|
||||
| none => panic! "failed to find element"
|
||||
|
||||
@[inline]
|
||||
def findSomeRev? {α : Type u} {β : Type v} (as : Array α) (f : α → Option β) : Option β :=
|
||||
def findSomeRev? {α : Type u} {β : Type v} (f : α → Option β) (as : Array α) : Option β :=
|
||||
Id.run <| as.findSomeRevM? f
|
||||
|
||||
@[inline]
|
||||
def findRev? {α : Type} (as : Array α) (p : α → Bool) : Option α :=
|
||||
def findRev? {α : Type} (p : α → Bool) (as : Array α) : Option α :=
|
||||
Id.run <| as.findRevM? p
|
||||
|
||||
@[inline]
|
||||
def findIdx? {α : Type u} (as : Array α) (p : α → Bool) : Option Nat :=
|
||||
def findIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option Nat :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) :=
|
||||
if h : j < as.size then
|
||||
@@ -618,8 +619,7 @@ def getIdx? [BEq α] (a : Array α) (v : α) : Option Nat :=
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size) :=
|
||||
if h : i < a.size then
|
||||
let idx : Fin a.size := ⟨i, h⟩;
|
||||
if a.get idx == v then some idx
|
||||
if a[i] == v then some ⟨i, h⟩
|
||||
else indexOfAux a v (i+1)
|
||||
else none
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
@@ -744,7 +744,7 @@ where
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
if h : as.size > 0 then
|
||||
if p (as.get ⟨as.size - 1, Nat.sub_lt h (by decide)⟩) then
|
||||
if p (as[as.size - 1]'(Nat.sub_lt h (by decide))) then
|
||||
popWhile p as.pop
|
||||
else
|
||||
as
|
||||
@@ -756,7 +756,7 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
go (i : Nat) (r : Array α) : Array α :=
|
||||
if h : i < as.size then
|
||||
let a := as.get ⟨i, h⟩
|
||||
let a := as[i]
|
||||
if p a then
|
||||
go (i+1) (r.push a)
|
||||
else
|
||||
|
||||
@@ -15,26 +15,26 @@ This file contains some theorems about `Array` and `List` needed for `Init.Data.
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem foldlM_eq_foldlM_toList.aux [Monad m]
|
||||
theorem foldlM_toList.aux [Monad m]
|
||||
(f : β → α → m β) (arr : Array α) (i j) (H : arr.size ≤ i + j) (b) :
|
||||
foldlM.loop f arr arr.size (Nat.le_refl _) i j b = (arr.toList.drop j).foldlM f b := by
|
||||
unfold foldlM.loop
|
||||
split; split
|
||||
· cases Nat.not_le_of_gt ‹_› (Nat.zero_add _ ▸ H)
|
||||
· rename_i i; rw [Nat.succ_add] at H
|
||||
simp [foldlM_eq_foldlM_toList.aux f arr i (j+1) H]
|
||||
simp [foldlM_toList.aux f arr i (j+1) H]
|
||||
rw (occs := .pos [2]) [← List.getElem_cons_drop_succ_eq_drop ‹_›]
|
||||
rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
|
||||
theorem foldlM_eq_foldlM_toList [Monad m]
|
||||
@[simp] theorem foldlM_toList [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
arr.foldlM f init = arr.toList.foldlM f init := by
|
||||
simp [foldlM, foldlM_eq_foldlM_toList.aux]
|
||||
arr.toList.foldlM f init = arr.foldlM f init := by
|
||||
simp [foldlM, foldlM_toList.aux]
|
||||
|
||||
theorem foldl_eq_foldl_toList (f : β → α → β) (init : β) (arr : Array α) :
|
||||
arr.foldl f init = arr.toList.foldl f init :=
|
||||
List.foldl_eq_foldlM .. ▸ foldlM_eq_foldlM_toList ..
|
||||
@[simp] theorem foldl_toList (f : β → α → β) (init : β) (arr : Array α) :
|
||||
arr.toList.foldl f init = arr.foldl f init :=
|
||||
List.foldl_eq_foldlM .. ▸ foldlM_toList ..
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_toList.aux [Monad m]
|
||||
(f : α → β → m β) (arr : Array α) (init : β) (i h) :
|
||||
@@ -51,23 +51,23 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] (f : α → β → m β) (init
|
||||
match arr, this with | _, .inl rfl => rfl | arr, .inr h => ?_
|
||||
simp [foldrM, h, ← foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
|
||||
|
||||
theorem foldrM_eq_foldrM_toList [Monad m]
|
||||
@[simp] theorem foldrM_toList [Monad m]
|
||||
(f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.toList.foldrM f init := by
|
||||
arr.toList.foldrM f init = arr.foldrM f init := by
|
||||
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
|
||||
|
||||
theorem foldr_eq_foldr_toList (f : α → β → β) (init : β) (arr : Array α) :
|
||||
arr.foldr f init = arr.toList.foldr f init :=
|
||||
List.foldr_eq_foldrM .. ▸ foldrM_eq_foldrM_toList ..
|
||||
@[simp] theorem foldr_toList (f : α → β → β) (init : β) (arr : Array α) :
|
||||
arr.toList.foldr f init = arr.foldr f init :=
|
||||
List.foldr_eq_foldrM .. ▸ foldrM_toList ..
|
||||
|
||||
@[simp] theorem push_toList (arr : Array α) (a : α) : (arr.push a).toList = arr.toList ++ [a] := by
|
||||
simp [push, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toListAppend_eq (arr : Array α) (l) : arr.toListAppend l = arr.toList ++ l := by
|
||||
simp [toListAppend, foldr_eq_foldr_toList]
|
||||
simp [toListAppend, ← foldr_toList]
|
||||
|
||||
@[simp] theorem toListImpl_eq (arr : Array α) : arr.toListImpl = arr.toList := by
|
||||
simp [toListImpl, foldr_eq_foldr_toList]
|
||||
simp [toListImpl, ← foldr_toList]
|
||||
|
||||
@[simp] theorem pop_toList (arr : Array α) : arr.pop.toList = arr.toList.dropLast := rfl
|
||||
|
||||
@@ -76,7 +76,7 @@ theorem foldr_eq_foldr_toList (f : α → β → β) (init : β) (arr : Array α
|
||||
@[simp] theorem toList_append (arr arr' : Array α) :
|
||||
(arr ++ arr').toList = arr.toList ++ arr'.toList := by
|
||||
rw [← append_eq_append]; unfold Array.append
|
||||
rw [foldl_eq_foldl_toList]
|
||||
rw [← foldl_toList]
|
||||
induction arr'.toList generalizing arr <;> simp [*]
|
||||
|
||||
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
|
||||
@@ -98,20 +98,44 @@ theorem foldr_eq_foldr_toList (f : α → β → β) (init : β) (arr : Array α
|
||||
rw [← appendList_eq_append]; unfold Array.appendList
|
||||
induction l generalizing arr <;> simp [*]
|
||||
|
||||
@[deprecated foldlM_eq_foldlM_toList (since := "2024-09-09")]
|
||||
abbrev foldlM_eq_foldlM_data := @foldlM_eq_foldlM_toList
|
||||
@[deprecated "Use the reverse direction of `foldrM_toList`." (since := "2024-11-13")]
|
||||
theorem foldrM_eq_foldrM_toList [Monad m]
|
||||
(f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.toList.foldrM f init := by
|
||||
simp
|
||||
|
||||
@[deprecated foldl_eq_foldl_toList (since := "2024-09-09")]
|
||||
abbrev foldl_eq_foldl_data := @foldl_eq_foldl_toList
|
||||
@[deprecated "Use the reverse direction of `foldlM_toList`." (since := "2024-11-13")]
|
||||
theorem foldlM_eq_foldlM_toList [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
arr.foldlM f init = arr.toList.foldlM f init:= by
|
||||
simp
|
||||
|
||||
@[deprecated "Use the reverse direction of `foldr_toList`." (since := "2024-11-13")]
|
||||
theorem foldr_eq_foldr_toList
|
||||
(f : α → β → β) (init : β) (arr : Array α) :
|
||||
arr.foldr f init = arr.toList.foldr f init := by
|
||||
simp
|
||||
|
||||
@[deprecated "Use the reverse direction of `foldl_toList`." (since := "2024-11-13")]
|
||||
theorem foldl_eq_foldl_toList
|
||||
(f : β → α → β) (init : β) (arr : Array α) :
|
||||
arr.foldl f init = arr.toList.foldl f init:= by
|
||||
simp
|
||||
|
||||
@[deprecated foldlM_toList (since := "2024-09-09")]
|
||||
abbrev foldlM_eq_foldlM_data := @foldlM_toList
|
||||
|
||||
@[deprecated foldl_toList (since := "2024-09-09")]
|
||||
abbrev foldl_eq_foldl_data := @foldl_toList
|
||||
|
||||
@[deprecated foldrM_eq_reverse_foldlM_toList (since := "2024-09-09")]
|
||||
abbrev foldrM_eq_reverse_foldlM_data := @foldrM_eq_reverse_foldlM_toList
|
||||
|
||||
@[deprecated foldrM_eq_foldrM_toList (since := "2024-09-09")]
|
||||
abbrev foldrM_eq_foldrM_data := @foldrM_eq_foldrM_toList
|
||||
@[deprecated foldrM_toList (since := "2024-09-09")]
|
||||
abbrev foldrM_eq_foldrM_data := @foldrM_toList
|
||||
|
||||
@[deprecated foldr_eq_foldr_toList (since := "2024-09-09")]
|
||||
abbrev foldr_eq_foldr_data := @foldr_eq_foldr_toList
|
||||
@[deprecated foldr_toList (since := "2024-09-09")]
|
||||
abbrev foldr_eq_foldr_data := @foldr_toList
|
||||
|
||||
@[deprecated push_toList (since := "2024-09-09")]
|
||||
abbrev push_data := @push_toList
|
||||
|
||||
@@ -76,6 +76,8 @@ theorem getElem_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size)
|
||||
theorem singleton_inj : #[a] = #[b] ↔ a = b := by
|
||||
simp
|
||||
|
||||
theorem singleton_eq_toArray_singleton (a : α) : #[a] = [a].toArray := rfl
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
@@ -111,6 +113,9 @@ We prefer to pull `List.toArray` outwards.
|
||||
@[simp] theorem back!_toArray [Inhabited α] (l : List α) : l.toArray.back! = l.getLast! := by
|
||||
simp only [back!, size_toArray, Array.get!_eq_getElem!, getElem!_toArray, getLast!_eq_getElem!]
|
||||
|
||||
@[simp] theorem back?_toArray (l : List α) : l.toArray.back? = l.getLast? := by
|
||||
simp [back?, List.getLast?_eq_getElem?]
|
||||
|
||||
@[simp] theorem forIn'_loop_toArray [Monad m] (l : List α) (f : (a : α) → a ∈ l.toArray → β → m (ForInStep β)) (i : Nat)
|
||||
(h : i ≤ l.length) (b : β) :
|
||||
Array.forIn'.loop l.toArray f i h b =
|
||||
@@ -146,15 +151,15 @@ theorem foldrM_toArray [Monad m] (f : α → β → m β) (init : β) (l : List
|
||||
|
||||
theorem foldlM_toArray [Monad m] (f : β → α → m β) (init : β) (l : List α) :
|
||||
l.toArray.foldlM f init = l.foldlM f init := by
|
||||
rw [foldlM_eq_foldlM_toList]
|
||||
rw [foldlM_toList]
|
||||
|
||||
theorem foldr_toArray (f : α → β → β) (init : β) (l : List α) :
|
||||
l.toArray.foldr f init = l.foldr f init := by
|
||||
rw [foldr_eq_foldr_toList]
|
||||
rw [foldr_toList]
|
||||
|
||||
theorem foldl_toArray (f : β → α → β) (init : β) (l : List α) :
|
||||
l.toArray.foldl f init = l.foldl f init := by
|
||||
rw [foldl_eq_foldl_toList]
|
||||
rw [foldl_toList]
|
||||
|
||||
/-- Variant of `foldrM_toArray` with a side condition for the `start` argument. -/
|
||||
@[simp] theorem foldrM_toArray' [Monad m] (f : α → β → m β) (init : β) (l : List α)
|
||||
@@ -169,21 +174,21 @@ theorem foldl_toArray (f : β → α → β) (init : β) (l : List α) :
|
||||
(h : stop = l.toArray.size) :
|
||||
l.toArray.foldlM f init 0 stop = l.foldlM f init := by
|
||||
subst h
|
||||
rw [foldlM_eq_foldlM_toList]
|
||||
rw [foldlM_toList]
|
||||
|
||||
/-- Variant of `foldr_toArray` with a side condition for the `start` argument. -/
|
||||
@[simp] theorem foldr_toArray' (f : α → β → β) (init : β) (l : List α)
|
||||
(h : start = l.toArray.size) :
|
||||
l.toArray.foldr f init start 0 = l.foldr f init := by
|
||||
subst h
|
||||
rw [foldr_eq_foldr_toList]
|
||||
rw [foldr_toList]
|
||||
|
||||
/-- Variant of `foldl_toArray` with a side condition for the `stop` argument. -/
|
||||
@[simp] theorem foldl_toArray' (f : β → α → β) (init : β) (l : List α)
|
||||
(h : stop = l.toArray.size) :
|
||||
l.toArray.foldl f init 0 stop = l.foldl f init := by
|
||||
subst h
|
||||
rw [foldl_eq_foldl_toList]
|
||||
rw [foldl_toList]
|
||||
|
||||
@[simp] theorem append_toArray (l₁ l₂ : List α) :
|
||||
l₁.toArray ++ l₂.toArray = (l₁ ++ l₂).toArray := by
|
||||
@@ -197,6 +202,9 @@ theorem foldl_toArray (f : β → α → β) (init : β) (l : List α) :
|
||||
@[simp] theorem foldl_push {l : List α} {as : Array α} : l.foldl Array.push as = as ++ l.toArray := by
|
||||
induction l generalizing as <;> simp [*]
|
||||
|
||||
@[simp] theorem foldr_push {l : List α} {as : Array α} : l.foldr (fun a b => push b a) as = as ++ l.reverse.toArray := by
|
||||
rw [foldr_eq_foldl_reverse, foldl_push]
|
||||
|
||||
@[simp] theorem findSomeM?_toArray [Monad m] [LawfulMonad m] (f : α → m (Option β)) (l : List α) :
|
||||
l.toArray.findSomeM? f = l.findSomeM? f := by
|
||||
rw [Array.findSomeM?]
|
||||
@@ -210,7 +218,7 @@ theorem foldl_toArray (f : β → α → β) (init : β) (l : List α) :
|
||||
|
||||
theorem findSomeRevM?_find_toArray [Monad m] [LawfulMonad m] (f : α → m (Option β)) (l : List α)
|
||||
(i : Nat) (h) :
|
||||
findSomeRevM?.find l.toArray f i h = (l.take i).reverse.findSomeM? f := by
|
||||
findSomeRevM?.find f l.toArray i h = (l.take i).reverse.findSomeM? f := by
|
||||
induction i generalizing l with
|
||||
| zero => simp [Array.findSomeRevM?.find.eq_def]
|
||||
| succ i ih =>
|
||||
@@ -357,7 +365,8 @@ namespace Array
|
||||
|
||||
theorem foldrM_push [Monad m] (f : α → β → m β) (init : β) (arr : Array α) (a : α) :
|
||||
(arr.push a).foldrM f init = f a init >>= arr.foldrM f := by
|
||||
simp [foldrM_eq_reverse_foldlM_toList, -size_push]
|
||||
simp only [foldrM_eq_reverse_foldlM_toList, push_toList, List.reverse_append, List.reverse_cons,
|
||||
List.reverse_nil, List.nil_append, List.singleton_append, List.foldlM_cons, List.foldlM_reverse]
|
||||
|
||||
/--
|
||||
Variant of `foldrM_push` with `h : start = arr.size + 1`
|
||||
@@ -383,11 +392,11 @@ rather than `(arr.push a).size` as the argument.
|
||||
@[inline] def toListRev (arr : Array α) : List α := arr.foldl (fun l t => t :: l) []
|
||||
|
||||
@[simp] theorem toListRev_eq (arr : Array α) : arr.toListRev = arr.toList.reverse := by
|
||||
rw [toListRev, foldl_eq_foldl_toList, ← List.foldr_reverse, List.foldr_cons_nil]
|
||||
rw [toListRev, ← foldl_toList, ← List.foldr_reverse, List.foldr_cons_nil]
|
||||
|
||||
theorem mapM_eq_foldlM [Monad m] [LawfulMonad m] (f : α → m β) (arr : Array α) :
|
||||
arr.mapM f = arr.foldlM (fun bs a => bs.push <$> f a) #[] := by
|
||||
rw [mapM, aux, foldlM_eq_foldlM_toList]; rfl
|
||||
rw [mapM, aux, ← foldlM_toList]; rfl
|
||||
where
|
||||
aux (i r) :
|
||||
mapM.map f arr i r = (arr.toList.drop i).foldlM (fun bs a => bs.push <$> f a) r := by
|
||||
@@ -402,7 +411,7 @@ where
|
||||
|
||||
@[simp] theorem toList_map (f : α → β) (arr : Array α) : (arr.map f).toList = arr.toList.map f := by
|
||||
rw [map, mapM_eq_foldlM]
|
||||
apply congrArg toList (foldl_eq_foldl_toList (fun bs a => push bs (f a)) #[] arr) |>.trans
|
||||
apply congrArg toList (foldl_toList (fun bs a => push bs (f a)) #[] arr).symm |>.trans
|
||||
have H (l arr) : List.foldl (fun bs a => push bs (f a)) arr l = ⟨arr.toList ++ l.map f⟩ := by
|
||||
induction l generalizing arr <;> simp [*]
|
||||
simp [H]
|
||||
@@ -450,7 +459,7 @@ theorem size_uset (a : Array α) (v i h) : (uset a i v h).size = a.size := by si
|
||||
|
||||
/-! # get -/
|
||||
|
||||
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
|
||||
@[simp] theorem get_eq_getElem (a : Array α) (i : Nat) (h) : a.get i h = a[i] := rfl
|
||||
|
||||
theorem getElem?_lt
|
||||
(a : Array α) {i : Nat} (h : i < a.size) : a[i]? = some a[i] := dif_pos h
|
||||
@@ -581,6 +590,8 @@ theorem getElem?_ofFn (f : Fin n → α) (i : Nat) :
|
||||
|
||||
@[simp] theorem toList_mkArray (n : Nat) (v : α) : (mkArray n v).toList = List.replicate n v := rfl
|
||||
|
||||
theorem mkArray_eq_toArray_replicate (n : Nat) (v : α) : mkArray n v = (List.replicate n v).toArray := rfl
|
||||
|
||||
@[simp] theorem getElem_mkArray (n : Nat) (v : α) (h : i < (mkArray n v).size) :
|
||||
(mkArray n v)[i] = v := by simp [Array.getElem_eq_getElem_toList]
|
||||
|
||||
@@ -730,11 +741,11 @@ theorem set_set (a : Array α) (i : Nat) (h) (v v' : α) :
|
||||
private theorem fin_cast_val (e : n = n') (i : Fin n) : e ▸ i = ⟨i.1, e ▸ i.2⟩ := by cases e; rfl
|
||||
|
||||
theorem swap_def (a : Array α) (i j : Fin a.size) :
|
||||
a.swap i j = (a.set i (a.get j)).set j (a.get i) := by
|
||||
a.swap i j = (a.set i a[j]).set j a[i] := by
|
||||
simp [swap, fin_cast_val]
|
||||
|
||||
@[simp] theorem toList_swap (a : Array α) (i j : Fin a.size) :
|
||||
(a.swap i j).toList = (a.toList.set i (a.get j)).set j (a.get i) := by simp [swap_def]
|
||||
(a.swap i j).toList = (a.toList.set i a[j]).set j a[i] := by simp [swap_def]
|
||||
|
||||
theorem getElem?_swap (a : Array α) (i j : Fin a.size) (k : Nat) : (a.swap i j)[k]? =
|
||||
if j = k then some a[i.1] else if i = k then some a[j.1] else a[k]? := by
|
||||
@@ -1016,7 +1027,7 @@ theorem foldr_congr {as bs : Array α} (h₀ : as = bs) {f g : α → β → β}
|
||||
|
||||
theorem mapM_eq_mapM_toList [Monad m] [LawfulMonad m] (f : α → m β) (arr : Array α) :
|
||||
arr.mapM f = List.toArray <$> (arr.toList.mapM f) := by
|
||||
rw [mapM_eq_foldlM, foldlM_eq_foldlM_toList, ← List.foldrM_reverse]
|
||||
rw [mapM_eq_foldlM, ← foldlM_toList, ← List.foldrM_reverse]
|
||||
conv => rhs; rw [← List.reverse_reverse arr.toList]
|
||||
induction arr.toList.reverse with
|
||||
| nil => simp
|
||||
@@ -1141,7 +1152,7 @@ theorem getElem?_modify {as : Array α} {i : Nat} {f : α → α} {j : Nat} :
|
||||
@[simp] theorem toList_filter (p : α → Bool) (l : Array α) :
|
||||
(l.filter p).toList = l.toList.filter p := by
|
||||
dsimp only [filter]
|
||||
rw [foldl_eq_foldl_toList]
|
||||
rw [← foldl_toList]
|
||||
generalize l.toList = l
|
||||
suffices ∀ a, (List.foldl (fun r a => if p a = true then push r a else r) a l).toList =
|
||||
a.toList ++ List.filter p l by
|
||||
@@ -1172,7 +1183,7 @@ theorem filter_congr {as bs : Array α} (h : as = bs)
|
||||
@[simp] theorem toList_filterMap (f : α → Option β) (l : Array α) :
|
||||
(l.filterMap f).toList = l.toList.filterMap f := by
|
||||
dsimp only [filterMap, filterMapM]
|
||||
rw [foldlM_eq_foldlM_toList]
|
||||
rw [← foldlM_toList]
|
||||
generalize l.toList = l
|
||||
have this : ∀ a : Array β, (Id.run (List.foldlM (m := Id) ?_ a l)).toList =
|
||||
a.toList ++ List.filterMap f l := ?_
|
||||
@@ -1251,7 +1262,7 @@ theorem getElem?_append {as bs : Array α} {n : Nat} :
|
||||
@[simp] theorem toList_flatten {l : Array (Array α)} :
|
||||
l.flatten.toList = (l.toList.map toList).flatten := by
|
||||
dsimp [flatten]
|
||||
simp only [foldl_eq_foldl_toList]
|
||||
simp only [← foldl_toList]
|
||||
generalize l.toList = l
|
||||
have : ∀ a : Array α, (List.foldl ?_ a l).toList = a.toList ++ ?_ := ?_
|
||||
exact this #[]
|
||||
@@ -1470,7 +1481,7 @@ termination_by stop - start
|
||||
|
||||
-- This could also be proved from `SatisfiesM_anyM_iff_exists` in `Batteries.Data.Array.Init.Monadic`
|
||||
theorem any_iff_exists {p : α → Bool} {as : Array α} {start stop} :
|
||||
any as p start stop ↔ ∃ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop ∧ p as[i] := by
|
||||
as.any p start stop ↔ ∃ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop ∧ p as[i] := by
|
||||
dsimp [any, anyM, Id.run]
|
||||
split
|
||||
· rw [anyM_loop_iff_exists]; rfl
|
||||
@@ -1482,7 +1493,7 @@ theorem any_iff_exists {p : α → Bool} {as : Array α} {start stop} :
|
||||
exact ⟨i, by omega, by omega, h⟩
|
||||
|
||||
theorem any_eq_true {p : α → Bool} {as : Array α} :
|
||||
any as p ↔ ∃ i : Fin as.size, p as[i] := by simp [any_iff_exists, Fin.isLt]
|
||||
as.any p ↔ ∃ i : Fin as.size, p as[i] := by simp [any_iff_exists, Fin.isLt]
|
||||
|
||||
theorem any_toList {p : α → Bool} (as : Array α) : as.toList.any p = as.any p := by
|
||||
rw [Bool.eq_iff_iff, any_eq_true, List.any_eq_true]; simp only [List.mem_iff_get]
|
||||
@@ -1502,20 +1513,20 @@ theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α → m Bool) (as :
|
||||
rw [List.allM_eq_not_anyM_not]
|
||||
|
||||
theorem all_eq_not_any_not (p : α → Bool) (as : Array α) (start stop) :
|
||||
all as p start stop = !(any as (!p ·) start stop) := by
|
||||
as.all p start stop = !(as.any (!p ·) start stop) := by
|
||||
dsimp [all, allM]
|
||||
rfl
|
||||
|
||||
theorem all_iff_forall {p : α → Bool} {as : Array α} {start stop} :
|
||||
all as p start stop ↔ ∀ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop → p as[i] := by
|
||||
as.all p start stop ↔ ∀ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop → p as[i] := by
|
||||
rw [all_eq_not_any_not]
|
||||
suffices ¬(any as (!p ·) start stop = true) ↔
|
||||
suffices ¬(as.any (!p ·) start stop = true) ↔
|
||||
∀ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop → p as[i] by
|
||||
simp_all
|
||||
rw [any_iff_exists]
|
||||
simp
|
||||
|
||||
theorem all_eq_true {p : α → Bool} {as : Array α} : all as p ↔ ∀ i : Fin as.size, p as[i] := by
|
||||
theorem all_eq_true {p : α → Bool} {as : Array α} : as.all p ↔ ∀ i : Fin as.size, p as[i] := by
|
||||
simp [all_iff_forall, Fin.isLt]
|
||||
|
||||
theorem all_toList {p : α → Bool} (as : Array α) : as.toList.all p = as.all p := by
|
||||
@@ -2037,8 +2048,8 @@ abbrev mapM_eq_mapM_data := @mapM_eq_mapM_toList
|
||||
|
||||
@[deprecated getElem_modify (since := "2024-08-08")]
|
||||
theorem get_modify {arr : Array α} {x i} (h : i < (arr.modify x f).size) :
|
||||
(arr.modify x f).get ⟨i, h⟩ =
|
||||
if x = i then f (arr.get ⟨i, by simpa using h⟩) else arr.get ⟨i, by simpa using h⟩ := by
|
||||
(arr.modify x f).get i h =
|
||||
if x = i then f (arr.get i (by simpa using h)) else arr.get i (by simpa using h) := by
|
||||
simp [getElem_modify h]
|
||||
|
||||
@[deprecated toList_filter (since := "2024-09-09")]
|
||||
|
||||
@@ -66,35 +66,35 @@ theorem mapFinIdx_spec (as : Array α) (f : Fin as.size → α → β)
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
theorem mapIdx_induction (as : Array α) (f : Nat → α → β)
|
||||
theorem mapIdx_induction (f : Nat → α → β) (as : Array α)
|
||||
(motive : Nat → Prop) (h0 : motive 0)
|
||||
(p : Fin as.size → β → Prop)
|
||||
(hs : ∀ i, motive i.1 → p i (f i as[i]) ∧ motive (i + 1)) :
|
||||
motive as.size ∧ ∃ eq : (Array.mapIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapIdx as f)[i]) :=
|
||||
motive as.size ∧ ∃ eq : (as.mapIdx f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((as.mapIdx f)[i]) :=
|
||||
mapFinIdx_induction as (fun i a => f i a) motive h0 p hs
|
||||
|
||||
theorem mapIdx_spec (as : Array α) (f : Nat → α → β)
|
||||
theorem mapIdx_spec (f : Nat → α → β) (as : Array α)
|
||||
(p : Fin as.size → β → Prop) (hs : ∀ i, p i (f i as[i])) :
|
||||
∃ eq : (Array.mapIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapIdx as f)[i]) :=
|
||||
∃ eq : (as.mapIdx f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((as.mapIdx f)[i]) :=
|
||||
(mapIdx_induction _ _ (fun _ => True) trivial p fun _ _ => ⟨hs .., trivial⟩).2
|
||||
|
||||
@[simp] theorem size_mapIdx (a : Array α) (f : Nat → α → β) : (a.mapIdx f).size = a.size :=
|
||||
@[simp] theorem size_mapIdx (f : Nat → α → β) (as : Array α) : (as.mapIdx f).size = as.size :=
|
||||
(mapIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
|
||||
|
||||
@[simp] theorem getElem_mapIdx (a : Array α) (f : Nat → α → β) (i : Nat)
|
||||
(h : i < (mapIdx a f).size) :
|
||||
(a.mapIdx f)[i] = f i (a[i]'(by simp_all)) :=
|
||||
(mapIdx_spec _ _ (fun i b => b = f i a[i]) fun _ => rfl).2 i (by simp_all)
|
||||
@[simp] theorem getElem_mapIdx (f : Nat → α → β) (as : Array α) (i : Nat)
|
||||
(h : i < (as.mapIdx f).size) :
|
||||
(as.mapIdx f)[i] = f i (as[i]'(by simp_all)) :=
|
||||
(mapIdx_spec _ _ (fun i b => b = f i as[i]) fun _ => rfl).2 i (by simp_all)
|
||||
|
||||
@[simp] theorem getElem?_mapIdx (a : Array α) (f : Nat → α → β) (i : Nat) :
|
||||
(a.mapIdx f)[i]? =
|
||||
a[i]?.map (f i) := by
|
||||
@[simp] theorem getElem?_mapIdx (f : Nat → α → β) (as : Array α) (i : Nat) :
|
||||
(as.mapIdx f)[i]? =
|
||||
as[i]?.map (f i) := by
|
||||
simp [getElem?_def, size_mapIdx, getElem_mapIdx]
|
||||
|
||||
@[simp] theorem toList_mapIdx (a : Array α) (f : Nat → α → β) :
|
||||
(a.mapIdx f).toList = a.toList.mapIdx (fun i a => f i a) := by
|
||||
@[simp] theorem toList_mapIdx (f : Nat → α → β) (as : Array α) :
|
||||
(as.mapIdx f).toList = as.toList.mapIdx (fun i a => f i a) := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
end Array
|
||||
@@ -105,7 +105,7 @@ namespace List
|
||||
l.toArray.mapFinIdx f = (l.mapFinIdx f).toArray := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem mapIdx_toArray (l : List α) (f : Nat → α → β) :
|
||||
@[simp] theorem mapIdx_toArray (f : Nat → α → β) (l : List α) :
|
||||
l.toArray.mapIdx f = (l.mapIdx f).toArray := by
|
||||
ext <;> simp
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a <
|
||||
cases as with | _ as =>
|
||||
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)
|
||||
|
||||
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
|
||||
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) : sizeOf (as.get i h) < sizeOf as := by
|
||||
cases as with | _ as =>
|
||||
exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)
|
||||
simpa using Nat.lt_trans (List.sizeOf_get _ ⟨i, h⟩) (by simp_arith)
|
||||
|
||||
@[simp] theorem sizeOf_getElem [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) :
|
||||
sizeOf (as[i]'h) < sizeOf as := sizeOf_get _ _
|
||||
sizeOf (as[i]'h) < sizeOf as := sizeOf_get _ _ h
|
||||
|
||||
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that
|
||||
`sizeOf arr[i] < sizeOf arr`, which is useful for well founded recursions
|
||||
|
||||
159
src/Init/Data/Array/Monadic.lean
Normal file
159
src/Init/Data/Array/Monadic.lean
Normal file
@@ -0,0 +1,159 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.List.Monadic
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.forIn'` and `Array.forIn`.
|
||||
-/
|
||||
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Monadic operations -/
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] (f : α → m β) (l : Array α) :
|
||||
mapM f l = l.foldlM (fun acc a => return (acc.push (← f a))) #[] := by
|
||||
rcases l with ⟨l⟩
|
||||
simp only [List.mapM_toArray, bind_pure_comp, size_toArray, List.foldlM_toArray']
|
||||
rw [List.mapM_eq_reverse_foldlM_cons]
|
||||
simp only [bind_pure_comp, Functor.map_map]
|
||||
suffices ∀ (k), (fun a => a.reverse.toArray) <$> List.foldlM (fun acc a => (fun a => a :: acc) <$> f a) k l =
|
||||
List.foldlM (fun acc a => acc.push <$> f a) k.reverse.toArray l by
|
||||
exact this []
|
||||
intro k
|
||||
induction l generalizing k with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp [ih, List.foldlM_cons]
|
||||
|
||||
/-! ### foldlM and foldrM -/
|
||||
|
||||
theorem foldlM_map [Monad m] (f : β₁ → β₂) (g : α → β₂ → m α) (l : Array β₁) (init : α) :
|
||||
(l.map f).foldlM g init = l.foldlM (fun x y => g x (f y)) init := by
|
||||
cases l
|
||||
rw [List.map_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem foldrM_map [Monad m] [LawfulMonad m] (f : β₁ → β₂) (g : β₂ → α → m α) (l : Array β₁)
|
||||
(init : α) : (l.map f).foldrM g init = l.foldrM (fun x y => g (f x) y) init := by
|
||||
cases l
|
||||
rw [List.map_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp [List.foldrM_map]
|
||||
|
||||
theorem foldlM_filterMap [Monad m] [LawfulMonad m] (f : α → Option β) (g : γ → β → m γ) (l : Array α) (init : γ) :
|
||||
(l.filterMap f).foldlM g init =
|
||||
l.foldlM (fun x y => match f y with | some b => g x b | none => pure x) init := by
|
||||
cases l
|
||||
rw [List.filterMap_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp [List.foldlM_filterMap]
|
||||
rfl
|
||||
|
||||
theorem foldrM_filterMap [Monad m] [LawfulMonad m] (f : α → Option β) (g : β → γ → m γ) (l : Array α) (init : γ) :
|
||||
(l.filterMap f).foldrM g init =
|
||||
l.foldrM (fun x y => match f x with | some b => g b y | none => pure y) init := by
|
||||
cases l
|
||||
rw [List.filterMap_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp [List.foldrM_filterMap]
|
||||
rfl
|
||||
|
||||
theorem foldlM_filter [Monad m] [LawfulMonad m] (p : α → Bool) (g : β → α → m β) (l : Array α) (init : β) :
|
||||
(l.filter p).foldlM g init =
|
||||
l.foldlM (fun x y => if p y then g x y else pure x) init := by
|
||||
cases l
|
||||
rw [List.filter_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp [List.foldlM_filter]
|
||||
|
||||
theorem foldrM_filter [Monad m] [LawfulMonad m] (p : α → Bool) (g : α → β → m β) (l : Array α) (init : β) :
|
||||
(l.filter p).foldrM g init =
|
||||
l.foldrM (fun x y => if p x then g x y else pure y) init := by
|
||||
cases l
|
||||
rw [List.filter_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp [List.foldrM_filter]
|
||||
|
||||
/-! ### forIn' -/
|
||||
|
||||
/--
|
||||
We can express a for loop over an array as a fold,
|
||||
in which whenever we reach `.done b` we keep that value through the rest of the fold.
|
||||
-/
|
||||
theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → m (ForInStep β)) (init : β) :
|
||||
forIn' l init f = ForInStep.value <$>
|
||||
l.attach.foldlM (fun b ⟨a, m⟩ => match b with
|
||||
| .yield b => f a m b
|
||||
| .done b => pure (.done b)) (ForInStep.yield init) := by
|
||||
cases l
|
||||
rw [List.attach_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp only [List.forIn'_toArray, List.forIn'_eq_foldlM, List.attachWith_mem_toArray, size_toArray,
|
||||
List.length_map, List.length_attach, List.foldlM_toArray', List.foldlM_map]
|
||||
congr
|
||||
|
||||
/-- We can express a for loop over an array which always yields as a fold. -/
|
||||
@[simp] theorem forIn'_yield_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → m γ) (g : (a : α) → a ∈ l → β → γ → β) (init : β) :
|
||||
forIn' l init (fun a m b => (fun c => .yield (g a m b c)) <$> f a m b) =
|
||||
l.attach.foldlM (fun b ⟨a, m⟩ => g a m b <$> f a m b) init := by
|
||||
cases l
|
||||
rw [List.attach_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → β) (init : β) :
|
||||
forIn' l init (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (l.attach.foldl (fun b ⟨a, h⟩ => f a h b) init) := by
|
||||
cases l
|
||||
simp [List.forIn'_pure_yield_eq_foldl, List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn'_yield_eq_foldl
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → β) (init : β) :
|
||||
forIn' (m := Id) l init (fun a m b => .yield (f a m b)) =
|
||||
l.attach.foldl (fun b ⟨a, h⟩ => f a h b) init := by
|
||||
cases l
|
||||
simp [List.foldl_map]
|
||||
|
||||
/--
|
||||
We can express a for loop over an array as a fold,
|
||||
in which whenever we reach `.done b` we keep that value through the rest of the fold.
|
||||
-/
|
||||
theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(f : α → β → m (ForInStep β)) (init : β) (l : Array α) :
|
||||
forIn l init f = ForInStep.value <$>
|
||||
l.foldlM (fun b a => match b with
|
||||
| .yield b => f a b
|
||||
| .done b => pure (.done b)) (ForInStep.yield init) := by
|
||||
cases l
|
||||
simp only [List.forIn_toArray, List.forIn_eq_foldlM, size_toArray, List.foldlM_toArray']
|
||||
congr
|
||||
|
||||
/-- We can express a for loop over an array which always yields as a fold. -/
|
||||
@[simp] theorem forIn_yield_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : α → β → m γ) (g : α → β → γ → β) (init : β) :
|
||||
forIn l init (fun a b => (fun c => .yield (g a b c)) <$> f a b) =
|
||||
l.foldlM (fun b a => g a b <$> f a b) init := by
|
||||
cases l
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : α → β → β) (init : β) :
|
||||
forIn l init (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (l.foldl (fun b a => f a b) init) := by
|
||||
cases l
|
||||
simp [List.forIn_pure_yield_eq_foldl, List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn_yield_eq_foldl
|
||||
(l : Array α) (f : α → β → β) (init : β) :
|
||||
forIn (m := Id) l init (fun a b => .yield (f a b)) =
|
||||
l.foldl (fun b a => f a b) init := by
|
||||
cases l
|
||||
simp [List.foldl_map]
|
||||
|
||||
end Array
|
||||
@@ -15,15 +15,6 @@ structure Subarray (α : Type u) where
|
||||
start_le_stop : start ≤ stop
|
||||
stop_le_array_size : stop ≤ array.size
|
||||
|
||||
@[deprecated Subarray.array (since := "2024-04-13")]
|
||||
abbrev Subarray.as (s : Subarray α) : Array α := s.array
|
||||
|
||||
@[deprecated Subarray.start_le_stop (since := "2024-04-13")]
|
||||
theorem Subarray.h₁ (s : Subarray α) : s.start ≤ s.stop := s.start_le_stop
|
||||
|
||||
@[deprecated Subarray.stop_le_array_size (since := "2024-04-13")]
|
||||
theorem Subarray.h₂ (s : Subarray α) : s.stop ≤ s.array.size := s.stop_le_array_size
|
||||
|
||||
namespace Subarray
|
||||
|
||||
def size (s : Subarray α) : Nat :=
|
||||
@@ -48,7 +39,7 @@ instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
|
||||
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
|
||||
if h : i < s.size then s.get ⟨i, h⟩ else v₀
|
||||
if h : i < s.size then s[i] else v₀
|
||||
|
||||
abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
|
||||
getD s i default
|
||||
|
||||
@@ -29,9 +29,6 @@ section Nat
|
||||
|
||||
instance natCastInst : NatCast (BitVec w) := ⟨BitVec.ofNat w⟩
|
||||
|
||||
@[deprecated isLt (since := "2024-03-12")]
|
||||
theorem toNat_lt (x : BitVec n) : x.toNat < 2^n := x.isLt
|
||||
|
||||
/-- Theorem for normalizing the bit vector literal representation. -/
|
||||
-- TODO: This needs more usage data to assess which direction the simp should go.
|
||||
@[simp, bv_toNat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
|
||||
|
||||
@@ -76,7 +76,7 @@ to prove the correctness of the circuit that is built by `bv_decide`.
|
||||
def blastMul (aig : AIG BVBit) (input : AIG.BinaryRefVec aig w) : AIG.RefVecEntry BVBit w
|
||||
theorem denote_blastMul (aig : AIG BVBit) (lhs rhs : BitVec w) (assign : Assignment) :
|
||||
...
|
||||
⟦(blastMul aig input).aig, (blastMul aig input).vec.get idx hidx, assign.toAIGAssignment⟧
|
||||
⟦(blastMul aig input).aig, (blastMul aig input).vec[idx], assign.toAIGAssignment⟧
|
||||
=
|
||||
(lhs * rhs).getLsbD idx
|
||||
```
|
||||
|
||||
@@ -42,7 +42,7 @@ def usize (a : @& ByteArray) : USize :=
|
||||
a.size.toUSize
|
||||
|
||||
@[extern "lean_byte_array_uget"]
|
||||
def uget : (a : @& ByteArray) → (i : USize) → i.toNat < a.size → UInt8
|
||||
def uget : (a : @& ByteArray) → (i : USize) → (h : i.toNat < a.size := by get_elem_tactic) → UInt8
|
||||
| ⟨bs⟩, i, h => bs[i]
|
||||
|
||||
@[extern "lean_byte_array_get"]
|
||||
@@ -50,11 +50,11 @@ def get! : (@& ByteArray) → (@& Nat) → UInt8
|
||||
| ⟨bs⟩, i => bs.get! i
|
||||
|
||||
@[extern "lean_byte_array_fget"]
|
||||
def get : (a : @& ByteArray) → (@& Fin a.size) → UInt8
|
||||
| ⟨bs⟩, i => bs.get i
|
||||
def get : (a : @& ByteArray) → (i : @& Nat) → (h : i < a.size := by get_elem_tactic) → UInt8
|
||||
| ⟨bs⟩, i, _ => bs[i]
|
||||
|
||||
instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
getElem xs i h := xs.get i
|
||||
|
||||
instance : GetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
@@ -64,11 +64,11 @@ def set! : ByteArray → (@& Nat) → UInt8 → ByteArray
|
||||
| ⟨bs⟩, i, b => ⟨bs.set! i b⟩
|
||||
|
||||
@[extern "lean_byte_array_fset"]
|
||||
def set : (a : ByteArray) → (@& Fin a.size) → UInt8 → ByteArray
|
||||
| ⟨bs⟩, i, b => ⟨bs.set i.1 b i.2⟩
|
||||
def set : (a : ByteArray) → (i : @& Nat) → UInt8 → (h : i < a.size := by get_elem_tactic) → ByteArray
|
||||
| ⟨bs⟩, i, b, h => ⟨bs.set i b h⟩
|
||||
|
||||
@[extern "lean_byte_array_uset"]
|
||||
def uset : (a : ByteArray) → (i : USize) → UInt8 → i.toNat < a.size → ByteArray
|
||||
def uset : (a : ByteArray) → (i : USize) → UInt8 → (h : i.toNat < a.size := by get_elem_tactic) → ByteArray
|
||||
| ⟨bs⟩, i, v, h => ⟨bs.uset i v h⟩
|
||||
|
||||
@[extern "lean_byte_array_hash"]
|
||||
@@ -144,7 +144,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteAr
|
||||
have h' : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self i) h
|
||||
have : as.size - 1 < as.size := Nat.sub_lt (Nat.zero_lt_of_lt h') (by decide)
|
||||
have : as.size - 1 - i < as.size := Nat.lt_of_le_of_lt (Nat.sub_le (as.size - 1) i) this
|
||||
match (← f (as.get ⟨as.size - 1 - i, this⟩) b) with
|
||||
match (← f as[as.size - 1 - i] b) with
|
||||
| ForInStep.done b => pure b
|
||||
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
|
||||
loop as.size (Nat.le_refl _) b
|
||||
@@ -178,7 +178,7 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 →
|
||||
match i with
|
||||
| 0 => pure b
|
||||
| i'+1 =>
|
||||
loop i' (j+1) (← f b (as.get ⟨j, Nat.lt_of_lt_of_le hlt h⟩))
|
||||
loop i' (j+1) (← f b as[j])
|
||||
else
|
||||
pure b
|
||||
loop (stop - start) start init
|
||||
|
||||
@@ -642,7 +642,7 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
ext
|
||||
simp
|
||||
|
||||
@[simp] theorem subNat_one_succ (i : Fin (n + 1)) (h : 1 ≤ ↑i) : (subNat 1 i h).succ = i := by
|
||||
@[simp] theorem subNat_one_succ (i : Fin (n + 1)) (h : 1 ≤ (i : Nat)) : (subNat 1 i h).succ = i := by
|
||||
ext
|
||||
simp
|
||||
omega
|
||||
|
||||
@@ -47,6 +47,25 @@ def Float.lt : Float → Float → Prop := fun a b =>
|
||||
def Float.le : Float → Float → Prop := fun a b =>
|
||||
floatSpec.le a.val b.val
|
||||
|
||||
/--
|
||||
Raw transmutation from `UInt64`.
|
||||
|
||||
Floats and UInts have the same endianness on all supported platforms.
|
||||
IEEE 754 very precisely specifies the bit layout of floats.
|
||||
-/
|
||||
@[extern "lean_float_from_bits"] opaque Float.fromBits : UInt64 → Float
|
||||
|
||||
/--
|
||||
Raw transmutation to `UInt64`.
|
||||
|
||||
Floats and UInts have the same endianness on all supported platforms.
|
||||
IEEE 754 very precisely specifies the bit layout of floats.
|
||||
|
||||
Note that this function is distinct from `Float.toUInt64`, which attempts
|
||||
to preserve the numeric value, and not the bitwise value.
|
||||
-/
|
||||
@[extern "lean_float_to_bits"] opaque Float.toBits : Float → UInt64
|
||||
|
||||
instance : Add Float := ⟨Float.add⟩
|
||||
instance : Sub Float := ⟨Float.sub⟩
|
||||
instance : Mul Float := ⟨Float.mul⟩
|
||||
|
||||
@@ -46,8 +46,8 @@ def uget : (a : @& FloatArray) → (i : USize) → i.toNat < a.size → Float
|
||||
| ⟨ds⟩, i, h => ds[i]
|
||||
|
||||
@[extern "lean_float_array_fget"]
|
||||
def get : (ds : @& FloatArray) → (@& Fin ds.size) → Float
|
||||
| ⟨ds⟩, i => ds.get i
|
||||
def get : (ds : @& FloatArray) → (i : @& Nat) → (h : i < ds.size := by get_elem_tactic) → Float
|
||||
| ⟨ds⟩, i, h => ds.get i h
|
||||
|
||||
@[extern "lean_float_array_get"]
|
||||
def get! : (@& FloatArray) → (@& Nat) → Float
|
||||
@@ -55,23 +55,23 @@ def get! : (@& FloatArray) → (@& Nat) → Float
|
||||
|
||||
def get? (ds : FloatArray) (i : Nat) : Option Float :=
|
||||
if h : i < ds.size then
|
||||
ds.get ⟨i, h⟩
|
||||
some (ds.get i h)
|
||||
else
|
||||
none
|
||||
|
||||
instance : GetElem FloatArray Nat Float fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
getElem xs i h := xs.get i h
|
||||
|
||||
instance : GetElem FloatArray USize Float fun xs i => i.val < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
|
||||
@[extern "lean_float_array_uset"]
|
||||
def uset : (a : FloatArray) → (i : USize) → Float → i.toNat < a.size → FloatArray
|
||||
def uset : (a : FloatArray) → (i : USize) → Float → (h : i.toNat < a.size := by get_elem_tactic) → FloatArray
|
||||
| ⟨ds⟩, i, v, h => ⟨ds.uset i v h⟩
|
||||
|
||||
@[extern "lean_float_array_fset"]
|
||||
def set : (ds : FloatArray) → (@& Fin ds.size) → Float → FloatArray
|
||||
| ⟨ds⟩, i, d => ⟨ds.set i.1 d i.2⟩
|
||||
def set : (ds : FloatArray) → (i : @& Nat) → Float → (h : i < ds.size := by get_elem_tactic) → FloatArray
|
||||
| ⟨ds⟩, i, d, h => ⟨ds.set i d h⟩
|
||||
|
||||
@[extern "lean_float_array_set"]
|
||||
def set! : FloatArray → (@& Nat) → Float → FloatArray
|
||||
@@ -83,7 +83,7 @@ def isEmpty (s : FloatArray) : Bool :=
|
||||
partial def toList (ds : FloatArray) : List Float :=
|
||||
let rec loop (i r) :=
|
||||
if h : i < ds.size then
|
||||
loop (i+1) (ds.get ⟨i, h⟩ :: r)
|
||||
loop (i+1) (ds[i] :: r)
|
||||
else
|
||||
r.reverse
|
||||
loop 0 []
|
||||
@@ -115,7 +115,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : FloatA
|
||||
have h' : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self i) h
|
||||
have : as.size - 1 < as.size := Nat.sub_lt (Nat.zero_lt_of_lt h') (by decide)
|
||||
have : as.size - 1 - i < as.size := Nat.lt_of_le_of_lt (Nat.sub_le (as.size - 1) i) this
|
||||
match (← f (as.get ⟨as.size - 1 - i, this⟩) b) with
|
||||
match (← f as[as.size - 1 - i] b) with
|
||||
| ForInStep.done b => pure b
|
||||
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
|
||||
loop as.size (Nat.le_refl _) b
|
||||
@@ -149,7 +149,7 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → Float →
|
||||
match i with
|
||||
| 0 => pure b
|
||||
| i'+1 =>
|
||||
loop i' (j+1) (← f b (as.get ⟨j, Nat.lt_of_lt_of_le hlt h⟩))
|
||||
loop i' (j+1) (← f b (as[j]'(Nat.lt_of_lt_of_le hlt h)))
|
||||
else
|
||||
pure b
|
||||
loop (stop - start) start init
|
||||
|
||||
@@ -551,7 +551,7 @@ theorem reverseAux_eq_append (as bs : List α) : reverseAux as bs = reverseAux a
|
||||
/-! ### flatten -/
|
||||
|
||||
/--
|
||||
`O(|flatten L|)`. `join L` concatenates all the lists in `L` into one list.
|
||||
`O(|flatten L|)`. `flatten L` concatenates all the lists in `L` into one list.
|
||||
* `flatten [[a], [], [b, c], [d, e, f]] = [a, b, c, d, e, f]`
|
||||
-/
|
||||
def flatten : List (List α) → List α
|
||||
|
||||
@@ -10,7 +10,8 @@ import Init.Data.List.Sublist
|
||||
import Init.Data.List.Range
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.findSome?`, `List.find?`, `List.findIdx`, `List.findIdx?`, and `List.indexOf`.
|
||||
Lemmas about `List.findSome?`, `List.find?`, `List.findIdx`, `List.findIdx?`, `List.indexOf`,
|
||||
and `List.lookup`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
@@ -95,22 +96,22 @@ theorem findSome?_eq_some_iff {f : α → Option β} {l : List α} {b : β} :
|
||||
· simp only [Option.guard_eq_none] at h
|
||||
simp [ih, h]
|
||||
|
||||
@[simp] theorem filterMap_head? (f : α → Option β) (l : List α) : (l.filterMap f).head? = l.findSome? f := by
|
||||
@[simp] theorem head?_filterMap (f : α → Option β) (l : List α) : (l.filterMap f).head? = l.findSome? f := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [filterMap_cons, findSome?_cons]
|
||||
split <;> simp [*]
|
||||
|
||||
@[simp] theorem filterMap_head (f : α → Option β) (l : List α) (h) :
|
||||
(l.filterMap f).head h = (l.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
@[simp] theorem head_filterMap (f : α → Option β) (l : List α) (h) :
|
||||
(l.filterMap f).head h = (l.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [head_eq_iff_head?_eq_some]
|
||||
|
||||
@[simp] theorem filterMap_getLast? (f : α → Option β) (l : List α) : (l.filterMap f).getLast? = l.reverse.findSome? f := by
|
||||
@[simp] theorem getLast?_filterMap (f : α → Option β) (l : List α) : (l.filterMap f).getLast? = l.reverse.findSome? f := by
|
||||
rw [getLast?_eq_head?_reverse]
|
||||
simp [← filterMap_reverse]
|
||||
|
||||
@[simp] theorem filterMap_getLast (f : α → Option β) (l : List α) (h) :
|
||||
@[simp] theorem getLast_filterMap (f : α → Option β) (l : List α) (h) :
|
||||
(l.filterMap f).getLast h = (l.reverse.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [getLast_eq_iff_getLast_eq_some]
|
||||
|
||||
@@ -291,18 +292,18 @@ theorem get_find?_mem (xs : List α) (p : α → Bool) (h) : (xs.find? p).get h
|
||||
· simp only [find?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem filter_head? (p : α → Bool) (l : List α) : (l.filter p).head? = l.find? p := by
|
||||
rw [← filterMap_eq_filter, filterMap_head?, findSome?_guard]
|
||||
@[simp] theorem head?_filter (p : α → Bool) (l : List α) : (l.filter p).head? = l.find? p := by
|
||||
rw [← filterMap_eq_filter, head?_filterMap, findSome?_guard]
|
||||
|
||||
@[simp] theorem filter_head (p : α → Bool) (l : List α) (h) :
|
||||
@[simp] theorem head_filter (p : α → Bool) (l : List α) (h) :
|
||||
(l.filter p).head h = (l.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [head_eq_iff_head?_eq_some]
|
||||
|
||||
@[simp] theorem filter_getLast? (p : α → Bool) (l : List α) : (l.filter p).getLast? = l.reverse.find? p := by
|
||||
@[simp] theorem getLast?_filter (p : α → Bool) (l : List α) : (l.filter p).getLast? = l.reverse.find? p := by
|
||||
rw [getLast?_eq_head?_reverse]
|
||||
simp [← filter_reverse]
|
||||
|
||||
@[simp] theorem filter_getLast (p : α → Bool) (l : List α) (h) :
|
||||
@[simp] theorem getLast_filter (p : α → Bool) (l : List α) (h) :
|
||||
(l.filter p).getLast h = (l.reverse.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
|
||||
simp [getLast_eq_iff_getLast_eq_some]
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ The following operations are given `@[csimp]` replacements below:
|
||||
@[specialize] def foldrTR (f : α → β → β) (init : β) (l : List α) : β := l.toArray.foldr f init
|
||||
|
||||
@[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by
|
||||
funext α β f init l; simp [foldrTR, Array.foldr_eq_foldr_toList, -Array.size_toArray]
|
||||
funext α β f init l; simp [foldrTR, ← Array.foldr_toList, -Array.size_toArray]
|
||||
|
||||
/-! ### flatMap -/
|
||||
|
||||
@@ -331,7 +331,7 @@ def enumFromTR (n : Nat) (l : List α) : List (Nat × α) :=
|
||||
| a::as, n => by
|
||||
rw [← show _ + as.length = n + (a::as).length from Nat.succ_add .., foldr, go as]
|
||||
simp [enumFrom, f]
|
||||
rw [Array.foldr_eq_foldr_toList]
|
||||
rw [← Array.foldr_toList]
|
||||
simp [go]
|
||||
|
||||
/-! ## Other list operations -/
|
||||
|
||||
@@ -1045,7 +1045,7 @@ theorem getLast_eq_getLastD (a l h) : @getLast α (a::l) h = getLastD l a := by
|
||||
|
||||
@[simp] theorem getLast_singleton (a h) : @getLast α [a] h = a := rfl
|
||||
|
||||
theorem getLast!_cons [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
|
||||
theorem getLast!_cons_eq_getLastD [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
|
||||
simp [getLast!, getLast_eq_getLastD]
|
||||
|
||||
@[simp] theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
|
||||
@@ -1109,7 +1109,12 @@ theorem getLastD_concat (a b l) : @getLastD α (l ++ [b]) a = b := by
|
||||
|
||||
/-! ### getLast! -/
|
||||
|
||||
@[simp] theorem getLast!_nil [Inhabited α] : ([] : List α).getLast! = default := rfl
|
||||
theorem getLast!_nil [Inhabited α] : ([] : List α).getLast! = default := rfl
|
||||
|
||||
@[simp] theorem getLast!_eq_getLast?_getD [Inhabited α] {l : List α} : getLast! l = (getLast? l).getD default := by
|
||||
cases l with
|
||||
| nil => simp [getLast!_nil]
|
||||
| cons _ _ => simp [getLast!, getLast?_eq_getLast]
|
||||
|
||||
theorem getLast!_of_getLast? [Inhabited α] : ∀ {l : List α}, getLast? l = some a → getLast! l = a
|
||||
| _ :: _, rfl => rfl
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.ByCases
|
||||
import Init.Data.Prod
|
||||
import Init.Data.RArray
|
||||
|
||||
namespace Nat.Linear
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Nat.Linear
|
||||
|
||||
abbrev Var := Nat
|
||||
|
||||
abbrev Context := List Nat
|
||||
abbrev Context := Lean.RArray Nat
|
||||
|
||||
/--
|
||||
When encoding polynomials. We use `fixedVar` for encoding numerals.
|
||||
@@ -23,12 +24,7 @@ abbrev Context := List Nat
|
||||
def fixedVar := 100000000 -- Any big number should work here
|
||||
|
||||
def Var.denote (ctx : Context) (v : Var) : Nat :=
|
||||
bif v == fixedVar then 1 else go ctx v
|
||||
where
|
||||
go : List Nat → Nat → Nat
|
||||
| [], _ => 0
|
||||
| a::_, 0 => a
|
||||
| _::as, i+1 => go as i
|
||||
bif v == fixedVar then 1 else ctx.get v
|
||||
|
||||
inductive Expr where
|
||||
| num (v : Nat)
|
||||
@@ -52,25 +48,23 @@ def Poly.denote (ctx : Context) (p : Poly) : Nat :=
|
||||
| [] => 0
|
||||
| (k, v) :: p => Nat.add (Nat.mul k (v.denote ctx)) (denote ctx p)
|
||||
|
||||
def Poly.insertSorted (k : Nat) (v : Var) (p : Poly) : Poly :=
|
||||
def Poly.insert (k : Nat) (v : Var) (p : Poly) : Poly :=
|
||||
match p with
|
||||
| [] => [(k, v)]
|
||||
| (k', v') :: p => bif Nat.blt v v' then (k, v) :: (k', v') :: p else (k', v') :: insertSorted k v p
|
||||
| (k', v') :: p =>
|
||||
bif Nat.blt v v' then
|
||||
(k, v) :: (k', v') :: p
|
||||
else bif Nat.beq v v' then
|
||||
(k + k', v') :: p
|
||||
else
|
||||
(k', v') :: insert k v p
|
||||
|
||||
def Poly.sort (p : Poly) : Poly :=
|
||||
let rec go (p : Poly) (r : Poly) : Poly :=
|
||||
def Poly.norm (p : Poly) : Poly := go p []
|
||||
where
|
||||
go (p : Poly) (r : Poly) : Poly :=
|
||||
match p with
|
||||
| [] => r
|
||||
| (k, v) :: p => go p (r.insertSorted k v)
|
||||
go p []
|
||||
|
||||
def Poly.fuse (p : Poly) : Poly :=
|
||||
match p with
|
||||
| [] => []
|
||||
| (k, v) :: p =>
|
||||
match fuse p with
|
||||
| [] => [(k, v)]
|
||||
| (k', v') :: p' => bif v == v' then (Nat.add k k', v)::p' else (k, v) :: (k', v') :: p'
|
||||
| (k, v) :: p => go p (r.insert k v)
|
||||
|
||||
def Poly.mul (k : Nat) (p : Poly) : Poly :=
|
||||
bif k == 0 then
|
||||
@@ -146,15 +140,17 @@ def Poly.combineAux (fuel : Nat) (p₁ p₂ : Poly) : Poly :=
|
||||
def Poly.combine (p₁ p₂ : Poly) : Poly :=
|
||||
combineAux hugeFuel p₁ p₂
|
||||
|
||||
def Expr.toPoly : Expr → Poly
|
||||
| Expr.num k => bif k == 0 then [] else [ (k, fixedVar) ]
|
||||
| Expr.var i => [(1, i)]
|
||||
| Expr.add a b => a.toPoly ++ b.toPoly
|
||||
| Expr.mulL k a => a.toPoly.mul k
|
||||
| Expr.mulR a k => a.toPoly.mul k
|
||||
|
||||
def Poly.norm (p : Poly) : Poly :=
|
||||
p.sort.fuse
|
||||
def Expr.toPoly (e : Expr) :=
|
||||
go 1 e []
|
||||
where
|
||||
-- Implementation note: This assembles the result using difference lists
|
||||
-- to avoid `++` on lists.
|
||||
go (coeff : Nat) : Expr → (Poly → Poly)
|
||||
| Expr.num k => bif k == 0 then id else ((coeff * k, fixedVar) :: ·)
|
||||
| Expr.var i => ((coeff, i) :: ·)
|
||||
| Expr.add a b => go coeff a ∘ go coeff b
|
||||
| Expr.mulL k a
|
||||
| Expr.mulR a k => bif k == 0 then id else go (coeff * k) a
|
||||
|
||||
def Expr.toNormPoly (e : Expr) : Poly :=
|
||||
e.toPoly.norm
|
||||
@@ -201,7 +197,7 @@ def PolyCnstr.denote (ctx : Context) (c : PolyCnstr) : Prop :=
|
||||
Poly.denote_le ctx (c.lhs, c.rhs)
|
||||
|
||||
def PolyCnstr.norm (c : PolyCnstr) : PolyCnstr :=
|
||||
let (lhs, rhs) := Poly.cancel c.lhs.sort.fuse c.rhs.sort.fuse
|
||||
let (lhs, rhs) := Poly.cancel c.lhs.norm c.rhs.norm
|
||||
{ eq := c.eq, lhs, rhs }
|
||||
|
||||
def PolyCnstr.isUnsat (c : PolyCnstr) : Bool :=
|
||||
@@ -268,24 +264,32 @@ def PolyCnstr.toExpr (c : PolyCnstr) : ExprCnstr :=
|
||||
{ c with lhs := c.lhs.toExpr, rhs := c.rhs.toExpr }
|
||||
|
||||
attribute [local simp] Nat.add_comm Nat.add_assoc Nat.add_left_comm Nat.right_distrib Nat.left_distrib Nat.mul_assoc Nat.mul_comm
|
||||
attribute [local simp] Poly.denote Expr.denote Poly.insertSorted Poly.sort Poly.sort.go Poly.fuse Poly.cancelAux
|
||||
attribute [local simp] Poly.denote Expr.denote Poly.insert Poly.norm Poly.norm.go Poly.cancelAux
|
||||
attribute [local simp] Poly.mul Poly.mul.go
|
||||
|
||||
theorem Poly.denote_insertSorted (ctx : Context) (k : Nat) (v : Var) (p : Poly) : (p.insertSorted k v).denote ctx = p.denote ctx + k * v.denote ctx := by
|
||||
theorem Poly.denote_insert (ctx : Context) (k : Nat) (v : Var) (p : Poly) :
|
||||
(p.insert k v).denote ctx = p.denote ctx + k * v.denote ctx := by
|
||||
match p with
|
||||
| [] => simp
|
||||
| (k', v') :: p => by_cases h : Nat.blt v v' <;> simp [h, denote_insertSorted]
|
||||
| (k', v') :: p =>
|
||||
by_cases h₁ : Nat.blt v v'
|
||||
· simp [h₁]
|
||||
· by_cases h₂ : Nat.beq v v'
|
||||
· simp only [insert, h₁, h₂, cond_false, cond_true]
|
||||
simp [Nat.eq_of_beq_eq_true h₂]
|
||||
· simp only [insert, h₁, h₂, cond_false, cond_true]
|
||||
simp [denote_insert]
|
||||
|
||||
attribute [local simp] Poly.denote_insertSorted
|
||||
attribute [local simp] Poly.denote_insert
|
||||
|
||||
theorem Poly.denote_sort_go (ctx : Context) (p : Poly) (r : Poly) : (sort.go p r).denote ctx = p.denote ctx + r.denote ctx := by
|
||||
theorem Poly.denote_norm_go (ctx : Context) (p : Poly) (r : Poly) : (norm.go p r).denote ctx = p.denote ctx + r.denote ctx := by
|
||||
match p with
|
||||
| [] => simp
|
||||
| (k, v):: p => simp [denote_sort_go]
|
||||
| (k, v):: p => simp [denote_norm_go]
|
||||
|
||||
attribute [local simp] Poly.denote_sort_go
|
||||
attribute [local simp] Poly.denote_norm_go
|
||||
|
||||
theorem Poly.denote_sort (ctx : Context) (m : Poly) : m.sort.denote ctx = m.denote ctx := by
|
||||
theorem Poly.denote_sort (ctx : Context) (m : Poly) : m.norm.denote ctx = m.denote ctx := by
|
||||
simp
|
||||
|
||||
attribute [local simp] Poly.denote_sort
|
||||
@@ -316,18 +320,6 @@ theorem Poly.denote_reverse (ctx : Context) (p : Poly) : denote ctx (List.revers
|
||||
|
||||
attribute [local simp] Poly.denote_reverse
|
||||
|
||||
theorem Poly.denote_fuse (ctx : Context) (p : Poly) : p.fuse.denote ctx = p.denote ctx := by
|
||||
match p with
|
||||
| [] => rfl
|
||||
| (k, v) :: p =>
|
||||
have ih := denote_fuse ctx p
|
||||
simp
|
||||
split
|
||||
case _ h => simp [← ih, h]
|
||||
case _ k' v' p' h => by_cases he : v == v' <;> simp [he, ← ih, h]; rw [eq_of_beq he]
|
||||
|
||||
attribute [local simp] Poly.denote_fuse
|
||||
|
||||
theorem Poly.denote_mul (ctx : Context) (k : Nat) (p : Poly) : (p.mul k).denote ctx = k * p.denote ctx := by
|
||||
simp
|
||||
by_cases h : k == 0 <;> simp [h]; simp [eq_of_beq h]
|
||||
@@ -516,13 +508,25 @@ theorem Poly.denote_combine (ctx : Context) (p₁ p₂ : Poly) : (p₁.combine p
|
||||
|
||||
attribute [local simp] Poly.denote_combine
|
||||
|
||||
theorem Expr.denote_toPoly_go (ctx : Context) (e : Expr) :
|
||||
(toPoly.go k e p).denote ctx = k * e.denote ctx + p.denote ctx := by
|
||||
induction k, e using Expr.toPoly.go.induct generalizing p with
|
||||
| case1 k k' =>
|
||||
simp only [toPoly.go]
|
||||
by_cases h : k' == 0
|
||||
· simp [h, eq_of_beq h]
|
||||
· simp [h, Var.denote]
|
||||
| case2 k i => simp [toPoly.go]
|
||||
| case3 k a b iha ihb => simp [toPoly.go, iha, ihb]
|
||||
| case4 k k' a ih
|
||||
| case5 k a k' ih =>
|
||||
simp only [toPoly.go, denote, mul_eq]
|
||||
by_cases h : k' == 0
|
||||
· simp [h, eq_of_beq h]
|
||||
· simp [h, cond_false, ih, Nat.mul_assoc]
|
||||
|
||||
theorem Expr.denote_toPoly (ctx : Context) (e : Expr) : e.toPoly.denote ctx = e.denote ctx := by
|
||||
induction e with
|
||||
| num k => by_cases h : k == 0 <;> simp [toPoly, h, Var.denote]; simp [eq_of_beq h]
|
||||
| var i => simp [toPoly]
|
||||
| add a b iha ihb => simp [toPoly, iha, ihb]
|
||||
| mulL k a ih => simp [toPoly, ih, -Poly.mul]
|
||||
| mulR k a ih => simp [toPoly, ih, -Poly.mul]
|
||||
simp [toPoly, Expr.denote_toPoly_go]
|
||||
|
||||
attribute [local simp] Expr.denote_toPoly
|
||||
|
||||
@@ -554,8 +558,8 @@ theorem ExprCnstr.denote_toPoly (ctx : Context) (c : ExprCnstr) : c.toPoly.denot
|
||||
cases c; rename_i eq lhs rhs
|
||||
simp [ExprCnstr.denote, PolyCnstr.denote, ExprCnstr.toPoly];
|
||||
by_cases h : eq = true <;> simp [h]
|
||||
· simp [Poly.denote_eq, Expr.toPoly]
|
||||
· simp [Poly.denote_le, Expr.toPoly]
|
||||
· simp [Poly.denote_eq]
|
||||
· simp [Poly.denote_le]
|
||||
|
||||
attribute [local simp] ExprCnstr.denote_toPoly
|
||||
|
||||
|
||||
@@ -16,22 +16,22 @@ def getM [Alternative m] : Option α → m α
|
||||
| none => failure
|
||||
| some a => pure a
|
||||
|
||||
@[deprecated getM (since := "2024-04-17")]
|
||||
-- `[Monad m]` is not needed here.
|
||||
def toMonad [Monad m] [Alternative m] : Option α → m α := getM
|
||||
|
||||
/-- Returns `true` on `some x` and `false` on `none`. -/
|
||||
@[inline] def isSome : Option α → Bool
|
||||
| some _ => true
|
||||
| none => false
|
||||
|
||||
@[deprecated isSome (since := "2024-04-17"), inline] def toBool : Option α → Bool := isSome
|
||||
@[simp] theorem isSome_none : @isSome α none = false := rfl
|
||||
@[simp] theorem isSome_some : isSome (some a) = true := rfl
|
||||
|
||||
/-- Returns `true` on `none` and `false` on `some x`. -/
|
||||
@[inline] def isNone : Option α → Bool
|
||||
| some _ => false
|
||||
| none => true
|
||||
|
||||
@[simp] theorem isNone_none : @isNone α none = true := rfl
|
||||
@[simp] theorem isNone_some : isNone (some a) = false := rfl
|
||||
|
||||
/--
|
||||
`x?.isEqSome y` is equivalent to `x? == some y`, but avoids an allocation.
|
||||
-/
|
||||
@@ -134,6 +134,10 @@ def merge (fn : α → α → α) : Option α → Option α → Option α
|
||||
@[inline] def get {α : Type u} : (o : Option α) → isSome o → α
|
||||
| some x, _ => x
|
||||
|
||||
@[simp] theorem some_get : ∀ {x : Option α} (h : isSome x), some (x.get h) = x
|
||||
| some _, _ => rfl
|
||||
@[simp] theorem get_some (x : α) (h : isSome (some x)) : (some x).get h = x := rfl
|
||||
|
||||
/-- `guard p a` returns `some a` if `p a` holds, otherwise `none`. -/
|
||||
@[inline] def guard (p : α → Prop) [DecidablePred p] (a : α) : Option α :=
|
||||
if p a then some a else none
|
||||
|
||||
@@ -36,11 +36,6 @@ theorem get_of_mem : ∀ {o : Option α} (h : isSome o), a ∈ o → o.get h = a
|
||||
|
||||
theorem not_mem_none (a : α) : a ∉ (none : Option α) := nofun
|
||||
|
||||
@[simp] theorem some_get : ∀ {x : Option α} (h : isSome x), some (x.get h) = x
|
||||
| some _, _ => rfl
|
||||
|
||||
@[simp] theorem get_some (x : α) (h : isSome (some x)) : (some x).get h = x := rfl
|
||||
|
||||
theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.getD y) = x := by
|
||||
cases x; {contradiction}; rw [getD_some]
|
||||
|
||||
@@ -73,19 +68,11 @@ theorem mem_unique {o : Option α} {a b : α} (ha : a ∈ o) (hb : b ∈ o) : a
|
||||
theorem eq_none_iff_forall_not_mem : o = none ↔ ∀ a, a ∉ o :=
|
||||
⟨fun e a h => by rw [e] at h; (cases h), fun h => ext <| by simp; exact h⟩
|
||||
|
||||
@[simp] theorem isSome_none : @isSome α none = false := rfl
|
||||
|
||||
@[simp] theorem isSome_some : isSome (some a) = true := rfl
|
||||
|
||||
theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by cases x <;> simp [isSome]
|
||||
|
||||
theorem isSome_eq_isSome : (isSome x = isSome y) ↔ (x = none ↔ y = none) := by
|
||||
cases x <;> cases y <;> simp
|
||||
|
||||
@[simp] theorem isNone_none : @isNone α none = true := rfl
|
||||
|
||||
@[simp] theorem isNone_some : isNone (some a) = false := rfl
|
||||
|
||||
@[simp] theorem not_isSome : isSome a = false ↔ a.isNone = true := by
|
||||
cases a <;> simp
|
||||
|
||||
|
||||
69
src/Init/Data/RArray.lean
Normal file
69
src/Init/Data/RArray.lean
Normal file
@@ -0,0 +1,69 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.PropLemmas
|
||||
|
||||
namespace Lean
|
||||
|
||||
/--
|
||||
A `RArray` can model `Fin n → α` or `Array α`, but is optimized for a fast kernel-reducible `get`
|
||||
operation.
|
||||
|
||||
The primary intended use case is the “denote” function of a typical proof by reflection proof, where
|
||||
only the `get` operation is necessary. It is not suitable as a general-purpose data structure.
|
||||
|
||||
There is no well-formedness invariant attached to this data structure, to keep it concise; it's
|
||||
semantics is given through `RArray.get`. In that way one can also view an `RArray` as a decision
|
||||
tree implementing `Nat → α`.
|
||||
|
||||
See `RArray.ofFn` and `RArray.ofArray` in module `Lean.Data.RArray` for functions that construct an
|
||||
`RArray`.
|
||||
|
||||
It is not universe-polymorphic. ; smaller proof objects and no complication with the `ToExpr` type
|
||||
class.
|
||||
-/
|
||||
inductive RArray (α : Type) : Type where
|
||||
| leaf : α → RArray α
|
||||
| branch : Nat → RArray α → RArray α → RArray α
|
||||
|
||||
variable {α : Type}
|
||||
|
||||
/-- The crucial operation, written with very little abstractional overhead -/
|
||||
noncomputable def RArray.get (a : RArray α) (n : Nat) : α :=
|
||||
RArray.rec (fun x => x) (fun p _ _ l r => (Nat.ble p n).rec l r) a
|
||||
|
||||
private theorem RArray.get_eq_def (a : RArray α) (n : Nat) :
|
||||
a.get n = match a with
|
||||
| .leaf x => x
|
||||
| .branch p l r => (Nat.ble p n).rec (l.get n) (r.get n) := by
|
||||
conv => lhs; unfold RArray.get
|
||||
split <;> rfl
|
||||
|
||||
/-- `RArray.get`, implemented conventionally -/
|
||||
def RArray.getImpl (a : RArray α) (n : Nat) : α :=
|
||||
match a with
|
||||
| .leaf x => x
|
||||
| .branch p l r => if n < p then l.getImpl n else r.getImpl n
|
||||
|
||||
@[csimp]
|
||||
theorem RArray.get_eq_getImpl : @RArray.get = @RArray.getImpl := by
|
||||
funext α a n
|
||||
induction a with
|
||||
| leaf _ => rfl
|
||||
| branch p l r ihl ihr =>
|
||||
rw [RArray.getImpl, RArray.get_eq_def]
|
||||
simp only [ihl, ihr, ← Nat.not_le, ← Nat.ble_eq, ite_not]
|
||||
cases hnp : Nat.ble p n <;> rfl
|
||||
|
||||
instance : GetElem (RArray α) Nat α (fun _ _ => True) where
|
||||
getElem a n _ := a.get n
|
||||
|
||||
def RArray.size : RArray α → Nat
|
||||
| leaf _ => 1
|
||||
| branch _ l r => l.size + r.size
|
||||
|
||||
end Lean
|
||||
@@ -162,7 +162,7 @@ private def reprArray : Array String := Id.run do
|
||||
List.range 128 |>.map (·.toUSize.repr) |> Array.mk
|
||||
|
||||
private def reprFast (n : Nat) : String :=
|
||||
if h : n < 128 then Nat.reprArray.get ⟨n, h⟩ else
|
||||
if h : n < 128 then Nat.reprArray.get n h else
|
||||
if h : n < USize.size then (USize.ofNatCore n h).repr
|
||||
else (toDigits 10 n).asString
|
||||
|
||||
|
||||
@@ -148,6 +148,9 @@ instance : ShiftLeft Int8 := ⟨Int8.shiftLeft⟩
|
||||
instance : ShiftRight Int8 := ⟨Int8.shiftRight⟩
|
||||
instance : DecidableEq Int8 := Int8.decEq
|
||||
|
||||
@[extern "lean_bool_to_int8"]
|
||||
def Bool.toInt8 (b : Bool) : Int8 := if b then 1 else 0
|
||||
|
||||
@[extern "lean_int8_dec_lt"]
|
||||
def Int8.decLt (a b : Int8) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
|
||||
@@ -249,6 +252,9 @@ instance : ShiftLeft Int16 := ⟨Int16.shiftLeft⟩
|
||||
instance : ShiftRight Int16 := ⟨Int16.shiftRight⟩
|
||||
instance : DecidableEq Int16 := Int16.decEq
|
||||
|
||||
@[extern "lean_bool_to_int16"]
|
||||
def Bool.toInt16 (b : Bool) : Int16 := if b then 1 else 0
|
||||
|
||||
@[extern "lean_int16_dec_lt"]
|
||||
def Int16.decLt (a b : Int16) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
|
||||
@@ -354,6 +360,9 @@ instance : ShiftLeft Int32 := ⟨Int32.shiftLeft⟩
|
||||
instance : ShiftRight Int32 := ⟨Int32.shiftRight⟩
|
||||
instance : DecidableEq Int32 := Int32.decEq
|
||||
|
||||
@[extern "lean_bool_to_int32"]
|
||||
def Bool.toInt32 (b : Bool) : Int32 := if b then 1 else 0
|
||||
|
||||
@[extern "lean_int32_dec_lt"]
|
||||
def Int32.decLt (a b : Int32) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
|
||||
@@ -463,6 +472,9 @@ instance : ShiftLeft Int64 := ⟨Int64.shiftLeft⟩
|
||||
instance : ShiftRight Int64 := ⟨Int64.shiftRight⟩
|
||||
instance : DecidableEq Int64 := Int64.decEq
|
||||
|
||||
@[extern "lean_bool_to_int64"]
|
||||
def Bool.toInt64 (b : Bool) : Int64 := if b then 1 else 0
|
||||
|
||||
@[extern "lean_int64_dec_lt"]
|
||||
def Int64.decLt (a b : Int64) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
|
||||
@@ -574,6 +586,9 @@ instance : ShiftLeft ISize := ⟨ISize.shiftLeft⟩
|
||||
instance : ShiftRight ISize := ⟨ISize.shiftRight⟩
|
||||
instance : DecidableEq ISize := ISize.decEq
|
||||
|
||||
@[extern "lean_bool_to_isize"]
|
||||
def Bool.toISize (b : Bool) : ISize := if b then 1 else 0
|
||||
|
||||
@[extern "lean_isize_dec_lt"]
|
||||
def ISize.decLt (a b : ISize) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
|
||||
|
||||
@@ -94,7 +94,7 @@ instance : Stream (Subarray α) α where
|
||||
next? s :=
|
||||
if h : s.start < s.stop then
|
||||
have : s.start + 1 ≤ s.stop := Nat.succ_le_of_lt h
|
||||
some (s.array.get ⟨s.start, Nat.lt_of_lt_of_le h s.stop_le_array_size⟩,
|
||||
some (s.array[s.start]'(Nat.lt_of_lt_of_le h s.stop_le_array_size),
|
||||
{ s with start := s.start + 1, start_le_stop := this })
|
||||
else
|
||||
none
|
||||
|
||||
@@ -514,9 +514,6 @@ instance : Inhabited String := ⟨""⟩
|
||||
|
||||
instance : Append String := ⟨String.append⟩
|
||||
|
||||
@[deprecated push (since := "2024-04-06")]
|
||||
def str : String → Char → String := push
|
||||
|
||||
@[inline] def pushn (s : String) (c : Char) (n : Nat) : String :=
|
||||
n.repeat (fun s => s.push c) s
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ def toUTF8 (a : @& String) : ByteArray :=
|
||||
/-- Accesses a byte in the UTF-8 encoding of the `String`. O(1) -/
|
||||
@[extern "lean_string_get_byte_fast"]
|
||||
def getUtf8Byte (s : @& String) (n : Nat) (h : n < s.utf8ByteSize) : UInt8 :=
|
||||
(toUTF8 s).get ⟨n, size_toUTF8 _ ▸ h⟩
|
||||
(toUTF8 s)[n]'(size_toUTF8 _ ▸ h)
|
||||
|
||||
theorem Iterator.sizeOf_next_lt_of_hasNext (i : String.Iterator) (h : i.hasNext) : sizeOf i.next < sizeOf i := by
|
||||
cases i; rename_i s pos; simp [Iterator.next, Iterator.sizeOf_eq]; simp [Iterator.hasNext] at h
|
||||
|
||||
@@ -56,6 +56,9 @@ instance : Xor UInt8 := ⟨UInt8.xor⟩
|
||||
instance : ShiftLeft UInt8 := ⟨UInt8.shiftLeft⟩
|
||||
instance : ShiftRight UInt8 := ⟨UInt8.shiftRight⟩
|
||||
|
||||
@[extern "lean_bool_to_uint8"]
|
||||
def Bool.toUInt8 (b : Bool) : UInt8 := if b then 1 else 0
|
||||
|
||||
@[extern "lean_uint8_dec_lt"]
|
||||
def UInt8.decLt (a b : UInt8) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
|
||||
@@ -116,6 +119,9 @@ instance : Xor UInt16 := ⟨UInt16.xor⟩
|
||||
instance : ShiftLeft UInt16 := ⟨UInt16.shiftLeft⟩
|
||||
instance : ShiftRight UInt16 := ⟨UInt16.shiftRight⟩
|
||||
|
||||
@[extern "lean_bool_to_uint16"]
|
||||
def Bool.toUInt16 (b : Bool) : UInt16 := if b then 1 else 0
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
@[extern "lean_uint16_dec_lt"]
|
||||
def UInt16.decLt (a b : UInt16) : Decidable (a < b) :=
|
||||
@@ -174,6 +180,9 @@ instance : Xor UInt32 := ⟨UInt32.xor⟩
|
||||
instance : ShiftLeft UInt32 := ⟨UInt32.shiftLeft⟩
|
||||
instance : ShiftRight UInt32 := ⟨UInt32.shiftRight⟩
|
||||
|
||||
@[extern "lean_bool_to_uint32"]
|
||||
def Bool.toUInt32 (b : Bool) : UInt32 := if b then 1 else 0
|
||||
|
||||
@[extern "lean_uint64_add"]
|
||||
def UInt64.add (a b : UInt64) : UInt64 := ⟨a.toBitVec + b.toBitVec⟩
|
||||
@[extern "lean_uint64_sub"]
|
||||
@@ -278,5 +287,8 @@ instance : Xor USize := ⟨USize.xor⟩
|
||||
instance : ShiftLeft USize := ⟨USize.shiftLeft⟩
|
||||
instance : ShiftRight USize := ⟨USize.shiftRight⟩
|
||||
|
||||
@[extern "lean_bool_to_usize"]
|
||||
def Bool.toUSize (b : Bool) : USize := if b then 1 else 0
|
||||
|
||||
instance : Max USize := maxOfLe
|
||||
instance : Min USize := minOfLe
|
||||
|
||||
@@ -166,6 +166,12 @@ theorem getElem!_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem d
|
||||
have : Decidable (dom c i) := .isFalse h
|
||||
simp [getElem!_def, getElem?_def, h]
|
||||
|
||||
@[simp] theorem get_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
|
||||
(c : cont) (i : idx) [Decidable (dom c i)] (h) :
|
||||
c[i]?.get h = c[i]'(by simp only [getElem?_def] at h; split at h <;> simp_all) := by
|
||||
simp only [getElem?_def] at h ⊢
|
||||
split <;> simp_all
|
||||
|
||||
namespace Fin
|
||||
|
||||
instance instGetElemFinVal [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
|
||||
@@ -224,7 +230,7 @@ end List
|
||||
namespace Array
|
||||
|
||||
instance : GetElem (Array α) Nat α fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
getElem xs i h := xs.get i h
|
||||
|
||||
end Array
|
||||
|
||||
|
||||
@@ -938,8 +938,8 @@ and `e` can depend on `h : ¬c`. (Both branches use the same name for the hypoth
|
||||
even though it has different types in the two cases.)
|
||||
|
||||
We use this to be able to communicate the if-then-else condition to the branches.
|
||||
For example, `Array.get arr ⟨i, h⟩` expects a proof `h : i < arr.size` in order to
|
||||
avoid a bounds check, so you can write `if h : i < arr.size then arr.get ⟨i, h⟩ else ...`
|
||||
For example, `Array.get arr i h` expects a proof `h : i < arr.size` in order to
|
||||
avoid a bounds check, so you can write `if h : i < arr.size then arr.get i h else ...`
|
||||
to avoid the bounds check inside the if branch. (Of course in this case we have only
|
||||
lifted the check into an explicit `if`, but we could also use this proof multiple times
|
||||
or derive `i < arr.size` from some other proposition that we are checking in the `if`.)
|
||||
@@ -2630,14 +2630,21 @@ def Array.empty {α : Type u} : Array α := mkEmpty 0
|
||||
def Array.size {α : Type u} (a : @& Array α) : Nat :=
|
||||
a.toList.length
|
||||
|
||||
/-- Access an element from an array without bounds checks, using a `Fin` index. -/
|
||||
/--
|
||||
Access an element from an array without needing a runtime bounds checks,
|
||||
using a `Nat` index and a proof that it is in bounds.
|
||||
|
||||
This function does not use `get_elem_tactic` to automatically find the proof that
|
||||
the index is in bounds. This is because the tactic itself needs to look up values in
|
||||
arrays. Use the indexing notation `a[i]` instead.
|
||||
-/
|
||||
@[extern "lean_array_fget"]
|
||||
def Array.get {α : Type u} (a : @& Array α) (i : @& Fin a.size) : α :=
|
||||
a.toList.get i
|
||||
def Array.get {α : Type u} (a : @& Array α) (i : @& Nat) (h : LT.lt i a.size) : α :=
|
||||
a.toList.get ⟨i, h⟩
|
||||
|
||||
/-- Access an element from an array, or return `v₀` if the index is out of bounds. -/
|
||||
@[inline] abbrev Array.getD (a : Array α) (i : Nat) (v₀ : α) : α :=
|
||||
dite (LT.lt i a.size) (fun h => a.get ⟨i, h⟩) (fun _ => v₀)
|
||||
dite (LT.lt i a.size) (fun h => a.get i h) (fun _ => v₀)
|
||||
|
||||
/-- Access an element from an array, or panic if the index is out of bounds. -/
|
||||
@[extern "lean_array_get"]
|
||||
@@ -2695,7 +2702,7 @@ protected def Array.appendCore {α : Type u} (as : Array α) (bs : Array α) :
|
||||
(fun hlt =>
|
||||
match i with
|
||||
| 0 => as
|
||||
| Nat.succ i' => loop i' (hAdd j 1) (as.push (bs.get ⟨j, hlt⟩)))
|
||||
| Nat.succ i' => loop i' (hAdd j 1) (as.push (bs.get j hlt)))
|
||||
(fun _ => as)
|
||||
loop bs.size 0 as
|
||||
|
||||
@@ -2710,7 +2717,7 @@ def Array.extract (as : Array α) (start stop : Nat) : Array α :=
|
||||
(fun hlt =>
|
||||
match i with
|
||||
| 0 => bs
|
||||
| Nat.succ i' => loop i' (hAdd j 1) (bs.push (as.get ⟨j, hlt⟩)))
|
||||
| Nat.succ i' => loop i' (hAdd j 1) (bs.push (as.get j hlt)))
|
||||
(fun _ => bs)
|
||||
let sz' := Nat.sub (min stop as.size) start
|
||||
loop sz' start (mkEmpty sz')
|
||||
@@ -2822,17 +2829,6 @@ instance {α : Type u} {m : Type u → Type v} [Monad m] [Inhabited α] : Inhabi
|
||||
instance [Monad m] : [Nonempty α] → Nonempty (m α)
|
||||
| ⟨x⟩ => ⟨pure x⟩
|
||||
|
||||
/-- A fusion of Haskell's `sequence` and `map`. Used in syntax quotations. -/
|
||||
def Array.sequenceMap {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : α → m β) : m (Array β) :=
|
||||
let rec loop (i : Nat) (j : Nat) (bs : Array β) : m (Array β) :=
|
||||
dite (LT.lt j as.size)
|
||||
(fun hlt =>
|
||||
match i with
|
||||
| 0 => pure bs
|
||||
| Nat.succ i' => Bind.bind (f (as.get ⟨j, hlt⟩)) fun b => loop i' (hAdd j 1) (bs.push b))
|
||||
(fun _ => pure bs)
|
||||
loop as.size 0 (Array.mkEmpty as.size)
|
||||
|
||||
/--
|
||||
A function for lifting a computation from an inner `Monad` to an outer `Monad`.
|
||||
Like Haskell's [`MonadTrans`], but `n` does not have to be a monad transformer.
|
||||
|
||||
@@ -466,7 +466,7 @@ hypotheses or the goal. It can have one of the forms:
|
||||
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
|
||||
* `at *`: target all hypotheses and the goal
|
||||
-/
|
||||
syntax location := withPosition(" at" (locationWildcard <|> locationHyp))
|
||||
syntax location := withPosition(ppGroup(" at" (locationWildcard <|> locationHyp)))
|
||||
|
||||
/--
|
||||
* `change tgt'` will change the goal from `tgt` to `tgt'`,
|
||||
|
||||
@@ -133,8 +133,8 @@ def foldNatBinBoolPred (fn : Nat → Nat → Bool) (a₁ a₂ : Expr) : Option E
|
||||
return mkConst ``Bool.false
|
||||
|
||||
def foldNatBeq := fun _ : Bool => foldNatBinBoolPred (fun a b => a == b)
|
||||
def foldNatBle := fun _ : Bool => foldNatBinBoolPred (fun a b => a < b)
|
||||
def foldNatBlt := fun _ : Bool => foldNatBinBoolPred (fun a b => a ≤ b)
|
||||
def foldNatBlt := fun _ : Bool => foldNatBinBoolPred (fun a b => a < b)
|
||||
def foldNatBle := fun _ : Bool => foldNatBinBoolPred (fun a b => a ≤ b)
|
||||
|
||||
def natFoldFns : List (Name × BinFoldFn) :=
|
||||
[(``Nat.add, foldNatAdd),
|
||||
|
||||
@@ -135,8 +135,8 @@ def checkExpr (ty : IRType) : Expr → M Unit
|
||||
match xType with
|
||||
| IRType.object => checkObjType ty
|
||||
| IRType.tobject => checkObjType ty
|
||||
| IRType.struct _ tys => if h : i < tys.size then checkEqTypes (tys.get ⟨i,h⟩) ty else throw "invalid proj index"
|
||||
| IRType.union _ tys => if h : i < tys.size then checkEqTypes (tys.get ⟨i,h⟩) ty else throw "invalid proj index"
|
||||
| IRType.struct _ tys => if h : i < tys.size then checkEqTypes (tys[i]) ty else throw "invalid proj index"
|
||||
| IRType.union _ tys => if h : i < tys.size then checkEqTypes (tys[i]) ty else throw "invalid proj index"
|
||||
| _ => throw s!"unexpected IR type '{xType}'"
|
||||
| Expr.uproj _ x => checkObjVar x *> checkType ty (fun t => t == IRType.usize)
|
||||
| Expr.sproj _ _ x => checkObjVar x *> checkScalarType ty
|
||||
|
||||
@@ -29,4 +29,4 @@ import Lean.Data.Xml
|
||||
import Lean.Data.NameTrie
|
||||
import Lean.Data.RBTree
|
||||
import Lean.Data.RBMap
|
||||
import Lean.Data.Rat
|
||||
import Lean.Data.RArray
|
||||
|
||||
@@ -90,10 +90,9 @@ def contains [BEq α] [Hashable α] (m : HashMapImp α β) (a : α) : Bool :=
|
||||
|
||||
def moveEntries [Hashable α] (i : Nat) (source : Array (AssocList α β)) (target : HashMapBucket α β) : HashMapBucket α β :=
|
||||
if h : i < source.size then
|
||||
let idx : Fin source.size := ⟨i, h⟩
|
||||
let es : AssocList α β := source.get idx
|
||||
let es : AssocList α β := source[i]
|
||||
-- We remove `es` from `source` to make sure we can reuse its memory cells when performing es.foldl
|
||||
let source := source.set idx AssocList.nil
|
||||
let source := source.set i AssocList.nil
|
||||
let target := es.foldl (reinsertAux hash) target
|
||||
moveEntries (i+1) source target
|
||||
else target
|
||||
|
||||
@@ -80,10 +80,9 @@ def contains [BEq α] [Hashable α] (m : HashSetImp α) (a : α) : Bool :=
|
||||
|
||||
def moveEntries [Hashable α] (i : Nat) (source : Array (List α)) (target : HashSetBucket α) : HashSetBucket α :=
|
||||
if h : i < source.size then
|
||||
let idx : Fin source.size := ⟨i, h⟩
|
||||
let es : List α := source.get idx
|
||||
let es : List α := source[i]
|
||||
-- We remove `es` from `source` to make sure we can reuse its memory cells when performing es.foldl
|
||||
let source := source.set idx []
|
||||
let source := source.set i []
|
||||
let target := es.foldl (reinsertAux hash) target
|
||||
moveEntries (i+1) source target
|
||||
else
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace FileMap
|
||||
|
||||
private def lineStartPos (text : FileMap) (line : Nat) : String.Pos :=
|
||||
if h : line < text.positions.size then
|
||||
text.positions.get ⟨line, h⟩
|
||||
text.positions[line]
|
||||
else if text.positions.isEmpty then
|
||||
0
|
||||
else
|
||||
|
||||
@@ -33,6 +33,16 @@ def find? (m : NameMap α) (n : Name) : Option α := RBMap.find? m n
|
||||
instance : ForIn m (NameMap α) (Name × α) :=
|
||||
inferInstanceAs (ForIn _ (RBMap ..) ..)
|
||||
|
||||
/-- `filter f m` returns the `NameMap` consisting of all
|
||||
"`key`/`val`"-pairs in `m` where `f key val` returns `true`. -/
|
||||
def filter (f : Name → α → Bool) (m : NameMap α) : NameMap α := RBMap.filter f m
|
||||
|
||||
/-- `filterMap f m` filters an `NameMap` and simultaneously modifies the filtered values.
|
||||
|
||||
It takes a function `f : Name → α → Option β` and applies `f name` to the value with key `name`.
|
||||
The resulting entries with non-`none` value are collected to form the output `NameMap`. -/
|
||||
def filterMap (f : Name → α → Option β) (m : NameMap α) : NameMap β := RBMap.filterMap f m
|
||||
|
||||
end NameMap
|
||||
|
||||
def NameSet := RBTree Name Name.quickCmp
|
||||
@@ -53,6 +63,9 @@ def append (s t : NameSet) : NameSet :=
|
||||
instance : Append NameSet where
|
||||
append := NameSet.append
|
||||
|
||||
/-- `filter f s` returns the `NameSet` consisting of all `x` in `s` where `f x` returns `true`. -/
|
||||
def filter (f : Name → Bool) (s : NameSet) : NameSet := RBTree.filter f s
|
||||
|
||||
end NameSet
|
||||
|
||||
def NameSSet := SSet Name
|
||||
@@ -73,6 +86,9 @@ instance : EmptyCollection NameHashSet := ⟨empty⟩
|
||||
instance : Inhabited NameHashSet := ⟨{}⟩
|
||||
def insert (s : NameHashSet) (n : Name) := Std.HashSet.insert s n
|
||||
def contains (s : NameHashSet) (n : Name) : Bool := Std.HashSet.contains s n
|
||||
|
||||
/-- `filter f s` returns the `NameHashSet` consisting of all `x` in `s` where `f x` returns `true`. -/
|
||||
def filter (f : Name → Bool) (s : NameHashSet) : NameHashSet := Std.HashSet.filter f s
|
||||
end NameHashSet
|
||||
|
||||
def MacroScopesView.isPrefixOf (v₁ v₂ : MacroScopesView) : Bool :=
|
||||
|
||||
@@ -149,8 +149,8 @@ private def emptyArray {α : Type u} : Array (PersistentArrayNode α) :=
|
||||
partial def popLeaf : PersistentArrayNode α → Option (Array α) × Array (PersistentArrayNode α)
|
||||
| node cs =>
|
||||
if h : cs.size ≠ 0 then
|
||||
let idx : Fin cs.size := ⟨cs.size - 1, by exact Nat.pred_lt h⟩
|
||||
let last := cs.get idx
|
||||
let idx := cs.size - 1
|
||||
let last := cs[idx]
|
||||
let cs' := cs.set idx default
|
||||
match popLeaf last with
|
||||
| (none, _) => (none, emptyArray)
|
||||
|
||||
@@ -84,11 +84,10 @@ private theorem size_push {ks : Array α} {vs : Array β} (h : ks.size = vs.size
|
||||
partial def insertAtCollisionNodeAux [BEq α] : CollisionNode α β → Nat → α → β → CollisionNode α β
|
||||
| n@⟨Node.collision keys vals heq, _⟩, i, k, v =>
|
||||
if h : i < keys.size then
|
||||
let idx : Fin keys.size := ⟨i, h⟩;
|
||||
let k' := keys.get idx;
|
||||
let k' := keys[i];
|
||||
if k == k' then
|
||||
let j : Fin vals.size := ⟨i, by rw [←heq]; assumption⟩
|
||||
⟨Node.collision (keys.set idx k) (vals.set j v) (size_set heq idx j k v), IsCollisionNode.mk _ _ _⟩
|
||||
⟨Node.collision (keys.set i k) (vals.set j v) (size_set heq ⟨i, h⟩ j k v), IsCollisionNode.mk _ _ _⟩
|
||||
else insertAtCollisionNodeAux n (i+1) k v
|
||||
else
|
||||
⟨Node.collision (keys.push k) (vals.push v) (size_push heq k v), IsCollisionNode.mk _ _ _⟩
|
||||
|
||||
@@ -97,7 +97,7 @@ partial def toPosition (fmap : FileMap) (pos : String.Pos) : Position :=
|
||||
def ofPosition (text : FileMap) (pos : Position) : String.Pos :=
|
||||
let colPos :=
|
||||
if h : pos.line - 1 < text.positions.size then
|
||||
text.positions.get ⟨pos.line - 1, h⟩
|
||||
text.positions[pos.line - 1]
|
||||
else if text.positions.isEmpty then
|
||||
0
|
||||
else
|
||||
@@ -110,7 +110,7 @@ This gives the same result as `map.ofPosition ⟨line, 0⟩`, but is more effici
|
||||
-/
|
||||
def lineStart (map : FileMap) (line : Nat) : String.Pos :=
|
||||
if h : line - 1 < map.positions.size then
|
||||
map.positions.get ⟨line - 1, h⟩
|
||||
map.positions[line - 1]
|
||||
else map.positions.back?.getD 0
|
||||
|
||||
end FileMap
|
||||
|
||||
75
src/Lean/Data/RArray.lean
Normal file
75
src/Lean/Data/RArray.lean
Normal file
@@ -0,0 +1,75 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.RArray
|
||||
import Lean.ToExpr
|
||||
|
||||
/-!
|
||||
Auxillary definitions related to `Lean.RArray` that are typically only used in meta-code, in
|
||||
particular the `ToExpr` instance.
|
||||
-/
|
||||
|
||||
namespace Lean
|
||||
|
||||
-- This function could live in Init/Data/RArray.lean, but without omega it's tedious to implement
|
||||
def RArray.ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) : RArray α :=
|
||||
go 0 n h (Nat.le_refl _)
|
||||
where
|
||||
go (lb ub : Nat) (h1 : lb < ub) (h2 : ub ≤ n) : RArray α :=
|
||||
if h : lb + 1 = ub then
|
||||
.leaf (f ⟨lb, Nat.lt_of_lt_of_le h1 h2⟩)
|
||||
else
|
||||
let mid := (lb + ub)/2
|
||||
.branch mid (go lb mid (by omega) (by omega)) (go mid ub (by omega) h2)
|
||||
|
||||
def RArray.ofArray (xs : Array α) (h : 0 < xs.size) : RArray α :=
|
||||
.ofFn (xs[·]) h
|
||||
|
||||
/-- The correctness theorem for `ofFn` -/
|
||||
theorem RArray.get_ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) (i : Fin n) :
|
||||
(ofFn f h).get i = f i :=
|
||||
go 0 n h (Nat.le_refl _) (Nat.zero_le _) i.2
|
||||
where
|
||||
go lb ub h1 h2 (h3 : lb ≤ i.val) (h3 : i.val < ub) : (ofFn.go f lb ub h1 h2).get i = f i := by
|
||||
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (f := f) (n := n)
|
||||
case case1 =>
|
||||
simp [ofFn.go, RArray.get_eq_getImpl, RArray.getImpl]
|
||||
congr
|
||||
omega
|
||||
case case2 ih1 ih2 hiu =>
|
||||
rw [ofFn.go]; simp only [↓reduceDIte, *]
|
||||
simp [RArray.get_eq_getImpl, RArray.getImpl] at *
|
||||
split
|
||||
· rw [ih1] <;> omega
|
||||
· rw [ih2] <;> omega
|
||||
|
||||
@[simp]
|
||||
theorem RArray.size_ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) :
|
||||
(ofFn f h).size = n :=
|
||||
go 0 n h (Nat.le_refl _)
|
||||
where
|
||||
go lb ub h1 h2 : (ofFn.go f lb ub h1 h2).size = ub - lb := by
|
||||
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (f := f) (n := n)
|
||||
case case1 => simp [ofFn.go, size]; omega
|
||||
case case2 ih1 ih2 hiu => rw [ofFn.go]; simp [size, *]; omega
|
||||
|
||||
section Meta
|
||||
open Lean
|
||||
|
||||
def RArray.toExpr (ty : Expr) (f : α → Expr) : RArray α → Expr
|
||||
| .leaf x =>
|
||||
mkApp2 (mkConst ``RArray.leaf) ty (f x)
|
||||
| .branch p l r =>
|
||||
mkApp4 (mkConst ``RArray.branch) ty (mkRawNatLit p) (l.toExpr ty f) (r.toExpr ty f)
|
||||
|
||||
instance [ToExpr α] : ToExpr (RArray α) where
|
||||
toTypeExpr := mkApp (mkConst ``RArray) (toTypeExpr α)
|
||||
toExpr a := a.toExpr (toTypeExpr α) toExpr
|
||||
|
||||
end Meta
|
||||
|
||||
end Lean
|
||||
@@ -404,6 +404,24 @@ def intersectBy {γ : Type v₁} {δ : Type v₂} (mergeFn : α → β → γ
|
||||
| some b₂ => acc.insert a <| mergeFn a b₁ b₂
|
||||
| none => acc
|
||||
|
||||
/--
|
||||
`filter f m` returns the `RBMap` consisting of all
|
||||
"`key`/`val`"-pairs in `m` where `f key val` returns `true`.
|
||||
-/
|
||||
def filter (f : α → β → Bool) (m : RBMap α β cmp) : RBMap α β cmp :=
|
||||
m.fold (fun r k v => if f k v then r.insert k v else r) {}
|
||||
|
||||
/--
|
||||
`filterMap f m` filters an `RBMap` and simultaneously modifies the filtered values.
|
||||
|
||||
It takes a function `f : α → β → Option γ` and applies `f k v` to the value with key `k`.
|
||||
The resulting entries with non-`none` value are collected to form the output `RBMap`.
|
||||
-/
|
||||
def filterMap (f : α → β → Option γ) (m : RBMap α β cmp) : RBMap α γ cmp :=
|
||||
m.fold (fun r k v => match f k v with
|
||||
| none => r
|
||||
| some b => r.insert k b) {}
|
||||
|
||||
end RBMap
|
||||
|
||||
def rbmapOf {α : Type u} {β : Type v} (l : List (α × β)) (cmp : α → α → Ordering) : RBMap α β cmp :=
|
||||
|
||||
@@ -114,6 +114,13 @@ def union (t₁ t₂ : RBTree α cmp) : RBTree α cmp :=
|
||||
def diff (t₁ t₂ : RBTree α cmp) : RBTree α cmp :=
|
||||
t₂.fold .erase t₁
|
||||
|
||||
/--
|
||||
`filter f m` returns the `RBTree` consisting of all
|
||||
`x` in `m` where `f x` returns `true`.
|
||||
-/
|
||||
def filter (f : α → Bool) (m : RBTree α cmp) : RBTree α cmp :=
|
||||
RBMap.filter (fun a _ => f a) m
|
||||
|
||||
end RBTree
|
||||
|
||||
def rbtreeOf {α : Type u} (l : List α) (cmp : α → α → Ordering) : RBTree α cmp :=
|
||||
|
||||
@@ -506,8 +506,7 @@ where
|
||||
if h : i < args.size then
|
||||
match (← whnf cType) with
|
||||
| .forallE _ d b _ =>
|
||||
let arg := args.get ⟨i, h⟩
|
||||
if arg == x && d.isOutParam then
|
||||
if args[i] == x && d.isOutParam then
|
||||
return true
|
||||
isOutParamOf x (i+1) args b
|
||||
| _ => return false
|
||||
|
||||
@@ -111,9 +111,8 @@ private def checkEndHeader : Name → List Scope → Option Name
|
||||
|
||||
private partial def elabChoiceAux (cmds : Array Syntax) (i : Nat) : CommandElabM Unit :=
|
||||
if h : i < cmds.size then
|
||||
let cmd := cmds.get ⟨i, h⟩;
|
||||
catchInternalId unsupportedSyntaxExceptionId
|
||||
(elabCommand cmd)
|
||||
(elabCommand cmds[i])
|
||||
(fun _ => elabChoiceAux cmds (i+1))
|
||||
else
|
||||
throwUnsupportedSyntax
|
||||
|
||||
@@ -214,7 +214,7 @@ private def addTraceAsMessagesCore (ctx : Context) (log : MessageLog) (traceStat
|
||||
let mut log := log
|
||||
let traces' := traces.toArray.qsort fun ((a, _), _) ((b, _), _) => a < b
|
||||
for ((pos, endPos), traceMsg) in traces' do
|
||||
let data := .tagged `_traceMsg <| .joinSep traceMsg.toList "\n"
|
||||
let data := .tagged `trace <| .joinSep traceMsg.toList "\n"
|
||||
log := log.add <| mkMessageCore ctx.fileName ctx.fileMap data .information pos endPos
|
||||
return log
|
||||
|
||||
|
||||
@@ -192,8 +192,7 @@ private def isMutualPreambleCommand (stx : Syntax) : Bool :=
|
||||
private partial def splitMutualPreamble (elems : Array Syntax) : Option (Array Syntax × Array Syntax) :=
|
||||
let rec loop (i : Nat) : Option (Array Syntax × Array Syntax) :=
|
||||
if h : i < elems.size then
|
||||
let elem := elems.get ⟨i, h⟩
|
||||
if isMutualPreambleCommand elem then
|
||||
if isMutualPreambleCommand elems[i] then
|
||||
loop (i+1)
|
||||
else if i == 0 then
|
||||
none -- `mutual` block does not contain any preamble commands
|
||||
|
||||
@@ -133,7 +133,7 @@ private def inductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) : Term
|
||||
private partial def elabHeaderAux (views : Array InductiveView) (i : Nat) (acc : Array ElabHeaderResult) : TermElabM (Array ElabHeaderResult) :=
|
||||
Term.withAutoBoundImplicitForbiddenPred (fun n => views.any (·.shortDeclName == n)) do
|
||||
if h : i < views.size then
|
||||
let view := views.get ⟨i, h⟩
|
||||
let view := views[i]
|
||||
let acc ← Term.withAutoBoundImplicit <| Term.elabBinders view.binders.getArgs fun params => do
|
||||
match view.type? with
|
||||
| none =>
|
||||
@@ -250,7 +250,7 @@ private partial def withInductiveLocalDecls (rs : Array ElabHeaderResult) (x : A
|
||||
withLCtx r0.lctx r0.localInsts <| withRef r0.view.ref do
|
||||
let rec loop (i : Nat) (indFVars : Array Expr) := do
|
||||
if h : i < namesAndTypes.size then
|
||||
let (declName, shortDeclName, type) := namesAndTypes.get ⟨i, h⟩
|
||||
let (declName, shortDeclName, type) := namesAndTypes[i]
|
||||
Term.withAuxDecl shortDeclName type declName fun indFVar => loop (i+1) (indFVars.push indFVar)
|
||||
else
|
||||
x params indFVars
|
||||
|
||||
@@ -77,7 +77,7 @@ private def mkLetRecDeclView (letRec : Syntax) : TermElabM LetRecView := do
|
||||
private partial def withAuxLocalDecls {α} (views : Array LetRecDeclView) (k : Array Expr → TermElabM α) : TermElabM α :=
|
||||
let rec loop (i : Nat) (fvars : Array Expr) : TermElabM α :=
|
||||
if h : i < views.size then
|
||||
let view := views.get ⟨i, h⟩
|
||||
let view := views[i]
|
||||
withAuxDecl view.shortDeclName view.type view.declName fun fvar => loop (i+1) (fvars.push fvar)
|
||||
else
|
||||
k fvars
|
||||
@@ -90,9 +90,9 @@ private def elabLetRecDeclValues (view : LetRecView) : TermElabM (Array Expr) :=
|
||||
for i in [0:view.binderIds.size] do
|
||||
addLocalVarInfo view.binderIds[i]! xs[i]!
|
||||
withDeclName view.declName do
|
||||
withInfoContext' view.valStx (mkInfo := mkTermInfo `MutualDef.body view.valStx) do
|
||||
let value ← elabTermEnsuringType view.valStx type
|
||||
mkLambdaFVars xs value
|
||||
withInfoContext' view.valStx (mkInfo := (pure <| .inl <| mkBodyInfo view.valStx ·)) do
|
||||
let value ← elabTermEnsuringType view.valStx type
|
||||
mkLambdaFVars xs value
|
||||
|
||||
private def registerLetRecsToLift (views : Array LetRecDeclView) (fvars : Array Expr) (values : Array Expr) : TermElabM Unit := do
|
||||
let letRecsToLiftCurr := (← get).letRecsToLift
|
||||
|
||||
@@ -108,7 +108,7 @@ where
|
||||
/-- Elaborate discriminants inferring the match-type -/
|
||||
elabDiscrs (i : Nat) (discrs : Array Discr) : TermElabM ElabMatchTypeAndDiscrsResult := do
|
||||
if h : i < discrStxs.size then
|
||||
let discrStx := discrStxs.get ⟨i, h⟩
|
||||
let discrStx := discrStxs[i]
|
||||
let discr ← elabAtomicDiscr discrStx
|
||||
let discr ← instantiateMVars discr
|
||||
let userName ← mkUserNameFor discr
|
||||
@@ -176,9 +176,8 @@ structure PatternVarDecl where
|
||||
private partial def withPatternVars {α} (pVars : Array PatternVar) (k : Array PatternVarDecl → TermElabM α) : TermElabM α :=
|
||||
let rec loop (i : Nat) (decls : Array PatternVarDecl) (userNames : Array Name) := do
|
||||
if h : i < pVars.size then
|
||||
let var := pVars.get ⟨i, h⟩
|
||||
let type ← mkFreshTypeMVar
|
||||
withLocalDecl var.getId BinderInfo.default type fun x =>
|
||||
withLocalDecl pVars[i].getId BinderInfo.default type fun x =>
|
||||
loop (i+1) (decls.push { fvarId := x.fvarId! }) (userNames.push Name.anonymous)
|
||||
else
|
||||
k decls
|
||||
@@ -760,7 +759,7 @@ where
|
||||
| [] => k eqs
|
||||
| p::ps =>
|
||||
if h : i < discrs.size then
|
||||
let discr := discrs.get ⟨i, h⟩
|
||||
let discr := discrs[i]
|
||||
if let some h := discr.h? then
|
||||
withLocalDeclD h.getId (← mkEqHEq discr.expr (← p.toExpr)) fun eq => do
|
||||
addTermInfo' h eq (isBinder := true)
|
||||
|
||||
@@ -77,7 +77,7 @@ private def check (prevHeaders : Array DefViewElabHeader) (newHeader : DefViewEl
|
||||
if newHeader.modifiers.isPartial && newHeader.modifiers.isUnsafe then
|
||||
throwError "'unsafe' subsumes 'partial'"
|
||||
if h : 0 < prevHeaders.size then
|
||||
let firstHeader := prevHeaders.get ⟨0, h⟩
|
||||
let firstHeader := prevHeaders[0]
|
||||
try
|
||||
unless newHeader.levelNames == firstHeader.levelNames do
|
||||
throwError "universe parameters mismatch"
|
||||
@@ -273,7 +273,7 @@ where
|
||||
private partial def withFunLocalDecls {α} (headers : Array DefViewElabHeader) (k : Array Expr → TermElabM α) : TermElabM α :=
|
||||
let rec loop (i : Nat) (fvars : Array Expr) := do
|
||||
if h : i < headers.size then
|
||||
let header := headers.get ⟨i, h⟩
|
||||
let header := headers[i]
|
||||
if header.modifiers.isNonrec then
|
||||
loop (i+1) fvars
|
||||
else
|
||||
@@ -417,7 +417,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
|
||||
-- Store instantiated body in info tree for the benefit of the unused variables linter
|
||||
-- and other metaprograms that may want to inspect it without paying for the instantiation
|
||||
-- again
|
||||
withInfoContext' valStx (mkInfo := mkTermInfo `MutualDef.body valStx) do
|
||||
withInfoContext' valStx (mkInfo := (pure <| .inl <| mkBodyInfo valStx ·)) do
|
||||
-- synthesize mvars here to force the top-level tactic block (if any) to run
|
||||
let val ← elabTermEnsuringType valStx type <* synthesizeSyntheticMVarsNoPostponing
|
||||
-- NOTE: without this `instantiatedMVars`, `mkLambdaFVars` may leave around a redex that
|
||||
@@ -936,7 +936,7 @@ end MutualClosure
|
||||
private def getAllUserLevelNames (headers : Array DefViewElabHeader) : List Name :=
|
||||
if h : 0 < headers.size then
|
||||
-- Recall that all top-level functions must have the same levels. See `check` method above
|
||||
(headers.get ⟨0, h⟩).levelNames
|
||||
headers[0].levelNames
|
||||
else
|
||||
[]
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ private def isNextArgAccessible (ctx : Context) : Bool :=
|
||||
| none =>
|
||||
if h : i < ctx.paramDecls.size then
|
||||
-- For `[match_pattern]` applications, only explicit parameters are accessible.
|
||||
let d := ctx.paramDecls.get ⟨i, h⟩
|
||||
let d := ctx.paramDecls[i]
|
||||
d.2.isExplicit
|
||||
else
|
||||
false
|
||||
|
||||
@@ -50,7 +50,9 @@ private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
|
||||
go mvarId
|
||||
else if let some mvarId ← whnfReducibleLHS? mvarId then
|
||||
go mvarId
|
||||
else match (← simpTargetStar mvarId { config.dsimp := false } (simprocs := {})).1 with
|
||||
else
|
||||
let ctx ← Simp.mkContext (config := { dsimp := false })
|
||||
match (← simpTargetStar mvarId ctx (simprocs := {})).1 with
|
||||
| TacticResultCNM.closed => return ()
|
||||
| TacticResultCNM.modified mvarId => go mvarId
|
||||
| TacticResultCNM.noChange =>
|
||||
|
||||
@@ -45,7 +45,9 @@ where
|
||||
go mvarId
|
||||
else if let some mvarId ← simpIf? mvarId then
|
||||
go mvarId
|
||||
else match (← simpTargetStar mvarId {} (simprocs := {})).1 with
|
||||
else
|
||||
let ctx ← Simp.mkContext
|
||||
match (← simpTargetStar mvarId ctx (simprocs := {})).1 with
|
||||
| TacticResultCNM.closed => return ()
|
||||
| TacticResultCNM.modified mvarId => go mvarId
|
||||
| TacticResultCNM.noChange =>
|
||||
|
||||
@@ -57,7 +57,9 @@ private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
|
||||
go mvarId
|
||||
else if let some mvarId ← whnfReducibleLHS? mvarId then
|
||||
go mvarId
|
||||
else match (← simpTargetStar mvarId { config.dsimp := false } (simprocs := {})).1 with
|
||||
else
|
||||
let ctx ← Simp.mkContext (config := { dsimp := false })
|
||||
match (← simpTargetStar mvarId ctx (simprocs := {})).1 with
|
||||
| TacticResultCNM.closed => return ()
|
||||
| TacticResultCNM.modified mvarId => go mvarId
|
||||
| TacticResultCNM.noChange =>
|
||||
|
||||
@@ -227,7 +227,7 @@ def mkFix (preDef : PreDefinition) (prefixArgs : Array Expr) (argsPacker : ArgsP
|
||||
-- decreasing goals when the function has only one non fixed argument.
|
||||
-- This renaming is irrelevant if the function has multiple non fixed arguments. See `process*` functions above.
|
||||
let lctx := (← getLCtx).setUserName x.fvarId! varName
|
||||
withTheReader Meta.Context (fun ctx => { ctx with lctx }) do
|
||||
withLCtx' lctx do
|
||||
let F := xs[1]!
|
||||
let val := preDef.value.beta (prefixArgs.push x)
|
||||
let val ← processSumCasesOn x F val fun x F val => do
|
||||
|
||||
@@ -166,7 +166,7 @@ def mayOmitSizeOf (is_mutual : Bool) (args : Array Expr) (x : Expr) : MetaM Bool
|
||||
def withUserNames {α} (xs : Array Expr) (ns : Array Name) (k : MetaM α) : MetaM α := do
|
||||
let mut lctx ← getLCtx
|
||||
for x in xs, n in ns do lctx := lctx.setUserName x.fvarId! n
|
||||
withTheReader Meta.Context (fun ctx => { ctx with lctx }) k
|
||||
withLCtx' lctx k
|
||||
|
||||
/-- Create one measure for each (eligible) parameter of the given predefintion. -/
|
||||
def simpleMeasures (preDefs : Array PreDefinition) (fixedPrefixSize : Nat)
|
||||
|
||||
@@ -87,7 +87,7 @@ def varyingVarNames (fixedPrefixSize : Nat) (preDef : PreDefinition) : MetaM (Ar
|
||||
xs.mapM (·.fvarId!.getUserName)
|
||||
|
||||
def wfRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
|
||||
let termArgs? := termArg?s.sequenceMap id -- Either all or none, checked by `elabTerminationByHints`
|
||||
let termArgs? := termArg?s.mapM id -- Either all or none, checked by `elabTerminationByHints`
|
||||
let preDefs ← preDefs.mapM fun preDef =>
|
||||
return { preDef with value := (← preprocess preDef.value) }
|
||||
let (fixedPrefixSize, argsPacker, unaryPreDef) ← withoutModifyingEnv do
|
||||
|
||||
@@ -434,7 +434,7 @@ private partial def getHeadInfo (alt : Alt) : TermElabM HeadInfo :=
|
||||
else mkNullNode contents
|
||||
-- We use `no_error_if_unused%` in auxiliary `match`-syntax to avoid spurious error messages,
|
||||
-- the outer `match` is checking for unused alternatives
|
||||
`(match ($(discrs).sequenceMap fun
|
||||
`(match ($(discrs).mapM fun
|
||||
| `($contents) => no_error_if_unused% some $tuple
|
||||
| _ => no_error_if_unused% none) with
|
||||
| some $resId => $yes
|
||||
|
||||
@@ -885,7 +885,7 @@ partial def tryToSynthesizeDefault (structs : Array Struct) (allStructNames : Ar
|
||||
if dist > maxDistance then
|
||||
return false
|
||||
else if h : i < structs.size then
|
||||
let struct := structs.get ⟨i, h⟩
|
||||
let struct := structs[i]
|
||||
match getDefaultFnForField? (← getEnv) struct.structName fieldName with
|
||||
| some defFn =>
|
||||
let cinfo ← getConstInfo defFn
|
||||
|
||||
@@ -321,7 +321,7 @@ private partial def processSubfields (structDeclName : Name) (parentFVar : Expr)
|
||||
where
|
||||
go (i : Nat) (infos : Array StructFieldInfo) := do
|
||||
if h : i < subfieldNames.size then
|
||||
let subfieldName := subfieldNames.get ⟨i, h⟩
|
||||
let subfieldName := subfieldNames[i]
|
||||
if containsFieldName infos subfieldName then
|
||||
throwError "field '{subfieldName}' from '{.ofConstName parentStructName}' has already been declared"
|
||||
let val ← mkProjection parentFVar subfieldName
|
||||
@@ -463,7 +463,7 @@ where
|
||||
let fieldNames := getStructureFields (← getEnv) parentStructName
|
||||
let rec copy (i : Nat) (infos : Array StructFieldInfo) (fieldMap : FieldMap) (expandedStructNames : NameSet) : TermElabM α := do
|
||||
if h : i < fieldNames.size then
|
||||
let fieldName := fieldNames.get ⟨i, h⟩
|
||||
let fieldName := fieldNames[i]
|
||||
let fieldType ← getFieldType infos parentType fieldName
|
||||
match findFieldInfo? infos fieldName with
|
||||
| some existingFieldInfo =>
|
||||
@@ -548,8 +548,9 @@ where
|
||||
let parentType ← whnf type
|
||||
let parentStructName ← getStructureName parentType
|
||||
if parents.any (fun info => info.structName == parentStructName) then
|
||||
logWarningAt parent m!"duplicate parent structure '{.ofConstName parentStructName}'"
|
||||
if let some existingFieldName ← findExistingField? infos parentStructName then
|
||||
logWarningAt parent m!"duplicate parent structure '{.ofConstName parentStructName}', skipping"
|
||||
go (i + 1) infos parents
|
||||
else if let some existingFieldName ← findExistingField? infos parentStructName then
|
||||
if structureDiamondWarning.get (← getOptions) then
|
||||
logWarning m!"field '{existingFieldName}' from '{.ofConstName parentStructName}' has already been declared"
|
||||
let parents := parents.push { ref := parent, fvar? := none, subobject := false, structName := parentStructName, type := parentType }
|
||||
@@ -854,6 +855,7 @@ private def setSourceInstImplicit (type : Expr) : Expr :=
|
||||
Creates a projection function to a non-subobject parent.
|
||||
-/
|
||||
private partial def mkCoercionToCopiedParent (levelParams : List Name) (params : Array Expr) (view : StructView) (parentStructName : Name) (parentType : Expr) : MetaM StructureParentInfo := do
|
||||
let isProp ← Meta.isProp parentType
|
||||
let env ← getEnv
|
||||
let structName := view.declName
|
||||
let sourceFieldNames := getStructureFieldsFlattened env structName
|
||||
@@ -883,17 +885,24 @@ private partial def mkCoercionToCopiedParent (levelParams : List Name) (params :
|
||||
return result
|
||||
let declVal ← instantiateMVars (← mkLambdaFVars params (← mkLambdaFVars #[source] (← copyFields parentType)))
|
||||
let declName := structName ++ mkToParentName (← getStructureName parentType) fun n => !env.contains (structName ++ n)
|
||||
addAndCompile <| Declaration.defnDecl {
|
||||
name := declName
|
||||
levelParams := levelParams
|
||||
type := declType
|
||||
value := declVal
|
||||
hints := ReducibilityHints.abbrev
|
||||
safety := if view.modifiers.isUnsafe then DefinitionSafety.unsafe else DefinitionSafety.safe
|
||||
}
|
||||
if binfo.isInstImplicit then
|
||||
addInstance declName AttributeKind.global (eval_prio default)
|
||||
-- Logic from `mk_projections`: prop-valued projections are theorems (or at least opaque)
|
||||
let cval : ConstantVal := { name := declName, levelParams, type := declType }
|
||||
if isProp then
|
||||
addDecl <|
|
||||
if view.modifiers.isUnsafe then
|
||||
-- Theorems cannot be unsafe.
|
||||
Declaration.opaqueDecl { cval with value := declVal, isUnsafe := true }
|
||||
else
|
||||
Declaration.thmDecl { cval with value := declVal }
|
||||
else
|
||||
addAndCompile <| Declaration.defnDecl { cval with
|
||||
value := declVal
|
||||
hints := ReducibilityHints.abbrev
|
||||
safety := if view.modifiers.isUnsafe then DefinitionSafety.unsafe else DefinitionSafety.safe
|
||||
}
|
||||
-- Logic from `mk_projections`: non-instance-implicits that aren't props become reducible.
|
||||
-- (Instances will get instance reducibility in `Lean.Elab.Command.addParentInstances`.)
|
||||
if !binfo.isInstImplicit && !(← Meta.isProp parentType) then
|
||||
setReducibleAttribute declName
|
||||
return { structName := parentStructName, subobject := false, projFn := declName }
|
||||
|
||||
@@ -965,6 +974,19 @@ private def checkResolutionOrder (structName : Name) : TermElabM Unit := do
|
||||
must come after {MessageData.andList conflicts.toList}" :: defects
|
||||
logWarning m!"failed to compute strict resolution order:\n{MessageData.joinSep defects.reverse "\n"}"
|
||||
|
||||
/--
|
||||
Adds each direct parent projection to a class as an instance, so long as the parent isn't an ancestor of the others.
|
||||
-/
|
||||
private def addParentInstances (parents : Array StructureParentInfo) : MetaM Unit := do
|
||||
let env ← getEnv
|
||||
let instParents := parents.filter fun parent => isClass env parent.structName
|
||||
-- A parent is an ancestor of the others if it appears with index ≥ 1 in one of the resolution orders.
|
||||
let resOrders : Array (Array Name) ← instParents.mapM fun parent => getStructureResolutionOrder parent.structName
|
||||
let instParents := instParents.filter fun parent =>
|
||||
!resOrders.any (fun resOrder => resOrder[1:].any (· == parent.structName))
|
||||
for instParent in instParents do
|
||||
addInstance instParent.projFn AttributeKind.global (eval_prio default)
|
||||
|
||||
def mkStructureDecl (vars : Array Expr) (view : StructView) : TermElabM Unit := Term.withoutSavingRecAppSyntax do
|
||||
let scopeLevelNames ← Term.getLevelNames
|
||||
let isUnsafe := view.modifiers.isUnsafe
|
||||
@@ -1008,9 +1030,6 @@ def mkStructureDecl (vars : Array Expr) (view : StructView) : TermElabM Unit :=
|
||||
addProjections r fieldInfos
|
||||
registerStructure view.declName fieldInfos
|
||||
mkAuxConstructions view.declName
|
||||
let instParents ← fieldInfos.filterM fun info => do
|
||||
let decl ← Term.getFVarLocalDecl! info.fvar
|
||||
pure (info.isSubobject && decl.binderInfo.isInstImplicit)
|
||||
withSaveInfoContext do -- save new env
|
||||
Term.addLocalVarInfo view.ref[1] (← mkConstWithLevelParams view.declName)
|
||||
if let some _ := view.ctor.ref.getPos? (canonicalOnly := true) then
|
||||
@@ -1021,8 +1040,6 @@ def mkStructureDecl (vars : Array Expr) (view : StructView) : TermElabM Unit :=
|
||||
Term.addTermInfo' field.ref (← mkConstWithLevelParams field.declName) (isBinder := true)
|
||||
withRef view.declId do
|
||||
Term.applyAttributesAt view.declName view.modifiers.attrs AttributeApplicationTime.afterTypeChecking
|
||||
let projInstances := instParents.toList.map fun info => info.declName
|
||||
projInstances.forM fun declName => addInstance declName AttributeKind.global (eval_prio default)
|
||||
let parentInfos ← r.parents.mapM fun parent => do
|
||||
if parent.subobject then
|
||||
let some info := fieldInfos.find? (·.kind == .subobject parent.structName) | unreachable!
|
||||
@@ -1031,6 +1048,8 @@ def mkStructureDecl (vars : Array Expr) (view : StructView) : TermElabM Unit :=
|
||||
mkCoercionToCopiedParent levelParams params view parent.structName parent.type
|
||||
setStructureParents view.declName parentInfos
|
||||
checkResolutionOrder view.declName
|
||||
if view.isClass then
|
||||
addParentInstances parentInfos
|
||||
|
||||
let lctx ← getLCtx
|
||||
/- The `lctx` and `defaultAuxDecls` are used to create the auxiliary "default value" declarations
|
||||
|
||||
@@ -233,11 +233,14 @@ where
|
||||
return (← `((with_annotate_term $(stx[0]) @ParserDescr.sepBy1) $p $sep $psep $(quote allowTrailingSep)), 1)
|
||||
|
||||
isValidAtom (s : String) : Bool :=
|
||||
-- Pretty-printing instructions shouldn't affect validity
|
||||
let s := s.trim
|
||||
!s.isEmpty &&
|
||||
s.front != '\'' &&
|
||||
(s.front != '\'' || s == "''") &&
|
||||
s.front != '\"' &&
|
||||
!(s.front == '`' && (s.endPos == ⟨1⟩ || isIdFirst (s.get ⟨1⟩) || isIdBeginEscape (s.get ⟨1⟩))) &&
|
||||
!s.front.isDigit
|
||||
!s.front.isDigit &&
|
||||
!(s.any Char.isWhitespace)
|
||||
|
||||
processAtom (stx : Syntax) := do
|
||||
match stx[0].isStrLit? with
|
||||
|
||||
@@ -163,7 +163,9 @@ builtin_simproc [bv_normalize] bv_udiv_of_two_pow (((_ : BitVec _) / (BitVec.ofN
|
||||
A pass in the normalization pipeline. Takes the current goal and produces a refined one or closes
|
||||
the goal fully, indicated by returning `none`.
|
||||
-/
|
||||
abbrev Pass := MVarId → MetaM (Option MVarId)
|
||||
structure Pass where
|
||||
name : Name
|
||||
run : MVarId → MetaM (Option MVarId)
|
||||
|
||||
namespace Pass
|
||||
|
||||
@@ -174,7 +176,8 @@ the goal anymore.
|
||||
partial def fixpointPipeline (passes : List Pass) (goal : MVarId) : MetaM (Option MVarId) := do
|
||||
let runPass (goal? : Option MVarId) (pass : Pass) : MetaM (Option MVarId) := do
|
||||
let some goal := goal? | return none
|
||||
pass goal
|
||||
withTraceNode `bv (fun _ => return s!"Running pass: {pass.name}") do
|
||||
pass.run goal
|
||||
|
||||
let some newGoal := ← passes.foldlM (init := some goal) runPass | return none
|
||||
if goal != newGoal then
|
||||
@@ -187,83 +190,156 @@ partial def fixpointPipeline (passes : List Pass) (goal : MVarId) : MetaM (Optio
|
||||
/--
|
||||
Responsible for applying the Bitwuzla style rewrite rules.
|
||||
-/
|
||||
def rewriteRulesPass (maxSteps : Nat) : Pass := fun goal => do
|
||||
let bvThms ← bvNormalizeExt.getTheorems
|
||||
let bvSimprocs ← bvNormalizeSimprocExt.getSimprocs
|
||||
let sevalThms ← getSEvalTheorems
|
||||
let sevalSimprocs ← Simp.getSEvalSimprocs
|
||||
def rewriteRulesPass (maxSteps : Nat) : Pass where
|
||||
name := `rewriteRules
|
||||
run goal := do
|
||||
let bvThms ← bvNormalizeExt.getTheorems
|
||||
let bvSimprocs ← bvNormalizeSimprocExt.getSimprocs
|
||||
let sevalThms ← getSEvalTheorems
|
||||
let sevalSimprocs ← Simp.getSEvalSimprocs
|
||||
|
||||
let simpCtx : Simp.Context := {
|
||||
config := { failIfUnchanged := false, zetaDelta := true, maxSteps }
|
||||
simpTheorems := #[bvThms, sevalThms]
|
||||
congrTheorems := (← getSimpCongrTheorems)
|
||||
}
|
||||
let simpCtx ← Simp.mkContext
|
||||
(config := { failIfUnchanged := false, zetaDelta := true, maxSteps })
|
||||
(simpTheorems := #[bvThms, sevalThms])
|
||||
(congrTheorems := (← getSimpCongrTheorems))
|
||||
|
||||
let hyps ← goal.getNondepPropHyps
|
||||
let ⟨result?, _⟩ ← simpGoal goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := #[bvSimprocs, sevalSimprocs])
|
||||
(fvarIdsToSimp := hyps)
|
||||
let some (_, newGoal) := result? | return none
|
||||
return newGoal
|
||||
|
||||
/--
|
||||
Substitute embedded constraints. That is look for hypotheses of the form `h : x = true` and use
|
||||
them to substitute occurences of `x` within other hypotheses
|
||||
-/
|
||||
def embeddedConstraintPass (maxSteps : Nat) : Pass := fun goal =>
|
||||
goal.withContext do
|
||||
let hyps ← goal.getNondepPropHyps
|
||||
let relevanceFilter acc hyp := do
|
||||
let typ ← hyp.getType
|
||||
let_expr Eq α _ rhs := typ | return acc
|
||||
let_expr Bool := α | return acc
|
||||
let_expr Bool.true := rhs | return acc
|
||||
let localDecl ← hyp.getDecl
|
||||
let proof := localDecl.toExpr
|
||||
acc.addTheorem (.fvar hyp) proof
|
||||
let relevantHyps : SimpTheoremsArray ← hyps.foldlM (init := #[]) relevanceFilter
|
||||
|
||||
let simpCtx : Simp.Context := {
|
||||
config := { failIfUnchanged := false, maxSteps }
|
||||
simpTheorems := relevantHyps
|
||||
congrTheorems := (← getSimpCongrTheorems)
|
||||
}
|
||||
|
||||
let ⟨result?, _⟩ ← simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := hyps)
|
||||
let ⟨result?, _⟩ ← simpGoal goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := #[bvSimprocs, sevalSimprocs])
|
||||
(fvarIdsToSimp := hyps)
|
||||
let some (_, newGoal) := result? | return none
|
||||
return newGoal
|
||||
|
||||
/--
|
||||
Normalize with respect to Associativity and Commutativity.
|
||||
Flatten out ands. That is look for hypotheses of the form `h : (x && y) = true` and replace them
|
||||
with `h.left : x = true` and `h.right : y = true`. This can enable more fine grained substitutions
|
||||
in embedded constraint substitution.
|
||||
-/
|
||||
def acNormalizePass : Pass := fun goal => do
|
||||
let mut newGoal := goal
|
||||
for hyp in (← goal.getNondepPropHyps) do
|
||||
let result ← Lean.Meta.AC.acNfHypMeta newGoal hyp
|
||||
partial def andFlatteningPass : Pass where
|
||||
name := `andFlattening
|
||||
run goal := do
|
||||
goal.withContext do
|
||||
let hyps ← goal.getNondepPropHyps
|
||||
let mut newHyps := #[]
|
||||
let mut oldHyps := #[]
|
||||
for fvar in hyps do
|
||||
let hyp : Hypothesis := {
|
||||
userName := (← fvar.getDecl).userName
|
||||
type := ← fvar.getType
|
||||
value := mkFVar fvar
|
||||
}
|
||||
let sizeBefore := newHyps.size
|
||||
newHyps ← splitAnds hyp newHyps
|
||||
if newHyps.size > sizeBefore then
|
||||
oldHyps := oldHyps.push fvar
|
||||
if newHyps.size == 0 then
|
||||
return goal
|
||||
else
|
||||
let (_, goal) ← goal.assertHypotheses newHyps
|
||||
-- Given that we collected the hypotheses in the correct order above the invariant is given
|
||||
let goal ← goal.tryClearMany oldHyps
|
||||
return goal
|
||||
where
|
||||
splitAnds (hyp : Hypothesis) (hyps : Array Hypothesis) (first : Bool := true) :
|
||||
MetaM (Array Hypothesis) := do
|
||||
match ← trySplit hyp with
|
||||
| some (left, right) =>
|
||||
let hyps ← splitAnds left hyps false
|
||||
splitAnds right hyps false
|
||||
| none =>
|
||||
if first then
|
||||
return hyps
|
||||
else
|
||||
return hyps.push hyp
|
||||
|
||||
if let .some nextGoal := result then
|
||||
newGoal := nextGoal
|
||||
else
|
||||
return none
|
||||
|
||||
return newGoal
|
||||
trySplit (hyp : Hypothesis) : MetaM (Option (Hypothesis × Hypothesis)) := do
|
||||
let typ := hyp.type
|
||||
let_expr Eq α eqLhs eqRhs := typ | return none
|
||||
let_expr Bool.and lhs rhs := eqLhs | return none
|
||||
let_expr Bool.true := eqRhs | return none
|
||||
let_expr Bool := α | return none
|
||||
let mkEqTrue (lhs : Expr) : Expr :=
|
||||
mkApp3 (mkConst ``Eq [1]) (mkConst ``Bool) lhs (mkConst ``Bool.true)
|
||||
let leftHyp : Hypothesis := {
|
||||
userName := hyp.userName,
|
||||
type := mkEqTrue lhs,
|
||||
value := mkApp3 (mkConst ``Std.Tactic.BVDecide.Normalize.Bool.and_left) lhs rhs hyp.value
|
||||
}
|
||||
let rightHyp : Hypothesis := {
|
||||
userName := hyp.userName,
|
||||
type := mkEqTrue rhs,
|
||||
value := mkApp3 (mkConst ``Std.Tactic.BVDecide.Normalize.Bool.and_right) lhs rhs hyp.value
|
||||
}
|
||||
return some (leftHyp, rightHyp)
|
||||
|
||||
/--
|
||||
The normalization passes used by `bv_normalize` and thus `bv_decide`.
|
||||
Substitute embedded constraints. That is look for hypotheses of the form `h : x = true` and use
|
||||
them to substitute occurences of `x` within other hypotheses. Additionally this drops all
|
||||
redundant top level hypotheses.
|
||||
-/
|
||||
def defaultPipeline (cfg : BVDecideConfig ): List Pass :=
|
||||
[
|
||||
rewriteRulesPass cfg.maxSteps,
|
||||
embeddedConstraintPass cfg.maxSteps
|
||||
]
|
||||
def embeddedConstraintPass (maxSteps : Nat) : Pass where
|
||||
name := `embeddedConstraintSubsitution
|
||||
run goal := do
|
||||
goal.withContext do
|
||||
let hyps ← goal.getNondepPropHyps
|
||||
let mut relevantHyps : SimpTheoremsArray := #[]
|
||||
let mut seen : Std.HashSet Expr := {}
|
||||
let mut duplicates : Array FVarId := #[]
|
||||
for hyp in hyps do
|
||||
let typ ← hyp.getType
|
||||
let_expr Eq α lhs rhs := typ | continue
|
||||
let_expr Bool.true := rhs | continue
|
||||
let_expr Bool := α | continue
|
||||
if seen.contains lhs then
|
||||
-- collect and later remove duplicates on the fly
|
||||
duplicates := duplicates.push hyp
|
||||
else
|
||||
seen := seen.insert lhs
|
||||
let localDecl ← hyp.getDecl
|
||||
let proof := localDecl.toExpr
|
||||
relevantHyps ← relevantHyps.addTheorem (.fvar hyp) proof
|
||||
|
||||
let goal ← goal.tryClearMany duplicates
|
||||
|
||||
let simpCtx ← Simp.mkContext
|
||||
(config := { failIfUnchanged := false, maxSteps })
|
||||
(simpTheorems := relevantHyps)
|
||||
(congrTheorems := (← getSimpCongrTheorems))
|
||||
|
||||
let ⟨result?, _⟩ ← simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := ← goal.getNondepPropHyps)
|
||||
let some (_, newGoal) := result? | return none
|
||||
return newGoal
|
||||
|
||||
/--
|
||||
Normalize with respect to Associativity and Commutativity.
|
||||
-/
|
||||
def acNormalizePass : Pass where
|
||||
name := `ac_nf
|
||||
run goal := do
|
||||
let mut newGoal := goal
|
||||
for hyp in (← goal.getNondepPropHyps) do
|
||||
let result ← Lean.Meta.AC.acNfHypMeta newGoal hyp
|
||||
|
||||
if let .some nextGoal := result then
|
||||
newGoal := nextGoal
|
||||
else
|
||||
return none
|
||||
|
||||
return newGoal
|
||||
|
||||
def passPipeline (cfg : BVDecideConfig) : List Pass := Id.run do
|
||||
let mut passPipeline := defaultPipeline cfg
|
||||
let mut passPipeline := [rewriteRulesPass cfg.maxSteps]
|
||||
|
||||
if cfg.acNf then
|
||||
passPipeline := passPipeline ++ [acNormalizePass]
|
||||
|
||||
if cfg.andFlattening then
|
||||
passPipeline := passPipeline ++ [andFlatteningPass]
|
||||
|
||||
if cfg.embeddedConstraintSubst then
|
||||
passPipeline := passPipeline ++ [embeddedConstraintPass cfg.maxSteps]
|
||||
|
||||
return passPipeline
|
||||
|
||||
end Pass
|
||||
|
||||
@@ -308,7 +308,7 @@ def evalTacticSeq : Tactic :=
|
||||
|
||||
partial def evalChoiceAux (tactics : Array Syntax) (i : Nat) : TacticM Unit :=
|
||||
if h : i < tactics.size then
|
||||
let tactic := tactics.get ⟨i, h⟩
|
||||
let tactic := tactics[i]
|
||||
catchInternalId unsupportedSyntaxExceptionId
|
||||
(evalTactic tactic)
|
||||
(fun _ => evalChoiceAux tactics (i+1))
|
||||
|
||||
@@ -12,11 +12,10 @@ namespace Lean.Elab.Tactic.Conv
|
||||
open Meta
|
||||
|
||||
private def getContext : MetaM Simp.Context := do
|
||||
return {
|
||||
simpTheorems := {}
|
||||
congrTheorems := (← getSimpCongrTheorems)
|
||||
config := Simp.neutralConfig
|
||||
}
|
||||
Simp.mkContext
|
||||
(simpTheorems := {})
|
||||
(congrTheorems := (← getSimpCongrTheorems))
|
||||
(config := Simp.neutralConfig)
|
||||
|
||||
partial def matchPattern? (pattern : AbstractMVarsResult) (e : Expr) : MetaM (Option (Expr × Array Expr)) :=
|
||||
withNewMCtxDepth do
|
||||
@@ -126,7 +125,7 @@ private def pre (pattern : AbstractMVarsResult) (state : IO.Ref PatternMatchStat
|
||||
pure (.occs #[] 0 ids.toList)
|
||||
| _ => throwUnsupportedSyntax
|
||||
let state ← IO.mkRef occs
|
||||
let ctx := { ← getContext with config.memoize := occs matches .all _ }
|
||||
let ctx := (← getContext).setMemoize (occs matches .all _)
|
||||
let (result, _) ← Simp.main lhs ctx (methods := { pre := pre patternA state })
|
||||
let subgoals ← match ← state.get with
|
||||
| .all #[] | .occs _ 0 _ =>
|
||||
|
||||
@@ -28,8 +28,10 @@ def proveEqUsing (s : SimpTheorems) (a b : Expr) : MetaM (Option Simp.Result) :=
|
||||
unless ← isDefEq a'.expr b'.expr do return none
|
||||
a'.mkEqTrans (← b'.mkEqSymm b)
|
||||
withReducible do
|
||||
(go (← Simp.mkDefaultMethods).toMethodsRef
|
||||
{ simpTheorems := #[s], congrTheorems := ← Meta.getSimpCongrTheorems }).run' {}
|
||||
let ctx ← Simp.mkContext
|
||||
(simpTheorems := #[s])
|
||||
(congrTheorems := ← Meta.getSimpCongrTheorems)
|
||||
(go (← Simp.mkDefaultMethods).toMethodsRef ctx).run' {}
|
||||
|
||||
/-- Proves `a = b` by simplifying using move and squash lemmas. -/
|
||||
def proveEqUsingDown (a b : Expr) : MetaM (Option Simp.Result) := do
|
||||
@@ -191,19 +193,25 @@ def derive (e : Expr) : MetaM Simp.Result := do
|
||||
-- step 1: pre-processing of numerals
|
||||
let r ← withTrace "pre-processing numerals" do
|
||||
let post e := return Simp.Step.done (← try numeralToCoe e catch _ => pure {expr := e})
|
||||
r.mkEqTrans (← Simp.main r.expr { config, congrTheorems } (methods := { post })).1
|
||||
let ctx ← Simp.mkContext (config := config) (congrTheorems := congrTheorems)
|
||||
r.mkEqTrans (← Simp.main r.expr ctx (methods := { post })).1
|
||||
|
||||
-- step 2: casts are moved upwards and eliminated
|
||||
let r ← withTrace "moving upward, splitting and eliminating" do
|
||||
let post := upwardAndElim (← normCastExt.up.getTheorems)
|
||||
r.mkEqTrans (← Simp.main r.expr { config, congrTheorems } (methods := { post })).1
|
||||
let ctx ← Simp.mkContext (config := config) (congrTheorems := congrTheorems)
|
||||
r.mkEqTrans (← Simp.main r.expr ctx (methods := { post })).1
|
||||
|
||||
let simprocs ← ({} : Simp.SimprocsArray).add `reduceCtorEq false
|
||||
|
||||
-- step 3: casts are squashed
|
||||
let r ← withTrace "squashing" do
|
||||
let simpTheorems := #[← normCastExt.squash.getTheorems]
|
||||
r.mkEqTrans (← simp r.expr { simpTheorems, config, congrTheorems } simprocs).1
|
||||
let ctx ← Simp.mkContext
|
||||
(config := config)
|
||||
(simpTheorems := simpTheorems)
|
||||
(congrTheorems := congrTheorems)
|
||||
r.mkEqTrans (← simp r.expr ctx simprocs).1
|
||||
|
||||
return r
|
||||
|
||||
@@ -263,7 +271,7 @@ def evalConvNormCast : Tactic :=
|
||||
def evalPushCast : Tactic := fun stx => do
|
||||
let { ctx, simprocs, dischargeWrapper } ← withMainContext do
|
||||
mkSimpContext (simpTheorems := pushCastExt.getTheorems) stx (eraseLocal := false)
|
||||
let ctx := { ctx with config := { ctx.config with failIfUnchanged := false } }
|
||||
let ctx := ctx.setFailIfUnchanged false
|
||||
dischargeWrapper.with fun discharge? =>
|
||||
discard <| simpLocation ctx simprocs discharge? (expandOptLocation stx[5])
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ Authors: Kim Morrison
|
||||
prelude
|
||||
import Lean.Elab.Tactic.Omega.Core
|
||||
import Lean.Elab.Tactic.FalseOrByContra
|
||||
import Lean.Meta.Tactic.Cases
|
||||
import Lean.Elab.Tactic.Config
|
||||
|
||||
/-!
|
||||
@@ -520,23 +519,6 @@ partial def processFacts (p : MetaProblem) : OmegaM (MetaProblem × Nat) := do
|
||||
|
||||
end MetaProblem
|
||||
|
||||
/--
|
||||
Given `p : P ∨ Q` (or any inductive type with two one-argument constructors),
|
||||
split the goal into two subgoals:
|
||||
one containing the hypothesis `h : P` and another containing `h : Q`.
|
||||
-/
|
||||
def cases₂ (mvarId : MVarId) (p : Expr) (hName : Name := `h) :
|
||||
MetaM ((MVarId × FVarId) × (MVarId × FVarId)) := do
|
||||
let mvarId ← mvarId.assert `hByCases (← inferType p) p
|
||||
let (fvarId, mvarId) ← mvarId.intro1
|
||||
let #[s₁, s₂] ← mvarId.cases fvarId #[{ varNames := [hName] }, { varNames := [hName] }] |
|
||||
throwError "'cases' tactic failed, unexpected number of subgoals"
|
||||
let #[Expr.fvar f₁ ..] ← pure s₁.fields
|
||||
| throwError "'cases' tactic failed, unexpected new hypothesis"
|
||||
let #[Expr.fvar f₂ ..] ← pure s₂.fields
|
||||
| throwError "'cases' tactic failed, unexpected new hypothesis"
|
||||
return ((s₁.mvarId, f₁), (s₂.mvarId, f₂))
|
||||
|
||||
/--
|
||||
Helpful error message when omega cannot find a solution
|
||||
-/
|
||||
@@ -628,33 +610,36 @@ mutual
|
||||
Split a disjunction in a `MetaProblem`, and if we find a new usable fact
|
||||
call `omegaImpl` in both branches.
|
||||
-/
|
||||
partial def splitDisjunction (m : MetaProblem) (g : MVarId) : OmegaM Unit := g.withContext do
|
||||
partial def splitDisjunction (m : MetaProblem) : OmegaM Expr := do
|
||||
match m.disjunctions with
|
||||
| [] => throwError "omega could not prove the goal:\n{← formatErrorMessage m.problem}"
|
||||
| h :: t =>
|
||||
trace[omega] "Case splitting on {← inferType h}"
|
||||
let ctx ← getMCtx
|
||||
let (⟨g₁, h₁⟩, ⟨g₂, h₂⟩) ← cases₂ g h
|
||||
trace[omega] "Adding facts:\n{← g₁.withContext <| inferType (.fvar h₁)}"
|
||||
let m₁ := { m with facts := [.fvar h₁], disjunctions := t }
|
||||
let r ← withoutModifyingState do
|
||||
let (m₁, n) ← g₁.withContext m₁.processFacts
|
||||
| h :: t => do
|
||||
let hType ← whnfD (← inferType h)
|
||||
trace[omega] "Case splitting on {hType}"
|
||||
let_expr Or hType₁ hType₂ := hType | throwError "Unexpected disjunction {hType}"
|
||||
let p?₁ ← withoutModifyingState do withLocalDeclD `h₁ hType₁ fun h₁ => do
|
||||
withTraceNode `omega (msg := fun _ => do pure m!"Assuming fact:{indentExpr hType₁}") do
|
||||
let m₁ := { m with facts := [h₁], disjunctions := t }
|
||||
let (m₁, n) ← m₁.processFacts
|
||||
if 0 < n then
|
||||
omegaImpl m₁ g₁
|
||||
pure true
|
||||
let p₁ ← omegaImpl m₁
|
||||
let p₁ ← mkLambdaFVars #[h₁] p₁
|
||||
return some p₁
|
||||
else
|
||||
pure false
|
||||
if r then
|
||||
trace[omega] "Adding facts:\n{← g₂.withContext <| inferType (.fvar h₂)}"
|
||||
let m₂ := { m with facts := [.fvar h₂], disjunctions := t }
|
||||
omegaImpl m₂ g₂
|
||||
return none
|
||||
if let some p₁ := p?₁ then
|
||||
withLocalDeclD `h₂ hType₂ fun h₂ => do
|
||||
withTraceNode `omega (msg := fun _ => do pure m!"Assuming fact:{indentExpr hType₂}") do
|
||||
let m₂ := { m with facts := [h₂], disjunctions := t }
|
||||
let p₂ ← omegaImpl m₂
|
||||
let p₂ ← mkLambdaFVars #[h₂] p₂
|
||||
return mkApp6 (mkConst ``Or.elim) hType₁ hType₂ (mkConst ``False) h p₁ p₂
|
||||
else
|
||||
trace[omega] "No new facts found."
|
||||
setMCtx ctx
|
||||
splitDisjunction { m with disjunctions := t } g
|
||||
splitDisjunction { m with disjunctions := t }
|
||||
|
||||
/-- Implementation of the `omega` algorithm, and handling disjunctions. -/
|
||||
partial def omegaImpl (m : MetaProblem) (g : MVarId) : OmegaM Unit := g.withContext do
|
||||
partial def omegaImpl (m : MetaProblem) : OmegaM Expr := do
|
||||
let (m, _) ← m.processFacts
|
||||
guard m.facts.isEmpty
|
||||
let p := m.problem
|
||||
@@ -663,12 +648,12 @@ partial def omegaImpl (m : MetaProblem) (g : MVarId) : OmegaM Unit := g.withCont
|
||||
trace[omega] "After elimination:\nAtoms: {← atomsList}\n{p'}"
|
||||
match p'.possible, p'.proveFalse?, p'.proveFalse?_spec with
|
||||
| true, _, _ =>
|
||||
splitDisjunction m g
|
||||
splitDisjunction m
|
||||
| false, .some prf, _ =>
|
||||
trace[omega] "Justification:\n{p'.explanation?.get}"
|
||||
let prf ← instantiateMVars (← prf)
|
||||
trace[omega] "omega found a contradiction, proving {← inferType prf}"
|
||||
g.assign prf
|
||||
return prf
|
||||
|
||||
end
|
||||
|
||||
@@ -677,7 +662,9 @@ Given a collection of facts, try prove `False` using the omega algorithm,
|
||||
and close the goal using that.
|
||||
-/
|
||||
def omega (facts : List Expr) (g : MVarId) (cfg : OmegaConfig := {}) : MetaM Unit :=
|
||||
OmegaM.run (omegaImpl { facts } g) cfg
|
||||
g.withContext do
|
||||
let prf ← OmegaM.run (omegaImpl { facts }) cfg
|
||||
g.assign prf
|
||||
|
||||
open Lean Elab Tactic Parser.Tactic
|
||||
|
||||
|
||||
@@ -526,7 +526,7 @@ where
|
||||
/-- Runs `rintroContinue` on `pats[i:]` -/
|
||||
loop i g fs clears a := do
|
||||
if h : i < pats.size then
|
||||
rintroCore g fs clears a ref (pats.get ⟨i, h⟩) ty? (loop (i+1))
|
||||
rintroCore g fs clears a ref pats[i] ty? (loop (i+1))
|
||||
else cont g fs clears a
|
||||
|
||||
end
|
||||
|
||||
@@ -234,7 +234,7 @@ def elabSimpArgs (stx : Syntax) (ctx : Simp.Context) (simprocs : Simp.SimprocsAr
|
||||
logException ex
|
||||
else
|
||||
throw ex
|
||||
return { ctx := { ctx with simpTheorems := thmsArray.set! 0 thms }, simprocs, starArg }
|
||||
return { ctx := ctx.setSimpTheorems (thmsArray.set! 0 thms), simprocs, starArg }
|
||||
-- If recovery is disabled, then we want simp argument elaboration failures to be exceptions.
|
||||
-- This affects `addSimpTheorem`.
|
||||
if (← read).recover then
|
||||
@@ -311,10 +311,11 @@ def mkSimpContext (stx : Syntax) (eraseLocal : Bool) (kind := SimpKind.simp)
|
||||
simpTheorems
|
||||
let simprocs ← if simpOnly then pure {} else Simp.getSimprocs
|
||||
let congrTheorems ← getSimpCongrTheorems
|
||||
let r ← elabSimpArgs stx[4] (eraseLocal := eraseLocal) (kind := kind) (simprocs := #[simprocs]) {
|
||||
config := (← elabSimpConfig stx[1] (kind := kind))
|
||||
simpTheorems := #[simpTheorems], congrTheorems
|
||||
}
|
||||
let ctx ← Simp.mkContext
|
||||
(config := (← elabSimpConfig stx[1] (kind := kind)))
|
||||
(simpTheorems := #[simpTheorems])
|
||||
congrTheorems
|
||||
let r ← elabSimpArgs stx[4] (eraseLocal := eraseLocal) (kind := kind) (simprocs := #[simprocs]) ctx
|
||||
if !r.starArg || ignoreStarArg then
|
||||
return { r with dischargeWrapper }
|
||||
else
|
||||
@@ -329,7 +330,7 @@ def mkSimpContext (stx : Syntax) (eraseLocal : Bool) (kind := SimpKind.simp)
|
||||
for h in hs do
|
||||
unless simpTheorems.isErased (.fvar h) do
|
||||
simpTheorems ← simpTheorems.addTheorem (.fvar h) (← h.getDecl).toExpr
|
||||
let ctx := { ctx with simpTheorems }
|
||||
let ctx := ctx.setSimpTheorems simpTheorems
|
||||
return { ctx, simprocs, dischargeWrapper }
|
||||
|
||||
register_builtin_option tactic.simp.trace : Bool := {
|
||||
|
||||
@@ -36,9 +36,9 @@ deriving instance Repr for UseImplicitLambdaResult
|
||||
let stx ← `(tactic| simp $cfg:optConfig $(disch)? $[only%$only]? $[[$args,*]]?)
|
||||
let { ctx, simprocs, dischargeWrapper } ←
|
||||
withMainContext <| mkSimpContext stx (eraseLocal := false)
|
||||
let ctx := if unfold.isSome then { ctx with config.autoUnfold := true } else ctx
|
||||
let ctx := if unfold.isSome then ctx.setAutoUnfold else ctx
|
||||
-- TODO: have `simpa` fail if it doesn't use `simp`.
|
||||
let ctx := { ctx with config := { ctx.config with failIfUnchanged := false } }
|
||||
let ctx := ctx.setFailIfUnchanged false
|
||||
dischargeWrapper.with fun discharge? => do
|
||||
let (some (_, g), stats) ← simpGoal (← getMainGoal) ctx (simprocs := simprocs)
|
||||
(simplifyTarget := true) (discharge? := discharge?)
|
||||
|
||||
@@ -1339,6 +1339,21 @@ def withInfoContext' (stx : Syntax) (x : TermElabM Expr) (mkInfo : Expr → Term
|
||||
else
|
||||
Elab.withInfoContext' x mkInfo
|
||||
|
||||
/-- Info node capturing `def/let rec` bodies, used by the unused variables linter. -/
|
||||
structure BodyInfo where
|
||||
/-- The body as a fully elaborated term. -/
|
||||
value : Expr
|
||||
deriving TypeName
|
||||
|
||||
/-- Creates an `Info.ofCustomInfo` node backed by a `BodyInfo`. -/
|
||||
def mkBodyInfo (stx : Syntax) (value : Expr) : Info :=
|
||||
.ofCustomInfo { stx, value := .mk { value : BodyInfo } }
|
||||
|
||||
/-- Extracts a `BodyInfo` custom info. -/
|
||||
def getBodyInfo? : Info → Option BodyInfo
|
||||
| .ofCustomInfo { value, .. } => value.get? BodyInfo
|
||||
| _ => none
|
||||
|
||||
/--
|
||||
Postpone the elaboration of `stx`, return a metavariable that acts as a placeholder, and
|
||||
ensures the info tree is updated and a hole id is introduced.
|
||||
|
||||
@@ -345,7 +345,7 @@ unsafe def setState {σ} (ext : Ext σ) (exts : Array EnvExtensionState) (s : σ
|
||||
|
||||
unsafe def getState {σ} [Inhabited σ] (ext : Ext σ) (exts : Array EnvExtensionState) : σ :=
|
||||
if h : ext.idx < exts.size then
|
||||
let s : EnvExtensionState := exts.get ⟨ext.idx, h⟩
|
||||
let s : EnvExtensionState := exts[ext.idx]
|
||||
unsafeCast s
|
||||
else
|
||||
panic! invalidExtMsg
|
||||
|
||||
@@ -37,7 +37,7 @@ def InternalExceptionId.getName (id : InternalExceptionId) : IO Name := do
|
||||
let exs ← internalExceptionsRef.get
|
||||
let i := id.idx;
|
||||
if h : i < exs.size then
|
||||
return exs.get ⟨i, h⟩
|
||||
return exs[i]
|
||||
else
|
||||
throw <| IO.userError "invalid internal exception id"
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ private def accMax (result : Level) (prev : Level) (offset : Nat) : Level :=
|
||||
-/
|
||||
private partial def mkMaxAux (lvls : Array Level) (extraK : Nat) (i : Nat) (prev : Level) (prevK : Nat) (result : Level) : Level :=
|
||||
if h : i < lvls.size then
|
||||
let lvl := lvls.get ⟨i, h⟩
|
||||
let lvl := lvls[i]
|
||||
let curr := lvl.getLevelOffset
|
||||
let currK := lvl.getOffset
|
||||
if curr == prev then
|
||||
@@ -335,7 +335,7 @@ private partial def mkMaxAux (lvls : Array Level) (extraK : Nat) (i : Nat) (prev
|
||||
It finds the first position that is not an explicit universe. -/
|
||||
private partial def skipExplicit (lvls : Array Level) (i : Nat) : Nat :=
|
||||
if h : i < lvls.size then
|
||||
let lvl := lvls.get ⟨i, h⟩
|
||||
let lvl := lvls[i]
|
||||
if lvl.getLevelOffset.isZero then skipExplicit lvls (i+1) else i
|
||||
else
|
||||
i
|
||||
@@ -349,7 +349,7 @@ It assumes `lvls` has been sorted using `normLt`.
|
||||
-/
|
||||
private partial def isExplicitSubsumedAux (lvls : Array Level) (maxExplicit : Nat) (i : Nat) : Bool :=
|
||||
if h : i < lvls.size then
|
||||
let lvl := lvls.get ⟨i, h⟩
|
||||
let lvl := lvls[i]
|
||||
if lvl.getOffset ≥ maxExplicit then true
|
||||
else isExplicitSubsumedAux lvls maxExplicit (i+1)
|
||||
else
|
||||
|
||||
@@ -18,7 +18,7 @@ It is not immediately obvious but this is a surprisingly expensive check without
|
||||
some optimizations. The main complication is that it can be difficult to
|
||||
determine what constitutes a "use" apart from direct references to a variable
|
||||
that we can easily find in the info tree. For example, we would like this to be
|
||||
considered a use of `x`:
|
||||
considered a use of `x`:
|
||||
```
|
||||
def foo (x : Nat) : Nat := by assumption
|
||||
```
|
||||
@@ -390,22 +390,20 @@ where
|
||||
-- set if `analyzeTactics` is unset, tactic infos are present, and we're inside the body
|
||||
let ignored ← read
|
||||
match info with
|
||||
| .ofCustomInfo ti =>
|
||||
if !linter.unusedVariables.analyzeTactics.get ci.options then
|
||||
if let some bodyInfo := ti.value.get? Elab.Term.BodyInfo then
|
||||
-- the body is the only `Expr` we will analyze in this case
|
||||
-- NOTE: we include it even if no tactics are present as at least for parameters we want
|
||||
-- to lint only truly unused binders
|
||||
let (e, _) := instantiateMVarsCore ci.mctx bodyInfo.value
|
||||
modify fun s => { s with
|
||||
assignments := s.assignments.push (.insert {} ⟨.anonymous⟩ e) }
|
||||
let tacticsPresent := children.any (·.findInfo? (· matches .ofTacticInfo ..) |>.isSome)
|
||||
withReader (· || tacticsPresent) do
|
||||
go children.toArray ci
|
||||
return false
|
||||
| .ofTermInfo ti =>
|
||||
-- NOTE: we have to do this check *before* `ignored` because nested bodies (e.g. from
|
||||
-- nested `let rec`s) do need to be included to find all `Expr` uses of the top-level
|
||||
-- parameters
|
||||
if ti.elaborator == `MutualDef.body &&
|
||||
!linter.unusedVariables.analyzeTactics.get ci.options then
|
||||
-- the body is the only `Expr` we will analyze in this case
|
||||
-- NOTE: we include it even if no tactics are present as at least for parameters we want
|
||||
-- to lint only truly unused binders
|
||||
let (e, _) := instantiateMVarsCore ci.mctx ti.expr
|
||||
modify fun s => { s with
|
||||
assignments := s.assignments.push (.insert {} ⟨.anonymous⟩ e) }
|
||||
let tacticsPresent := children.any (·.findInfo? (· matches .ofTacticInfo ..) |>.isSome)
|
||||
withReader (· || tacticsPresent) do
|
||||
go children.toArray ci
|
||||
return false
|
||||
if ignored then return true
|
||||
match ti.expr with
|
||||
| .const .. =>
|
||||
@@ -441,22 +439,20 @@ where
|
||||
-- Found a direct use, keep track of it
|
||||
modify fun s => { s with fvarUses := s.fvarUses.insert id }
|
||||
| _ => pure ()
|
||||
return true
|
||||
| .ofTacticInfo ti =>
|
||||
-- When ignoring new binders, no need to look at intermediate tactic states either as
|
||||
-- references to binders outside the body will be covered by the body `Expr`
|
||||
if ignored then return true
|
||||
-- Keep track of the `MetavarContext` after a tactic for later
|
||||
modify fun s => { s with assignments := s.assignments.push ti.mctxAfter.eAssignment }
|
||||
return true
|
||||
| .ofFVarAliasInfo i =>
|
||||
if ignored then return true
|
||||
-- record any aliases we find
|
||||
modify fun s =>
|
||||
let id := followAliases s.fvarAliases i.baseId
|
||||
{ s with fvarAliases := s.fvarAliases.insert i.id id }
|
||||
return true
|
||||
| _ => return true)
|
||||
| _ => pure ()
|
||||
return true)
|
||||
/-- Since declarations attach the declaration info to the `declId`,
|
||||
we skip that to get to the `.ident` if possible. -/
|
||||
skipDeclIdIfPresent (stx : Syntax) : Syntax :=
|
||||
|
||||
@@ -116,7 +116,7 @@ variable (p : Name → Bool) in
|
||||
/-- Returns true when the message contains a `MessageData.tagged tag ..` constructor where `p tag`
|
||||
is true.
|
||||
|
||||
This does not descend into lazily generated subtress (`.ofLazy`); message tags
|
||||
This does not descend into lazily generated subtrees (`.ofLazy`); message tags
|
||||
of interest (like those added by `logLinter`) are expected to be near the root
|
||||
of the `MessageData`, and not hidden inside `.ofLazy`.
|
||||
-/
|
||||
@@ -130,6 +130,19 @@ partial def hasTag : MessageData → Bool
|
||||
| trace data msg msgs => p data.cls || hasTag msg || msgs.any hasTag
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Returns the top-level tag of the message.
|
||||
If none, returns `Name.anonymous`.
|
||||
|
||||
This does not descend into message subtrees (e.g., `.compose`, `.ofLazy`).
|
||||
The message kind is expected to describe the whole message.
|
||||
-/
|
||||
def kind : MessageData → Name
|
||||
| withContext _ msg => kind msg
|
||||
| withNamingContext _ msg => kind msg
|
||||
| tagged n _ => n
|
||||
| _ => .anonymous
|
||||
|
||||
/-- An empty message. -/
|
||||
def nil : MessageData :=
|
||||
ofFormat Format.nil
|
||||
@@ -250,7 +263,7 @@ instance : Coe (Option Expr) MessageData := ⟨fun o => match o with | none => "
|
||||
|
||||
partial def arrayExpr.toMessageData (es : Array Expr) (i : Nat) (acc : MessageData) : MessageData :=
|
||||
if h : i < es.size then
|
||||
let e := es.get ⟨i, h⟩;
|
||||
let e := es[i];
|
||||
let acc := if i == 0 then acc ++ ofExpr e else acc ++ ", " ++ ofExpr e;
|
||||
toMessageData es (i+1) acc
|
||||
else
|
||||
@@ -315,7 +328,7 @@ structure BaseMessage (α : Type u) where
|
||||
endPos : Option Position := none
|
||||
/-- If `true`, report range as given; see `msgToInteractiveDiagnostic`. -/
|
||||
keepFullRange : Bool := false
|
||||
severity : MessageSeverity := MessageSeverity.error
|
||||
severity : MessageSeverity := .error
|
||||
caption : String := ""
|
||||
/-- The content of the message. -/
|
||||
data : α
|
||||
@@ -328,7 +341,10 @@ abbrev Message := BaseMessage MessageData
|
||||
/-- A `SerialMessage` is a `Message` whose `MessageData` has been eagerly
|
||||
serialized and is thus appropriate for use in pure contexts where the effectful
|
||||
`MessageData.toString` cannot be used. -/
|
||||
abbrev SerialMessage := BaseMessage String
|
||||
structure SerialMessage extends BaseMessage String where
|
||||
/-- The message kind (i.e., the top-level tag). -/
|
||||
kind : Name
|
||||
deriving ToJson, FromJson
|
||||
|
||||
namespace SerialMessage
|
||||
|
||||
@@ -354,8 +370,12 @@ end SerialMessage
|
||||
|
||||
namespace Message
|
||||
|
||||
@[inherit_doc MessageData.kind] abbrev kind (msg : Message) :=
|
||||
msg.data.kind
|
||||
|
||||
/-- Serializes the message, converting its data into a string and saving its kind. -/
|
||||
@[inline] def serialize (msg : Message) : IO SerialMessage := do
|
||||
return {msg with data := ← msg.data.toString}
|
||||
return {msg with kind := msg.kind, data := ← msg.data.toString}
|
||||
|
||||
protected def toString (msg : Message) (includeEndPos := false) : IO String := do
|
||||
-- Remark: The inline here avoids a new message allocation when `msg` is shared
|
||||
|
||||
@@ -352,7 +352,7 @@ private partial def mkAppOptMAux (f : Expr) (xs : Array (Option Expr)) : Nat →
|
||||
| i, args, j, instMVars, Expr.forallE n d b bi => do
|
||||
let d := d.instantiateRevRange j args.size args
|
||||
if h : i < xs.size then
|
||||
match xs.get ⟨i, h⟩ with
|
||||
match xs[i] with
|
||||
| none =>
|
||||
match bi with
|
||||
| BinderInfo.instImplicit => do
|
||||
|
||||
@@ -332,7 +332,7 @@ register_builtin_option maxSynthPendingDepth : Nat := {
|
||||
Contextual information for the `MetaM` monad.
|
||||
-/
|
||||
structure Context where
|
||||
config : Config := {}
|
||||
private config : Config := {}
|
||||
/-- Local context -/
|
||||
lctx : LocalContext := {}
|
||||
/-- Local instances in `lctx`. -/
|
||||
@@ -943,6 +943,15 @@ def elimMVarDeps (xs : Array Expr) (e : Expr) (preserveOrder : Bool := false) :
|
||||
@[inline] def withConfig (f : Config → Config) : n α → n α :=
|
||||
mapMetaM <| withReader (fun ctx => { ctx with config := f ctx.config })
|
||||
|
||||
@[inline] def withCanUnfoldPred (p : Config → ConstantInfo → CoreM Bool) : n α → n α :=
|
||||
mapMetaM <| withReader (fun ctx => { ctx with canUnfold? := p })
|
||||
|
||||
@[inline] def withIncSynthPending : n α → n α :=
|
||||
mapMetaM <| withReader (fun ctx => { ctx with synthPendingDepth := ctx.synthPendingDepth + 1 })
|
||||
|
||||
@[inline] def withInTypeClassResolution : n α → n α :=
|
||||
mapMetaM <| withReader (fun ctx => { ctx with inTypeClassResolution := true })
|
||||
|
||||
/--
|
||||
Executes `x` tracking zetaDelta reductions `Config.trackZetaDelta := true`
|
||||
-/
|
||||
@@ -1081,7 +1090,7 @@ mutual
|
||||
private partial def withNewLocalInstancesImp
|
||||
(fvars : Array Expr) (i : Nat) (k : MetaM α) : MetaM α := do
|
||||
if h : i < fvars.size then
|
||||
let fvar := fvars.get ⟨i, h⟩
|
||||
let fvar := fvars[i]
|
||||
let decl ← getFVarLocalDecl fvar
|
||||
match (← isClassQuick? decl.type) with
|
||||
| .none => withNewLocalInstancesImp fvars (i+1) k
|
||||
@@ -1422,6 +1431,14 @@ def withLocalDecl (name : Name) (bi : BinderInfo) (type : Expr) (k : Expr → n
|
||||
def withLocalDeclD (name : Name) (type : Expr) (k : Expr → n α) : n α :=
|
||||
withLocalDecl name BinderInfo.default type k
|
||||
|
||||
/--
|
||||
Similar to `withLocalDecl`, but it does **not** check whether the new variable is a local instance or not.
|
||||
-/
|
||||
def withLocalDeclNoLocalInstanceUpdate (name : Name) (bi : BinderInfo) (type : Expr) (x : Expr → MetaM α) : MetaM α := do
|
||||
let fvarId ← mkFreshFVarId
|
||||
withReader (fun ctx => { ctx with lctx := ctx.lctx.mkLocalDecl fvarId name type bi }) do
|
||||
x (mkFVar fvarId)
|
||||
|
||||
/-- Append an array of free variables `xs` to the local context and execute `k xs`.
|
||||
`declInfos` takes the form of an array consisting of:
|
||||
- the name of the variable
|
||||
@@ -1538,11 +1555,11 @@ def withReplaceFVarId {α} (fvarId : FVarId) (e : Expr) : MetaM α → MetaM α
|
||||
localInstances := ctx.localInstances.erase fvarId }
|
||||
|
||||
/--
|
||||
`withNewMCtxDepth k` executes `k` with a higher metavariable context depth,
|
||||
where metavariables created outside the `withNewMCtxDepth` (with a lower depth) cannot be assigned.
|
||||
If `allowLevelAssignments` is set to true, then the level metavariable depth
|
||||
is not increased, and level metavariables from the outer scope can be
|
||||
assigned. (This is used by TC synthesis.)
|
||||
`withNewMCtxDepth k` executes `k` with a higher metavariable context depth,
|
||||
where metavariables created outside the `withNewMCtxDepth` (with a lower depth) cannot be assigned.
|
||||
If `allowLevelAssignments` is set to true, then the level metavariable depth
|
||||
is not increased, and level metavariables from the outer scope can be
|
||||
assigned. (This is used by TC synthesis.)
|
||||
-/
|
||||
def withNewMCtxDepth (k : n α) (allowLevelAssignments := false) : n α :=
|
||||
mapMetaM (withNewMCtxDepthImp allowLevelAssignments) k
|
||||
@@ -1552,13 +1569,20 @@ private def withLocalContextImp (lctx : LocalContext) (localInsts : LocalInstanc
|
||||
x
|
||||
|
||||
/--
|
||||
`withLCtx lctx localInsts k` replaces the local context and local instances, and then executes `k`.
|
||||
The local context and instances are restored after executing `k`.
|
||||
This method assumes that the local instances in `localInsts` are in the local context `lctx`.
|
||||
`withLCtx lctx localInsts k` replaces the local context and local instances, and then executes `k`.
|
||||
The local context and instances are restored after executing `k`.
|
||||
This method assumes that the local instances in `localInsts` are in the local context `lctx`.
|
||||
-/
|
||||
def withLCtx (lctx : LocalContext) (localInsts : LocalInstances) : n α → n α :=
|
||||
mapMetaM <| withLocalContextImp lctx localInsts
|
||||
|
||||
/--
|
||||
Simpler version of `withLCtx` which just updates the local context. It is the resposability of the
|
||||
caller ensure the local instances are also properly updated.
|
||||
-/
|
||||
def withLCtx' (lctx : LocalContext) : n α → n α :=
|
||||
mapMetaM <| withReader (fun ctx => { ctx with lctx })
|
||||
|
||||
/--
|
||||
Runs `k` in a local environment with the `fvarIds` erased.
|
||||
-/
|
||||
@@ -1650,7 +1674,7 @@ def setInlineAttribute (declName : Name) (kind := Compiler.InlineAttributeKind.i
|
||||
|
||||
private partial def instantiateForallAux (ps : Array Expr) (i : Nat) (e : Expr) : MetaM Expr := do
|
||||
if h : i < ps.size then
|
||||
let p := ps.get ⟨i, h⟩
|
||||
let p := ps[i]
|
||||
match (← whnf e) with
|
||||
| .forallE _ _ b _ => instantiateForallAux ps (i+1) (b.instantiate1 p)
|
||||
| _ => throwError "invalid instantiateForall, too many parameters"
|
||||
@@ -1663,7 +1687,7 @@ def instantiateForall (e : Expr) (ps : Array Expr) : MetaM Expr :=
|
||||
|
||||
private partial def instantiateLambdaAux (ps : Array Expr) (i : Nat) (e : Expr) : MetaM Expr := do
|
||||
if h : i < ps.size then
|
||||
let p := ps.get ⟨i, h⟩
|
||||
let p := ps[i]
|
||||
match (← whnf e) with
|
||||
| .lam _ _ b _ => instantiateLambdaAux ps (i+1) (b.instantiate1 p)
|
||||
| _ => throwError "invalid instantiateLambda, too many parameters"
|
||||
|
||||
@@ -224,7 +224,7 @@ def collectExpr (e : Expr) : ClosureM Expr := do
|
||||
partial def pickNextToProcessAux (lctx : LocalContext) (i : Nat) (toProcess : Array ToProcessElement) (elem : ToProcessElement)
|
||||
: ToProcessElement × Array ToProcessElement :=
|
||||
if h : i < toProcess.size then
|
||||
let elem' := toProcess.get ⟨i, h⟩
|
||||
let elem' := toProcess[i]
|
||||
if (lctx.get! elem.fvarId).index < (lctx.get! elem'.fvarId).index then
|
||||
pickNextToProcessAux lctx (i+1) (toProcess.set i elem) elem'
|
||||
else
|
||||
|
||||
@@ -157,9 +157,11 @@ def coerceMonadLift? (e expectedType : Expr) : MetaM (Option Expr) := do
|
||||
let eType ← instantiateMVars (← inferType e)
|
||||
let some (n, β) ← isTypeApp? expectedType | return none
|
||||
let some (m, α) ← isTypeApp? eType | return none
|
||||
-- Need to save and restore the state in case `m` and `n` are defeq but not monads to prevent this procedure from having side effects.
|
||||
let saved ← saveState
|
||||
if (← isDefEq m n) then
|
||||
let some monadInst ← isMonad? n | return none
|
||||
try expandCoe (← mkAppOptM ``Lean.Internal.coeM #[m, α, β, none, monadInst, e]) catch _ => return none
|
||||
let some monadInst ← isMonad? n | restoreState saved; return none
|
||||
try expandCoe (← mkAppOptM ``Lean.Internal.coeM #[m, α, β, none, monadInst, e]) catch _ => restoreState saved; return none
|
||||
else if autoLift.get (← getOptions) then
|
||||
try
|
||||
-- Construct lift from `m` to `n`
|
||||
|
||||
@@ -29,7 +29,7 @@ where
|
||||
let s ← getThe CollectFVars.State
|
||||
let i ← get
|
||||
if h : i < s.fvarIds.size then
|
||||
let r := s.fvarIds.get ⟨i, h⟩
|
||||
let r := s.fvarIds[i]
|
||||
modify (· + 1)
|
||||
return some r
|
||||
else
|
||||
|
||||
@@ -202,7 +202,7 @@ instance : Inhabited (DiscrTree α) where
|
||||
-/
|
||||
private def ignoreArg (a : Expr) (i : Nat) (infos : Array ParamInfo) : MetaM Bool := do
|
||||
if h : i < infos.size then
|
||||
let info := infos.get ⟨i, h⟩
|
||||
let info := infos[i]
|
||||
if info.isInstImplicit then
|
||||
return true
|
||||
else if info.isImplicit || info.isStrictImplicit then
|
||||
@@ -442,7 +442,7 @@ def mkPath (e : Expr) (config : WhnfCoreConfig) (noIndexAtArgs := false) : MetaM
|
||||
|
||||
private partial def createNodes (keys : Array Key) (v : α) (i : Nat) : Trie α :=
|
||||
if h : i < keys.size then
|
||||
let k := keys.get ⟨i, h⟩
|
||||
let k := keys[i]
|
||||
let c := createNodes keys v (i+1)
|
||||
.node #[] #[(k, c)]
|
||||
else
|
||||
@@ -470,7 +470,7 @@ where
|
||||
private partial def insertAux [BEq α] (keys : Array Key) (v : α) : Nat → Trie α → Trie α
|
||||
| i, .node vs cs =>
|
||||
if h : i < keys.size then
|
||||
let k := keys.get ⟨i, h⟩
|
||||
let k := keys[i]
|
||||
let c := Id.run $ cs.binInsertM
|
||||
(fun a b => a.1 < b.1)
|
||||
(fun ⟨_, s⟩ => let c := insertAux keys v (i+1) s; (k, c)) -- merge with existing
|
||||
@@ -553,8 +553,8 @@ private def getKeyArgs (e : Expr) (isMatch root : Bool) (config : WhnfCoreConfig
|
||||
if isMatch then
|
||||
return (.other, #[])
|
||||
else do
|
||||
let ctx ← read
|
||||
if ctx.config.isDefEqStuckEx then
|
||||
let cfg ← getConfig
|
||||
if cfg.isDefEqStuckEx then
|
||||
/-
|
||||
When the configuration flag `isDefEqStuckEx` is set to true,
|
||||
we want `isDefEq` to throw an exception whenever it tries to assign
|
||||
|
||||
@@ -332,7 +332,7 @@ private partial def isDefEqArgs (f : Expr) (args₁ args₂ : Array Expr) : Meta
|
||||
@[specialize] partial def isDefEqBindingDomain (fvars : Array Expr) (ds₂ : Array Expr) (k : MetaM Bool) : MetaM Bool :=
|
||||
let rec loop (i : Nat) := do
|
||||
if h : i < fvars.size then do
|
||||
let fvar := fvars.get ⟨i, h⟩
|
||||
let fvar := fvars[i]
|
||||
let fvarDecl ← getFVarLocalDecl fvar
|
||||
let fvarType := fvarDecl.type
|
||||
let d₂ := ds₂[i]!
|
||||
@@ -364,7 +364,7 @@ private partial def isDefEqBindingAux (lctx : LocalContext) (fvars : Array Expr)
|
||||
| Expr.forallE n d₁ b₁ _, Expr.forallE _ d₂ b₂ _ => process n d₁ d₂ b₁ b₂
|
||||
| Expr.lam n d₁ b₁ _, Expr.lam _ d₂ b₂ _ => process n d₁ d₂ b₁ b₂
|
||||
| _, _ =>
|
||||
withReader (fun ctx => { ctx with lctx := lctx }) do
|
||||
withLCtx' lctx do
|
||||
isDefEqBindingDomain fvars ds₂ do
|
||||
Meta.isExprDefEqAux (e₁.instantiateRev fvars) (e₂.instantiateRev fvars)
|
||||
|
||||
@@ -758,8 +758,8 @@ mutual
|
||||
if mvarDecl.depth != (← getMCtx).depth || mvarDecl.kind.isSyntheticOpaque then
|
||||
traceM `Meta.isDefEq.assign.readOnlyMVarWithBiggerLCtx <| addAssignmentInfo (mkMVar mvarId)
|
||||
throwCheckAssignmentFailure
|
||||
let ctxMeta ← readThe Meta.Context
|
||||
unless ctxMeta.config.ctxApprox && ctx.mvarDecl.lctx.isSubPrefixOf mvarDecl.lctx do
|
||||
let cfg ← getConfig
|
||||
unless cfg.ctxApprox && ctx.mvarDecl.lctx.isSubPrefixOf mvarDecl.lctx do
|
||||
traceM `Meta.isDefEq.assign.readOnlyMVarWithBiggerLCtx <| addAssignmentInfo (mkMVar mvarId)
|
||||
throwCheckAssignmentFailure
|
||||
/- Create an auxiliary metavariable with a smaller context and "checked" type.
|
||||
@@ -814,8 +814,8 @@ mutual
|
||||
|
||||
partial def checkApp (e : Expr) : CheckAssignmentM Expr :=
|
||||
e.withApp fun f args => do
|
||||
let ctxMeta ← readThe Meta.Context
|
||||
if f.isMVar && ctxMeta.config.ctxApprox && args.all Expr.isFVar then
|
||||
let cfg ← getConfig
|
||||
if f.isMVar && cfg.ctxApprox && args.all Expr.isFVar then
|
||||
let f ← check f
|
||||
catchInternalId outOfScopeExceptionId
|
||||
(do
|
||||
@@ -1203,7 +1203,7 @@ private partial def processAssignment (mvarApp : Expr) (v : Expr) : MetaM Bool :
|
||||
let useFOApprox (args : Array Expr) : MetaM Bool :=
|
||||
processAssignmentFOApprox mvar args v <||> processConstApprox mvar args i v
|
||||
if h : i < args.size then
|
||||
let arg := args.get ⟨i, h⟩
|
||||
let arg := args[i]
|
||||
let arg ← simpAssignmentArg arg
|
||||
let args := args.set i arg
|
||||
match arg with
|
||||
@@ -1794,8 +1794,8 @@ private partial def isDefEqQuickOther (t s : Expr) : MetaM LBool := do
|
||||
| LBool.true => return LBool.true
|
||||
| LBool.false => return LBool.false
|
||||
| _ =>
|
||||
let ctx ← read
|
||||
if ctx.config.isDefEqStuckEx then do
|
||||
let cfg ← getConfig
|
||||
if cfg.isDefEqStuckEx then do
|
||||
trace[Meta.isDefEq.stuck] "{t} =?= {s}"
|
||||
Meta.throwIsDefEqStuck
|
||||
else
|
||||
@@ -1834,7 +1834,7 @@ end
|
||||
let e ← instantiateMVars e
|
||||
successK e
|
||||
else
|
||||
if (← read).config.isDefEqStuckEx then
|
||||
if (← getConfig).isDefEqStuckEx then
|
||||
/-
|
||||
When `isDefEqStuckEx := true` and `mvar` was created in a previous level,
|
||||
we should throw an exception. See issue #2736 for a situation where this can happen.
|
||||
|
||||
@@ -17,7 +17,7 @@ structure Entry where
|
||||
|
||||
partial def updateTypes (e eNew : Expr) (entries : Array Entry) (i : Nat) : MetaM (Array Entry) :=
|
||||
if h : i < entries.size then
|
||||
let entry := entries.get ⟨i, h⟩
|
||||
let entry := entries[i]
|
||||
match entry with
|
||||
| ⟨_, type, _⟩ => do
|
||||
let typeAbst ← kabstract type e
|
||||
@@ -38,7 +38,7 @@ partial def generalizeTelescopeAux {α} (k : Array Expr → MetaM α)
|
||||
withLocalDeclD userName type fun x => do
|
||||
let entries ← updateTypes e x entries (i+1)
|
||||
generalizeTelescopeAux k entries (i+1) (fvars.push x)
|
||||
match entries.get ⟨i, h⟩ with
|
||||
match entries[i] with
|
||||
| ⟨e@(.fvar fvarId), type, false⟩ =>
|
||||
let localDecl ← fvarId.getDecl
|
||||
match localDecl with
|
||||
|
||||
@@ -22,10 +22,11 @@ private def canUnfoldDefault (cfg : Config) (info : ConstantInfo) : CoreM Bool :
|
||||
|
||||
def canUnfold (info : ConstantInfo) : MetaM Bool := do
|
||||
let ctx ← read
|
||||
let cfg ← getConfig
|
||||
if let some f := ctx.canUnfold? then
|
||||
f ctx.config info
|
||||
f cfg info
|
||||
else
|
||||
canUnfoldDefault ctx.config info
|
||||
canUnfoldDefault cfg info
|
||||
|
||||
/--
|
||||
Look up a constant name, returning the `ConstantInfo`
|
||||
|
||||
@@ -382,11 +382,6 @@ def isType (e : Expr) : MetaM Bool := do
|
||||
| .sort .. => return true
|
||||
| _ => return false
|
||||
|
||||
@[inline] private def withLocalDecl' {α} (name : Name) (bi : BinderInfo) (type : Expr) (x : Expr → MetaM α) : MetaM α := do
|
||||
let fvarId ← mkFreshFVarId
|
||||
withReader (fun ctx => { ctx with lctx := ctx.lctx.mkLocalDecl fvarId name type bi }) do
|
||||
x (mkFVar fvarId)
|
||||
|
||||
def typeFormerTypeLevelQuick : Expr → Option Level
|
||||
| .forallE _ _ b _ => typeFormerTypeLevelQuick b
|
||||
| .sort l => some l
|
||||
@@ -403,7 +398,7 @@ where
|
||||
go (type : Expr) (xs : Array Expr) : MetaM (Option Level) := do
|
||||
match type with
|
||||
| .sort l => return some l
|
||||
| .forallE n d b c => withLocalDecl' n c (d.instantiateRev xs) fun x => go b (xs.push x)
|
||||
| .forallE n d b c => withLocalDeclNoLocalInstanceUpdate n c (d.instantiateRev xs) fun x => go b (xs.push x)
|
||||
| _ =>
|
||||
let type ← whnfD (type.instantiateRev xs)
|
||||
match type with
|
||||
|
||||
@@ -86,7 +86,7 @@ private partial def mkInjectiveTheoremTypeCore? (ctorVal : ConstructorVal) (useE
|
||||
if h : i < args1.size then
|
||||
match (← whnf type) with
|
||||
| Expr.forallE n d b _ =>
|
||||
let arg1 := args1.get ⟨i, h⟩
|
||||
let arg1 := args1[i]
|
||||
if occursOrInType (← getLCtx) arg1 resultType then
|
||||
mkArgs2 (i + 1) (b.instantiate1 arg1) (args2.push arg1) args2New
|
||||
else
|
||||
|
||||
@@ -73,7 +73,7 @@ private def tmpStar := mkMVar tmpMVarId
|
||||
-/
|
||||
private def ignoreArg (a : Expr) (i : Nat) (infos : Array ParamInfo) : MetaM Bool := do
|
||||
if h : i < infos.size then
|
||||
let info := infos.get ⟨i, h⟩
|
||||
let info := infos[i]
|
||||
if info.isInstImplicit then
|
||||
return true
|
||||
else if info.isImplicit || info.isStrictImplicit then
|
||||
@@ -222,8 +222,8 @@ private def getKeyArgs (e : Expr) (isMatch root : Bool) (config : WhnfCoreConfig
|
||||
if isMatch then
|
||||
return (.other, #[])
|
||||
else do
|
||||
let ctx ← read
|
||||
if ctx.config.isDefEqStuckEx then
|
||||
let cfg ← getConfig
|
||||
if cfg.isDefEqStuckEx then
|
||||
/-
|
||||
When the configuration flag `isDefEqStuckEx` is set to true,
|
||||
we want `isDefEq` to throw an exception whenever it tries to assign
|
||||
|
||||
@@ -149,8 +149,8 @@ mutual
|
||||
if r != LBool.undef then
|
||||
return r == LBool.true
|
||||
else if !(← hasAssignableLevelMVar lhs <||> hasAssignableLevelMVar rhs) then
|
||||
let ctx ← read
|
||||
if ctx.config.isDefEqStuckEx && (lhs.isMVar || rhs.isMVar) then do
|
||||
let cfg ← getConfig
|
||||
if cfg.isDefEqStuckEx && (lhs.isMVar || rhs.isMVar) then do
|
||||
trace[Meta.isLevelDefEq.stuck] "{lhs} =?= {rhs}"
|
||||
Meta.throwIsDefEqStuck
|
||||
else
|
||||
|
||||
@@ -71,7 +71,7 @@ def caseArraySizes (mvarId : MVarId) (fvarId : FVarId) (sizes : Array Nat) (xNam
|
||||
let mvarId := subgoal.mvarId
|
||||
let hEqSz := (subst.get hEq).fvarId!
|
||||
if h : i < sizes.size then
|
||||
let n := sizes.get ⟨i, h⟩
|
||||
let n := sizes[i]
|
||||
let mvarId ← mvarId.clear subgoal.newHs[0]!
|
||||
let mvarId ← mvarId.clear (subst.get aSizeFVarId).fvarId!
|
||||
mvarId.withContext do
|
||||
|
||||
@@ -546,7 +546,7 @@ private def processValue (p : Problem) : MetaM (Array Problem) := do
|
||||
subgoals.mapIdxM fun i subgoal => do
|
||||
trace[Meta.Match.match] "processValue subgoal\n{MessageData.ofGoal subgoal.mvarId}"
|
||||
if h : i < values.size then
|
||||
let value := values.get ⟨i, h⟩
|
||||
let value := values[i]
|
||||
-- (x = value) branch
|
||||
let subst := subgoal.subst
|
||||
trace[Meta.Match.match] "processValue subst: {subst.map.toList.map fun p => mkFVar p.1}, {subst.map.toList.map fun p => p.2}"
|
||||
|
||||
@@ -366,7 +366,7 @@ private partial def withSplitterAlts (altTypes : Array Expr) (f : Array Expr →
|
||||
let rec go (i : Nat) (xs : Array Expr) : MetaM α := do
|
||||
if h : i < altTypes.size then
|
||||
let hName := (`h).appendIndexAfter (i+1)
|
||||
withLocalDeclD hName (altTypes.get ⟨i, h⟩) fun x =>
|
||||
withLocalDeclD hName altTypes[i] fun x =>
|
||||
go (i+1) (xs.push x)
|
||||
else
|
||||
f xs
|
||||
@@ -525,7 +525,7 @@ where
|
||||
let rec go (i : Nat) (motiveTypeArgsNew : Array Expr) : ConvertM Expr := do
|
||||
assert! motiveTypeArgsNew.size == i
|
||||
if h : i < motiveTypeArgs.size then
|
||||
let motiveTypeArg := motiveTypeArgs.get ⟨i, h⟩
|
||||
let motiveTypeArg := motiveTypeArgs[i]
|
||||
if i < isAlt.size && isAlt[i]! then
|
||||
let altNew := argsNew[6+i]! -- Recall that `Eq.ndrec` has 6 arguments
|
||||
let altTypeNew ← inferType altNew
|
||||
@@ -636,8 +636,7 @@ private partial def withNewAlts (numDiscrEqs : Nat) (discrs : Array Expr) (patte
|
||||
where
|
||||
go (i : Nat) (altsNew : Array Expr) : MetaM α := do
|
||||
if h : i < alts.size then
|
||||
let alt := alts.get ⟨i, h⟩
|
||||
let altLocalDecl ← getFVarLocalDecl alt
|
||||
let altLocalDecl ← getFVarLocalDecl alts[i]
|
||||
let typeNew := altLocalDecl.type.replaceFVars discrs patterns
|
||||
withLocalDecl altLocalDecl.userName altLocalDecl.binderInfo typeNew fun altNew =>
|
||||
go (i+1) (altsNew.push altNew)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Lean.Meta.MatcherApp
|
||||
/-- Auxiliary function for MatcherApp.addArg -/
|
||||
private partial def updateAlts (unrefinedArgType : Expr) (typeNew : Expr) (altNumParams : Array Nat) (alts : Array Expr) (refined : Bool) (i : Nat) : MetaM (Array Nat × Array Expr) := do
|
||||
if h : i < alts.size then
|
||||
let alt := alts.get ⟨i, h⟩
|
||||
let alt := alts[i]
|
||||
let numParams := altNumParams[i]!
|
||||
let typeNew ← whnfD typeNew
|
||||
match typeNew with
|
||||
@@ -162,7 +162,7 @@ def refineThrough? (matcherApp : MatcherApp) (e : Expr) :
|
||||
private def withUserNamesImpl {α} (fvars : Array Expr) (names : Array Name) (k : MetaM α) : MetaM α := do
|
||||
let lctx := (Array.zip fvars names).foldl (init := ← (getLCtx)) fun lctx (fvar, name) =>
|
||||
lctx.setUserName fvar.fvarId! name
|
||||
withTheReader Meta.Context (fun ctx => { ctx with lctx }) k
|
||||
withLCtx' lctx k
|
||||
|
||||
/--
|
||||
Sets the user name of the FVars in the local context according to the given array of names.
|
||||
|
||||
@@ -89,8 +89,7 @@ private def checkMotive (declName : Name) (motive : Expr) (motiveArgs : Array Ex
|
||||
We assume a parameter is anything that occurs before the motive -/
|
||||
private partial def getNumParams (xs : Array Expr) (motive : Expr) (i : Nat) : Nat :=
|
||||
if h : i < xs.size then
|
||||
let x := xs.get ⟨i, h⟩
|
||||
if motive == x then i
|
||||
if motive == xs[i] then i
|
||||
else getNumParams xs motive (i+1)
|
||||
else
|
||||
i
|
||||
@@ -100,7 +99,7 @@ private def getMajorPosDepElim (declName : Name) (majorPos? : Option Nat) (xs :
|
||||
match majorPos? with
|
||||
| some majorPos =>
|
||||
if h : majorPos < xs.size then
|
||||
let major := xs.get ⟨majorPos, h⟩
|
||||
let major := xs[majorPos]
|
||||
let depElim := motiveArgs.contains major
|
||||
pure (major, majorPos, depElim)
|
||||
else throwError "invalid major premise position for user defined recursor, recursor has only {xs.size} arguments"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user