Compare commits

..

90 Commits

Author SHA1 Message Date
Leonardo de Moura
b23f4355c0 fix: replace_fn.cpp 2024-07-19 21:11:08 -07:00
Lean stage0 autoupdater
c2117d75a6 chore: update stage0 2024-07-20 03:58:16 +00:00
Leonardo de Moura
3477b0e7f6 fix: for_each_fn.cpp (#4797) 2024-07-20 03:22:56 +00:00
Lean stage0 autoupdater
696f70bb4e chore: update stage0 2024-07-20 02:35:13 +00:00
Leonardo de Moura
726e162527 perf: kernel replace with precise cache (#4796)
Changes:

- We avoid the thread local storage.
- We use a hash map to ensure that cached values are not lost.
- We remove `check_system`. If this becomes an issue in the future we
should precompute the remaining amount of stack space, and use a cheaper
check.
- We add a `Expr.replaceImpl`, and will use it to implement
`Expr.replace` after update-stage0
2024-07-20 02:00:29 +00:00
Leonardo de Moura
de5e07c4d2 perf: find? and findExt? (#4795)
use the kernel implementation.
2024-07-20 01:13:54 +00:00
Lean stage0 autoupdater
327986e6fb chore: update stage0 2024-07-20 00:51:23 +00:00
Leonardo de Moura
6c33b9c57f perf: for_each with precise cache (#4794)
This commit also adds support for `find?` and `findExt?` using kernel
`for_each`.
We need to perform `update-stage0`.
2024-07-20 00:18:55 +00:00
Henrik Böving
d907771fdd feat: theory from LeanSAT (#4742)
Co-authored-by: Kim Morrison <scott.morrison@gmail.com>
2024-07-19 16:21:03 +00:00
Leonardo de Moura
5c3360200e fix: add term elaborator for Lean.Parser.Term.namedPattern (#4792)
closes #4662
2024-07-19 16:14:32 +00:00
Joachim Breitner
204d4839fa refactor: add numFixed to Structural.EqnInfo (#4788) 2024-07-19 10:21:43 +00:00
Joachim Breitner
e32f3e8140 refactor: IndGroupInst.brecOn (#4787)
this logic fits nicely within `IndGroupInst`.

Also makes `isAuxRecursorWithSuffix` recognize `brecOn_<n>`.
2024-07-19 10:20:50 +00:00
Sebastian Ullrich
7d2155943c doc: fix integer division example
Fixes #4785
2024-07-19 10:36:43 +02:00
Lean stage0 autoupdater
78c4d6daff chore: update stage0 2024-07-18 20:38:21 +00:00
Leonardo de Moura
5526ff6320 chore: Simp.Config.implicitDefEqProofs := true by default (#4784)
Motivation: unblock PR #4595
`Simp.Config.implicitDefEqProofs := false` is currently creating too
many issues in Mathlib.
2024-07-18 19:10:18 +00:00
Leonardo de Moura
bfca7ec72a fix: .eq_def theorem generation with messy universes (#4712)
closes #4673
2024-07-18 17:34:23 +00:00
Leonardo de Moura
9208b3585f chore: document replaceUnsafeM issue (#4783) 2024-07-18 16:26:20 +00:00
Leonardo de Moura
a94805ff71 perf: ensure Expr.replaceExpr preserve DAG structure in Exprs (#4779) 2024-07-18 02:24:15 +00:00
Lean stage0 autoupdater
4eb842560c chore: update stage0 2024-07-18 01:19:02 +00:00
Kyle Miller
490d16c80d fix: have elabAsElim check inferred motive for type correctness (#4722)
Declarations with `@[elab_as_elim]` could elaborate as type-incorrect
expressions. Reported by Jireh Loreaux [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/bug.20in.20revert/near/450522157).

(In principle the elabAsElim routine could revert fvars appearing in the
expected type that depend on the discriminants (if the discriminants are
fvars) to increase the likelihood of type correctness, but that's at the
cost of some complexity to both the elaborator and to the user.)
2024-07-17 20:48:03 +00:00
Leonardo de Moura
f60721bfbd feat: add some low level helper APIs (#4778) 2024-07-17 20:12:05 +00:00
Kyle Miller
a5ecdd0a17 feat: improve @[ext] error message when ext_iff generation fails (#4762)
Now it suggests using `@[ext (iff := false)]` to disable generating the
`ext_iff` lemma.

This PR also adjusts error messages and attribute documentation.
Additionally, to simplify the code now the `x` and `y` arguments can't
come in reverse order (this feature was was added in the refactor
#4543).

Closes #4758
2024-07-17 18:26:12 +00:00
Leonardo de Moura
be717f03ef fix: missing assignment validation at closeMainGoal (#4777)
This primitive is used by the `exact` tactic. This issue allowed users
to create loops in the metavariable assignment.

closes #4773
2024-07-17 18:25:02 +00:00
Leonardo de Moura
41b4914836 perf: Replacement.apply (#4776)
Avoid potentially expensive `e.replace` if it is not applicable.
2024-07-17 16:17:47 +00:00
Leonardo de Moura
933445608c chore: simplify shareCommon' (#4775) 2024-07-17 15:32:35 +00:00
Markus Himmel
8e396068e4 doc: mention linearity in hash map docstring (#4771) 2024-07-17 09:26:38 +00:00
Markus Himmel
c1df7564ce fix: resolve instances for HashMap via unification (#4759) 2024-07-17 08:02:22 +00:00
Markus Himmel
ba3565f441 chore: fix BEq argument order in hash map lemmas (#4732)
The previous argument order was a conscious choice, but I had missed
#3056.
2024-07-17 04:25:21 +00:00
Kim Morrison
af03af5037 feat: simprocs for #[1,2,3,4,5][2] (#4765)
None of these were working previously:

```
#check_simp #[1,2,3,4,5][2]  ~> 3
#check_simp #[1,2,3,4,5][2]? ~> some 3
#check_simp #[1,2,3,4,5][7]? ~> none
#check_simp #[][0]? ~> none
#check_simp #[1,2,3,4,5][2]! ~> 3
#check_simp #[1,2,3,4,5][7]! ~> (default : Nat)
#check_simp (#[] : Array Nat)[0]! ~> (default : Nat)
```
2024-07-17 03:05:17 +00:00
Leonardo de Moura
f6666fe266 chore: add missing withTraceNode (#4769)
Motivation: improve `trace.profiler`
2024-07-17 02:32:32 +00:00
Leonardo de Moura
c580684c22 perf: add ShareCommon.shareCommon' (#4767)
A more restrictive but efficient max sharing primitive.

**Motivation:** Some software verification proofs may contain
significant redundancy that can be eliminated using hash-consing (also
known as `shareCommon`). For example, [theorem
`sha512_block_armv8_test_4_sym`](460fe5d74c/Proofs/SHA512/SHA512Sym.lean (L29))
took a few seconds at [`addPreDefinitions`
](1a12f63f74/src/Lean/Elab/PreDefinition/Main.lean (L155))
and one second at `fixLevelParams` on a MacBook Pro (with M1 Pro). The
proof term initially had over 16 million subterms, but the redundancy
was indirectly and inefficiently eliminated using `Core.transform` at
`addPreDefinitions`. I tried to use `shareCommon` method to fix the
performance issue, but it was too inefficient. This PR introduces a new
`shareCommon'` method that, although less flexible (e.g., it uses only a
local cache and hash-consing table), is much more efficient. The new
procedure minimizes the number of RC operations and optimizes the
caching strategy. It is 20 times faster than the old `shareCommon`
procedure for theorem `sha512_block_armv8_test_4_sym`.
2024-07-17 01:33:54 +00:00
Joachim Breitner
1a12f63f74 refactor: move Synax.hasIdent, shake dependencies (#4766)
I noticed that a change to `Lean.PrettyPrinter.Delaborator.Builtins`
rebuilt more modules than I expected, so I moved a definition and
reduced some dependcies.

More reduction would be possible to move const-delaboration out of the
big `Lean.PrettyPrinter`, and import from `Lean.PrettyPrinter`
selectively.
2024-07-16 21:19:26 +00:00
Joachim Breitner
95b8095fa6 feat: PProd syntax (part 3) (#4756)
reworks #4730 based on feedback from @kmill:

 * Uses `×'` for PProd
 * No syntax for MProd for now
 * Angle brackets (without nesting) for the values
2024-07-16 21:06:04 +00:00
Kyle Miller
94cc8eb863 chore: add comment for why anonymous constructor notation isn't flattened during pretty printing (#4764) 2024-07-16 19:04:51 +00:00
Kim Morrison
1cf47bce5a chore: rename TC to Relation.TransGen (#4760)
This is barely used in Lean, and this rename is both more readable, and
consistent with further developments downstream.

See
[zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Relation.2ETransGen.20vs.2E.20TC.20from.20Init.2ECore/near/448941824)
discussion.
2024-07-16 17:06:49 +00:00
Leonardo de Moura
b73fe04710 feat: add Lean.Expr.numObjs (#4754)
Add helper function for computing the number of allocated
sub-expressions in a given expression. Note: Use this function primarily
for diagnosing performance issues.
2024-07-16 15:52:33 +00:00
Leonardo de Moura
f986a2e9ef chore: missing profileitM (#4753)
This PR addresses the absence of the `profileitM` function in two
auxiliary functions. The added `profileitM` instances are particularly
useful for diagnosing performance issues in declarations that contain
many repeated sub-terms.
2024-07-16 15:43:23 +00:00
Markus Himmel
1a9cbc96f1 chore: rename HashMap.remove to HashMap.erase (#4725)
The name `remove` was chosen because it is more popular in mainstream
programming languages, but being consistent with other Lean container
types (including `Lean.HashMap` and `Batteries.HashMap`) is more
important, so let's change the name while we still can.
2024-07-16 08:14:56 +00:00
Sebastian Ullrich
7aec6c9ae7 chore: temporarily remove test broken by #4746 2024-07-16 09:43:26 +02:00
Lean stage0 autoupdater
31de2494fb chore: update stage0 2024-07-15 21:53:05 +00:00
Lean stage0 autoupdater
d679591880 chore: update stage0 2024-07-15 21:29:34 +00:00
Sebastian Ullrich
f167cfba71 chore: exclude more symbols to get below Windows symbol limit 2024-07-15 23:19:04 +02:00
Joachim Breitner
180c6aaa5e feat: PProd and MProd syntax (part 2) (#4730)
the internal constructions for structural and well-founded recursion
use plenty of `PProd` and `MProd`, and reading these, deeply
nested and in prefix notation, is unnecessarily troublesome.

Therefore this introduces notations
```
a ×ₚ b   -- PProd a b
a ×ₘ b   -- MProd a b
()ₚ      -- PUnit.unit
(x,y,z)ₚ -- PProd.mk x (PProd.mk y z)
(x,y,z)ₘ -- MProd.mk x (MProd.mk y z)
```

(This is the post-stage0-part 2.)
2024-07-15 15:40:42 +00:00
Lean stage0 autoupdater
ab0241dac8 chore: update stage0 2024-07-15 15:02:32 +00:00
Joachim Breitner
dc65f03c41 feat: PProd and MProd syntax (part 1) (#4747)
the internal constructions for structural and well-founded recursion
use plenty of `PProd` and `MProd`, and reading these, deeply
nested and in prefix notation, is unnecessarily troublesome.

Therefore this introduces notations
```
a ×ₚ b   -- PProd a b
a ×ₘ b   -- MProd a b
()ₚ      -- PUnit.unit
(x,y,z)ₚ -- PProd.mk x (PProd.mk y z)
(x,y,z)ₘ -- MProd.mk x (MProd.mk y z)
```

(This is part 1, the rest will follow in #4730 after a stage0 update.)
2024-07-15 14:21:11 +00:00
Joachim Breitner
de96b6d8a7 feat: structural recursion over nested datatypes (#4733)
This now works:

```lean
inductive Tree where | node : List Tree → Tree

mutual
def Tree.size : Tree → Nat
  | node ts => list_size ts

def Tree.list_size : List Tree → Nat
  | [] => 0
  | t::ts => t.size + list_size ts
end
```

It is still out of scope to expect to be able to use nested recursion
(e.g. through `List.map` or `List.foldl`) here.

Depends on #4718.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-15 11:49:53 +00:00
Joachim Breitner
3ab2c714ec feat: infer mutual structural recursion (#4718)
the support for mutual structural recursion (new since #4575) is
extended so that Lean tries to infer it even without annotations.

* The error message when termination checking fails looks quite
different now. Maybe a bit better, maybe with more room for
improvements.
* If there are too many combinations (with an arbitrary cut-off) for a
given argument type, it will just give up and ask the user to use
`termination_by structural`.
* It is now legal to specify `termination_by structural` on not
necessarily all functions of a clique; this simply restricts the
combinations of arguments that Lean considers.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-15 09:34:06 +00:00
Joachim Breitner
f99427bd1a test: extend test for #4671 with nice example reported on zulip (#4740)
from
<https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.E2.9C.94.20Doubly-nested.20inductive/near/451204850>

I really like when I can respond to bug report with “will fix in -1
weeks”
2024-07-13 22:22:40 +00:00
Joachim Breitner
1118978cbb refactor: IndGroupInfo and IndGroupInst (#4738)
This adds the types
* `IndGroupInfo`, a variant of `InductiveVal` with information that
   applies to a whole group of mutual inductives and
* `IndGroupInst` which extends `IndGroupInfo` with levels and parameters
   to indicate a instantiation of the group.

One purpose of this abstraction is to make it clear when a fuction
operates on a group as a whole, rather than a specific inductive within
the group.

This is extracted from #4718 and #4733 to reduce PR size and improve
bisectability.
2024-07-13 08:30:09 +00:00
James Sully
4ea8c5ad8d doc: fix misplaced docstring for getThe (#4737) 2024-07-13 08:10:05 +00:00
Mac Malone
a6ae49c3ab feat: lake: cleaner release handling & related touchups (#4735)
Improves a number of elements related to Git checkouts, cloud releases,
and related error handling.

* On error, Lake now prints all top-level logs. Top-level logs are those
produced by Lake outside of the job monitor (e.g., when cloning
dependencies).
* When fetching a remote for a dependency, Lake now forcibly fetches
tags. This prevents potential errors caused by a repository recreating
tags already fetched.
* Tweaked Git error handling to hopefully be more informative.
* The builtin package facets `release`, `optRelease`, `extraDep` are now
caption in the same manner as other facets. Previously, they were
attempting to be too clever.
* `afterReleaseSync` and `afterReleaseAsync` now fetch `optRelease`
rather than `release`.
* Added support for optional jobs, whose failure does not cause the
whole build to failure (and made `optRelease` such a job).

Closes #4302.
2024-07-13 01:10:41 +00:00
Joachim Breitner
2ad6d397f8 refactor: use indVal.numNested or indVal.numTypeFormers where applicable (#4734)
follow-up to #4684
2024-07-12 22:07:25 +00:00
Joachim Breitner
891824bc51 feat: .below and .brecOn for nested inductive (#4658)
We now get `.below` and `.brecOn` definitions for nested inductives.

No surprises in the implementation: the kernel already gives us suitable
`.rec_1` etc. recursors, and our construction follows the structure of
this recursor.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-12 21:26:35 +00:00
Matthew Robert Ballard
f35c562ef8 feat: add #discr_tree_key command and discr_tree_key tactic (#4447)
Adds a command and tactic to print the `Array <| DiscrTree.Key` for
equalities helping the user to debug perceived `simp` failures.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-07-12 15:05:10 +00:00
Joachim Breitner
bcd8517307 feat: Meta.withErasedFVars (#4731)
this idiom shows up multiple times, is non-trivial (in the sense that
the `localInsts` has to be updated, and I am about to use it once more.
Hence time to abstract this out.
2024-07-12 14:58:04 +00:00
Kyle Miller
ce73bbe277 feat: detailed feedback on decide tactic failure (#4674)
When the `decide` tactic fails, it can try to give hints about the
failure:
- It tells you which `Decidable` instances it unfolded, by making use of
the diagnostics feature.
- If it encounters `Eq.rec`, it gives you a hint that one of these
instances was likely defined using tactics.
- If it encounters `Classical.choice`, it hints that you might have
classical instances in scope.
- During this, it tries to process `Decidable.rec`s and matchers to pin
blame on a particular instance that failed to reduce.

This idea comes from discussion with Heather Macbeth [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Decidable.20with.20structures/near/449409870).
2024-07-11 20:08:29 +00:00
Joachim Breitner
f0eab4b7b1 fix: nested structural recursion over reflexive data type (#4728)
this code
```
inductive N where
 | cons : (Nat -> N) -> N

mutual
def f : N -> Nat
 | .cons a => g (a 32) + 1
termination_by structural n => n
def g : N -> Nat
 | .cons a => f (a 42) + 1
termination_by structural  n => n
end
```
would break. When searching for the right `belowDict` we now have to,
evne after instantiating the paramters for a reflexive argument, again
search through a bunch of `PProd`s.

(Instead of searching we could pass down the index, but since we are
searching anyways in this function let's just re-use.)

Fixes: #4726
2024-07-11 15:25:48 +00:00
Kyle Miller
5f70c1ca64 fix: make matcher pretty printer sensitive to pp.explicit (#4724)
Matchers usually have implicit arguments, and even if they don't the
notation hides the name of the matcher function.

Now when hovering over `match` expressions you can see the actual
underlying matcher expression.
2024-07-11 01:49:49 +00:00
Kim Morrison
fce82eba40 feat: further theorems for List.erase (#4723) 2024-07-10 22:19:12 +00:00
grunweg
9d14e4423c chore: fix typo in doc-string (#4719)
Fix a typo "to at" in a doc-string.
2024-07-10 22:03:11 +00:00
Kim Morrison
0c7859a7dd feat: upstream more erase API (#4720)
This should complete leansat's requirements.
2024-07-10 20:26:51 +00:00
Joachim Breitner
c01e003b49 fix: mutual structural recursion: check that datatype parameters agree (#4715)
if will fail otherwise, but with a worse error message, and it's helpful
in later transformation to know that the parameters are the same for the
whole group.
2024-07-10 08:14:57 +00:00
Leonardo de Moura
ce8a130724 fix: deprecated warnings for overloaded symbols (#4713)
closes #4636
2024-07-10 04:06:25 +00:00
Kyle Miller
3c18d151a6 fix: make iff theorem generated by @[ext] preserve inst implicits (#4710)
Previously all arguments from the ext theorem were made implicit, but
now only default and strict implicits are made implicit.
2024-07-10 03:48:39 +00:00
Leonardo de Moura
0f48e926eb fix: decide tactic transparency (#4711)
closes #4644
2024-07-10 01:40:32 +00:00
Leonardo de Moura
850964999e fix: Repr instances for Int and Float (#4709)
closes #4677
2024-07-10 00:10:58 +00:00
Kim Morrison
57b8b32c72 chore: reorganise lemmas on list getters (#4708)
Just reordering lemmas, sorting into subsections.
2024-07-09 22:55:20 +00:00
Kim Morrison
bd2aefee01 feat: simp normal form tests for Pairwise and Nodup (#4707) 2024-07-09 22:26:27 +00:00
Kim Morrison
74dcd6c2a9 feat: lemmas for List.head and List.getLast (#4678) 2024-07-09 22:13:41 +00:00
Kyle Miller
23b893f778 doc: update release checklist for new release notes workflow (#4458)
This makes it reflect how we are writing release notes for 4.9.0,
including how to handle the `releases_drafts` folder and how and when to
update `RELEASES.md`.

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-07-09 21:44:15 +00:00
Kim Morrison
1e02c08111 feat: basic material on List.Pairwise and Nodup (#4706)
Upstreaming of basic material on `List.Pairwise` and `List.Nodup`. More
complete API to follow later, this is just a first approximation of what
leansat will need.
2024-07-09 21:39:08 +00:00
Kim Morrison
0f6a802314 feat: characterisations of List.Sublist (#4704) 2024-07-09 21:34:29 +00:00
Mario Carneiro
be197cd431 fix: prefer original module in const2ModIdx (#4652)
When a definition is redeclared, the original code would clobber the
value of `const2ModIdx` every time, meaning that a constant would be
attributed to a module which occurs later than the modules for constants
referencing this one. Preferring the original module ensures that these
module indexes are dependency-ordered. This originally came up as a bug
in `shake`, which assumes this property, see
[Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/check.20for.20unused.20imports.20doesn't.20stop/near/449139309).
2024-07-09 14:14:39 +00:00
Kim Morrison
f531f4e5db feat: chore upstream List.Sublist and API from Batteries (#4697)
I'll update `list_simp.lean` (simp normal form testing) and add missing
lemmas in follow-up PRs.

This just upstreams the material, and reorders the lemmas to match the
other sections.
2024-07-09 12:57:09 +00:00
Kim Morrison
8229b28cc9 feat: omega doesn't push coercion over multiplication unnecessarily (#4695) 2024-07-09 12:49:31 +00:00
Sebastian Ullrich
582d6e7f71 chore: CI: update download-artifact actions 2024-07-09 10:17:19 +02:00
Mac Malone
4daa29e71d feat: lake: require @ git (#4692)
Adds syntactic sugar specifying a git revision as a dependency version
in a `require` command. For example:

```
require "leanprover-community" / "proofwidgets" @ git "v0.0.39"
```
2024-07-09 02:50:50 +00:00
Kim Morrison
9124426c55 chore: upstream eq_iff_true_of_subsingleton (#4689) 2024-07-08 21:09:33 +00:00
Kyle Miller
cb0755bac0 chore: make use of ext_iff realization now that stage0 is updated (#4694)
This is a followup to #4543. This also adds "go to definition" for
generated lemmas.
2024-07-08 21:05:53 +00:00
Lean stage0 autoupdater
4b32d9b9a1 chore: update stage0 2024-07-08 20:22:24 +00:00
Kyle Miller
7602265923 feat: make @[ext] derive ext_iff theorems from user ext theorems (#4543)
This PR refactors the 'ext' attribute and implements the following
features:
- The 'local' and 'scoped' attribute kinds are now usable.
- The attribute realizes the `ext`/`ext_iff` lemmas when they do not
already exist, rather than always generating them. This is useful in
conjunction with `@[local ext]`.
- Adding `@[ext]` to a user ext lemma now realizes an `ext_iff` lemma as
well; formerly this was only for structures. The name of the generated
`ext_iff` theorem for a user `ext` theorem named `A.B.myext` is
`A.B.myext_iff`. If this process leads to an error, the user can write
`@[ext (iff := false)]` to disable this feature.

Breaking changes:
- Now the "x" and "y" term arguments to the realized `ext` and `ext_iff`
lemmas are implicit.
- Now the realized `ext` and `ext_iff` lemmas are protected.

Bootstrapping notes:
- There are a few `ext_iff` lemmas to address after the next stage0
update.

Closes https://github.com/leanprover/lean4/issues/3643

Suggested by Floris [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.22Missing.20Tactics.22.20list/near/446267660).
2024-07-08 19:37:56 +00:00
Lean stage0 autoupdater
6ba5704e00 chore: update stage0 2024-07-08 21:18:50 +02:00
Joachim Breitner
98ee789990 refactor: InductiveVal.numNested instead of .isNested
right now, in order to find out how many auxilary datatype are in a
mutual group of inductive with nested data type, one has to jump
through hoops like this:

```
private def numNestedInducts (indName : Name) : MetaM Nat := do
  let .inductInfo indVal ← getConstInfo indName | panic! "{indName} is an inductive"
  let .recInfo recVal ← getConstInfo (mkRecName indName) | panic! "{indName} has a recursor"
  return recVal.numMotives - indVal.all.lengt
```

The `InductiveVal` data structure already has `.isNested : Bool`, so it
seems to be a natural extension to beef that up to `.numNested: Nat`.

This touched kernel code.
2024-07-08 21:18:50 +02:00
Kim Morrison
e08a562c48 chore: add step to release checklist (#4693) 2024-07-08 18:58:18 +00:00
Kim Morrison
84c40d9999 chore: improve compatibility of tests/list_simp with Mathlib (#4691)
I'd like to be able to automatically re-test simp normal forms
post-Mathlib. This makes the file compatible with Mathlib.
2024-07-08 17:17:28 +00:00
Kim Morrison
aecebaab74 chore: upstream SMap.foldM (#4690) 2024-07-08 17:11:58 +00:00
Kim Morrison
3b3901b824 chore: forward and backward directions of not_exists (#4688)
These are added in Batteries.
2024-07-08 16:31:04 +00:00
Kim Morrison
811c1e3685 chore: upstream ToExpr FilePath and compile_time_search_path% (#4453) 2024-07-08 15:41:03 +00:00
Kim Morrison
27e85cc947 chore: adjust List.replicate simp lemmas (#4687) 2024-07-08 15:29:19 +00:00
Henrik Böving
9a852595c4 feat: Process.tryWait (#4660)
Reopen of #4659 due to "processing updates" bug.
2024-07-08 15:14:13 +00:00
450 changed files with 6389 additions and 2414 deletions

View File

@@ -470,7 +470,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Release
@@ -500,7 +500,7 @@ jobs:
# needed for tagging
fetch-depth: 0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare Nightly Release

View File

@@ -5,7 +5,8 @@ See below for the checklist for release candidates.
We'll use `v4.6.0` as the intended release version as a running example.
- One week before the planned release, ensure that someone has written the first draft of the release blog post
- One week before the planned release, ensure that (1) someone has written the release notes and (2) someone has written the first draft of the release blog post.
If there is any material in `./releases_drafts/`, then the release notes are not done. (See the section "Writing the release notes".)
- `git checkout releases/v4.6.0`
(This branch should already exist, from the release candidates.)
- `git pull`
@@ -13,11 +14,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
- `set(LEAN_VERSION_IS_RELEASE 1)`
- (both of these should already be in place from the release candidates)
- In `RELEASES.md`, verify that the `v4.6.0` section has been completed during the release candidate cycle.
It should be in bullet point form, with a point for every significant PR,
and may have a paragraph describing each major new language feature.
It should have a "breaking changes" section calling out changes that are specifically likely
to cause problems for downstream users.
- `git tag v4.6.0`
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
- Now wait, while CI runs.
@@ -28,8 +24,9 @@ We'll use `v4.6.0` as the intended release version as a running example.
you may want to start on the release candidate checklist now.
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
- Edit the release notes on Github to select the "Set as the latest release".
- Copy and paste the Github release notes from the previous releases candidate for this version
(e.g. `v4.6.0-rc1`), and quickly sanity check.
- Follow the instructions in creating a release candidate for the "GitHub release notes" step,
now that we have a written `RELEASES.md` section.
Do a quick sanity check.
- Next, we will move a curated list of downstream repos to the latest stable release.
- For each of the repositories listed below:
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
@@ -92,6 +89,10 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Toolchain bump PR including updated Lake manifest
- Create and push the tag
- Merge the tag into `stable`
- The `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Replace the `v4.6.0` section on `master` with the `v4.6.0` section on `releases/v4.6.0`
and commit this to `master`.
- Merge the release announcement PR for the Lean website - it will be deployed automatically
- Finally, make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
@@ -102,7 +103,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
## Optimistic(?) time estimates:
- Initial checks and push the tag: 30 minutes.
- Note that if `RELEASES.md` has discrepancies this could take longer!
- Waiting for the release: 60 minutes.
- Fixing release notes: 10 minutes.
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
@@ -129,29 +129,26 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
```
- In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
- Our current goal is to have written release notes only about major language features or breaking changes,
and to rely on automatically generated release notes for bugfixes and minor changes.
- Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
- Commit your changes to `RELEASES.md`, and push.
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.`
- We will rely on automatically generated release notes for release candidates,
and the written release notes will be used for stable versions only.
It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion.
- In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
- Commit your changes to `src/CMakeLists.txt`, and push.
- `git tag v4.7.0-rc1`
- `git push origin v4.7.0-rc1`
- Ping the FRO Zulip that release notes need to be written. The release notes do not block completing the rest of this checklist.
- Now wait, while CI runs.
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
- This step can take up to an hour.
- Once the release appears at https://github.com/leanprover/lean4/releases/
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/
- Edit the release notes on Github to select the "Set as a pre-release box".
- Copy the section of `RELEASES.md` for this version into the Github release notes.
- Use the title "Changes since v4.6.0 (from RELEASES.md)"
- Then in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
- This will add a list of all the commits since the last stable version.
- If release notes have been written already, copy the section of `RELEASES.md` for this version into the Github release notes
and use the title "Changes since v4.6.0 (from RELEASES.md)".
- Otherwise, in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
This will add a list of all the commits since the last stable version.
- Delete anything already mentioned in the hand-written release notes above.
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
@@ -177,6 +174,9 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- We do this for the same list of repositories as for stable releases, see above.
As above, there are dependencies between these, and so the process above is iterative.
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
It is essential for Mathlib CI that you then create the next `bump/v4.8.0` branch
for the next development cycle.
Set the `lean-toolchain` file on this branch to same `nightly` you used for this release.
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
@@ -187,12 +187,17 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
Please also make sure that whoever is handling social media knows the release is out.
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
- In `RELEASES.md`, update the `v4.7.0` section to say:
"Release candidate, release notes will be copied from branch `releases/v4.7.0` once completed."
Make sure that whoever is preparing the release notes during this cycle knows that it is their job to do so!
- In `RELEASES.md`, update the `v4.8.0` section to say:
"Development in progress".
- In `RELEASES.md`, verify that the old section `v4.6.0` has the full releases notes from the `releases/v4.6.0` branch.
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
```
Release candidate, release notes will be copied from `branch releases/v4.7.0` once completed.
```
and inserts the following section before that section:
```
v4.8.0
----------
Development in progress.
```
- Removes all the entries from the `./releases_drafts/` folder.
## Time estimates:
Slightly longer than the corresponding steps for a stable release.
@@ -226,3 +231,18 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
* It is always okay to merge in the following directions:
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
Please remember to push any merges you make to intermediate steps!
# Writing the release notes
We are currently trying a system where release notes are compiled all at once from someone looking through the commit history.
The exact steps are a work in progress.
Here is the general idea:
* The work is done right on the `releases/v4.6.0` branch sometime after it is created but before the stable release is made.
The release notes for `v4.6.0` will be copied to `master`.
* There can be material for release notes entries in commit messages.
* There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
See `./releases_drafts/README.md` for more information.
* The release notes should be written from a downstream expert user's point of view.
This section will be updated when the next release notes are written (for `v4.10.0`).

View File

@@ -13,7 +13,7 @@ Recall that nonnegative numerals are considered to be a `Nat` if there are no ty
The operator `/` for `Int` implements integer division.
```lean
#eval -10 / 4 -- -2
#eval -10 / 4 -- -3
```
Similar to `Nat`, the internal representation of `Int` is optimized. Small integers are

View File

@@ -1089,15 +1089,18 @@ def InvImage {α : Sort u} {β : Sort v} (r : β → β → Prop) (f : α → β
fun a₁ a₂ => r (f a₁) (f a₂)
/--
The transitive closure `r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `r a z` if and only if there exists a sequence
The transitive closure `TransGen r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `TransGen r a z` if and only if there exists a sequence
`a r b r ... r z` of length at least 1 connecting `a` to `z`.
-/
inductive TC {α : Sort u} (r : α α Prop) : α α Prop where
/-- If `r a b` then `r a b`. This is the base case of the transitive closure. -/
| base : a b, r a b TC r a b
inductive Relation.TransGen {α : Sort u} (r : α α Prop) : α α Prop
/-- If `r a b` then `TransGen r a b`. This is the base case of the transitive closure. -/
| single {a b} : r a b TransGen r a b
/-- The transitive closure is transitive. -/
| trans : a b c, TC r a b TC r b c TC r a c
| tail {a b c} : TransGen r a b r b c TransGen r a c
/-- Deprecated synonym for `Relation.TransGen`. -/
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
/-! # Subtype -/
@@ -1362,6 +1365,9 @@ theorem iff_false_right (ha : ¬a) : (b ↔ a) ↔ ¬b := Iff.comm.trans (iff_fa
theorem of_iff_true (h : a True) : a := h.mpr trivial
theorem iff_true_intro (h : a) : a True := iff_of_true h trivial
theorem eq_iff_true_of_subsingleton [Subsingleton α] (x y : α) : x = y True :=
iff_true_intro (Subsingleton.elim ..)
theorem not_of_iff_false : (p False) ¬p := Iff.mp
theorem iff_false_intro (h : ¬a) : a False := iff_of_false h id

View File

@@ -51,7 +51,7 @@ theorem foldlM_eq_foldlM_data.aux [Monad m]
simp [foldlM_eq_foldlM_data.aux f arr i (j+1) H]
rw (config := {occs := .pos [2]}) [ List.get_drop_eq_drop _ _ _]
rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
theorem foldlM_eq_foldlM_data [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
@@ -141,7 +141,7 @@ where
· rw [ List.get_drop_eq_drop _ i _]
simp only [aux (i + 1), map_eq_pure_bind, data_length, List.foldlM_cons, bind_assoc, pure_bind]
rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
termination_by arr.size - i
decreasing_by decreasing_trivial_pre_omega

View File

@@ -1437,7 +1437,7 @@ theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
@[simp]
theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j)) := by
rcases w with rfl | w
· simp; omega
· simp
· simp only [twoPow, getLsb_shiftLeft, getLsb_ofNat]
by_cases hj : j < i
· simp only [hj, decide_True, Bool.not_true, Bool.and_false, Bool.false_and, Bool.false_eq,

View File

@@ -31,11 +31,9 @@ theorem utf8Size_eq (c : Char) : c.utf8Size = 1 c.utf8Size = 2 c.utf8Siz
rw [Char.ofNat, dif_pos]
rfl
@[ext] theorem ext : {a b : Char} a.val = b.val a = b
@[ext] protected theorem ext : {a b : Char} a.val = b.val a = b
| _,_, _,_, rfl => rfl
theorem ext_iff {x y : Char} : x = y x.val = y.val := congrArg _, Char.ext
end Char
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size

View File

@@ -37,9 +37,7 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (a, h : Fin n) = a := rfl
@[ext] theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem ext_iff {a b : Fin n} : a = b a.1 = b.1 := val_inj.symm
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -47,12 +45,12 @@ theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩
fun h i hi => h i, hi, fun h i, hi => h i hi
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
(a, ha : Fin n) = b, hb a = b := ext_iff
(a, ha : Fin n) = b, hb a = b := Fin.ext_iff
theorem val_mk {m n : Nat} (h : m < n) : (m, h : Fin n).val = m := rfl
theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
a = k, hk (a : Nat) = k := ext_iff
a = k, hk (a : Nat) = k := Fin.ext_iff
theorem mk_val (i : Fin n) : (i, i.isLt : Fin n) = i := Fin.eta ..
@@ -145,7 +143,7 @@ theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) : ∃ j :
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := ext <| by
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@[simp] theorem rev_le_rev {i j : Fin n} : rev i rev j j i := by
@@ -171,12 +169,12 @@ theorem le_last (i : Fin (n + 1)) : i ≤ last n := Nat.le_of_lt_succ i.is_lt
theorem last_pos : (0 : Fin (n + 2)) < last (n + 1) := Nat.succ_pos _
theorem eq_last_of_not_lt {i : Fin (n + 1)} (h : ¬(i : Nat) < n) : i = last n :=
ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
Fin.ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
theorem val_lt_last {i : Fin (n + 1)} : i last n (i : Nat) < n :=
Decidable.not_imp_comm.1 eq_last_of_not_lt
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := ext <| by simp
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := Fin.ext <| by simp
@[simp] theorem rev_zero (n : Nat) : rev 0 = last n := by
rw [ rev_rev (last _), rev_last]
@@ -244,11 +242,11 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt one_pos
@[simp] theorem succ_lt_succ_iff {a b : Fin n} : a.succ < b.succ a < b := Nat.succ_lt_succ_iff
@[simp] theorem succ_inj {a b : Fin n} : a.succ = b.succ a = b := by
refine fun h => ext ?_, congrArg _
refine fun h => Fin.ext ?_, congrArg _
apply Nat.le_antisymm <;> exact succ_le_succ_iff.1 (h Nat.le_refl _)
theorem succ_ne_zero {n} : k : Fin n, Fin.succ k 0
| k, _, heq => Nat.succ_ne_zero k <| ext_iff.1 heq
| k, _, heq => Nat.succ_ne_zero k <| congrArg Fin.val heq
@[simp] theorem succ_zero_eq_one : Fin.succ (0 : Fin (n + 1)) = 1 := rfl
@@ -267,7 +265,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
rw [ succ_zero_eq_one, succ_lt_succ_iff]; exact succ_pos a
@[simp] theorem add_one_lt_iff {n : Nat} {k : Fin (n + 2)} : k + 1 < k k = last _ := by
simp only [lt_def, val_add, val_last, ext_iff]
simp only [lt_def, val_add, val_last, Fin.ext_iff]
let k, hk := k
match Nat.eq_or_lt_of_le (Nat.le_of_lt_succ hk) with
| .inl h => cases h; simp [Nat.succ_pos]
@@ -285,7 +283,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
split <;> simp [*, (Nat.succ_ne_zero _).symm, Nat.ne_of_gt (Nat.lt_succ_self _)]
@[simp] theorem last_le_iff {n : Nat} {k : Fin (n + 1)} : last n k k = last n := by
rw [ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
rw [Fin.ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
@[simp] theorem lt_add_one_iff {n : Nat} {k : Fin (n + 1)} : k < k + 1 k < last n := by
rw [ Decidable.not_iff_not]; simp
@@ -306,10 +304,10 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n m) :
castLE h i, hn = i, Nat.lt_of_lt_of_le hn h := rfl
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [ext_iff]
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [Fin.ext_iff]
@[simp] theorem castLE_succ {m n : Nat} (h : m + 1 n + 1) (i : Fin m) :
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [ext_iff]
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [Fin.ext_iff]
@[simp] theorem castLE_castLE {k m n} (km : k m) (mn : m n) (i : Fin k) :
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
@@ -322,7 +320,7 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h i, hn = i, h hn := rfl
@@ -348,7 +346,7 @@ theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m :=
/-- For rewriting in the reverse direction, see `Fin.cast_castAdd_left`. -/
theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := ext rfl
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := Fin.ext rfl
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
@@ -397,7 +395,7 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
Fin.castSucc a < Fin.castSucc b a < b := .rfl
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [ext_iff]
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [Fin.ext_iff]
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
@@ -409,7 +407,7 @@ theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
simpa [lt_def] using h
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 a = 0 := by simp [ext_iff]
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 a = 0 := by simp [Fin.ext_iff]
theorem castSucc_ne_zero_iff (a : Fin (n + 1)) : castSucc a 0 a 0 :=
not_congr <| castSucc_eq_zero_iff a
@@ -421,7 +419,7 @@ theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
cases n
· exact a.elim0
· simp [ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
@@ -454,7 +452,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
cast h (addNat i m') = addNat i m :=
ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@@ -474,7 +472,7 @@ theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
cast h (natAdd m' i) = natAdd m i :=
ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
Fin.ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
@@ -484,27 +482,27 @@ theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
ext <| (Nat.add_assoc ..).symm
Fin.ext <| (Nat.add_assoc ..).symm
@[simp]
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
ext <| Nat.zero_add _
Fin.ext <| Nat.zero_add _
@[simp]
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := Fin.ext <| Nat.add_comm ..
@[simp]
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := Fin.ext <| Nat.add_comm ..
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSucc (natAdd n i) :=
rfl
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := ext <| by
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
@@ -534,7 +532,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i ≠ 0) (j : Fin
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
Fin.pred i + 1, Nat.add_lt_add_right h 1 (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
i, h := by
simp only [ext_iff, coe_pred, Nat.add_sub_cancel]
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
Fin.pred i + 1, h₁ h₂ = i, Nat.lt_of_succ_lt_succ h₁ := pred_mk_succ i _
@@ -554,14 +552,14 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
{a b : Fin (n + 1)} {ha : a 0} {hb : b 0}, a.pred ha = b.pred hb a = b
| 0, _, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
| i + 1, _, 0, _, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
| i + 1, hi, j + 1, hj, ha, hb => by simp [ext_iff, Nat.succ.injEq]
| i + 1, hi, j + 1, hj, ha, hb => by simp [Fin.ext_iff, Nat.succ.injEq]
@[simp] theorem pred_one {n : Nat} :
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
rw [ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
exact Nat.add_lt_add_right h 1
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@@ -573,10 +571,10 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (castSucc i.succ) (Fin.ne_of_gt (castSucc_pos i.succ_pos)) = castSucc i := rfl
@[simp] theorem addNat_subNat {i : Fin (n + m)} (h : m i) : addNat (subNat m i h) m = i :=
ext <| Nat.sub_add_cancel h
Fin.ext <| Nat.sub_add_cancel h
@[simp] theorem subNat_addNat (i : Fin n) (m : Nat) (h : m addNat i m := le_coe_addNat m i) :
subNat m (addNat i m) h = i := ext <| Nat.add_sub_cancel i m
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n i) :
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [ cast_addNat]; rfl
@@ -810,10 +808,10 @@ theorem coe_mul {n : Nat} : ∀ a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
match n with
| 0 => exact Subsingleton.elim (α := Fin 1) ..
| n+1 => simp [ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
| n+1 => simp [Fin.ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
Fin.ext <| by rw [mul_def, mul_def, Nat.mul_comm]
instance : Std.Commutative (α := Fin n) (· * ·) := Fin.mul_comm
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
@@ -829,9 +827,9 @@ instance : Std.LawfulIdentity (α := Fin (n + 1)) (· * ·) 1 where
left_id := Fin.one_mul
right_id := Fin.mul_one
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [ext_iff, mul_def]
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [Fin.ext_iff, mul_def]
protected theorem zero_mul (k : Fin (n + 1)) : (0 : Fin (n + 1)) * k = 0 := by
simp [ext_iff, mul_def]
simp [Fin.ext_iff, mul_def]
end Fin

View File

@@ -101,13 +101,13 @@ Returns an undefined value if `x` is not finite.
instance : ToString Float where
toString := Float.toString
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
instance : Repr Float where
reprPrec n _ := Float.toString n
reprPrec n prec := if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
instance : ReprAtom Float :=
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
@[extern "sin"] opaque Float.sin : Float Float
@[extern "cos"] opaque Float.cos : Float Float
@[extern "tan"] opaque Float.tan : Float Float

View File

@@ -22,7 +22,7 @@ along with `@[csimp]` lemmas,
In `Init.Data.List.Lemmas` we develop the full API for these functions.
Recall that `length`, `get`, `set`, `fold`, and `concat` have already been defined in `Init.Prelude`.
Recall that `length`, `get`, `set`, `foldl`, and `concat` have already been defined in `Init.Prelude`.
The operations are organized as follow:
* Equality: `beq`, `isEqv`.
@@ -32,8 +32,8 @@ The operations are organized as follow:
* List membership: `isEmpty`, `elem`, `contains`, `mem` (and the `∈` notation),
and decidability for predicates quantifying over membership in a `List`.
* Sublists: `take`, `drop`, `takeWhile`, `dropWhile`, `partition`, `dropLast`,
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`, `rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseP`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
* Logic: `any`, `all`, `or`, and `and`.
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
@@ -866,6 +866,40 @@ def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
/-! ### Subset -/
/--
`l₁ ⊆ l₂` means that every element of `l₁` is also an element of `l₂`, ignoring multiplicity.
-/
protected def Subset (l₁ l₂ : List α) := a : α, a l₁ a l₂
instance : HasSubset (List α) := List.Subset
instance [DecidableEq α] : DecidableRel (Subset : List α List α Prop) :=
fun _ _ => decidableBAll _ _
/-! ### Sublist and isSublist -/
/-- `l₁ <+ l₂`, or `Sublist l₁ l₂`, says that `l₁` is a (non-contiguous) subsequence of `l₂`. -/
inductive Sublist {α} : List α List α Prop
/-- the base case: `[]` is a sublist of `[]` -/
| slnil : Sublist [] []
/-- If `l₁` is a subsequence of `l₂`, then it is also a subsequence of `a :: l₂`. -/
| cons a : Sublist l₁ l₂ Sublist l₁ (a :: l₂)
/-- If `l₁` is a subsequence of `l₂`, then `a :: l₁` is a subsequence of `a :: l₂`. -/
| cons₂ a : Sublist l₁ l₂ Sublist (a :: l₁) (a :: l₂)
@[inherit_doc] scoped infixl:50 " <+ " => Sublist
/-- True if the first list is a potentially non-contiguous sub-sequence of the second list. -/
def isSublist [BEq α] : List α List α Bool
| [], _ => true
| _, [] => false
| l₁@(hd₁::tl₁), hd₂::tl₂ =>
if hd₁ == hd₂
then tl₁.isSublist tl₂
else l₁.isSublist tl₂
/-! ### rotateLeft -/
/--
@@ -908,6 +942,55 @@ def rotateRight (xs : List α) (n : Nat := 1) : List α :=
@[simp] theorem rotateRight_nil : ([] : List α).rotateRight n = [] := rfl
/-! ## Pairwise, Nodup -/
section Pairwise
variable (R : α α Prop)
/--
`Pairwise R l` means that all the elements with earlier indexes are
`R`-related to all the elements with later indexes.
```
Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3
```
For example if `R = (·≠·)` then it asserts `l` has no duplicates,
and if `R = (·<·)` then it asserts that `l` is (strictly) sorted.
-/
inductive Pairwise : List α Prop
/-- All elements of the empty list are vacuously pairwise related. -/
| nil : Pairwise []
/-- `a :: l` is `Pairwise R` if `a` `R`-relates to every element of `l`,
and `l` is `Pairwise R`. -/
| cons : {a : α} {l : List α}, ( a', a' l R a a') Pairwise l Pairwise (a :: l)
attribute [simp] Pairwise.nil
variable {R}
@[simp] theorem pairwise_cons : Pairwise R (a::l) ( a', a' l R a a') Pairwise R l :=
fun | .cons h₁ h₂ => h₁, h₂, fun h₁, h₂ => h₂.cons h₁
instance instDecidablePairwise [DecidableRel R] :
(l : List α) Decidable (Pairwise R l)
| [] => isTrue .nil
| hd :: tl =>
match instDecidablePairwise tl with
| isTrue ht =>
match decidableBAll (R hd) tl with
| isFalse hf => isFalse fun hf' => hf (pairwise_cons.1 hf').1
| isTrue ht' => isTrue <| pairwise_cons.mpr (And.intro ht' ht)
| isFalse hf => isFalse fun | .cons _ ih => hf ih
end Pairwise
/-- `Nodup l` means that `l` has no duplicates, that is, any element appears at most
once in the List. It is defined as `Pairwise (≠)`. -/
def Nodup : List α Prop := Pairwise (· ·)
instance nodupDecidable [DecidableEq α] : l : List α, Decidable (Nodup l) :=
instDecidablePairwise
/-! ## Manipulating elements -/
/-! ### replace -/
@@ -953,6 +1036,11 @@ theorem erase_cons [BEq α] (a b : α) (l : List α) :
(b :: l).erase a = if b == a then l else b :: l.erase a := by
simp only [List.erase]; split <;> simp_all
/-- `eraseP p l` removes the first element of `l` satisfying the predicate `p`. -/
def eraseP (p : α Bool) : List α List α
| [] => []
| a :: l => bif p a then l else a :: eraseP p l
/-! ### eraseIdx -/
/--

View File

@@ -295,6 +295,24 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
· rw [IH] <;> simp_all
· simp
/-- Tail-recursive version of `eraseP`. -/
@[inline] def erasePTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
@[specialize] go : List α Array α List α
| [], _ => l
| a :: l, acc => bif p a then acc.toListAppend l else go l (acc.push a)
@[csimp] theorem eraseP_eq_erasePTR : @eraseP = @erasePTR := by
funext α p l; simp [erasePTR]
let rec go (acc) : xs, l = acc.data ++ xs
erasePTR.go p l xs acc = acc.data ++ xs.eraseP p
| [] => fun h => by simp [erasePTR.go, eraseP, h]
| x::xs => by
simp [erasePTR.go, eraseP]; cases p x <;> simp
· intro h; rw [go _ xs]; {simp}; simp [h]
exact (go #[] _ rfl).symm
/-! ### eraseIdx -/
/-- Tail recursive version of `List.eraseIdx`. -/

File diff suppressed because it is too large Load Diff

View File

@@ -120,6 +120,43 @@ theorem get?_take_eq_if {l : List α} {n m : Nat} :
(l.take n).get? m = if m < n then l.get? m else none := by
simp [getElem?_take_eq_if]
theorem head?_take {l : List α} {n : Nat} :
(l.take n).head? = if n = 0 then none else l.head? := by
simp [head?_eq_getElem?, getElem?_take_eq_if]
split
· rw [if_neg (by omega)]
· rw [if_pos (by omega)]
theorem head_take {l : List α} {n : Nat} (h : l.take n []) :
(l.take n).head h = l.head (by simp_all) := by
apply Option.some_inj.1
rw [ head?_eq_head, head?_eq_head, head?_take, if_neg]
simp_all
theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none else l[n - 1]?.or l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_take_eq_if, length_take]
split
· rw [if_neg (by omega)]
rw [Nat.min_def]
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [ getLast?_eq_getElem?, getElem?_eq_none (by omega)]
simp
· rw [if_pos]
omega
theorem getLast_take {l : List α} (h : l.take n []) :
(l.take n).getLast h = l[n - 1]?.getD (l.getLast (by simp_all)) := by
rw [getLast_eq_getElem, getElem_take']
simp [length_take, Nat.min_def]
simp at h
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [getElem?_eq_none (by omega), getLast_eq_getElem]
simp
@[simp]
theorem take_eq_take :
{l : List α} {m n : Nat}, l.take m = l.take n min m l.length = min n l.length
@@ -245,6 +282,31 @@ theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? :=
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
simp
theorem head?_drop (l : List α) (n : Nat) :
(l.drop n).head? = l[n]? := by
rw [head?_eq_getElem?, getElem?_drop, Nat.add_zero]
theorem head_drop {l : List α} {n : Nat} (h : l.drop n []) :
(l.drop n).head h = l[n]'(by simp_all) := by
have w : n < l.length := length_lt_of_drop_ne_nil h
simpa [head?_eq_head, getElem?_eq_getElem, h, w] using head?_drop l n
theorem getLast?_drop {l : List α} : (l.drop n).getLast? = if l.length n then none else l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_drop]
rw [length_drop]
split
· rw [getElem?_eq_none (by omega)]
· rw [getLast?_eq_getElem?]
congr
omega
theorem getLast_drop {l : List α} (h : l.drop n []) :
(l.drop n).getLast h = l.getLast (ne_nil_of_length_pos (by simp at h; omega)) := by
simp only [ne_eq, drop_eq_nil_iff_le] at h
apply Option.some_inj.1
simp only [ getLast?_eq_getLast, getLast?_drop, ite_eq_right_iff]
omega
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
split <;> rename_i h

View File

@@ -100,6 +100,7 @@ def blt (a b : Nat) : Bool :=
ble a.succ b
attribute [simp] Nat.zero_le
attribute [simp] Nat.not_lt_zero
/-! # Helper "packing" theorems -/
@@ -633,6 +634,10 @@ theorem succ_lt_succ_iff : succ a < succ b ↔ a < b := ⟨lt_of_succ_lt_succ, s
theorem add_one_inj : a + 1 = b + 1 a = b := succ_inj'
theorem ne_add_one (n : Nat) : n n + 1 := fun h => by cases h
theorem add_one_ne (n : Nat) : n + 1 n := fun h => by cases h
theorem add_one_le_add_one_iff : a + 1 b + 1 a b := succ_le_succ_iff
theorem add_one_lt_add_one_iff : a + 1 < b + 1 a < b := succ_lt_succ_iff
@@ -814,6 +819,9 @@ protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
@[simp] protected theorem zero_sub_one : 0 - 1 = 0 := rfl
@[simp] protected theorem add_one_sub_one (n : Nat) : n + 1 - 1 = n := rfl
theorem sub_one_eq_self (n : Nat) : n - 1 = n n = 0 := by cases n <;> simp [ne_add_one]
theorem eq_self_sub_one (n : Nat) : n = n - 1 n = 0 := by cases n <;> simp [add_one_ne]
theorem succ_pred {a : Nat} (h : a 0) : a.pred.succ = a := by
induction a with
| zero => contradiction

View File

@@ -82,7 +82,7 @@ theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by cases x <;> sim
cases a <;> simp
theorem eq_some_iff_get_eq : o = some a h : o.isSome, o.get h = a := by
cases o <;> simp; nofun
cases o <;> simp
theorem eq_some_of_isSome : {o : Option α} (h : o.isSome), o = some (o.get h)
| some _, _ => rfl
@@ -190,6 +190,9 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
theorem mem_map_of_mem (g : α β) (h : a x) : g a Option.map g x := h.symm map_some' ..
@[simp] theorem filter_none (p : α Bool) : none.filter p = none := rfl
theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α β} :
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp

View File

@@ -230,7 +230,7 @@ protected def Int.repr : Int → String
| negSucc m => "-" ++ Nat.repr (succ m)
instance : Repr Int where
reprPrec i _ := i.repr
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
def hexDigitRepr (n : Nat) : String :=
String.singleton <| Nat.digitChar n

View File

@@ -10,58 +10,39 @@ import Init.RCases
namespace Lean
namespace Parser.Attr
/-- Registers an extensionality theorem.
* When `@[ext]` is applied to a structure, it generates `.ext` and `.ext_iff` theorems and registers
them for the `ext` tactic.
/--
The flag `(iff := false)` prevents `ext` from generating an `ext_iff` lemma.
-/
syntax extIff := atomic("(" &"iff" " := " &"false" ")")
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic.
/--
The flag `(flat := false)` causes `ext` to not flatten parents' fields when generating an `ext` lemma.
-/
syntax extFlat := atomic("(" &"flat" " := " &"false" ")")
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the lemma. Higher-priority lemmas are chosen first, and the default is `1000`.
/--
Registers an extensionality theorem.
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic, and it generates an "`ext_iff`" theorem.
The name of the theorem is from adding the suffix `_iff` to the theorem name.
* When `@[ext]` is applied to a structure, it generates an `.ext` theorem and applies the `@[ext]` attribute to it.
The result is an `.ext` and an `.ext_iff` theorem with the `.ext` theorem registered for the `ext` tactic.
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the `ext` lemma.
Higher-priority lemmas are chosen first, and the default is `1000`.
* The flag `@[ext (iff := false)]` disables generating an `ext_iff` theorem.
* The flag `@[ext (flat := false)]` causes generated structure extensionality theorems to show inherited fields based on their representation,
rather than flattening the parents' fields into the lemma's equality hypotheses.
structures in the generated extensionality theorems. -/
syntax (name := ext) "ext" (" (" &"flat" " := " term ")")? (ppSpace prio)? : attr
-/
syntax (name := ext) "ext" (ppSpace extIff)? (ppSpace extFlat)? (ppSpace prio)? : attr
end Parser.Attr
-- TODO: rename this namespace?
-- Remark: `ext` has scoped syntax, Mathlib may depend on the actual namespace name.
namespace Elab.Tactic.Ext
/--
Creates the type of the extensionality theorem for the given structure,
elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
-/
scoped syntax (name := extType) "ext_type% " term:max ppSpace ident : term
/--
Creates the type of the iff-variant of the extensionality theorem for the given structure,
elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
-/
scoped syntax (name := extIffType) "ext_iff_type% " term:max ppSpace ident : term
/--
`declare_ext_theorems_for A` declares the extensionality theorems for the structure `A`.
These theorems state that two expressions with the structure type are equal if their fields are equal.
-/
syntax (name := declareExtTheoremFor) "declare_ext_theorems_for " ("(" &"flat" " := " term ") ")? ident (ppSpace prio)? : command
macro_rules | `(declare_ext_theorems_for $[(flat := $f)]? $struct:ident $(prio)?) => do
let flat := f.getD (mkIdent `true)
let names Macro.resolveGlobalName struct.getId.eraseMacroScopes
let name match names.filter (·.2.isEmpty) with
| [] => Macro.throwError s!"unknown constant {struct.getId}"
| [(name, _)] => pure name
| _ => Macro.throwError s!"ambiguous name {struct.getId}"
let extName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext"
let extIffName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext_iff"
`(@[ext $(prio)?] protected theorem $extName:ident : ext_type% $flat $struct:ident :=
fun {..} {..} => by intros; subst_eqs; rfl
protected theorem $extIffName:ident : ext_iff_type% $flat $struct:ident :=
fun {..} {..} =>
fun h => by cases h; and_intros <;> rfl,
fun _ => by (repeat cases _ _); subst_eqs; rfl)
/--
Applies extensionality lemmas that are registered with the `@[ext]` attribute.
@@ -96,19 +77,8 @@ macro "ext1" xs:(colGt ppSpace rintroPat)* : tactic =>
end Elab.Tactic.Ext
end Lean
attribute [ext] Prod PProd Sigma PSigma
attribute [ext] funext propext Subtype.eq
@[ext] theorem Prod.ext : {x y : Prod α β} x.fst = y.fst x.snd = y.snd x = y
| _,_, _,_, rfl, rfl => rfl
@[ext] theorem PProd.ext : {x y : PProd α β} x.fst = y.fst x.snd = y.snd x = y
| _,_, _,_, rfl, rfl => rfl
@[ext] theorem Sigma.ext : {x y : Sigma β} x.fst = y.fst HEq x.snd y.snd x = y
| _,_, _,_, rfl, .rfl => rfl
@[ext] theorem PSigma.ext : {x y : PSigma β} x.fst = y.fst HEq x.snd y.snd x = y
| _,_, _,_, rfl, .rfl => rfl
@[ext] protected theorem PUnit.ext (x y : PUnit) : x = y := rfl
protected theorem Unit.ext (x y : Unit) : x = y := rfl

View File

@@ -219,13 +219,13 @@ structure Config where
-/
index : Bool := true
/--
When `true` (default: `false`), `simp` will **not** create a proof for a rewriting rule associated
When `true` (default: `true`), `simp` will **not** create a proof for a rewriting rule associated
with an `rfl`-theorem.
Rewriting rules are provided by users by annotating theorems with the attribute `@[simp]`.
If the proof of the theorem is just `rfl` (reflexivity), and `implicitDefEqProofs := true`, `simp`
will **not** create a proof term which is an application of the annotated theorem.
-/
implicitDefEqProofs : Bool := false
implicitDefEqProofs : Bool := true
deriving Inhabited, BEq
-- Configuration object for `simp_all`

View File

@@ -267,6 +267,7 @@ syntax (name := rawNatLit) "nat_lit " num : term
@[inherit_doc] infixr:90 "" => Function.comp
@[inherit_doc] infixr:35 " × " => Prod
@[inherit_doc] infixr:35 " ×' " => PProd
@[inherit_doc] infix:50 " " => Dvd.dvd
@[inherit_doc] infixl:55 " ||| " => HOr.hOr
@@ -703,6 +704,28 @@ syntax (name := checkSimp) "#check_simp " term "~>" term : command
-/
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
/--
`#discr_tree_key t` prints the discrimination tree keys for a term `t` (or, if it is a single identifier, the type of that constant).
It uses the default configuration for generating keys.
For example,
```
#discr_tree_key (∀ {a n : Nat}, bar a (OfNat.ofNat n))
-- bar _ (@OfNat.ofNat Nat _ _)
#discr_tree_simp_key Nat.add_assoc
-- @HAdd.hAdd Nat Nat Nat _ (@HAdd.hAdd Nat Nat Nat _ _ _) _
```
`#discr_tree_simp_key` is similar to `#discr_tree_key`, but treats the underlying type
as one of a simp lemma, i.e. transforms it into an equality and produces the key of the
left-hand side.
-/
syntax (name := discrTreeKeyCmd) "#discr_tree_key " term : command
@[inherit_doc discrTreeKeyCmd]
syntax (name := discrTreeSimpKeyCmd) "#discr_tree_simp_key" term : command
/--
The `seal foo` command ensures that the definition of `foo` is sealed, meaning it is marked as `[irreducible]`.
This command is particularly useful in contexts where you want to prevent the reduction of `foo` in proofs.

View File

@@ -38,6 +38,10 @@ theorem ext {a b : LinearCombo} (w₁ : a.const = b.const) (w₂ : a.coeffs = b.
subst w₁; subst w₂
congr
/-- Check if a linear combination is an atom, i.e. the constant term is zero and there is exactly one nonzero coefficient, which is one. -/
def isAtom (a : LinearCombo) : Bool :=
a.const == 0 && (a.coeffs.filter (· == 1)).length == 1 && a.coeffs.all fun c => c == 0 || c == 1
/--
Evaluate a linear combination `⟨r, [c_1, …, c_k]⟩` at values `[v_1, …, v_k]` to obtain
`r + (c_1 * x_1 + (c_2 * x_2 + ... (c_k * x_k + 0))))`.

View File

@@ -488,9 +488,9 @@ attribute [unbox] Prod
/--
Similar to `Prod`, but `α` and `β` can be propositions.
You can use `α ×' β` as notation for `PProd α β`.
We use this type internally to automatically generate the `brecOn` recursor.
-/
@[pp_using_anonymous_constructor]
structure PProd (α : Sort u) (β : Sort v) where
/-- The first projection out of a pair. if `p : PProd α β` then `p.1 : α`. -/
fst : α
@@ -3172,8 +3172,8 @@ class MonadStateOf (σ : semiOutParam (Type u)) (m : Type u → Type v) where
export MonadStateOf (set)
/--
Like `withReader`, but with `ρ` explicit. This is useful if a monad supports
`MonadWithReaderOf` for multiple different types `ρ`.
Like `get`, but with `σ` explicit. This is useful if a monad supports
`MonadStateOf` for multiple different types `σ`.
-/
abbrev getThe (σ : Type u) {m : Type u Type v} [MonadStateOf σ m] : m σ :=
MonadStateOf.get

View File

@@ -253,6 +253,9 @@ end forall_congr
@[simp] theorem not_exists : (¬ x, p x) x, ¬p x := exists_imp
theorem forall_not_of_not_exists (h : ¬ x, p x) : x, ¬p x := not_exists.mp h
theorem not_exists_of_forall_not (h : x, ¬p x) : ¬ x, p x := not_exists.mpr h
theorem forall_and : ( x, p x q x) ( x, p x) ( x, q x) :=
fun h => fun x => (h x).1, fun x => (h x).2, fun h₁, h₂ x => h₁ x, h₂ x
@@ -292,6 +295,8 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
@[simp] theorem exists_eq_left' : ( a, a' = a p a) p a' := by simp [@eq_comm _ a']
@[simp] theorem exists_eq_right' : ( a, p a a' = a) p a' := by simp [@eq_comm _ a']
@[simp] theorem forall_eq_or_imp : ( a, a = a' q a p a) p a' a, q a p a := by
simp only [or_imp, forall_and, forall_eq]
@@ -304,6 +309,11 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
@[simp] theorem exists_eq_right_right' : ( (a : α), p a q a a' = a) p a' q a' := by
simp [@eq_comm _ a']
@[simp] theorem exists_or_eq_left (y : α) (p : α Prop) : x : α, x = y p x := y, .inl rfl
@[simp] theorem exists_or_eq_right (y : α) (p : α Prop) : x : α, p x x = y := y, .inr rfl
@[simp] theorem exists_or_eq_left' (y : α) (p : α Prop) : x : α, y = x p x := y, .inl rfl
@[simp] theorem exists_or_eq_right' (y : α) (p : α Prop) : x : α, p x y = x := y, .inr rfl
@[simp] theorem exists_prop : ( _h : a, b) a b :=
fun hp, hq => hp, hq, fun hp, hq => hp, hq

View File

@@ -102,3 +102,11 @@ instance ShareCommonT.monadShareCommon [Monad m] : MonadShareCommon (ShareCommon
@[inline] def ShareCommonT.run [Monad m] (x : ShareCommonT σ m α) : m α := x.run' default
@[inline] def ShareCommonM.run (x : ShareCommonM σ α) : α := ShareCommonT.run x
/--
A more restrictive but efficient max sharing primitive.
Remark: it optimizes the number of RC operations, and the strategy for caching results.
-/
@[extern "lean_sharecommon_quick"]
def ShareCommon.shareCommon' (a : α) : α := a

View File

@@ -129,6 +129,7 @@ instance : Std.LawfulIdentity Or False where
@[simp] theorem iff_false (p : Prop) : (p False) = ¬p := propext (·.1), (·, False.elim)
@[simp] theorem false_iff (p : Prop) : (False p) = ¬p := propext (·.2), (False.elim, ·)
@[simp] theorem false_implies (p : Prop) : (False p) = True := eq_true False.elim
@[simp] theorem forall_false (p : False Prop) : ( h : False, p h) = True := eq_true (False.elim ·)
@[simp] theorem implies_true (α : Sort u) : (α True) = True := eq_true fun _ => trivial
@[simp] theorem true_implies (p : Prop) : (True p) = p := propext (· trivial), (fun _ => ·)
@[simp] theorem not_false_eq_true : (¬ False) = True := eq_true False.elim

View File

@@ -712,8 +712,17 @@ structure Child (cfg : StdioConfig) where
@[extern "lean_io_process_spawn"] opaque spawn (args : SpawnArgs) : IO (Child args.toStdioConfig)
/--
Block until the child process has exited and return its exit code.
-/
@[extern "lean_io_process_child_wait"] opaque Child.wait {cfg : @& StdioConfig} : @& Child cfg IO UInt32
/--
Check whether the child has exited yet. If it hasn't return none, otherwise its exit code.
-/
@[extern "lean_io_process_child_try_wait"] opaque Child.tryWait {cfg : @& StdioConfig} : @& Child cfg
IO (Option UInt32)
/-- Terminates the child process using the SIGTERM signal or a platform analogue.
If the process was started using `SpawnArgs.setsid`, terminates the entire process group instead. -/
@[extern "lean_io_process_child_kill"] opaque Child.kill {cfg : @& StdioConfig} : @& Child cfg IO Unit

View File

@@ -45,6 +45,13 @@ def dbgSleep {α : Type u} (ms : UInt32) (f : Unit → α) : α := f ()
@[extern "lean_ptr_addr"]
unsafe opaque ptrAddrUnsafe {α : Type u} (a : @& α) : USize
/--
Returns `true` if `a` is an exclusive object.
We say an object is exclusive if it is single-threaded and its reference counter is 1.
-/
@[extern "lean_is_exclusive_obj"]
unsafe opaque isExclusiveUnsafe {α : Type u} (a : @& α) : Bool
set_option linter.unusedVariables.funArgs false in
@[inline] unsafe def withPtrAddrUnsafe {α : Type u} {β : Type v} (a : α) (k : USize β) (h : u₁ u₂, k u₁ = k u₂) : β :=
k (ptrAddrUnsafe a)

View File

@@ -148,22 +148,26 @@ end InvImage
wf := InvImage.wf f h.wf
-- The transitive closure of a well-founded relation is well-founded
namespace TC
variable {α : Sort u} {r : α α Prop}
open Relation
theorem accessible {z : α} (ac : Acc r z) : Acc (TC r) z := by
induction ac with
| intro x acx ih =>
apply Acc.intro x
intro y rel
induction rel with
| base a b rab => exact ih a rab
| trans a b c rab _ _ ih₂ => apply Acc.inv (ih₂ acx ih) rab
theorem Acc.transGen (h : Acc r a) : Acc (TransGen r) a := by
induction h with
| intro x _ H =>
refine Acc.intro x fun y hy ?_
cases hy with
| single hyx =>
exact H y hyx
| tail hyz hzx =>
exact (H _ hzx).inv hyz
theorem wf (h : WellFounded r) : WellFounded (TC r) :=
fun a => accessible (apply h a)
end TC
theorem acc_transGen_iff : Acc (TransGen r) a Acc r a :=
Subrelation.accessible TransGen.single, Acc.transGen
theorem WellFounded.transGen (h : WellFounded r) : WellFounded (TransGen r) :=
fun a (h.apply a).transGen
@[deprecated Acc.transGen (since := "2024-07-16")] abbrev TC.accessible := @Acc.transGen
@[deprecated WellFounded.transGen (since := "2024-07-16")] abbrev TC.wf := @WellFounded.transGen
namespace Nat
-- less-than is well-founded

View File

@@ -37,7 +37,7 @@ def isAuxRecursor (env : Environment) (declName : Name) : Bool :=
def isAuxRecursorWithSuffix (env : Environment) (declName : Name) (suffix : String) : Bool :=
match declName with
| .str _ s => s == suffix && isAuxRecursor env declName
| .str _ s => (s == suffix || s.startsWith s!"{suffix}_") && isAuxRecursor env declName
| _ => false
def isCasesOnRecursor (env : Environment) (declName : Name) : Bool :=

View File

@@ -94,7 +94,7 @@ def emitCInitName (n : Name) : M Unit :=
def shouldExport (n : Name) : Bool :=
-- HACK: exclude symbols very unlikely to be used by the interpreter or other consumers of
-- libleanshared to avoid Windows symbol limit
!(`Lean.Compiler.LCNF).isPrefixOf n
!(`Lean.Compiler.LCNF).isPrefixOf n && !(`Lean.IR).isPrefixOf n && !(`Lean.Server).isPrefixOf n
def emitFnDeclAux (decl : Decl) (cppBaseName : String) (isExternal : Bool) : M Unit := do
let ps := decl.params

View File

@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Compiler.Options
import Lean.Compiler.ExternAttr
import Lean.Compiler.LCNF.PassManager
import Lean.Compiler.LCNF.Passes
import Lean.Compiler.LCNF.PrettyPrinter

View File

@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.PrettyPrinter
import Lean.PrettyPrinter.Delaborator.Options
import Lean.Compiler.LCNF.CompilerM
import Lean.Compiler.LCNF.Internalize

View File

@@ -80,6 +80,10 @@ protected def max : RBNode α β → Option (Sigma (fun k => β k))
def singleton (k : α) (v : β k) : RBNode α β :=
node red leaf k v leaf
def isSingleton : RBNode α β Bool
| node _ leaf _ _ leaf => true
| _ => false
-- the first half of Okasaki's `balance`, concerning red-red sequences in the left child
@[inline] def balance1 : RBNode α β (a : α) β a RBNode α β RBNode α β
| node red (node red a kx vx b) ky vy c, kz, vz, d
@@ -269,6 +273,9 @@ variable {α : Type u} {β : Type v} {σ : Type w} {cmp : αα → Ordering
def depth (f : Nat Nat Nat) (t : RBMap α β cmp) : Nat :=
t.val.depth f
def isSingleton (t : RBMap α β cmp) : Bool :=
t.val.isSingleton
@[inline] def fold (f : σ α β σ) : (init : σ) RBMap α β cmp σ
| b, t, _ => t.fold f b

View File

@@ -87,6 +87,11 @@ def switch (m : SMap α β) : SMap α β :=
@[inline] def foldStage2 {σ : Type w} (f : σ α β σ) (s : σ) (m : SMap α β) : σ :=
m.map₂.foldl f s
/-- Monadic fold over a staged map. -/
def foldM {m : Type w Type w} [Monad m]
(f : σ α β m σ) (init : σ) (map : SMap α β) : m σ := do
map.map₂.foldlM f ( map.map₁.foldM f init)
def fold {σ : Type w} (f : σ α β σ) (init : σ) (m : SMap α β) : σ :=
m.map₂.foldl f $ m.map₁.fold f init

View File

@@ -239,6 +239,10 @@ structure InductiveVal extends ConstantVal where
all : List Name
/-- List of the names of the constructors for this inductive datatype. -/
ctors : List Name
/-- Number of auxillary data types produced from nested occurrences.
An inductive definition `T` is nested when there is a constructor with an argument `x : F T`,
where `F : Type → Type` is some suitably behaved (ie strictly positive) function (Eg `Array T`, `List T`, `T × T`, ...). -/
numNested : Nat
/-- `true` when recursive (that is, the inductive type appears as an argument in a constructor). -/
isRec : Bool
/-- Whether the definition is flagged as unsafe. -/
@@ -257,14 +261,12 @@ structure InductiveVal extends ConstantVal where
Section 2.2, Definition 3
-/
isReflexive : Bool
/-- An inductive definition `T` is nested when there is a constructor with an argument `x : F T`,
where `F : Type → Type` is some suitably behaved (ie strictly positive) function (Eg `Array T`, `List T`, `T × T`, ...). -/
isNested : Bool
deriving Inhabited
@[export lean_mk_inductive_val]
def mkInductiveValEx (name : Name) (levelParams : List Name) (type : Expr) (numParams numIndices : Nat)
(all ctors : List Name) (isRec isUnsafe isReflexive isNested : Bool) : InductiveVal := {
(all ctors : List Name) (numNested : Nat) (isRec isUnsafe isReflexive : Bool) : InductiveVal := {
name := name
levelParams := levelParams
type := type
@@ -272,18 +274,19 @@ def mkInductiveValEx (name : Name) (levelParams : List Name) (type : Expr) (numP
numIndices := numIndices
all := all
ctors := ctors
numNested := numNested
isRec := isRec
isUnsafe := isUnsafe
isReflexive := isReflexive
isNested := isNested
}
@[export lean_inductive_val_is_rec] def InductiveVal.isRecEx (v : InductiveVal) : Bool := v.isRec
@[export lean_inductive_val_is_unsafe] def InductiveVal.isUnsafeEx (v : InductiveVal) : Bool := v.isUnsafe
@[export lean_inductive_val_is_reflexive] def InductiveVal.isReflexiveEx (v : InductiveVal) : Bool := v.isReflexive
@[export lean_inductive_val_is_nested] def InductiveVal.isNestedEx (v : InductiveVal) : Bool := v.isNested
def InductiveVal.numCtors (v : InductiveVal) : Nat := v.ctors.length
def InductiveVal.isNested (v : InductiveVal) : Bool := v.numNested > 0
def InductiveVal.numTypeFormers (v : InductiveVal) : Nat := v.all.length + v.numNested
structure ConstructorVal extends ConstantVal where
/-- Inductive type this constructor is a member of -/

View File

@@ -742,7 +742,10 @@ def mkMotive (discrs : Array Expr) (expectedType : Expr): MetaM Expr := do
let motiveBody kabstract motive discr
/- We use `transform (usedLetOnly := true)` to eliminate unnecessary let-expressions. -/
let discrType transform (usedLetOnly := true) ( instantiateMVars ( inferType discr))
return Lean.mkLambda ( mkFreshBinderName) BinderInfo.default discrType motiveBody
let motive := Lean.mkLambda ( mkFreshBinderName) BinderInfo.default discrType motiveBody
unless ( isTypeCorrect motive) do
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motive}"
return motive
/-- If the eliminator is over-applied, we "revert" the extra arguments. -/
def revertArgs (args : List Arg) (f : Expr) (expectedType : Expr) : TermElabM (Expr × Expr) :=
@@ -1292,6 +1295,7 @@ private partial def elabAppFnId (fIdent : Syntax) (fExplicitUnivs : List Level)
funLVals.foldlM (init := acc) fun acc (f, fIdent, fields) => do
let lvals' := toLVals fields (first := true)
let s observing do
checkDeprecated fIdent f
let f addTermInfo fIdent f expectedType?
let e elabAppLVals f (lvals' ++ lvals) namedArgs args expectedType? explicit ellipsis
if overloaded then ensureHasType expectedType? e else return e

View File

@@ -11,7 +11,6 @@ import Lean.Elab.Eval
import Lean.Elab.Command
import Lean.Elab.Open
import Lean.Elab.SetOption
import Lean.PrettyPrinter
namespace Lean.Elab.Command

View File

@@ -220,6 +220,31 @@ partial def mkPairs (elems : Array Term) : MacroM Term :=
pure acc
loop (elems.size - 1) elems.back
/-- Return syntax `PProd.mk elems[0] (PProd.mk elems[1] ... (PProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
partial def mkPPairs (elems : Array Term) : MacroM Term :=
let rec loop (i : Nat) (acc : Term) := do
if i > 0 then
let i := i - 1
let elem := elems[i]!
let acc `(PProd.mk $elem $acc)
loop i acc
else
pure acc
loop (elems.size - 1) elems.back
/-- Return syntax `MProd.mk elems[0] (MProd.mk elems[1] ... (MProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
partial def mkMPairs (elems : Array Term) : MacroM Term :=
let rec loop (i : Nat) (acc : Term) := do
if i > 0 then
let i := i - 1
let elem := elems[i]!
let acc `(MProd.mk $elem $acc)
loop i acc
else
pure acc
loop (elems.size - 1) elems.back
open Parser in
partial def hasCDot : Syntax Bool
| Syntax.node _ k args =>

View File

@@ -316,9 +316,7 @@ private def mkSilentAnnotationIfHole (e : Expr) : TermElabM Expr := do
return false
return true
if canClear then
let lctx := ( getLCtx).erase fvarId
let localInsts := ( getLocalInstances).filter (·.fvar.fvarId! != fvarId)
withLCtx lctx localInsts do elabTerm body expectedType?
withErasedFVars #[fvarId] do elabTerm body expectedType?
else
elabTerm body expectedType?
@@ -364,4 +362,7 @@ private opaque evalFilePath (stx : Syntax) : TermElabM System.FilePath
mkStrLit <$> IO.FS.readFile path
| _, _ => throwUnsupportedSyntax
@[builtin_term_elab Lean.Parser.Term.namedPattern] def elabNamedPatternErr : TermElab := fun stx _ =>
throwError "`<identifier>@<term>` is a named pattern and can only be used in pattern matching contexts{indentD stx}"
end Lean.Elab.Term

View File

@@ -672,8 +672,7 @@ partial def main (patternVarDecls : Array PatternVarDecl) (ps : Array Expr) (mat
throwError "invalid patterns, `{mkFVar explicit}` is an explicit pattern variable, but it only occurs in positions that are inaccessible to pattern matching{indentD (MessageData.joinSep (ps.toList.map (MessageData.ofExpr .)) m!"\n\n")}"
let packed pack patternVars ps matchType
trace[Elab.match] "packed: {packed}"
let lctx := explicitPatternVars.foldl (init := ( getLCtx)) fun lctx d => lctx.erase d
withTheReader Meta.Context (fun ctx => { ctx with lctx := lctx }) do
withErasedFVars explicitPatternVars do
check packed
unpack packed fun patternVars patterns matchType => do
let localDecls patternVars.mapM fun x => x.fvarId!.getDecl

View File

@@ -728,12 +728,26 @@ def insertReplacementForLetRecs (r : Replacement) (letRecClosures : List LetRecC
letRecClosures.foldl (init := r) fun r c =>
r.insert c.toLift.fvarId c.closed
def isApplicable (r : Replacement) (e : Expr) : Bool :=
Option.isSome <| e.findExt? fun e =>
if e.hasFVar then
match e with
| .fvar fvarId => if r.contains fvarId then .found else .done
| _ => .visit
else
.done
def Replacement.apply (r : Replacement) (e : Expr) : Expr :=
e.replace fun e => match e with
| .fvar fvarId => match r.find? fvarId with
| some c => some c
| _ => none
| _ => none
-- Remark: if `r` is not a singlenton, then declaration is using `mutual` or `let rec`,
-- and there is a big chance `isApplicable r e` is true.
if r.isSingleton && !isApplicable r e then
e
else
e.replace fun e => match e with
| .fvar fvarId => match r.find? fvarId with
| some c => some c
| _ => none
| _ => none
def pushMain (preDefs : Array PreDefinition) (sectionVars : Array Expr) (mainHeaders : Array DefViewElabHeader) (mainVals : Array Expr)
: TermElabM (Array PreDefinition) :=
@@ -923,6 +937,7 @@ where
trace[Elab.definition] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
let preDefs withLevelNames allUserLevelNames <| levelMVarToParamPreDecls preDefs
let preDefs instantiateMVarsAtPreDecls preDefs
let preDefs shareCommonPreDefs preDefs
let preDefs fixLevelParams preDefs scopeLevelNames allUserLevelNames
for preDef in preDefs do
trace[Elab.definition] "after eraseAuxDiscr, {preDef.declName} : {preDef.type} :=\n{preDef.value}"

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.ShareCommon
import Lean.Compiler.NoncomputableAttr
import Lean.Util.CollectLevelParams
import Lean.Meta.AbstractNestedProofs
@@ -53,18 +54,20 @@ private def getLevelParamsPreDecls (preDefs : Array PreDefinition) (scopeLevelNa
| Except.ok levelParams => pure levelParams
def fixLevelParams (preDefs : Array PreDefinition) (scopeLevelNames allUserLevelNames : List Name) : TermElabM (Array PreDefinition) := do
-- We used to use `shareCommon` here, but is was a bottleneck
let levelParams getLevelParamsPreDecls preDefs scopeLevelNames allUserLevelNames
let us := levelParams.map mkLevelParam
let fixExpr (e : Expr) : Expr :=
e.replace fun c => match c with
| Expr.const declName _ => if preDefs.any fun preDef => preDef.declName == declName then some $ Lean.mkConst declName us else none
| _ => none
return preDefs.map fun preDef =>
{ preDef with
type := fixExpr preDef.type,
value := fixExpr preDef.value,
levelParams := levelParams }
profileitM Exception s!"fix level params" ( getOptions) do
withTraceNode `Elab.def.fixLevelParams (fun _ => return m!"fix level params") do
-- We used to use `shareCommon` here, but is was a bottleneck
let levelParams getLevelParamsPreDecls preDefs scopeLevelNames allUserLevelNames
let us := levelParams.map mkLevelParam
let fixExpr (e : Expr) : Expr :=
e.replace fun c => match c with
| Expr.const declName _ => if preDefs.any fun preDef => preDef.declName == declName then some $ Lean.mkConst declName us else none
| _ => none
return preDefs.map fun preDef =>
{ preDef with
type := fixExpr preDef.type,
value := fixExpr preDef.value,
levelParams := levelParams }
def applyAttributesOf (preDefs : Array PreDefinition) (applicationTime : AttributeApplicationTime) : TermElabM Unit := do
for preDef in preDefs do
@@ -210,4 +213,17 @@ def checkCodomainsLevel (preDefs : Array PreDefinition) : MetaM Unit := do
m!"for `{preDefs[0]!.declName}` is{indentExpr type₀} : {← inferType type₀}\n" ++
m!"and for `{preDefs[i]!.declName}` is{indentExpr typeᵢ} : {← inferType typeᵢ}"
def shareCommonPreDefs (preDefs : Array PreDefinition) : CoreM (Array PreDefinition) := do
profileitM Exception "share common exprs" ( getOptions) do
withTraceNode `Elab.def.maxSharing (fun _ => return m!"share common exprs") do
let mut es := #[]
for preDef in preDefs do
es := es.push preDef.type |>.push preDef.value
es := ShareCommon.shareCommon' es
let mut result := #[]
for h : i in [:preDefs.size] do
let preDef := preDefs[i]
result := result.push { preDef with type := es[2*i]!, value := es[2*i+1]! }
return result
end Lean.Elab

View File

@@ -333,7 +333,7 @@ def tryContradiction (mvarId : MVarId) : MetaM Bool := do
partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
let some eqs getEqnsFor? declName | throwError "failed to generate equations for '{declName}'"
let tryEqns (mvarId : MVarId) : MetaM Bool :=
eqs.anyM fun eq => commitWhen do
eqs.anyM fun eq => commitWhen do checkpointDefEq (mayPostpone := false) do
try
let subgoals mvarId.apply ( mkConstWithFreshMVarLevels eq)
subgoals.allM fun subgoal => do

View File

@@ -111,7 +111,7 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
preDefWith.termination.terminationBy? matches some {structural := true, ..}
for preDef in preDefs do
if let .some termBy := preDef.termination.terminationBy? then
if !preDefsWithout.isEmpty then
if !structural && !preDefsWithout.isEmpty then
let m := MessageData.andList (preDefsWithout.toList.map (m!"{·.declName}"))
let doOrDoes := if preDefsWithout.size = 1 then "does" else "do"
logErrorAt termBy.ref (m!"Incomplete set of `termination_by` annotations:\n"++
@@ -135,13 +135,12 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
/--
Elaborates the `TerminationHint` in the clique to `TerminationArguments`
-/
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Option TerminationArguments) := do
let tas preDefs.mapM fun preDef => do
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Array (Option TerminationArgument)) := do
preDefs.mapM fun preDef => do
let arity lambdaTelescope preDef.value fun xs _ => pure xs.size
let hints := preDef.termination
hints.terminationBy?.mapM
(TerminationArgument.elab preDef.declName preDef.type arity hints.extraParams ·)
return tas.sequenceMap id -- only return something if every function has a hint
def shouldUseStructural (preDefs : Array PreDefinition) : Bool :=
preDefs.any fun preDef =>
@@ -154,68 +153,70 @@ def shouldUseWF (preDefs : Array PreDefinition) : Bool :=
def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLCtx {} {} do
for preDef in preDefs do
trace[Elab.definition.body] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
let preDefs preDefs.mapM ensureNoUnassignedMVarsAtPreDef
let preDefs betaReduceLetRecApps preDefs
let cliques := partitionPreDefs preDefs
for preDefs in cliques do
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"
if preDefs.size == 1 && isNonRecursive preDefs[0]! then
/-
We must erase `recApp` annotations even when `preDef` is not recursive
because it may use another recursive declaration in the same mutual block.
See issue #2321
-/
let preDef eraseRecAppSyntax preDefs[0]!
ensureEqnReservedNamesAvailable preDef.declName
if preDef.modifiers.isNoncomputable then
addNonRec preDef
else
addAndCompileNonRec preDef
preDef.termination.ensureNone "not recursive"
else if preDefs.any (·.modifiers.isUnsafe) then
addAndCompileUnsafe preDefs
preDefs.forM (·.termination.ensureNone "unsafe")
else if preDefs.any (·.modifiers.isPartial) then
profileitM Exception "process pre-definitions" ( getOptions) do
withTraceNode `Elab.def.processPreDef (fun _ => return m!"process pre-definitions") do
for preDef in preDefs do
if preDef.modifiers.isPartial && !( whnfD preDef.type).isForall then
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
addAndCompilePartial preDefs
preDefs.forM (·.termination.ensureNone "partial")
else
ensureFunIndReservedNamesAvailable preDefs
try
checkCodomainsLevel preDefs
checkTerminationByHints preDefs
let termArgs elabTerminationByHints preDefs
if shouldUseStructural preDefs then
structuralRecursion preDefs termArgs
else if shouldUseWF preDefs then
wfRecursion preDefs termArgs
trace[Elab.definition.body] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
let preDefs preDefs.mapM ensureNoUnassignedMVarsAtPreDef
let preDefs betaReduceLetRecApps preDefs
let cliques := partitionPreDefs preDefs
for preDefs in cliques do
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"
if preDefs.size == 1 && isNonRecursive preDefs[0]! then
/-
We must erase `recApp` annotations even when `preDef` is not recursive
because it may use another recursive declaration in the same mutual block.
See issue #2321
-/
let preDef eraseRecAppSyntax preDefs[0]!
ensureEqnReservedNamesAvailable preDef.declName
if preDef.modifiers.isNoncomputable then
addNonRec preDef
else
addAndCompileNonRec preDef
preDef.termination.ensureNone "not recursive"
else if preDefs.any (·.modifiers.isUnsafe) then
addAndCompileUnsafe preDefs
preDefs.forM (·.termination.ensureNone "unsafe")
else if preDefs.any (·.modifiers.isPartial) then
for preDef in preDefs do
if preDef.modifiers.isPartial && !( whnfD preDef.type).isForall then
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
addAndCompilePartial preDefs
preDefs.forM (·.termination.ensureNone "partial")
else
withRef (preDefs[0]!.ref) <| mapError
(orelseMergeErrors
(structuralRecursion preDefs termArgs)
(wfRecursion preDefs termArgs))
(fun msg =>
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
catch ex =>
logException ex
let s saveState
try
if preDefs.all fun preDef => preDef.kind == DefKind.def || preDefs.all fun preDef => preDef.kind == DefKind.abbrev then
-- try to add as partial definition
ensureFunIndReservedNamesAvailable preDefs
try
checkCodomainsLevel preDefs
checkTerminationByHints preDefs
let termArg?s elabTerminationByHints preDefs
if shouldUseStructural preDefs then
structuralRecursion preDefs termArg?s
else if shouldUseWF preDefs then
wfRecursion preDefs termArg?s
else
withRef (preDefs[0]!.ref) <| mapError
(orelseMergeErrors
(structuralRecursion preDefs termArg?s)
(wfRecursion preDefs termArg?s))
(fun msg =>
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
catch ex =>
logException ex
let s saveState
try
addAndCompilePartial preDefs (useSorry := true)
catch _ =>
-- Compilation failed try again just as axiom
s.restore
addAsAxioms preDefs
else if preDefs.all fun preDef => preDef.kind == DefKind.theorem then
addAsAxioms preDefs
catch _ => s.restore
if preDefs.all fun preDef => preDef.kind == DefKind.def || preDefs.all fun preDef => preDef.kind == DefKind.abbrev then
-- try to add as partial definition
try
addAndCompilePartial preDefs (useSorry := true)
catch _ =>
-- Compilation failed try again just as axiom
s.restore
addAsAxioms preDefs
else if preDefs.all fun preDef => preDef.kind == DefKind.theorem then
addAsAxioms preDefs
catch _ => s.restore
builtin_initialize
registerTraceClass `Elab.definition.body

View File

@@ -10,6 +10,7 @@ import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.FunPacker
import Lean.Elab.PreDefinition.Structural.RecArgInfo
namespace Lean.Elab.Structural
open Meta
@@ -17,51 +18,63 @@ open Meta
private def throwToBelowFailed : MetaM α :=
throwError "toBelow failed"
partial def searchPProd (e : Expr) (F : Expr) (k : Expr Expr MetaM α) : MetaM α := do
match ( whnf e) with
| .app (.app (.const `PProd _) d1) d2 =>
(do searchPProd d1 ( mkAppM ``PProd.fst #[F]) k)
<|> (do searchPProd d2 ( mkAppM `PProd.snd #[F]) k)
| .app (.app (.const `And _) d1) d2 =>
(do searchPProd d1 ( mkAppM `And.left #[F]) k)
<|> (do searchPProd d2 ( mkAppM `And.right #[F]) k)
| .const `PUnit _
| .const `True _ => throwToBelowFailed
| _ => k e F
/-- See `toBelow` -/
private partial def toBelowAux (C : Expr) (belowDict : Expr) (arg : Expr) (F : Expr) : MetaM Expr := do
let belowDict whnf belowDict
trace[Elab.definition.structural] "belowDict: {belowDict}, arg: {arg}"
match belowDict with
| .app (.app (.const `PProd _) d1) d2 =>
(do toBelowAux C d1 arg ( mkAppM `PProd.fst #[F]))
<|>
(do toBelowAux C d2 arg ( mkAppM `PProd.snd #[F]))
| .app (.app (.const `And _) d1) d2 =>
(do toBelowAux C d1 arg ( mkAppM `And.left #[F]))
<|>
(do toBelowAux C d2 arg ( mkAppM `And.right #[F]))
| _ => forallTelescopeReducing belowDict fun xs belowDict => do
let arg zetaReduce arg
let argArgs := arg.getAppArgs
unless argArgs.size >= xs.size do throwToBelowFailed
let n := argArgs.size
let argTailArgs := argArgs.extract (n - xs.size) n
let belowDict := belowDict.replaceFVars xs argTailArgs
match belowDict with
| .app belowDictFun belowDictArg =>
unless belowDictFun.getAppFn == C do throwToBelowFailed
unless isDefEq belowDictArg arg do throwToBelowFailed
pure (mkAppN F argTailArgs)
| _ =>
trace[Elab.definition.structural] "belowDict not an app: {belowDict}"
throwToBelowFailed
trace[Elab.definition.structural] "belowDict start:{indentExpr belowDict}\narg:{indentExpr arg}"
-- First search through the PProd packing of the different `brecOn` motives
searchPProd belowDict F fun belowDict F => do
trace[Elab.definition.structural] "belowDict step 1:{indentExpr belowDict}"
-- Then instantiate parameters of a reflexive type, if needed
forallTelescopeReducing belowDict fun xs belowDict => do
let arg zetaReduce arg
let argArgs := arg.getAppArgs
unless argArgs.size >= xs.size do throwToBelowFailed
let n := argArgs.size
let argTailArgs := argArgs.extract (n - xs.size) n
let belowDict := belowDict.replaceFVars xs argTailArgs
-- And again search through the PProd packing due to multiple functions recursing on the
-- same inductive data type
-- (We could use the funIdx and the `positions` array to replace this search with more
-- targeted indexing.)
searchPProd belowDict (mkAppN F argTailArgs) fun belowDict F => do
trace[Elab.definition.structural] "belowDict step 2:{indentExpr belowDict}"
match belowDict with
| .app belowDictFun belowDictArg =>
unless belowDictFun.getAppFn == C do throwToBelowFailed
unless isDefEq belowDictArg arg do throwToBelowFailed
pure F
| _ =>
trace[Elab.definition.structural] "belowDict not an app:{indentExpr belowDict}"
throwToBelowFailed
/-- See `toBelow` -/
private def withBelowDict [Inhabited α] (below : Expr) (numIndParams : Nat)
(positions : Positions) (k : Array Expr Expr MetaM α) : MetaM α := do
let numIndAll := positions.size
let numTypeFormers := positions.size
let belowType inferType below
trace[Elab.definition.structural] "belowType: {belowType}"
unless ( isTypeCorrect below) do
trace[Elab.definition.structural] "not type correct!"
belowType.withApp fun f args => do
unless numIndParams + numIndAll < args.size do
unless numIndParams + numTypeFormers < args.size do
trace[Elab.definition.structural] "unexpected 'below' type{indentExpr belowType}"
throwToBelowFailed
let params := args[:numIndParams]
let finalArgs := args[numIndParams+numIndAll:]
let finalArgs := args[numIndParams+numTypeFormers:]
let pre := mkAppN f params
let motiveTypes inferArgumentTypesN numIndAll pre
let motiveTypes inferArgumentTypesN numTypeFormers pre
let numMotives : Nat := positions.numIndices
trace[Elab.definition.structural] "numMotives: {numMotives}"
let mut CTypes := Array.mkArray numMotives (.sort 37) -- dummy value
@@ -133,26 +146,16 @@ private partial def replaceRecApps (recArgInfos : Array RecArgInfo) (positions :
e.withApp fun f args => do
if let .some fnIdx := recArgInfos.findIdx? (f.isConstOf ·.fnName) then
let recArgInfo := recArgInfos[fnIdx]!
let numFixed := recArgInfo.numFixed
let recArgPos := recArgInfo.recArgPos
if recArgPos >= args.size then
throwError "insufficient number of parameters at recursive application {indentExpr e}"
let recArg := args[recArgPos]!
let some recArg := args[recArgInfo.recArgPos]?
| throwError "insufficient number of parameters at recursive application {indentExpr e}"
-- For reflexive type, we may have nested recursive applications in recArg
let recArg loop below recArg
let f
try toBelow below recArgInfo.indParams.size positions fnIdx recArg
try toBelow below recArgInfo.indGroupInst.params.size positions fnIdx recArg
catch _ => throwError "failed to eliminate recursive application{indentExpr e}"
-- Recall that the fixed parameters are not in the scope of the `brecOn`. So, we skip them.
let argsNonFixed := args.extract numFixed args.size
-- The function `f` does not explicitly take `recArg` and its indices as arguments. So, we skip them too.
let mut fArgs := #[]
for i in [:argsNonFixed.size] do
let j := i + numFixed
if recArgInfo.recArgPos != j && !recArgInfo.indicesPos.contains j then
let arg := argsNonFixed[i]!
let arg replaceRecApps recArgInfos positions below arg
fArgs := fArgs.push arg
-- We don't pass the fixed parameters, the indices and the major arg to `f`, only the rest
let (_, fArgs) := recArgInfo.pickIndicesMajor args[recArgInfo.numFixed:]
let fArgs fArgs.mapM (replaceRecApps recArgInfos positions below ·)
return mkAppN f fArgs
else
return mkAppN ( loop below f) ( args.mapM (loop below))
@@ -225,35 +228,28 @@ def mkBRecOnF (recArgInfos : Array RecArgInfo) (positions : Positions)
let valueNew replaceRecApps recArgInfos positions below value
mkLambdaFVars (indexMajorArgs ++ #[below] ++ otherArgs) valueNew
/--
Given the `motives`, figures out whether to use `.brecOn` or `.binductionOn`, pass
the right universe levels, the parameters, and the motives.
It was already checked earlier in `checkCodomainsLevel` that the functions live in the same universe.
-/
def mkBRecOnConst (recArgInfos : Array RecArgInfo) (positions : Positions)
(motives : Array Expr) : MetaM (Name Expr) := do
-- For now, just look at the first
let recArgInfo := recArgInfos[0]!
(motives : Array Expr) : MetaM (Nat Expr) := do
let indGroup := recArgInfos[0]!.indGroupInst
let motive := motives[0]!
let brecOnUniv lambdaTelescope motive fun _ type => getLevel type
let indInfo getConstInfoInduct recArgInfo.indName
let indInfo getConstInfoInduct indGroup.all[0]!
let useBInductionOn := indInfo.isReflexive && brecOnUniv == levelZero
let brecOnUniv
if indInfo.isReflexive && brecOnUniv != levelZero then
decLevel brecOnUniv
else
pure brecOnUniv
let brecOnCons := fun n =>
let brecOn :=
if useBInductionOn then .const (mkBInductionOnName n) recArgInfo.indLevels
else .const (mkBRecOnName n) (brecOnUniv :: recArgInfo.indLevels)
mkAppN brecOn recArgInfo.indParams
let brecOnCons := fun idx => indGroup.brecOn useBInductionOn brecOnUniv idx
-- Pick one as a prototype
let brecOnAux := brecOnCons recArgInfo.indName
let brecOnAux := brecOnCons 0
-- Infer the type of the packed motive arguments
let packedMotiveTypes inferArgumentTypesN recArgInfo.indAll.size brecOnAux
let packedMotiveTypes inferArgumentTypesN indGroup.numMotives brecOnAux
let packedMotives positions.mapMwith packMotives packedMotiveTypes motives
return fun n => mkAppN (brecOnCons n) packedMotives
@@ -265,17 +261,18 @@ combinators. This assumes that all `.brecOn` functions of a mutual inductive hav
It also undoes the permutation and packing done by `packMotives`
-/
def inferBRecOnFTypes (recArgInfos : Array RecArgInfo) (positions : Positions)
(brecOnConst : Name Expr) : MetaM (Array Expr) := do
(brecOnConst : Nat Expr) : MetaM (Array Expr) := do
let numTypeFormers := positions.size
let recArgInfo := recArgInfos[0]! -- pick an arbitrary one
let brecOn := brecOnConst recArgInfo.indName
let brecOn := brecOnConst 0
check brecOn
let brecOnType inferType brecOn
-- Skip the indices and major argument
let packedFTypes forallBoundedTelescope brecOnType (some (recArgInfo.indicesPos.size + 1)) fun _ brecOnType =>
-- And return the types of of the next arguments
arrowDomainsN recArgInfo.indAll.size brecOnType
arrowDomainsN numTypeFormers brecOnType
let mut FTypes := Array.mkArray recArgInfos.size (Expr.sort 0)
let mut FTypes := Array.mkArray positions.numIndices (Expr.sort 0)
for packedFType in packedFTypes, poss in positions do
for pos in poss do
FTypes := FTypes.set! pos packedFType
@@ -285,11 +282,11 @@ def inferBRecOnFTypes (recArgInfos : Array RecArgInfo) (positions : Positions)
Completes the `.brecOn` for the given function.
The `value` is the function with (only) the fixed parameters moved into the context.
-/
def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Name Expr)
def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Nat Expr)
(FArgs : Array Expr) (recArgInfo : RecArgInfo) (value : Expr) : MetaM Expr := do
lambdaTelescope value fun ys _value => do
let (indexMajorArgs, otherArgs) := recArgInfo.pickIndicesMajor ys
let brecOn := brecOnConst recArgInfo.indName
let brecOn := brecOnConst recArgInfo.indIdx
let brecOn := mkAppN brecOn indexMajorArgs
let packedFTypes inferArgumentTypesN positions.size brecOn
let packedFArgs positions.mapMwith packFArgs packedFTypes FArgs

View File

@@ -9,46 +9,6 @@ import Lean.Meta.ForEachExpr
namespace Lean.Elab.Structural
/--
Information about the argument of interest of a structurally recursive function.
The `Expr`s in this data structure expect the `fixedParams` to be in scope, but not the other
parameters of the function. This ensures that this data structure makes sense in the other functions
of a mutually recursive group.
-/
structure RecArgInfo where
/-- the name of the recursive function -/
fnName : Name
/-- the fixed prefix of arguments of the function we are trying to justify termination using structural recursion. -/
numFixed : Nat
/-- position of the argument (counted including fixed prefix) we are recursing on -/
recArgPos : Nat
/-- position of the indices (counted including fixed prefix) of the inductive datatype indices we are recursing on -/
indicesPos : Array Nat
/-- inductive datatype name of the argument we are recursing on -/
indName : Name
/-- inductive datatype universe levels of the argument we are recursing on -/
indLevels : List Level
/-- inductive datatype parameters of the argument we are recursing on -/
indParams : Array Expr
/-- The types mutually inductive with indName -/
indAll : Array Name
deriving Inhabited
/--
If `xs` are the parameters of the functions (excluding fixed prefix), partitions them
into indices and major arguments, and other parameters.
-/
def RecArgInfo.pickIndicesMajor (info : RecArgInfo) (xs : Array Expr) : (Array Expr × Array Expr) := Id.run do
let mut indexMajorArgs := #[]
let mut otherArgs := #[]
for h : i in [:xs.size] do
let j := i + info.numFixed
if j = info.recArgPos || info.indicesPos.contains j then
indexMajorArgs := indexMajorArgs.push xs[i]
else
otherArgs := otherArgs.push xs[i]
return (indexMajorArgs, otherArgs)
structure State where
/-- As part of the inductive predicates case, we keep adding more and more discriminants from the
local context and build up a bigger matcher application until we reach a fixed point.
@@ -91,10 +51,11 @@ and for each such type, keep track of the order of the functions.
We represent these positions as an `Array (Array Nat)`. We have that
* `positions.size = indInfo.all.length`
* `positions.size = indInfo.numTypeFormers`
* `positions.flatten` is a permutation of `[0:n]`, so each of the `n` functions has exactly one
position, and each position refers to one of the `n` functions.
* if `k ∈ positions[i]` then the recursive argument of function `k` is has type `indInfo.all[i]`
(or corresponding nested inductive type)
-/
abbrev Positions := Array (Array Nat)
@@ -127,3 +88,6 @@ def Positions.mapMwith {α β m} [Monad m] [Inhabited β] (f : α → Array β
(Array.zip ys positions).mapM fun y, poss => f y (poss.map (xs[·]!))
end Lean.Elab.Structural
builtin_initialize
Lean.registerTraceClass `Elab.definition.structural

View File

@@ -21,6 +21,7 @@ namespace Structural
structure EqnInfo extends EqnInfoCore where
recArgPos : Nat
declNames : Array Name
numFixed : Nat
deriving Inhabited
private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
@@ -81,9 +82,11 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo mkMapDeclarationExtension
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat) : CoreM Unit := do
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat)
(numFixed : Nat) : CoreM Unit := do
ensureEqnReservedNamesAvailable preDef.declName
modifyEnv fun env => eqnInfoExt.insert env preDef.declName { preDef with recArgPos, declNames }
modifyEnv fun env => eqnInfoExt.insert env preDef.declName
{ preDef with recArgPos, declNames, numFixed }
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
if let some info := eqnInfoExt.find? ( getEnv) declName then

View File

@@ -4,11 +4,31 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Elab.PreDefinition.TerminationArgument
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.RecArgInfo
namespace Lean.Elab.Structural
open Meta
def prettyParam (xs : Array Expr) (i : Nat) : MetaM MessageData := do
let x := xs[i]!
let n x.fvarId!.getUserName
addMessageContextFull <| if n.hasMacroScopes then m!"#{i+1}" else m!"{x}"
def prettyRecArg (xs : Array Expr) (value : Expr) (recArgInfo : RecArgInfo) : MetaM MessageData := do
lambdaTelescope value fun ys _ => prettyParam (xs ++ ys) recArgInfo.recArgPos
def prettyParameterSet (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
(recArgInfos : Array RecArgInfo) : MetaM MessageData := do
if fnNames.size = 1 then
return m!"parameter " ++ ( prettyRecArg xs values[0]! recArgInfos[0]!)
else
let mut l := #[]
for fnName in fnNames, value in values, recArgInfo in recArgInfos do
l := l.push m!"{(← prettyRecArg xs value recArgInfo)} of {fnName}"
return m!"parameters " ++ .andList l.toList
private def getIndexMinPos (xs : Array Expr) (indices : Array Expr) : Nat := Id.run do
let mut minPos := xs.size
for index in indices do
@@ -72,60 +92,190 @@ def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) :
| some (indParam, y) =>
throwError "its type is an inductive datatype{indentExpr xType}\nand the datatype parameter{indentExpr indParam}\ndepends on the function parameter{indentExpr y}\nwhich does not come before the varying parameters and before the indices of the recursion parameter."
| none =>
let indAll := indInfo.all.toArray
let .some indIdx := indAll.indexOf? indInfo.name | panic! "{indInfo.name} not in {indInfo.all}"
let indicesPos := indIndices.map fun index => match xs.indexOf? index with | some i => i.val | none => unreachable!
return { fnName := fnName
numFixed := numFixed
recArgPos := i
indicesPos := indicesPos
indName := indInfo.name
indLevels := us
indParams := indParams
indAll := indInfo.all.toArray }
let indGroupInst := {
IndGroupInfo.ofInductiveVal indInfo with
levels := us
params := indParams }
return { fnName := fnName
numFixed := numFixed
recArgPos := i
indicesPos := indicesPos
indGroupInst := indGroupInst
indIdx := indIdx }
else
throwError "the index #{i+1} exceeds {xs.size}, the number of parameters"
/--
Runs `k` on all argument indices, until it succeeds.
We use this argument to justify termination using the auxiliary `brecOn` construction.
Collects the `RecArgInfos` for one function, and returns a report for why the others were not
considered.
We give preference for arguments that are *not* indices of inductive types of other arguments.
See issue #837 for an example where we can show termination using the index of an inductive family, but
we don't get the desired definitional equalities.
The `xs` are the fixed parameters, `value` the body with the fixed prefix instantiated.
`value` is the function value (including fixed parameters)
Takes the optional user annotations into account (`termArg?`). If this is given and the argument
is unsuitable, throw an error.
-/
partial def tryAllArgs (value : Expr) (k : Nat M α) : M α := do
-- It's improtant to keep the call to `k` outside the scope of `lambdaTelescope`:
-- The tactics in the IndPred construction search the full local context, so we must not have
-- extra FVars there
let (indices, nonIndices) lambdaTelescope value fun xs _ => do
let indicesRef : IO.Ref (Array Nat) IO.mkRef {}
for x in xs do
let xType inferType x
/- Traverse all sub-expressions in the type of `x` -/
forEachExpr xType fun e =>
/- If `e` is an inductive family, we store in `indicesRef` all variables in `xs` that occur in "index positions". -/
matchConstInduct e.getAppFn (fun _ => pure ()) fun info _ => do
if info.numIndices > 0 && info.numParams + info.numIndices == e.getAppNumArgs then
for arg in e.getAppArgs[info.numParams:] do
forEachExpr arg fun e => do
if let .some idx := xs.getIdx? e then
indicesRef.modify (·.push idx)
let indices indicesRef.get
let nonIndices := (Array.range xs.size).filter (fun i => !(indices.contains i))
return (indices, nonIndices)
def getRecArgInfos (fnName : Name) (xs : Array Expr) (value : Expr)
(termArg? : Option TerminationArgument) : MetaM (Array RecArgInfo × MessageData) := do
lambdaTelescope value fun ys _ => do
if let .some termArg := termArg? then
-- User explictly asked to use a certain argument, so throw errors eagerly
let recArgInfo withRef termArg.ref do
mapError (f := (m!"cannot use specified parameter for structural recursion:{indentD ·}")) do
getRecArgInfo fnName xs.size (xs ++ ys) ( termArg.structuralArg)
return (#[recArgInfo], m!"")
else
let mut recArgInfos := #[]
let mut report : MessageData := m!""
-- No `termination_by`, so try all, and remember the errors
for idx in [:xs.size + ys.size] do
try
let recArgInfo getRecArgInfo fnName xs.size (xs ++ ys) idx
recArgInfos := recArgInfos.push recArgInfo
catch e =>
report := report ++ (m!"Not considering parameter {← prettyParam (xs ++ ys) idx} of {fnName}:" ++
indentD e.toMessageData) ++ "\n"
trace[Elab.definition.structural] "getRecArgInfos report: {report}"
return (recArgInfos, report)
let mut errors : Array MessageData := Array.mkArray (indices.size + nonIndices.size) m!""
let saveState get -- backtrack the state for each argument
for i in id (nonIndices ++ indices) do
trace[Elab.definition.structural] "findRecArg i: {i}"
try
set saveState
return ( k i)
catch e => errors := errors.set! i e.toMessageData
throwError
errors.foldl
(init := m!"structural recursion cannot be used:")
(f := (· ++ Format.line ++ Format.line ++ .))
/--
Reorders the `RecArgInfos` of one function to put arguments that are indices of other arguments
last.
See issue #837 for an example where we can show termination using the index of an inductive family, but
we don't get the desired definitional equalities.
-/
def nonIndicesFirst (recArgInfos : Array RecArgInfo) : Array RecArgInfo := Id.run do
let mut indicesPos : HashSet Nat := {}
for recArgInfo in recArgInfos do
for pos in recArgInfo.indicesPos do
indicesPos := indicesPos.insert pos
let (indices,nonIndices) := recArgInfos.partition (indicesPos.contains ·.recArgPos)
return nonIndices ++ indices
private def dedup [Monad m] (eq : α α m Bool) (xs : Array α) : m (Array α) := do
let mut ret := #[]
for x in xs do
unless ( ret.anyM (eq · x)) do
ret := ret.push x
return ret
/--
Given the `RecArgInfo`s of all the recursive functions, find the inductive groups to consider.
-/
def inductiveGroups (recArgInfos : Array RecArgInfo) : MetaM (Array IndGroupInst) :=
dedup IndGroupInst.isDefEq (recArgInfos.map (·.indGroupInst))
/--
Filters the `recArgInfos` by those that describe an argument that's part of the recursive inductive
group `group`.
Because of nested inductives this function has the ability to change the `recArgInfo`.
Consider
```
inductive Tree where | node : List Tree → Tree
```
then when we look for arguments whose type is part of the group `Tree`, we want to also consider
the argument of type `List Tree`, even though that arguments `RecArgInfo` refers to initially to
`List`.
-/
def argsInGroup (group : IndGroupInst) (xs : Array Expr) (value : Expr)
(recArgInfos : Array RecArgInfo) : MetaM (Array RecArgInfo) := do
let nestedTypeFormers group.nestedTypeFormers
recArgInfos.filterMapM fun recArgInfo => do
-- Is this argument from the same mutual group of inductives?
if ( group.isDefEq recArgInfo.indGroupInst) then
return (.some recArgInfo)
-- Can this argument be understood as the auxillary type former of a nested inductive?
if nestedTypeFormers.isEmpty then return .none
lambdaTelescope value fun ys _ => do
let x := (xs++ys)[recArgInfo.recArgPos]!
for nestedTypeFormer in nestedTypeFormers, indIdx in [group.all.size : group.numMotives] do
let xType whnfD ( inferType x)
let (indIndices, _, type) forallMetaTelescope nestedTypeFormer
if ( isDefEqGuarded type xType) then
let indIndices indIndices.mapM instantiateMVars
if !indIndices.all Expr.isFVar then
-- throwError "indices are not variables{indentExpr xType}"
continue
if !indIndices.allDiff then
-- throwError "indices are not pairwise distinct{indentExpr xType}"
continue
-- TODO: Do we have to worry about the indices ending up in the fixed prefix here?
if let some (_index, _y) hasBadIndexDep? ys indIndices then
-- throwError "its type {indInfo.name} is an inductive family{indentExpr xType}\nand index{indentExpr index}\ndepends on the non index{indentExpr y}"
continue
let indicesPos := indIndices.map fun index => match (xs++ys).indexOf? index with | some i => i.val | none => unreachable!
return .some
{ fnName := recArgInfo.fnName
numFixed := recArgInfo.numFixed
recArgPos := recArgInfo.recArgPos
indicesPos := indicesPos
indGroupInst := group
indIdx := indIdx }
return .none
def maxCombinationSize : Nat := 10
def allCombinations (xss : Array (Array α)) : Option (Array (Array α)) :=
if xss.foldl (· * ·.size) 1 > maxCombinationSize then
none
else
let rec go i acc : Array (Array α):=
if h : i < xss.size then
xss[i].concatMap fun x => go (i + 1) (acc.push x)
else
#[acc]
some (go 0 #[])
def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
(termArg?s : Array (Option TerminationArgument)) (k : Array RecArgInfo M α) : M α := do
let mut report := m!""
-- Gather information on all possible recursive arguments
let mut recArgInfoss := #[]
for fnName in fnNames, value in values, termArg? in termArg?s do
let (recArgInfos, thisReport) getRecArgInfos fnName xs value termArg?
report := report ++ thisReport
recArgInfoss := recArgInfoss.push recArgInfos
-- Put non-indices first
recArgInfoss := recArgInfoss.map nonIndicesFirst
trace[Elab.definition.structural] "recArgInfoss: {recArgInfoss.map (·.map (·.recArgPos))}"
-- Inductive groups to consider
let groups inductiveGroups recArgInfoss.flatten
trace[Elab.definition.structural] "inductive groups: {groups}"
if groups.isEmpty then
report := report ++ "no parameters suitable for structural recursion"
-- Consider each group
for group in groups do
-- Select those RecArgInfos that are compatible with this inductive group
let mut recArgInfoss' := #[]
for value in values, recArgInfos in recArgInfoss do
recArgInfoss' := recArgInfoss'.push ( argsInGroup group xs value recArgInfos)
if let some idx := recArgInfoss'.findIdx? (·.isEmpty) then
report := report ++ m!"Skipping arguments of type {group}, as {fnNames[idx]!} has no compatible argument.\n"
continue
if let some combs := allCombinations recArgInfoss' then
for comb in combs do
try
-- TODO: Here we used to save and restore the state. But should the `try`-`catch`
-- not suffice?
let r k comb
trace[Elab.definition.structural] "tryAllArgs report:\n{report}"
return r
catch e =>
let m prettyParameterSet fnNames xs values comb
report := report ++ m!"Cannot use {m}:{indentD e.toMessageData}\n"
else
report := report ++ m!"Too many possible combinations of parameters of type {group} (or " ++
m!"please indicate the recursive argument explicitly using `termination_by structural`).\n"
report := m!"failed to infer structural recursion:\n" ++ report
trace[Elab.definition.structural] "tryAllArgs:\n{report}"
throwError report
end Lean.Elab.Structural

View File

@@ -0,0 +1,106 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
prelude
import Lean.Meta.InferType
/-!
This module contains the types
* `IndGroupInfo`, a variant of `InductiveVal` with information that
applies to a whole group of mutual inductives and
* `IndGroupInst` which extends `IndGroupInfo` with levels and parameters
to indicate a instantiation of the group.
One purpose of this abstraction is to make it clear when a fuction operates on a group as
a whole, rather than a specific inductive within the group.
-/
namespace Lean.Elab.Structural
open Lean Meta
/--
A mutually inductive group, identified by the `all` array of the `InductiveVal` of its
constituents.
-/
structure IndGroupInfo where
all : Array Name
numNested : Nat
deriving BEq, Inhabited
def IndGroupInfo.ofInductiveVal (indInfo : InductiveVal) : IndGroupInfo where
all := indInfo.all.toArray
numNested := indInfo.numNested
def IndGroupInfo.numMotives (group : IndGroupInfo) : Nat :=
group.all.size + group.numNested
/--
An instance of an mutually inductive group of inductives, identified by the `all` array
and the level and expressions parameters.
For example this distinguishes between `List α` and `List β` so that we will not even attempt
mutual structural recursion on such incompatible types.
-/
structure IndGroupInst extends IndGroupInfo where
levels : List Level
params : Array Expr
deriving Inhabited
def IndGroupInst.toMessageData (igi : IndGroupInst) : MessageData :=
mkAppN (.const igi.all[0]! igi.levels) igi.params
instance : ToMessageData IndGroupInst where
toMessageData := IndGroupInst.toMessageData
def IndGroupInst.isDefEq (igi1 igi2 : IndGroupInst) : MetaM Bool := do
unless igi1.toIndGroupInfo == igi2.toIndGroupInfo do return false
unless igi1.levels.length = igi2.levels.length do return false
unless (igi1.levels.zip igi2.levels).all (fun (l₁, l₂) => Level.isEquiv l₁ l₂) do return false
unless igi1.params.size = igi2.params.size do return false
unless ( (igi1.params.zip igi2.params).allM (fun (e₁, e₂) => Meta.isDefEqGuarded e₁ e₂)) do return false
return true
/-- Instantiates the right `.brecOn` or `.bInductionOn` for the given type former index,
including universe parameters and fixed prefix. -/
def IndGroupInst.brecOn (group : IndGroupInst) (ind : Bool) (lvl : Level) (idx : Nat) : Expr :=
let e := if let .some n := group.all[idx]? then
if ind then .const (mkBInductionOnName n) group.levels
else .const (mkBRecOnName n) (lvl :: group.levels)
else
let n := group.all[0]!
let j := idx - group.all.size + 1
if ind then .const (mkBInductionOnName n |>.appendIndexAfter j) group.levels
else .const (mkBRecOnName n |>.appendIndexAfter j) (lvl :: group.levels)
mkAppN e group.params
/--
Figures out the nested type formers of an inductive group, with parameters instantiated
and indices still forall-abstracted.
For example given a nested inductive
```
inductive Tree α where | node : α → Vector (Tree α) n → Tree α
```
(where `n` is an index of `Vector`) and the instantiation `Tree Int` it will return
```
#[(n : Nat) → Vector (Tree Int) n]
```
-/
def IndGroupInst.nestedTypeFormers (igi : IndGroupInst) : MetaM (Array Expr) := do
if igi.numNested = 0 then return #[]
-- We extract this information from the motives of the recursor
let recName := mkRecName igi.all[0]!
let recInfo getConstInfoRec recName
assert! recInfo.numMotives = igi.numMotives
let aux := mkAppN (.const recName (0 :: igi.levels)) igi.params
let motives inferArgumentTypesN recInfo.numMotives aux
let auxMotives : Array Expr := motives[igi.all.size:]
auxMotives.mapM fun motive =>
forallTelescopeReducing motive fun xs _ => do
assert! xs.size > 0
mkForallFVars xs.pop ( inferType xs.back)
end Lean.Elab.Structural

View File

@@ -7,6 +7,7 @@ prelude
import Lean.Meta.IndPredBelow
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.RecArgInfo
namespace Lean.Elab.Structural
open Meta
@@ -81,8 +82,8 @@ def mkIndPredBRecOn (recArgInfo : RecArgInfo) (value : Expr) : M Expr := do
let motive mkForallFVars otherArgs type
let motive mkLambdaFVars indexMajorArgs motive
trace[Elab.definition.structural] "brecOn motive: {motive}"
let brecOn := Lean.mkConst (mkBRecOnName recArgInfo.indName) recArgInfo.indLevels
let brecOn := mkAppN brecOn recArgInfo.indParams
let brecOn := Lean.mkConst (mkBRecOnName recArgInfo.indName!) recArgInfo.indGroupInst.levels
let brecOn := mkAppN brecOn recArgInfo.indGroupInst.params
let brecOn := mkApp brecOn motive
let brecOn := mkAppN brecOn indexMajorArgs
check brecOn

View File

@@ -89,87 +89,72 @@ def getMutualFixedPrefix (preDefs : Array PreDefinition) : M Nat :=
return true
resultRef.get
/-- Checks that all parameter types are mutually inductive -/
private def checkAllFromSameClique (recArgInfos : Array RecArgInfo) : MetaM Unit := do
for recArgInfo in recArgInfos do
unless recArgInfos[0]!.indAll.contains recArgInfo.indName do
throwError m!"Cannot use structural mutual recursion: The recursive argument of " ++
m!"{recArgInfos[0]!.fnName} is of type {recArgInfos[0]!.indName}, " ++
m!"the recursive argument of {recArgInfo.fnName} is of type " ++
m!"{recArgInfo.indName}, and these are not mutually recursive."
private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr)
(recArgInfos : Array RecArgInfo) : M (Array PreDefinition) := do
let values preDefs.mapM (instantiateLambda ·.value xs)
let indInfo getConstInfoInduct recArgInfos[0]!.indGroupInst.all[0]!
if isInductivePredicate indInfo.name then
-- Here we branch off to the IndPred construction, but only for non-mutual functions
unless preDefs.size = 1 do
throwError "structural mutual recursion over inductive predicates is not supported"
trace[Elab.definition.structural] "Using mkIndPred construction"
let preDef := preDefs[0]!
let recArgInfo := recArgInfos[0]!
let value := values[0]!
let valueNew mkIndPredBRecOn recArgInfo value
let valueNew mkLambdaFVars xs valueNew
trace[Elab.definition.structural] "Nonrecursive value:{indentExpr valueNew}"
check valueNew
return #[{ preDef with value := valueNew }]
private def elimMutualRecursion (preDefs : Array PreDefinition) (recArgPoss : Array Nat) : M (Array PreDefinition) := do
-- Sort the (indices of the) definitions by their position in indInfo.all
let positions : Positions := .groupAndSort (·.indIdx) recArgInfos (Array.range indInfo.numTypeFormers)
trace[Elab.definition.structural] "positions: {positions}"
-- Construct the common `.brecOn` arguments
let motives (Array.zip recArgInfos values).mapM fun (r, v) => mkBRecOnMotive r v
trace[Elab.definition.structural] "motives: {motives}"
let brecOnConst mkBRecOnConst recArgInfos positions motives
let FTypes inferBRecOnFTypes recArgInfos positions brecOnConst
trace[Elab.definition.structural] "FTypes: {FTypes}"
let FArgs (recArgInfos.zip (values.zip FTypes)).mapM fun (r, (v, t)) =>
mkBRecOnF recArgInfos positions r v t
trace[Elab.definition.structural] "FArgs: {FArgs}"
-- Assemble the individual `.brecOn` applications
let valuesNew (Array.zip recArgInfos values).mapIdxM fun i (r, v) =>
mkBrecOnApp positions i brecOnConst FArgs r v
-- Abstract over the fixed prefixed
let valuesNew valuesNew.mapM (mkLambdaFVars xs ·)
return (Array.zip preDefs valuesNew).map fun preDef, valueNew => { preDef with value := valueNew }
private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) :
M (Array Nat × (Array PreDefinition) × Nat) := do
withoutModifyingEnv do
preDefs.forM (addAsAxiom ·)
let names := preDefs.map (·.declName)
let fnNames := preDefs.map (·.declName)
let preDefs preDefs.mapM fun preDef =>
return { preDef with value := ( preprocess preDef.value names) }
return { preDef with value := ( preprocess preDef.value fnNames) }
-- The syntactically fixed arguments
let maxNumFixed getMutualFixedPrefix preDefs
-- We do two passes to get the RecArgInfo values.
-- From the first pass, we only keep the mininum of the `numFixed` reported.
let numFixed lambdaBoundedTelescope preDefs[0]!.value maxNumFixed fun xs _ => do
lambdaBoundedTelescope preDefs[0]!.value maxNumFixed fun xs _ => do
assert! xs.size = maxNumFixed
let values preDefs.mapM (instantiateLambda ·.value xs)
let recArgInfos preDefs.mapIdxM fun i preDef => do
let recArgPos := recArgPoss[i]!
let value := values[i]!
lambdaTelescope value fun ys _value => do
getRecArgInfo preDef.declName maxNumFixed (xs ++ ys) recArgPos
return (recArgInfos.map (·.numFixed)).foldl Nat.min maxNumFixed
if numFixed < maxNumFixed then
trace[Elab.definition.structural] "Reduced numFixed from {maxNumFixed} to {numFixed}"
-- Now we bring exactly that `numFixed` parameter into scope.
lambdaBoundedTelescope preDefs[0]!.value numFixed fun xs _ => do
assert! xs.size = numFixed
let values preDefs.mapM (instantiateLambda ·.value xs)
let recArgInfos preDefs.mapIdxM fun i preDef => do
let recArgPos := recArgPoss[i]!
let value := values[i]!
lambdaTelescope value fun ys _value => do
getRecArgInfo preDef.declName numFixed (xs ++ ys) recArgPos
-- Two passes should suffice
assert! recArgInfos.all (·.numFixed = numFixed)
let indInfo getConstInfoInduct recArgInfos[0]!.indName
if isInductivePredicate indInfo.name then
-- Here we branch off to the IndPred construction, but only for non-mutual functions
unless preDefs.size = 1 do
throwError "structural mutual recursion over inductive predicates is not supported"
trace[Elab.definition.structural] "Using mkIndPred construction"
let preDef := preDefs[0]!
let recArgInfo := recArgInfos[0]!
let value := values[0]!
let valueNew mkIndPredBRecOn recArgInfo value
let valueNew mkLambdaFVars xs valueNew
trace[Elab.definition.structural] "Nonrecursive value:{indentExpr valueNew}"
check valueNew
return #[{ preDef with value := valueNew }]
checkAllFromSameClique recArgInfos
-- Sort the (indices of the) definitions by their position in indInfo.all
let positions : Positions := .groupAndSort (·.indName) recArgInfos indInfo.all.toArray
-- Construct the common `.brecOn` arguments
let motives (Array.zip recArgInfos values).mapM fun (r, v) => mkBRecOnMotive r v
let brecOnConst mkBRecOnConst recArgInfos positions motives
let FTypes inferBRecOnFTypes recArgInfos positions brecOnConst
let FArgs (recArgInfos.zip (values.zip FTypes)).mapM fun (r, (v, t)) =>
mkBRecOnF recArgInfos positions r v t
-- Assemble the individual `.brecOn` applications
let valuesNew (Array.zip recArgInfos values).mapIdxM fun i (r, v) =>
mkBrecOnApp positions i brecOnConst FArgs r v
-- Abstract over the fixed prefixed
let valuesNew valuesNew.mapM (mkLambdaFVars xs ·)
return (Array.zip preDefs valuesNew).map fun preDef, valueNew => { preDef with value := valueNew }
tryAllArgs fnNames xs values termArg?s fun recArgInfos => do
let recArgPoss := recArgInfos.map (·.recArgPos)
trace[Elab.definition.structural] "Trying argument set {recArgPoss}"
let numFixed := recArgInfos.foldl (·.min ·.numFixed) maxNumFixed
if numFixed < maxNumFixed then
trace[Elab.definition.structural] "Reduced numFixed from {maxNumFixed} to {numFixed}"
-- We may have decreased the number of arguments we consider fixed, so update
-- the recArgInfos, remove the extra arguments from local environment, and recalculate value
let recArgInfos := recArgInfos.map ({· with numFixed := numFixed })
withErasedFVars (xs.extract numFixed xs.size |>.map (·.fvarId!)) do
let xs := xs[:numFixed]
let preDefs' elimMutualRecursion preDefs xs recArgInfos
return (recArgPoss, preDefs', numFixed)
def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
if let some ref := preDef.termination.terminationBy?? then
@@ -179,34 +164,20 @@ def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
let stx termArg.delab arity (extraParams := preDef.termination.extraParams)
Tactic.TryThis.addSuggestion ref stx
private def inferRecArgPos (preDefs : Array PreDefinition)
(termArgs? : Option TerminationArguments) : M (Array Nat × Array PreDefinition) := do
withoutModifyingEnv do
if let some termArgs := termArgs? then
let recArgPoss termArgs.mapM (·.structuralArg)
let preDefsNew elimMutualRecursion preDefs recArgPoss
return (recArgPoss, preDefsNew)
else
let #[preDef] := preDefs
| throwError "mutual structural recursion requires explicit `termination_by` clauses"
-- Use termination_by annotation to find argument to recurse on, or just try all
tryAllArgs preDef.value fun i =>
mapError (f := fun msg => m!"argument #{i+1} cannot be used for structural recursion{indentD msg}") do
let preDefsNew elimMutualRecursion #[preDef] #[i]
return (#[i], preDefsNew)
def structuralRecursion (preDefs : Array PreDefinition) (termArgs? : Option TerminationArguments) : TermElabM Unit := do
let ((recArgPoss, preDefsNonRec), state) run <| inferRecArgPos preDefs termArgs?
def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
let names := preDefs.map (·.declName)
let ((recArgPoss, preDefsNonRec, numFixed), state) run <| inferRecArgPos preDefs termArg?s
for recArgPos in recArgPoss, preDef in preDefs do
reportTermArg preDef recArgPos
state.addMatchers.forM liftM
preDefsNonRec.forM fun preDefNonRec => do
let preDefNonRec eraseRecAppSyntax preDefNonRec
-- state.addMatchers.forM liftM
mapError (addNonRec preDefNonRec (applyAttrAfterCompilation := false)) fun msg =>
m!"structural recursion failed, produced type incorrect term{indentD msg}"
-- We create the `_unsafe_rec` before we abstract nested proofs.
-- Reason: the nested proofs may be referring to the _unsafe_rec.
mapError (f := (m!"structural recursion failed, produced type incorrect term{indentD ·}")) do
-- We create the `_unsafe_rec` before we abstract nested proofs.
-- Reason: the nested proofs may be referring to the _unsafe_rec.
addNonRec preDefNonRec (applyAttrAfterCompilation := false) (all := names.toList)
let preDefs preDefs.mapM (eraseRecAppSyntax ·)
addAndCompilePartialRec preDefs
for preDef in preDefs, recArgPos in recArgPoss do
@@ -219,13 +190,11 @@ def structuralRecursion (preDefs : Array PreDefinition) (termArgs? : Option Term
for theorems and definitions that are propositions.
See issue #2327
-/
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
addSmartUnfoldingDef preDef recArgPos
markAsRecursive preDef.declName
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation
builtin_initialize
registerTraceClass `Elab.definition.structural
end Structural

View File

@@ -0,0 +1,60 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Meta.Basic
import Lean.Meta.ForEachExpr
import Lean.Elab.PreDefinition.Structural.IndGroupInfo
namespace Lean.Elab.Structural
/--
Information about the argument of interest of a structurally recursive function.
The `Expr`s in this data structure expect the `fixedParams` to be in scope, but not the other
parameters of the function. This ensures that this data structure makes sense in the other functions
of a mutually recursive group.
-/
structure RecArgInfo where
/-- the name of the recursive function -/
fnName : Name
/-- the fixed prefix of arguments of the function we are trying to justify termination using structural recursion. -/
numFixed : Nat
/-- position of the argument (counted including fixed prefix) we are recursing on -/
recArgPos : Nat
/-- position of the indices (counted including fixed prefix) of the inductive datatype indices we are recursing on -/
indicesPos : Array Nat
/-- The inductive group (with parameters) of the argument's type -/
indGroupInst : IndGroupInst
/--
index of the inductive datatype of the argument we are recursing on.
If `< indAll.all`, a normal data type, else an auxillary data type due to nested recursion
-/
indIdx : Nat
deriving Inhabited
/--
If `xs` are the parameters of the functions (excluding fixed prefix), partitions them
into indices and major arguments, and other parameters.
-/
def RecArgInfo.pickIndicesMajor (info : RecArgInfo) (xs : Array Expr) : (Array Expr × Array Expr) := Id.run do
let mut indexMajorArgs := #[]
let mut otherArgs := #[]
for h : i in [:xs.size] do
let j := i + info.numFixed
if j = info.recArgPos || info.indicesPos.contains j then
indexMajorArgs := indexMajorArgs.push xs[i]
else
otherArgs := otherArgs.push xs[i]
return (indexMajorArgs, otherArgs)
/--
Name of the recursive data type. Assumes that it is not one of the auxillary ones.
-/
def RecArgInfo.indName! (info : RecArgInfo) : Name :=
info.indGroupInst.all[info.indIdx]!
end Lean.Elab.Structural

View File

@@ -10,7 +10,7 @@ import Lean.Elab.Term
import Lean.Elab.Binders
import Lean.Elab.SyntheticMVars
import Lean.Elab.PreDefinition.TerminationHint
import Lean.PrettyPrinter.Delaborator
import Lean.PrettyPrinter.Delaborator.Basic
/-!
This module contains
@@ -115,7 +115,7 @@ def TerminationArgument.delab (arity : Nat) (extraParams : Nat) (termArg : Termi
-- any variable not mentioned syntatically (it may appear in the `Expr`, so do not just use
-- `e.bindingBody!.hasLooseBVar`) should be delaborated as a hole.
let vars : TSyntaxArray [`ident, `Lean.Parser.Term.hole] :=
Array.map (fun (i : Ident) => if hasIdent i.getId stxBody then i else hole) vars
Array.map (fun (i : Ident) => if stxBody.raw.hasIdent i.getId then i else hole) vars
-- drop trailing underscores
let mut vars := vars
while ! vars.isEmpty && vars.back.raw.isOfKind ``hole do vars := vars.pop

View File

@@ -86,7 +86,8 @@ def varyingVarNames (fixedPrefixSize : Nat) (preDef : PreDefinition) : MetaM (Ar
let xs : Array Expr := xs[fixedPrefixSize:]
xs.mapM (·.fvarId!.getUserName)
def wfRecursion (preDefs : Array PreDefinition) (termArgs? : Option TerminationArguments) : TermElabM Unit := do
def wfRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
let termArgs? := termArg?s.sequenceMap id -- Either all or none, checked by `elabTerminationByHints`
let preDefs preDefs.mapM fun preDef =>
return { preDef with value := ( preprocess preDef.value) }
let (fixedPrefixSize, argsPacker, unaryPreDef) withoutModifyingEnv do

View File

@@ -40,3 +40,4 @@ import Lean.Elab.Tactic.LibrarySearch
import Lean.Elab.Tactic.ShowTerm
import Lean.Elab.Tactic.Rfl
import Lean.Elab.Tactic.Rewrites
import Lean.Elab.Tactic.DiscrTreeKey

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
prelude
import Lean.Meta.Tactic.Util
import Lean.Elab.Term
namespace Lean.Elab
@@ -398,12 +399,19 @@ def ensureHasNoMVars (e : Expr) : TacticM Unit := do
if e.hasExprMVar then
throwError "tactic failed, resulting expression contains metavariables{indentExpr e}"
/-- Close main goal using the given expression. If `checkUnassigned == true`, then `val` must not contain unassigned metavariables. -/
def closeMainGoal (val : Expr) (checkUnassigned := true): TacticM Unit := do
/--
Closes main goal using the given expression.
If `checkUnassigned == true`, then `val` must not contain unassigned metavariables.
Returns `true` if `val` was successfully used to close the goal.
-/
def closeMainGoal (tacName : Name) (val : Expr) (checkUnassigned := true): TacticM Unit := do
if checkUnassigned then
ensureHasNoMVars val
( getMainGoal).assign val
replaceMainGoal []
let mvarId getMainGoal
if ( mvarId.checkedAssign val) then
replaceMainGoal []
else
throwTacticEx tacName mvarId m!"attempting to close the goal using{indentExpr val}\nthis is often due occurs-check failure"
@[inline] def liftMetaMAtMain (x : MVarId MetaM α) : TacticM α := do
withMainContext do x ( getMainGoal)

View File

@@ -0,0 +1,65 @@
/-
Copyright (c) 2024 Matthew Robert Ballard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tomas Skrivan, Matthew Robert Ballard
-/
prelude
import Init.Tactics
import Lean.Elab.Command
import Lean.Meta.Tactic.Simp.SimpTheorems
namespace Lean.Elab.Tactic.DiscrTreeKey
open Lean.Meta DiscrTree
open Lean.Elab.Tactic
open Lean.Elab.Command
private def mkKey (e : Expr) (simp : Bool) : MetaM (Array Key) := do
let (_, _, type) withReducible <| forallMetaTelescopeReducing e
let type whnfR type
if simp then
if let some (_, lhs, _) := type.eq? then
mkPath lhs simpDtConfig
else if let some (lhs, _) := type.iff? then
mkPath lhs simpDtConfig
else if let some (_, lhs, _) := type.ne? then
mkPath lhs simpDtConfig
else if let some p := type.not? then
match p.eq? with
| some (_, lhs, _) =>
mkPath lhs simpDtConfig
| _ => mkPath p simpDtConfig
else
mkPath type simpDtConfig
else
mkPath type {}
private def getType (t : TSyntax `term) : TermElabM Expr := do
if let `($id:ident) := t then
if let some ldecl := ( getLCtx).findFromUserName? id.getId then
return ldecl.type
else
let info getConstInfo ( realizeGlobalConstNoOverloadWithInfo id)
return info.type
else
Term.elabTerm t none
@[builtin_command_elab Lean.Parser.discrTreeKeyCmd]
def evalDiscrTreeKeyCmd : CommandElab := fun stx => do
Command.liftTermElabM <| do
match stx with
| `(command| #discr_tree_key $t:term) => do
let type getType t
logInfo ( keysAsPattern <| mkKey type false)
| _ => Elab.throwUnsupportedSyntax
@[builtin_command_elab Lean.Parser.discrTreeSimpKeyCmd]
def evalDiscrTreeSimpKeyCmd : CommandElab := fun stx => do
Command.liftTermElabM <| do
match stx with
| `(command| #discr_tree_simp_key $t:term) => do
let type getType t
logInfo ( keysAsPattern <| mkKey type true)
| _ => Elab.throwUnsupportedSyntax
end Lean.Elab.Tactic.DiscrTreeKey

View File

@@ -56,9 +56,9 @@ def elabTermEnsuringType (stx : Syntax) (expectedType? : Option Expr) (mayPostpo
return e
/-- Try to close main goal using `x target`, where `target` is the type of the main goal. -/
def closeMainGoalUsing (x : Expr TacticM Expr) (checkUnassigned := true) : TacticM Unit :=
def closeMainGoalUsing (tacName : Name) (x : Expr TacticM Expr) (checkUnassigned := true) : TacticM Unit :=
withMainContext do
closeMainGoal (checkUnassigned := checkUnassigned) ( x ( getMainTarget))
closeMainGoal (tacName := tacName) (checkUnassigned := checkUnassigned) ( x ( getMainTarget))
def logUnassignedAndAbort (mvarIds : Array MVarId) : TacticM Unit := do
if ( Term.logUnassignedUsingErrorInfos mvarIds) then
@@ -68,13 +68,14 @@ def filterOldMVars (mvarIds : Array MVarId) (mvarCounterSaved : Nat) : MetaM (Ar
let mctx getMCtx
return mvarIds.filter fun mvarId => (mctx.getDecl mvarId |>.index) >= mvarCounterSaved
@[builtin_tactic «exact»] def evalExact : Tactic := fun stx =>
@[builtin_tactic «exact»] def evalExact : Tactic := fun stx => do
match stx with
| `(tactic| exact $e) => closeMainGoalUsing (checkUnassigned := false) fun type => do
let mvarCounterSaved := ( getMCtx).mvarCounter
let r elabTermEnsuringType e type
logUnassignedAndAbort ( filterOldMVars ( getMVars r) mvarCounterSaved)
return r
| `(tactic| exact $e) =>
closeMainGoalUsing `exact (checkUnassigned := false) fun type => do
let mvarCounterSaved := ( getMCtx).mvarCounter
let r elabTermEnsuringType e type
logUnassignedAndAbort ( filterOldMVars ( getMVars r) mvarCounterSaved)
return r
| _ => throwUnsupportedSyntax
def sortMVarIdArrayByIndex [MonadMCtx m] [Monad m] (mvarIds : Array MVarId) : m (Array MVarId) := do
@@ -359,8 +360,8 @@ def elabAsFVar (stx : Syntax) (userName? : Option Name := none) : TacticM FVarId
| _ => throwUnsupportedSyntax
/--
Make sure `expectedType` does not contain free and metavariables.
It applies zeta and zetaDelta-reduction to eliminate let-free-vars.
Make sure `expectedType` does not contain free and metavariables.
It applies zeta and zetaDelta-reduction to eliminate let-free-vars.
-/
private def preprocessPropToDecide (expectedType : Expr) : TermElabM Expr := do
let mut expectedType instantiateMVars expectedType
@@ -370,31 +371,95 @@ private def preprocessPropToDecide (expectedType : Expr) : TermElabM Expr := do
throwError "expected type must not contain free or meta variables{indentExpr expectedType}"
return expectedType
/--
Given the decidable instance `inst`, reduces it and returns a decidable instance expression
in whnf that can be regarded as the reason for the failure of `inst` to fully reduce.
-/
private partial def blameDecideReductionFailure (inst : Expr) : MetaM Expr := do
let inst whnf inst
-- If it's the Decidable recursor, then blame the major premise.
if inst.isAppOfArity ``Decidable.rec 5 then
return blameDecideReductionFailure inst.appArg!
-- If it is a matcher, look for a discriminant that's a Decidable instance to blame.
if let .const c _ := inst.getAppFn then
if let some info getMatcherInfo? c then
if inst.getAppNumArgs == info.arity then
let args := inst.getAppArgs
for i in [0:info.numDiscrs] do
let inst' := args[info.numParams + 1 + i]!
if ( Meta.isClass? ( inferType inst')) == ``Decidable then
let inst'' whnf inst'
if !(inst''.isAppOf ``isTrue || inst''.isAppOf ``isFalse) then
return blameDecideReductionFailure inst''
return inst
@[builtin_tactic Lean.Parser.Tactic.decide] def evalDecide : Tactic := fun _ =>
closeMainGoalUsing fun expectedType => do
closeMainGoalUsing `decide fun expectedType => do
let expectedType preprocessPropToDecide expectedType
let d mkDecide expectedType
let d instantiateMVars d
-- Get instance from `d`
let s := d.appArg!
-- Reduce the instance rather than `d` itself, since that gives a nicer error message on failure.
let r withDefault <| whnf s
if r.isAppOf ``isFalse then
throwError "\
tactic 'decide' proved that the proposition\
{indentExpr expectedType}\n\
is false"
unless r.isAppOf ``isTrue do
throwError "\
tactic 'decide' failed for proposition\
{indentExpr expectedType}\n\
since its 'Decidable' instance reduced to\
{indentExpr r}\n\
rather than to the 'isTrue' constructor."
-- While we have a proof from reduction, we do not embed it in the proof term,
-- but rather we let the kernel recompute it during type checking from a more efficient term.
let rflPrf mkEqRefl (toExpr true)
return mkApp3 (Lean.mkConst ``of_decide_eq_true) expectedType s rflPrf
-- Reduce the instance rather than `d` itself for diagnostics purposes.
let r withAtLeastTransparency .default <| whnf s
if r.isAppOf ``isTrue then
-- Success!
-- While we have a proof from reduction, we do not embed it in the proof term,
-- and instead we let the kernel recompute it during type checking from the following more efficient term.
let rflPrf mkEqRefl (toExpr true)
return mkApp3 (Lean.mkConst ``of_decide_eq_true) expectedType s rflPrf
else
-- Diagnose the failure, lazily so that there is no performance impact if `decide` isn't being used interactively.
throwError MessageData.ofLazyM (es := #[expectedType]) do
if r.isAppOf ``isFalse then
return m!"\
tactic 'decide' proved that the proposition\
{indentExpr expectedType}\n\
is false"
-- Re-reduce the instance and collect diagnostics, to get all unfolded Decidable instances
let (reason, unfoldedInsts) withoutModifyingState <| withOptions (fun opt => diagnostics.set opt true) do
modifyDiag (fun _ => {})
let reason withAtLeastTransparency .default <| blameDecideReductionFailure s
let unfolded := ( get).diag.unfoldCounter.foldl (init := #[]) fun cs n _ => cs.push n
let unfoldedInsts unfolded |>.qsort Name.lt |>.filterMapM fun n => do
let e mkConstWithLevelParams n
if ( Meta.isClass? ( inferType e)) == ``Decidable then
return m!"'{MessageData.ofConst e}'"
else
return none
return (reason, unfoldedInsts)
let stuckMsg :=
if unfoldedInsts.isEmpty then
m!"Reduction got stuck at the '{MessageData.ofConstName ``Decidable}' instance{indentExpr reason}"
else
let instances := if unfoldedInsts.size == 1 then "instance" else "instances"
m!"After unfolding the {instances} {MessageData.andList unfoldedInsts.toList}, \
reduction got stuck at the '{MessageData.ofConstName ``Decidable}' instance{indentExpr reason}"
let hint :=
if reason.isAppOf ``Eq.rec then
m!"\n\n\
Hint: Reduction got stuck on '▸' ({MessageData.ofConstName ``Eq.rec}), \
which suggests that one of the '{MessageData.ofConstName ``Decidable}' instances is defined using tactics such as 'rw' or 'simp'. \
To avoid tactics, make use of functions such as \
'{MessageData.ofConstName ``inferInstanceAs}' or '{MessageData.ofConstName ``decidable_of_decidable_of_iff}' \
to alter a proposition."
else if reason.isAppOf ``Classical.choice then
m!"\n\n\
Hint: Reduction got stuck on '{MessageData.ofConstName ``Classical.choice}', \
which indicates that a '{MessageData.ofConstName ``Decidable}' instance \
is defined using classical reasoning, proving an instance exists rather than giving a concrete construction. \
The 'decide' tactic works by evaluating a decision procedure via reduction, and it cannot make progress with such instances. \
This can occur due to the 'opened scoped Classical' command, which enables the instance \
'{MessageData.ofConstName ``Classical.propDecidable}'."
else
MessageData.nil
return m!"\
tactic 'decide' failed for proposition\
{indentExpr expectedType}\n\
since its '{MessageData.ofConstName ``Decidable}' instance\
{indentExpr s}\n\
did not reduce to '{MessageData.ofConstName ``isTrue}' or '{MessageData.ofConstName ``isFalse}'.\n\n\
{stuckMsg}{hint}"
private def mkNativeAuxDecl (baseName : Name) (type value : Expr) : TermElabM Name := do
let auxName Term.mkAuxName baseName
@@ -408,7 +473,7 @@ private def mkNativeAuxDecl (baseName : Name) (type value : Expr) : TermElabM Na
pure auxName
@[builtin_tactic Lean.Parser.Tactic.nativeDecide] def evalNativeDecide : Tactic := fun _ =>
closeMainGoalUsing fun expectedType => do
closeMainGoalUsing `nativeDecide fun expectedType => do
let expectedType preprocessPropToDecide expectedType
let d mkDecide expectedType
let auxDeclName mkNativeAuxDecl `_nativeDecide (Lean.mkConst `Bool) d

View File

@@ -5,14 +5,179 @@ Authors: Gabriel Ebner, Mario Carneiro
-/
prelude
import Init.Ext
import Lean.Elab.DeclarationRange
import Lean.Elab.Tactic.RCases
import Lean.Elab.Tactic.Repeat
import Lean.Elab.Tactic.BuiltinTactic
import Lean.Elab.Command
import Lean.Linter.Util
/-!
# Implementation of the `@[ext]` attribute
-/
namespace Lean.Elab.Tactic.Ext
open Meta Term
/-!
### Meta code for creating ext theorems
-/
/--
Constructs the hypotheses for the structure extensionality theorem that
states that two structures are equal if their fields are equal.
Calls the continuation `k` with the list of parameters to the structure,
two structure variables `x` and `y`, and a list of pairs `(field, ty)`
where each `ty` is of the form `x.field = y.field` or `HEq x.field y.field`.
If `flat` parses to `true`, any fields inherited from parent structures
are treated as fields of the given structure type.
If it is `false`, then the behind-the-scenes encoding of inherited fields
is visible in the extensionality lemma.
-/
def withExtHyps (struct : Name) (flat : Bool)
(k : Array Expr (x y : Expr) Array (Name × Expr) MetaM α) : MetaM α := do
unless isStructure ( getEnv) struct do throwError "not a structure: {struct}"
let structC mkConstWithLevelParams struct
forallTelescope ( inferType structC) fun params _ => do
withNewBinderInfos (params.map (·.fvarId!, BinderInfo.implicit)) do
withLocalDecl `x .implicit (mkAppN structC params) fun x => do
withLocalDecl `y .implicit (mkAppN structC params) fun y => do
let mut hyps := #[]
let fields if flat then
pure <| getStructureFieldsFlattened ( getEnv) struct (includeSubobjectFields := false)
else
pure <| getStructureFields ( getEnv) struct
for field in fields do
let x_f mkProjection x field
let y_f mkProjection y field
unless isProof x_f do
hyps := hyps.push (field, mkEqHEq x_f y_f)
k params x y hyps
/--
Creates the type of the extensionality theorem for the given structure,
returning `∀ {x y : Struct}, x.1 = y.1 → x.2 = y.2 → x = y`, for example.
-/
def mkExtType (structName : Name) (flat : Bool) : MetaM Expr := withLCtx {} {} do
withExtHyps structName flat fun params x y hyps => do
let ty := hyps.foldr (init := mkEq x y) fun (f, h) ty => .forallE f h ty .default
mkForallFVars (params |>.push x |>.push y) ty
/--
Derives the type of the `iff` form of an ext theorem.
-/
def mkExtIffType (extThmName : Name) : MetaM Expr := withLCtx {} {} do
forallTelescopeReducing ( getConstInfo extThmName).type fun args ty => do
let failNotEq := throwError "expecting a theorem proving x = y, but instead it proves{indentD ty}"
let some (_, x, y) := ty.eq? | failNotEq
let some xIdx := args.findIdx? (· == x) | failNotEq
let some yIdx := args.findIdx? (· == y) | failNotEq
unless xIdx + 1 == yIdx do
throwError "expecting {x} and {y} to be consecutive arguments"
let startIdx := yIdx + 1
let toRevert := args[startIdx:].toArray
let fvars toRevert.foldlM (init := {}) (fun st e => return collectFVars st ( inferType e))
for fvar in toRevert do
unless Meta.isProof fvar do
throwError "argument {fvar} is not a proof, which is not supported for arguments after {x} and {y}"
if fvars.fvarSet.contains fvar.fvarId! then
throwError "argument {fvar} is depended upon, which is not supported for arguments after {x} and {y}"
let conj := mkAndN ( toRevert.mapM (inferType ·)).toList
-- Make everything implicit except for inst implicits
let mut newBis := #[]
for fvar in args[0:startIdx] do
if ( fvar.fvarId!.getBinderInfo) matches .default | .strictImplicit then
newBis := newBis.push (fvar.fvarId!, .implicit)
withNewBinderInfos newBis do
mkForallFVars args[:startIdx] <| mkIff ty conj
/--
Ensures that the given structure has an ext theorem, without validating any pre-existing theorems.
Returns the name of the ext theorem.
See `Lean.Elab.Tactic.Ext.withExtHyps` for an explanation of the `flat` argument.
-/
def realizeExtTheorem (structName : Name) (flat : Bool) : Elab.Command.CommandElabM Name := do
unless isStructure ( getEnv) structName do
throwError "'{structName}' is not a structure"
let extName := structName.mkStr "ext"
unless ( getEnv).contains extName do
try
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extName do
let type mkExtType structName flat
let pf withSynthesize do
let indVal getConstInfoInduct structName
let params := Array.mkArray indVal.numParams ( `(_))
Elab.Term.elabTermEnsuringType (expectedType? := type) (implicitLambda := false)
-- introduce the params, do cases on 'x' and 'y', and then substitute each equation
( `(by intro $params* {..} {..}; intros; subst_eqs; rfl))
let pf instantiateMVars pf
if pf.hasMVar then throwError "(internal error) synthesized ext proof contains metavariables{indentD pf}"
let info getConstInfo structName
addDecl <| Declaration.thmDecl {
name := extName
type
value := pf
levelParams := info.levelParams
}
modifyEnv fun env => addProtected env extName
Lean.addDeclarationRanges extName {
range := getDeclarationRange ( getRef)
selectionRange := getDeclarationRange ( getRef) }
catch e =>
throwError m!"\
Failed to generate an 'ext' theorem for '{MessageData.ofConstName structName}': {e.toMessageData}"
return extName
/--
Given an 'ext' theorem, ensures that there is an iff version of the theorem (if possible),
without validating any pre-existing theorems.
Returns the name of the 'ext_iff' theorem.
-/
def realizeExtIffTheorem (extName : Name) : Elab.Command.CommandElabM Name := do
let extIffName : Name :=
match extName with
| .str n s => .str n (s ++ "_iff")
| _ => .str extName "ext_iff"
unless ( getEnv).contains extIffName do
try
let info getConstInfo extName
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extIffName do
let type mkExtIffType extName
let pf withSynthesize do
Elab.Term.elabTermEnsuringType (expectedType? := type) <| `(by
intros
refine ?_, ?_
· intro h; cases h; and_intros <;> (intros; first | rfl | simp | fail "Failed to prove converse of ext theorem")
· intro; (repeat cases _ _); apply $(mkCIdent extName) <;> assumption)
let pf instantiateMVars pf
if pf.hasMVar then throwError "(internal error) synthesized ext_iff proof contains metavariables{indentD pf}"
addDecl <| Declaration.thmDecl {
name := extIffName
type
value := pf
levelParams := info.levelParams
}
-- Only declarations in a namespace can be protected:
unless extIffName.isAtomic do
modifyEnv fun env => addProtected env extIffName
Lean.addDeclarationRanges extIffName {
range := getDeclarationRange ( getRef)
selectionRange := getDeclarationRange ( getRef) }
catch e =>
throwError m!"\
Failed to generate an 'ext_iff' theorem from '{MessageData.ofConstName extName}': {e.toMessageData}\n\
\n\
Try '@[ext (iff := false)]' to prevent generating an 'ext_iff' theorem."
return extIffName
/-!
### Attribute
-/
/-- Information about an extensionality theorem, stored in the environment extension. -/
structure ExtTheorem where
/-- Declaration name of the extensionality theorem. -/
@@ -66,9 +231,9 @@ def ExtTheorems.eraseCore (d : ExtTheorems) (declName : Name) : ExtTheorems :=
{ d with erased := d.erased.insert declName }
/--
Erases a name marked as a `ext` attribute.
Check that it does in fact have the `ext` attribute by making sure it names a `ExtTheorem`
found somewhere in the state's tree, and is not erased.
Erases a name marked as a `ext` attribute.
Check that it does in fact have the `ext` attribute by making sure it names a `ExtTheorem`
found somewhere in the state's tree, and is not erased.
-/
def ExtTheorems.erase [Monad m] [MonadError m] (d : ExtTheorems) (declName : Name) :
m ExtTheorems := do
@@ -79,97 +244,40 @@ def ExtTheorems.erase [Monad m] [MonadError m] (d : ExtTheorems) (declName : Nam
builtin_initialize registerBuiltinAttribute {
name := `ext
descr := "Marks a theorem as an extensionality theorem"
add := fun declName stx kind => do
let `(attr| ext $[(flat := $f)]? $(prio)?) := stx
| throwError "unexpected @[ext] attribute {stx}"
add := fun declName stx kind => MetaM.run' do
let `(attr| ext $[(iff := false%$iffFalse?)]? $[(flat := false%$flatFalse?)]? $(prio)?) := stx
| throwError "invalid syntax for 'ext' attribute"
let iff := iffFalse?.isNone
let flat := flatFalse?.isNone
let mut declName := declName
if isStructure ( getEnv) declName then
liftCommandElabM <| Elab.Command.elabCommand <|
`(declare_ext_theorems_for $[(flat := $f)]? $(mkCIdentFrom stx declName) $[$prio]?)
else MetaM.run' do
if let some flat := f then
throwErrorAt flat "unexpected 'flat' config on @[ext] theorem"
let declTy := ( getConstInfo declName).type
let (_, _, declTy) withDefault <| forallMetaTelescopeReducing declTy
let failNotEq := throwError
"@[ext] attribute only applies to structures or theorems proving x = y, got {declTy}"
let some (ty, lhs, rhs) := declTy.eq? | failNotEq
unless lhs.isMVar && rhs.isMVar do failNotEq
let keys withReducible <| DiscrTree.mkPath ty extExt.config
let priority liftCommandElabM do Elab.liftMacroM do
evalPrio (prio.getD ( `(prio| default)))
extExtension.add {declName, keys, priority} kind
declName liftCommandElabM <| withRef stx <| realizeExtTheorem declName flat
else if let some stx := flatFalse? then
throwErrorAt stx "unexpected 'flat' configuration on @[ext] theorem"
-- Validate and add theorem to environment extension
let declTy := ( getConstInfo declName).type
let (_, _, declTy) withDefault <| forallMetaTelescopeReducing declTy
let failNotEq := throwError "\
@[ext] attribute only applies to structures and to theorems proving 'x = y' where 'x' and 'y' are variables, \
but this theorem proves{indentD declTy}"
let some (ty, lhs, rhs) := declTy.eq? | failNotEq
unless lhs.isMVar && rhs.isMVar do failNotEq
let keys withReducible <| DiscrTree.mkPath ty extExt.config
let priority liftCommandElabM <| Elab.liftMacroM do evalPrio (prio.getD ( `(prio| default)))
extExtension.add {declName, keys, priority} kind
-- Realize iff theorem
if iff then
discard <| liftCommandElabM <| withRef stx <| realizeExtIffTheorem declName
erase := fun declName => do
let s := extExtension.getState ( getEnv)
let s s.erase declName
modifyEnv fun env => extExtension.modifyState env fun _ => s
}
/--
Constructs the hypotheses for the structure extensionality theorem that
states that two structures are equal if their fields are equal.
Calls the continuation `k` with the list of parameters to the structure,
two structure variables `x` and `y`, and a list of pairs `(field, ty)`
where `ty` is `x.field = y.field` or `HEq x.field y.field`.
If `flat` parses to `true`, any fields inherited from parent structures
are treated fields of the given structure type.
If it is `false`, then the behind-the-scenes encoding of inherited fields
is visible in the extensionality lemma.
/-!
### Implementation of `ext` tactic
-/
-- TODO: this is probably the wrong place to have this function
def withExtHyps (struct : Name) (flat : Term)
(k : Array Expr (x y : Expr) Array (Name × Expr) MetaM α) : MetaM α := do
let flat match flat with
| `(true) => pure true
| `(false) => pure false
| _ => throwErrorAt flat "expected 'true' or 'false'"
unless isStructure ( getEnv) struct do throwError "not a structure: {struct}"
let structC mkConstWithLevelParams struct
forallTelescope ( inferType structC) fun params _ => do
withNewBinderInfos (params.map (·.fvarId!, BinderInfo.implicit)) do
withLocalDeclD `x (mkAppN structC params) fun x => do
withLocalDeclD `y (mkAppN structC params) fun y => do
let mut hyps := #[]
let fields if flat then
pure <| getStructureFieldsFlattened ( getEnv) struct (includeSubobjectFields := false)
else
pure <| getStructureFields ( getEnv) struct
for field in fields do
let x_f mkProjection x field
let y_f mkProjection y field
if isProof x_f then
pure ()
else if isDefEq ( inferType x_f) ( inferType y_f) then
hyps := hyps.push (field, mkEq x_f y_f)
else
hyps := hyps.push (field, mkHEq x_f y_f)
k params x y hyps
/--
Creates the type of the extensionality theorem for the given structure,
elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
-/
@[builtin_term_elab extType] def elabExtType : TermElab := fun stx _ => do
match stx with
| `(ext_type% $flat:term $struct:ident) => do
withExtHyps ( realizeGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
let ty := hyps.foldr (init := mkEq x y) fun (f, h) ty =>
mkForall f BinderInfo.default h ty
mkForallFVars (params |>.push x |>.push y) ty
| _ => throwUnsupportedSyntax
/--
Creates the type of the iff-variant of the extensionality theorem for the given structure,
elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
-/
@[builtin_term_elab extIffType] def elabExtIffType : TermElab := fun stx _ => do
match stx with
| `(ext_iff_type% $flat:term $struct:ident) => do
withExtHyps ( realizeGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
mkForallFVars (params |>.push x |>.push y) <|
mkIff ( mkEq x y) <| mkAndN (hyps.map (·.2)).toList
| _ => throwUnsupportedSyntax
/-- Apply a single extensionality theorem to `goal`. -/
def applyExtTheoremAt (goal : MVarId) : MetaM (List MVarId) := goal.withContext do

View File

@@ -564,7 +564,7 @@ def getInductiveValFromMajor (major : Expr) : TacticM InductiveVal :=
/--
Elaborates the term in the `using` clause. We want to allow parameters to be instantiated
(e.g. `using foo (p := …)`), but preserve other paramters, like the motives, as parameters,
(e.g. `using foo (p := …)`), but preserve other parameters, like the motives, as parameters,
without turning them into MVars. So this uses `abstractMVars` at the end. This is inspired by
`Lean.Elab.Tactic.addSimpTheorem`.

View File

@@ -254,7 +254,17 @@ where
| _ => match n.getAppFnArgs with
| (``Nat.succ, #[n]) => rewrite e (.app (.const ``Int.ofNat_succ []) n)
| (``HAdd.hAdd, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_add []) a b)
| (``HMul.hMul, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_mul []) a b)
| (``HMul.hMul, #[_, _, _, _, a, b]) =>
-- Don't push the cast into a multiplication unless it produces a non-trivial linear combination.
let r? commitWhen do
let (lc, prf, r) rewrite e (mkApp2 (.const ``Int.ofNat_mul []) a b)
if lc.isAtom then
pure (none, false)
else
pure (some (lc, prf, r), true)
match r? with
| some r => pure r
| none => mkAtomLinearCombo e
| (``HDiv.hDiv, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_ediv []) a b)
| (``OfNat.ofNat, #[_, n, _]) => rewrite e (.app (.const ``Int.natCast_ofNat []) n)
| (``HMod.hMod, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_emod []) a b)

View File

@@ -1827,9 +1827,13 @@ def isLetRecAuxMVar (mvarId : MVarId) : TermElabM Bool := do
/--
Create an `Expr.const` using the given name and explicit levels.
Remark: fresh universe metavariables are created if the constant has more universe
parameters than `explicitLevels`. -/
def mkConst (constName : Name) (explicitLevels : List Level := []) : TermElabM Expr := do
Linter.checkDeprecated constName -- TODO: check is occurring too early if there are multiple alternatives. Fix if it is not ok in practice
parameters than `explicitLevels`.
If `checkDeprecated := true`, then `Linter.checkDeprecated` is invoked.
-/
def mkConst (constName : Name) (explicitLevels : List Level := []) (checkDeprecated := true) : TermElabM Expr := do
if checkDeprecated then
Linter.checkDeprecated constName
let cinfo getConstInfo constName
if explicitLevels.length > cinfo.levelParams.length then
throwError "too many explicit universe levels for '{constName}'"
@@ -1838,10 +1842,21 @@ def mkConst (constName : Name) (explicitLevels : List Level := []) : TermElabM E
let us mkFreshLevelMVars numMissingLevels
return Lean.mkConst constName (explicitLevels ++ us)
def checkDeprecated (ref : Syntax) (e : Expr) : TermElabM Unit := do
if let .const declName _ := e.getAppFn then
withRef ref do Linter.checkDeprecated declName
private def mkConsts (candidates : List (Name × List String)) (explicitLevels : List Level) : TermElabM (List (Expr × List String)) := do
candidates.foldlM (init := []) fun result (declName, projs) => do
-- TODO: better support for `mkConst` failure. We may want to cache the failures, and report them if all candidates fail.
let const mkConst declName explicitLevels
/-
We disable `checkDeprecated` here because there may be many overloaded symbols.
Note that, this method and `resolveName` and `resolveName'` return a list of pairs instead of a list of `TermElabResult`s.
We perform the `checkDeprecated` test at `resolveId?` and `elabAppFnId`.
At `elabAppFnId`, we perform the check when converting the list returned by `resolveName'` into a list of
`TermElabResult`s.
-/
let const mkConst declName explicitLevels (checkDeprecated := false)
return (const, projs) :: result
def resolveName (stx : Syntax) (n : Name) (preresolved : List Syntax.Preresolved) (explicitLevels : List Level) (expectedType? : Option Expr := none) : TermElabM (List (Expr × List String)) := do
@@ -1895,11 +1910,11 @@ def resolveId? (stx : Syntax) (kind := "term") (withInfo := false) : TermElabM (
| [] => return none
| [f] =>
let f if withInfo then addTermInfo stx f else pure f
checkDeprecated stx f
return some f
| _ => throwError "ambiguous {kind}, use fully qualified name, possible interpretations {fs}"
| _ => throwError "identifier expected"
def TermElabM.run (x : TermElabM α) (ctx : Context := {}) (s : State := {}) : MetaM (α × State) :=
withConfig setElabConfig (x ctx |>.run s)

View File

@@ -868,9 +868,9 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
-- Recall that the map has not been modified when `cinfoPrev? = some _`.
unless equivInfo cinfoPrev cinfo do
throwAlreadyImported s const2ModIdx modIdx cname
const2ModIdx := const2ModIdx.insert cname modIdx
const2ModIdx := const2ModIdx.insertIfNew cname modIdx |>.1
for cname in mod.extraConstNames do
const2ModIdx := const2ModIdx.insert cname modIdx
const2ModIdx := const2ModIdx.insertIfNew cname modIdx |>.1
let constants : ConstMap := SMap.fromHashMap constantMap false
let exts mkInitialExtensionStates
let mut env : Environment := {

View File

@@ -504,6 +504,14 @@ def mkArrayLit (type : Expr) (xs : List Expr) : MetaM Expr := do
let listLit mkListLit type xs
return mkApp (mkApp (mkConst ``List.toArray [u]) type) listLit
def mkNone (type : Expr) : MetaM Expr := do
let u getDecLevel type
return mkApp (mkConst ``Option.none [u]) type
def mkSome (type value : Expr) : MetaM Expr := do
let u getDecLevel type
return mkApp2 (mkConst ``Option.some [u]) type value
def mkSorry (type : Expr) (synthetic : Bool) : MetaM Expr := do
let u getLevel type
return mkApp2 (mkConst ``sorryAx [u]) type (toExpr synthetic)

View File

@@ -1492,6 +1492,16 @@ private def withLocalContextImp (lctx : LocalContext) (localInsts : LocalInstanc
def withLCtx (lctx : LocalContext) (localInsts : LocalInstances) : n α n α :=
mapMetaM <| withLocalContextImp lctx localInsts
/--
Runs `k` in a local envrionment with the `fvarIds` erased.
-/
def withErasedFVars [MonadLCtx n] [MonadLiftT MetaM n] (fvarIds : Array FVarId) (k : n α) : n α := do
let lctx getLCtx
let localInsts getLocalInstances
let lctx' := fvarIds.foldl (·.erase ·) lctx
let localInsts' := localInsts.filter (!fvarIds.contains ·.fvar.fvarId!)
withLCtx lctx' localInsts' k
private def withMVarContextImp (mvarId : MVarId) (x : MetaM α) : MetaM α := do
let mvarDecl mvarId.getDecl
withLocalContextImp mvarDecl.lctx mvarDecl.localInstances x
@@ -1855,9 +1865,13 @@ abbrev isDefEqGuarded (t s : Expr) : MetaM Bool :=
def isDefEqNoConstantApprox (t s : Expr) : MetaM Bool :=
approxDefEq <| isDefEq t s
/-- Shorthand for `isDefEq (mkMVar mvarId) val` -/
def _root_.Lean.MVarId.checkedAssign (mvarId : MVarId) (val : Expr) : MetaM Bool :=
isDefEq (mkMVar mvarId) val
/--
Returns `true` if `mvarId := val` was successfully assigned.
This method uses the same assignment validation performed by `isDefEq`, but it does not check whether the types match.
-/
-- Remark: this method is implemented at `ExprDefEq`
@[extern "lean_checked_assign"]
opaque _root_.Lean.MVarId.checkedAssign (mvarId : MVarId) (val : Expr) : MetaM Bool
/--
Eta expand the given expression.

View File

@@ -85,9 +85,8 @@ of type
```
α → List α → Sort (max 1 u_1) → Sort (max 1 u_1)
```
The parameter `typeFormers` are the `motive`s.
-/
private def buildBelowMinorPremise (rlvl : Level) (typeFormers : Array Expr) (minorType : Expr) : MetaM Expr :=
private def buildBelowMinorPremise (rlvl : Level) (motives : Array Expr) (minorType : Expr) : MetaM Expr :=
forallTelescope minorType fun minor_args _ => do go #[] minor_args.toList
where
ibelow := rlvl matches .zero
@@ -96,7 +95,7 @@ where
| arg::args => do
let argType inferType arg
forallTelescope argType fun arg_args arg_type => do
if typeFormers.contains arg_type.getAppFn then
if motives.contains arg_type.getAppFn then
let name arg.fvarId!.getUserName
let type' forallTelescope argType fun args _ => mkForallFVars args (.sort rlvl)
withLocalDeclD name type' fun arg' => do
@@ -124,81 +123,100 @@ fun {α} {motive} t =>
List.rec True (fun head tail tail_ih => (motive tail ∧ tail_ih) ∧ True) t
```
-/
private def mkBelowOrIBelow (indName : Name) (ibelow : Bool) : MetaM Unit := do
let .inductInfo indVal getConstInfo indName | return
unless indVal.isRec do return
if isPropFormerType indVal.type then return
let recName := mkRecName indName
private def mkBelowFromRec (recName : Name) (ibelow reflexive : Bool) (nParams : Nat)
(belowName : Name) : MetaM Unit := do
-- The construction follows the type of `ind.rec`
let .recInfo recVal getConstInfo recName
| throwError "{recName} not a .recInfo"
let lvl::lvls := recVal.levelParams.map (Level.param ·)
| throwError "recursor {recName} has no levelParams"
let lvlParam := recVal.levelParams.head!
-- universe parameter names of ibelow/below
let blvls :=
-- For ibelow we instantiate the first universe parameter of `.rec` to `.zero`
if ibelow then recVal.levelParams.tail!
else recVal.levelParams
let .some ilvl typeFormerTypeLevel indVal.type
| throwError "type {indVal.type} of inductive {indVal.name} not a type former?"
-- universe level of the resultant type
let rlvl : Level :=
if ibelow then
0
else if indVal.isReflexive then
if let .max 1 ilvl' := ilvl then
mkLevelMax' (.succ lvl) ilvl'
else
mkLevelMax' (.succ lvl) ilvl
else
mkLevelMax' 1 lvl
let refType :=
if ibelow then
recVal.type.instantiateLevelParams [lvlParam] [0]
else if indVal.isReflexive then
else if reflexive then
recVal.type.instantiateLevelParams [lvlParam] [lvl.succ]
else
recVal.type
let decl forallTelescope refType fun refArgs _ => do
assert! refArgs.size == indVal.numParams + recVal.numMotives + recVal.numMinors + indVal.numIndices + 1
let params : Array Expr := refArgs[:indVal.numParams]
let typeFormers : Array Expr := refArgs[indVal.numParams:indVal.numParams + recVal.numMotives]
let minors : Array Expr := refArgs[indVal.numParams + recVal.numMotives:indVal.numParams + recVal.numMotives + recVal.numMinors]
let remaining : Array Expr := refArgs[indVal.numParams + recVal.numMotives + recVal.numMinors:]
assert! refArgs.size > nParams + recVal.numMotives + recVal.numMinors
let params : Array Expr := refArgs[:nParams]
let motives : Array Expr := refArgs[nParams:nParams + recVal.numMotives]
let minors : Array Expr := refArgs[nParams + recVal.numMotives:nParams + recVal.numMotives + recVal.numMinors]
let indices : Array Expr := refArgs[nParams + recVal.numMotives + recVal.numMinors:refArgs.size - 1]
let major : Expr := refArgs[refArgs.size - 1]!
-- universe parameter names of ibelow/below
let blvls :=
-- For ibelow we instantiate the first universe parameter of `.rec` to `.zero`
if ibelow then recVal.levelParams.tail!
else recVal.levelParams
-- universe parameter of the type fomer.
-- same as `typeFormerTypeLevel indVal.type`, but we want to infer it from the
-- type of the recursor, to be more robust when facing nested induction
let majorTypeType inferType ( inferType major)
let .some ilvl typeFormerTypeLevel majorTypeType
| throwError "type of type of major premise {major} not a type former"
-- universe level of the resultant type
let rlvl : Level :=
if ibelow then
0
else if reflexive then
if let .max 1 ilvl' := ilvl then
mkLevelMax' (.succ lvl) ilvl'
else
mkLevelMax' (.succ lvl) ilvl
else
mkLevelMax' 1 lvl
let mut val := .const recName (rlvl.succ :: lvls)
-- add parameters
val := mkAppN val params
-- add type formers
for typeFormer in typeFormers do
let arg forallTelescope ( inferType typeFormer) fun targs _ =>
for motive in motives do
let arg forallTelescope ( inferType motive) fun targs _ =>
mkLambdaFVars targs (.sort rlvl)
val := .app val arg
-- add minor premises
for minor in minors do
let arg buildBelowMinorPremise rlvl typeFormers ( inferType minor)
let arg buildBelowMinorPremise rlvl motives ( inferType minor)
val := .app val arg
-- add indices and major premise
val := mkAppN val remaining
val := mkAppN val indices
val := mkApp val major
-- All paramaters of `.rec` besides the `minors` become parameters of `.below`
let below_params := params ++ typeFormers ++ remaining
let below_params := params ++ motives ++ indices ++ #[major]
let type mkForallFVars below_params (.sort rlvl)
val mkLambdaFVars below_params val
let name := if ibelow then mkIBelowName indName else mkBelowName indName
mkDefinitionValInferrringUnsafe name blvls type val .abbrev
mkDefinitionValInferrringUnsafe belowName blvls type val .abbrev
addDecl (.defnDecl decl)
setReducibleAttribute decl.name
modifyEnv fun env => markAuxRecursor env decl.name
modifyEnv fun env => addProtected env decl.name
private def mkBelowOrIBelow (indName : Name) (ibelow : Bool) : MetaM Unit := do
let .inductInfo indVal getConstInfo indName | return
unless indVal.isRec do return
if isPropFormerType indVal.type then return
let recName := mkRecName indName
let belowName := if ibelow then mkIBelowName indName else mkBelowName indName
mkBelowFromRec recName ibelow indVal.isReflexive indVal.numParams belowName
-- If this is the first inductive in a mutual group with nested inductives,
-- generate the constructions for the nested inductives now
if indVal.all[0]! = indName then
for i in [:indVal.numNested] do
let recName := recName.appendIndexAfter (i + 1)
let belowName := belowName.appendIndexAfter (i + 1)
mkBelowFromRec recName ibelow indVal.isReflexive indVal.numParams belowName
def mkBelow (declName : Name) : MetaM Unit := mkBelowOrIBelow declName true
def mkIBelow (declName : Name) : MetaM Unit := mkBelowOrIBelow declName false
@@ -219,22 +237,21 @@ of type
PProd (motive tail) (List.below tail) →
PProd (motive (head :: tail)) (PProd (PProd (motive tail) (List.below tail)) PUnit)
```
The parameter `typeFormers` are the `motive`s.
-/
private def buildBRecOnMinorPremise (rlvl : Level) (typeFormers : Array Expr)
private def buildBRecOnMinorPremise (rlvl : Level) (motives : Array Expr)
(belows : Array Expr) (fs : Array Expr) (minorType : Expr) : MetaM Expr :=
forallTelescope minorType fun minor_args minor_type => do
let rec go (prods : Array Expr) : List Expr MetaM Expr
| [] => minor_type.withApp fun minor_type_fn minor_type_args => do
let b mkNProdMk rlvl prods
let .some idx, _ := typeFormers.indexOf? minor_type_fn
| throwError m!"Did not find {minor_type} in {typeFormers}"
let .some idx, _ := motives.indexOf? minor_type_fn
| throwError m!"Did not find {minor_type} in {motives}"
mkPProdMk (mkAppN fs[idx]! (minor_type_args.push b)) b
| arg::args => do
let argType inferType arg
forallTelescope argType fun arg_args arg_type => do
arg_type.withApp fun arg_type_fn arg_type_args => do
if let .some idx := typeFormers.indexOf? arg_type_fn then
if let .some idx := motives.indexOf? arg_type_fn then
let name arg.fvarId!.getUserName
let type' mkForallFVars arg_args
( mkPProd arg_type (mkAppN belows[idx]! arg_type_args) )
@@ -277,81 +294,72 @@ fun {α} {motive} t F_1 => (
).1
```
-/
def mkBRecOnOrBInductionOn (indName : Name) (ind : Bool) : MetaM Unit := do
let .inductInfo indVal getConstInfo indName | return
unless indVal.isRec do return
if isPropFormerType indVal.type then return
let recName := mkRecName indName
private def mkBRecOnFromRec (recName : Name) (ind reflexive : Bool) (nParams : Nat)
(all : Array Name) (brecOnName : Name) : MetaM Unit := do
let .recInfo recVal getConstInfo recName | return
unless recVal.numMotives = indVal.all.length do
/-
The mutual declaration containing `declName` contains nested inductive datatypes.
We don't support this kind of declaration here yet. We probably never will :)
To support it, we will need to generate an auxiliary `below` for each nested inductive
type since their default `below` is not good here. For example, at
```
inductive Term
| var : String -> Term
| app : String -> List Term -> Term
```
The `List.below` is not useful since it will not allow us to recurse over the nested terms.
We need to generate another one using the auxiliary recursor `Term.rec_1` for `List Term`.
-/
return
let lvl::lvls := recVal.levelParams.map (Level.param ·)
| throwError "recursor {recName} has no levelParams"
let lvlParam := recVal.levelParams.head!
-- universe parameter names of brecOn/binductionOn
let blps := if ind then recVal.levelParams.tail! else recVal.levelParams
-- universe arguments of below/ibelow
let blvls := if ind then lvls else lvl::lvls
let .some idx, _ := indVal.all.toArray.indexOf? indName
| throwError m!"Did not find {indName} in {indVal.all}"
let .some ilvl typeFormerTypeLevel indVal.type
| throwError "type {indVal.type} of inductive {indVal.name} not a type former?"
-- universe level of the resultant type
let rlvl : Level :=
if ind then
0
else if indVal.isReflexive then
if let .max 1 ilvl' := ilvl then
mkLevelMax' (.succ lvl) ilvl'
else
mkLevelMax' (.succ lvl) ilvl
else
mkLevelMax' 1 lvl
let refType :=
if ind then
recVal.type.instantiateLevelParams [lvlParam] [0]
else if indVal.isReflexive then
else if reflexive then
recVal.type.instantiateLevelParams [lvlParam] [lvl.succ]
else
recVal.type
let decl forallTelescope refType fun refArgs _ => do
assert! refArgs.size == indVal.numParams + recVal.numMotives + recVal.numMinors + indVal.numIndices + 1
let params : Array Expr := refArgs[:indVal.numParams]
let typeFormers : Array Expr := refArgs[indVal.numParams:indVal.numParams + recVal.numMotives]
let minors : Array Expr := refArgs[indVal.numParams + recVal.numMotives:indVal.numParams + recVal.numMotives + recVal.numMinors]
let remaining : Array Expr := refArgs[indVal.numParams + recVal.numMotives + recVal.numMinors:]
let decl forallTelescope refType fun refArgs refBody => do
assert! refArgs.size > nParams + recVal.numMotives + recVal.numMinors
let params : Array Expr := refArgs[:nParams]
let motives : Array Expr := refArgs[nParams:nParams + recVal.numMotives]
let minors : Array Expr := refArgs[nParams + recVal.numMotives:nParams + recVal.numMotives + recVal.numMinors]
let indices : Array Expr := refArgs[nParams + recVal.numMotives + recVal.numMinors:refArgs.size - 1]
let major : Expr := refArgs[refArgs.size - 1]!
-- One `below` for each type former (same parameters)
let belows := indVal.all.toArray.map fun n =>
let belowName := if ind then mkIBelowName n else mkBelowName n
mkAppN (.const belowName blvls) (params ++ typeFormers)
let some idx := motives.indexOf? refBody.getAppFn
| throwError "result type of {refType} is not one of {motives}"
-- create types of functionals (one for each type former)
-- universe parameter of the type fomer.
-- same as `typeFormerTypeLevel indVal.type`, but we want to infer it from the
-- type of the recursor, to be more robust when facing nested induction
let majorTypeType inferType ( inferType major)
let .some ilvl typeFormerTypeLevel majorTypeType
| throwError "type of type of major premise {major} not a type former"
-- universe level of the resultant type
let rlvl : Level :=
if ind then
0
else if reflexive then
if let .max 1 ilvl' := ilvl then
mkLevelMax' (.succ lvl) ilvl'
else
mkLevelMax' (.succ lvl) ilvl
else
mkLevelMax' 1 lvl
-- One `below` for each motive, with the same motive parameters
let blvls := if ind then lvls else lvl::lvls
let belows := Array.ofFn (n := motives.size) fun i,_ =>
let belowName :=
if let some n := all[i]? then
if ind then mkIBelowName n else mkBelowName n
else
if ind then .str all[0]! s!"ibelow_{i-all.size+1}"
else .str all[0]! s!"below_{i-all.size+1}"
mkAppN (.const belowName blvls) (params ++ motives)
-- create types of functionals (one for each motive)
-- (F_1 : (t : List α) → (f : List.below t) → motive t)
-- and bring parameters of that type into scope
let mut fDecls : Array (Name × (Array Expr -> MetaM Expr)) := #[]
for typeFormer in typeFormers, below in belows, i in [:typeFormers.size] do
let fType forallTelescope ( inferType typeFormer) fun targs _ => do
for motive in motives, below in belows, i in [:motives.size] do
let fType forallTelescope ( inferType motive) fun targs _ => do
withLocalDeclD `f (mkAppN below targs) fun f =>
mkForallFVars (targs.push f) (mkAppN typeFormer targs)
mkForallFVars (targs.push f) (mkAppN motive targs)
let fName := .mkSimple s!"F_{i + 1}"
fDecls := fDecls.push (fName, fun _ => pure fType)
withLocalDeclsD fDecls fun fs => do
@@ -359,35 +367,53 @@ def mkBRecOnOrBInductionOn (indName : Name) (ind : Bool) : MetaM Unit := do
-- add parameters
val := mkAppN val params
-- add type formers
for typeFormer in typeFormers, below in belows do
for motive in motives, below in belows do
-- example: (motive := fun t => PProd (motive t) (@List.below α motive t))
let arg forallTelescope ( inferType typeFormer) fun targs _ => do
let cType := mkAppN typeFormer targs
let arg forallTelescope ( inferType motive) fun targs _ => do
let cType := mkAppN motive targs
let belowType := mkAppN below targs
let arg mkPProd cType belowType
mkLambdaFVars targs arg
val := .app val arg
-- add minor premises
for minor in minors do
let arg buildBRecOnMinorPremise rlvl typeFormers belows fs ( inferType minor)
let arg buildBRecOnMinorPremise rlvl motives belows fs ( inferType minor)
val := .app val arg
-- add indices and major premise
val := mkAppN val remaining
val := mkAppN val indices
val := mkApp val major
-- project out first component
val mkPProdFst val
-- All paramaters of `.rec` besides the `minors` become parameters of `.bRecOn`, and the `fs`
let below_params := params ++ typeFormers ++ remaining ++ fs
let type mkForallFVars below_params (mkAppN typeFormers[idx]! remaining)
let below_params := params ++ motives ++ indices ++ #[major] ++ fs
let type mkForallFVars below_params (mkAppN motives[idx]! (indices ++ #[major]))
val mkLambdaFVars below_params val
let name := if ind then mkBInductionOnName indName else mkBRecOnName indName
mkDefinitionValInferrringUnsafe name blps type val .abbrev
mkDefinitionValInferrringUnsafe brecOnName blps type val .abbrev
addDecl (.defnDecl decl)
setReducibleAttribute decl.name
modifyEnv fun env => markAuxRecursor env decl.name
modifyEnv fun env => addProtected env decl.name
def mkBRecOnOrBInductionOn (indName : Name) (ind : Bool) : MetaM Unit := do
let .inductInfo indVal getConstInfo indName | return
unless indVal.isRec do return
if isPropFormerType indVal.type then return
let recName := mkRecName indName
let brecOnName := if ind then mkBInductionOnName indName else mkBRecOnName indName
mkBRecOnFromRec recName ind indVal.isReflexive indVal.numParams indVal.all.toArray brecOnName
-- If this is the first inductive in a mutual group with nested inductives,
-- generate the constructions for the nested inductives now.
if indVal.all[0]! = indName then
for i in [:indVal.numNested] do
let recName := recName.appendIndexAfter (i + 1)
let brecOnName := brecOnName.appendIndexAfter (i + 1)
mkBRecOnFromRec recName ind indVal.isReflexive indVal.numParams indVal.all.toArray brecOnName
def mkBRecOn (declName : Name) : MetaM Unit := mkBRecOnOrBInductionOn declName false
def mkBInductionOn (declName : Name) : MetaM Unit := mkBRecOnOrBInductionOn declName true

View File

@@ -1046,6 +1046,15 @@ def checkAssignment (mvarId : MVarId) (fvars : Array Expr) (v : Expr) : MetaM (O
return none
return some v
-- Implementation for `_root_.Lean.MVarId.checkedAssign`
@[export lean_checked_assign]
def checkedAssignImpl (mvarId : MVarId) (val : Expr) : MetaM Bool := do
if let some val checkAssignment mvarId #[] val then
mvarId.assign val
return true
else
return false
private def processAssignmentFOApproxAux (mvar : Expr) (args : Array Expr) (v : Expr) : MetaM Bool :=
match v with
| .mdata _ e => processAssignmentFOApproxAux mvar args e

View File

@@ -176,4 +176,48 @@ def litToCtor (e : Expr) : MetaM Expr := do
return mkApp3 (mkConst ``Fin.mk) n i h
return e
/--
Check if an expression is a list literal (i.e. a nested chain of `List.cons`, ending at a `List.nil`),
where each element is "recognised" by a given function `f : Expr → MetaM (Option α)`,
and return the array of recognised values.
-/
partial def getListLitOf? (e : Expr) (f : Expr MetaM (Option α)) : MetaM (Option (Array α)) := do
let mut e instantiateMVars e.consumeMData
let mut r := #[]
while true do
match_expr e with
| List.nil _ => break
| List.cons _ a as => do
let some a f a | return none
r := r.push a
e := as
| _ => return none
return some r
/--
Check if an expression is a list literal (i.e. a nested chain of `List.cons`, ending at a `List.nil`),
returning the array of `Expr` values.
-/
def getListLit? (e : Expr) : MetaM (Option (Array Expr)) := getListLitOf? e fun s => return some s
/--
Check if an expression is an array literal
(i.e. `List.toArray` applied to a nested chain of `List.cons`, ending at a `List.nil`),
where each element is "recognised" by a given function `f : Expr → MetaM (Option α)`,
and return the array of recognised values.
-/
def getArrayLitOf? (e : Expr) (f : Expr MetaM (Option α)) : MetaM (Option (Array α)) := do
let e instantiateMVars e.consumeMData
match_expr e with
| List.toArray _ as => getListLitOf? as f
| _ => return none
/--
Check if an expression is an array literal
(i.e. `List.toArray` applied to a nested chain of `List.cons`, ending at a `List.nil`),
returning the array of `Expr` values.
-/
def getArrayLit? (e : Expr) : MetaM (Option (Array Expr)) := getArrayLitOf? e fun s => return some s
end Lean.Meta

View File

@@ -294,7 +294,7 @@ def transform
altType in altTypes do
let alt' forallAltTelescope' origAltType (numParams - numDiscrEqs) 0 fun ys args => do
let altType instantiateForall altType ys
-- The splitter inserts its extra paramters after the first ys.size parameters, before
-- The splitter inserts its extra parameters after the first ys.size parameters, before
-- the parameters for the numDiscrEqs
forallBoundedTelescope altType (splitterNumParams - ys.size) fun ys2 altType => do
forallBoundedTelescope altType numDiscrEqs fun ys3 altType => do

View File

@@ -164,8 +164,6 @@ partial def mkSizeOfFn (recName : Name) (declName : Name): MetaM Unit := do
-/
def mkSizeOfFns (typeName : Name) : MetaM (Array Name × NameMap Name) := do
let indInfo getConstInfoInduct typeName
let recInfo getConstInfoRec (mkRecName typeName)
let numExtra := recInfo.numMotives - indInfo.all.length -- numExtra > 0 for nested inductive types
let mut result := #[]
let baseName := indInfo.all.head! ++ `_sizeOf -- we use the first inductive type as the base name for `sizeOf` functions
let mut i := 1
@@ -177,7 +175,7 @@ def mkSizeOfFns (typeName : Name) : MetaM (Array Name × NameMap Name) := do
recMap := recMap.insert recName sizeOfName
result := result.push sizeOfName
i := i + 1
for j in [:numExtra] do
for j in [:indInfo.numNested] do
let recName := (mkRecName indInfo.all.head!).appendIndexAfter (j+1)
let sizeOfName := baseName.appendIndexAfter i
mkSizeOfFn recName sizeOfName

View File

@@ -719,7 +719,7 @@ to the continuation
recursion and extra parameters passed to the recursor)
* the position of the motive/induction hypothesis in the body's arguments
* the body, as passed to the recursor. Expected to be a lambda that takes the
varying paramters and the motive
varying parameters and the motive
* a function to re-assemble the call with a new Motive. The resulting expression expects
the new body next, so that the expected type of the body can be inferred
* a function to finish assembling the call with the new body.
@@ -744,8 +744,8 @@ def findRecursor {α} (name : Name) (varNames : Array Name) (e : Expr)
-- Bail out on mutual or nested inductives
let .str indName _ := f.constName! | unreachable!
let indInfo getConstInfoInduct indName
if indInfo.all.length > 1 then
throwError "functional induction: cannot handle mutual inductives"
if indInfo.numTypeFormers > 1 then
throwError "functional induction: cannot handle mutual or nested inductives"
let elimInfo getElimExprInfo f
let targets : Array Expr := elimInfo.targetsPos.map (args[·]!)

View File

@@ -13,3 +13,4 @@ import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Char
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.String
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.BitVec
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.List
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Array

View File

@@ -0,0 +1,36 @@
/-
Copyright (c) 2024 Lean FRO. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Lean.Meta.LitValues
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
namespace Array
open Lean Meta Simp
/-- Simplification procedure for `#[...][n]` for `n` a `Nat` literal. -/
builtin_dsimproc [simp, seval] reduceGetElem (@GetElem.getElem (Array _) Nat _ _ _ _ _ _) := fun e => do
let_expr GetElem.getElem _ _ _ _ _ xs n _ e | return .continue
let some n Nat.fromExpr? n | return .continue
let some xs getArrayLit? xs | return .continue
return .done <| xs[n]!
/-- Simplification procedure for `#[...][n]?` for `n` a `Nat` literal. -/
builtin_dsimproc [simp, seval] reduceGetElem? (@GetElem?.getElem? (Array _) Nat _ _ _ _ _) := fun e => do
let_expr GetElem?.getElem? _ _ α _ _ xs n e | return .continue
let some n Nat.fromExpr? n | return .continue
let some xs getArrayLit? xs | return .continue
let r if h : n < xs.size then mkSome α xs[n] else mkNone α
return .done r
/-- Simplification procedure for `#[...][n]!` for `n` a `Nat` literal. -/
builtin_dsimproc [simp, seval] reduceGetElem! (@GetElem?.getElem! (Array _) Nat _ _ _ _ _ _) := fun e => do
let_expr GetElem?.getElem! _ _ α _ _ I xs n e | return .continue
let some n Nat.fromExpr? n | return .continue
let some xs getArrayLit? xs | return .continue
let r if h : n < xs.size then pure xs[n] else mkDefault α
return .done r
end Array

View File

@@ -142,8 +142,8 @@ def findModuleOf? [Monad m] [MonadEnv m] [MonadError m] (declName : Name) : m (O
def isEnumType [Monad m] [MonadEnv m] [MonadError m] (declName : Name) : m Bool := do
if let ConstantInfo.inductInfo info getConstInfo declName then
if !info.type.isProp && info.all.length == 1 && info.numIndices == 0 && info.numParams == 0
&& !info.ctors.isEmpty && !info.isRec && !info.isNested && !info.isUnsafe then
if !info.type.isProp && info.numTypeFormers == 1 && info.numIndices == 0 && info.numParams == 0
&& !info.ctors.isEmpty && !info.isRec && !info.isUnsafe then
info.ctors.allM fun ctorName => do
let ConstantInfo.ctorInfo info getConstInfo ctorName | return false
return info.numFields == 0

View File

@@ -701,7 +701,7 @@ list, so it should be brief.
@[builtin_command_parser] def genInjectiveTheorems := leading_parser
"gen_injective_theorems% " >> ident
/-- No-op parser used as syntax kind for attaching remaining whitespace to at the end of the input. -/
/-- No-op parser used as syntax kind for attaching remaining whitespace at the end of the input. -/
@[run_builtin_parser_attribute_hooks] def eoi : Parser := leading_parser ""
builtin_initialize

View File

@@ -174,9 +174,11 @@ do not yield the right result.
-/
@[builtin_term_parser] def typeAscription := leading_parser
"(" >> (withoutPosition (withoutForbidden (termParser >> " :" >> optional (ppSpace >> termParser)))) >> ")"
/-- Tuple notation; `()` is short for `Unit.unit`, `(a, b, c)` for `Prod.mk a (Prod.mk b c)`, etc. -/
@[builtin_term_parser] def tuple := leading_parser
"(" >> optional (withoutPosition (withoutForbidden (termParser >> ", " >> sepBy1 termParser ", " (allowTrailingSep := true)))) >> ")"
/--
Parentheses, used for grouping expressions (e.g., `a * (b + c)`).
Can also be used for creating simple functions when combined with `·`. Here are some examples:

View File

@@ -128,10 +128,12 @@ def ofLazyM (f : MetaM MessageData) (es : Array Expr := #[]) : MessageData :=
instantiateMVarsCore mvarctxt a |>.1.hasSyntheticSorry
))
/-- Pretty print a const expression using `delabConst` and generate terminfo.
/--
Pretty print a const expression using `delabConst` and generate terminfo.
This function avoids inserting `@` if the constant is for a function whose first
argument is implicit, which is what the default `toMessageData` for `Expr` does.
Panics if `e` is not a constant. -/
Panics if `e` is not a constant.
-/
def ofConst (e : Expr) : MessageData :=
if e.isConst then
let delab : Delab := withOptionAtCurrPos `pp.tagAppFns true delabConst
@@ -139,6 +141,19 @@ def ofConst (e : Expr) : MessageData :=
else
panic! "not a constant"
/--
Pretty print a constant given its name, similar to `Lean.MessageData.ofConst`.
Uses the constant's universe level parameters when pretty printing.
If there is no such constant in the environment, the name is simply formatted.
-/
def ofConstName (constName : Name) : MessageData :=
.ofFormatWithInfosM do
if let some info := ( getEnv).find? constName then
let delab : Delab := withOptionAtCurrPos `pp.tagAppFns true delabConst
PrettyPrinter.ppExprWithInfos (delab := delab) (.const constName <| info.levelParams.map mkLevelParam)
else
return format constName
/-- Generates `MessageData` for a declaration `c` as `c.{<levels>} <params> : <type>`, with terminfo. -/
def signature (c : Name) : MessageData :=
.ofFormatWithInfosM (PrettyPrinter.ppSignature c)

View File

@@ -199,6 +199,10 @@ def unexpandStructureInstance (stx : Syntax) : Delab := whenPPOption getPPStruct
let mut fields := #[]
guard $ fieldNames.size == stx[1].getNumArgs
if hasPPUsingAnonymousConstructorAttribute env s.induct then
/- Note that we don't flatten anonymous constructor notation. Only a complete such notation receives TermInfo,
and flattening would cause the flattened-in notation to lose its TermInfo.
Potentially it would be justified to flatten anonymous constructor notation when the terms are
from the same type family (think `Sigma`), but for now users can write a custom delaborator in such instances. -/
return withTypeAscription (cond := ( withType <| getPPOption getPPStructureInstanceType)) do
`($[$(stx[1].getArgs)],*)
let args := e.getAppArgs
@@ -638,7 +642,7 @@ List.map.match_1 : {α : Type _} →
```
-/
@[builtin_delab app]
partial def delabAppMatch : Delab := whenPPOption getPPNotation <| whenPPOption getPPMatch do
partial def delabAppMatch : Delab := whenNotPPOption getPPExplicit <| whenPPOption getPPNotation <| whenPPOption getPPMatch do
-- Check that this is a matcher, and then set up overapplication.
let Expr.const c us := ( getExpr).getAppFn | failure
let some info getMatcherInfo? c | failure
@@ -769,16 +773,6 @@ def delabMData : Delab := do
else
withMDataOptions delab
/--
Check for a `Syntax.ident` of the given name anywhere in the tree.
This is usually a bad idea since it does not check for shadowing bindings,
but in the delaborator we assume that bindings are never shadowed.
-/
partial def hasIdent (id : Name) : Syntax Bool
| Syntax.ident _ _ id' _ => id == id'
| Syntax.node _ _ args => args.any (hasIdent id)
| _ => false
/--
Return `true` iff current binder should be merged with the nested
binder, if any, into a single binder group:
@@ -824,7 +818,7 @@ def delabLam : Delab :=
let e getExpr
let stxT withBindingDomain delab
let ppTypes getPPOption getPPFunBinderTypes
let usedDownstream := curNames.any (fun n => hasIdent n.getId stxBody)
let usedDownstream := curNames.any (fun n => stxBody.hasIdent n.getId)
-- leave lambda implicit if possible
-- TODO: for now we just always block implicit lambdas when delaborating. We can revisit.
@@ -1135,6 +1129,24 @@ def delabSigma : Delab := delabSigmaCore (sigma := true)
@[builtin_delab app.PSigma]
def delabPSigma : Delab := delabSigmaCore (sigma := false)
-- PProd and MProd value delaborator
-- (like pp_using_anonymous_constructor but flattening nested tuples)
def delabPProdMkCore (mkName : Name) : Delab := whenNotPPOption getPPExplicit <| whenPPOption getPPNotation do
guard <| ( getExpr).getAppNumArgs == 4
let a withAppFn <| withAppArg delab
let b withAppArg <| delab
if ( getExpr).appArg!.isAppOfArity mkName 4 then
if let `($xs,*) := b then
return `($a, $xs,*)
`($a, $b)
@[builtin_delab app.PProd.mk]
def delabPProdMk : Delab := delabPProdMkCore ``PProd.mk
@[builtin_delab app.MProd.mk]
def delabMProdMk : Delab := delabPProdMkCore ``MProd.mk
partial def delabDoElems : DelabM (List Syntax) := do
let e getExpr
if e.isAppOfArity ``Bind.bind 6 then

View File

@@ -164,6 +164,16 @@ def asNode : Syntax → SyntaxNode
def getIdAt (stx : Syntax) (i : Nat) : Name :=
(stx.getArg i).getId
/--
Check for a `Syntax.ident` of the given name anywhere in the tree.
This is usually a bad idea since it does not check for shadowing bindings,
but in the delaborator we assume that bindings are never shadowed.
-/
partial def hasIdent (id : Name) : Syntax Bool
| ident _ _ id' _ => id == id'
| node _ _ args => args.any (hasIdent id)
| _ => false
@[inline] def modifyArgs (stx : Syntax) (fn : Array Syntax Array Syntax) : Syntax :=
match stx with
| node i k args => node i k (fn args)

View File

@@ -106,6 +106,10 @@ instance : ToExpr Unit where
toExpr := fun _ => mkConst `Unit.unit
toTypeExpr := mkConst ``Unit
instance : ToExpr System.FilePath where
toExpr p := mkApp (mkConst ``System.FilePath.mk) (toExpr p.toString)
toTypeExpr := mkConst ``System.FilePath
private def Name.toExprAux (n : Name) : Expr :=
if isSimple n 0 then
mkStr n 0 #[]

View File

@@ -29,4 +29,6 @@ import Lean.Util.OccursCheck
import Lean.Util.HasConstCache
import Lean.Util.FileSetupInfo
import Lean.Util.Heartbeats
import Lean.Util.SearchPath
import Lean.Util.SafeExponentiation
import Lean.Util.NumObjs

View File

@@ -9,48 +9,11 @@ import Lean.Util.PtrSet
namespace Lean
namespace Expr
namespace FindImpl
unsafe abbrev FindM := StateT (PtrSet Expr) Id
@[extern "lean_find_expr"]
opaque findImpl? (p : @& (Expr Bool)) (e : @& Expr) : Option Expr
@[inline] unsafe def checkVisited (e : Expr) : OptionT FindM Unit := do
if ( get).contains e then
failure
modify fun s => s.insert e
unsafe def findM? (p : Expr Bool) (e : Expr) : OptionT FindM Expr :=
let rec visit (e : Expr) := do
checkVisited e
if p e then
pure e
else match e with
| .forallE _ d b _ => visit d <|> visit b
| .lam _ d b _ => visit d <|> visit b
| .mdata _ b => visit b
| .letE _ t v b _ => visit t <|> visit v <|> visit b
| .app f a => visit f <|> visit a
| .proj _ _ b => visit b
| _ => failure
visit e
unsafe def findUnsafe? (p : Expr Bool) (e : Expr) : Option Expr :=
Id.run <| findM? p e |>.run' mkPtrSet
end FindImpl
@[implemented_by FindImpl.findUnsafe?]
def find? (p : Expr Bool) (e : Expr) : Option Expr :=
/- This is a reference implementation for the unsafe one above -/
if p e then
some e
else match e with
| .forallE _ d b _ => find? p d <|> find? p b
| .lam _ d b _ => find? p d <|> find? p b
| .mdata _ b => find? p b
| .letE _ t v b _ => find? p t <|> find? p v <|> find? p b
| .app f a => find? p f <|> find? p a
| .proj _ _ b => find? p b
| _ => none
@[inline] def find? (p : Expr Bool) (e : Expr) : Option Expr := findImpl? p e
/-- Return true if `e` occurs in `t` -/
def occurs (e : Expr) (t : Expr) : Bool :=
@@ -64,41 +27,13 @@ inductive FindStep where
/-- Search subterms -/ | visit
/-- Do not search subterms -/ | done
namespace FindExtImpl
unsafe def findM? (p : Expr FindStep) (e : Expr) : OptionT FindImpl.FindM Expr :=
visit e
where
visitApp (e : Expr) :=
match e with
| .app f a .. => visitApp f <|> visit a
| e => visit e
visit (e : Expr) := do
FindImpl.checkVisited e
match p e with
| .done => failure
| .found => pure e
| .visit =>
match e with
| .forallE _ d b _ => visit d <|> visit b
| .lam _ d b _ => visit d <|> visit b
| .mdata _ b => visit b
| .letE _ t v b _ => visit t <|> visit v <|> visit b
| .app .. => visitApp e
| .proj _ _ b => visit b
| _ => failure
unsafe def findUnsafe? (p : Expr FindStep) (e : Expr) : Option Expr :=
Id.run <| findM? p e |>.run' mkPtrSet
end FindExtImpl
@[extern "lean_find_ext_expr"]
opaque findExtImpl? (p : @& (Expr FindStep)) (e : @& Expr) : Option Expr
/--
Similar to `find?`, but `p` can return `FindStep.done` to interrupt the search on subterms.
Remark: Differently from `find?`, we do not invoke `p` for partial applications of an application. -/
@[implemented_by FindExtImpl.findUnsafe?]
opaque findExt? (p : Expr FindStep) (e : Expr) : Option Expr
@[inline] def findExt? (p : Expr FindStep) (e : Expr) : Option Expr := findExtImpl? p e
end Expr
end Lean

View File

@@ -0,0 +1,47 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Expr
import Lean.Util.PtrSet
namespace Lean.Expr
namespace NumObjs
unsafe structure State where
visited : PtrSet Expr := mkPtrSet
counter : Nat := 0
unsafe abbrev M := StateM State
unsafe def visit (e : Expr) : M Unit :=
unless ( get).visited.contains e do
modify fun { visited, counter } => { visited := visited.insert e, counter := counter + 1 }
match e with
| .forallE _ d b _ => visit d; visit b
| .lam _ d b _ => visit d; visit b
| .mdata _ b => visit b
| .letE _ t v b _ => visit t; visit v; visit b
| .app f a => visit f; visit a
| .proj _ _ b => visit b
| _ => return ()
unsafe def main (e : Expr) : Nat :=
let (_, s) := NumObjs.visit e |>.run {}
s.counter
end NumObjs
/--
Returns the number of allocated `Expr` objects in the given expression `e`.
This operation is performed in `IO` because the result depends on the memory representation of the object.
Note: Use this function primarily for diagnosing performance issues.
-/
def numObjs (e : Expr) : IO Nat :=
return unsafe NumObjs.main e
end Lean.Expr

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
prelude
import Init.Data.Hashable
import Lean.Data.HashSet
import Lean.Data.HashMap
namespace Lean
@@ -33,4 +34,22 @@ unsafe abbrev PtrSet.insert (s : PtrSet α) (a : α) : PtrSet α :=
unsafe abbrev PtrSet.contains (s : PtrSet α) (a : α) : Bool :=
HashSet.contains s { value := a }
/--
Map of pointers. It is a low-level auxiliary datastructure used for traversing DAGs.
-/
unsafe def PtrMap (α : Type) (β : Type) :=
HashMap (Ptr α) β
unsafe def mkPtrMap {α β : Type} (capacity : Nat := 64) : PtrMap α β :=
mkHashMap capacity
unsafe abbrev PtrMap.insert (s : PtrMap α β) (a : α) (b : β) : PtrMap α β :=
HashMap.insert s { value := a } b
unsafe abbrev PtrMap.contains (s : PtrMap α β) (a : α) : Bool :=
HashMap.contains s { value := a }
unsafe abbrev PtrMap.find? (s : PtrMap α β) (a : α) : Option β :=
HashMap.find? s { value := a }
end Lean

View File

@@ -5,74 +5,59 @@ Authors: Leonardo de Moura, Gabriel Ebner, Sebastian Ullrich
-/
prelude
import Lean.Expr
import Lean.Util.PtrSet
namespace Lean
namespace Expr
namespace ReplaceImpl
structure Cache where
size : USize
-- First `size` elements are the keys.
-- Second `size` elements are the results.
keysResults : Array NonScalar -- Either Expr or Unit (disjoint memory representation)
unsafe abbrev ReplaceM := StateM (PtrMap Expr Expr)
unsafe def Cache.new (e : Expr) : Cache :=
-- scale size with approximate number of subterms up to 8k
-- make sure size is coprime with power of two for collision avoidance
let size := (1 <<< min (max e.approxDepth.toUSize 1) 13) - 1
{ size, keysResults := mkArray (2 * size).toNat (unsafeCast ()) }
@[inline]
unsafe def Cache.keyIdx (c : Cache) (key : Expr) : USize :=
ptrAddrUnsafe key % c.size
@[inline]
unsafe def Cache.resultIdx (c : Cache) (key : Expr) : USize :=
c.keyIdx key + c.size
@[inline]
unsafe def Cache.hasResultFor (c : Cache) (key : Expr) : Bool :=
have : (c.keyIdx key).toNat < c.keysResults.size := lcProof
ptrEq (unsafeCast key) c.keysResults[c.keyIdx key]
@[inline]
unsafe def Cache.getResultFor (c : Cache) (key : Expr) : Expr :=
have : (c.resultIdx key).toNat < c.keysResults.size := lcProof
unsafeCast c.keysResults[c.resultIdx key]
unsafe def Cache.store (c : Cache) (key result : Expr) : Cache :=
{ c with keysResults := c.keysResults
|>.uset (c.keyIdx key) (unsafeCast key) lcProof
|>.uset (c.resultIdx key) (unsafeCast result) lcProof }
abbrev ReplaceM := StateM Cache
@[inline]
unsafe def cache (key : Expr) (result : Expr) : ReplaceM Expr := do
modify (·.store key result)
unsafe def cache (key : Expr) (exclusive : Bool) (result : Expr) : ReplaceM Expr := do
unless exclusive do
modify (·.insert key result)
pure result
@[specialize]
unsafe def replaceUnsafeM (f? : Expr Option Expr) (e : Expr) : ReplaceM Expr := do
let rec @[specialize] visit (e : Expr) := do
if ( get).hasResultFor e then
return ( get).getResultFor e
else match f? e with
| some eNew => cache e eNew
/-
TODO: We need better control over RC operations to ensure
the following (unsafe) optimization is correctly applied.
Optimization goal: only cache results for shared objects.
The main problem is that the current code generator ignores borrow annotations
for code written in Lean. These annotations are only taken into account for extern functions.
Moveover, the borrow inference heuristic currently tags `e` as "owned" since it may be stored
in the cache and is used in "update" functions.
Thus, when visiting `e` sub-expressions the code generator increases their RC
because we are recursively invoking `visit` :(
Thus, to fix this issue, we must
1- Take borrow annotations into account for code written in Lean.
2- Mark `e` is borrowed (i.e., `(e : @& Expr)`)
-/
let excl := isExclusiveUnsafe e
unless excl do
if let some result := ( get).find? e then
return result
match f? e with
| some eNew => cache e excl eNew
| none => match e with
| Expr.forallE _ d b _ => cache e <| e.updateForallE! ( visit d) ( visit b)
| Expr.lam _ d b _ => cache e <| e.updateLambdaE! ( visit d) ( visit b)
| Expr.mdata _ b => cache e <| e.updateMData! ( visit b)
| Expr.letE _ t v b _ => cache e <| e.updateLet! ( visit t) ( visit v) ( visit b)
| Expr.app f a => cache e <| e.updateApp! ( visit f) ( visit a)
| Expr.proj _ _ b => cache e <| e.updateProj! ( visit b)
| e => pure e
| .forallE _ d b _ => cache e excl <| e.updateForallE! ( visit d) ( visit b)
| .lam _ d b _ => cache e excl <| e.updateLambdaE! ( visit d) ( visit b)
| .mdata _ b => cache e excl <| e.updateMData! ( visit b)
| .letE _ t v b _ => cache e excl <| e.updateLet! ( visit t) ( visit v) ( visit b)
| .app f a => cache e excl <| e.updateApp! ( visit f) ( visit a)
| .proj _ _ b => cache e excl <| e.updateProj! ( visit b)
| e => return e
visit e
@[inline]
unsafe def replaceUnsafe (f? : Expr Option Expr) (e : Expr) : Expr :=
(replaceUnsafeM f? e).run' (Cache.new e)
(replaceUnsafeM f? e).run' mkPtrMap
end ReplaceImpl
@@ -92,6 +77,10 @@ def replaceNoCache (f? : Expr → Option Expr) (e : Expr) : Expr :=
| .proj _ _ b => let b := replaceNoCache f? b; e.updateProj! b
| e => e
@[extern "lean_replace_expr"]
opaque replaceImpl (f? : @& (Expr Option Expr)) (e : @& Expr) : Expr
@[implemented_by ReplaceImpl.replaceUnsafe]
partial def replace (f? : Expr Option Expr) (e : Expr) : Expr :=
def replace (f? : Expr Option Expr) (e : Expr) : Expr :=
e.replaceNoCache f?

View File

@@ -0,0 +1,23 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Lean.ToExpr
import Lean.Util.Path
import Lean.Elab.Term
open Lean
/--
Term elaborator that retrieves the current `SearchPath`.
Typical usage is `searchPathRef.set compile_time_search_path%`.
This must not be used in files that are potentially compiled on another machine and then imported.
(That is, if used in an imported file it will embed the search path from whichever machine
compiled the `.olean`.)
-/
elab "compile_time_search_path%" : term =>
return toExpr ( searchPathRef.get)

View File

@@ -5,7 +5,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Wojciech Nawrocki
-/
prelude
import Lean.PrettyPrinter
import Lean.Server.Rpc.Basic
import Lean.Server.InfoUtils
import Lean.Widget.TaggedText

View File

@@ -30,6 +30,8 @@ universe u v w
variable {α : Type u} {β : α Type v} {δ : Type w} {m : Type w Type w} [Monad m]
variable {_ : BEq α} {_ : Hashable α}
namespace Std
open DHashMap.Internal DHashMap.Internal.List
@@ -42,6 +44,9 @@ and an array of buckets, where each bucket is a linked list of key-value pais. T
is always a power of two. The hash map doubles its size upon inserting an element such that the
number of elements is more than 75% of the number of buckets.
The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to
avoid expensive copies.
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
@@ -66,34 +71,34 @@ instance [BEq α] [Hashable α] : EmptyCollection (DHashMap α β) where
instance [BEq α] [Hashable α] : Inhabited (DHashMap α β) where
default :=
@[inline, inherit_doc Raw.insert] def insert [BEq α] [Hashable α] (m : DHashMap α β) (a : α)
@[inline, inherit_doc Raw.insert] def insert (m : DHashMap α β) (a : α)
(b : β a) : DHashMap α β :=
Raw₀.insert m.1, m.2.size_buckets_pos a b, .insert₀ m.2
@[inline, inherit_doc Raw.insertIfNew] def insertIfNew [BEq α] [Hashable α] (m : DHashMap α β)
@[inline, inherit_doc Raw.insertIfNew] def insertIfNew (m : DHashMap α β)
(a : α) (b : β a) : DHashMap α β :=
Raw₀.insertIfNew m.1, m.2.size_buckets_pos a b, .insertIfNew₀ m.2
@[inline, inherit_doc Raw.containsThenInsert] def containsThenInsert [BEq α] [Hashable α]
@[inline, inherit_doc Raw.containsThenInsert] def containsThenInsert
(m : DHashMap α β) (a : α) (b : β a) : Bool × DHashMap α β :=
let m' := Raw₀.containsThenInsert m.1, m.2.size_buckets_pos a b
m'.1, m'.2.1, .containsThenInsert₀ m.2
@[inline, inherit_doc Raw.containsThenInsertIfNew] def containsThenInsertIfNew [BEq α] [Hashable α]
@[inline, inherit_doc Raw.containsThenInsertIfNew] def containsThenInsertIfNew
(m : DHashMap α β) (a : α) (b : β a) : Bool × DHashMap α β :=
let m' := Raw₀.containsThenInsertIfNew m.1, m.2.size_buckets_pos a b
m'.1, m'.2.1, .containsThenInsertIfNew₀ m.2
@[inline, inherit_doc Raw.getThenInsertIfNew?] def getThenInsertIfNew? [BEq α] [Hashable α]
@[inline, inherit_doc Raw.getThenInsertIfNew?] def getThenInsertIfNew?
[LawfulBEq α] (m : DHashMap α β) (a : α) (b : β a) : Option (β a) × DHashMap α β :=
let m' := Raw₀.getThenInsertIfNew? m.1, m.2.size_buckets_pos a b
m'.1, m'.2.1, .getThenInsertIfNew?₀ m.2
@[inline, inherit_doc Raw.get?] def get? [BEq α] [LawfulBEq α] [Hashable α] (m : DHashMap α β)
@[inline, inherit_doc Raw.get?] def get? [LawfulBEq α] (m : DHashMap α β)
(a : α) : Option (β a) :=
Raw₀.get? m.1, m.2.size_buckets_pos a
@[inline, inherit_doc Raw.contains] def contains [BEq α] [Hashable α] (m : DHashMap α β) (a : α) :
@[inline, inherit_doc Raw.contains] def contains (m : DHashMap α β) (a : α) :
Bool :=
Raw₀.contains m.1, m.2.size_buckets_pos a
@@ -103,77 +108,77 @@ instance [BEq α] [Hashable α] : Membership α (DHashMap α β) where
instance [BEq α] [Hashable α] {m : DHashMap α β} {a : α} : Decidable (a m) :=
show Decidable (m.contains a) from inferInstance
@[inline, inherit_doc Raw.get] def get [BEq α] [Hashable α] [LawfulBEq α] (m : DHashMap α β) (a : α)
@[inline, inherit_doc Raw.get] def get [LawfulBEq α] (m : DHashMap α β) (a : α)
(h : a m) : β a :=
Raw₀.get m.1, m.2.size_buckets_pos a h
@[inline, inherit_doc Raw.get!] def get! [BEq α] [Hashable α] [LawfulBEq α] (m : DHashMap α β)
@[inline, inherit_doc Raw.get!] def get! [LawfulBEq α] (m : DHashMap α β)
(a : α) [Inhabited (β a)] : β a :=
Raw₀.get! m.1, m.2.size_buckets_pos a
@[inline, inherit_doc Raw.getD] def getD [BEq α] [Hashable α] [LawfulBEq α] (m : DHashMap α β)
@[inline, inherit_doc Raw.getD] def getD [LawfulBEq α] (m : DHashMap α β)
(a : α) (fallback : β a) : β a :=
Raw₀.getD m.1, m.2.size_buckets_pos a fallback
@[inline, inherit_doc Raw.remove] def remove [BEq α] [Hashable α] (m : DHashMap α β) (a : α) :
@[inline, inherit_doc Raw.erase] def erase (m : DHashMap α β) (a : α) :
DHashMap α β :=
Raw₀.remove m.1, m.2.size_buckets_pos a, .remove₀ m.2
Raw₀.erase m.1, m.2.size_buckets_pos a, .erase₀ m.2
section
variable {β : Type v}
@[inline, inherit_doc Raw.Const.get?] def Const.get? [BEq α] [Hashable α]
@[inline, inherit_doc Raw.Const.get?] def Const.get?
(m : DHashMap α (fun _ => β)) (a : α) : Option β :=
Raw₀.Const.get? m.1, m.2.size_buckets_pos a
@[inline, inherit_doc Raw.Const.get] def Const.get [BEq α] [Hashable α]
@[inline, inherit_doc Raw.Const.get] def Const.get
(m : DHashMap α (fun _ => β)) (a : α) (h : a m) : β :=
Raw₀.Const.get m.1, m.2.size_buckets_pos a h
@[inline, inherit_doc Raw.Const.getD] def Const.getD [BEq α] [Hashable α]
@[inline, inherit_doc Raw.Const.getD] def Const.getD
(m : DHashMap α (fun _ => β)) (a : α) (fallback : β) : β :=
Raw₀.Const.getD m.1, m.2.size_buckets_pos a fallback
@[inline, inherit_doc Raw.Const.get!] def Const.get! [BEq α] [Hashable α] [Inhabited β]
@[inline, inherit_doc Raw.Const.get!] def Const.get! [Inhabited β]
(m : DHashMap α (fun _ => β)) (a : α) : β :=
Raw₀.Const.get! m.1, m.2.size_buckets_pos a
@[inline, inherit_doc Raw.Const.getThenInsertIfNew?] def Const.getThenInsertIfNew? [BEq α]
[Hashable α] (m : DHashMap α (fun _ => β)) (a : α) (b : β) :
@[inline, inherit_doc Raw.Const.getThenInsertIfNew?] def Const.getThenInsertIfNew?
(m : DHashMap α (fun _ => β)) (a : α) (b : β) :
Option β × DHashMap α (fun _ => β) :=
let m' := Raw₀.Const.getThenInsertIfNew? m.1, m.2.size_buckets_pos a b
m'.1, m'.2.1, .constGetThenInsertIfNew?₀ m.2
end
@[inline, inherit_doc Raw.size] def size [BEq α] [Hashable α] (m : DHashMap α β) : Nat :=
@[inline, inherit_doc Raw.size] def size (m : DHashMap α β) : Nat :=
m.1.size
@[inline, inherit_doc Raw.isEmpty] def isEmpty [BEq α] [Hashable α] (m : DHashMap α β) : Bool :=
@[inline, inherit_doc Raw.isEmpty] def isEmpty (m : DHashMap α β) : Bool :=
m.1.isEmpty
section Unverified
/-! We currently do not provide lemmas for the functions below. -/
@[inline, inherit_doc Raw.filter] def filter [BEq α] [Hashable α] (f : (a : α) β a Bool)
@[inline, inherit_doc Raw.filter] def filter (f : (a : α) β a Bool)
(m : DHashMap α β) : DHashMap α β :=
Raw₀.filter f m.1, m.2.size_buckets_pos, .filter₀ m.2
@[inline, inherit_doc Raw.foldM] def foldM [BEq α] [Hashable α] (f : δ (a : α) β a m δ)
@[inline, inherit_doc Raw.foldM] def foldM (f : δ (a : α) β a m δ)
(init : δ) (b : DHashMap α β) : m δ :=
b.1.foldM f init
@[inline, inherit_doc Raw.fold] def fold [BEq α] [Hashable α] (f : δ (a : α) β a δ)
@[inline, inherit_doc Raw.fold] def fold (f : δ (a : α) β a δ)
(init : δ) (b : DHashMap α β) : δ :=
b.1.fold f init
@[inline, inherit_doc Raw.forM] def forM [BEq α] [Hashable α] (f : (a : α) β a m PUnit)
@[inline, inherit_doc Raw.forM] def forM (f : (a : α) β a m PUnit)
(b : DHashMap α β) : m PUnit :=
b.1.forM f
@[inline, inherit_doc Raw.forIn] def forIn [BEq α] [Hashable α]
@[inline, inherit_doc Raw.forIn] def forIn
(f : (a : α) β a δ m (ForInStep δ)) (init : δ) (b : DHashMap α β) : m δ :=
b.1.forIn f init
@@ -183,49 +188,49 @@ instance [BEq α] [Hashable α] : ForM m (DHashMap α β) ((a : α) × β a) whe
instance [BEq α] [Hashable α] : ForIn m (DHashMap α β) ((a : α) × β a) where
forIn m init f := m.forIn (fun a b acc => f a, b acc) init
@[inline, inherit_doc Raw.toList] def toList [BEq α] [Hashable α] (m : DHashMap α β) :
@[inline, inherit_doc Raw.toList] def toList (m : DHashMap α β) :
List ((a : α) × β a) :=
m.1.toList
@[inline, inherit_doc Raw.toArray] def toArray [BEq α] [Hashable α] (m : DHashMap α β) :
@[inline, inherit_doc Raw.toArray] def toArray (m : DHashMap α β) :
Array ((a : α) × β a) :=
m.1.toArray
@[inline, inherit_doc Raw.Const.toList] def Const.toList {β : Type v} [BEq α] [Hashable α]
@[inline, inherit_doc Raw.Const.toList] def Const.toList {β : Type v}
(m : DHashMap α (fun _ => β)) : List (α × β) :=
Raw.Const.toList m.1
@[inline, inherit_doc Raw.Const.toArray] def Const.toArray {β : Type v} [BEq α] [Hashable α]
@[inline, inherit_doc Raw.Const.toArray] def Const.toArray {β : Type v}
(m : DHashMap α (fun _ => β)) : Array (α × β) :=
Raw.Const.toArray m.1
@[inline, inherit_doc Raw.keys] def keys [BEq α] [Hashable α] (m : DHashMap α β) : List α :=
@[inline, inherit_doc Raw.keys] def keys (m : DHashMap α β) : List α :=
m.1.keys
@[inline, inherit_doc Raw.keysArray] def keysArray [BEq α] [Hashable α] (m : DHashMap α β) :
@[inline, inherit_doc Raw.keysArray] def keysArray (m : DHashMap α β) :
Array α :=
m.1.keysArray
@[inline, inherit_doc Raw.values] def values {β : Type v} [BEq α] [Hashable α]
@[inline, inherit_doc Raw.values] def values {β : Type v}
(m : DHashMap α (fun _ => β)) : List β :=
m.1.values
@[inline, inherit_doc Raw.valuesArray] def valuesArray {β : Type v} [BEq α] [Hashable α]
@[inline, inherit_doc Raw.valuesArray] def valuesArray {β : Type v}
(m : DHashMap α (fun _ => β)) : Array β :=
m.1.valuesArray
@[inline, inherit_doc Raw.insertMany] def insertMany [BEq α] [Hashable α] {ρ : Type w}
@[inline, inherit_doc Raw.insertMany] def insertMany {ρ : Type w}
[ForIn Id ρ ((a : α) × β a)] (m : DHashMap α β) (l : ρ) : DHashMap α β :=
(Raw₀.insertMany m.1, m.2.size_buckets_pos l).1,
(Raw₀.insertMany m.1, m.2.size_buckets_pos l).2 _ Raw.WF.insert₀ m.2
@[inline, inherit_doc Raw.Const.insertMany] def Const.insertMany {β : Type v} [BEq α] [Hashable α]
@[inline, inherit_doc Raw.Const.insertMany] def Const.insertMany {β : Type v}
{ρ : Type w} [ForIn Id ρ (α × β)] (m : DHashMap α (fun _ => β)) (l : ρ) :
DHashMap α (fun _ => β) :=
(Raw₀.Const.insertMany m.1, m.2.size_buckets_pos l).1,
(Raw₀.Const.insertMany m.1, m.2.size_buckets_pos l).2 _ Raw.WF.insert₀ m.2
@[inline, inherit_doc Raw.Const.insertManyUnit] def Const.insertManyUnit [BEq α] [Hashable α]
@[inline, inherit_doc Raw.Const.insertManyUnit] def Const.insertManyUnit
{ρ : Type w} [ForIn Id ρ α] (m : DHashMap α (fun _ => Unit)) (l : ρ) :
DHashMap α (fun _ => Unit) :=
(Raw₀.Const.insertManyUnit m.1, m.2.size_buckets_pos l).1,
@@ -243,7 +248,7 @@ instance [BEq α] [Hashable α] : ForIn m (DHashMap α β) ((a : α) × β a) wh
DHashMap α (fun _ => Unit) :=
Const.insertManyUnit l
@[inherit_doc Raw.Internal.numBuckets] def Internal.numBuckets [BEq α] [Hashable α]
@[inherit_doc Raw.Internal.numBuckets] def Internal.numBuckets
(m : DHashMap α β) : Nat :=
Raw.Internal.numBuckets m.1

View File

@@ -77,61 +77,61 @@ variable {β : Type v}
/-- Internal implementation detail of the hash map -/
def get? [BEq α] (a : α) : AssocList α (fun _ => β) Option β
| nil => none
| cons k v es => bif a == k then some v else get? a es
| cons k v es => bif k == a then some v else get? a es
end
/-- Internal implementation detail of the hash map -/
def getCast? [BEq α] [LawfulBEq α] (a : α) : AssocList α β Option (β a)
| nil => none
| cons k v es => if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v)
| cons k v es => if h : k == a then some (cast (congrArg β (eq_of_beq h)) v)
else es.getCast? a
/-- Internal implementation detail of the hash map -/
def contains [BEq α] (a : α) : AssocList α β Bool
| nil => false
| cons k _ l => a == k || l.contains a
| cons k _ l => k == a || l.contains a
/-- Internal implementation detail of the hash map -/
def get {β : Type v} [BEq α] (a : α) : (l : AssocList α (fun _ => β)) l.contains a β
| cons k v es, h => if hka : a == k then v else get a es
| cons k v es, h => if hka : k == a then v else get a es
(by rw [ h, contains, Bool.of_not_eq_true hka, Bool.false_or])
/-- Internal implementation detail of the hash map -/
def getCast [BEq α] [LawfulBEq α] (a : α) : (l : AssocList α β) l.contains a β a
| cons k v es, h => if hka : a == k then cast (congrArg β (eq_of_beq hka).symm) v
| cons k v es, h => if hka : k == a then cast (congrArg β (eq_of_beq hka)) v
else es.getCast a (by rw [ h, contains, Bool.of_not_eq_true hka, Bool.false_or])
/-- Internal implementation detail of the hash map -/
def getCast! [BEq α] [LawfulBEq α] (a : α) [Inhabited (β a)] : AssocList α β β a
| nil => panic! "key is not present in hash table"
| cons k v es => if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else es.getCast! a
| cons k v es => if h : k == a then cast (congrArg β (eq_of_beq h)) v else es.getCast! a
/-- Internal implementation detail of the hash map -/
def get! {β : Type v} [BEq α] [Inhabited β] (a : α) : AssocList α (fun _ => β) β
| nil => panic! "key is not present in hash table"
| cons k v es => bif a == k then v else es.get! a
| cons k v es => bif k == a then v else es.get! a
/-- Internal implementation detail of the hash map -/
def getCastD [BEq α] [LawfulBEq α] (a : α) (fallback : β a) : AssocList α β β a
| nil => fallback
| cons k v es => if h : a == k then cast (congrArg β (eq_of_beq h).symm) v
| cons k v es => if h : k == a then cast (congrArg β (eq_of_beq h)) v
else es.getCastD a fallback
/-- Internal implementation detail of the hash map -/
def getD {β : Type v} [BEq α] (a : α) (fallback : β) : AssocList α (fun _ => β) β
| nil => fallback
| cons k v es => bif a == k then v else es.getD a fallback
| cons k v es => bif k == a then v else es.getD a fallback
/-- Internal implementation detail of the hash map -/
def replace [BEq α] (a : α) (b : β a) : AssocList α β AssocList α β
| nil => nil
| cons k v l => bif a == k then cons a b l else cons k v (replace a b l)
| cons k v l => bif k == a then cons a b l else cons k v (replace a b l)
/-- Internal implementation detail of the hash map -/
def remove [BEq α] (a : α) : AssocList α β AssocList α β
def erase [BEq α] (a : α) : AssocList α β AssocList α β
| nil => nil
| cons k v l => bif a == k then l else cons k v (l.remove a)
| cons k v l => bif k == a then l else cons k v (l.erase a)
/-- Internal implementation detail of the hash map -/
@[inline] def filterMap (f : (a : α) β a Option (γ a)) :

View File

@@ -116,14 +116,14 @@ theorem toList_replace [BEq α] {l : AssocList α β} {a : α} {b : β a} :
(l.replace a b).toList = replaceEntry a b l.toList := by
induction l
· simp [replace]
· next k v t ih => cases h : a == k <;> simp_all [replace, List.replaceEntry_cons]
· next k v t ih => cases h : k == a <;> simp_all [replace, List.replaceEntry_cons]
@[simp]
theorem toList_remove [BEq α] {l : AssocList α β} {a : α} :
(l.remove a).toList = removeKey a l.toList := by
theorem toList_erase [BEq α] {l : AssocList α β} {a : α} :
(l.erase a).toList = eraseKey a l.toList := by
induction l
· simp [remove]
· next k v t ih => cases h : a == k <;> simp_all [remove, List.removeKey_cons]
· simp [erase]
· next k v t ih => cases h : k == a <;> simp_all [erase, List.eraseKey_cons]
theorem toList_filterMap {f : (a : α) β a Option (γ a)} {l : AssocList α β} :
Perm (l.filterMap f).toList (l.toList.filterMap fun p => (f p.1 p.2).map (p.1, ·)) := by

View File

@@ -79,7 +79,7 @@ maintainable. To this end, we provide theorems `apply_bucket`, `apply_bucket_wit
`toListModel_updateBucket` and `toListModel_updateAllBuckets`, which do all of the heavy lifting in
a general way. The verification for each actual operation in `Internal.WF` is then extremely
straightward, requiring only to plug in some results about lists. See for example the functions
`containsₘ_eq_containsKey` and the section on `removeₘ` for prototypical examples of this technique.
`containsₘ_eq_containsKey` and the section on `eraseₘ` for prototypical examples of this technique.
Here is a summary of the steps required to add and verify a new operation:
1. Write the executable implementation
@@ -197,7 +197,7 @@ where
if h : i < source.size then
let idx : Fin source.size := i, h
let es := source.get idx
-- We remove `es` from `source` to make sure we can reuse its memory cells
-- We erase `es` from `source` to make sure we can reuse its memory cells
-- when performing es.foldl
let source := source.set idx .nil
let target := es.foldl (reinsertAux hash) target
@@ -313,13 +313,13 @@ where
buckets[idx.1].getCast! a
/-- Internal implementation detail of the hash map -/
@[inline] def remove [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
@[inline] def erase [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
let size, buckets, hb := m
let i, h := mkIdx buckets.size hb (hash a)
let bkt := buckets[i]
if bkt.contains a then
let buckets' := buckets.uset i .nil h
size - 1, buckets'.uset i (bkt.remove a) (by simpa [buckets']), by simpa [buckets']
size - 1, buckets'.uset i (bkt.erase a) (by simpa [buckets']), by simpa [buckets']
else
size, buckets, hb

View File

@@ -35,19 +35,19 @@ theorem assoc_induction {motive : List ((a : α) × β a) → Prop} (nil : motiv
/-- Internal implementation detail of the hash map -/
def getEntry? [BEq α] (a : α) : List ((a : α) × β a) Option ((a : α) × β a)
| [] => none
| k, v :: l => bif a == k then some k, v else getEntry? a l
| k, v :: l => bif k == a then some k, v else getEntry? a l
@[simp] theorem getEntry?_nil [BEq α] {a : α} :
getEntry? a ([] : List ((a : α) × β a)) = none := rfl
theorem getEntry?_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
getEntry? a (k, v :: l) = bif a == k then some k, v else getEntry? a l := rfl
getEntry? a (k, v :: l) = bif k == a then some k, v else getEntry? a l := rfl
theorem getEntry?_cons_of_true [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : a == k) :
theorem getEntry?_cons_of_true [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : k == a) :
getEntry? a (k, v :: l) = some k, v := by
simp [getEntry?, h]
theorem getEntry?_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
(h : (a == k) = false) : getEntry? a (k, v :: l) = getEntry? a l := by
(h : (k == a) = false) : getEntry? a (k, v :: l) = getEntry? a l := by
simp [getEntry?, h]
@[simp]
@@ -56,11 +56,11 @@ theorem getEntry?_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)}
getEntry?_cons_of_true BEq.refl
theorem getEntry?_eq_some [BEq α] {l : List ((a : α) × β a)} {a : α} {p : (a : α) × β a}
(h : getEntry? a l = some p) : a == p.1 := by
(h : getEntry? a l = some p) : p.1 == a := by
induction l using assoc_induction
· simp at h
· next k' v' t ih =>
cases h' : a == k'
cases h' : k' == a
· rw [getEntry?_cons_of_false h'] at h
exact ih h
· rw [getEntry?_cons_of_true h', Option.some.injEq] at h
@@ -72,10 +72,10 @@ theorem getEntry?_congr [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β
induction l using assoc_induction
· simp
· next k v l ih =>
cases h' : b == k
· have h₂ : (a == k) = false := BEq.neq_of_beq_of_neq h h'
cases h' : k == a
· have h₂ : (k == b) = false := BEq.neq_of_neq_of_beq h' h
rw [getEntry?_cons_of_false h', getEntry?_cons_of_false h₂, ih]
· rw [getEntry?_cons_of_true h', getEntry?_cons_of_true (BEq.trans h h')]
· rw [getEntry?_cons_of_true h', getEntry?_cons_of_true (BEq.trans h' h)]
theorem isEmpty_eq_false_iff_exists_isSome_getEntry? [BEq α] [ReflBEq α] :
{l : List ((a : α) × β a)} l.isEmpty = false a, (getEntry? a l).isSome
@@ -89,18 +89,18 @@ variable {β : Type v}
/-- Internal implementation detail of the hash map -/
def getValue? [BEq α] (a : α) : List ((_ : α) × β) Option β
| [] => none
| k, v :: l => bif a == k then some v else getValue? a l
| k, v :: l => bif k == a then some v else getValue? a l
@[simp] theorem getValue?_nil [BEq α] {a : α} : getValue? a ([] : List ((_ : α) × β)) = none := rfl
theorem getValue?_cons [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} :
getValue? a (k, v :: l) = bif a == k then some v else getValue? a l := rfl
getValue? a (k, v :: l) = bif k == a then some v else getValue? a l := rfl
theorem getValue?_cons_of_true [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : a == k) :
theorem getValue?_cons_of_true [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : k == a) :
getValue? a (k, v :: l) = some v := by
simp [getValue?, h]
theorem getValue?_cons_of_false [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β}
(h : (a == k) = false) : getValue? a (k, v :: l) = getValue? a l := by
(h : (k == a) = false) : getValue? a (k, v :: l) = getValue? a l := by
simp [getValue?, h]
@[simp]
@@ -113,7 +113,7 @@ theorem getValue?_eq_getEntry? [BEq α] {l : List ((_ : α) × β)} {a : α} :
induction l using assoc_induction
· simp
· next k v l ih =>
cases h : a == k
cases h : k == a
· rw [getEntry?_cons_of_false h, getValue?_cons_of_false h, ih]
· rw [getEntry?_cons_of_true h, getValue?_cons_of_true h, Option.map_some']
@@ -130,22 +130,22 @@ end
/-- Internal implementation detail of the hash map -/
def getValueCast? [BEq α] [LawfulBEq α] (a : α) : List ((a : α) × β a) Option (β a)
| [] => none
| k, v :: l => if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v)
| k, v :: l => if h : k == a then some (cast (congrArg β (eq_of_beq h)) v)
else getValueCast? a l
@[simp] theorem getValueCast?_nil [BEq α] [LawfulBEq α] {a : α} :
getValueCast? a ([] : List ((a : α) × β a)) = none := rfl
theorem getValueCast?_cons [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
getValueCast? a (k, v :: l) = if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v)
getValueCast? a (k, v :: l) = if h : k == a then some (cast (congrArg β (eq_of_beq h)) v)
else getValueCast? a l := rfl
theorem getValueCast?_cons_of_true [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} (h : a == k) :
getValueCast? a (k, v :: l) = some (cast (congrArg β (eq_of_beq h).symm) v) := by
{v : β k} (h : k == a) :
getValueCast? a (k, v :: l) = some (cast (congrArg β (eq_of_beq h)) v) := by
simp [getValueCast?, h]
theorem getValueCast?_cons_of_false [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} (h : (a == k) = false) : getValueCast? a (k, v :: l) = getValueCast? a l := by
{v : β k} (h : (k == a) = false) : getValueCast? a (k, v :: l) = getValueCast? a l := by
simp [getValueCast?, h]
@[simp]
@@ -187,11 +187,11 @@ end
theorem getValueCast?_eq_getEntry? [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {a : α} :
getValueCast? a l = Option.dmap (getEntry? a l)
(fun p h => cast (congrArg β (eq_of_beq (getEntry?_eq_some h)).symm) p.2) := by
(fun p h => cast (congrArg β (eq_of_beq (getEntry?_eq_some h))) p.2) := by
induction l using assoc_induction
· simp
· next k v t ih =>
cases h : a == k
cases h : k == a
· rw [getValueCast?_cons_of_false h, ih, Option.dmap_congr (getEntry?_cons_of_false h)]
· rw [getValueCast?_cons_of_true h, Option.dmap_congr (getEntry?_cons_of_true h),
Option.dmap_some]
@@ -207,23 +207,23 @@ theorem isEmpty_eq_false_iff_exists_isSome_getValueCast? [BEq α] [LawfulBEq α]
/-- Internal implementation detail of the hash map -/
def containsKey [BEq α] (a : α) : List ((a : α) × β a) Bool
| [] => false
| k, _ :: l => a == k || containsKey a l
| k, _ :: l => k == a || containsKey a l
@[simp] theorem containsKey_nil [BEq α] {a : α} :
containsKey a ([] : List ((a : α) × β a)) = false := rfl
@[simp] theorem containsKey_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
containsKey a (k, v :: l) = (a == k || containsKey a l) := rfl
containsKey a (k, v :: l) = (k == a || containsKey a l) := rfl
theorem containsKey_cons_eq_false [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
(containsKey a (k, v :: l) = false) ((a == k) = false) (containsKey a l = false) := by
(containsKey a (k, v :: l) = false) ((k == a) = false) (containsKey a l = false) := by
simp [containsKey_cons, not_or]
theorem containsKey_cons_eq_true [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
(containsKey a (k, v :: l)) (a == k) (containsKey a l) := by
(containsKey a (k, v :: l)) (k == a) (containsKey a l) := by
simp [containsKey_cons]
theorem containsKey_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
(h : a == k) : containsKey a (k, v :: l) := containsKey_cons_eq_true.2 <| Or.inl h
(h : k == a) : containsKey a (k, v :: l) := containsKey_cons_eq_true.2 <| Or.inl h
@[simp]
theorem containsKey_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
@@ -233,7 +233,7 @@ theorem containsKey_cons_of_containsKey [BEq α] {l : List ((a : α) × β a)} {
(h : containsKey a l) : containsKey a (k, v :: l) := containsKey_cons_eq_true.2 <| Or.inr h
theorem containsKey_of_containsKey_cons [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
(h₁ : containsKey a (k, v :: l)) (h₂ : (a == k) = false) : containsKey a l := by
(h₁ : containsKey a (k, v :: l)) (h₂ : (k == a) = false) : containsKey a l := by
rcases (containsKey_cons_eq_true.1 h₁) with (h|h)
· exact False.elim (Bool.eq_false_iff.1 h₂ h)
· exact h
@@ -243,7 +243,7 @@ theorem containsKey_eq_isSome_getEntry? [BEq α] {l : List ((a : α) × β a)} {
induction l using assoc_induction
· simp
· next k v l ih =>
cases h : a == k
cases h : k == a
· simp [getEntry?_cons_of_false h, h, ih]
· simp [getEntry?_cons_of_true h, h]
@@ -297,7 +297,7 @@ theorem getEntry_eq_of_getEntry?_eq_some [BEq α] {l : List ((a : α) × β a)}
(h : getEntry? a l = some k, v) {h'} : getEntry a l h' = k, v := by
simp [getEntry, h]
theorem getEntry_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : a == k) :
theorem getEntry_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} (h : k == a) :
getEntry a (k, v :: l) (containsKey_cons_of_beq (v := v) h) = k, v := by
simp [getEntry, getEntry?_cons_of_true h]
@@ -307,7 +307,7 @@ theorem getEntry_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {
getEntry_cons_of_beq BEq.refl
theorem getEntry_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
{h₁ : containsKey a (k, v :: l)} (h₂ : (a == k) = false) : getEntry a (k, v :: l) h₁ =
{h₁ : containsKey a (k, v :: l)} (h₂ : (k == a) = false) : getEntry a (k, v :: l) h₁ =
getEntry a l (containsKey_of_containsKey_cons (v := v) h₁ h₂) := by
simp [getEntry, getEntry?_cons_of_false h₂]
@@ -323,7 +323,7 @@ theorem getValue?_eq_some_getValue [BEq α] {l : List ((_ : α) × β)} {a : α}
getValue? a l = some (getValue a l h) := by
simp [getValue]
theorem getValue_cons_of_beq [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : a == k) :
theorem getValue_cons_of_beq [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} (h : k == a) :
getValue a (k, v :: l) (containsKey_cons_of_beq (k := k) (v := v) h) = v := by
simp [getValue, getValue?_cons_of_true h]
@@ -333,12 +333,12 @@ theorem getValue_cons_self [BEq α] [ReflBEq α] {l : List ((_ : α) × β)} {k
getValue_cons_of_beq BEq.refl
theorem getValue_cons_of_false [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β}
{h₁ : containsKey a (k, v :: l)} (h₂ : (a == k) = false) : getValue a (k, v :: l) h₁ =
{h₁ : containsKey a (k, v :: l)} (h₂ : (k == a) = false) : getValue a (k, v :: l) h₁ =
getValue a l (containsKey_of_containsKey_cons (k := k) (v := v) h₁ h₂) := by
simp [getValue, getValue?_cons_of_false h₂]
theorem getValue_cons [BEq α] {l : List ((_ : α) × β)} {k a : α} {v : β} {h} :
getValue a (k, v :: l) h = if h' : a == k then v
getValue a (k, v :: l) h = if h' : k == a then v
else getValue a l (containsKey_of_containsKey_cons (k := k) h (Bool.eq_false_iff.2 h')) := by
rw [ Option.some_inj, getValue?_eq_some_getValue, getValue?_cons, apply_dite Option.some,
cond_eq_if]
@@ -369,8 +369,8 @@ theorem Option.get_congr {o o' : Option α} {ho : o.isSome} (h : o = o') :
theorem getValueCast_cons [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k}
(h : containsKey a (k, v :: l)) :
getValueCast a (k, v :: l) h =
if h' : a == k then
cast (congrArg β (eq_of_beq h').symm) v
if h' : k == a then
cast (congrArg β (eq_of_beq h')) v
else
getValueCast a l (containsKey_of_containsKey_cons (k := k) h (Bool.eq_false_iff.2 h')) := by
rw [getValueCast, Option.get_congr getValueCast?_cons]
@@ -515,19 +515,19 @@ end
/-- Internal implementation detail of the hash map -/
def replaceEntry [BEq α] (k : α) (v : β k) : List ((a : α) × β a) List ((a : α) × β a)
| [] => []
| k', v' :: l => bif k == k' then k, v :: l else k', v' :: replaceEntry k v l
| k', v' :: l => bif k' == k then k, v :: l else k', v' :: replaceEntry k v l
@[simp] theorem replaceEntry_nil [BEq α] {k : α} {v : β k} : replaceEntry k v [] = [] := rfl
theorem replaceEntry_cons [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v : β k} {v' : β k'} :
replaceEntry k v (k', v' :: l) =
bif k == k' then k, v :: l else k', v' :: replaceEntry k v l := rfl
bif k' == k then k, v :: l else k', v' :: replaceEntry k v l := rfl
theorem replaceEntry_cons_of_true [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v : β k}
{v' : β k'} (h : k == k') : replaceEntry k v (k', v' :: l) = k, v :: l := by
{v' : β k'} (h : k' == k) : replaceEntry k v (k', v' :: l) = k, v :: l := by
simp [replaceEntry, h]
theorem replaceEntry_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v : β k}
{v' : β k'} (h : (k == k') = false) :
{v' : β k'} (h : (k' == k) = false) :
replaceEntry k v (k', v' :: l) = k', v' :: replaceEntry k v l := by
simp [replaceEntry, h]
@@ -553,37 +553,37 @@ theorem getEntry?_replaceEntry_of_containsKey_eq_false [BEq α] {l : List ((a :
rw [replaceEntry_of_containsKey_eq_false hl]
theorem getEntry?_replaceEntry_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{a k : α} {v : β k} (h : (a == k) = false) :
{a k : α} {v : β k} (h : (k == a) = false) :
getEntry? a (replaceEntry k v l) = getEntry? a l := by
induction l using assoc_induction
· simp
· next k' v' l ih =>
cases h' : k == k'
cases h' : k' == k
· rw [replaceEntry_cons_of_false h', getEntry?_cons, getEntry?_cons, ih]
· rw [replaceEntry_cons_of_true h']
have hk : (a == k') = false := BEq.neq_of_neq_of_beq h h'
have hk : (k' == a) = false := BEq.neq_of_beq_of_neq h' h
simp [getEntry?_cons_of_false h, getEntry?_cons_of_false hk]
theorem getEntry?_replaceEntry_of_true [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{a k : α} {v : β k} (hl : containsKey k l = true) (h : a == k) :
{a k : α} {v : β k} (hl : containsKey k l = true) (h : k == a) :
getEntry? a (replaceEntry k v l) = some k, v := by
induction l using assoc_induction
· simp at hl
· next k' v' l ih =>
cases hk'a : k == k'
cases hk'a : k' == k
· rw [replaceEntry_cons_of_false hk'a]
have hk'k : (a == k') = false := BEq.neq_of_beq_of_neq h hk'a
have hk'k : (k' == a) = false := BEq.neq_of_neq_of_beq hk'a h
rw [getEntry?_cons_of_false hk'k]
exact ih (containsKey_of_containsKey_cons hl hk'a)
· rw [replaceEntry_cons_of_true hk'a, getEntry?_cons_of_true h]
theorem getEntry?_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
{v : β k} :
getEntry? a (replaceEntry k v l) = bif containsKey k l && a == k then some k, v else
getEntry? a (replaceEntry k v l) = bif containsKey k l && k == a then some k, v else
getEntry? a l := by
cases hl : containsKey k l
· simp [getEntry?_replaceEntry_of_containsKey_eq_false hl]
· cases h : a == k
· cases h : k == a
· simp [getEntry?_replaceEntry_of_false h]
· simp [getEntry?_replaceEntry_of_true hl h]
@@ -601,12 +601,12 @@ theorem getValue?_replaceEntry_of_containsKey_eq_false [BEq α] {l : List ((_ :
simp [getValue?_eq_getEntry?, getEntry?_replaceEntry_of_containsKey_eq_false hl]
theorem getValue?_replaceEntry_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {v : β} (h : (a == k) = false) :
{k a : α} {v : β} (h : (k == a) = false) :
getValue? a (replaceEntry k v l) = getValue? a l := by
simp [getValue?_eq_getEntry?, getEntry?_replaceEntry_of_false h]
theorem getValue?_replaceEntry_of_true [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {v : β} (hl : containsKey k l = true) (h : a == k) :
{k a : α} {v : β} (hl : containsKey k l = true) (h : k == a) :
getValue? a (replaceEntry k v l) = some v := by
simp [getValue?_eq_getEntry?, getEntry?_replaceEntry_of_true hl h]
@@ -614,7 +614,7 @@ end
theorem getValueCast?_replaceEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {a k : α}
{v : β k} : getValueCast? a (replaceEntry k v l) =
if h : containsKey k l a == k then some (cast (congrArg β (eq_of_beq h.2).symm) v)
if h : containsKey k l k == a then some (cast (congrArg β (eq_of_beq h.2)) v)
else getValueCast? a l := by
rw [getValueCast?_eq_getEntry?]
split
@@ -632,61 +632,61 @@ theorem getValueCast?_replaceEntry [BEq α] [LawfulBEq α] {l : List ((a : α)
@[simp]
theorem containsKey_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
{v : β k} : containsKey a (replaceEntry k v l) = containsKey a l := by
cases h : containsKey k l && a == k
cases h : containsKey k l && k == a
· rw [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h, cond_false,
containsKey_eq_isSome_getEntry?]
· rw [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h, cond_true, Option.isSome_some,
Eq.comm]
rw [Bool.and_eq_true] at h
exact containsKey_of_beq h.1 (BEq.symm h.2)
exact containsKey_of_beq h.1 h.2
/-- Internal implementation detail of the hash map -/
def removeKey [BEq α] (k : α) : List ((a : α) × β a) List ((a : α) × β a)
def eraseKey [BEq α] (k : α) : List ((a : α) × β a) List ((a : α) × β a)
| [] => []
| k', v' :: l => bif k == k' then l else k', v' :: removeKey k l
| k', v' :: l => bif k' == k then l else k', v' :: eraseKey k l
@[simp] theorem removeKey_nil [BEq α] {k : α} : removeKey k ([] : List ((a : α) × β a)) = [] := rfl
theorem removeKey_cons [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'} :
removeKey k (k', v' :: l) = bif k == k' then l else k', v' :: removeKey k l := rfl
@[simp] theorem eraseKey_nil [BEq α] {k : α} : eraseKey k ([] : List ((a : α) × β a)) = [] := rfl
theorem eraseKey_cons [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'} :
eraseKey k (k', v' :: l) = bif k' == k then l else k', v' :: eraseKey k l := rfl
theorem removeKey_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
(h : k == k') : removeKey k (k', v' :: l) = l :=
by simp [removeKey_cons, h]
theorem eraseKey_cons_of_beq [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
(h : k' == k) : eraseKey k (k', v' :: l) = l :=
by simp [eraseKey_cons, h]
@[simp]
theorem removeKey_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
removeKey k (k, v :: l) = l :=
removeKey_cons_of_beq BEq.refl
theorem eraseKey_cons_self [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
eraseKey k (k, v :: l) = l :=
eraseKey_cons_of_beq BEq.refl
theorem removeKey_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
(h : (k == k') = false) : removeKey k (k', v' :: l) = k', v' :: removeKey k l := by
simp [removeKey_cons, h]
theorem eraseKey_cons_of_false [BEq α] {l : List ((a : α) × β a)} {k k' : α} {v' : β k'}
(h : (k' == k) = false) : eraseKey k (k', v' :: l) = k', v' :: eraseKey k l := by
simp [eraseKey_cons, h]
theorem removeKey_of_containsKey_eq_false [BEq α] {l : List ((a : α) × β a)} {k : α}
(h : containsKey k l = false) : removeKey k l = l := by
theorem eraseKey_of_containsKey_eq_false [BEq α] {l : List ((a : α) × β a)} {k : α}
(h : containsKey k l = false) : eraseKey k l = l := by
induction l using assoc_induction
· simp
· next k' v' t ih =>
simp only [containsKey_cons, Bool.or_eq_false_iff] at h
rw [removeKey_cons_of_false h.1, ih h.2]
rw [eraseKey_cons_of_false h.1, ih h.2]
theorem sublist_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
Sublist (removeKey k l) l := by
theorem sublist_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
Sublist (eraseKey k l) l := by
induction l using assoc_induction
· simp
· next k' v' t ih =>
rw [removeKey_cons]
cases k == k'
rw [eraseKey_cons]
cases k' == k
· simpa
· simpa using Sublist.cons_right Sublist.refl
theorem length_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
(removeKey k l).length = bif containsKey k l then l.length - 1 else l.length := by
theorem length_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
(eraseKey k l).length = bif containsKey k l then l.length - 1 else l.length := by
induction l using assoc_induction
· simp
· next k' v' t ih =>
rw [removeKey_cons, containsKey_cons]
cases k == k'
rw [eraseKey_cons, containsKey_cons]
cases k' == k
· rw [cond_false, Bool.false_or, List.length_cons, ih]
cases h : containsKey k t
· simp
@@ -697,15 +697,15 @@ theorem length_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
· simp
· simp
theorem length_removeKey_le [BEq α] {l : List ((a : α) × β a)} {k : α} :
(removeKey k l).length l.length :=
sublist_removeKey.length_le
theorem length_eraseKey_le [BEq α] {l : List ((a : α) × β a)} {k : α} :
(eraseKey k l).length l.length :=
sublist_eraseKey.length_le
theorem isEmpty_removeKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
(removeKey k l).isEmpty = (l.isEmpty || (l.length == 1 && containsKey k l)) := by
theorem isEmpty_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
(eraseKey k l).isEmpty = (l.isEmpty || (l.length == 1 && containsKey k l)) := by
rw [Bool.eq_iff_iff]
simp only [Bool.or_eq_true, Bool.and_eq_true, beq_iff_eq]
rw [List.isEmpty_iff_length_eq_zero, length_removeKey, List.isEmpty_iff_length_eq_zero]
rw [List.isEmpty_iff_length_eq_zero, length_eraseKey, List.isEmpty_iff_length_eq_zero]
cases containsKey k l <;> cases l <;> simp
@[simp] theorem keys_nil : keys ([] : List ((a : α) × β a)) = [] := rfl
@@ -722,7 +722,7 @@ theorem containsKey_eq_keys_contains [BEq α] [PartialEquivBEq α] {l : List ((a
· next k _ l ih => simp [ih, BEq.comm]
theorem containsKey_eq_true_iff_exists_mem [BEq α] {l : List ((a : α) × β a)} {a : α} :
containsKey a l = true p l, a == p.1 := by
containsKey a l = true p l, p.1 == a := by
induction l using assoc_induction <;> simp_all
theorem containsKey_of_mem [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} {p : (a : α) × β a}
@@ -798,11 +798,11 @@ theorem mem_iff_getEntry?_eq_some [BEq α] [EquivBEq α] {l : List ((a : α) ×
refine ?_, ?_
· rintro (rfl|hk)
· simp
· suffices (p.fst == k) = false by simp_all
· suffices (k == p.fst) = false by simp_all
refine Bool.eq_false_iff.2 fun hcon => Bool.false_ne_true ?_
rw [ h.containsKey_eq_false, containsKey_congr (BEq.symm hcon),
rw [ h.containsKey_eq_false, containsKey_congr hcon,
containsKey_eq_isSome_getEntry?, hk, Option.isSome_some]
· cases p.fst == k
· cases k == p.fst
· rw [cond_false]
exact Or.inr
· rw [cond_true, Option.some.injEq]
@@ -814,7 +814,7 @@ theorem DistinctKeys.replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a :
· simp
· next k' v' l ih =>
rw [distinctKeys_cons_iff] at h
cases hk'k : k == k'
cases hk'k : k' == k
· rw [replaceEntry_cons_of_false hk'k, distinctKeys_cons_iff]
refine ih h.1, ?_
simpa using h.2
@@ -870,7 +870,7 @@ section
variable {β : Type v}
theorem getValue?_insertEntry_of_beq [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
{v : β} (h : a == k) : getValue? a (insertEntry k v l) = some v := by
{v : β} (h : k == a) : getValue? a (insertEntry k v l) = some v := by
cases h' : containsKey k l
· rw [insertEntry_of_containsKey_eq_false h', getValue?_cons_of_true h]
· rw [insertEntry_of_containsKey h', getValue?_replaceEntry_of_true h' h]
@@ -880,14 +880,14 @@ theorem getValue?_insertEntry_of_self [BEq α] [EquivBEq α] {l : List ((_ : α)
getValue?_insertEntry_of_beq BEq.refl
theorem getValue?_insertEntry_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {v : β} (h : (a == k) = false) : getValue? a (insertEntry k v l) = getValue? a l := by
{k a : α} {v : β} (h : (k == a) = false) : getValue? a (insertEntry k v l) = getValue? a l := by
cases h' : containsKey k l
· rw [insertEntry_of_containsKey_eq_false h', getValue?_cons_of_false h]
· rw [insertEntry_of_containsKey h', getValue?_replaceEntry_of_false h]
theorem getValue?_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
{v : β} : getValue? a (insertEntry k v l) = bif a == k then some v else getValue? a l := by
cases h : a == k
{v : β} : getValue? a (insertEntry k v l) = bif k == a then some v else getValue? a l := by
cases h : k == a
· simp [getValue?_insertEntry_of_false h, h]
· simp [getValue?_insertEntry_of_beq h, h]
@@ -899,14 +899,14 @@ end
theorem getEntry?_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} :
getEntry? a (insertEntry k v l) = bif a == k then some k, v else getEntry? a l := by
getEntry? a (insertEntry k v l) = bif k == a then some k, v else getEntry? a l := by
cases hl : containsKey k l
· rw [insertEntry_of_containsKey_eq_false hl, getEntry?_cons]
· rw [insertEntry_of_containsKey hl, getEntry?_replaceEntry, hl, Bool.true_and, BEq.comm]
theorem getValueCast?_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} : getValueCast? a (insertEntry k v l) =
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else getValueCast? a l := by
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else getValueCast? a l := by
cases hl : containsKey k l
· rw [insertEntry_of_containsKey_eq_false hl, getValueCast?_cons]
· rw [insertEntry_of_containsKey hl, getValueCast?_replaceEntry, hl]
@@ -918,7 +918,7 @@ theorem getValueCast?_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a :
theorem getValueCast!_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
[Inhabited (β a)] {v : β k} : getValueCast! a (insertEntry k v l) =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else getValueCast! a l := by
if h : k == a then cast (congrArg β (eq_of_beq h)) v else getValueCast! a l := by
simp [getValueCast!_eq_getValueCast?, getValueCast?_insertEntry, apply_dite Option.get!]
theorem getValueCast!_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
@@ -927,7 +927,7 @@ theorem getValueCast!_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a :
theorem getValueCastD_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{fallback : β a} {v : β k} : getValueCastD a (insertEntry k v l) fallback =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v
if h : k == a then cast (congrArg β (eq_of_beq h)) v
else getValueCastD a l fallback := by
simp [getValueCastD_eq_getValueCast?, getValueCast?_insertEntry,
apply_dite (fun x => Option.getD x fallback)]
@@ -938,7 +938,7 @@ theorem getValueCastD_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a :
theorem getValue!_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
{l : List ((_ : α) × β)} {k a : α} {v : β} :
getValue! a (insertEntry k v l) = bif a == k then v else getValue! a l := by
getValue! a (insertEntry k v l) = bif k == a then v else getValue! a l := by
simp [getValue!_eq_getValue?, getValue?_insertEntry, Bool.apply_cond Option.get!]
theorem getValue!_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] [Inhabited β]
@@ -947,7 +947,7 @@ theorem getValue!_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] [Inhabit
theorem getValueD_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {fallback v : β} : getValueD a (insertEntry k v l) fallback =
bif a == k then v else getValueD a l fallback := by
bif k == a then v else getValueD a l fallback := by
simp [getValueD_eq_getValue?, getValue?_insertEntry, Bool.apply_cond (fun x => Option.getD x fallback)]
theorem getValueD_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : List ((_ : α) × β)}
@@ -956,12 +956,12 @@ theorem getValueD_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : Lis
@[simp]
theorem containsKey_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} : containsKey a (insertEntry k v l) = ((a == k) || containsKey a l) := by
{v : β k} : containsKey a (insertEntry k v l) = ((k == a) || containsKey a l) := by
rw [containsKey_eq_isSome_getEntry?, containsKey_eq_isSome_getEntry?, getEntry?_insertEntry]
cases a == k <;> simp
cases k == a <;> simp
theorem containsKey_insertEntry_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} {v : β k} (h : a == k) : containsKey a (insertEntry k v l) := by
{k a : α} {v : β k} (h : k == a) : containsKey a (insertEntry k v l) := by
simp [h]
@[simp]
@@ -971,12 +971,12 @@ theorem containsKey_insertEntry_self [BEq α] [EquivBEq α] {l : List ((a : α)
theorem containsKey_of_containsKey_insertEntry [BEq α] [PartialEquivBEq α]
{l : List ((a : α) × β a)} {k a : α} {v : β k} (h₁ : containsKey a (insertEntry k v l))
(h₂ : (a == k) = false) : containsKey a l := by
(h₂ : (k == a) = false) : containsKey a l := by
rwa [containsKey_insertEntry, h₂, Bool.false_or] at h₁
theorem getValueCast_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} {h} : getValueCast a (insertEntry k v l) h =
if h' : a == k then cast (congrArg β (eq_of_beq h').symm) v
if h' : k == a then cast (congrArg β (eq_of_beq h')) v
else getValueCast a l (containsKey_of_containsKey_insertEntry h (Bool.eq_false_iff.2 h')) := by
rw [ Option.some_inj, getValueCast?_eq_some_getValueCast, apply_dite Option.some,
getValueCast?_insertEntry]
@@ -988,7 +988,7 @@ theorem getValueCast_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a : α
theorem getValue_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {v : β} {h} : getValue a (insertEntry k v l) h =
if h' : a == k then v
if h' : k == a then v
else getValue a l (containsKey_of_containsKey_insertEntry h (Bool.eq_false_iff.2 h')) := by
rw [ Option.some_inj, getValue?_eq_some_getValue, apply_dite Option.some,
getValue?_insertEntry, cond_eq_if, dite_eq_ite]
@@ -1020,14 +1020,14 @@ theorem isEmpty_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)} {k : α}
theorem getEntry?_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} : getEntry? a (insertEntryIfNew k v l) =
bif a == k && !containsKey k l then some k, v else getEntry? a l := by
bif k == a && !containsKey k l then some k, v else getEntry? a l := by
cases h : containsKey k l
· simp [insertEntryIfNew_of_containsKey_eq_false h, getEntry?_cons]
· simp [insertEntryIfNew_of_containsKey h]
theorem getValueCast?_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} : getValueCast? a (insertEntryIfNew k v l) =
if h : a == k containsKey k l = false then some (cast (congrArg β (eq_of_beq h.1).symm) v)
if h : k == a containsKey k l = false then some (cast (congrArg β (eq_of_beq h.1)) v)
else getValueCast? a l := by
cases h : containsKey k l
· rw [insertEntryIfNew_of_containsKey_eq_false h, getValueCast?_cons]
@@ -1036,16 +1036,16 @@ theorem getValueCast?_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a :
theorem getValue?_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {v : β} : getValue? a (insertEntryIfNew k v l) =
bif a == k && !containsKey k l then some v else getValue? a l := by
bif k == a && !containsKey k l then some v else getValue? a l := by
simp [getValue?_eq_getEntry?, getEntry?_insertEntryIfNew,
Bool.apply_cond (Option.map (fun (y : ((_ : α) × β)) => y.2))]
theorem containsKey_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} {v : β k} :
containsKey a (insertEntryIfNew k v l) = ((a == k) || containsKey a l) := by
containsKey a (insertEntryIfNew k v l) = ((k == a) || containsKey a l) := by
simp only [containsKey_eq_isSome_getEntry?, getEntry?_insertEntryIfNew, Bool.apply_cond Option.isSome,
Option.isSome_some, Bool.cond_true_left]
cases h : a == k
cases h : k == a
· simp
· rw [Bool.true_and, Bool.true_or, getEntry?_congr h, Bool.not_or_self]
@@ -1055,7 +1055,7 @@ theorem containsKey_insertEntryIfNew_self [BEq α] [EquivBEq α] {l : List ((a :
theorem containsKey_of_containsKey_insertEntryIfNew [BEq α] [PartialEquivBEq α]
{l : List ((a : α) × β a)} {k a : α} {v : β k} (h₁ : containsKey a (insertEntryIfNew k v l))
(h₂ : (a == k) = false) : containsKey a l := by
(h₂ : (k == a) = false) : containsKey a l := by
rwa [containsKey_insertEntryIfNew, h₂, Bool.false_or] at h₁
/--
@@ -1064,7 +1064,7 @@ obligation in the statement of `getValueCast_insertEntryIfNew`.
-/
theorem containsKey_of_containsKey_insertEntryIfNew' [BEq α] [PartialEquivBEq α]
{l : List ((a : α) × β a)} {k a : α} {v : β k} (h₁ : containsKey a (insertEntryIfNew k v l))
(h₂ : ¬((a == k) containsKey k l = false)) : containsKey a l := by
(h₂ : ¬((k == a) containsKey k l = false)) : containsKey a l := by
rw [Decidable.not_and_iff_or_not, Bool.not_eq_true, Bool.not_eq_false] at h₂
rcases h₂ with h₂|h₂
· rwa [containsKey_insertEntryIfNew, h₂, Bool.false_or] at h₁
@@ -1072,8 +1072,8 @@ theorem containsKey_of_containsKey_insertEntryIfNew' [BEq α] [PartialEquivBEq
theorem getValueCast_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} {h} : getValueCast a (insertEntryIfNew k v l) h =
if h' : a == k containsKey k l = false then
cast (congrArg β (eq_of_beq h'.1).symm) v
if h' : k == a containsKey k l = false then
cast (congrArg β (eq_of_beq h'.1)) v
else
getValueCast a l (containsKey_of_containsKey_insertEntryIfNew' h h') := by
rw [ Option.some_inj, getValueCast?_eq_some_getValueCast, apply_dite Option.some,
@@ -1082,7 +1082,7 @@ theorem getValueCast_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α
theorem getValue_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {v : β} {h} : getValue a (insertEntryIfNew k v l) h =
if h' : a == k containsKey k l = false then v
if h' : k == a containsKey k l = false then v
else getValue a l (containsKey_of_containsKey_insertEntryIfNew' h h') := by
rw [ Option.some_inj, getValue?_eq_some_getValue, apply_dite Option.some,
getValue?_insertEntryIfNew, cond_eq_if, dite_eq_ite]
@@ -1090,25 +1090,25 @@ theorem getValue_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l
theorem getValueCast!_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} [Inhabited (β a)] : getValueCast! a (insertEntryIfNew k v l) =
if h : a == k containsKey k l = false then cast (congrArg β (eq_of_beq h.1).symm) v
if h : k == a containsKey k l = false then cast (congrArg β (eq_of_beq h.1)) v
else getValueCast! a l := by
simp [getValueCast!_eq_getValueCast?, getValueCast?_insertEntryIfNew, apply_dite Option.get!]
theorem getValue!_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
{l : List ((_ : α) × β)} {k a : α} {v : β} : getValue! a (insertEntryIfNew k v l) =
bif a == k && !containsKey k l then v else getValue! a l := by
bif k == a && !containsKey k l then v else getValue! a l := by
simp [getValue!_eq_getValue?, getValue?_insertEntryIfNew, Bool.apply_cond Option.get!]
theorem getValueCastD_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{v : β k} {fallback : β a} : getValueCastD a (insertEntryIfNew k v l) fallback =
if h : a == k containsKey k l = false then cast (congrArg β (eq_of_beq h.1).symm) v
if h : k == a containsKey k l = false then cast (congrArg β (eq_of_beq h.1)) v
else getValueCastD a l fallback := by
simp [getValueCastD_eq_getValueCast?, getValueCast?_insertEntryIfNew,
apply_dite (fun x => Option.getD x fallback)]
theorem getValueD_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {fallback v : β} : getValueD a (insertEntryIfNew k v l) fallback =
bif a == k && !containsKey k l then v else getValueD a l fallback := by
bif k == a && !containsKey k l then v else getValueD a l fallback := by
simp [getValueD_eq_getValue?, getValue?_insertEntryIfNew,
Bool.apply_cond (fun x => Option.getD x fallback)]
@@ -1124,55 +1124,55 @@ theorem length_le_length_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)}
· simp
@[simp]
theorem keys_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
keys (removeKey k l) = (keys l).erase k := by
theorem keys_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
keys (eraseKey k l) = (keys l).erase k := by
induction l using assoc_induction
· rfl
· next k' v' l ih =>
simp only [removeKey_cons, keys_cons, List.erase_cons]
simp only [eraseKey_cons, keys_cons, List.erase_cons]
rw [BEq.comm]
cases k' == k <;> simp [ih]
cases k == k' <;> simp [ih]
theorem DistinctKeys.removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
DistinctKeys l DistinctKeys (removeKey k l) := by
theorem DistinctKeys.eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
DistinctKeys l DistinctKeys (eraseKey k l) := by
apply distinctKeys_of_sublist_keys (by simpa using erase_sublist _ _)
theorem getEntry?_removeKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
(h : DistinctKeys l) : getEntry? k (removeKey k l) = none := by
theorem getEntry?_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
(h : DistinctKeys l) : getEntry? k (eraseKey k l) = none := by
induction l using assoc_induction
· simp
· next k' v' t ih =>
cases h' : k == k'
· rw [removeKey_cons_of_false h', getEntry?_cons_of_false h']
cases h' : k' == k
· rw [eraseKey_cons_of_false h', getEntry?_cons_of_false h']
exact ih h.tail
· rw [removeKey_cons_of_beq h', Option.not_isSome_iff_eq_none, Bool.not_eq_true,
containsKey_eq_isSome_getEntry?, containsKey_congr (BEq.symm h')]
· rw [eraseKey_cons_of_beq h', Option.not_isSome_iff_eq_none, Bool.not_eq_true,
containsKey_eq_isSome_getEntry?, containsKey_congr h']
exact h.containsKey_eq_false
theorem getEntry?_removeKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
(hl : DistinctKeys l) (hka : a == k) : getEntry? a (removeKey k l) = none := by
rw [ getEntry?_congr (BEq.symm hka), getEntry?_removeKey_self hl]
theorem getEntry?_eraseKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
(hl : DistinctKeys l) (hka : k == a) : getEntry? a (eraseKey k l) = none := by
rw [ getEntry?_congr hka, getEntry?_eraseKey_self hl]
theorem getEntry?_removeKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hka : (a == k) = false) : getEntry? a (removeKey k l) = getEntry? a l := by
theorem getEntry?_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hka : (k == a) = false) : getEntry? a (eraseKey k l) = getEntry? a l := by
induction l using assoc_induction
· simp
· next k' v' t ih =>
cases h' : k == k'
· rw [removeKey_cons_of_false h']
cases h'' : a == k'
cases h' : k' == k
· rw [eraseKey_cons_of_false h']
cases h'' : k' == a
· rw [getEntry?_cons_of_false h'', ih, getEntry?_cons_of_false h'']
· rw [getEntry?_cons_of_true h'', getEntry?_cons_of_true h'']
· rw [removeKey_cons_of_beq h']
have hx : (a == k') = false := BEq.neq_of_neq_of_beq hka h'
· rw [eraseKey_cons_of_beq h']
have hx : (k' == a) = false := BEq.neq_of_beq_of_neq h' hka
rw [getEntry?_cons_of_false hx]
theorem getEntry?_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
theorem getEntry?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
(hl : DistinctKeys l) :
getEntry? a (removeKey k l) = bif a == k then none else getEntry? a l := by
cases h : a == k
· simp [getEntry?_removeKey_of_false h, h]
· simp [getEntry?_removeKey_of_beq hl h, h]
getEntry? a (eraseKey k l) = bif k == a then none else getEntry? a l := by
cases h : k == a
· simp [getEntry?_eraseKey_of_false h, h]
· simp [getEntry?_eraseKey_of_beq hl h, h]
theorem keys_filterMap [BEq α] {l : List ((a : α) × β a)} {f : (a : α) β a Option (γ a)} :
keys (l.filterMap fun p => (f p.1 p.2).map (p.1, ·)) =
@@ -1208,110 +1208,110 @@ section
variable {β : Type v}
theorem getValue?_removeKey_self [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k : α}
(h : DistinctKeys l) : getValue? k (removeKey k l) = none := by
simp [getValue?_eq_getEntry?, getEntry?_removeKey_self h]
theorem getValue?_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k : α}
(h : DistinctKeys l) : getValue? k (eraseKey k l) = none := by
simp [getValue?_eq_getEntry?, getEntry?_eraseKey_self h]
theorem getValue?_removeKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
(hl : DistinctKeys l) (hka : a == k) : getValue? a (removeKey k l) = none := by
simp [getValue?_eq_getEntry?, getEntry?_removeKey_of_beq hl hka]
theorem getValue?_eraseKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
(hl : DistinctKeys l) (hka : k == a) : getValue? a (eraseKey k l) = none := by
simp [getValue?_eq_getEntry?, getEntry?_eraseKey_of_beq hl hka]
theorem getValue?_removeKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
(hka : (a == k) = false) : getValue? a (removeKey k l) = getValue? a l := by
simp [getValue?_eq_getEntry?, getEntry?_removeKey_of_false hka]
theorem getValue?_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
(hka : (k == a) = false) : getValue? a (eraseKey k l) = getValue? a l := by
simp [getValue?_eq_getEntry?, getEntry?_eraseKey_of_false hka]
theorem getValue?_removeKey [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
theorem getValue?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
(hl : DistinctKeys l) :
getValue? a (removeKey k l) = bif a == k then none else getValue? a l := by
simp [getValue?_eq_getEntry?, getEntry?_removeKey hl, Bool.apply_cond (Option.map _)]
getValue? a (eraseKey k l) = bif k == a then none else getValue? a l := by
simp [getValue?_eq_getEntry?, getEntry?_eraseKey hl, Bool.apply_cond (Option.map _)]
end
theorem containsKey_removeKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
(h : DistinctKeys l) : containsKey k (removeKey k l) = false := by
simp [containsKey_eq_isSome_getEntry?, getEntry?_removeKey_self h]
theorem containsKey_eraseKey_self [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α}
(h : DistinctKeys l) : containsKey k (eraseKey k l) = false := by
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey_self h]
theorem containsKey_removeKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hl : DistinctKeys l) (hka : a == k) : containsKey a (removeKey k l) = false := by
rw [containsKey_congr hka, containsKey_removeKey_self hl]
theorem containsKey_eraseKey_of_beq [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hl : DistinctKeys l) (hka : a == k) : containsKey a (eraseKey k l) = false := by
rw [containsKey_congr hka, containsKey_eraseKey_self hl]
theorem containsKey_removeKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hka : (a == k) = false) : containsKey a (removeKey k l) = containsKey a l := by
simp [containsKey_eq_isSome_getEntry?, getEntry?_removeKey_of_false hka]
theorem containsKey_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hka : (k == a) = false) : containsKey a (eraseKey k l) = containsKey a l := by
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey_of_false hka]
theorem containsKey_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
(hl : DistinctKeys l) : containsKey a (removeKey k l) = (!(a == k) && containsKey a l) := by
simp [containsKey_eq_isSome_getEntry?, getEntry?_removeKey hl, Bool.apply_cond]
theorem containsKey_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
(hl : DistinctKeys l) : containsKey a (eraseKey k l) = (!(k == a) && containsKey a l) := by
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey hl, Bool.apply_cond]
theorem getValueCast?_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
theorem getValueCast?_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
(hl : DistinctKeys l) :
getValueCast? a (removeKey k l) = bif a == k then none else getValueCast? a l := by
rw [getValueCast?_eq_getEntry?, Option.dmap_congr (getEntry?_removeKey hl)]
rcases Bool.eq_false_or_eq_true (a == k) with h|h
getValueCast? a (eraseKey k l) = bif k == a then none else getValueCast? a l := by
rw [getValueCast?_eq_getEntry?, Option.dmap_congr (getEntry?_eraseKey hl)]
rcases Bool.eq_false_or_eq_true (k == a) with h|h
· rw [Option.dmap_congr (Bool.cond_pos h), Option.dmap_none, Bool.cond_pos h]
· rw [Option.dmap_congr (Bool.cond_neg h), getValueCast?_eq_getEntry?]
exact (Bool.cond_neg h).symm
theorem getValueCast?_removeKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
(hl : DistinctKeys l) : getValueCast? k (removeKey k l) = none := by
rw [getValueCast?_removeKey hl, Bool.cond_pos BEq.refl]
theorem getValueCast?_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
(hl : DistinctKeys l) : getValueCast? k (eraseKey k l) = none := by
rw [getValueCast?_eraseKey hl, Bool.cond_pos BEq.refl]
theorem getValueCast!_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
theorem getValueCast!_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
[Inhabited (β a)] (hl : DistinctKeys l) :
getValueCast! a (removeKey k l) = bif a == k then default else getValueCast! a l := by
simp [getValueCast!_eq_getValueCast?, getValueCast?_removeKey hl, Bool.apply_cond Option.get!]
getValueCast! a (eraseKey k l) = bif k == a then default else getValueCast! a l := by
simp [getValueCast!_eq_getValueCast?, getValueCast?_eraseKey hl, Bool.apply_cond Option.get!]
theorem getValueCast!_removeKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
[Inhabited (β k)] (hl : DistinctKeys l) : getValueCast! k (removeKey k l) = default := by
simp [getValueCast!_eq_getValueCast?, getValueCast?_removeKey_self hl]
theorem getValueCast!_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
[Inhabited (β k)] (hl : DistinctKeys l) : getValueCast! k (eraseKey k l) = default := by
simp [getValueCast!_eq_getValueCast?, getValueCast?_eraseKey_self hl]
theorem getValueCastD_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{fallback : β a} (hl : DistinctKeys l) : getValueCastD a (removeKey k l) fallback =
bif a == k then fallback else getValueCastD a l fallback := by
simp [getValueCastD_eq_getValueCast?, getValueCast?_removeKey hl,
theorem getValueCastD_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
{fallback : β a} (hl : DistinctKeys l) : getValueCastD a (eraseKey k l) fallback =
bif k == a then fallback else getValueCastD a l fallback := by
simp [getValueCastD_eq_getValueCast?, getValueCast?_eraseKey hl,
Bool.apply_cond (fun x => Option.getD x fallback)]
theorem getValueCastD_removeKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
theorem getValueCastD_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
{fallback : β k} (hl : DistinctKeys l) :
getValueCastD k (removeKey k l) fallback = fallback := by
simp [getValueCastD_eq_getValueCast?, getValueCast?_removeKey_self hl]
getValueCastD k (eraseKey k l) fallback = fallback := by
simp [getValueCastD_eq_getValueCast?, getValueCast?_eraseKey_self hl]
theorem getValue!_removeKey {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
theorem getValue!_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
{l : List ((_ : α) × β)} {k a : α} (hl : DistinctKeys l) :
getValue! a (removeKey k l) = bif a == k then default else getValue! a l := by
simp [getValue!_eq_getValue?, getValue?_removeKey hl, Bool.apply_cond Option.get!]
getValue! a (eraseKey k l) = bif k == a then default else getValue! a l := by
simp [getValue!_eq_getValue?, getValue?_eraseKey hl, Bool.apply_cond Option.get!]
theorem getValue!_removeKey_self {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
theorem getValue!_eraseKey_self {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
{l : List ((_ : α) × β)} {k : α} (hl : DistinctKeys l) :
getValue! k (removeKey k l) = default := by
simp [getValue!_eq_getValue?, getValue?_removeKey_self hl]
getValue! k (eraseKey k l) = default := by
simp [getValue!_eq_getValue?, getValue?_eraseKey_self hl]
theorem getValueD_removeKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {fallback : β} (hl : DistinctKeys l) : getValueD a (removeKey k l) fallback =
bif a == k then fallback else getValueD a l fallback := by
simp [getValueD_eq_getValue?, getValue?_removeKey hl, Bool.apply_cond (fun x => Option.getD x fallback)]
theorem getValueD_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {fallback : β} (hl : DistinctKeys l) : getValueD a (eraseKey k l) fallback =
bif k == a then fallback else getValueD a l fallback := by
simp [getValueD_eq_getValue?, getValue?_eraseKey hl, Bool.apply_cond (fun x => Option.getD x fallback)]
theorem getValueD_removeKey_self {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
theorem getValueD_eraseKey_self {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k : α} {fallback : β} (hl : DistinctKeys l) :
getValueD k (removeKey k l) fallback = fallback := by
simp [getValueD_eq_getValue?, getValue?_removeKey_self hl]
getValueD k (eraseKey k l) fallback = fallback := by
simp [getValueD_eq_getValue?, getValue?_eraseKey_self hl]
theorem containsKey_of_containsKey_removeKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hl : DistinctKeys l) : containsKey a (removeKey k l) containsKey a l := by
simp [containsKey_removeKey hl]
theorem containsKey_of_containsKey_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
{k a : α} (hl : DistinctKeys l) : containsKey a (eraseKey k l) containsKey a l := by
simp [containsKey_eraseKey hl]
theorem getValueCast_removeKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {h}
(hl : DistinctKeys l) : getValueCast a (removeKey k l) h =
getValueCast a l (containsKey_of_containsKey_removeKey hl h) := by
rw [containsKey_removeKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
rw [ Option.some_inj, getValueCast?_eq_some_getValueCast, getValueCast?_removeKey hl, h.1,
theorem getValueCast_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {h}
(hl : DistinctKeys l) : getValueCast a (eraseKey k l) h =
getValueCast a l (containsKey_of_containsKey_eraseKey hl h) := by
rw [containsKey_eraseKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
rw [ Option.some_inj, getValueCast?_eq_some_getValueCast, getValueCast?_eraseKey hl, h.1,
cond_false, getValueCast?_eq_some_getValueCast]
theorem getValue_removeKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
theorem getValue_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
{k a : α} {h} (hl : DistinctKeys l) :
getValue a (removeKey k l) h = getValue a l (containsKey_of_containsKey_removeKey hl h) := by
rw [containsKey_removeKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
rw [ Option.some_inj, getValue?_eq_some_getValue, getValue?_removeKey hl, h.1, cond_false,
getValue a (eraseKey k l) h = getValue a l (containsKey_of_containsKey_eraseKey hl h) := by
rw [containsKey_eraseKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
rw [ Option.some_inj, getValue?_eq_some_getValue, getValue?_eraseKey hl, h.1, cond_false,
getValue?_eq_some_getValue]
theorem getEntry?_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {a : α}
@@ -1325,9 +1325,9 @@ theorem getEntry?_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α)
rcases p with k₁, v₁
rcases p' with k₂, v₂
simp only [getEntry?_cons]
cases h₂ : a == k₂ <;> cases h₁ : a == k₁ <;> try simp; done
cases h₂ : k₂ == a <;> cases h₁ : k₁ == a <;> try simp; done
simp only [distinctKeys_cons_iff, containsKey_cons, Bool.or_eq_false_iff] at hl
exact ((Bool.eq_false_iff.1 hl.2.1).elim (BEq.trans (BEq.symm h₁) h₂)).elim
exact ((Bool.eq_false_iff.1 hl.2.1).elim (BEq.trans h₂ (BEq.symm h₁))).elim
· next l₁ l₂ l₃ hl₁₂ _ ih₁ ih₂ => exact (ih₁ hl).trans (ih₂ (hl.perm (hl₁₂.symm)))
theorem containsKey_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {k : α}
@@ -1392,7 +1392,7 @@ theorem perm_cons_getEntry [BEq α] {l : List ((a : α) × β a)} {a : α} (h :
· simp at h
· next k' v' t ih =>
simp only [containsKey_cons, Bool.or_eq_true] at h
cases hk : a == k'
cases hk : k' == a
· obtain l', hl' := ih (h.resolve_left (Bool.not_eq_true _ hk))
rw [getEntry_cons_of_false hk]
exact k', v' :: l', (hl'.cons _).trans (Perm.swap _ _ (Perm.refl _))
@@ -1414,9 +1414,9 @@ theorem getEntry?_ext [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} (h
suffices Perm t l'' from (this.cons _).trans hl''.symm
apply ih hl.tail (hl'.perm hl''.symm).tail
intro k'
cases hk' : k' == k
cases hk' : k == k'
· simpa only [getEntry?_of_perm hl' hl'', getEntry?_cons_of_false hk'] using h k'
· rw [getEntry?_congr hk', getEntry?_congr hk', getEntry?_eq_none.2 hl.containsKey_eq_false,
· rw [ getEntry?_congr hk', getEntry?_congr hk', getEntry?_eq_none.2 hl.containsKey_eq_false,
getEntry?_eq_none.2 (hl'.perm hl''.symm).containsKey_eq_false]
theorem replaceEntry_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} {k : α} {v : β k}
@@ -1429,17 +1429,17 @@ theorem insertEntry_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β
apply getEntry?_ext hl.insertEntry (hl.perm h.symm).insertEntry
simp [getEntry?_insertEntry, getEntry?_of_perm hl h]
theorem removeKey_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} {k : α}
(hl : DistinctKeys l) (h : Perm l l') : Perm (removeKey k l) (removeKey k l') := by
apply getEntry?_ext hl.removeKey (hl.perm h.symm).removeKey
simp [getEntry?_removeKey hl, getEntry?_removeKey (hl.perm h.symm), getEntry?_of_perm hl h]
theorem eraseKey_of_perm [BEq α] [EquivBEq α] {l l' : List ((a : α) × β a)} {k : α}
(hl : DistinctKeys l) (h : Perm l l') : Perm (eraseKey k l) (eraseKey k l') := by
apply getEntry?_ext hl.eraseKey (hl.perm h.symm).eraseKey
simp [getEntry?_eraseKey hl, getEntry?_eraseKey (hl.perm h.symm), getEntry?_of_perm hl h]
@[simp]
theorem getEntry?_append [BEq α] {l l' : List ((a : α) × β a)} {a : α} :
getEntry? a (l ++ l') = (getEntry? a l).or (getEntry? a l') := by
induction l using assoc_induction
· simp
· next k' v' t ih => cases h : a == k' <;> simp_all [getEntry?_cons]
· next k' v' t ih => cases h : k' == a <;> simp_all [getEntry?_cons]
theorem getEntry?_append_of_containsKey_eq_false [BEq α] {l l' : List ((a : α) × β a)} {a : α}
(h : containsKey a l' = false) : getEntry? a (l ++ l') = getEntry? a l := by
@@ -1501,7 +1501,7 @@ theorem replaceEntry_append_of_containsKey_left [BEq α] {l l' : List ((a : α)
· simp at h
· next k' v' t ih =>
simp only [containsKey_cons, Bool.or_eq_true] at h
cases h' : k == k'
cases h' : k' == k
· simpa [replaceEntry_cons, h'] using ih (h.resolve_left (Bool.not_eq_true _ h'))
· simp [replaceEntry_cons, h']
@@ -1529,13 +1529,13 @@ theorem insertEntry_append_of_not_contains_right [BEq α] {l l' : List ((a : α)
· simp [insertEntry, containsKey_append, h, h']
· simp [insertEntry, containsKey_append, h, h', replaceEntry_append_of_containsKey_left h]
theorem removeKey_append_of_containsKey_right_eq_false [BEq α] {l l' : List ((a : α) × β a)} {k : α}
(h : containsKey k l' = false) : removeKey k (l ++ l') = removeKey k l ++ l' := by
theorem eraseKey_append_of_containsKey_right_eq_false [BEq α] {l l' : List ((a : α) × β a)} {k : α}
(h : containsKey k l' = false) : eraseKey k (l ++ l') = eraseKey k l ++ l' := by
induction l using assoc_induction
· simp [removeKey_of_containsKey_eq_false h]
· simp [eraseKey_of_containsKey_eq_false h]
· next k' v' t ih =>
rw [List.cons_append, removeKey_cons, removeKey_cons]
cases k == k'
rw [List.cons_append, eraseKey_cons, eraseKey_cons]
cases k' == k
· rw [cond_false, cond_false, ih, List.cons_append]
· rw [cond_true, cond_true]

View File

@@ -241,7 +241,7 @@ theorem updateAllBuckets [BEq α] [Hashable α] [LawfulHashable α] {m : Array (
simp only [Array.getElem_map, Array.size_map]
refine fun h p hp => ?_
rcases containsKey_eq_true_iff_exists_mem.1 (hf _ _ hp) with q, hq₁, hq₂
rw [hash_eq hq₂, (hm.hashes_to _ _).hash_self _ _ hq₁]
rw [ hash_eq hq₂, (hm.hashes_to _ _).hash_self _ _ hq₁]
end IsHashSelf
@@ -286,12 +286,12 @@ def insertIfNewₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a)
if m.containsₘ a then m else Raw₀.expandIfNecessary (m.consₘ a b)
/-- Internal implementation detail of the hash map -/
def removeₘaux [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
m.1.size - 1, updateBucket m.1.buckets m.2 a (fun l => l.remove a), by simpa using m.2
def eraseₘaux [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
m.1.size - 1, updateBucket m.1.buckets m.2 a (fun l => l.erase a), by simpa using m.2
/-- Internal implementation detail of the hash map -/
def removeₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
if m.containsₘ a then m.removeₘaux a else m
def eraseₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
if m.containsₘ a then m.eraseₘaux a else m
/-- Internal implementation detail of the hash map -/
def filterMapₘ (m : Raw₀ α β) (f : (a : α) β a Option (δ a)) : Raw₀ α δ :=
@@ -405,12 +405,12 @@ theorem getThenInsertIfNew?_eq_get?ₘ [BEq α] [Hashable α] [LawfulBEq α] (m
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
split <;> simp_all
theorem remove_eq_removeₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
m.remove a = m.removeₘ a := by
rw [remove, removeₘ, containsₘ, bucket]
theorem erase_eq_eraseₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
m.erase a = m.eraseₘ a := by
rw [erase, eraseₘ, containsₘ, bucket]
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
split
· simp only [removeₘaux, Subtype.mk.injEq, Raw.mk.injEq, true_and]
· simp only [eraseₘaux, Subtype.mk.injEq, Raw.mk.injEq, true_and]
rw [Array.set_set, updateBucket]
simp only [Array.uset, Array.ugetElem_eq_getElem]
· rfl

View File

@@ -135,13 +135,13 @@ theorem get!_val [BEq α] [Hashable α] [LawfulBEq α] {m : Raw₀ α β} {a :
m.val.get! a = m.get! a := by
simp [Raw.get!, m.2]
theorem remove_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
m.remove a = Raw₀.remove m, h.size_buckets_pos a := by
simp [Raw.remove, h.size_buckets_pos]
theorem erase_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF) {a : α} :
m.erase a = Raw₀.erase m, h.size_buckets_pos a := by
simp [Raw.erase, h.size_buckets_pos]
theorem remove_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
m.val.remove a = m.remove a := by
simp [Raw.remove, m.2]
theorem erase_val [BEq α] [Hashable α] {m : Raw₀ α β} {a : α} :
m.val.erase a = m.erase a := by
simp [Raw.erase, m.2]
theorem filterMap_eq [BEq α] [Hashable α] {m : Raw α β} (h : m.WF)
{f : (a : α) β a Option (δ a)} : m.filterMap f =

View File

@@ -60,7 +60,7 @@ variable (m : Raw₀ α β) (h : m.1.WF)
/-- Internal implementation detail of the hash map -/
scoped macro "wf_trivial" : tactic => `(tactic|
repeat (first
| apply Raw₀.wfImp_insert | apply Raw₀.wfImp_insertIfNew | apply Raw₀.wfImp_remove
| apply Raw₀.wfImp_insert | apply Raw₀.wfImp_insertIfNew | apply Raw₀.wfImp_erase
| apply Raw.WF.out | assumption | apply Raw₀.wfImp_empty | apply Raw.WFImp.distinct
| apply Raw.WF.empty₀))
@@ -76,7 +76,7 @@ private def queryNames : Array Name :=
``Const.get!_eq_getValue!, ``Const.getD_eq_getValueD]
private def modifyNames : Array Name :=
#[``toListModel_insert, ``toListModel_remove, ``toListModel_insertIfNew]
#[``toListModel_insert, ``toListModel_erase, ``toListModel_insertIfNew]
private def congrNames : MacroM (Array (TSyntax `term)) := do
return #[ `(Std.DHashMap.Internal.List.Perm.isEmpty_eq), `(containsKey_of_perm),
@@ -127,11 +127,11 @@ theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashab
simp_to_model using List.isEmpty_eq_false_iff_exists_containsKey
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insert k v).contains a = ((a == k) || m.contains a) := by
(m.insert k v).contains a = ((k == a) || m.contains a) := by
simp_to_model using List.containsKey_insertEntry
theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insert k v).contains a (a == k) = false m.contains a := by
(m.insert k v).contains a (k == a) = false m.contains a := by
simp_to_model using List.containsKey_of_containsKey_insertEntry
theorem contains_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
@@ -153,28 +153,28 @@ theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
simp_to_model using List.length_le_length_insertEntry
@[simp]
theorem remove_empty {k : α} {c : Nat} : (empty c : Raw₀ α β).remove k = empty c := by
simp [remove, empty]
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw₀ α β).erase k = empty c := by
simp [erase, empty]
theorem isEmpty_remove [EquivBEq α] [LawfulHashable α] {k : α} :
(m.remove k).1.isEmpty = (m.1.isEmpty || (m.1.size == 1 && m.contains k)) := by
simp_to_model using List.isEmpty_removeKey
theorem isEmpty_erase [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).1.isEmpty = (m.1.isEmpty || (m.1.size == 1 && m.contains k)) := by
simp_to_model using List.isEmpty_eraseKey
theorem contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.remove k).contains a = (!(a == k) && m.contains a) := by
simp_to_model using List.containsKey_removeKey
theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).contains a = (!(k == a) && m.contains a) := by
simp_to_model using List.containsKey_eraseKey
theorem contains_of_contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.remove k).contains a m.contains a := by
simp_to_model using List.containsKey_of_containsKey_removeKey
theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).contains a m.contains a := by
simp_to_model using List.containsKey_of_containsKey_eraseKey
theorem size_remove [EquivBEq α] [LawfulHashable α] {k : α} :
(m.remove k).1.size = bif m.contains k then m.1.size - 1 else m.1.size := by
simp_to_model using List.length_removeKey
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).1.size = bif m.contains k then m.1.size - 1 else m.1.size := by
simp_to_model using List.length_eraseKey
theorem size_remove_le [EquivBEq α] [LawfulHashable α] {k : α} :
(m.remove k).1.size m.1.size := by
simp_to_model using List.length_removeKey_le
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).1.size m.1.size := by
simp_to_model using List.length_eraseKey_le
@[simp]
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k := by
@@ -202,7 +202,7 @@ theorem get?_of_isEmpty [LawfulBEq α] {a : α} : m.1.isEmpty = true → m.get?
simp_to_model; empty
theorem get?_insert [LawfulBEq α] {a k : α} {v : β k} : (m.insert k v).get? a =
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else m.get? a := by
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else m.get? a := by
simp_to_model using List.getValueCast?_insertEntry
theorem get?_insert_self [LawfulBEq α] {k : α} {v : β k} : (m.insert k v).get? k = some v := by
@@ -214,12 +214,12 @@ theorem contains_eq_isSome_get? [LawfulBEq α] {a : α} : m.contains a = (m.get?
theorem get?_eq_none [LawfulBEq α] {a : α} : m.contains a = false m.get? a = none := by
simp_to_model using List.getValueCast?_eq_none
theorem get?_remove [LawfulBEq α] {k a : α} :
(m.remove k).get? a = bif a == k then none else m.get? a := by
simp_to_model using List.getValueCast?_removeKey
theorem get?_erase [LawfulBEq α] {k a : α} :
(m.erase k).get? a = bif k == a then none else m.get? a := by
simp_to_model using List.getValueCast?_eraseKey
theorem get?_remove_self [LawfulBEq α] {k : α} : (m.remove k).get? k = none := by
simp_to_model using List.getValueCast?_removeKey_self
theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none := by
simp_to_model using List.getValueCast?_eraseKey_self
namespace Const
@@ -234,7 +234,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
simp_to_model; empty
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
get? (m.insert k v) a = bif a == k then some v else get? m a := by
get? (m.insert k v) a = bif k == a then some v else get? m a := by
simp_to_model using List.getValue?_insertEntry
theorem get?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
@@ -249,13 +249,13 @@ theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} :
m.contains a = false get? m a = none := by
simp_to_model using List.getValue?_eq_none.2
theorem get?_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
Const.get? (m.remove k) a = bif a == k then none else get? m a := by
simp_to_model using List.getValue?_removeKey
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
Const.get? (m.erase k) a = bif k == a then none else get? m a := by
simp_to_model using List.getValue?_eraseKey
theorem get?_remove_self [EquivBEq α] [LawfulHashable α] {k : α} :
get? (m.remove k) k = none := by
simp_to_model using List.getValue?_removeKey_self
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} :
get? (m.erase k) k = none := by
simp_to_model using List.getValue?_eraseKey_self
theorem get?_eq_get? [LawfulBEq α] {a : α} : get? m a = m.get? a := by
simp_to_model using List.getValue?_eq_getValueCast?
@@ -268,8 +268,8 @@ end Const
theorem get_insert [LawfulBEq α] {k a : α} {v : β k} {h₁} :
(m.insert k v).get a h₁ =
if h₂ : a == k then
cast (congrArg β (eq_of_beq h₂).symm) v
if h₂ : k == a then
cast (congrArg β (eq_of_beq h₂)) v
else
m.get a (contains_of_contains_insert _ h h₁ (Bool.eq_false_iff.2 h₂)) := by
simp_to_model using List.getValueCast_insertEntry
@@ -279,9 +279,9 @@ theorem get_insert_self [LawfulBEq α] {k : α} {v : β k} :
simp_to_model using List.getValueCast_insertEntry_self
@[simp]
theorem get_remove [LawfulBEq α] {k a : α} {h'} :
(m.remove k).get a h' = m.get a (contains_of_contains_remove _ h h') := by
simp_to_model using List.getValueCast_removeKey
theorem get_erase [LawfulBEq α] {k a : α} {h'} :
(m.erase k).get a h' = m.get a (contains_of_contains_erase _ h h') := by
simp_to_model using List.getValueCast_eraseKey
theorem get?_eq_some_get [LawfulBEq α] {a : α} {h} : m.get? a = some (m.get a h) := by
simp_to_model using List.getValueCast?_eq_some_getValueCast
@@ -292,7 +292,7 @@ variable {β : Type v} (m : Raw₀ α (fun _ => β)) (h : m.1.WF)
theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
get (m.insert k v) a h₁ =
if h₂ : a == k then v
if h₂ : k == a then v
else get m a (contains_of_contains_insert _ h h₁ (Bool.eq_false_iff.2 h₂)) := by
simp_to_model using List.getValue_insertEntry
@@ -301,9 +301,9 @@ theorem get_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
simp_to_model using List.getValue_insertEntry_self
@[simp]
theorem get_remove [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
get (m.remove k) a h' = get m a (contains_of_contains_remove _ h h') := by
simp_to_model using List.getValue_removeKey
theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
get (m.erase k) a h' = get m a (contains_of_contains_erase _ h h') := by
simp_to_model using List.getValue_eraseKey
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} {h} :
get? m a = some (get m a h) := by
@@ -328,7 +328,7 @@ theorem get!_of_isEmpty [LawfulBEq α] {a : α} [Inhabited (β a)] :
theorem get!_insert [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
(m.insert k v).get! a =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.get! a := by
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.get! a := by
simp_to_model using List.getValueCast!_insertEntry
theorem get!_insert_self [LawfulBEq α] {a : α} [Inhabited (β a)] {b : β a} :
@@ -339,13 +339,13 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
m.contains a = false m.get! a = default := by
simp_to_model using List.getValueCast!_eq_default
theorem get!_remove [LawfulBEq α] {k a : α} [Inhabited (β a)] :
(m.remove k).get! a = bif a == k then default else m.get! a := by
simp_to_model using List.getValueCast!_removeKey
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
(m.erase k).get! a = bif k == a then default else m.get! a := by
simp_to_model using List.getValueCast!_eraseKey
theorem get!_remove_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
(m.remove k).get! k = default := by
simp_to_model using List.getValueCast!_removeKey_self
theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
(m.erase k).get! k = default := by
simp_to_model using List.getValueCast!_eraseKey_self
theorem get?_eq_some_get! [LawfulBEq α] {a : α} [Inhabited (β a)] :
m.contains a = true m.get? a = some (m.get! a) := by
@@ -372,7 +372,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
simp_to_model; empty
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
get! (m.insert k v) a = bif a == k then v else get! m a := by
get! (m.insert k v) a = bif k == a then v else get! m a := by
simp_to_model using List.getValue!_insertEntry
theorem get!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} {v : β} :
@@ -383,13 +383,13 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
m.contains a = false get! m a = default := by
simp_to_model using List.getValue!_eq_default
theorem get!_remove [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
get! (m.remove k) a = bif a == k then default else get! m a := by
simp_to_model using List.getValue!_removeKey
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
get! (m.erase k) a = bif k == a then default else get! m a := by
simp_to_model using List.getValue!_eraseKey
theorem get!_remove_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
get! (m.remove k) k = default := by
simp_to_model using List.getValue!_removeKey_self
theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
get! (m.erase k) k = default := by
simp_to_model using List.getValue!_eraseKey_self
theorem get?_eq_some_get! [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} :
m.contains a = true get? m a = some (get! m a) := by
@@ -423,7 +423,7 @@ theorem getD_of_isEmpty [LawfulBEq α] {a : α} {fallback : β a} :
theorem getD_insert [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
(m.insert k v).getD a fallback =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.getD a fallback := by
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.getD a fallback := by
simp_to_model using List.getValueCastD_insertEntry
theorem getD_insert_self [LawfulBEq α] {a : α} {fallback b : β a} :
@@ -434,13 +434,13 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
m.contains a = false m.getD a fallback = fallback := by
simp_to_model using List.getValueCastD_eq_fallback
theorem getD_remove [LawfulBEq α] {k a : α} {fallback : β a} :
(m.remove k).getD a fallback = bif a == k then fallback else m.getD a fallback := by
simp_to_model using List.getValueCastD_removeKey
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback := by
simp_to_model using List.getValueCastD_eraseKey
theorem getD_remove_self [LawfulBEq α] {k : α} {fallback : β k} :
(m.remove k).getD k fallback = fallback := by
simp_to_model using List.getValueCastD_removeKey_self
theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} :
(m.erase k).getD k fallback = fallback := by
simp_to_model using List.getValueCastD_eraseKey_self
theorem get?_eq_some_getD [LawfulBEq α] {a : α} {fallback : β a} :
m.contains a = true m.get? a = some (m.getD a fallback) := by
@@ -471,7 +471,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
simp_to_model; empty
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
getD (m.insert k v) a fallback = bif a == k then v else getD m a fallback := by
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback := by
simp_to_model using List.getValueD_insertEntry
theorem getD_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback v : β} :
@@ -482,13 +482,13 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
m.contains a = false getD m a fallback = fallback := by
simp_to_model using List.getValueD_eq_fallback
theorem getD_remove [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
getD (m.remove k) a fallback = bif a == k then fallback else getD m a fallback := by
simp_to_model using List.getValueD_removeKey
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback := by
simp_to_model using List.getValueD_eraseKey
theorem getD_remove_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
getD (m.remove k) k fallback = fallback := by
simp_to_model using List.getValueD_removeKey_self
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
getD (m.erase k) k fallback = fallback := by
simp_to_model using List.getValueD_eraseKey_self
theorem get?_eq_some_getD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} :
m.contains a = true get? m a = some (getD m a fallback) := by
@@ -521,7 +521,7 @@ theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
simp_to_model using List.isEmpty_insertEntryIfNew
theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a = (a == k || m.contains a) := by
(m.insertIfNew k v).contains a = (k == a || m.contains a) := by
simp_to_model using List.containsKey_insertEntryIfNew
theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
@@ -529,13 +529,13 @@ theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v
simp_to_model using List.containsKey_insertEntryIfNew_self
theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a (a == k) = false m.contains a := by
(m.insertIfNew k v).contains a (k == a) = false m.contains a := by
simp_to_model using List.containsKey_of_containsKey_insertEntryIfNew
/-- This is a restatement of `contains_insertIfNew` that is written to exactly match the proof
obligation in the statement of `get_insertIfNew`. -/
theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a ¬((a == k) m.contains k = false) m.contains a := by
(m.insertIfNew k v).contains a ¬((k == a) m.contains k = false) m.contains a := by
simp_to_model using List.containsKey_of_containsKey_insertEntryIfNew'
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
@@ -548,25 +548,25 @@ theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v :
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} :
(m.insertIfNew k v).get? a =
if h : a == k m.contains k = false then some (cast (congrArg β (eq_of_beq h.1).symm) v)
if h : k == a m.contains k = false then some (cast (congrArg β (eq_of_beq h.1)) v)
else m.get? a := by
simp_to_model using List.getValueCast?_insertEntryIfNew
theorem get_insertIfNew [LawfulBEq α] {k a : α} {v : β k} {h₁} :
(m.insertIfNew k v).get a h₁ =
if h₂ : a == k m.contains k = false then cast (congrArg β (eq_of_beq h₂.1).symm) v
if h₂ : k == a m.contains k = false then cast (congrArg β (eq_of_beq h₂.1)) v
else m.get a (contains_of_contains_insertIfNew' _ h h₁ h₂) := by
simp_to_model using List.getValueCast_insertEntryIfNew
theorem get!_insertIfNew [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
(m.insertIfNew k v).get! a =
if h : a == k m.contains k = false then cast (congrArg β (eq_of_beq h.1).symm) v
if h : k == a m.contains k = false then cast (congrArg β (eq_of_beq h.1)) v
else m.get! a := by
simp_to_model using List.getValueCast!_insertEntryIfNew
theorem getD_insertIfNew [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
(m.insertIfNew k v).getD a fallback =
if h : a == k m.contains k = false then cast (congrArg β (eq_of_beq h.1).symm) v
if h : k == a m.contains k = false then cast (congrArg β (eq_of_beq h.1)) v
else m.getD a fallback := by
simp_to_model using List.getValueCastD_insertEntryIfNew
@@ -575,22 +575,22 @@ namespace Const
variable {β : Type v} (m : Raw₀ α (fun _ => β)) (h : m.1.WF)
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
get? (m.insertIfNew k v) a = bif a == k && !m.contains k then some v else get? m a := by
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a := by
simp_to_model using List.getValue?_insertEntryIfNew
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
get (m.insertIfNew k v) a h₁ =
if h₂ : a == k m.contains k = false then v
if h₂ : k == a m.contains k = false then v
else get m a (contains_of_contains_insertIfNew' _ h h₁ h₂) := by
simp_to_model using List.getValue_insertEntryIfNew
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
get! (m.insertIfNew k v) a = bif a == k && !m.contains k then v else get! m a := by
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a := by
simp_to_model using List.getValue!_insertEntryIfNew
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
getD (m.insertIfNew k v) a fallback =
bif a == k && !m.contains k then v else getD m a fallback := by
bif k == a && !m.contains k then v else getD m a fallback := by
simp_to_model using List.getValueD_insertEntryIfNew
end Const

View File

@@ -453,63 +453,63 @@ theorem Const.wfImp_getThenInsertIfNew? {β : Type v} [BEq α] [Hashable α] [Eq
rw [getThenInsertIfNew?_eq_insertIfNewₘ]
exact wfImp_insertIfNewₘ h
/-! # `removeₘ` -/
/-! # `eraseₘ` -/
theorem toListModel_removeₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
theorem toListModel_eraseₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
(a : α) (h : Raw.WFImp m.1) :
Perm (toListModel (m.removeₘaux a).1.buckets) (removeKey a (toListModel m.1.buckets)) :=
toListModel_updateBucket h AssocList.toList_remove List.removeKey_of_perm
List.removeKey_append_of_containsKey_right_eq_false
Perm (toListModel (m.eraseₘaux a).1.buckets) (eraseKey a (toListModel m.1.buckets)) :=
toListModel_updateBucket h AssocList.toList_erase List.eraseKey_of_perm
List.eraseKey_append_of_containsKey_right_eq_false
theorem isHashSelf_removeₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
(a : α) (h : Raw.WFImp m.1) : IsHashSelf (m.removeₘaux a).1.buckets := by
theorem isHashSelf_eraseₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β)
(a : α) (h : Raw.WFImp m.1) : IsHashSelf (m.eraseₘaux a).1.buckets := by
apply h.buckets_hash_self.updateBucket (fun l p hp => ?_)
rw [AssocList.toList_remove] at hp
exact Or.inl (containsKey_of_mem ((sublist_removeKey.mem hp)))
rw [AssocList.toList_erase] at hp
exact Or.inl (containsKey_of_mem ((sublist_eraseKey.mem hp)))
theorem wfImp_removeₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β) (a : α)
(h : Raw.WFImp m.1) (h' : m.containsₘ a = true) : Raw.WFImp (m.removeₘaux a).1 where
buckets_hash_self := isHashSelf_removeₘaux m a h
theorem wfImp_eraseₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] (m : Raw₀ α β) (a : α)
(h : Raw.WFImp m.1) (h' : m.containsₘ a = true) : Raw.WFImp (m.eraseₘaux a).1 where
buckets_hash_self := isHashSelf_eraseₘaux m a h
size_eq := by
rw [(toListModel_removeₘaux m a h).length_eq, removeₘaux, length_removeKey,
rw [(toListModel_eraseₘaux m a h).length_eq, eraseₘaux, length_eraseKey,
containsₘ_eq_containsKey h, h', cond_true, h.size_eq]
distinct := h.distinct.removeKey.perm (toListModel_removeₘaux m a h)
distinct := h.distinct.eraseKey.perm (toListModel_eraseₘaux m a h)
theorem toListModel_perm_removeKey_of_containsₘ_eq_false [BEq α] [Hashable α] [EquivBEq α]
theorem toListModel_perm_eraseKey_of_containsₘ_eq_false [BEq α] [Hashable α] [EquivBEq α]
[LawfulHashable α] (m : Raw₀ α β) (a : α) (h : Raw.WFImp m.1) (h' : m.containsₘ a = false) :
Perm (toListModel m.1.buckets) (removeKey a (toListModel m.1.buckets)) := by
rw [removeKey_of_containsKey_eq_false]
Perm (toListModel m.1.buckets) (eraseKey a (toListModel m.1.buckets)) := by
rw [eraseKey_of_containsKey_eq_false]
· exact Perm.refl _
· rw [ containsₘ_eq_containsKey h, h']
theorem toListModel_removeₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
theorem toListModel_eraseₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
{a : α} (h : Raw.WFImp m.1) :
Perm (toListModel (m.removeₘ a).1.buckets) (removeKey a (toListModel m.1.buckets)) := by
rw [removeₘ]
Perm (toListModel (m.eraseₘ a).1.buckets) (eraseKey a (toListModel m.1.buckets)) := by
rw [eraseₘ]
split
· exact toListModel_removeₘaux m a h
· exact toListModel_eraseₘaux m a h
· next h' =>
exact toListModel_perm_removeKey_of_containsₘ_eq_false _ _ h (eq_false_of_ne_true h')
exact toListModel_perm_eraseKey_of_containsₘ_eq_false _ _ h (eq_false_of_ne_true h')
theorem wfImp_removeₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
(h : Raw.WFImp m.1) : Raw.WFImp (m.removeₘ a).1 := by
rw [removeₘ]
theorem wfImp_eraseₘ [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
(h : Raw.WFImp m.1) : Raw.WFImp (m.eraseₘ a).1 := by
rw [eraseₘ]
split
· next h' => exact wfImp_removeₘaux m a h h'
· next h' => exact wfImp_eraseₘaux m a h h'
· exact h
/-! # `remove` -/
/-! # `erase` -/
theorem toListModel_remove [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
theorem toListModel_erase [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
{a : α} (h : Raw.WFImp m.1) :
Perm (toListModel (m.remove a).1.buckets) (removeKey a (toListModel m.1.buckets)) := by
rw [remove_eq_removeₘ]
exact toListModel_removeₘ h
Perm (toListModel (m.erase a).1.buckets) (eraseKey a (toListModel m.1.buckets)) := by
rw [erase_eq_eraseₘ]
exact toListModel_eraseₘ h
theorem wfImp_remove [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
(h : Raw.WFImp m.1) : Raw.WFImp (m.remove a).1 := by
rw [remove_eq_removeₘ]
exact wfImp_removeₘ h
theorem wfImp_erase [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] {m : Raw₀ α β} {a : α}
(h : Raw.WFImp m.1) : Raw.WFImp (m.erase a).1 := by
rw [erase_eq_eraseₘ]
exact wfImp_eraseₘ h
/-! # `filterMapₘ` -/
@@ -626,7 +626,7 @@ theorem WF.out [BEq α] [Hashable α] [i₁ : EquivBEq α] [i₂ : LawfulHashabl
· next h => exact Raw₀.wfImp_insert (by apply h)
· next h => exact Raw₀.wfImp_containsThenInsert (by apply h)
· next h => exact Raw₀.wfImp_containsThenInsertIfNew (by apply h)
· next h => exact Raw₀.wfImp_remove (by apply h)
· next h => exact Raw₀.wfImp_erase (by apply h)
· next h => exact Raw₀.wfImp_insertIfNew (by apply h)
· next h => exact Raw₀.wfImp_getThenInsertIfNew? (by apply h)
· next h => exact Raw₀.wfImp_filter (by apply h)

View File

@@ -22,7 +22,7 @@ set_option autoImplicit false
universe u v
variable {α : Type u} {β : α Type v} [BEq α] [Hashable α]
variable {α : Type u} {β : α Type v} {_ : BEq α} {_ : Hashable α}
namespace Std.DHashMap
@@ -65,20 +65,20 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
@[simp]
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insert k v).contains a = (a == k || m.contains a) :=
(m.insert k v).contains a = (k == a || m.contains a) :=
Raw₀.contains_insert m.1, _ m.2
@[simp]
theorem mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insert k v a == k a m := by
a m.insert k v k == a a m := by
simp [mem_iff_contains, contains_insert]
theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insert k v).contains a (a == k) = false m.contains a :=
(m.insert k v).contains a (k == a) = false m.contains a :=
Raw₀.contains_of_contains_insert m.1, _ m.2
theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insert k v (a == k) = false a m := by
a m.insert k v (k == a) = false a m := by
simpa [mem_iff_contains, -contains_insert] using contains_of_contains_insert
@[simp]
@@ -110,41 +110,41 @@ theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
Raw₀.size_le_size_insert m.1, _ m.2
@[simp]
theorem remove_empty {k : α} {c : Nat} : (empty c : DHashMap α β).remove k = empty c :=
Subtype.eq (congrArg Subtype.val (Raw₀.remove_empty (k := k)) :) -- Lean code is happy
theorem erase_empty {k : α} {c : Nat} : (empty c : DHashMap α β).erase k = empty c :=
Subtype.eq (congrArg Subtype.val (Raw₀.erase_empty (k := k)) :) -- Lean code is happy
@[simp]
theorem remove_emptyc {k : α} : ( : DHashMap α β).remove k = :=
remove_empty
theorem erase_emptyc {k : α} : ( : DHashMap α β).erase k = :=
erase_empty
@[simp]
theorem isEmpty_remove [EquivBEq α] [LawfulHashable α] {k : α} :
(m.remove k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) :=
Raw₀.isEmpty_remove _ m.2
theorem isEmpty_erase [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) :=
Raw₀.isEmpty_erase _ m.2
@[simp]
theorem contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.remove k).contains a = (!(a == k) && m.contains a) :=
Raw₀.contains_remove m.1, _ m.2
theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).contains a = (!(k == a) && m.contains a) :=
Raw₀.contains_erase m.1, _ m.2
@[simp]
theorem mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
a m.remove k (a == k) = false a m := by
simp [mem_iff_contains, contains_remove]
theorem mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
a m.erase k (k == a) = false a m := by
simp [mem_iff_contains, contains_erase]
theorem contains_of_contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.remove k).contains a m.contains a :=
Raw₀.contains_of_contains_remove m.1, _ m.2
theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).contains a m.contains a :=
Raw₀.contains_of_contains_erase m.1, _ m.2
theorem mem_of_mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} : a m.remove k a m := by
theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a m.erase k a m := by
simp
theorem size_remove [EquivBEq α] [LawfulHashable α] {k : α} :
(m.remove k).size = bif m.contains k then m.size - 1 else m.size :=
Raw₀.size_remove _ m.2
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).size = bif m.contains k then m.size - 1 else m.size :=
Raw₀.size_erase _ m.2
theorem size_remove_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.remove k).size m.size :=
Raw₀.size_remove_le _ m.2
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size m.size :=
Raw₀.size_erase_le _ m.2
@[simp]
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k :=
@@ -176,7 +176,7 @@ theorem get?_of_isEmpty [LawfulBEq α] {a : α} : m.isEmpty = true → m.get? a
Raw₀.get?_of_isEmpty m.1, _ m.2
theorem get?_insert [LawfulBEq α] {a k : α} {v : β k} : (m.insert k v).get? a =
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else m.get? a :=
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else m.get? a :=
Raw₀.get?_insert m.1, _ m.2
@[simp]
@@ -193,13 +193,13 @@ theorem get?_eq_none_of_contains_eq_false [LawfulBEq α] {a : α} :
theorem get?_eq_none [LawfulBEq α] {a : α} : ¬a m m.get? a = none := by
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false
theorem get?_remove [LawfulBEq α] {k a : α} :
(m.remove k).get? a = bif a == k then none else m.get? a :=
Raw₀.get?_remove m.1, _ m.2
theorem get?_erase [LawfulBEq α] {k a : α} :
(m.erase k).get? a = bif k == a then none else m.get? a :=
Raw₀.get?_erase m.1, _ m.2
@[simp]
theorem get?_remove_self [LawfulBEq α] {k : α} : (m.remove k).get? k = none :=
Raw₀.get?_remove_self m.1, _ m.2
theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none :=
Raw₀.get?_erase_self m.1, _ m.2
namespace Const
@@ -218,7 +218,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
Raw₀.Const.get?_of_isEmpty m.1, _ m.2
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
get? (m.insert k v) a = bif a == k then some v else get? m a :=
get? (m.insert k v) a = bif k == a then some v else get? m a :=
Raw₀.Const.get?_insert m.1, _ m.2
@[simp]
@@ -237,13 +237,13 @@ theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a :
theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α } : ¬a m get? m a = none := by
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false
theorem get?_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
Const.get? (m.remove k) a = bif a == k then none else get? m a :=
Raw₀.Const.get?_remove m.1, _ m.2
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
Const.get? (m.erase k) a = bif k == a then none else get? m a :=
Raw₀.Const.get?_erase m.1, _ m.2
@[simp]
theorem get?_remove_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.remove k) k = none :=
Raw₀.Const.get?_remove_self m.1, _ m.2
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.erase k) k = none :=
Raw₀.Const.get?_erase_self m.1, _ m.2
theorem get?_eq_get? [LawfulBEq α] {a : α} : get? m a = m.get? a :=
Raw₀.Const.get?_eq_get? m.1, _ m.2
@@ -255,8 +255,8 @@ end Const
theorem get_insert [LawfulBEq α] {k a : α} {v : β k} {h₁} :
(m.insert k v).get a h₁ =
if h₂ : a == k then
cast (congrArg β (eq_of_beq h₂).symm) v
if h₂ : k == a then
cast (congrArg β (eq_of_beq h₂)) v
else
m.get a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
Raw₀.get_insert m.1, _ m.2
@@ -267,9 +267,9 @@ theorem get_insert_self [LawfulBEq α] {k : α} {v : β k} :
Raw₀.get_insert_self m.1, _ m.2
@[simp]
theorem get_remove [LawfulBEq α] {k a : α} {h'} :
(m.remove k).get a h' = m.get a (mem_of_mem_remove h') :=
Raw₀.get_remove m.1, _ m.2
theorem get_erase [LawfulBEq α] {k a : α} {h'} :
(m.erase k).get a h' = m.get a (mem_of_mem_erase h') :=
Raw₀.get_erase m.1, _ m.2
theorem get?_eq_some_get [LawfulBEq α] {a : α} {h} : m.get? a = some (m.get a h) :=
Raw₀.get?_eq_some_get m.1, _ m.2
@@ -280,7 +280,7 @@ variable {β : Type v} {m : DHashMap α (fun _ => β)}
theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
get (m.insert k v) a h₁ =
if h₂ : a == k then v else get m a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
if h₂ : k == a then v else get m a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) :=
Raw₀.Const.get_insert m.1, _ m.2
@[simp]
@@ -289,9 +289,9 @@ theorem get_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
Raw₀.Const.get_insert_self m.1, _ m.2
@[simp]
theorem get_remove [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
get (m.remove k) a h' = get m a (mem_of_mem_remove h') :=
Raw₀.Const.get_remove m.1, _ m.2
theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
get (m.erase k) a h' = get m a (mem_of_mem_erase h') :=
Raw₀.Const.get_erase m.1, _ m.2
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} {h} :
get? m a = some (get m a h) :=
@@ -322,7 +322,7 @@ theorem get!_of_isEmpty [LawfulBEq α] {a : α} [Inhabited (β a)] :
theorem get!_insert [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
(m.insert k v).get! a =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.get! a :=
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.get! a :=
Raw₀.get!_insert m.1, _ m.2
@[simp]
@@ -338,14 +338,14 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
¬a m m.get! a = default := by
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false
theorem get!_remove [LawfulBEq α] {k a : α} [Inhabited (β a)] :
(m.remove k).get! a = bif a == k then default else m.get! a :=
Raw₀.get!_remove m.1, _ m.2
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
(m.erase k).get! a = bif k == a then default else m.get! a :=
Raw₀.get!_erase m.1, _ m.2
@[simp]
theorem get!_remove_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
(m.remove k).get! k = default :=
Raw₀.get!_remove_self m.1, _ m.2
theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
(m.erase k).get! k = default :=
Raw₀.get!_erase_self m.1, _ m.2
theorem get?_eq_some_get!_of_contains [LawfulBEq α] {a : α} [Inhabited (β a)] :
m.contains a = true m.get? a = some (m.get! a) :=
@@ -381,7 +381,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
Raw₀.Const.get!_of_isEmpty m.1, _ m.2
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
get! (m.insert k v) a = bif a == k then v else get! m a :=
get! (m.insert k v) a = bif k == a then v else get! m a :=
Raw₀.Const.get!_insert m.1, _ m.2
@[simp]
@@ -397,14 +397,14 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
¬a m get! m a = default := by
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false
theorem get!_remove [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
get! (m.remove k) a = bif a == k then default else get! m a :=
Raw₀.Const.get!_remove m.1, _ m.2
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
get! (m.erase k) a = bif k == a then default else get! m a :=
Raw₀.Const.get!_erase m.1, _ m.2
@[simp]
theorem get!_remove_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
get! (m.remove k) k = default :=
Raw₀.Const.get!_remove_self m.1, _ m.2
theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
get! (m.erase k) k = default :=
Raw₀.Const.get!_erase_self m.1, _ m.2
theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} :
m.contains a = true get? m a = some (get! m a) :=
@@ -448,7 +448,7 @@ theorem getD_of_isEmpty [LawfulBEq α] {a : α} {fallback : β a} :
theorem getD_insert [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
(m.insert k v).getD a fallback =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.getD a fallback :=
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.getD a fallback :=
Raw₀.getD_insert m.1, _ m.2
@[simp]
@@ -464,14 +464,14 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
¬a m m.getD a fallback = fallback := by
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false
theorem getD_remove [LawfulBEq α] {k a : α} {fallback : β a} :
(m.remove k).getD a fallback = bif a == k then fallback else m.getD a fallback :=
Raw₀.getD_remove m.1, _ m.2
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback :=
Raw₀.getD_erase m.1, _ m.2
@[simp]
theorem getD_remove_self [LawfulBEq α] {k : α} {fallback : β k} :
(m.remove k).getD k fallback = fallback :=
Raw₀.getD_remove_self m.1, _ m.2
theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} :
(m.erase k).getD k fallback = fallback :=
Raw₀.getD_erase_self m.1, _ m.2
theorem get?_eq_some_getD_of_contains [LawfulBEq α] {a : α} {fallback : β a} :
m.contains a = true m.get? a = some (m.getD a fallback) :=
@@ -512,7 +512,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
Raw₀.Const.getD_of_isEmpty m.1, _ m.2
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
getD (m.insert k v) a fallback = bif a == k then v else getD m a fallback :=
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback :=
Raw₀.Const.getD_insert m.1, _ m.2
@[simp]
@@ -528,14 +528,14 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
¬a m getD m a fallback = fallback := by
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false
theorem getD_remove [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
getD (m.remove k) a fallback = bif a == k then fallback else getD m a fallback :=
Raw₀.Const.getD_remove m.1, _ m.2
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback :=
Raw₀.Const.getD_erase m.1, _ m.2
@[simp]
theorem getD_remove_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
getD (m.remove k) k fallback = fallback :=
Raw₀.Const.getD_remove_self m.1, _ m.2
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
getD (m.erase k) k fallback = fallback :=
Raw₀.Const.getD_erase_self m.1, _ m.2
theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} :
m.contains a = true get? m a = some (getD m a fallback) :=
@@ -574,12 +574,12 @@ theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
@[simp]
theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a = (a == k || m.contains a) :=
(m.insertIfNew k v).contains a = (k == a || m.contains a) :=
Raw₀.contains_insertIfNew m.1, _ m.2
@[simp]
theorem mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insertIfNew k v a == k a m := by
a m.insertIfNew k v k == a a m := by
simp [mem_iff_contains, contains_insertIfNew]
theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
@@ -591,23 +591,23 @@ theorem mem_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β
simpa [mem_iff_contains, -contains_insertIfNew] using contains_insertIfNew_self
theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a (a == k) = false m.contains a :=
(m.insertIfNew k v).contains a (k == a) = false m.contains a :=
Raw₀.contains_of_contains_insertIfNew m.1, _ m.2
theorem mem_of_mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insertIfNew k v (a == k) = false a m := by
a m.insertIfNew k v (k == a) = false a m := by
simpa [mem_iff_contains, -contains_insertIfNew] using contains_of_contains_insertIfNew
/-- This is a restatement of `contains_insertIfNew` that is written to exactly match the proof
obligation in the statement of `get_insertIfNew`. -/
theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a ¬((a == k) m.contains k = false) m.contains a :=
(m.insertIfNew k v).contains a ¬((k == a) m.contains k = false) m.contains a :=
Raw₀.contains_of_contains_insertIfNew' m.1, _ m.2
/-- This is a restatement of `mem_insertIfNew` that is written to exactly match the proof obligation
in the statement of `get_insertIfNew`. -/
theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insertIfNew k v ¬((a == k) ¬k m) a m := by
a m.insertIfNew k v ¬((k == a) ¬k m) a m := by
simpa [mem_iff_contains, -contains_insertIfNew] using contains_of_contains_insertIfNew'
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
@@ -619,25 +619,25 @@ theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v :
Raw₀.size_le_size_insertIfNew m.1, _ m.2
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} : (m.insertIfNew k v).get? a =
if h : a == k ¬k m then some (cast (congrArg β (eq_of_beq h.1).symm) v) else m.get? a := by
if h : k == a ¬k m then some (cast (congrArg β (eq_of_beq h.1)) v) else m.get? a := by
simp only [mem_iff_contains, Bool.not_eq_true]
exact Raw₀.get?_insertIfNew m.1, _ m.2
theorem get_insertIfNew [LawfulBEq α] {k a : α} {v : β k} {h₁} : (m.insertIfNew k v).get a h₁ =
if h₂ : a == k ¬k m then cast (congrArg β (eq_of_beq h₂.1).symm) v else m.get a
if h₂ : k == a ¬k m then cast (congrArg β (eq_of_beq h₂.1)) v else m.get a
(mem_of_mem_insertIfNew' h₁ h₂) := by
simp only [mem_iff_contains, Bool.not_eq_true]
exact Raw₀.get_insertIfNew m.1, _ m.2
theorem get!_insertIfNew [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
(m.insertIfNew k v).get! a =
if h : a == k ¬k m then cast (congrArg β (eq_of_beq h.1).symm) v else m.get! a := by
if h : k == a ¬k m then cast (congrArg β (eq_of_beq h.1)) v else m.get! a := by
simp only [mem_iff_contains, Bool.not_eq_true]
exact Raw₀.get!_insertIfNew m.1, _ m.2
theorem getD_insertIfNew [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
(m.insertIfNew k v).getD a fallback =
if h : a == k ¬k m then cast (congrArg β (eq_of_beq h.1).symm) v
if h : k == a ¬k m then cast (congrArg β (eq_of_beq h.1)) v
else m.getD a fallback := by
simp only [mem_iff_contains, Bool.not_eq_true]
exact Raw₀.getD_insertIfNew m.1, _ m.2
@@ -647,22 +647,22 @@ namespace Const
variable {β : Type v} {m : DHashMap α (fun _ => β)}
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
get? (m.insertIfNew k v) a = bif a == k && !m.contains k then some v else get? m a :=
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a :=
Raw₀.Const.get?_insertIfNew m.1, _ m.2
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
get (m.insertIfNew k v) a h₁ =
if h₂ : a == k ¬k m then v else get m a (mem_of_mem_insertIfNew' h₁ h₂) := by
if h₂ : k == a ¬k m then v else get m a (mem_of_mem_insertIfNew' h₁ h₂) := by
simp only [mem_iff_contains, Bool.not_eq_true]
exact Raw₀.Const.get_insertIfNew m.1, _ m.2
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
get! (m.insertIfNew k v) a = bif a == k && !m.contains k then v else get! m a :=
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a :=
Raw₀.Const.get!_insertIfNew m.1, _ m.2
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
getD (m.insertIfNew k v) a fallback =
bif a == k && !m.contains k then v else getD m a fallback :=
bif k == a && !m.contains k then v else getD m a fallback :=
Raw₀.Const.getD_insertIfNew m.1, _ m.2
end Const

View File

@@ -180,9 +180,9 @@ Uses the `LawfulBEq` instance to cast the retrieved value to the correct type.
else default -- will never happen for well-formed inputs
/-- Removes the mapping for the given key if it exists. -/
@[inline] def remove [BEq α] [Hashable α] (m : Raw α β) (a : α) : Raw α β :=
@[inline] def erase [BEq α] [Hashable α] (m : Raw α β) (a : α) : Raw α β :=
if h : 0 < m.buckets.size then
Raw₀.remove m, h a
Raw₀.erase m, h a
else m -- will never happen for well-formed inputs
section
@@ -416,7 +416,7 @@ inductive WF : {α : Type u} → {β : α → Type v} → [BEq α] → [Hashable
| containsThenInsertIfNew₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a b} :
WF m WF (Raw₀.containsThenInsertIfNew m, h a b).2.1
/-- Internal implementation detail of the hash map -/
| remove₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a} : WF m WF (Raw₀.remove m, h a).1
| erase₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a} : WF m WF (Raw₀.erase m, h a).1
/-- Internal implementation detail of the hash map -/
| insertIfNew₀ {α β} [BEq α] [Hashable α] {m : Raw α β} {h a b} :
WF m WF (Raw₀.insertIfNew m, h a b).1
@@ -436,7 +436,7 @@ theorem WF.size_buckets_pos [BEq α] [Hashable α] (m : Raw α β) : WF m → 0
| insert₀ _ => (Raw₀.insert _, _ _ _).2
| containsThenInsert₀ _ => (Raw₀.containsThenInsert _, _ _ _).2.2
| containsThenInsertIfNew₀ _ => (Raw₀.containsThenInsertIfNew _, _ _ _).2.2
| remove₀ _ => (Raw₀.remove _, _ _).2
| erase₀ _ => (Raw₀.erase _, _ _).2
| insertIfNew₀ _ => (Raw₀.insertIfNew _, _ _ _).2
| getThenInsertIfNew?₀ _ => (Raw₀.getThenInsertIfNew? _, _ _ _).2.2
| filter₀ _ => (Raw₀.filter _ _, _).2
@@ -460,8 +460,8 @@ theorem WF.containsThenInsertIfNew [BEq α] [Hashable α] {m : Raw α β} {a :
(m.containsThenInsertIfNew a b).2.WF := by
simpa [Raw.containsThenInsertIfNew, h.size_buckets_pos] using .containsThenInsertIfNew₀ h
theorem WF.remove [BEq α] [Hashable α] {m : Raw α β} {a : α} (h : m.WF) : (m.remove a).WF := by
simpa [Raw.remove, h.size_buckets_pos] using .remove₀ h
theorem WF.erase [BEq α] [Hashable α] {m : Raw α β} {a : α} (h : m.WF) : (m.erase a).WF := by
simpa [Raw.erase, h.size_buckets_pos] using .erase₀ h
theorem WF.insertIfNew [BEq α] [Hashable α] {m : Raw α β} {a : α} {b : β a} (h : m.WF) :
(m.insertIfNew a b).WF := by

View File

@@ -26,16 +26,19 @@ inductive types. The well-formedness invariant is called `Raw.WF`. When in doubt
over `DHashMap.Raw`. Lemmas about the operations on `Std.Data.DHashMap.Raw` are available in the
module `Std.Data.DHashMap.RawLemmas`.
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq
instance is lawful, i.e., if `a == b` implies `a = b`.
The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to
avoid expensive copies.
This is a simple separate-chaining hash table. The data of the hash map consists of a cached size
and an array of buckets, where each bucket is a linked list of key-value pais. The number of buckets
is always a power of two. The hash map doubles its size upon inserting an element such that the
number of elements is more than 75% of the number of buckets.
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq
instance is lawful, i.e., if `a == b` implies `a = b`.
-/
structure Raw (α : Type u) (β : α Type v) where
/-- The number of mappings present in the hash map -/

View File

@@ -39,7 +39,7 @@ private def baseNames : Array Name :=
``getThenInsertIfNew?_snd_eq, ``getThenInsertIfNew?_snd_val,
``map_eq, ``map_val,
``filter_eq, ``filter_val,
``remove_eq, ``remove_val,
``erase_eq, ``erase_val,
``filterMap_eq, ``filterMap_val,
``Const.getThenInsertIfNew?_snd_eq, ``Const.getThenInsertIfNew?_snd_val,
``containsThenInsert_fst_eq, ``containsThenInsert_fst_val,
@@ -113,20 +113,20 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
@[simp]
theorem contains_insert [EquivBEq α] [LawfulHashable α] {a k : α} {v : β k} :
(m.insert k v).contains a = (a == k || m.contains a) := by
(m.insert k v).contains a = (k == a || m.contains a) := by
simp_to_raw using Raw₀.contains_insert
@[simp]
theorem mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insert k v a == k a m := by
a m.insert k v k == a a m := by
simp [mem_iff_contains, contains_insert h]
theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {a k : α} {v : β k} :
(m.insert k v).contains a (a == k) = false m.contains a := by
(m.insert k v).contains a (k == a) = false m.contains a := by
simp_to_raw using Raw₀.contains_of_contains_insert
theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insert k v (a == k) = false a m := by
a m.insert k v (k == a) = false a m := by
simpa [mem_iff_contains] using contains_of_contains_insert h
@[simp]
@@ -158,42 +158,42 @@ theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
simp_to_raw using Raw₀.size_le_size_insert m, _ h
@[simp]
theorem remove_empty {k : α} {c : Nat} : (empty c : Raw α β).remove k = empty c := by
rw [remove_eq (by wf_trivial)]
exact congrArg Subtype.val Raw₀.remove_empty
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw α β).erase k = empty c := by
rw [erase_eq (by wf_trivial)]
exact congrArg Subtype.val Raw₀.erase_empty
@[simp]
theorem remove_emptyc {k : α} : ( : Raw α β).remove k = :=
remove_empty
theorem erase_emptyc {k : α} : ( : Raw α β).erase k = :=
erase_empty
@[simp]
theorem isEmpty_remove [EquivBEq α] [LawfulHashable α] {k : α} :
(m.remove k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) := by
simp_to_raw using Raw₀.isEmpty_remove
theorem isEmpty_erase [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).isEmpty = (m.isEmpty || (m.size == 1 && m.contains k)) := by
simp_to_raw using Raw₀.isEmpty_erase
@[simp]
theorem contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.remove k).contains a = (!(a == k) && m.contains a) := by
simp_to_raw using Raw₀.contains_remove
theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).contains a = (!(k == a) && m.contains a) := by
simp_to_raw using Raw₀.contains_erase
@[simp]
theorem mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
a m.remove k (a == k) = false a m := by
simp [mem_iff_contains, contains_remove h]
theorem mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
a m.erase k (k == a) = false a m := by
simp [mem_iff_contains, contains_erase h]
theorem contains_of_contains_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.remove k).contains a m.contains a := by
simp_to_raw using Raw₀.contains_of_contains_remove
theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
(m.erase k).contains a m.contains a := by
simp_to_raw using Raw₀.contains_of_contains_erase
theorem mem_of_mem_remove [EquivBEq α] [LawfulHashable α] {k a : α} : a m.remove k a m := by
simpa [mem_iff_contains] using contains_of_contains_remove h
theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a m.erase k a m := by
simpa [mem_iff_contains] using contains_of_contains_erase h
theorem size_remove [EquivBEq α] [LawfulHashable α] {k : α} :
(m.remove k).size = bif m.contains k then m.size - 1 else m.size := by
simp_to_raw using Raw₀.size_remove
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
(m.erase k).size = bif m.contains k then m.size - 1 else m.size := by
simp_to_raw using Raw₀.size_erase
theorem size_remove_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.remove k).size m.size := by
simp_to_raw using Raw₀.size_remove_le
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size m.size := by
simp_to_raw using Raw₀.size_erase_le
@[simp]
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k := by
@@ -225,7 +225,7 @@ theorem get?_of_isEmpty [LawfulBEq α] {a : α} : m.isEmpty = true → m.get? a
simp_to_raw using Raw₀.get?_of_isEmpty m, _
theorem get?_insert [LawfulBEq α] {a k : α} {v : β k} : (m.insert k v).get? a =
if h : a == k then some (cast (congrArg β (eq_of_beq h).symm) v) else m.get? a := by
if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else m.get? a := by
simp_to_raw using Raw₀.get?_insert
@[simp]
@@ -242,13 +242,13 @@ theorem get?_eq_none_of_contains_eq_false [LawfulBEq α] {a : α} :
theorem get?_eq_none [LawfulBEq α] {a : α} : ¬a m m.get? a = none := by
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false h
theorem get?_remove [LawfulBEq α] {k a : α} :
(m.remove k).get? a = bif a == k then none else m.get? a := by
simp_to_raw using Raw₀.get?_remove
theorem get?_erase [LawfulBEq α] {k a : α} :
(m.erase k).get? a = bif k == a then none else m.get? a := by
simp_to_raw using Raw₀.get?_erase
@[simp]
theorem get?_remove_self [LawfulBEq α] {k : α} : (m.remove k).get? k = none := by
simp_to_raw using Raw₀.get?_remove_self
theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none := by
simp_to_raw using Raw₀.get?_erase_self
namespace Const
@@ -267,7 +267,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
simp_to_raw using Raw₀.Const.get?_of_isEmpty m, _
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
get? (m.insert k v) a = bif a == k then some v else get? m a := by
get? (m.insert k v) a = bif k == a then some v else get? m a := by
simp_to_raw using Raw₀.Const.get?_insert
@[simp]
@@ -286,13 +286,13 @@ theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a :
theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a m get? m a = none := by
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false h
theorem get?_remove [EquivBEq α] [LawfulHashable α] {k a : α} :
Const.get? (m.remove k) a = bif a == k then none else get? m a := by
simp_to_raw using Raw₀.Const.get?_remove
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
Const.get? (m.erase k) a = bif k == a then none else get? m a := by
simp_to_raw using Raw₀.Const.get?_erase
@[simp]
theorem get?_remove_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.remove k) k = none := by
simp_to_raw using Raw₀.Const.get?_remove_self
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.erase k) k = none := by
simp_to_raw using Raw₀.Const.get?_erase_self
theorem get?_eq_get? [LawfulBEq α] {a : α} : get? m a = m.get? a := by
simp_to_raw using Raw₀.Const.get?_eq_get?
@@ -305,8 +305,8 @@ end Const
theorem get_insert [LawfulBEq α] {k a : α} {v : β k} {h₁} :
(m.insert k v).get a h₁ =
if h₂ : a == k then
cast (congrArg β (eq_of_beq h₂).symm) v
if h₂ : k == a then
cast (congrArg β (eq_of_beq h₂)) v
else
m.get a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) := by
simp_to_raw using Raw₀.get_insert m, _
@@ -317,9 +317,9 @@ theorem get_insert_self [LawfulBEq α] {k : α} {v : β k} :
simp_to_raw using Raw₀.get_insert_self m, _
@[simp]
theorem get_remove [LawfulBEq α] {k a : α} {h'} :
(m.remove a).get k h' = m.get k (mem_of_mem_remove h h') := by
simp_to_raw using Raw₀.get_remove m, _
theorem get_erase [LawfulBEq α] {k a : α} {h'} :
(m.erase a).get k h' = m.get k (mem_of_mem_erase h h') := by
simp_to_raw using Raw₀.get_erase m, _
theorem get?_eq_some_get [LawfulBEq α] {a : α} {h} : m.get? a = some (m.get a h) := by
simp_to_raw using Raw₀.get?_eq_some_get
@@ -330,7 +330,7 @@ variable {β : Type v} {m : DHashMap.Raw α (fun _ => β)} (h : m.WF)
theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
get (m.insert k v) a h₁ =
if h₂ : a == k then v else get m a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) := by
if h₂ : k == a then v else get m a (mem_of_mem_insert h h₁ (Bool.eq_false_iff.2 h₂)) := by
simp_to_raw using Raw₀.Const.get_insert m, _
@[simp]
@@ -339,9 +339,9 @@ theorem get_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
simp_to_raw using Raw₀.Const.get_insert_self m, _
@[simp]
theorem get_remove [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
get (m.remove k) a h' = get m a (mem_of_mem_remove h h') := by
simp_to_raw using Raw₀.Const.get_remove m, _
theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} :
get (m.erase k) a h' = get m a (mem_of_mem_erase h h') := by
simp_to_raw using Raw₀.Const.get_erase m, _
theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} {h : a m} :
get? m a = some (get m a h) := by
@@ -371,7 +371,7 @@ theorem get!_of_isEmpty [LawfulBEq α] {a : α} [Inhabited (β a)] :
simp_to_raw using Raw₀.get!_of_isEmpty m, _
theorem get!_insert [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} : (m.insert k v).get! a =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.get! a := by
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.get! a := by
simp_to_raw using Raw₀.get!_insert
@[simp]
@@ -387,14 +387,14 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
¬a m m.get! a = default := by
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false h
theorem get!_remove [LawfulBEq α] {k a : α} [Inhabited (β a)] :
(m.remove k).get! a = bif a == k then default else m.get! a := by
simp_to_raw using Raw₀.get!_remove
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
(m.erase k).get! a = bif k == a then default else m.get! a := by
simp_to_raw using Raw₀.get!_erase
@[simp]
theorem get!_remove_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
(m.remove k).get! k = default := by
simp_to_raw using Raw₀.get!_remove_self
theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
(m.erase k).get! k = default := by
simp_to_raw using Raw₀.get!_erase_self
theorem get?_eq_some_get!_of_contains [LawfulBEq α] {a : α} [Inhabited (β a)] :
m.contains a = true m.get? a = some (m.get! a) := by
@@ -429,7 +429,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
simp_to_raw using Raw₀.Const.get!_of_isEmpty m, _
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
get! (m.insert k v) a = bif a == k then v else get! m a := by
get! (m.insert k v) a = bif k == a then v else get! m a := by
simp_to_raw using Raw₀.Const.get!_insert
@[simp]
@@ -445,14 +445,14 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
¬a m get! m a = default := by
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false h
theorem get!_remove [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
get! (m.remove k) a = bif a == k then default else get! m a := by
simp_to_raw using Raw₀.Const.get!_remove
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
get! (m.erase k) a = bif k == a then default else get! m a := by
simp_to_raw using Raw₀.Const.get!_erase
@[simp]
theorem get!_remove_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
get! (m.remove k) k = default := by
simp_to_raw using Raw₀.Const.get!_remove_self
theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
get! (m.erase k) k = default := by
simp_to_raw using Raw₀.Const.get!_erase_self
theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} :
m.contains a = true get? m a = some (get! m a) := by
@@ -496,7 +496,7 @@ theorem getD_of_isEmpty [LawfulBEq α] {a : α} {fallback : β a} :
theorem getD_insert [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
(m.insert k v).getD a fallback =
if h : a == k then cast (congrArg β (eq_of_beq h).symm) v else m.getD a fallback := by
if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.getD a fallback := by
simp_to_raw using Raw₀.getD_insert
@[simp]
@@ -512,14 +512,14 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
¬a m m.getD a fallback = fallback := by
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false h
theorem getD_remove [LawfulBEq α] {k a : α} {fallback : β a} :
(m.remove k).getD a fallback = bif a == k then fallback else m.getD a fallback := by
simp_to_raw using Raw₀.getD_remove
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback := by
simp_to_raw using Raw₀.getD_erase
@[simp]
theorem getD_remove_self [LawfulBEq α] {k : α} {fallback : β k} :
(m.remove k).getD k fallback = fallback := by
simp_to_raw using Raw₀.getD_remove_self
theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} :
(m.erase k).getD k fallback = fallback := by
simp_to_raw using Raw₀.getD_erase_self
theorem get?_eq_some_getD_of_contains [LawfulBEq α] {a : α} {fallback : β a} :
m.contains a = true m.get? a = some (m.getD a fallback) := by
@@ -559,7 +559,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
simp_to_raw using Raw₀.Const.getD_of_isEmpty m, _
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
getD (m.insert k v) a fallback = bif a == k then v else getD m a fallback := by
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback := by
simp_to_raw using Raw₀.Const.getD_insert
@[simp]
@@ -575,14 +575,14 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
¬a m getD m a fallback = fallback := by
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false h
theorem getD_remove [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
getD (m.remove k) a fallback = bif a == k then fallback else getD m a fallback := by
simp_to_raw using Raw₀.Const.getD_remove
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback := by
simp_to_raw using Raw₀.Const.getD_erase
@[simp]
theorem getD_remove_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
getD (m.remove k) k fallback = fallback := by
simp_to_raw using Raw₀.Const.getD_remove_self
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
getD (m.erase k) k fallback = fallback := by
simp_to_raw using Raw₀.Const.getD_erase_self
theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} :
m.contains a = true get? m a = some (getD m a fallback) := by
@@ -621,12 +621,12 @@ theorem isEmpty_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k
@[simp]
theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a = (a == k || m.contains a) := by
(m.insertIfNew k v).contains a = (k == a || m.contains a) := by
simp_to_raw using Raw₀.contains_insertIfNew
@[simp]
theorem mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insertIfNew k v a == k a m := by
a m.insertIfNew k v k == a a m := by
simp [mem_iff_contains, contains_insertIfNew h]
theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
@@ -638,23 +638,23 @@ theorem mem_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β
simpa [mem_iff_contains, -contains_insertIfNew] using contains_insertIfNew_self h
theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a (a == k) = false m.contains a := by
(m.insertIfNew k v).contains a (k == a) = false m.contains a := by
simp_to_raw using Raw₀.contains_of_contains_insertIfNew
theorem mem_of_mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insertIfNew k v (a == k) = false a m := by
a m.insertIfNew k v (k == a) = false a m := by
simpa [mem_iff_contains, -contains_insertIfNew] using contains_of_contains_insertIfNew h
/-- This is a restatement of `contains_insertIfNew` that is written to exactly match the proof
obligation in the statement of `get_insertIfNew`. -/
theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
(m.insertIfNew k v).contains a ¬((a == k) m.contains k = false) m.contains a := by
(m.insertIfNew k v).contains a ¬((k == a) m.contains k = false) m.contains a := by
simp_to_raw using Raw₀.contains_of_contains_insertIfNew'
/-- This is a restatement of `mem_insertIfNew` that is written to exactly match the proof obligation
in the statement of `get_insertIfNew`. -/
theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
a m.insertIfNew k v ¬((a == k) ¬k m) a m := by
a m.insertIfNew k v ¬((k == a) ¬k m) a m := by
simpa [mem_iff_contains] using contains_of_contains_insertIfNew' h
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
@@ -667,27 +667,27 @@ theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v :
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} :
(m.insertIfNew k v).get? a =
if h : a == k ¬k m then some (cast (congrArg β (eq_of_beq h.1).symm) v)
if h : k == a ¬k m then some (cast (congrArg β (eq_of_beq h.1)) v)
else m.get? a := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.get?_insertIfNew m, _
theorem get_insertIfNew [LawfulBEq α] {k a : α} {v : β k} {h₁} :
(m.insertIfNew k v).get a h₁ =
if h₂ : a == k ¬k m then cast (congrArg β (eq_of_beq h₂.1).symm) v
if h₂ : k == a ¬k m then cast (congrArg β (eq_of_beq h₂.1)) v
else m.get a (mem_of_mem_insertIfNew' h h₁ h₂) := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.get_insertIfNew m, _
theorem get!_insertIfNew [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} :
(m.insertIfNew k v).get! a =
if h : a == k ¬k m then cast (congrArg β (eq_of_beq h.1).symm) v else m.get! a := by
if h : k == a ¬k m then cast (congrArg β (eq_of_beq h.1)) v else m.get! a := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.get!_insertIfNew m, _
theorem getD_insertIfNew [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} :
(m.insertIfNew k v).getD a fallback =
if h : a == k ¬k m then cast (congrArg β (eq_of_beq h.1).symm) v
if h : k == a ¬k m then cast (congrArg β (eq_of_beq h.1)) v
else m.getD a fallback := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.getD_insertIfNew
@@ -697,23 +697,23 @@ namespace Const
variable {β : Type v} {m : DHashMap.Raw α (fun _ => β)} (h : m.WF)
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
get? (m.insertIfNew k v) a = bif a == k && !m.contains k then some v else get? m a := by
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a := by
simp_to_raw using Raw₀.Const.get?_insertIfNew
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
get (m.insertIfNew k v) a h₁ =
if h₂ : a == k ¬k m then v
if h₂ : k == a ¬k m then v
else get m a (mem_of_mem_insertIfNew' h h₁ h₂) := by
simp only [mem_iff_contains, Bool.not_eq_true]
simp_to_raw using Raw₀.Const.get_insertIfNew m, _
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
get! (m.insertIfNew k v) a = bif a == k && !m.contains k then v else get! m a := by
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a := by
simp_to_raw using Raw₀.Const.get!_insertIfNew
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
getD (m.insertIfNew k v) a fallback =
bif a == k && !m.contains k then v else getD m a fallback := by
bif k == a && !m.contains k then v else getD m a fallback := by
simp_to_raw using Raw₀.Const.getD_insertIfNew
end Const

View File

@@ -27,7 +27,7 @@ nested inductive types.
universe u v w
variable {α : Type u} {β : Type v}
variable {α : Type u} {β : Type v} {_ : BEq α} {_ : Hashable α}
namespace Std
@@ -39,6 +39,9 @@ and an array of buckets, where each bucket is a linked list of key-value pais. T
is always a power of two. The hash map doubles its size upon inserting an element such that the
number of elements is more than 75% of the number of buckets.
The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to
avoid expensive copies.
The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by
the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==`
should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the
@@ -69,21 +72,21 @@ instance [BEq α] [Hashable α] : EmptyCollection (HashMap α β) where
instance [BEq α] [Hashable α] : Inhabited (HashMap α β) where
default :=
@[inline, inherit_doc DHashMap.insert] def insert [BEq α] [Hashable α] (m : HashMap α β) (a : α)
@[inline, inherit_doc DHashMap.insert] def insert (m : HashMap α β) (a : α)
(b : β) : HashMap α β :=
m.inner.insert a b
@[inline, inherit_doc DHashMap.insertIfNew] def insertIfNew [BEq α] [Hashable α] (m : HashMap α β)
@[inline, inherit_doc DHashMap.insertIfNew] def insertIfNew (m : HashMap α β)
(a : α) (b : β) : HashMap α β :=
m.inner.insertIfNew a b
@[inline, inherit_doc DHashMap.containsThenInsert] def containsThenInsert [BEq α] [Hashable α]
@[inline, inherit_doc DHashMap.containsThenInsert] def containsThenInsert
(m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
let replaced, r := m.inner.containsThenInsert a b
replaced, r
@[inline, inherit_doc DHashMap.containsThenInsertIfNew] def containsThenInsertIfNew [BEq α]
[Hashable α] (m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
@[inline, inherit_doc DHashMap.containsThenInsertIfNew] def containsThenInsertIfNew
(m : HashMap α β) (a : α) (b : β) : Bool × HashMap α β :=
let replaced, r := m.inner.containsThenInsertIfNew a b
replaced, r
@@ -96,7 +99,7 @@ returned map has a new value inserted.
Equivalent to (but potentially faster than) calling `get?` followed by `insertIfNew`.
-/
@[inline] def getThenInsertIfNew? [BEq α] [Hashable α] (m : HashMap α β) (a : α) (b : β) :
@[inline] def getThenInsertIfNew? (m : HashMap α β) (a : α) (b : β) :
Option β × HashMap α β :=
let previous, r := DHashMap.Const.getThenInsertIfNew? m.inner a b
previous, r
@@ -106,10 +109,10 @@ The notation `m[a]?` is preferred over calling this function directly.
Tries to retrieve the mapping for the given key, returning `none` if no such mapping is present.
-/
@[inline] def get? [BEq α] [Hashable α] (m : HashMap α β) (a : α) : Option β :=
@[inline] def get? (m : HashMap α β) (a : α) : Option β :=
DHashMap.Const.get? m.inner a
@[inline, inherit_doc DHashMap.contains] def contains [BEq α] [Hashable α] (m : HashMap α β)
@[inline, inherit_doc DHashMap.contains] def contains (m : HashMap α β)
(a : α) : Bool :=
m.inner.contains a
@@ -125,10 +128,10 @@ The notation `m[a]` or `m[a]'h` is preferred over calling this function directly
Retrieves the mapping for the given key. Ensures that such a mapping exists by requiring a proof of
`a ∈ m`.
-/
@[inline] def get [BEq α] [Hashable α] (m : HashMap α β) (a : α) (h : a m) : β :=
@[inline] def get (m : HashMap α β) (a : α) (h : a m) : β :=
DHashMap.Const.get m.inner a h
@[inline, inherit_doc DHashMap.Const.getD] def getD [BEq α] [Hashable α] (m : HashMap α β) (a : α)
@[inline, inherit_doc DHashMap.Const.getD] def getD (m : HashMap α β) (a : α)
(fallback : β) : β :=
DHashMap.Const.getD m.inner a fallback
@@ -137,7 +140,7 @@ The notation `m[a]!` is preferred over calling this function directly.
Tries to retrieve the mapping for the given key, panicking if no such mapping is present.
-/
@[inline] def get! [BEq α] [Hashable α] [Inhabited β] (m : HashMap α β) (a : α) : β :=
@[inline] def get! [Inhabited β] (m : HashMap α β) (a : α) : β :=
DHashMap.Const.get! m.inner a
instance [BEq α] [Hashable α] : GetElem? (HashMap α β) α β (fun m a => a m) where
@@ -145,37 +148,37 @@ instance [BEq α] [Hashable α] : GetElem? (HashMap α β) α β (fun m a => a
getElem? m a := m.get? a
getElem! m a := m.get! a
@[inline, inherit_doc DHashMap.remove] def remove [BEq α] [Hashable α] (m : HashMap α β) (a : α) :
@[inline, inherit_doc DHashMap.erase] def erase (m : HashMap α β) (a : α) :
HashMap α β :=
m.inner.remove a
m.inner.erase a
@[inline, inherit_doc DHashMap.size] def size [BEq α] [Hashable α] (m : HashMap α β) : Nat :=
@[inline, inherit_doc DHashMap.size] def size (m : HashMap α β) : Nat :=
m.inner.size
@[inline, inherit_doc DHashMap.isEmpty] def isEmpty [BEq α] [Hashable α] (m : HashMap α β) : Bool :=
@[inline, inherit_doc DHashMap.isEmpty] def isEmpty (m : HashMap α β) : Bool :=
m.inner.isEmpty
section Unverified
/-! We currently do not provide lemmas for the functions below. -/
@[inline, inherit_doc DHashMap.filter] def filter [BEq α] [Hashable α] (f : α β Bool)
@[inline, inherit_doc DHashMap.filter] def filter (f : α β Bool)
(m : HashMap α β) : HashMap α β :=
m.inner.filter f
@[inline, inherit_doc DHashMap.foldM] def foldM [BEq α] [Hashable α] {m : Type w Type w}
@[inline, inherit_doc DHashMap.foldM] def foldM {m : Type w Type w}
[Monad m] {γ : Type w} (f : γ α β m γ) (init : γ) (b : HashMap α β) : m γ :=
b.inner.foldM f init
@[inline, inherit_doc DHashMap.fold] def fold [BEq α] [Hashable α] {γ : Type w}
@[inline, inherit_doc DHashMap.fold] def fold {γ : Type w}
(f : γ α β γ) (init : γ) (b : HashMap α β) : γ :=
b.inner.fold f init
@[inline, inherit_doc DHashMap.forM] def forM [BEq α] [Hashable α] {m : Type w Type w} [Monad m]
@[inline, inherit_doc DHashMap.forM] def forM {m : Type w Type w} [Monad m]
(f : (a : α) β m PUnit) (b : HashMap α β) : m PUnit :=
b.inner.forM f
@[inline, inherit_doc DHashMap.forIn] def forIn [BEq α] [Hashable α] {m : Type w Type w} [Monad m]
@[inline, inherit_doc DHashMap.forIn] def forIn {m : Type w Type w} [Monad m]
{γ : Type w} (f : (a : α) β γ m (ForInStep γ)) (init : γ) (b : HashMap α β) : m γ :=
b.inner.forIn f init
@@ -185,33 +188,33 @@ instance [BEq α] [Hashable α] {m : Type w → Type w} : ForM m (HashMap α β)
instance [BEq α] [Hashable α] {m : Type w Type w} : ForIn m (HashMap α β) (α × β) where
forIn m init f := m.forIn (fun a b acc => f (a, b) acc) init
@[inline, inherit_doc DHashMap.Const.toList] def toList [BEq α] [Hashable α] (m : HashMap α β) :
@[inline, inherit_doc DHashMap.Const.toList] def toList (m : HashMap α β) :
List (α × β) :=
DHashMap.Const.toList m.inner
@[inline, inherit_doc DHashMap.Const.toArray] def toArray [BEq α] [Hashable α] (m : HashMap α β) :
@[inline, inherit_doc DHashMap.Const.toArray] def toArray (m : HashMap α β) :
Array (α × β) :=
DHashMap.Const.toArray m.inner
@[inline, inherit_doc DHashMap.keys] def keys [BEq α] [Hashable α] (m : HashMap α β) : List α :=
@[inline, inherit_doc DHashMap.keys] def keys (m : HashMap α β) : List α :=
m.inner.keys
@[inline, inherit_doc DHashMap.keysArray] def keysArray [BEq α] [Hashable α] (m : HashMap α β) :
@[inline, inherit_doc DHashMap.keysArray] def keysArray (m : HashMap α β) :
Array α :=
m.inner.keysArray
@[inline, inherit_doc DHashMap.values] def values [BEq α] [Hashable α] (m : HashMap α β) : List β :=
@[inline, inherit_doc DHashMap.values] def values (m : HashMap α β) : List β :=
m.inner.values
@[inline, inherit_doc DHashMap.valuesArray] def valuesArray [BEq α] [Hashable α] (m : HashMap α β) :
@[inline, inherit_doc DHashMap.valuesArray] def valuesArray (m : HashMap α β) :
Array β :=
m.inner.valuesArray
@[inline, inherit_doc DHashMap.Const.insertMany] def insertMany [BEq α] [Hashable α] {ρ : Type w}
@[inline, inherit_doc DHashMap.Const.insertMany] def insertMany {ρ : Type w}
[ForIn Id ρ (α × β)] (m : HashMap α β) (l : ρ) : HashMap α β :=
DHashMap.Const.insertMany m.inner l
@[inline, inherit_doc DHashMap.Const.insertManyUnit] def insertManyUnit [BEq α] [Hashable α]
@[inline, inherit_doc DHashMap.Const.insertManyUnit] def insertManyUnit
{ρ : Type w} [ForIn Id ρ α] (m : HashMap α Unit) (l : ρ) : HashMap α Unit :=
DHashMap.Const.insertManyUnit m.inner l
@@ -223,7 +226,7 @@ instance [BEq α] [Hashable α] {m : Type w → Type w} : ForIn m (HashMap α β
HashMap α Unit :=
DHashMap.Const.unitOfList l
@[inline, inherit_doc DHashMap.Internal.numBuckets] def Internal.numBuckets [BEq α] [Hashable α]
@[inline, inherit_doc DHashMap.Internal.numBuckets] def Internal.numBuckets
(m : HashMap α β) : Nat :=
DHashMap.Internal.numBuckets m.inner

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