mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-19 11:24:07 +00:00
Compare commits
90 Commits
reduceRepl
...
replace_fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b23f4355c0 | ||
|
|
c2117d75a6 | ||
|
|
3477b0e7f6 | ||
|
|
696f70bb4e | ||
|
|
726e162527 | ||
|
|
de5e07c4d2 | ||
|
|
327986e6fb | ||
|
|
6c33b9c57f | ||
|
|
d907771fdd | ||
|
|
5c3360200e | ||
|
|
204d4839fa | ||
|
|
e32f3e8140 | ||
|
|
7d2155943c | ||
|
|
78c4d6daff | ||
|
|
5526ff6320 | ||
|
|
bfca7ec72a | ||
|
|
9208b3585f | ||
|
|
a94805ff71 | ||
|
|
4eb842560c | ||
|
|
490d16c80d | ||
|
|
f60721bfbd | ||
|
|
a5ecdd0a17 | ||
|
|
be717f03ef | ||
|
|
41b4914836 | ||
|
|
933445608c | ||
|
|
8e396068e4 | ||
|
|
c1df7564ce | ||
|
|
ba3565f441 | ||
|
|
af03af5037 | ||
|
|
f6666fe266 | ||
|
|
c580684c22 | ||
|
|
1a12f63f74 | ||
|
|
95b8095fa6 | ||
|
|
94cc8eb863 | ||
|
|
1cf47bce5a | ||
|
|
b73fe04710 | ||
|
|
f986a2e9ef | ||
|
|
1a9cbc96f1 | ||
|
|
7aec6c9ae7 | ||
|
|
31de2494fb | ||
|
|
d679591880 | ||
|
|
f167cfba71 | ||
|
|
180c6aaa5e | ||
|
|
ab0241dac8 | ||
|
|
dc65f03c41 | ||
|
|
de96b6d8a7 | ||
|
|
3ab2c714ec | ||
|
|
f99427bd1a | ||
|
|
1118978cbb | ||
|
|
4ea8c5ad8d | ||
|
|
a6ae49c3ab | ||
|
|
2ad6d397f8 | ||
|
|
891824bc51 | ||
|
|
f35c562ef8 | ||
|
|
bcd8517307 | ||
|
|
ce73bbe277 | ||
|
|
f0eab4b7b1 | ||
|
|
5f70c1ca64 | ||
|
|
fce82eba40 | ||
|
|
9d14e4423c | ||
|
|
0c7859a7dd | ||
|
|
c01e003b49 | ||
|
|
ce8a130724 | ||
|
|
3c18d151a6 | ||
|
|
0f48e926eb | ||
|
|
850964999e | ||
|
|
57b8b32c72 | ||
|
|
bd2aefee01 | ||
|
|
74dcd6c2a9 | ||
|
|
23b893f778 | ||
|
|
1e02c08111 | ||
|
|
0f6a802314 | ||
|
|
be197cd431 | ||
|
|
f531f4e5db | ||
|
|
8229b28cc9 | ||
|
|
582d6e7f71 | ||
|
|
4daa29e71d | ||
|
|
9124426c55 | ||
|
|
cb0755bac0 | ||
|
|
4b32d9b9a1 | ||
|
|
7602265923 | ||
|
|
6ba5704e00 | ||
|
|
98ee789990 | ||
|
|
e08a562c48 | ||
|
|
84c40d9999 | ||
|
|
aecebaab74 | ||
|
|
3b3901b824 | ||
|
|
811c1e3685 | ||
|
|
27e85cc947 | ||
|
|
9a852595c4 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -470,7 +470,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Release
|
||||
@@ -500,7 +500,7 @@ jobs:
|
||||
# needed for tagging
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Prepare Nightly Release
|
||||
|
||||
@@ -5,7 +5,8 @@ See below for the checklist for release candidates.
|
||||
|
||||
We'll use `v4.6.0` as the intended release version as a running example.
|
||||
|
||||
- One week before the planned release, ensure that someone has written the first draft of the release blog post
|
||||
- One week before the planned release, ensure that (1) someone has written the release notes and (2) someone has written the first draft of the release blog post.
|
||||
If there is any material in `./releases_drafts/`, then the release notes are not done. (See the section "Writing the release notes".)
|
||||
- `git checkout releases/v4.6.0`
|
||||
(This branch should already exist, from the release candidates.)
|
||||
- `git pull`
|
||||
@@ -13,11 +14,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
|
||||
- `set(LEAN_VERSION_IS_RELEASE 1)`
|
||||
- (both of these should already be in place from the release candidates)
|
||||
- In `RELEASES.md`, verify that the `v4.6.0` section has been completed during the release candidate cycle.
|
||||
It should be in bullet point form, with a point for every significant PR,
|
||||
and may have a paragraph describing each major new language feature.
|
||||
It should have a "breaking changes" section calling out changes that are specifically likely
|
||||
to cause problems for downstream users.
|
||||
- `git tag v4.6.0`
|
||||
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
|
||||
- Now wait, while CI runs.
|
||||
@@ -28,8 +24,9 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
you may want to start on the release candidate checklist now.
|
||||
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
|
||||
- Edit the release notes on Github to select the "Set as the latest release".
|
||||
- Copy and paste the Github release notes from the previous releases candidate for this version
|
||||
(e.g. `v4.6.0-rc1`), and quickly sanity check.
|
||||
- Follow the instructions in creating a release candidate for the "GitHub release notes" step,
|
||||
now that we have a written `RELEASES.md` section.
|
||||
Do a quick sanity check.
|
||||
- Next, we will move a curated list of downstream repos to the latest stable release.
|
||||
- For each of the repositories listed below:
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
|
||||
@@ -92,6 +89,10 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- The `v4.6.0` section of `RELEASES.md` is out of sync between
|
||||
`releases/v4.6.0` and `master`. This should be reconciled:
|
||||
- Replace the `v4.6.0` section on `master` with the `v4.6.0` section on `releases/v4.6.0`
|
||||
and commit this to `master`.
|
||||
- Merge the release announcement PR for the Lean website - it will be deployed automatically
|
||||
- Finally, make an announcement!
|
||||
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
|
||||
@@ -102,7 +103,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
|
||||
## Optimistic(?) time estimates:
|
||||
- Initial checks and push the tag: 30 minutes.
|
||||
- Note that if `RELEASES.md` has discrepancies this could take longer!
|
||||
- Waiting for the release: 60 minutes.
|
||||
- Fixing release notes: 10 minutes.
|
||||
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
|
||||
@@ -129,29 +129,26 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
git checkout nightly-2024-02-29
|
||||
git checkout -b releases/v4.7.0
|
||||
```
|
||||
- In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
|
||||
- Our current goal is to have written release notes only about major language features or breaking changes,
|
||||
and to rely on automatically generated release notes for bugfixes and minor changes.
|
||||
- Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
|
||||
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
|
||||
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
|
||||
- Commit your changes to `RELEASES.md`, and push.
|
||||
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
|
||||
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.`
|
||||
- We will rely on automatically generated release notes for release candidates,
|
||||
and the written release notes will be used for stable versions only.
|
||||
It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion.
|
||||
- In `src/CMakeLists.txt`,
|
||||
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
|
||||
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
|
||||
- Commit your changes to `src/CMakeLists.txt`, and push.
|
||||
- `git tag v4.7.0-rc1`
|
||||
- `git push origin v4.7.0-rc1`
|
||||
- Ping the FRO Zulip that release notes need to be written. The release notes do not block completing the rest of this checklist.
|
||||
- Now wait, while CI runs.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
|
||||
- This step can take up to an hour.
|
||||
- Once the release appears at https://github.com/leanprover/lean4/releases/
|
||||
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/
|
||||
- Edit the release notes on Github to select the "Set as a pre-release box".
|
||||
- Copy the section of `RELEASES.md` for this version into the Github release notes.
|
||||
- Use the title "Changes since v4.6.0 (from RELEASES.md)"
|
||||
- Then in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
|
||||
- This will add a list of all the commits since the last stable version.
|
||||
- If release notes have been written already, copy the section of `RELEASES.md` for this version into the Github release notes
|
||||
and use the title "Changes since v4.6.0 (from RELEASES.md)".
|
||||
- Otherwise, in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
|
||||
This will add a list of all the commits since the last stable version.
|
||||
- Delete anything already mentioned in the hand-written release notes above.
|
||||
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
|
||||
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
|
||||
@@ -177,6 +174,9 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
- We do this for the same list of repositories as for stable releases, see above.
|
||||
As above, there are dependencies between these, and so the process above is iterative.
|
||||
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
|
||||
It is essential for Mathlib CI that you then create the next `bump/v4.8.0` branch
|
||||
for the next development cycle.
|
||||
Set the `lean-toolchain` file on this branch to same `nightly` you used for this release.
|
||||
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
|
||||
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
|
||||
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
|
||||
@@ -187,12 +187,17 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
Please also make sure that whoever is handling social media knows the release is out.
|
||||
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
|
||||
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
|
||||
- In `RELEASES.md`, update the `v4.7.0` section to say:
|
||||
"Release candidate, release notes will be copied from branch `releases/v4.7.0` once completed."
|
||||
Make sure that whoever is preparing the release notes during this cycle knows that it is their job to do so!
|
||||
- In `RELEASES.md`, update the `v4.8.0` section to say:
|
||||
"Development in progress".
|
||||
- In `RELEASES.md`, verify that the old section `v4.6.0` has the full releases notes from the `releases/v4.6.0` branch.
|
||||
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
|
||||
```
|
||||
Release candidate, release notes will be copied from `branch releases/v4.7.0` once completed.
|
||||
```
|
||||
and inserts the following section before that section:
|
||||
```
|
||||
v4.8.0
|
||||
----------
|
||||
Development in progress.
|
||||
```
|
||||
- Removes all the entries from the `./releases_drafts/` folder.
|
||||
|
||||
## Time estimates:
|
||||
Slightly longer than the corresponding steps for a stable release.
|
||||
@@ -226,3 +231,18 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
|
||||
* It is always okay to merge in the following directions:
|
||||
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
|
||||
Please remember to push any merges you make to intermediate steps!
|
||||
|
||||
# Writing the release notes
|
||||
|
||||
We are currently trying a system where release notes are compiled all at once from someone looking through the commit history.
|
||||
The exact steps are a work in progress.
|
||||
Here is the general idea:
|
||||
|
||||
* The work is done right on the `releases/v4.6.0` branch sometime after it is created but before the stable release is made.
|
||||
The release notes for `v4.6.0` will be copied to `master`.
|
||||
* There can be material for release notes entries in commit messages.
|
||||
* There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
|
||||
See `./releases_drafts/README.md` for more information.
|
||||
* The release notes should be written from a downstream expert user's point of view.
|
||||
|
||||
This section will be updated when the next release notes are written (for `v4.10.0`).
|
||||
|
||||
@@ -13,7 +13,7 @@ Recall that nonnegative numerals are considered to be a `Nat` if there are no ty
|
||||
|
||||
The operator `/` for `Int` implements integer division.
|
||||
```lean
|
||||
#eval -10 / 4 -- -2
|
||||
#eval -10 / 4 -- -3
|
||||
```
|
||||
|
||||
Similar to `Nat`, the internal representation of `Int` is optimized. Small integers are
|
||||
|
||||
@@ -1089,15 +1089,18 @@ def InvImage {α : Sort u} {β : Sort v} (r : β → β → Prop) (f : α → β
|
||||
fun a₁ a₂ => r (f a₁) (f a₂)
|
||||
|
||||
/--
|
||||
The transitive closure `r⁺` of a relation `r` is the smallest relation which is
|
||||
transitive and contains `r`. `r⁺ a z` if and only if there exists a sequence
|
||||
The transitive closure `TransGen r` of a relation `r` is the smallest relation which is
|
||||
transitive and contains `r`. `TransGen r a z` if and only if there exists a sequence
|
||||
`a r b r ... r z` of length at least 1 connecting `a` to `z`.
|
||||
-/
|
||||
inductive TC {α : Sort u} (r : α → α → Prop) : α → α → Prop where
|
||||
/-- If `r a b` then `r⁺ a b`. This is the base case of the transitive closure. -/
|
||||
| base : ∀ a b, r a b → TC r a b
|
||||
inductive Relation.TransGen {α : Sort u} (r : α → α → Prop) : α → α → Prop
|
||||
/-- If `r a b` then `TransGen r a b`. This is the base case of the transitive closure. -/
|
||||
| single {a b} : r a b → TransGen r a b
|
||||
/-- The transitive closure is transitive. -/
|
||||
| trans : ∀ a b c, TC r a b → TC r b c → TC r a c
|
||||
| tail {a b c} : TransGen r a b → r b c → TransGen r a c
|
||||
|
||||
/-- Deprecated synonym for `Relation.TransGen`. -/
|
||||
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
|
||||
|
||||
/-! # Subtype -/
|
||||
|
||||
@@ -1362,6 +1365,9 @@ theorem iff_false_right (ha : ¬a) : (b ↔ a) ↔ ¬b := Iff.comm.trans (iff_fa
|
||||
theorem of_iff_true (h : a ↔ True) : a := h.mpr trivial
|
||||
theorem iff_true_intro (h : a) : a ↔ True := iff_of_true h trivial
|
||||
|
||||
theorem eq_iff_true_of_subsingleton [Subsingleton α] (x y : α) : x = y ↔ True :=
|
||||
iff_true_intro (Subsingleton.elim ..)
|
||||
|
||||
theorem not_of_iff_false : (p ↔ False) → ¬p := Iff.mp
|
||||
theorem iff_false_intro (h : ¬a) : a ↔ False := iff_of_false h id
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ theorem foldlM_eq_foldlM_data.aux [Monad m]
|
||||
simp [foldlM_eq_foldlM_data.aux f arr i (j+1) H]
|
||||
rw (config := {occs := .pos [2]}) [← List.get_drop_eq_drop _ _ ‹_›]
|
||||
rfl
|
||||
· rw [List.drop_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
|
||||
theorem foldlM_eq_foldlM_data [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
@@ -141,7 +141,7 @@ where
|
||||
· rw [← List.get_drop_eq_drop _ i ‹_›]
|
||||
simp only [aux (i + 1), map_eq_pure_bind, data_length, List.foldlM_cons, bind_assoc, pure_bind]
|
||||
rfl
|
||||
· rw [List.drop_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
termination_by arr.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
|
||||
@@ -1437,7 +1437,7 @@ theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
|
||||
@[simp]
|
||||
theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j)) := by
|
||||
rcases w with rfl | w
|
||||
· simp; omega
|
||||
· simp
|
||||
· simp only [twoPow, getLsb_shiftLeft, getLsb_ofNat]
|
||||
by_cases hj : j < i
|
||||
· simp only [hj, decide_True, Bool.not_true, Bool.and_false, Bool.false_and, Bool.false_eq,
|
||||
|
||||
@@ -31,11 +31,9 @@ theorem utf8Size_eq (c : Char) : c.utf8Size = 1 ∨ c.utf8Size = 2 ∨ c.utf8Siz
|
||||
rw [Char.ofNat, dif_pos]
|
||||
rfl
|
||||
|
||||
@[ext] theorem ext : {a b : Char} → a.val = b.val → a = b
|
||||
@[ext] protected theorem ext : {a b : Char} → a.val = b.val → a = b
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl => rfl
|
||||
|
||||
theorem ext_iff {x y : Char} : x = y ↔ x.val = y.val := ⟨congrArg _, Char.ext⟩
|
||||
|
||||
end Char
|
||||
|
||||
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size
|
||||
|
||||
@@ -37,9 +37,7 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
|
||||
|
||||
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (⟨a, h⟩ : Fin n) = a := rfl
|
||||
|
||||
@[ext] theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
|
||||
|
||||
theorem ext_iff {a b : Fin n} : a = b ↔ a.1 = b.1 := val_inj.symm
|
||||
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
|
||||
|
||||
theorem val_ne_iff {a b : Fin n} : a.1 ≠ b.1 ↔ a ≠ b := not_congr val_inj
|
||||
|
||||
@@ -47,12 +45,12 @@ theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩
|
||||
⟨fun h i hi => h ⟨i, hi⟩, fun h ⟨i, hi⟩ => h i hi⟩
|
||||
|
||||
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
|
||||
(⟨a, ha⟩ : Fin n) = ⟨b, hb⟩ ↔ a = b := ext_iff
|
||||
(⟨a, ha⟩ : Fin n) = ⟨b, hb⟩ ↔ a = b := Fin.ext_iff
|
||||
|
||||
theorem val_mk {m n : Nat} (h : m < n) : (⟨m, h⟩ : Fin n).val = m := rfl
|
||||
|
||||
theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
|
||||
a = ⟨k, hk⟩ ↔ (a : Nat) = k := ext_iff
|
||||
a = ⟨k, hk⟩ ↔ (a : Nat) = k := Fin.ext_iff
|
||||
|
||||
theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
|
||||
@@ -145,7 +143,7 @@ theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) : ∃ j :
|
||||
|
||||
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
|
||||
|
||||
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := ext <| by
|
||||
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
|
||||
rw [val_rev, val_rev, ← Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
|
||||
|
||||
@[simp] theorem rev_le_rev {i j : Fin n} : rev i ≤ rev j ↔ j ≤ i := by
|
||||
@@ -171,12 +169,12 @@ theorem le_last (i : Fin (n + 1)) : i ≤ last n := Nat.le_of_lt_succ i.is_lt
|
||||
theorem last_pos : (0 : Fin (n + 2)) < last (n + 1) := Nat.succ_pos _
|
||||
|
||||
theorem eq_last_of_not_lt {i : Fin (n + 1)} (h : ¬(i : Nat) < n) : i = last n :=
|
||||
ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
|
||||
Fin.ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
|
||||
|
||||
theorem val_lt_last {i : Fin (n + 1)} : i ≠ last n → (i : Nat) < n :=
|
||||
Decidable.not_imp_comm.1 eq_last_of_not_lt
|
||||
|
||||
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := ext <| by simp
|
||||
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := Fin.ext <| by simp
|
||||
|
||||
@[simp] theorem rev_zero (n : Nat) : rev 0 = last n := by
|
||||
rw [← rev_rev (last _), rev_last]
|
||||
@@ -244,11 +242,11 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt one_pos
|
||||
@[simp] theorem succ_lt_succ_iff {a b : Fin n} : a.succ < b.succ ↔ a < b := Nat.succ_lt_succ_iff
|
||||
|
||||
@[simp] theorem succ_inj {a b : Fin n} : a.succ = b.succ ↔ a = b := by
|
||||
refine ⟨fun h => ext ?_, congrArg _⟩
|
||||
refine ⟨fun h => Fin.ext ?_, congrArg _⟩
|
||||
apply Nat.le_antisymm <;> exact succ_le_succ_iff.1 (h ▸ Nat.le_refl _)
|
||||
|
||||
theorem succ_ne_zero {n} : ∀ k : Fin n, Fin.succ k ≠ 0
|
||||
| ⟨k, _⟩, heq => Nat.succ_ne_zero k <| ext_iff.1 heq
|
||||
| ⟨k, _⟩, heq => Nat.succ_ne_zero k <| congrArg Fin.val heq
|
||||
|
||||
@[simp] theorem succ_zero_eq_one : Fin.succ (0 : Fin (n + 1)) = 1 := rfl
|
||||
|
||||
@@ -267,7 +265,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
|
||||
rw [← succ_zero_eq_one, succ_lt_succ_iff]; exact succ_pos a
|
||||
|
||||
@[simp] theorem add_one_lt_iff {n : Nat} {k : Fin (n + 2)} : k + 1 < k ↔ k = last _ := by
|
||||
simp only [lt_def, val_add, val_last, ext_iff]
|
||||
simp only [lt_def, val_add, val_last, Fin.ext_iff]
|
||||
let ⟨k, hk⟩ := k
|
||||
match Nat.eq_or_lt_of_le (Nat.le_of_lt_succ hk) with
|
||||
| .inl h => cases h; simp [Nat.succ_pos]
|
||||
@@ -285,7 +283,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
|
||||
split <;> simp [*, (Nat.succ_ne_zero _).symm, Nat.ne_of_gt (Nat.lt_succ_self _)]
|
||||
|
||||
@[simp] theorem last_le_iff {n : Nat} {k : Fin (n + 1)} : last n ≤ k ↔ k = last n := by
|
||||
rw [ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
|
||||
rw [Fin.ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
|
||||
|
||||
@[simp] theorem lt_add_one_iff {n : Nat} {k : Fin (n + 1)} : k < k + 1 ↔ k < last n := by
|
||||
rw [← Decidable.not_iff_not]; simp
|
||||
@@ -306,10 +304,10 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
|
||||
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n ≤ m) :
|
||||
castLE h ⟨i, hn⟩ = ⟨i, Nat.lt_of_lt_of_le hn h⟩ := rfl
|
||||
|
||||
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ ≤ m.succ) : castLE h 0 = 0 := by simp [ext_iff]
|
||||
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ ≤ m.succ) : castLE h 0 = 0 := by simp [Fin.ext_iff]
|
||||
|
||||
@[simp] theorem castLE_succ {m n : Nat} (h : m + 1 ≤ n + 1) (i : Fin m) :
|
||||
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [ext_iff]
|
||||
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [Fin.ext_iff]
|
||||
|
||||
@[simp] theorem castLE_castLE {k m n} (km : k ≤ m) (mn : m ≤ n) (i : Fin k) :
|
||||
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
|
||||
@@ -322,7 +320,7 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
|
||||
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
|
||||
ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
|
||||
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
|
||||
|
||||
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h ⟨i, hn⟩ = ⟨i, h ▸ hn⟩ := rfl
|
||||
|
||||
@@ -348,7 +346,7 @@ theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m :=
|
||||
|
||||
/-- For rewriting in the reverse direction, see `Fin.cast_castAdd_left`. -/
|
||||
theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
|
||||
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := ext rfl
|
||||
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := Fin.ext rfl
|
||||
|
||||
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
|
||||
@@ -397,7 +395,7 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
|
||||
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
|
||||
Fin.castSucc a < Fin.castSucc b ↔ a < b := .rfl
|
||||
|
||||
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b ↔ a = b := by simp [ext_iff]
|
||||
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b ↔ a = b := by simp [Fin.ext_iff]
|
||||
|
||||
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
|
||||
|
||||
@@ -409,7 +407,7 @@ theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
|
||||
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
|
||||
simpa [lt_def] using h
|
||||
|
||||
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 ↔ a = 0 := by simp [ext_iff]
|
||||
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 ↔ a = 0 := by simp [Fin.ext_iff]
|
||||
|
||||
theorem castSucc_ne_zero_iff (a : Fin (n + 1)) : castSucc a ≠ 0 ↔ a ≠ 0 :=
|
||||
not_congr <| castSucc_eq_zero_iff a
|
||||
@@ -421,7 +419,7 @@ theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
|
||||
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
|
||||
cases n
|
||||
· exact a.elim0
|
||||
· simp [ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
|
||||
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
|
||||
|
||||
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
|
||||
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
|
||||
@@ -454,7 +452,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
|
||||
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
|
||||
cast h (addNat i m') = addNat i m :=
|
||||
ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
|
||||
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
|
||||
|
||||
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
|
||||
|
||||
@@ -474,7 +472,7 @@ theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
|
||||
|
||||
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
|
||||
cast h (natAdd m' i) = natAdd m i :=
|
||||
ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
|
||||
Fin.ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
|
||||
|
||||
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
|
||||
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
|
||||
@@ -484,27 +482,27 @@ theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
|
||||
|
||||
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
|
||||
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
|
||||
ext <| (Nat.add_assoc ..).symm
|
||||
Fin.ext <| (Nat.add_assoc ..).symm
|
||||
|
||||
@[simp]
|
||||
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
|
||||
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
|
||||
ext <| Nat.zero_add _
|
||||
Fin.ext <| Nat.zero_add _
|
||||
|
||||
@[simp]
|
||||
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
|
||||
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := ext <| Nat.add_comm ..
|
||||
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := Fin.ext <| Nat.add_comm ..
|
||||
|
||||
@[simp]
|
||||
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
|
||||
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := ext <| Nat.add_comm ..
|
||||
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := Fin.ext <| Nat.add_comm ..
|
||||
|
||||
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
|
||||
|
||||
theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSucc (natAdd n i) :=
|
||||
rfl
|
||||
|
||||
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := ext <| by
|
||||
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
|
||||
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
|
||||
|
||||
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
|
||||
@@ -534,7 +532,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i ≠ 0) (j : Fin
|
||||
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
|
||||
Fin.pred ⟨i + 1, Nat.add_lt_add_right h 1⟩ (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
|
||||
⟨i, h⟩ := by
|
||||
simp only [ext_iff, coe_pred, Nat.add_sub_cancel]
|
||||
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
|
||||
|
||||
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
|
||||
Fin.pred ⟨i + 1, h₁⟩ h₂ = ⟨i, Nat.lt_of_succ_lt_succ h₁⟩ := pred_mk_succ i _
|
||||
@@ -554,14 +552,14 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
|
||||
∀ {a b : Fin (n + 1)} {ha : a ≠ 0} {hb : b ≠ 0}, a.pred ha = b.pred hb ↔ a = b
|
||||
| ⟨0, _⟩, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
|
||||
| ⟨i + 1, _⟩, ⟨0, _⟩, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
|
||||
| ⟨i + 1, hi⟩, ⟨j + 1, hj⟩, ha, hb => by simp [ext_iff, Nat.succ.injEq]
|
||||
| ⟨i + 1, hi⟩, ⟨j + 1, hj⟩, ha, hb => by simp [Fin.ext_iff, Nat.succ.injEq]
|
||||
|
||||
@[simp] theorem pred_one {n : Nat} :
|
||||
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
|
||||
|
||||
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
|
||||
rw [ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
|
||||
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
|
||||
exact Nat.add_lt_add_right h 1
|
||||
|
||||
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m ≤ i) : (i.subNat m h : Nat) = i - m := rfl
|
||||
@@ -573,10 +571,10 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
pred (castSucc i.succ) (Fin.ne_of_gt (castSucc_pos i.succ_pos)) = castSucc i := rfl
|
||||
|
||||
@[simp] theorem addNat_subNat {i : Fin (n + m)} (h : m ≤ i) : addNat (subNat m i h) m = i :=
|
||||
ext <| Nat.sub_add_cancel h
|
||||
Fin.ext <| Nat.sub_add_cancel h
|
||||
|
||||
@[simp] theorem subNat_addNat (i : Fin n) (m : Nat) (h : m ≤ addNat i m := le_coe_addNat m i) :
|
||||
subNat m (addNat i m) h = i := ext <| Nat.add_sub_cancel i m
|
||||
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
|
||||
|
||||
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n ≤ i) :
|
||||
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [← cast_addNat]; rfl
|
||||
@@ -810,10 +808,10 @@ theorem coe_mul {n : Nat} : ∀ a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
|
||||
protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
|
||||
match n with
|
||||
| 0 => exact Subsingleton.elim (α := Fin 1) ..
|
||||
| n+1 => simp [ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
|
||||
| n+1 => simp [Fin.ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
|
||||
|
||||
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
|
||||
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
|
||||
Fin.ext <| by rw [mul_def, mul_def, Nat.mul_comm]
|
||||
instance : Std.Commutative (α := Fin n) (· * ·) := ⟨Fin.mul_comm⟩
|
||||
|
||||
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
|
||||
@@ -829,9 +827,9 @@ instance : Std.LawfulIdentity (α := Fin (n + 1)) (· * ·) 1 where
|
||||
left_id := Fin.one_mul
|
||||
right_id := Fin.mul_one
|
||||
|
||||
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [ext_iff, mul_def]
|
||||
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [Fin.ext_iff, mul_def]
|
||||
|
||||
protected theorem zero_mul (k : Fin (n + 1)) : (0 : Fin (n + 1)) * k = 0 := by
|
||||
simp [ext_iff, mul_def]
|
||||
simp [Fin.ext_iff, mul_def]
|
||||
|
||||
end Fin
|
||||
|
||||
@@ -101,13 +101,13 @@ Returns an undefined value if `x` is not finite.
|
||||
instance : ToString Float where
|
||||
toString := Float.toString
|
||||
|
||||
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
|
||||
|
||||
instance : Repr Float where
|
||||
reprPrec n _ := Float.toString n
|
||||
reprPrec n prec := if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
|
||||
|
||||
instance : ReprAtom Float := ⟨⟩
|
||||
|
||||
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
|
||||
|
||||
@[extern "sin"] opaque Float.sin : Float → Float
|
||||
@[extern "cos"] opaque Float.cos : Float → Float
|
||||
@[extern "tan"] opaque Float.tan : Float → Float
|
||||
|
||||
@@ -22,7 +22,7 @@ along with `@[csimp]` lemmas,
|
||||
|
||||
In `Init.Data.List.Lemmas` we develop the full API for these functions.
|
||||
|
||||
Recall that `length`, `get`, `set`, `fold`, and `concat` have already been defined in `Init.Prelude`.
|
||||
Recall that `length`, `get`, `set`, `foldl`, and `concat` have already been defined in `Init.Prelude`.
|
||||
|
||||
The operations are organized as follow:
|
||||
* Equality: `beq`, `isEqv`.
|
||||
@@ -32,8 +32,8 @@ The operations are organized as follow:
|
||||
* List membership: `isEmpty`, `elem`, `contains`, `mem` (and the `∈` notation),
|
||||
and decidability for predicates quantifying over membership in a `List`.
|
||||
* Sublists: `take`, `drop`, `takeWhile`, `dropWhile`, `partition`, `dropLast`,
|
||||
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `rotateLeft` and `rotateRight`.
|
||||
* Manipulating elements: `replace`, `insert`, `erase`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
|
||||
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`, `rotateLeft` and `rotateRight`.
|
||||
* Manipulating elements: `replace`, `insert`, `erase`, `eraseP`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
|
||||
* Logic: `any`, `all`, `or`, and `and`.
|
||||
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
|
||||
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
|
||||
@@ -866,6 +866,40 @@ def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
|
||||
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
|
||||
Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
|
||||
|
||||
/-! ### Subset -/
|
||||
|
||||
/--
|
||||
`l₁ ⊆ l₂` means that every element of `l₁` is also an element of `l₂`, ignoring multiplicity.
|
||||
-/
|
||||
protected def Subset (l₁ l₂ : List α) := ∀ ⦃a : α⦄, a ∈ l₁ → a ∈ l₂
|
||||
|
||||
instance : HasSubset (List α) := ⟨List.Subset⟩
|
||||
|
||||
instance [DecidableEq α] : DecidableRel (Subset : List α → List α → Prop) :=
|
||||
fun _ _ => decidableBAll _ _
|
||||
|
||||
/-! ### Sublist and isSublist -/
|
||||
|
||||
/-- `l₁ <+ l₂`, or `Sublist l₁ l₂`, says that `l₁` is a (non-contiguous) subsequence of `l₂`. -/
|
||||
inductive Sublist {α} : List α → List α → Prop
|
||||
/-- the base case: `[]` is a sublist of `[]` -/
|
||||
| slnil : Sublist [] []
|
||||
/-- If `l₁` is a subsequence of `l₂`, then it is also a subsequence of `a :: l₂`. -/
|
||||
| cons a : Sublist l₁ l₂ → Sublist l₁ (a :: l₂)
|
||||
/-- If `l₁` is a subsequence of `l₂`, then `a :: l₁` is a subsequence of `a :: l₂`. -/
|
||||
| cons₂ a : Sublist l₁ l₂ → Sublist (a :: l₁) (a :: l₂)
|
||||
|
||||
@[inherit_doc] scoped infixl:50 " <+ " => Sublist
|
||||
|
||||
/-- True if the first list is a potentially non-contiguous sub-sequence of the second list. -/
|
||||
def isSublist [BEq α] : List α → List α → Bool
|
||||
| [], _ => true
|
||||
| _, [] => false
|
||||
| l₁@(hd₁::tl₁), hd₂::tl₂ =>
|
||||
if hd₁ == hd₂
|
||||
then tl₁.isSublist tl₂
|
||||
else l₁.isSublist tl₂
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
/--
|
||||
@@ -908,6 +942,55 @@ def rotateRight (xs : List α) (n : Nat := 1) : List α :=
|
||||
|
||||
@[simp] theorem rotateRight_nil : ([] : List α).rotateRight n = [] := rfl
|
||||
|
||||
/-! ## Pairwise, Nodup -/
|
||||
|
||||
section Pairwise
|
||||
|
||||
variable (R : α → α → Prop)
|
||||
|
||||
/--
|
||||
`Pairwise R l` means that all the elements with earlier indexes are
|
||||
`R`-related to all the elements with later indexes.
|
||||
```
|
||||
Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3
|
||||
```
|
||||
For example if `R = (·≠·)` then it asserts `l` has no duplicates,
|
||||
and if `R = (·<·)` then it asserts that `l` is (strictly) sorted.
|
||||
-/
|
||||
inductive Pairwise : List α → Prop
|
||||
/-- All elements of the empty list are vacuously pairwise related. -/
|
||||
| nil : Pairwise []
|
||||
/-- `a :: l` is `Pairwise R` if `a` `R`-relates to every element of `l`,
|
||||
and `l` is `Pairwise R`. -/
|
||||
| cons : ∀ {a : α} {l : List α}, (∀ a', a' ∈ l → R a a') → Pairwise l → Pairwise (a :: l)
|
||||
|
||||
attribute [simp] Pairwise.nil
|
||||
|
||||
variable {R}
|
||||
|
||||
@[simp] theorem pairwise_cons : Pairwise R (a::l) ↔ (∀ a', a' ∈ l → R a a') ∧ Pairwise R l :=
|
||||
⟨fun | .cons h₁ h₂ => ⟨h₁, h₂⟩, fun ⟨h₁, h₂⟩ => h₂.cons h₁⟩
|
||||
|
||||
instance instDecidablePairwise [DecidableRel R] :
|
||||
(l : List α) → Decidable (Pairwise R l)
|
||||
| [] => isTrue .nil
|
||||
| hd :: tl =>
|
||||
match instDecidablePairwise tl with
|
||||
| isTrue ht =>
|
||||
match decidableBAll (R hd) tl with
|
||||
| isFalse hf => isFalse fun hf' => hf (pairwise_cons.1 hf').1
|
||||
| isTrue ht' => isTrue <| pairwise_cons.mpr (And.intro ht' ht)
|
||||
| isFalse hf => isFalse fun | .cons _ ih => hf ih
|
||||
|
||||
end Pairwise
|
||||
|
||||
/-- `Nodup l` means that `l` has no duplicates, that is, any element appears at most
|
||||
once in the List. It is defined as `Pairwise (≠)`. -/
|
||||
def Nodup : List α → Prop := Pairwise (· ≠ ·)
|
||||
|
||||
instance nodupDecidable [DecidableEq α] : ∀ l : List α, Decidable (Nodup l) :=
|
||||
instDecidablePairwise
|
||||
|
||||
/-! ## Manipulating elements -/
|
||||
|
||||
/-! ### replace -/
|
||||
@@ -953,6 +1036,11 @@ theorem erase_cons [BEq α] (a b : α) (l : List α) :
|
||||
(b :: l).erase a = if b == a then l else b :: l.erase a := by
|
||||
simp only [List.erase]; split <;> simp_all
|
||||
|
||||
/-- `eraseP p l` removes the first element of `l` satisfying the predicate `p`. -/
|
||||
def eraseP (p : α → Bool) : List α → List α
|
||||
| [] => []
|
||||
| a :: l => bif p a then l else a :: eraseP p l
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -295,6 +295,24 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
|
||||
· rw [IH] <;> simp_all
|
||||
· simp
|
||||
|
||||
/-- Tail-recursive version of `eraseP`. -/
|
||||
@[inline] def erasePTR (p : α → Bool) (l : List α) : List α := go l #[] where
|
||||
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
|
||||
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
|
||||
@[specialize] go : List α → Array α → List α
|
||||
| [], _ => l
|
||||
| a :: l, acc => bif p a then acc.toListAppend l else go l (acc.push a)
|
||||
|
||||
@[csimp] theorem eraseP_eq_erasePTR : @eraseP = @erasePTR := by
|
||||
funext α p l; simp [erasePTR]
|
||||
let rec go (acc) : ∀ xs, l = acc.data ++ xs →
|
||||
erasePTR.go p l xs acc = acc.data ++ xs.eraseP p
|
||||
| [] => fun h => by simp [erasePTR.go, eraseP, h]
|
||||
| x::xs => by
|
||||
simp [erasePTR.go, eraseP]; cases p x <;> simp
|
||||
· intro h; rw [go _ xs]; {simp}; simp [h]
|
||||
exact (go #[] _ rfl).symm
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
/-- Tail recursive version of `List.eraseIdx`. -/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -120,6 +120,43 @@ theorem get?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n).get? m = if m < n then l.get? m else none := by
|
||||
simp [getElem?_take_eq_if]
|
||||
|
||||
theorem head?_take {l : List α} {n : Nat} :
|
||||
(l.take n).head? = if n = 0 then none else l.head? := by
|
||||
simp [head?_eq_getElem?, getElem?_take_eq_if]
|
||||
split
|
||||
· rw [if_neg (by omega)]
|
||||
· rw [if_pos (by omega)]
|
||||
|
||||
theorem head_take {l : List α} {n : Nat} (h : l.take n ≠ []) :
|
||||
(l.take n).head h = l.head (by simp_all) := by
|
||||
apply Option.some_inj.1
|
||||
rw [← head?_eq_head, ← head?_eq_head, head?_take, if_neg]
|
||||
simp_all
|
||||
|
||||
theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none else l[n - 1]?.or l.getLast? := by
|
||||
rw [getLast?_eq_getElem?, getElem?_take_eq_if, length_take]
|
||||
split
|
||||
· rw [if_neg (by omega)]
|
||||
rw [Nat.min_def]
|
||||
split
|
||||
· rw [getElem?_eq_getElem (by omega)]
|
||||
simp
|
||||
· rw [← getLast?_eq_getElem?, getElem?_eq_none (by omega)]
|
||||
simp
|
||||
· rw [if_pos]
|
||||
omega
|
||||
|
||||
theorem getLast_take {l : List α} (h : l.take n ≠ []) :
|
||||
(l.take n).getLast h = l[n - 1]?.getD (l.getLast (by simp_all)) := by
|
||||
rw [getLast_eq_getElem, getElem_take']
|
||||
simp [length_take, Nat.min_def]
|
||||
simp at h
|
||||
split
|
||||
· rw [getElem?_eq_getElem (by omega)]
|
||||
simp
|
||||
· rw [getElem?_eq_none (by omega), getLast_eq_getElem]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem take_eq_take :
|
||||
∀ {l : List α} {m n : Nat}, l.take m = l.take n ↔ min m l.length = min n l.length
|
||||
@@ -245,6 +282,31 @@ theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? :=
|
||||
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
|
||||
simp
|
||||
|
||||
theorem head?_drop (l : List α) (n : Nat) :
|
||||
(l.drop n).head? = l[n]? := by
|
||||
rw [head?_eq_getElem?, getElem?_drop, Nat.add_zero]
|
||||
|
||||
theorem head_drop {l : List α} {n : Nat} (h : l.drop n ≠ []) :
|
||||
(l.drop n).head h = l[n]'(by simp_all) := by
|
||||
have w : n < l.length := length_lt_of_drop_ne_nil h
|
||||
simpa [head?_eq_head, getElem?_eq_getElem, h, w] using head?_drop l n
|
||||
|
||||
theorem getLast?_drop {l : List α} : (l.drop n).getLast? = if l.length ≤ n then none else l.getLast? := by
|
||||
rw [getLast?_eq_getElem?, getElem?_drop]
|
||||
rw [length_drop]
|
||||
split
|
||||
· rw [getElem?_eq_none (by omega)]
|
||||
· rw [getLast?_eq_getElem?]
|
||||
congr
|
||||
omega
|
||||
|
||||
theorem getLast_drop {l : List α} (h : l.drop n ≠ []) :
|
||||
(l.drop n).getLast h = l.getLast (ne_nil_of_length_pos (by simp at h; omega)) := by
|
||||
simp only [ne_eq, drop_eq_nil_iff_le] at h
|
||||
apply Option.some_inj.1
|
||||
simp only [← getLast?_eq_getLast, getLast?_drop, ite_eq_right_iff]
|
||||
omega
|
||||
|
||||
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
|
||||
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
|
||||
split <;> rename_i h
|
||||
|
||||
@@ -100,6 +100,7 @@ def blt (a b : Nat) : Bool :=
|
||||
ble a.succ b
|
||||
|
||||
attribute [simp] Nat.zero_le
|
||||
attribute [simp] Nat.not_lt_zero
|
||||
|
||||
/-! # Helper "packing" theorems -/
|
||||
|
||||
@@ -633,6 +634,10 @@ theorem succ_lt_succ_iff : succ a < succ b ↔ a < b := ⟨lt_of_succ_lt_succ, s
|
||||
|
||||
theorem add_one_inj : a + 1 = b + 1 ↔ a = b := succ_inj'
|
||||
|
||||
theorem ne_add_one (n : Nat) : n ≠ n + 1 := fun h => by cases h
|
||||
|
||||
theorem add_one_ne (n : Nat) : n + 1 ≠ n := fun h => by cases h
|
||||
|
||||
theorem add_one_le_add_one_iff : a + 1 ≤ b + 1 ↔ a ≤ b := succ_le_succ_iff
|
||||
|
||||
theorem add_one_lt_add_one_iff : a + 1 < b + 1 ↔ a < b := succ_lt_succ_iff
|
||||
@@ -814,6 +819,9 @@ protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
|
||||
@[simp] protected theorem zero_sub_one : 0 - 1 = 0 := rfl
|
||||
@[simp] protected theorem add_one_sub_one (n : Nat) : n + 1 - 1 = n := rfl
|
||||
|
||||
theorem sub_one_eq_self (n : Nat) : n - 1 = n ↔ n = 0 := by cases n <;> simp [ne_add_one]
|
||||
theorem eq_self_sub_one (n : Nat) : n = n - 1 ↔ n = 0 := by cases n <;> simp [add_one_ne]
|
||||
|
||||
theorem succ_pred {a : Nat} (h : a ≠ 0) : a.pred.succ = a := by
|
||||
induction a with
|
||||
| zero => contradiction
|
||||
|
||||
@@ -82,7 +82,7 @@ theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by cases x <;> sim
|
||||
cases a <;> simp
|
||||
|
||||
theorem eq_some_iff_get_eq : o = some a ↔ ∃ h : o.isSome, o.get h = a := by
|
||||
cases o <;> simp; nofun
|
||||
cases o <;> simp
|
||||
|
||||
theorem eq_some_of_isSome : ∀ {o : Option α} (h : o.isSome), o = some (o.get h)
|
||||
| some _, _ => rfl
|
||||
@@ -190,6 +190,9 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
|
||||
|
||||
theorem mem_map_of_mem (g : α → β) (h : a ∈ x) : g a ∈ Option.map g x := h.symm ▸ map_some' ..
|
||||
|
||||
@[simp] theorem filter_none (p : α → Bool) : none.filter p = none := rfl
|
||||
theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
|
||||
|
||||
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α → β} :
|
||||
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ protected def Int.repr : Int → String
|
||||
| negSucc m => "-" ++ Nat.repr (succ m)
|
||||
|
||||
instance : Repr Int where
|
||||
reprPrec i _ := i.repr
|
||||
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
|
||||
|
||||
def hexDigitRepr (n : Nat) : String :=
|
||||
String.singleton <| Nat.digitChar n
|
||||
|
||||
@@ -10,58 +10,39 @@ import Init.RCases
|
||||
|
||||
namespace Lean
|
||||
namespace Parser.Attr
|
||||
/-- Registers an extensionality theorem.
|
||||
|
||||
* When `@[ext]` is applied to a structure, it generates `.ext` and `.ext_iff` theorems and registers
|
||||
them for the `ext` tactic.
|
||||
/--
|
||||
The flag `(iff := false)` prevents `ext` from generating an `ext_iff` lemma.
|
||||
-/
|
||||
syntax extIff := atomic("(" &"iff" " := " &"false" ")")
|
||||
|
||||
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic.
|
||||
/--
|
||||
The flag `(flat := false)` causes `ext` to not flatten parents' fields when generating an `ext` lemma.
|
||||
-/
|
||||
syntax extFlat := atomic("(" &"flat" " := " &"false" ")")
|
||||
|
||||
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the lemma. Higher-priority lemmas are chosen first, and the default is `1000`.
|
||||
/--
|
||||
Registers an extensionality theorem.
|
||||
|
||||
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic, and it generates an "`ext_iff`" theorem.
|
||||
The name of the theorem is from adding the suffix `_iff` to the theorem name.
|
||||
|
||||
* When `@[ext]` is applied to a structure, it generates an `.ext` theorem and applies the `@[ext]` attribute to it.
|
||||
The result is an `.ext` and an `.ext_iff` theorem with the `.ext` theorem registered for the `ext` tactic.
|
||||
|
||||
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the `ext` lemma.
|
||||
Higher-priority lemmas are chosen first, and the default is `1000`.
|
||||
|
||||
* The flag `@[ext (iff := false)]` disables generating an `ext_iff` theorem.
|
||||
|
||||
* The flag `@[ext (flat := false)]` causes generated structure extensionality theorems to show inherited fields based on their representation,
|
||||
rather than flattening the parents' fields into the lemma's equality hypotheses.
|
||||
structures in the generated extensionality theorems. -/
|
||||
syntax (name := ext) "ext" (" (" &"flat" " := " term ")")? (ppSpace prio)? : attr
|
||||
-/
|
||||
syntax (name := ext) "ext" (ppSpace extIff)? (ppSpace extFlat)? (ppSpace prio)? : attr
|
||||
end Parser.Attr
|
||||
|
||||
-- TODO: rename this namespace?
|
||||
-- Remark: `ext` has scoped syntax, Mathlib may depend on the actual namespace name.
|
||||
namespace Elab.Tactic.Ext
|
||||
/--
|
||||
Creates the type of the extensionality theorem for the given structure,
|
||||
elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
|
||||
-/
|
||||
scoped syntax (name := extType) "ext_type% " term:max ppSpace ident : term
|
||||
|
||||
/--
|
||||
Creates the type of the iff-variant of the extensionality theorem for the given structure,
|
||||
elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
|
||||
-/
|
||||
scoped syntax (name := extIffType) "ext_iff_type% " term:max ppSpace ident : term
|
||||
|
||||
/--
|
||||
`declare_ext_theorems_for A` declares the extensionality theorems for the structure `A`.
|
||||
|
||||
These theorems state that two expressions with the structure type are equal if their fields are equal.
|
||||
-/
|
||||
syntax (name := declareExtTheoremFor) "declare_ext_theorems_for " ("(" &"flat" " := " term ") ")? ident (ppSpace prio)? : command
|
||||
|
||||
macro_rules | `(declare_ext_theorems_for $[(flat := $f)]? $struct:ident $(prio)?) => do
|
||||
let flat := f.getD (mkIdent `true)
|
||||
let names ← Macro.resolveGlobalName struct.getId.eraseMacroScopes
|
||||
let name ← match names.filter (·.2.isEmpty) with
|
||||
| [] => Macro.throwError s!"unknown constant {struct.getId}"
|
||||
| [(name, _)] => pure name
|
||||
| _ => Macro.throwError s!"ambiguous name {struct.getId}"
|
||||
let extName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext"
|
||||
let extIffName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext_iff"
|
||||
`(@[ext $(prio)?] protected theorem $extName:ident : ext_type% $flat $struct:ident :=
|
||||
fun {..} {..} => by intros; subst_eqs; rfl
|
||||
protected theorem $extIffName:ident : ext_iff_type% $flat $struct:ident :=
|
||||
fun {..} {..} =>
|
||||
⟨fun h => by cases h; and_intros <;> rfl,
|
||||
fun _ => by (repeat cases ‹_ ∧ _›); subst_eqs; rfl⟩)
|
||||
|
||||
/--
|
||||
Applies extensionality lemmas that are registered with the `@[ext]` attribute.
|
||||
@@ -96,19 +77,8 @@ macro "ext1" xs:(colGt ppSpace rintroPat)* : tactic =>
|
||||
end Elab.Tactic.Ext
|
||||
end Lean
|
||||
|
||||
attribute [ext] Prod PProd Sigma PSigma
|
||||
attribute [ext] funext propext Subtype.eq
|
||||
|
||||
@[ext] theorem Prod.ext : {x y : Prod α β} → x.fst = y.fst → x.snd = y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, rfl => rfl
|
||||
|
||||
@[ext] theorem PProd.ext : {x y : PProd α β} → x.fst = y.fst → x.snd = y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, rfl => rfl
|
||||
|
||||
@[ext] theorem Sigma.ext : {x y : Sigma β} → x.fst = y.fst → HEq x.snd y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, .rfl => rfl
|
||||
|
||||
@[ext] theorem PSigma.ext : {x y : PSigma β} → x.fst = y.fst → HEq x.snd y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, .rfl => rfl
|
||||
|
||||
@[ext] protected theorem PUnit.ext (x y : PUnit) : x = y := rfl
|
||||
protected theorem Unit.ext (x y : Unit) : x = y := rfl
|
||||
|
||||
@@ -219,13 +219,13 @@ structure Config where
|
||||
-/
|
||||
index : Bool := true
|
||||
/--
|
||||
When `true` (default: `false`), `simp` will **not** create a proof for a rewriting rule associated
|
||||
When `true` (default: `true`), `simp` will **not** create a proof for a rewriting rule associated
|
||||
with an `rfl`-theorem.
|
||||
Rewriting rules are provided by users by annotating theorems with the attribute `@[simp]`.
|
||||
If the proof of the theorem is just `rfl` (reflexivity), and `implicitDefEqProofs := true`, `simp`
|
||||
will **not** create a proof term which is an application of the annotated theorem.
|
||||
-/
|
||||
implicitDefEqProofs : Bool := false
|
||||
implicitDefEqProofs : Bool := true
|
||||
deriving Inhabited, BEq
|
||||
|
||||
-- Configuration object for `simp_all`
|
||||
|
||||
@@ -267,6 +267,7 @@ syntax (name := rawNatLit) "nat_lit " num : term
|
||||
|
||||
@[inherit_doc] infixr:90 " ∘ " => Function.comp
|
||||
@[inherit_doc] infixr:35 " × " => Prod
|
||||
@[inherit_doc] infixr:35 " ×' " => PProd
|
||||
|
||||
@[inherit_doc] infix:50 " ∣ " => Dvd.dvd
|
||||
@[inherit_doc] infixl:55 " ||| " => HOr.hOr
|
||||
@@ -703,6 +704,28 @@ syntax (name := checkSimp) "#check_simp " term "~>" term : command
|
||||
-/
|
||||
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
|
||||
|
||||
/--
|
||||
`#discr_tree_key t` prints the discrimination tree keys for a term `t` (or, if it is a single identifier, the type of that constant).
|
||||
It uses the default configuration for generating keys.
|
||||
|
||||
For example,
|
||||
```
|
||||
#discr_tree_key (∀ {a n : Nat}, bar a (OfNat.ofNat n))
|
||||
-- bar _ (@OfNat.ofNat Nat _ _)
|
||||
|
||||
#discr_tree_simp_key Nat.add_assoc
|
||||
-- @HAdd.hAdd Nat Nat Nat _ (@HAdd.hAdd Nat Nat Nat _ _ _) _
|
||||
```
|
||||
|
||||
`#discr_tree_simp_key` is similar to `#discr_tree_key`, but treats the underlying type
|
||||
as one of a simp lemma, i.e. transforms it into an equality and produces the key of the
|
||||
left-hand side.
|
||||
-/
|
||||
syntax (name := discrTreeKeyCmd) "#discr_tree_key " term : command
|
||||
|
||||
@[inherit_doc discrTreeKeyCmd]
|
||||
syntax (name := discrTreeSimpKeyCmd) "#discr_tree_simp_key" term : command
|
||||
|
||||
/--
|
||||
The `seal foo` command ensures that the definition of `foo` is sealed, meaning it is marked as `[irreducible]`.
|
||||
This command is particularly useful in contexts where you want to prevent the reduction of `foo` in proofs.
|
||||
|
||||
@@ -38,6 +38,10 @@ theorem ext {a b : LinearCombo} (w₁ : a.const = b.const) (w₂ : a.coeffs = b.
|
||||
subst w₁; subst w₂
|
||||
congr
|
||||
|
||||
/-- Check if a linear combination is an atom, i.e. the constant term is zero and there is exactly one nonzero coefficient, which is one. -/
|
||||
def isAtom (a : LinearCombo) : Bool :=
|
||||
a.const == 0 && (a.coeffs.filter (· == 1)).length == 1 && a.coeffs.all fun c => c == 0 || c == 1
|
||||
|
||||
/--
|
||||
Evaluate a linear combination `⟨r, [c_1, …, c_k]⟩` at values `[v_1, …, v_k]` to obtain
|
||||
`r + (c_1 * x_1 + (c_2 * x_2 + ... (c_k * x_k + 0))))`.
|
||||
|
||||
@@ -488,9 +488,9 @@ attribute [unbox] Prod
|
||||
|
||||
/--
|
||||
Similar to `Prod`, but `α` and `β` can be propositions.
|
||||
You can use `α ×' β` as notation for `PProd α β`.
|
||||
We use this type internally to automatically generate the `brecOn` recursor.
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure PProd (α : Sort u) (β : Sort v) where
|
||||
/-- The first projection out of a pair. if `p : PProd α β` then `p.1 : α`. -/
|
||||
fst : α
|
||||
@@ -3172,8 +3172,8 @@ class MonadStateOf (σ : semiOutParam (Type u)) (m : Type u → Type v) where
|
||||
export MonadStateOf (set)
|
||||
|
||||
/--
|
||||
Like `withReader`, but with `ρ` explicit. This is useful if a monad supports
|
||||
`MonadWithReaderOf` for multiple different types `ρ`.
|
||||
Like `get`, but with `σ` explicit. This is useful if a monad supports
|
||||
`MonadStateOf` for multiple different types `σ`.
|
||||
-/
|
||||
abbrev getThe (σ : Type u) {m : Type u → Type v} [MonadStateOf σ m] : m σ :=
|
||||
MonadStateOf.get
|
||||
|
||||
@@ -253,6 +253,9 @@ end forall_congr
|
||||
|
||||
@[simp] theorem not_exists : (¬∃ x, p x) ↔ ∀ x, ¬p x := exists_imp
|
||||
|
||||
theorem forall_not_of_not_exists (h : ¬∃ x, p x) : ∀ x, ¬p x := not_exists.mp h
|
||||
theorem not_exists_of_forall_not (h : ∀ x, ¬p x) : ¬∃ x, p x := not_exists.mpr h
|
||||
|
||||
theorem forall_and : (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
|
||||
⟨fun h => ⟨fun x => (h x).1, fun x => (h x).2⟩, fun ⟨h₁, h₂⟩ x => ⟨h₁ x, h₂ x⟩⟩
|
||||
|
||||
@@ -292,6 +295,8 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
|
||||
|
||||
@[simp] theorem exists_eq_left' : (∃ a, a' = a ∧ p a) ↔ p a' := by simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem exists_eq_right' : (∃ a, p a ∧ a' = a) ↔ p a' := by simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem forall_eq_or_imp : (∀ a, a = a' ∨ q a → p a) ↔ p a' ∧ ∀ a, q a → p a := by
|
||||
simp only [or_imp, forall_and, forall_eq]
|
||||
|
||||
@@ -304,6 +309,11 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
|
||||
@[simp] theorem exists_eq_right_right' : (∃ (a : α), p a ∧ q a ∧ a' = a) ↔ p a' ∧ q a' := by
|
||||
simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem exists_or_eq_left (y : α) (p : α → Prop) : ∃ x : α, x = y ∨ p x := ⟨y, .inl rfl⟩
|
||||
@[simp] theorem exists_or_eq_right (y : α) (p : α → Prop) : ∃ x : α, p x ∨ x = y := ⟨y, .inr rfl⟩
|
||||
@[simp] theorem exists_or_eq_left' (y : α) (p : α → Prop) : ∃ x : α, y = x ∨ p x := ⟨y, .inl rfl⟩
|
||||
@[simp] theorem exists_or_eq_right' (y : α) (p : α → Prop) : ∃ x : α, p x ∨ y = x := ⟨y, .inr rfl⟩
|
||||
|
||||
@[simp] theorem exists_prop : (∃ _h : a, b) ↔ a ∧ b :=
|
||||
⟨fun ⟨hp, hq⟩ => ⟨hp, hq⟩, fun ⟨hp, hq⟩ => ⟨hp, hq⟩⟩
|
||||
|
||||
|
||||
@@ -102,3 +102,11 @@ instance ShareCommonT.monadShareCommon [Monad m] : MonadShareCommon (ShareCommon
|
||||
|
||||
@[inline] def ShareCommonT.run [Monad m] (x : ShareCommonT σ m α) : m α := x.run' default
|
||||
@[inline] def ShareCommonM.run (x : ShareCommonM σ α) : α := ShareCommonT.run x
|
||||
|
||||
/--
|
||||
A more restrictive but efficient max sharing primitive.
|
||||
|
||||
Remark: it optimizes the number of RC operations, and the strategy for caching results.
|
||||
-/
|
||||
@[extern "lean_sharecommon_quick"]
|
||||
def ShareCommon.shareCommon' (a : α) : α := a
|
||||
|
||||
@@ -129,6 +129,7 @@ instance : Std.LawfulIdentity Or False where
|
||||
@[simp] theorem iff_false (p : Prop) : (p ↔ False) = ¬p := propext ⟨(·.1), (⟨·, False.elim⟩)⟩
|
||||
@[simp] theorem false_iff (p : Prop) : (False ↔ p) = ¬p := propext ⟨(·.2), (⟨False.elim, ·⟩)⟩
|
||||
@[simp] theorem false_implies (p : Prop) : (False → p) = True := eq_true False.elim
|
||||
@[simp] theorem forall_false (p : False → Prop) : (∀ h : False, p h) = True := eq_true (False.elim ·)
|
||||
@[simp] theorem implies_true (α : Sort u) : (α → True) = True := eq_true fun _ => trivial
|
||||
@[simp] theorem true_implies (p : Prop) : (True → p) = p := propext ⟨(· trivial), (fun _ => ·)⟩
|
||||
@[simp] theorem not_false_eq_true : (¬ False) = True := eq_true False.elim
|
||||
|
||||
@@ -712,8 +712,17 @@ structure Child (cfg : StdioConfig) where
|
||||
|
||||
@[extern "lean_io_process_spawn"] opaque spawn (args : SpawnArgs) : IO (Child args.toStdioConfig)
|
||||
|
||||
/--
|
||||
Block until the child process has exited and return its exit code.
|
||||
-/
|
||||
@[extern "lean_io_process_child_wait"] opaque Child.wait {cfg : @& StdioConfig} : @& Child cfg → IO UInt32
|
||||
|
||||
/--
|
||||
Check whether the child has exited yet. If it hasn't return none, otherwise its exit code.
|
||||
-/
|
||||
@[extern "lean_io_process_child_try_wait"] opaque Child.tryWait {cfg : @& StdioConfig} : @& Child cfg →
|
||||
IO (Option UInt32)
|
||||
|
||||
/-- Terminates the child process using the SIGTERM signal or a platform analogue.
|
||||
If the process was started using `SpawnArgs.setsid`, terminates the entire process group instead. -/
|
||||
@[extern "lean_io_process_child_kill"] opaque Child.kill {cfg : @& StdioConfig} : @& Child cfg → IO Unit
|
||||
|
||||
@@ -45,6 +45,13 @@ def dbgSleep {α : Type u} (ms : UInt32) (f : Unit → α) : α := f ()
|
||||
@[extern "lean_ptr_addr"]
|
||||
unsafe opaque ptrAddrUnsafe {α : Type u} (a : @& α) : USize
|
||||
|
||||
/--
|
||||
Returns `true` if `a` is an exclusive object.
|
||||
We say an object is exclusive if it is single-threaded and its reference counter is 1.
|
||||
-/
|
||||
@[extern "lean_is_exclusive_obj"]
|
||||
unsafe opaque isExclusiveUnsafe {α : Type u} (a : @& α) : Bool
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
@[inline] unsafe def withPtrAddrUnsafe {α : Type u} {β : Type v} (a : α) (k : USize → β) (h : ∀ u₁ u₂, k u₁ = k u₂) : β :=
|
||||
k (ptrAddrUnsafe a)
|
||||
|
||||
@@ -148,22 +148,26 @@ end InvImage
|
||||
wf := InvImage.wf f h.wf
|
||||
|
||||
-- The transitive closure of a well-founded relation is well-founded
|
||||
namespace TC
|
||||
variable {α : Sort u} {r : α → α → Prop}
|
||||
open Relation
|
||||
|
||||
theorem accessible {z : α} (ac : Acc r z) : Acc (TC r) z := by
|
||||
induction ac with
|
||||
| intro x acx ih =>
|
||||
apply Acc.intro x
|
||||
intro y rel
|
||||
induction rel with
|
||||
| base a b rab => exact ih a rab
|
||||
| trans a b c rab _ _ ih₂ => apply Acc.inv (ih₂ acx ih) rab
|
||||
theorem Acc.transGen (h : Acc r a) : Acc (TransGen r) a := by
|
||||
induction h with
|
||||
| intro x _ H =>
|
||||
refine Acc.intro x fun y hy ↦ ?_
|
||||
cases hy with
|
||||
| single hyx =>
|
||||
exact H y hyx
|
||||
| tail hyz hzx =>
|
||||
exact (H _ hzx).inv hyz
|
||||
|
||||
theorem wf (h : WellFounded r) : WellFounded (TC r) :=
|
||||
⟨fun a => accessible (apply h a)⟩
|
||||
end TC
|
||||
theorem acc_transGen_iff : Acc (TransGen r) a ↔ Acc r a :=
|
||||
⟨Subrelation.accessible TransGen.single, Acc.transGen⟩
|
||||
|
||||
theorem WellFounded.transGen (h : WellFounded r) : WellFounded (TransGen r) :=
|
||||
⟨fun a ↦ (h.apply a).transGen⟩
|
||||
|
||||
@[deprecated Acc.transGen (since := "2024-07-16")] abbrev TC.accessible := @Acc.transGen
|
||||
@[deprecated WellFounded.transGen (since := "2024-07-16")] abbrev TC.wf := @WellFounded.transGen
|
||||
namespace Nat
|
||||
|
||||
-- less-than is well-founded
|
||||
|
||||
@@ -37,7 +37,7 @@ def isAuxRecursor (env : Environment) (declName : Name) : Bool :=
|
||||
|
||||
def isAuxRecursorWithSuffix (env : Environment) (declName : Name) (suffix : String) : Bool :=
|
||||
match declName with
|
||||
| .str _ s => s == suffix && isAuxRecursor env declName
|
||||
| .str _ s => (s == suffix || s.startsWith s!"{suffix}_") && isAuxRecursor env declName
|
||||
| _ => false
|
||||
|
||||
def isCasesOnRecursor (env : Environment) (declName : Name) : Bool :=
|
||||
|
||||
@@ -94,7 +94,7 @@ def emitCInitName (n : Name) : M Unit :=
|
||||
def shouldExport (n : Name) : Bool :=
|
||||
-- HACK: exclude symbols very unlikely to be used by the interpreter or other consumers of
|
||||
-- libleanshared to avoid Windows symbol limit
|
||||
!(`Lean.Compiler.LCNF).isPrefixOf n
|
||||
!(`Lean.Compiler.LCNF).isPrefixOf n && !(`Lean.IR).isPrefixOf n && !(`Lean.Server).isPrefixOf n
|
||||
|
||||
def emitFnDeclAux (decl : Decl) (cppBaseName : String) (isExternal : Bool) : M Unit := do
|
||||
let ps := decl.params
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Compiler.Options
|
||||
import Lean.Compiler.ExternAttr
|
||||
import Lean.Compiler.LCNF.PassManager
|
||||
import Lean.Compiler.LCNF.Passes
|
||||
import Lean.Compiler.LCNF.PrettyPrinter
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.PrettyPrinter
|
||||
import Lean.PrettyPrinter.Delaborator.Options
|
||||
import Lean.Compiler.LCNF.CompilerM
|
||||
import Lean.Compiler.LCNF.Internalize
|
||||
|
||||
|
||||
@@ -80,6 +80,10 @@ protected def max : RBNode α β → Option (Sigma (fun k => β k))
|
||||
def singleton (k : α) (v : β k) : RBNode α β :=
|
||||
node red leaf k v leaf
|
||||
|
||||
def isSingleton : RBNode α β → Bool
|
||||
| node _ leaf _ _ leaf => true
|
||||
| _ => false
|
||||
|
||||
-- the first half of Okasaki's `balance`, concerning red-red sequences in the left child
|
||||
@[inline] def balance1 : RBNode α β → (a : α) → β a → RBNode α β → RBNode α β
|
||||
| node red (node red a kx vx b) ky vy c, kz, vz, d
|
||||
@@ -269,6 +273,9 @@ variable {α : Type u} {β : Type v} {σ : Type w} {cmp : α → α → Ordering
|
||||
def depth (f : Nat → Nat → Nat) (t : RBMap α β cmp) : Nat :=
|
||||
t.val.depth f
|
||||
|
||||
def isSingleton (t : RBMap α β cmp) : Bool :=
|
||||
t.val.isSingleton
|
||||
|
||||
@[inline] def fold (f : σ → α → β → σ) : (init : σ) → RBMap α β cmp → σ
|
||||
| b, ⟨t, _⟩ => t.fold f b
|
||||
|
||||
|
||||
@@ -87,6 +87,11 @@ def switch (m : SMap α β) : SMap α β :=
|
||||
@[inline] def foldStage2 {σ : Type w} (f : σ → α → β → σ) (s : σ) (m : SMap α β) : σ :=
|
||||
m.map₂.foldl f s
|
||||
|
||||
/-- Monadic fold over a staged map. -/
|
||||
def foldM {m : Type w → Type w} [Monad m]
|
||||
(f : σ → α → β → m σ) (init : σ) (map : SMap α β) : m σ := do
|
||||
map.map₂.foldlM f (← map.map₁.foldM f init)
|
||||
|
||||
def fold {σ : Type w} (f : σ → α → β → σ) (init : σ) (m : SMap α β) : σ :=
|
||||
m.map₂.foldl f $ m.map₁.fold f init
|
||||
|
||||
|
||||
@@ -239,6 +239,10 @@ structure InductiveVal extends ConstantVal where
|
||||
all : List Name
|
||||
/-- List of the names of the constructors for this inductive datatype. -/
|
||||
ctors : List Name
|
||||
/-- Number of auxillary data types produced from nested occurrences.
|
||||
An inductive definition `T` is nested when there is a constructor with an argument `x : F T`,
|
||||
where `F : Type → Type` is some suitably behaved (ie strictly positive) function (Eg `Array T`, `List T`, `T × T`, ...). -/
|
||||
numNested : Nat
|
||||
/-- `true` when recursive (that is, the inductive type appears as an argument in a constructor). -/
|
||||
isRec : Bool
|
||||
/-- Whether the definition is flagged as unsafe. -/
|
||||
@@ -257,14 +261,12 @@ structure InductiveVal extends ConstantVal where
|
||||
Section 2.2, Definition 3
|
||||
-/
|
||||
isReflexive : Bool
|
||||
/-- An inductive definition `T` is nested when there is a constructor with an argument `x : F T`,
|
||||
where `F : Type → Type` is some suitably behaved (ie strictly positive) function (Eg `Array T`, `List T`, `T × T`, ...). -/
|
||||
isNested : Bool
|
||||
|
||||
deriving Inhabited
|
||||
|
||||
@[export lean_mk_inductive_val]
|
||||
def mkInductiveValEx (name : Name) (levelParams : List Name) (type : Expr) (numParams numIndices : Nat)
|
||||
(all ctors : List Name) (isRec isUnsafe isReflexive isNested : Bool) : InductiveVal := {
|
||||
(all ctors : List Name) (numNested : Nat) (isRec isUnsafe isReflexive : Bool) : InductiveVal := {
|
||||
name := name
|
||||
levelParams := levelParams
|
||||
type := type
|
||||
@@ -272,18 +274,19 @@ def mkInductiveValEx (name : Name) (levelParams : List Name) (type : Expr) (numP
|
||||
numIndices := numIndices
|
||||
all := all
|
||||
ctors := ctors
|
||||
numNested := numNested
|
||||
isRec := isRec
|
||||
isUnsafe := isUnsafe
|
||||
isReflexive := isReflexive
|
||||
isNested := isNested
|
||||
}
|
||||
|
||||
@[export lean_inductive_val_is_rec] def InductiveVal.isRecEx (v : InductiveVal) : Bool := v.isRec
|
||||
@[export lean_inductive_val_is_unsafe] def InductiveVal.isUnsafeEx (v : InductiveVal) : Bool := v.isUnsafe
|
||||
@[export lean_inductive_val_is_reflexive] def InductiveVal.isReflexiveEx (v : InductiveVal) : Bool := v.isReflexive
|
||||
@[export lean_inductive_val_is_nested] def InductiveVal.isNestedEx (v : InductiveVal) : Bool := v.isNested
|
||||
|
||||
def InductiveVal.numCtors (v : InductiveVal) : Nat := v.ctors.length
|
||||
def InductiveVal.isNested (v : InductiveVal) : Bool := v.numNested > 0
|
||||
def InductiveVal.numTypeFormers (v : InductiveVal) : Nat := v.all.length + v.numNested
|
||||
|
||||
structure ConstructorVal extends ConstantVal where
|
||||
/-- Inductive type this constructor is a member of -/
|
||||
|
||||
@@ -742,7 +742,10 @@ def mkMotive (discrs : Array Expr) (expectedType : Expr): MetaM Expr := do
|
||||
let motiveBody ← kabstract motive discr
|
||||
/- We use `transform (usedLetOnly := true)` to eliminate unnecessary let-expressions. -/
|
||||
let discrType ← transform (usedLetOnly := true) (← instantiateMVars (← inferType discr))
|
||||
return Lean.mkLambda (← mkFreshBinderName) BinderInfo.default discrType motiveBody
|
||||
let motive := Lean.mkLambda (← mkFreshBinderName) BinderInfo.default discrType motiveBody
|
||||
unless (← isTypeCorrect motive) do
|
||||
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motive}"
|
||||
return motive
|
||||
|
||||
/-- If the eliminator is over-applied, we "revert" the extra arguments. -/
|
||||
def revertArgs (args : List Arg) (f : Expr) (expectedType : Expr) : TermElabM (Expr × Expr) :=
|
||||
@@ -1292,6 +1295,7 @@ private partial def elabAppFnId (fIdent : Syntax) (fExplicitUnivs : List Level)
|
||||
funLVals.foldlM (init := acc) fun acc (f, fIdent, fields) => do
|
||||
let lvals' := toLVals fields (first := true)
|
||||
let s ← observing do
|
||||
checkDeprecated fIdent f
|
||||
let f ← addTermInfo fIdent f expectedType?
|
||||
let e ← elabAppLVals f (lvals' ++ lvals) namedArgs args expectedType? explicit ellipsis
|
||||
if overloaded then ensureHasType expectedType? e else return e
|
||||
|
||||
@@ -11,7 +11,6 @@ import Lean.Elab.Eval
|
||||
import Lean.Elab.Command
|
||||
import Lean.Elab.Open
|
||||
import Lean.Elab.SetOption
|
||||
import Lean.PrettyPrinter
|
||||
|
||||
namespace Lean.Elab.Command
|
||||
|
||||
|
||||
@@ -220,6 +220,31 @@ partial def mkPairs (elems : Array Term) : MacroM Term :=
|
||||
pure acc
|
||||
loop (elems.size - 1) elems.back
|
||||
|
||||
/-- Return syntax `PProd.mk elems[0] (PProd.mk elems[1] ... (PProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
|
||||
partial def mkPPairs (elems : Array Term) : MacroM Term :=
|
||||
let rec loop (i : Nat) (acc : Term) := do
|
||||
if i > 0 then
|
||||
let i := i - 1
|
||||
let elem := elems[i]!
|
||||
let acc ← `(PProd.mk $elem $acc)
|
||||
loop i acc
|
||||
else
|
||||
pure acc
|
||||
loop (elems.size - 1) elems.back
|
||||
|
||||
/-- Return syntax `MProd.mk elems[0] (MProd.mk elems[1] ... (MProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
|
||||
partial def mkMPairs (elems : Array Term) : MacroM Term :=
|
||||
let rec loop (i : Nat) (acc : Term) := do
|
||||
if i > 0 then
|
||||
let i := i - 1
|
||||
let elem := elems[i]!
|
||||
let acc ← `(MProd.mk $elem $acc)
|
||||
loop i acc
|
||||
else
|
||||
pure acc
|
||||
loop (elems.size - 1) elems.back
|
||||
|
||||
|
||||
open Parser in
|
||||
partial def hasCDot : Syntax → Bool
|
||||
| Syntax.node _ k args =>
|
||||
|
||||
@@ -316,9 +316,7 @@ private def mkSilentAnnotationIfHole (e : Expr) : TermElabM Expr := do
|
||||
return false
|
||||
return true
|
||||
if canClear then
|
||||
let lctx := (← getLCtx).erase fvarId
|
||||
let localInsts := (← getLocalInstances).filter (·.fvar.fvarId! != fvarId)
|
||||
withLCtx lctx localInsts do elabTerm body expectedType?
|
||||
withErasedFVars #[fvarId] do elabTerm body expectedType?
|
||||
else
|
||||
elabTerm body expectedType?
|
||||
|
||||
@@ -364,4 +362,7 @@ private opaque evalFilePath (stx : Syntax) : TermElabM System.FilePath
|
||||
mkStrLit <$> IO.FS.readFile path
|
||||
| _, _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_term_elab Lean.Parser.Term.namedPattern] def elabNamedPatternErr : TermElab := fun stx _ =>
|
||||
throwError "`<identifier>@<term>` is a named pattern and can only be used in pattern matching contexts{indentD stx}"
|
||||
|
||||
end Lean.Elab.Term
|
||||
|
||||
@@ -672,8 +672,7 @@ partial def main (patternVarDecls : Array PatternVarDecl) (ps : Array Expr) (mat
|
||||
throwError "invalid patterns, `{mkFVar explicit}` is an explicit pattern variable, but it only occurs in positions that are inaccessible to pattern matching{indentD (MessageData.joinSep (ps.toList.map (MessageData.ofExpr .)) m!"\n\n")}"
|
||||
let packed ← pack patternVars ps matchType
|
||||
trace[Elab.match] "packed: {packed}"
|
||||
let lctx := explicitPatternVars.foldl (init := (← getLCtx)) fun lctx d => lctx.erase d
|
||||
withTheReader Meta.Context (fun ctx => { ctx with lctx := lctx }) do
|
||||
withErasedFVars explicitPatternVars do
|
||||
check packed
|
||||
unpack packed fun patternVars patterns matchType => do
|
||||
let localDecls ← patternVars.mapM fun x => x.fvarId!.getDecl
|
||||
|
||||
@@ -728,12 +728,26 @@ def insertReplacementForLetRecs (r : Replacement) (letRecClosures : List LetRecC
|
||||
letRecClosures.foldl (init := r) fun r c =>
|
||||
r.insert c.toLift.fvarId c.closed
|
||||
|
||||
def isApplicable (r : Replacement) (e : Expr) : Bool :=
|
||||
Option.isSome <| e.findExt? fun e =>
|
||||
if e.hasFVar then
|
||||
match e with
|
||||
| .fvar fvarId => if r.contains fvarId then .found else .done
|
||||
| _ => .visit
|
||||
else
|
||||
.done
|
||||
|
||||
def Replacement.apply (r : Replacement) (e : Expr) : Expr :=
|
||||
e.replace fun e => match e with
|
||||
| .fvar fvarId => match r.find? fvarId with
|
||||
| some c => some c
|
||||
| _ => none
|
||||
| _ => none
|
||||
-- Remark: if `r` is not a singlenton, then declaration is using `mutual` or `let rec`,
|
||||
-- and there is a big chance `isApplicable r e` is true.
|
||||
if r.isSingleton && !isApplicable r e then
|
||||
e
|
||||
else
|
||||
e.replace fun e => match e with
|
||||
| .fvar fvarId => match r.find? fvarId with
|
||||
| some c => some c
|
||||
| _ => none
|
||||
| _ => none
|
||||
|
||||
def pushMain (preDefs : Array PreDefinition) (sectionVars : Array Expr) (mainHeaders : Array DefViewElabHeader) (mainVals : Array Expr)
|
||||
: TermElabM (Array PreDefinition) :=
|
||||
@@ -923,6 +937,7 @@ where
|
||||
trace[Elab.definition] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
let preDefs ← withLevelNames allUserLevelNames <| levelMVarToParamPreDecls preDefs
|
||||
let preDefs ← instantiateMVarsAtPreDecls preDefs
|
||||
let preDefs ← shareCommonPreDefs preDefs
|
||||
let preDefs ← fixLevelParams preDefs scopeLevelNames allUserLevelNames
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition] "after eraseAuxDiscr, {preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.ShareCommon
|
||||
import Lean.Compiler.NoncomputableAttr
|
||||
import Lean.Util.CollectLevelParams
|
||||
import Lean.Meta.AbstractNestedProofs
|
||||
@@ -53,18 +54,20 @@ private def getLevelParamsPreDecls (preDefs : Array PreDefinition) (scopeLevelNa
|
||||
| Except.ok levelParams => pure levelParams
|
||||
|
||||
def fixLevelParams (preDefs : Array PreDefinition) (scopeLevelNames allUserLevelNames : List Name) : TermElabM (Array PreDefinition) := do
|
||||
-- We used to use `shareCommon` here, but is was a bottleneck
|
||||
let levelParams ← getLevelParamsPreDecls preDefs scopeLevelNames allUserLevelNames
|
||||
let us := levelParams.map mkLevelParam
|
||||
let fixExpr (e : Expr) : Expr :=
|
||||
e.replace fun c => match c with
|
||||
| Expr.const declName _ => if preDefs.any fun preDef => preDef.declName == declName then some $ Lean.mkConst declName us else none
|
||||
| _ => none
|
||||
return preDefs.map fun preDef =>
|
||||
{ preDef with
|
||||
type := fixExpr preDef.type,
|
||||
value := fixExpr preDef.value,
|
||||
levelParams := levelParams }
|
||||
profileitM Exception s!"fix level params" (← getOptions) do
|
||||
withTraceNode `Elab.def.fixLevelParams (fun _ => return m!"fix level params") do
|
||||
-- We used to use `shareCommon` here, but is was a bottleneck
|
||||
let levelParams ← getLevelParamsPreDecls preDefs scopeLevelNames allUserLevelNames
|
||||
let us := levelParams.map mkLevelParam
|
||||
let fixExpr (e : Expr) : Expr :=
|
||||
e.replace fun c => match c with
|
||||
| Expr.const declName _ => if preDefs.any fun preDef => preDef.declName == declName then some $ Lean.mkConst declName us else none
|
||||
| _ => none
|
||||
return preDefs.map fun preDef =>
|
||||
{ preDef with
|
||||
type := fixExpr preDef.type,
|
||||
value := fixExpr preDef.value,
|
||||
levelParams := levelParams }
|
||||
|
||||
def applyAttributesOf (preDefs : Array PreDefinition) (applicationTime : AttributeApplicationTime) : TermElabM Unit := do
|
||||
for preDef in preDefs do
|
||||
@@ -210,4 +213,17 @@ def checkCodomainsLevel (preDefs : Array PreDefinition) : MetaM Unit := do
|
||||
m!"for `{preDefs[0]!.declName}` is{indentExpr type₀} : {← inferType type₀}\n" ++
|
||||
m!"and for `{preDefs[i]!.declName}` is{indentExpr typeᵢ} : {← inferType typeᵢ}"
|
||||
|
||||
def shareCommonPreDefs (preDefs : Array PreDefinition) : CoreM (Array PreDefinition) := do
|
||||
profileitM Exception "share common exprs" (← getOptions) do
|
||||
withTraceNode `Elab.def.maxSharing (fun _ => return m!"share common exprs") do
|
||||
let mut es := #[]
|
||||
for preDef in preDefs do
|
||||
es := es.push preDef.type |>.push preDef.value
|
||||
es := ShareCommon.shareCommon' es
|
||||
let mut result := #[]
|
||||
for h : i in [:preDefs.size] do
|
||||
let preDef := preDefs[i]
|
||||
result := result.push { preDef with type := es[2*i]!, value := es[2*i+1]! }
|
||||
return result
|
||||
|
||||
end Lean.Elab
|
||||
|
||||
@@ -333,7 +333,7 @@ def tryContradiction (mvarId : MVarId) : MetaM Bool := do
|
||||
partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
|
||||
let some eqs ← getEqnsFor? declName | throwError "failed to generate equations for '{declName}'"
|
||||
let tryEqns (mvarId : MVarId) : MetaM Bool :=
|
||||
eqs.anyM fun eq => commitWhen do
|
||||
eqs.anyM fun eq => commitWhen do checkpointDefEq (mayPostpone := false) do
|
||||
try
|
||||
let subgoals ← mvarId.apply (← mkConstWithFreshMVarLevels eq)
|
||||
subgoals.allM fun subgoal => do
|
||||
|
||||
@@ -111,7 +111,7 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
|
||||
preDefWith.termination.terminationBy? matches some {structural := true, ..}
|
||||
for preDef in preDefs do
|
||||
if let .some termBy := preDef.termination.terminationBy? then
|
||||
if !preDefsWithout.isEmpty then
|
||||
if !structural && !preDefsWithout.isEmpty then
|
||||
let m := MessageData.andList (preDefsWithout.toList.map (m!"{·.declName}"))
|
||||
let doOrDoes := if preDefsWithout.size = 1 then "does" else "do"
|
||||
logErrorAt termBy.ref (m!"Incomplete set of `termination_by` annotations:\n"++
|
||||
@@ -135,13 +135,12 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
|
||||
/--
|
||||
Elaborates the `TerminationHint` in the clique to `TerminationArguments`
|
||||
-/
|
||||
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Option TerminationArguments) := do
|
||||
let tas ← preDefs.mapM fun preDef => do
|
||||
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Array (Option TerminationArgument)) := do
|
||||
preDefs.mapM fun preDef => do
|
||||
let arity ← lambdaTelescope preDef.value fun xs _ => pure xs.size
|
||||
let hints := preDef.termination
|
||||
hints.terminationBy?.mapM
|
||||
(TerminationArgument.elab preDef.declName preDef.type arity hints.extraParams ·)
|
||||
return tas.sequenceMap id -- only return something if every function has a hint
|
||||
|
||||
def shouldUseStructural (preDefs : Array PreDefinition) : Bool :=
|
||||
preDefs.any fun preDef =>
|
||||
@@ -154,68 +153,70 @@ def shouldUseWF (preDefs : Array PreDefinition) : Bool :=
|
||||
|
||||
|
||||
def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLCtx {} {} do
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition.body] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
let preDefs ← preDefs.mapM ensureNoUnassignedMVarsAtPreDef
|
||||
let preDefs ← betaReduceLetRecApps preDefs
|
||||
let cliques := partitionPreDefs preDefs
|
||||
for preDefs in cliques do
|
||||
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"
|
||||
if preDefs.size == 1 && isNonRecursive preDefs[0]! then
|
||||
/-
|
||||
We must erase `recApp` annotations even when `preDef` is not recursive
|
||||
because it may use another recursive declaration in the same mutual block.
|
||||
See issue #2321
|
||||
-/
|
||||
let preDef ← eraseRecAppSyntax preDefs[0]!
|
||||
ensureEqnReservedNamesAvailable preDef.declName
|
||||
if preDef.modifiers.isNoncomputable then
|
||||
addNonRec preDef
|
||||
else
|
||||
addAndCompileNonRec preDef
|
||||
preDef.termination.ensureNone "not recursive"
|
||||
else if preDefs.any (·.modifiers.isUnsafe) then
|
||||
addAndCompileUnsafe preDefs
|
||||
preDefs.forM (·.termination.ensureNone "unsafe")
|
||||
else if preDefs.any (·.modifiers.isPartial) then
|
||||
profileitM Exception "process pre-definitions" (← getOptions) do
|
||||
withTraceNode `Elab.def.processPreDef (fun _ => return m!"process pre-definitions") do
|
||||
for preDef in preDefs do
|
||||
if preDef.modifiers.isPartial && !(← whnfD preDef.type).isForall then
|
||||
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
|
||||
addAndCompilePartial preDefs
|
||||
preDefs.forM (·.termination.ensureNone "partial")
|
||||
else
|
||||
ensureFunIndReservedNamesAvailable preDefs
|
||||
try
|
||||
checkCodomainsLevel preDefs
|
||||
checkTerminationByHints preDefs
|
||||
let termArgs ← elabTerminationByHints preDefs
|
||||
if shouldUseStructural preDefs then
|
||||
structuralRecursion preDefs termArgs
|
||||
else if shouldUseWF preDefs then
|
||||
wfRecursion preDefs termArgs
|
||||
trace[Elab.definition.body] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
let preDefs ← preDefs.mapM ensureNoUnassignedMVarsAtPreDef
|
||||
let preDefs ← betaReduceLetRecApps preDefs
|
||||
let cliques := partitionPreDefs preDefs
|
||||
for preDefs in cliques do
|
||||
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"
|
||||
if preDefs.size == 1 && isNonRecursive preDefs[0]! then
|
||||
/-
|
||||
We must erase `recApp` annotations even when `preDef` is not recursive
|
||||
because it may use another recursive declaration in the same mutual block.
|
||||
See issue #2321
|
||||
-/
|
||||
let preDef ← eraseRecAppSyntax preDefs[0]!
|
||||
ensureEqnReservedNamesAvailable preDef.declName
|
||||
if preDef.modifiers.isNoncomputable then
|
||||
addNonRec preDef
|
||||
else
|
||||
addAndCompileNonRec preDef
|
||||
preDef.termination.ensureNone "not recursive"
|
||||
else if preDefs.any (·.modifiers.isUnsafe) then
|
||||
addAndCompileUnsafe preDefs
|
||||
preDefs.forM (·.termination.ensureNone "unsafe")
|
||||
else if preDefs.any (·.modifiers.isPartial) then
|
||||
for preDef in preDefs do
|
||||
if preDef.modifiers.isPartial && !(← whnfD preDef.type).isForall then
|
||||
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
|
||||
addAndCompilePartial preDefs
|
||||
preDefs.forM (·.termination.ensureNone "partial")
|
||||
else
|
||||
withRef (preDefs[0]!.ref) <| mapError
|
||||
(orelseMergeErrors
|
||||
(structuralRecursion preDefs termArgs)
|
||||
(wfRecursion preDefs termArgs))
|
||||
(fun msg =>
|
||||
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
|
||||
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
|
||||
catch ex =>
|
||||
logException ex
|
||||
let s ← saveState
|
||||
try
|
||||
if preDefs.all fun preDef => preDef.kind == DefKind.def || preDefs.all fun preDef => preDef.kind == DefKind.abbrev then
|
||||
-- try to add as partial definition
|
||||
ensureFunIndReservedNamesAvailable preDefs
|
||||
try
|
||||
checkCodomainsLevel preDefs
|
||||
checkTerminationByHints preDefs
|
||||
let termArg?s ← elabTerminationByHints preDefs
|
||||
if shouldUseStructural preDefs then
|
||||
structuralRecursion preDefs termArg?s
|
||||
else if shouldUseWF preDefs then
|
||||
wfRecursion preDefs termArg?s
|
||||
else
|
||||
withRef (preDefs[0]!.ref) <| mapError
|
||||
(orelseMergeErrors
|
||||
(structuralRecursion preDefs termArg?s)
|
||||
(wfRecursion preDefs termArg?s))
|
||||
(fun msg =>
|
||||
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
|
||||
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
|
||||
catch ex =>
|
||||
logException ex
|
||||
let s ← saveState
|
||||
try
|
||||
addAndCompilePartial preDefs (useSorry := true)
|
||||
catch _ =>
|
||||
-- Compilation failed try again just as axiom
|
||||
s.restore
|
||||
addAsAxioms preDefs
|
||||
else if preDefs.all fun preDef => preDef.kind == DefKind.theorem then
|
||||
addAsAxioms preDefs
|
||||
catch _ => s.restore
|
||||
if preDefs.all fun preDef => preDef.kind == DefKind.def || preDefs.all fun preDef => preDef.kind == DefKind.abbrev then
|
||||
-- try to add as partial definition
|
||||
try
|
||||
addAndCompilePartial preDefs (useSorry := true)
|
||||
catch _ =>
|
||||
-- Compilation failed try again just as axiom
|
||||
s.restore
|
||||
addAsAxioms preDefs
|
||||
else if preDefs.all fun preDef => preDef.kind == DefKind.theorem then
|
||||
addAsAxioms preDefs
|
||||
catch _ => s.restore
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.body
|
||||
|
||||
@@ -10,6 +10,7 @@ import Lean.Elab.RecAppSyntax
|
||||
import Lean.Elab.PreDefinition.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.FunPacker
|
||||
import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
@@ -17,51 +18,63 @@ open Meta
|
||||
private def throwToBelowFailed : MetaM α :=
|
||||
throwError "toBelow failed"
|
||||
|
||||
partial def searchPProd (e : Expr) (F : Expr) (k : Expr → Expr → MetaM α) : MetaM α := do
|
||||
match (← whnf e) with
|
||||
| .app (.app (.const `PProd _) d1) d2 =>
|
||||
(do searchPProd d1 (← mkAppM ``PProd.fst #[F]) k)
|
||||
<|> (do searchPProd d2 (← mkAppM `PProd.snd #[F]) k)
|
||||
| .app (.app (.const `And _) d1) d2 =>
|
||||
(do searchPProd d1 (← mkAppM `And.left #[F]) k)
|
||||
<|> (do searchPProd d2 (← mkAppM `And.right #[F]) k)
|
||||
| .const `PUnit _
|
||||
| .const `True _ => throwToBelowFailed
|
||||
| _ => k e F
|
||||
|
||||
/-- See `toBelow` -/
|
||||
private partial def toBelowAux (C : Expr) (belowDict : Expr) (arg : Expr) (F : Expr) : MetaM Expr := do
|
||||
let belowDict ← whnf belowDict
|
||||
trace[Elab.definition.structural] "belowDict: {belowDict}, arg: {arg}"
|
||||
match belowDict with
|
||||
| .app (.app (.const `PProd _) d1) d2 =>
|
||||
(do toBelowAux C d1 arg (← mkAppM `PProd.fst #[F]))
|
||||
<|>
|
||||
(do toBelowAux C d2 arg (← mkAppM `PProd.snd #[F]))
|
||||
| .app (.app (.const `And _) d1) d2 =>
|
||||
(do toBelowAux C d1 arg (← mkAppM `And.left #[F]))
|
||||
<|>
|
||||
(do toBelowAux C d2 arg (← mkAppM `And.right #[F]))
|
||||
| _ => forallTelescopeReducing belowDict fun xs belowDict => do
|
||||
let arg ← zetaReduce arg
|
||||
let argArgs := arg.getAppArgs
|
||||
unless argArgs.size >= xs.size do throwToBelowFailed
|
||||
let n := argArgs.size
|
||||
let argTailArgs := argArgs.extract (n - xs.size) n
|
||||
let belowDict := belowDict.replaceFVars xs argTailArgs
|
||||
match belowDict with
|
||||
| .app belowDictFun belowDictArg =>
|
||||
unless belowDictFun.getAppFn == C do throwToBelowFailed
|
||||
unless ← isDefEq belowDictArg arg do throwToBelowFailed
|
||||
pure (mkAppN F argTailArgs)
|
||||
| _ =>
|
||||
trace[Elab.definition.structural] "belowDict not an app: {belowDict}"
|
||||
throwToBelowFailed
|
||||
trace[Elab.definition.structural] "belowDict start:{indentExpr belowDict}\narg:{indentExpr arg}"
|
||||
-- First search through the PProd packing of the different `brecOn` motives
|
||||
searchPProd belowDict F fun belowDict F => do
|
||||
trace[Elab.definition.structural] "belowDict step 1:{indentExpr belowDict}"
|
||||
-- Then instantiate parameters of a reflexive type, if needed
|
||||
forallTelescopeReducing belowDict fun xs belowDict => do
|
||||
let arg ← zetaReduce arg
|
||||
let argArgs := arg.getAppArgs
|
||||
unless argArgs.size >= xs.size do throwToBelowFailed
|
||||
let n := argArgs.size
|
||||
let argTailArgs := argArgs.extract (n - xs.size) n
|
||||
let belowDict := belowDict.replaceFVars xs argTailArgs
|
||||
-- And again search through the PProd packing due to multiple functions recursing on the
|
||||
-- same inductive data type
|
||||
-- (We could use the funIdx and the `positions` array to replace this search with more
|
||||
-- targeted indexing.)
|
||||
searchPProd belowDict (mkAppN F argTailArgs) fun belowDict F => do
|
||||
trace[Elab.definition.structural] "belowDict step 2:{indentExpr belowDict}"
|
||||
match belowDict with
|
||||
| .app belowDictFun belowDictArg =>
|
||||
unless belowDictFun.getAppFn == C do throwToBelowFailed
|
||||
unless ← isDefEq belowDictArg arg do throwToBelowFailed
|
||||
pure F
|
||||
| _ =>
|
||||
trace[Elab.definition.structural] "belowDict not an app:{indentExpr belowDict}"
|
||||
throwToBelowFailed
|
||||
|
||||
/-- See `toBelow` -/
|
||||
private def withBelowDict [Inhabited α] (below : Expr) (numIndParams : Nat)
|
||||
(positions : Positions) (k : Array Expr → Expr → MetaM α) : MetaM α := do
|
||||
let numIndAll := positions.size
|
||||
let numTypeFormers := positions.size
|
||||
let belowType ← inferType below
|
||||
trace[Elab.definition.structural] "belowType: {belowType}"
|
||||
unless (← isTypeCorrect below) do
|
||||
trace[Elab.definition.structural] "not type correct!"
|
||||
belowType.withApp fun f args => do
|
||||
unless numIndParams + numIndAll < args.size do
|
||||
unless numIndParams + numTypeFormers < args.size do
|
||||
trace[Elab.definition.structural] "unexpected 'below' type{indentExpr belowType}"
|
||||
throwToBelowFailed
|
||||
let params := args[:numIndParams]
|
||||
let finalArgs := args[numIndParams+numIndAll:]
|
||||
let finalArgs := args[numIndParams+numTypeFormers:]
|
||||
let pre := mkAppN f params
|
||||
let motiveTypes ← inferArgumentTypesN numIndAll pre
|
||||
let motiveTypes ← inferArgumentTypesN numTypeFormers pre
|
||||
let numMotives : Nat := positions.numIndices
|
||||
trace[Elab.definition.structural] "numMotives: {numMotives}"
|
||||
let mut CTypes := Array.mkArray numMotives (.sort 37) -- dummy value
|
||||
@@ -133,26 +146,16 @@ private partial def replaceRecApps (recArgInfos : Array RecArgInfo) (positions :
|
||||
e.withApp fun f args => do
|
||||
if let .some fnIdx := recArgInfos.findIdx? (f.isConstOf ·.fnName) then
|
||||
let recArgInfo := recArgInfos[fnIdx]!
|
||||
let numFixed := recArgInfo.numFixed
|
||||
let recArgPos := recArgInfo.recArgPos
|
||||
if recArgPos >= args.size then
|
||||
throwError "insufficient number of parameters at recursive application {indentExpr e}"
|
||||
let recArg := args[recArgPos]!
|
||||
let some recArg := args[recArgInfo.recArgPos]?
|
||||
| throwError "insufficient number of parameters at recursive application {indentExpr e}"
|
||||
-- For reflexive type, we may have nested recursive applications in recArg
|
||||
let recArg ← loop below recArg
|
||||
let f ←
|
||||
try toBelow below recArgInfo.indParams.size positions fnIdx recArg
|
||||
try toBelow below recArgInfo.indGroupInst.params.size positions fnIdx recArg
|
||||
catch _ => throwError "failed to eliminate recursive application{indentExpr e}"
|
||||
-- Recall that the fixed parameters are not in the scope of the `brecOn`. So, we skip them.
|
||||
let argsNonFixed := args.extract numFixed args.size
|
||||
-- The function `f` does not explicitly take `recArg` and its indices as arguments. So, we skip them too.
|
||||
let mut fArgs := #[]
|
||||
for i in [:argsNonFixed.size] do
|
||||
let j := i + numFixed
|
||||
if recArgInfo.recArgPos != j && !recArgInfo.indicesPos.contains j then
|
||||
let arg := argsNonFixed[i]!
|
||||
let arg ← replaceRecApps recArgInfos positions below arg
|
||||
fArgs := fArgs.push arg
|
||||
-- We don't pass the fixed parameters, the indices and the major arg to `f`, only the rest
|
||||
let (_, fArgs) := recArgInfo.pickIndicesMajor args[recArgInfo.numFixed:]
|
||||
let fArgs ← fArgs.mapM (replaceRecApps recArgInfos positions below ·)
|
||||
return mkAppN f fArgs
|
||||
else
|
||||
return mkAppN (← loop below f) (← args.mapM (loop below))
|
||||
@@ -225,35 +228,28 @@ def mkBRecOnF (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
let valueNew ← replaceRecApps recArgInfos positions below value
|
||||
mkLambdaFVars (indexMajorArgs ++ #[below] ++ otherArgs) valueNew
|
||||
|
||||
|
||||
/--
|
||||
Given the `motives`, figures out whether to use `.brecOn` or `.binductionOn`, pass
|
||||
the right universe levels, the parameters, and the motives.
|
||||
It was already checked earlier in `checkCodomainsLevel` that the functions live in the same universe.
|
||||
-/
|
||||
def mkBRecOnConst (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
(motives : Array Expr) : MetaM (Name → Expr) := do
|
||||
-- For now, just look at the first
|
||||
let recArgInfo := recArgInfos[0]!
|
||||
(motives : Array Expr) : MetaM (Nat → Expr) := do
|
||||
let indGroup := recArgInfos[0]!.indGroupInst
|
||||
let motive := motives[0]!
|
||||
let brecOnUniv ← lambdaTelescope motive fun _ type => getLevel type
|
||||
let indInfo ← getConstInfoInduct recArgInfo.indName
|
||||
let indInfo ← getConstInfoInduct indGroup.all[0]!
|
||||
let useBInductionOn := indInfo.isReflexive && brecOnUniv == levelZero
|
||||
let brecOnUniv ←
|
||||
if indInfo.isReflexive && brecOnUniv != levelZero then
|
||||
decLevel brecOnUniv
|
||||
else
|
||||
pure brecOnUniv
|
||||
let brecOnCons := fun n =>
|
||||
let brecOn :=
|
||||
if useBInductionOn then .const (mkBInductionOnName n) recArgInfo.indLevels
|
||||
else .const (mkBRecOnName n) (brecOnUniv :: recArgInfo.indLevels)
|
||||
mkAppN brecOn recArgInfo.indParams
|
||||
|
||||
let brecOnCons := fun idx => indGroup.brecOn useBInductionOn brecOnUniv idx
|
||||
-- Pick one as a prototype
|
||||
let brecOnAux := brecOnCons recArgInfo.indName
|
||||
let brecOnAux := brecOnCons 0
|
||||
-- Infer the type of the packed motive arguments
|
||||
let packedMotiveTypes ← inferArgumentTypesN recArgInfo.indAll.size brecOnAux
|
||||
let packedMotiveTypes ← inferArgumentTypesN indGroup.numMotives brecOnAux
|
||||
let packedMotives ← positions.mapMwith packMotives packedMotiveTypes motives
|
||||
|
||||
return fun n => mkAppN (brecOnCons n) packedMotives
|
||||
@@ -265,17 +261,18 @@ combinators. This assumes that all `.brecOn` functions of a mutual inductive hav
|
||||
It also undoes the permutation and packing done by `packMotives`
|
||||
-/
|
||||
def inferBRecOnFTypes (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
(brecOnConst : Name → Expr) : MetaM (Array Expr) := do
|
||||
(brecOnConst : Nat → Expr) : MetaM (Array Expr) := do
|
||||
let numTypeFormers := positions.size
|
||||
let recArgInfo := recArgInfos[0]! -- pick an arbitrary one
|
||||
let brecOn := brecOnConst recArgInfo.indName
|
||||
let brecOn := brecOnConst 0
|
||||
check brecOn
|
||||
let brecOnType ← inferType brecOn
|
||||
-- Skip the indices and major argument
|
||||
let packedFTypes ← forallBoundedTelescope brecOnType (some (recArgInfo.indicesPos.size + 1)) fun _ brecOnType =>
|
||||
-- And return the types of of the next arguments
|
||||
arrowDomainsN recArgInfo.indAll.size brecOnType
|
||||
arrowDomainsN numTypeFormers brecOnType
|
||||
|
||||
let mut FTypes := Array.mkArray recArgInfos.size (Expr.sort 0)
|
||||
let mut FTypes := Array.mkArray positions.numIndices (Expr.sort 0)
|
||||
for packedFType in packedFTypes, poss in positions do
|
||||
for pos in poss do
|
||||
FTypes := FTypes.set! pos packedFType
|
||||
@@ -285,11 +282,11 @@ def inferBRecOnFTypes (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
Completes the `.brecOn` for the given function.
|
||||
The `value` is the function with (only) the fixed parameters moved into the context.
|
||||
-/
|
||||
def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Name → Expr)
|
||||
def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Nat → Expr)
|
||||
(FArgs : Array Expr) (recArgInfo : RecArgInfo) (value : Expr) : MetaM Expr := do
|
||||
lambdaTelescope value fun ys _value => do
|
||||
let (indexMajorArgs, otherArgs) := recArgInfo.pickIndicesMajor ys
|
||||
let brecOn := brecOnConst recArgInfo.indName
|
||||
let brecOn := brecOnConst recArgInfo.indIdx
|
||||
let brecOn := mkAppN brecOn indexMajorArgs
|
||||
let packedFTypes ← inferArgumentTypesN positions.size brecOn
|
||||
let packedFArgs ← positions.mapMwith packFArgs packedFTypes FArgs
|
||||
|
||||
@@ -9,46 +9,6 @@ import Lean.Meta.ForEachExpr
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
|
||||
/--
|
||||
Information about the argument of interest of a structurally recursive function.
|
||||
|
||||
The `Expr`s in this data structure expect the `fixedParams` to be in scope, but not the other
|
||||
parameters of the function. This ensures that this data structure makes sense in the other functions
|
||||
of a mutually recursive group.
|
||||
-/
|
||||
structure RecArgInfo where
|
||||
/-- the name of the recursive function -/
|
||||
fnName : Name
|
||||
/-- the fixed prefix of arguments of the function we are trying to justify termination using structural recursion. -/
|
||||
numFixed : Nat
|
||||
/-- position of the argument (counted including fixed prefix) we are recursing on -/
|
||||
recArgPos : Nat
|
||||
/-- position of the indices (counted including fixed prefix) of the inductive datatype indices we are recursing on -/
|
||||
indicesPos : Array Nat
|
||||
/-- inductive datatype name of the argument we are recursing on -/
|
||||
indName : Name
|
||||
/-- inductive datatype universe levels of the argument we are recursing on -/
|
||||
indLevels : List Level
|
||||
/-- inductive datatype parameters of the argument we are recursing on -/
|
||||
indParams : Array Expr
|
||||
/-- The types mutually inductive with indName -/
|
||||
indAll : Array Name
|
||||
deriving Inhabited
|
||||
/--
|
||||
If `xs` are the parameters of the functions (excluding fixed prefix), partitions them
|
||||
into indices and major arguments, and other parameters.
|
||||
-/
|
||||
def RecArgInfo.pickIndicesMajor (info : RecArgInfo) (xs : Array Expr) : (Array Expr × Array Expr) := Id.run do
|
||||
let mut indexMajorArgs := #[]
|
||||
let mut otherArgs := #[]
|
||||
for h : i in [:xs.size] do
|
||||
let j := i + info.numFixed
|
||||
if j = info.recArgPos || info.indicesPos.contains j then
|
||||
indexMajorArgs := indexMajorArgs.push xs[i]
|
||||
else
|
||||
otherArgs := otherArgs.push xs[i]
|
||||
return (indexMajorArgs, otherArgs)
|
||||
|
||||
structure State where
|
||||
/-- As part of the inductive predicates case, we keep adding more and more discriminants from the
|
||||
local context and build up a bigger matcher application until we reach a fixed point.
|
||||
@@ -91,10 +51,11 @@ and for each such type, keep track of the order of the functions.
|
||||
|
||||
We represent these positions as an `Array (Array Nat)`. We have that
|
||||
|
||||
* `positions.size = indInfo.all.length`
|
||||
* `positions.size = indInfo.numTypeFormers`
|
||||
* `positions.flatten` is a permutation of `[0:n]`, so each of the `n` functions has exactly one
|
||||
position, and each position refers to one of the `n` functions.
|
||||
* if `k ∈ positions[i]` then the recursive argument of function `k` is has type `indInfo.all[i]`
|
||||
(or corresponding nested inductive type)
|
||||
|
||||
-/
|
||||
abbrev Positions := Array (Array Nat)
|
||||
@@ -127,3 +88,6 @@ def Positions.mapMwith {α β m} [Monad m] [Inhabited β] (f : α → Array β
|
||||
(Array.zip ys positions).mapM fun ⟨y, poss⟩ => f y (poss.map (xs[·]!))
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
builtin_initialize
|
||||
Lean.registerTraceClass `Elab.definition.structural
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Structural
|
||||
structure EqnInfo extends EqnInfoCore where
|
||||
recArgPos : Nat
|
||||
declNames : Array Name
|
||||
numFixed : Nat
|
||||
deriving Inhabited
|
||||
|
||||
private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
|
||||
@@ -81,9 +82,11 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
|
||||
|
||||
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo ← mkMapDeclarationExtension
|
||||
|
||||
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat) : CoreM Unit := do
|
||||
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat)
|
||||
(numFixed : Nat) : CoreM Unit := do
|
||||
ensureEqnReservedNamesAvailable preDef.declName
|
||||
modifyEnv fun env => eqnInfoExt.insert env preDef.declName { preDef with recArgPos, declNames }
|
||||
modifyEnv fun env => eqnInfoExt.insert env preDef.declName
|
||||
{ preDef with recArgPos, declNames, numFixed }
|
||||
|
||||
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
|
||||
if let some info := eqnInfoExt.find? (← getEnv) declName then
|
||||
|
||||
@@ -4,11 +4,31 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.PreDefinition.TerminationArgument
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
|
||||
def prettyParam (xs : Array Expr) (i : Nat) : MetaM MessageData := do
|
||||
let x := xs[i]!
|
||||
let n ← x.fvarId!.getUserName
|
||||
addMessageContextFull <| if n.hasMacroScopes then m!"#{i+1}" else m!"{x}"
|
||||
|
||||
def prettyRecArg (xs : Array Expr) (value : Expr) (recArgInfo : RecArgInfo) : MetaM MessageData := do
|
||||
lambdaTelescope value fun ys _ => prettyParam (xs ++ ys) recArgInfo.recArgPos
|
||||
|
||||
def prettyParameterSet (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
|
||||
(recArgInfos : Array RecArgInfo) : MetaM MessageData := do
|
||||
if fnNames.size = 1 then
|
||||
return m!"parameter " ++ (← prettyRecArg xs values[0]! recArgInfos[0]!)
|
||||
else
|
||||
let mut l := #[]
|
||||
for fnName in fnNames, value in values, recArgInfo in recArgInfos do
|
||||
l := l.push m!"{(← prettyRecArg xs value recArgInfo)} of {fnName}"
|
||||
return m!"parameters " ++ .andList l.toList
|
||||
|
||||
private def getIndexMinPos (xs : Array Expr) (indices : Array Expr) : Nat := Id.run do
|
||||
let mut minPos := xs.size
|
||||
for index in indices do
|
||||
@@ -72,60 +92,190 @@ def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) :
|
||||
| some (indParam, y) =>
|
||||
throwError "its type is an inductive datatype{indentExpr xType}\nand the datatype parameter{indentExpr indParam}\ndepends on the function parameter{indentExpr y}\nwhich does not come before the varying parameters and before the indices of the recursion parameter."
|
||||
| none =>
|
||||
let indAll := indInfo.all.toArray
|
||||
let .some indIdx := indAll.indexOf? indInfo.name | panic! "{indInfo.name} not in {indInfo.all}"
|
||||
let indicesPos := indIndices.map fun index => match xs.indexOf? index with | some i => i.val | none => unreachable!
|
||||
return { fnName := fnName
|
||||
numFixed := numFixed
|
||||
recArgPos := i
|
||||
indicesPos := indicesPos
|
||||
indName := indInfo.name
|
||||
indLevels := us
|
||||
indParams := indParams
|
||||
indAll := indInfo.all.toArray }
|
||||
let indGroupInst := {
|
||||
IndGroupInfo.ofInductiveVal indInfo with
|
||||
levels := us
|
||||
params := indParams }
|
||||
return { fnName := fnName
|
||||
numFixed := numFixed
|
||||
recArgPos := i
|
||||
indicesPos := indicesPos
|
||||
indGroupInst := indGroupInst
|
||||
indIdx := indIdx }
|
||||
else
|
||||
throwError "the index #{i+1} exceeds {xs.size}, the number of parameters"
|
||||
|
||||
/--
|
||||
Runs `k` on all argument indices, until it succeeds.
|
||||
We use this argument to justify termination using the auxiliary `brecOn` construction.
|
||||
Collects the `RecArgInfos` for one function, and returns a report for why the others were not
|
||||
considered.
|
||||
|
||||
We give preference for arguments that are *not* indices of inductive types of other arguments.
|
||||
See issue #837 for an example where we can show termination using the index of an inductive family, but
|
||||
we don't get the desired definitional equalities.
|
||||
The `xs` are the fixed parameters, `value` the body with the fixed prefix instantiated.
|
||||
|
||||
`value` is the function value (including fixed parameters)
|
||||
Takes the optional user annotations into account (`termArg?`). If this is given and the argument
|
||||
is unsuitable, throw an error.
|
||||
-/
|
||||
partial def tryAllArgs (value : Expr) (k : Nat → M α) : M α := do
|
||||
-- It's improtant to keep the call to `k` outside the scope of `lambdaTelescope`:
|
||||
-- The tactics in the IndPred construction search the full local context, so we must not have
|
||||
-- extra FVars there
|
||||
let (indices, nonIndices) ← lambdaTelescope value fun xs _ => do
|
||||
let indicesRef : IO.Ref (Array Nat) ← IO.mkRef {}
|
||||
for x in xs do
|
||||
let xType ← inferType x
|
||||
/- Traverse all sub-expressions in the type of `x` -/
|
||||
forEachExpr xType fun e =>
|
||||
/- If `e` is an inductive family, we store in `indicesRef` all variables in `xs` that occur in "index positions". -/
|
||||
matchConstInduct e.getAppFn (fun _ => pure ()) fun info _ => do
|
||||
if info.numIndices > 0 && info.numParams + info.numIndices == e.getAppNumArgs then
|
||||
for arg in e.getAppArgs[info.numParams:] do
|
||||
forEachExpr arg fun e => do
|
||||
if let .some idx := xs.getIdx? e then
|
||||
indicesRef.modify (·.push idx)
|
||||
let indices ← indicesRef.get
|
||||
let nonIndices := (Array.range xs.size).filter (fun i => !(indices.contains i))
|
||||
return (indices, nonIndices)
|
||||
def getRecArgInfos (fnName : Name) (xs : Array Expr) (value : Expr)
|
||||
(termArg? : Option TerminationArgument) : MetaM (Array RecArgInfo × MessageData) := do
|
||||
lambdaTelescope value fun ys _ => do
|
||||
if let .some termArg := termArg? then
|
||||
-- User explictly asked to use a certain argument, so throw errors eagerly
|
||||
let recArgInfo ← withRef termArg.ref do
|
||||
mapError (f := (m!"cannot use specified parameter for structural recursion:{indentD ·}")) do
|
||||
getRecArgInfo fnName xs.size (xs ++ ys) (← termArg.structuralArg)
|
||||
return (#[recArgInfo], m!"")
|
||||
else
|
||||
let mut recArgInfos := #[]
|
||||
let mut report : MessageData := m!""
|
||||
-- No `termination_by`, so try all, and remember the errors
|
||||
for idx in [:xs.size + ys.size] do
|
||||
try
|
||||
let recArgInfo ← getRecArgInfo fnName xs.size (xs ++ ys) idx
|
||||
recArgInfos := recArgInfos.push recArgInfo
|
||||
catch e =>
|
||||
report := report ++ (m!"Not considering parameter {← prettyParam (xs ++ ys) idx} of {fnName}:" ++
|
||||
indentD e.toMessageData) ++ "\n"
|
||||
trace[Elab.definition.structural] "getRecArgInfos report: {report}"
|
||||
return (recArgInfos, report)
|
||||
|
||||
let mut errors : Array MessageData := Array.mkArray (indices.size + nonIndices.size) m!""
|
||||
let saveState ← get -- backtrack the state for each argument
|
||||
for i in id (nonIndices ++ indices) do
|
||||
trace[Elab.definition.structural] "findRecArg i: {i}"
|
||||
try
|
||||
set saveState
|
||||
return (← k i)
|
||||
catch e => errors := errors.set! i e.toMessageData
|
||||
throwError
|
||||
errors.foldl
|
||||
(init := m!"structural recursion cannot be used:")
|
||||
(f := (· ++ Format.line ++ Format.line ++ .))
|
||||
|
||||
/--
|
||||
Reorders the `RecArgInfos` of one function to put arguments that are indices of other arguments
|
||||
last.
|
||||
See issue #837 for an example where we can show termination using the index of an inductive family, but
|
||||
we don't get the desired definitional equalities.
|
||||
-/
|
||||
def nonIndicesFirst (recArgInfos : Array RecArgInfo) : Array RecArgInfo := Id.run do
|
||||
let mut indicesPos : HashSet Nat := {}
|
||||
for recArgInfo in recArgInfos do
|
||||
for pos in recArgInfo.indicesPos do
|
||||
indicesPos := indicesPos.insert pos
|
||||
let (indices,nonIndices) := recArgInfos.partition (indicesPos.contains ·.recArgPos)
|
||||
return nonIndices ++ indices
|
||||
|
||||
private def dedup [Monad m] (eq : α → α → m Bool) (xs : Array α) : m (Array α) := do
|
||||
let mut ret := #[]
|
||||
for x in xs do
|
||||
unless (← ret.anyM (eq · x)) do
|
||||
ret := ret.push x
|
||||
return ret
|
||||
|
||||
/--
|
||||
Given the `RecArgInfo`s of all the recursive functions, find the inductive groups to consider.
|
||||
-/
|
||||
def inductiveGroups (recArgInfos : Array RecArgInfo) : MetaM (Array IndGroupInst) :=
|
||||
dedup IndGroupInst.isDefEq (recArgInfos.map (·.indGroupInst))
|
||||
|
||||
/--
|
||||
Filters the `recArgInfos` by those that describe an argument that's part of the recursive inductive
|
||||
group `group`.
|
||||
|
||||
Because of nested inductives this function has the ability to change the `recArgInfo`.
|
||||
Consider
|
||||
```
|
||||
inductive Tree where | node : List Tree → Tree
|
||||
```
|
||||
then when we look for arguments whose type is part of the group `Tree`, we want to also consider
|
||||
the argument of type `List Tree`, even though that argument’s `RecArgInfo` refers to initially to
|
||||
`List`.
|
||||
-/
|
||||
def argsInGroup (group : IndGroupInst) (xs : Array Expr) (value : Expr)
|
||||
(recArgInfos : Array RecArgInfo) : MetaM (Array RecArgInfo) := do
|
||||
|
||||
let nestedTypeFormers ← group.nestedTypeFormers
|
||||
|
||||
recArgInfos.filterMapM fun recArgInfo => do
|
||||
-- Is this argument from the same mutual group of inductives?
|
||||
if (← group.isDefEq recArgInfo.indGroupInst) then
|
||||
return (.some recArgInfo)
|
||||
|
||||
-- Can this argument be understood as the auxillary type former of a nested inductive?
|
||||
if nestedTypeFormers.isEmpty then return .none
|
||||
lambdaTelescope value fun ys _ => do
|
||||
let x := (xs++ys)[recArgInfo.recArgPos]!
|
||||
for nestedTypeFormer in nestedTypeFormers, indIdx in [group.all.size : group.numMotives] do
|
||||
let xType ← whnfD (← inferType x)
|
||||
let (indIndices, _, type) ← forallMetaTelescope nestedTypeFormer
|
||||
if (← isDefEqGuarded type xType) then
|
||||
let indIndices ← indIndices.mapM instantiateMVars
|
||||
if !indIndices.all Expr.isFVar then
|
||||
-- throwError "indices are not variables{indentExpr xType}"
|
||||
continue
|
||||
if !indIndices.allDiff then
|
||||
-- throwError "indices are not pairwise distinct{indentExpr xType}"
|
||||
continue
|
||||
-- TODO: Do we have to worry about the indices ending up in the fixed prefix here?
|
||||
if let some (_index, _y) ← hasBadIndexDep? ys indIndices then
|
||||
-- throwError "its type {indInfo.name} is an inductive family{indentExpr xType}\nand index{indentExpr index}\ndepends on the non index{indentExpr y}"
|
||||
continue
|
||||
let indicesPos := indIndices.map fun index => match (xs++ys).indexOf? index with | some i => i.val | none => unreachable!
|
||||
return .some
|
||||
{ fnName := recArgInfo.fnName
|
||||
numFixed := recArgInfo.numFixed
|
||||
recArgPos := recArgInfo.recArgPos
|
||||
indicesPos := indicesPos
|
||||
indGroupInst := group
|
||||
indIdx := indIdx }
|
||||
return .none
|
||||
|
||||
def maxCombinationSize : Nat := 10
|
||||
|
||||
def allCombinations (xss : Array (Array α)) : Option (Array (Array α)) :=
|
||||
if xss.foldl (· * ·.size) 1 > maxCombinationSize then
|
||||
none
|
||||
else
|
||||
let rec go i acc : Array (Array α):=
|
||||
if h : i < xss.size then
|
||||
xss[i].concatMap fun x => go (i + 1) (acc.push x)
|
||||
else
|
||||
#[acc]
|
||||
some (go 0 #[])
|
||||
|
||||
|
||||
def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
|
||||
(termArg?s : Array (Option TerminationArgument)) (k : Array RecArgInfo → M α) : M α := do
|
||||
let mut report := m!""
|
||||
-- Gather information on all possible recursive arguments
|
||||
let mut recArgInfoss := #[]
|
||||
for fnName in fnNames, value in values, termArg? in termArg?s do
|
||||
let (recArgInfos, thisReport) ← getRecArgInfos fnName xs value termArg?
|
||||
report := report ++ thisReport
|
||||
recArgInfoss := recArgInfoss.push recArgInfos
|
||||
-- Put non-indices first
|
||||
recArgInfoss := recArgInfoss.map nonIndicesFirst
|
||||
trace[Elab.definition.structural] "recArgInfoss: {recArgInfoss.map (·.map (·.recArgPos))}"
|
||||
-- Inductive groups to consider
|
||||
let groups ← inductiveGroups recArgInfoss.flatten
|
||||
trace[Elab.definition.structural] "inductive groups: {groups}"
|
||||
if groups.isEmpty then
|
||||
report := report ++ "no parameters suitable for structural recursion"
|
||||
-- Consider each group
|
||||
for group in groups do
|
||||
-- Select those RecArgInfos that are compatible with this inductive group
|
||||
let mut recArgInfoss' := #[]
|
||||
for value in values, recArgInfos in recArgInfoss do
|
||||
recArgInfoss' := recArgInfoss'.push (← argsInGroup group xs value recArgInfos)
|
||||
if let some idx := recArgInfoss'.findIdx? (·.isEmpty) then
|
||||
report := report ++ m!"Skipping arguments of type {group}, as {fnNames[idx]!} has no compatible argument.\n"
|
||||
continue
|
||||
if let some combs := allCombinations recArgInfoss' then
|
||||
for comb in combs do
|
||||
try
|
||||
-- TODO: Here we used to save and restore the state. But should the `try`-`catch`
|
||||
-- not suffice?
|
||||
let r ← k comb
|
||||
trace[Elab.definition.structural] "tryAllArgs report:\n{report}"
|
||||
return r
|
||||
catch e =>
|
||||
let m ← prettyParameterSet fnNames xs values comb
|
||||
report := report ++ m!"Cannot use {m}:{indentD e.toMessageData}\n"
|
||||
else
|
||||
report := report ++ m!"Too many possible combinations of parameters of type {group} (or " ++
|
||||
m!"please indicate the recursive argument explicitly using `termination_by structural`).\n"
|
||||
report := m!"failed to infer structural recursion:\n" ++ report
|
||||
trace[Elab.definition.structural] "tryAllArgs:\n{report}"
|
||||
throwError report
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
106
src/Lean/Elab/PreDefinition/Structural/IndGroupInfo.lean
Normal file
106
src/Lean/Elab/PreDefinition/Structural/IndGroupInfo.lean
Normal file
@@ -0,0 +1,106 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.InferType
|
||||
|
||||
/-!
|
||||
This module contains the types
|
||||
* `IndGroupInfo`, a variant of `InductiveVal` with information that
|
||||
applies to a whole group of mutual inductives and
|
||||
* `IndGroupInst` which extends `IndGroupInfo` with levels and parameters
|
||||
to indicate a instantiation of the group.
|
||||
|
||||
One purpose of this abstraction is to make it clear when a fuction operates on a group as
|
||||
a whole, rather than a specific inductive within the group.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Lean Meta
|
||||
|
||||
/--
|
||||
A mutually inductive group, identified by the `all` array of the `InductiveVal` of its
|
||||
constituents.
|
||||
-/
|
||||
structure IndGroupInfo where
|
||||
all : Array Name
|
||||
numNested : Nat
|
||||
deriving BEq, Inhabited
|
||||
|
||||
def IndGroupInfo.ofInductiveVal (indInfo : InductiveVal) : IndGroupInfo where
|
||||
all := indInfo.all.toArray
|
||||
numNested := indInfo.numNested
|
||||
|
||||
def IndGroupInfo.numMotives (group : IndGroupInfo) : Nat :=
|
||||
group.all.size + group.numNested
|
||||
|
||||
/--
|
||||
An instance of an mutually inductive group of inductives, identified by the `all` array
|
||||
and the level and expressions parameters.
|
||||
|
||||
For example this distinguishes between `List α` and `List β` so that we will not even attempt
|
||||
mutual structural recursion on such incompatible types.
|
||||
-/
|
||||
structure IndGroupInst extends IndGroupInfo where
|
||||
levels : List Level
|
||||
params : Array Expr
|
||||
deriving Inhabited
|
||||
|
||||
def IndGroupInst.toMessageData (igi : IndGroupInst) : MessageData :=
|
||||
mkAppN (.const igi.all[0]! igi.levels) igi.params
|
||||
|
||||
instance : ToMessageData IndGroupInst where
|
||||
toMessageData := IndGroupInst.toMessageData
|
||||
|
||||
def IndGroupInst.isDefEq (igi1 igi2 : IndGroupInst) : MetaM Bool := do
|
||||
unless igi1.toIndGroupInfo == igi2.toIndGroupInfo do return false
|
||||
unless igi1.levels.length = igi2.levels.length do return false
|
||||
unless (igi1.levels.zip igi2.levels).all (fun (l₁, l₂) => Level.isEquiv l₁ l₂) do return false
|
||||
unless igi1.params.size = igi2.params.size do return false
|
||||
unless (← (igi1.params.zip igi2.params).allM (fun (e₁, e₂) => Meta.isDefEqGuarded e₁ e₂)) do return false
|
||||
return true
|
||||
|
||||
/-- Instantiates the right `.brecOn` or `.bInductionOn` for the given type former index,
|
||||
including universe parameters and fixed prefix. -/
|
||||
def IndGroupInst.brecOn (group : IndGroupInst) (ind : Bool) (lvl : Level) (idx : Nat) : Expr :=
|
||||
let e := if let .some n := group.all[idx]? then
|
||||
if ind then .const (mkBInductionOnName n) group.levels
|
||||
else .const (mkBRecOnName n) (lvl :: group.levels)
|
||||
else
|
||||
let n := group.all[0]!
|
||||
let j := idx - group.all.size + 1
|
||||
if ind then .const (mkBInductionOnName n |>.appendIndexAfter j) group.levels
|
||||
else .const (mkBRecOnName n |>.appendIndexAfter j) (lvl :: group.levels)
|
||||
mkAppN e group.params
|
||||
|
||||
/--
|
||||
Figures out the nested type formers of an inductive group, with parameters instantiated
|
||||
and indices still forall-abstracted.
|
||||
|
||||
For example given a nested inductive
|
||||
```
|
||||
inductive Tree α where | node : α → Vector (Tree α) n → Tree α
|
||||
```
|
||||
(where `n` is an index of `Vector`) and the instantiation `Tree Int` it will return
|
||||
```
|
||||
#[(n : Nat) → Vector (Tree Int) n]
|
||||
```
|
||||
|
||||
-/
|
||||
def IndGroupInst.nestedTypeFormers (igi : IndGroupInst) : MetaM (Array Expr) := do
|
||||
if igi.numNested = 0 then return #[]
|
||||
-- We extract this information from the motives of the recursor
|
||||
let recName := mkRecName igi.all[0]!
|
||||
let recInfo ← getConstInfoRec recName
|
||||
assert! recInfo.numMotives = igi.numMotives
|
||||
let aux := mkAppN (.const recName (0 :: igi.levels)) igi.params
|
||||
let motives ← inferArgumentTypesN recInfo.numMotives aux
|
||||
let auxMotives : Array Expr := motives[igi.all.size:]
|
||||
auxMotives.mapM fun motive =>
|
||||
forallTelescopeReducing motive fun xs _ => do
|
||||
assert! xs.size > 0
|
||||
mkForallFVars xs.pop (← inferType xs.back)
|
||||
|
||||
end Lean.Elab.Structural
|
||||
@@ -7,6 +7,7 @@ prelude
|
||||
import Lean.Meta.IndPredBelow
|
||||
import Lean.Elab.PreDefinition.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
@@ -81,8 +82,8 @@ def mkIndPredBRecOn (recArgInfo : RecArgInfo) (value : Expr) : M Expr := do
|
||||
let motive ← mkForallFVars otherArgs type
|
||||
let motive ← mkLambdaFVars indexMajorArgs motive
|
||||
trace[Elab.definition.structural] "brecOn motive: {motive}"
|
||||
let brecOn := Lean.mkConst (mkBRecOnName recArgInfo.indName) recArgInfo.indLevels
|
||||
let brecOn := mkAppN brecOn recArgInfo.indParams
|
||||
let brecOn := Lean.mkConst (mkBRecOnName recArgInfo.indName!) recArgInfo.indGroupInst.levels
|
||||
let brecOn := mkAppN brecOn recArgInfo.indGroupInst.params
|
||||
let brecOn := mkApp brecOn motive
|
||||
let brecOn := mkAppN brecOn indexMajorArgs
|
||||
check brecOn
|
||||
|
||||
@@ -89,87 +89,72 @@ def getMutualFixedPrefix (preDefs : Array PreDefinition) : M Nat :=
|
||||
return true
|
||||
resultRef.get
|
||||
|
||||
/-- Checks that all parameter types are mutually inductive -/
|
||||
private def checkAllFromSameClique (recArgInfos : Array RecArgInfo) : MetaM Unit := do
|
||||
for recArgInfo in recArgInfos do
|
||||
unless recArgInfos[0]!.indAll.contains recArgInfo.indName do
|
||||
throwError m!"Cannot use structural mutual recursion: The recursive argument of " ++
|
||||
m!"{recArgInfos[0]!.fnName} is of type {recArgInfos[0]!.indName}, " ++
|
||||
m!"the recursive argument of {recArgInfo.fnName} is of type " ++
|
||||
m!"{recArgInfo.indName}, and these are not mutually recursive."
|
||||
private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr)
|
||||
(recArgInfos : Array RecArgInfo) : M (Array PreDefinition) := do
|
||||
let values ← preDefs.mapM (instantiateLambda ·.value xs)
|
||||
let indInfo ← getConstInfoInduct recArgInfos[0]!.indGroupInst.all[0]!
|
||||
if ← isInductivePredicate indInfo.name then
|
||||
-- Here we branch off to the IndPred construction, but only for non-mutual functions
|
||||
unless preDefs.size = 1 do
|
||||
throwError "structural mutual recursion over inductive predicates is not supported"
|
||||
trace[Elab.definition.structural] "Using mkIndPred construction"
|
||||
let preDef := preDefs[0]!
|
||||
let recArgInfo := recArgInfos[0]!
|
||||
let value := values[0]!
|
||||
let valueNew ← mkIndPredBRecOn recArgInfo value
|
||||
let valueNew ← mkLambdaFVars xs valueNew
|
||||
trace[Elab.definition.structural] "Nonrecursive value:{indentExpr valueNew}"
|
||||
check valueNew
|
||||
return #[{ preDef with value := valueNew }]
|
||||
|
||||
private def elimMutualRecursion (preDefs : Array PreDefinition) (recArgPoss : Array Nat) : M (Array PreDefinition) := do
|
||||
-- Sort the (indices of the) definitions by their position in indInfo.all
|
||||
let positions : Positions := .groupAndSort (·.indIdx) recArgInfos (Array.range indInfo.numTypeFormers)
|
||||
trace[Elab.definition.structural] "positions: {positions}"
|
||||
|
||||
-- Construct the common `.brecOn` arguments
|
||||
let motives ← (Array.zip recArgInfos values).mapM fun (r, v) => mkBRecOnMotive r v
|
||||
trace[Elab.definition.structural] "motives: {motives}"
|
||||
let brecOnConst ← mkBRecOnConst recArgInfos positions motives
|
||||
let FTypes ← inferBRecOnFTypes recArgInfos positions brecOnConst
|
||||
trace[Elab.definition.structural] "FTypes: {FTypes}"
|
||||
let FArgs ← (recArgInfos.zip (values.zip FTypes)).mapM fun (r, (v, t)) =>
|
||||
mkBRecOnF recArgInfos positions r v t
|
||||
trace[Elab.definition.structural] "FArgs: {FArgs}"
|
||||
-- Assemble the individual `.brecOn` applications
|
||||
let valuesNew ← (Array.zip recArgInfos values).mapIdxM fun i (r, v) =>
|
||||
mkBrecOnApp positions i brecOnConst FArgs r v
|
||||
-- Abstract over the fixed prefixed
|
||||
let valuesNew ← valuesNew.mapM (mkLambdaFVars xs ·)
|
||||
return (Array.zip preDefs valuesNew).map fun ⟨preDef, valueNew⟩ => { preDef with value := valueNew }
|
||||
|
||||
private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) :
|
||||
M (Array Nat × (Array PreDefinition) × Nat) := do
|
||||
withoutModifyingEnv do
|
||||
preDefs.forM (addAsAxiom ·)
|
||||
let names := preDefs.map (·.declName)
|
||||
let fnNames := preDefs.map (·.declName)
|
||||
let preDefs ← preDefs.mapM fun preDef =>
|
||||
return { preDef with value := (← preprocess preDef.value names) }
|
||||
return { preDef with value := (← preprocess preDef.value fnNames) }
|
||||
|
||||
-- The syntactically fixed arguments
|
||||
let maxNumFixed ← getMutualFixedPrefix preDefs
|
||||
|
||||
-- We do two passes to get the RecArgInfo values.
|
||||
-- From the first pass, we only keep the mininum of the `numFixed` reported.
|
||||
let numFixed ← lambdaBoundedTelescope preDefs[0]!.value maxNumFixed fun xs _ => do
|
||||
lambdaBoundedTelescope preDefs[0]!.value maxNumFixed fun xs _ => do
|
||||
assert! xs.size = maxNumFixed
|
||||
let values ← preDefs.mapM (instantiateLambda ·.value xs)
|
||||
|
||||
let recArgInfos ← preDefs.mapIdxM fun i preDef => do
|
||||
let recArgPos := recArgPoss[i]!
|
||||
let value := values[i]!
|
||||
lambdaTelescope value fun ys _value => do
|
||||
getRecArgInfo preDef.declName maxNumFixed (xs ++ ys) recArgPos
|
||||
|
||||
return (recArgInfos.map (·.numFixed)).foldl Nat.min maxNumFixed
|
||||
|
||||
if numFixed < maxNumFixed then
|
||||
trace[Elab.definition.structural] "Reduced numFixed from {maxNumFixed} to {numFixed}"
|
||||
|
||||
-- Now we bring exactly that `numFixed` parameter into scope.
|
||||
lambdaBoundedTelescope preDefs[0]!.value numFixed fun xs _ => do
|
||||
assert! xs.size = numFixed
|
||||
let values ← preDefs.mapM (instantiateLambda ·.value xs)
|
||||
|
||||
let recArgInfos ← preDefs.mapIdxM fun i preDef => do
|
||||
let recArgPos := recArgPoss[i]!
|
||||
let value := values[i]!
|
||||
lambdaTelescope value fun ys _value => do
|
||||
getRecArgInfo preDef.declName numFixed (xs ++ ys) recArgPos
|
||||
|
||||
-- Two passes should suffice
|
||||
assert! recArgInfos.all (·.numFixed = numFixed)
|
||||
|
||||
let indInfo ← getConstInfoInduct recArgInfos[0]!.indName
|
||||
if ← isInductivePredicate indInfo.name then
|
||||
-- Here we branch off to the IndPred construction, but only for non-mutual functions
|
||||
unless preDefs.size = 1 do
|
||||
throwError "structural mutual recursion over inductive predicates is not supported"
|
||||
trace[Elab.definition.structural] "Using mkIndPred construction"
|
||||
let preDef := preDefs[0]!
|
||||
let recArgInfo := recArgInfos[0]!
|
||||
let value := values[0]!
|
||||
let valueNew ← mkIndPredBRecOn recArgInfo value
|
||||
let valueNew ← mkLambdaFVars xs valueNew
|
||||
trace[Elab.definition.structural] "Nonrecursive value:{indentExpr valueNew}"
|
||||
check valueNew
|
||||
return #[{ preDef with value := valueNew }]
|
||||
|
||||
checkAllFromSameClique recArgInfos
|
||||
-- Sort the (indices of the) definitions by their position in indInfo.all
|
||||
let positions : Positions := .groupAndSort (·.indName) recArgInfos indInfo.all.toArray
|
||||
|
||||
-- Construct the common `.brecOn` arguments
|
||||
let motives ← (Array.zip recArgInfos values).mapM fun (r, v) => mkBRecOnMotive r v
|
||||
let brecOnConst ← mkBRecOnConst recArgInfos positions motives
|
||||
let FTypes ← inferBRecOnFTypes recArgInfos positions brecOnConst
|
||||
let FArgs ← (recArgInfos.zip (values.zip FTypes)).mapM fun (r, (v, t)) =>
|
||||
mkBRecOnF recArgInfos positions r v t
|
||||
-- Assemble the individual `.brecOn` applications
|
||||
let valuesNew ← (Array.zip recArgInfos values).mapIdxM fun i (r, v) =>
|
||||
mkBrecOnApp positions i brecOnConst FArgs r v
|
||||
-- Abstract over the fixed prefixed
|
||||
let valuesNew ← valuesNew.mapM (mkLambdaFVars xs ·)
|
||||
return (Array.zip preDefs valuesNew).map fun ⟨preDef, valueNew⟩ => { preDef with value := valueNew }
|
||||
tryAllArgs fnNames xs values termArg?s fun recArgInfos => do
|
||||
let recArgPoss := recArgInfos.map (·.recArgPos)
|
||||
trace[Elab.definition.structural] "Trying argument set {recArgPoss}"
|
||||
let numFixed := recArgInfos.foldl (·.min ·.numFixed) maxNumFixed
|
||||
if numFixed < maxNumFixed then
|
||||
trace[Elab.definition.structural] "Reduced numFixed from {maxNumFixed} to {numFixed}"
|
||||
-- We may have decreased the number of arguments we consider fixed, so update
|
||||
-- the recArgInfos, remove the extra arguments from local environment, and recalculate value
|
||||
let recArgInfos := recArgInfos.map ({· with numFixed := numFixed })
|
||||
withErasedFVars (xs.extract numFixed xs.size |>.map (·.fvarId!)) do
|
||||
let xs := xs[:numFixed]
|
||||
let preDefs' ← elimMutualRecursion preDefs xs recArgInfos
|
||||
return (recArgPoss, preDefs', numFixed)
|
||||
|
||||
def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
if let some ref := preDef.termination.terminationBy?? then
|
||||
@@ -179,34 +164,20 @@ def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
let stx ← termArg.delab arity (extraParams := preDef.termination.extraParams)
|
||||
Tactic.TryThis.addSuggestion ref stx
|
||||
|
||||
private def inferRecArgPos (preDefs : Array PreDefinition)
|
||||
(termArgs? : Option TerminationArguments) : M (Array Nat × Array PreDefinition) := do
|
||||
withoutModifyingEnv do
|
||||
if let some termArgs := termArgs? then
|
||||
let recArgPoss ← termArgs.mapM (·.structuralArg)
|
||||
let preDefsNew ← elimMutualRecursion preDefs recArgPoss
|
||||
return (recArgPoss, preDefsNew)
|
||||
else
|
||||
let #[preDef] := preDefs
|
||||
| throwError "mutual structural recursion requires explicit `termination_by` clauses"
|
||||
-- Use termination_by annotation to find argument to recurse on, or just try all
|
||||
tryAllArgs preDef.value fun i =>
|
||||
mapError (f := fun msg => m!"argument #{i+1} cannot be used for structural recursion{indentD msg}") do
|
||||
let preDefsNew ← elimMutualRecursion #[preDef] #[i]
|
||||
return (#[i], preDefsNew)
|
||||
|
||||
def structuralRecursion (preDefs : Array PreDefinition) (termArgs? : Option TerminationArguments) : TermElabM Unit := do
|
||||
let ((recArgPoss, preDefsNonRec), state) ← run <| inferRecArgPos preDefs termArgs?
|
||||
def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
|
||||
let names := preDefs.map (·.declName)
|
||||
let ((recArgPoss, preDefsNonRec, numFixed), state) ← run <| inferRecArgPos preDefs termArg?s
|
||||
for recArgPos in recArgPoss, preDef in preDefs do
|
||||
reportTermArg preDef recArgPos
|
||||
state.addMatchers.forM liftM
|
||||
preDefsNonRec.forM fun preDefNonRec => do
|
||||
let preDefNonRec ← eraseRecAppSyntax preDefNonRec
|
||||
-- state.addMatchers.forM liftM
|
||||
mapError (addNonRec preDefNonRec (applyAttrAfterCompilation := false)) fun msg =>
|
||||
m!"structural recursion failed, produced type incorrect term{indentD msg}"
|
||||
-- We create the `_unsafe_rec` before we abstract nested proofs.
|
||||
-- Reason: the nested proofs may be referring to the _unsafe_rec.
|
||||
mapError (f := (m!"structural recursion failed, produced type incorrect term{indentD ·}")) do
|
||||
-- We create the `_unsafe_rec` before we abstract nested proofs.
|
||||
-- Reason: the nested proofs may be referring to the _unsafe_rec.
|
||||
addNonRec preDefNonRec (applyAttrAfterCompilation := false) (all := names.toList)
|
||||
let preDefs ← preDefs.mapM (eraseRecAppSyntax ·)
|
||||
addAndCompilePartialRec preDefs
|
||||
for preDef in preDefs, recArgPos in recArgPoss do
|
||||
@@ -219,13 +190,11 @@ def structuralRecursion (preDefs : Array PreDefinition) (termArgs? : Option Term
|
||||
for theorems and definitions that are propositions.
|
||||
See issue #2327
|
||||
-/
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
|
||||
addSmartUnfoldingDef preDef recArgPos
|
||||
markAsRecursive preDef.declName
|
||||
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.structural
|
||||
|
||||
end Structural
|
||||
|
||||
|
||||
60
src/Lean/Elab/PreDefinition/Structural/RecArgInfo.lean
Normal file
60
src/Lean/Elab/PreDefinition/Structural/RecArgInfo.lean
Normal file
@@ -0,0 +1,60 @@
|
||||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Basic
|
||||
import Lean.Meta.ForEachExpr
|
||||
import Lean.Elab.PreDefinition.Structural.IndGroupInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
|
||||
|
||||
/--
|
||||
Information about the argument of interest of a structurally recursive function.
|
||||
|
||||
The `Expr`s in this data structure expect the `fixedParams` to be in scope, but not the other
|
||||
parameters of the function. This ensures that this data structure makes sense in the other functions
|
||||
of a mutually recursive group.
|
||||
-/
|
||||
structure RecArgInfo where
|
||||
/-- the name of the recursive function -/
|
||||
fnName : Name
|
||||
/-- the fixed prefix of arguments of the function we are trying to justify termination using structural recursion. -/
|
||||
numFixed : Nat
|
||||
/-- position of the argument (counted including fixed prefix) we are recursing on -/
|
||||
recArgPos : Nat
|
||||
/-- position of the indices (counted including fixed prefix) of the inductive datatype indices we are recursing on -/
|
||||
indicesPos : Array Nat
|
||||
/-- The inductive group (with parameters) of the argument's type -/
|
||||
indGroupInst : IndGroupInst
|
||||
/--
|
||||
index of the inductive datatype of the argument we are recursing on.
|
||||
If `< indAll.all`, a normal data type, else an auxillary data type due to nested recursion
|
||||
-/
|
||||
indIdx : Nat
|
||||
deriving Inhabited
|
||||
|
||||
/--
|
||||
If `xs` are the parameters of the functions (excluding fixed prefix), partitions them
|
||||
into indices and major arguments, and other parameters.
|
||||
-/
|
||||
def RecArgInfo.pickIndicesMajor (info : RecArgInfo) (xs : Array Expr) : (Array Expr × Array Expr) := Id.run do
|
||||
let mut indexMajorArgs := #[]
|
||||
let mut otherArgs := #[]
|
||||
for h : i in [:xs.size] do
|
||||
let j := i + info.numFixed
|
||||
if j = info.recArgPos || info.indicesPos.contains j then
|
||||
indexMajorArgs := indexMajorArgs.push xs[i]
|
||||
else
|
||||
otherArgs := otherArgs.push xs[i]
|
||||
return (indexMajorArgs, otherArgs)
|
||||
|
||||
/--
|
||||
Name of the recursive data type. Assumes that it is not one of the auxillary ones.
|
||||
-/
|
||||
def RecArgInfo.indName! (info : RecArgInfo) : Name :=
|
||||
info.indGroupInst.all[info.indIdx]!
|
||||
|
||||
end Lean.Elab.Structural
|
||||
@@ -10,7 +10,7 @@ import Lean.Elab.Term
|
||||
import Lean.Elab.Binders
|
||||
import Lean.Elab.SyntheticMVars
|
||||
import Lean.Elab.PreDefinition.TerminationHint
|
||||
import Lean.PrettyPrinter.Delaborator
|
||||
import Lean.PrettyPrinter.Delaborator.Basic
|
||||
|
||||
/-!
|
||||
This module contains
|
||||
@@ -115,7 +115,7 @@ def TerminationArgument.delab (arity : Nat) (extraParams : Nat) (termArg : Termi
|
||||
-- any variable not mentioned syntatically (it may appear in the `Expr`, so do not just use
|
||||
-- `e.bindingBody!.hasLooseBVar`) should be delaborated as a hole.
|
||||
let vars : TSyntaxArray [`ident, `Lean.Parser.Term.hole] :=
|
||||
Array.map (fun (i : Ident) => if hasIdent i.getId stxBody then i else hole) vars
|
||||
Array.map (fun (i : Ident) => if stxBody.raw.hasIdent i.getId then i else hole) vars
|
||||
-- drop trailing underscores
|
||||
let mut vars := vars
|
||||
while ! vars.isEmpty && vars.back.raw.isOfKind ``hole do vars := vars.pop
|
||||
|
||||
@@ -86,7 +86,8 @@ def varyingVarNames (fixedPrefixSize : Nat) (preDef : PreDefinition) : MetaM (Ar
|
||||
let xs : Array Expr := xs[fixedPrefixSize:]
|
||||
xs.mapM (·.fvarId!.getUserName)
|
||||
|
||||
def wfRecursion (preDefs : Array PreDefinition) (termArgs? : Option TerminationArguments) : TermElabM Unit := do
|
||||
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 preDefs ← preDefs.mapM fun preDef =>
|
||||
return { preDef with value := (← preprocess preDef.value) }
|
||||
let (fixedPrefixSize, argsPacker, unaryPreDef) ← withoutModifyingEnv do
|
||||
|
||||
@@ -40,3 +40,4 @@ import Lean.Elab.Tactic.LibrarySearch
|
||||
import Lean.Elab.Tactic.ShowTerm
|
||||
import Lean.Elab.Tactic.Rfl
|
||||
import Lean.Elab.Tactic.Rewrites
|
||||
import Lean.Elab.Tactic.DiscrTreeKey
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Util
|
||||
import Lean.Elab.Term
|
||||
|
||||
namespace Lean.Elab
|
||||
@@ -398,12 +399,19 @@ def ensureHasNoMVars (e : Expr) : TacticM Unit := do
|
||||
if e.hasExprMVar then
|
||||
throwError "tactic failed, resulting expression contains metavariables{indentExpr e}"
|
||||
|
||||
/-- Close main goal using the given expression. If `checkUnassigned == true`, then `val` must not contain unassigned metavariables. -/
|
||||
def closeMainGoal (val : Expr) (checkUnassigned := true): TacticM Unit := do
|
||||
/--
|
||||
Closes main goal using the given expression.
|
||||
If `checkUnassigned == true`, then `val` must not contain unassigned metavariables.
|
||||
Returns `true` if `val` was successfully used to close the goal.
|
||||
-/
|
||||
def closeMainGoal (tacName : Name) (val : Expr) (checkUnassigned := true): TacticM Unit := do
|
||||
if checkUnassigned then
|
||||
ensureHasNoMVars val
|
||||
(← getMainGoal).assign val
|
||||
replaceMainGoal []
|
||||
let mvarId ← getMainGoal
|
||||
if (← mvarId.checkedAssign val) then
|
||||
replaceMainGoal []
|
||||
else
|
||||
throwTacticEx tacName mvarId m!"attempting to close the goal using{indentExpr val}\nthis is often due occurs-check failure"
|
||||
|
||||
@[inline] def liftMetaMAtMain (x : MVarId → MetaM α) : TacticM α := do
|
||||
withMainContext do x (← getMainGoal)
|
||||
|
||||
65
src/Lean/Elab/Tactic/DiscrTreeKey.lean
Normal file
65
src/Lean/Elab/Tactic/DiscrTreeKey.lean
Normal file
@@ -0,0 +1,65 @@
|
||||
/-
|
||||
Copyright (c) 2024 Matthew Robert Ballard. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Tomas Skrivan, Matthew Robert Ballard
|
||||
-/
|
||||
prelude
|
||||
import Init.Tactics
|
||||
import Lean.Elab.Command
|
||||
import Lean.Meta.Tactic.Simp.SimpTheorems
|
||||
|
||||
namespace Lean.Elab.Tactic.DiscrTreeKey
|
||||
|
||||
open Lean.Meta DiscrTree
|
||||
open Lean.Elab.Tactic
|
||||
open Lean.Elab.Command
|
||||
|
||||
private def mkKey (e : Expr) (simp : Bool) : MetaM (Array Key) := do
|
||||
let (_, _, type) ← withReducible <| forallMetaTelescopeReducing e
|
||||
let type ← whnfR type
|
||||
if simp then
|
||||
if let some (_, lhs, _) := type.eq? then
|
||||
mkPath lhs simpDtConfig
|
||||
else if let some (lhs, _) := type.iff? then
|
||||
mkPath lhs simpDtConfig
|
||||
else if let some (_, lhs, _) := type.ne? then
|
||||
mkPath lhs simpDtConfig
|
||||
else if let some p := type.not? then
|
||||
match p.eq? with
|
||||
| some (_, lhs, _) =>
|
||||
mkPath lhs simpDtConfig
|
||||
| _ => mkPath p simpDtConfig
|
||||
else
|
||||
mkPath type simpDtConfig
|
||||
else
|
||||
mkPath type {}
|
||||
|
||||
private def getType (t : TSyntax `term) : TermElabM Expr := do
|
||||
if let `($id:ident) := t then
|
||||
if let some ldecl := (← getLCtx).findFromUserName? id.getId then
|
||||
return ldecl.type
|
||||
else
|
||||
let info ← getConstInfo (← realizeGlobalConstNoOverloadWithInfo id)
|
||||
return info.type
|
||||
else
|
||||
Term.elabTerm t none
|
||||
|
||||
@[builtin_command_elab Lean.Parser.discrTreeKeyCmd]
|
||||
def evalDiscrTreeKeyCmd : CommandElab := fun stx => do
|
||||
Command.liftTermElabM <| do
|
||||
match stx with
|
||||
| `(command| #discr_tree_key $t:term) => do
|
||||
let type ← getType t
|
||||
logInfo (← keysAsPattern <| ← mkKey type false)
|
||||
| _ => Elab.throwUnsupportedSyntax
|
||||
|
||||
@[builtin_command_elab Lean.Parser.discrTreeSimpKeyCmd]
|
||||
def evalDiscrTreeSimpKeyCmd : CommandElab := fun stx => do
|
||||
Command.liftTermElabM <| do
|
||||
match stx with
|
||||
| `(command| #discr_tree_simp_key $t:term) => do
|
||||
let type ← getType t
|
||||
logInfo (← keysAsPattern <| ← mkKey type true)
|
||||
| _ => Elab.throwUnsupportedSyntax
|
||||
|
||||
end Lean.Elab.Tactic.DiscrTreeKey
|
||||
@@ -56,9 +56,9 @@ def elabTermEnsuringType (stx : Syntax) (expectedType? : Option Expr) (mayPostpo
|
||||
return e
|
||||
|
||||
/-- Try to close main goal using `x target`, where `target` is the type of the main goal. -/
|
||||
def closeMainGoalUsing (x : Expr → TacticM Expr) (checkUnassigned := true) : TacticM Unit :=
|
||||
def closeMainGoalUsing (tacName : Name) (x : Expr → TacticM Expr) (checkUnassigned := true) : TacticM Unit :=
|
||||
withMainContext do
|
||||
closeMainGoal (checkUnassigned := checkUnassigned) (← x (← getMainTarget))
|
||||
closeMainGoal (tacName := tacName) (checkUnassigned := checkUnassigned) (← x (← getMainTarget))
|
||||
|
||||
def logUnassignedAndAbort (mvarIds : Array MVarId) : TacticM Unit := do
|
||||
if (← Term.logUnassignedUsingErrorInfos mvarIds) then
|
||||
@@ -68,13 +68,14 @@ def filterOldMVars (mvarIds : Array MVarId) (mvarCounterSaved : Nat) : MetaM (Ar
|
||||
let mctx ← getMCtx
|
||||
return mvarIds.filter fun mvarId => (mctx.getDecl mvarId |>.index) >= mvarCounterSaved
|
||||
|
||||
@[builtin_tactic «exact»] def evalExact : Tactic := fun stx =>
|
||||
@[builtin_tactic «exact»] def evalExact : Tactic := fun stx => do
|
||||
match stx with
|
||||
| `(tactic| exact $e) => closeMainGoalUsing (checkUnassigned := false) fun type => do
|
||||
let mvarCounterSaved := (← getMCtx).mvarCounter
|
||||
let r ← elabTermEnsuringType e type
|
||||
logUnassignedAndAbort (← filterOldMVars (← getMVars r) mvarCounterSaved)
|
||||
return r
|
||||
| `(tactic| exact $e) =>
|
||||
closeMainGoalUsing `exact (checkUnassigned := false) fun type => do
|
||||
let mvarCounterSaved := (← getMCtx).mvarCounter
|
||||
let r ← elabTermEnsuringType e type
|
||||
logUnassignedAndAbort (← filterOldMVars (← getMVars r) mvarCounterSaved)
|
||||
return r
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
def sortMVarIdArrayByIndex [MonadMCtx m] [Monad m] (mvarIds : Array MVarId) : m (Array MVarId) := do
|
||||
@@ -359,8 +360,8 @@ def elabAsFVar (stx : Syntax) (userName? : Option Name := none) : TacticM FVarId
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
/--
|
||||
Make sure `expectedType` does not contain free and metavariables.
|
||||
It applies zeta and zetaDelta-reduction to eliminate let-free-vars.
|
||||
Make sure `expectedType` does not contain free and metavariables.
|
||||
It applies zeta and zetaDelta-reduction to eliminate let-free-vars.
|
||||
-/
|
||||
private def preprocessPropToDecide (expectedType : Expr) : TermElabM Expr := do
|
||||
let mut expectedType ← instantiateMVars expectedType
|
||||
@@ -370,31 +371,95 @@ private def preprocessPropToDecide (expectedType : Expr) : TermElabM Expr := do
|
||||
throwError "expected type must not contain free or meta variables{indentExpr expectedType}"
|
||||
return expectedType
|
||||
|
||||
/--
|
||||
Given the decidable instance `inst`, reduces it and returns a decidable instance expression
|
||||
in whnf that can be regarded as the reason for the failure of `inst` to fully reduce.
|
||||
-/
|
||||
private partial def blameDecideReductionFailure (inst : Expr) : MetaM Expr := do
|
||||
let inst ← whnf inst
|
||||
-- If it's the Decidable recursor, then blame the major premise.
|
||||
if inst.isAppOfArity ``Decidable.rec 5 then
|
||||
return ← blameDecideReductionFailure inst.appArg!
|
||||
-- If it is a matcher, look for a discriminant that's a Decidable instance to blame.
|
||||
if let .const c _ := inst.getAppFn then
|
||||
if let some info ← getMatcherInfo? c then
|
||||
if inst.getAppNumArgs == info.arity then
|
||||
let args := inst.getAppArgs
|
||||
for i in [0:info.numDiscrs] do
|
||||
let inst' := args[info.numParams + 1 + i]!
|
||||
if (← Meta.isClass? (← inferType inst')) == ``Decidable then
|
||||
let inst'' ← whnf inst'
|
||||
if !(inst''.isAppOf ``isTrue || inst''.isAppOf ``isFalse) then
|
||||
return ← blameDecideReductionFailure inst''
|
||||
return inst
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.decide] def evalDecide : Tactic := fun _ =>
|
||||
closeMainGoalUsing fun expectedType => do
|
||||
closeMainGoalUsing `decide fun expectedType => do
|
||||
let expectedType ← preprocessPropToDecide expectedType
|
||||
let d ← mkDecide expectedType
|
||||
let d ← instantiateMVars d
|
||||
-- Get instance from `d`
|
||||
let s := d.appArg!
|
||||
-- Reduce the instance rather than `d` itself, since that gives a nicer error message on failure.
|
||||
let r ← withDefault <| whnf s
|
||||
if r.isAppOf ``isFalse then
|
||||
throwError "\
|
||||
tactic 'decide' proved that the proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
is false"
|
||||
unless r.isAppOf ``isTrue do
|
||||
throwError "\
|
||||
tactic 'decide' failed for proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
since its 'Decidable' instance reduced to\
|
||||
{indentExpr r}\n\
|
||||
rather than to the 'isTrue' constructor."
|
||||
-- While we have a proof from reduction, we do not embed it in the proof term,
|
||||
-- but rather we let the kernel recompute it during type checking from a more efficient term.
|
||||
let rflPrf ← mkEqRefl (toExpr true)
|
||||
return mkApp3 (Lean.mkConst ``of_decide_eq_true) expectedType s rflPrf
|
||||
-- Reduce the instance rather than `d` itself for diagnostics purposes.
|
||||
let r ← withAtLeastTransparency .default <| whnf s
|
||||
if r.isAppOf ``isTrue then
|
||||
-- Success!
|
||||
-- While we have a proof from reduction, we do not embed it in the proof term,
|
||||
-- and instead we let the kernel recompute it during type checking from the following more efficient term.
|
||||
let rflPrf ← mkEqRefl (toExpr true)
|
||||
return mkApp3 (Lean.mkConst ``of_decide_eq_true) expectedType s rflPrf
|
||||
else
|
||||
-- Diagnose the failure, lazily so that there is no performance impact if `decide` isn't being used interactively.
|
||||
throwError MessageData.ofLazyM (es := #[expectedType]) do
|
||||
if r.isAppOf ``isFalse then
|
||||
return m!"\
|
||||
tactic 'decide' proved that the proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
is false"
|
||||
-- Re-reduce the instance and collect diagnostics, to get all unfolded Decidable instances
|
||||
let (reason, unfoldedInsts) ← withoutModifyingState <| withOptions (fun opt => diagnostics.set opt true) do
|
||||
modifyDiag (fun _ => {})
|
||||
let reason ← withAtLeastTransparency .default <| blameDecideReductionFailure s
|
||||
let unfolded := (← get).diag.unfoldCounter.foldl (init := #[]) fun cs n _ => cs.push n
|
||||
let unfoldedInsts ← unfolded |>.qsort Name.lt |>.filterMapM fun n => do
|
||||
let e ← mkConstWithLevelParams n
|
||||
if (← Meta.isClass? (← inferType e)) == ``Decidable then
|
||||
return m!"'{MessageData.ofConst e}'"
|
||||
else
|
||||
return none
|
||||
return (reason, unfoldedInsts)
|
||||
let stuckMsg :=
|
||||
if unfoldedInsts.isEmpty then
|
||||
m!"Reduction got stuck at the '{MessageData.ofConstName ``Decidable}' instance{indentExpr reason}"
|
||||
else
|
||||
let instances := if unfoldedInsts.size == 1 then "instance" else "instances"
|
||||
m!"After unfolding the {instances} {MessageData.andList unfoldedInsts.toList}, \
|
||||
reduction got stuck at the '{MessageData.ofConstName ``Decidable}' instance{indentExpr reason}"
|
||||
let hint :=
|
||||
if reason.isAppOf ``Eq.rec then
|
||||
m!"\n\n\
|
||||
Hint: Reduction got stuck on '▸' ({MessageData.ofConstName ``Eq.rec}), \
|
||||
which suggests that one of the '{MessageData.ofConstName ``Decidable}' instances is defined using tactics such as 'rw' or 'simp'. \
|
||||
To avoid tactics, make use of functions such as \
|
||||
'{MessageData.ofConstName ``inferInstanceAs}' or '{MessageData.ofConstName ``decidable_of_decidable_of_iff}' \
|
||||
to alter a proposition."
|
||||
else if reason.isAppOf ``Classical.choice then
|
||||
m!"\n\n\
|
||||
Hint: Reduction got stuck on '{MessageData.ofConstName ``Classical.choice}', \
|
||||
which indicates that a '{MessageData.ofConstName ``Decidable}' instance \
|
||||
is defined using classical reasoning, proving an instance exists rather than giving a concrete construction. \
|
||||
The 'decide' tactic works by evaluating a decision procedure via reduction, and it cannot make progress with such instances. \
|
||||
This can occur due to the 'opened scoped Classical' command, which enables the instance \
|
||||
'{MessageData.ofConstName ``Classical.propDecidable}'."
|
||||
else
|
||||
MessageData.nil
|
||||
return m!"\
|
||||
tactic 'decide' failed for proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
since its '{MessageData.ofConstName ``Decidable}' instance\
|
||||
{indentExpr s}\n\
|
||||
did not reduce to '{MessageData.ofConstName ``isTrue}' or '{MessageData.ofConstName ``isFalse}'.\n\n\
|
||||
{stuckMsg}{hint}"
|
||||
|
||||
private def mkNativeAuxDecl (baseName : Name) (type value : Expr) : TermElabM Name := do
|
||||
let auxName ← Term.mkAuxName baseName
|
||||
@@ -408,7 +473,7 @@ private def mkNativeAuxDecl (baseName : Name) (type value : Expr) : TermElabM Na
|
||||
pure auxName
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.nativeDecide] def evalNativeDecide : Tactic := fun _ =>
|
||||
closeMainGoalUsing fun expectedType => do
|
||||
closeMainGoalUsing `nativeDecide fun expectedType => do
|
||||
let expectedType ← preprocessPropToDecide expectedType
|
||||
let d ← mkDecide expectedType
|
||||
let auxDeclName ← mkNativeAuxDecl `_nativeDecide (Lean.mkConst `Bool) d
|
||||
|
||||
@@ -5,14 +5,179 @@ Authors: Gabriel Ebner, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Ext
|
||||
import Lean.Elab.DeclarationRange
|
||||
import Lean.Elab.Tactic.RCases
|
||||
import Lean.Elab.Tactic.Repeat
|
||||
import Lean.Elab.Tactic.BuiltinTactic
|
||||
import Lean.Elab.Command
|
||||
import Lean.Linter.Util
|
||||
|
||||
/-!
|
||||
# Implementation of the `@[ext]` attribute
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.Ext
|
||||
open Meta Term
|
||||
|
||||
/-!
|
||||
### Meta code for creating ext theorems
|
||||
-/
|
||||
|
||||
/--
|
||||
Constructs the hypotheses for the structure extensionality theorem that
|
||||
states that two structures are equal if their fields are equal.
|
||||
|
||||
Calls the continuation `k` with the list of parameters to the structure,
|
||||
two structure variables `x` and `y`, and a list of pairs `(field, ty)`
|
||||
where each `ty` is of the form `x.field = y.field` or `HEq x.field y.field`.
|
||||
|
||||
If `flat` parses to `true`, any fields inherited from parent structures
|
||||
are treated as fields of the given structure type.
|
||||
If it is `false`, then the behind-the-scenes encoding of inherited fields
|
||||
is visible in the extensionality lemma.
|
||||
-/
|
||||
def withExtHyps (struct : Name) (flat : Bool)
|
||||
(k : Array Expr → (x y : Expr) → Array (Name × Expr) → MetaM α) : MetaM α := do
|
||||
unless isStructure (← getEnv) struct do throwError "not a structure: {struct}"
|
||||
let structC ← mkConstWithLevelParams struct
|
||||
forallTelescope (← inferType structC) fun params _ => do
|
||||
withNewBinderInfos (params.map (·.fvarId!, BinderInfo.implicit)) do
|
||||
withLocalDecl `x .implicit (mkAppN structC params) fun x => do
|
||||
withLocalDecl `y .implicit (mkAppN structC params) fun y => do
|
||||
let mut hyps := #[]
|
||||
let fields ← if flat then
|
||||
pure <| getStructureFieldsFlattened (← getEnv) struct (includeSubobjectFields := false)
|
||||
else
|
||||
pure <| getStructureFields (← getEnv) struct
|
||||
for field in fields do
|
||||
let x_f ← mkProjection x field
|
||||
let y_f ← mkProjection y field
|
||||
unless ← isProof x_f do
|
||||
hyps := hyps.push (field, ← mkEqHEq x_f y_f)
|
||||
k params x y hyps
|
||||
|
||||
/--
|
||||
Creates the type of the extensionality theorem for the given structure,
|
||||
returning `∀ {x y : Struct}, x.1 = y.1 → x.2 = y.2 → x = y`, for example.
|
||||
-/
|
||||
def mkExtType (structName : Name) (flat : Bool) : MetaM Expr := withLCtx {} {} do
|
||||
withExtHyps structName flat fun params x y hyps => do
|
||||
let ty := hyps.foldr (init := ← mkEq x y) fun (f, h) ty => .forallE f h ty .default
|
||||
mkForallFVars (params |>.push x |>.push y) ty
|
||||
|
||||
/--
|
||||
Derives the type of the `iff` form of an ext theorem.
|
||||
-/
|
||||
def mkExtIffType (extThmName : Name) : MetaM Expr := withLCtx {} {} do
|
||||
forallTelescopeReducing (← getConstInfo extThmName).type fun args ty => do
|
||||
let failNotEq := throwError "expecting a theorem proving x = y, but instead it proves{indentD ty}"
|
||||
let some (_, x, y) := ty.eq? | failNotEq
|
||||
let some xIdx := args.findIdx? (· == x) | failNotEq
|
||||
let some yIdx := args.findIdx? (· == y) | failNotEq
|
||||
unless xIdx + 1 == yIdx do
|
||||
throwError "expecting {x} and {y} to be consecutive arguments"
|
||||
let startIdx := yIdx + 1
|
||||
let toRevert := args[startIdx:].toArray
|
||||
let fvars ← toRevert.foldlM (init := {}) (fun st e => return collectFVars st (← inferType e))
|
||||
for fvar in toRevert do
|
||||
unless ← Meta.isProof fvar do
|
||||
throwError "argument {fvar} is not a proof, which is not supported for arguments after {x} and {y}"
|
||||
if fvars.fvarSet.contains fvar.fvarId! then
|
||||
throwError "argument {fvar} is depended upon, which is not supported for arguments after {x} and {y}"
|
||||
let conj := mkAndN (← toRevert.mapM (inferType ·)).toList
|
||||
-- Make everything implicit except for inst implicits
|
||||
let mut newBis := #[]
|
||||
for fvar in args[0:startIdx] do
|
||||
if (← fvar.fvarId!.getBinderInfo) matches .default | .strictImplicit then
|
||||
newBis := newBis.push (fvar.fvarId!, .implicit)
|
||||
withNewBinderInfos newBis do
|
||||
mkForallFVars args[:startIdx] <| mkIff ty conj
|
||||
|
||||
/--
|
||||
Ensures that the given structure has an ext theorem, without validating any pre-existing theorems.
|
||||
Returns the name of the ext theorem.
|
||||
|
||||
See `Lean.Elab.Tactic.Ext.withExtHyps` for an explanation of the `flat` argument.
|
||||
-/
|
||||
def realizeExtTheorem (structName : Name) (flat : Bool) : Elab.Command.CommandElabM Name := do
|
||||
unless isStructure (← getEnv) structName do
|
||||
throwError "'{structName}' is not a structure"
|
||||
let extName := structName.mkStr "ext"
|
||||
unless (← getEnv).contains extName do
|
||||
try
|
||||
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extName do
|
||||
let type ← mkExtType structName flat
|
||||
let pf ← withSynthesize do
|
||||
let indVal ← getConstInfoInduct structName
|
||||
let params := Array.mkArray indVal.numParams (← `(_))
|
||||
Elab.Term.elabTermEnsuringType (expectedType? := type) (implicitLambda := false)
|
||||
-- introduce the params, do cases on 'x' and 'y', and then substitute each equation
|
||||
(← `(by intro $params* {..} {..}; intros; subst_eqs; rfl))
|
||||
let pf ← instantiateMVars pf
|
||||
if pf.hasMVar then throwError "(internal error) synthesized ext proof contains metavariables{indentD pf}"
|
||||
let info ← getConstInfo structName
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name := extName
|
||||
type
|
||||
value := pf
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
modifyEnv fun env => addProtected env extName
|
||||
Lean.addDeclarationRanges extName {
|
||||
range := ← getDeclarationRange (← getRef)
|
||||
selectionRange := ← getDeclarationRange (← getRef) }
|
||||
catch e =>
|
||||
throwError m!"\
|
||||
Failed to generate an 'ext' theorem for '{MessageData.ofConstName structName}': {e.toMessageData}"
|
||||
return extName
|
||||
|
||||
/--
|
||||
Given an 'ext' theorem, ensures that there is an iff version of the theorem (if possible),
|
||||
without validating any pre-existing theorems.
|
||||
Returns the name of the 'ext_iff' theorem.
|
||||
-/
|
||||
def realizeExtIffTheorem (extName : Name) : Elab.Command.CommandElabM Name := do
|
||||
let extIffName : Name :=
|
||||
match extName with
|
||||
| .str n s => .str n (s ++ "_iff")
|
||||
| _ => .str extName "ext_iff"
|
||||
unless (← getEnv).contains extIffName do
|
||||
try
|
||||
let info ← getConstInfo extName
|
||||
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extIffName do
|
||||
let type ← mkExtIffType extName
|
||||
let pf ← withSynthesize do
|
||||
Elab.Term.elabTermEnsuringType (expectedType? := type) <| ← `(by
|
||||
intros
|
||||
refine ⟨?_, ?_⟩
|
||||
· intro h; cases h; and_intros <;> (intros; first | rfl | simp | fail "Failed to prove converse of ext theorem")
|
||||
· intro; (repeat cases ‹_ ∧ _›); apply $(mkCIdent extName) <;> assumption)
|
||||
let pf ← instantiateMVars pf
|
||||
if pf.hasMVar then throwError "(internal error) synthesized ext_iff proof contains metavariables{indentD pf}"
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name := extIffName
|
||||
type
|
||||
value := pf
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
-- Only declarations in a namespace can be protected:
|
||||
unless extIffName.isAtomic do
|
||||
modifyEnv fun env => addProtected env extIffName
|
||||
Lean.addDeclarationRanges extIffName {
|
||||
range := ← getDeclarationRange (← getRef)
|
||||
selectionRange := ← getDeclarationRange (← getRef) }
|
||||
catch e =>
|
||||
throwError m!"\
|
||||
Failed to generate an 'ext_iff' theorem from '{MessageData.ofConstName extName}': {e.toMessageData}\n\
|
||||
\n\
|
||||
Try '@[ext (iff := false)]' to prevent generating an 'ext_iff' theorem."
|
||||
return extIffName
|
||||
|
||||
|
||||
/-!
|
||||
### Attribute
|
||||
-/
|
||||
|
||||
/-- Information about an extensionality theorem, stored in the environment extension. -/
|
||||
structure ExtTheorem where
|
||||
/-- Declaration name of the extensionality theorem. -/
|
||||
@@ -66,9 +231,9 @@ def ExtTheorems.eraseCore (d : ExtTheorems) (declName : Name) : ExtTheorems :=
|
||||
{ d with erased := d.erased.insert declName }
|
||||
|
||||
/--
|
||||
Erases a name marked as a `ext` attribute.
|
||||
Check that it does in fact have the `ext` attribute by making sure it names a `ExtTheorem`
|
||||
found somewhere in the state's tree, and is not erased.
|
||||
Erases a name marked as a `ext` attribute.
|
||||
Check that it does in fact have the `ext` attribute by making sure it names a `ExtTheorem`
|
||||
found somewhere in the state's tree, and is not erased.
|
||||
-/
|
||||
def ExtTheorems.erase [Monad m] [MonadError m] (d : ExtTheorems) (declName : Name) :
|
||||
m ExtTheorems := do
|
||||
@@ -79,97 +244,40 @@ def ExtTheorems.erase [Monad m] [MonadError m] (d : ExtTheorems) (declName : Nam
|
||||
builtin_initialize registerBuiltinAttribute {
|
||||
name := `ext
|
||||
descr := "Marks a theorem as an extensionality theorem"
|
||||
add := fun declName stx kind => do
|
||||
let `(attr| ext $[(flat := $f)]? $(prio)?) := stx
|
||||
| throwError "unexpected @[ext] attribute {stx}"
|
||||
add := fun declName stx kind => MetaM.run' do
|
||||
let `(attr| ext $[(iff := false%$iffFalse?)]? $[(flat := false%$flatFalse?)]? $(prio)?) := stx
|
||||
| throwError "invalid syntax for 'ext' attribute"
|
||||
let iff := iffFalse?.isNone
|
||||
let flat := flatFalse?.isNone
|
||||
let mut declName := declName
|
||||
if isStructure (← getEnv) declName then
|
||||
liftCommandElabM <| Elab.Command.elabCommand <|
|
||||
← `(declare_ext_theorems_for $[(flat := $f)]? $(mkCIdentFrom stx declName) $[$prio]?)
|
||||
else MetaM.run' do
|
||||
if let some flat := f then
|
||||
throwErrorAt flat "unexpected 'flat' config on @[ext] theorem"
|
||||
let declTy := (← getConstInfo declName).type
|
||||
let (_, _, declTy) ← withDefault <| forallMetaTelescopeReducing declTy
|
||||
let failNotEq := throwError
|
||||
"@[ext] attribute only applies to structures or theorems proving x = y, got {declTy}"
|
||||
let some (ty, lhs, rhs) := declTy.eq? | failNotEq
|
||||
unless lhs.isMVar && rhs.isMVar do failNotEq
|
||||
let keys ← withReducible <| DiscrTree.mkPath ty extExt.config
|
||||
let priority ← liftCommandElabM do Elab.liftMacroM do
|
||||
evalPrio (prio.getD (← `(prio| default)))
|
||||
extExtension.add {declName, keys, priority} kind
|
||||
declName ← liftCommandElabM <| withRef stx <| realizeExtTheorem declName flat
|
||||
else if let some stx := flatFalse? then
|
||||
throwErrorAt stx "unexpected 'flat' configuration on @[ext] theorem"
|
||||
-- Validate and add theorem to environment extension
|
||||
let declTy := (← getConstInfo declName).type
|
||||
let (_, _, declTy) ← withDefault <| forallMetaTelescopeReducing declTy
|
||||
let failNotEq := throwError "\
|
||||
@[ext] attribute only applies to structures and to theorems proving 'x = y' where 'x' and 'y' are variables, \
|
||||
but this theorem proves{indentD declTy}"
|
||||
let some (ty, lhs, rhs) := declTy.eq? | failNotEq
|
||||
unless lhs.isMVar && rhs.isMVar do failNotEq
|
||||
let keys ← withReducible <| DiscrTree.mkPath ty extExt.config
|
||||
let priority ← liftCommandElabM <| Elab.liftMacroM do evalPrio (prio.getD (← `(prio| default)))
|
||||
extExtension.add {declName, keys, priority} kind
|
||||
-- Realize iff theorem
|
||||
if iff then
|
||||
discard <| liftCommandElabM <| withRef stx <| realizeExtIffTheorem declName
|
||||
erase := fun declName => do
|
||||
let s := extExtension.getState (← getEnv)
|
||||
let s ← s.erase declName
|
||||
modifyEnv fun env => extExtension.modifyState env fun _ => s
|
||||
}
|
||||
|
||||
/--
|
||||
Constructs the hypotheses for the structure extensionality theorem that
|
||||
states that two structures are equal if their fields are equal.
|
||||
|
||||
Calls the continuation `k` with the list of parameters to the structure,
|
||||
two structure variables `x` and `y`, and a list of pairs `(field, ty)`
|
||||
where `ty` is `x.field = y.field` or `HEq x.field y.field`.
|
||||
|
||||
If `flat` parses to `true`, any fields inherited from parent structures
|
||||
are treated fields of the given structure type.
|
||||
If it is `false`, then the behind-the-scenes encoding of inherited fields
|
||||
is visible in the extensionality lemma.
|
||||
/-!
|
||||
### Implementation of `ext` tactic
|
||||
-/
|
||||
-- TODO: this is probably the wrong place to have this function
|
||||
def withExtHyps (struct : Name) (flat : Term)
|
||||
(k : Array Expr → (x y : Expr) → Array (Name × Expr) → MetaM α) : MetaM α := do
|
||||
let flat ← match flat with
|
||||
| `(true) => pure true
|
||||
| `(false) => pure false
|
||||
| _ => throwErrorAt flat "expected 'true' or 'false'"
|
||||
unless isStructure (← getEnv) struct do throwError "not a structure: {struct}"
|
||||
let structC ← mkConstWithLevelParams struct
|
||||
forallTelescope (← inferType structC) fun params _ => do
|
||||
withNewBinderInfos (params.map (·.fvarId!, BinderInfo.implicit)) do
|
||||
withLocalDeclD `x (mkAppN structC params) fun x => do
|
||||
withLocalDeclD `y (mkAppN structC params) fun y => do
|
||||
let mut hyps := #[]
|
||||
let fields ← if flat then
|
||||
pure <| getStructureFieldsFlattened (← getEnv) struct (includeSubobjectFields := false)
|
||||
else
|
||||
pure <| getStructureFields (← getEnv) struct
|
||||
for field in fields do
|
||||
let x_f ← mkProjection x field
|
||||
let y_f ← mkProjection y field
|
||||
if ← isProof x_f then
|
||||
pure ()
|
||||
else if ← isDefEq (← inferType x_f) (← inferType y_f) then
|
||||
hyps := hyps.push (field, ← mkEq x_f y_f)
|
||||
else
|
||||
hyps := hyps.push (field, ← mkHEq x_f y_f)
|
||||
k params x y hyps
|
||||
|
||||
/--
|
||||
Creates the type of the extensionality theorem for the given structure,
|
||||
elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
|
||||
-/
|
||||
@[builtin_term_elab extType] def elabExtType : TermElab := fun stx _ => do
|
||||
match stx with
|
||||
| `(ext_type% $flat:term $struct:ident) => do
|
||||
withExtHyps (← realizeGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
|
||||
let ty := hyps.foldr (init := ← mkEq x y) fun (f, h) ty =>
|
||||
mkForall f BinderInfo.default h ty
|
||||
mkForallFVars (params |>.push x |>.push y) ty
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
/--
|
||||
Creates the type of the iff-variant of the extensionality theorem for the given structure,
|
||||
elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
|
||||
-/
|
||||
@[builtin_term_elab extIffType] def elabExtIffType : TermElab := fun stx _ => do
|
||||
match stx with
|
||||
| `(ext_iff_type% $flat:term $struct:ident) => do
|
||||
withExtHyps (← realizeGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
|
||||
mkForallFVars (params |>.push x |>.push y) <|
|
||||
mkIff (← mkEq x y) <| mkAndN (hyps.map (·.2)).toList
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
/-- Apply a single extensionality theorem to `goal`. -/
|
||||
def applyExtTheoremAt (goal : MVarId) : MetaM (List MVarId) := goal.withContext do
|
||||
|
||||
@@ -564,7 +564,7 @@ def getInductiveValFromMajor (major : Expr) : TacticM InductiveVal :=
|
||||
|
||||
/--
|
||||
Elaborates the term in the `using` clause. We want to allow parameters to be instantiated
|
||||
(e.g. `using foo (p := …)`), but preserve other paramters, like the motives, as parameters,
|
||||
(e.g. `using foo (p := …)`), but preserve other parameters, like the motives, as parameters,
|
||||
without turning them into MVars. So this uses `abstractMVars` at the end. This is inspired by
|
||||
`Lean.Elab.Tactic.addSimpTheorem`.
|
||||
|
||||
|
||||
@@ -254,7 +254,17 @@ where
|
||||
| _ => match n.getAppFnArgs with
|
||||
| (``Nat.succ, #[n]) => rewrite e (.app (.const ``Int.ofNat_succ []) n)
|
||||
| (``HAdd.hAdd, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_add []) a b)
|
||||
| (``HMul.hMul, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_mul []) a b)
|
||||
| (``HMul.hMul, #[_, _, _, _, a, b]) =>
|
||||
-- Don't push the cast into a multiplication unless it produces a non-trivial linear combination.
|
||||
let r? ← commitWhen do
|
||||
let (lc, prf, r) ← rewrite e (mkApp2 (.const ``Int.ofNat_mul []) a b)
|
||||
if lc.isAtom then
|
||||
pure (none, false)
|
||||
else
|
||||
pure (some (lc, prf, r), true)
|
||||
match r? with
|
||||
| some r => pure r
|
||||
| none => mkAtomLinearCombo e
|
||||
| (``HDiv.hDiv, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_ediv []) a b)
|
||||
| (``OfNat.ofNat, #[_, n, _]) => rewrite e (.app (.const ``Int.natCast_ofNat []) n)
|
||||
| (``HMod.hMod, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_emod []) a b)
|
||||
|
||||
@@ -1827,9 +1827,13 @@ def isLetRecAuxMVar (mvarId : MVarId) : TermElabM Bool := do
|
||||
/--
|
||||
Create an `Expr.const` using the given name and explicit levels.
|
||||
Remark: fresh universe metavariables are created if the constant has more universe
|
||||
parameters than `explicitLevels`. -/
|
||||
def mkConst (constName : Name) (explicitLevels : List Level := []) : TermElabM Expr := do
|
||||
Linter.checkDeprecated constName -- TODO: check is occurring too early if there are multiple alternatives. Fix if it is not ok in practice
|
||||
parameters than `explicitLevels`.
|
||||
|
||||
If `checkDeprecated := true`, then `Linter.checkDeprecated` is invoked.
|
||||
-/
|
||||
def mkConst (constName : Name) (explicitLevels : List Level := []) (checkDeprecated := true) : TermElabM Expr := do
|
||||
if checkDeprecated then
|
||||
Linter.checkDeprecated constName
|
||||
let cinfo ← getConstInfo constName
|
||||
if explicitLevels.length > cinfo.levelParams.length then
|
||||
throwError "too many explicit universe levels for '{constName}'"
|
||||
@@ -1838,10 +1842,21 @@ def mkConst (constName : Name) (explicitLevels : List Level := []) : TermElabM E
|
||||
let us ← mkFreshLevelMVars numMissingLevels
|
||||
return Lean.mkConst constName (explicitLevels ++ us)
|
||||
|
||||
def checkDeprecated (ref : Syntax) (e : Expr) : TermElabM Unit := do
|
||||
if let .const declName _ := e.getAppFn then
|
||||
withRef ref do Linter.checkDeprecated declName
|
||||
|
||||
private def mkConsts (candidates : List (Name × List String)) (explicitLevels : List Level) : TermElabM (List (Expr × List String)) := do
|
||||
candidates.foldlM (init := []) fun result (declName, projs) => do
|
||||
-- TODO: better support for `mkConst` failure. We may want to cache the failures, and report them if all candidates fail.
|
||||
let const ← mkConst declName explicitLevels
|
||||
/-
|
||||
We disable `checkDeprecated` here because there may be many overloaded symbols.
|
||||
Note that, this method and `resolveName` and `resolveName'` return a list of pairs instead of a list of `TermElabResult`s.
|
||||
We perform the `checkDeprecated` test at `resolveId?` and `elabAppFnId`.
|
||||
At `elabAppFnId`, we perform the check when converting the list returned by `resolveName'` into a list of
|
||||
`TermElabResult`s.
|
||||
-/
|
||||
let const ← mkConst declName explicitLevels (checkDeprecated := false)
|
||||
return (const, projs) :: result
|
||||
|
||||
def resolveName (stx : Syntax) (n : Name) (preresolved : List Syntax.Preresolved) (explicitLevels : List Level) (expectedType? : Option Expr := none) : TermElabM (List (Expr × List String)) := do
|
||||
@@ -1895,11 +1910,11 @@ def resolveId? (stx : Syntax) (kind := "term") (withInfo := false) : TermElabM (
|
||||
| [] => return none
|
||||
| [f] =>
|
||||
let f ← if withInfo then addTermInfo stx f else pure f
|
||||
checkDeprecated stx f
|
||||
return some f
|
||||
| _ => throwError "ambiguous {kind}, use fully qualified name, possible interpretations {fs}"
|
||||
| _ => throwError "identifier expected"
|
||||
|
||||
|
||||
def TermElabM.run (x : TermElabM α) (ctx : Context := {}) (s : State := {}) : MetaM (α × State) :=
|
||||
withConfig setElabConfig (x ctx |>.run s)
|
||||
|
||||
|
||||
@@ -868,9 +868,9 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
|
||||
-- Recall that the map has not been modified when `cinfoPrev? = some _`.
|
||||
unless equivInfo cinfoPrev cinfo do
|
||||
throwAlreadyImported s const2ModIdx modIdx cname
|
||||
const2ModIdx := const2ModIdx.insert cname modIdx
|
||||
const2ModIdx := const2ModIdx.insertIfNew cname modIdx |>.1
|
||||
for cname in mod.extraConstNames do
|
||||
const2ModIdx := const2ModIdx.insert cname modIdx
|
||||
const2ModIdx := const2ModIdx.insertIfNew cname modIdx |>.1
|
||||
let constants : ConstMap := SMap.fromHashMap constantMap false
|
||||
let exts ← mkInitialExtensionStates
|
||||
let mut env : Environment := {
|
||||
|
||||
@@ -504,6 +504,14 @@ def mkArrayLit (type : Expr) (xs : List Expr) : MetaM Expr := do
|
||||
let listLit ← mkListLit type xs
|
||||
return mkApp (mkApp (mkConst ``List.toArray [u]) type) listLit
|
||||
|
||||
def mkNone (type : Expr) : MetaM Expr := do
|
||||
let u ← getDecLevel type
|
||||
return mkApp (mkConst ``Option.none [u]) type
|
||||
|
||||
def mkSome (type value : Expr) : MetaM Expr := do
|
||||
let u ← getDecLevel type
|
||||
return mkApp2 (mkConst ``Option.some [u]) type value
|
||||
|
||||
def mkSorry (type : Expr) (synthetic : Bool) : MetaM Expr := do
|
||||
let u ← getLevel type
|
||||
return mkApp2 (mkConst ``sorryAx [u]) type (toExpr synthetic)
|
||||
|
||||
@@ -1492,6 +1492,16 @@ private def withLocalContextImp (lctx : LocalContext) (localInsts : LocalInstanc
|
||||
def withLCtx (lctx : LocalContext) (localInsts : LocalInstances) : n α → n α :=
|
||||
mapMetaM <| withLocalContextImp lctx localInsts
|
||||
|
||||
/--
|
||||
Runs `k` in a local envrionment with the `fvarIds` erased.
|
||||
-/
|
||||
def withErasedFVars [MonadLCtx n] [MonadLiftT MetaM n] (fvarIds : Array FVarId) (k : n α) : n α := do
|
||||
let lctx ← getLCtx
|
||||
let localInsts ← getLocalInstances
|
||||
let lctx' := fvarIds.foldl (·.erase ·) lctx
|
||||
let localInsts' := localInsts.filter (!fvarIds.contains ·.fvar.fvarId!)
|
||||
withLCtx lctx' localInsts' k
|
||||
|
||||
private def withMVarContextImp (mvarId : MVarId) (x : MetaM α) : MetaM α := do
|
||||
let mvarDecl ← mvarId.getDecl
|
||||
withLocalContextImp mvarDecl.lctx mvarDecl.localInstances x
|
||||
@@ -1855,9 +1865,13 @@ abbrev isDefEqGuarded (t s : Expr) : MetaM Bool :=
|
||||
def isDefEqNoConstantApprox (t s : Expr) : MetaM Bool :=
|
||||
approxDefEq <| isDefEq t s
|
||||
|
||||
/-- Shorthand for `isDefEq (mkMVar mvarId) val` -/
|
||||
def _root_.Lean.MVarId.checkedAssign (mvarId : MVarId) (val : Expr) : MetaM Bool :=
|
||||
isDefEq (mkMVar mvarId) val
|
||||
/--
|
||||
Returns `true` if `mvarId := val` was successfully assigned.
|
||||
This method uses the same assignment validation performed by `isDefEq`, but it does not check whether the types match.
|
||||
-/
|
||||
-- Remark: this method is implemented at `ExprDefEq`
|
||||
@[extern "lean_checked_assign"]
|
||||
opaque _root_.Lean.MVarId.checkedAssign (mvarId : MVarId) (val : Expr) : MetaM Bool
|
||||
|
||||
/--
|
||||
Eta expand the given expression.
|
||||
|
||||
@@ -85,9 +85,8 @@ of type
|
||||
```
|
||||
α → List α → Sort (max 1 u_1) → Sort (max 1 u_1)
|
||||
```
|
||||
The parameter `typeFormers` are the `motive`s.
|
||||
-/
|
||||
private def buildBelowMinorPremise (rlvl : Level) (typeFormers : Array Expr) (minorType : Expr) : MetaM Expr :=
|
||||
private def buildBelowMinorPremise (rlvl : Level) (motives : Array Expr) (minorType : Expr) : MetaM Expr :=
|
||||
forallTelescope minorType fun minor_args _ => do go #[] minor_args.toList
|
||||
where
|
||||
ibelow := rlvl matches .zero
|
||||
@@ -96,7 +95,7 @@ where
|
||||
| arg::args => do
|
||||
let argType ← inferType arg
|
||||
forallTelescope argType fun arg_args arg_type => do
|
||||
if typeFormers.contains arg_type.getAppFn then
|
||||
if motives.contains arg_type.getAppFn then
|
||||
let name ← arg.fvarId!.getUserName
|
||||
let type' ← forallTelescope argType fun args _ => mkForallFVars args (.sort rlvl)
|
||||
withLocalDeclD name type' fun arg' => do
|
||||
@@ -124,81 +123,100 @@ fun {α} {motive} t =>
|
||||
List.rec True (fun head tail tail_ih => (motive tail ∧ tail_ih) ∧ True) t
|
||||
```
|
||||
-/
|
||||
private def mkBelowOrIBelow (indName : Name) (ibelow : Bool) : MetaM Unit := do
|
||||
let .inductInfo indVal ← getConstInfo indName | return
|
||||
unless indVal.isRec do return
|
||||
if ← isPropFormerType indVal.type then return
|
||||
|
||||
let recName := mkRecName indName
|
||||
private def mkBelowFromRec (recName : Name) (ibelow reflexive : Bool) (nParams : Nat)
|
||||
(belowName : Name) : MetaM Unit := do
|
||||
-- The construction follows the type of `ind.rec`
|
||||
let .recInfo recVal ← getConstInfo recName
|
||||
| throwError "{recName} not a .recInfo"
|
||||
let lvl::lvls := recVal.levelParams.map (Level.param ·)
|
||||
| throwError "recursor {recName} has no levelParams"
|
||||
let lvlParam := recVal.levelParams.head!
|
||||
-- universe parameter names of ibelow/below
|
||||
let blvls :=
|
||||
-- For ibelow we instantiate the first universe parameter of `.rec` to `.zero`
|
||||
if ibelow then recVal.levelParams.tail!
|
||||
else recVal.levelParams
|
||||
let .some ilvl ← typeFormerTypeLevel indVal.type
|
||||
| throwError "type {indVal.type} of inductive {indVal.name} not a type former?"
|
||||
|
||||
-- universe level of the resultant type
|
||||
let rlvl : Level :=
|
||||
if ibelow then
|
||||
0
|
||||
else if indVal.isReflexive then
|
||||
if let .max 1 ilvl' := ilvl then
|
||||
mkLevelMax' (.succ lvl) ilvl'
|
||||
else
|
||||
mkLevelMax' (.succ lvl) ilvl
|
||||
else
|
||||
mkLevelMax' 1 lvl
|
||||
|
||||
let refType :=
|
||||
if ibelow then
|
||||
recVal.type.instantiateLevelParams [lvlParam] [0]
|
||||
else if indVal.isReflexive then
|
||||
else if reflexive then
|
||||
recVal.type.instantiateLevelParams [lvlParam] [lvl.succ]
|
||||
else
|
||||
recVal.type
|
||||
|
||||
let decl ← forallTelescope refType fun refArgs _ => do
|
||||
assert! refArgs.size == indVal.numParams + recVal.numMotives + recVal.numMinors + indVal.numIndices + 1
|
||||
let params : Array Expr := refArgs[:indVal.numParams]
|
||||
let typeFormers : Array Expr := refArgs[indVal.numParams:indVal.numParams + recVal.numMotives]
|
||||
let minors : Array Expr := refArgs[indVal.numParams + recVal.numMotives:indVal.numParams + recVal.numMotives + recVal.numMinors]
|
||||
let remaining : Array Expr := refArgs[indVal.numParams + recVal.numMotives + recVal.numMinors:]
|
||||
assert! refArgs.size > nParams + recVal.numMotives + recVal.numMinors
|
||||
let params : Array Expr := refArgs[:nParams]
|
||||
let motives : Array Expr := refArgs[nParams:nParams + recVal.numMotives]
|
||||
let minors : Array Expr := refArgs[nParams + recVal.numMotives:nParams + recVal.numMotives + recVal.numMinors]
|
||||
let indices : Array Expr := refArgs[nParams + recVal.numMotives + recVal.numMinors:refArgs.size - 1]
|
||||
let major : Expr := refArgs[refArgs.size - 1]!
|
||||
|
||||
-- universe parameter names of ibelow/below
|
||||
let blvls :=
|
||||
-- For ibelow we instantiate the first universe parameter of `.rec` to `.zero`
|
||||
if ibelow then recVal.levelParams.tail!
|
||||
else recVal.levelParams
|
||||
-- universe parameter of the type fomer.
|
||||
-- same as `typeFormerTypeLevel indVal.type`, but we want to infer it from the
|
||||
-- type of the recursor, to be more robust when facing nested induction
|
||||
let majorTypeType ← inferType (← inferType major)
|
||||
let .some ilvl ← typeFormerTypeLevel majorTypeType
|
||||
| throwError "type of type of major premise {major} not a type former"
|
||||
|
||||
-- universe level of the resultant type
|
||||
let rlvl : Level :=
|
||||
if ibelow then
|
||||
0
|
||||
else if reflexive then
|
||||
if let .max 1 ilvl' := ilvl then
|
||||
mkLevelMax' (.succ lvl) ilvl'
|
||||
else
|
||||
mkLevelMax' (.succ lvl) ilvl
|
||||
else
|
||||
mkLevelMax' 1 lvl
|
||||
|
||||
let mut val := .const recName (rlvl.succ :: lvls)
|
||||
-- add parameters
|
||||
val := mkAppN val params
|
||||
-- add type formers
|
||||
for typeFormer in typeFormers do
|
||||
let arg ← forallTelescope (← inferType typeFormer) fun targs _ =>
|
||||
for motive in motives do
|
||||
let arg ← forallTelescope (← inferType motive) fun targs _ =>
|
||||
mkLambdaFVars targs (.sort rlvl)
|
||||
val := .app val arg
|
||||
-- add minor premises
|
||||
for minor in minors do
|
||||
let arg ← buildBelowMinorPremise rlvl typeFormers (← inferType minor)
|
||||
let arg ← buildBelowMinorPremise rlvl motives (← inferType minor)
|
||||
val := .app val arg
|
||||
-- add indices and major premise
|
||||
val := mkAppN val remaining
|
||||
val := mkAppN val indices
|
||||
val := mkApp val major
|
||||
|
||||
-- All paramaters of `.rec` besides the `minors` become parameters of `.below`
|
||||
let below_params := params ++ typeFormers ++ remaining
|
||||
let below_params := params ++ motives ++ indices ++ #[major]
|
||||
let type ← mkForallFVars below_params (.sort rlvl)
|
||||
val ← mkLambdaFVars below_params val
|
||||
|
||||
let name := if ibelow then mkIBelowName indName else mkBelowName indName
|
||||
mkDefinitionValInferrringUnsafe name blvls type val .abbrev
|
||||
mkDefinitionValInferrringUnsafe belowName blvls type val .abbrev
|
||||
|
||||
addDecl (.defnDecl decl)
|
||||
setReducibleAttribute decl.name
|
||||
modifyEnv fun env => markAuxRecursor env decl.name
|
||||
modifyEnv fun env => addProtected env decl.name
|
||||
|
||||
private def mkBelowOrIBelow (indName : Name) (ibelow : Bool) : MetaM Unit := do
|
||||
let .inductInfo indVal ← getConstInfo indName | return
|
||||
unless indVal.isRec do return
|
||||
if ← isPropFormerType indVal.type then return
|
||||
|
||||
let recName := mkRecName indName
|
||||
let belowName := if ibelow then mkIBelowName indName else mkBelowName indName
|
||||
mkBelowFromRec recName ibelow indVal.isReflexive indVal.numParams belowName
|
||||
|
||||
-- If this is the first inductive in a mutual group with nested inductives,
|
||||
-- generate the constructions for the nested inductives now
|
||||
if indVal.all[0]! = indName then
|
||||
for i in [:indVal.numNested] do
|
||||
let recName := recName.appendIndexAfter (i + 1)
|
||||
let belowName := belowName.appendIndexAfter (i + 1)
|
||||
mkBelowFromRec recName ibelow indVal.isReflexive indVal.numParams belowName
|
||||
|
||||
def mkBelow (declName : Name) : MetaM Unit := mkBelowOrIBelow declName true
|
||||
def mkIBelow (declName : Name) : MetaM Unit := mkBelowOrIBelow declName false
|
||||
|
||||
@@ -219,22 +237,21 @@ of type
|
||||
PProd (motive tail) (List.below tail) →
|
||||
PProd (motive (head :: tail)) (PProd (PProd (motive tail) (List.below tail)) PUnit)
|
||||
```
|
||||
The parameter `typeFormers` are the `motive`s.
|
||||
-/
|
||||
private def buildBRecOnMinorPremise (rlvl : Level) (typeFormers : Array Expr)
|
||||
private def buildBRecOnMinorPremise (rlvl : Level) (motives : Array Expr)
|
||||
(belows : Array Expr) (fs : Array Expr) (minorType : Expr) : MetaM Expr :=
|
||||
forallTelescope minorType fun minor_args minor_type => do
|
||||
let rec go (prods : Array Expr) : List Expr → MetaM Expr
|
||||
| [] => minor_type.withApp fun minor_type_fn minor_type_args => do
|
||||
let b ← mkNProdMk rlvl prods
|
||||
let .some ⟨idx, _⟩ := typeFormers.indexOf? minor_type_fn
|
||||
| throwError m!"Did not find {minor_type} in {typeFormers}"
|
||||
let .some ⟨idx, _⟩ := motives.indexOf? minor_type_fn
|
||||
| throwError m!"Did not find {minor_type} in {motives}"
|
||||
mkPProdMk (mkAppN fs[idx]! (minor_type_args.push b)) b
|
||||
| arg::args => do
|
||||
let argType ← inferType arg
|
||||
forallTelescope argType fun arg_args arg_type => do
|
||||
arg_type.withApp fun arg_type_fn arg_type_args => do
|
||||
if let .some idx := typeFormers.indexOf? arg_type_fn then
|
||||
if let .some idx := motives.indexOf? arg_type_fn then
|
||||
let name ← arg.fvarId!.getUserName
|
||||
let type' ← mkForallFVars arg_args
|
||||
(← mkPProd arg_type (mkAppN belows[idx]! arg_type_args) )
|
||||
@@ -277,81 +294,72 @@ fun {α} {motive} t F_1 => (
|
||||
).1
|
||||
```
|
||||
-/
|
||||
def mkBRecOnOrBInductionOn (indName : Name) (ind : Bool) : MetaM Unit := do
|
||||
let .inductInfo indVal ← getConstInfo indName | return
|
||||
unless indVal.isRec do return
|
||||
if ← isPropFormerType indVal.type then return
|
||||
let recName := mkRecName indName
|
||||
private def mkBRecOnFromRec (recName : Name) (ind reflexive : Bool) (nParams : Nat)
|
||||
(all : Array Name) (brecOnName : Name) : MetaM Unit := do
|
||||
let .recInfo recVal ← getConstInfo recName | return
|
||||
unless recVal.numMotives = indVal.all.length do
|
||||
/-
|
||||
The mutual declaration containing `declName` contains nested inductive datatypes.
|
||||
We don't support this kind of declaration here yet. We probably never will :)
|
||||
To support it, we will need to generate an auxiliary `below` for each nested inductive
|
||||
type since their default `below` is not good here. For example, at
|
||||
```
|
||||
inductive Term
|
||||
| var : String -> Term
|
||||
| app : String -> List Term -> Term
|
||||
```
|
||||
The `List.below` is not useful since it will not allow us to recurse over the nested terms.
|
||||
We need to generate another one using the auxiliary recursor `Term.rec_1` for `List Term`.
|
||||
-/
|
||||
return
|
||||
|
||||
let lvl::lvls := recVal.levelParams.map (Level.param ·)
|
||||
| throwError "recursor {recName} has no levelParams"
|
||||
let lvlParam := recVal.levelParams.head!
|
||||
-- universe parameter names of brecOn/binductionOn
|
||||
let blps := if ind then recVal.levelParams.tail! else recVal.levelParams
|
||||
-- universe arguments of below/ibelow
|
||||
let blvls := if ind then lvls else lvl::lvls
|
||||
|
||||
let .some ⟨idx, _⟩ := indVal.all.toArray.indexOf? indName
|
||||
| throwError m!"Did not find {indName} in {indVal.all}"
|
||||
|
||||
let .some ilvl ← typeFormerTypeLevel indVal.type
|
||||
| throwError "type {indVal.type} of inductive {indVal.name} not a type former?"
|
||||
-- universe level of the resultant type
|
||||
let rlvl : Level :=
|
||||
if ind then
|
||||
0
|
||||
else if indVal.isReflexive then
|
||||
if let .max 1 ilvl' := ilvl then
|
||||
mkLevelMax' (.succ lvl) ilvl'
|
||||
else
|
||||
mkLevelMax' (.succ lvl) ilvl
|
||||
else
|
||||
mkLevelMax' 1 lvl
|
||||
|
||||
let refType :=
|
||||
if ind then
|
||||
recVal.type.instantiateLevelParams [lvlParam] [0]
|
||||
else if indVal.isReflexive then
|
||||
else if reflexive then
|
||||
recVal.type.instantiateLevelParams [lvlParam] [lvl.succ]
|
||||
else
|
||||
recVal.type
|
||||
|
||||
let decl ← forallTelescope refType fun refArgs _ => do
|
||||
assert! refArgs.size == indVal.numParams + recVal.numMotives + recVal.numMinors + indVal.numIndices + 1
|
||||
let params : Array Expr := refArgs[:indVal.numParams]
|
||||
let typeFormers : Array Expr := refArgs[indVal.numParams:indVal.numParams + recVal.numMotives]
|
||||
let minors : Array Expr := refArgs[indVal.numParams + recVal.numMotives:indVal.numParams + recVal.numMotives + recVal.numMinors]
|
||||
let remaining : Array Expr := refArgs[indVal.numParams + recVal.numMotives + recVal.numMinors:]
|
||||
let decl ← forallTelescope refType fun refArgs refBody => do
|
||||
assert! refArgs.size > nParams + recVal.numMotives + recVal.numMinors
|
||||
let params : Array Expr := refArgs[:nParams]
|
||||
let motives : Array Expr := refArgs[nParams:nParams + recVal.numMotives]
|
||||
let minors : Array Expr := refArgs[nParams + recVal.numMotives:nParams + recVal.numMotives + recVal.numMinors]
|
||||
let indices : Array Expr := refArgs[nParams + recVal.numMotives + recVal.numMinors:refArgs.size - 1]
|
||||
let major : Expr := refArgs[refArgs.size - 1]!
|
||||
|
||||
-- One `below` for each type former (same parameters)
|
||||
let belows := indVal.all.toArray.map fun n =>
|
||||
let belowName := if ind then mkIBelowName n else mkBelowName n
|
||||
mkAppN (.const belowName blvls) (params ++ typeFormers)
|
||||
let some idx := motives.indexOf? refBody.getAppFn
|
||||
| throwError "result type of {refType} is not one of {motives}"
|
||||
|
||||
-- create types of functionals (one for each type former)
|
||||
-- universe parameter of the type fomer.
|
||||
-- same as `typeFormerTypeLevel indVal.type`, but we want to infer it from the
|
||||
-- type of the recursor, to be more robust when facing nested induction
|
||||
let majorTypeType ← inferType (← inferType major)
|
||||
let .some ilvl ← typeFormerTypeLevel majorTypeType
|
||||
| throwError "type of type of major premise {major} not a type former"
|
||||
|
||||
-- universe level of the resultant type
|
||||
let rlvl : Level :=
|
||||
if ind then
|
||||
0
|
||||
else if reflexive then
|
||||
if let .max 1 ilvl' := ilvl then
|
||||
mkLevelMax' (.succ lvl) ilvl'
|
||||
else
|
||||
mkLevelMax' (.succ lvl) ilvl
|
||||
else
|
||||
mkLevelMax' 1 lvl
|
||||
|
||||
-- One `below` for each motive, with the same motive parameters
|
||||
let blvls := if ind then lvls else lvl::lvls
|
||||
let belows := Array.ofFn (n := motives.size) fun ⟨i,_⟩ =>
|
||||
let belowName :=
|
||||
if let some n := all[i]? then
|
||||
if ind then mkIBelowName n else mkBelowName n
|
||||
else
|
||||
if ind then .str all[0]! s!"ibelow_{i-all.size+1}"
|
||||
else .str all[0]! s!"below_{i-all.size+1}"
|
||||
mkAppN (.const belowName blvls) (params ++ motives)
|
||||
|
||||
-- create types of functionals (one for each motive)
|
||||
-- (F_1 : (t : List α) → (f : List.below t) → motive t)
|
||||
-- and bring parameters of that type into scope
|
||||
let mut fDecls : Array (Name × (Array Expr -> MetaM Expr)) := #[]
|
||||
for typeFormer in typeFormers, below in belows, i in [:typeFormers.size] do
|
||||
let fType ← forallTelescope (← inferType typeFormer) fun targs _ => do
|
||||
for motive in motives, below in belows, i in [:motives.size] do
|
||||
let fType ← forallTelescope (← inferType motive) fun targs _ => do
|
||||
withLocalDeclD `f (mkAppN below targs) fun f =>
|
||||
mkForallFVars (targs.push f) (mkAppN typeFormer targs)
|
||||
mkForallFVars (targs.push f) (mkAppN motive targs)
|
||||
let fName := .mkSimple s!"F_{i + 1}"
|
||||
fDecls := fDecls.push (fName, fun _ => pure fType)
|
||||
withLocalDeclsD fDecls fun fs => do
|
||||
@@ -359,35 +367,53 @@ def mkBRecOnOrBInductionOn (indName : Name) (ind : Bool) : MetaM Unit := do
|
||||
-- add parameters
|
||||
val := mkAppN val params
|
||||
-- add type formers
|
||||
for typeFormer in typeFormers, below in belows do
|
||||
for motive in motives, below in belows do
|
||||
-- example: (motive := fun t => PProd (motive t) (@List.below α motive t))
|
||||
let arg ← forallTelescope (← inferType typeFormer) fun targs _ => do
|
||||
let cType := mkAppN typeFormer targs
|
||||
let arg ← forallTelescope (← inferType motive) fun targs _ => do
|
||||
let cType := mkAppN motive targs
|
||||
let belowType := mkAppN below targs
|
||||
let arg ← mkPProd cType belowType
|
||||
mkLambdaFVars targs arg
|
||||
val := .app val arg
|
||||
-- add minor premises
|
||||
for minor in minors do
|
||||
let arg ← buildBRecOnMinorPremise rlvl typeFormers belows fs (← inferType minor)
|
||||
let arg ← buildBRecOnMinorPremise rlvl motives belows fs (← inferType minor)
|
||||
val := .app val arg
|
||||
-- add indices and major premise
|
||||
val := mkAppN val remaining
|
||||
val := mkAppN val indices
|
||||
val := mkApp val major
|
||||
-- project out first component
|
||||
val ← mkPProdFst val
|
||||
|
||||
-- All paramaters of `.rec` besides the `minors` become parameters of `.bRecOn`, and the `fs`
|
||||
let below_params := params ++ typeFormers ++ remaining ++ fs
|
||||
let type ← mkForallFVars below_params (mkAppN typeFormers[idx]! remaining)
|
||||
let below_params := params ++ motives ++ indices ++ #[major] ++ fs
|
||||
let type ← mkForallFVars below_params (mkAppN motives[idx]! (indices ++ #[major]))
|
||||
val ← mkLambdaFVars below_params val
|
||||
|
||||
let name := if ind then mkBInductionOnName indName else mkBRecOnName indName
|
||||
mkDefinitionValInferrringUnsafe name blps type val .abbrev
|
||||
mkDefinitionValInferrringUnsafe brecOnName blps type val .abbrev
|
||||
|
||||
addDecl (.defnDecl decl)
|
||||
setReducibleAttribute decl.name
|
||||
modifyEnv fun env => markAuxRecursor env decl.name
|
||||
modifyEnv fun env => addProtected env decl.name
|
||||
|
||||
def mkBRecOnOrBInductionOn (indName : Name) (ind : Bool) : MetaM Unit := do
|
||||
let .inductInfo indVal ← getConstInfo indName | return
|
||||
unless indVal.isRec do return
|
||||
if ← isPropFormerType indVal.type then return
|
||||
|
||||
let recName := mkRecName indName
|
||||
let brecOnName := if ind then mkBInductionOnName indName else mkBRecOnName indName
|
||||
mkBRecOnFromRec recName ind indVal.isReflexive indVal.numParams indVal.all.toArray brecOnName
|
||||
|
||||
-- If this is the first inductive in a mutual group with nested inductives,
|
||||
-- generate the constructions for the nested inductives now.
|
||||
if indVal.all[0]! = indName then
|
||||
for i in [:indVal.numNested] do
|
||||
let recName := recName.appendIndexAfter (i + 1)
|
||||
let brecOnName := brecOnName.appendIndexAfter (i + 1)
|
||||
mkBRecOnFromRec recName ind indVal.isReflexive indVal.numParams indVal.all.toArray brecOnName
|
||||
|
||||
|
||||
def mkBRecOn (declName : Name) : MetaM Unit := mkBRecOnOrBInductionOn declName false
|
||||
def mkBInductionOn (declName : Name) : MetaM Unit := mkBRecOnOrBInductionOn declName true
|
||||
|
||||
@@ -1046,6 +1046,15 @@ def checkAssignment (mvarId : MVarId) (fvars : Array Expr) (v : Expr) : MetaM (O
|
||||
return none
|
||||
return some v
|
||||
|
||||
-- Implementation for `_root_.Lean.MVarId.checkedAssign`
|
||||
@[export lean_checked_assign]
|
||||
def checkedAssignImpl (mvarId : MVarId) (val : Expr) : MetaM Bool := do
|
||||
if let some val ← checkAssignment mvarId #[] val then
|
||||
mvarId.assign val
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
private def processAssignmentFOApproxAux (mvar : Expr) (args : Array Expr) (v : Expr) : MetaM Bool :=
|
||||
match v with
|
||||
| .mdata _ e => processAssignmentFOApproxAux mvar args e
|
||||
|
||||
@@ -176,4 +176,48 @@ def litToCtor (e : Expr) : MetaM Expr := do
|
||||
return mkApp3 (mkConst ``Fin.mk) n i h
|
||||
return e
|
||||
|
||||
/--
|
||||
Check if an expression is a list literal (i.e. a nested chain of `List.cons`, ending at a `List.nil`),
|
||||
where each element is "recognised" by a given function `f : Expr → MetaM (Option α)`,
|
||||
and return the array of recognised values.
|
||||
-/
|
||||
partial def getListLitOf? (e : Expr) (f : Expr → MetaM (Option α)) : MetaM (Option (Array α)) := do
|
||||
let mut e ← instantiateMVars e.consumeMData
|
||||
let mut r := #[]
|
||||
while true do
|
||||
match_expr e with
|
||||
| List.nil _ => break
|
||||
| List.cons _ a as => do
|
||||
let some a ← f a | return none
|
||||
r := r.push a
|
||||
e := as
|
||||
| _ => return none
|
||||
return some r
|
||||
|
||||
/--
|
||||
Check if an expression is a list literal (i.e. a nested chain of `List.cons`, ending at a `List.nil`),
|
||||
returning the array of `Expr` values.
|
||||
-/
|
||||
def getListLit? (e : Expr) : MetaM (Option (Array Expr)) := getListLitOf? e fun s => return some s
|
||||
|
||||
/--
|
||||
Check if an expression is an array literal
|
||||
(i.e. `List.toArray` applied to a nested chain of `List.cons`, ending at a `List.nil`),
|
||||
where each element is "recognised" by a given function `f : Expr → MetaM (Option α)`,
|
||||
and return the array of recognised values.
|
||||
-/
|
||||
def getArrayLitOf? (e : Expr) (f : Expr → MetaM (Option α)) : MetaM (Option (Array α)) := do
|
||||
let e ← instantiateMVars e.consumeMData
|
||||
match_expr e with
|
||||
| List.toArray _ as => getListLitOf? as f
|
||||
| _ => return none
|
||||
|
||||
/--
|
||||
Check if an expression is an array literal
|
||||
(i.e. `List.toArray` applied to a nested chain of `List.cons`, ending at a `List.nil`),
|
||||
returning the array of `Expr` values.
|
||||
-/
|
||||
def getArrayLit? (e : Expr) : MetaM (Option (Array Expr)) := getArrayLitOf? e fun s => return some s
|
||||
|
||||
|
||||
end Lean.Meta
|
||||
|
||||
@@ -294,7 +294,7 @@ def transform
|
||||
altType in altTypes do
|
||||
let alt' ← forallAltTelescope' origAltType (numParams - numDiscrEqs) 0 fun ys args => do
|
||||
let altType ← instantiateForall altType ys
|
||||
-- The splitter inserts its extra paramters after the first ys.size parameters, before
|
||||
-- The splitter inserts its extra parameters after the first ys.size parameters, before
|
||||
-- the parameters for the numDiscrEqs
|
||||
forallBoundedTelescope altType (splitterNumParams - ys.size) fun ys2 altType => do
|
||||
forallBoundedTelescope altType numDiscrEqs fun ys3 altType => do
|
||||
|
||||
@@ -164,8 +164,6 @@ partial def mkSizeOfFn (recName : Name) (declName : Name): MetaM Unit := do
|
||||
-/
|
||||
def mkSizeOfFns (typeName : Name) : MetaM (Array Name × NameMap Name) := do
|
||||
let indInfo ← getConstInfoInduct typeName
|
||||
let recInfo ← getConstInfoRec (mkRecName typeName)
|
||||
let numExtra := recInfo.numMotives - indInfo.all.length -- numExtra > 0 for nested inductive types
|
||||
let mut result := #[]
|
||||
let baseName := indInfo.all.head! ++ `_sizeOf -- we use the first inductive type as the base name for `sizeOf` functions
|
||||
let mut i := 1
|
||||
@@ -177,7 +175,7 @@ def mkSizeOfFns (typeName : Name) : MetaM (Array Name × NameMap Name) := do
|
||||
recMap := recMap.insert recName sizeOfName
|
||||
result := result.push sizeOfName
|
||||
i := i + 1
|
||||
for j in [:numExtra] do
|
||||
for j in [:indInfo.numNested] do
|
||||
let recName := (mkRecName indInfo.all.head!).appendIndexAfter (j+1)
|
||||
let sizeOfName := baseName.appendIndexAfter i
|
||||
mkSizeOfFn recName sizeOfName
|
||||
|
||||
@@ -719,7 +719,7 @@ to the continuation
|
||||
recursion and extra parameters passed to the recursor)
|
||||
* the position of the motive/induction hypothesis in the body's arguments
|
||||
* the body, as passed to the recursor. Expected to be a lambda that takes the
|
||||
varying paramters and the motive
|
||||
varying parameters and the motive
|
||||
* a function to re-assemble the call with a new Motive. The resulting expression expects
|
||||
the new body next, so that the expected type of the body can be inferred
|
||||
* a function to finish assembling the call with the new body.
|
||||
@@ -744,8 +744,8 @@ def findRecursor {α} (name : Name) (varNames : Array Name) (e : Expr)
|
||||
-- Bail out on mutual or nested inductives
|
||||
let .str indName _ := f.constName! | unreachable!
|
||||
let indInfo ← getConstInfoInduct indName
|
||||
if indInfo.all.length > 1 then
|
||||
throwError "functional induction: cannot handle mutual inductives"
|
||||
if indInfo.numTypeFormers > 1 then
|
||||
throwError "functional induction: cannot handle mutual or nested inductives"
|
||||
|
||||
let elimInfo ← getElimExprInfo f
|
||||
let targets : Array Expr := elimInfo.targetsPos.map (args[·]!)
|
||||
|
||||
@@ -13,3 +13,4 @@ import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Char
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.String
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.BitVec
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.List
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Array
|
||||
|
||||
36
src/Lean/Meta/Tactic/Simp/BuiltinSimprocs/Array.lean
Normal file
36
src/Lean/Meta/Tactic/Simp/BuiltinSimprocs/Array.lean
Normal file
@@ -0,0 +1,36 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.LitValues
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
|
||||
|
||||
namespace Array
|
||||
open Lean Meta Simp
|
||||
|
||||
/-- Simplification procedure for `#[...][n]` for `n` a `Nat` literal. -/
|
||||
builtin_dsimproc [simp, seval] reduceGetElem (@GetElem.getElem (Array _) Nat _ _ _ _ _ _) := fun e => do
|
||||
let_expr GetElem.getElem _ _ _ _ _ xs n _ ← e | return .continue
|
||||
let some n ← Nat.fromExpr? n | return .continue
|
||||
let some xs ← getArrayLit? xs | return .continue
|
||||
return .done <| xs[n]!
|
||||
|
||||
/-- Simplification procedure for `#[...][n]?` for `n` a `Nat` literal. -/
|
||||
builtin_dsimproc [simp, seval] reduceGetElem? (@GetElem?.getElem? (Array _) Nat _ _ _ _ _) := fun e => do
|
||||
let_expr GetElem?.getElem? _ _ α _ _ xs n ← e | return .continue
|
||||
let some n ← Nat.fromExpr? n | return .continue
|
||||
let some xs ← getArrayLit? xs | return .continue
|
||||
let r ← if h : n < xs.size then mkSome α xs[n] else mkNone α
|
||||
return .done r
|
||||
|
||||
/-- Simplification procedure for `#[...][n]!` for `n` a `Nat` literal. -/
|
||||
builtin_dsimproc [simp, seval] reduceGetElem! (@GetElem?.getElem! (Array _) Nat _ _ _ _ _ _) := fun e => do
|
||||
let_expr GetElem?.getElem! _ _ α _ _ I xs n ← e | return .continue
|
||||
let some n ← Nat.fromExpr? n | return .continue
|
||||
let some xs ← getArrayLit? xs | return .continue
|
||||
let r ← if h : n < xs.size then pure xs[n] else mkDefault α
|
||||
return .done r
|
||||
|
||||
end Array
|
||||
@@ -142,8 +142,8 @@ def findModuleOf? [Monad m] [MonadEnv m] [MonadError m] (declName : Name) : m (O
|
||||
|
||||
def isEnumType [Monad m] [MonadEnv m] [MonadError m] (declName : Name) : m Bool := do
|
||||
if let ConstantInfo.inductInfo info ← getConstInfo declName then
|
||||
if !info.type.isProp && info.all.length == 1 && info.numIndices == 0 && info.numParams == 0
|
||||
&& !info.ctors.isEmpty && !info.isRec && !info.isNested && !info.isUnsafe then
|
||||
if !info.type.isProp && info.numTypeFormers == 1 && info.numIndices == 0 && info.numParams == 0
|
||||
&& !info.ctors.isEmpty && !info.isRec && !info.isUnsafe then
|
||||
info.ctors.allM fun ctorName => do
|
||||
let ConstantInfo.ctorInfo info ← getConstInfo ctorName | return false
|
||||
return info.numFields == 0
|
||||
|
||||
@@ -701,7 +701,7 @@ list, so it should be brief.
|
||||
@[builtin_command_parser] def genInjectiveTheorems := leading_parser
|
||||
"gen_injective_theorems% " >> ident
|
||||
|
||||
/-- No-op parser used as syntax kind for attaching remaining whitespace to at the end of the input. -/
|
||||
/-- No-op parser used as syntax kind for attaching remaining whitespace at the end of the input. -/
|
||||
@[run_builtin_parser_attribute_hooks] def eoi : Parser := leading_parser ""
|
||||
|
||||
builtin_initialize
|
||||
|
||||
@@ -174,9 +174,11 @@ do not yield the right result.
|
||||
-/
|
||||
@[builtin_term_parser] def typeAscription := leading_parser
|
||||
"(" >> (withoutPosition (withoutForbidden (termParser >> " :" >> optional (ppSpace >> termParser)))) >> ")"
|
||||
|
||||
/-- Tuple notation; `()` is short for `Unit.unit`, `(a, b, c)` for `Prod.mk a (Prod.mk b c)`, etc. -/
|
||||
@[builtin_term_parser] def tuple := leading_parser
|
||||
"(" >> optional (withoutPosition (withoutForbidden (termParser >> ", " >> sepBy1 termParser ", " (allowTrailingSep := true)))) >> ")"
|
||||
|
||||
/--
|
||||
Parentheses, used for grouping expressions (e.g., `a * (b + c)`).
|
||||
Can also be used for creating simple functions when combined with `·`. Here are some examples:
|
||||
|
||||
@@ -128,10 +128,12 @@ def ofLazyM (f : MetaM MessageData) (es : Array Expr := #[]) : MessageData :=
|
||||
instantiateMVarsCore mvarctxt a |>.1.hasSyntheticSorry
|
||||
))
|
||||
|
||||
/-- Pretty print a const expression using `delabConst` and generate terminfo.
|
||||
/--
|
||||
Pretty print a const expression using `delabConst` and generate terminfo.
|
||||
This function avoids inserting `@` if the constant is for a function whose first
|
||||
argument is implicit, which is what the default `toMessageData` for `Expr` does.
|
||||
Panics if `e` is not a constant. -/
|
||||
Panics if `e` is not a constant.
|
||||
-/
|
||||
def ofConst (e : Expr) : MessageData :=
|
||||
if e.isConst then
|
||||
let delab : Delab := withOptionAtCurrPos `pp.tagAppFns true delabConst
|
||||
@@ -139,6 +141,19 @@ def ofConst (e : Expr) : MessageData :=
|
||||
else
|
||||
panic! "not a constant"
|
||||
|
||||
/--
|
||||
Pretty print a constant given its name, similar to `Lean.MessageData.ofConst`.
|
||||
Uses the constant's universe level parameters when pretty printing.
|
||||
If there is no such constant in the environment, the name is simply formatted.
|
||||
-/
|
||||
def ofConstName (constName : Name) : MessageData :=
|
||||
.ofFormatWithInfosM do
|
||||
if let some info := (← getEnv).find? constName then
|
||||
let delab : Delab := withOptionAtCurrPos `pp.tagAppFns true delabConst
|
||||
PrettyPrinter.ppExprWithInfos (delab := delab) (.const constName <| info.levelParams.map mkLevelParam)
|
||||
else
|
||||
return format constName
|
||||
|
||||
/-- Generates `MessageData` for a declaration `c` as `c.{<levels>} <params> : <type>`, with terminfo. -/
|
||||
def signature (c : Name) : MessageData :=
|
||||
.ofFormatWithInfosM (PrettyPrinter.ppSignature c)
|
||||
|
||||
@@ -199,6 +199,10 @@ def unexpandStructureInstance (stx : Syntax) : Delab := whenPPOption getPPStruct
|
||||
let mut fields := #[]
|
||||
guard $ fieldNames.size == stx[1].getNumArgs
|
||||
if hasPPUsingAnonymousConstructorAttribute env s.induct then
|
||||
/- Note that we don't flatten anonymous constructor notation. Only a complete such notation receives TermInfo,
|
||||
and flattening would cause the flattened-in notation to lose its TermInfo.
|
||||
Potentially it would be justified to flatten anonymous constructor notation when the terms are
|
||||
from the same type family (think `Sigma`), but for now users can write a custom delaborator in such instances. -/
|
||||
return ← withTypeAscription (cond := (← withType <| getPPOption getPPStructureInstanceType)) do
|
||||
`(⟨$[$(stx[1].getArgs)],*⟩)
|
||||
let args := e.getAppArgs
|
||||
@@ -638,7 +642,7 @@ List.map.match_1 : {α : Type _} →
|
||||
```
|
||||
-/
|
||||
@[builtin_delab app]
|
||||
partial def delabAppMatch : Delab := whenPPOption getPPNotation <| whenPPOption getPPMatch do
|
||||
partial def delabAppMatch : Delab := whenNotPPOption getPPExplicit <| whenPPOption getPPNotation <| whenPPOption getPPMatch do
|
||||
-- Check that this is a matcher, and then set up overapplication.
|
||||
let Expr.const c us := (← getExpr).getAppFn | failure
|
||||
let some info ← getMatcherInfo? c | failure
|
||||
@@ -769,16 +773,6 @@ def delabMData : Delab := do
|
||||
else
|
||||
withMDataOptions delab
|
||||
|
||||
/--
|
||||
Check for a `Syntax.ident` of the given name anywhere in the tree.
|
||||
This is usually a bad idea since it does not check for shadowing bindings,
|
||||
but in the delaborator we assume that bindings are never shadowed.
|
||||
-/
|
||||
partial def hasIdent (id : Name) : Syntax → Bool
|
||||
| Syntax.ident _ _ id' _ => id == id'
|
||||
| Syntax.node _ _ args => args.any (hasIdent id)
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Return `true` iff current binder should be merged with the nested
|
||||
binder, if any, into a single binder group:
|
||||
@@ -824,7 +818,7 @@ def delabLam : Delab :=
|
||||
let e ← getExpr
|
||||
let stxT ← withBindingDomain delab
|
||||
let ppTypes ← getPPOption getPPFunBinderTypes
|
||||
let usedDownstream := curNames.any (fun n => hasIdent n.getId stxBody)
|
||||
let usedDownstream := curNames.any (fun n => stxBody.hasIdent n.getId)
|
||||
|
||||
-- leave lambda implicit if possible
|
||||
-- TODO: for now we just always block implicit lambdas when delaborating. We can revisit.
|
||||
@@ -1135,6 +1129,24 @@ def delabSigma : Delab := delabSigmaCore (sigma := true)
|
||||
@[builtin_delab app.PSigma]
|
||||
def delabPSigma : Delab := delabSigmaCore (sigma := false)
|
||||
|
||||
-- PProd and MProd value delaborator
|
||||
-- (like pp_using_anonymous_constructor but flattening nested tuples)
|
||||
|
||||
def delabPProdMkCore (mkName : Name) : Delab := whenNotPPOption getPPExplicit <| whenPPOption getPPNotation do
|
||||
guard <| (← getExpr).getAppNumArgs == 4
|
||||
let a ← withAppFn <| withAppArg delab
|
||||
let b ← withAppArg <| delab
|
||||
if (← getExpr).appArg!.isAppOfArity mkName 4 then
|
||||
if let `(⟨$xs,*⟩) := b then
|
||||
return ← `(⟨$a, $xs,*⟩)
|
||||
`(⟨$a, $b⟩)
|
||||
|
||||
@[builtin_delab app.PProd.mk]
|
||||
def delabPProdMk : Delab := delabPProdMkCore ``PProd.mk
|
||||
|
||||
@[builtin_delab app.MProd.mk]
|
||||
def delabMProdMk : Delab := delabPProdMkCore ``MProd.mk
|
||||
|
||||
partial def delabDoElems : DelabM (List Syntax) := do
|
||||
let e ← getExpr
|
||||
if e.isAppOfArity ``Bind.bind 6 then
|
||||
|
||||
@@ -164,6 +164,16 @@ def asNode : Syntax → SyntaxNode
|
||||
def getIdAt (stx : Syntax) (i : Nat) : Name :=
|
||||
(stx.getArg i).getId
|
||||
|
||||
/--
|
||||
Check for a `Syntax.ident` of the given name anywhere in the tree.
|
||||
This is usually a bad idea since it does not check for shadowing bindings,
|
||||
but in the delaborator we assume that bindings are never shadowed.
|
||||
-/
|
||||
partial def hasIdent (id : Name) : Syntax → Bool
|
||||
| ident _ _ id' _ => id == id'
|
||||
| node _ _ args => args.any (hasIdent id)
|
||||
| _ => false
|
||||
|
||||
@[inline] def modifyArgs (stx : Syntax) (fn : Array Syntax → Array Syntax) : Syntax :=
|
||||
match stx with
|
||||
| node i k args => node i k (fn args)
|
||||
|
||||
@@ -106,6 +106,10 @@ instance : ToExpr Unit where
|
||||
toExpr := fun _ => mkConst `Unit.unit
|
||||
toTypeExpr := mkConst ``Unit
|
||||
|
||||
instance : ToExpr System.FilePath where
|
||||
toExpr p := mkApp (mkConst ``System.FilePath.mk) (toExpr p.toString)
|
||||
toTypeExpr := mkConst ``System.FilePath
|
||||
|
||||
private def Name.toExprAux (n : Name) : Expr :=
|
||||
if isSimple n 0 then
|
||||
mkStr n 0 #[]
|
||||
|
||||
@@ -29,4 +29,6 @@ import Lean.Util.OccursCheck
|
||||
import Lean.Util.HasConstCache
|
||||
import Lean.Util.FileSetupInfo
|
||||
import Lean.Util.Heartbeats
|
||||
import Lean.Util.SearchPath
|
||||
import Lean.Util.SafeExponentiation
|
||||
import Lean.Util.NumObjs
|
||||
|
||||
@@ -9,48 +9,11 @@ import Lean.Util.PtrSet
|
||||
|
||||
namespace Lean
|
||||
namespace Expr
|
||||
namespace FindImpl
|
||||
|
||||
unsafe abbrev FindM := StateT (PtrSet Expr) Id
|
||||
@[extern "lean_find_expr"]
|
||||
opaque findImpl? (p : @& (Expr → Bool)) (e : @& Expr) : Option Expr
|
||||
|
||||
@[inline] unsafe def checkVisited (e : Expr) : OptionT FindM Unit := do
|
||||
if (← get).contains e then
|
||||
failure
|
||||
modify fun s => s.insert e
|
||||
|
||||
unsafe def findM? (p : Expr → Bool) (e : Expr) : OptionT FindM Expr :=
|
||||
let rec visit (e : Expr) := do
|
||||
checkVisited e
|
||||
if p e then
|
||||
pure e
|
||||
else match e with
|
||||
| .forallE _ d b _ => visit d <|> visit b
|
||||
| .lam _ d b _ => visit d <|> visit b
|
||||
| .mdata _ b => visit b
|
||||
| .letE _ t v b _ => visit t <|> visit v <|> visit b
|
||||
| .app f a => visit f <|> visit a
|
||||
| .proj _ _ b => visit b
|
||||
| _ => failure
|
||||
visit e
|
||||
|
||||
unsafe def findUnsafe? (p : Expr → Bool) (e : Expr) : Option Expr :=
|
||||
Id.run <| findM? p e |>.run' mkPtrSet
|
||||
|
||||
end FindImpl
|
||||
|
||||
@[implemented_by FindImpl.findUnsafe?]
|
||||
def find? (p : Expr → Bool) (e : Expr) : Option Expr :=
|
||||
/- This is a reference implementation for the unsafe one above -/
|
||||
if p e then
|
||||
some e
|
||||
else match e with
|
||||
| .forallE _ d b _ => find? p d <|> find? p b
|
||||
| .lam _ d b _ => find? p d <|> find? p b
|
||||
| .mdata _ b => find? p b
|
||||
| .letE _ t v b _ => find? p t <|> find? p v <|> find? p b
|
||||
| .app f a => find? p f <|> find? p a
|
||||
| .proj _ _ b => find? p b
|
||||
| _ => none
|
||||
@[inline] def find? (p : Expr → Bool) (e : Expr) : Option Expr := findImpl? p e
|
||||
|
||||
/-- Return true if `e` occurs in `t` -/
|
||||
def occurs (e : Expr) (t : Expr) : Bool :=
|
||||
@@ -64,41 +27,13 @@ inductive FindStep where
|
||||
/-- Search subterms -/ | visit
|
||||
/-- Do not search subterms -/ | done
|
||||
|
||||
namespace FindExtImpl
|
||||
|
||||
unsafe def findM? (p : Expr → FindStep) (e : Expr) : OptionT FindImpl.FindM Expr :=
|
||||
visit e
|
||||
where
|
||||
visitApp (e : Expr) :=
|
||||
match e with
|
||||
| .app f a .. => visitApp f <|> visit a
|
||||
| e => visit e
|
||||
|
||||
visit (e : Expr) := do
|
||||
FindImpl.checkVisited e
|
||||
match p e with
|
||||
| .done => failure
|
||||
| .found => pure e
|
||||
| .visit =>
|
||||
match e with
|
||||
| .forallE _ d b _ => visit d <|> visit b
|
||||
| .lam _ d b _ => visit d <|> visit b
|
||||
| .mdata _ b => visit b
|
||||
| .letE _ t v b _ => visit t <|> visit v <|> visit b
|
||||
| .app .. => visitApp e
|
||||
| .proj _ _ b => visit b
|
||||
| _ => failure
|
||||
|
||||
unsafe def findUnsafe? (p : Expr → FindStep) (e : Expr) : Option Expr :=
|
||||
Id.run <| findM? p e |>.run' mkPtrSet
|
||||
|
||||
end FindExtImpl
|
||||
@[extern "lean_find_ext_expr"]
|
||||
opaque findExtImpl? (p : @& (Expr → FindStep)) (e : @& Expr) : Option Expr
|
||||
|
||||
/--
|
||||
Similar to `find?`, but `p` can return `FindStep.done` to interrupt the search on subterms.
|
||||
Remark: Differently from `find?`, we do not invoke `p` for partial applications of an application. -/
|
||||
@[implemented_by FindExtImpl.findUnsafe?]
|
||||
opaque findExt? (p : Expr → FindStep) (e : Expr) : Option Expr
|
||||
@[inline] def findExt? (p : Expr → FindStep) (e : Expr) : Option Expr := findExtImpl? p e
|
||||
|
||||
end Expr
|
||||
end Lean
|
||||
|
||||
47
src/Lean/Util/NumObjs.lean
Normal file
47
src/Lean/Util/NumObjs.lean
Normal file
@@ -0,0 +1,47 @@
|
||||
/-
|
||||
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Expr
|
||||
import Lean.Util.PtrSet
|
||||
|
||||
namespace Lean.Expr
|
||||
namespace NumObjs
|
||||
|
||||
unsafe structure State where
|
||||
visited : PtrSet Expr := mkPtrSet
|
||||
counter : Nat := 0
|
||||
|
||||
unsafe abbrev M := StateM State
|
||||
|
||||
unsafe def visit (e : Expr) : M Unit :=
|
||||
unless (← get).visited.contains e do
|
||||
modify fun { visited, counter } => { visited := visited.insert e, counter := counter + 1 }
|
||||
match e with
|
||||
| .forallE _ d b _ => visit d; visit b
|
||||
| .lam _ d b _ => visit d; visit b
|
||||
| .mdata _ b => visit b
|
||||
| .letE _ t v b _ => visit t; visit v; visit b
|
||||
| .app f a => visit f; visit a
|
||||
| .proj _ _ b => visit b
|
||||
| _ => return ()
|
||||
|
||||
unsafe def main (e : Expr) : Nat :=
|
||||
let (_, s) := NumObjs.visit e |>.run {}
|
||||
s.counter
|
||||
|
||||
end NumObjs
|
||||
|
||||
/--
|
||||
Returns the number of allocated `Expr` objects in the given expression `e`.
|
||||
|
||||
This operation is performed in `IO` because the result depends on the memory representation of the object.
|
||||
|
||||
Note: Use this function primarily for diagnosing performance issues.
|
||||
-/
|
||||
def numObjs (e : Expr) : IO Nat :=
|
||||
return unsafe NumObjs.main e
|
||||
|
||||
end Lean.Expr
|
||||
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Hashable
|
||||
import Lean.Data.HashSet
|
||||
import Lean.Data.HashMap
|
||||
|
||||
namespace Lean
|
||||
|
||||
@@ -33,4 +34,22 @@ unsafe abbrev PtrSet.insert (s : PtrSet α) (a : α) : PtrSet α :=
|
||||
unsafe abbrev PtrSet.contains (s : PtrSet α) (a : α) : Bool :=
|
||||
HashSet.contains s { value := a }
|
||||
|
||||
/--
|
||||
Map of pointers. It is a low-level auxiliary datastructure used for traversing DAGs.
|
||||
-/
|
||||
unsafe def PtrMap (α : Type) (β : Type) :=
|
||||
HashMap (Ptr α) β
|
||||
|
||||
unsafe def mkPtrMap {α β : Type} (capacity : Nat := 64) : PtrMap α β :=
|
||||
mkHashMap capacity
|
||||
|
||||
unsafe abbrev PtrMap.insert (s : PtrMap α β) (a : α) (b : β) : PtrMap α β :=
|
||||
HashMap.insert s { value := a } b
|
||||
|
||||
unsafe abbrev PtrMap.contains (s : PtrMap α β) (a : α) : Bool :=
|
||||
HashMap.contains s { value := a }
|
||||
|
||||
unsafe abbrev PtrMap.find? (s : PtrMap α β) (a : α) : Option β :=
|
||||
HashMap.find? s { value := a }
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -5,74 +5,59 @@ Authors: Leonardo de Moura, Gabriel Ebner, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Lean.Expr
|
||||
import Lean.Util.PtrSet
|
||||
|
||||
namespace Lean
|
||||
namespace Expr
|
||||
|
||||
namespace ReplaceImpl
|
||||
|
||||
structure Cache where
|
||||
size : USize
|
||||
-- First `size` elements are the keys.
|
||||
-- Second `size` elements are the results.
|
||||
keysResults : Array NonScalar -- Either Expr or Unit (disjoint memory representation)
|
||||
unsafe abbrev ReplaceM := StateM (PtrMap Expr Expr)
|
||||
|
||||
unsafe def Cache.new (e : Expr) : Cache :=
|
||||
-- scale size with approximate number of subterms up to 8k
|
||||
-- make sure size is coprime with power of two for collision avoidance
|
||||
let size := (1 <<< min (max e.approxDepth.toUSize 1) 13) - 1
|
||||
{ size, keysResults := mkArray (2 * size).toNat (unsafeCast ()) }
|
||||
|
||||
@[inline]
|
||||
unsafe def Cache.keyIdx (c : Cache) (key : Expr) : USize :=
|
||||
ptrAddrUnsafe key % c.size
|
||||
|
||||
@[inline]
|
||||
unsafe def Cache.resultIdx (c : Cache) (key : Expr) : USize :=
|
||||
c.keyIdx key + c.size
|
||||
|
||||
@[inline]
|
||||
unsafe def Cache.hasResultFor (c : Cache) (key : Expr) : Bool :=
|
||||
have : (c.keyIdx key).toNat < c.keysResults.size := lcProof
|
||||
ptrEq (unsafeCast key) c.keysResults[c.keyIdx key]
|
||||
|
||||
@[inline]
|
||||
unsafe def Cache.getResultFor (c : Cache) (key : Expr) : Expr :=
|
||||
have : (c.resultIdx key).toNat < c.keysResults.size := lcProof
|
||||
unsafeCast c.keysResults[c.resultIdx key]
|
||||
|
||||
unsafe def Cache.store (c : Cache) (key result : Expr) : Cache :=
|
||||
{ c with keysResults := c.keysResults
|
||||
|>.uset (c.keyIdx key) (unsafeCast key) lcProof
|
||||
|>.uset (c.resultIdx key) (unsafeCast result) lcProof }
|
||||
|
||||
abbrev ReplaceM := StateM Cache
|
||||
|
||||
@[inline]
|
||||
unsafe def cache (key : Expr) (result : Expr) : ReplaceM Expr := do
|
||||
modify (·.store key result)
|
||||
unsafe def cache (key : Expr) (exclusive : Bool) (result : Expr) : ReplaceM Expr := do
|
||||
unless exclusive do
|
||||
modify (·.insert key result)
|
||||
pure result
|
||||
|
||||
@[specialize]
|
||||
unsafe def replaceUnsafeM (f? : Expr → Option Expr) (e : Expr) : ReplaceM Expr := do
|
||||
let rec @[specialize] visit (e : Expr) := do
|
||||
if (← get).hasResultFor e then
|
||||
return (← get).getResultFor e
|
||||
else match f? e with
|
||||
| some eNew => cache e eNew
|
||||
/-
|
||||
TODO: We need better control over RC operations to ensure
|
||||
the following (unsafe) optimization is correctly applied.
|
||||
Optimization goal: only cache results for shared objects.
|
||||
|
||||
The main problem is that the current code generator ignores borrow annotations
|
||||
for code written in Lean. These annotations are only taken into account for extern functions.
|
||||
|
||||
Moveover, the borrow inference heuristic currently tags `e` as "owned" since it may be stored
|
||||
in the cache and is used in "update" functions.
|
||||
Thus, when visiting `e` sub-expressions the code generator increases their RC
|
||||
because we are recursively invoking `visit` :(
|
||||
|
||||
Thus, to fix this issue, we must
|
||||
1- Take borrow annotations into account for code written in Lean.
|
||||
2- Mark `e` is borrowed (i.e., `(e : @& Expr)`)
|
||||
-/
|
||||
let excl := isExclusiveUnsafe e
|
||||
unless excl do
|
||||
if let some result := (← get).find? e then
|
||||
return result
|
||||
match f? e with
|
||||
| some eNew => cache e excl eNew
|
||||
| none => match e with
|
||||
| Expr.forallE _ d b _ => cache e <| e.updateForallE! (← visit d) (← visit b)
|
||||
| Expr.lam _ d b _ => cache e <| e.updateLambdaE! (← visit d) (← visit b)
|
||||
| Expr.mdata _ b => cache e <| e.updateMData! (← visit b)
|
||||
| Expr.letE _ t v b _ => cache e <| e.updateLet! (← visit t) (← visit v) (← visit b)
|
||||
| Expr.app f a => cache e <| e.updateApp! (← visit f) (← visit a)
|
||||
| Expr.proj _ _ b => cache e <| e.updateProj! (← visit b)
|
||||
| e => pure e
|
||||
| .forallE _ d b _ => cache e excl <| e.updateForallE! (← visit d) (← visit b)
|
||||
| .lam _ d b _ => cache e excl <| e.updateLambdaE! (← visit d) (← visit b)
|
||||
| .mdata _ b => cache e excl <| e.updateMData! (← visit b)
|
||||
| .letE _ t v b _ => cache e excl <| e.updateLet! (← visit t) (← visit v) (← visit b)
|
||||
| .app f a => cache e excl <| e.updateApp! (← visit f) (← visit a)
|
||||
| .proj _ _ b => cache e excl <| e.updateProj! (← visit b)
|
||||
| e => return e
|
||||
visit e
|
||||
|
||||
@[inline]
|
||||
unsafe def replaceUnsafe (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
(replaceUnsafeM f? e).run' (Cache.new e)
|
||||
(replaceUnsafeM f? e).run' mkPtrMap
|
||||
|
||||
end ReplaceImpl
|
||||
|
||||
@@ -92,6 +77,10 @@ def replaceNoCache (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
| .proj _ _ b => let b := replaceNoCache f? b; e.updateProj! b
|
||||
| e => e
|
||||
|
||||
|
||||
@[extern "lean_replace_expr"]
|
||||
opaque replaceImpl (f? : @& (Expr → Option Expr)) (e : @& Expr) : Expr
|
||||
|
||||
@[implemented_by ReplaceImpl.replaceUnsafe]
|
||||
partial def replace (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
def replace (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
e.replaceNoCache f?
|
||||
|
||||
23
src/Lean/Util/SearchPath.lean
Normal file
23
src/Lean/Util/SearchPath.lean
Normal file
@@ -0,0 +1,23 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Lean.ToExpr
|
||||
import Lean.Util.Path
|
||||
import Lean.Elab.Term
|
||||
|
||||
open Lean
|
||||
|
||||
/--
|
||||
Term elaborator that retrieves the current `SearchPath`.
|
||||
|
||||
Typical usage is `searchPathRef.set compile_time_search_path%`.
|
||||
|
||||
This must not be used in files that are potentially compiled on another machine and then imported.
|
||||
(That is, if used in an imported file it will embed the search path from whichever machine
|
||||
compiled the `.olean`.)
|
||||
-/
|
||||
elab "compile_time_search_path%" : term =>
|
||||
return toExpr (← searchPathRef.get)
|
||||
@@ -5,7 +5,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Wojciech Nawrocki
|
||||
-/
|
||||
prelude
|
||||
import Lean.PrettyPrinter
|
||||
import Lean.Server.Rpc.Basic
|
||||
import Lean.Server.InfoUtils
|
||||
import Lean.Widget.TaggedText
|
||||
|
||||
@@ -30,6 +30,8 @@ universe u v w
|
||||
|
||||
variable {α : Type u} {β : α → Type v} {δ : Type w} {m : Type w → Type w} [Monad m]
|
||||
|
||||
variable {_ : BEq α} {_ : Hashable α}
|
||||
|
||||
namespace Std
|
||||
|
||||
open DHashMap.Internal DHashMap.Internal.List
|
||||
@@ -42,6 +44,9 @@ and an array of buckets, where each bucket is a linked list of key-value pais. T
|
||||
is always a power of two. The hash map doubles its size upon inserting an element such that the
|
||||
number of elements is more than 75% of the number of buckets.
|
||||
|
||||
The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to
|
||||
avoid expensive copies.
|
||||
|
||||
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
|
||||
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
|
||||
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
|
||||
@@ -66,34 +71,34 @@ instance [BEq α] [Hashable α] : EmptyCollection (DHashMap α β) where
|
||||
instance [BEq α] [Hashable α] : Inhabited (DHashMap α β) where
|
||||
default := ∅
|
||||
|
||||
@[inline, inherit_doc Raw.insert] def insert [BEq α] [Hashable α] (m : DHashMap α β) (a : α)
|
||||
@[inline, inherit_doc Raw.insert] def insert (m : DHashMap α β) (a : α)
|
||||
(b : β a) : DHashMap α β :=
|
||||
⟨Raw₀.insert ⟨m.1, m.2.size_buckets_pos⟩ a b, .insert₀ m.2⟩
|
||||
|
||||
@[inline, inherit_doc Raw.insertIfNew] def insertIfNew [BEq α] [Hashable α] (m : DHashMap α β)
|
||||
@[inline, inherit_doc Raw.insertIfNew] def insertIfNew (m : DHashMap α β)
|
||||
(a : α) (b : β a) : DHashMap α β :=
|
||||
⟨Raw₀.insertIfNew ⟨m.1, m.2.size_buckets_pos⟩ a b, .insertIfNew₀ m.2⟩
|
||||
|
||||
@[inline, inherit_doc Raw.containsThenInsert] def containsThenInsert [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.containsThenInsert] def containsThenInsert
|
||||
(m : DHashMap α β) (a : α) (b : β a) : Bool × DHashMap α β :=
|
||||
let m' := Raw₀.containsThenInsert ⟨m.1, m.2.size_buckets_pos⟩ a b
|
||||
⟨m'.1, ⟨m'.2.1, .containsThenInsert₀ m.2⟩⟩
|
||||
|
||||
@[inline, inherit_doc Raw.containsThenInsertIfNew] def containsThenInsertIfNew [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.containsThenInsertIfNew] def containsThenInsertIfNew
|
||||
(m : DHashMap α β) (a : α) (b : β a) : Bool × DHashMap α β :=
|
||||
let m' := Raw₀.containsThenInsertIfNew ⟨m.1, m.2.size_buckets_pos⟩ a b
|
||||
⟨m'.1, ⟨m'.2.1, .containsThenInsertIfNew₀ m.2⟩⟩
|
||||
|
||||
@[inline, inherit_doc Raw.getThenInsertIfNew?] def getThenInsertIfNew? [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.getThenInsertIfNew?] def getThenInsertIfNew?
|
||||
[LawfulBEq α] (m : DHashMap α β) (a : α) (b : β a) : Option (β a) × DHashMap α β :=
|
||||
let m' := Raw₀.getThenInsertIfNew? ⟨m.1, m.2.size_buckets_pos⟩ a b
|
||||
⟨m'.1, ⟨m'.2.1, .getThenInsertIfNew?₀ m.2⟩⟩
|
||||
|
||||
@[inline, inherit_doc Raw.get?] def get? [BEq α] [LawfulBEq α] [Hashable α] (m : DHashMap α β)
|
||||
@[inline, inherit_doc Raw.get?] def get? [LawfulBEq α] (m : DHashMap α β)
|
||||
(a : α) : Option (β a) :=
|
||||
Raw₀.get? ⟨m.1, m.2.size_buckets_pos⟩ a
|
||||
|
||||
@[inline, inherit_doc Raw.contains] def contains [BEq α] [Hashable α] (m : DHashMap α β) (a : α) :
|
||||
@[inline, inherit_doc Raw.contains] def contains (m : DHashMap α β) (a : α) :
|
||||
Bool :=
|
||||
Raw₀.contains ⟨m.1, m.2.size_buckets_pos⟩ a
|
||||
|
||||
@@ -103,77 +108,77 @@ instance [BEq α] [Hashable α] : Membership α (DHashMap α β) where
|
||||
instance [BEq α] [Hashable α] {m : DHashMap α β} {a : α} : Decidable (a ∈ m) :=
|
||||
show Decidable (m.contains a) from inferInstance
|
||||
|
||||
@[inline, inherit_doc Raw.get] def get [BEq α] [Hashable α] [LawfulBEq α] (m : DHashMap α β) (a : α)
|
||||
@[inline, inherit_doc Raw.get] def get [LawfulBEq α] (m : DHashMap α β) (a : α)
|
||||
(h : a ∈ m) : β a :=
|
||||
Raw₀.get ⟨m.1, m.2.size_buckets_pos⟩ a h
|
||||
|
||||
@[inline, inherit_doc Raw.get!] def get! [BEq α] [Hashable α] [LawfulBEq α] (m : DHashMap α β)
|
||||
@[inline, inherit_doc Raw.get!] def get! [LawfulBEq α] (m : DHashMap α β)
|
||||
(a : α) [Inhabited (β a)] : β a :=
|
||||
Raw₀.get! ⟨m.1, m.2.size_buckets_pos⟩ a
|
||||
|
||||
@[inline, inherit_doc Raw.getD] def getD [BEq α] [Hashable α] [LawfulBEq α] (m : DHashMap α β)
|
||||
@[inline, inherit_doc Raw.getD] def getD [LawfulBEq α] (m : DHashMap α β)
|
||||
(a : α) (fallback : β a) : β a :=
|
||||
Raw₀.getD ⟨m.1, m.2.size_buckets_pos⟩ a fallback
|
||||
|
||||
@[inline, inherit_doc Raw.remove] def remove [BEq α] [Hashable α] (m : DHashMap α β) (a : α) :
|
||||
@[inline, inherit_doc Raw.erase] def erase (m : DHashMap α β) (a : α) :
|
||||
DHashMap α β :=
|
||||
⟨Raw₀.remove ⟨m.1, m.2.size_buckets_pos⟩ a, .remove₀ m.2⟩
|
||||
⟨Raw₀.erase ⟨m.1, m.2.size_buckets_pos⟩ a, .erase₀ m.2⟩
|
||||
|
||||
section
|
||||
|
||||
variable {β : Type v}
|
||||
|
||||
@[inline, inherit_doc Raw.Const.get?] def Const.get? [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.Const.get?] def Const.get?
|
||||
(m : DHashMap α (fun _ => β)) (a : α) : Option β :=
|
||||
Raw₀.Const.get? ⟨m.1, m.2.size_buckets_pos⟩ a
|
||||
|
||||
@[inline, inherit_doc Raw.Const.get] def Const.get [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.Const.get] def Const.get
|
||||
(m : DHashMap α (fun _ => β)) (a : α) (h : a ∈ m) : β :=
|
||||
Raw₀.Const.get ⟨m.1, m.2.size_buckets_pos⟩ a h
|
||||
|
||||
@[inline, inherit_doc Raw.Const.getD] def Const.getD [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.Const.getD] def Const.getD
|
||||
(m : DHashMap α (fun _ => β)) (a : α) (fallback : β) : β :=
|
||||
Raw₀.Const.getD ⟨m.1, m.2.size_buckets_pos⟩ a fallback
|
||||
|
||||
@[inline, inherit_doc Raw.Const.get!] def Const.get! [BEq α] [Hashable α] [Inhabited β]
|
||||
@[inline, inherit_doc Raw.Const.get!] def Const.get! [Inhabited β]
|
||||
(m : DHashMap α (fun _ => β)) (a : α) : β :=
|
||||
Raw₀.Const.get! ⟨m.1, m.2.size_buckets_pos⟩ a
|
||||
|
||||
@[inline, inherit_doc Raw.Const.getThenInsertIfNew?] def Const.getThenInsertIfNew? [BEq α]
|
||||
[Hashable α] (m : DHashMap α (fun _ => β)) (a : α) (b : β) :
|
||||
@[inline, inherit_doc Raw.Const.getThenInsertIfNew?] def Const.getThenInsertIfNew?
|
||||
(m : DHashMap α (fun _ => β)) (a : α) (b : β) :
|
||||
Option β × DHashMap α (fun _ => β) :=
|
||||
let m' := Raw₀.Const.getThenInsertIfNew? ⟨m.1, m.2.size_buckets_pos⟩ a b
|
||||
⟨m'.1, ⟨m'.2.1, .constGetThenInsertIfNew?₀ m.2⟩⟩
|
||||
|
||||
end
|
||||
|
||||
@[inline, inherit_doc Raw.size] def size [BEq α] [Hashable α] (m : DHashMap α β) : Nat :=
|
||||
@[inline, inherit_doc Raw.size] def size (m : DHashMap α β) : Nat :=
|
||||
m.1.size
|
||||
|
||||
@[inline, inherit_doc Raw.isEmpty] def isEmpty [BEq α] [Hashable α] (m : DHashMap α β) : Bool :=
|
||||
@[inline, inherit_doc Raw.isEmpty] def isEmpty (m : DHashMap α β) : Bool :=
|
||||
m.1.isEmpty
|
||||
|
||||
section Unverified
|
||||
|
||||
/-! We currently do not provide lemmas for the functions below. -/
|
||||
|
||||
@[inline, inherit_doc Raw.filter] def filter [BEq α] [Hashable α] (f : (a : α) → β a → Bool)
|
||||
@[inline, inherit_doc Raw.filter] def filter (f : (a : α) → β a → Bool)
|
||||
(m : DHashMap α β) : DHashMap α β :=
|
||||
⟨Raw₀.filter f ⟨m.1, m.2.size_buckets_pos⟩, .filter₀ m.2⟩
|
||||
|
||||
@[inline, inherit_doc Raw.foldM] def foldM [BEq α] [Hashable α] (f : δ → (a : α) → β a → m δ)
|
||||
@[inline, inherit_doc Raw.foldM] def foldM (f : δ → (a : α) → β a → m δ)
|
||||
(init : δ) (b : DHashMap α β) : m δ :=
|
||||
b.1.foldM f init
|
||||
|
||||
@[inline, inherit_doc Raw.fold] def fold [BEq α] [Hashable α] (f : δ → (a : α) → β a → δ)
|
||||
@[inline, inherit_doc Raw.fold] def fold (f : δ → (a : α) → β a → δ)
|
||||
(init : δ) (b : DHashMap α β) : δ :=
|
||||
b.1.fold f init
|
||||
|
||||
@[inline, inherit_doc Raw.forM] def forM [BEq α] [Hashable α] (f : (a : α) → β a → m PUnit)
|
||||
@[inline, inherit_doc Raw.forM] def forM (f : (a : α) → β a → m PUnit)
|
||||
(b : DHashMap α β) : m PUnit :=
|
||||
b.1.forM f
|
||||
|
||||
@[inline, inherit_doc Raw.forIn] def forIn [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.forIn] def forIn
|
||||
(f : (a : α) → β a → δ → m (ForInStep δ)) (init : δ) (b : DHashMap α β) : m δ :=
|
||||
b.1.forIn f init
|
||||
|
||||
@@ -183,49 +188,49 @@ instance [BEq α] [Hashable α] : ForM m (DHashMap α β) ((a : α) × β a) whe
|
||||
instance [BEq α] [Hashable α] : ForIn m (DHashMap α β) ((a : α) × β a) where
|
||||
forIn m init f := m.forIn (fun a b acc => f ⟨a, b⟩ acc) init
|
||||
|
||||
@[inline, inherit_doc Raw.toList] def toList [BEq α] [Hashable α] (m : DHashMap α β) :
|
||||
@[inline, inherit_doc Raw.toList] def toList (m : DHashMap α β) :
|
||||
List ((a : α) × β a) :=
|
||||
m.1.toList
|
||||
|
||||
@[inline, inherit_doc Raw.toArray] def toArray [BEq α] [Hashable α] (m : DHashMap α β) :
|
||||
@[inline, inherit_doc Raw.toArray] def toArray (m : DHashMap α β) :
|
||||
Array ((a : α) × β a) :=
|
||||
m.1.toArray
|
||||
|
||||
@[inline, inherit_doc Raw.Const.toList] def Const.toList {β : Type v} [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.Const.toList] def Const.toList {β : Type v}
|
||||
(m : DHashMap α (fun _ => β)) : List (α × β) :=
|
||||
Raw.Const.toList m.1
|
||||
|
||||
@[inline, inherit_doc Raw.Const.toArray] def Const.toArray {β : Type v} [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.Const.toArray] def Const.toArray {β : Type v}
|
||||
(m : DHashMap α (fun _ => β)) : Array (α × β) :=
|
||||
Raw.Const.toArray m.1
|
||||
|
||||
@[inline, inherit_doc Raw.keys] def keys [BEq α] [Hashable α] (m : DHashMap α β) : List α :=
|
||||
@[inline, inherit_doc Raw.keys] def keys (m : DHashMap α β) : List α :=
|
||||
m.1.keys
|
||||
|
||||
@[inline, inherit_doc Raw.keysArray] def keysArray [BEq α] [Hashable α] (m : DHashMap α β) :
|
||||
@[inline, inherit_doc Raw.keysArray] def keysArray (m : DHashMap α β) :
|
||||
Array α :=
|
||||
m.1.keysArray
|
||||
|
||||
@[inline, inherit_doc Raw.values] def values {β : Type v} [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.values] def values {β : Type v}
|
||||
(m : DHashMap α (fun _ => β)) : List β :=
|
||||
m.1.values
|
||||
|
||||
@[inline, inherit_doc Raw.valuesArray] def valuesArray {β : Type v} [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.valuesArray] def valuesArray {β : Type v}
|
||||
(m : DHashMap α (fun _ => β)) : Array β :=
|
||||
m.1.valuesArray
|
||||
|
||||
@[inline, inherit_doc Raw.insertMany] def insertMany [BEq α] [Hashable α] {ρ : Type w}
|
||||
@[inline, inherit_doc Raw.insertMany] def insertMany {ρ : Type w}
|
||||
[ForIn Id ρ ((a : α) × β a)] (m : DHashMap α β) (l : ρ) : DHashMap α β :=
|
||||
⟨(Raw₀.insertMany ⟨m.1, m.2.size_buckets_pos⟩ l).1,
|
||||
(Raw₀.insertMany ⟨m.1, m.2.size_buckets_pos⟩ l).2 _ Raw.WF.insert₀ m.2⟩
|
||||
|
||||
@[inline, inherit_doc Raw.Const.insertMany] def Const.insertMany {β : Type v} [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.Const.insertMany] def Const.insertMany {β : Type v}
|
||||
{ρ : Type w} [ForIn Id ρ (α × β)] (m : DHashMap α (fun _ => β)) (l : ρ) :
|
||||
DHashMap α (fun _ => β) :=
|
||||
⟨(Raw₀.Const.insertMany ⟨m.1, m.2.size_buckets_pos⟩ l).1,
|
||||
(Raw₀.Const.insertMany ⟨m.1, m.2.size_buckets_pos⟩ l).2 _ Raw.WF.insert₀ m.2⟩
|
||||
|
||||
@[inline, inherit_doc Raw.Const.insertManyUnit] def Const.insertManyUnit [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc Raw.Const.insertManyUnit] def Const.insertManyUnit
|
||||
{ρ : Type w} [ForIn Id ρ α] (m : DHashMap α (fun _ => Unit)) (l : ρ) :
|
||||
DHashMap α (fun _ => Unit) :=
|
||||
⟨(Raw₀.Const.insertManyUnit ⟨m.1, m.2.size_buckets_pos⟩ l).1,
|
||||
@@ -243,7 +248,7 @@ instance [BEq α] [Hashable α] : ForIn m (DHashMap α β) ((a : α) × β a) wh
|
||||
DHashMap α (fun _ => Unit) :=
|
||||
Const.insertManyUnit ∅ l
|
||||
|
||||
@[inherit_doc Raw.Internal.numBuckets] def Internal.numBuckets [BEq α] [Hashable α]
|
||||
@[inherit_doc Raw.Internal.numBuckets] def Internal.numBuckets
|
||||
(m : DHashMap α β) : Nat :=
|
||||
Raw.Internal.numBuckets m.1
|
||||
|
||||
|
||||
@@ -77,61 +77,61 @@ variable {β : Type v}
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def get? [BEq α] (a : α) : AssocList α (fun _ => β) → Option β
|
||||
| nil => none
|
||||
| cons k v es => bif a == k then some v else get? a es
|
||||
| cons k v es => bif k == a then some v else get? a es
|
||||
|
||||
end
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getCast? [BEq α] [LawfulBEq α] (a : α) : AssocList α β → Option (β a)
|
||||
| nil => none
|
||||
| cons k v es => if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v)
|
||||
| cons k v es => if h : k == a then some (cast (congrArg β (eq_of_beq h)) v)
|
||||
else es.getCast? a
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def contains [BEq α] (a : α) : AssocList α β → Bool
|
||||
| nil => false
|
||||
| cons k _ l => a == k || l.contains a
|
||||
| cons k _ l => k == a || l.contains a
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def get {β : Type v} [BEq α] (a : α) : (l : AssocList α (fun _ => β)) → l.contains a → β
|
||||
| cons k v es, h => if hka : a == k then v else get a es
|
||||
| cons k v es, h => if hka : k == a then v else get a es
|
||||
(by rw [← h, contains, Bool.of_not_eq_true hka, Bool.false_or])
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getCast [BEq α] [LawfulBEq α] (a : α) : (l : AssocList α β) → l.contains a → β a
|
||||
| cons k v es, h => if hka : a == k then cast (congrArg β (eq_of_beq hka).symm) v
|
||||
| cons k v es, h => if hka : k == a then cast (congrArg β (eq_of_beq hka)) v
|
||||
else es.getCast a (by rw [← h, contains, Bool.of_not_eq_true hka, Bool.false_or])
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getCast! [BEq α] [LawfulBEq α] (a : α) [Inhabited (β a)] : AssocList α β → β a
|
||||
| nil => panic! "key is not present in hash table"
|
||||
| cons k v es => if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else es.getCast! a
|
||||
| cons k v es => if h : k == a then cast (congrArg β (eq_of_beq h)) v else es.getCast! a
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def get! {β : Type v} [BEq α] [Inhabited β] (a : α) : AssocList α (fun _ => β) → β
|
||||
| nil => panic! "key is not present in hash table"
|
||||
| cons k v es => bif a == k then v else es.get! a
|
||||
| cons k v es => bif k == a then v else es.get! a
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getCastD [BEq α] [LawfulBEq α] (a : α) (fallback : β a) : AssocList α β → β a
|
||||
| nil => fallback
|
||||
| cons k v es => if h : a == k then cast (congrArg β (eq_of_beq h).symm) v
|
||||
| cons k v es => if h : k == a then cast (congrArg β (eq_of_beq h)) v
|
||||
else es.getCastD a fallback
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getD {β : Type v} [BEq α] (a : α) (fallback : β) : AssocList α (fun _ => β) → β
|
||||
| nil => fallback
|
||||
| cons k v es => bif a == k then v else es.getD a fallback
|
||||
| cons k v es => bif k == a then v else es.getD a fallback
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def replace [BEq α] (a : α) (b : β a) : AssocList α β → AssocList α β
|
||||
| nil => nil
|
||||
| cons k v l => bif a == k then cons a b l else cons k v (replace a b l)
|
||||
| cons k v l => bif k == a then cons a b l else cons k v (replace a b l)
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def remove [BEq α] (a : α) : AssocList α β → AssocList α β
|
||||
def erase [BEq α] (a : α) : AssocList α β → AssocList α β
|
||||
| nil => nil
|
||||
| cons k v l => bif a == k then l else cons k v (l.remove a)
|
||||
| cons k v l => bif k == a then l else cons k v (l.erase a)
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
@[inline] def filterMap (f : (a : α) → β a → Option (γ a)) :
|
||||
|
||||
@@ -116,14 +116,14 @@ theorem toList_replace [BEq α] {l : AssocList α β} {a : α} {b : β a} :
|
||||
(l.replace a b).toList = replaceEntry a b l.toList := by
|
||||
induction l
|
||||
· simp [replace]
|
||||
· next k v t ih => cases h : a == k <;> simp_all [replace, List.replaceEntry_cons]
|
||||
· next k v t ih => cases h : k == a <;> simp_all [replace, List.replaceEntry_cons]
|
||||
|
||||
@[simp]
|
||||
theorem toList_remove [BEq α] {l : AssocList α β} {a : α} :
|
||||
(l.remove a).toList = removeKey a l.toList := by
|
||||
theorem toList_erase [BEq α] {l : AssocList α β} {a : α} :
|
||||
(l.erase a).toList = eraseKey a l.toList := by
|
||||
induction l
|
||||
· simp [remove]
|
||||
· next k v t ih => cases h : a == k <;> simp_all [remove, List.removeKey_cons]
|
||||
· simp [erase]
|
||||
· next k v t ih => cases h : k == a <;> simp_all [erase, List.eraseKey_cons]
|
||||
|
||||
theorem toList_filterMap {f : (a : α) → β a → Option (γ a)} {l : AssocList α β} :
|
||||
Perm (l.filterMap f).toList (l.toList.filterMap fun p => (f p.1 p.2).map (⟨p.1, ·⟩)) := by
|
||||
|
||||
@@ -79,7 +79,7 @@ maintainable. To this end, we provide theorems `apply_bucket`, `apply_bucket_wit
|
||||
`toListModel_updateBucket` and `toListModel_updateAllBuckets`, which do all of the heavy lifting in
|
||||
a general way. The verification for each actual operation in `Internal.WF` is then extremely
|
||||
straightward, requiring only to plug in some results about lists. See for example the functions
|
||||
`containsₘ_eq_containsKey` and the section on `removeₘ` for prototypical examples of this technique.
|
||||
`containsₘ_eq_containsKey` and the section on `eraseₘ` for prototypical examples of this technique.
|
||||
|
||||
Here is a summary of the steps required to add and verify a new operation:
|
||||
1. Write the executable implementation
|
||||
@@ -197,7 +197,7 @@ where
|
||||
if h : i < source.size then
|
||||
let idx : Fin source.size := ⟨i, h⟩
|
||||
let es := source.get idx
|
||||
-- We remove `es` from `source` to make sure we can reuse its memory cells
|
||||
-- We erase `es` from `source` to make sure we can reuse its memory cells
|
||||
-- when performing es.foldl
|
||||
let source := source.set idx .nil
|
||||
let target := es.foldl (reinsertAux hash) target
|
||||
@@ -313,13 +313,13 @@ where
|
||||
buckets[idx.1].getCast! a
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
@[inline] def remove [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||||
@[inline] def erase [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||||
let ⟨⟨size, buckets⟩, hb⟩ := m
|
||||
let ⟨i, h⟩ := mkIdx buckets.size hb (hash a)
|
||||
let bkt := buckets[i]
|
||||
if bkt.contains a then
|
||||
let buckets' := buckets.uset i .nil h
|
||||
⟨⟨size - 1, buckets'.uset i (bkt.remove a) (by simpa [buckets'])⟩, by simpa [buckets']⟩
|
||||
⟨⟨size - 1, buckets'.uset i (bkt.erase a) (by simpa [buckets'])⟩, by simpa [buckets']⟩
|
||||
else
|
||||
⟨⟨size, buckets⟩, hb⟩
|
||||
|
||||
|
||||
@@ -35,19 +35,19 @@ theorem assoc_induction {motive : List ((a : α) × β a) → Prop} (nil : motiv
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getEntry? [BEq α] (a : α) : List ((a : α) × β a) → Option ((a : α) × β a)
|
||||
| [] => none
|
||||
| ⟨k, v⟩ :: l => bif a == k then some ⟨k, v⟩ else getEntry? a l
|
||||
| ⟨k, v⟩ :: l => bif k == a then some ⟨k, v⟩ else getEntry? a l
|
||||
|
||||
@[simp] theorem getEntry?_nil [BEq α] {a : α} :
|
||||
getEntry? a ([] : List ((a : α) × β a)) = none := rfl
|
||||
theorem getEntry?_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
|
||||
getEntry? a (⟨k, v⟩ :: l) = bif a == k then some ⟨k, v⟩ else getEntry? a l := rfl
|
||||
getEntry? a (⟨k, v⟩ :: l) = bif k == a then some ⟨k, v⟩ else getEntry? a l := rfl
|
||||
|
||||
theorem getEntry?_cons_of_true [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : a == k) :
|
||||
theorem getEntry?_cons_of_true [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : k == a) :
|
||||
getEntry? a (⟨k, v⟩ :: l) = some ⟨k, v⟩ := by
|
||||
simp [getEntry?, h]
|
||||
|
||||
theorem getEntry?_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
|
||||
(h : (a == k) = false) : getEntry? a (⟨k, v⟩ :: l) = getEntry? a l := by
|
||||
(h : (k == a) = false) : getEntry? a (⟨k, v⟩ :: l) = getEntry? a l := by
|
||||
simp [getEntry?, h]
|
||||
|
||||
@[simp]
|
||||
@@ -56,11 +56,11 @@ theorem getEntry?_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)}
|
||||
getEntry?_cons_of_true BEq.refl
|
||||
|
||||
theorem getEntry?_eq_some [BEq α] {l : List ((a : α) × β a)} {a : α} {p : (a : α) × β a}
|
||||
(h : getEntry? a l = some p) : a == p.1 := by
|
||||
(h : getEntry? a l = some p) : p.1 == a := by
|
||||
induction l using assoc_induction
|
||||
· simp at h
|
||||
· next k' v' t ih =>
|
||||
cases h' : a == k'
|
||||
cases h' : k' == a
|
||||
· rw [getEntry?_cons_of_false h'] at h
|
||||
exact ih h
|
||||
· rw [getEntry?_cons_of_true h', Option.some.injEq] at h
|
||||
@@ -72,10 +72,10 @@ theorem getEntry?_congr [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k v l ih =>
|
||||
cases h' : b == k
|
||||
· have h₂ : (a == k) = false := BEq.neq_of_beq_of_neq h h'
|
||||
cases h' : k == a
|
||||
· have h₂ : (k == b) = false := BEq.neq_of_neq_of_beq h' h
|
||||
rw [getEntry?_cons_of_false h', getEntry?_cons_of_false h₂, ih]
|
||||
· rw [getEntry?_cons_of_true h', getEntry?_cons_of_true (BEq.trans h h')]
|
||||
· rw [getEntry?_cons_of_true h', getEntry?_cons_of_true (BEq.trans h' h)]
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_isSome_getEntry? [BEq α] [ReflBEq α] :
|
||||
{l : List ((a : α) × β a)} → l.isEmpty = false ↔ ∃ a, (getEntry? a l).isSome
|
||||
@@ -89,18 +89,18 @@ variable {β : Type v}
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getValue? [BEq α] (a : α) : List ((_ : α) × β) → Option β
|
||||
| [] => none
|
||||
| ⟨k, v⟩ :: l => bif a == k then some v else getValue? a l
|
||||
| ⟨k, v⟩ :: l => bif k == a then some v else getValue? a l
|
||||
|
||||
@[simp] theorem getValue?_nil [BEq α] {a : α} : getValue? a ([] : List ((_ : α) × β)) = none := rfl
|
||||
theorem getValue?_cons [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} :
|
||||
getValue? a (⟨k, v⟩ :: l) = bif a == k then some v else getValue? a l := rfl
|
||||
getValue? a (⟨k, v⟩ :: l) = bif k == a then some v else getValue? a l := rfl
|
||||
|
||||
theorem getValue?_cons_of_true [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : a == k) :
|
||||
theorem getValue?_cons_of_true [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : k == a) :
|
||||
getValue? a (⟨k, v⟩ :: l) = some v := by
|
||||
simp [getValue?, h]
|
||||
|
||||
theorem getValue?_cons_of_false [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β}
|
||||
(h : (a == k) = false) : getValue? a (⟨k, v⟩ :: l) = getValue? a l := by
|
||||
(h : (k == a) = false) : getValue? a (⟨k, v⟩ :: l) = getValue? a l := by
|
||||
simp [getValue?, h]
|
||||
|
||||
@[simp]
|
||||
@@ -113,7 +113,7 @@ theorem getValue?_eq_getEntry? [BEq α] {l : List ((_ : α) × β)} {a : α} :
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k v l ih =>
|
||||
cases h : a == k
|
||||
cases h : k == a
|
||||
· rw [getEntry?_cons_of_false h, getValue?_cons_of_false h, ih]
|
||||
· rw [getEntry?_cons_of_true h, getValue?_cons_of_true h, Option.map_some']
|
||||
|
||||
@@ -130,22 +130,22 @@ end
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def getValueCast? [BEq α] [LawfulBEq α] (a : α) : List ((a : α) × β a) → Option (β a)
|
||||
| [] => none
|
||||
| ⟨k, v⟩ :: l => if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v)
|
||||
| ⟨k, v⟩ :: l => if h : k == a then some (cast (congrArg β (eq_of_beq h)) v)
|
||||
else getValueCast? a l
|
||||
|
||||
@[simp] theorem getValueCast?_nil [BEq α] [LawfulBEq α] {a : α} :
|
||||
getValueCast? a ([] : List ((a : α) × β a)) = none := rfl
|
||||
theorem getValueCast?_cons [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
|
||||
getValueCast? a (⟨k, v⟩ :: l) = if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v)
|
||||
getValueCast? a (⟨k, v⟩ :: l) = if h : k == a then some (cast (congrArg β (eq_of_beq h)) v)
|
||||
else getValueCast? a l := rfl
|
||||
|
||||
theorem getValueCast?_cons_of_true [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} (h : a == k) :
|
||||
getValueCast? a (⟨k, v⟩ :: l) = some (cast (congrArg β (eq_of_beq h).symm) v) := by
|
||||
{v : β k} (h : k == a) :
|
||||
getValueCast? a (⟨k, v⟩ :: l) = some (cast (congrArg β (eq_of_beq h)) v) := by
|
||||
simp [getValueCast?, h]
|
||||
|
||||
theorem getValueCast?_cons_of_false [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} (h : (a == k) = false) : getValueCast? a (⟨k, v⟩ :: l) = getValueCast? a l := by
|
||||
{v : β k} (h : (k == a) = false) : getValueCast? a (⟨k, v⟩ :: l) = getValueCast? a l := by
|
||||
simp [getValueCast?, h]
|
||||
|
||||
@[simp]
|
||||
@@ -187,11 +187,11 @@ end
|
||||
|
||||
theorem getValueCast?_eq_getEntry? [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {a : α} :
|
||||
getValueCast? a l = Option.dmap (getEntry? a l)
|
||||
(fun p h => cast (congrArg β (eq_of_beq (getEntry?_eq_some h)).symm) p.2) := by
|
||||
(fun p h => cast (congrArg β (eq_of_beq (getEntry?_eq_some h))) p.2) := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k v t ih =>
|
||||
cases h : a == k
|
||||
cases h : k == a
|
||||
· rw [getValueCast?_cons_of_false h, ih, Option.dmap_congr (getEntry?_cons_of_false h)]
|
||||
· rw [getValueCast?_cons_of_true h, Option.dmap_congr (getEntry?_cons_of_true h),
|
||||
Option.dmap_some]
|
||||
@@ -207,23 +207,23 @@ theorem isEmpty_eq_false_iff_exists_isSome_getValueCast? [BEq α] [LawfulBEq α]
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def containsKey [BEq α] (a : α) : List ((a : α) × β a) → Bool
|
||||
| [] => false
|
||||
| ⟨k, _⟩ :: l => a == k || containsKey a l
|
||||
| ⟨k, _⟩ :: l => k == a || containsKey a l
|
||||
|
||||
@[simp] theorem containsKey_nil [BEq α] {a : α} :
|
||||
containsKey a ([] : List ((a : α) × β a)) = false := rfl
|
||||
@[simp] theorem containsKey_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
|
||||
containsKey a (⟨k, v⟩ :: l) = (a == k || containsKey a l) := rfl
|
||||
containsKey a (⟨k, v⟩ :: l) = (k == a || containsKey a l) := rfl
|
||||
|
||||
theorem containsKey_cons_eq_false [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
|
||||
(containsKey a (⟨k, v⟩ :: l) = false) ↔ ((a == k) = false) ∧ (containsKey a l = false) := by
|
||||
(containsKey a (⟨k, v⟩ :: l) = false) ↔ ((k == a) = false) ∧ (containsKey a l = false) := by
|
||||
simp [containsKey_cons, not_or]
|
||||
|
||||
theorem containsKey_cons_eq_true [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
|
||||
(containsKey a (⟨k, v⟩ :: l)) ↔ (a == k) ∨ (containsKey a l) := by
|
||||
(containsKey a (⟨k, v⟩ :: l)) ↔ (k == a) ∨ (containsKey a l) := by
|
||||
simp [containsKey_cons]
|
||||
|
||||
theorem containsKey_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
|
||||
(h : a == k) : containsKey a (⟨k, v⟩ :: l) := containsKey_cons_eq_true.2 <| Or.inl h
|
||||
(h : k == a) : containsKey a (⟨k, v⟩ :: l) := containsKey_cons_eq_true.2 <| Or.inl h
|
||||
|
||||
@[simp]
|
||||
theorem containsKey_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
@@ -233,7 +233,7 @@ theorem containsKey_cons_of_containsKey [BEq α] {l : List ((a : α) × β a)} {
|
||||
(h : containsKey a l) : containsKey a (⟨k, v⟩ :: l) := containsKey_cons_eq_true.2 <| Or.inr h
|
||||
|
||||
theorem containsKey_of_containsKey_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
|
||||
(h₁ : containsKey a (⟨k, v⟩ :: l)) (h₂ : (a == k) = false) : containsKey a l := by
|
||||
(h₁ : containsKey a (⟨k, v⟩ :: l)) (h₂ : (k == a) = false) : containsKey a l := by
|
||||
rcases (containsKey_cons_eq_true.1 h₁) with (h|h)
|
||||
· exact False.elim (Bool.eq_false_iff.1 h₂ h)
|
||||
· exact h
|
||||
@@ -243,7 +243,7 @@ theorem containsKey_eq_isSome_getEntry? [BEq α] {l : List ((a : α) × β a)} {
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k v l ih =>
|
||||
cases h : a == k
|
||||
cases h : k == a
|
||||
· simp [getEntry?_cons_of_false h, h, ih]
|
||||
· simp [getEntry?_cons_of_true h, h]
|
||||
|
||||
@@ -297,7 +297,7 @@ theorem getEntry_eq_of_getEntry?_eq_some [BEq α] {l : List ((a : α) × β a)}
|
||||
(h : getEntry? a l = some ⟨k, v⟩) {h'} : getEntry a l h' = ⟨k, v⟩ := by
|
||||
simp [getEntry, h]
|
||||
|
||||
theorem getEntry_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : a == k) :
|
||||
theorem getEntry_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : k == a) :
|
||||
getEntry a (⟨k, v⟩ :: l) (containsKey_cons_of_beq (v := v) h) = ⟨k, v⟩ := by
|
||||
simp [getEntry, getEntry?_cons_of_true h]
|
||||
|
||||
@@ -307,7 +307,7 @@ theorem getEntry_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {
|
||||
getEntry_cons_of_beq BEq.refl
|
||||
|
||||
theorem getEntry_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
|
||||
{h₁ : containsKey a (⟨k, v⟩ :: l)} (h₂ : (a == k) = false) : getEntry a (⟨k, v⟩ :: l) h₁ =
|
||||
{h₁ : containsKey a (⟨k, v⟩ :: l)} (h₂ : (k == a) = false) : getEntry a (⟨k, v⟩ :: l) h₁ =
|
||||
getEntry a l (containsKey_of_containsKey_cons (v := v) h₁ h₂) := by
|
||||
simp [getEntry, getEntry?_cons_of_false h₂]
|
||||
|
||||
@@ -323,7 +323,7 @@ theorem getValue?_eq_some_getValue [BEq α] {l : List ((_ : α) × β)} {a : α}
|
||||
getValue? a l = some (getValue a l h) := by
|
||||
simp [getValue]
|
||||
|
||||
theorem getValue_cons_of_beq [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : a == k) :
|
||||
theorem getValue_cons_of_beq [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : k == a) :
|
||||
getValue a (⟨k, v⟩ :: l) (containsKey_cons_of_beq (k := k) (v := v) h) = v := by
|
||||
simp [getValue, getValue?_cons_of_true h]
|
||||
|
||||
@@ -333,12 +333,12 @@ theorem getValue_cons_self [BEq α] [ReflBEq α] {l : List ((_ : α) × β)} {k
|
||||
getValue_cons_of_beq BEq.refl
|
||||
|
||||
theorem getValue_cons_of_false [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β}
|
||||
{h₁ : containsKey a (⟨k, v⟩ :: l)} (h₂ : (a == k) = false) : getValue a (⟨k, v⟩ :: l) h₁ =
|
||||
{h₁ : containsKey a (⟨k, v⟩ :: l)} (h₂ : (k == a) = false) : getValue a (⟨k, v⟩ :: l) h₁ =
|
||||
getValue a l (containsKey_of_containsKey_cons (k := k) (v := v) h₁ h₂) := by
|
||||
simp [getValue, getValue?_cons_of_false h₂]
|
||||
|
||||
theorem getValue_cons [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} {h} :
|
||||
getValue a (⟨k, v⟩ :: l) h = if h' : a == k then v
|
||||
getValue a (⟨k, v⟩ :: l) h = if h' : k == a then v
|
||||
else getValue a l (containsKey_of_containsKey_cons (k := k) h (Bool.eq_false_iff.2 h')) := by
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, getValue?_cons, apply_dite Option.some,
|
||||
cond_eq_if]
|
||||
@@ -369,8 +369,8 @@ theorem Option.get_congr {o o' : Option α} {ho : o.isSome} (h : o = o') :
|
||||
theorem getValueCast_cons [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
|
||||
(h : containsKey a (⟨k, v⟩ :: l)) :
|
||||
getValueCast a (⟨k, v⟩ :: l) h =
|
||||
if h' : a == k then
|
||||
cast (congrArg β (eq_of_beq h').symm) v
|
||||
if h' : k == a then
|
||||
cast (congrArg β (eq_of_beq h')) v
|
||||
else
|
||||
getValueCast a l (containsKey_of_containsKey_cons (k := k) h (Bool.eq_false_iff.2 h')) := by
|
||||
rw [getValueCast, Option.get_congr getValueCast?_cons]
|
||||
@@ -515,19 +515,19 @@ end
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def replaceEntry [BEq α] (k : α) (v : β k) : List ((a : α) × β a) → List ((a : α) × β a)
|
||||
| [] => []
|
||||
| ⟨k', v'⟩ :: l => bif k == k' then ⟨k, v⟩ :: l else ⟨k', v'⟩ :: replaceEntry k v l
|
||||
| ⟨k', v'⟩ :: l => bif k' == k then ⟨k, v⟩ :: l else ⟨k', v'⟩ :: replaceEntry k v l
|
||||
|
||||
@[simp] theorem replaceEntry_nil [BEq α] {k : α} {v : β k} : replaceEntry k v [] = [] := rfl
|
||||
theorem replaceEntry_cons [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v : β k} {v' : β k'} :
|
||||
replaceEntry k v (⟨k', v'⟩ :: l) =
|
||||
bif k == k' then ⟨k, v⟩ :: l else ⟨k', v'⟩ :: replaceEntry k v l := rfl
|
||||
bif k' == k then ⟨k, v⟩ :: l else ⟨k', v'⟩ :: replaceEntry k v l := rfl
|
||||
|
||||
theorem replaceEntry_cons_of_true [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v : β k}
|
||||
{v' : β k'} (h : k == k') : replaceEntry k v (⟨k', v'⟩ :: l) = ⟨k, v⟩ :: l := by
|
||||
{v' : β k'} (h : k' == k) : replaceEntry k v (⟨k', v'⟩ :: l) = ⟨k, v⟩ :: l := by
|
||||
simp [replaceEntry, h]
|
||||
|
||||
theorem replaceEntry_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v : β k}
|
||||
{v' : β k'} (h : (k == k') = false) :
|
||||
{v' : β k'} (h : (k' == k) = false) :
|
||||
replaceEntry k v (⟨k', v'⟩ :: l) = ⟨k', v'⟩ :: replaceEntry k v l := by
|
||||
simp [replaceEntry, h]
|
||||
|
||||
@@ -553,37 +553,37 @@ theorem getEntry?_replaceEntry_of_containsKey_eq_false [BEq α] {l : List ((a :
|
||||
rw [replaceEntry_of_containsKey_eq_false hl]
|
||||
|
||||
theorem getEntry?_replaceEntry_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{a k : α} {v : β k} (h : (a == k) = false) :
|
||||
{a k : α} {v : β k} (h : (k == a) = false) :
|
||||
getEntry? a (replaceEntry k v l) = getEntry? a l := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' l ih =>
|
||||
cases h' : k == k'
|
||||
cases h' : k' == k
|
||||
· rw [replaceEntry_cons_of_false h', getEntry?_cons, getEntry?_cons, ih]
|
||||
· rw [replaceEntry_cons_of_true h']
|
||||
have hk : (a == k') = false := BEq.neq_of_neq_of_beq h h'
|
||||
have hk : (k' == a) = false := BEq.neq_of_beq_of_neq h' h
|
||||
simp [getEntry?_cons_of_false h, getEntry?_cons_of_false hk]
|
||||
|
||||
theorem getEntry?_replaceEntry_of_true [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{a k : α} {v : β k} (hl : containsKey k l = true) (h : a == k) :
|
||||
{a k : α} {v : β k} (hl : containsKey k l = true) (h : k == a) :
|
||||
getEntry? a (replaceEntry k v l) = some ⟨k, v⟩ := by
|
||||
induction l using assoc_induction
|
||||
· simp at hl
|
||||
· next k' v' l ih =>
|
||||
cases hk'a : k == k'
|
||||
cases hk'a : k' == k
|
||||
· rw [replaceEntry_cons_of_false hk'a]
|
||||
have hk'k : (a == k') = false := BEq.neq_of_beq_of_neq h hk'a
|
||||
have hk'k : (k' == a) = false := BEq.neq_of_neq_of_beq hk'a h
|
||||
rw [getEntry?_cons_of_false hk'k]
|
||||
exact ih (containsKey_of_containsKey_cons hl hk'a)
|
||||
· rw [replaceEntry_cons_of_true hk'a, getEntry?_cons_of_true h]
|
||||
|
||||
theorem getEntry?_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
|
||||
{v : β k} :
|
||||
getEntry? a (replaceEntry k v l) = bif containsKey k l && a == k then some ⟨k, v⟩ else
|
||||
getEntry? a (replaceEntry k v l) = bif containsKey k l && k == a then some ⟨k, v⟩ else
|
||||
getEntry? a l := by
|
||||
cases hl : containsKey k l
|
||||
· simp [getEntry?_replaceEntry_of_containsKey_eq_false hl]
|
||||
· cases h : a == k
|
||||
· cases h : k == a
|
||||
· simp [getEntry?_replaceEntry_of_false h]
|
||||
· simp [getEntry?_replaceEntry_of_true hl h]
|
||||
|
||||
@@ -601,12 +601,12 @@ theorem getValue?_replaceEntry_of_containsKey_eq_false [BEq α] {l : List ((_ :
|
||||
simp [getValue?_eq_getEntry?, getEntry?_replaceEntry_of_containsKey_eq_false hl]
|
||||
|
||||
theorem getValue?_replaceEntry_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {v : β} (h : (a == k) = false) :
|
||||
{k a : α} {v : β} (h : (k == a) = false) :
|
||||
getValue? a (replaceEntry k v l) = getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_replaceEntry_of_false h]
|
||||
|
||||
theorem getValue?_replaceEntry_of_true [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {v : β} (hl : containsKey k l = true) (h : a == k) :
|
||||
{k a : α} {v : β} (hl : containsKey k l = true) (h : k == a) :
|
||||
getValue? a (replaceEntry k v l) = some v := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_replaceEntry_of_true hl h]
|
||||
|
||||
@@ -614,7 +614,7 @@ end
|
||||
|
||||
theorem getValueCast?_replaceEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {a k : α}
|
||||
{v : β k} : getValueCast? a (replaceEntry k v l) =
|
||||
if h : containsKey k l ∧ a == k then some (cast (congrArg β (eq_of_beq h.2).symm) v)
|
||||
if h : containsKey k l ∧ k == a then some (cast (congrArg β (eq_of_beq h.2)) v)
|
||||
else getValueCast? a l := by
|
||||
rw [getValueCast?_eq_getEntry?]
|
||||
split
|
||||
@@ -632,61 +632,61 @@ theorem getValueCast?_replaceEntry [BEq α] [LawfulBEq α] {l : List ((a : α)
|
||||
@[simp]
|
||||
theorem containsKey_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
|
||||
{v : β k} : containsKey a (replaceEntry k v l) = containsKey a l := by
|
||||
cases h : containsKey k l && a == k
|
||||
cases h : containsKey k l && k == a
|
||||
· rw [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h, cond_false,
|
||||
containsKey_eq_isSome_getEntry?]
|
||||
· rw [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h, cond_true, Option.isSome_some,
|
||||
Eq.comm]
|
||||
rw [Bool.and_eq_true] at h
|
||||
exact containsKey_of_beq h.1 (BEq.symm h.2)
|
||||
exact containsKey_of_beq h.1 h.2
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def removeKey [BEq α] (k : α) : List ((a : α) × β a) → List ((a : α) × β a)
|
||||
def eraseKey [BEq α] (k : α) : List ((a : α) × β a) → List ((a : α) × β a)
|
||||
| [] => []
|
||||
| ⟨k', v'⟩ :: l => bif k == k' then l else ⟨k', v'⟩ :: removeKey k l
|
||||
| ⟨k', v'⟩ :: l => bif k' == k then l else ⟨k', v'⟩ :: eraseKey k l
|
||||
|
||||
@[simp] theorem removeKey_nil [BEq α] {k : α} : removeKey k ([] : List ((a : α) × β a)) = [] := rfl
|
||||
theorem removeKey_cons [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'} :
|
||||
removeKey k (⟨k', v'⟩ :: l) = bif k == k' then l else ⟨k', v'⟩ :: removeKey k l := rfl
|
||||
@[simp] theorem eraseKey_nil [BEq α] {k : α} : eraseKey k ([] : List ((a : α) × β a)) = [] := rfl
|
||||
theorem eraseKey_cons [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'} :
|
||||
eraseKey k (⟨k', v'⟩ :: l) = bif k' == k then l else ⟨k', v'⟩ :: eraseKey k l := rfl
|
||||
|
||||
theorem removeKey_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
|
||||
(h : k == k') : removeKey k (⟨k', v'⟩ :: l) = l :=
|
||||
by simp [removeKey_cons, h]
|
||||
theorem eraseKey_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
|
||||
(h : k' == k) : eraseKey k (⟨k', v'⟩ :: l) = l :=
|
||||
by simp [eraseKey_cons, h]
|
||||
|
||||
@[simp]
|
||||
theorem removeKey_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
removeKey k (⟨k, v⟩ :: l) = l :=
|
||||
removeKey_cons_of_beq BEq.refl
|
||||
theorem eraseKey_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
eraseKey k (⟨k, v⟩ :: l) = l :=
|
||||
eraseKey_cons_of_beq BEq.refl
|
||||
|
||||
theorem removeKey_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
|
||||
(h : (k == k') = false) : removeKey k (⟨k', v'⟩ :: l) = ⟨k', v'⟩ :: removeKey k l := by
|
||||
simp [removeKey_cons, h]
|
||||
theorem eraseKey_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
|
||||
(h : (k' == k) = false) : eraseKey k (⟨k', v'⟩ :: l) = ⟨k', v'⟩ :: eraseKey k l := by
|
||||
simp [eraseKey_cons, h]
|
||||
|
||||
theorem removeKey_of_containsKey_eq_false [BEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(h : containsKey k l = false) : removeKey k l = l := by
|
||||
theorem eraseKey_of_containsKey_eq_false [BEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(h : containsKey k l = false) : eraseKey k l = l := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' t ih =>
|
||||
simp only [containsKey_cons, Bool.or_eq_false_iff] at h
|
||||
rw [removeKey_cons_of_false h.1, ih h.2]
|
||||
rw [eraseKey_cons_of_false h.1, ih h.2]
|
||||
|
||||
theorem sublist_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
Sublist (removeKey k l) l := by
|
||||
theorem sublist_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
Sublist (eraseKey k l) l := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' t ih =>
|
||||
rw [removeKey_cons]
|
||||
cases k == k'
|
||||
rw [eraseKey_cons]
|
||||
cases k' == k
|
||||
· simpa
|
||||
· simpa using Sublist.cons_right Sublist.refl
|
||||
|
||||
theorem length_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(removeKey k l).length = bif containsKey k l then l.length - 1 else l.length := by
|
||||
theorem length_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(eraseKey k l).length = bif containsKey k l then l.length - 1 else l.length := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' t ih =>
|
||||
rw [removeKey_cons, containsKey_cons]
|
||||
cases k == k'
|
||||
rw [eraseKey_cons, containsKey_cons]
|
||||
cases k' == k
|
||||
· rw [cond_false, Bool.false_or, List.length_cons, ih]
|
||||
cases h : containsKey k t
|
||||
· simp
|
||||
@@ -697,15 +697,15 @@ theorem length_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
· simp
|
||||
· simp
|
||||
|
||||
theorem length_removeKey_le [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(removeKey k l).length ≤ l.length :=
|
||||
sublist_removeKey.length_le
|
||||
theorem length_eraseKey_le [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(eraseKey k l).length ≤ l.length :=
|
||||
sublist_eraseKey.length_le
|
||||
|
||||
theorem isEmpty_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(removeKey k l).isEmpty = (l.isEmpty || (l.length == 1 && containsKey k l)) := by
|
||||
theorem isEmpty_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(eraseKey k l).isEmpty = (l.isEmpty || (l.length == 1 && containsKey k l)) := by
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp only [Bool.or_eq_true, Bool.and_eq_true, beq_iff_eq]
|
||||
rw [List.isEmpty_iff_length_eq_zero, length_removeKey, List.isEmpty_iff_length_eq_zero]
|
||||
rw [List.isEmpty_iff_length_eq_zero, length_eraseKey, List.isEmpty_iff_length_eq_zero]
|
||||
cases containsKey k l <;> cases l <;> simp
|
||||
|
||||
@[simp] theorem keys_nil : keys ([] : List ((a : α) × β a)) = [] := rfl
|
||||
@@ -722,7 +722,7 @@ theorem containsKey_eq_keys_contains [BEq α] [PartialEquivBEq α] {l : List ((a
|
||||
· next k _ l ih => simp [ih, BEq.comm]
|
||||
|
||||
theorem containsKey_eq_true_iff_exists_mem [BEq α] {l : List ((a : α) × β a)} {a : α} :
|
||||
containsKey a l = true ↔ ∃ p ∈ l, a == p.1 := by
|
||||
containsKey a l = true ↔ ∃ p ∈ l, p.1 == a := by
|
||||
induction l using assoc_induction <;> simp_all
|
||||
|
||||
theorem containsKey_of_mem [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {p : (a : α) × β a}
|
||||
@@ -798,11 +798,11 @@ theorem mem_iff_getEntry?_eq_some [BEq α] [EquivBEq α] {l : List ((a : α) ×
|
||||
refine ⟨?_, ?_⟩
|
||||
· rintro (rfl|hk)
|
||||
· simp
|
||||
· suffices (p.fst == k) = false by simp_all
|
||||
· suffices (k == p.fst) = false by simp_all
|
||||
refine Bool.eq_false_iff.2 fun hcon => Bool.false_ne_true ?_
|
||||
rw [← h.containsKey_eq_false, containsKey_congr (BEq.symm hcon),
|
||||
rw [← h.containsKey_eq_false, containsKey_congr hcon,
|
||||
containsKey_eq_isSome_getEntry?, hk, Option.isSome_some]
|
||||
· cases p.fst == k
|
||||
· cases k == p.fst
|
||||
· rw [cond_false]
|
||||
exact Or.inr
|
||||
· rw [cond_true, Option.some.injEq]
|
||||
@@ -814,7 +814,7 @@ theorem DistinctKeys.replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a :
|
||||
· simp
|
||||
· next k' v' l ih =>
|
||||
rw [distinctKeys_cons_iff] at h
|
||||
cases hk'k : k == k'
|
||||
cases hk'k : k' == k
|
||||
· rw [replaceEntry_cons_of_false hk'k, distinctKeys_cons_iff]
|
||||
refine ⟨ih h.1, ?_⟩
|
||||
simpa using h.2
|
||||
@@ -870,7 +870,7 @@ section
|
||||
variable {β : Type v}
|
||||
|
||||
theorem getValue?_insertEntry_of_beq [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
{v : β} (h : a == k) : getValue? a (insertEntry k v l) = some v := by
|
||||
{v : β} (h : k == a) : getValue? a (insertEntry k v l) = some v := by
|
||||
cases h' : containsKey k l
|
||||
· rw [insertEntry_of_containsKey_eq_false h', getValue?_cons_of_true h]
|
||||
· rw [insertEntry_of_containsKey h', getValue?_replaceEntry_of_true h' h]
|
||||
@@ -880,14 +880,14 @@ theorem getValue?_insertEntry_of_self [BEq α] [EquivBEq α] {l : List ((_ : α)
|
||||
getValue?_insertEntry_of_beq BEq.refl
|
||||
|
||||
theorem getValue?_insertEntry_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {v : β} (h : (a == k) = false) : getValue? a (insertEntry k v l) = getValue? a l := by
|
||||
{k a : α} {v : β} (h : (k == a) = false) : getValue? a (insertEntry k v l) = getValue? a l := by
|
||||
cases h' : containsKey k l
|
||||
· rw [insertEntry_of_containsKey_eq_false h', getValue?_cons_of_false h]
|
||||
· rw [insertEntry_of_containsKey h', getValue?_replaceEntry_of_false h]
|
||||
|
||||
theorem getValue?_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
{v : β} : getValue? a (insertEntry k v l) = bif a == k then some v else getValue? a l := by
|
||||
cases h : a == k
|
||||
{v : β} : getValue? a (insertEntry k v l) = bif k == a then some v else getValue? a l := by
|
||||
cases h : k == a
|
||||
· simp [getValue?_insertEntry_of_false h, h]
|
||||
· simp [getValue?_insertEntry_of_beq h, h]
|
||||
|
||||
@@ -899,14 +899,14 @@ end
|
||||
|
||||
theorem getEntry?_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} :
|
||||
getEntry? a (insertEntry k v l) = bif a == k then some ⟨k, v⟩ else getEntry? a l := by
|
||||
getEntry? a (insertEntry k v l) = bif k == a then some ⟨k, v⟩ else getEntry? a l := by
|
||||
cases hl : containsKey k l
|
||||
· rw [insertEntry_of_containsKey_eq_false hl, getEntry?_cons]
|
||||
· rw [insertEntry_of_containsKey hl, getEntry?_replaceEntry, hl, Bool.true_and, BEq.comm]
|
||||
|
||||
theorem getValueCast?_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} : getValueCast? a (insertEntry k v l) =
|
||||
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else getValueCast? a l := by
|
||||
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else getValueCast? a l := by
|
||||
cases hl : containsKey k l
|
||||
· rw [insertEntry_of_containsKey_eq_false hl, getValueCast?_cons]
|
||||
· rw [insertEntry_of_containsKey hl, getValueCast?_replaceEntry, hl]
|
||||
@@ -918,7 +918,7 @@ theorem getValueCast?_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValueCast!_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
[Inhabited (β a)] {v : β k} : getValueCast! a (insertEntry k v l) =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else getValueCast! a l := by
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v else getValueCast! a l := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_insertEntry, apply_dite Option.get!]
|
||||
|
||||
theorem getValueCast!_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
@@ -927,7 +927,7 @@ theorem getValueCast!_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValueCastD_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{fallback : β a} {v : β k} : getValueCastD a (insertEntry k v l) fallback =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v
|
||||
else getValueCastD a l fallback := by
|
||||
simp [getValueCastD_eq_getValueCast?, getValueCast?_insertEntry,
|
||||
apply_dite (fun x => Option.getD x fallback)]
|
||||
@@ -938,7 +938,7 @@ theorem getValueCastD_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValue!_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k a : α} {v : β} :
|
||||
getValue! a (insertEntry k v l) = bif a == k then v else getValue! a l := by
|
||||
getValue! a (insertEntry k v l) = bif k == a then v else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_insertEntry, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValue!_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] [Inhabited β]
|
||||
@@ -947,7 +947,7 @@ theorem getValue!_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] [Inhabit
|
||||
|
||||
theorem getValueD_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {fallback v : β} : getValueD a (insertEntry k v l) fallback =
|
||||
bif a == k then v else getValueD a l fallback := by
|
||||
bif k == a then v else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_insertEntry, Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem getValueD_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : List ((_ : α) × β)}
|
||||
@@ -956,12 +956,12 @@ theorem getValueD_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : Lis
|
||||
|
||||
@[simp]
|
||||
theorem containsKey_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} : containsKey a (insertEntry k v l) = ((a == k) || containsKey a l) := by
|
||||
{v : β k} : containsKey a (insertEntry k v l) = ((k == a) || containsKey a l) := by
|
||||
rw [containsKey_eq_isSome_getEntry?, containsKey_eq_isSome_getEntry?, getEntry?_insertEntry]
|
||||
cases a == k <;> simp
|
||||
cases k == a <;> simp
|
||||
|
||||
theorem containsKey_insertEntry_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} {v : β k} (h : a == k) : containsKey a (insertEntry k v l) := by
|
||||
{k a : α} {v : β k} (h : k == a) : containsKey a (insertEntry k v l) := by
|
||||
simp [h]
|
||||
|
||||
@[simp]
|
||||
@@ -971,12 +971,12 @@ theorem containsKey_insertEntry_self [BEq α] [EquivBEq α] {l : List ((a : α)
|
||||
|
||||
theorem containsKey_of_containsKey_insertEntry [BEq α] [PartialEquivBEq α]
|
||||
{l : List ((a : α) × β a)} {k a : α} {v : β k} (h₁ : containsKey a (insertEntry k v l))
|
||||
(h₂ : (a == k) = false) : containsKey a l := by
|
||||
(h₂ : (k == a) = false) : containsKey a l := by
|
||||
rwa [containsKey_insertEntry, h₂, Bool.false_or] at h₁
|
||||
|
||||
theorem getValueCast_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} {h} : getValueCast a (insertEntry k v l) h =
|
||||
if h' : a == k then cast (congrArg β (eq_of_beq h').symm) v
|
||||
if h' : k == a then cast (congrArg β (eq_of_beq h')) v
|
||||
else getValueCast a l (containsKey_of_containsKey_insertEntry h (Bool.eq_false_iff.2 h')) := by
|
||||
rw [← Option.some_inj, ← getValueCast?_eq_some_getValueCast, apply_dite Option.some,
|
||||
getValueCast?_insertEntry]
|
||||
@@ -988,7 +988,7 @@ theorem getValueCast_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a : α
|
||||
|
||||
theorem getValue_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {v : β} {h} : getValue a (insertEntry k v l) h =
|
||||
if h' : a == k then v
|
||||
if h' : k == a then v
|
||||
else getValue a l (containsKey_of_containsKey_insertEntry h (Bool.eq_false_iff.2 h')) := by
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, apply_dite Option.some,
|
||||
getValue?_insertEntry, cond_eq_if, ← dite_eq_ite]
|
||||
@@ -1020,14 +1020,14 @@ theorem isEmpty_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
|
||||
theorem getEntry?_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} : getEntry? a (insertEntryIfNew k v l) =
|
||||
bif a == k && !containsKey k l then some ⟨k, v⟩ else getEntry? a l := by
|
||||
bif k == a && !containsKey k l then some ⟨k, v⟩ else getEntry? a l := by
|
||||
cases h : containsKey k l
|
||||
· simp [insertEntryIfNew_of_containsKey_eq_false h, getEntry?_cons]
|
||||
· simp [insertEntryIfNew_of_containsKey h]
|
||||
|
||||
theorem getValueCast?_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} : getValueCast? a (insertEntryIfNew k v l) =
|
||||
if h : a == k ∧ containsKey k l = false then some (cast (congrArg β (eq_of_beq h.1).symm) v)
|
||||
if h : k == a ∧ containsKey k l = false then some (cast (congrArg β (eq_of_beq h.1)) v)
|
||||
else getValueCast? a l := by
|
||||
cases h : containsKey k l
|
||||
· rw [insertEntryIfNew_of_containsKey_eq_false h, getValueCast?_cons]
|
||||
@@ -1036,16 +1036,16 @@ theorem getValueCast?_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValue?_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {v : β} : getValue? a (insertEntryIfNew k v l) =
|
||||
bif a == k && !containsKey k l then some v else getValue? a l := by
|
||||
bif k == a && !containsKey k l then some v else getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_insertEntryIfNew,
|
||||
Bool.apply_cond (Option.map (fun (y : ((_ : α) × β)) => y.2))]
|
||||
|
||||
theorem containsKey_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} {v : β k} :
|
||||
containsKey a (insertEntryIfNew k v l) = ((a == k) || containsKey a l) := by
|
||||
containsKey a (insertEntryIfNew k v l) = ((k == a) || containsKey a l) := by
|
||||
simp only [containsKey_eq_isSome_getEntry?, getEntry?_insertEntryIfNew, Bool.apply_cond Option.isSome,
|
||||
Option.isSome_some, Bool.cond_true_left]
|
||||
cases h : a == k
|
||||
cases h : k == a
|
||||
· simp
|
||||
· rw [Bool.true_and, Bool.true_or, getEntry?_congr h, Bool.not_or_self]
|
||||
|
||||
@@ -1055,7 +1055,7 @@ theorem containsKey_insertEntryIfNew_self [BEq α] [EquivBEq α] {l : List ((a :
|
||||
|
||||
theorem containsKey_of_containsKey_insertEntryIfNew [BEq α] [PartialEquivBEq α]
|
||||
{l : List ((a : α) × β a)} {k a : α} {v : β k} (h₁ : containsKey a (insertEntryIfNew k v l))
|
||||
(h₂ : (a == k) = false) : containsKey a l := by
|
||||
(h₂ : (k == a) = false) : containsKey a l := by
|
||||
rwa [containsKey_insertEntryIfNew, h₂, Bool.false_or] at h₁
|
||||
|
||||
/--
|
||||
@@ -1064,7 +1064,7 @@ obligation in the statement of `getValueCast_insertEntryIfNew`.
|
||||
-/
|
||||
theorem containsKey_of_containsKey_insertEntryIfNew' [BEq α] [PartialEquivBEq α]
|
||||
{l : List ((a : α) × β a)} {k a : α} {v : β k} (h₁ : containsKey a (insertEntryIfNew k v l))
|
||||
(h₂ : ¬((a == k) ∧ containsKey k l = false)) : containsKey a l := by
|
||||
(h₂ : ¬((k == a) ∧ containsKey k l = false)) : containsKey a l := by
|
||||
rw [Decidable.not_and_iff_or_not, Bool.not_eq_true, Bool.not_eq_false] at h₂
|
||||
rcases h₂ with h₂|h₂
|
||||
· rwa [containsKey_insertEntryIfNew, h₂, Bool.false_or] at h₁
|
||||
@@ -1072,8 +1072,8 @@ theorem containsKey_of_containsKey_insertEntryIfNew' [BEq α] [PartialEquivBEq
|
||||
|
||||
theorem getValueCast_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} {h} : getValueCast a (insertEntryIfNew k v l) h =
|
||||
if h' : a == k ∧ containsKey k l = false then
|
||||
cast (congrArg β (eq_of_beq h'.1).symm) v
|
||||
if h' : k == a ∧ containsKey k l = false then
|
||||
cast (congrArg β (eq_of_beq h'.1)) v
|
||||
else
|
||||
getValueCast a l (containsKey_of_containsKey_insertEntryIfNew' h h') := by
|
||||
rw [← Option.some_inj, ← getValueCast?_eq_some_getValueCast, apply_dite Option.some,
|
||||
@@ -1082,7 +1082,7 @@ theorem getValueCast_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α
|
||||
|
||||
theorem getValue_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {v : β} {h} : getValue a (insertEntryIfNew k v l) h =
|
||||
if h' : a == k ∧ containsKey k l = false then v
|
||||
if h' : k == a ∧ containsKey k l = false then v
|
||||
else getValue a l (containsKey_of_containsKey_insertEntryIfNew' h h') := by
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, apply_dite Option.some,
|
||||
getValue?_insertEntryIfNew, cond_eq_if, ← dite_eq_ite]
|
||||
@@ -1090,25 +1090,25 @@ theorem getValue_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l
|
||||
|
||||
theorem getValueCast!_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} [Inhabited (β a)] : getValueCast! a (insertEntryIfNew k v l) =
|
||||
if h : a == k ∧ containsKey k l = false then cast (congrArg β (eq_of_beq h.1).symm) v
|
||||
if h : k == a ∧ containsKey k l = false then cast (congrArg β (eq_of_beq h.1)) v
|
||||
else getValueCast! a l := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_insertEntryIfNew, apply_dite Option.get!]
|
||||
|
||||
theorem getValue!_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k a : α} {v : β} : getValue! a (insertEntryIfNew k v l) =
|
||||
bif a == k && !containsKey k l then v else getValue! a l := by
|
||||
bif k == a && !containsKey k l then v else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_insertEntryIfNew, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValueCastD_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} {fallback : β a} : getValueCastD a (insertEntryIfNew k v l) fallback =
|
||||
if h : a == k ∧ containsKey k l = false then cast (congrArg β (eq_of_beq h.1).symm) v
|
||||
if h : k == a ∧ containsKey k l = false then cast (congrArg β (eq_of_beq h.1)) v
|
||||
else getValueCastD a l fallback := by
|
||||
simp [getValueCastD_eq_getValueCast?, getValueCast?_insertEntryIfNew,
|
||||
apply_dite (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem getValueD_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {fallback v : β} : getValueD a (insertEntryIfNew k v l) fallback =
|
||||
bif a == k && !containsKey k l then v else getValueD a l fallback := by
|
||||
bif k == a && !containsKey k l then v else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_insertEntryIfNew,
|
||||
Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
@@ -1124,55 +1124,55 @@ theorem length_le_length_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)}
|
||||
· simp
|
||||
|
||||
@[simp]
|
||||
theorem keys_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
keys (removeKey k l) = (keys l).erase k := by
|
||||
theorem keys_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
keys (eraseKey k l) = (keys l).erase k := by
|
||||
induction l using assoc_induction
|
||||
· rfl
|
||||
· next k' v' l ih =>
|
||||
simp only [removeKey_cons, keys_cons, List.erase_cons]
|
||||
simp only [eraseKey_cons, keys_cons, List.erase_cons]
|
||||
rw [BEq.comm]
|
||||
cases k' == k <;> simp [ih]
|
||||
cases k == k' <;> simp [ih]
|
||||
|
||||
theorem DistinctKeys.removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
DistinctKeys l → DistinctKeys (removeKey k l) := by
|
||||
theorem DistinctKeys.eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
DistinctKeys l → DistinctKeys (eraseKey k l) := by
|
||||
apply distinctKeys_of_sublist_keys (by simpa using erase_sublist _ _)
|
||||
|
||||
theorem getEntry?_removeKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(h : DistinctKeys l) : getEntry? k (removeKey k l) = none := by
|
||||
theorem getEntry?_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(h : DistinctKeys l) : getEntry? k (eraseKey k l) = none := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' t ih =>
|
||||
cases h' : k == k'
|
||||
· rw [removeKey_cons_of_false h', getEntry?_cons_of_false h']
|
||||
cases h' : k' == k
|
||||
· rw [eraseKey_cons_of_false h', getEntry?_cons_of_false h']
|
||||
exact ih h.tail
|
||||
· rw [removeKey_cons_of_beq h', ← Option.not_isSome_iff_eq_none, Bool.not_eq_true,
|
||||
← containsKey_eq_isSome_getEntry?, ← containsKey_congr (BEq.symm h')]
|
||||
· rw [eraseKey_cons_of_beq h', ← Option.not_isSome_iff_eq_none, Bool.not_eq_true,
|
||||
← containsKey_eq_isSome_getEntry?, ← containsKey_congr h']
|
||||
exact h.containsKey_eq_false
|
||||
|
||||
theorem getEntry?_removeKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) (hka : a == k) : getEntry? a (removeKey k l) = none := by
|
||||
rw [← getEntry?_congr (BEq.symm hka), getEntry?_removeKey_self hl]
|
||||
theorem getEntry?_eraseKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) (hka : k == a) : getEntry? a (eraseKey k l) = none := by
|
||||
rw [← getEntry?_congr hka, getEntry?_eraseKey_self hl]
|
||||
|
||||
theorem getEntry?_removeKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hka : (a == k) = false) : getEntry? a (removeKey k l) = getEntry? a l := by
|
||||
theorem getEntry?_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hka : (k == a) = false) : getEntry? a (eraseKey k l) = getEntry? a l := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' t ih =>
|
||||
cases h' : k == k'
|
||||
· rw [removeKey_cons_of_false h']
|
||||
cases h'' : a == k'
|
||||
cases h' : k' == k
|
||||
· rw [eraseKey_cons_of_false h']
|
||||
cases h'' : k' == a
|
||||
· rw [getEntry?_cons_of_false h'', ih, getEntry?_cons_of_false h'']
|
||||
· rw [getEntry?_cons_of_true h'', getEntry?_cons_of_true h'']
|
||||
· rw [removeKey_cons_of_beq h']
|
||||
have hx : (a == k') = false := BEq.neq_of_neq_of_beq hka h'
|
||||
· rw [eraseKey_cons_of_beq h']
|
||||
have hx : (k' == a) = false := BEq.neq_of_beq_of_neq h' hka
|
||||
rw [getEntry?_cons_of_false hx]
|
||||
|
||||
theorem getEntry?_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
theorem getEntry?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) :
|
||||
getEntry? a (removeKey k l) = bif a == k then none else getEntry? a l := by
|
||||
cases h : a == k
|
||||
· simp [getEntry?_removeKey_of_false h, h]
|
||||
· simp [getEntry?_removeKey_of_beq hl h, h]
|
||||
getEntry? a (eraseKey k l) = bif k == a then none else getEntry? a l := by
|
||||
cases h : k == a
|
||||
· simp [getEntry?_eraseKey_of_false h, h]
|
||||
· simp [getEntry?_eraseKey_of_beq hl h, h]
|
||||
|
||||
theorem keys_filterMap [BEq α] {l : List ((a : α) × β a)} {f : (a : α) → β a → Option (γ a)} :
|
||||
keys (l.filterMap fun p => (f p.1 p.2).map (⟨p.1, ·⟩)) =
|
||||
@@ -1208,110 +1208,110 @@ section
|
||||
|
||||
variable {β : Type v}
|
||||
|
||||
theorem getValue?_removeKey_self [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k : α}
|
||||
(h : DistinctKeys l) : getValue? k (removeKey k l) = none := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_removeKey_self h]
|
||||
theorem getValue?_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k : α}
|
||||
(h : DistinctKeys l) : getValue? k (eraseKey k l) = none := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_eraseKey_self h]
|
||||
|
||||
theorem getValue?_removeKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
(hl : DistinctKeys l) (hka : a == k) : getValue? a (removeKey k l) = none := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_removeKey_of_beq hl hka]
|
||||
theorem getValue?_eraseKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
(hl : DistinctKeys l) (hka : k == a) : getValue? a (eraseKey k l) = none := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_eraseKey_of_beq hl hka]
|
||||
|
||||
theorem getValue?_removeKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
(hka : (a == k) = false) : getValue? a (removeKey k l) = getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_removeKey_of_false hka]
|
||||
theorem getValue?_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
(hka : (k == a) = false) : getValue? a (eraseKey k l) = getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_eraseKey_of_false hka]
|
||||
|
||||
theorem getValue?_removeKey [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
theorem getValue?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
(hl : DistinctKeys l) :
|
||||
getValue? a (removeKey k l) = bif a == k then none else getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_removeKey hl, Bool.apply_cond (Option.map _)]
|
||||
getValue? a (eraseKey k l) = bif k == a then none else getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_eraseKey hl, Bool.apply_cond (Option.map _)]
|
||||
|
||||
end
|
||||
|
||||
theorem containsKey_removeKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(h : DistinctKeys l) : containsKey k (removeKey k l) = false := by
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_removeKey_self h]
|
||||
theorem containsKey_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(h : DistinctKeys l) : containsKey k (eraseKey k l) = false := by
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey_self h]
|
||||
|
||||
theorem containsKey_removeKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hl : DistinctKeys l) (hka : a == k) : containsKey a (removeKey k l) = false := by
|
||||
rw [containsKey_congr hka, containsKey_removeKey_self hl]
|
||||
theorem containsKey_eraseKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hl : DistinctKeys l) (hka : a == k) : containsKey a (eraseKey k l) = false := by
|
||||
rw [containsKey_congr hka, containsKey_eraseKey_self hl]
|
||||
|
||||
theorem containsKey_removeKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hka : (a == k) = false) : containsKey a (removeKey k l) = containsKey a l := by
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_removeKey_of_false hka]
|
||||
theorem containsKey_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hka : (k == a) = false) : containsKey a (eraseKey k l) = containsKey a l := by
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey_of_false hka]
|
||||
|
||||
theorem containsKey_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) : containsKey a (removeKey k l) = (!(a == k) && containsKey a l) := by
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_removeKey hl, Bool.apply_cond]
|
||||
theorem containsKey_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) : containsKey a (eraseKey k l) = (!(k == a) && containsKey a l) := by
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey hl, Bool.apply_cond]
|
||||
|
||||
theorem getValueCast?_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
theorem getValueCast?_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) :
|
||||
getValueCast? a (removeKey k l) = bif a == k then none else getValueCast? a l := by
|
||||
rw [getValueCast?_eq_getEntry?, Option.dmap_congr (getEntry?_removeKey hl)]
|
||||
rcases Bool.eq_false_or_eq_true (a == k) with h|h
|
||||
getValueCast? a (eraseKey k l) = bif k == a then none else getValueCast? a l := by
|
||||
rw [getValueCast?_eq_getEntry?, Option.dmap_congr (getEntry?_eraseKey hl)]
|
||||
rcases Bool.eq_false_or_eq_true (k == a) with h|h
|
||||
· rw [Option.dmap_congr (Bool.cond_pos h), Option.dmap_none, Bool.cond_pos h]
|
||||
· rw [Option.dmap_congr (Bool.cond_neg h), getValueCast?_eq_getEntry?]
|
||||
exact (Bool.cond_neg h).symm
|
||||
|
||||
theorem getValueCast?_removeKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(hl : DistinctKeys l) : getValueCast? k (removeKey k l) = none := by
|
||||
rw [getValueCast?_removeKey hl, Bool.cond_pos BEq.refl]
|
||||
theorem getValueCast?_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(hl : DistinctKeys l) : getValueCast? k (eraseKey k l) = none := by
|
||||
rw [getValueCast?_eraseKey hl, Bool.cond_pos BEq.refl]
|
||||
|
||||
theorem getValueCast!_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
theorem getValueCast!_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
[Inhabited (β a)] (hl : DistinctKeys l) :
|
||||
getValueCast! a (removeKey k l) = bif a == k then default else getValueCast! a l := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_removeKey hl, Bool.apply_cond Option.get!]
|
||||
getValueCast! a (eraseKey k l) = bif k == a then default else getValueCast! a l := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_eraseKey hl, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValueCast!_removeKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
[Inhabited (β k)] (hl : DistinctKeys l) : getValueCast! k (removeKey k l) = default := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_removeKey_self hl]
|
||||
theorem getValueCast!_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
[Inhabited (β k)] (hl : DistinctKeys l) : getValueCast! k (eraseKey k l) = default := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_eraseKey_self hl]
|
||||
|
||||
theorem getValueCastD_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{fallback : β a} (hl : DistinctKeys l) : getValueCastD a (removeKey k l) fallback =
|
||||
bif a == k then fallback else getValueCastD a l fallback := by
|
||||
simp [getValueCastD_eq_getValueCast?, getValueCast?_removeKey hl,
|
||||
theorem getValueCastD_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{fallback : β a} (hl : DistinctKeys l) : getValueCastD a (eraseKey k l) fallback =
|
||||
bif k == a then fallback else getValueCastD a l fallback := by
|
||||
simp [getValueCastD_eq_getValueCast?, getValueCast?_eraseKey hl,
|
||||
Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem getValueCastD_removeKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
theorem getValueCastD_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
{fallback : β k} (hl : DistinctKeys l) :
|
||||
getValueCastD k (removeKey k l) fallback = fallback := by
|
||||
simp [getValueCastD_eq_getValueCast?, getValueCast?_removeKey_self hl]
|
||||
getValueCastD k (eraseKey k l) fallback = fallback := by
|
||||
simp [getValueCastD_eq_getValueCast?, getValueCast?_eraseKey_self hl]
|
||||
|
||||
theorem getValue!_removeKey {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
theorem getValue!_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k a : α} (hl : DistinctKeys l) :
|
||||
getValue! a (removeKey k l) = bif a == k then default else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_removeKey hl, Bool.apply_cond Option.get!]
|
||||
getValue! a (eraseKey k l) = bif k == a then default else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_eraseKey hl, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValue!_removeKey_self {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
theorem getValue!_eraseKey_self {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k : α} (hl : DistinctKeys l) :
|
||||
getValue! k (removeKey k l) = default := by
|
||||
simp [getValue!_eq_getValue?, getValue?_removeKey_self hl]
|
||||
getValue! k (eraseKey k l) = default := by
|
||||
simp [getValue!_eq_getValue?, getValue?_eraseKey_self hl]
|
||||
|
||||
theorem getValueD_removeKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {fallback : β} (hl : DistinctKeys l) : getValueD a (removeKey k l) fallback =
|
||||
bif a == k then fallback else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_removeKey hl, Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
theorem getValueD_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {fallback : β} (hl : DistinctKeys l) : getValueD a (eraseKey k l) fallback =
|
||||
bif k == a then fallback else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_eraseKey hl, Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem getValueD_removeKey_self {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
theorem getValueD_eraseKey_self {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k : α} {fallback : β} (hl : DistinctKeys l) :
|
||||
getValueD k (removeKey k l) fallback = fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_removeKey_self hl]
|
||||
getValueD k (eraseKey k l) fallback = fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_eraseKey_self hl]
|
||||
|
||||
theorem containsKey_of_containsKey_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hl : DistinctKeys l) : containsKey a (removeKey k l) → containsKey a l := by
|
||||
simp [containsKey_removeKey hl]
|
||||
theorem containsKey_of_containsKey_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} (hl : DistinctKeys l) : containsKey a (eraseKey k l) → containsKey a l := by
|
||||
simp [containsKey_eraseKey hl]
|
||||
|
||||
theorem getValueCast_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {h}
|
||||
(hl : DistinctKeys l) : getValueCast a (removeKey k l) h =
|
||||
getValueCast a l (containsKey_of_containsKey_removeKey hl h) := by
|
||||
rw [containsKey_removeKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
|
||||
rw [← Option.some_inj, ← getValueCast?_eq_some_getValueCast, getValueCast?_removeKey hl, h.1,
|
||||
theorem getValueCast_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {h}
|
||||
(hl : DistinctKeys l) : getValueCast a (eraseKey k l) h =
|
||||
getValueCast a l (containsKey_of_containsKey_eraseKey hl h) := by
|
||||
rw [containsKey_eraseKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
|
||||
rw [← Option.some_inj, ← getValueCast?_eq_some_getValueCast, getValueCast?_eraseKey hl, h.1,
|
||||
cond_false, ← getValueCast?_eq_some_getValueCast]
|
||||
|
||||
theorem getValue_removeKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
theorem getValue_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {h} (hl : DistinctKeys l) :
|
||||
getValue a (removeKey k l) h = getValue a l (containsKey_of_containsKey_removeKey hl h) := by
|
||||
rw [containsKey_removeKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, getValue?_removeKey hl, h.1, cond_false,
|
||||
getValue a (eraseKey k l) h = getValue a l (containsKey_of_containsKey_eraseKey hl h) := by
|
||||
rw [containsKey_eraseKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, getValue?_eraseKey hl, h.1, cond_false,
|
||||
← getValue?_eq_some_getValue]
|
||||
|
||||
theorem getEntry?_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {a : α}
|
||||
@@ -1325,9 +1325,9 @@ theorem getEntry?_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α)
|
||||
rcases p with ⟨k₁, v₁⟩
|
||||
rcases p' with ⟨k₂, v₂⟩
|
||||
simp only [getEntry?_cons]
|
||||
cases h₂ : a == k₂ <;> cases h₁ : a == k₁ <;> try simp; done
|
||||
cases h₂ : k₂ == a <;> cases h₁ : k₁ == a <;> try simp; done
|
||||
simp only [distinctKeys_cons_iff, containsKey_cons, Bool.or_eq_false_iff] at hl
|
||||
exact ((Bool.eq_false_iff.1 hl.2.1).elim (BEq.trans (BEq.symm h₁) h₂)).elim
|
||||
exact ((Bool.eq_false_iff.1 hl.2.1).elim (BEq.trans h₂ (BEq.symm h₁))).elim
|
||||
· next l₁ l₂ l₃ hl₁₂ _ ih₁ ih₂ => exact (ih₁ hl).trans (ih₂ (hl.perm (hl₁₂.symm)))
|
||||
|
||||
theorem containsKey_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {k : α}
|
||||
@@ -1392,7 +1392,7 @@ theorem perm_cons_getEntry [BEq α] {l : List ((a : α) × β a)} {a : α} (h :
|
||||
· simp at h
|
||||
· next k' v' t ih =>
|
||||
simp only [containsKey_cons, Bool.or_eq_true] at h
|
||||
cases hk : a == k'
|
||||
cases hk : k' == a
|
||||
· obtain ⟨l', hl'⟩ := ih (h.resolve_left (Bool.not_eq_true _ ▸ hk))
|
||||
rw [getEntry_cons_of_false hk]
|
||||
exact ⟨⟨k', v'⟩ :: l', (hl'.cons _).trans (Perm.swap _ _ (Perm.refl _))⟩
|
||||
@@ -1414,9 +1414,9 @@ theorem getEntry?_ext [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} (h
|
||||
suffices Perm t l'' from (this.cons _).trans hl''.symm
|
||||
apply ih hl.tail (hl'.perm hl''.symm).tail
|
||||
intro k'
|
||||
cases hk' : k' == k
|
||||
cases hk' : k == k'
|
||||
· simpa only [getEntry?_of_perm hl' hl'', getEntry?_cons_of_false hk'] using h k'
|
||||
· rw [getEntry?_congr hk', getEntry?_congr hk', getEntry?_eq_none.2 hl.containsKey_eq_false,
|
||||
· rw [← getEntry?_congr hk', ← getEntry?_congr hk', getEntry?_eq_none.2 hl.containsKey_eq_false,
|
||||
getEntry?_eq_none.2 (hl'.perm hl''.symm).containsKey_eq_false]
|
||||
|
||||
theorem replaceEntry_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} {k : α} {v : β k}
|
||||
@@ -1429,17 +1429,17 @@ theorem insertEntry_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β
|
||||
apply getEntry?_ext hl.insertEntry (hl.perm h.symm).insertEntry
|
||||
simp [getEntry?_insertEntry, getEntry?_of_perm hl h]
|
||||
|
||||
theorem removeKey_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} {k : α}
|
||||
(hl : DistinctKeys l) (h : Perm l l') : Perm (removeKey k l) (removeKey k l') := by
|
||||
apply getEntry?_ext hl.removeKey (hl.perm h.symm).removeKey
|
||||
simp [getEntry?_removeKey hl, getEntry?_removeKey (hl.perm h.symm), getEntry?_of_perm hl h]
|
||||
theorem eraseKey_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} {k : α}
|
||||
(hl : DistinctKeys l) (h : Perm l l') : Perm (eraseKey k l) (eraseKey k l') := by
|
||||
apply getEntry?_ext hl.eraseKey (hl.perm h.symm).eraseKey
|
||||
simp [getEntry?_eraseKey hl, getEntry?_eraseKey (hl.perm h.symm), getEntry?_of_perm hl h]
|
||||
|
||||
@[simp]
|
||||
theorem getEntry?_append [BEq α] {l l' : List ((a : α) × β a)} {a : α} :
|
||||
getEntry? a (l ++ l') = (getEntry? a l).or (getEntry? a l') := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' t ih => cases h : a == k' <;> simp_all [getEntry?_cons]
|
||||
· next k' v' t ih => cases h : k' == a <;> simp_all [getEntry?_cons]
|
||||
|
||||
theorem getEntry?_append_of_containsKey_eq_false [BEq α] {l l' : List ((a : α) × β a)} {a : α}
|
||||
(h : containsKey a l' = false) : getEntry? a (l ++ l') = getEntry? a l := by
|
||||
@@ -1501,7 +1501,7 @@ theorem replaceEntry_append_of_containsKey_left [BEq α] {l l' : List ((a : α)
|
||||
· simp at h
|
||||
· next k' v' t ih =>
|
||||
simp only [containsKey_cons, Bool.or_eq_true] at h
|
||||
cases h' : k == k'
|
||||
cases h' : k' == k
|
||||
· simpa [replaceEntry_cons, h'] using ih (h.resolve_left (Bool.not_eq_true _ ▸ h'))
|
||||
· simp [replaceEntry_cons, h']
|
||||
|
||||
@@ -1529,13 +1529,13 @@ theorem insertEntry_append_of_not_contains_right [BEq α] {l l' : List ((a : α)
|
||||
· simp [insertEntry, containsKey_append, h, h']
|
||||
· simp [insertEntry, containsKey_append, h, h', replaceEntry_append_of_containsKey_left h]
|
||||
|
||||
theorem removeKey_append_of_containsKey_right_eq_false [BEq α] {l l' : List ((a : α) × β a)} {k : α}
|
||||
(h : containsKey k l' = false) : removeKey k (l ++ l') = removeKey k l ++ l' := by
|
||||
theorem eraseKey_append_of_containsKey_right_eq_false [BEq α] {l l' : List ((a : α) × β a)} {k : α}
|
||||
(h : containsKey k l' = false) : eraseKey k (l ++ l') = eraseKey k l ++ l' := by
|
||||
induction l using assoc_induction
|
||||
· simp [removeKey_of_containsKey_eq_false h]
|
||||
· simp [eraseKey_of_containsKey_eq_false h]
|
||||
· next k' v' t ih =>
|
||||
rw [List.cons_append, removeKey_cons, removeKey_cons]
|
||||
cases k == k'
|
||||
rw [List.cons_append, eraseKey_cons, eraseKey_cons]
|
||||
cases k' == k
|
||||
· rw [cond_false, cond_false, ih, List.cons_append]
|
||||
· rw [cond_true, cond_true]
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ theorem updateAllBuckets [BEq α] [Hashable α] [LawfulHashable α] {m : Array (
|
||||
simp only [Array.getElem_map, Array.size_map]
|
||||
refine ⟨fun h p hp => ?_⟩
|
||||
rcases containsKey_eq_true_iff_exists_mem.1 (hf _ _ hp) with ⟨q, hq₁, hq₂⟩
|
||||
rw [hash_eq hq₂, (hm.hashes_to _ _).hash_self _ _ hq₁]
|
||||
rw [← hash_eq hq₂, (hm.hashes_to _ _).hash_self _ _ hq₁]
|
||||
|
||||
end IsHashSelf
|
||||
|
||||
@@ -286,12 +286,12 @@ def insertIfNewₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a)
|
||||
if m.containsₘ a then m else Raw₀.expandIfNecessary (m.consₘ a b)
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def removeₘaux [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||||
⟨⟨m.1.size - 1, updateBucket m.1.buckets m.2 a (fun l => l.remove a)⟩, by simpa using m.2⟩
|
||||
def eraseₘaux [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||||
⟨⟨m.1.size - 1, updateBucket m.1.buckets m.2 a (fun l => l.erase a)⟩, by simpa using m.2⟩
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def removeₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||||
if m.containsₘ a then m.removeₘaux a else m
|
||||
def eraseₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||||
if m.containsₘ a then m.eraseₘaux a else m
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def filterMapₘ (m : Raw₀ α β) (f : (a : α) → β a → Option (δ a)) : Raw₀ α δ :=
|
||||
@@ -405,12 +405,12 @@ theorem getThenInsertIfNew?_eq_get?ₘ [BEq α] [Hashable α] [LawfulBEq α] (m
|
||||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||||
split <;> simp_all
|
||||
|
||||
theorem remove_eq_removeₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
|
||||
m.remove a = m.removeₘ a := by
|
||||
rw [remove, removeₘ, containsₘ, bucket]
|
||||
theorem erase_eq_eraseₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
|
||||
m.erase a = m.eraseₘ a := by
|
||||
rw [erase, eraseₘ, containsₘ, bucket]
|
||||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||||
split
|
||||
· simp only [removeₘaux, Subtype.mk.injEq, Raw.mk.injEq, true_and]
|
||||
· simp only [eraseₘaux, Subtype.mk.injEq, Raw.mk.injEq, true_and]
|
||||
rw [Array.set_set, updateBucket]
|
||||
simp only [Array.uset, Array.ugetElem_eq_getElem]
|
||||
· rfl
|
||||
|
||||
@@ -135,13 +135,13 @@ theorem get!_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a :
|
||||
m.val.get! a = m.get! a := by
|
||||
simp [Raw.get!, m.2]
|
||||
|
||||
theorem remove_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
|
||||
m.remove a = Raw₀.remove ⟨m, h.size_buckets_pos⟩ a := by
|
||||
simp [Raw.remove, h.size_buckets_pos]
|
||||
theorem erase_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
|
||||
m.erase a = Raw₀.erase ⟨m, h.size_buckets_pos⟩ a := by
|
||||
simp [Raw.erase, h.size_buckets_pos]
|
||||
|
||||
theorem remove_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
|
||||
m.val.remove a = m.remove a := by
|
||||
simp [Raw.remove, m.2]
|
||||
theorem erase_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
|
||||
m.val.erase a = m.erase a := by
|
||||
simp [Raw.erase, m.2]
|
||||
|
||||
theorem filterMap_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF)
|
||||
{f : (a : α) → β a → Option (δ a)} : m.filterMap f =
|
||||
|
||||
@@ -60,7 +60,7 @@ variable (m : Raw₀ α β) (h : m.1.WF)
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
scoped macro "wf_trivial" : tactic => `(tactic|
|
||||
repeat (first
|
||||
| apply Raw₀.wfImp_insert | apply Raw₀.wfImp_insertIfNew | apply Raw₀.wfImp_remove
|
||||
| apply Raw₀.wfImp_insert | apply Raw₀.wfImp_insertIfNew | apply Raw₀.wfImp_erase
|
||||
| apply Raw.WF.out | assumption | apply Raw₀.wfImp_empty | apply Raw.WFImp.distinct
|
||||
| apply Raw.WF.empty₀))
|
||||
|
||||
@@ -76,7 +76,7 @@ private def queryNames : Array Name :=
|
||||
``Const.get!_eq_getValue!, ``Const.getD_eq_getValueD]
|
||||
|
||||
private def modifyNames : Array Name :=
|
||||
#[``toListModel_insert, ``toListModel_remove, ``toListModel_insertIfNew]
|
||||
#[``toListModel_insert, ``toListModel_erase, ``toListModel_insertIfNew]
|
||||
|
||||
private def congrNames : MacroM (Array (TSyntax `term)) := do
|
||||
return #[← `(Std.DHashMap.Internal.List.Perm.isEmpty_eq), ← `(containsKey_of_perm),
|
||||
@@ -127,11 +127,11 @@ theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashab
|
||||
simp_to_model using List.isEmpty_eq_false_iff_exists_containsKey
|
||||
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insert k v).contains a = ((a == k) || m.contains a) := by
|
||||
(m.insert k v).contains a = ((k == a) || m.contains a) := by
|
||||
simp_to_model using List.containsKey_insertEntry
|
||||
|
||||
theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insert k v).contains a → (a == k) = false → m.contains a := by
|
||||
(m.insert k v).contains a → (k == a) = false → m.contains a := by
|
||||
simp_to_model using List.containsKey_of_containsKey_insertEntry
|
||||
|
||||
theorem contains_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
@@ -153,28 +153,28 @@ theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
|
||||
simp_to_model using List.length_le_length_insertEntry
|
||||
|
||||
@[simp]
|
||||
theorem remove_empty {k : α} {c : Nat} : (empty c : Raw₀ α β).remove k = empty c := by
|
||||
simp [remove, empty]
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw₀ α β).erase k = empty c := by
|
||||
simp [erase, empty]
|
||||
|
||||
theorem isEmpty_remove [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.remove k).1.isEmpty = (m.1.isEmpty || (m.1.size == 1 && m.contains k)) := by
|
||||
simp_to_model using List.isEmpty_removeKey
|
||||
theorem isEmpty_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).1.isEmpty = (m.1.isEmpty || (m.1.size == 1 && m.contains k)) := by
|
||||
simp_to_model using List.isEmpty_eraseKey
|
||||
|
||||
theorem contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.remove k).contains a = (!(a == k) && m.contains a) := by
|
||||
simp_to_model using List.containsKey_removeKey
|
||||
theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k).contains a = (!(k == a) && m.contains a) := by
|
||||
simp_to_model using List.containsKey_eraseKey
|
||||
|
||||
theorem contains_of_contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.remove k).contains a → m.contains a := by
|
||||
simp_to_model using List.containsKey_of_containsKey_removeKey
|
||||
theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k).contains a → m.contains a := by
|
||||
simp_to_model using List.containsKey_of_containsKey_eraseKey
|
||||
|
||||
theorem size_remove [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.remove k).1.size = bif m.contains k then m.1.size - 1 else m.1.size := by
|
||||
simp_to_model using List.length_removeKey
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).1.size = bif m.contains k then m.1.size - 1 else m.1.size := by
|
||||
simp_to_model using List.length_eraseKey
|
||||
|
||||
theorem size_remove_le [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.remove k).1.size ≤ m.1.size := by
|
||||
simp_to_model using List.length_removeKey_le
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).1.size ≤ m.1.size := by
|
||||
simp_to_model using List.length_eraseKey_le
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k := by
|
||||
@@ -202,7 +202,7 @@ theorem get?_of_isEmpty [LawfulBEq α] {a : α} : m.1.isEmpty = true → m.get?
|
||||
simp_to_model; empty
|
||||
|
||||
theorem get?_insert [LawfulBEq α] {a k : α} {v : β k} : (m.insert k v).get? a =
|
||||
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else m.get? a := by
|
||||
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else m.get? a := by
|
||||
simp_to_model using List.getValueCast?_insertEntry
|
||||
|
||||
theorem get?_insert_self [LawfulBEq α] {k : α} {v : β k} : (m.insert k v).get? k = some v := by
|
||||
@@ -214,12 +214,12 @@ theorem contains_eq_isSome_get? [LawfulBEq α] {a : α} : m.contains a = (m.get?
|
||||
theorem get?_eq_none [LawfulBEq α] {a : α} : m.contains a = false → m.get? a = none := by
|
||||
simp_to_model using List.getValueCast?_eq_none
|
||||
|
||||
theorem get?_remove [LawfulBEq α] {k a : α} :
|
||||
(m.remove k).get? a = bif a == k then none else m.get? a := by
|
||||
simp_to_model using List.getValueCast?_removeKey
|
||||
theorem get?_erase [LawfulBEq α] {k a : α} :
|
||||
(m.erase k).get? a = bif k == a then none else m.get? a := by
|
||||
simp_to_model using List.getValueCast?_eraseKey
|
||||
|
||||
theorem get?_remove_self [LawfulBEq α] {k : α} : (m.remove k).get? k = none := by
|
||||
simp_to_model using List.getValueCast?_removeKey_self
|
||||
theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none := by
|
||||
simp_to_model using List.getValueCast?_eraseKey_self
|
||||
|
||||
namespace Const
|
||||
|
||||
@@ -234,7 +234,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
simp_to_model; empty
|
||||
|
||||
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insert k v) a = bif a == k then some v else get? m a := by
|
||||
get? (m.insert k v) a = bif k == a then some v else get? m a := by
|
||||
simp_to_model using List.getValue?_insertEntry
|
||||
|
||||
theorem get?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
@@ -249,13 +249,13 @@ theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.contains a = false → get? m a = none := by
|
||||
simp_to_model using List.getValue?_eq_none.2
|
||||
|
||||
theorem get?_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.remove k) a = bif a == k then none else get? m a := by
|
||||
simp_to_model using List.getValue?_removeKey
|
||||
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.erase k) a = bif k == a then none else get? m a := by
|
||||
simp_to_model using List.getValue?_eraseKey
|
||||
|
||||
theorem get?_remove_self [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
get? (m.remove k) k = none := by
|
||||
simp_to_model using List.getValue?_removeKey_self
|
||||
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
get? (m.erase k) k = none := by
|
||||
simp_to_model using List.getValue?_eraseKey_self
|
||||
|
||||
theorem get?_eq_get? [LawfulBEq α] {a : α} : get? m a = m.get? a := by
|
||||
simp_to_model using List.getValue?_eq_getValueCast?
|
||||
@@ -268,8 +268,8 @@ end Const
|
||||
|
||||
theorem get_insert [LawfulBEq α] {k a : α} {v : β k} {h₁} :
|
||||
(m.insert k v).get a h₁ =
|
||||
if h₂ : a == k then
|
||||
cast (congrArg β (eq_of_beq h₂).symm) v
|
||||
if h₂ : k == a then
|
||||
cast (congrArg β (eq_of_beq h₂)) v
|
||||
else
|
||||
m.get a (contains_of_contains_insert _ h h₁ (Bool.eq_false_iff.2 h₂)) := by
|
||||
simp_to_model using List.getValueCast_insertEntry
|
||||
@@ -279,9 +279,9 @@ theorem get_insert_self [LawfulBEq α] {k : α} {v : β k} :
|
||||
simp_to_model using List.getValueCast_insertEntry_self
|
||||
|
||||
@[simp]
|
||||
theorem get_remove [LawfulBEq α] {k a : α} {h'} :
|
||||
(m.remove k).get a h' = m.get a (contains_of_contains_remove _ h h') := by
|
||||
simp_to_model using List.getValueCast_removeKey
|
||||
theorem get_erase [LawfulBEq α] {k a : α} {h'} :
|
||||
(m.erase k).get a h' = m.get a (contains_of_contains_erase _ h h') := by
|
||||
simp_to_model using List.getValueCast_eraseKey
|
||||
|
||||
theorem get?_eq_some_get [LawfulBEq α] {a : α} {h} : m.get? a = some (m.get a h) := by
|
||||
simp_to_model using List.getValueCast?_eq_some_getValueCast
|
||||
@@ -292,7 +292,7 @@ variable {β : Type v} (m : Raw₀ α (fun _ => β)) (h : m.1.WF)
|
||||
|
||||
theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
get (m.insert k v) a h₁ =
|
||||
if h₂ : a == k then v
|
||||
if h₂ : k == a then v
|
||||
else get m a (contains_of_contains_insert _ h h₁ (Bool.eq_false_iff.2 h₂)) := by
|
||||
simp_to_model using List.getValue_insertEntry
|
||||
|
||||
@@ -301,9 +301,9 @@ theorem get_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
simp_to_model using List.getValue_insertEntry_self
|
||||
|
||||
@[simp]
|
||||
theorem get_remove [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
|
||||
get (m.remove k) a h' = get m a (contains_of_contains_remove _ h h') := by
|
||||
simp_to_model using List.getValue_removeKey
|
||||
theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
|
||||
get (m.erase k) a h' = get m a (contains_of_contains_erase _ h h') := by
|
||||
simp_to_model using List.getValue_eraseKey
|
||||
|
||||
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} {h} :
|
||||
get? m a = some (get m a h) := by
|
||||
@@ -328,7 +328,7 @@ theorem get!_of_isEmpty [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
|
||||
theorem get!_insert [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
|
||||
(m.insert k v).get! a =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.get! a := by
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.get! a := by
|
||||
simp_to_model using List.getValueCast!_insertEntry
|
||||
|
||||
theorem get!_insert_self [LawfulBEq α] {a : α} [Inhabited (β a)] {b : β a} :
|
||||
@@ -339,13 +339,13 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
m.contains a = false → m.get! a = default := by
|
||||
simp_to_model using List.getValueCast!_eq_default
|
||||
|
||||
theorem get!_remove [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.remove k).get! a = bif a == k then default else m.get! a := by
|
||||
simp_to_model using List.getValueCast!_removeKey
|
||||
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.erase k).get! a = bif k == a then default else m.get! a := by
|
||||
simp_to_model using List.getValueCast!_eraseKey
|
||||
|
||||
theorem get!_remove_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
|
||||
(m.remove k).get! k = default := by
|
||||
simp_to_model using List.getValueCast!_removeKey_self
|
||||
theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
|
||||
(m.erase k).get! k = default := by
|
||||
simp_to_model using List.getValueCast!_eraseKey_self
|
||||
|
||||
theorem get?_eq_some_get! [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
m.contains a = true → m.get? a = some (m.get! a) := by
|
||||
@@ -372,7 +372,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
simp_to_model; empty
|
||||
|
||||
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insert k v) a = bif a == k then v else get! m a := by
|
||||
get! (m.insert k v) a = bif k == a then v else get! m a := by
|
||||
simp_to_model using List.getValue!_insertEntry
|
||||
|
||||
theorem get!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} {v : β} :
|
||||
@@ -383,13 +383,13 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
m.contains a = false → get! m a = default := by
|
||||
simp_to_model using List.getValue!_eq_default
|
||||
|
||||
theorem get!_remove [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.remove k) a = bif a == k then default else get! m a := by
|
||||
simp_to_model using List.getValue!_removeKey
|
||||
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.erase k) a = bif k == a then default else get! m a := by
|
||||
simp_to_model using List.getValue!_eraseKey
|
||||
|
||||
theorem get!_remove_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
|
||||
get! (m.remove k) k = default := by
|
||||
simp_to_model using List.getValue!_removeKey_self
|
||||
theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
|
||||
get! (m.erase k) k = default := by
|
||||
simp_to_model using List.getValue!_eraseKey_self
|
||||
|
||||
theorem get?_eq_some_get! [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} :
|
||||
m.contains a = true → get? m a = some (get! m a) := by
|
||||
@@ -423,7 +423,7 @@ theorem getD_of_isEmpty [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
|
||||
theorem getD_insert [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
|
||||
(m.insert k v).getD a fallback =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.getD a fallback := by
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.getD a fallback := by
|
||||
simp_to_model using List.getValueCastD_insertEntry
|
||||
|
||||
theorem getD_insert_self [LawfulBEq α] {a : α} {fallback b : β a} :
|
||||
@@ -434,13 +434,13 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
m.contains a = false → m.getD a fallback = fallback := by
|
||||
simp_to_model using List.getValueCastD_eq_fallback
|
||||
|
||||
theorem getD_remove [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.remove k).getD a fallback = bif a == k then fallback else m.getD a fallback := by
|
||||
simp_to_model using List.getValueCastD_removeKey
|
||||
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback := by
|
||||
simp_to_model using List.getValueCastD_eraseKey
|
||||
|
||||
theorem getD_remove_self [LawfulBEq α] {k : α} {fallback : β k} :
|
||||
(m.remove k).getD k fallback = fallback := by
|
||||
simp_to_model using List.getValueCastD_removeKey_self
|
||||
theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} :
|
||||
(m.erase k).getD k fallback = fallback := by
|
||||
simp_to_model using List.getValueCastD_eraseKey_self
|
||||
|
||||
theorem get?_eq_some_getD [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
m.contains a = true → m.get? a = some (m.getD a fallback) := by
|
||||
@@ -471,7 +471,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
simp_to_model; empty
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insert k v) a fallback = bif a == k then v else getD m a fallback := by
|
||||
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback := by
|
||||
simp_to_model using List.getValueD_insertEntry
|
||||
|
||||
theorem getD_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback v : β} :
|
||||
@@ -482,13 +482,13 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
m.contains a = false → getD m a fallback = fallback := by
|
||||
simp_to_model using List.getValueD_eq_fallback
|
||||
|
||||
theorem getD_remove [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.remove k) a fallback = bif a == k then fallback else getD m a fallback := by
|
||||
simp_to_model using List.getValueD_removeKey
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback := by
|
||||
simp_to_model using List.getValueD_eraseKey
|
||||
|
||||
theorem getD_remove_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
|
||||
getD (m.remove k) k fallback = fallback := by
|
||||
simp_to_model using List.getValueD_removeKey_self
|
||||
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
|
||||
getD (m.erase k) k fallback = fallback := by
|
||||
simp_to_model using List.getValueD_eraseKey_self
|
||||
|
||||
theorem get?_eq_some_getD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} :
|
||||
m.contains a = true → get? m a = some (getD m a fallback) := by
|
||||
@@ -521,7 +521,7 @@ theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
|
||||
simp_to_model using List.isEmpty_insertEntryIfNew
|
||||
|
||||
theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a = (a == k || m.contains a) := by
|
||||
(m.insertIfNew k v).contains a = (k == a || m.contains a) := by
|
||||
simp_to_model using List.containsKey_insertEntryIfNew
|
||||
|
||||
theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
@@ -529,13 +529,13 @@ theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v
|
||||
simp_to_model using List.containsKey_insertEntryIfNew_self
|
||||
|
||||
theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a → (a == k) = false → m.contains a := by
|
||||
(m.insertIfNew k v).contains a → (k == a) = false → m.contains a := by
|
||||
simp_to_model using List.containsKey_of_containsKey_insertEntryIfNew
|
||||
|
||||
/-- This is a restatement of `contains_insertIfNew` that is written to exactly match the proof
|
||||
obligation in the statement of `get_insertIfNew`. -/
|
||||
theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a → ¬((a == k) ∧ m.contains k = false) → m.contains a := by
|
||||
(m.insertIfNew k v).contains a → ¬((k == a) ∧ m.contains k = false) → m.contains a := by
|
||||
simp_to_model using List.containsKey_of_containsKey_insertEntryIfNew'
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
@@ -548,25 +548,25 @@ theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v :
|
||||
|
||||
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).get? a =
|
||||
if h : a == k ∧ m.contains k = false then some (cast (congrArg β (eq_of_beq h.1).symm) v)
|
||||
if h : k == a ∧ m.contains k = false then some (cast (congrArg β (eq_of_beq h.1)) v)
|
||||
else m.get? a := by
|
||||
simp_to_model using List.getValueCast?_insertEntryIfNew
|
||||
|
||||
theorem get_insertIfNew [LawfulBEq α] {k a : α} {v : β k} {h₁} :
|
||||
(m.insertIfNew k v).get a h₁ =
|
||||
if h₂ : a == k ∧ m.contains k = false then cast (congrArg β (eq_of_beq h₂.1).symm) v
|
||||
if h₂ : k == a ∧ m.contains k = false then cast (congrArg β (eq_of_beq h₂.1)) v
|
||||
else m.get a (contains_of_contains_insertIfNew' _ h h₁ h₂) := by
|
||||
simp_to_model using List.getValueCast_insertEntryIfNew
|
||||
|
||||
theorem get!_insertIfNew [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
|
||||
(m.insertIfNew k v).get! a =
|
||||
if h : a == k ∧ m.contains k = false then cast (congrArg β (eq_of_beq h.1).symm) v
|
||||
if h : k == a ∧ m.contains k = false then cast (congrArg β (eq_of_beq h.1)) v
|
||||
else m.get! a := by
|
||||
simp_to_model using List.getValueCast!_insertEntryIfNew
|
||||
|
||||
theorem getD_insertIfNew [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
|
||||
(m.insertIfNew k v).getD a fallback =
|
||||
if h : a == k ∧ m.contains k = false then cast (congrArg β (eq_of_beq h.1).symm) v
|
||||
if h : k == a ∧ m.contains k = false then cast (congrArg β (eq_of_beq h.1)) v
|
||||
else m.getD a fallback := by
|
||||
simp_to_model using List.getValueCastD_insertEntryIfNew
|
||||
|
||||
@@ -575,22 +575,22 @@ namespace Const
|
||||
variable {β : Type v} (m : Raw₀ α (fun _ => β)) (h : m.1.WF)
|
||||
|
||||
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insertIfNew k v) a = bif a == k && !m.contains k then some v else get? m a := by
|
||||
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a := by
|
||||
simp_to_model using List.getValue?_insertEntryIfNew
|
||||
|
||||
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
get (m.insertIfNew k v) a h₁ =
|
||||
if h₂ : a == k ∧ m.contains k = false then v
|
||||
if h₂ : k == a ∧ m.contains k = false then v
|
||||
else get m a (contains_of_contains_insertIfNew' _ h h₁ h₂) := by
|
||||
simp_to_model using List.getValue_insertEntryIfNew
|
||||
|
||||
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insertIfNew k v) a = bif a == k && !m.contains k then v else get! m a := by
|
||||
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a := by
|
||||
simp_to_model using List.getValue!_insertEntryIfNew
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insertIfNew k v) a fallback =
|
||||
bif a == k && !m.contains k then v else getD m a fallback := by
|
||||
bif k == a && !m.contains k then v else getD m a fallback := by
|
||||
simp_to_model using List.getValueD_insertEntryIfNew
|
||||
|
||||
end Const
|
||||
|
||||
@@ -453,63 +453,63 @@ theorem Const.wfImp_getThenInsertIfNew? {β : Type v} [BEq α] [Hashable α] [Eq
|
||||
rw [getThenInsertIfNew?_eq_insertIfNewₘ]
|
||||
exact wfImp_insertIfNewₘ h
|
||||
|
||||
/-! # `removeₘ` -/
|
||||
/-! # `eraseₘ` -/
|
||||
|
||||
theorem toListModel_removeₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
|
||||
theorem toListModel_eraseₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
|
||||
(a : α) (h : Raw.WFImp m.1) :
|
||||
Perm (toListModel (m.removeₘaux a).1.buckets) (removeKey a (toListModel m.1.buckets)) :=
|
||||
toListModel_updateBucket h AssocList.toList_remove List.removeKey_of_perm
|
||||
List.removeKey_append_of_containsKey_right_eq_false
|
||||
Perm (toListModel (m.eraseₘaux a).1.buckets) (eraseKey a (toListModel m.1.buckets)) :=
|
||||
toListModel_updateBucket h AssocList.toList_erase List.eraseKey_of_perm
|
||||
List.eraseKey_append_of_containsKey_right_eq_false
|
||||
|
||||
theorem isHashSelf_removeₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
|
||||
(a : α) (h : Raw.WFImp m.1) : IsHashSelf (m.removeₘaux a).1.buckets := by
|
||||
theorem isHashSelf_eraseₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
|
||||
(a : α) (h : Raw.WFImp m.1) : IsHashSelf (m.eraseₘaux a).1.buckets := by
|
||||
apply h.buckets_hash_self.updateBucket (fun l p hp => ?_)
|
||||
rw [AssocList.toList_remove] at hp
|
||||
exact Or.inl (containsKey_of_mem ((sublist_removeKey.mem hp)))
|
||||
rw [AssocList.toList_erase] at hp
|
||||
exact Or.inl (containsKey_of_mem ((sublist_eraseKey.mem hp)))
|
||||
|
||||
theorem wfImp_removeₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β) (a : α)
|
||||
(h : Raw.WFImp m.1) (h' : m.containsₘ a = true) : Raw.WFImp (m.removeₘaux a).1 where
|
||||
buckets_hash_self := isHashSelf_removeₘaux m a h
|
||||
theorem wfImp_eraseₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β) (a : α)
|
||||
(h : Raw.WFImp m.1) (h' : m.containsₘ a = true) : Raw.WFImp (m.eraseₘaux a).1 where
|
||||
buckets_hash_self := isHashSelf_eraseₘaux m a h
|
||||
size_eq := by
|
||||
rw [(toListModel_removeₘaux m a h).length_eq, removeₘaux, length_removeKey,
|
||||
rw [(toListModel_eraseₘaux m a h).length_eq, eraseₘaux, length_eraseKey,
|
||||
← containsₘ_eq_containsKey h, h', cond_true, h.size_eq]
|
||||
distinct := h.distinct.removeKey.perm (toListModel_removeₘaux m a h)
|
||||
distinct := h.distinct.eraseKey.perm (toListModel_eraseₘaux m a h)
|
||||
|
||||
theorem toListModel_perm_removeKey_of_containsₘ_eq_false [BEq α] [Hashable α] [EquivBEq α]
|
||||
theorem toListModel_perm_eraseKey_of_containsₘ_eq_false [BEq α] [Hashable α] [EquivBEq α]
|
||||
[LawfulHashable α] (m : Raw₀ α β) (a : α) (h : Raw.WFImp m.1) (h' : m.containsₘ a = false) :
|
||||
Perm (toListModel m.1.buckets) (removeKey a (toListModel m.1.buckets)) := by
|
||||
rw [removeKey_of_containsKey_eq_false]
|
||||
Perm (toListModel m.1.buckets) (eraseKey a (toListModel m.1.buckets)) := by
|
||||
rw [eraseKey_of_containsKey_eq_false]
|
||||
· exact Perm.refl _
|
||||
· rw [← containsₘ_eq_containsKey h, h']
|
||||
|
||||
theorem toListModel_removeₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
|
||||
theorem toListModel_eraseₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
|
||||
{a : α} (h : Raw.WFImp m.1) :
|
||||
Perm (toListModel (m.removeₘ a).1.buckets) (removeKey a (toListModel m.1.buckets)) := by
|
||||
rw [removeₘ]
|
||||
Perm (toListModel (m.eraseₘ a).1.buckets) (eraseKey a (toListModel m.1.buckets)) := by
|
||||
rw [eraseₘ]
|
||||
split
|
||||
· exact toListModel_removeₘaux m a h
|
||||
· exact toListModel_eraseₘaux m a h
|
||||
· next h' =>
|
||||
exact toListModel_perm_removeKey_of_containsₘ_eq_false _ _ h (eq_false_of_ne_true h')
|
||||
exact toListModel_perm_eraseKey_of_containsₘ_eq_false _ _ h (eq_false_of_ne_true h')
|
||||
|
||||
theorem wfImp_removeₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
|
||||
(h : Raw.WFImp m.1) : Raw.WFImp (m.removeₘ a).1 := by
|
||||
rw [removeₘ]
|
||||
theorem wfImp_eraseₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
|
||||
(h : Raw.WFImp m.1) : Raw.WFImp (m.eraseₘ a).1 := by
|
||||
rw [eraseₘ]
|
||||
split
|
||||
· next h' => exact wfImp_removeₘaux m a h h'
|
||||
· next h' => exact wfImp_eraseₘaux m a h h'
|
||||
· exact h
|
||||
|
||||
/-! # `remove` -/
|
||||
/-! # `erase` -/
|
||||
|
||||
theorem toListModel_remove [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
|
||||
theorem toListModel_erase [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
|
||||
{a : α} (h : Raw.WFImp m.1) :
|
||||
Perm (toListModel (m.remove a).1.buckets) (removeKey a (toListModel m.1.buckets)) := by
|
||||
rw [remove_eq_removeₘ]
|
||||
exact toListModel_removeₘ h
|
||||
Perm (toListModel (m.erase a).1.buckets) (eraseKey a (toListModel m.1.buckets)) := by
|
||||
rw [erase_eq_eraseₘ]
|
||||
exact toListModel_eraseₘ h
|
||||
|
||||
theorem wfImp_remove [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
|
||||
(h : Raw.WFImp m.1) : Raw.WFImp (m.remove a).1 := by
|
||||
rw [remove_eq_removeₘ]
|
||||
exact wfImp_removeₘ h
|
||||
theorem wfImp_erase [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
|
||||
(h : Raw.WFImp m.1) : Raw.WFImp (m.erase a).1 := by
|
||||
rw [erase_eq_eraseₘ]
|
||||
exact wfImp_eraseₘ h
|
||||
|
||||
/-! # `filterMapₘ` -/
|
||||
|
||||
@@ -626,7 +626,7 @@ theorem WF.out [BEq α] [Hashable α] [i₁ : EquivBEq α] [i₂ : LawfulHashabl
|
||||
· next h => exact Raw₀.wfImp_insert (by apply h)
|
||||
· next h => exact Raw₀.wfImp_containsThenInsert (by apply h)
|
||||
· next h => exact Raw₀.wfImp_containsThenInsertIfNew (by apply h)
|
||||
· next h => exact Raw₀.wfImp_remove (by apply h)
|
||||
· next h => exact Raw₀.wfImp_erase (by apply h)
|
||||
· next h => exact Raw₀.wfImp_insertIfNew (by apply h)
|
||||
· next h => exact Raw₀.wfImp_getThenInsertIfNew? (by apply h)
|
||||
· next h => exact Raw₀.wfImp_filter (by apply h)
|
||||
|
||||
@@ -22,7 +22,7 @@ set_option autoImplicit false
|
||||
|
||||
universe u v
|
||||
|
||||
variable {α : Type u} {β : α → Type v} [BEq α] [Hashable α]
|
||||
variable {α : Type u} {β : α → Type v} {_ : BEq α} {_ : Hashable α}
|
||||
|
||||
namespace Std.DHashMap
|
||||
|
||||
@@ -65,20 +65,20 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insert k v).contains a = (a == k || m.contains a) :=
|
||||
(m.insert k v).contains a = (k == a || m.contains a) :=
|
||||
Raw₀.contains_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insert k v ↔ a == k ∨ a ∈ m := by
|
||||
a ∈ m.insert k v ↔ k == a ∨ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_insert]
|
||||
|
||||
theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insert k v).contains a → (a == k) = false → m.contains a :=
|
||||
(m.insert k v).contains a → (k == a) = false → m.contains a :=
|
||||
Raw₀.contains_of_contains_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insert k v → (a == k) = false → a ∈ m := by
|
||||
a ∈ m.insert k v → (k == a) = false → a ∈ m := by
|
||||
simpa [mem_iff_contains, -contains_insert] using contains_of_contains_insert
|
||||
|
||||
@[simp]
|
||||
@@ -110,41 +110,41 @@ theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
|
||||
Raw₀.size_le_size_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem remove_empty {k : α} {c : Nat} : (empty c : DHashMap α β).remove k = empty c :=
|
||||
Subtype.eq (congrArg Subtype.val (Raw₀.remove_empty (k := k)) :) -- Lean code is happy
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : DHashMap α β).erase k = empty c :=
|
||||
Subtype.eq (congrArg Subtype.val (Raw₀.erase_empty (k := k)) :) -- Lean code is happy
|
||||
|
||||
@[simp]
|
||||
theorem remove_emptyc {k : α} : (∅ : DHashMap α β).remove k = ∅ :=
|
||||
remove_empty
|
||||
theorem erase_emptyc {k : α} : (∅ : DHashMap α β).erase k = ∅ :=
|
||||
erase_empty
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_remove [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.remove k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) :=
|
||||
Raw₀.isEmpty_remove _ m.2
|
||||
theorem isEmpty_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) :=
|
||||
Raw₀.isEmpty_erase _ m.2
|
||||
|
||||
@[simp]
|
||||
theorem contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.remove k).contains a = (!(a == k) && m.contains a) :=
|
||||
Raw₀.contains_remove ⟨m.1, _⟩ m.2
|
||||
theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k).contains a = (!(k == a) && m.contains a) :=
|
||||
Raw₀.contains_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
a ∈ m.remove k ↔ (a == k) = false ∧ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_remove]
|
||||
theorem mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
a ∈ m.erase k ↔ (k == a) = false ∧ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_erase]
|
||||
|
||||
theorem contains_of_contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.remove k).contains a → m.contains a :=
|
||||
Raw₀.contains_of_contains_remove ⟨m.1, _⟩ m.2
|
||||
theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k).contains a → m.contains a :=
|
||||
Raw₀.contains_of_contains_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem mem_of_mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.remove k → a ∈ m := by
|
||||
theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.erase k → a ∈ m := by
|
||||
simp
|
||||
|
||||
theorem size_remove [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.remove k).size = bif m.contains k then m.size - 1 else m.size :=
|
||||
Raw₀.size_remove _ m.2
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size :=
|
||||
Raw₀.size_erase _ m.2
|
||||
|
||||
theorem size_remove_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.remove k).size ≤ m.size :=
|
||||
Raw₀.size_remove_le _ m.2
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size :=
|
||||
Raw₀.size_erase_le _ m.2
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k :=
|
||||
@@ -176,7 +176,7 @@ theorem get?_of_isEmpty [LawfulBEq α] {a : α} : m.isEmpty = true → m.get? a
|
||||
Raw₀.get?_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_insert [LawfulBEq α] {a k : α} {v : β k} : (m.insert k v).get? a =
|
||||
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else m.get? a :=
|
||||
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else m.get? a :=
|
||||
Raw₀.get?_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -193,13 +193,13 @@ theorem get?_eq_none_of_contains_eq_false [LawfulBEq α] {a : α} :
|
||||
theorem get?_eq_none [LawfulBEq α] {a : α} : ¬a ∈ m → m.get? a = none := by
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false
|
||||
|
||||
theorem get?_remove [LawfulBEq α] {k a : α} :
|
||||
(m.remove k).get? a = bif a == k then none else m.get? a :=
|
||||
Raw₀.get?_remove ⟨m.1, _⟩ m.2
|
||||
theorem get?_erase [LawfulBEq α] {k a : α} :
|
||||
(m.erase k).get? a = bif k == a then none else m.get? a :=
|
||||
Raw₀.get?_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem get?_remove_self [LawfulBEq α] {k : α} : (m.remove k).get? k = none :=
|
||||
Raw₀.get?_remove_self ⟨m.1, _⟩ m.2
|
||||
theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none :=
|
||||
Raw₀.get?_erase_self ⟨m.1, _⟩ m.2
|
||||
|
||||
namespace Const
|
||||
|
||||
@@ -218,7 +218,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
Raw₀.Const.get?_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insert k v) a = bif a == k then some v else get? m a :=
|
||||
get? (m.insert k v) a = bif k == a then some v else get? m a :=
|
||||
Raw₀.Const.get?_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -237,13 +237,13 @@ theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a :
|
||||
theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α } : ¬a ∈ m → get? m a = none := by
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false
|
||||
|
||||
theorem get?_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.remove k) a = bif a == k then none else get? m a :=
|
||||
Raw₀.Const.get?_remove ⟨m.1, _⟩ m.2
|
||||
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.erase k) a = bif k == a then none else get? m a :=
|
||||
Raw₀.Const.get?_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem get?_remove_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.remove k) k = none :=
|
||||
Raw₀.Const.get?_remove_self ⟨m.1, _⟩ m.2
|
||||
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.erase k) k = none :=
|
||||
Raw₀.Const.get?_erase_self ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_eq_get? [LawfulBEq α] {a : α} : get? m a = m.get? a :=
|
||||
Raw₀.Const.get?_eq_get? ⟨m.1, _⟩ m.2
|
||||
@@ -255,8 +255,8 @@ end Const
|
||||
|
||||
theorem get_insert [LawfulBEq α] {k a : α} {v : β k} {h₁} :
|
||||
(m.insert k v).get a h₁ =
|
||||
if h₂ : a == k then
|
||||
cast (congrArg β (eq_of_beq h₂).symm) v
|
||||
if h₂ : k == a then
|
||||
cast (congrArg β (eq_of_beq h₂)) v
|
||||
else
|
||||
m.get a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
|
||||
Raw₀.get_insert ⟨m.1, _⟩ m.2
|
||||
@@ -267,9 +267,9 @@ theorem get_insert_self [LawfulBEq α] {k : α} {v : β k} :
|
||||
Raw₀.get_insert_self ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem get_remove [LawfulBEq α] {k a : α} {h'} :
|
||||
(m.remove k).get a h' = m.get a (mem_of_mem_remove h') :=
|
||||
Raw₀.get_remove ⟨m.1, _⟩ m.2
|
||||
theorem get_erase [LawfulBEq α] {k a : α} {h'} :
|
||||
(m.erase k).get a h' = m.get a (mem_of_mem_erase h') :=
|
||||
Raw₀.get_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_eq_some_get [LawfulBEq α] {a : α} {h} : m.get? a = some (m.get a h) :=
|
||||
Raw₀.get?_eq_some_get ⟨m.1, _⟩ m.2
|
||||
@@ -280,7 +280,7 @@ variable {β : Type v} {m : DHashMap α (fun _ => β)}
|
||||
|
||||
theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
get (m.insert k v) a h₁ =
|
||||
if h₂ : a == k then v else get m a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
|
||||
if h₂ : k == a then v else get m a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
|
||||
Raw₀.Const.get_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -289,9 +289,9 @@ theorem get_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
Raw₀.Const.get_insert_self ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem get_remove [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
|
||||
get (m.remove k) a h' = get m a (mem_of_mem_remove h') :=
|
||||
Raw₀.Const.get_remove ⟨m.1, _⟩ m.2
|
||||
theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
|
||||
get (m.erase k) a h' = get m a (mem_of_mem_erase h') :=
|
||||
Raw₀.Const.get_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} {h} :
|
||||
get? m a = some (get m a h) :=
|
||||
@@ -322,7 +322,7 @@ theorem get!_of_isEmpty [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
|
||||
theorem get!_insert [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
|
||||
(m.insert k v).get! a =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.get! a :=
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.get! a :=
|
||||
Raw₀.get!_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -338,14 +338,14 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
¬a ∈ m → m.get! a = default := by
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false
|
||||
|
||||
theorem get!_remove [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.remove k).get! a = bif a == k then default else m.get! a :=
|
||||
Raw₀.get!_remove ⟨m.1, _⟩ m.2
|
||||
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.erase k).get! a = bif k == a then default else m.get! a :=
|
||||
Raw₀.get!_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem get!_remove_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
|
||||
(m.remove k).get! k = default :=
|
||||
Raw₀.get!_remove_self ⟨m.1, _⟩ m.2
|
||||
theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
|
||||
(m.erase k).get! k = default :=
|
||||
Raw₀.get!_erase_self ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_eq_some_get!_of_contains [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
m.contains a = true → m.get? a = some (m.get! a) :=
|
||||
@@ -381,7 +381,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
Raw₀.Const.get!_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insert k v) a = bif a == k then v else get! m a :=
|
||||
get! (m.insert k v) a = bif k == a then v else get! m a :=
|
||||
Raw₀.Const.get!_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -397,14 +397,14 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
¬a ∈ m → get! m a = default := by
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false
|
||||
|
||||
theorem get!_remove [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.remove k) a = bif a == k then default else get! m a :=
|
||||
Raw₀.Const.get!_remove ⟨m.1, _⟩ m.2
|
||||
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.erase k) a = bif k == a then default else get! m a :=
|
||||
Raw₀.Const.get!_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem get!_remove_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
|
||||
get! (m.remove k) k = default :=
|
||||
Raw₀.Const.get!_remove_self ⟨m.1, _⟩ m.2
|
||||
theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
|
||||
get! (m.erase k) k = default :=
|
||||
Raw₀.Const.get!_erase_self ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} :
|
||||
m.contains a = true → get? m a = some (get! m a) :=
|
||||
@@ -448,7 +448,7 @@ theorem getD_of_isEmpty [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
|
||||
theorem getD_insert [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
|
||||
(m.insert k v).getD a fallback =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.getD a fallback :=
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.getD a fallback :=
|
||||
Raw₀.getD_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -464,14 +464,14 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
¬a ∈ m → m.getD a fallback = fallback := by
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false
|
||||
|
||||
theorem getD_remove [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.remove k).getD a fallback = bif a == k then fallback else m.getD a fallback :=
|
||||
Raw₀.getD_remove ⟨m.1, _⟩ m.2
|
||||
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback :=
|
||||
Raw₀.getD_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem getD_remove_self [LawfulBEq α] {k : α} {fallback : β k} :
|
||||
(m.remove k).getD k fallback = fallback :=
|
||||
Raw₀.getD_remove_self ⟨m.1, _⟩ m.2
|
||||
theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} :
|
||||
(m.erase k).getD k fallback = fallback :=
|
||||
Raw₀.getD_erase_self ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_eq_some_getD_of_contains [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
m.contains a = true → m.get? a = some (m.getD a fallback) :=
|
||||
@@ -512,7 +512,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
Raw₀.Const.getD_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insert k v) a fallback = bif a == k then v else getD m a fallback :=
|
||||
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback :=
|
||||
Raw₀.Const.getD_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -528,14 +528,14 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
¬a ∈ m → getD m a fallback = fallback := by
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false
|
||||
|
||||
theorem getD_remove [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.remove k) a fallback = bif a == k then fallback else getD m a fallback :=
|
||||
Raw₀.Const.getD_remove ⟨m.1, _⟩ m.2
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback :=
|
||||
Raw₀.Const.getD_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem getD_remove_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
|
||||
getD (m.remove k) k fallback = fallback :=
|
||||
Raw₀.Const.getD_remove_self ⟨m.1, _⟩ m.2
|
||||
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
|
||||
getD (m.erase k) k fallback = fallback :=
|
||||
Raw₀.Const.getD_erase_self ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} :
|
||||
m.contains a = true → get? m a = some (getD m a fallback) :=
|
||||
@@ -574,12 +574,12 @@ theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
|
||||
|
||||
@[simp]
|
||||
theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a = (a == k || m.contains a) :=
|
||||
(m.insertIfNew k v).contains a = (k == a || m.contains a) :=
|
||||
Raw₀.contains_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insertIfNew k v ↔ a == k ∨ a ∈ m := by
|
||||
a ∈ m.insertIfNew k v ↔ k == a ∨ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_insertIfNew]
|
||||
|
||||
theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
@@ -591,23 +591,23 @@ theorem mem_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β
|
||||
simpa [mem_iff_contains, -contains_insertIfNew] using contains_insertIfNew_self
|
||||
|
||||
theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a → (a == k) = false → m.contains a :=
|
||||
(m.insertIfNew k v).contains a → (k == a) = false → m.contains a :=
|
||||
Raw₀.contains_of_contains_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem mem_of_mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insertIfNew k v → (a == k) = false → a ∈ m := by
|
||||
a ∈ m.insertIfNew k v → (k == a) = false → a ∈ m := by
|
||||
simpa [mem_iff_contains, -contains_insertIfNew] using contains_of_contains_insertIfNew
|
||||
|
||||
/-- This is a restatement of `contains_insertIfNew` that is written to exactly match the proof
|
||||
obligation in the statement of `get_insertIfNew`. -/
|
||||
theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a → ¬((a == k) ∧ m.contains k = false) → m.contains a :=
|
||||
(m.insertIfNew k v).contains a → ¬((k == a) ∧ m.contains k = false) → m.contains a :=
|
||||
Raw₀.contains_of_contains_insertIfNew' ⟨m.1, _⟩ m.2
|
||||
|
||||
/-- This is a restatement of `mem_insertIfNew` that is written to exactly match the proof obligation
|
||||
in the statement of `get_insertIfNew`. -/
|
||||
theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insertIfNew k v → ¬((a == k) ∧ ¬k ∈ m) → a ∈ m := by
|
||||
a ∈ m.insertIfNew k v → ¬((k == a) ∧ ¬k ∈ m) → a ∈ m := by
|
||||
simpa [mem_iff_contains, -contains_insertIfNew] using contains_of_contains_insertIfNew'
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
@@ -619,25 +619,25 @@ theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v :
|
||||
Raw₀.size_le_size_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} : (m.insertIfNew k v).get? a =
|
||||
if h : a == k ∧ ¬k ∈ m then some (cast (congrArg β (eq_of_beq h.1).symm) v) else m.get? a := by
|
||||
if h : k == a ∧ ¬k ∈ m then some (cast (congrArg β (eq_of_beq h.1)) v) else m.get? a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.get?_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get_insertIfNew [LawfulBEq α] {k a : α} {v : β k} {h₁} : (m.insertIfNew k v).get a h₁ =
|
||||
if h₂ : a == k ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h₂.1).symm) v else m.get a
|
||||
if h₂ : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h₂.1)) v else m.get a
|
||||
(mem_of_mem_insertIfNew' h₁ h₂) := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.get_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get!_insertIfNew [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
|
||||
(m.insertIfNew k v).get! a =
|
||||
if h : a == k ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1).symm) v else m.get! a := by
|
||||
if h : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1)) v else m.get! a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.get!_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem getD_insertIfNew [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
|
||||
(m.insertIfNew k v).getD a fallback =
|
||||
if h : a == k ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1).symm) v
|
||||
if h : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1)) v
|
||||
else m.getD a fallback := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.getD_insertIfNew ⟨m.1, _⟩ m.2
|
||||
@@ -647,22 +647,22 @@ namespace Const
|
||||
variable {β : Type v} {m : DHashMap α (fun _ => β)}
|
||||
|
||||
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insertIfNew k v) a = bif a == k && !m.contains k then some v else get? m a :=
|
||||
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a :=
|
||||
Raw₀.Const.get?_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
get (m.insertIfNew k v) a h₁ =
|
||||
if h₂ : a == k ∧ ¬k ∈ m then v else get m a (mem_of_mem_insertIfNew' h₁ h₂) := by
|
||||
if h₂ : k == a ∧ ¬k ∈ m then v else get m a (mem_of_mem_insertIfNew' h₁ h₂) := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.Const.get_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insertIfNew k v) a = bif a == k && !m.contains k then v else get! m a :=
|
||||
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a :=
|
||||
Raw₀.Const.get!_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insertIfNew k v) a fallback =
|
||||
bif a == k && !m.contains k then v else getD m a fallback :=
|
||||
bif k == a && !m.contains k then v else getD m a fallback :=
|
||||
Raw₀.Const.getD_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
end Const
|
||||
|
||||
@@ -180,9 +180,9 @@ Uses the `LawfulBEq` instance to cast the retrieved value to the correct type.
|
||||
else default -- will never happen for well-formed inputs
|
||||
|
||||
/-- Removes the mapping for the given key if it exists. -/
|
||||
@[inline] def remove [BEq α] [Hashable α] (m : Raw α β) (a : α) : Raw α β :=
|
||||
@[inline] def erase [BEq α] [Hashable α] (m : Raw α β) (a : α) : Raw α β :=
|
||||
if h : 0 < m.buckets.size then
|
||||
Raw₀.remove ⟨m, h⟩ a
|
||||
Raw₀.erase ⟨m, h⟩ a
|
||||
else m -- will never happen for well-formed inputs
|
||||
|
||||
section
|
||||
@@ -416,7 +416,7 @@ inductive WF : {α : Type u} → {β : α → Type v} → [BEq α] → [Hashable
|
||||
| containsThenInsertIfNew₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a b} :
|
||||
WF m → WF (Raw₀.containsThenInsertIfNew ⟨m, h⟩ a b).2.1
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
| remove₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a} : WF m → WF (Raw₀.remove ⟨m, h⟩ a).1
|
||||
| erase₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a} : WF m → WF (Raw₀.erase ⟨m, h⟩ a).1
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
| insertIfNew₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a b} :
|
||||
WF m → WF (Raw₀.insertIfNew ⟨m, h⟩ a b).1
|
||||
@@ -436,7 +436,7 @@ theorem WF.size_buckets_pos [BEq α] [Hashable α] (m : Raw α β) : WF m → 0
|
||||
| insert₀ _ => (Raw₀.insert ⟨_, _⟩ _ _).2
|
||||
| containsThenInsert₀ _ => (Raw₀.containsThenInsert ⟨_, _⟩ _ _).2.2
|
||||
| containsThenInsertIfNew₀ _ => (Raw₀.containsThenInsertIfNew ⟨_, _⟩ _ _).2.2
|
||||
| remove₀ _ => (Raw₀.remove ⟨_, _⟩ _).2
|
||||
| erase₀ _ => (Raw₀.erase ⟨_, _⟩ _).2
|
||||
| insertIfNew₀ _ => (Raw₀.insertIfNew ⟨_, _⟩ _ _).2
|
||||
| getThenInsertIfNew?₀ _ => (Raw₀.getThenInsertIfNew? ⟨_, _⟩ _ _).2.2
|
||||
| filter₀ _ => (Raw₀.filter _ ⟨_, _⟩).2
|
||||
@@ -460,8 +460,8 @@ theorem WF.containsThenInsertIfNew [BEq α] [Hashable α] {m : Raw α β} {a :
|
||||
(m.containsThenInsertIfNew a b).2.WF := by
|
||||
simpa [Raw.containsThenInsertIfNew, h.size_buckets_pos] using .containsThenInsertIfNew₀ h
|
||||
|
||||
theorem WF.remove [BEq α] [Hashable α] {m : Raw α β} {a : α} (h : m.WF) : (m.remove a).WF := by
|
||||
simpa [Raw.remove, h.size_buckets_pos] using .remove₀ h
|
||||
theorem WF.erase [BEq α] [Hashable α] {m : Raw α β} {a : α} (h : m.WF) : (m.erase a).WF := by
|
||||
simpa [Raw.erase, h.size_buckets_pos] using .erase₀ h
|
||||
|
||||
theorem WF.insertIfNew [BEq α] [Hashable α] {m : Raw α β} {a : α} {b : β a} (h : m.WF) :
|
||||
(m.insertIfNew a b).WF := by
|
||||
|
||||
@@ -26,16 +26,19 @@ inductive types. The well-formedness invariant is called `Raw.WF`. When in doubt
|
||||
over `DHashMap.Raw`. Lemmas about the operations on `Std.Data.DHashMap.Raw` are available in the
|
||||
module `Std.Data.DHashMap.RawLemmas`.
|
||||
|
||||
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
|
||||
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
|
||||
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
|
||||
`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq
|
||||
instance is lawful, i.e., if `a == b` implies `a = b`.
|
||||
The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to
|
||||
avoid expensive copies.
|
||||
|
||||
This is a simple separate-chaining hash table. The data of the hash map consists of a cached size
|
||||
and an array of buckets, where each bucket is a linked list of key-value pais. The number of buckets
|
||||
is always a power of two. The hash map doubles its size upon inserting an element such that the
|
||||
number of elements is more than 75% of the number of buckets.
|
||||
|
||||
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
|
||||
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
|
||||
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
|
||||
`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq
|
||||
instance is lawful, i.e., if `a == b` implies `a = b`.
|
||||
-/
|
||||
structure Raw (α : Type u) (β : α → Type v) where
|
||||
/-- The number of mappings present in the hash map -/
|
||||
|
||||
@@ -39,7 +39,7 @@ private def baseNames : Array Name :=
|
||||
``getThenInsertIfNew?_snd_eq, ``getThenInsertIfNew?_snd_val,
|
||||
``map_eq, ``map_val,
|
||||
``filter_eq, ``filter_val,
|
||||
``remove_eq, ``remove_val,
|
||||
``erase_eq, ``erase_val,
|
||||
``filterMap_eq, ``filterMap_val,
|
||||
``Const.getThenInsertIfNew?_snd_eq, ``Const.getThenInsertIfNew?_snd_val,
|
||||
``containsThenInsert_fst_eq, ``containsThenInsert_fst_val,
|
||||
@@ -113,20 +113,20 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {a k : α} {v : β k} :
|
||||
(m.insert k v).contains a = (a == k || m.contains a) := by
|
||||
(m.insert k v).contains a = (k == a || m.contains a) := by
|
||||
simp_to_raw using Raw₀.contains_insert
|
||||
|
||||
@[simp]
|
||||
theorem mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insert k v ↔ a == k ∨ a ∈ m := by
|
||||
a ∈ m.insert k v ↔ k == a ∨ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_insert h]
|
||||
|
||||
theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {a k : α} {v : β k} :
|
||||
(m.insert k v).contains a → (a == k) = false → m.contains a := by
|
||||
(m.insert k v).contains a → (k == a) = false → m.contains a := by
|
||||
simp_to_raw using Raw₀.contains_of_contains_insert
|
||||
|
||||
theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insert k v → (a == k) = false → a ∈ m := by
|
||||
a ∈ m.insert k v → (k == a) = false → a ∈ m := by
|
||||
simpa [mem_iff_contains] using contains_of_contains_insert h
|
||||
|
||||
@[simp]
|
||||
@@ -158,42 +158,42 @@ theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
|
||||
simp_to_raw using Raw₀.size_le_size_insert ⟨m, _⟩ h
|
||||
|
||||
@[simp]
|
||||
theorem remove_empty {k : α} {c : Nat} : (empty c : Raw α β).remove k = empty c := by
|
||||
rw [remove_eq (by wf_trivial)]
|
||||
exact congrArg Subtype.val Raw₀.remove_empty
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw α β).erase k = empty c := by
|
||||
rw [erase_eq (by wf_trivial)]
|
||||
exact congrArg Subtype.val Raw₀.erase_empty
|
||||
|
||||
@[simp]
|
||||
theorem remove_emptyc {k : α} : (∅ : Raw α β).remove k = ∅ :=
|
||||
remove_empty
|
||||
theorem erase_emptyc {k : α} : (∅ : Raw α β).erase k = ∅ :=
|
||||
erase_empty
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_remove [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.remove k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) := by
|
||||
simp_to_raw using Raw₀.isEmpty_remove
|
||||
theorem isEmpty_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) := by
|
||||
simp_to_raw using Raw₀.isEmpty_erase
|
||||
|
||||
@[simp]
|
||||
theorem contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.remove k).contains a = (!(a == k) && m.contains a) := by
|
||||
simp_to_raw using Raw₀.contains_remove
|
||||
theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k).contains a = (!(k == a) && m.contains a) := by
|
||||
simp_to_raw using Raw₀.contains_erase
|
||||
|
||||
@[simp]
|
||||
theorem mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
a ∈ m.remove k ↔ (a == k) = false ∧ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_remove h]
|
||||
theorem mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
a ∈ m.erase k ↔ (k == a) = false ∧ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_erase h]
|
||||
|
||||
theorem contains_of_contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.remove k).contains a → m.contains a := by
|
||||
simp_to_raw using Raw₀.contains_of_contains_remove
|
||||
theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k).contains a → m.contains a := by
|
||||
simp_to_raw using Raw₀.contains_of_contains_erase
|
||||
|
||||
theorem mem_of_mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.remove k → a ∈ m := by
|
||||
simpa [mem_iff_contains] using contains_of_contains_remove h
|
||||
theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.erase k → a ∈ m := by
|
||||
simpa [mem_iff_contains] using contains_of_contains_erase h
|
||||
|
||||
theorem size_remove [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.remove k).size = bif m.contains k then m.size - 1 else m.size := by
|
||||
simp_to_raw using Raw₀.size_remove
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size := by
|
||||
simp_to_raw using Raw₀.size_erase
|
||||
|
||||
theorem size_remove_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.remove k).size ≤ m.size := by
|
||||
simp_to_raw using Raw₀.size_remove_le
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size := by
|
||||
simp_to_raw using Raw₀.size_erase_le
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k := by
|
||||
@@ -225,7 +225,7 @@ theorem get?_of_isEmpty [LawfulBEq α] {a : α} : m.isEmpty = true → m.get? a
|
||||
simp_to_raw using Raw₀.get?_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem get?_insert [LawfulBEq α] {a k : α} {v : β k} : (m.insert k v).get? a =
|
||||
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else m.get? a := by
|
||||
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else m.get? a := by
|
||||
simp_to_raw using Raw₀.get?_insert
|
||||
|
||||
@[simp]
|
||||
@@ -242,13 +242,13 @@ theorem get?_eq_none_of_contains_eq_false [LawfulBEq α] {a : α} :
|
||||
theorem get?_eq_none [LawfulBEq α] {a : α} : ¬a ∈ m → m.get? a = none := by
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false h
|
||||
|
||||
theorem get?_remove [LawfulBEq α] {k a : α} :
|
||||
(m.remove k).get? a = bif a == k then none else m.get? a := by
|
||||
simp_to_raw using Raw₀.get?_remove
|
||||
theorem get?_erase [LawfulBEq α] {k a : α} :
|
||||
(m.erase k).get? a = bif k == a then none else m.get? a := by
|
||||
simp_to_raw using Raw₀.get?_erase
|
||||
|
||||
@[simp]
|
||||
theorem get?_remove_self [LawfulBEq α] {k : α} : (m.remove k).get? k = none := by
|
||||
simp_to_raw using Raw₀.get?_remove_self
|
||||
theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none := by
|
||||
simp_to_raw using Raw₀.get?_erase_self
|
||||
|
||||
namespace Const
|
||||
|
||||
@@ -267,7 +267,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
simp_to_raw using Raw₀.Const.get?_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insert k v) a = bif a == k then some v else get? m a := by
|
||||
get? (m.insert k v) a = bif k == a then some v else get? m a := by
|
||||
simp_to_raw using Raw₀.Const.get?_insert
|
||||
|
||||
@[simp]
|
||||
@@ -286,13 +286,13 @@ theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a :
|
||||
theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m → get? m a = none := by
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false h
|
||||
|
||||
theorem get?_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.remove k) a = bif a == k then none else get? m a := by
|
||||
simp_to_raw using Raw₀.Const.get?_remove
|
||||
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.erase k) a = bif k == a then none else get? m a := by
|
||||
simp_to_raw using Raw₀.Const.get?_erase
|
||||
|
||||
@[simp]
|
||||
theorem get?_remove_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.remove k) k = none := by
|
||||
simp_to_raw using Raw₀.Const.get?_remove_self
|
||||
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.erase k) k = none := by
|
||||
simp_to_raw using Raw₀.Const.get?_erase_self
|
||||
|
||||
theorem get?_eq_get? [LawfulBEq α] {a : α} : get? m a = m.get? a := by
|
||||
simp_to_raw using Raw₀.Const.get?_eq_get?
|
||||
@@ -305,8 +305,8 @@ end Const
|
||||
|
||||
theorem get_insert [LawfulBEq α] {k a : α} {v : β k} {h₁} :
|
||||
(m.insert k v).get a h₁ =
|
||||
if h₂ : a == k then
|
||||
cast (congrArg β (eq_of_beq h₂).symm) v
|
||||
if h₂ : k == a then
|
||||
cast (congrArg β (eq_of_beq h₂)) v
|
||||
else
|
||||
m.get a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) := by
|
||||
simp_to_raw using Raw₀.get_insert ⟨m, _⟩
|
||||
@@ -317,9 +317,9 @@ theorem get_insert_self [LawfulBEq α] {k : α} {v : β k} :
|
||||
simp_to_raw using Raw₀.get_insert_self ⟨m, _⟩
|
||||
|
||||
@[simp]
|
||||
theorem get_remove [LawfulBEq α] {k a : α} {h'} :
|
||||
(m.remove a).get k h' = m.get k (mem_of_mem_remove h h') := by
|
||||
simp_to_raw using Raw₀.get_remove ⟨m, _⟩
|
||||
theorem get_erase [LawfulBEq α] {k a : α} {h'} :
|
||||
(m.erase a).get k h' = m.get k (mem_of_mem_erase h h') := by
|
||||
simp_to_raw using Raw₀.get_erase ⟨m, _⟩
|
||||
|
||||
theorem get?_eq_some_get [LawfulBEq α] {a : α} {h} : m.get? a = some (m.get a h) := by
|
||||
simp_to_raw using Raw₀.get?_eq_some_get
|
||||
@@ -330,7 +330,7 @@ variable {β : Type v} {m : DHashMap.Raw α (fun _ => β)} (h : m.WF)
|
||||
|
||||
theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
get (m.insert k v) a h₁ =
|
||||
if h₂ : a == k then v else get m a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) := by
|
||||
if h₂ : k == a then v else get m a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) := by
|
||||
simp_to_raw using Raw₀.Const.get_insert ⟨m, _⟩
|
||||
|
||||
@[simp]
|
||||
@@ -339,9 +339,9 @@ theorem get_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
simp_to_raw using Raw₀.Const.get_insert_self ⟨m, _⟩
|
||||
|
||||
@[simp]
|
||||
theorem get_remove [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
|
||||
get (m.remove k) a h' = get m a (mem_of_mem_remove h h') := by
|
||||
simp_to_raw using Raw₀.Const.get_remove ⟨m, _⟩
|
||||
theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
|
||||
get (m.erase k) a h' = get m a (mem_of_mem_erase h h') := by
|
||||
simp_to_raw using Raw₀.Const.get_erase ⟨m, _⟩
|
||||
|
||||
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} {h : a ∈ m} :
|
||||
get? m a = some (get m a h) := by
|
||||
@@ -371,7 +371,7 @@ theorem get!_of_isEmpty [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
simp_to_raw using Raw₀.get!_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem get!_insert [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} : (m.insert k v).get! a =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.get! a := by
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.get! a := by
|
||||
simp_to_raw using Raw₀.get!_insert
|
||||
|
||||
@[simp]
|
||||
@@ -387,14 +387,14 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
¬a ∈ m → m.get! a = default := by
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false h
|
||||
|
||||
theorem get!_remove [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.remove k).get! a = bif a == k then default else m.get! a := by
|
||||
simp_to_raw using Raw₀.get!_remove
|
||||
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.erase k).get! a = bif k == a then default else m.get! a := by
|
||||
simp_to_raw using Raw₀.get!_erase
|
||||
|
||||
@[simp]
|
||||
theorem get!_remove_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
|
||||
(m.remove k).get! k = default := by
|
||||
simp_to_raw using Raw₀.get!_remove_self
|
||||
theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
|
||||
(m.erase k).get! k = default := by
|
||||
simp_to_raw using Raw₀.get!_erase_self
|
||||
|
||||
theorem get?_eq_some_get!_of_contains [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
m.contains a = true → m.get? a = some (m.get! a) := by
|
||||
@@ -429,7 +429,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
simp_to_raw using Raw₀.Const.get!_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insert k v) a = bif a == k then v else get! m a := by
|
||||
get! (m.insert k v) a = bif k == a then v else get! m a := by
|
||||
simp_to_raw using Raw₀.Const.get!_insert
|
||||
|
||||
@[simp]
|
||||
@@ -445,14 +445,14 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
¬a ∈ m → get! m a = default := by
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false h
|
||||
|
||||
theorem get!_remove [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.remove k) a = bif a == k then default else get! m a := by
|
||||
simp_to_raw using Raw₀.Const.get!_remove
|
||||
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.erase k) a = bif k == a then default else get! m a := by
|
||||
simp_to_raw using Raw₀.Const.get!_erase
|
||||
|
||||
@[simp]
|
||||
theorem get!_remove_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
|
||||
get! (m.remove k) k = default := by
|
||||
simp_to_raw using Raw₀.Const.get!_remove_self
|
||||
theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
|
||||
get! (m.erase k) k = default := by
|
||||
simp_to_raw using Raw₀.Const.get!_erase_self
|
||||
|
||||
theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} :
|
||||
m.contains a = true → get? m a = some (get! m a) := by
|
||||
@@ -496,7 +496,7 @@ theorem getD_of_isEmpty [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
|
||||
theorem getD_insert [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
|
||||
(m.insert k v).getD a fallback =
|
||||
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.getD a fallback := by
|
||||
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.getD a fallback := by
|
||||
simp_to_raw using Raw₀.getD_insert
|
||||
|
||||
@[simp]
|
||||
@@ -512,14 +512,14 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
¬a ∈ m → m.getD a fallback = fallback := by
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false h
|
||||
|
||||
theorem getD_remove [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.remove k).getD a fallback = bif a == k then fallback else m.getD a fallback := by
|
||||
simp_to_raw using Raw₀.getD_remove
|
||||
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback := by
|
||||
simp_to_raw using Raw₀.getD_erase
|
||||
|
||||
@[simp]
|
||||
theorem getD_remove_self [LawfulBEq α] {k : α} {fallback : β k} :
|
||||
(m.remove k).getD k fallback = fallback := by
|
||||
simp_to_raw using Raw₀.getD_remove_self
|
||||
theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} :
|
||||
(m.erase k).getD k fallback = fallback := by
|
||||
simp_to_raw using Raw₀.getD_erase_self
|
||||
|
||||
theorem get?_eq_some_getD_of_contains [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
m.contains a = true → m.get? a = some (m.getD a fallback) := by
|
||||
@@ -559,7 +559,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
simp_to_raw using Raw₀.Const.getD_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insert k v) a fallback = bif a == k then v else getD m a fallback := by
|
||||
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_insert
|
||||
|
||||
@[simp]
|
||||
@@ -575,14 +575,14 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
¬a ∈ m → getD m a fallback = fallback := by
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false h
|
||||
|
||||
theorem getD_remove [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.remove k) a fallback = bif a == k then fallback else getD m a fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_remove
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_erase
|
||||
|
||||
@[simp]
|
||||
theorem getD_remove_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
|
||||
getD (m.remove k) k fallback = fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_remove_self
|
||||
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
|
||||
getD (m.erase k) k fallback = fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_erase_self
|
||||
|
||||
theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} :
|
||||
m.contains a = true → get? m a = some (getD m a fallback) := by
|
||||
@@ -621,12 +621,12 @@ theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
|
||||
|
||||
@[simp]
|
||||
theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a = (a == k || m.contains a) := by
|
||||
(m.insertIfNew k v).contains a = (k == a || m.contains a) := by
|
||||
simp_to_raw using Raw₀.contains_insertIfNew
|
||||
|
||||
@[simp]
|
||||
theorem mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insertIfNew k v ↔ a == k ∨ a ∈ m := by
|
||||
a ∈ m.insertIfNew k v ↔ k == a ∨ a ∈ m := by
|
||||
simp [mem_iff_contains, contains_insertIfNew h]
|
||||
|
||||
theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
@@ -638,23 +638,23 @@ theorem mem_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β
|
||||
simpa [mem_iff_contains, -contains_insertIfNew] using contains_insertIfNew_self h
|
||||
|
||||
theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a → (a == k) = false → m.contains a := by
|
||||
(m.insertIfNew k v).contains a → (k == a) = false → m.contains a := by
|
||||
simp_to_raw using Raw₀.contains_of_contains_insertIfNew
|
||||
|
||||
theorem mem_of_mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insertIfNew k v → (a == k) = false → a ∈ m := by
|
||||
a ∈ m.insertIfNew k v → (k == a) = false → a ∈ m := by
|
||||
simpa [mem_iff_contains, -contains_insertIfNew] using contains_of_contains_insertIfNew h
|
||||
|
||||
/-- This is a restatement of `contains_insertIfNew` that is written to exactly match the proof
|
||||
obligation in the statement of `get_insertIfNew`. -/
|
||||
theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).contains a → ¬((a == k) ∧ m.contains k = false) → m.contains a := by
|
||||
(m.insertIfNew k v).contains a → ¬((k == a) ∧ m.contains k = false) → m.contains a := by
|
||||
simp_to_raw using Raw₀.contains_of_contains_insertIfNew'
|
||||
|
||||
/-- This is a restatement of `mem_insertIfNew` that is written to exactly match the proof obligation
|
||||
in the statement of `get_insertIfNew`. -/
|
||||
theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
a ∈ m.insertIfNew k v → ¬((a == k) ∧ ¬k ∈ m) → a ∈ m := by
|
||||
a ∈ m.insertIfNew k v → ¬((k == a) ∧ ¬k ∈ m) → a ∈ m := by
|
||||
simpa [mem_iff_contains] using contains_of_contains_insertIfNew' h
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
@@ -667,27 +667,27 @@ theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v :
|
||||
|
||||
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).get? a =
|
||||
if h : a == k ∧ ¬k ∈ m then some (cast (congrArg β (eq_of_beq h.1).symm) v)
|
||||
if h : k == a ∧ ¬k ∈ m then some (cast (congrArg β (eq_of_beq h.1)) v)
|
||||
else m.get? a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
simp_to_raw using Raw₀.get?_insertIfNew ⟨m, _⟩
|
||||
|
||||
theorem get_insertIfNew [LawfulBEq α] {k a : α} {v : β k} {h₁} :
|
||||
(m.insertIfNew k v).get a h₁ =
|
||||
if h₂ : a == k ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h₂.1).symm) v
|
||||
if h₂ : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h₂.1)) v
|
||||
else m.get a (mem_of_mem_insertIfNew' h h₁ h₂) := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
simp_to_raw using Raw₀.get_insertIfNew ⟨m, _⟩
|
||||
|
||||
theorem get!_insertIfNew [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
|
||||
(m.insertIfNew k v).get! a =
|
||||
if h : a == k ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1).symm) v else m.get! a := by
|
||||
if h : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1)) v else m.get! a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
simp_to_raw using Raw₀.get!_insertIfNew ⟨m, _⟩
|
||||
|
||||
theorem getD_insertIfNew [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
|
||||
(m.insertIfNew k v).getD a fallback =
|
||||
if h : a == k ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1).symm) v
|
||||
if h : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1)) v
|
||||
else m.getD a fallback := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
simp_to_raw using Raw₀.getD_insertIfNew
|
||||
@@ -697,23 +697,23 @@ namespace Const
|
||||
variable {β : Type v} {m : DHashMap.Raw α (fun _ => β)} (h : m.WF)
|
||||
|
||||
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insertIfNew k v) a = bif a == k && !m.contains k then some v else get? m a := by
|
||||
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a := by
|
||||
simp_to_raw using Raw₀.Const.get?_insertIfNew
|
||||
|
||||
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
get (m.insertIfNew k v) a h₁ =
|
||||
if h₂ : a == k ∧ ¬k ∈ m then v
|
||||
if h₂ : k == a ∧ ¬k ∈ m then v
|
||||
else get m a (mem_of_mem_insertIfNew' h h₁ h₂) := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
simp_to_raw using Raw₀.Const.get_insertIfNew ⟨m, _⟩
|
||||
|
||||
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insertIfNew k v) a = bif a == k && !m.contains k then v else get! m a := by
|
||||
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a := by
|
||||
simp_to_raw using Raw₀.Const.get!_insertIfNew
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insertIfNew k v) a fallback =
|
||||
bif a == k && !m.contains k then v else getD m a fallback := by
|
||||
bif k == a && !m.contains k then v else getD m a fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_insertIfNew
|
||||
|
||||
end Const
|
||||
|
||||
@@ -27,7 +27,7 @@ nested inductive types.
|
||||
|
||||
universe u v w
|
||||
|
||||
variable {α : Type u} {β : Type v}
|
||||
variable {α : Type u} {β : Type v} {_ : BEq α} {_ : Hashable α}
|
||||
|
||||
namespace Std
|
||||
|
||||
@@ -39,6 +39,9 @@ and an array of buckets, where each bucket is a linked list of key-value pais. T
|
||||
is always a power of two. The hash map doubles its size upon inserting an element such that the
|
||||
number of elements is more than 75% of the number of buckets.
|
||||
|
||||
The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to
|
||||
avoid expensive copies.
|
||||
|
||||
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
|
||||
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
|
||||
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
|
||||
@@ -69,21 +72,21 @@ instance [BEq α] [Hashable α] : EmptyCollection (HashMap α β) where
|
||||
instance [BEq α] [Hashable α] : Inhabited (HashMap α β) where
|
||||
default := ∅
|
||||
|
||||
@[inline, inherit_doc DHashMap.insert] def insert [BEq α] [Hashable α] (m : HashMap α β) (a : α)
|
||||
@[inline, inherit_doc DHashMap.insert] def insert (m : HashMap α β) (a : α)
|
||||
(b : β) : HashMap α β :=
|
||||
⟨m.inner.insert a b⟩
|
||||
|
||||
@[inline, inherit_doc DHashMap.insertIfNew] def insertIfNew [BEq α] [Hashable α] (m : HashMap α β)
|
||||
@[inline, inherit_doc DHashMap.insertIfNew] def insertIfNew (m : HashMap α β)
|
||||
(a : α) (b : β) : HashMap α β :=
|
||||
⟨m.inner.insertIfNew a b⟩
|
||||
|
||||
@[inline, inherit_doc DHashMap.containsThenInsert] def containsThenInsert [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc DHashMap.containsThenInsert] def containsThenInsert
|
||||
(m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
|
||||
let ⟨replaced, r⟩ := m.inner.containsThenInsert a b
|
||||
⟨replaced, ⟨r⟩⟩
|
||||
|
||||
@[inline, inherit_doc DHashMap.containsThenInsertIfNew] def containsThenInsertIfNew [BEq α]
|
||||
[Hashable α] (m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
|
||||
@[inline, inherit_doc DHashMap.containsThenInsertIfNew] def containsThenInsertIfNew
|
||||
(m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
|
||||
let ⟨replaced, r⟩ := m.inner.containsThenInsertIfNew a b
|
||||
⟨replaced, ⟨r⟩⟩
|
||||
|
||||
@@ -96,7 +99,7 @@ returned map has a new value inserted.
|
||||
|
||||
Equivalent to (but potentially faster than) calling `get?` followed by `insertIfNew`.
|
||||
-/
|
||||
@[inline] def getThenInsertIfNew? [BEq α] [Hashable α] (m : HashMap α β) (a : α) (b : β) :
|
||||
@[inline] def getThenInsertIfNew? (m : HashMap α β) (a : α) (b : β) :
|
||||
Option β × HashMap α β :=
|
||||
let ⟨previous, r⟩ := DHashMap.Const.getThenInsertIfNew? m.inner a b
|
||||
⟨previous, ⟨r⟩⟩
|
||||
@@ -106,10 +109,10 @@ The notation `m[a]?` is preferred over calling this function directly.
|
||||
|
||||
Tries to retrieve the mapping for the given key, returning `none` if no such mapping is present.
|
||||
-/
|
||||
@[inline] def get? [BEq α] [Hashable α] (m : HashMap α β) (a : α) : Option β :=
|
||||
@[inline] def get? (m : HashMap α β) (a : α) : Option β :=
|
||||
DHashMap.Const.get? m.inner a
|
||||
|
||||
@[inline, inherit_doc DHashMap.contains] def contains [BEq α] [Hashable α] (m : HashMap α β)
|
||||
@[inline, inherit_doc DHashMap.contains] def contains (m : HashMap α β)
|
||||
(a : α) : Bool :=
|
||||
m.inner.contains a
|
||||
|
||||
@@ -125,10 +128,10 @@ The notation `m[a]` or `m[a]'h` is preferred over calling this function directly
|
||||
Retrieves the mapping for the given key. Ensures that such a mapping exists by requiring a proof of
|
||||
`a ∈ m`.
|
||||
-/
|
||||
@[inline] def get [BEq α] [Hashable α] (m : HashMap α β) (a : α) (h : a ∈ m) : β :=
|
||||
@[inline] def get (m : HashMap α β) (a : α) (h : a ∈ m) : β :=
|
||||
DHashMap.Const.get m.inner a h
|
||||
|
||||
@[inline, inherit_doc DHashMap.Const.getD] def getD [BEq α] [Hashable α] (m : HashMap α β) (a : α)
|
||||
@[inline, inherit_doc DHashMap.Const.getD] def getD (m : HashMap α β) (a : α)
|
||||
(fallback : β) : β :=
|
||||
DHashMap.Const.getD m.inner a fallback
|
||||
|
||||
@@ -137,7 +140,7 @@ The notation `m[a]!` is preferred over calling this function directly.
|
||||
|
||||
Tries to retrieve the mapping for the given key, panicking if no such mapping is present.
|
||||
-/
|
||||
@[inline] def get! [BEq α] [Hashable α] [Inhabited β] (m : HashMap α β) (a : α) : β :=
|
||||
@[inline] def get! [Inhabited β] (m : HashMap α β) (a : α) : β :=
|
||||
DHashMap.Const.get! m.inner a
|
||||
|
||||
instance [BEq α] [Hashable α] : GetElem? (HashMap α β) α β (fun m a => a ∈ m) where
|
||||
@@ -145,37 +148,37 @@ instance [BEq α] [Hashable α] : GetElem? (HashMap α β) α β (fun m a => a
|
||||
getElem? m a := m.get? a
|
||||
getElem! m a := m.get! a
|
||||
|
||||
@[inline, inherit_doc DHashMap.remove] def remove [BEq α] [Hashable α] (m : HashMap α β) (a : α) :
|
||||
@[inline, inherit_doc DHashMap.erase] def erase (m : HashMap α β) (a : α) :
|
||||
HashMap α β :=
|
||||
⟨m.inner.remove a⟩
|
||||
⟨m.inner.erase a⟩
|
||||
|
||||
@[inline, inherit_doc DHashMap.size] def size [BEq α] [Hashable α] (m : HashMap α β) : Nat :=
|
||||
@[inline, inherit_doc DHashMap.size] def size (m : HashMap α β) : Nat :=
|
||||
m.inner.size
|
||||
|
||||
@[inline, inherit_doc DHashMap.isEmpty] def isEmpty [BEq α] [Hashable α] (m : HashMap α β) : Bool :=
|
||||
@[inline, inherit_doc DHashMap.isEmpty] def isEmpty (m : HashMap α β) : Bool :=
|
||||
m.inner.isEmpty
|
||||
|
||||
section Unverified
|
||||
|
||||
/-! We currently do not provide lemmas for the functions below. -/
|
||||
|
||||
@[inline, inherit_doc DHashMap.filter] def filter [BEq α] [Hashable α] (f : α → β → Bool)
|
||||
@[inline, inherit_doc DHashMap.filter] def filter (f : α → β → Bool)
|
||||
(m : HashMap α β) : HashMap α β :=
|
||||
⟨m.inner.filter f⟩
|
||||
|
||||
@[inline, inherit_doc DHashMap.foldM] def foldM [BEq α] [Hashable α] {m : Type w → Type w}
|
||||
@[inline, inherit_doc DHashMap.foldM] def foldM {m : Type w → Type w}
|
||||
[Monad m] {γ : Type w} (f : γ → α → β → m γ) (init : γ) (b : HashMap α β) : m γ :=
|
||||
b.inner.foldM f init
|
||||
|
||||
@[inline, inherit_doc DHashMap.fold] def fold [BEq α] [Hashable α] {γ : Type w}
|
||||
@[inline, inherit_doc DHashMap.fold] def fold {γ : Type w}
|
||||
(f : γ → α → β → γ) (init : γ) (b : HashMap α β) : γ :=
|
||||
b.inner.fold f init
|
||||
|
||||
@[inline, inherit_doc DHashMap.forM] def forM [BEq α] [Hashable α] {m : Type w → Type w} [Monad m]
|
||||
@[inline, inherit_doc DHashMap.forM] def forM {m : Type w → Type w} [Monad m]
|
||||
(f : (a : α) → β → m PUnit) (b : HashMap α β) : m PUnit :=
|
||||
b.inner.forM f
|
||||
|
||||
@[inline, inherit_doc DHashMap.forIn] def forIn [BEq α] [Hashable α] {m : Type w → Type w} [Monad m]
|
||||
@[inline, inherit_doc DHashMap.forIn] def forIn {m : Type w → Type w} [Monad m]
|
||||
{γ : Type w} (f : (a : α) → β → γ → m (ForInStep γ)) (init : γ) (b : HashMap α β) : m γ :=
|
||||
b.inner.forIn f init
|
||||
|
||||
@@ -185,33 +188,33 @@ instance [BEq α] [Hashable α] {m : Type w → Type w} : ForM m (HashMap α β)
|
||||
instance [BEq α] [Hashable α] {m : Type w → Type w} : ForIn m (HashMap α β) (α × β) where
|
||||
forIn m init f := m.forIn (fun a b acc => f (a, b) acc) init
|
||||
|
||||
@[inline, inherit_doc DHashMap.Const.toList] def toList [BEq α] [Hashable α] (m : HashMap α β) :
|
||||
@[inline, inherit_doc DHashMap.Const.toList] def toList (m : HashMap α β) :
|
||||
List (α × β) :=
|
||||
DHashMap.Const.toList m.inner
|
||||
|
||||
@[inline, inherit_doc DHashMap.Const.toArray] def toArray [BEq α] [Hashable α] (m : HashMap α β) :
|
||||
@[inline, inherit_doc DHashMap.Const.toArray] def toArray (m : HashMap α β) :
|
||||
Array (α × β) :=
|
||||
DHashMap.Const.toArray m.inner
|
||||
|
||||
@[inline, inherit_doc DHashMap.keys] def keys [BEq α] [Hashable α] (m : HashMap α β) : List α :=
|
||||
@[inline, inherit_doc DHashMap.keys] def keys (m : HashMap α β) : List α :=
|
||||
m.inner.keys
|
||||
|
||||
@[inline, inherit_doc DHashMap.keysArray] def keysArray [BEq α] [Hashable α] (m : HashMap α β) :
|
||||
@[inline, inherit_doc DHashMap.keysArray] def keysArray (m : HashMap α β) :
|
||||
Array α :=
|
||||
m.inner.keysArray
|
||||
|
||||
@[inline, inherit_doc DHashMap.values] def values [BEq α] [Hashable α] (m : HashMap α β) : List β :=
|
||||
@[inline, inherit_doc DHashMap.values] def values (m : HashMap α β) : List β :=
|
||||
m.inner.values
|
||||
|
||||
@[inline, inherit_doc DHashMap.valuesArray] def valuesArray [BEq α] [Hashable α] (m : HashMap α β) :
|
||||
@[inline, inherit_doc DHashMap.valuesArray] def valuesArray (m : HashMap α β) :
|
||||
Array β :=
|
||||
m.inner.valuesArray
|
||||
|
||||
@[inline, inherit_doc DHashMap.Const.insertMany] def insertMany [BEq α] [Hashable α] {ρ : Type w}
|
||||
@[inline, inherit_doc DHashMap.Const.insertMany] def insertMany {ρ : Type w}
|
||||
[ForIn Id ρ (α × β)] (m : HashMap α β) (l : ρ) : HashMap α β :=
|
||||
⟨DHashMap.Const.insertMany m.inner l⟩
|
||||
|
||||
@[inline, inherit_doc DHashMap.Const.insertManyUnit] def insertManyUnit [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc DHashMap.Const.insertManyUnit] def insertManyUnit
|
||||
{ρ : Type w} [ForIn Id ρ α] (m : HashMap α Unit) (l : ρ) : HashMap α Unit :=
|
||||
⟨DHashMap.Const.insertManyUnit m.inner l⟩
|
||||
|
||||
@@ -223,7 +226,7 @@ instance [BEq α] [Hashable α] {m : Type w → Type w} : ForIn m (HashMap α β
|
||||
HashMap α Unit :=
|
||||
⟨DHashMap.Const.unitOfList l⟩
|
||||
|
||||
@[inline, inherit_doc DHashMap.Internal.numBuckets] def Internal.numBuckets [BEq α] [Hashable α]
|
||||
@[inline, inherit_doc DHashMap.Internal.numBuckets] def Internal.numBuckets
|
||||
(m : HashMap α β) : Nat :=
|
||||
DHashMap.Internal.numBuckets m.inner
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user