Compare commits

..

49 Commits

Author SHA1 Message Date
Kim Morrison
2513be6a09 feat: HashSet.ofArray (unverified) 2024-09-17 16:42:31 +10:00
Kim Morrison
c25d206647 chore: Fin.ofNat' uses NeZero (#5356) 2024-09-16 07:13:18 +00:00
Violeta Hernández
078e9b6d77 doc: add documentation for groupBy.loop (#5349)
We add some documentation explaining the auxiliary function in the
definition of `groupBy`. This has been moved here from Mathlib PR
[16818](https://github.com/leanprover-community/mathlib4/pull/16818) by
request of @semorrison.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-09-16 05:56:44 +00:00
Kim Morrison
a745e33123 feat: BitVec.truncate lemmas (#5357)
These improve confluence of lemmas involving `truncate`.
2024-09-16 05:55:50 +00:00
Kim Morrison
7740a38a71 chore: remove @[simp] from Option.bind_map (#5354) 2024-09-16 04:44:38 +00:00
Kim Morrison
9568f305d8 chore: switch primes on List.getElem_take (#5294)
This will probably have fallout downstream, and as it is a direct name
switch I'm not going to provide any deprecations.
2024-09-16 03:40:42 +00:00
Kim Morrison
b1179d5cc3 chore: fix implicitness of List.getElem_mem (#5331) 2024-09-16 03:28:14 +00:00
Kim Morrison
e6145a6937 feat: simp lemmas for LawfulBEq (#5355) 2024-09-16 03:21:30 +00:00
Kim Morrison
d47ae99721 feat: List.head_mem_head? (#5353) 2024-09-16 03:05:17 +00:00
Kim Morrison
0aac83fe40 feat: List.attachWith lemmas (#5352) 2024-09-16 02:24:14 +00:00
Kim Morrison
8c6ac845b1 chore: cleanup after export Bool.and/or/not/xor 2024-09-16 12:45:51 +10:00
Kim Morrison
b714a96034 chore: update stage0 2024-09-16 12:45:51 +10:00
Kim Morrison
4e0f6b8b45 feat: export Bool.and/or/not/xor 2024-09-16 12:45:51 +10:00
Kim Morrison
979c5a4d6a chore: update stage0 2024-09-16 12:45:51 +10:00
Kim Morrison
2079bdcbca feat: deprecate _root_.or/and/not/xor 2024-09-16 12:45:51 +10:00
Kim Morrison
1a2217d47e feat: cleanup of List.getElem_append variants (#5303) 2024-09-16 02:01:37 +00:00
Kim Morrison
3ef67c468a feat: List.replicate lemmas (#5350) 2024-09-15 23:57:04 +00:00
Joachim Breitner
4c439c73a7 test: tracing and test case for #5347 (#5348)
not a fix, unfortunately, just recording the test.
2024-09-15 15:45:39 +00:00
thorimur
5eea8355ba fix: set check level correctly during workflow (#5344)
Fixes a workflow bug where the `check-level` was not always set
correctly. Arguments to a `gh` call used to determine the `check_level`
were accidentally outside of the relevant command substitution (`$(gh
...)`).

-----

This can be observed in [these
logs](https://github.com/leanprover/lean4/actions/runs/10859763037/job/30139540920),
where the check level (shown first under "configure build matrix") is
`2`, but the PR does not have the `release-ci` tag. As a "test", run the
script for "set check level" printed in those logs (with some lines
omitted):
```
check_level=0

labels="$(gh api repos/leanprover/lean4/pulls/5343) --jq '.labels'"
if echo "$labels" | grep -q "release-ci"; then
  check_level=2
elif echo "$labels" | grep -q "merge-ci"; then
  check_level=1
fi

echo "check_level=$check_level"
```
Note that this prints `check_level=2`, but changing `labels` to
`labels="$(gh api repos/leanprover/lean4/pulls/5343 --jq '.labels')"`
prints `check_level=0`.
2024-09-14 08:14:08 +00:00
thorimur
60bb451d45 feat: allow addition of release-ci label via comment (#5343)
Updates the PR labeling workflow to allow an external contributor to add
the `release-ci` label to their own PR via comment. This is allows users
on Windows and Intel-based macs to generate toolchains for local
testing. The pull request template is also updated to reflect this.

-----

See Zulip discussion
[here](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/No.20binary.20for.20lean.20PR.20testing.20locally).
2024-09-14 08:13:48 +00:00
Marc Huisinga
f989520d2b fix: invalid namespace completions (#5322)
This PR fixes an issue reported a while ago at
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.60Monad.2Emap.60.20is.20a.20namespace.3F/near/425662846
where `Monad.map` was incorrectly reported by the autocompletion as a
namespace.

The underlying issue is that `Monad.map` contains an internal
declaration `_default`. This PR ensures that no namespaces are
registered that only contain internal declarations.

This also means that `open`ing namespaces that only contain internal
declarations will now fail.

The Mathlib adaption for this is a minor change where a declaration
(i.e. a namespace that only contains internal declarations) was `open`ed
by accident.
2024-09-13 12:23:03 +00:00
Jeremy Tan Jie Rui
626dda9358 refactor: tag Iff.refl with @[refl] (#5329)
and remove `exact Off.rfl` from the `rfl` macro.


This upstreams a property found in
[`Mathlib.Init.Logic`](4e40837aec/Mathlib/Init/Logic.lean (L63)).
2024-09-13 11:55:36 +00:00
Sebastian Ullrich
5f789e63fa chore: remove confusing test 2024-09-13 13:04:57 +02:00
Sebastian Ullrich
438061a924 fix: inaccessible pattern vars reported as binders (#5337)
Fixes an unused variable false positive on some wildcard patterns

Fixes #1633, fixes #2830
2024-09-13 09:53:58 +00:00
Mario Carneiro
ec98c92ba6 feat: @[builtin_doc] attribute (part 2) (#3918)
This solves the issue where certain subexpressions are lacking syntax
hovers because the hover text is not "builtin" - it only shows up if the
`Parser` constant is imported in the environment. For top level syntaxes
this is not a problem because `builtin_term_parser` will automatically
add this doc information, but nested syntaxes don't get the same
treatment.

We could walk the expression and add builtin docs recursively, but this
is somewhat expensive and unnecessary given that it's a fixed list of
declarations in lean core. Moreover, there are reasons to want to
control which syntax nodes actually get hovers, and while a better
system for that is forthcoming, for now it can be achieved by
strategically not applying the `@[builtin_doc]` attribute.

Fixes #3842
2024-09-13 08:05:10 +00:00
Henrik Böving
2080fc0221 feat: (DHashMap|HashMap|HashSet).(getKey?|getKey|getKey!|getKeyD) (#5244) 2024-09-13 05:40:10 +00:00
Marc Huisinga
b34379554d feat: completion fallback (#5299)
When the elaborator doesn't provide us with any `CompletionInfo`, we
currently provide no completions whatsoever. But in many cases, we can
still provide some helpful identifier completions without elaborator
information. This PR adds a fallback mode for this situation.

There is more potential here, but this should be a good start.

In principle, this issue alleviates #5172 (since we now provide
completions in these contexts). I'll leave it up to an elaboration
maintainer whether we also want to ensure that the completion infos are
provided correctly in these cases.
2024-09-12 16:09:20 +00:00
Siddharth
273b7540b2 feat: toNat_sub_of_le (#5314)
This adds a simplification lemma for `(x - y).toNat` when the
subtraction is known to not overflow (i.e., `y ≤ x`).

We make a new section for this for two reasons:
1. Definitions of subtraction occur before the definition of
`BitVec.le_def`, so we cannot directly place this lemma at `sub`.
2. There are other theorems of this kind, for addition and
multiplication, which can morally live in the same section.
2024-09-12 13:19:39 +00:00
Lars - he/him
b875627198 feat: add ediv_nonneg_of_nonpos_of_nonpos to DivModLemmas (#5320)
The theorem 

```lean
namespace Int

theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0) : 0 ≤ a / b := by
  match a, b with
  | ofNat a, b =>
    match Int.le_antisymm Ha (ofNat_zero_le a) with
    | h1 =>
    rw [h1, zero_ediv,]
    exact Int.le_refl 0
  | a, ofNat b =>
    match Int.le_antisymm Hb (ofNat_zero_le  b) with
    | h1 =>
    rw [h1, Int.ediv_zero]
    exact Int.le_refl 0
  | negSucc a, negSucc b =>
    rw [Int.div_def, ediv]
    have le_succ {a: Int} : a ≤ a+1 := (le_add_one (Int.le_refl a))
    have h2: 0 ≤ ((↑b:Int) + 1) := Int.le_trans (ofNat_zero_le b) le_succ
    have h3: (0:Int) ≤ ↑a / (↑b + 1) := (ediv_nonneg (ofNat_zero_le a) h2)
    exact Int.le_trans h3 le_succ
```
is nontrivial to prove from existing theorems and would be nice to add
as standard theorem in DivModLemmas.

See the zullip conversation
[here](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Adding.20theorem.20theorem.20ediv_nonneg'.20for.20negative.20a.20and.20b)

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-09-12 11:26:20 +00:00
Kim Morrison
adfd6c090e chore: add Nat.self_sub_mod lemma (#5306) 2024-09-12 03:36:50 +00:00
Kim Morrison
da0d309d65 feat: provide mergeSort comparator autoParam (#5302)
Write `mergeSort xs ys cmp` to provide an explicit comparator, or
otherwise `mergeSort xs ys` falls back to `LE` and `DecidablePred` via
an autoparam.
2024-09-12 01:50:01 +00:00
Kim Morrison
87fdd7809f feat: List.tail lemma (#5316) 2024-09-12 01:09:57 +00:00
Henrik Böving
8fd6e46a9c feat: more basic BitVec ordering theory for UInt (#5313) 2024-09-11 18:16:21 +00:00
Sebastian Ullrich
0602b805c8 fix: changing whitespace after module header may break subsequent commands (#5312)
`with` considered harmful when merging old and new state, let's always
be explicit in these cases
2024-09-11 13:00:42 +00:00
Kim Morrison
0b7debe376 chore: fix List.countP lemmas (#5311) 2024-09-11 10:09:37 +00:00
Kim Morrison
f5146c6edb chore: fix List.all/any lemmas (#5310) 2024-09-11 10:02:47 +00:00
Kim Morrison
461283ecf4 chore: restoring Option simp confluence (#5307) 2024-09-11 06:52:31 +00:00
Kim Morrison
27bf7367ca chore: rename Nat bitwise lemmas (#5305) 2024-09-11 06:29:00 +00:00
Kim Morrison
d4cc934149 chore: rename Int.div/mod to tdiv/tmod (#5301)
From the new doc-string:
```quote
In early versions of Lean, the typeclasses provided by `/` and `%`
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.

However we decided it was better to use `ediv` and `emod`,
as they are consistent with the conventions used in SMTLib, Mathlib,
and often mathematical reasoning is easier with these conventions.

At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
In September 2024, we decided to do this rename (with deprecations in place),
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
ever need to use these functions and their associated lemmas.
```
2024-09-11 06:15:44 +00:00
Kim Morrison
b88cdf6a3e chore: Array.not_mem_empty (#5304) 2024-09-11 06:13:24 +00:00
Kim Morrison
325a058893 feat: more List.findIdx theorems (#5300) 2024-09-11 04:53:59 +00:00
Henrik Böving
f869018447 feat: BitVec unsigned order theoretic results (#5297)
Proves that `<` and `<=` on `BitVec` are (strict) (total) partial
orders. This is required for the `UInt` as `BitVec` refactor.

This does open the question how to state these theorems "correctly" for
`BitVec`, we have both `<` living in `Prop` and `BitVec.ult` living in
`Bool`. We might of course say to always use `<` but: Once we start
adding `IntX` we need to prove the same results for `BitVec.slt` to
provide an equivalent API. So it would appear that it is unavoidable to
have a `= true` variant of these theorems there?

Question answered: Use `<` and `slt`.
2024-09-10 12:32:44 +00:00
Kim Morrison
c1da100997 chore: remove debug.byAsSorry 2024-09-10 19:30:09 +10:00
Kim Morrison
6c97c4ce37 chore: update stage0 2024-09-10 19:30:09 +10:00
Kim Morrison
c209d0d745 chore: upstream Zero and NeZero 2024-09-10 19:30:09 +10:00
Kim Morrison
5bc199ea1c chore: debug.byAsSorry on broken proofs 2024-09-10 19:30:09 +10:00
Arthur Adjedj
cb4a73a487 refactor: Lean.Elab.Deriving.FromToJson (#5292)
Refactors the derive handlers for `ToJson` and `FromJson` in preparation
for #3160.
This splits up the different parts of the handler according to how other
similar handlers are implemented while keeping the original logic
intact. This makes the changes necessary to adapt the file in #3160 much
easier.
2024-09-10 08:55:52 +00:00
Lean stage0 autoupdater
92e1f168b2 chore: update stage0 2024-09-10 08:04:39 +00:00
Marc Huisinga
a58520da16 fix: travelling auto-completion (#5257)
Fixes #4455, fixes #4705, fixes #5219

Also fixes a minor bug where a dot in brackets would report incorrect
completions instead of no completions.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-09-10 07:26:44 +00:00
297 changed files with 3511 additions and 701 deletions

View File

@@ -5,6 +5,7 @@
* Include the link to your `RFC` or `bug` issue in the description.
* If the issue does not already have approval from a developer, submit the PR as draft.
* The PR title/description will become the commit message. Keep it up-to-date as the PR evolves.
* A toolchain of the form `leanprover/lean4-pr-releases:pr-release-NNNN` for Linux and M-series Macs will be generated upon build. To generate binaries for Windows and Intel-based Macs as well, write a comment containing `release-ci` on its own line.
* If you rebase your PR onto `nightly-with-mathlib` then CI will test Mathlib against your PR.
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels yourself, by writing a comment containing one of these labels on its own line.
* Remove this section, up to and including the `---` before submitting.

View File

@@ -114,7 +114,7 @@ jobs:
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
else
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }}) --jq '.labels'"
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels')"
if echo "$labels" | grep -q "release-ci"; then
check_level=2
elif echo "$labels" | grep -q "merge-ci"; then

View File

@@ -1,6 +1,7 @@
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, or `WIP` labels,
# by commenting on the PR or issue.
# Other labels from this set are removed automatically at the same time.
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, `WIP`,
# or `release-ci` labels by commenting on the PR or issue.
# If any labels from the set {`awaiting-review`, `awaiting-author`, `WIP`} are added, other labels
# from that set are removed automatically at the same time.
name: Label PR based on Comment
@@ -10,7 +11,7 @@ on:
jobs:
update-label:
if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP'))
if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP') || contains(github.event.comment.body, 'release-ci'))
runs-on: ubuntu-latest
steps:
@@ -25,6 +26,7 @@ jobs:
const awaitingReview = commentLines.includes('awaiting-review');
const awaitingAuthor = commentLines.includes('awaiting-author');
const wip = commentLines.includes('WIP');
const releaseCI = commentLines.includes('release-ci');
if (awaitingReview || awaitingAuthor || wip) {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-review' }).catch(() => {});
@@ -41,3 +43,7 @@ jobs:
if (wip) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['WIP'] });
}
if (releaseCI) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['release-ci'] });
}

View File

@@ -28,7 +28,7 @@ Important instances include
* `Option`, where `failure := none` and `<|>` returns the left-most `some`.
* Parser combinators typically provide an `Applicative` instance for error-handling and
backtracking.
Error recovery and state can interact subtly. For example, the implementation of `Alternative` for `OptionT (StateT σ Id)` keeps modifications made to the state while recovering from failure, while `StateT σ (OptionT Id)` discards them.
-/
-- NB: List instance is in mathlib. Once upstreamed, add

View File

@@ -817,14 +817,12 @@ variable {a b c d : Prop}
theorem iff_iff_implies_and_implies {a b : Prop} : (a b) (a b) (b a) :=
Iff.intro (fun h => And.intro h.mp h.mpr) (fun h => Iff.intro h.left h.right)
theorem Iff.refl (a : Prop) : a a :=
@[refl] theorem Iff.refl (a : Prop) : a a :=
Iff.intro (fun h => h) (fun h => h)
protected theorem Iff.rfl {a : Prop} : a a :=
Iff.refl a
macro_rules | `(tactic| rfl) => `(tactic| exact Iff.rfl)
theorem Iff.of_eq (h : a = b) : a b := h Iff.rfl
theorem Iff.trans (h₁ : a b) (h₂ : b c) : a c :=

View File

@@ -39,3 +39,5 @@ import Init.Data.BEq
import Init.Data.Subtype
import Init.Data.ULift
import Init.Data.PLift
import Init.Data.Zero
import Init.Data.NeZero

View File

@@ -147,6 +147,9 @@ theorem anyM_stop_le_start [Monad m] (p : α → m Bool) (as : Array α) (start
theorem mem_def {a : α} {as : Array α} : a as a as.toList :=
fun | .mk h => h, Array.Mem.mk
@[simp] theorem not_mem_empty (a : α) : ¬(a #[]) := by
simp [mem_def]
/-! # get -/
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
@@ -825,7 +828,7 @@ theorem get_append_right {as bs : Array α} {h : i < (as ++ bs).size} (hle : as.
(as ++ bs)[i] = bs[i - as.size] := by
simp only [getElem_eq_toList_getElem]
have h' : i < (as.toList ++ bs.toList).length := by rwa [ toList_length, append_toList] at h
conv => rhs; rw [ List.getElem_append_right (h' := h') (h := Nat.not_lt_of_ge hle)]
conv => rhs; rw [ List.getElem_append_right (h := hle) (h := h')]
apply List.get_of_eq; rw [append_toList]
@[simp] theorem append_nil (as : Array α) : as ++ #[] = as := by

View File

@@ -64,7 +64,7 @@ protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2^n) : BitVec n where
/-- The `BitVec` with value `i mod 2^n`. -/
@[match_pattern]
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat' i (Nat.two_pow_pos n)
toFin := Fin.ofNat' (2^n) i
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
instance natCastInst : NatCast (BitVec w) := BitVec.ofNat w

View File

@@ -31,13 +31,13 @@ namespace BitVec
simp only [Bool.and_eq_false_imp, decide_eq_true_eq]
omega
theorem lt_of_getLsbD (x : BitVec w) (i : Nat) : getLsbD x i = true i < w := by
theorem lt_of_getLsbD {x : BitVec w} {i : Nat} : getLsbD x i = true i < w := by
if h : i < w then
simp [h]
else
simp [Nat.ge_of_not_lt h]
theorem lt_of_getMsbD (x : BitVec w) (i : Nat) : getMsbD x i = true i < w := by
theorem lt_of_getMsbD {x : BitVec w} {i : Nat} : getMsbD x i = true i < w := by
if h : i < w then
simp [h]
else
@@ -245,7 +245,7 @@ theorem ofBool_eq_iff_eq : ∀ {b b' : Bool}, BitVec.ofBool b = BitVec.ofBool b'
@[simp, bv_toNat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
@[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' x (Nat.two_pow_pos w) := rfl
@[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' (2^w) x := rfl
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
@@ -546,7 +546,7 @@ theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsbD k :=
(x.zeroExtend l).zeroExtend k = x.zeroExtend k := by
ext i
simp only [getLsbD_zeroExtend, Fin.is_lt, decide_True, Bool.true_and]
have p := lt_of_getLsbD x i
have p := lt_of_getLsbD (x := x) (i := i)
revert p
cases getLsbD x i <;> simp; omega
@@ -592,6 +592,12 @@ theorem truncate_one {x : BitVec w} :
ext i
simp [show i = 0 by omega]
@[simp] theorem truncate_ofNat_of_le (h : v w) (x : Nat) : truncate v (BitVec.ofNat w x) = BitVec.ofNat v x := by
apply BitVec.eq_of_toNat_eq
simp only [toNat_truncate, toNat_ofNat]
rw [Nat.mod_mod_of_dvd]
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
/-! ## extractLsb -/
@[simp]
@@ -826,7 +832,7 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
BitVec.toNat_ofNat _ _
@[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
BitVec.toFin (x <<< n) = Fin.ofNat' (x.toNat <<< n) (Nat.two_pow_pos w) := rfl
BitVec.toFin (x <<< n) = Fin.ofNat' (2^w) (x.toNat <<< n) := rfl
@[simp]
theorem shiftLeft_zero_eq (x : BitVec w) : x <<< 0 = x := by
@@ -1027,7 +1033,7 @@ theorem sshiftRight_eq_of_msb_true {x : BitVec w} {s : Nat} (h : x.msb = true) :
· simp only [hi, decide_False, Bool.not_false, Bool.true_and, Bool.iff_and_self,
decide_eq_true_eq]
intros hlsb
apply BitVec.lt_of_getLsbD _ _ hlsb
apply BitVec.lt_of_getLsbD hlsb
· by_cases hi : i w
· simp [hi]
· simp only [sshiftRight_eq_of_msb_true hmsb, getLsbD_not, getLsbD_ushiftRight, Bool.not_and,
@@ -1228,7 +1234,7 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = cast (by omega) y := by
ext
rw [getLsbD_append]
simpa using lt_of_getLsbD _ _
simpa using lt_of_getLsbD
@[simp] theorem cast_append_right (h : w + v = w + v') (x : BitVec w) (y : BitVec v) :
cast h (x ++ y) = x ++ cast (by omega) y := by
@@ -1259,6 +1265,18 @@ theorem truncate_append {x : BitVec w} {y : BitVec v} :
· have t' : i - v < k - v := by omega
simp [t, t']
@[simp] theorem truncate_append_of_eq {x : BitVec v} {y : BitVec w} (h : w' = w) : truncate (v' + w') (x ++ y) = truncate v' x ++ truncate w' y := by
subst h
ext i
simp only [getLsbD_zeroExtend, Fin.is_lt, decide_True, getLsbD_append, cond_eq_if,
decide_eq_true_eq, Bool.true_and, zeroExtend_eq]
split
· simp_all
· simp_all only [Bool.iff_and_self, decide_eq_true_eq]
intro h
have := BitVec.lt_of_getLsbD h
omega
@[simp] theorem truncate_cons {x : BitVec w} : (cons a x).truncate w = x := by
simp [cons, truncate_append]
@@ -1508,7 +1526,7 @@ theorem ofNat_sub_ofNat {n} (x y : Nat) : BitVec.ofNat n x - BitVec.ofNat n y =
simp [Neg.neg, BitVec.neg]
@[simp] theorem toFin_neg (x : BitVec n) :
(-x).toFin = Fin.ofNat' (2^n - x.toNat) (Nat.two_pow_pos _) :=
(-x).toFin = Fin.ofNat' (2^n) (2^n - x.toNat) :=
rfl
theorem sub_toAdd {n} (x y : BitVec n) : x - y = x + - y := by
@@ -1631,12 +1649,49 @@ theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
@[simp] theorem ofNat_lt_ofNat {n} (x y : Nat) : BitVec.ofNat n x < BitVec.ofNat n y x % 2^n < y % 2^n := by
simp [lt_def]
protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x < y := by
revert h1 h2
let x, lt := x
let y, lt := y
simp
exact Nat.lt_of_le_of_ne
@[simp] protected theorem not_le {x y : BitVec n} : ¬ x y y < x := by
simp [le_def, lt_def]
@[simp] protected theorem not_lt {x y : BitVec n} : ¬ x < y y x := by
simp [le_def, lt_def]
@[simp] protected theorem le_refl (x : BitVec n) : x x := by
simp [le_def]
@[simp] protected theorem lt_irrefl (x : BitVec n) : ¬x < x := by
simp [lt_def]
protected theorem le_trans {x y z : BitVec n} : x y y z x z := by
simp only [le_def]
apply Nat.le_trans
protected theorem lt_trans {x y z : BitVec n} : x < y y < z x < z := by
simp only [lt_def]
apply Nat.lt_trans
protected theorem le_total (x y : BitVec n) : x y y x := by
simp only [le_def]
apply Nat.le_total
protected theorem le_antisymm {x y : BitVec n} : x y y x x = y := by
simp only [le_def, BitVec.toNat_eq]
apply Nat.le_antisymm
protected theorem lt_asymm {x y : BitVec n} : x < y ¬ y < x := by
simp only [lt_def]
apply Nat.lt_asymm
protected theorem lt_of_le_ne {x y : BitVec n} : x y ¬ x = y x < y := by
simp only [lt_def, le_def, BitVec.toNat_eq]
apply Nat.lt_of_le_of_ne
protected theorem ne_of_lt {x y : BitVec n} : x < y x y := by
simp only [lt_def, ne_eq, toNat_eq]
apply Nat.ne_of_lt
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y x.umod y < y := by
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod, umod, toNat_ofNatLt]
apply Nat.mod_lt
/-! ### ofBoolList -/
@@ -2009,4 +2064,20 @@ theorem getLsbD_intMax (w : Nat) : (intMax w).getLsbD i = decide (i + 1 < w) :=
· simp [h]
· rw [Nat.sub_add_cancel (Nat.two_pow_pos (w - 1)), Nat.two_pow_pred_mod_two_pow (by omega)]
/-! ### Non-overflow theorems -/
/--
If `y ≤ x`, then the subtraction `(x - y)` does not overflow.
Thus, `(x - y).toNat = x.toNat - y.toNat`
-/
theorem toNat_sub_of_le {x y : BitVec n} (h : y x) :
(x - y).toNat = x.toNat - y.toNat := by
simp only [toNat_sub]
rw [BitVec.le_def] at h
by_cases h' : x.toNat = y.toNat
· rw [h', Nat.sub_self, Nat.sub_add_cancel (by omega), Nat.mod_self]
· have : 2 ^ n - y.toNat + x.toNat = 2 ^ n + (x.toNat - y.toNat) := by omega
rw [this, Nat.add_mod_left, Nat.mod_eq_of_lt (by omega)]
end BitVec

View File

@@ -6,16 +6,11 @@ Authors: F. G. Dorais
prelude
import Init.NotationExtra
/-- Boolean exclusive or -/
abbrev xor : Bool Bool Bool := bne
namespace Bool
/- Namespaced versions that can be used instead of prefixing `_root_` -/
@[inherit_doc not] protected abbrev not := not
@[inherit_doc or] protected abbrev or := or
@[inherit_doc and] protected abbrev and := and
@[inherit_doc xor] protected abbrev xor := xor
/-- Boolean exclusive or -/
abbrev xor : Bool Bool Bool := bne
instance (p : Bool Prop) [inst : DecidablePred p] : Decidable ( x, p x) :=
match inst true, inst false with
@@ -597,7 +592,7 @@ theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidab
end Bool
export Bool (cond_eq_if)
export Bool (cond_eq_if xor and or not)
/-! ### decide -/

View File

@@ -31,7 +31,7 @@ This differs from addition, which wraps around:
(2 : Fin 3) + 1 = (0 : Fin 3)
```
-/
def succ : Fin n Fin n.succ
def succ : Fin n Fin (n + 1)
| i, h => i+1, Nat.succ_lt_succ h
variable {n : Nat}
@@ -39,16 +39,20 @@ variable {n : Nat}
/--
Returns `a` modulo `n + 1` as a `Fin n.succ`.
-/
protected def ofNat {n : Nat} (a : Nat) : Fin n.succ :=
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)
/--
Returns `a` modulo `n` as a `Fin n`.
The assumption `n > 0` ensures that `Fin n` is nonempty.
The assumption `NeZero n` ensures that `Fin n` is nonempty.
-/
protected def ofNat' {n : Nat} (a : Nat) (h : n > 0) : Fin n :=
a % n, Nat.mod_lt _ h
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
a % n, Nat.mod_lt _ (pos_of_neZero n)
-- We intend to deprecate `Fin.ofNat` in favor of `Fin.ofNat'` (and later rename).
-- This is waiting on https://github.com/leanprover/lean4/pull/5323
-- attribute [deprecated Fin.ofNat' (since := "2024-09-16")] Fin.ofNat
private theorem mlt {b : Nat} : {a : Nat} a < n b % n < n
| 0, h => Nat.mod_lt _ h
@@ -141,10 +145,10 @@ instance : ShiftLeft (Fin n) where
instance : ShiftRight (Fin n) where
shiftRight := Fin.shiftRight
instance instOfNat : OfNat (Fin (no_index (n+1))) i where
ofNat := Fin.ofNat i
instance instOfNat {n : Nat} [NeZero n] {i : Nat} : OfNat (Fin n) i where
ofNat := Fin.ofNat' n i
instance : Inhabited (Fin (no_index (n+1))) where
instance instInhabited {n : Nat} [NeZero n] : Inhabited (Fin n) where
default := 0
@[simp] theorem zero_eta : (0, Nat.zero_lt_succ _ : Fin (n + 1)) = 0 := rfl

View File

@@ -51,10 +51,10 @@ theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
theorem mk_val (i : Fin n) : (i, i.isLt : Fin n) = i := Fin.eta ..
@[simp] theorem val_ofNat' (a : Nat) (is_pos : n > 0) :
(Fin.ofNat' a is_pos).val = a % n := rfl
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat' n a).val = a % n := rfl
@[simp] theorem ofNat'_val_eq_self (x : Fin n) (h) : (Fin.ofNat' x h) = x := by
@[simp] theorem ofNat'_val_eq_self [NeZero n](x : Fin n) : (Fin.ofNat' n x) = x := by
ext
rw [val_ofNat', Nat.mod_eq_of_lt]
exact x.2
@@ -750,13 +750,13 @@ theorem addCases_right {m n : Nat} {motive : Fin (m + n) → Sort _} {left right
/-! ### add -/
@[simp] theorem ofNat'_add (x : Nat) (lt : 0 < n) (y : Fin n) :
Fin.ofNat' x lt + y = Fin.ofNat' (x + y.val) lt := by
@[simp] theorem ofNat'_add [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x + y = Fin.ofNat' n (x + y.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.add_def]
@[simp] theorem add_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
x + Fin.ofNat' y lt = Fin.ofNat' (x.val + y) lt := by
@[simp] theorem add_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x + Fin.ofNat' n y = Fin.ofNat' n (x.val + y) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.add_def]
@@ -765,13 +765,13 @@ theorem addCases_right {m n : Nat} {motive : Fin (m + n) → Sort _} {left right
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = ((n - b) + a) % n := by
cases a; cases b; rfl
@[simp] theorem ofNat'_sub (x : Nat) (lt : 0 < n) (y : Fin n) :
Fin.ofNat' x lt - y = Fin.ofNat' ((n - y.val) + x) lt := by
@[simp] theorem ofNat'_sub [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x - y = Fin.ofNat' n ((n - y.val) + x) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.sub_def]
@[simp] theorem sub_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
x - Fin.ofNat' y lt = Fin.ofNat' ((n - y % n) + x.val) lt := by
@[simp] theorem sub_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x - Fin.ofNat' n y = Fin.ofNat' n ((n - y % n) + x.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.sub_def]

View File

@@ -306,6 +306,22 @@ theorem ediv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a / b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_zero_le _
theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a / b := by
match a, b with
| ofNat a, b =>
match Int.le_antisymm Ha (ofNat_zero_le a) with
| h1 =>
rw [h1, zero_ediv]
exact Int.le_refl 0
| a, ofNat b =>
match Int.le_antisymm Hb (ofNat_zero_le b) with
| h1 =>
rw [h1, Int.ediv_zero]
exact Int.le_refl 0
| negSucc a, negSucc b =>
rw [Int.div_def, ediv]
exact le_add_one (ediv_nonneg (ofNat_zero_le a) (Int.le_trans (ofNat_zero_le b) (le.intro 1 rfl)))
theorem ediv_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a / b 0 :=
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)

View File

@@ -48,6 +48,8 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
@[simp] theorem attach_nil : ([] : List α).attach = [] := rfl
@[simp] theorem attachWith_nil : ([] : List α).attachWith P H = [] := rfl
@[simp]
theorem pmap_eq_map (p : α Prop) (f : α β) (l : List α) (H) :
@pmap _ _ p (fun a _ => f a) l H = map f l := by
@@ -81,7 +83,12 @@ theorem attach_congr {l₁ l₂ : List α} (h : l₁ = l₂) :
subst h
simp
@[simp] theorem attach_cons (x : α) (xs : List α) :
theorem attachWith_congr {l₁ l₂ : List α} (w : l₁ = l₂) {P : α Prop} {H : x l₁, P x} :
l₁.attachWith P H = l₂.attachWith P fun x h => H _ (w h) := by
subst w
simp
@[simp] theorem attach_cons {x : α} {xs : List α} :
(x :: xs).attach =
x, mem_cons_self x xs :: xs.attach.map fun y, h => y, mem_cons_of_mem x h := by
simp only [attach, attachWith, pmap, map_pmap, cons.injEq, true_and]
@@ -89,6 +96,12 @@ theorem attach_congr {l₁ l₂ : List α} (h : l₁ = l₂) :
intros a _ m' _
rfl
@[simp]
theorem attachWith_cons {x : α} {xs : List α} {p : α Prop} (h : a x :: xs, p a) :
(x :: xs).attachWith p h = x, h x (mem_cons_self x xs) ::
xs.attachWith p (fun a ha h a (mem_cons_of_mem x ha)) :=
rfl
theorem pmap_eq_map_attach {p : α Prop} (f : a, p a β) (l H) :
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
rw [attach, attachWith, map_pmap]; exact pmap_congr_left l fun _ _ _ _ => rfl
@@ -104,15 +117,37 @@ theorem attach_map_val (l : List α) (f : α → β) : (l.attach.map fun i => f
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
(attach_map_coe _ _).trans (List.map_id _)
theorem attachWith_map_coe {p : α Prop} (f : α β) (l : List α) (H : a l, p a) :
((l.attachWith p H).map fun (i : { i // p i}) => f i) = l.map f := by
rw [attachWith, map_pmap]; exact pmap_eq_map _ _ _ _
theorem attachWith_map_val {p : α Prop} (f : α β) (l : List α) (H : a l, p a) :
((l.attachWith p H).map fun i => f i.val) = l.map f :=
attachWith_map_coe _ _ _
@[simp]
theorem attachWith_map_subtype_val {p : α Prop} (l : List α) (H : a l, p a) :
(l.attachWith p H).map Subtype.val = l :=
(attachWith_map_coe _ _ _).trans (List.map_id _)
theorem countP_attach (l : List α) (p : α Bool) :
l.attach.countP (fun a : {x // x l} => p a) = l.countP p := by
simp only [ Function.comp_apply (g := Subtype.val), countP_map, attach_map_subtype_val]
theorem countP_attachWith {p : α Prop} (l : List α) (H : a l, p a) (q : α Bool) :
(l.attachWith p H).countP (fun a : {x // p x} => q a) = l.countP q := by
simp only [ Function.comp_apply (g := Subtype.val), countP_map, attachWith_map_subtype_val]
@[simp]
theorem count_attach [DecidableEq α] (l : List α) (a : {x // x l}) :
l.attach.count a = l.count a :=
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attach _ _
@[simp]
theorem count_attachWith [DecidableEq α] {p : α Prop} (l : List α) (H : a l, p a) (a : {x // p x}) :
(l.attachWith p H).count a = l.count a :=
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attachWith _ _ _
@[simp]
theorem mem_attach (l : List α) : x, x l.attach
| a, h => by
@@ -137,7 +172,11 @@ theorem length_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H} : length (pm
· simp only [*, pmap, length]
@[simp]
theorem length_attach (L : List α) : L.attach.length = L.length :=
theorem length_attach {L : List α} : L.attach.length = L.length :=
length_pmap
@[simp]
theorem length_attachWith {p : α Prop} {l H} : length (l.attachWith p H) = length l :=
length_pmap
@[simp]
@@ -155,6 +194,15 @@ theorem attach_eq_nil_iff {l : List α} : l.attach = [] ↔ l = [] :=
theorem attach_ne_nil_iff {l : List α} : l.attach [] l [] :=
pmap_ne_nil_iff _ _
@[simp]
theorem attachWith_eq_nil_iff {l : List α} {P : α Prop} {H : a l, P a} :
l.attachWith P H = [] l = [] :=
pmap_eq_nil_iff
theorem attachWith_ne_nil_iff {l : List α} {P : α Prop} {H : a l, P a} :
l.attachWith P H [] l [] :=
pmap_ne_nil_iff _ _
@[deprecated pmap_eq_nil_iff (since := "2024-09-06")] abbrev pmap_eq_nil := @pmap_eq_nil_iff
@[deprecated pmap_ne_nil_iff (since := "2024-09-06")] abbrev pmap_ne_nil := @pmap_ne_nil_iff
@[deprecated attach_eq_nil_iff (since := "2024-09-06")] abbrev attach_eq_nil := @attach_eq_nil_iff
@@ -187,7 +235,7 @@ theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h
(hn : n < (pmap f l h).length) :
(pmap f l h)[n] =
f (l[n]'(@length_pmap _ _ p f l h hn))
(h _ (getElem_mem l n (@length_pmap _ _ p f l h hn))) := by
(h _ (getElem_mem (@length_pmap _ _ p f l h hn))) := by
induction l generalizing n with
| nil =>
simp only [length, pmap] at hn
@@ -205,34 +253,26 @@ theorem get_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h :
simp only [get_eq_getElem]
simp [getElem_pmap]
@[simp]
theorem getElem?_attachWith {xs : List α} {i : Nat} {P : α Prop} {H : a xs, P a} :
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (getElem?_mem a)) :=
getElem?_pmap ..
@[simp]
theorem getElem?_attach {xs : List α} {i : Nat} :
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => getElem?_mem a) := by
induction xs generalizing i with
| nil => simp
| cons x xs ih =>
rcases i with i
· simp only [attach_cons, Option.pmap]
split <;> simp_all
· simp only [attach_cons, getElem?_cons_succ, getElem?_map, ih]
simp only [Option.pmap]
split <;> split <;> simp_all
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => getElem?_mem a) :=
getElem?_attachWith
@[simp]
theorem getElem_attachWith {xs : List α} {P : α Prop} {H : a xs, P a}
{i : Nat} (h : i < (xs.attachWith P H).length) :
(xs.attachWith P H)[i] = xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h)) :=
getElem_pmap ..
@[simp]
theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
xs.attach[i] = xs[i]'(by simpa using h), getElem_mem xs i (by simpa using h) := by
apply Option.some.inj
rw [ getElem?_eq_getElem]
rw [getElem?_attach]
simp only [Option.pmap]
split <;> rename_i h' _
· simp at h
simp at h'
exfalso
exact Nat.lt_irrefl _ (Nat.lt_of_le_of_lt h' h)
· simp only [Option.some.injEq, Subtype.mk.injEq]
apply Option.some.inj
rw [ getElem?_eq_getElem, h']
xs.attach[i] = xs[i]'(by simpa using h), getElem_mem (by simpa using h) :=
getElem_attachWith h
@[simp] theorem head?_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) :
@@ -250,11 +290,23 @@ theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
| nil => simp at h
| cons x xs ih => simp [head_pmap, ih]
@[simp] theorem head?_attachWith {P : α Prop} {xs : List α}
(H : (a : α), a xs P a) :
(xs.attachWith P H).head? = xs.head?.pbind (fun a h => some a, H _ (mem_of_mem_head? h)) := by
cases xs <;> simp_all
@[simp] theorem head_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} (h : xs.attachWith P H []) :
(xs.attachWith P H).head h = xs.head (by simpa using h), H _ (head_mem _) := by
cases xs with
| nil => simp at h
| cons x xs => simp [head_attachWith, h]
@[simp] theorem head?_attach (xs : List α) :
xs.attach.head? = xs.head?.pbind (fun a h => some a, mem_of_mem_head? h) := by
cases xs <;> simp_all
theorem head_attach {xs : List α} (h) :
@[simp] theorem head_attach {xs : List α} (h) :
xs.attach.head h = xs.head (by simpa using h), head_mem (by simpa using h) := by
cases xs with
| nil => simp at h
@@ -264,6 +316,32 @@ theorem attach_map {l : List α} (f : α → β) :
(l.map f).attach = l.attach.map (fun x, h => f x, mem_map_of_mem f h) := by
induction l <;> simp [*]
theorem attachWith_map {l : List α} (f : α β) {P : β Prop} {H : (b : β), b l.map f P b} :
(l.map f).attachWith P H = (l.attachWith (P f) (fun a h => H _ (mem_map_of_mem f h))).map
fun x, h => f x, h := by
induction l <;> simp [*]
theorem map_attachWith {l : List α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f =
l.pmap (fun a (h : a l P a) => f a, H _ h.1) (fun a h => h, H a h) := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [attachWith_cons, map_cons, ih, pmap, cons.injEq, true_and]
apply pmap_congr_left
simp
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
theorem map_attach {l : List α} (f : { x // x l } β) :
l.attach.map f = l.pmap (fun a h => f a, h) (fun _ => id) := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [attach_cons, map_cons, map_map, Function.comp_apply, pmap, cons.injEq, true_and, ih]
apply pmap_congr_left
simp
theorem attach_filterMap {l : List α} {f : α Option β} :
(l.filterMap f).attach = l.attach.filterMap
fun x, h => (f x).pbind (fun b m => some b, mem_filterMap.mpr x, h, m) := by
@@ -304,6 +382,9 @@ theorem attach_filter {l : List α} (p : α → Bool) :
ext1
split <;> simp
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
-- Also missing are `filterMap_attach`, `filter_attach`, `filterMap_attachWith` and `filter_attachWith`.
theorem pmap_pmap {p : α Prop} {q : β Prop} (g : a, p a β) (f : b, q b γ) (l H₁ H₂) :
pmap f (pmap g l H₁) H₂ =
pmap (α := { x // x l }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) l.attach
@@ -334,6 +415,12 @@ theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ l₂ :
congr 1 <;>
exact pmap_congr_left _ fun _ _ _ _ => rfl
@[simp] theorem attachWith_append {P : α Prop} {xs ys : List α}
{H : (a : α), a xs ++ ys P a} :
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_of_mem_left ys h)) ++
ys.attachWith P (fun a h => H a (mem_append_of_mem_right xs h)) := by
simp only [attachWith, attach_append, map_pmap, pmap_append]
@[simp] theorem pmap_reverse {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs.reverse P a) :
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
@@ -344,6 +431,17 @@ theorem reverse_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
rw [pmap_reverse]
@[simp] theorem attachWith_reverse {P : α Prop} {xs : List α}
{H : (a : α), a xs.reverse P a} :
xs.reverse.attachWith P H =
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse :=
pmap_reverse ..
theorem reverse_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) :=
reverse_pmap ..
@[simp] theorem attach_reverse (xs : List α) :
xs.reverse.attach = xs.attach.reverse.map fun x, h => x, by simpa using h := by
simp only [attach, attachWith, reverse_pmap, map_pmap]
@@ -358,17 +456,6 @@ theorem reverse_attach (xs : List α) :
intros
rfl
@[simp]
theorem getLast?_attach {xs : List α} :
xs.attach.getLast? = xs.getLast?.pbind fun a h => some a, mem_of_getLast?_eq_some h := by
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map, head?_attach]
simp
@[simp]
theorem getLast_attach {xs : List α} (h : xs.attach []) :
xs.attach.getLast h = xs.getLast (by simpa using h), getLast_mem (by simpa using h) := by
simp only [getLast_eq_head_reverse, reverse_attach, head_map, head_attach]
@[simp] theorem getLast?_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) :
(xs.pmap f H).getLast? = xs.attach.getLast?.map fun a, m => f a (H a m) := by
@@ -383,4 +470,26 @@ theorem getLast_attach {xs : List α} (h : xs.attach ≠ []) :
simp only [getLast_eq_head_reverse]
simp only [reverse_pmap, head_pmap, head_reverse]
@[simp] theorem getLast?_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).getLast? = xs.getLast?.pbind (fun a h => some a, H _ (mem_of_getLast?_eq_some h)) := by
rw [getLast?_eq_head?_reverse, reverse_attachWith, head?_attachWith]
simp
@[simp] theorem getLast_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} (h : xs.attachWith P H []) :
(xs.attachWith P H).getLast h = xs.getLast (by simpa using h), H _ (getLast_mem _) := by
simp only [getLast_eq_head_reverse, reverse_attachWith, head_attachWith, head_map]
@[simp]
theorem getLast?_attach {xs : List α} :
xs.attach.getLast? = xs.getLast?.pbind fun a h => some a, mem_of_getLast?_eq_some h := by
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map, head?_attach]
simp
@[simp]
theorem getLast_attach {xs : List α} (h : xs.attach []) :
xs.attach.getLast h = xs.getLast (by simpa using h), getLast_mem (by simpa using h) := by
simp only [getLast_eq_head_reverse, reverse_attach, head_map, head_attach]
end List

View File

@@ -1588,6 +1588,14 @@ such that adjacent elements are related by `R`.
| [] => []
| a::as => loop as a [] []
where
/--
The arguments of `groupBy.loop l ag g gs` represent the following:
- `l : List α` are the elements which we still need to group.
- `ag : α` is the previous element for which a comparison was performed.
- `g : List α` is the group currently being assembled, in **reverse order**.
- `gs : List (List α)` is all of the groups that have been completed, in **reverse order**.
-/
@[specialize] loop : List α α List α List (List α) List (List α)
| a::as, ag, g, gs => match R ag a with
| true => loop as a (ag::g) gs

View File

@@ -155,7 +155,7 @@ def mapMono (as : List α) (f : αα) : List α :=
/-! ## Additional lemmas required for bootstrapping `Array`. -/
theorem getElem_append_left (as bs : List α) (h : i < as.length) {h'} : (as ++ bs)[i] = as[i] := by
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h'} : (as ++ bs)[i] = as[i] := by
induction as generalizing i with
| nil => trivial
| cons a as ih =>
@@ -163,12 +163,14 @@ theorem getElem_append_left (as bs : List α) (h : i < as.length) {h'} : (as ++
| zero => rfl
| succ i => apply ih
theorem getElem_append_right (as bs : List α) (h : ¬ i < as.length) {h' h''} : (as ++ bs)[i]'h' = bs[i - as.length]'h'' := by
theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length i) {h₂} :
(as ++ bs)[i]'h₂ =
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by
induction as generalizing i with
| nil => trivial
| cons a as ih =>
cases i with simp [get, Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h
| succ i => apply ih; simp [h]
cases i with simp [get, Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h
| succ i => apply ih; simp [h]
theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.length) : (as ++ [a] : List _).get i = a := by
cases i; rename_i i h'

View File

@@ -119,12 +119,12 @@ theorem countP_filter (l : List α) :
countP p (filter q l) = countP (fun a => p a && q a) l := by
simp only [countP_eq_length_filter, filter_filter]
@[simp] theorem countP_true {l : List α} : (l.countP fun _ => true) = l.length := by
rw [countP_eq_length]
@[simp] theorem countP_true : (countP fun (_ : α) => true) = length := by
funext l
simp
@[simp] theorem countP_false {l : List α} : (l.countP fun _ => false) = 0 := by
rw [countP_eq_zero]
@[simp] theorem countP_false : (countP fun (_ : α) => false) = Function.const _ 0 := by
funext l
simp
@[simp] theorem countP_map (p : β Bool) (f : α β) :

View File

@@ -627,11 +627,24 @@ theorem IsPrefix.findIdx_eq_of_findIdx_lt_length {l₁ l₂ : List α} {p : α
@[simp] theorem findIdx?_cons :
(x :: xs).findIdx? p i = if p x then some i else findIdx? p xs (i + 1) := rfl
@[simp] theorem findIdx?_succ :
theorem findIdx?_succ :
(xs : List α).findIdx? p (i+1) = (xs.findIdx? p i).map fun i => i + 1 := by
induction xs generalizing i with simp
| cons _ _ _ => split <;> simp_all
@[simp] theorem findIdx?_start_succ :
(xs : List α).findIdx? p (i+1) = (xs.findIdx? p 0).map fun k => k + (i + 1) := by
induction xs generalizing i with
| nil => simp
| cons _ _ _ =>
simp only [findIdx?_succ, findIdx?_cons, Nat.zero_add]
split
· simp_all
· simp_all only [findIdx?_succ, Bool.not_eq_true, Option.map_map, Nat.zero_add]
congr
ext
simp only [Nat.add_comm i, Function.comp_apply, Nat.add_assoc]
@[simp]
theorem findIdx?_eq_none_iff {xs : List α} {p : α Bool} :
xs.findIdx? p = none x, x xs p x = false := by
@@ -683,6 +696,16 @@ theorem findIdx?_eq_none_iff_findIdx_eq {xs : List α} {p : α → Bool} :
xs.findIdx? p = none xs.findIdx p = xs.length := by
simp
theorem findIdx?_eq_guard_findIdx_lt {xs : List α} {p : α Bool} :
xs.findIdx? p = Option.guard (fun i => i < xs.length) (xs.findIdx p) := by
match h : xs.findIdx? p with
| none =>
simp only [findIdx?_eq_none_iff] at h
simp [findIdx_eq_length_of_false h, Option.guard]
| some i =>
simp only [findIdx?_eq_some_iff_findIdx_eq] at h
simp [h]
theorem findIdx?_eq_some_iff_getElem {xs : List α} {p : α Bool} {i : Nat} :
xs.findIdx? p = some i
h : i < xs.length, p xs[i] j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji h)) := by

View File

@@ -109,6 +109,9 @@ theorem cons_eq_cons {a b : α} {l l' : List α} : a :: l = b :: l' ↔ a = b
theorem exists_cons_of_ne_nil : {l : List α}, l [] b L, l = b :: L
| c :: l', _ => c, l', rfl
theorem singleton_inj {α : Type _} {a b : α} : [a] = [b] a = b := by
simp
/-! ### length -/
theorem eq_nil_of_length_eq_zero (_ : length l = 0) : l = [] := match l with | [] => rfl
@@ -480,9 +483,9 @@ theorem getElem?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n : Nat, l[n]? = s
theorem get?_of_mem {a} {l : List α} (h : a l) : n, l.get? n = some a :=
let n, _, e := get_of_mem h; n, e get?_eq_get _
theorem getElem_mem : (l : List α) n (h : n < l.length), l[n]'h l
theorem getElem_mem : {l : List α} {n} (h : n < l.length), l[n]'h l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (getElem_mem l ..)
| _ :: l, _+1, _ => .tail _ (getElem_mem (l := l) ..)
theorem get_mem : (l : List α) n h, get l n, h l
| _ :: _, 0, _ => .head ..
@@ -524,7 +527,7 @@ theorem forall_getElem {l : List α} {p : α → Prop} :
· simpa
· apply w
simp only [getElem_cons_succ]
exact getElem_mem l n (lt_of_succ_lt_succ h)
exact getElem_mem (lt_of_succ_lt_succ h)
@[simp] theorem decide_mem_cons [BEq α] [LawfulBEq α] {l : List α} :
decide (y a :: l) = (y == a || decide (y l)) := by
@@ -572,17 +575,25 @@ theorem any_eq {l : List α} : l.any p = decide (∃ x, x ∈ l ∧ p x) := by i
theorem all_eq {l : List α} : l.all p = decide ( x, x l p x) := by induction l <;> simp [*]
@[simp] theorem any_decide {l : List α} {p : α Prop} [DecidablePred p] :
l.any p = decide ( x, x l p x) := by
theorem decide_exists_mem {l : List α} {p : α Prop} [DecidablePred p] :
decide ( x, x l p x) = l.any p := by
simp [any_eq]
@[simp] theorem all_decide {l : List α} {p : α Prop} [DecidablePred p] :
l.all p = decide ( x, x l p x) := by
theorem decide_forall_mem {l : List α} {p : α Prop} [DecidablePred p] :
decide ( x, x l p x) = l.all p := by
simp [all_eq]
@[simp] theorem any_eq_true {l : List α} : l.any p = true x, x l p x := by simp [any_eq]
@[simp] theorem any_eq_true {l : List α} : l.any p = true x, x l p x := by
simp only [any_eq, decide_eq_true_eq]
@[simp] theorem all_eq_true {l : List α} : l.all p = true x, x l p x := by simp [all_eq]
@[simp] theorem all_eq_true {l : List α} : l.all p = true x, x l p x := by
simp only [all_eq, decide_eq_true_eq]
@[simp] theorem any_eq_false {l : List α} : l.any p = false x, x l ¬p x := by
simp [any_eq]
@[simp] theorem all_eq_false {l : List α} : l.all p = false x, x l ¬p x := by
simp [all_eq]
/-! ### set -/
@@ -721,6 +732,45 @@ theorem mem_or_eq_of_mem_set : ∀ {l : List α} {n : Nat} {a b : α}, a ∈ l.s
-- See also `set_eq_take_append_cons_drop` in `Init.Data.List.TakeDrop`.
/-! ### BEq -/
@[simp] theorem reflBEq_iff [BEq α] : ReflBEq (List α) ReflBEq α := by
constructor
· intro h
constructor
intro a
suffices ([a] == [a]) = true by
simpa only [List.instBEq, List.beq, Bool.and_true]
simp
· intro h
constructor
intro a
induction a with
| nil => simp only [List.instBEq, List.beq]
| cons a as ih =>
simp [List.instBEq, List.beq]
exact ih
@[simp] theorem lawfulBEq_iff [BEq α] : LawfulBEq (List α) LawfulBEq α := by
constructor
· intro h
constructor
· intro a b h
apply singleton_inj.1
apply eq_of_beq
simp only [List.instBEq, List.beq]
simpa
· intro a
suffices ([a] == [a]) = true by
simpa only [List.instBEq, List.beq, Bool.and_true]
simp
· intro h
constructor
· intro a b h
simpa using h
· intro a
simp
/-! ### Lexicographic ordering -/
protected theorem lt_irrefl [LT α] (lt_irrefl : x : α, ¬x < x) (l : List α) : ¬l < l := by
@@ -921,6 +971,10 @@ theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
| [_], _ => .head ..
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
theorem getLast_mem_getLast? : {l : List α} (h : l []), getLast l h getLast? l
| [], h => by contradiction
| a :: l, _ => rfl
theorem getLastD_mem_cons : (l : List α) (a : α), getLastD l a a::l
| [], _ => .head ..
| _::_, _ => .tail _ <| getLast_mem _
@@ -1018,6 +1072,10 @@ theorem mem_of_mem_head? : ∀ {l : List α} {a : α}, a ∈ l.head? → a ∈ l
cases h
exact mem_cons_self a l
theorem head_mem_head? : {l : List α} (h : l []), head l h head? l
| [], h => by contradiction
| a :: l, _ => rfl
theorem head?_concat {a : α} : (l ++ [a]).head? = l.head?.getD a := by
cases l <;> simp
@@ -1044,6 +1102,9 @@ theorem tail_eq_tailD (l) : @tail α l = tailD l [] := by cases l <;> rfl
theorem tail_eq_tail? (l) : @tail α l = (tail? l).getD [] := by simp [tail_eq_tailD]
theorem mem_of_mem_tail {a : α} {l : List α} (h : a tail l) : a l := by
induction l <;> simp_all
/-! ## Basic operations -/
/-! ### map -/
@@ -1496,10 +1557,11 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
/-! ### append -/
theorem getElem_append : {l₁ l₂ : List α} (n : Nat) (h : n < l₁.length),
(l₁ ++ l₂)[n]'(length_append .. Nat.lt_add_right _ h) = l₁[n]
| a :: l, _, 0, h => rfl
| a :: l, _, n+1, h => by simp only [get, cons_append]; apply getElem_append
theorem getElem_append {l₁ l₂ : List α} (n : Nat) (h) :
(l₁ ++ l₂)[n] = if h' : n < l₁.length then l₁[n] else l₂[n - l₁.length]'(by simp at h h'; exact Nat.sub_lt_left_of_lt_add h' h) := by
split <;> rename_i h'
· rw [getElem_append_left h']
· rw [getElem_append_right (by simpa using h')]
theorem getElem?_append_left {l₁ l₂ : List α} {n : Nat} (hn : n < l₁.length) :
(l₁ ++ l₂)[n]? = l₁[n]? := by
@@ -1525,12 +1587,13 @@ theorem get?_append_right {l₁ l₂ : List α} {n : Nat} (h : l₁.length ≤ n
(l₁ ++ l₂).get? n = l₂.get? (n - l₁.length) := by
simp [getElem?_append_right, h]
theorem getElem_append_right' {l₁ l₂ : List α} {n : Nat} (h₁ : l₁.length n) (h₂) :
(l₁ ++ l₂)[n]'h₂ =
l[n - l₁.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h) :=
Option.some.inj <| by rw [ getElem?_eq_getElem, getElem?_eq_getElem, getElem?_append_right h₁]
/-- Variant of `getElem_append_left` useful for rewriting from the small list to the big list. -/
theorem getElem_append_left' (l₂ : List α) {l₁ : List α} {n : Nat} (hn : n < l₁.length) :
l[n] = (l₁ ++ l₂)[n]'(by simpa using Nat.lt_add_right l₂.length hn) := by
rw [getElem_append_left] <;> simp
theorem getElem_append_right'' (l₁ : List α) {l₂ : List α} {n : Nat} (hn : n < l₂.length) :
/-- Variant of `getElem_append_right` useful for rewriting from the small list to the big list. -/
theorem getElem_append_right' (l₁ : List α) {l₂ : List α} {n : Nat} (hn : n < l₂.length) :
l₂[n] = (l₁ ++ l₂)[n + l₁.length]'(by simpa [Nat.add_comm] using Nat.add_lt_add_left hn _) := by
rw [getElem_append_right] <;> simp [*, le_add_left]
@@ -1541,7 +1604,7 @@ theorem get_append_right_aux {l₁ l₂ : List α} {n : Nat}
exact Nat.sub_lt_left_of_lt_add h₁ h₂
set_option linter.deprecated false in
@[deprecated getElem_append_right' (since := "2024-06-12")]
@[deprecated getElem_append_right (since := "2024-06-12")]
theorem get_append_right' {l₁ l₂ : List α} {n : Nat} (h₁ : l₁.length n) (h₂) :
(l₁ ++ l₂).get n, h₂ = l₂.get n - l₁.length, get_append_right_aux h₁ h₂ :=
Option.some.inj <| by rw [ get?_eq_get, get?_eq_get, get?_append_right h₁]
@@ -1633,7 +1696,7 @@ theorem get_append_left (as bs : List α) (h : i < as.length) {h'} :
simp [getElem_append_left, h, h']
@[deprecated getElem_append_right (since := "2024-06-12")]
theorem get_append_right (as bs : List α) (h : ¬ i < as.length) {h' h''} :
theorem get_append_right (as bs : List α) (h : as.length i) {h' h''} :
(as ++ bs).get i, h' = bs.get i - as.length, h'' := by
simp [getElem_append_right, h, h', h'']
@@ -2312,6 +2375,47 @@ theorem bind_replicate {β} (f : α → List β) : (replicate n a).bind f = (rep
@[simp] theorem isEmpty_replicate : (replicate n a).isEmpty = decide (n = 0) := by
cases n <;> simp [replicate_succ]
/-- Every list is either empty, a non-empty `replicate`, or begins with a non-empty `replicate`
followed by a different element. -/
theorem eq_replicate_or_eq_replicate_append_cons {α : Type _} (l : List α) :
(l = []) ( n a, l = replicate n a 0 < n)
( n a b l', l = replicate n a ++ b :: l' 0 < n a b) := by
induction l with
| nil => simp
| cons x l ih =>
right
rcases ih with rfl | n, a, rfl, h | n, a, b, l', rfl, h
· left
exact 1, x, rfl, by decide
· by_cases h' : x = a
· subst h'
left
exact n + 1, x, rfl, by simp
· right
refine 1, x, a, replicate (n - 1) a, ?_, by decide, h'
match n with | n + 1 => simp [replicate_succ]
· right
by_cases h' : x = a
· subst h'
refine n + 1, x, b, l', by simp [replicate_succ], by simp, h.2
· refine 1, x, a, replicate (n - 1) a ++ b :: l', ?_, by decide, h'
match n with | n + 1 => simp [replicate_succ]
/-- An induction principle for lists based on contiguous runs of identical elements. -/
-- A `Sort _` valued version would require a different design. (And associated `@[simp]` lemmas.)
theorem replicateRecOn {α : Type _} {p : List α Prop} (m : List α)
(h0 : p []) (hr : a n, 0 < n p (replicate n a))
(hi : a b n l, a b 0 < n p (b :: l) p (replicate n a ++ b :: l)) : p m := by
rcases eq_replicate_or_eq_replicate_append_cons m with
rfl | n, a, rfl, hn | n, a, b, l', w, hn, h
· exact h0
· exact hr _ _ hn
· have : (b :: l').length < m.length := by
simpa [w] using Nat.lt_add_of_pos_left hn
subst w
exact hi _ _ _ _ h hn (replicateRecOn (b :: l') h0 hr hi)
termination_by m.length
/-! ### reverse -/
@[simp] theorem length_reverse (as : List α) : (as.reverse).length = as.length := by

View File

@@ -176,7 +176,7 @@ theorem pairwise_le_range (n : Nat) : Pairwise (· ≤ ·) (range n) :=
theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
apply List.ext_getElem
· simp
· simp (config := { contextual := true }) [ getElem_take, Nat.lt_min]
· simp (config := { contextual := true }) [getElem_take, Nat.lt_min]
theorem nodup_range (n : Nat) : Nodup (range n) := by
simp (config := {decide := true}) only [range_eq_range', nodup_range']

View File

@@ -36,23 +36,23 @@ theorem length_take_of_le (h : n ≤ length l) : length (take n l) = n := by sim
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
theorem getElem_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
theorem getElem_take' (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
L[i] = (L.take j)[i]'(length_take .. Nat.lt_min.mpr hj, hi) :=
getElem_of_eq (take_append_drop j L).symm _ getElem_append ..
getElem_of_eq (take_append_drop j L).symm _ getElem_append_left ..
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
theorem getElem_take' (L : List α) {j i : Nat} {h : i < (L.take j).length} :
theorem getElem_take (L : List α) {j i : Nat} {h : i < (L.take j).length} :
(L.take j)[i] =
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
rw [length_take, Nat.lt_min] at h; rw [getElem_take L _ h.1]
rw [length_take, Nat.lt_min] at h; rw [getElem_take' L _ h.1]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
@[deprecated getElem_take (since := "2024-06-12")]
@[deprecated getElem_take' (since := "2024-06-12")]
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
get L i, hi = get (L.take j) i, length_take .. Nat.lt_min.mpr hj, hi := by
simp [getElem_take _ hi hj]
simp [getElem_take' _ hi hj]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
@@ -60,7 +60,7 @@ length `> i`. Version designed to rewrite from the small list to the big list. -
theorem get_take' (L : List α) {j i} :
get (L.take j) i =
get L i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _) := by
simp [getElem_take']
simp [getElem_take]
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n m) :
(l.take n)[m]? = none :=
@@ -110,7 +110,7 @@ theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none e
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']
rw [getLast_eq_getElem, getElem_take]
simp [length_take, Nat.min_def]
simp at h
split
@@ -191,20 +191,12 @@ theorem dropLast_take {n : Nat} {l : List α} (h : n < l.length) :
(l.take n).dropLast = l.take (n - 1) := by
simp only [dropLast_eq_take, length_take, Nat.le_of_lt h, Nat.min_eq_left, take_take, sub_le]
theorem map_eq_append_split {f : α β} {l : List α} {s₁ s₂ : List β}
(h : map f l = s₁ ++ s₂) : l₁ l₂, l = l₁ ++ l₂ map f l₁ = s₁ map f l₂ = s₂ := by
have := h
rw [ take_append_drop (length s₁) l] at this
rw [map_append] at this
refine _, _, rfl, append_inj this ?_
rw [length_map, length_take, Nat.min_eq_left]
rw [ length_map l f, h, length_append]
apply Nat.le_add_right
@[deprecated map_eq_append_iff (since := "2024-09-05")] abbrev map_eq_append_split := @map_eq_append_iff
theorem take_prefix_take_left (l : List α) {m n : Nat} (h : m n) : take m l <+: take n l := by
rw [isPrefix_iff]
intro i w
rw [getElem?_take_of_lt, getElem_take', getElem?_eq_getElem]
rw [getElem?_take_of_lt, getElem_take, getElem?_eq_getElem]
simp only [length_take] at w
exact Nat.lt_of_lt_of_le (Nat.lt_of_lt_of_le w (Nat.min_le_left _ _)) h
@@ -227,8 +219,9 @@ dropping the first `i` elements. Version designed to rewrite from the big list t
theorem getElem_drop' (L : List α) {i j : Nat} (h : i + j < L.length) :
L[i + j] = (L.drop i)[j]'(lt_length_drop L h) := by
have : i L.length := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_lt h)
rw [getElem_of_eq (take_append_drop i L).symm h, getElem_append_right'] <;>
simp [Nat.min_eq_left this, Nat.add_sub_cancel_left, Nat.le_add_right]
rw [getElem_of_eq (take_append_drop i L).symm h, getElem_append_right]
· simp [Nat.min_eq_left this, Nat.add_sub_cancel_left]
· simp [Nat.min_eq_left this, Nat.le_add_right]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
@@ -275,9 +268,9 @@ theorem mem_take_iff_getElem {l : List α} {a : α} :
constructor
· rintro i, hm, rfl
simp at hm
refine i, by omega, by rw [getElem_take']
refine i, by omega, by rw [getElem_take]
· rintro i, hm, rfl
refine i, by simpa, by rw [getElem_take']
refine i, by simpa, by rw [getElem_take]
theorem mem_drop_iff_getElem {l : List α} {a : α} :
a l.drop n (i : Nat) (hm : i + n < l.length), l[n + i] = a := by
@@ -464,7 +457,7 @@ theorem false_of_mem_take_findIdx {xs : List α} {p : α → Bool} (h : x ∈ xs
obtain i, h, rfl := h
exact not_of_lt_findIdx (by omega)
theorem findIdx_take {xs : List α} {n : Nat} {p : α Bool} :
@[simp] theorem findIdx_take {xs : List α} {n : Nat} {p : α Bool} :
(xs.take n).findIdx p = min n (xs.findIdx p) := by
induction xs generalizing n with
| nil => simp
@@ -476,6 +469,44 @@ theorem findIdx_take {xs : List α} {n : Nat} {p : α → Bool} :
· simp
· rw [Nat.add_min_add_right]
@[simp] theorem findIdx?_take {xs : List α} {n : Nat} {p : α Bool} :
(xs.take n).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun i => i < n)) := by
induction xs generalizing n with
| nil => simp
| cons x xs ih =>
cases n
· simp
· simp only [take_succ_cons, findIdx?_cons]
split
· simp
· simp [ih, Option.guard_comp, Option.bind_map]
@[simp] theorem min_findIdx_findIdx {xs : List α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx_cons, cond_eq_if, Bool.not_eq_eq_eq_not, Bool.not_true]
split <;> split <;> simp_all [Nat.add_min_add_right]
/-! ### takeWhile -/
theorem takeWhile_eq_take_findIdx_not {xs : List α} {p : α Bool} :
takeWhile p xs = take (xs.findIdx (fun a => !p a)) xs := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [takeWhile_cons, ih, findIdx_cons, cond_eq_if, Bool.not_eq_eq_eq_not, Bool.not_true]
split <;> simp_all
theorem dropWhile_eq_drop_findIdx_not {xs : List α} {p : α Bool} :
dropWhile p xs = drop (xs.findIdx (fun a => !p a)) xs := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [dropWhile_cons, ih, findIdx_cons, cond_eq_if, Bool.not_eq_eq_eq_not, Bool.not_true]
split <;> simp_all
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_replicate (n) (a : α) : rotateLeft (replicate m a) n = replicate m a := by

View File

@@ -22,17 +22,18 @@ namespace List
This version is not tail-recursive,
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
-/
def merge (le : α α Bool) : List α List α List α
def merge (xs ys : List α) (le : α α Bool := by exact fun a b => a b) : List α :=
match xs, ys with
| [], ys => ys
| xs, [] => xs
| x :: xs, y :: ys =>
if le x y then
x :: merge le xs (y :: ys)
x :: merge xs (y :: ys) le
else
y :: merge le (x :: xs) ys
y :: merge (x :: xs) ys le
@[simp] theorem nil_merge (ys : List α) : merge le [] ys = ys := by simp [merge]
@[simp] theorem merge_right (xs : List α) : merge le xs [] = xs := by
@[simp] theorem nil_merge (ys : List α) : merge [] ys le = ys := by simp [merge]
@[simp] theorem merge_right (xs : List α) : merge xs [] le = xs := by
induction xs with
| nil => simp [merge]
| cons x xs ih => simp [merge, ih]
@@ -45,6 +46,7 @@ def splitInTwo (l : { l : List α // l.length = n }) :
let r := splitAt ((n+1)/2) l.1
(r.1, by simp [r, splitAt_eq, l.2]; omega, r.2, by simp [r, splitAt_eq, l.2]; omega)
set_option linter.unusedVariables false in
/--
Simplified implementation of stable merge sort.
@@ -56,16 +58,15 @@ It is replaced at runtime in the compiler by `mergeSortTR₂` using a `@[csimp]`
Because we want the sort to be stable,
it is essential that we split the list in two contiguous sublists.
-/
def mergeSort (le : α α Bool) : List α List α
| [] => []
| [a] => [a]
| a :: b :: xs =>
def mergeSort : (xs : List α) (le : α α Bool := by exact fun a b => a b), List α
| [], _ => []
| [a], _ => [a]
| a :: b :: xs, le =>
let lr := splitInTwo a :: b :: xs, rfl
have := by simpa using lr.2.2
have := by simpa using lr.1.2
merge le (mergeSort le lr.1) (mergeSort le lr.2)
termination_by l => l.length
merge (mergeSort lr.1 le) (mergeSort lr.2 le) le
termination_by xs => xs.length
/--
Given an ordering relation `le : αα → Bool`,

View File

@@ -38,7 +38,7 @@ namespace List.MergeSort.Internal
/--
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
-/
def mergeTR (le : α α Bool) (l l₂ : List α) : List α :=
def mergeTR (l l₂ : List α) (le : α α Bool) : List α :=
go l₁ l₂ []
where go : List α List α List α List α
| [], l₂, acc => reverseAux acc l₂
@@ -49,7 +49,7 @@ where go : List α → List α → List α → List α
else
go (x :: xs) ys (y :: acc)
theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge le l₁ l₂ := by
theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge l₁ l₂ le := by
induction l₁ generalizing l₂ acc with
| nil => simp [mergeTR.go, merge, reverseAux_eq]
| cons x l₁ ih₁ =>
@@ -97,14 +97,14 @@ This version uses the tail-recurive `mergeTR` function as a subroutine.
This is not the final version we use at runtime, as `mergeSortTR₂` is faster.
This definition is useful as an intermediate step in proving the `@[csimp]` lemma for `mergeSortTR₂`.
-/
def mergeSortTR (le : α α Bool) (l : List α) : List α :=
def mergeSortTR (l : List α) (le : α α Bool := by exact fun a b => a b) : List α :=
run l, rfl
where run : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitInTwo xs
mergeTR le (run l) (run r)
mergeTR (run l) (run r) le
/--
Split a list in two equal parts, reversing the first part.
@@ -130,7 +130,7 @@ Faster version of `mergeSortTR`, which avoids unnecessary list reversals.
-- Per the benchmark in `tests/bench/mergeSort/`
-- (which averages over 4 use cases: already sorted lists, reverse sorted lists, almost sorted lists, and random lists),
-- for lists of length 10^6, `mergeSortTR₂` is about 20% faster than `mergeSortTR`.
def mergeSortTR₂ (le : α α Bool) (l : List α) : List α :=
def mergeSortTR₂ (l : List α) (le : α α Bool := by exact fun a b => a b) : List α :=
run l, rfl
where
run : {n : Nat} { l : List α // l.length = n } List α
@@ -138,13 +138,13 @@ where
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitRevInTwo xs
mergeTR le (run' l) (run r)
mergeTR (run' l) (run r) le
run' : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitRevInTwo' xs
mergeTR le (run' r) (run l)
mergeTR (run' r) (run l) le
theorem splitRevInTwo'_fst (l : { l : List α // l.length = n }) :
(splitRevInTwo' l).1 = (splitInTwo l.1.reverse, by simpa using l.2).2.1, by have := l.2; simp; omega := by
@@ -166,7 +166,7 @@ theorem splitRevInTwo_snd (l : { l : List α // l.length = n }) :
(splitRevInTwo l).2 = (splitInTwo l).2.1, by have := l.2; simp; omega := by
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_snd]
theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort le l.1
theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort l.1 le
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR.run, mergeSort]
| n+2, a :: b :: l, h => by
@@ -183,7 +183,7 @@ theorem mergeSort_eq_mergeSortTR : @mergeSort = @mergeSortTR := by
-- This mutual block is unfortunately quite slow to elaborate.
set_option maxHeartbeats 400000 in
mutual
theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort le l.1
theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort l.1 le
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR₂.run, mergeSort]
| n+2, a :: b :: l, h => by
@@ -195,7 +195,7 @@ theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} → (l : { l : List α // l.
rw [reverse_reverse]
termination_by n => n
theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort le l'
theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort l' le
| 0, [], _, w
| 1, [a], _, w => by simp_all [mergeSortTR₂.run', mergeSort]
| n+2, a :: b :: l, h, w => by

View File

@@ -23,11 +23,6 @@ import Init.Data.Bool
namespace List
-- We enable this instance locally so we can write `Pairwise le` instead of `Pairwise (le · ·)` everywhere.
attribute [local instance] boolRelToRel
variable {le : α α Bool}
/-! ### splitInTwo -/
@[simp] theorem splitInTwo_fst (l : { l : List α // l.length = n }) :
@@ -89,6 +84,8 @@ theorem splitInTwo_fst_le_splitInTwo_snd {l : { l : List α // l.length = n }} (
/-! ### enumLE -/
variable {le : α α Bool}
theorem enumLE_trans (trans : a b c, le a b le b c le a c)
(a b c : Nat × α) : enumLE le a b enumLE le b c enumLE le a c := by
simp only [enumLE]
@@ -129,7 +126,7 @@ theorem enumLE_total (total : ∀ a b, !le a b → le b a)
/-! ### merge -/
theorem merge_stable : (xs ys) (_ : x y, x xs y ys x.1 y.1),
(merge (enumLE le) xs ys).map (·.2) = merge le (xs.map (·.2)) (ys.map (·.2))
(merge xs ys (enumLE le)).map (·.2) = merge (xs.map (·.2)) (ys.map (·.2)) le
| [], ys, _ => by simp [merge]
| xs, [], _ => by simp [merge]
| (i, x) :: xs, (j, y) :: ys, h => by
@@ -147,7 +144,7 @@ theorem merge_stable : ∀ (xs ys) (_ : ∀ x y, x ∈ xs → y ∈ ys → x.1
The elements of `merge le xs ys` are exactly the elements of `xs` and `ys`.
-/
-- We subsequently prove that `mergeSort_perm : merge le xs ys ~ xs ++ ys`.
theorem mem_merge {a : α} {xs ys : List α} : a merge le xs ys a xs a ys := by
theorem mem_merge {a : α} {xs ys : List α} : a merge xs ys le a xs a ys := by
induction xs generalizing ys with
| nil => simp [merge]
| cons x xs ih =>
@@ -161,6 +158,9 @@ theorem mem_merge {a : α} {xs ys : List α} : a ∈ merge le xs ys ↔ a ∈ xs
apply or_congr_left
simp only [or_comm (a := a = y), or_assoc]
-- We enable this instance locally so we can write `Pairwise le` instead of `Pairwise (le · ·)` everywhere.
attribute [local instance] boolRelToRel
/--
If the ordering relation `le` is transitive and total (i.e. `le a b le b a` for all `a, b`)
then the `merge` of two sorted lists is sorted.
@@ -168,7 +168,7 @@ then the `merge` of two sorted lists is sorted.
theorem sorted_merge
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(l₁ l₂ : List α) (h₁ : l₁.Pairwise le) (h₂ : l₂.Pairwise le) : (merge le l₁ l₂).Pairwise le := by
(l₁ l₂ : List α) (h₁ : l₁.Pairwise le) (h₂ : l₂.Pairwise le) : (merge l₁ l₂ le).Pairwise le := by
induction l₁ generalizing l₂ with
| nil => simpa only [merge]
| cons x l₁ ih₁ =>
@@ -195,7 +195,7 @@ theorem sorted_merge
· exact ih₂ h₂.tail
theorem merge_of_le : {xs ys : List α} (_ : a b, a xs b ys le a b),
merge le xs ys = xs ++ ys
merge xs ys le = xs ++ ys
| [], ys, _
| xs, [], _ => by simp [merge]
| x :: xs, y :: ys, h => by
@@ -206,7 +206,7 @@ theorem merge_of_le : ∀ {xs ys : List α} (_ : ∀ a b, a ∈ xs → b ∈ ys
· exact h x y (mem_cons_self _ _) (mem_cons_self _ _)
variable (le) in
theorem merge_perm_append : {xs ys : List α}, merge le xs ys ~ xs ++ ys
theorem merge_perm_append : {xs ys : List α}, merge xs ys le ~ xs ++ ys
| [], ys => by simp [merge]
| xs, [] => by simp [merge]
| x :: xs, y :: ys => by
@@ -222,24 +222,23 @@ theorem merge_perm_append : ∀ {xs ys : List α}, merge le xs ys ~ xs ++ ys
@[simp] theorem mergeSort_singleton (a : α) : [a].mergeSort r = [a] := by rw [List.mergeSort]
variable (le) in
theorem mergeSort_perm : (l : List α), mergeSort le l ~ l
| [] => by simp [mergeSort]
| [a] => by simp [mergeSort]
| a :: b :: xs => by
theorem mergeSort_perm : (l : List α) (le), mergeSort l le ~ l
| [], _ => by simp [mergeSort]
| [a], _ => by simp [mergeSort]
| a :: b :: xs, le => by
simp only [mergeSort]
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
exact (merge_perm_append le).trans
(((mergeSort_perm _).append (mergeSort_perm _)).trans
(((mergeSort_perm _ _).append (mergeSort_perm _ _)).trans
(Perm.of_eq (splitInTwo_fst_append_splitInTwo_snd _)))
termination_by l => l.length
@[simp] theorem mergeSort_length (l : List α) : (mergeSort le l).length = l.length :=
(mergeSort_perm le l).length_eq
@[simp] theorem mergeSort_length (l : List α) : (mergeSort l le).length = l.length :=
(mergeSort_perm l le).length_eq
@[simp] theorem mem_mergeSort {a : α} {l : List α} : a mergeSort le l a l :=
(mergeSort_perm le l).mem_iff
@[simp] theorem mem_mergeSort {a : α} {l : List α} : a mergeSort l le a l :=
(mergeSort_perm l le).mem_iff
/--
The result of `mergeSort` is sorted,
@@ -251,7 +250,7 @@ The comparison function need not be irreflexive, i.e. `le a b` and `le b a` is a
theorem sorted_mergeSort
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a) :
(l : List α) (mergeSort le l).Pairwise le
(l : List α) (mergeSort l le).Pairwise le
| [] => by simp [mergeSort]
| [a] => by simp [mergeSort]
| a :: b :: xs => by
@@ -268,7 +267,7 @@ termination_by l => l.length
/--
If the input list is already sorted, then `mergeSort` does not change the list.
-/
theorem mergeSort_of_sorted : {l : List α} (_ : Pairwise le l), mergeSort le l = l
theorem mergeSort_of_sorted : {l : List α} (_ : Pairwise le l), mergeSort l le = l
| [], _ => by simp [mergeSort]
| [a], _ => by simp [mergeSort]
| a :: b :: xs, h => by
@@ -294,10 +293,10 @@ See also:
* `pair_sublist_mergeSort`: if `[a, b] <+ l` and `le a b`, then `[a, b] <+ mergeSort le l`)
-/
theorem mergeSort_enum {l : List α} :
(mergeSort (enumLE le) (l.enum)).map (·.2) = mergeSort le l :=
(mergeSort (l.enum) (enumLE le)).map (·.2) = mergeSort l le :=
go 0 l
where go : (i : Nat) (l : List α),
(mergeSort (enumLE le) (l.enumFrom i)).map (·.2) = mergeSort le l
(mergeSort (l.enumFrom i) (enumLE le)).map (·.2) = mergeSort l le
| _, []
| _, [a] => by simp [mergeSort]
| _, a :: b :: xs => by
@@ -320,24 +319,24 @@ theorem mergeSort_cons {le : αα → Bool}
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(a : α) (l : List α) :
l₁ l₂, mergeSort le (a :: l) = l₁ ++ a :: l₂ mergeSort le l = l₁ ++ l₂
l₁ l₂, mergeSort (a :: l) le = l₁ ++ a :: l₂ mergeSort l le = l₁ ++ l₂
b, b l₁ !le a b := by
rw [ mergeSort_enum]
rw [enum_cons]
have nd : Nodup ((a :: l).enum.map (·.1)) := by rw [enum_map_fst]; exact nodup_range _
have m₁ : (0, a) mergeSort (enumLE le) ((a :: l).enum) :=
have m₁ : (0, a) mergeSort ((a :: l).enum) (enumLE le) :=
mem_mergeSort.mpr (mem_cons_self _ _)
obtain l₁, l₂, h := append_of_mem m₁
have s := sorted_mergeSort (enumLE_trans trans) (enumLE_total total) ((a :: l).enum)
rw [h] at s
have p := mergeSort_perm (enumLE le) ((a :: l).enum)
have p := mergeSort_perm ((a :: l).enum) (enumLE le)
rw [h] at p
refine l₁.map (·.2), l₂.map (·.2), ?_, ?_, ?_
· simpa using congrArg (·.map (·.2)) h
· rw [ mergeSort_enum.go 1, map_append]
congr 1
have q : mergeSort (enumLE le) (enumFrom 1 l) ~ l₁ ++ l₂ :=
(mergeSort_perm (enumLE le) (enumFrom 1 l)).trans
have q : mergeSort (enumFrom 1 l) (enumLE le) ~ l₁ ++ l₂ :=
(mergeSort_perm (enumFrom 1 l) (enumLE le)).trans
(p.symm.trans perm_middle).cons_inv
apply Perm.eq_of_sorted (le := enumLE le)
· rintro i, a j, b ha hb
@@ -379,7 +378,7 @@ theorem sublist_mergeSort
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a) :
{c : List α} (_ : c.Pairwise le) (_ : c <+ l),
c <+ mergeSort le l
c <+ mergeSort l le
| _, _, .slnil => nil_sublist _
| c, hc, @Sublist.cons _ _ l a h => by
obtain l₁, l₂, h₁, h₂, - := mergeSort_cons trans total a l
@@ -409,7 +408,7 @@ then `[a, b]` is still a sublist of `mergeSort le l`.
theorem pair_sublist_mergeSort
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(hab : le a b) (h : [a, b] <+ l) : [a, b] <+ mergeSort le l :=
(hab : le a b) (h : [a, b] <+ l) : [a, b] <+ mergeSort l le :=
sublist_mergeSort trans total (pairwise_pair.mpr hab) h
@[deprecated (since := "2024-09-02")] abbrev mergeSort_stable_pair := @pair_sublist_mergeSort

View File

@@ -667,7 +667,7 @@ theorem IsSuffix.length_le (h : l₁ <:+ l₂) : l₁.length ≤ l₂.length :=
theorem IsPrefix.getElem {x y : List α} (h : x <+: y) {n} (hn : n < x.length) :
x[n] = y[n]'(Nat.le_trans hn h.length_le) := by
obtain _, rfl := h
exact (List.getElem_append n hn).symm
exact (List.getElem_append_left hn).symm
-- See `Init.Data.List.Nat.Sublist` for `IsSuffix.getElem`.

View File

@@ -5,6 +5,8 @@ Authors: Floris van Doorn, Leonardo de Moura
-/
prelude
import Init.SimpLemmas
import Init.Data.NeZero
set_option linter.missingDocs true -- keep it documented
universe u
@@ -356,6 +358,8 @@ theorem eq_zero_or_pos : ∀ (n : Nat), n = 0 n > 0
protected theorem pos_of_ne_zero {n : Nat} : n 0 0 < n := (eq_zero_or_pos n).resolve_left
theorem pos_of_neZero (n : Nat) [NeZero n] : 0 < n := Nat.pos_of_ne_zero (NeZero.ne _)
theorem lt.base (n : Nat) : n < succ n := Nat.le_refl (succ n)
theorem lt_succ_self (n : Nat) : n < succ n := lt.base n
@@ -510,6 +514,10 @@ protected theorem add_lt_add_left {n m : Nat} (h : n < m) (k : Nat) : k + n < k
protected theorem add_lt_add_right {n m : Nat} (h : n < m) (k : Nat) : n + k < m + k :=
Nat.add_comm k m Nat.add_comm k n Nat.add_lt_add_left h k
protected theorem lt_add_of_pos_left (h : 0 < k) : n < k + n := by
rw [Nat.add_comm]
exact Nat.add_lt_add_left h n
protected theorem lt_add_of_pos_right (h : 0 < k) : n < n + k :=
Nat.add_lt_add_left h n
@@ -714,6 +722,8 @@ protected theorem zero_ne_one : 0 ≠ (1 : Nat) :=
theorem succ_ne_zero (n : Nat) : succ n 0 := by simp
instance instNeZeroSucc {n : Nat} : NeZero (n + 1) := succ_ne_zero n
/-! # mul + order -/
theorem mul_le_mul_left {n m : Nat} (k : Nat) (h : n m) : k * n k * m :=
@@ -784,6 +794,9 @@ theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
| zero => cases h
| succ n => simp [Nat.pow_succ]
instance {n m : Nat} [NeZero n] : NeZero (n^m) :=
Nat.ne_zero_iff_zero_lt.mpr (Nat.pos_pow_of_pos m (pos_of_neZero _))
/-! # min/max -/
/--

View File

@@ -226,12 +226,12 @@ private theorem succ_mod_two : succ x % 2 = 1 - x % 2 := by
simp [Nat.mod_eq (x+2) 2, p, hyp]
cases Nat.mod_two_eq_zero_or_one x with | _ p => simp [p]
private theorem testBit_succ_zero : testBit (x + 1) 0 = not (testBit x 0) := by
private theorem testBit_succ_zero : testBit (x + 1) 0 = !(testBit x 0) := by
simp [testBit_to_div_mod, succ_mod_two]
cases Nat.mod_two_eq_zero_or_one x with | _ p =>
simp [p]
theorem testBit_two_pow_add_eq (x i : Nat) : testBit (2^i + x) i = not (testBit x i) := by
theorem testBit_two_pow_add_eq (x i : Nat) : testBit (2^i + x) i = !(testBit x i) := by
simp [testBit_to_div_mod, add_div_left, Nat.two_pow_pos, succ_mod_two]
cases mod_two_eq_zero_or_one (x / 2 ^ i) with
| _ p => simp [p]
@@ -476,16 +476,20 @@ theorem and_lt_two_pow (x : Nat) {y n : Nat} (right : y < 2^n) : (x &&& y) < 2^n
exact pow_le_pow_of_le_right Nat.zero_lt_two i_ge_n
simp [testBit_and, yf]
@[simp] theorem and_pow_two_is_mod (x n : Nat) : x &&& (2^n-1) = x % 2^n := by
@[simp] theorem and_pow_two_sub_one_eq_mod (x n : Nat) : x &&& 2^n - 1 = x % 2^n := by
apply eq_of_testBit_eq
intro i
simp only [testBit_and, testBit_mod_two_pow]
cases testBit x i <;> simp
theorem and_pow_two_identity {x : Nat} (lt : x < 2^n) : x &&& 2^n-1 = x := by
rw [and_pow_two_is_mod]
@[deprecated and_pow_two_sub_one_eq_mod (since := "2024-09-11")] abbrev and_pow_two_is_mod := @and_pow_two_sub_one_eq_mod
theorem and_pow_two_sub_one_of_lt_two_pow {x : Nat} (lt : x < 2^n) : x &&& 2^n - 1 = x := by
rw [and_pow_two_sub_one_eq_mod]
apply Nat.mod_eq_of_lt lt
@[deprecated and_pow_two_sub_one_of_lt_two_pow (since := "2024-09-11")] abbrev and_two_pow_identity := @and_pow_two_sub_one_of_lt_two_pow
@[simp] theorem and_mod_two_eq_one : (a &&& b) % 2 = 1 a % 2 = 1 b % 2 = 1 := by
simp only [mod_two_eq_one_iff_testBit_zero]
rw [testBit_and]

View File

@@ -84,9 +84,6 @@ protected theorem add_lt_add_of_lt_of_le {a b c d : Nat} (hlt : a < b) (hle : c
a + c < b + d :=
Nat.lt_of_le_of_lt (Nat.add_le_add_left hle _) (Nat.add_lt_add_right hlt _)
protected theorem lt_add_of_pos_left : 0 < k n < k + n := by
rw [Nat.add_comm]; exact Nat.lt_add_of_pos_right
protected theorem pos_of_lt_add_right (h : n < n + k) : 0 < k :=
Nat.lt_of_add_lt_add_left h
@@ -577,6 +574,15 @@ theorem mul_mod (a b n : Nat) : a * b % n = (a % n) * (b % n) % n := by
theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
rw [add_mod_mod, mod_add_mod]
@[simp] theorem self_sub_mod (n k : Nat) [NeZero k] : (n - k) % n = n - k := by
cases n with
| zero => simp
| succ n =>
rw [mod_eq_of_lt]
cases k with
| zero => simp_all
| succ k => omega
/-! ### pow -/
theorem pow_succ' {m n : Nat} : m ^ n.succ = m * m ^ n := by

38
src/Init/Data/NeZero.lean Normal file
View File

@@ -0,0 +1,38 @@
/-
Copyright (c) 2021 Eric Rodriguez. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Rodriguez
-/
prelude
import Init.Data.Zero
/-!
# `NeZero` typeclass
We create a typeclass `NeZero n` which carries around the fact that `(n : R) ≠ 0`.
## Main declarations
* `NeZero`: `n ≠ 0` as a typeclass.
-/
variable {R : Type _} [Zero R]
/-- A type-class version of `n ≠ 0`. -/
class NeZero (n : R) : Prop where
/-- The proposition that `n` is not zero. -/
out : n 0
theorem NeZero.ne (n : R) [h : NeZero n] : n 0 :=
h.out
theorem NeZero.ne' (n : R) [h : NeZero n] : 0 n :=
h.out.symm
theorem neZero_iff {n : R} : NeZero n n 0 :=
fun h h.out, NeZero.mk
@[simp] theorem neZero_zero_iff_false {α : Type _} [Zero α] : NeZero (0 : α) False :=
fun h h.ne rfl, fun h h.elim

View File

@@ -6,6 +6,7 @@ Authors: Mario Carneiro
prelude
import Init.Data.Option.BasicAux
import Init.Data.Option.Instances
import Init.Data.BEq
import Init.Classical
import Init.Ext
@@ -13,7 +14,7 @@ namespace Option
theorem mem_iff {a : α} {b : Option α} : a b b = some a := .rfl
@[simp] theorem mem_some {a b : α} : a some b a = b := by simp [mem_iff, eq_comm]
@[simp] theorem mem_some {a b : α} : a some b b = a := by simp [mem_iff]
theorem mem_some_self (a : α) : a some a := mem_some.2 rfl
@@ -247,6 +248,12 @@ theorem isSome_filter_of_isSome (p : α → Bool) (o : Option α) (h : (o.filter
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α β} :
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp
theorem bind_map {f : α β} {g : β Option γ} {x : Option α} :
(x.map f).bind g = x.bind (g f) := by cases x <;> simp
@[simp] theorem map_bind {f : α Option β} {g : β γ} {x : Option α} :
(x.bind f).map g = x.bind (Option.map g f) := by cases x <;> simp
theorem join_map_eq_map_join {f : α β} {x : Option (Option α)} :
(x.map (Option.map f)).join = x.join.map f := by cases x <;> simp
@@ -277,6 +284,27 @@ theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) :
@[simp] theorem guard_pos [DecidablePred p] (h : p a) : Option.guard p a = some a := by
simp [Option.guard, h]
@[congr] theorem guard_congr {f g : α Prop} [DecidablePred f] [DecidablePred g]
(h : a, f a g a):
guard f = guard g := by
funext a
simp [guard, h]
@[simp] theorem guard_false {α} :
guard (fun (_ : α) => False) = fun _ => none := by
funext a
simp [guard]
@[simp] theorem guard_true {α} :
guard (fun (_ : α) => True) = some := by
funext a
simp [guard]
theorem guard_comp {p : α Prop} [DecidablePred p] {f : β α} :
guard p f = Option.map f guard (p f) := by
ext1 b
simp [guard]
theorem liftOrGet_eq_or_eq {f : α α α} (h : a b, f a b = a f a b = b) :
o₁ o₂, liftOrGet f o₁ o₂ = o₁ liftOrGet f o₁ o₂ = o₂
| none, none => .inl rfl
@@ -384,6 +412,37 @@ variable [BEq α]
@[simp] theorem some_beq_none (a : α) : ((some a : Option α) == none) = false := rfl
@[simp] theorem some_beq_some {a b : α} : (some a == some b) = (a == b) := rfl
@[simp] theorem reflBEq_iff : ReflBEq (Option α) ReflBEq α := by
constructor
· intro h
constructor
intro a
suffices (some a == some a) = true by
simpa only [some_beq_some]
simp
· intro h
constructor
· rintro (_ | a) <;> simp
@[simp] theorem lawfulBEq_iff : LawfulBEq (Option α) LawfulBEq α := by
constructor
· intro h
constructor
· intro a b h
apply Option.some.inj
apply eq_of_beq
simpa
· intro a
suffices (some a == some a) = true by
simpa only [some_beq_some]
simp
· intro h
constructor
· intro a b h
simpa using h
· intro a
simp
end beq
/-! ### ite -/
@@ -405,6 +464,38 @@ section ite
(x if p then l else none) p x l := by
split <;> simp_all
@[simp] theorem dite_none_left_eq_some {p : Prop} [Decidable p] {b : ¬p Option β} :
(if h : p then none else b h) = some a h, b h = some a := by
split <;> simp_all
@[simp] theorem dite_none_right_eq_some {p : Prop} [Decidable p] {b : p Option α} :
(if h : p then b h else none) = some a h, b h = some a := by
split <;> simp_all
@[simp] theorem some_eq_dite_none_left {p : Prop} [Decidable p] {b : ¬p Option β} :
some a = (if h : p then none else b h) h, some a = b h := by
split <;> simp_all
@[simp] theorem some_eq_dite_none_right {p : Prop} [Decidable p] {b : p Option α} :
some a = (if h : p then b h else none) h, some a = b h := by
split <;> simp_all
@[simp] theorem ite_none_left_eq_some {p : Prop} [Decidable p] {b : Option β} :
(if p then none else b) = some a ¬ p b = some a := by
split <;> simp_all
@[simp] theorem ite_none_right_eq_some {p : Prop} [Decidable p] {b : Option α} :
(if p then b else none) = some a p b = some a := by
split <;> simp_all
@[simp] theorem some_eq_ite_none_left {p : Prop} [Decidable p] {b : Option β} :
some a = (if p then none else b) ¬ p some a = b := by
split <;> simp_all
@[simp] theorem some_eq_ite_none_right {p : Prop} [Decidable p] {b : Option α} :
some a = (if p then b else none) p some a = b := by
split <;> simp_all
@[simp] theorem isSome_dite {p : Prop} [Decidable p] {b : p β} :
(if h : p then some (b h) else none).isSome = true p := by
split <;> simpa

View File

@@ -290,11 +290,17 @@ instance (a b : UInt64) : Decidable (a ≤ b) := UInt64.decLe a b
instance : Max UInt64 := maxOfLe
instance : Min UInt64 := minOfLe
-- This instance would interfere with the global instance `NeZero (n + 1)`,
-- so we only enable it locally.
@[local instance]
private def instNeZeroUSizeSize : NeZero USize.size := add_one_ne_zero _
@[deprecated (since := "2024-09-16")]
theorem usize_size_gt_zero : USize.size > 0 :=
Nat.zero_lt_succ ..
@[extern "lean_usize_of_nat"]
def USize.ofNat (n : @& Nat) : USize := Fin.ofNat' n usize_size_gt_zero
def USize.ofNat (n : @& Nat) : USize := Fin.ofNat' _ n
abbrev Nat.toUSize := USize.ofNat
@[extern "lean_usize_to_nat"]
def USize.toNat (n : USize) : Nat := n.val.val

17
src/Init/Data/Zero.lean Normal file
View File

@@ -0,0 +1,17 @@
/-
Copyright (c) 2021 Gabriel Ebner. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner, Mario Carneiro
-/
prelude
import Init.Core
/-!
Instances converting between `Zero α` and `OfNat α (nat_lit 0)`.
-/
instance (priority := 300) Zero.toOfNat0 {α} [Zero α] : OfNat α (nat_lit 0) where
ofNat := Zero α.1
instance (priority := 200) Zero.ofOfNat0 {α} [OfNat α (nat_lit 0)] : Zero α where
zero := 0

View File

@@ -1014,7 +1014,7 @@ with `Or : Prop → Prop → Prop`, which is the propositional connective).
It is `@[macro_inline]` because it has C-like short-circuiting behavior:
if `x` is true then `y` is not evaluated.
-/
@[macro_inline] def or (x y : Bool) : Bool :=
@[macro_inline] def Bool.or (x y : Bool) : Bool :=
match x with
| true => true
| false => y
@@ -1025,7 +1025,7 @@ with `And : Prop → Prop → Prop`, which is the propositional connective).
It is `@[macro_inline]` because it has C-like short-circuiting behavior:
if `x` is false then `y` is not evaluated.
-/
@[macro_inline] def and (x y : Bool) : Bool :=
@[macro_inline] def Bool.and (x y : Bool) : Bool :=
match x with
| false => false
| true => y
@@ -1034,10 +1034,12 @@ if `x` is false then `y` is not evaluated.
`not x`, or `!x`, is the boolean "not" operation (not to be confused
with `Not : Prop → Prop`, which is the propositional connective).
-/
@[inline] def not : Bool Bool
@[inline] def Bool.not : Bool Bool
| true => false
| false => true
export Bool (or and not)
/--
The type of natural numbers, starting at zero. It is defined as an
inductive type freely generated by "zero is a natural number" and
@@ -1304,6 +1306,11 @@ class HShiftRight (α : Type u) (β : Type v) (γ : outParam (Type w)) where
this is equivalent to `a / 2 ^ b`. -/
hShiftRight : α β γ
/-- A type with a zero element. -/
class Zero (α : Type u) where
/-- The zero element of the type. -/
zero : α
/-- The homogeneous version of `HAdd`: `a + b : α` where `a b : α`. -/
class Add (α : Type u) where
/-- `a + b` computes the sum of `a` and `b`. See `HAdd`. -/

View File

@@ -556,6 +556,9 @@ This is the same as `decidable_of_iff` but the iff is flipped. -/
instance Decidable.predToBool (p : α Prop) [DecidablePred p] :
CoeDep (α Prop) p (α Bool) := fun b => decide <| p b
instance [DecidablePred p] : DecidablePred (p f) :=
fun x => inferInstanceAs (Decidable (p (f x)))
/-- Prove that `a` is decidable by constructing a boolean `b` and a proof that `b ↔ a`.
(This is sometimes taken as an alternate definition of decidability.) -/
def decidable_of_bool : (b : Bool), (b a) Decidable a

View File

@@ -91,7 +91,7 @@ private def isBorrowParamAux (x : VarId) (ys : Array Arg) (consumeParamPred : Na
| Arg.var y => x == y && !consumeParamPred i
private def isBorrowParam (x : VarId) (ys : Array Arg) (ps : Array Param) : Bool :=
isBorrowParamAux x ys fun i => not ps[i]!.borrow
isBorrowParamAux x ys fun i => ! ps[i]!.borrow
/--
Return `n`, the number of times `x` is consumed.
@@ -124,7 +124,7 @@ private def addIncBeforeAux (ctx : Context) (xs : Array Arg) (consumeParamPred :
addInc ctx x b numIncs
private def addIncBefore (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
addIncBeforeAux ctx xs (fun i => not ps[i]!.borrow) b liveVarsAfter
addIncBeforeAux ctx xs (fun i => ! ps[i]!.borrow) b liveVarsAfter
/-- See `addIncBeforeAux`/`addIncBefore` for the procedure that inserts `inc` operations before an application. -/
private def addDecAfterFullApp (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=

View File

@@ -317,8 +317,8 @@ variable {m : Type → Type w} [Monad m]
anyMAux p t.root <||> t.tail.anyM p
@[inline] def allM (a : PersistentArray α) (p : α m Bool) : m Bool := do
let b anyM a (fun v => do let b p v; pure (not b))
pure (not b)
let b anyM a (fun v => do let b p v; pure (!b))
pure (!b)
end

View File

@@ -1594,10 +1594,13 @@ private def elabAtom : TermElab := fun stx expectedType? => do
@[builtin_term_elab dotIdent] def elabDotIdent : TermElab := elabAtom
@[builtin_term_elab explicitUniv] def elabExplicitUniv : TermElab := elabAtom
@[builtin_term_elab pipeProj] def elabPipeProj : TermElab
| `($e |>.$f $args*), expectedType? =>
| `($e |>.%$tk$f $args*), expectedType? =>
universeConstraintsCheckpoint do
let (namedArgs, args, ellipsis) expandArgs args
elabAppAux ( `($e |>.$f)) namedArgs args (ellipsis := ellipsis) expectedType?
let mut stx `($e |>.%$tk$f)
if let (some startPos, some stopPos) := (e.raw.getPos?, f.raw.getTailPos?) then
stx := stx.raw.setInfo <| .synthetic (canonical := true) startPos stopPos
elabAppAux stx namedArgs args (ellipsis := ellipsis) expectedType?
| _, _ => throwUnsupportedSyntax
@[builtin_term_elab explicit] def elabExplicit : TermElab := fun stx expectedType? =>

View File

@@ -15,145 +15,109 @@ open Lean.Json
open Lean.Parser.Term
open Lean.Meta
def mkToJsonHeader (indVal : InductiveVal) : TermElabM Header := do
mkHeader ``ToJson 1 indVal
def mkFromJsonHeader (indVal : InductiveVal) : TermElabM Header := do
let header mkHeader ``FromJson 0 indVal
let jsonArg `(bracketedBinderF|(json : Json))
return {header with
binders := header.binders.push jsonArg}
def mkJsonField (n : Name) : CoreM (Bool × Term) := do
let .str .anonymous s := n | throwError "invalid json field name {n}"
let s₁ := s.dropRightWhile (· == '?')
return (s != s₁, Syntax.mkStrLit s₁)
def mkToJsonInstance (declName : Name) : CommandElabM Bool := do
if isStructure ( getEnv) declName then
let cmds liftTermElabM do
let ctx mkContext "toJson" declName
let header mkHeader ``ToJson 1 ctx.typeInfos[0]!
let fields := getStructureFieldsFlattened ( getEnv) declName (includeSubobjectFields := false)
let fields fields.mapM fun field => do
let (isOptField, nm) mkJsonField field
let target := mkIdent header.targetNames[0]!
if isOptField then ``(opt $nm ($target).$(mkIdent field))
else ``([($nm, toJson ($target).$(mkIdent field))])
let cmd `(private def $(mkIdent ctx.auxFunNames[0]!):ident $header.binders:bracketedBinder* : Json :=
mkObj <| List.join [$fields,*])
return #[cmd] ++ ( mkInstanceCmds ctx ``ToJson #[declName])
cmds.forM elabCommand
return true
else
let indVal getConstInfoInduct declName
let cmds liftTermElabM do
let ctx mkContext "toJson" declName
let toJsonFuncId := mkIdent ctx.auxFunNames[0]!
-- Return syntax to JSONify `id`, either via `ToJson` or recursively
-- if `id`'s type is the type we're deriving for.
let mkToJson (id : Ident) (type : Expr) : TermElabM Term := do
def mkToJsonBodyForStruct (header : Header) (indName : Name) : TermElabM Term := do
let fields := getStructureFieldsFlattened ( getEnv) indName (includeSubobjectFields := false)
let fields fields.mapM fun field => do
let (isOptField, nm) mkJsonField field
let target := mkIdent header.targetNames[0]!
if isOptField then ``(opt $nm $target.$(mkIdent field))
else ``([($nm, toJson ($target).$(mkIdent field))])
`(mkObj <| List.join [$fields,*])
def mkToJsonBodyForInduct (ctx : Context) (header : Header) (indName : Name) : TermElabM Term := do
let indVal getConstInfoInduct indName
let toJsonFuncId := mkIdent ctx.auxFunNames[0]!
-- Return syntax to JSONify `id`, either via `ToJson` or recursively
-- if `id`'s type is the type we're deriving for.
let mkToJson (id : Ident) (type : Expr) : TermElabM Term := do
if type.isAppOf indVal.name then `($toJsonFuncId:ident $id:ident)
else ``(toJson $id:ident)
let header mkHeader ``ToJson 1 ctx.typeInfos[0]!
let discrs mkDiscrs header indVal
let alts mkAlts indVal fun ctor args userNames => do
let ctorStr := ctor.name.eraseMacroScopes.getString!
match args, userNames with
| #[], _ => ``(toJson $(quote ctorStr))
| #[(x, t)], none => ``(mkObj [($(quote ctorStr), $( mkToJson x t))])
| xs, none =>
let xs xs.mapM fun (x, t) => mkToJson x t
``(mkObj [($(quote ctorStr), Json.arr #[$[$xs:term],*])])
| xs, some userNames =>
let xs xs.mapIdxM fun idx (x, t) => do
`(($(quote userNames[idx]!.eraseMacroScopes.getString!), $( mkToJson x t)))
``(mkObj [($(quote ctorStr), mkObj [$[$xs:term],*])])
let auxTerm `(match $[$discrs],* with $alts:matchAlt*)
let auxCmd
if ctx.usePartial then
let letDecls mkLocalInstanceLetDecls ctx ``ToJson header.argNames
let auxTerm mkLet letDecls auxTerm
`(private partial def $toJsonFuncId:ident $header.binders:bracketedBinder* : Json := $auxTerm)
else
`(private def $toJsonFuncId:ident $header.binders:bracketedBinder* : Json := $auxTerm)
return #[auxCmd] ++ ( mkInstanceCmds ctx ``ToJson #[declName])
cmds.forM elabCommand
return true
let discrs mkDiscrs header indVal
let alts mkAlts indVal fun ctor args userNames => do
let ctorStr := ctor.name.eraseMacroScopes.getString!
match args, userNames with
| #[], _ => ``(toJson $(quote ctorStr))
| #[(x, t)], none => ``(mkObj [($(quote ctorStr), $( mkToJson x t))])
| xs, none =>
let xs xs.mapM fun (x, t) => mkToJson x t
``(mkObj [($(quote ctorStr), Json.arr #[$[$xs:term],*])])
| xs, some userNames =>
let xs xs.mapIdxM fun idx (x, t) => do
`(($(quote userNames[idx]!.eraseMacroScopes.getString!), $( mkToJson x t)))
``(mkObj [($(quote ctorStr), mkObj [$[$xs:term],*])])
`(match $[$discrs],* with $alts:matchAlt*)
where
mkAlts
(indVal : InductiveVal)
(rhs : ConstructorVal Array (Ident × Expr) Option (Array Name) TermElabM Term) : TermElabM (Array (TSyntax ``matchAlt)) := do
indVal.ctors.toArray.mapM fun ctor => do
let ctorInfo getConstInfoCtor ctor
forallTelescopeReducing ctorInfo.type fun xs _ => do
let mut patterns := #[]
-- add `_` pattern for indices
for _ in [:indVal.numIndices] do
patterns := patterns.push ( `(_))
let mut ctorArgs := #[]
-- add `_` for inductive parameters, they are inaccessible
for _ in [:indVal.numParams] do
ctorArgs := ctorArgs.push ( `(_))
-- bound constructor arguments and their types
let mut binders := #[]
let mut userNames := #[]
for i in [:ctorInfo.numFields] do
let x := xs[indVal.numParams + i]!
let localDecl x.fvarId!.getDecl
if !localDecl.userName.hasMacroScopes then
userNames := userNames.push localDecl.userName
let a := mkIdent ( mkFreshUserName `a)
binders := binders.push (a, localDecl.type)
ctorArgs := ctorArgs.push a
patterns := patterns.push ( `(@$(mkIdent ctorInfo.name):ident $ctorArgs:term*))
let rhs rhs ctorInfo binders (if userNames.size == binders.size then some userNames else none)
`(matchAltExpr| | $[$patterns:term],* => $rhs:term)
(rhs : ConstructorVal Array (Ident × Expr) Option (Array Name) TermElabM Term): TermElabM (Array (TSyntax ``matchAlt)) := do
let mut alts := #[]
for ctorName in indVal.ctors do
let ctorInfo getConstInfoCtor ctorName
let alt forallTelescopeReducing ctorInfo.type fun xs _ => do
let mut patterns := #[]
-- add `_` pattern for indices
for _ in [:indVal.numIndices] do
patterns := patterns.push ( `(_))
let mut ctorArgs := #[]
-- add `_` for inductive parameters, they are inaccessible
for _ in [:indVal.numParams] do
ctorArgs := ctorArgs.push ( `(_))
-- bound constructor arguments and their types
let mut binders := #[]
let mut userNames := #[]
for i in [:ctorInfo.numFields] do
let x := xs[indVal.numParams + i]!
let localDecl x.fvarId!.getDecl
if !localDecl.userName.hasMacroScopes then
userNames := userNames.push localDecl.userName
let a := mkIdent ( mkFreshUserName `a)
binders := binders.push (a, localDecl.type)
ctorArgs := ctorArgs.push a
patterns := patterns.push ( `(@$(mkIdent ctorInfo.name):ident $ctorArgs:term*))
let rhs rhs ctorInfo binders (if userNames.size == binders.size then some userNames else none)
`(matchAltExpr| | $[$patterns:term],* => $rhs:term)
alts := alts.push alt
return alts
def mkFromJsonInstance (declName : Name) : CommandElabM Bool := do
if isStructure ( getEnv) declName then
let cmds liftTermElabM do
let ctx mkContext "fromJson" declName
let header mkHeader ``FromJson 0 ctx.typeInfos[0]!
let fields := getStructureFieldsFlattened ( getEnv) declName (includeSubobjectFields := false)
let getters fields.mapM (fun field => do
let getter `(getObjValAs? j _ $(Prod.snd <| mkJsonField field))
let getter `(doElem| Except.mapError (fun s => (toString $(quote declName)) ++ "." ++ (toString $(quote field)) ++ ": " ++ s) <| $getter)
return getter
)
let fields := fields.map mkIdent
let cmd `(private def $(mkIdent ctx.auxFunNames[0]!):ident $header.binders:bracketedBinder* (j : Json)
: Except String $( mkInductiveApp ctx.typeInfos[0]! header.argNames) := do
$[let $fields:ident $getters]*
return { $[$fields:ident := $(id fields)],* })
return #[cmd] ++ ( mkInstanceCmds ctx ``FromJson #[declName])
cmds.forM elabCommand
return true
else
let indVal getConstInfoInduct declName
let cmds liftTermElabM do
let ctx mkContext "fromJson" declName
let header mkHeader ``FromJson 0 ctx.typeInfos[0]!
let fromJsonFuncId := mkIdent ctx.auxFunNames[0]!
let alts mkAlts indVal fromJsonFuncId
let mut auxTerm alts.foldrM (fun xs x => `(Except.orElseLazy $xs (fun _ => $x))) ( `(Except.error "no inductive constructor matched"))
if ctx.usePartial then
let letDecls mkLocalInstanceLetDecls ctx ``FromJson header.argNames
auxTerm mkLet letDecls auxTerm
-- FromJson is not structurally recursive even non-nested recursive inductives,
-- so we also use `partial` then.
let auxCmd
if ctx.usePartial || indVal.isRec then
`(private partial def $fromJsonFuncId:ident $header.binders:bracketedBinder* (json : Json)
: Except String $( mkInductiveApp ctx.typeInfos[0]! header.argNames) :=
$auxTerm)
else
`(private def $fromJsonFuncId:ident $header.binders:bracketedBinder* (json : Json)
: Except String $( mkInductiveApp ctx.typeInfos[0]! header.argNames) :=
$auxTerm)
return #[auxCmd] ++ ( mkInstanceCmds ctx ``FromJson #[declName])
cmds.forM elabCommand
return true
def mkFromJsonBodyForStruct (indName : Name) : TermElabM Term := do
let fields := getStructureFieldsFlattened ( getEnv) indName (includeSubobjectFields := false)
let getters fields.mapM (fun field => do
let getter `(getObjValAs? json _ $(Prod.snd <| mkJsonField field))
let getter `(doElem| Except.mapError (fun s => (toString $(quote indName)) ++ "." ++ (toString $(quote field)) ++ ": " ++ s) <| $getter)
return getter
)
let fields := fields.map mkIdent
`(do
$[let $fields:ident $getters]*
return { $[$fields:ident := $(id fields)],* })
def mkFromJsonBodyForInduct (ctx : Context) (indName : Name) : TermElabM Term := do
let indVal getConstInfoInduct indName
let alts mkAlts indVal
let auxTerm alts.foldrM (fun xs x => `(Except.orElseLazy $xs (fun _ => $x))) ( `(Except.error "no inductive constructor matched"))
`($auxTerm)
where
mkAlts (indVal : InductiveVal) (fromJsonFuncId : Ident) : TermElabM (Array Term) := do
let alts
indVal.ctors.toArray.mapM fun ctor => do
let ctorInfo getConstInfoCtor ctor
forallTelescopeReducing ctorInfo.type fun xs _ => do
let mut binders := #[]
mkAlts (indVal : InductiveVal) : TermElabM (Array Term) := do
let mut alts := #[]
for ctorName in indVal.ctors do
let ctorInfo getConstInfoCtor ctorName
let alt do forallTelescopeReducing ctorInfo.type fun xs _ => do
let mut binders := #[]
let mut userNames := #[]
for i in [:ctorInfo.numFields] do
let x := xs[indVal.numParams + i]!
@@ -162,7 +126,7 @@ where
userNames := userNames.push localDecl.userName
let a := mkIdent ( mkFreshUserName `a)
binders := binders.push (a, localDecl.type)
let fromJsonFuncId := mkIdent ctx.auxFunNames[0]!
-- Return syntax to parse `id`, either via `FromJson` or recursively
-- if `id`'s type is the type we're deriving for.
let mkFromJson (idx : Nat) (type : Expr) : TermElabM (TSyntax ``doExpr) :=
@@ -175,23 +139,111 @@ where
else
``(none)
let stx
`((Json.parseTagged json $(quote ctor.eraseMacroScopes.getString!) $(quote ctorInfo.numFields) $(quote userNamesOpt)).bind
`((Json.parseTagged json $(quote ctorName.eraseMacroScopes.getString!) $(quote ctorInfo.numFields) $(quote userNamesOpt)).bind
(fun jsons => do
$[let $identNames:ident $fromJsons:doExpr]*
return $(mkIdent ctor):ident $identNames*))
return $(mkIdent ctorName):ident $identNames*))
pure (stx, ctorInfo.numFields)
alts := alts.push alt
-- the smaller cases, especially the ones without fields are likely faster
let alts := alts.qsort (fun (_, x) (_, y) => x < y)
return alts.map Prod.fst
let alts' := alts.qsort (fun (_, x) (_, y) => x < y)
return alts'.map Prod.fst
def mkToJsonBody (ctx : Context) (header : Header) (e : Expr): TermElabM Term := do
let indName := e.getAppFn.constName!
if isStructure ( getEnv) indName then
mkToJsonBodyForStruct header indName
else
mkToJsonBodyForInduct ctx header indName
def mkToJsonAuxFunction (ctx : Context) (i : Nat) : TermElabM Command := do
let auxFunName := ctx.auxFunNames[i]!
let header mkToJsonHeader ctx.typeInfos[i]!
let binders := header.binders
Term.elabBinders binders fun _ => do
let type Term.elabTerm header.targetType none
let mut body mkToJsonBody ctx header type
if ctx.usePartial then
let letDecls mkLocalInstanceLetDecls ctx ``ToJson header.argNames
body mkLet letDecls body
`(private partial def $(mkIdent auxFunName):ident $binders:bracketedBinder* : Json := $body:term)
else
`(private def $(mkIdent auxFunName):ident $binders:bracketedBinder* : Json := $body:term)
def mkFromJsonBody (ctx : Context) (e : Expr) : TermElabM Term := do
let indName := e.getAppFn.constName!
if isStructure ( getEnv) indName then
mkFromJsonBodyForStruct indName
else
mkFromJsonBodyForInduct ctx indName
def mkFromJsonAuxFunction (ctx : Context) (i : Nat) : TermElabM Command := do
let auxFunName := ctx.auxFunNames[i]!
let indval := ctx.typeInfos[i]!
let header mkFromJsonHeader indval --TODO fix header info
let binders := header.binders
Term.elabBinders binders fun _ => do
let type Term.elabTerm header.targetType none
let mut body mkFromJsonBody ctx type
if ctx.usePartial || indval.isRec then
let letDecls mkLocalInstanceLetDecls ctx ``FromJson header.argNames
body mkLet letDecls body
`(private partial def $(mkIdent auxFunName):ident $binders:bracketedBinder* : Except String $( mkInductiveApp ctx.typeInfos[i]! header.argNames) := $body:term)
else
`(private def $(mkIdent auxFunName):ident $binders:bracketedBinder* : Except String $( mkInductiveApp ctx.typeInfos[i]! header.argNames) := $body:term)
def mkToJsonMutualBlock (ctx : Context) : TermElabM Command := do
let mut auxDefs := #[]
for i in [:ctx.typeInfos.size] do
auxDefs := auxDefs.push ( mkToJsonAuxFunction ctx i)
`(mutual
$auxDefs:command*
end)
def mkFromJsonMutualBlock (ctx : Context) : TermElabM Command := do
let mut auxDefs := #[]
for i in [:ctx.typeInfos.size] do
auxDefs := auxDefs.push ( mkFromJsonAuxFunction ctx i)
`(mutual
$auxDefs:command*
end)
private def mkToJsonInstance (declName : Name) : TermElabM (Array Command) := do
let ctx mkContext "toJson" declName
let cmds := #[ mkToJsonMutualBlock ctx] ++ ( mkInstanceCmds ctx ``ToJson #[declName])
trace[Elab.Deriving.toJson] "\n{cmds}"
return cmds
private def mkFromJsonInstance (declName : Name) : TermElabM (Array Command) := do
let ctx mkContext "fromJson" declName
let cmds := #[ mkFromJsonMutualBlock ctx] ++ ( mkInstanceCmds ctx ``FromJson #[declName])
trace[Elab.Deriving.fromJson] "\n{cmds}"
return cmds
def mkToJsonInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
declNames.foldlM (fun b n => andM (pure b) (mkToJsonInstance n)) true
if ( declNames.allM isInductive) && declNames.size > 0 then
for declName in declNames do
let cmds liftTermElabM <| mkToJsonInstance declName
cmds.forM elabCommand
return true
else
return false
def mkFromJsonInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
declNames.foldlM (fun b n => andM (pure b) (mkFromJsonInstance n)) true
if ( declNames.allM isInductive) && declNames.size > 0 then
for declName in declNames do
let cmds liftTermElabM <| mkFromJsonInstance declName
cmds.forM elabCommand
return true
else
return false
builtin_initialize
registerDerivingHandler ``ToJson mkToJsonInstanceHandler
registerDerivingHandler ``FromJson mkFromJsonInstanceHandler
registerTraceClass `Elab.Deriving.toJson
registerTraceClass `Elab.Deriving.fromJson
end Lean.Elab.Deriving.FromToJson

View File

@@ -21,7 +21,7 @@ builtin_initialize
let some id := id?
| throwError "invalid `[inherit_doc]` attribute, could not infer doc source"
let declName Elab.realizeGlobalConstNoOverloadWithInfo id
if ( findSimpleDocString? ( getEnv) decl).isSome then
if ( findSimpleDocString? ( getEnv) decl (includeBuiltin := false)).isSome then
logWarning m!"{← mkConstWithLevelParams decl} already has a doc string"
let some doc findSimpleDocString? ( getEnv) declName
| logWarningAt id m!"{← mkConstWithLevelParams declName} does not have a doc string"

View File

@@ -643,7 +643,7 @@ where
| .proj _ _ b => return p.updateProj! ( go b)
| .mdata k b =>
if inaccessible? p |>.isSome then
return mkMData k ( withReader (fun _ => false) (go b))
return mkMData k ( withReader (fun _ => true) (go b))
else if let some (stx, p) := patternWithRef? p then
Elab.withInfoContext' (go p) fun p => do
/- If `p` is a free variable and we are not inside of an "inaccessible" pattern, this `p` is a binder. -/

View File

@@ -599,7 +599,7 @@ partial def solve {m} {α} [Monad m] (measures : Array α)
all_le := false
break
-- No progress here? Try the next measure
if not (has_lt && all_le) then continue
if !(has_lt && all_le) then continue
-- We found a suitable measure, remove it from the list (mild optimization)
let measures' := measures.eraseIdx measureIdx
return go measures' todo (acc.push measure)

View File

@@ -56,7 +56,7 @@ partial def of (t : Expr) : M (Option ReifiedBVLogical) := do
let expr := mkApp2 (mkConst ``BoolExpr.const) (mkConst ``BVPred) (toExpr Bool.false)
let proof := return mkRefl (mkConst ``Bool.false)
return some boolExpr, proof, expr
| not subExpr =>
| Bool.not subExpr =>
let some sub of subExpr | return none
let boolExpr := .not sub.bvExpr
let expr := mkApp2 (mkConst ``BoolExpr.not) (mkConst ``BVPred) sub.expr
@@ -65,9 +65,9 @@ partial def of (t : Expr) : M (Option ReifiedBVLogical) := do
let subProof sub.evalsAtAtoms
return mkApp3 (mkConst ``Std.Tactic.BVDecide.Reflect.Bool.not_congr) subExpr subEvalExpr subProof
return some boolExpr, proof, expr
| or lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .or ``Std.Tactic.BVDecide.Reflect.Bool.or_congr
| and lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .and ``Std.Tactic.BVDecide.Reflect.Bool.and_congr
| xor lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .xor ``Std.Tactic.BVDecide.Reflect.Bool.xor_congr
| Bool.or lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .or ``Std.Tactic.BVDecide.Reflect.Bool.or_congr
| Bool.and lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .and ``Std.Tactic.BVDecide.Reflect.Bool.and_congr
| Bool.xor lhsExpr rhsExpr => gateReflection lhsExpr rhsExpr .xor ``Std.Tactic.BVDecide.Reflect.Bool.xor_congr
| BEq.beq α _ lhsExpr rhsExpr =>
match_expr α with
| Bool => gateReflection lhsExpr rhsExpr .beq ``Std.Tactic.BVDecide.Reflect.Bool.beq_congr

View File

@@ -1439,7 +1439,7 @@ def resolveLocalName (n : Name) : TermElabM (Option (Expr × List String)) := do
let localDecl? := lctx.decls.findSomeRev? fun localDecl? => do
let localDecl localDecl?
if localDecl.isAuxDecl then
guard (not skipAuxDecl)
guard (!skipAuxDecl)
if let some fullDeclName := auxDeclToFullName.find? localDecl.fvarId then
matchAuxRecDecl? localDecl fullDeclName givenNameView
else
@@ -1497,7 +1497,7 @@ def resolveLocalName (n : Name) : TermElabM (Option (Expr × List String)) := do
foo := 10
```
-/
match findLocalDecl? givenNameView (skipAuxDecl := globalDeclFound && not projs.isEmpty) with
match findLocalDecl? givenNameView (skipAuxDecl := globalDeclFound && !projs.isEmpty) with
| some decl => return some (decl.toExpr, projs)
| none => match n with
| .str pre s => loop pre (s::projs) globalDeclFoundNext

View File

@@ -1016,7 +1016,15 @@ private def registerNamePrefixes : Environment → Name → Environment
@[export lean_environment_add]
private def add (env : Environment) (cinfo : ConstantInfo) : Environment :=
let env := registerNamePrefixes env cinfo.name
let name := cinfo.name
let env := match name with
| .str _ s =>
if s.get 0 == '_' then
-- Do not register namespaces that only contain internal declarations.
env
else
registerNamePrefixes env name
| _ => env
env.addAux cinfo
@[export lean_display_stats]

View File

@@ -322,7 +322,8 @@ where
stx := newStx
diagnostics := old.diagnostics
cancelTk? := ctx.newCancelTk
result? := some { oldSuccess with
result? := some {
parserState := newParserState
processedSnap := ( oldSuccess.processedSnap.bindIO (sync := true) fun oldProcessed => do
if let some oldProcSuccess := oldProcessed.result? then
-- also wait on old command parse snapshot as parsing is cheap and may allow for
@@ -330,8 +331,11 @@ where
oldProcSuccess.firstCmdSnap.bindIO (sync := true) fun oldCmd => do
let prom IO.Promise.new
let _ IO.asTask (parseCmd oldCmd newParserState oldProcSuccess.cmdState prom ctx)
return .pure { oldProcessed with result? := some { oldProcSuccess with
firstCmdSnap := { range? := none, task := prom.result } } }
return .pure {
diagnostics := oldProcessed.diagnostics
result? := some {
cmdState := oldProcSuccess.cmdState
firstCmdSnap := { range? := none, task := prom.result } } }
else
return .pure oldProcessed) } }
else return old

View File

@@ -82,7 +82,7 @@ where
return (a, b)
else if a.getAppNumArgs != b.getAppNumArgs then
return (a, b)
else if not ( isDefEq a.getAppFn b.getAppFn) then
else if !( isDefEq a.getAppFn b.getAppFn) then
return (a, b)
else
let fType inferType a.getAppFn

View File

@@ -211,7 +211,7 @@ private def ignoreArg (a : Expr) (i : Nat) (infos : Array ParamInfo) : MetaM Boo
if info.isInstImplicit then
return true
else if info.isImplicit || info.isStrictImplicit then
return not ( isType a)
return !( isType a)
else
isProof a
else

View File

@@ -418,7 +418,7 @@ This method assumes that for any `xs[i]` and `xs[j]` where `i < j`, we have that
where the index is the position in the local context.
-/
private partial def mkLambdaFVarsWithLetDeps (xs : Array Expr) (v : Expr) : MetaM (Option Expr) := do
if not ( hasLetDeclsInBetween) then
if !( hasLetDeclsInBetween) then
mkLambdaFVars xs v (etaReduce := true)
else
let ys addLetDeps

View File

@@ -77,7 +77,7 @@ private def ignoreArg (a : Expr) (i : Nat) (infos : Array ParamInfo) : MetaM Boo
if info.isInstImplicit then
return true
else if info.isImplicit || info.isStrictImplicit then
return not ( isType a)
return !( isType a)
else
isProof a
else

View File

@@ -438,9 +438,11 @@ def buildInductionCase (oldIH newIH : FVarId) (isRecCall : Expr → Option Expr)
let mvar mkFreshExprSyntheticOpaqueMVar goal (tag := `hyp)
let mut mvarId := mvar.mvarId!
mvarId assertIHs IHs mvarId
trace[Meta.FunInd] "Goal before cleanup:{mvarId}"
for fvarId in toClear do
mvarId mvarId.clear fvarId
mvarId mvarId.cleanup (toPreserve := toPreserve)
trace[Meta.FunInd] "Goal after cleanup (toClear := {toClear.map mkFVar}) (toPreserve := {toPreserve.map mkFVar}):{mvarId}"
modify (·.push mvarId)
let mvar instantiateMVars mvar
pure mvar

View File

@@ -77,8 +77,9 @@ builtin_dsimproc [simp, seval] reduceFinMk (Fin.mk _ _) := fun e => do
let_expr Fin.mk n v _ e | return .continue
let some n evalNat n |>.run | return .continue
let some v getNatValue? v | return .continue
if h : n > 0 then
return .done <| toExpr (Fin.ofNat' v h)
if h : n 0 then
have : NeZero n := h
return .done <| toExpr (Fin.ofNat' n v)
else
return .continue

View File

@@ -145,7 +145,7 @@ def errorAtSavedPosFn (msg : String) (delta : Bool) : ParserFn := fun c s =>
/-- Generate an error at the position saved with the `withPosition` combinator.
If `delta == true`, then it reports at saved position+1.
This useful to make sure a parser consumed at least one character. -/
def errorAtSavedPos (msg : String) (delta : Bool) : Parser := {
@[builtin_doc] def errorAtSavedPos (msg : String) (delta : Bool) : Parser := {
fn := errorAtSavedPosFn msg delta
}
@@ -271,7 +271,7 @@ def orelseFn (p q : ParserFn) : ParserFn :=
NOTE: In order for the pretty printer to retrace an `orelse`, `p` must be a call to `node` or some other parser
producing a single node kind. Nested `orelse` calls are flattened for this, i.e. `(node k1 p1 <|> node k2 p2) <|> ...`
is fine as well. -/
def orelse (p q : Parser) : Parser where
@[builtin_doc] def orelse (p q : Parser) : Parser where
info := orelseInfo p.info q.info
fn := orelseFn p.fn q.fn
@@ -295,7 +295,7 @@ This is important for the `p <|> q` combinator, because it is not backtracking,
`p` fails after consuming some tokens. To get backtracking behavior, use `atomic(p) <|> q` instead.
This parser has the same arity as `p` - it produces the same result as `p`. -/
def atomic : Parser Parser := withFn atomicFn
@[builtin_doc] def atomic : Parser Parser := withFn atomicFn
/-- Information about the state of the parse prior to the failing parser's execution -/
structure RecoveryContext where
@@ -335,7 +335,7 @@ state immediately after the failure.
The interactions between <|> and `recover'` are subtle, especially for syntactic
categories that admit user extension. Consider avoiding it in these cases. -/
def recover' (parser : Parser) (handler : RecoveryContext Parser) : Parser where
@[builtin_doc] def recover' (parser : Parser) (handler : RecoveryContext Parser) : Parser where
info := parser.info
fn := recoverFn parser.fn fun s => handler s |>.fn
@@ -347,7 +347,7 @@ If `handler` fails itself, then no recovery is performed.
The interactions between <|> and `recover` are subtle, especially for syntactic
categories that admit user extension. Consider avoiding it in these cases. -/
def recover (parser handler : Parser) : Parser := recover' parser fun _ => handler
@[builtin_doc] def recover (parser handler : Parser) : Parser := recover' parser fun _ => handler
def optionalFn (p : ParserFn) : ParserFn := fun c s =>
let iniSz := s.stackSize
@@ -378,7 +378,7 @@ position to the original state on success. So for example `lookahead("=>")` will
next token is `"=>"`, without actually consuming this token.
This parser has arity 0 - it does not capture anything. -/
def lookahead : Parser Parser := withFn lookaheadFn
@[builtin_doc] def lookahead : Parser Parser := withFn lookaheadFn
def notFollowedByFn (p : ParserFn) (msg : String) : ParserFn := fun c s =>
let iniSz := s.stackSize
@@ -394,7 +394,7 @@ def notFollowedByFn (p : ParserFn) (msg : String) : ParserFn := fun c s =>
if `p` succeeds then it fails with the message `"unexpected foo"`.
This parser has arity 0 - it does not capture anything. -/
def notFollowedBy (p : Parser) (msg : String) : Parser where
@[builtin_doc] def notFollowedBy (p : Parser) (msg : String) : Parser where
fn := notFollowedByFn p.fn msg
partial def manyAux (p : ParserFn) : ParserFn := fun c s => Id.run do
@@ -1143,7 +1143,7 @@ def checkWsBeforeFn (errorMsg : String) : ParserFn := fun _ s =>
For example, the parser `"foo" ws "+"` parses `foo +` or `foo/- -/+` but not `foo+`.
This parser has arity 0 - it does not capture anything. -/
def checkWsBefore (errorMsg : String := "space before") : Parser where
@[builtin_doc] def checkWsBefore (errorMsg : String := "space before") : Parser where
info := epsilonInfo
fn := checkWsBeforeFn errorMsg
@@ -1160,7 +1160,7 @@ def checkLinebreakBeforeFn (errorMsg : String) : ParserFn := fun _ s =>
(The line break may be inside a comment.)
This parser has arity 0 - it does not capture anything. -/
def checkLinebreakBefore (errorMsg : String := "line break") : Parser where
@[builtin_doc] def checkLinebreakBefore (errorMsg : String := "line break") : Parser where
info := epsilonInfo
fn := checkLinebreakBeforeFn errorMsg
@@ -1180,7 +1180,7 @@ This is almost the same as `"foo+"`, but using this parser will make `foo+` a to
problems for the use of `"foo"` and `"+"` as separate tokens in other parsers.
This parser has arity 0 - it does not capture anything. -/
def checkNoWsBefore (errorMsg : String := "no space before") : Parser := {
@[builtin_doc] def checkNoWsBefore (errorMsg : String := "no space before") : Parser := {
info := epsilonInfo
fn := checkNoWsBeforeFn errorMsg
}
@@ -1430,7 +1430,7 @@ position (see `withPosition`). This can be used to do whitespace sensitive synta
a `by` block or `do` block, where all the lines have to line up.
This parser has arity 0 - it does not capture anything. -/
def checkColEq (errorMsg : String := "checkColEq") : Parser where
@[builtin_doc] def checkColEq (errorMsg : String := "checkColEq") : Parser where
fn := checkColEqFn errorMsg
def checkColGeFn (errorMsg : String) : ParserFn := fun c s =>
@@ -1449,7 +1449,7 @@ certain indentation scope. For example it is used in the lean grammar for `else
that the `else` is not less indented than the `if` it matches with.
This parser has arity 0 - it does not capture anything. -/
def checkColGe (errorMsg : String := "checkColGe") : Parser where
@[builtin_doc] def checkColGe (errorMsg : String := "checkColGe") : Parser where
fn := checkColGeFn errorMsg
def checkColGtFn (errorMsg : String) : ParserFn := fun c s =>
@@ -1473,7 +1473,7 @@ Here, the `revert` tactic is followed by a list of `colGt ident`, because otherw
interpret `exact` as an identifier and try to revert a variable named `exact`.
This parser has arity 0 - it does not capture anything. -/
def checkColGt (errorMsg : String := "checkColGt") : Parser where
@[builtin_doc] def checkColGt (errorMsg : String := "checkColGt") : Parser where
fn := checkColGtFn errorMsg
def checkLineEqFn (errorMsg : String) : ParserFn := fun c s =>
@@ -1491,7 +1491,7 @@ different lines. For example, `else if` is parsed using `lineEq` to ensure that
are on the same line.
This parser has arity 0 - it does not capture anything. -/
def checkLineEq (errorMsg : String := "checkLineEq") : Parser where
@[builtin_doc] def checkLineEq (errorMsg : String := "checkLineEq") : Parser where
fn := checkLineEqFn errorMsg
/-- `withPosition(p)` runs `p` while setting the "saved position" to the current position.
@@ -1507,7 +1507,7 @@ The saved position is only available in the read-only state, which is why this i
after the `withPosition(..)` block the saved position will be restored to its original value.
This parser has the same arity as `p` - it just forwards the results of `p`. -/
def withPosition : Parser → Parser := withFn fun f c s =>
@[builtin_doc] def withPosition : Parser → Parser := withFn fun f c s =>
adaptCacheableContextFn ({ · with savedPos? := s.pos }) f c s
def withPositionAfterLinebreak : Parser → Parser := withFn fun f c s =>
@@ -1519,7 +1519,7 @@ parsers like `colGt` will have no effect. This is usually used by bracketing con
`(...)` so that the user can locally override whitespace sensitivity.
This parser has the same arity as `p` - it just forwards the results of `p`. -/
def withoutPosition (p : Parser) : Parser :=
@[builtin_doc] def withoutPosition (p : Parser) : Parser :=
adaptCacheableContext ({ · with savedPos? := none }) p
/-- `withForbidden tk p` runs `p` with `tk` as a "forbidden token". This means that if the token
@@ -1529,7 +1529,7 @@ stop there, making `tk` effectively a lowest-precedence operator. This is used f
would be treated as an application.
This parser has the same arity as `p` - it just forwards the results of `p`. -/
def withForbidden (tk : Token) (p : Parser) : Parser :=
@[builtin_doc] def withForbidden (tk : Token) (p : Parser) : Parser :=
adaptCacheableContext ({ · with forbiddenTk? := tk }) p
/-- `withoutForbidden(p)` runs `p` disabling the "forbidden token" (see `withForbidden`), if any.
@@ -1537,7 +1537,7 @@ This is usually used by bracketing constructs like `(...)` because there is no p
inside these nested constructs.
This parser has the same arity as `p` - it just forwards the results of `p`. -/
def withoutForbidden (p : Parser) : Parser :=
@[builtin_doc] def withoutForbidden (p : Parser) : Parser :=
adaptCacheableContext ({ · with forbiddenTk? := none }) p
def eoiFn : ParserFn := fun c s =>
@@ -1692,7 +1692,7 @@ def termParser (prec : Nat := 0) : Parser :=
-- ==================
/-- Fail if previous token is immediately followed by ':'. -/
def checkNoImmediateColon : Parser := {
@[builtin_doc] def checkNoImmediateColon : Parser := {
fn := fun c s =>
let prev := s.stxStack.back
if checkTailNoWs prev then
@@ -1756,7 +1756,7 @@ def unicodeSymbol (sym asciiSym : String) : Parser :=
Define parser for `$e` (if `anonymous == true`) and `$e:name`.
`kind` is embedded in the antiquotation's kind, and checked at syntax `match` unless `isPseudoKind` is true.
Antiquotations can be escaped as in `$$e`, which produces the syntax tree for `$e`. -/
def mkAntiquot (name : String) (kind : SyntaxNodeKind) (anonymous := true) (isPseudoKind := false) : Parser :=
@[builtin_doc] def mkAntiquot (name : String) (kind : SyntaxNodeKind) (anonymous := true) (isPseudoKind := false) : Parser :=
let kind := kind ++ (if isPseudoKind then `pseudo else .anonymous) ++ `antiquot
let nameP := node `antiquotName <| checkNoWsBefore ("no space before ':" ++ name ++ "'") >> symbol ":" >> nonReservedSymbol name
-- if parsing the kind fails and `anonymous` is true, check that we're not ignoring a different
@@ -1781,7 +1781,7 @@ def withAntiquotFn (antiquotP p : ParserFn) (isCatAntiquot := false) : ParserFn
p c s
/-- Optimized version of `mkAntiquot ... <|> p`. -/
def withAntiquot (antiquotP p : Parser) : Parser := {
@[builtin_doc] def withAntiquot (antiquotP p : Parser) : Parser := {
fn := withAntiquotFn antiquotP.fn p.fn
info := orelseInfo antiquotP.info p.info
}
@@ -1791,7 +1791,7 @@ def withoutInfo (p : Parser) : Parser := {
}
/-- Parse `$[p]suffix`, e.g. `$[p],*`. -/
def mkAntiquotSplice (kind : SyntaxNodeKind) (p suffix : Parser) : Parser :=
@[builtin_doc] def mkAntiquotSplice (kind : SyntaxNodeKind) (p suffix : Parser) : Parser :=
let kind := kind ++ `antiquot_scope
leadingNode kind maxPrec <| atomic <|
setExpected [] "$" >>
@@ -1808,7 +1808,7 @@ private def withAntiquotSuffixSpliceFn (kind : SyntaxNodeKind) (suffix : ParserF
s.mkNode (kind ++ `antiquot_suffix_splice) (s.stxStack.size - 2)
/-- Parse `suffix` after an antiquotation, e.g. `$x,*`, and put both into a new node. -/
def withAntiquotSuffixSplice (kind : SyntaxNodeKind) (p suffix : Parser) : Parser where
@[builtin_doc] def withAntiquotSuffixSplice (kind : SyntaxNodeKind) (p suffix : Parser) : Parser where
info := andthenInfo p.info suffix.info
fn c s :=
let s := p.fn c s

View File

@@ -78,7 +78,7 @@ All modifiers are optional, and have to come in the listed order.
`nestedDeclModifiers` is the same as `declModifiers`, but attributes are printed
on the same line as the declaration. It is used for declarations nested inside other syntax,
such as inductive constructors, structure projections, and `let rec` / `where` definitions. -/
def declModifiers (inline : Bool) := leading_parser
@[builtin_doc] def declModifiers (inline : Bool) := leading_parser
optional docComment >>
optional (Term.«attributes» >> if inline then skip else ppDedent ppLine) >>
optional visibility >>
@@ -86,13 +86,16 @@ def declModifiers (inline : Bool) := leading_parser
optional «unsafe» >>
optional («partial» <|> «nonrec»)
/-- `declId` matches `foo` or `foo.{u,v}`: an identifier possibly followed by a list of universe names -/
def declId := leading_parser
-- @[builtin_doc] -- FIXME: suppress the hover
def declId := leading_parser
ident >> optional (".{" >> sepBy1 (recover ident (skipUntil (fun c => c.isWhitespace || c [',', '}']))) ", " >> "}")
/-- `declSig` matches the signature of a declaration with required type: a list of binders and then `: type` -/
def declSig := leading_parser
-- @[builtin_doc] -- FIXME: suppress the hover
def declSig := leading_parser
many (ppSpace >> (Term.binderIdent <|> Term.bracketedBinder)) >> Term.typeSpec
/-- `optDeclSig` matches the signature of a declaration with optional type: a list of binders and then possibly `: type` -/
def optDeclSig := leading_parser
-- @[builtin_doc] -- FIXME: suppress the hover
def optDeclSig := leading_parser
many (ppSpace >> (Term.binderIdent <|> Term.bracketedBinder)) >> Term.optType
/-- Right-hand side of a `:=` in a declaration, a term. -/
def declBody : Parser :=
@@ -141,11 +144,11 @@ def whereStructInst := leading_parser
* a sequence of `| pat => expr` (a declaration by equations), shorthand for a `match`
* `where` and then a sequence of `field := value` initializers, shorthand for a structure constructor
-/
def declVal :=
@[builtin_doc] def declVal :=
-- Remark: we should not use `Term.whereDecls` at `declVal`
-- because `Term.whereDecls` is defined using `Term.letRecDecl` which may contain attributes.
-- Issue #753 shows an example that fails to be parsed when we used `Term.whereDecls`.
withAntiquot (mkAntiquot "declVal" `Lean.Parser.Command.declVal (isPseudoKind := true)) <|
withAntiquot (mkAntiquot "declVal" decl_name% (isPseudoKind := true)) <|
declValSimple <|> declValEqns <|> whereStructInst
def «abbrev» := leading_parser
"abbrev " >> declId >> ppIndent optDeclSig >> declVal
@@ -193,9 +196,10 @@ inductive List (α : Type u) where
```
A list of elements of type `α` is either the empty list, `nil`,
or an element `head : α` followed by a list `tail : List α`.
For more information about [inductive types](https://lean-lang.org/theorem_proving_in_lean4/inductive_types.html).
See [Inductive types](https://lean-lang.org/theorem_proving_in_lean4/inductive_types.html)
for more information.
-/
def «inductive» := leading_parser
@[builtin_doc] def «inductive» := leading_parser
"inductive " >> recover declId skipUntilWsOrDelim >> ppIndent optDeclSig >> optional (symbol " :=" <|> " where") >>
many ctor >> optional (ppDedent ppLine >> computedFields) >> optDeriving
def classInductive := leading_parser
@@ -544,7 +548,7 @@ def openSimple := leading_parser
def openScoped := leading_parser
" scoped" >> many1 (ppSpace >> checkColGt >> ident)
/-- `openDecl` is the body of an `open` declaration (see `open`) -/
def openDecl :=
@[builtin_doc] def openDecl :=
withAntiquot (mkAntiquot "openDecl" `Lean.Parser.Command.openDecl (isPseudoKind := true)) <|
openHiding <|> openRenaming <|> openOnly <|> openSimple <|> openScoped
/-- Makes names from other namespaces visible without writing the namespace prefix.

View File

@@ -30,7 +30,7 @@ def doSeqBracketed := leading_parser
do elements that take blocks. It can either have the form `"{" (doElem ";"?)* "}"` or
`many1Indent (doElem ";"?)`, where `many1Indent` ensures that all the items are at
the same or higher indentation level as the first line. -/
def doSeq :=
@[builtin_doc] def doSeq :=
withAntiquot (mkAntiquot "doSeq" decl_name% (isPseudoKind := true)) <|
doSeqBracketed <|> doSeqIndent
/-- `termBeforeDo` is defined as `withForbidden("do", term)`, which will parse a term but

View File

@@ -31,7 +31,7 @@ it to parse correctly. `ident?` will not work; one must write `(ident)?` instead
This parser has arity 1: it produces a `nullKind` node containing either zero arguments
(for the `none` case) or the list of arguments produced by `p`.
(In particular, if `p` has arity 0 then the two cases are not differentiated!) -/
@[run_builtin_parser_attribute_hooks] def optional (p : Parser) : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def optional (p : Parser) : Parser :=
optionalNoAntiquot (withAntiquotSpliceAndSuffix `optional p (symbol "?"))
/-- The parser `many(p)`, or `p*`, repeats `p` until it fails, and returns the list of results.
@@ -41,7 +41,7 @@ automatically replaced by `group(p)` to ensure that it produces exactly 1 value.
This parser has arity 1: it produces a `nullKind` node containing one argument for each
invocation of `p` (or `group(p)`). -/
@[run_builtin_parser_attribute_hooks] def many (p : Parser) : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def many (p : Parser) : Parser :=
manyNoAntiquot (withAntiquotSpliceAndSuffix `many p (symbol "*"))
/-- The parser `many1(p)`, or `p+`, repeats `p` until it fails, and returns the list of results.
@@ -56,7 +56,7 @@ automatically replaced by `group(p)` to ensure that it produces exactly 1 value.
This parser has arity 1: it produces a `nullKind` node containing one argument for each
invocation of `p` (or `group(p)`). -/
@[run_builtin_parser_attribute_hooks] def many1 (p : Parser) : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def many1 (p : Parser) : Parser :=
many1NoAntiquot (withAntiquotSpliceAndSuffix `many p (symbol "*"))
/-- The parser `ident` parses a single identifier, possibly with namespaces, such as `foo` or
@@ -70,18 +70,18 @@ using disallowed characters in identifiers such as `«foo.bar».baz` or `«hello
This parser has arity 1: it produces a `Syntax.ident` node containing the parsed identifier.
You can use `TSyntax.getId` to extract the name from the resulting syntax object. -/
@[run_builtin_parser_attribute_hooks] def ident : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def ident : Parser :=
withAntiquot (mkAntiquot "ident" identKind) identNoAntiquot
-- `optional (checkNoWsBefore >> "." >> checkNoWsBefore >> ident)`
-- can never fully succeed but ensures that the identifier
-- produces a partial syntax that contains the dot.
-- The partial syntax is sometimes useful for dot-auto-completion.
@[run_builtin_parser_attribute_hooks] def identWithPartialTrailingDot :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def identWithPartialTrailingDot :=
ident >> optional (checkNoWsBefore >> "." >> checkNoWsBefore >> ident)
-- `ident` and `rawIdent` produce the same syntax tree, so we reuse the antiquotation kind name
@[run_builtin_parser_attribute_hooks] def rawIdent : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def rawIdent : Parser :=
withAntiquot (mkAntiquot "ident" identKind) rawIdentNoAntiquot
/-- The parser `hygieneInfo` parses no text, but captures the current macro scope information
@@ -96,7 +96,7 @@ for more information about macro hygiene.
This parser has arity 1: it produces a `Syntax.ident` node containing the parsed identifier.
You can use `TSyntax.getHygieneInfo` to extract the name from the resulting syntax object. -/
@[run_builtin_parser_attribute_hooks] def hygieneInfo : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def hygieneInfo : Parser :=
withAntiquot (mkAntiquot "hygieneInfo" hygieneInfoKind (anonymous := false)) hygieneInfoNoAntiquot
/-- The parser `num` parses a numeric literal in several bases:
@@ -109,7 +109,7 @@ You can use `TSyntax.getHygieneInfo` to extract the name from the resulting synt
This parser has arity 1: it produces a `numLitKind` node containing an atom with the text of the
literal.
You can use `TSyntax.getNat` to extract the number from the resulting syntax object. -/
@[run_builtin_parser_attribute_hooks] def numLit : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def numLit : Parser :=
withAntiquot (mkAntiquot "num" numLitKind) numLitNoAntiquot
/-- The parser `scientific` parses a scientific-notation literal, such as `1.3e-24`.
@@ -117,7 +117,7 @@ You can use `TSyntax.getNat` to extract the number from the resulting syntax obj
This parser has arity 1: it produces a `scientificLitKind` node containing an atom with the text
of the literal.
You can use `TSyntax.getScientific` to extract the parts from the resulting syntax object. -/
@[run_builtin_parser_attribute_hooks] def scientificLit : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def scientificLit : Parser :=
withAntiquot (mkAntiquot "scientific" scientificLitKind) scientificLitNoAntiquot
/-- The parser `str` parses a string literal, such as `"foo"` or `"\r\n"`. Strings can contain
@@ -127,7 +127,7 @@ Newlines in a string are interpreted literally.
This parser has arity 1: it produces a `strLitKind` node containing an atom with the raw
literal (including the quote marks and without interpreting the escapes).
You can use `TSyntax.getString` to decode the string from the resulting syntax object. -/
@[run_builtin_parser_attribute_hooks] def strLit : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def strLit : Parser :=
withAntiquot (mkAntiquot "str" strLitKind) strLitNoAntiquot
/-- The parser `char` parses a character literal, such as `'a'` or `'\n'`. Character literals can
@@ -138,7 +138,7 @@ like `∈`, but must evaluate to a single unicode codepoint, so `'♥'` is allow
This parser has arity 1: it produces a `charLitKind` node containing an atom with the raw
literal (including the quote marks and without interpreting the escapes).
You can use `TSyntax.getChar` to decode the string from the resulting syntax object. -/
@[run_builtin_parser_attribute_hooks] def charLit : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def charLit : Parser :=
withAntiquot (mkAntiquot "char" charLitKind) charLitNoAntiquot
/-- The parser `name` parses a name literal like `` `foo``. The syntax is the same as for identifiers
@@ -147,7 +147,7 @@ You can use `TSyntax.getChar` to decode the string from the resulting syntax obj
This parser has arity 1: it produces a `nameLitKind` node containing the raw literal
(including the backquote).
You can use `TSyntax.getName` to extract the name from the resulting syntax object. -/
@[run_builtin_parser_attribute_hooks] def nameLit : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] def nameLit : Parser :=
withAntiquot (mkAntiquot "name" nameLitKind) nameLitNoAntiquot
/-- The parser `group(p)` parses the same thing as `p`, but it wraps the results in a `groupKind`
@@ -156,7 +156,7 @@ node.
This parser always has arity 1, even if `p` does not. Parsers like `p*` are automatically
rewritten to `group(p)*` if `p` does not have arity 1, so that the results from separate invocations
of `p` can be differentiated. -/
@[run_builtin_parser_attribute_hooks, inline] def group (p : Parser) : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc, inline] def group (p : Parser) : Parser :=
node groupKind p
/-- The parser `many1Indent(p)` is equivalent to `withPosition((colGe p)+)`. This has the effect of
@@ -165,7 +165,7 @@ the same or more than the first parse.
This parser has arity 1, and returns a list of the results from `p`.
`p` is "auto-grouped" if it is not arity 1. -/
@[run_builtin_parser_attribute_hooks, inline] def many1Indent (p : Parser) : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc, inline] def many1Indent (p : Parser) : Parser :=
withPosition $ many1 (checkColGe "irrelevant" >> p)
/-- The parser `manyIndent(p)` is equivalent to `withPosition((colGe p)*)`. This has the effect of
@@ -174,14 +174,14 @@ the same or more than the first parse.
This parser has arity 1, and returns a list of the results from `p`.
`p` is "auto-grouped" if it is not arity 1. -/
@[run_builtin_parser_attribute_hooks, inline] def manyIndent (p : Parser) : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc, inline] def manyIndent (p : Parser) : Parser :=
withPosition $ many (checkColGe "irrelevant" >> p)
@[inline] def sepByIndent (p : Parser) (sep : String) (psep : Parser := symbol sep) (allowTrailingSep : Bool := false) : Parser :=
@[builtin_doc, inline] def sepByIndent (p : Parser) (sep : String) (psep : Parser := symbol sep) (allowTrailingSep : Bool := false) : Parser :=
let p := withAntiquotSpliceAndSuffix `sepBy p (symbol "*")
withPosition $ sepBy (checkColGe "irrelevant" >> p) sep (psep <|> checkColEq "irrelevant" >> checkLinebreakBefore >> pushNone) allowTrailingSep
@[inline] def sepBy1Indent (p : Parser) (sep : String) (psep : Parser := symbol sep) (allowTrailingSep : Bool := false) : Parser :=
@[builtin_doc, inline] def sepBy1Indent (p : Parser) (sep : String) (psep : Parser := symbol sep) (allowTrailingSep : Bool := false) : Parser :=
let p := withAntiquotSpliceAndSuffix `sepBy p (symbol "*")
withPosition $ sepBy1 (checkColGe "irrelevant" >> p) sep (psep <|> checkColEq "irrelevant" >> checkLinebreakBefore >> pushNone) allowTrailingSep
@@ -205,32 +205,33 @@ def sepByIndent.formatter (p : Formatter) (_sep : String) (pSep : Formatter) : F
attribute [run_builtin_parser_attribute_hooks] sepByIndent sepBy1Indent
@[run_builtin_parser_attribute_hooks] abbrev notSymbol (s : String) : Parser :=
@[run_builtin_parser_attribute_hooks, builtin_doc] abbrev notSymbol (s : String) : Parser :=
notFollowedBy (symbol s) s
/-- No-op parser combinator that annotates subtrees to be ignored in syntax patterns. -/
@[inline, run_builtin_parser_attribute_hooks] def patternIgnore : Parser Parser := node `patternIgnore
@[run_builtin_parser_attribute_hooks, builtin_doc, inline]
def patternIgnore : Parser Parser := node `patternIgnore
/-- No-op parser that advises the pretty printer to emit a non-breaking space. -/
@[inline] def ppHardSpace : Parser := skip
@[builtin_doc, inline] def ppHardSpace : Parser := skip
/-- No-op parser that advises the pretty printer to emit a space/soft line break. -/
@[inline] def ppSpace : Parser := skip
@[builtin_doc, inline] def ppSpace : Parser := skip
/-- No-op parser that advises the pretty printer to emit a hard line break. -/
@[inline] def ppLine : Parser := skip
@[builtin_doc, inline] def ppLine : Parser := skip
/-- No-op parser combinator that advises the pretty printer to emit a `Format.fill` node. -/
@[inline] def ppRealFill : Parser Parser := id
@[builtin_doc, inline] def ppRealFill : Parser Parser := id
/-- No-op parser combinator that advises the pretty printer to emit a `Format.group` node. -/
@[inline] def ppRealGroup : Parser Parser := id
@[builtin_doc, inline] def ppRealGroup : Parser Parser := id
/-- No-op parser combinator that advises the pretty printer to indent the given syntax without grouping it. -/
@[inline] def ppIndent : Parser Parser := id
@[builtin_doc, inline] def ppIndent : Parser Parser := id
/--
No-op parser combinator that advises the pretty printer to group and indent the given syntax.
By default, only syntax categories are grouped. -/
@[inline] def ppGroup (p : Parser) : Parser := ppRealFill (ppIndent p)
@[builtin_doc, inline] def ppGroup (p : Parser) : Parser := ppRealFill (ppIndent p)
/--
No-op parser combinator that advises the pretty printer to dedent the given syntax.
Dedenting can in particular be used to counteract automatic indentation. -/
@[inline] def ppDedent : Parser Parser := id
@[builtin_doc, inline] def ppDedent : Parser Parser := id
/--
No-op parser combinator that allows the pretty printer to omit the group and
@@ -242,19 +243,19 @@ attribute [run_builtin_parser_attribute_hooks] sepByIndent sepBy1Indent
trivial
```
-/
@[inline] def ppAllowUngrouped : Parser := skip
@[builtin_doc, inline] def ppAllowUngrouped : Parser := skip
/--
No-op parser combinator that advises the pretty printer to dedent the given syntax,
if it was grouped by the category parser.
Dedenting can in particular be used to counteract automatic indentation. -/
@[inline] def ppDedentIfGrouped : Parser Parser := id
@[builtin_doc, inline] def ppDedentIfGrouped : Parser Parser := id
/--
No-op parser combinator that prints a line break.
The line break is soft if the combinator is followed
by an ungrouped parser (see ppAllowUngrouped), otherwise hard. -/
@[inline] def ppHardLineUnlessUngrouped : Parser := skip
@[builtin_doc, inline] def ppHardLineUnlessUngrouped : Parser := skip
end Parser

View File

@@ -68,7 +68,7 @@ This parser has arity 1, and returns a `interpolatedStrKind` with an odd number
alternating between chunks of literal text and results from `p`. The literal chunks contain
uninterpreted substrings of the input. For example, `"foo\n{2 + 2}"` would have three arguments:
an atom `"foo\n{`, the parsed `2 + 2` term, and then the atom `}"`. -/
def interpolatedStr (p : Parser) : Parser :=
@[builtin_doc] def interpolatedStr (p : Parser) : Parser :=
withAntiquot (mkAntiquot "interpolatedStr" interpolatedStrKind) $ interpolatedStrNoAntiquot p
end Lean.Parser

View File

@@ -24,6 +24,7 @@ a regular comment (that is, as whitespace); it is parsed and forms part of the s
A `docComment` node contains a `/--` atom and then the remainder of the comment, `foo -/` in this
example. Use `TSyntax.getDocString` to extract the body text from a doc string syntax node. -/
-- @[builtin_doc] -- FIXME: suppress the hover
def docComment := leading_parser
ppDedent $ "/--" >> ppSpace >> commentBody >> ppLine
end Command
@@ -49,7 +50,7 @@ example := by
skip
```
is legal, but `by skip skip` is not - it must be written as `by skip; skip`. -/
@[run_builtin_parser_attribute_hooks]
@[run_builtin_parser_attribute_hooks, builtin_doc]
def sepByIndentSemicolon (p : Parser) : Parser :=
sepByIndent p "; " (allowTrailingSep := true)
@@ -62,7 +63,7 @@ example := by
skip
```
is legal, but `by skip skip` is not - it must be written as `by skip; skip`. -/
@[run_builtin_parser_attribute_hooks]
@[run_builtin_parser_attribute_hooks, builtin_doc]
def sepBy1IndentSemicolon (p : Parser) : Parser :=
sepBy1Indent p "; " (allowTrailingSep := true)
@@ -70,22 +71,22 @@ builtin_initialize
register_parser_alias sepByIndentSemicolon
register_parser_alias sepBy1IndentSemicolon
def tacticSeq1Indented : Parser := leading_parser
@[builtin_doc] def tacticSeq1Indented : Parser := leading_parser
sepBy1IndentSemicolon tacticParser
/-- The syntax `{ tacs }` is an alternative syntax for `· tacs`.
It runs the tactics in sequence, and fails if the goal is not solved. -/
def tacticSeqBracketed : Parser := leading_parser
@[builtin_doc] def tacticSeqBracketed : Parser := leading_parser
"{" >> sepByIndentSemicolon tacticParser >> ppDedent (ppLine >> "}")
/-- A sequence of tactics in brackets, or a delimiter-free indented sequence of tactics.
Delimiter-free indentation is determined by the *first* tactic of the sequence. -/
def tacticSeq := leading_parser
@[builtin_doc] def tacticSeq := leading_parser
tacticSeqBracketed <|> tacticSeq1Indented
/-- Same as [`tacticSeq`] but requires delimiter-free tactic sequence to have strict indentation.
The strict indentation requirement only apply to *nested* `by`s, as top-level `by`s do not have a
position set. -/
def tacticSeqIndentGt := withAntiquot (mkAntiquot "tacticSeq" ``tacticSeq) <| node ``tacticSeq <|
@[builtin_doc] def tacticSeqIndentGt := withAntiquot (mkAntiquot "tacticSeq" ``tacticSeq) <| node ``tacticSeq <|
tacticSeqBracketed <|> (checkColGt "indented tactic sequence" >> tacticSeq1Indented)
/- Raw sequence for quotation and grouping -/
@@ -206,7 +207,7 @@ def fromTerm := leading_parser
def showRhs := fromTerm <|> byTactic'
/-- A `sufficesDecl` represents everything that comes after the `suffices` keyword:
an optional `x :`, then a term `ty`, then `from val` or `by tac`. -/
def sufficesDecl := leading_parser
@[builtin_doc] def sufficesDecl := leading_parser
(atomic (group (binderIdent >> " : ")) <|> hygieneInfo) >> termParser >> ppSpace >> showRhs
@[builtin_term_parser] def «suffices» := leading_parser:leadPrec
withPosition ("suffices " >> sufficesDecl) >> optSemicolon termParser
@@ -272,7 +273,7 @@ open Lean.PrettyPrinter Parenthesizer Syntax.MonadTraverser in
Explicit binder, like `(x y : A)` or `(x y)`.
Default values can be specified using `(x : A := v)` syntax, and tactics using `(x : A := by tac)`.
-/
def explicitBinder (requireType := false) := leading_parser ppGroup <|
@[builtin_doc] def explicitBinder (requireType := false) := leading_parser ppGroup <|
"(" >> withoutPosition (many1 binderIdent >> binderType requireType >> optional (binderTactic <|> binderDefault)) >> ")"
/--
Implicit binder, like `{x y : A}` or `{x y}`.
@@ -283,7 +284,7 @@ by unification.
In `@` explicit mode, implicit binders behave like explicit binders.
-/
def implicitBinder (requireType := false) := leading_parser ppGroup <|
@[builtin_doc] def implicitBinder (requireType := false) := leading_parser ppGroup <|
"{" >> withoutPosition (many1 binderIdent >> binderType requireType) >> "}"
def strictImplicitLeftBracket := atomic (group (symbol "{" >> "{")) <|> ""
def strictImplicitRightBracket := atomic (group (symbol "}" >> "}")) <|> ""
@@ -300,7 +301,7 @@ and `h hs` has type `p y`.
In contrast, if `h' : ∀ {x : A}, x ∈ s → p x`, then `h` by itself elaborates to have type `?m ∈ s → p ?m`
with `?m` a fresh metavariable.
-/
def strictImplicitBinder (requireType := false) := leading_parser ppGroup <|
@[builtin_doc] def strictImplicitBinder (requireType := false) := leading_parser ppGroup <|
strictImplicitLeftBracket >> many1 binderIdent >>
binderType requireType >> strictImplicitRightBracket
/--
@@ -310,7 +311,7 @@ and solved for by typeclass inference for the specified class `C`.
In `@` explicit mode, if `_` is used for an an instance-implicit parameter, then it is still solved for by typeclass inference;
use `(_)` to inhibit this and have it be solved for by unification instead, like an implicit argument.
-/
def instBinder := leading_parser ppGroup <|
@[builtin_doc] def instBinder := leading_parser ppGroup <|
"[" >> withoutPosition (optIdent >> termParser) >> "]"
/-- A `bracketedBinder` matches any kind of binder group that uses some kind of brackets:
* An explicit binder like `(x y : A)`
@@ -318,7 +319,7 @@ def instBinder := leading_parser ppGroup <|
* A strict implicit binder, `⦃y z : A⦄` or its ASCII alternative `{{y z : A}}`
* An instance binder `[A]` or `[x : A]` (multiple variables are not allowed here)
-/
def bracketedBinder (requireType := false) :=
@[builtin_doc] def bracketedBinder (requireType := false) :=
withAntiquot (mkAntiquot "bracketedBinder" decl_name% (isPseudoKind := true)) <|
explicitBinder requireType <|> strictImplicitBinder requireType <|>
implicitBinder requireType <|> instBinder
@@ -366,7 +367,7 @@ def matchAlts (rhsParser : Parser := termParser) : Parser :=
/-- `matchDiscr` matches a "match discriminant", either `h : tm` or `tm`, used in `match` as
`match h1 : e1, e2, h3 : e3 with ...`. -/
def matchDiscr := leading_parser
@[builtin_doc] def matchDiscr := leading_parser
optional (atomic (ident >> " : ")) >> termParser
def trueVal := leading_parser nonReservedSymbol "true"
@@ -497,7 +498,7 @@ def letEqnsDecl := leading_parser (withAnonymousAntiquot := false)
`let pat := e` (where `pat` is an arbitrary term) or `let f | pat1 => e1 | pat2 => e2 ...`
(a pattern matching declaration), except for the `let` keyword itself.
`let rec` declarations are not handled here. -/
def letDecl := leading_parser (withAnonymousAntiquot := false)
@[builtin_doc] def letDecl := leading_parser (withAnonymousAntiquot := false)
-- Remark: we disable anonymous antiquotations here to make sure
-- anonymous antiquotations (e.g., `$x`) are not `letDecl`
notFollowedBy (nonReservedSymbol "rec") "rec" >>
@@ -556,7 +557,7 @@ def haveEqnsDecl := leading_parser (withAnonymousAntiquot := false)
/-- `haveDecl` matches the body of a have declaration: `have := e`, `have f x1 x2 := e`,
`have pat := e` (where `pat` is an arbitrary term) or `have f | pat1 => e1 | pat2 => e2 ...`
(a pattern matching declaration), except for the `have` keyword itself. -/
def haveDecl := leading_parser (withAnonymousAntiquot := false)
@[builtin_doc] def haveDecl := leading_parser (withAnonymousAntiquot := false)
haveIdDecl <|> (ppSpace >> letPatDecl) <|> haveEqnsDecl
@[builtin_term_parser] def «have» := leading_parser:leadPrec
withPosition ("have" >> haveDecl) >> optSemicolon termParser
@@ -570,7 +571,7 @@ def haveDecl := leading_parser (withAnonymousAntiquot := false)
def «scoped» := leading_parser "scoped "
def «local» := leading_parser "local "
/-- `attrKind` matches `("scoped" <|> "local")?`, used before an attribute like `@[local simp]`. -/
def attrKind := leading_parser optional («scoped» <|> «local»)
@[builtin_doc] def attrKind := leading_parser optional («scoped» <|> «local»)
def attrInstance := ppGroup $ leading_parser attrKind >> attrParser
def attributes := leading_parser
@@ -607,13 +608,13 @@ If omitted, a termination argument will be inferred. If written as `termination_
the inferrred termination argument will be suggested.
-/
def terminationBy := leading_parser
@[builtin_doc] def terminationBy := leading_parser
"termination_by " >>
optional (nonReservedSymbol "structural ") >>
optional (atomic (many (ppSpace >> Term.binderIdent) >> " => ")) >>
termParser
@[inherit_doc terminationBy]
@[inherit_doc terminationBy, builtin_doc]
def terminationBy? := leading_parser
"termination_by?"
@@ -626,13 +627,13 @@ By default, the tactic `decreasing_tactic` is used.
Forces the use of well-founded recursion and is hence incompatible with
`termination_by structural`.
-/
def decreasingBy := leading_parser
@[builtin_doc] def decreasingBy := leading_parser
ppDedent ppLine >> "decreasing_by " >> Tactic.tacticSeqIndentGt
/--
Termination hints are `termination_by` and `decreasing_by`, in that order.
-/
def suffix := leading_parser
@[builtin_doc] def suffix := leading_parser
optional (ppDedent ppLine >> (terminationBy? <|> terminationBy)) >> optional decreasingBy
end Termination
@@ -640,9 +641,10 @@ namespace Term
/-- `letRecDecl` matches the body of a let-rec declaration: a doc comment, attributes, and then
a let declaration without the `let` keyword, such as `/-- foo -/ @[simp] bar := 1`. -/
def letRecDecl := leading_parser
@[builtin_doc] def letRecDecl := leading_parser
optional Command.docComment >> optional «attributes» >> letDecl >> Termination.suffix
/-- `letRecDecls` matches `letRecDecl,+`, a comma-separated list of let-rec declarations (see `letRecDecl`). -/
-- @[builtin_doc] -- FIXME: suppress the hover
def letRecDecls := leading_parser
sepBy1 letRecDecl ", "
@[builtin_term_parser]

View File

@@ -7,6 +7,7 @@ prelude
import Lean.Data.Trie
import Lean.Syntax
import Lean.Message
import Lean.DocString.Extension
namespace Lean.Parser
@@ -428,7 +429,7 @@ def withCacheFn (parserName : Name) (p : ParserFn) : ParserFn := fun c s => Id.r
panic! s!"withCacheFn: unexpected stack growth {s.stxStack.raw}"
{ s with cache.parserCache := s.cache.parserCache.insert key s.stxStack.back, s.lhsPrec, s.pos, s.errorMsg }
@[inherit_doc withCacheFn]
@[inherit_doc withCacheFn, builtin_doc]
def withCache (parserName : Name) : Parser Parser := withFn (withCacheFn parserName)
def ParserFn.run (p : ParserFn) (ictx : InputContext) (pmctx : ParserModuleContext) (tokens : TokenTable) (s : ParserState) : ParserState :=

View File

@@ -137,7 +137,7 @@ def isNonConstFun (motive : Expr) : MetaM Bool := do
| _ => return motive.hasLooseBVars
def isSimpleHOFun (motive : Expr) : MetaM Bool :=
return not ( returnsPi motive) && not ( isNonConstFun motive)
return !( returnsPi motive) && !( isNonConstFun motive)
def isType2Type (motive : Expr) : MetaM Bool := do
match inferType motive with
@@ -278,7 +278,7 @@ where
inspectAux (fType mType : Expr) (i : Nat) (args mvars : Array Expr) := do
let fType whnf fType
let mType whnf mType
if not (i < args.size) then return ()
if !(i < args.size) then return ()
match fType, mType with
| Expr.forallE _ fd fb _, Expr.forallE _ _ mb _ => do
-- TODO: do I need to check (← okBottomUp? args[i] mvars[i] fuel).isSafe here?
@@ -324,7 +324,7 @@ def withKnowing (knowsType knowsLevel : Bool) (x : AnalyzeM α) : AnalyzeM α :=
builtin_initialize analyzeFailureId : InternalExceptionId registerInternalExceptionId `analyzeFailure
def checkKnowsType : AnalyzeM Unit := do
if not ( read).knowsType then
if !( read).knowsType then
throw $ Exception.internal analyzeFailureId
def annotateBoolAt (n : Name) (pos : Pos) : AnalyzeM Unit := do
@@ -425,7 +425,7 @@ mutual
funBinders := mkArray args.size false
}
if not rest.isEmpty then
if !rest.isEmpty then
-- Note: this shouldn't happen for type-correct terms
if !args.isEmpty then
analyzeAppStaged (mkAppN f args) rest
@@ -501,11 +501,11 @@ mutual
collectHigherOrders := do
let { args, mvars, bInfos, ..} read
for i in [:args.size] do
if not (bInfos[i]! == BinderInfo.implicit || bInfos[i]! == BinderInfo.strictImplicit) then continue
if not ( isHigherOrder ( inferType args[i]!)) then continue
if !(bInfos[i]! == BinderInfo.implicit || bInfos[i]! == BinderInfo.strictImplicit) then continue
if !( isHigherOrder ( inferType args[i]!)) then continue
if getPPAnalyzeTrustId ( getOptions) && isIdLike args[i]! then continue
if getPPAnalyzeTrustKnownFOType2TypeHOFuns ( getOptions) && not ( valUnknown mvars[i]!)
if getPPAnalyzeTrustKnownFOType2TypeHOFuns ( getOptions) && !( valUnknown mvars[i]!)
&& ( isType2Type (args[i]!)) && ( isFOLike (args[i]!)) then continue
tryUnify args[i]! mvars[i]!
@@ -602,7 +602,7 @@ mutual
| _ => annotateNamedArg ( mvarName mvars[i]!)
else annotateBool `pp.analysis.skip; provided := false
modify fun s => { s with provideds := s.provideds.set! i provided }
if ( get).provideds[i]! then withKnowing (not ( typeUnknown mvars[i]!)) true analyze
if ( get).provideds[i]! then withKnowing (!( typeUnknown mvars[i]!)) true analyze
tryUnify mvars[i]! args[i]!
maybeSetExplicit := do

View File

@@ -235,7 +235,6 @@ private def normPrivateName? (declName : Name) : MetaM (Option Name) := do
Remark: `danglingDot == true` when the completion point is an identifier followed by `.`.
-/
private def matchDecl? (ns : Name) (id : Name) (danglingDot : Bool) (declName : Name) : MetaM (Option (Name × Float)) := do
-- dbg_trace "{ns}, {id}, {declName}, {danglingDot}"
let some declName normPrivateName? declName
| return none
if !ns.isPrefixOf declName then
@@ -324,16 +323,24 @@ def completeNamespaces (ctx : ContextInfo) (id : Name) (danglingDot : Bool) : M
private def idCompletionCore
(ctx : ContextInfo)
(stx : Syntax)
(id : Name)
(hoverInfo : HoverInfo)
(danglingDot : Bool)
: M Unit := do
let mut id := id.eraseMacroScopes
let mut id := id
if id.hasMacroScopes then
if stx.getHeadInfo matches .original .. then
id := id.eraseMacroScopes
else
-- Identifier is synthetic and has macro scopes => no completions
-- Erasing the macro scopes does not make sense in this case because the identifier name
-- is some random synthetic string.
return
let mut danglingDot := danglingDot
if let HoverInfo.inside delta := hoverInfo then
id := truncate id delta
danglingDot := false
-- dbg_trace ">> id {id} : {expectedType?}"
if id.isAtomic then
-- search for matches in the local context
for localDecl in ( getLCtx) do
@@ -419,12 +426,13 @@ private def idCompletion
(params : CompletionParams)
(ctx : ContextInfo)
(lctx : LocalContext)
(stx : Syntax)
(id : Name)
(hoverInfo : HoverInfo)
(danglingDot : Bool)
: IO (Option CompletionList) :=
runM params ctx lctx do
idCompletionCore ctx id hoverInfo danglingDot
idCompletionCore ctx stx id hoverInfo danglingDot
private def unfoldeDefinitionGuarded? (e : Expr) : MetaM (Option Expr) :=
try unfoldDefinition? e catch _ => pure none
@@ -525,10 +533,10 @@ private def dotCompletion
if nameSet.isEmpty then
let stx := info.stx
if stx.isIdent then
idCompletionCore ctx stx.getId hoverInfo (danglingDot := false)
idCompletionCore ctx stx stx.getId hoverInfo (danglingDot := false)
else if stx.getKind == ``Lean.Parser.Term.completion && stx[0].isIdent then
-- TODO: truncation when there is a dangling dot
idCompletionCore ctx stx[0].getId HoverInfo.after (danglingDot := true)
idCompletionCore ctx stx stx[0].getId HoverInfo.after (danglingDot := true)
else
failure
return
@@ -658,6 +666,50 @@ private def tacticCompletion (params : CompletionParams) (ctx : ContextInfo) : I
}, 1)
return some { items := sortCompletionItems items, isIncomplete := true }
/--
If there are `Info`s that contain `hoverPos` and have a nonempty `LocalContext`,
yields the closest one of those `Info`s.
Otherwise, yields the closest `Info` that contains `hoverPos` and has an empty `LocalContext`.
-/
private def findClosestInfoWithLocalContextAt?
(hoverPos : String.Pos)
(infoTree : InfoTree)
: Option (ContextInfo × Info) :=
infoTree.visitM (m := Id) (postNode := choose) |>.join
where
choose
(ctx : ContextInfo)
(info : Info)
(_ : PersistentArray InfoTree)
(childValues : List (Option (Option (ContextInfo × Info))))
: Option (ContextInfo × Info) :=
let bestChildValue := childValues.map (·.join) |>.foldl (init := none) fun v best =>
if isBetter v best then
v
else
best
if info.occursInOrOnBoundary hoverPos && isBetter (ctx, info) bestChildValue then
(ctx, info)
else
bestChildValue
isBetter (a b : Option (ContextInfo × Info)) : Bool :=
match a, b with
| none, none => false
| some _, none => true
| none, some _ => false
| some (_, ia), some (_, ib) =>
if !ia.lctx.isEmpty && ib.lctx.isEmpty then
true
else if ia.lctx.isEmpty && !ib.lctx.isEmpty then
false
else if ia.isSmaller ib then
true
else if ib.isSmaller ia then
false
else
false
private def findCompletionInfoAt?
(fileMap : FileMap)
(hoverPos : String.Pos)
@@ -667,9 +719,32 @@ private def findCompletionInfoAt?
match infoTree.foldInfo (init := none) (choose hoverLine) with
| some (hoverInfo, ctx, Info.ofCompletionInfo info) =>
some (hoverInfo, ctx, info)
| _ =>
-- TODO try to extract id from `fileMap` and some `ContextInfo` from `InfoTree`
none
| _ => do
-- No completion info => Attempt providing identifier completions
let some (ctx, info) := findClosestInfoWithLocalContextAt? hoverPos infoTree
| none
let some stack := info.stx.findStack? (·.getRange?.any (·.contains hoverPos (includeStop := true)))
| none
let stack := stack.dropWhile fun (stx, _) => !(stx matches `($_:ident) || stx matches `($_:ident.))
let some (stx, _) := stack.head?
| none
let isDotIdCompletion := stack.any fun (stx, _) => stx matches `(.$_:ident)
if isDotIdCompletion then
-- An identifier completion is never useful in a dotId completion context.
none
let some (id, danglingDot) :=
match stx with
| `($id:ident) => some (id.getId, false)
| `($id:ident.) => some (id.getId, true)
| _ => none
| none
let tailPos := stx.getTailPos?.get!
let hoverInfo :=
if hoverPos < tailPos then
HoverInfo.inside (tailPos - hoverPos).byteIdx
else
HoverInfo.after
some (hoverInfo, ctx, .id stx id danglingDot info.lctx none)
where
choose
(hoverLine : Nat)
@@ -679,37 +754,48 @@ where
: Option (HoverInfo × ContextInfo × Info) :=
if !info.isCompletion then
best?
else if info.occursInside? hoverPos |>.isSome then
let headPos := info.pos?.get!
else if info.occursInOrOnBoundary hoverPos then
let headPos := info.pos?.get!
let tailPos := info.tailPos?.get!
let hoverInfo :=
if hoverPos < tailPos then
HoverInfo.inside (hoverPos - headPos).byteIdx
else
HoverInfo.after
let headPosLine, _ := fileMap.toPosition headPos
let tailPosLine, _ := fileMap.toPosition info.tailPos?.get!
if headPosLine != hoverLine || headPosLine != tailPosLine then
best?
else match best? with
| none => (HoverInfo.inside (hoverPos - headPos).byteIdx, ctx, info)
| some (HoverInfo.after, _, _) => (HoverInfo.inside (hoverPos - headPos).byteIdx, ctx, info)
| none => (hoverInfo, ctx, info)
| some (_, _, best) =>
if info.isSmaller best then
(HoverInfo.inside (hoverPos - headPos).byteIdx, ctx, info)
else
best?
else if let some (HoverInfo.inside _, _, _) := best? then
-- We assume the "inside matches" have precedence over "before ones".
best?
else if info.occursDirectlyBefore hoverPos then
let pos := info.tailPos?.get!
let line, _ := fileMap.toPosition pos
if line != hoverLine then best?
else match best? with
| none => (HoverInfo.after, ctx, info)
| some (_, _, best) =>
if info.isSmaller best then
(HoverInfo.after, ctx, info)
if isBetter info best then
(hoverInfo, ctx, info)
else
best?
else
best?
isBetter : Info Info Bool
| i₁@(.ofCompletionInfo ci₁), i₂@(.ofCompletionInfo ci₂) =>
-- Use the smallest info available and prefer non-id completion over id completions as a
-- tie-breaker.
-- This is necessary because the elaborator sometimes generates both for the same range.
-- If two infos are equivalent, always prefer the first one.
if i₁.isSmaller i₂ then
true
else if i₂.isSmaller i₁ then
false
else if !(ci₁ matches .id ..) && ci₂ matches .id .. then
true
else if ci₁ matches .id .. && !(ci₂ matches .id ..) then
false
else
true
| .ofCompletionInfo _, _ => true
| _, .ofCompletionInfo _ => false
| _, _ => true
/--
Assigns the `CompletionItem.sortText?` for all items in `completions` according to their order
in `completions`. This is necessary because clients will use their own sort order if the server
@@ -740,8 +826,8 @@ partial def find?
match info with
| .dot info .. =>
dotCompletion params ctx info hoverInfo
| .id _ id danglingDot lctx .. =>
idCompletion params ctx lctx id hoverInfo danglingDot
| .id stx id danglingDot lctx .. =>
idCompletion params ctx lctx stx id hoverInfo danglingDot
| .dotId _ id lctx expectedType? =>
dotIdCompletion params ctx lctx id expectedType?
| .fieldId _ id lctx structName =>

View File

@@ -141,10 +141,12 @@ def Info.stx : Info → Syntax
| ofOmissionInfo i => i.stx
def Info.lctx : Info LocalContext
| Info.ofTermInfo i => i.lctx
| Info.ofFieldInfo i => i.lctx
| Info.ofOmissionInfo i => i.lctx
| _ => LocalContext.empty
| .ofTermInfo i => i.lctx
| .ofFieldInfo i => i.lctx
| .ofOmissionInfo i => i.lctx
| .ofMacroExpansionInfo i => i.lctx
| .ofCompletionInfo i => i.lctx
| _ => LocalContext.empty
def Info.pos? (i : Info) : Option String.Pos :=
i.stx.getPos? (canonicalOnly := true)
@@ -165,7 +167,7 @@ def Info.size? (i : Info) : Option String.Pos := do
-- `Info` without position information are considered to have "infinite" size
def Info.isSmaller (i₁ i₂ : Info) : Bool :=
match i₁.size?, i₂.pos? with
match i₁.size?, i₂.size? with
| some sz₁, some sz₂ => sz₁ < sz₂
| some _, none => true
| _, _ => false
@@ -181,6 +183,13 @@ def Info.occursInside? (i : Info) (hoverPos : String.Pos) : Option String.Pos :=
guard (headPos hoverPos && hoverPos < tailPos)
return hoverPos - headPos
def Info.occursInOrOnBoundary (i : Info) (hoverPos : String.Pos) : Bool := Id.run do
let some headPos := i.pos?
| return false
let some tailPos := i.tailPos?
| return false
return headPos <= hoverPos && hoverPos <= tailPos
def InfoTree.smallestInfo? (p : Info Bool) (t : InfoTree) : Option (ContextInfo × Info) :=
let ts := t.deepestNodes fun ctx i _ => if p i then some (ctx, i) else none

View File

@@ -48,7 +48,8 @@ instance : ToExpr (Fin n) where
toExpr a :=
let r := mkRawNatLit a.val
mkApp3 (.const ``OfNat.ofNat [0]) (.app (mkConst ``Fin) (toExpr n)) r
(mkApp2 (.const ``Fin.instOfNat []) (mkNatLit (n-1)) r)
(mkApp3 (.const ``Fin.instOfNat []) (toExpr n)
(.app (.const ``Nat.instNeZeroSucc []) (mkNatLit (n-1))) r)
instance : ToExpr (BitVec n) where
toTypeExpr := .app (mkConst ``BitVec) (toExpr n)

View File

@@ -152,6 +152,18 @@ variable {β : Type v}
end
@[inline, inherit_doc Raw.getKey?] def getKey? (m : DHashMap α β) (a : α) : Option α :=
Raw₀.getKey? m.1, m.2.size_buckets_pos a
@[inline, inherit_doc Raw.getKey] def getKey (m : DHashMap α β) (a : α) (h : a m) : α :=
Raw₀.getKey m.1, m.2.size_buckets_pos a h
@[inline, inherit_doc Raw.getKey!] def getKey! [Inhabited α] (m : DHashMap α β) (a : α) : α :=
Raw₀.getKey! m.1, m.2.size_buckets_pos a
@[inline, inherit_doc Raw.getKeyD] def getKeyD (m : DHashMap α β) (a : α) (fallback : α) : α :=
Raw₀.getKeyD m.1, m.2.size_buckets_pos a fallback
@[inline, inherit_doc Raw.size] def size (m : DHashMap α β) : Nat :=
m.1.size
@@ -248,6 +260,10 @@ instance [BEq α] [Hashable α] : ForIn m (DHashMap α β) ((a : α) × β a) wh
DHashMap α (fun _ => Unit) :=
Const.insertManyUnit l
@[inline, inherit_doc Raw.Const.unitOfArray] def Const.unitOfArray [BEq α] [Hashable α] (l : Array α) :
DHashMap α (fun _ => Unit) :=
Const.insertManyUnit l
@[inherit_doc Raw.Internal.numBuckets] def Internal.numBuckets
(m : DHashMap α β) : Nat :=
Raw.Internal.numBuckets m.1

View File

@@ -102,16 +102,31 @@ def getCast [BEq α] [LawfulBEq α] (a : α) : (l : AssocList α β) → l.conta
| 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 getKey [BEq α] (a : α) : (l : AssocList α β) l.contains a α
| cons k _ es, h => if hka : k == a then k
else es.getKey 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 : k == a then cast (congrArg β (eq_of_beq h)) v else es.getCast! a
/-- Internal implementation detail of the hash map -/
def getKey? [BEq α] (a : α) : AssocList α β Option α
| nil => none
| cons k _ es => if k == a then some k else es.getKey? 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 k == a then v else es.get! a
/-- Internal implementation detail of the hash map -/
def getKey! [BEq α] [Inhabited α] (a : α) : AssocList α β α
| nil => panic! "key is not present in hash table"
| cons k _ es => if k == a then k else es.getKey! a
/-- Internal implementation detail of the hash map -/
def getCastD [BEq α] [LawfulBEq α] (a : α) (fallback : β a) : AssocList α β β a
| nil => fallback
@@ -123,6 +138,11 @@ def getD {β : Type v} [BEq α] (a : α) (fallback : β) : AssocList α (fun _ =
| nil => fallback
| cons k v es => bif k == a then v else es.getD a fallback
/-- Internal implementation detail of the hash map -/
def getKeyD [BEq α] (a : α) (fallback : α) : AssocList α β α
| nil => fallback
| cons k _ es => if k == a then k else es.getKeyD a fallback
/-- Internal implementation detail of the hash map -/
def replace [BEq α] (a : α) (b : β a) : AssocList α β AssocList α β
| nil => nil

View File

@@ -112,6 +112,32 @@ theorem get!_eq {β : Type v} [BEq α] [Inhabited β] {l : AssocList α (fun _ =
· simp_all [get!, List.getValue!, List.getValue!, List.getValue?_cons,
Bool.apply_cond Option.get!]
@[simp]
theorem getKey?_eq [BEq α] {l : AssocList α β} {a : α} :
l.getKey? a = List.getKey? a l.toList := by
induction l <;> simp_all [getKey?]
@[simp]
theorem getKey_eq [BEq α] {l : AssocList α β} {a : α} {h} :
l.getKey a h = List.getKey a l.toList (contains_eq.symm.trans h) := by
induction l
· simp [contains] at h
· next k v t ih => simp only [getKey, toList_cons, List.getKey_cons, ih]
@[simp]
theorem getKeyD_eq [BEq α] {l : AssocList α β} {a fallback : α} :
l.getKeyD a fallback = List.getKeyD a l.toList fallback := by
induction l
· simp [getKeyD, List.getKeyD]
· simp_all [getKeyD, List.getKeyD, Bool.apply_cond (fun x => Option.getD x fallback)]
@[simp]
theorem getKey!_eq [BEq α] [Inhabited α] {l : AssocList α β} {a : α} :
l.getKey! a = List.getKey! a l.toList := by
induction l
· simp [getKey!, List.getKey!]
· simp_all [getKey!, List.getKey!, Bool.apply_cond Option.get!]
@[simp]
theorem toList_replace [BEq α] {l : AssocList α β} {a : α} {b : β a} :
(l.replace a b).toList = replaceEntry a b l.toList := by

View File

@@ -100,9 +100,9 @@ Here is a summary of the steps required to add and verify a new operation:
* Connect the implementation on lists and associative lists in `Internal.AssocList.Lemmas` via a
lemma `AssocList.operation_eq`.
3. Write the model implementation
* Write the model implementation `Raw₀.operationₘ` in `Internal.List.Model`
* Write the model implementation `Raw₀.operationₘ` in `Internal.Model`
* Prove that the model implementation is equal to the actual implementation in
`Internal.List.Model` via a lemma `operation_eq_operationₘ`.
`Internal.Model` via a lemma `operation_eq_operationₘ`.
4. Verify the model implementation
* In `Internal.WF`, prove `operationₘ_eq_List.operation` (for access operations) or
`wfImp_operationₘ` and `toListModel_operationₘ`
@@ -121,18 +121,18 @@ Here is a summary of the steps required to add and verify a new operation:
might also have to prove that your list operation is invariant under permutation and add that to
the tactic.
7. State and prove the user-facing lemmas
* Restate all of your lemmas for `DHashMap.Raw` in `DHashMap.Lemmas` and prove them using the
* Restate all of your lemmas for `DHashMap.Raw` in `DHashMap.RawLemmas` and prove them using the
provided tactic after hooking in your `operation_eq` and `operation_val` from step 5.
* Restate all of your lemmas for `DHashMap` in `DHashMap.Lemmas` and prove them by reducing to
`Raw₀`.
* Restate all of your lemmas for `HashMap.Raw` in `HashMap.Lemmas` and prove them by reducing to
* Restate all of your lemmas for `HashMap.Raw` in `HashMap.RawLemmas` and prove them by reducing to
`DHashMap.Raw`.
* Restate all of your lemmas for `HashMap` in `HashMap.Lemmas` and prove them by reducing to
`DHashMap`.
* Restate all of your lemmas for `HashSet.Raw` in `HashSet.Lemmas` and prove them by reducing to
`DHashSet.Raw`.
* Restate all of your lemmas for `HashSet.Raw` in `HashSet.RawLemmas` and prove them by reducing to
`HashMap.Raw`.
* Restate all of your lemmas for `HashSet` in `HashSet.Lemmas` and prove them by reducing to
`DHashSet`.
`HashMap`.
This sounds like a lot of work (and it is if you have to add a lot of user-facing lemmas), but the
framework is set up in such a way that each step is really easy and the proofs are all really short
@@ -420,6 +420,30 @@ variable {β : Type v}
end
/-- Internal implementation detail of the hash map -/
@[inline] def getKey? [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Option α :=
let _, buckets, h := m
let i, h := mkIdx buckets.size h (hash a)
buckets[i].getKey? a
/-- Internal implementation detail of the hash map -/
@[inline] def getKey [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (hma : m.contains a) : α :=
let _, buckets, h := m
let idx := mkIdx buckets.size h (hash a)
buckets[idx.1].getKey a hma
/-- Internal implementation detail of the hash map -/
@[inline] def getKeyD [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (fallback : α) : α :=
let _, buckets, h := m
let idx := mkIdx buckets.size h (hash a)
buckets[idx.1].getKeyD a fallback
/-- Internal implementation detail of the hash map -/
@[inline] def getKey! [BEq α] [Hashable α] [Inhabited α] (m : Raw₀ α β) (a : α) : α :=
let _, buckets, h := m
let idx := mkIdx buckets.size h (hash a)
buckets[idx.1].getKey! a
end Raw₀
/-- Internal implementation detail of the hash map -/

View File

@@ -526,6 +526,111 @@ theorem getValue!_eq_getValueD_default [BEq α] [Inhabited β] {l : List ((_ :
end
/-- Internal implementation detail of the hash map -/
def getKey? [BEq α] (a : α) : List ((a : α) × β a) Option α
| [] => none
| k, _ :: l => bif k == a then some k else getKey? a l
@[simp] theorem getKey?_nil [BEq α] {a : α} :
getKey? a ([] : List ((a : α) × β a)) = none := rfl
@[simp] theorem getKey?_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
getKey? a (k, v :: l) = bif k == a then some k else getKey? a l := rfl
theorem getKey?_cons_of_true [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : k == a) :
getKey? a (k, v :: l) = some k := by
simp [h]
theorem getKey?_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
(h : (k == a) = false) : getKey? a (k, v :: l) = getKey? a l := by
simp [h]
theorem getKey?_eq_getEntry? [BEq α] {l : List ((a : α) × β a)} {a : α} :
getKey? a l = (getEntry? a l).map (·.1) := by
induction l using assoc_induction
· simp
· next k v l ih =>
cases h : k == a
· rw [getEntry?_cons_of_false h, getKey?_cons_of_false h, ih]
· rw [getEntry?_cons_of_true h, getKey?_cons_of_true h, Option.map_some']
theorem containsKey_eq_isSome_getKey? [BEq α] {l : List ((a : α) × β a)} {a : α} :
containsKey a l = (getKey? a l).isSome := by
simp [containsKey_eq_isSome_getEntry?, getKey?_eq_getEntry?]
/-- Internal implementation detail of the hash map -/
def getKey [BEq α] (a : α) (l : List ((a : α) × β a)) (h : containsKey a l) : α :=
(getKey? a l).get <| containsKey_eq_isSome_getKey?.symm.trans h
theorem getKey?_eq_some_getKey [BEq α] {l : List ((a : α) × β a)} {a : α} (h : containsKey a l) :
getKey? a l = some (getKey a l h) := by
simp [getKey]
theorem getKey_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} {h} :
getKey a (k, v :: l) h = if h' : k == a then k
else getKey a l (containsKey_of_containsKey_cons (k := k) h (Bool.eq_false_iff.2 h')) := by
rw [ Option.some_inj, getKey?_eq_some_getKey, getKey?_cons, apply_dite Option.some,
cond_eq_if]
split
· rfl
· exact getKey?_eq_some_getKey _
/-- Internal implementation detail of the hash map -/
def getKeyD [BEq α] (a : α) (l : List ((a : α) × β a)) (fallback : α) : α :=
(getKey? a l).getD fallback
@[simp]
theorem getKeyD_nil [BEq α] {a fallback : α} :
getKeyD a ([] : List ((a : α) × β a)) fallback = fallback := rfl
theorem getKeyD_eq_getKey? [BEq α] {l : List ((a : α) × β a)} {a fallback : α} :
getKeyD a l fallback = (getKey? a l).getD fallback := rfl
theorem getKeyD_eq_fallback [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {a fallback : α}
(h : containsKey a l = false) : getKeyD a l fallback = fallback := by
rw [containsKey_eq_isSome_getKey?, Bool.eq_false_iff, ne_eq,
Option.not_isSome_iff_eq_none] at h
rw [getKeyD_eq_getKey?, h, Option.getD_none]
theorem getKey_eq_getKeyD [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {a fallback : α}
(h : containsKey a l = true) :
getKey a l h = getKeyD a l fallback := by
rw [getKeyD_eq_getKey?, getKey, Option.get_eq_getD]
theorem getKey?_eq_some_getKeyD [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {a fallback : α}
(h : containsKey a l = true) :
getKey? a l = some (getKeyD a l fallback) := by
rw [getKey?_eq_some_getKey h, getKey_eq_getKeyD]
/-- Internal implementation detail of the hash map -/
def getKey! [BEq α] [Inhabited α] (a : α) (l : List ((a : α) × β a)) : α :=
(getKey? a l).get!
@[simp]
theorem getKey!_nil [BEq α] [Inhabited α] {a : α} :
getKey! a ([] : List ((a : α) × β a)) = default := rfl
theorem getKey!_eq_getKey? [BEq α] [Inhabited α] {l : List ((a : α) × β a)} {a : α} :
getKey! a l = (getKey? a l).get! := rfl
theorem getKey!_eq_default [BEq α] [Inhabited α] {l : List ((a : α) × β a)} {a : α}
(h : containsKey a l = false) : getKey! a l = default := by
rw [containsKey_eq_isSome_getKey?, Bool.eq_false_iff, ne_eq,
Option.not_isSome_iff_eq_none] at h
rw [getKey!_eq_getKey?, h, Option.get!_none]
theorem getKey_eq_getKey! [BEq α] [Inhabited α] {l : List ((a : α) × β a)} {a : α}
(h : containsKey a l = true) : getKey a l h = getKey! a l := by
rw [getKey!_eq_getKey?, getKey, Option.get_eq_get!]
theorem getKey?_eq_some_getKey! [BEq α] [Inhabited α] {l : List ((a : α) × β a)} {a : α}
(h : containsKey a l = true) :
getKey? a l = some (getKey! a l) := by
rw [getKey?_eq_some_getKey h, getKey_eq_getKey!]
theorem getKey!_eq_getKeyD_default [BEq α] [EquivBEq α] [Inhabited α] {l : List ((a : α) × β a)}
{a : α} : getKey! a l = getKeyD a l default := rfl
/-- Internal implementation detail of the hash map -/
def replaceEntry [BEq α] (k : α) (v : β k) : List ((a : α) × β a) List ((a : α) × β a)
| [] => []
@@ -643,6 +748,18 @@ theorem getValueCast?_replaceEntry [BEq α] [LawfulBEq α] {l : List ((a : α)
· rw [Option.dmap_congr (getEntry?_replaceEntry_of_false (Bool.eq_false_iff.2 h)),
getValueCast?_eq_getEntry?]
theorem getKey?_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
{v : β k} : getKey? a (replaceEntry k v l) =
if containsKey k l k == a then some k else getKey? a l := by
rw [getKey?_eq_getEntry?]
split
· next h => simp [getEntry?_replaceEntry_of_true h.1 h.2]
· next h =>
simp only [Decidable.not_and_iff_or_not_not] at h
rcases h with h|h
· rw [getEntry?_replaceEntry_of_containsKey_eq_false (Bool.eq_false_iff.2 h), getKey?_eq_getEntry?]
· rw [getEntry?_replaceEntry_of_false (Bool.eq_false_iff.2 h), getKey?_eq_getEntry?]
@[simp]
theorem containsKey_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
{v : β k} : containsKey a (replaceEntry k v l) = containsKey a l := by
@@ -974,6 +1091,39 @@ theorem getValueD_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : Lis
{k : α} {fallback v : β} : getValueD k (insertEntry k v l) fallback = v := by
simp [getValueD_insertEntry, BEq.refl]
theorem getKey?_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} : getKey? a (insertEntry k v l) = if k == a then some k else getKey? a l := by
cases hl : containsKey k l
· simp [insertEntry_of_containsKey_eq_false hl]
· rw [insertEntry_of_containsKey hl, getKey?_replaceEntry, hl]
split <;> simp_all
theorem getKey?_insertEntry_self [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {k : α}
{v : β k} : getKey? k (insertEntry k v l) = some k := by
simp [getKey?_insertEntry]
theorem getKey?_eq_none [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a : α}
(h : containsKey a l = false) : getKey? a l = none := by
rwa [ Option.not_isSome_iff_eq_none, containsKey_eq_isSome_getKey?, Bool.not_eq_true]
theorem getKey!_insertEntry [BEq α] [EquivBEq α] [Inhabited α] {l : List ((a : α) × β a)}
{k a : α} {v : β k} : getKey! a (insertEntry k v l) =
if k == a then k else getKey! a l := by
simp [getKey!_eq_getKey?, getKey?_insertEntry, apply_ite Option.get!]
theorem getKey!_insertEntry_self [BEq α] [EquivBEq α] [Inhabited α] {l : List ((a : α) × β a)}
{k : α} {v : β k} : getKey! k (insertEntry k v l) = k := by
rw [getKey!_insertEntry, if_pos BEq.refl]
theorem getKeyD_insertEntry [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {k a fallback : α}
{v : β k} : getKeyD a (insertEntry k v l) fallback =
if k == a then k else getKeyD a l fallback := by
simp [getKeyD_eq_getKey?, getKey?_insertEntry, apply_ite (fun x => Option.getD x fallback)]
theorem getKeyD_insertEntry_self [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {k fallback : α}
{v : β k} : getKeyD k (insertEntry k v l) fallback = k := by
rw [getKeyD_insertEntry, if_pos BEq.refl]
@[local simp]
theorem containsKey_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} : containsKey a (insertEntry k v l) = ((k == a) || containsKey a l) := by
@@ -1017,6 +1167,17 @@ theorem getValue_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : List
{v : β} : getValue k (insertEntry k v l) containsKey_insertEntry_self = v := by
simp [getValue_insertEntry]
theorem getKey_insertEntry [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} {h} : getKey a (insertEntry k v l) h =
if h' : k == a then k
else getKey a l (containsKey_of_containsKey_insertEntry h (Bool.eq_false_iff.2 h')) := by
rw [ Option.some_inj, getKey?_eq_some_getKey, apply_dite Option.some, getKey?_insertEntry]
simp only [ getKey?_eq_some_getKey, dite_eq_ite]
theorem getKey_insertEntry_self [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {k : α}
{v : β k} : getKey k (insertEntry k v l) containsKey_insertEntry_self = k := by
simp [getKey_insertEntry]
/-- Internal implementation detail of the hash map -/
def insertEntryIfNew [BEq α] (k : α) (v : β k) (l : List ((a : α) × β a)) : List ((a : α) × β a) :=
bif containsKey k l then l else k, v :: l
@@ -1135,6 +1296,32 @@ theorem getValueD_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {
simp [getValueD_eq_getValue?, getValue?_insertEntryIfNew,
apply_ite (fun x => Option.getD x fallback)]
theorem getKey?_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} : getKey? a (insertEntryIfNew k v l) =
if k == a containsKey k l = false then some k else getKey? a l := by
cases h : containsKey k l
· rw [insertEntryIfNew_of_containsKey_eq_false h]
split <;> simp_all
· simp [insertEntryIfNew_of_containsKey h]
theorem getKey_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} {v : β k} {h} : getKey a (insertEntryIfNew k v l) h =
if h' : k == a containsKey k l = false then k
else getKey a l (containsKey_of_containsKey_insertEntryIfNew' h h') := by
rw [ Option.some_inj, getKey?_eq_some_getKey, apply_dite Option.some,
getKey?_insertEntryIfNew, dite_eq_ite]
simp [ getKey?_eq_some_getKey]
theorem getKey!_insertEntryIfNew [BEq α] [PartialEquivBEq α] [Inhabited α]
{l : List ((a : α) × β a)} {k a : α} {v : β k} : getKey! a (insertEntryIfNew k v l) =
if k == a containsKey k l = false then k else getKey! a l := by
simp [getKey!_eq_getKey?, getKey?_insertEntryIfNew, apply_ite Option.get!]
theorem getKeyD_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a fallback : α} {v : β k} : getKeyD a (insertEntryIfNew k v l) fallback =
if k == a containsKey k l = false then k else getKeyD a l fallback := by
simp [getKeyD_eq_getKey?, getKey?_insertEntryIfNew, apply_ite (fun x => Option.getD x fallback)]
theorem length_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
(insertEntryIfNew k v l).length = if containsKey k l then l.length else l.length + 1 := by
simp [insertEntryIfNew, Bool.apply_cond List.length]
@@ -1253,6 +1440,36 @@ theorem getValue?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((_ : α) ×
end
theorem getKey?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
(hl : DistinctKeys l) :
getKey? a (eraseKey k l) = if k == a then none else getKey? a l := by
rw [getKey?_eq_getEntry?, getEntry?_eraseKey hl]
by_cases h : k == a
. simp [h]
. simp [h, getKey?_eq_getEntry?]
theorem getKey?_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
(hl : DistinctKeys l) : getKey? k (eraseKey k l) = none := by
simp [getKey?_eq_getEntry?, getEntry?_eraseKey_self hl]
theorem getKey!_eraseKey [BEq α] [PartialEquivBEq α] [Inhabited α] {l : List ((a : α) × β a)}
{k a : α} (hl : DistinctKeys l) :
getKey! a (eraseKey k l) = if k == a then default else getKey! a l := by
simp [getKey!_eq_getKey?, getKey?_eraseKey hl, apply_ite Option.get!]
theorem getKey!_eraseKey_self [BEq α] [PartialEquivBEq α] [Inhabited α] {l : List ((a : α) × β a)}
{k : α} (hl : DistinctKeys l) : getKey! k (eraseKey k l) = default := by
simp [getKey!_eq_getKey?, getKey?_eraseKey_self hl]
theorem getKeyD_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a fallback : α}
(hl : DistinctKeys l) :
getKeyD a (eraseKey k l) fallback = if k == a then fallback else getKeyD a l fallback := by
simp [getKeyD_eq_getKey?, getKey?_eraseKey hl, apply_ite (fun x => Option.getD x fallback)]
theorem getKeyD_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k fallback : α} (hl : DistinctKeys l) : getKeyD k (eraseKey k l) fallback = fallback := by
simp [getKeyD_eq_getKey?, getKey?_eraseKey_self hl]
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]
@@ -1340,6 +1557,13 @@ theorem getValue_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List
rw [ Option.some_inj, getValue?_eq_some_getValue, getValue?_eraseKey hl, h.1]
simp [ getValue?_eq_some_getValue]
theorem getKey_eraseKey [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {k a : α} {h}
(hl : DistinctKeys l) : getKey a (eraseKey k l) h =
getKey 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, getKey?_eq_some_getKey, getKey?_eraseKey hl, h.1]
simp [ getKey?_eq_some_getKey]
theorem getEntry?_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {a : α}
(hl : DistinctKeys l) (h : Perm l l') : getEntry? a l = getEntry? a l' := by
induction h
@@ -1412,6 +1636,26 @@ theorem getValueD_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((_ : α)
end
theorem getKey?_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {a : α}
(hl : DistinctKeys l) (h : Perm l l') : getKey? a l = getKey? a l' := by
rw [getKey?_eq_getEntry?, getKey?_eq_getEntry?, getEntry?_of_perm hl h]
theorem getKey_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {a : α} {h'}
(hl : DistinctKeys l) (h : Perm l l') :
getKey a l h' = getKey a l' ((containsKey_of_perm h).symm.trans h') := by
rw [ Option.some_inj, getKey?_eq_some_getKey, getKey?_eq_some_getKey,
getKey?_of_perm hl h]
theorem getKey!_of_perm [BEq α] [PartialEquivBEq α] [Inhabited α] {l l' : List ((a : α) × β a)}
{a : α} (hl : DistinctKeys l) (h : Perm l l') :
getKey! a l = getKey! a l' := by
simp only [getKey!_eq_getKey?, getKey?_of_perm hl h]
theorem getKeyD_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {a fallback : α}
(hl : DistinctKeys l) (h : Perm l l') :
getKeyD a l fallback = getKeyD a l' fallback := by
simp only [getKeyD_eq_getKey?, getKey?_of_perm hl h]
theorem perm_cons_getEntry [BEq α] {l : List ((a : α) × β a)} {a : α} (h : containsKey a l) :
l', Perm l (getEntry a l h :: l') := by
induction l using assoc_induction
@@ -1521,6 +1765,18 @@ theorem getValueCast_append_of_containsKey_eq_false [BEq α] [LawfulBEq α]
rw [ Option.some_inj, getValueCast?_eq_some_getValueCast, getValueCast?_eq_some_getValueCast,
getValueCast?_append_of_containsKey_eq_false hl']
theorem getKey?_append_of_containsKey_eq_false [BEq α] [PartialEquivBEq α]
{l l' : List ((a : α) × β a)} {a : α} (hl' : containsKey a l' = false) :
getKey? a (l ++ l') = getKey? a l := by
simp [getKey?_eq_getEntry?, getEntry?_eq_none.2 hl']
theorem getKey_append_of_containsKey_eq_false [BEq α] [PartialEquivBEq α]
{l l' : List ((a : α) × β a)} {a : α} {h} (hl' : containsKey a l' = false) :
getKey a (l ++ l') h =
getKey a l ((containsKey_append_of_not_contains_right hl').symm.trans h) := by
rw [ Option.some_inj, getKey?_eq_some_getKey, getKey?_eq_some_getKey,
getKey?_append_of_containsKey_eq_false hl']
theorem replaceEntry_append_of_containsKey_left [BEq α] {l l' : List ((a : α) × β a)} {k : α}
{v : β k} (h : containsKey k l) : replaceEntry k v (l ++ l') = replaceEntry k v l ++ l' := by
induction l using assoc_induction

View File

@@ -98,14 +98,14 @@ theorem exists_bucket_of_uset [BEq α] [Hashable α]
refine ?_, ?_
· apply List.containsKey_bind_eq_false
intro j hj
rw [ List.getElem_append (l₂ := self[i] :: l₂), getElem_congr_coll h₁.symm]
rw [List.getElem_append_left' (l₂ := self[i] :: l₂), getElem_congr_coll h₁.symm]
apply (h.hashes_to j _).containsKey_eq_false h₀ k
omega
· apply List.containsKey_bind_eq_false
intro j hj
rw [ List.getElem_cons_succ self[i] _ _
(by simp only [Array.ugetElem_eq_getElem, List.length_cons]; omega)]
rw [List.getElem_append_right'' l₁, getElem_congr_coll h₁.symm]
rw [List.getElem_append_right' l₁, getElem_congr_coll h₁.symm]
apply (h.hashes_to (j + 1 + l₁.length) _).containsKey_eq_false h₀ k
omega
@@ -262,6 +262,10 @@ def consₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) : Raw
def get?ₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Option (β a) :=
(bucket m.1.buckets m.2 a).getCast? a
/-- Internal implementation detail of the hash map -/
def getKey?ₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Option α :=
(bucket m.1.buckets m.2 a).getKey? a
/-- Internal implementation detail of the hash map -/
def containsₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Bool :=
(bucket m.1.buckets m.2 a).contains a
@@ -278,6 +282,18 @@ def getDₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) (f
def get!ₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) [Inhabited (β a)] : β a :=
(m.get?ₘ a).get!
/-- Internal implementation detail of the hash map -/
def getKeyₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (h : m.containsₘ a) : α :=
(bucket m.1.buckets m.2 a).getKey a h
/-- Internal implementation detail of the hash map -/
def getKeyDₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (fallback : α) : α :=
(m.getKey?ₘ a).getD fallback
/-- Internal implementation detail of the hash map -/
def getKey!ₘ [BEq α] [Hashable α] [Inhabited α] (m : Raw₀ α β) (a : α) : α :=
(m.getKey?ₘ a).get!
/-- Internal implementation detail of the hash map -/
def insertₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) : Raw₀ α β :=
if m.containsₘ a then m.replaceₘ a b else Raw₀.expandIfNecessary (m.consₘ a b)
@@ -348,6 +364,20 @@ theorem get!_eq_get!ₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β)
get! m a = get!ₘ m a := by
simp [get!, get!ₘ, get?ₘ, List.getValueCast!_eq_getValueCast?, bucket]
theorem getKey?_eq_getKey?ₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
getKey? m a = getKey?ₘ m a := rfl
theorem getKey_eq_getKeyₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (h : m.contains a) :
getKey m a h = getKeyₘ m a h := rfl
theorem getKeyD_eq_getKeyDₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a fallback : α) :
getKeyD m a fallback = getKeyDₘ m a fallback := by
simp [getKeyD, getKeyDₘ, getKey?ₘ, List.getKeyD_eq_getKey?, bucket]
theorem getKey!_eq_getKey!ₘ [BEq α] [Hashable α] [Inhabited α] (m : Raw₀ α β) (a : α) :
getKey! m a = getKey!ₘ m a := by
simp [getKey!, getKey!ₘ, getKey?ₘ, List.getKey!_eq_getKey?, bucket]
theorem contains_eq_containsₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
m.contains a = m.containsₘ a := rfl

View File

@@ -135,6 +135,37 @@ theorem get!_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a :
m.val.get! a = m.get! a := by
simp [Raw.get!, m.2]
theorem getKey?_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
m.getKey? a = Raw₀.getKey? m, h.size_buckets_pos a := by
simp [Raw.getKey?, h.size_buckets_pos]
theorem getKey?_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
m.val.getKey? a = m.getKey? a := by
simp [Raw.getKey?, m.2]
theorem getKey_eq [BEq α] [Hashable α] {m : Raw α β} {a : α} {h : a m} :
m.getKey a h = Raw₀.getKey m, by change dite .. = true at h; split at h <;> simp_all a
(by change dite .. = true at h; split at h <;> simp_all) := rfl
theorem getKey_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} {h : a m.val} :
m.val.getKey a h = m.getKey a (contains_val (m := m) h) := rfl
theorem getKeyD_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a fallback : α} :
m.getKeyD a fallback = Raw₀.getKeyD m, h.size_buckets_pos a fallback := by
simp [Raw.getKeyD, h.size_buckets_pos]
theorem getKeyD_val [BEq α] [Hashable α] {m : Raw₀ α β} {a fallback : α} :
m.val.getKeyD a fallback = m.getKeyD a fallback := by
simp [Raw.getKeyD, m.2]
theorem getKey!_eq [BEq α] [Hashable α] [Inhabited α] {m : Raw α β} (h : m.WF) {a : α} :
m.getKey! a = Raw₀.getKey! m, h.size_buckets_pos a := by
simp [Raw.getKey!, h.size_buckets_pos]
theorem getKey!_val [BEq α] [Hashable α] [Inhabited α] {m : Raw₀ α β} {a : α} :
m.val.getKey! a = m.getKey! a := by
simp [Raw.getKey!, m.2]
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]

View File

@@ -83,7 +83,8 @@ private def queryNames : Array Name :=
#[``contains_eq_containsKey, ``Raw.isEmpty_eq_isEmpty, ``Raw.size_eq_length,
``get?_eq_getValueCast?, ``Const.get?_eq_getValue?, ``get_eq_getValueCast,
``Const.get_eq_getValue, ``get!_eq_getValueCast!, ``getD_eq_getValueCastD,
``Const.get!_eq_getValue!, ``Const.getD_eq_getValueD]
``Const.get!_eq_getValue!, ``Const.getD_eq_getValueD, ``getKey?_eq_getKey?,
``getKey_eq_getKey, ``getKeyD_eq_getKeyD, ``getKey!_eq_getKey!]
private def modifyNames : Array Name :=
#[``toListModel_insert, ``toListModel_erase, ``toListModel_insertIfNew]
@@ -93,7 +94,8 @@ private def congrNames : MacroM (Array (TSyntax `term)) := do
`(_root_.List.Perm.length_eq), `(getValueCast?_of_perm _),
`(getValue?_of_perm _), `(getValue_of_perm _), `(getValueCast_of_perm _),
`(getValueCast!_of_perm _), `(getValueCastD_of_perm _), `(getValue!_of_perm _),
`(getValueD_of_perm _) ]
`(getValueD_of_perm _), `(getKey?_of_perm _), `(getKey_of_perm _), `(getKeyD_of_perm _),
`(getKey!_of_perm _)]
/-- Internal implementation detail of the hash map -/
scoped syntax "simp_to_model" ("using" term)? : tactic
@@ -535,6 +537,147 @@ theorem getD_congr [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a b : α} {fa
end Const
@[simp]
theorem getKey?_empty {a : α} {c} : (empty c : Raw₀ α β).getKey? a = none := by
simp [getKey?]
theorem getKey?_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a : α} :
m.1.isEmpty = true m.getKey? a = none := by
simp_to_model; empty
theorem getKey?_insert [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a k : α} {v : β k} :
(m.insert k v).getKey? a = if k == a then some k else m.getKey? a := by
simp_to_model using List.getKey?_insertEntry
theorem getKey?_insert_self [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k : α} {v : β k} :
(m.insert k v).getKey? k = some k := by
simp_to_model using List.getKey?_insertEntry_self
theorem contains_eq_isSome_getKey? [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a : α} :
m.contains a = (m.getKey? a).isSome := by
simp_to_model using List.containsKey_eq_isSome_getKey?
theorem getKey?_eq_none [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a : α} :
m.contains a = false m.getKey? a = none := by
simp_to_model using List.getKey?_eq_none
theorem getKey?_erase [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a : α} :
(m.erase k).getKey? a = if k == a then none else m.getKey? a := by
simp_to_model using List.getKey?_eraseKey
theorem getKey?_erase_self [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k : α} :
(m.erase k).getKey? k = none := by
simp_to_model using List.getKey?_eraseKey_self
theorem getKey_insert [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a : α} {v : β k} {h₁} :
(m.insert k v).getKey a h₁ =
if h₂ : k == a then
k
else
m.getKey a (contains_of_contains_insert _ h h₁ (Bool.eq_false_iff.2 h₂)) := by
simp_to_model using List.getKey_insertEntry
theorem getKey_insert_self [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k : α} {v : β k} :
(m.insert k v).getKey k (contains_insert_self _ h) = k := by
simp_to_model using List.getKey_insertEntry_self
@[simp]
theorem getKey_erase [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a : α} {h'} :
(m.erase k).getKey a h' = m.getKey a (contains_of_contains_erase _ h h') := by
simp_to_model using List.getKey_eraseKey
theorem getKey?_eq_some_getKey [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a : α} {h} :
m.getKey? a = some (m.getKey a h) := by
simp_to_model using List.getKey?_eq_some_getKey
theorem getKey!_empty {a : α} [Inhabited α] {c} :
(empty c : Raw₀ α β).getKey! a = default := by
simp [getKey!, empty]
theorem getKey!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {a : α} :
m.1.isEmpty = true m.getKey! a = default := by
simp_to_model; empty;
theorem getKey!_insert [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {k a : α}
{v : β k} :
(m.insert k v).getKey! a = if k == a then k else m.getKey! a := by
simp_to_model using List.getKey!_insertEntry
theorem getKey!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {a : α}
{b : β a} : (m.insert a b).getKey! a = a := by
simp_to_model using List.getKey!_insertEntry_self
theorem getKey!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {a : α} :
m.contains a = false m.getKey! a = default := by
simp_to_model using List.getKey!_eq_default
theorem getKey!_erase [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {k a : α} :
(m.erase k).getKey! a = if k == a then default else m.getKey! a := by
simp_to_model using List.getKey!_eraseKey
theorem getKey!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {k : α} :
(m.erase k).getKey! k = default := by
simp_to_model using List.getKey!_eraseKey_self
theorem getKey?_eq_some_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {a : α} :
m.contains a = true m.getKey? a = some (m.getKey! a) := by
simp_to_model using List.getKey?_eq_some_getKey!
theorem getKey!_eq_get!_getKey? [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {a : α} :
m.getKey! a = (m.getKey? a).get! := by
simp_to_model using List.getKey!_eq_getKey?
theorem getKey_eq_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {a : α} {h} :
m.getKey a h = m.getKey! a := by
simp_to_model using List.getKey_eq_getKey!
theorem getKeyD_empty {a : α} {fallback : α} {c} :
(empty c : Raw₀ α β).getKeyD a fallback = fallback := by
simp [getKeyD, empty]
theorem getKeyD_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a fallback : α} :
m.1.isEmpty = true m.getKeyD a fallback = fallback := by
simp_to_model; empty
theorem getKeyD_insert [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a fallback : α} {v : β k} :
(m.insert k v).getKeyD a fallback =
if k == a then k else m.getKeyD a fallback := by
simp_to_model using List.getKeyD_insertEntry
theorem getKeyD_insert_self [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a fallback : α}
{b : β a} :
(m.insert a b).getKeyD a fallback = a := by
simp_to_model using List.getKeyD_insertEntry_self
theorem getKeyD_eq_fallback [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a fallback : α} :
m.contains a = false m.getKeyD a fallback = fallback := by
simp_to_model using List.getKeyD_eq_fallback
theorem getKeyD_erase [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a fallback : α} :
(m.erase k).getKeyD a fallback = if k == a then fallback else m.getKeyD a fallback := by
simp_to_model using List.getKeyD_eraseKey
theorem getKeyD_erase_self [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k fallback : α} :
(m.erase k).getKeyD k fallback = fallback := by
simp_to_model using List.getKeyD_eraseKey_self
theorem getKey?_eq_some_getKeyD [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a fallback : α} :
m.contains a = true m.getKey? a = some (m.getKeyD a fallback) := by
simp_to_model using List.getKey?_eq_some_getKeyD
theorem getKeyD_eq_getD_getKey? [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a fallback : α} :
m.getKeyD a fallback = (m.getKey? a).getD fallback := by
simp_to_model using List.getKeyD_eq_getKey?
theorem getKey_eq_getKeyD [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {a fallback : α} {h} :
m.getKey a h = m.getKeyD a fallback := by
simp_to_model using List.getKey_eq_getKeyD
theorem getKey!_eq_getKeyD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF)
{a : α} :
m.getKey! a = m.getKeyD a default := by
simp_to_model using List.getKey!_eq_getKeyD_default
theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k : α} {v : β k} :
(m.insertIfNew k v).1.isEmpty = false := by
simp_to_model using List.isEmpty_insertEntryIfNew
@@ -620,6 +763,29 @@ theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a :
end Const
theorem getKey?_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a : α} {v : β k} :
(m.insertIfNew k v).getKey? a =
if k == a m.contains k = false then some k else m.getKey? a := by
simp_to_model using List.getKey?_insertEntryIfNew
theorem getKey_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a : α} {v : β k} {h₁} :
(m.insertIfNew k v).getKey a h₁ =
if h₂ : k == a m.contains k = false then k
else m.getKey a (contains_of_contains_insertIfNew' _ h h₁ h₂) := by
simp_to_model using List.getKey_insertEntryIfNew
theorem getKey!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.1.WF) {k a : α}
{v : β k} :
(m.insertIfNew k v).getKey! a =
if k == a m.contains k = false then k else m.getKey! a := by
simp_to_model using List.getKey!_insertEntryIfNew
theorem getKeyD_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {k a fallback : α}
{v : β k} :
(m.insertIfNew k v).getKeyD a fallback =
if k == a m.contains k = false then k else m.getKeyD a fallback := by
simp_to_model using List.getKeyD_insertEntryIfNew
@[simp]
theorem getThenInsertIfNew?_fst [LawfulBEq α] {k : α} {v : β k} :
(m.getThenInsertIfNew? k v).1 = m.get? k := by

View File

@@ -233,6 +233,47 @@ theorem getD_eq_getValueCastD [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀
m.getD a fallback = getValueCastD a (toListModel m.1.buckets) fallback := by
rw [getD_eq_getDₘ, getDₘ_eq_getValueCastD hm]
theorem getKey?ₘ_eq_getKey? [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
(hm : Raw.WFImp m.1) {a : α} :
m.getKey?ₘ a = List.getKey? a (toListModel m.1.buckets) :=
apply_bucket hm AssocList.getKey?_eq List.getKey?_of_perm List.getKey?_append_of_containsKey_eq_false
theorem getKey?_eq_getKey? [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
(hm : Raw.WFImp m.1) {a : α} :
m.getKey? a = List.getKey? a (toListModel m.1.buckets) := by
rw [getKey?_eq_getKey?ₘ, getKey?ₘ_eq_getKey? hm]
theorem getKeyₘ_eq_getKey [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
(hm : Raw.WFImp m.1) {a : α} {h : m.contains a} :
m.getKeyₘ a h = List.getKey a (toListModel m.1.buckets) (contains_eq_containsKey hm h) :=
apply_bucket_with_proof hm a AssocList.getKey List.getKey AssocList.getKey_eq
List.getKey_of_perm List.getKey_append_of_containsKey_eq_false
theorem getKey_eq_getKey [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
(hm : Raw.WFImp m.1) {a : α} {h : m.contains a} :
m.getKey a h = List.getKey a (toListModel m.1.buckets) (contains_eq_containsKey hm h) := by
rw [getKey_eq_getKeyₘ, getKeyₘ_eq_getKey hm]
theorem getKey!ₘ_eq_getKey! [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] [Inhabited α]
{m : Raw₀ α β} (hm : Raw.WFImp m.1) {a : α} :
m.getKey!ₘ a = List.getKey! a (toListModel m.1.buckets) := by
rw [getKey!ₘ, getKey?ₘ_eq_getKey? hm, List.getKey!_eq_getKey?]
theorem getKey!_eq_getKey! [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] [Inhabited α]
{m : Raw₀ α β} (hm : Raw.WFImp m.1) {a : α} :
m.getKey! a = List.getKey! a (toListModel m.1.buckets) := by
rw [getKey!_eq_getKey!ₘ, getKey!ₘ_eq_getKey! hm]
theorem getKeyDₘ_eq_getKeyD [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
(hm : Raw.WFImp m.1) {a fallback : α} :
m.getKeyDₘ a fallback = List.getKeyD a (toListModel m.1.buckets) fallback := by
rw [getKeyDₘ, getKey?ₘ_eq_getKey? hm, List.getKeyD_eq_getKey?]
theorem getKeyD_eq_getKeyD [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
(hm : Raw.WFImp m.1) {a fallback : α} :
m.getKeyD a fallback = List.getKeyD a (toListModel m.1.buckets) fallback := by
rw [getKeyD_eq_getKeyDₘ, getKeyDₘ_eq_getKeyD hm]
section
variable {β : Type v}

View File

@@ -599,6 +599,189 @@ theorem getD_congr [EquivBEq α] [LawfulHashable α] {a b : α} {fallback : β}
end Const
@[simp]
theorem getKey?_empty {a : α} {c} : (empty c : DHashMap α β).getKey? a = none :=
Raw₀.getKey?_empty
@[simp]
theorem getKey?_emptyc {a : α} : ( : DHashMap α β).getKey? a = none :=
Raw₀.getKey?_empty
theorem getKey?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
m.isEmpty = true m.getKey? a = none :=
Raw₀.getKey?_of_isEmpty m.1, _ m.2
theorem getKey?_insert [EquivBEq α] [LawfulHashable α] {a k : α} {v : β k} :
(m.insert k v).getKey? a = if k == a then some k else m.getKey? a :=
Raw₀.getKey?_insert m.1, _ m.2
@[simp]
theorem getKey?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
(m.insert k v).getKey? k = some k :=
Raw₀.getKey?_insert_self m.1, _ m.2
theorem contains_eq_isSome_getKey? [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = (m.getKey? a).isSome :=
Raw₀.contains_eq_isSome_getKey? m.1, _ m.2
theorem getKey?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = false m.getKey? a = none :=
Raw₀.getKey?_eq_none m.1, _ m.2
theorem getKey?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a m m.getKey? a = none := by
simpa [mem_iff_contains] using getKey?_eq_none_of_contains_eq_false
theorem getKey?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).getKey? a = if k == a then none else m.getKey? a :=
Raw₀.getKey?_erase m.1, _ m.2
@[simp]
theorem getKey?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).getKey? k = none :=
Raw₀.getKey?_erase_self m.1, _ m.2
theorem getKey_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} {h₁} :
(m.insert k v).getKey a h₁ =
if h₂ : k == a then
k
else
m.getKey a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
Raw₀.getKey_insert m.1, _ m.2
@[simp]
theorem getKey_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
(m.insert k v).getKey k mem_insert_self = k :=
Raw₀.getKey_insert_self m.1, _ m.2
@[simp]
theorem getKey_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
(m.erase k).getKey a h' = m.getKey a (mem_of_mem_erase h') :=
Raw₀.getKey_erase m.1, _ m.2
theorem getKey?_eq_some_getKey [EquivBEq α] [LawfulHashable α] {a : α}
{h} :
m.getKey? a = some (m.getKey a h) :=
Raw₀.getKey?_eq_some_getKey m.1, _ m.2
@[simp]
theorem getKey!_empty [Inhabited α] {a : α} {c} :
(empty c : DHashMap α β).getKey! a = default :=
Raw₀.getKey!_empty
@[simp]
theorem getKey!_emptyc [Inhabited α] {a : α} :
( : DHashMap α β).getKey! a = default :=
getKey!_empty
theorem getKey!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.isEmpty = true m.getKey! a = default :=
Raw₀.getKey!_of_isEmpty m.1, _ m.2
theorem getKey!_insert [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β k} :
(m.insert k v).getKey! a =
if k == a then k else m.getKey! a :=
Raw₀.getKey!_insert m.1, _ m.2
@[simp]
theorem getKey!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {b : β a} :
(m.insert a b).getKey! a = a :=
Raw₀.getKey!_insert_self m.1, _ m.2
theorem getKey!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α]
{a : α} :
m.contains a = false m.getKey! a = default :=
Raw₀.getKey!_eq_default m.1, _ m.2
theorem getKey!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
¬a m m.getKey! a = default := by
simpa [mem_iff_contains] using getKey!_eq_default_of_contains_eq_false
theorem getKey!_erase [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} :
(m.erase k).getKey! a = if k == a then default else m.getKey! a :=
Raw₀.getKey!_erase m.1, _ m.2
@[simp]
theorem getKey!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} :
(m.erase k).getKey! k = default :=
Raw₀.getKey!_erase_self m.1, _ m.2
theorem getKey?_eq_some_getKey!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.contains a = true m.getKey? a = some (m.getKey! a) :=
Raw₀.getKey?_eq_some_getKey! m.1, _ m.2
theorem getKey?_eq_some_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
a m m.getKey? a = some (m.getKey! a) := by
simpa [mem_iff_contains] using getKey?_eq_some_getKey!_of_contains
theorem getKey!_eq_get!_getKey? [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.getKey! a = (m.getKey? a).get! :=
Raw₀.getKey!_eq_get!_getKey? m.1, _ m.2
theorem getKey_eq_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {h} :
m.getKey a h = m.getKey! a :=
Raw₀.getKey_eq_getKey! m.1, _ m.2
@[simp]
theorem getKeyD_empty {a fallback : α} {c} :
(empty c : DHashMap α β).getKeyD a fallback = fallback :=
Raw₀.getKeyD_empty
@[simp]
theorem getKeyD_emptyc {a fallback : α} :
( : DHashMap α β).getKeyD a fallback = fallback :=
getKeyD_empty
theorem getKeyD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a fallback : α} :
m.isEmpty = true m.getKeyD a fallback = fallback :=
Raw₀.getKeyD_of_isEmpty m.1, _ m.2
theorem getKeyD_insert [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β k} :
(m.insert k v).getKeyD a fallback =
if k == a then k else m.getKeyD a fallback :=
Raw₀.getKeyD_insert m.1, _ m.2
@[simp]
theorem getKeyD_insert_self [EquivBEq α] [LawfulHashable α] {k fallback : α} {v : β k} :
(m.insert k v).getKeyD k fallback = k :=
Raw₀.getKeyD_insert_self m.1, _ m.2
theorem getKeyD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α}
{fallback : α} :
m.contains a = false m.getKeyD a fallback = fallback :=
Raw₀.getKeyD_eq_fallback m.1, _ m.2
theorem getKeyD_eq_fallback [EquivBEq α] [LawfulHashable α] {a fallback : α} :
¬a m m.getKeyD a fallback = fallback := by
simpa [mem_iff_contains] using getKeyD_eq_fallback_of_contains_eq_false
theorem getKeyD_erase [EquivBEq α] [LawfulHashable α] {k a fallback : α} :
(m.erase k).getKeyD a fallback = if k == a then fallback else m.getKeyD a fallback :=
Raw₀.getKeyD_erase m.1, _ m.2
@[simp]
theorem getKeyD_erase_self [EquivBEq α] [LawfulHashable α] {k fallback : α} :
(m.erase k).getKeyD k fallback = fallback :=
Raw₀.getKeyD_erase_self m.1, _ m.2
theorem getKey?_eq_some_getKeyD_of_contains [EquivBEq α] [LawfulHashable α] {a fallback : α} :
m.contains a = true m.getKey? a = some (m.getKeyD a fallback) :=
Raw₀.getKey?_eq_some_getKeyD m.1, _ m.2
theorem getKey?_eq_some_getKeyD [EquivBEq α] [LawfulHashable α] {a fallback : α} :
a m m.getKey? a = some (m.getKeyD a fallback) := by
simpa [mem_iff_contains] using getKey?_eq_some_getKeyD_of_contains
theorem getKeyD_eq_getD_getKey? [EquivBEq α] [LawfulHashable α] {a fallback : α} :
m.getKeyD a fallback = (m.getKey? a).getD fallback :=
Raw₀.getKeyD_eq_getD_getKey? m.1, _ m.2
theorem getKey_eq_getKeyD [EquivBEq α] [LawfulHashable α] {a fallback : α} {h} :
m.getKey a h = m.getKeyD a fallback :=
Raw₀.getKey_eq_getKeyD m.1, _ m.2
theorem getKey!_eq_getKeyD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.getKey! a = m.getKeyD a default :=
Raw₀.getKey!_eq_getKeyD_default m.1, _ m.2
@[simp]
theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
(m.insertIfNew k v).isEmpty = false :=
@@ -706,6 +889,29 @@ theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback
end Const
theorem getKey?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
getKey? (m.insertIfNew k v) a = if k == a ¬k m then some k else getKey? m a := by
simp [mem_iff_contains, contains_insertIfNew]
exact Raw₀.getKey?_insertIfNew m.1, _ m.2
theorem getKey_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} {h₁} :
getKey (m.insertIfNew k v) a h₁ =
if h₂ : k == a ¬k m then k else getKey m a (mem_of_mem_insertIfNew' h₁ h₂) := by
simp [mem_iff_contains, contains_insertIfNew]
exact Raw₀.getKey_insertIfNew m.1, _ m.2
theorem getKey!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β k} :
getKey! (m.insertIfNew k v) a = if k == a ¬k m then k else getKey! m a := by
simp [mem_iff_contains, contains_insertIfNew]
exact Raw₀.getKey!_insertIfNew m.1, _ m.2
theorem getKeyD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β k} :
getKeyD (m.insertIfNew k v) a fallback =
if k == a ¬k m then k else getKeyD m a fallback := by
simp [mem_iff_contains, contains_insertIfNew]
exact Raw₀.getKeyD_insertIfNew m.1, _ m.2
@[simp]
theorem getThenInsertIfNew?_fst [LawfulBEq α] {k : α} {v : β k} :
(m.getThenInsertIfNew? k v).1 = m.get? k :=

View File

@@ -237,6 +237,41 @@ returned map has a new value inserted.
end
/--
Checks if a mapping for the given key exists and returns the key if it does, otherwise `none`.
The result in the `some` case is guaranteed to be pointer equal to the key in the map.
-/
@[inline] def getKey? [BEq α] [Hashable α] (m : Raw α β) (a : α) : Option α :=
if h : 0 < m.buckets.size then
Raw₀.getKey? m, h a
else none -- will never happen for well-formed inputs
/--
Retrieves the key from the mapping that matches `a`. Ensures that such a mapping exists by
requiring a proof of `a ∈ m`. The result is guaranteed to be pointer equal to the key in the map.
-/
@[inline] def getKey [BEq α] [Hashable α] (m : Raw α β) (a : α) (h : a m) : α :=
Raw₀.getKey m, by change dite .. = true at h; split at h <;> simp_all a
(by change dite .. = true at h; split at h <;> simp_all)
/--
Checks if a mapping for the given key exists and returns the key if it does, otherwise `fallback`.
If a mapping exists the result is guaranteed to be pointer equal to the key in the map.
-/
@[inline] def getKeyD [BEq α] [Hashable α] (m : Raw α β) (a : α) (fallback : α) : α :=
if h : 0 < m.buckets.size then
Raw₀.getKeyD m, h a fallback
else fallback -- will never happen for well-formed inputs
/--
Checks if a mapping for the given key exists and returns the key if it does, otherwise panics.
If no panic occurs the result is guaranteed to be pointer equal to the key in the map.
-/
@[inline] def getKey! [BEq α] [Hashable α] [Inhabited α] (m : Raw α β) (a : α) : α :=
if h : 0 < m.buckets.size then
Raw₀.getKey! m, h a
else default -- will never happen for well-formed inputs
/--
Returns `true` if the hash map contains no mappings.
@@ -376,6 +411,14 @@ This is mainly useful to implement `HashSet.ofList`, so if you are considering u
Raw α (fun _ => Unit) :=
Const.insertManyUnit l
/-- Creates a hash map from an array of keys, associating the value `()` with each key.
This is mainly useful to implement `HashSet.ofArray`, so if you are considering using this,
`HashSet` or `HashSet.Raw` might be a better fit for you. -/
@[inline] def Const.unitOfArray [BEq α] [Hashable α] (l : Array α) :
Raw α (fun _ => Unit) :=
Const.insertManyUnit l
/--
Returns the number of buckets in the internal representation of the hash map. This function may be
useful for things like monitoring system health, but it should be considered an internal

View File

@@ -54,7 +54,11 @@ private def baseNames : Array Name :=
``contains_eq, ``contains_val,
``get_eq, ``get_val,
``getD_eq, ``getD_val,
``get!_eq, ``get!_val]
``get!_eq, ``get!_val,
``getKey?_eq, ``getKey?_val,
``getKey_eq, ``getKey_val,
``getKey!_eq, ``getKey!_val,
``getKeyD_eq, ``getKeyD_val]
/-- Internal implementation detail of the hash map -/
scoped syntax "simp_to_raw" ("using" term)? : tactic
@@ -661,6 +665,194 @@ theorem getD_congr [EquivBEq α] [LawfulHashable α] (h : m.WF) {a b : α} {fall
end Const
@[simp]
theorem getKey?_empty {a : α} {c} :
(empty c : Raw α β).getKey? a = none := by
simp_to_raw using Raw₀.getKey?_empty
@[simp]
theorem getKey?_emptyc {a : α} : ( : Raw α β).getKey? a = none :=
getKey?_empty
theorem getKey?_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.isEmpty = true m.getKey? a = none := by
simp_to_raw using Raw₀.getKey?_of_isEmpty m, _
theorem getKey?_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {a k : α} {v : β k} :
(m.insert k v).getKey? a = if k == a then some k else m.getKey? a := by
simp_to_raw using Raw₀.getKey?_insert
@[simp]
theorem getKey?_insert_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} {v : β k} :
(m.insert k v).getKey? k = some k := by
simp_to_raw using Raw₀.getKey?_insert_self
theorem contains_eq_isSome_getKey? [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.contains a = (m.getKey? a).isSome := by
simp_to_raw using Raw₀.contains_eq_isSome_getKey?
theorem getKey?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.contains a = false m.getKey? a = none := by
simp_to_raw using Raw₀.getKey?_eq_none
theorem getKey?_eq_none [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
¬a m m.getKey? a = none := by
simpa [mem_iff_contains] using getKey?_eq_none_of_contains_eq_false h
theorem getKey?_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
(m.erase k).getKey? a = if k == a then none else m.getKey? a := by
simp_to_raw using Raw₀.getKey?_erase
@[simp]
theorem getKey?_erase_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} :
(m.erase k).getKey? k = none := by
simp_to_raw using Raw₀.getKey?_erase_self
theorem getKey_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {v : β k} {h₁} :
(m.insert k v).getKey a h₁ =
if h₂ : k == a then
k
else
m.getKey a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) := by
simp_to_raw using Raw₀.getKey_insert m, _
@[simp]
theorem getKey_insert_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} {v : β k} :
(m.insert k v).getKey k (mem_insert_self h) = k := by
simp_to_raw using Raw₀.getKey_insert_self m, _
@[simp]
theorem getKey_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {h'} :
(m.erase a).getKey k h' = m.getKey k (mem_of_mem_erase h h') := by
simp_to_raw using Raw₀.getKey_erase m, _
theorem getKey?_eq_some_getKey [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} {h} :
m.getKey? a = some (m.getKey a h) := by
simp_to_raw using Raw₀.getKey?_eq_some_getKey
@[simp]
theorem getKey!_empty [Inhabited α] {a : α} {c} :
(empty c : Raw α β).getKey! a = default := by
simp_to_raw using Raw₀.getKey!_empty
@[simp]
theorem getKey!_emptyc [Inhabited α] {a : α} :
( : Raw α β).getKey! a = default :=
getKey!_empty
theorem getKey!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
m.isEmpty = true m.getKey! a = default := by
simp_to_raw using Raw₀.getKey!_of_isEmpty m, _
theorem getKey!_insert [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k a : α} {v : β k} :
(m.insert k v).getKey! a = if k == a then k else m.getKey! a := by
simp_to_raw using Raw₀.getKey!_insert
@[simp]
theorem getKey!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k : α}
{v : β k} :
(m.insert k v).getKey! k = k := by
simp_to_raw using Raw₀.getKey!_insert_self
theorem getKey!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α]
(h : m.WF) {a : α} :
m.contains a = false m.getKey! a = default := by
simp_to_raw using Raw₀.getKey!_eq_default
theorem getKey!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α}:
¬a m m.getKey! a = default := by
simpa [mem_iff_contains] using getKey!_eq_default_of_contains_eq_false h
theorem getKey!_erase [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k a : α} :
(m.erase k).getKey! a = if k == a then default else m.getKey! a := by
simp_to_raw using Raw₀.getKey!_erase
@[simp]
theorem getKey!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k : α} :
(m.erase k).getKey! k = default := by
simp_to_raw using Raw₀.getKey!_erase_self
theorem getKey?_eq_some_getKey!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF)
{a : α} :
m.contains a = true m.getKey? a = some (m.getKey! a) := by
simp_to_raw using Raw₀.getKey?_eq_some_getKey!
theorem getKey?_eq_some_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
a m m.getKey? a = some (m.getKey! a) := by
simpa [mem_iff_contains] using getKey?_eq_some_getKey!_of_contains h
theorem getKey!_eq_get!_getKey? [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
m.getKey! a = (m.getKey? a).get! := by
simp_to_raw using Raw₀.getKey!_eq_get!_getKey?
theorem getKey_eq_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} {h} :
m.getKey a h = m.getKey! a := by
simp_to_raw using Raw₀.getKey_eq_getKey!
@[simp]
theorem getKeyD_empty {a fallback : α} {c} :
(empty c : Raw α β).getKeyD a fallback = fallback := by
simp_to_raw using Raw₀.getKeyD_empty
@[simp]
theorem getKeyD_emptyc {a fallback : α} :
( : Raw α β).getKeyD a fallback = fallback :=
getKeyD_empty
theorem getKeyD_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
m.isEmpty = true m.getKeyD a fallback = fallback := by
simp_to_raw using Raw₀.getKeyD_of_isEmpty m, _
theorem getKeyD_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α} {v : β k} :
(m.insert k v).getKeyD a fallback =
if k == a then k else m.getKeyD a fallback := by
simp_to_raw using Raw₀.getKeyD_insert
@[simp]
theorem getKeyD_insert_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} {b : β a} :
(m.insert a b).getKeyD a fallback = a := by
simp_to_raw using Raw₀.getKeyD_insert_self
theorem getKeyD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF)
{a fallback : α} :
m.contains a = false m.getKeyD a fallback = fallback := by
simp_to_raw using Raw₀.getKeyD_eq_fallback
theorem getKeyD_eq_fallback [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
¬a m m.getKeyD a fallback = fallback := by
simpa [mem_iff_contains] using getKeyD_eq_fallback_of_contains_eq_false h
theorem getKeyD_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α} :
(m.erase k).getKeyD a fallback = if k == a then fallback else m.getKeyD a fallback := by
simp_to_raw using Raw₀.getKeyD_erase
@[simp]
theorem getKeyD_erase_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k fallback : α} :
(m.erase k).getKeyD k fallback = fallback := by
simp_to_raw using Raw₀.getKeyD_erase_self
theorem getKey?_eq_some_getKeyD_of_contains [EquivBEq α] [LawfulHashable α] (h : m.WF)
{a fallback : α} :
m.contains a = true m.getKey? a = some (m.getKeyD a fallback) := by
simp_to_raw using Raw₀.getKey?_eq_some_getKeyD
theorem getKey?_eq_some_getKeyD [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
a m m.getKey? a = some (m.getKeyD a fallback) := by
simpa [mem_iff_contains] using getKey?_eq_some_getKeyD_of_contains h
theorem getKeyD_eq_getD_getKey? [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
m.getKeyD a fallback = (m.getKey? a).getD fallback := by
simp_to_raw using Raw₀.getKeyD_eq_getD_getKey?
theorem getKey_eq_getKeyD [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} {h} :
m.getKey a h = m.getKeyD a fallback := by
simp_to_raw using Raw₀.getKey_eq_getKeyD
theorem getKey!_eq_getKeyD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF)
{a : α} :
m.getKey! a = m.getKeyD a default := by
simp_to_raw using Raw₀.getKey!_eq_getKeyD_default
@[simp]
theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} {v : β k} :
(m.insertIfNew k v).isEmpty = false := by
@@ -774,6 +966,30 @@ theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α}
end Const
theorem getKey?_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {v : β k} :
getKey? (m.insertIfNew k v) a = if k == a ¬k m then some k else getKey? m a := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.getKey?_insertIfNew
theorem getKey_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {v : β k} {h₁} :
getKey (m.insertIfNew k v) a h₁ =
if h₂ : k == a ¬k m then k else getKey m a (mem_of_mem_insertIfNew' h h₁ h₂) := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.getKey_insertIfNew m, _
theorem getKey!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k a : α}
{v : β k} :
getKey! (m.insertIfNew k v) a = if k == a ¬k m then k else getKey! m a := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.getKey!_insertIfNew
theorem getKeyD_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α}
{v : β k} :
getKeyD (m.insertIfNew k v) a fallback =
if k == a ¬k m then k else getKeyD m a fallback := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.getKeyD_insertIfNew
@[simp]
theorem getThenInsertIfNew?_fst [LawfulBEq α] (h : m.WF) {k : α} {v : β k} :
(m.getThenInsertIfNew? k v).1 = m.get? k := by

View File

@@ -160,6 +160,18 @@ 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.getKey?] def getKey? (m : HashMap α β) (a : α) : Option α :=
DHashMap.getKey? m.inner a
@[inline, inherit_doc DHashMap.getKey] def getKey (m : HashMap α β) (a : α) (h : a m) : α :=
DHashMap.getKey m.inner a h
@[inline, inherit_doc DHashMap.getKeyD] def getKeyD (m : HashMap α β) (a : α) (fallback : α) : α :=
DHashMap.getKeyD m.inner a fallback
@[inline, inherit_doc DHashMap.getKey!] def getKey! [Inhabited α] (m : HashMap α β) (a : α) : α :=
DHashMap.getKey! m.inner a
@[inline, inherit_doc DHashMap.erase] def erase (m : HashMap α β) (a : α) :
HashMap α β :=
m.inner.erase a
@@ -238,6 +250,10 @@ instance [BEq α] [Hashable α] {m : Type w → Type w} : ForIn m (HashMap α β
HashMap α Unit :=
DHashMap.Const.unitOfList l
@[inline, inherit_doc DHashMap.Const.unitOfArray] def unitOfArray [BEq α] [Hashable α] (l : Array α) :
HashMap α Unit :=
DHashMap.Const.unitOfArray l
@[inline, inherit_doc DHashMap.Internal.numBuckets] def Internal.numBuckets
(m : HashMap α β) : Nat :=
DHashMap.Internal.numBuckets m.inner

View File

@@ -391,6 +391,178 @@ theorem getD_congr [EquivBEq α] [LawfulHashable α] {a b : α} {fallback : β}
m.getD a fallback = m.getD b fallback :=
DHashMap.Const.getD_congr hab
@[simp]
theorem getKey?_empty {a : α} {c} : (empty c : HashMap α β).getKey? a = none :=
DHashMap.getKey?_empty
@[simp]
theorem getKey?_emptyc {a : α} : ( : HashMap α β).getKey? a = none :=
DHashMap.getKey?_emptyc
theorem getKey?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
m.isEmpty = true m.getKey? a = none :=
DHashMap.getKey?_of_isEmpty
theorem getKey?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
(m.insert k v).getKey? a = if k == a then some k else m.getKey? a :=
DHashMap.getKey?_insert
@[simp]
theorem getKey?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
(m.insert k v).getKey? k = some k :=
DHashMap.getKey?_insert_self
theorem contains_eq_isSome_getKey? [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = (m.getKey? a).isSome :=
DHashMap.contains_eq_isSome_getKey?
theorem getKey?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = false m.getKey? a = none :=
DHashMap.getKey?_eq_none_of_contains_eq_false
theorem getKey?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a m m.getKey? a = none :=
DHashMap.getKey?_eq_none
theorem getKey?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).getKey? a = if k == a then none else m.getKey? a :=
DHashMap.getKey?_erase
@[simp]
theorem getKey?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).getKey? k = none :=
DHashMap.getKey?_erase_self
theorem getKey_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
(m.insert k v)[a]'h₁ =
if h₂ : k == a then v else m[a]'(mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
DHashMap.Const.get_insert (h₁ := h₁)
@[simp]
theorem getKey_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
(m.insert k v).getKey k mem_insert_self = k :=
DHashMap.getKey_insert_self
@[simp]
theorem getKey_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
(m.erase k).getKey a h' = m.getKey a (mem_of_mem_erase h') :=
DHashMap.getKey_erase (h' := h')
theorem getKey?_eq_some_getKey [EquivBEq α] [LawfulHashable α] {a : α} {h' : a m} :
m.getKey? a = some (m.getKey a h') :=
@DHashMap.getKey?_eq_some_getKey _ _ _ _ _ _ _ _ h'
@[simp]
theorem getKey!_empty [Inhabited α] {a : α} {c} : (empty c : HashMap α β).getKey! a = default :=
DHashMap.getKey!_empty
@[simp]
theorem getKey!_emptyc [Inhabited α] {a : α} : ( : HashMap α β).getKey! a = default :=
DHashMap.getKey!_emptyc
theorem getKey!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.isEmpty = true m.getKey! a = default :=
DHashMap.getKey!_of_isEmpty
theorem getKey!_insert [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β} :
(m.insert k v).getKey! a = if k == a then k else m.getKey! a :=
DHashMap.getKey!_insert
@[simp]
theorem getKey!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} {v : β} :
(m.insert k v).getKey! k = k :=
DHashMap.getKey!_insert_self
theorem getKey!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α]
{a : α} : m.contains a = false m.getKey! a = default :=
DHashMap.getKey!_eq_default_of_contains_eq_false
theorem getKey!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
¬a m m.getKey! a = default :=
DHashMap.getKey!_eq_default
theorem getKey!_erase [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} :
(m.erase k).getKey! a = if k == a then default else m.getKey! a :=
DHashMap.getKey!_erase
@[simp]
theorem getKey!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} :
(m.erase k).getKey! k = default :=
DHashMap.getKey!_erase_self
theorem getKey?_eq_some_getKey!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α]
{a : α} : m.contains a = true m.getKey? a = some (m.getKey! a) :=
DHashMap.getKey?_eq_some_getKey!_of_contains
theorem getKey?_eq_some_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
a m m.getKey? a = some (m.getKey! a) :=
DHashMap.getKey?_eq_some_getKey!
theorem getKey!_eq_get!_getKey? [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.getKey! a = (m.getKey? a).get! :=
DHashMap.getKey!_eq_get!_getKey?
theorem getKey_eq_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {h'} :
m.getKey a h' = m.getKey! a :=
@DHashMap.getKey_eq_getKey! _ _ _ _ _ _ _ _ _ h'
@[simp]
theorem getKeyD_empty {a : α} {fallback : α} {c} :
(empty c : HashMap α β).getKeyD a fallback = fallback :=
DHashMap.getKeyD_empty
@[simp]
theorem getKeyD_emptyc {a : α} {fallback : α} : ( : HashMap α β).getKeyD a fallback = fallback :=
DHashMap.getKeyD_empty
theorem getKeyD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} :
m.isEmpty = true m.getKeyD a fallback = fallback :=
DHashMap.getKeyD_of_isEmpty
theorem getKeyD_insert [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β} :
(m.insert k v).getKeyD a fallback = if k == a then k else m.getKeyD a fallback :=
DHashMap.getKeyD_insert
@[simp]
theorem getKeyD_insert_self [EquivBEq α] [LawfulHashable α] {k fallback : α} {v : β} :
(m.insert k v).getKeyD k fallback = k :=
DHashMap.getKeyD_insert_self
theorem getKeyD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α}
{fallback : α} : m.contains a = false m.getKeyD a fallback = fallback :=
DHashMap.getKeyD_eq_fallback_of_contains_eq_false
theorem getKeyD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} :
¬a m m.getKeyD a fallback = fallback :=
DHashMap.getKeyD_eq_fallback
theorem getKeyD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : α} :
(m.erase k).getKeyD a fallback = if k == a then fallback else m.getKeyD a fallback :=
DHashMap.getKeyD_erase
@[simp]
theorem getKeyD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : α} :
(m.erase k).getKeyD k fallback = fallback :=
DHashMap.getKeyD_erase_self
theorem getKey?_eq_some_getKeyD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} :
m.contains a = true m.getKey? a = some (m.getKeyD a fallback) :=
DHashMap.getKey?_eq_some_getKeyD_of_contains
theorem getKey?_eq_some_getKeyD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} :
a m m.getKey? a = some (m.getKeyD a fallback) :=
DHashMap.getKey?_eq_some_getKeyD
theorem getKeyD_eq_getD_getKey? [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} :
m.getKeyD a fallback = (m.getKey? a).getD fallback :=
DHashMap.getKeyD_eq_getD_getKey?
theorem getKey_eq_getKeyD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} {h'} :
m.getKey a h' = m.getKeyD a fallback :=
@DHashMap.getKey_eq_getKeyD _ _ _ _ _ _ _ _ _ h'
theorem getKey!_eq_getKeyD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.getKey! a = m.getKeyD a default :=
DHashMap.getKey!_eq_getKeyD_default
@[simp]
theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
(m.insertIfNew k v).isEmpty = false :=
@@ -464,6 +636,23 @@ theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback
if k == a ¬k m then v else m.getD a fallback :=
DHashMap.Const.getD_insertIfNew
theorem getKey?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
getKey? (m.insertIfNew k v) a = if k == a ¬k m then some k else getKey? m a :=
DHashMap.getKey?_insertIfNew
theorem getKey_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
getKey (m.insertIfNew k v) a h₁ =
if h₂ : k == a ¬k m then k else getKey m a (mem_of_mem_insertIfNew' h₁ h₂) :=
DHashMap.getKey_insertIfNew
theorem getKey!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β} :
getKey! (m.insertIfNew k v) a = if k == a ¬k m then k else getKey! m a :=
DHashMap.getKey!_insertIfNew
theorem getKeyD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β} :
getKeyD (m.insertIfNew k v) a fallback = if k == a ¬k m then k else getKeyD m a fallback :=
DHashMap.getKeyD_insertIfNew
@[simp]
theorem getThenInsertIfNew?_fst {k : α} {v : β} : (getThenInsertIfNew? m k v).1 = get? m k :=
DHashMap.Const.getThenInsertIfNew?_fst

View File

@@ -138,6 +138,22 @@ instance [BEq α] [Hashable α] : GetElem? (Raw α β) α β (fun m a => a ∈ m
getElem? m a := m.get? a
getElem! m a := m.get! a
@[inline, inherit_doc DHashMap.Raw.getKey?] def getKey? [BEq α] [Hashable α] (m : Raw α β) (a : α) :
Option α :=
DHashMap.Raw.getKey? m.inner a
@[inline, inherit_doc DHashMap.Raw.getKey] def getKey [BEq α] [Hashable α] (m : Raw α β) (a : α)
(h : a m) : α :=
DHashMap.Raw.getKey m.inner a h
@[inline, inherit_doc DHashMap.Raw.getKeyD] def getKeyD [BEq α] [Hashable α] (m : Raw α β) (a : α)
(fallback : α) : α :=
DHashMap.Raw.getKeyD m.inner a fallback
@[inline, inherit_doc DHashMap.Raw.getKey!] def getKey! [BEq α] [Hashable α] [Inhabited α]
(m : Raw α β) (a : α) : α :=
DHashMap.Raw.getKey! m.inner a
@[inline, inherit_doc DHashMap.Raw.erase] def erase [BEq α] [Hashable α] (m : Raw α β)
(a : α) : Raw α β :=
m.inner.erase a

View File

@@ -400,6 +400,181 @@ theorem getD_congr [EquivBEq α] [LawfulHashable α] (h : m.WF) {a b : α} {fall
(hab : a == b) : m.getD a fallback = m.getD b fallback :=
DHashMap.Raw.Const.getD_congr h.out hab
@[simp]
theorem getKey?_empty {a : α} {c} : (empty c : Raw α β).getKey? a = none :=
DHashMap.Raw.getKey?_empty
@[simp]
theorem getKey?_emptyc {a : α} : ( : Raw α β).getKey? a = none :=
DHashMap.Raw.getKey?_emptyc
theorem getKey?_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.isEmpty = true m.getKey? a = none :=
DHashMap.Raw.getKey?_of_isEmpty h.out
theorem getKey?_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {v : β} :
(m.insert k v).getKey? a = if k == a then some k else m.getKey? a :=
DHashMap.Raw.getKey?_insert h.out
@[simp]
theorem getKey?_insert_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} {v : β} :
(m.insert k v).getKey? k = some k :=
DHashMap.Raw.getKey?_insert_self h.out
theorem contains_eq_isSome_getKey? [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.contains a = (m.getKey? a).isSome :=
DHashMap.Raw.contains_eq_isSome_getKey? h.out
theorem getKey?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.contains a = false m.getKey? a = none :=
DHashMap.Raw.getKey?_eq_none_of_contains_eq_false h.out
theorem getKey?_eq_none [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
¬a m m.getKey? a = none :=
DHashMap.Raw.getKey?_eq_none h.out
theorem getKey?_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
(m.erase k).getKey? a = if k == a then none else m.getKey? a :=
DHashMap.Raw.getKey?_erase h.out
@[simp]
theorem getKey?_erase_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} :
(m.erase k).getKey? k = none :=
DHashMap.Raw.getKey?_erase_self h.out
theorem getKey_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {v : β} {h₁} :
(m.insert k v).getKey a h₁ =
if h₂ : k == a then k else m.getKey a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) :=
DHashMap.Raw.getKey_insert (h₁ := h₁) h.out
@[simp]
theorem getKey_insert_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} {v : β} :
(m.insert k v).getKey k (mem_insert_self h) = k :=
DHashMap.Raw.getKey_insert_self h.out
@[simp]
theorem getKey_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {h'} :
(m.erase k).getKey a h' = m.getKey a (mem_of_mem_erase h h') :=
DHashMap.Raw.getKey_erase (h' := h') h.out
theorem getKey?_eq_some_getKey [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} {h' : a m} :
m.getKey? a = some (m.getKey a h') :=
@DHashMap.Raw.getKey?_eq_some_getKey _ _ _ _ _ _ _ h.out _ h'
@[simp]
theorem getKey!_empty [Inhabited α] {a : α} {c} : (empty c : Raw α β).getKey! a = default :=
DHashMap.Raw.getKey!_empty
@[simp]
theorem getKey!_emptyc [Inhabited α] {a : α} : ( : Raw α β).getKey! a = default :=
DHashMap.Raw.getKey!_emptyc
theorem getKey!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
m.isEmpty = true m.getKey! a = default :=
DHashMap.Raw.getKey!_of_isEmpty h.out
theorem getKey!_insert [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k a : α} {v : β} :
(m.insert k v).getKey! a = if k == a then k else m.getKey! a :=
DHashMap.Raw.getKey!_insert h.out
@[simp]
theorem getKey!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k : α}
{v : β} : (m.insert k v).getKey! k = k :=
DHashMap.Raw.getKey!_insert_self h.out
theorem getKey!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α]
(h : m.WF) {a : α} : m.contains a = false m.getKey! a = default :=
DHashMap.Raw.getKey!_eq_default_of_contains_eq_false h.out
theorem getKey!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
¬a m m.getKey! a = default :=
DHashMap.Raw.getKey!_eq_default h.out
theorem getKey!_erase [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k a : α} :
(m.erase k).getKey! a = if k == a then default else m.getKey! a :=
DHashMap.Raw.getKey!_erase h.out
@[simp]
theorem getKey!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k : α} :
(m.erase k).getKey! k = default :=
DHashMap.Raw.getKey!_erase_self h.out
theorem getKey?_eq_some_getKey!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α]
(h : m.WF) {a : α} : m.contains a = true m.getKey? a = some (m.getKey! a) :=
DHashMap.Raw.getKey?_eq_some_getKey!_of_contains h.out
theorem getKey?_eq_some_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
a m m.getKey? a = some (m.getKey! a) :=
DHashMap.Raw.getKey?_eq_some_getKey! h.out
theorem getKey!_eq_get!_getKey? [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
m.getKey! a = (m.getKey? a).get! :=
DHashMap.Raw.getKey!_eq_get!_getKey? h.out
theorem getKey_eq_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} {h'} :
m.getKey a h' = m.getKey! a :=
DHashMap.Raw.getKey_eq_getKey! h.out
@[simp]
theorem getKeyD_empty {a fallback : α} {c} :
(empty c : Raw α β).getKeyD a fallback = fallback :=
DHashMap.Raw.getKeyD_empty
@[simp]
theorem getKeyD_emptyc {a fallback : α} : ( : Raw α β).getKeyD a fallback = fallback :=
DHashMap.Raw.getKeyD_empty
theorem getKeyD_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
m.isEmpty = true m.getKeyD a fallback = fallback :=
DHashMap.Raw.getKeyD_of_isEmpty h.out
theorem getKeyD_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α} {v : β} :
(m.insert k v).getKeyD a fallback = if k == a then k else m.getKeyD a fallback :=
DHashMap.Raw.getKeyD_insert h.out
@[simp]
theorem getKeyD_insert_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k fallback : α} {v : β} :
(m.insert k v).getKeyD k fallback = k :=
DHashMap.Raw.getKeyD_insert_self h.out
theorem getKeyD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF)
{a fallback : α} : m.contains a = false m.getKeyD a fallback = fallback :=
DHashMap.Raw.getKeyD_eq_fallback_of_contains_eq_false h.out
theorem getKeyD_eq_fallback [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
¬a m m.getKeyD a fallback = fallback :=
DHashMap.Raw.getKeyD_eq_fallback h.out
theorem getKeyD_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α} :
(m.erase k).getKeyD a fallback = if k == a then fallback else m.getKeyD a fallback :=
DHashMap.Raw.getKeyD_erase h.out
@[simp]
theorem getKeyD_erase_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k fallback : α} :
(m.erase k).getKeyD k fallback = fallback :=
DHashMap.Raw.getKeyD_erase_self h.out
theorem getKey?_eq_some_getKeyD_of_contains [EquivBEq α] [LawfulHashable α] (h : m.WF)
{a fallback : α} : m.contains a = true m.getKey? a = some (m.getKeyD a fallback) :=
DHashMap.Raw.getKey?_eq_some_getKeyD_of_contains h.out
theorem getKey?_eq_some_getKeyD [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
a m m.getKey? a = some (m.getKeyD a fallback) :=
DHashMap.Raw.getKey?_eq_some_getKeyD h.out
theorem getKeyD_eq_getD_getKey? [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
m.getKeyD a fallback = (m.getKey? a).getD fallback :=
DHashMap.Raw.getKeyD_eq_getD_getKey? h.out
theorem getKey_eq_getKeyD [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} {h'} :
m.getKey a h' = m.getKeyD a fallback :=
@DHashMap.Raw.getKey_eq_getKeyD _ _ _ _ _ _ _ h.out _ _ h'
theorem getKey!_eq_getKeyD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF)
{a : α} :
m.getKey! a = m.getKeyD a default :=
DHashMap.Raw.getKey!_eq_getKeyD_default h.out
@[simp]
theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} {v : β} :
(m.insertIfNew k v).isEmpty = false :=
@@ -473,6 +648,24 @@ theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α}
if k == a ¬k m then v else m.getD a fallback :=
DHashMap.Raw.Const.getD_insertIfNew h.out
theorem getKey?_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {v : β} :
(m.insertIfNew k v).getKey? a = if k == a ¬k m then some k else m.getKey? a :=
DHashMap.Raw.getKey?_insertIfNew h.out
theorem getKey_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {v : β} {h₁} :
(m.insertIfNew k v).getKey a h₁ =
if h₂ : k == a ¬k m then k else m.getKey a (mem_of_mem_insertIfNew' h h₁ h₂) :=
DHashMap.Raw.getKey_insertIfNew h.out
theorem getKey!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {k a : α} {v : β} :
(m.insertIfNew k v).getKey! a = if k == a ¬k m then k else m.getKey! a :=
DHashMap.Raw.getKey!_insertIfNew h.out
theorem getKeyD_insertIfNew [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α} {v : β} :
(m.insertIfNew k v).getKeyD a fallback = if k == a ¬k m then k else m.getKeyD a fallback :=
DHashMap.Raw.getKeyD_insertIfNew h.out
@[simp]
theorem getThenInsertIfNew?_fst (h : m.WF) {k : α} {v : β} :
(getThenInsertIfNew? m k v).1 = get? m k :=

View File

@@ -112,6 +112,34 @@ instance [BEq α] [Hashable α] {m : HashSet α} {a : α} : Decidable (a ∈ m)
@[inline] def size (m : HashSet α) : Nat :=
m.inner.size
/--
Checks if given key is contained and returns the key if it is, otherwise `none`.
The result in the `some` case is guaranteed to be pointer equal to the key in the set.
-/
@[inline] def get? (m : HashSet α) (a : α) : Option α :=
m.inner.getKey? a
/--
Retrieves the key from the set that matches `a`. Ensures that such a key exists by requiring a proof
of `a ∈ m`. The result is guaranteed to be pointer equal to the key in the set.
-/
@[inline] def get [BEq α] [Hashable α] (m : HashSet α) (a : α) (h : a m) : α :=
m.inner.getKey a h
/--
Checks if given key is contained and returns the key if it is, otherwise `fallback`.
If they key is contained the result is guaranteed to be pointer equal to the key in the set.
-/
@[inline] def getD [BEq α] [Hashable α] (m : HashSet α) (a : α) (fallback : α) : α :=
m.inner.getKeyD a fallback
/--
Checks if given key is contained and returns the key if it is, otherwise panics.
If no panic occurs the result is guaranteed to be pointer equal to the key in the set.
-/
@[inline] def get! [BEq α] [Hashable α] [Inhabited α] (m : HashSet α) (a : α) : α :=
m.inner.getKey! a
/--
Returns `true` if the hash set contains no elements.
@@ -184,6 +212,14 @@ in the collection will be present in the returned hash set.
@[inline] def ofList [BEq α] [Hashable α] (l : List α) : HashSet α :=
HashMap.unitOfList l
/--
Creates a hash set from an array of elements. Note that unlike repeatedly calling `insert`, if the
collection contains multiple elements that are equal (with regard to `==`), then the last element
in the collection will be present in the returned hash set.
-/
@[inline] def ofArray [BEq α] [Hashable α] (l : Array α) : HashSet α :=
HashMap.unitOfArray l
/-- Computes the union of the given hash sets. -/
@[inline] def union [BEq α] [Hashable α] (m₁ m₂ : HashSet α) : HashSet α :=
m₂.fold (init := m₁) fun acc x => acc.insert x

View File

@@ -106,6 +106,18 @@ theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} :
a m.insert k (k == a) = false a m :=
HashMap.mem_of_mem_insertIfNew
/-- This is a restatement of `contains_insert` that is written to exactly match the proof
obligation in the statement of `get_insert`. -/
theorem contains_of_contains_insert' [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.insert k).contains a ¬((k == a) m.contains k = false) m.contains a :=
HashMap.contains_of_contains_insertIfNew'
/-- This is a restatement of `mem_insert` that is written to exactly match the proof obligation
in the statement of `get_insert`. -/
theorem mem_of_mem_insert' [EquivBEq α] [LawfulHashable α] {k a : α} :
a m.insert k ¬((k == a) ¬k m) a m :=
DHashMap.mem_of_mem_insertIfNew'
@[simp]
theorem contains_insert_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.insert k).contains k :=
HashMap.contains_insertIfNew_self
@@ -177,6 +189,158 @@ theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
m.size (m.erase k).size + 1 :=
HashMap.size_le_size_erase
@[simp]
theorem get?_empty {a : α} {c} : (empty c : HashSet α).get? a = none :=
HashMap.getKey?_empty
@[simp]
theorem get?_emptyc {a : α} : ( : HashSet α).get? a = none :=
HashMap.getKey?_emptyc
theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
m.isEmpty = true m.get? a = none :=
HashMap.getKey?_of_isEmpty
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.insert k).get? a = if k == a ¬k m then some k else m.get? a :=
HashMap.getKey?_insertIfNew
theorem contains_eq_isSome_get? [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = (m.get? a).isSome :=
HashMap.contains_eq_isSome_getKey?
theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = false m.get? a = none :=
HashMap.getKey?_eq_none_of_contains_eq_false
theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a m m.get? a = none :=
HashMap.getKey?_eq_none
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).get? a = if k == a then none else m.get? a :=
HashMap.getKey?_erase
@[simp]
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).get? k = none :=
HashMap.getKey?_erase_self
theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {h₁} :
(m.insert k).get a h₁ =
if h₂ : k == a ¬k m then k else m.get a (mem_of_mem_insert' h₁ h₂) :=
HashMap.getKey_insertIfNew (h₁ := h₁)
@[simp]
theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
(m.erase k).get a h' = m.get a (mem_of_mem_erase h') :=
HashMap.getKey_erase (h' := h')
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} {h' : a m} :
m.get? a = some (m.get a h') :=
@HashMap.getKey?_eq_some_getKey _ _ _ _ _ _ _ _ h'
@[simp]
theorem get!_empty [Inhabited α] {a : α} {c} : (empty c : HashSet α).get! a = default :=
HashMap.getKey!_empty
@[simp]
theorem get!_emptyc [Inhabited α] {a : α} : ( : HashSet α).get! a = default :=
HashMap.getKey!_emptyc
theorem get!_of_isEmpty [Inhabited α] [EquivBEq α] [LawfulHashable α] {a : α} :
m.isEmpty = true m.get! a = default :=
HashMap.getKey!_of_isEmpty
theorem get!_insert [Inhabited α] [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.insert k).get! a = if k == a ¬k m then k else m.get! a :=
HashMap.getKey!_insertIfNew
theorem get!_eq_default_of_contains_eq_false [Inhabited α] [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = false m.get! a = default :=
HashMap.getKey!_eq_default_of_contains_eq_false
theorem get!_eq_default [Inhabited α] [EquivBEq α] [LawfulHashable α] {a : α} :
¬a m m.get! a = default :=
HashMap.getKey!_eq_default
theorem get!_erase [Inhabited α] [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).get! a = if k == a then default else m.get! a :=
HashMap.getKey!_erase
@[simp]
theorem get!_erase_self [Inhabited α] [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).get! k = default :=
HashMap.getKey!_erase_self
theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α]
{a : α} : m.contains a = true m.get? a = some (m.get! a) :=
HashMap.getKey?_eq_some_getKey!_of_contains
theorem get?_eq_some_get! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
a m m.get? a = some (m.get! a) :=
HashMap.getKey?_eq_some_getKey!
theorem get!_eq_get!_get? [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.get! a = (m.get? a).get! :=
HashMap.getKey!_eq_get!_getKey?
theorem get_eq_get! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {h'} :
m.get a h' = m.get! a :=
HashMap.getKey_eq_getKey!
@[simp]
theorem getD_empty {a fallback : α} {c} : (empty c : HashSet α).getD a fallback = fallback :=
HashMap.getKeyD_empty
@[simp]
theorem getD_emptyc {a fallback : α} : ( : HashSet α).getD a fallback = fallback :=
HashMap.getKeyD_emptyc
theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a fallback : α} :
m.isEmpty = true m.getD a fallback = fallback :=
HashMap.getKeyD_of_isEmpty
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a fallback : α} :
(m.insert k).getD a fallback = if k == a ¬k m then k else m.getD a fallback :=
HashMap.getKeyD_insertIfNew
theorem getD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α]
{a fallback : α} :
m.contains a = false m.getD a fallback = fallback :=
HashMap.getKeyD_eq_fallback_of_contains_eq_false
theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a fallback : α} :
¬a m m.getD a fallback = fallback :=
HashMap.getKeyD_eq_fallback
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a fallback : α} :
(m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback :=
HashMap.getKeyD_erase
@[simp]
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k fallback : α} :
(m.erase k).getD k fallback = fallback :=
HashMap.getKeyD_erase_self
theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a fallback : α} :
m.contains a = true m.get? a = some (m.getD a fallback) :=
HashMap.getKey?_eq_some_getKeyD_of_contains
theorem get?_eq_some_getD [EquivBEq α] [LawfulHashable α] {a fallback : α} :
a m m.get? a = some (m.getD a fallback) :=
HashMap.getKey?_eq_some_getKeyD
theorem getD_eq_getD_get? [EquivBEq α] [LawfulHashable α] {a fallback : α} :
m.getD a fallback = (m.get? a).getD fallback :=
HashMap.getKeyD_eq_getD_getKey?
theorem get_eq_getD [EquivBEq α] [LawfulHashable α] {a fallback : α} {h'} :
m.get a h' = m.getD a fallback :=
@HashMap.getKey_eq_getKeyD _ _ _ _ _ _ _ _ _ h'
theorem get!_eq_getD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} :
m.get! a = m.getD a default :=
HashMap.getKey!_eq_getKeyD_default
@[simp]
theorem containsThenInsert_fst {k : α} : (m.containsThenInsert k).1 = m.contains k :=
HashMap.containsThenInsertIfNew_fst

View File

@@ -14,7 +14,7 @@ set with unbundled well-formedness invariant.
This version is safe to use in nested inductive types. The well-formedness predicate is
available as `Std.Data.HashSet.Raw.WF` and we prove in this file that all operations preserve
well-formedness. When in doubt, prefer `HashSet` over `DHashSet.Raw`.
well-formedness. When in doubt, prefer `HashSet` over `HashSet.Raw`.
Lemmas about the operations on `Std.Data.HashSet.Raw` are available in the module
`Std.Data.HashSet.RawLemmas`.
@@ -113,6 +113,34 @@ instance [BEq α] [Hashable α] {m : Raw α} {a : α} : Decidable (a ∈ m) :=
@[inline] def size (m : Raw α) : Nat :=
m.inner.size
/--
Checks if given key is contained and returns the key if it is, otherwise `none`.
The result in the `some` case is guaranteed to be pointer equal to the key in the map.
-/
@[inline] def get? [BEq α] [Hashable α] (m : Raw α) (a : α) : Option α :=
m.inner.getKey? a
/--
Retrieves the key from the set that matches `a`. Ensures that such a key exists by requiring a proof
of `a ∈ m`. The result is guaranteed to be pointer equal to the key in the set.
-/
@[inline] def get [BEq α] [Hashable α] (m : Raw α) (a : α) (h : a m) : α :=
m.inner.getKey a h
/--
Checks if given key is contained and returns the key if it is, otherwise `fallback`.
If they key is contained the result is guaranteed to be pointer equal to the key in the set.
-/
@[inline] def getD [BEq α] [Hashable α] (m : Raw α) (a : α) (fallback : α) : α :=
m.inner.getKeyD a fallback
/--
Checks if given key is contained and returns the key if it is, otherwise panics.
If no panic occurs the result is guaranteed to be pointer equal to the key in the set.
-/
@[inline] def get! [BEq α] [Hashable α] [Inhabited α] (m : Raw α) (a : α) : α :=
m.inner.getKey! a
/--
Returns `true` if the hash set contains no elements.

View File

@@ -122,6 +122,18 @@ theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α
a m.insert k (k == a) = false a m :=
HashMap.Raw.mem_of_mem_insertIfNew h.out
/-- This is a restatement of `contains_insert` that is written to exactly match the proof
obligation in the statement of `get_insert`. -/
theorem contains_of_contains_insert' [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
(m.insert k).contains a ¬((k == a) m.contains k = false) m.contains a :=
HashMap.Raw.contains_of_contains_insertIfNew' h.out
/-- This is a restatement of `mem_insert` that is written to exactly match the proof obligation
in the statement of `get_insertIfNew`. -/
theorem mem_of_mem_insert' [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
a m.insert k ¬((k == a) ¬k m) a m :=
HashMap.Raw.mem_of_mem_insertIfNew' h.out
@[simp]
theorem contains_insert_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} :
(m.insert k).contains k :=
@@ -186,6 +198,162 @@ theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α}
m.size (m.erase k).size + 1 :=
HashMap.Raw.size_le_size_erase h.out
@[simp]
theorem get?_empty {a : α} {c} : (empty c : Raw α).get? a = none :=
HashMap.Raw.getKey?_empty
@[simp]
theorem get?_emptyc {a : α} : ( : Raw α).get? a = none :=
HashMap.Raw.getKey?_emptyc
theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.isEmpty = true m.get? a = none :=
HashMap.Raw.getKey?_of_isEmpty h.out
theorem get?_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
(m.insert k).get? a = if k == a ¬k m then some k else m.get? a :=
HashMap.Raw.getKey?_insertIfNew h.out
theorem contains_eq_isSome_get? [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.contains a = (m.get? a).isSome :=
HashMap.Raw.contains_eq_isSome_getKey? h.out
theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.contains a = false m.get? a = none :=
HashMap.Raw.getKey?_eq_none_of_contains_eq_false h.out
theorem get?_eq_none [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
¬a m m.get? a = none :=
HashMap.Raw.getKey?_eq_none h.out
theorem get?_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
(m.erase k).get? a = if k == a then none else m.get? a :=
HashMap.Raw.getKey?_erase h.out
theorem get_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {h₁} :
(m.insert k).get a h₁ =
if h₂ : k == a ¬k m then k else m.get a (mem_of_mem_insert' h h₁ h₂) :=
HashMap.Raw.getKey_insertIfNew (h₁ := h₁) h.out
@[simp]
theorem get_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} {h'} :
(m.erase k).get a h' = m.get a (mem_of_mem_erase h h') :=
HashMap.Raw.getKey_erase (h' := h') h.out
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} {h' : a m} :
m.get? a = some (m.get a h') :=
@HashMap.Raw.getKey?_eq_some_getKey _ _ _ _ _ _ _ h.out _ h'
@[simp]
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} :
(m.erase k).get? k = none :=
HashMap.Raw.getKey?_erase_self h.out
@[simp]
theorem get!_empty [Inhabited α] {a : α} {c} : (empty c : Raw α).get! a = default :=
HashMap.Raw.getKey!_empty
@[simp]
theorem get!_emptyc [Inhabited α] {a : α} : ( : Raw α).get! a = default :=
HashMap.Raw.getKey!_emptyc
theorem get!_of_isEmpty [Inhabited α] [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
m.isEmpty = true m.get! a = default :=
HashMap.Raw.getKey!_of_isEmpty h.out
theorem get!_insert [Inhabited α] [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
(m.insert k).get! a = if k == a ¬k m then k else m.get! a :=
HashMap.Raw.getKey!_insertIfNew h.out
theorem get!_eq_default_of_contains_eq_false [Inhabited α] [EquivBEq α] [LawfulHashable α]
(h : m.WF) {a : α} :
m.contains a = false m.get! a = default :=
HashMap.Raw.getKey!_eq_default_of_contains_eq_false h.out
theorem get!_eq_default [Inhabited α] [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} :
¬a m m.get! a = default :=
HashMap.Raw.getKey!_eq_default h.out
theorem get!_erase [Inhabited α] [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a : α} :
(m.erase k).get! a = if k == a then default else m.get! a :=
HashMap.Raw.getKey!_erase h.out
@[simp]
theorem get!_erase_self [Inhabited α] [EquivBEq α] [LawfulHashable α] (h : m.WF) {k : α} :
(m.erase k).get! k = default :=
HashMap.Raw.getKey!_erase_self h.out
theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α]
(h : m.WF) {a : α} : m.contains a = true m.get? a = some (m.get! a) :=
HashMap.Raw.getKey?_eq_some_getKey!_of_contains h.out
theorem get?_eq_some_get! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
a m m.get? a = some (m.get! a) :=
HashMap.Raw.getKey?_eq_some_getKey! h.out
theorem get!_eq_get!_get? [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} :
m.get! a = (m.get? a).get! :=
HashMap.Raw.getKey!_eq_get!_getKey? h.out
theorem get_eq_get! [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF) {a : α} {h'} :
m.get a h' = m.get! a :=
HashMap.Raw.getKey_eq_getKey! h.out
@[simp]
theorem getD_empty {a fallback : α} {c} : (empty c : Raw α).getD a fallback = fallback :=
HashMap.Raw.getKeyD_empty
@[simp]
theorem getD_emptyc {a fallback : α} : ( : Raw α).getD a fallback = fallback :=
HashMap.Raw.getKeyD_emptyc
theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
m.isEmpty = true m.getD a fallback = fallback :=
HashMap.Raw.getKeyD_of_isEmpty h.out
theorem getD_insert [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α} :
(m.insert k).getD a fallback = if k == a ¬k m then k else m.getD a fallback :=
HashMap.Raw.getKeyD_insertIfNew h.out
theorem getD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF)
{a fallback : α} :
m.contains a = false m.getD a fallback = fallback :=
HashMap.Raw.getKeyD_eq_fallback_of_contains_eq_false h.out
theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] (h : m.WF) {a fallback : α} :
¬a m m.getD a fallback = fallback :=
HashMap.Raw.getKeyD_eq_fallback h.out
theorem getD_erase [EquivBEq α] [LawfulHashable α] (h : m.WF) {k a fallback : α} :
(m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback :=
HashMap.Raw.getKeyD_erase h.out
@[simp]
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] (h : m.WF) {k fallback : α} :
(m.erase k).getD k fallback = fallback :=
HashMap.Raw.getKeyD_erase_self h.out
theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α}
{fallback : α} : m.contains a = true m.get? a = some (m.getD a fallback) :=
HashMap.Raw.getKey?_eq_some_getKeyD_of_contains h.out
theorem get?_eq_some_getD [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} {fallback : α} :
a m m.get? a = some (m.getD a fallback) :=
HashMap.Raw.getKey?_eq_some_getKeyD h.out
theorem getD_eq_getD_get? [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} {fallback : α} :
m.getD a fallback = (m.get? a).getD fallback :=
HashMap.Raw.getKeyD_eq_getD_getKey? h.out
theorem get_eq_getD [EquivBEq α] [LawfulHashable α] (h : m.WF) {a : α} {fallback : α} {h'} :
m.get a h' = m.getD a fallback :=
@HashMap.Raw.getKey_eq_getKeyD _ _ _ _ _ _ _ h.out _ _ h'
theorem get!_eq_getD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] (h : m.WF)
{a : α} :
m.get! a = m.getD a default :=
HashMap.Raw.getKey!_eq_getKeyD_default h.out
@[simp]
theorem containsThenInsert_fst (h : m.WF) {k : α} : (m.containsThenInsert k).1 = m.contains k :=
HashMap.Raw.containsThenInsertIfNew_fst h.out

View File

@@ -21,7 +21,7 @@ namespace Literal
/--
Flip the polarity of `l`.
-/
def negate (l : Literal α) : Literal α := (l.1, not l.2)
def negate (l : Literal α) : Literal α := (l.1, !l.2)
end Literal

View File

@@ -37,15 +37,11 @@ theorem denote_mkFullAdderOut (assign : α → Bool) (aig : AIG α) (input : Ful
theorem denote_mkFullAdderCarry (assign : α Bool) (aig : AIG α) (input : FullAdderInput aig) :
mkFullAdderCarry aig input, assign
=
or
(and
(xor
((xor
aig, input.lhs, assign
aig, input.rhs, assign)
aig, input.cin, assign)
(and
aig, input.lhs, assign
aig, input.rhs, assign)
aig, input.rhs, assign) &&
aig, input.cin, assign ||
aig, input.lhs, assign && aig, input.rhs, assign)
:= by
simp only [mkFullAdderCarry, Ref.cast_eq, Int.reduceNeg, denote_mkOrCached,
LawfulOperator.denote_input_entry, denote_mkAndCached, denote_projected_entry',

View File

@@ -33,7 +33,7 @@ def toString : Gate → String
def eval : Gate Bool Bool Bool
| and => (· && ·)
| or => (· || ·)
| xor => _root_.xor
| xor => Bool.xor
| beq => (· == ·)
| imp => (!· || ·)

View File

@@ -21,12 +21,11 @@ theorem sat_negate_iff_not_sat {p : α → Bool} {l : Literal α} : p ⊨ Litera
simp only [Literal.negate, sat_iff]
constructor
· intro h pl
rw [sat_iff, h, not.eq_def] at pl
split at pl <;> simp_all
rw [sat_iff, h] at pl
simp at pl
· intro h
rw [sat_iff] at h
rw [not.eq_def]
split <;> simp_all
cases h : p l.fst <;> simp_all
theorem unsat_of_limplies_complement [Entails α t] (x : t) (l : Literal α) :
Limplies α x l Limplies α x (Literal.negate l) Unsatisfiable α x := by

View File

@@ -168,7 +168,7 @@ Attempts to add the literal `(idx, b)` to clause `c`. Returns none if doing so w
tautology.
-/
def insert (c : DefaultClause n) (l : Literal (PosFin n)) : Option (DefaultClause n) :=
if heq1 : c.clause.contains (l.1, not l.2) then
if heq1 : c.clause.contains (l.1, !l.2) then
none -- Adding l would make c a tautology
else if heq2 : c.clause.contains l then
some c
@@ -353,7 +353,7 @@ def reduce_fold_fn (assignments : Array Assignment) (acc : ReduceResult (PosFin
else
.reducedToEmpty
| .neg =>
if not l.2 then
if !l.2 then
.reducedToUnit l
else
.reducedToEmpty
@@ -367,7 +367,7 @@ def reduce_fold_fn (assignments : Array Assignment) (acc : ReduceResult (PosFin
else
.reducedToUnit l'
| .neg =>
if not l.2 then
if !l.2 then
.reducedToNonunit -- Assignment fails to refute both l and l'
else
.reducedToUnit l'

View File

@@ -219,7 +219,7 @@ def performRupAdd {n : Nat} (f : DefaultFormula n) (c : DefaultClause n) (rupHin
let (f, derivedLits, derivedEmpty, encounteredError) := performRupCheck f rupHints
if encounteredError then
(f, false)
else if not derivedEmpty then
else if !derivedEmpty then
(f, false)
else -- derivedEmpty is true and encounteredError is false
let clauses, rupUnits, ratUnits, assignments := f
@@ -284,7 +284,7 @@ def performRatCheck {n : Nat} (f : DefaultFormula n) (negPivot : Literal (PosFin
-- assignments should now be the same as it was before the performRupCheck call
let f := clearRatUnits clauses, rupUnits, ratUnits, assignments
-- f should now be the same as it was before insertRatUnits
if encounteredError || not derivedEmpty then (f, false)
if encounteredError || !derivedEmpty then (f, false)
else (f, true)
| none => (clauses, rupUnits, ratUnits, assignments, false)
@@ -309,7 +309,7 @@ def performRatAdd {n : Nat} (f : DefaultFormula n) (c : DefaultClause n)
if allChecksPassed then performRatCheck f (Literal.negate pivot) ratHint
else (f, false)
let (f, allChecksPassed) := ratHints.foldl fold_fn (f, true)
if not allChecksPassed then (f, false)
if !allChecksPassed then (f, false)
else
match f with
| clauses, rupUnits, ratUnits, assignments =>

View File

@@ -507,8 +507,8 @@ theorem deleteOne_preserves_strongAssignmentsInvariant {n : Nat} (f : DefaultFor
Fin.eta, Array.get_eq_getElem, Array.getElem_eq_toList_getElem, decidableGetElem?] at heq
rw [hidx, hl] at heq
simp only [unit, Option.some.injEq, DefaultClause.mk.injEq, List.cons.injEq, and_true] at heq
simp only [ heq, not] at l_ne_b
split at l_ne_b <;> simp at l_ne_b
simp only [ heq] at l_ne_b
simp at l_ne_b
· next id_ne_idx => simp [id_ne_idx]
· exact hf
· exact Or.inr hf

View File

@@ -57,8 +57,8 @@ theorem entails_of_irrelevant_assignment {n : Nat} {p : (PosFin n) → Bool} {c
· simp [Clause.toList, delete_iff, negl_ne_v, v_in_c_del_l]
· split
· next heq =>
simp only [heq, Literal.negate, not, ne_eq, Prod.mk.injEq, true_and] at negl_ne_v
split at negl_ne_v <;> simp_all
simp only [heq, Literal.negate, ne_eq, Prod.mk.injEq, true_and] at negl_ne_v
simp_all
· next hne =>
exact pv
· exists v
@@ -67,8 +67,8 @@ theorem entails_of_irrelevant_assignment {n : Nat} {p : (PosFin n) → Bool} {c
· simp [Clause.toList, delete_iff, negl_ne_v, v_in_c_del_l]
· split
· next heq =>
simp only [heq, Literal.negate, not, ne_eq, Prod.mk.injEq, true_and] at negl_ne_v
split at negl_ne_v <;> simp_all
simp only [heq, Literal.negate, ne_eq, Prod.mk.injEq, true_and] at negl_ne_v
simp_all
· next hne =>
exact pv

View File

@@ -126,7 +126,7 @@ theorem or_congr (lhs rhs lhs' rhs' : Bool) (h1 : lhs' = lhs) (h2 : rhs' = rhs)
simp[*]
theorem xor_congr (lhs rhs lhs' rhs' : Bool) (h1 : lhs' = lhs) (h2 : rhs' = rhs) :
(xor lhs' rhs') = (xor lhs rhs) := by
(Bool.xor lhs' rhs') = (xor lhs rhs) := by
simp[*]
theorem beq_congr (lhs rhs lhs' rhs' : Bool) (h1 : lhs' = lhs) (h2 : rhs' = rhs) :

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More