Compare commits

..

1 Commits

Author SHA1 Message Date
Kim Morrison
921472c67e initial exploration for a ExtHashMapD 2025-05-19 13:24:18 +10:00
180 changed files with 810 additions and 6280 deletions

View File

@@ -1,9 +0,0 @@
# The Lean standard library
This directory contains development information about the Lean standard library. The user-facing documentation of the standard library
is part of the [Lean Language Reference](https://lean-lang.org/doc/reference/latest/).
Here you will find
* the [standard library vision document](./vision.md), including the call for contributions,
* the [standard library style guide](./style.md), and
* the [standard library naming conventions](./naming.md).

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -1,260 +0,0 @@
# Standard library naming conventions
The easiest way to access a result in the standard library is to correctly guess the name of the declaration (possibly with the help of identifier autocompletion). This is faster and has lower friction than more sophisticated search tools, so easily guessable names (which are still reasonably short) make Lean users more productive.
The guide that follows contains very few hard rules, many heuristics and a selection of examples. It cannot and does not present a deterministic algorithm for choosing good names in all situations. It is intended as a living document that gets clarified and expanded as situations arise during code reviews for the standard library. If applying one of the suggestions in this guide leads to nonsensical results in a certain situation, it is
probably safe to ignore the suggestion (or even better, suggest a way to improve the suggestion).
## Prelude
Identifiers use a mix of `UpperCamelCase`, `lowerCamelCase` and `snake_case`, used for types, data, and theorems, respectively.
Structure fields should be named such that the projections have the correct names.
## Naming convention for types
When defining a type, i.e., a (possibly 0-ary) function whose codomain is Sort u for some u, it should be named in UpperCamelCase. Examples include `List`, and `List.IsPrefix`.
When defining a predicate, prefix the name by `Is`, like in `List.IsPrefix`. The `Is` prefix may be omitted if
* the resulting name would be ungrammatical, or
* the predicate depends on additional data in a way where the `Is` prefix would be confusing (like `List.Pairwise`), or
* the name is an adjective (like `Std.Time.Month.Ordinal.Valid`)
## Namespaces and generalized projection notation
Almost always, definitions and theorems relating to a type should be placed in a namespace with the same name as the type. For example, operations and theorems about lists should be placed in the `List` namespace, and operations and theorems about `Std.Time.PlainDate` should be placed in the `Std.Time.PlainDate` namespace.
Declarations in the root namespace will be relatively rare. The most common type of declaration in the root namespace are declarations about data and properties exported by notation type classes, as long as they are not about a specific type implementing that type class. For example, we have
```lean
theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b := sorry
```
in the root namespace, but
```lean
theorem List.cons_beq_cons [BEq α] {a b : α} {l₁ l₂ : List α} :
(a :: l₁ == b :: l₂) = (a == b && l₁ == l₂) := rfl
```
belongs in the `List` namespace.
Subtleties arise when multiple namespaces are in play. Generally, place your theorem in the most specific namespace that appears in one of the hypotheses of the theorem. The following names are both correct according to this convention:
```lean
theorem List.Sublist.reverse : l₁ <+ l₂ l₁.reverse <+ l₂.reverse := sorry
theorem List.reverse_sublist : l₁.reverse <+ l₂.reverse l₁ <+ l₂ := sorry
```
Notice that the second theorem does not have a hypothesis of type `List.Sublist l` for some `l`, so the name `List.Sublist.reverse_iff` would be incorrect.
The advantage of placing results in a namespace like `List.Sublist` is that it enables generalized projection notation, i.e., given `h : l₁ <+ l₂`,
one can write `h.reverse` to obtain a proof of `l₁.reverse <+ l₂.reverse`. Thinking about which dot notations are convenient can act as a guideline
for deciding where to place a theorem, and is, on occasion, a good reason to duplicate a theorem into multiple namespaces.
### The `Std` namespace
New types that are added will usually be placed in the `Std` namespace and in the `Std/` source directory, unless there are good reasons to place
them elsewhere.
Inside the `Std` namespace, all internal declarations should be `private` or else have a name component that clearly marks them as internal, preferably
`Internal`.
## Naming convention for data
When defining data, i.e., a (possibly 0-ary) function whose codomain is not Sort u, but has type Type u for some u, it should be named in lowerCamelCase. Examples include `List.append` and `List.isPrefixOf`.
If your data is morally fully specified by its type, then use the naming procedure for theorems described below and convert the result to lower camel case.
If your function returns an `Option`, consider adding `?` as a suffix. If your function may panic, consider adding `!` as a suffix. In many cases, there will be multiple variants of a function; one returning an option, one that may panic and possibly one that takes a proof argument.
## Naming algorithm for theorems and some definitions
There is, in principle, a general algorithm for naming a theorem. The problem with this algorithm is that it produces very long and unwieldy names which need to be shortened. So choosing a name for a declaration can be thought of as consisting of a mechanical part and a creative part.
Usually the first part is to decide which namespace the result should live in, according to the guidelines described above.
Next, consider the type of your declaration as a tree. Inner nodes of this tree are function types or function applications. Leaves of the tree are 0-ary functions or bound variables.
As an example, consider the following result from the standard library:
```lean
example {α : Type u} {β : Type v} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
[Inhabited β] {m : Std.HashMap α β} {a : α} {h' : a m} : m[a]? = some (m[a]'h') :=
sorry
```
The correct namespace is clearly `Std.HashMap`. The corresponding tree looks like this:
![](naming-tree.svg)
The preferred spelling of a notation can be looked up by hovering over the notation.
Now traverse the tree and build a name according to the following rules:
* When encountering a function type, first turn the result type into a name, then all of the argument types from left to right, and join the names using `_of_`.
* When encountering a function that is neither an infix notation nor a structure projection, first put the function name and then the arguments, joined by an underscore.
* When encountering an infix notation, join the arguments using the name of the notation, separated by underscores.
* When encountering a structure projection, proceed as for normal functions, but put the name of the projection last.
* When encountering a name, put it in lower camel case.
* Skip bound variables and proofs.
* Type class arguments are also generally skipped.
When encountering namespaces names, concatenate them in lower camel case.
Applying this algorithm to our example yields the name `Std.HashMap.getElem?_eq_optionSome_getElem_of_mem`.
From there, the name should be shortened, using the following heuristics:
* The namespace of functions can be omitted if it is clear from context or if the namespace is the current one. This is almost always the case.
* For infix operators, it is possible to leave out the RHS or the name of the notation and the RHS if they are clear from context.
* Hypotheses can be left out if it is clear that they are required or if they appear in the conclusion.
Based on this, here are some possible names for our example:
1. `Std.HashMap.getElem?_eq`
2. `Std.HashMap.getElem?_eq_of_mem`
3. `Std.HashMap.getElem?_eq_some`
4. `Std.HashMap.getElem?_eq_some_of_mem`
5. `Std.HashMap.getElem?_eq_some_getElem`
6. `Std.Hashmap.getElem?_eq_some_getElem_of_mem`
Choosing a good name among these then requires considering the context of the lemma. In this case it turns out that the first four options are underspecified as there is also a lemma relating `m[a]?` and `m[a]!` which could have the same name. This leaves the last two options, the first of which is shorter, and this is how the lemma is called in the Lean standard library.
Here are some additional examples:
```lean
example {x y : List α} (h : x <+: y) (hx : x []) :
x.head hx = y.head (h.ne_nil hx) := sorry
```
Since we have an `IsPrefix` parameter, this should live in the `List.IsPrefix` namespace, and the algorithm suggests `List.IsPrefix.head_eq_head_of_ne_nil`, which is shortened to `List.IsPrefix.head`. Note here the difference between the namespace name (`IsPrefix`) and the recommended spelling of the corresponding notation (`prefix`).
```lean
example : l₁ <+: l₂ reverse l₁ <:+ reverse l₂ := sorry
```
Again, this result should be in the `List.IsPrefix` namespace; the algorithm suggests `List.IsPrefix.reverse_prefix_reverse`, which becomes `List.IsPrefix.reverse`.
The following examples show how the traversal order often matters.
```lean
theorem Nat.mul_zero (n : Nat) : n * 0 = 0 := sorry
theorem Nat.zero_mul (n : Nat) : 0 * n = 0 := sorry
```
Here we see that one name may be a prefix of another name:
```lean
theorem Int.mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 := sorry
theorem Int.mul_ne_zero_iff {a b : Int} : a * b 0 a 0 b 0 := sorry
```
It is usually a good idea to include the `iff` in a theorem name even if the name would still be unique without the name. For example,
```lean
theorem List.head?_eq_none_iff : l.head? = none l = [] := sorry
```
is a good name: if the lemma was simply called `List.head?_eq_none`, users might try to `apply` it when the goal is `l.head? = none`, leading
to confusion.
The more common you expect (or want) a theorem to be, the shorter you should try to make the name. For example, we have both
```lean
theorem Std.HashMap.getElem?_eq_none_of_contains_eq_false {a : α} : m.contains a = false m[a]? = none := sorry
theorem Std.HashMap.getElem?_eq_none {a : α} : ¬a m m[a]? = none := sorry
```
As users of the hash map are encouraged to use ∈ rather than contains, the second lemma gets the shorter name.
## Special cases
There are certain special “keywords” that may appear in identifiers.
| Keyword | Meaning | Example |
| :---- | :---- | :---- |
| `def` | Unfold a definition. Avoid this for public APIs. | `Nat.max_def` |
| `refl` | Theorems of the form `a R a`, where R is a reflexive relation and `a` is an explicit parameter | `Nat.le_refl` |
| `rfl` | Like `refl`, but with `a` implicit | `Nat.le_rfl` |
| `irrefl` | Theorems of the form `¬a R a`, where R is an irreflexive relation | `Nat.lt_irrefl` |
| `symm` | Theorems of the form `a R b → b R a`, where R is a symmetric relation (compare `comm` below) | `Eq.symm` |
| `trans` | Theorems of the form `a R b → b R c → a R c`, where R is a transitive relation (R may carry data) | `Eq.trans` |
| `antisymmm` | Theorems of the form `a R b → b R a → a = b`, where R is an antisymmetric relation | `Nat.le_antisymm` |
| `congr` | Theorems of the form `a R b → f a S f b`, where R and S are usually equivalence relations | `Std.HashMap.mem_congr` |
| `comm` | Theorems of the form `f a b = f b a` (compare `symm` above) | `Eq.comm`, `Nat.add_comm` |
| `assoc` | Theorems of the form `g (f a b) c = f a (g b c)` (note the order! In most cases, we have f = g) | `Nat.add_sub_assoc` |
| `distrib` | Theorems of the form `f (g a b) = g (f a) (f b)` | `Nat.add_left_distrib` |
| `self` | May be used if a variable appears multiple times in the conclusion | `List.mem_cons_self` |
| `inj` | Theorems of the form `f a = f b ↔ a = b`. | `Int.neg_inj`, `Nat.add_left_inj` |
| `cancel` | Theorems which have one of the forms `f a = f b → a = b` or `g (f a) = a`, where `f` and `g` usually involve a binary operator | `Nat.add_sub_cancel` |
| `cancel_iff` | Same as `inj`, but with different conventions for left and right (see below) | `Nat.add_right_cancel_iff` |
| `ext` | Theorems of the form `f a = f b → a = b`, where `f` usually involves some kind of projection | `List.ext_getElem`
| `mono` | Theorems of the form `a R b → f a R f b`, where `R` is a transitive relation | `List.countP_mono_left`
### Left and right
The keywords left and right are useful to disambiguate symmetric variants of theorems.
```lean
theorem imp_congr_left (h : a b) : (a c) (b c) := sorry
theorem imp_congr_right (h : a (b c)) : (a b) (a c) := sorry
```
It is not always obvious which version of a theorem should be “left” and which should be “right”.
Heuristically, the theorem should name the side which is “more variable”, but there are exceptions. For some of the special keywords discussed in this section, there are conventions which should be followed, as laid out in the following examples:
```lean
theorem Nat.left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := sorry
theorem Nat.right_distrib (n m k : Nat) : (n + m) * k = n * k + m * k := sorry
theorem Nat.add_left_cancel {n m k : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel {n m k : Nat} : n + m = k + m n = k := sorry
theorem Nat.add_left_cancel_iff {m k n : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel_iff {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_left_inj {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_right_inj {m k n : Nat} : n + m = n + k m = k := sorry
```
Note in particular that the convention is opposite for `cancel_iff` and `inj`.
```lean
theorem Nat.add_sub_self_left (a b : Nat) : (a + b) - a = b := sorry
theorem Nat.add_sub_self_right (a b : Nat) : (a + b) - b = a := sorry
theorem Nat.add_sub_cancel (n m : Nat) : (n + m) - m = n := sorry
```
## Primed names
Avoid disambiguating variants of a concept by appending the `'` character (e.g., introducing both `BitVec.sshiftRight` and `BitVec.sshiftRight'`), as it is impossible to tell the difference without looking at the type signature, the documentation or even the code, and even if you know what the two variants are there is no way to tell which is which. Prefer descriptive pairs `BitVec.sshiftRightNat`/`BitVec.sshiftRight`.
## Acronyms
For acronyms which are three letters or shorter, all letters should use the same case as dictated by the convention. For example, `IO` is a correct name for a type and the name `IO.Ref` may become `IORef` when used as part of a definition name and `ioRef` when used as part of a theorem name.
For acronyms which are at least four letters long, switch to lower case starting from the second letter. For example, `Json` is a correct name for a type, as is `JsonRPC`.
If an acronym is typically spelled using mixed case, this mixed spelling may be used in identifiers (for example `Std.Net.IPv4Addr`).
## Simp sets
Simp sets centered around a conversion function should be called `source_to_target`. For example, a simp set for the `BitVec.toNat` function, which goes from `BitVec` to
`Nat`, should be called `bitvec_to_nat`.
## Variable names
We make the following recommendations for variable names, but without insisting on them:
* Simple hypotheses should be named `h`, `h'`, or using a numerical sequence `h₁`, `h₂`, etc.
* Another common name for a simple hypothesis is `w` (for "witness").
* `List`s should be named `l`, `l'`, `l₁`, etc, or `as`, `bs`, etc.
(Use of `as`, `bs` is encouraged when the lists are of different types, e.g. `as : List α` and `bs : List β`.)
`xs`, `ys`, `zs` are allowed, but it is better if these are reserved for `Array` and `Vector`.
A list of lists may be named `L`.
* `Array`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the arrays are of different types, e.g. `as : Array α` and `bs : Array β`.
An array of arrays may be named `xss`.
* `Vector`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the vectors are of different types, e.g. `as : Vector α n` and `bs : Vector β n`.
A vector of vectors may be named `xss`.
* A common exception for `List` / `Array` / `Vector` is to use `acc` for an accumulator in a recursive function.
* `i`, `j`, `k` are preferred for numerical indices.
Descriptive names such as `start`, `stop`, `lo`, and `hi` are encouraged when they increase readability.
* `n`, `m` are preferred for sizes, e.g. in `Vector α n` or `xs.size = n`.
* `w` is preferred for the width of a `BitVec`.

View File

@@ -1,522 +0,0 @@
# Standard library style
Please take some time to familiarize yourself with the stylistic conventions of
the project and the specific part of the library you are planning to contribute
to. While the Lean compiler may not enforce strict formatting rules,
consistently formatted code is much easier for others to read and maintain.
Attention to formatting is more than a cosmetic concern—it reflects the same
level of precision and care required to meet the deeper standards of the Lean 4
standard library.
Below we will give specific formatting prescriptions for various language constructs. Note that this style guide only applies to the Lean standard library, even though some examples in the guide are taken from other parts of the Lean code base.
## Basic whitespace rules
Syntactic elements (like `:`, `:=`, `|`, `::`) are surrounded by single spaces, with the exception of `,` and `;`, which are followed by a space but not preceded by one. Delimiters (like `()`, `{}`) do not have spaces on the inside, with the exceptions of subtype notation and structure instance notation.
Examples of correctly formatted function parameters:
* `{α : Type u}`
* `[BEq α]`
* `(cmp : αα → Ordering)`
* `(hab : a = b)`
* `{d : { l : List ((n : Nat) × Vector Nat n) // l.length % 2 = 0 }}`
Examples of correctly formatted terms:
* `1 :: [2, 3]`
* `letI : Ord α := ⟨cmp⟩; True`
* `(⟨2, 3⟩ : Nat × Nat)`
* `((2, 3) : Nat × Nat)`
* `{ x with fst := f (4 + f 0), snd := 4, .. }`
* `match 1 with | 0 => 0 | _ => 0`
* `fun ⟨a, b⟩ _ _ => by cases hab <;> apply id; rw [hbc]`
Configure your editor to remove trailing whitespace. If you have set up Visual Studio Code for Lean development in the recommended way then the correct setting is applied automatically.
## Splitting terms across multiple lines
When splitting a term across multiple lines, increase indentation by two spaces starting from the second line. When splitting a function application, try to split at argument boundaries. If an argument itself needs to be split, increase indentation further as appropriate.
When splitting at an infix operator, the operator goes at the end of the first line, not at the beginning of the second line. When splitting at an infix operator, you may or may not increase indentation depth, depending on what is more readable.
When splitting an `if`-`then`-`else` expression, the `then` keyword wants to stay with the condition and the `else` keyword wants to stay with the alternative term. Otherwise, indent as if the `if` and `else` keywords were arguments to the same function.
When splitting a comma-separated bracketed sequence (i.e., anonymous constructor application, list/array/vector literal, tuple) it is allowed to indent subsequent lines for alignment, but indenting by two spaces is also allowed.
Do not orphan parentheses.
Correct:
```lean
def MacroScopesView.isPrefixOf (v₁ v₂ : MacroScopesView) : Bool :=
v₁.name.isPrefixOf v₂.name &&
v₁.scopes == v₂.scopes &&
v₁.mainModule == v₂.mainModule &&
v₁.imported == v₂.imported
```
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
example : Nat :=
functionWithAVeryLongNameSoThatSomeArgumentsWillNotFit firstArgument secondArgument
(firstArgumentWithAnEquallyLongNameAndThatFunctionDoesHaveMoreArguments firstArgument
secondArgument)
secondArgument
```
Correct:
```lean
theorem size_alter [LawfulBEq α] {k : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).size =
if m.contains k && (f (m.get? k)).isNone then
m.size - 1
else if !m.contains k && (f (m.get? k)).isSome then
m.size + 1
else
m.size := by
simp_to_raw using Raw₀.size_alter
```
Correct:
```lean
theorem get?_alter [LawfulBEq α] {k k' : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).get? k' =
if h : k == k' then
cast (congrArg (Option β) (eq_of_beq h)) (f (m.get? k))
else m.get? k' := by
simp_to_raw using Raw₀.get?_alter
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Vector Nat :=
#v[imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm]
```
## Basic file structure
Every file should start with a copyright header, imports (in the standard library, this always includes a `prelude` declaration) and a module documentation string. There should not be a blank line between the copyright header and the imports. There should be a blank line between the imports and the module documentation string.
If you explicitly declare universe variables, do so at the top of the file, after the module documentation.
Correct:
```lean
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
Yury Kudryashov
-/
prelude
import Init.Data.List.Pairwise
import Init.Data.List.Find
/-!
**# Lemmas about `List.eraseP` and `List.erase`.**
-/
universe u u'
```
Syntax that is not supposed to be user-facing must be scoped. New public syntax must always be discussed explicitly in an RFC.
## Top-level commands and declarations
All top-level commands are unindented. Sectioning commands like `section` and `namespace` do not increase the indentation level.
Attributes may be placed on the same line as the rest of the command or on a separate line.
Multi-line declaration headers are indented by four spaces starting from the second line. The colon that indicates the type of a declaration may not be placed at the start of a line or on its own line.
Declaration bodies are indented by two spaces. Short declaration bodies may be placed on the same line as the declaration type.
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
```
Correct:
```lean
@[simp]
theorem eraseP_nil : [].eraseP p = [] := rfl
```
### Documentation comments
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
Declarations should be documented as required by the `docBlame` linter, which may be activated in a file using
`set_option linter.missingDocs true` (we allow these to stay in the file).
Single-line documentation comments should go on the same line as `/--`/`-/`, while multi-line documentation strings
should have these delimiters on their own line, with the documentation comment itself unindented.
Documentation comments must be written in the indicative mood. Use American orthography.
Correct:
```lean
/-- Carries out a monadic action on each mapping in the hash map in some order. -/
@[inline] def forM (f : (a : α) β a m PUnit) (b : Raw α β) : m PUnit :=
b.buckets.forM (AssocList.forM f)
```
Correct:
```lean
/--
Monadically computes a value by folding the given function over the mappings in the hash
map in some order.
-/
@[inline] def foldM (f : δ (a : α) β a m δ) (init : δ) (b : Raw α β) : m δ :=
b.buckets.foldlM (fun acc l => l.foldlM f acc) init
```
### Where clauses
The `where` keyword should be unindented, and all declarations bound by it should be indented with two spaces.
Blank lines before and after `where` and between declarations bound by `where` are optional and should be chosen
to maximize readability.
Correct:
```lean
@[simp] theorem partition_eq_filter_filter (p : α Bool) (l : List α) :
partition p l = (filter p l, filter (not p) l) := by
simp [partition, aux]
where
aux (l) {as bs} : partition.loop p l (as, bs) =
(as.reverse ++ filter p l, bs.reverse ++ filter (not p) l) :=
match l with
| [] => by simp [partition.loop, filter]
| a :: l => by cases pa : p a <;> simp [partition.loop, pa, aux, filter, append_assoc]
```
### Termination arguments
The `termination_by`, `decreasing_by`, `partial_fixpoint` keywords should be unindented. The associated terms should be indented like declaration bodies.
Correct:
```lean
@[inline] def multiShortOption (handle : Char m PUnit) (opt : String) : m PUnit := do
let rec loop (p : String.Pos) := do
if h : opt.atEnd p then
return
else
handle (opt.get' p h)
loop (opt.next' p h)
termination_by opt.utf8ByteSize - p.byteIdx
decreasing_by
simp [String.atEnd] at h
apply Nat.sub_lt_sub_left h
simp [String.lt_next opt p]
loop 1
```
Correct:
```lean
def substrEq (s1 : String) (off1 : String.Pos) (s2 : String) (off2 : String.Pos) (sz : Nat) : Bool :=
off1.byteIdx + sz s1.endPos.byteIdx && off2.byteIdx + sz s2.endPos.byteIdx && loop off1 off2 { byteIdx := off1.byteIdx + sz }
where
loop (off1 off2 stop1 : Pos) :=
if _h : off1.byteIdx < stop1.byteIdx then
let c₁ := s1.get off1
let c₂ := s2.get off2
c₁ == c₂ && loop (off1 + c₁) (off2 + c₂) stop1
else true
termination_by stop1.1 - off1.1
decreasing_by
have := Nat.sub_lt_sub_left _h (Nat.add_lt_add_left c₁.utf8Size_pos off1.1)
decreasing_tactic
```
Correct:
```lean
theorem div_add_mod (m n : Nat) : n * (m / n) + m % n = m := by
rw [div_eq, mod_eq]
have h : Decidable (0 < n n m) := inferInstance
cases h with
| isFalse h => simp [h]
| isTrue h =>
simp [h]
have ih := div_add_mod (m - n) n
rw [Nat.left_distrib, Nat.mul_one, Nat.add_assoc, Nat.add_left_comm, ih, Nat.add_comm, Nat.sub_add_cancel h.2]
decreasing_by apply div_rec_lemma; assumption
```
### Deriving
The `deriving` clause should be unindented.
Correct:
```lean
structure Iterator where
array : ByteArray
idx : Nat
deriving Inhabited
```
## Notation and Unicode
We generally prefer to use notation as available. We usually prefer the Unicode versions of notations over non-Unicode alternatives.
There are some rules and exceptions regarding specific notations which are listed below:
* Sigma types: use `(a : α) × β a` instead of `Σ a, β a` or `Sigma β`.
* Function arrows: use `fun a => f x` instead of `fun x ↦ f x` or `λ x => f x` or any other variant.
## Language constructs
### Pattern matching, induction etc.
Match arms are indented at the indentation level that the match statement would have if it was on its own line. If the match is implicit, then the arms should be indented as if the match was explicitly given. The content of match arms is indented two spaces, so that it appears on the same level as the match pattern.
Correct:
```lean
def alter [BEq α] {β : Type v} (a : α) (f : Option β Option β) :
AssocList α (fun _ => β) AssocList α (fun _ => β)
| nil => match f none with
| none => nil
| some b => AssocList.cons a b nil
| cons k v l =>
if k == a then
match f v with
| none => l
| some b => cons a b l
else
cons k v (alter a f l)
```
Correct:
```lean
theorem eq_append_cons_of_mem {a : α} {xs : List α} (h : a xs) :
as bs, xs = as ++ a :: bs a as := by
induction xs with
| nil => cases h
| cons x xs ih =>
simp at h
cases h with
| inl h => exact [], xs, by simp_all
| inr h =>
by_cases h' : a = x
· subst h'
exact [], xs, by simp
· obtain as, bs, rfl, h := ih h
exact x :: as, bs, rfl, by simp_all
```
Aligning match arms is allowed, but not required.
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
### Structures
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
When using structure instance syntax over multiple lines, the opening brace should go on the preceding line, while the closing brace should go on its own line. The rest of the syntax should be indented by one level. During structure updates, the `with` clause goes on the same line as the opening brace. Aligning at the assignment symbol is allowed but not required.
Correct:
```lean
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
IO AddConstAsyncResult := do
let sigPromise IO.Promise.new
let infoPromise IO.Promise.new
let extensionsPromise IO.Promise.new
let checkedEnvPromise IO.Promise.new
let asyncConst := {
constInfo := {
name := constName
kind
sig := sigPromise.result
constInfo := infoPromise.result
}
exts? := guard reportExts *> some extensionsPromise.result
}
return {
constName, kind
mainEnv := { env with
asyncConsts := env.asyncConsts.add asyncConst
checked := checkedEnvPromise.result }
asyncEnv := { env with
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
}
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
}
```
Correct:
```lean
instance [Inhabited α] : Inhabited (Descr α β σ) where
default := {
name := default
mkInitial := default
ofOLeanEntry := default
toOLeanEntry := default
addEntry := fun s _ => s
}
```
### Declaring structures
When defining structure types, do not parenthesize structure fields.
When declaring a structure type with a custom constructor name, put the custom name on its own line, indented like the
structure fields, and add a documentation comment.
Correct:
```lean
/--
A bitvector of the specified width.
This is represented as the underlying `Nat` number in both the runtime
and the kernel, inheriting all the special support for `Nat`.
-/
structure BitVec (w : Nat) where
/--
Constructs a `BitVec w` from a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector.
-/
ofFin ::
/--
Interprets a bitvector as a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector.
-/
toFin : Fin (2 ^ w)
```
## Tactic proofs
Tactic proofs are the most common thing to break during any kind of upgrade, so it is important to write them in a way that minimizes the likelihood of proofs breaking and that makes it easy to debug breakages if they do occur.
If there are multiple goals, either use a tactic combinator (like `all_goals`) to operate on all of them or a clearly specified subset, or use focus dots to work on goals one at a time. Using structured proofs (e.g., `induction … with`) is encouraged but not mandatory.
Squeeze non-terminal `simp`s (i.e., calls to `simp` which do not close the goal). Squeezing terminal `simp`s is generally discouraged, although there are exceptions (for example if squeezing yields a noticeable performance improvement).
Do not over-golf proofs in ways that are likely to lead to hard-to-debug breakage. Examples of things to avoid include complex multi-goal manipulation using lots of tactic combinators, complex uses of the substitution operator (`▸`) and clever point-free expressions (possibly involving anonymous function notation for multiple arguments).
Do not under-golf proofs: for routine tasks, use the most powerful tactics available.
Do not use `erw`. Avoid using `rfl` after `simp` or `rw`, as this usually indicates a missing lemma that should be used instead of `rfl`.
Use `(d)simp` or `rw` instead of `delta` or `unfold`. Use `refine` instead of `refine`. Use `haveI` and `letI` only if they are actually required.
Prefer highly automated tactics (like `grind` and `omega`) over low-level proofs, unless the automated tactic requires unacceptable additional imports or has bad performance. If you decide against using a highly automated tactic, leave a comment explaining the decision.
## `do` notation
The `do` keyword goes on the same line as the corresponding `:=` (or `=>`, or similar). `Id.run do` should be treated as if it was a bare `do`.
Use early `return` statements to reduce nesting depth and make the non-exceptional control flow of a function easier to see.
Alternatives for `let` matches may be placed in the same line or in the next line, indented by two spaces. If the term that is
being matched on is itself more than one line and there is an alternative present, consider breaking immediately after `←` and indent
as far as necessary to ensure readability.
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl? fvarId | throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl
findFunDecl? fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl?
fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def tagUntaggedGoals (parentTag : Name) (newSuffix : Name) (newGoals : List MVarId) : TacticM Unit := do
let mctx getMCtx
let mut numAnonymous := 0
for g in newGoals do
if mctx.isAnonymousMVar g then
numAnonymous := numAnonymous + 1
modifyMCtx fun mctx => Id.run do
let mut mctx := mctx
let mut idx := 1
for g in newGoals do
if mctx.isAnonymousMVar g then
if numAnonymous == 1 then
mctx := mctx.setMVarUserName g parentTag
else
mctx := mctx.setMVarUserName g (parentTag ++ newSuffix.appendIndexAfter idx)
idx := idx + 1
pure mctx
```

View File

@@ -1,98 +0,0 @@
# The Lean 4 standard library
Maintainer team (in alphabetical order): Henrik Böving, Markus Himmel
(community contact & external contribution coordinator), Kim Morrison, Paul
Reichert, Sofia Rodrigues.
The Lean 4 standard library is a core part of the Lean distribution, providing
essential building blocks for functional programming, verified software
development, and software verification. Unlike the standard libraries of most
other languages, many of its components are formally verified and can be used
as part of verified applications.
The standard library is a public API that contains the components listed in the
standard library outline below. Not all public APIs in the Lean distribution
are part of the standard library, and the standard library does not correspond
to a certain directory within the Lean source repository (like `Std`). For
example, the metaprogramming framework is not part of the standard library, but
basic types like `True` and `Nat` are.
The standard library is under active development. Our guiding principles are:
* Provide comprehensive, verified building blocks for real-world software.
* Build a public API of the highest quality with excellent internal consistency.
* Carefully optimize components that may be used in performance-critical software.
* Ensure smooth adoption and maintenance for users.
* Offer excellent documentation, example projects, and guides.
* Provide a reliable and extensible basis that libraries for software
development, software verification and mathematics can build on.
The standard library is principally developed by the Lean FRO. Community
contributions are welcome. If you would like to contribute, please refer to the
call for contributions below.
### Standard library outline
1. Core types and operations
1. Basic types
2. Numeric types, including floating point numbers
3. Containers
4. Strings and formatting
2. Language constructs
1. Ranges and iterators
2. Comparison, ordering, hashing and related type classes
3. Basic monad infrastructure
3. Libraries
1. Random numbers
2. Dates and times
4. Operating system abstractions
1. Concurrency and parallelism primitives
2. Asynchronous I/O
3. FFI helpers
4. Environment, file system, processes
5. Locales
The material covered in the first three sections (core types and operations,
language constructs and libraries) will be verified, with the exception of
floating point numbers and the parts of the libraries that interface with the
operating system (e.g., sources of operating system randomness or time zone
database access).
### Call for contributions
Thank you for taking interest in contributing to the Lean standard library\!
There are two main ways for community members to contribute to the Lean
standard library: by contributing experience reports or by contributing code
and lemmas.
**If you are using Lean for software verification or verified software
development:** hearing about your experiences using Lean and its standard
library for software verification is extremely valuable to us. We are committed
to building a standard library suitable for real-world applications and your
input will directly influence the continued evolution of the Lean standard
library. Please reach out to the standard library maintainer team via Zulip
(either in a public thread in the \#lean4 channel or via direct message). Even
just a link to your code helps. Thanks\!
**If you have code that you believe could enhance the Lean 4 standard
library:** we encourage you to initiate a discussion in the \#lean4 channel on
Zulip. This is the most effective way to receive preliminary feedback on your
contribution. The Lean standard library has a very precise scope and it has
very high quality standards, so at the moment we are mostly interested in
contributions that expand upon existing material rather than introducing novel
concepts.
**If you would like to contribute code to the standard library but dont know
what to work on:** we are always excited to meet motivated community members
who would like to contribute, and there is always impactful work that is
suitable for new contributors. Please reach out to Markus Himmel on Zulip to
discuss possible contributions.
As laid out in the [project-wide External Contribution
Guidelines](../../CONTRIBUTING.md),
PRs are much more likely to be merged if they are preceded by an RFC or if you
discussed your planned contribution with a member of the standard library
maintainer team. When in doubt, introducing yourself is always a good idea.
All code in the standard library is expected to strictly adhere to the
[standard library coding conventions](./style.md).

View File

@@ -107,8 +107,8 @@ noncomputable def epsilon {α : Sort u} [h : Nonempty α] (p : α → Prop) : α
theorem epsilon_spec_aux {α : Sort u} (h : Nonempty α) (p : α Prop) : ( y, p y) p (@epsilon α h p) :=
(strongIndefiniteDescription p h).property
theorem epsilon_spec {α : Sort u} {p : α Prop} (hex : y, p y) : p (@epsilon α hex.nonempty p) :=
epsilon_spec_aux hex.nonempty p hex
theorem epsilon_spec {α : Sort u} {p : α Prop} (hex : y, p y) : p (@epsilon α (nonempty_of_exists hex) p) :=
epsilon_spec_aux (nonempty_of_exists hex) p hex
theorem epsilon_singleton {α : Sort u} (x : α) : @epsilon α x (fun y => y = x) = x :=
@epsilon_spec α (fun y => y = x) x, rfl

View File

@@ -1212,7 +1212,10 @@ abbrev noConfusionEnum {α : Sort u} {β : Sort v} [inst : DecidableEq β] (f :
instance : Inhabited Prop where
default := True
deriving instance Inhabited for NonScalar, PNonScalar, True
deriving instance Inhabited for NonScalar, PNonScalar, True, ForInStep
theorem nonempty_of_exists {α : Sort u} {p : α Prop} : Exists (fun x => p x) Nonempty α
| w, _ => w
/-! # Subsingleton -/
@@ -1386,7 +1389,16 @@ instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
instance Sum.nonemptyRight [h : Nonempty β] : Nonempty (Sum α β) :=
Nonempty.elim h (fun b => Sum.inr b)
deriving instance DecidableEq for Sum
instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b =>
match a, b with
| Sum.inl a, Sum.inl b =>
if h : a = b then isTrue (h rfl)
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
| Sum.inr a, Sum.inr b =>
if h : a = b then isTrue (h rfl)
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
| Sum.inr _, Sum.inl _ => isFalse fun h => Sum.noConfusion h
| Sum.inl _, Sum.inr _ => isFalse fun h => Sum.noConfusion h
end

View File

@@ -112,10 +112,6 @@ theorem mem_def {a : α} {as : Array α} : a ∈ as ↔ a ∈ as.toList :=
rw [Array.mem_def, getElem_toList]
apply List.getElem_mem
@[simp] theorem emptyWithCapacity_eq {α n} : @emptyWithCapacity α n = #[] := rfl
@[simp] theorem mkEmpty_eq {α n} : @mkEmpty α n = #[] := rfl
end Array
namespace List
@@ -338,8 +334,6 @@ def ofFn {n} (f : Fin n → α) : Array α := go 0 (emptyWithCapacity n) where
if h : i < n then go (i+1) (acc.push (f i, h)) else acc
decreasing_by simp_wf; decreasing_trivial_pre_omega
-- See also `Array.ofFnM` defined in `Init.Data.Array.OfFn`.
/--
Constructs an array that contains all the numbers from `0` to `n`, exclusive.

View File

@@ -61,6 +61,11 @@ theorem toArray_eq : List.toArray as = xs ↔ as = xs.toList := by
@[grind] theorem size_empty : (#[] : Array α).size = 0 := rfl
@[simp] theorem emptyWithCapacity_eq {α n} : @emptyWithCapacity α n = #[] := rfl
@[deprecated emptyWithCapacity_eq (since := "2025-03-12")]
theorem mkEmpty_eq {α n} : @mkEmpty α n = #[] := rfl
/-! ### size -/
@[grind ] theorem eq_empty_of_size_eq_zero (h : xs.size = 0) : xs = #[] := by
@@ -242,12 +247,6 @@ theorem back?_pop {xs : Array α} :
/-! ### push -/
@[simp] theorem push_empty : #[].push x = #[x] := rfl
@[simp] theorem toList_push {xs : Array α} {x : α} : (xs.push x).toList = xs.toList ++ [x] := by
rcases xs with xs
simp
@[simp] theorem push_ne_empty {a : α} {xs : Array α} : xs.push a #[] := by
cases xs
simp
@@ -3267,22 +3266,6 @@ rather than `(arr.push a).size` as the argument.
(xs.push a).foldrM f init start = f a init >>= xs.foldrM f := by
simp [ foldrM_push, h]
@[simp, grind] theorem _root_.List.foldrM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α m β} {xs : Array β} :
l.foldrM (fun x xs => xs.push <$> f x) xs = do return xs ++ ( l.reverse.mapM f).toArray := by
induction l with
| nil => simp
| cons a l ih =>
simp [ih]
congr 1
funext l'
congr 1
funext x
simp
@[simp, grind] theorem _root_.List.foldlM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α m β} {xs : Array β} :
l.foldlM (fun xs x => xs.push <$> f x) xs = do return xs ++ ( l.mapM f).toArray := by
induction l generalizing xs <;> simp [*]
/-! ### foldl / foldr -/
@[grind] theorem foldl_empty {f : β α β} {init : β} : (#[].foldl f init) = init := rfl
@@ -3379,32 +3362,6 @@ rather than `(arr.push a).size` as the argument.
rcases as with as
simp
@[simp, grind] theorem _root_.List.foldr_push_eq_append {l : List α} {f : α β} {xs : Array β} :
l.foldr (fun x xs => xs.push (f x)) xs = xs ++ (l.reverse.map f).toArray := by
induction l <;> simp [*]
/-- Variant of `List.foldr_push_eq_append` specialized to `f = id`. -/
@[simp, grind] theorem _root_.List.foldr_push_eq_append' {l : List α} {xs : Array α} :
l.foldr (fun x xs => xs.push x) xs = xs ++ l.reverse.toArray := by
induction l <;> simp [*]
@[simp, grind] theorem _root_.List.foldl_push_eq_append {l : List α} {f : α β} {xs : Array β} :
l.foldl (fun xs x => xs.push (f x)) xs = xs ++ (l.map f).toArray := by
induction l generalizing xs <;> simp [*]
/-- Variant of `List.foldl_push_eq_append` specialized to `f = id`. -/
@[simp, grind] theorem _root_.List.foldl_push_eq_append' {l : List α} {xs : Array α} :
l.foldl (fun xs x => xs.push x) xs = xs ++ l.toArray := by
simpa using List.foldl_push_eq_append (f := id)
@[deprecated _root_.List.foldl_push_eq_append' (since := "2025-05-18")]
theorem _root_.List.foldl_push {l : List α} {as : Array α} : l.foldl Array.push as = as ++ l.toArray := by
induction l generalizing as <;> simp [*]
@[deprecated _root_.List.foldr_push_eq_append' (since := "2025-05-18")]
theorem _root_.List.foldr_push {l : List α} {as : Array α} : l.foldr (fun a bs => push bs a) as = as ++ l.reverse.toArray := by
rw [List.foldr_eq_foldl_reverse, List.foldl_push_eq_append']
@[simp, grind] theorem foldr_append_eq_append {xs : Array α} {f : α Array β} {ys : Array β} :
xs.foldr (f · ++ ·) ys = (xs.map f).flatten ++ ys := by
rcases xs with xs

View File

@@ -25,11 +25,6 @@ open Nat
/-! ## Monadic operations -/
@[simp] theorem map_toList_inj [Monad m] [LawfulMonad m]
{xs : m (Array α)} {ys : m (Array α)} :
toList <$> xs = toList <$> ys xs = ys :=
_root_.map_inj_right (by simp)
/-! ### mapM -/
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] {xs : Array α} {f : α β} :
@@ -39,11 +34,6 @@ open Nat
@[simp] theorem mapM_id {xs : Array α} {f : α Id β} : xs.mapM f = xs.map f :=
mapM_pure
@[simp] theorem mapM_map [Monad m] [LawfulMonad m] {f : α β} {g : β m γ} {xs : Array α} :
(xs.map f).mapM g = xs.mapM (g f) := by
rcases xs with xs
simp
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] {f : α m β} {xs ys : Array α} :
(xs ++ ys).mapM f = (return ( xs.mapM f) ++ ( ys.mapM f)) := by
rcases xs with xs

View File

@@ -8,9 +8,7 @@ module
prelude
import all Init.Data.Array.Basic
import Init.Data.Array.Lemmas
import Init.Data.Array.Monadic
import Init.Data.List.OfFn
import Init.Data.List.FinRange
/-!
# Theorems about `Array.ofFn`
@@ -21,8 +19,6 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/-! ### ofFn -/
@[simp] theorem ofFn_zero {f : Fin 0 α} : ofFn f = #[] := by
simp [ofFn, ofFn.go]
@@ -36,23 +32,12 @@ theorem ofFn_succ {f : Fin (n+1) → α} :
intro h₃
simp only [show i = n by omega]
theorem ofFn_add {n m} {f : Fin (n + m) α} :
ofFn f = (ofFn (fun i => f (i.castLE (Nat.le_add_right n m)))) ++ (ofFn (fun i => f (i.natAdd n))) := by
induction m with
| zero => simp
| succ m ih => simp [ofFn_succ, ih]
@[simp] theorem _root_.List.toArray_ofFn {f : Fin n α} : (List.ofFn f).toArray = Array.ofFn f := by
ext <;> simp
@[simp] theorem toList_ofFn {f : Fin n α} : (Array.ofFn f).toList = List.ofFn f := by
apply List.ext_getElem <;> simp
theorem ofFn_succ' {f : Fin (n+1) α} :
ofFn f = #[f 0] ++ ofFn (fun i => f i.succ) := by
apply Array.toList_inj.mp
simp [List.ofFn_succ]
@[simp]
theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by
rw [ Array.toList_inj]
@@ -67,71 +52,4 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
· rintro i, rfl
apply mem_of_getElem (i := i) <;> simp
/-! ### ofFnM -/
/-- Construct (in a monadic context) an array by applying a monadic function to each index. -/
def ofFnM {n} [Monad m] (f : Fin n m α) : m (Array α) :=
Fin.foldlM n (fun xs i => xs.push <$> f i) (Array.emptyWithCapacity n)
@[simp]
theorem ofFnM_zero [Monad m] {f : Fin 0 m α} : ofFnM f = pure #[] := by
simp [ofFnM]
theorem ofFnM_succ' {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let a f 0
let as ofFnM fun i => f i.succ
pure (#[a] ++ as)) := by
simp [ofFnM, Fin.foldlM_eq_foldlM_finRange, List.foldlM_push_eq_append, List.finRange_succ, Function.comp_def]
theorem ofFnM_succ {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let as ofFnM fun i => f i.castSucc
let a f (Fin.last n)
pure (as.push a)) := by
simp [ofFnM, Fin.foldlM_succ_last]
theorem ofFnM_add {n m} [Monad m] [LawfulMonad m] {f : Fin (n + k) m α} :
ofFnM f = (do
let as ofFnM fun i : Fin n => f (i.castLE (Nat.le_add_right n k))
let bs ofFnM fun i : Fin k => f (i.natAdd n)
pure (as ++ bs)) := by
induction k with
| zero => simp
| succ k ih =>
simp only [ofFnM_succ, Nat.add_eq, ih, Fin.castSucc_castLE, Fin.castSucc_natAdd, bind_pure_comp,
bind_assoc, bind_map_left, Fin.natAdd_last, map_bind, Functor.map_map]
congr 1
funext xs
congr 1
funext ys
congr 1
funext x
simp
@[simp] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
toList <$> ofFnM f = List.ofFnM f := by
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ, List.ofFnM_succ_last, ih]
@[simp]
theorem ofFnM_pure_comp [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (pure f) = (pure (ofFn f) : m (Array α)) := by
apply Array.map_toList_inj.mp
simp
-- Variant of `ofFnM_pure_comp` using a lambda.
-- This is not marked a `@[simp]` as it would match on every occurrence of `ofFnM`.
theorem ofFnM_pure [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (fun i => pure (f i)) = (pure (ofFn f) : m (Array α)) :=
ofFnM_pure_comp
@[simp, grind =] theorem idRun_ofFnM {f : Fin n Id α} :
Id.run (ofFnM f) = ofFn (fun i => Id.run (f i)) := by
unfold Id.run
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ', ofFn_succ', ih]
end Array

View File

@@ -100,11 +100,6 @@ Fin.foldrM n f xₙ = do
/-! ### foldlM -/
@[congr] theorem foldlM_congr [Monad m] {n k : Nat} (w : n = k) (f : α Fin n m α) :
foldlM n f = foldlM k (fun x i => f x (i.cast w.symm)) := by
subst w
rfl
theorem foldlM_loop_lt [Monad m] (f : α Fin n m α) (x) (h : i < n) :
foldlM.loop n f x i = f x i, h >>= (foldlM.loop n f . (i+1)) := by
rw [foldlM.loop, dif_pos h]
@@ -125,49 +120,14 @@ theorem foldlM_loop [Monad m] (f : α → Fin (n+1) → m α) (x) (h : i < n+1)
rw [foldlM_loop_eq, foldlM_loop_eq]
termination_by n - i
@[simp] theorem foldlM_zero [Monad m] (f : α Fin 0 m α) : foldlM 0 f = pure := by
funext x
exact foldlM_loop_eq ..
@[simp] theorem foldlM_zero [Monad m] (f : α Fin 0 m α) (x) : foldlM 0 f x = pure x :=
foldlM_loop_eq ..
theorem foldlM_succ [Monad m] (f : α Fin (n+1) m α) :
foldlM (n+1) f = fun x => f x 0 >>= foldlM n (fun x j => f x j.succ) := by
funext x
exact foldlM_loop ..
/-- Variant of `foldlM_succ` that splits off `Fin.last n` rather than `0`. -/
theorem foldlM_succ_last [Monad m] [LawfulMonad m] (f : α Fin (n+1) m α) :
foldlM (n+1) f = fun x => foldlM n (fun x j => f x j.castSucc) x >>= (f · (Fin.last n)) := by
funext x
induction n generalizing x with
| zero =>
simp [foldlM_succ]
| succ n ih =>
rw [foldlM_succ]
conv => rhs; rw [foldlM_succ]
simp only [castSucc_zero, castSucc_succ, bind_assoc]
congr 1
funext x
rw [ih]
simp
theorem foldlM_add [Monad m] [LawfulMonad m] (f : α Fin (n + k) m α) :
foldlM (n + k) f =
fun x => foldlM n (fun x i => f x (i.castLE (Nat.le_add_right n k))) x >>= foldlM k (fun x i => f x (i.natAdd n)) := by
induction k with
| zero =>
funext x
simp
| succ k ih =>
funext x
simp [foldlM_succ_last, Nat.add_assoc, ih]
theorem foldlM_succ [Monad m] (f : α Fin (n+1) m α) (x) :
foldlM (n+1) f x = f x 0 >>= foldlM n (fun x j => f x j.succ) := foldlM_loop ..
/-! ### foldrM -/
@[congr] theorem foldrM_congr [Monad m] {n k : Nat} (w : n = k) (f : Fin n α m α) :
foldrM n f = foldrM k (fun i => f (i.cast w.symm)) := by
subst w
rfl
theorem foldrM_loop_zero [Monad m] (f : Fin n α m α) (x) :
foldrM.loop n f 0, Nat.zero_le _ x = pure x := by
rw [foldrM.loop]
@@ -185,47 +145,19 @@ theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) → α → m α) (x
conv => rhs; rw [bind_pure (f 0 x)]
congr
funext
simp [foldrM_loop_zero]
try simp only [foldrM.loop] -- the try makes this proof work with and without opaque wf rec
| succ i ih =>
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
congr; funext; exact ih ..
@[simp] theorem foldrM_zero [Monad m] (f : Fin 0 α m α) : foldrM 0 f = pure := by
funext x
exact foldrM_loop_zero ..
@[simp] theorem foldrM_zero [Monad m] (f : Fin 0 α m α) (x) : foldrM 0 f x = pure x :=
foldrM_loop_zero ..
theorem foldrM_succ [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) :
foldrM (n+1) f = fun x => foldrM n (fun i => f i.succ) x >>= f 0 := by
funext x
exact foldrM_loop ..
theorem foldrM_succ_last [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) :
foldrM (n+1) f = fun x => f (Fin.last n) x >>= foldrM n (fun i => f i.castSucc) := by
funext x
induction n generalizing x with
| zero => simp [foldrM_succ]
| succ n ih =>
rw [foldrM_succ]
conv => rhs; rw [foldrM_succ]
simp [ih]
theorem foldrM_add [Monad m] [LawfulMonad m] (f : Fin (n + k) α m α) :
foldrM (n + k) f =
fun x => foldrM k (fun i => f (i.natAdd n)) x >>= foldrM n (fun i => f (i.castLE (Nat.le_add_right n k))) := by
induction k with
| zero =>
simp
| succ k ih =>
funext x
simp [foldrM_succ_last, Nat.add_assoc, ih]
theorem foldrM_succ [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) (x) :
foldrM (n+1) f x = foldrM n (fun i => f i.succ) x >>= f 0 := foldrM_loop ..
/-! ### foldl -/
@[congr] theorem foldl_congr {n k : Nat} (w : n = k) (f : α Fin n α) :
foldl n f = foldl k (fun x i => f x (i.cast w.symm)) := by
subst w
rfl
theorem foldl_loop_lt (f : α Fin n α) (x) (h : i < n) :
foldl.loop n f x i = foldl.loop n f (f x i, h) (i+1) := by
rw [foldl.loop, dif_pos h]
@@ -255,34 +187,14 @@ theorem foldl_succ_last (f : α → Fin (n+1) → α) (x) :
rw [foldl_succ]
induction n generalizing x with
| zero => simp [foldl_succ, Fin.last]
| succ n ih => rw [foldl_succ, ih (f · ·.succ), foldl_succ]; simp
theorem foldl_add (f : α Fin (n + m) α) (x) :
foldl (n + m) f x =
foldl m (fun x i => f x (i.natAdd n))
(foldl n (fun x i => f x (i.castLE (Nat.le_add_right n m))) x):= by
induction m with
| zero => simp
| succ m ih => simp [foldl_succ_last, ih, Nat.add_assoc]
| succ n ih => rw [foldl_succ, ih (f · ·.succ), foldl_succ]; simp [succ_castSucc]
theorem foldl_eq_foldlM (f : α Fin n α) (x) :
foldl n f x = foldlM (m:=Id) n f x := by
induction n generalizing x <;> simp [foldl_succ, foldlM_succ, *]
-- This is not marked `@[simp]` as it would match on every occurrence of `foldlM`.
theorem foldlM_pure [Monad m] [LawfulMonad m] {n} {f : α Fin n α} :
foldlM n (fun x i => pure (f x i)) x = (pure (foldl n f x) : m α) := by
induction n generalizing x with
| zero => simp
| succ n ih => simp [foldlM_succ, foldl_succ, ih]
/-! ### foldr -/
@[congr] theorem foldr_congr {n k : Nat} (w : n = k) (f : Fin n α α) :
foldr n f = foldr k (fun i => f (i.cast w.symm)) := by
subst w
rfl
theorem foldr_loop_zero (f : Fin n α α) (x) :
foldr.loop n f 0 (Nat.zero_le _) x = x := by
rw [foldr.loop]
@@ -308,15 +220,7 @@ theorem foldr_succ_last (f : Fin (n+1) → αα) (x) :
foldr (n+1) f x = foldr n (f ·.castSucc) (f (last n) x) := by
induction n generalizing x with
| zero => simp [foldr_succ, Fin.last]
| succ n ih => rw [foldr_succ, ih (f ·.succ), foldr_succ]; simp
theorem foldr_add (f : Fin (n + m) α α) (x) :
foldr (n + m) f x =
foldr n (fun i => f (i.castLE (Nat.le_add_right n m)))
(foldr m (fun i => f (i.natAdd n)) x) := by
induction m generalizing x with
| zero => simp
| succ m ih => simp [foldr_succ_last, ih, Nat.add_assoc]
| succ n ih => rw [foldr_succ, ih (f ·.succ), foldr_succ]; simp [succ_castSucc]
theorem foldr_eq_foldrM (f : Fin n α α) (x) :
foldr n f x = foldrM (m:=Id) n f x := by
@@ -334,11 +238,4 @@ theorem foldr_rev (f : α → Fin n → α) (x) :
| zero => simp
| succ n ih => rw [foldl_succ_last, foldr_succ, ih]; simp [rev_succ]
-- This is not marked `@[simp]` as it would match on every occurrence of `foldrM`.
theorem foldrM_pure [Monad m] [LawfulMonad m] {n} {f : Fin n α α} :
foldrM n (fun i x => pure (f i x)) x = (pure (foldr n f x) : m α) := by
induction n generalizing x with
| zero => simp
| succ n ih => simp [foldrM_succ, foldr_succ, ih]
end Fin

View File

@@ -646,20 +646,6 @@ theorem rev_castSucc (k : Fin n) : rev (castSucc k) = succ (rev k) := k.rev_cast
theorem rev_succ (k : Fin n) : rev (succ k) = castSucc (rev k) := k.rev_addNat 1
@[simp, grind _=_]
theorem castSucc_succ (i : Fin n) : i.succ.castSucc = i.castSucc.succ := rfl
@[simp, grind =]
theorem castLE_refl (h : n n) (i : Fin n) : i.castLE h = i := rfl
@[simp, grind =]
theorem castSucc_castLE (h : n m) (i : Fin n) :
(i.castLE h).castSucc = i.castLE (by omega) := rfl
@[simp, grind =]
theorem castSucc_natAdd (n : Nat) (i : Fin k) :
(i.natAdd n).castSucc = (i.castSucc).natAdd n := rfl
/-! ### pred -/
@[simp] theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl

View File

@@ -161,7 +161,8 @@ This function does not reduce in the kernel. It is compiled to the C inequality
match a, b with
| a, b => floatSpec.decLe a b
attribute [instance] Float.decLt Float.decLe
instance floatDecLt (a b : Float) : Decidable (a < b) := Float.decLt a b
instance floatDecLe (a b : Float) : Decidable (a b) := Float.decLe a b
/--
Converts a floating-point number to a string.

View File

@@ -145,7 +145,7 @@ Compares two floating point numbers for strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float32_decLt", instance] opaque Float32.decLt (a b : Float32) : Decidable (a < b) :=
@[extern "lean_float32_decLt"] opaque Float32.decLt (a b : Float32) : Decidable (a < b) :=
match a, b with
| a, b => float32Spec.decLt a b
@@ -154,10 +154,13 @@ Compares two floating point numbers for non-strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float32_decLe", instance] opaque Float32.decLe (a b : Float32) : Decidable (a b) :=
@[extern "lean_float32_decLe"] opaque Float32.decLe (a b : Float32) : Decidable (a b) :=
match a, b with
| a, b => float32Spec.decLe a b
instance float32DecLt (a b : Float32) : Decidable (a < b) := Float32.decLt a b
instance float32DecLe (a b : Float32) : Decidable (a b) := Float32.decLe a b
/--
Converts a floating-point number to a string.

View File

@@ -57,6 +57,9 @@ instance : Hashable UInt64 where
instance : Hashable USize where
hash n := n.toUInt64
instance : Hashable ByteArray where
hash as := as.foldl (fun r a => mixHash r (hash a)) 7
instance : Hashable (Fin n) where
hash v := v.val.toUInt64

View File

@@ -121,7 +121,7 @@ theorem toNat_lt_toNat {n m : Int} (hn : 0 < m) : n.toNat < m.toNat ↔ n < m :=
/-! ### min and max -/
@[simp] protected theorem min_assoc : (a b c : Int), min (min a b) c = min a (min b c) := by omega
instance : Std.Associative (α := Int) min := Int.min_assoc
instance : Std.Associative (α := Nat) min := Nat.min_assoc
@[simp] protected theorem min_self_assoc {m n : Int} : min m (min m n) = min m n := by
rw [ Int.min_assoc, Int.min_self]
@@ -130,7 +130,7 @@ instance : Std.Associative (α := Int) min := ⟨Int.min_assoc⟩
rw [Int.min_comm m n, Int.min_assoc, Int.min_self]
@[simp] protected theorem max_assoc (a b c : Int) : max (max a b) c = max a (max b c) := by omega
instance : Std.Associative (α := Int) max := Int.max_assoc
instance : Std.Associative (α := Nat) max := Nat.max_assoc
@[simp] protected theorem max_self_assoc {m n : Int} : max m (max m n) = max m n := by
rw [ Int.max_assoc, Int.max_self]

View File

@@ -6,8 +6,7 @@ Authors: François G. Dorais
module
prelude
import all Init.Data.List.OfFn
import Init.Data.List.Monadic
import Init.Data.List.OfFn
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -58,50 +57,3 @@ theorem finRange_reverse {n} : (finRange n).reverse = (finRange n).map Fin.rev :
simp [Fin.rev_succ]
end List
namespace Fin
theorem foldlM_eq_foldlM_finRange [Monad m] (f : α Fin n m α) (x : α) :
foldlM n f x = (List.finRange n).foldlM f x := by
induction n generalizing x with
| zero => simp
| succ n ih =>
simp [foldlM_succ, List.finRange_succ, List.foldlM_cons]
congr 1
funext y
simp [ih, List.foldlM_map]
theorem foldrM_eq_foldrM_finRange [Monad m] [LawfulMonad m] (f : Fin n α m α) (x : α) :
foldrM n f x = (List.finRange n).foldrM f x := by
induction n generalizing x with
| zero => simp
| succ n ih =>
simp [foldrM_succ, List.finRange_succ, ih, List.foldrM_map]
theorem foldl_eq_finRange_foldl (f : α Fin n α) (x : α) :
foldl n f x = (List.finRange n).foldl f x := by
induction n generalizing x with
| zero => simp
| succ n ih =>
simp [foldl_succ, List.finRange_succ, ih, List.foldl_map]
theorem foldr_eq_finRange_foldr (f : Fin n α α) (x : α) :
foldr n f x = (List.finRange n).foldr f x := by
induction n generalizing x with
| zero => simp
| succ n ih =>
simp [foldr_succ, List.finRange_succ, ih, List.foldr_map]
end Fin
namespace List
theorem ofFnM_succ {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let a f 0
let as ofFnM fun i => f i.succ
pure (a :: as)) := by
simp [ofFnM, Fin.foldlM_eq_foldlM_finRange, List.finRange_succ, List.foldlM_cons_eq_append,
List.foldlM_map]
end List

View File

@@ -2576,11 +2576,6 @@ theorem foldr_eq_foldrM {f : α → β → β} {b : β} {l : List α} :
l.foldl (fun xs y => f y :: xs) l' = (l.map f).reverse ++ l' := by
induction l generalizing l' <;> simp [*]
/-- Variant of `foldl_flip_cons_eq_append` specalized to `f = id`. -/
@[simp, grind] theorem foldl_flip_cons_eq_append' {l l' : List α} :
l.foldl (fun xs y => y :: xs) l' = l.reverse ++ l' := by
induction l generalizing l' <;> simp [*]
@[simp, grind] theorem foldr_append_eq_append {l : List α} {f : α List β} {l' : List β} :
l.foldr (f · ++ ·) l' = (l.map f).flatten ++ l' := by
induction l <;> simp [*]

View File

@@ -8,8 +8,6 @@ module
prelude
import Init.Data.List.TakeDrop
import Init.Data.List.Attach
import Init.Data.List.OfFn
import Init.Data.Array.Bootstrap
import all Init.Data.List.Control
/-!
@@ -71,17 +69,13 @@ theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] {f : α → m β} {l : List α}
@[simp] theorem mapM_id {l : List α} {f : α Id β} : l.mapM f = l.map f :=
mapM_pure
@[simp] theorem mapM_map [Monad m] [LawfulMonad m] {f : α β} {g : β m γ} {l : List α} :
(l.map f).mapM g = l.mapM (g f) := by
induction l <;> simp_all
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] {f : α m β} {l₁ l₂ : List α} :
(l₁ ++ l₂).mapM f = (return ( l₁.mapM f) ++ ( l₂.mapM f)) := by induction l₁ <;> simp [*]
/-- Auxiliary lemma for `mapM_eq_reverse_foldlM_cons`. -/
theorem foldlM_cons_eq_append [Monad m] [LawfulMonad m] {f : α m β} {as : List α} {b : β} {bs : List β} :
(as.foldlM (init := b :: bs) fun acc a => (· :: acc) <$> f a) =
(· ++ b :: bs) <$> as.foldlM (init := []) fun acc a => (· :: acc) <$> f a := by
(as.foldlM (init := b :: bs) fun acc a => return (( f a) :: acc)) =
(· ++ b :: bs) <$> as.foldlM (init := []) fun acc a => return (( f a) :: acc) := by
induction as generalizing b bs with
| nil => simp
| cons a as ih =>
@@ -89,7 +83,7 @@ theorem foldlM_cons_eq_append [Monad m] [LawfulMonad m] {f : α → m β} {as :
simp [ih, _root_.map_bind, Functor.map_map, Function.comp_def]
theorem mapM_eq_reverse_foldlM_cons [Monad m] [LawfulMonad m] {f : α m β} {l : List α} :
mapM f l = reverse <$> (l.foldlM (fun acc a => (· :: acc) <$> f a) []) := by
mapM f l = reverse <$> (l.foldlM (fun acc a => return (( f a) :: acc)) []) := by
rw [ mapM'_eq_mapM]
induction l with
| nil => simp

View File

@@ -27,13 +27,6 @@ Examples:
-/
def ofFn {n} (f : Fin n α) : List α := Fin.foldr n (f · :: ·) []
/--
Creates a list wrapped in a monad by applying the monadic function `f : Fin n → m α`
to each potential index in order, starting at `0`.
-/
def ofFnM {n} [Monad m] (f : Fin n m α) : m (List α) :=
List.reverse <$> Fin.foldlM n (fun xs i => (· :: xs) <$> f i) []
@[simp]
theorem length_ofFn {f : Fin n α} : (ofFn f).length = n := by
simp only [ofFn]
@@ -56,8 +49,7 @@ protected theorem getElem_ofFn {f : Fin n → α} (h : i < (ofFn f).length) :
simp_all
@[simp]
protected theorem getElem?_ofFn {f : Fin n α} :
(ofFn f)[i]? = if h : i < n then some (f i, h) else none :=
protected theorem getElem?_ofFn {f : Fin n α} : (ofFn f)[i]? = if h : i < n then some (f i, h) else none :=
if h : i < (ofFn f).length
then by
rw [getElem?_eq_getElem h, List.getElem_ofFn]
@@ -68,31 +60,16 @@ protected theorem getElem?_ofFn {f : Fin n → α} :
/-- `ofFn` on an empty domain is the empty list. -/
@[simp]
theorem ofFn_zero {f : Fin 0 α} : ofFn f = [] := by
rw [ofFn, Fin.foldr_zero]
theorem ofFn_zero {f : Fin 0 α} : ofFn f = [] :=
ext_get (by simp) (fun i hi₁ hi₂ => by contradiction)
@[simp]
theorem ofFn_succ {n} {f : Fin (n + 1) α} : ofFn f = f 0 :: ofFn fun i => f i.succ :=
ext_get (by simp) (fun i hi₁ hi₂ => by
cases i
· simp
· simp)
theorem ofFn_succ_last {n} {f : Fin (n + 1) α} :
ofFn f = (ofFn fun i => f i.castSucc) ++ [f (Fin.last n)] := by
induction n with
| zero => simp [ofFn_succ]
| succ n ih =>
rw [ofFn_succ]
conv => rhs; rw [ofFn_succ]
rw [ih]
simp
theorem ofFn_add {n m} {f : Fin (n + m) α} :
ofFn f = (ofFn fun i => f (i.castLE (Nat.le_add_right n m))) ++ (ofFn fun i => f (i.natAdd n)) := by
induction m with
| zero => simp
| succ m ih => simp [ofFn_succ_last, ih]
@[simp]
theorem ofFn_eq_nil_iff {f : Fin n α} : ofFn f = [] n = 0 := by
cases n <;> simp only [ofFn_zero, ofFn_succ, eq_self_iff_true, Nat.succ_ne_zero, reduceCtorEq]
@@ -115,66 +92,4 @@ theorem getLast_ofFn {n} {f : Fin n → α} (h : ofFn f ≠ []) :
(ofFn f).getLast h = f n - 1, Nat.sub_one_lt (mt ofFn_eq_nil_iff.2 h) := by
simp [getLast_eq_getElem, length_ofFn, List.getElem_ofFn]
/-- `ofFnM` on an empty domain is the empty list. -/
@[simp]
theorem ofFnM_zero [Monad m] [LawfulMonad m] {f : Fin 0 m α} : ofFnM f = pure [] := by
simp [ofFnM]
/-! See `Init.Data.List.FinRange` for the `ofFnM_succ` variant. -/
theorem ofFnM_succ_last {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let as ofFnM fun i => f i.castSucc
let a f (Fin.last n)
pure (as ++ [a])) := by
simp [ofFnM, Fin.foldlM_succ_last]
theorem ofFnM_add {n m} [Monad m] [LawfulMonad m] {f : Fin (n + k) m α} :
ofFnM f = (do
let as ofFnM fun i : Fin n => f (i.castLE (Nat.le_add_right n k))
let bs ofFnM fun i : Fin k => f (i.natAdd n)
pure (as ++ bs)) := by
induction k with
| zero => simp
| succ k ih => simp [ofFnM_succ_last, ih]
end List
namespace Fin
theorem foldl_cons_eq_append {f : Fin n α} {xs : List α} :
Fin.foldl n (fun xs i => f i :: xs) xs = (List.ofFn f).reverse ++ xs := by
induction n generalizing xs with
| zero => simp
| succ n ih => simp [Fin.foldl_succ, List.ofFn_succ, ih]
theorem foldr_cons_eq_append {f : Fin n α} {xs : List α} :
Fin.foldr n (fun i xs => f i :: xs) xs = List.ofFn f ++ xs:= by
induction n generalizing xs with
| zero => simp
| succ n ih => simp [Fin.foldr_succ, List.ofFn_succ, ih]
end Fin
namespace List
@[simp]
theorem ofFnM_pure_comp [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (pure f) = (pure (ofFn f) : m (List α)) := by
simp [ofFnM, Fin.foldlM_pure, Fin.foldl_cons_eq_append]
-- Variant of `ofFnM_pure_comp` using a lambda.
-- This is not marked a `@[simp]` as it would match on every occurrence of `ofFnM`.
theorem ofFnM_pure [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (fun i => pure (f i)) = (pure (ofFn f) : m (List α)) :=
ofFnM_pure_comp
@[simp, grind =] theorem idRun_ofFnM {f : Fin n Id α} :
Id.run (ofFnM f) = ofFn (fun i => Id.run (f i)) := by
unfold Id.run
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ_last, ofFn_succ_last, ih]
end List

View File

@@ -210,6 +210,12 @@ theorem forM_toArray [Monad m] (l : List α) (f : α → m PUnit) :
cases as
simp
@[simp] theorem foldl_push {l : List α} {as : Array α} : l.foldl Array.push as = as ++ l.toArray := by
induction l generalizing as <;> simp [*]
@[simp] theorem foldr_push {l : List α} {as : Array α} : l.foldr (fun a bs => push bs a) as = as ++ l.reverse.toArray := by
rw [foldr_eq_foldl_reverse, foldl_push]
@[simp, grind =] theorem findSomeM?_toArray [Monad m] [LawfulMonad m] (f : α m (Option β)) (l : List α) :
l.toArray.findSomeM? f = l.findSomeM? f := by
rw [Array.findSomeM?]

View File

@@ -197,8 +197,6 @@ theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bo
omega
go n 0 f
/-! ### `fold` -/
@[simp] theorem fold_zero {α : Type u} (f : (i : Nat) i < 0 α α) (init : α) :
fold 0 f init = init := by simp [fold]
@@ -212,8 +210,6 @@ theorem fold_eq_finRange_foldl {α : Type u} (n : Nat) (f : (i : Nat) → i < n
| succ n ih =>
simp [ih, List.finRange_succ_last, List.foldl_map]
/-! ### `foldRev` -/
@[simp] theorem foldRev_zero {α : Type u} (f : (i : Nat) i < 0 α α) (init : α) :
foldRev 0 f init = init := by simp [foldRev]
@@ -227,12 +223,10 @@ theorem foldRev_eq_finRange_foldr {α : Type u} (n : Nat) (f : (i : Nat) → i <
| zero => simp
| succ n ih => simp [ih, List.finRange_succ_last, List.foldr_map]
/-! ### `any` -/
@[simp] theorem any_zero {f : (i : Nat) i < 0 Bool} : any 0 f = false := by simp [any]
@[simp] theorem any_succ {n : Nat} (f : (i : Nat) i < n + 1 Bool) :
any (n + 1) f = (any n (fun i h => f i (by omega)) || f n (by omega)) := by simp [any]
any (n + 1) f = (any n (fun i h => f i (by omega)) || f n (by omega)) := by simp [any]
theorem any_eq_finRange_any {n : Nat} (f : (i : Nat) i < n Bool) :
any n f = (List.finRange n).any (fun i, h => f i h) := by
@@ -240,12 +234,10 @@ theorem any_eq_finRange_any {n : Nat} (f : (i : Nat) → i < n → Bool) :
| zero => simp
| succ n ih => simp [ih, List.finRange_succ_last, List.any_map, Function.comp_def]
/-! ### `all` -/
@[simp] theorem all_zero {f : (i : Nat) i < 0 Bool} : all 0 f = true := by simp [all]
@[simp] theorem all_succ {n : Nat} (f : (i : Nat) i < n + 1 Bool) :
all (n + 1) f = (all n (fun i h => f i (by omega)) && f n (by omega)) := by simp [all]
all (n + 1) f = (all n (fun i h => f i (by omega)) && f n (by omega)) := by simp [all]
theorem all_eq_finRange_all {n : Nat} (f : (i : Nat) i < n Bool) :
all n f = (List.finRange n).all (fun i, h => f i h) := by
@@ -258,7 +250,7 @@ end Nat
namespace Prod
/--
Combines an initial value with each natural number from a range, in increasing order.
Combines an initial value with each natural number from in a range, in increasing order.
In particular, `(start, stop).foldI f init` applies `f`on all the numbers
from `start` (inclusive) to `stop` (exclusive) in increasing order:
@@ -268,7 +260,7 @@ Examples:
* `(5, 8).foldI (fun j _ _ xs => xs.push j) #[] = #[5, 6, 7]`
* `(5, 8).foldI (fun j _ _ xs => toString j :: xs) [] = ["7", "6", "5"]`
-/
@[inline, simp] def foldI {α : Type u} (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 α α) (init : α) : α :=
@[inline] def foldI {α : Type u} (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 α α) (init : α) : α :=
(i.2 - i.1).fold (fun j _ => f (i.1 + j) (by omega) (by omega)) init
/--
@@ -282,7 +274,7 @@ Examples:
* `(5, 8).anyI (fun j _ _ => j % 2 = 0) = true`
* `(6, 6).anyI (fun j _ _ => j % 2 = 0) = false`
-/
@[inline, simp] def anyI (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 Bool) : Bool :=
@[inline] def anyI (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 Bool) : Bool :=
(i.2 - i.1).any (fun j _ => f (i.1 + j) (by omega) (by omega))
/--
@@ -296,7 +288,7 @@ Examples:
* `(5, 8).allI (fun j _ _ => j % 2 = 0) = false`
* `(6, 7).allI (fun j _ _ => j % 2 = 0) = true`
-/
@[inline, simp] def allI (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 Bool) : Bool :=
@[inline] def allI (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 Bool) : Bool :=
(i.2 - i.1).all (fun j _ => f (i.1 + j) (by omega) (by omega))
end Prod

View File

@@ -8,37 +8,9 @@ module
prelude
import Init.Data.Array.Lemmas
import Init.Data.Option.List
import all Init.Data.Option.Instances
namespace Option
@[simp, grind] theorem mem_toArray {a : α} {o : Option α} : a o.toArray o = some a := by
cases o <;> simp [eq_comm]
@[simp, grind] theorem forIn'_toArray [Monad m] (o : Option α) (b : β) (f : (a : α) a o.toArray β m (ForInStep β)) :
forIn' o.toArray b f = forIn' o b fun a m b => f a (by simpa using m) b := by
cases o <;> simp <;> rfl
@[simp, grind] theorem forIn_toArray [Monad m] (o : Option α) (b : β) (f : α β m (ForInStep β)) :
forIn o.toArray b f = forIn o b f := by
cases o <;> simp <;> rfl
@[simp, grind] theorem foldlM_toArray [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : α β m α) :
o.toArray.foldlM f a = o.elim (pure a) (fun b => f a b) := by
cases o <;> simp
@[simp, grind] theorem foldrM_toArray [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : β α m α) :
o.toArray.foldrM f a = o.elim (pure a) (fun b => f b a) := by
cases o <;> simp
@[simp, grind] theorem foldl_toArray (o : Option β) (a : α) (f : α β α) :
o.toArray.foldl f a = o.elim a (fun b => f a b) := by
cases o <;> simp
@[simp, grind] theorem foldr_toArray (o : Option β) (a : α) (f : β α α) :
o.toArray.foldr f a = o.elim a (fun b => f b a) := by
cases o <;> simp
@[simp]
theorem toList_toArray {o : Option α} : o.toArray.toList = o.toList := by
cases o <;> simp
@@ -51,47 +23,4 @@ theorem toArray_filter {o : Option α} {p : α → Bool} :
(o.filter p).toArray = o.toArray.filter p := by
rw [ toArray_toList, toList_filter, List.filter_toArray, toArray_toList]
theorem toArray_bind {o : Option α} {f : α Option β} :
(o.bind f).toArray = o.toArray.flatMap (Option.toArray f) := by
cases o <;> simp
theorem toArray_join {o : Option (Option α)} : o.join.toArray = o.toArray.flatMap Option.toArray := by
simp [toArray_bind, bind_id_eq_join]
theorem toArray_map {o : Option α} {f : α β} : (o.map f).toArray = o.toArray.map f := by
cases o <;> simp
theorem toArray_min [Min α] {o o' : Option α} :
(min o o').toArray = o.toArray.zipWith min o'.toArray := by
cases o <;> cases o' <;> simp
@[simp]
theorem size_toArray_le {o : Option α} : o.toArray.size 1 := by
cases o <;> simp
theorem size_toArray_eq_ite {o : Option α} :
o.toArray.size = if o.isSome then 1 else 0 := by
cases o <;> simp
@[simp]
theorem toArray_eq_empty_iff {o : Option α} : o.toArray = #[] o = none := by
cases o <;> simp
@[simp]
theorem toArray_eq_singleton_iff {o : Option α} : o.toArray = #[a] o = some a := by
cases o <;> simp
@[simp]
theorem size_toArray_eq_zero_iff {o : Option α} :
o.toArray.size = 0 o = none := by
cases o <;> simp
@[simp]
theorem size_toArray_eq_one_iff {o : Option α} :
o.toArray.size = 1 o.isSome := by
cases o <;> simp
theorem size_toArray_choice_eq_one [Nonempty α] : (choice α).toArray.size = 1 := by
simp
end Option

View File

@@ -8,16 +8,11 @@ module
prelude
import Init.Data.Option.Basic
import Init.Data.Option.List
import Init.Data.Option.Array
import Init.Data.Array.Attach
import Init.Data.List.Attach
import Init.BinderPredicates
namespace Option
instance {o : Option α} : Subsingleton { x // o = some x } where
allEq a b := Subtype.ext (Option.some.inj (a.property.symm.trans b.property))
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
`Option {x // P x}` is the same as the input `Option α`.
@@ -91,7 +86,7 @@ theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a,
(o.attachWith p H).map Subtype.val = o :=
(attachWith_map_val _ _ _).trans (congrFun Option.map_id _)
theorem attach_eq_some : (o : Option α) (x : {x // o = some x}), o.attach = some x
theorem attach_eq_some : (o : Option a) (x : {x // o = some x}), o.attach = some x
| none, x, h => by simp at h
| some a, x, h => by simpa using h
@@ -128,41 +123,20 @@ theorem mem_attach : ∀ (o : Option α) (x : {x // o = some x}), x ∈ o.attach
cases o <;> cases x <;> simp
@[simp] theorem get_attach {o : Option α} (h : o.attach.isSome = true) :
o.attach.get h = o.get (by simpa using h), by simp :=
Subsingleton.elim _ _
@[simp] theorem getD_attach {o : Option α} {fallback} :
o.attach.getD fallback = fallback :=
Subsingleton.elim _ _
@[simp] theorem get!_attach {o : Option α} [Inhabited { x // o = some x }] :
o.attach.get! = default :=
Subsingleton.elim _ _
o.attach.get h = o.get (by simpa using h), by simp := by
cases o
· simp at h
· simp [get_some]
@[simp] theorem get_attachWith {p : α Prop} {o : Option α} (H : a, o = some a p a) (h : (o.attachWith p H).isSome) :
(o.attachWith p H).get h = o.get (by simpa using h), H _ (by simp) := by
cases o <;> simp
@[simp] theorem getD_attachWith {p : α Prop} {o : Option α} {h} {fallback} :
(o.attachWith p h).getD fallback =
o.getD fallback.1, by cases o <;> (try exact fallback.2) <;> exact h _ (by simp) := by
cases o <;> simp
cases o
· simp at h
· simp [get_some]
theorem toList_attach (o : Option α) :
o.attach.toList = o.toList.attach.map fun x => x.1, by simpa using x.2 := by
cases o <;> simp
theorem toList_attachWith {p : α Prop} {o : Option α} {h} :
(o.attachWith p h).toList = o.toList.attach.map fun x => x.1, h _ (by simpa using x.2) := by
cases o <;> simp
theorem toArray_attach (o : Option α) :
o.attach.toArray = o.toArray.attach.map fun x => x.1, by simpa using x.2 := by
cases o <;> simp
theorem toArray_attachWith {p : α Prop} {o : Option α} {h} :
(o.attachWith p h).toArray = o.toArray.attach.map fun x => x.1, h _ (by simpa using x.2) := by
cases o <;> simp
o.attach.toList = o.toList.attach.map fun x, h => x, by simpa using h := by
cases o <;> simp [toList]
@[simp, grind =] theorem attach_toList (o : Option α) :
o.toList.attach = (o.attach.map fun a, h => a, by simpa using h).toList := by
@@ -229,11 +203,6 @@ theorem attach_filter {o : Option α} {p : α → Bool} :
· rintro h, rfl
simp [h]
theorem filter_attachWith {P : α Prop} {o : Option α} {h : x, o = some x P x} {q : α Bool} :
(o.attachWith P h).filter q =
(o.filter q).attachWith P (fun _ h' => h _ (eq_some_of_filter_eq_some h')) := by
cases o <;> simp [filter_some] <;> split <;> simp
theorem filter_attach {o : Option α} {p : {x // o = some x} Bool} :
o.attach.filter p = o.pbind fun a h => if p a, h then some a, h else none := by
cases o <;> simp [filter_some]
@@ -242,64 +211,6 @@ theorem toList_pbind {o : Option α} {f : (a : α) → o = some a → Option β}
(o.pbind f).toList = o.attach.toList.flatMap (fun x, h => (f x h).toList) := by
cases o <;> simp
theorem toArray_pbind {o : Option α} {f : (a : α) o = some a Option β} :
(o.pbind f).toArray = o.attach.toArray.flatMap (fun x, h => (f x h).toArray) := by
cases o <;> simp
theorem toList_pfilter {o : Option α} {p : (a : α) o = some a Bool} :
(o.pfilter p).toList = (o.toList.attach.filter (fun x => p x.1 (by simpa using x.2))).unattach := by
cases o with
| none => simp
| some a =>
simp only [pfilter_some, toList_some, List.attach_cons, List.attach_nil, List.map_nil]
split <;> rename_i h
· rw [List.filter_cons_of_pos h]; simp
· rw [List.filter_cons_of_neg h]; simp
theorem toArray_pfilter {o : Option α} {p : (a : α) o = some a Bool} :
(o.pfilter p).toArray = (o.toArray.attach.filter (fun x => p x.1 (by simpa using x.2))).unattach := by
cases o with
| none => simp
| some a =>
simp only [pfilter_some, toArray_some, List.attach_toArray, List.attachWith_mem_toArray,
List.attach_cons, List.attach_nil, List.map_nil, List.map_cons, List.size_toArray,
List.length_cons, List.length_nil, Nat.zero_add, List.filter_toArray', List.unattach_toArray]
split <;> rename_i h
· rw [List.filter_cons_of_pos h]; simp
· rw [List.filter_cons_of_neg h]; simp
theorem toList_pmap {p : α Prop} {o : Option α} {f : (a : α) p a β}
(h : a, o = some a p a) :
(o.pmap f h).toList = o.attach.toList.map (fun x => f x.1 (h _ x.2)) := by
cases o <;> simp
theorem toArray_pmap {p : α Prop} {o : Option α} {f : (a : α) p a β}
(h : a, o = some a p a) :
(o.pmap f h).toArray = o.attach.toArray.map (fun x => f x.1 (h _ x.2)) := by
cases o <;> simp
theorem attach_pfilter {o : Option α} {p : (a : α) o = some a Bool} :
(o.pfilter p).attach =
o.attach.pbind fun x h => if h' : p x (by simp_all) then
some x.1, by simpa [pfilter_eq_some_iff] using _, h' else none := by
cases o with
| none => simp
| some a =>
simp only [attach_some, eq_mp_eq_cast, id_eq, pbind_some]
rw [attach_congr pfilter_some]
split <;> simp [*]
theorem attach_guard {p : α Bool} {x : α} :
(guard p x).attach = if h : p x then some x, by simp_all else none := by
simp only [guard_eq_ite]
split <;> simp [*]
theorem attachWith_guard {q : α Bool} {x : α} {P : α Prop}
{h : a, guard q x = some a P a} :
(guard q x).attachWith P h = if h' : q x then some x, h _ (by simp_all) else none := by
simp only [guard_eq_ite]
split <;> simp [*]
/-! ## unattach
`Option.unattach` is the (one-sided) inverse of `Option.attach`. It is a synonym for `Option.map Subtype.val`.
@@ -344,29 +255,6 @@ def unattach {α : Type _} {p : α → Prop} (o : Option { x // p x }) := o.map
(o.attachWith p H).unattach = o := by
cases o <;> simp
theorem unattach_eq_some_iff {p : α Prop} {o : Option { x // p x }} {x : α} :
o.unattach = some x h, o = some x, h :=
match o with
| none => by simp
| some y, h => by simpa using fun h' => h' h
@[simp]
theorem unattach_eq_none_iff {p : α Prop} {o : Option { x // p x }} :
o.unattach = none o = none := by
cases o <;> simp
theorem get_unattach {p : α Prop} {o : Option { x // p x }} {h} :
o.unattach.get h = (o.get (by simpa using h)).1 := by
cases o <;> simp
theorem toList_unattach {p : α Prop} {o : Option { x // p x }} :
o.unattach.toList = o.toList.unattach := by
cases o <;> simp
theorem toArray_unattach {p : α Prop} {o : Option { x // p x }} :
o.unattach.toArray = o.toArray.unattach := by
cases o <;> simp
/-! ### Recognizing higher order functions on subtypes using a function that only depends on the value. -/
/--
@@ -391,51 +279,4 @@ and simplifies these to the function directly taking the value.
· simp only [filter_some, hf, unattach_some]
split <;> simp
@[simp] theorem unattach_guard {p : α Prop} {q : { x // p x } Bool} {r : α Bool}
(hq : x h, q x, h = r x) {x : { x // p x }} :
(guard q x).unattach = guard r x.1 := by
simp only [guard]
split <;> simp_all
@[simp] theorem unattach_pfilter {p : α Prop} {o : Option { x // p x }}
{f : (a : { x // p x }) o = some a Bool}
{g : (a : α) o.unattach = some a Bool} (hf : x h h', f x, h h' = g x (by simp_all)) :
(o.pfilter f).unattach = o.unattach.pfilter g := by
cases o with
| none => simp
| some a =>
simp only [hf, pfilter_some, unattach_some]
split <;> simp
@[simp] theorem unattach_merge {p : α Prop} {f : { x // p x } { x // p x } { x // p x }}
{g : α α α} (hf : x h y h', (f x, h y, h').1 = g x y) {o o' : Option { x // p x }} :
(o.merge f o').unattach = o.unattach.merge g o'.unattach := by
cases o <;> cases o' <;> simp [*]
theorem any_attach {p : α Bool} {o : Option α} {q : { x // o = some x } Bool}
(h : x h, q x, h = p x) : o.attach.any q = o.any p := by
cases o <;> simp [*]
theorem any_attachWith {p : α Bool} {o : Option α} {r : α Prop} (hr : x, o = some x r x)
{q : { x // r x } Bool}
(h : x h, q x, h = p x) : (o.attachWith r hr).any q = o.any p := by
cases o <;> simp [*]
theorem any_unattach {p : α Prop} {o : Option { x // p x }} {q : α Bool} :
o.unattach.any q = o.any (q Subtype.val) := by
cases o <;> simp
theorem all_attach {p : α Bool} {o : Option α} {q : { x // o = some x } Bool}
(h : x h, q x, h = p x) : o.attach.all q = o.all p := by
cases o <;> simp [*]
theorem all_attachWith {p : α Bool} {o : Option α} {r : α Prop} (hr : x, o = some x r x)
{q : { x // r x } Bool}
(h : x h, q x, h = p x) : (o.attachWith r hr).all q = o.all p := by
cases o <;> simp [*]
theorem all_unattach {p : α Prop} {o : Option { x // p x }} {q : α Bool} :
o.unattach.all q = o.all (q Subtype.val) := by
cases o <;> simp
end Option

View File

@@ -102,9 +102,11 @@ From the perspective of `Option` as a collection with at most one element, the m
is applied to the element if present, and the final result is empty if either the initial or the
resulting collections are empty.
-/
@[inline] protected def bindM [Pure m] (f : α m (Option β)) : Option α m (Option β)
| none => pure none
| some a => f a
@[inline] protected def bindM [Monad m] (f : α m (Option β)) (o : Option α) : m (Option β) := do
if let some a := o then
return ( f a)
else
return none
/--
Applies a function in some applicative functor to an optional value, returning `none` with no

View File

@@ -14,8 +14,6 @@ import Init.Ext
namespace Option
theorem default_eq_none : (default : Option α) = none := rfl
@[deprecated mem_def (since := "2025-04-07")]
theorem mem_iff {a : α} {b : Option α} : a b b = some a := .rfl
@@ -151,22 +149,6 @@ theorem not_isSome_iff_eq_none : ¬o.isSome ↔ o = none := by
theorem ne_none_iff_isSome : o none o.isSome := by cases o <;> simp
@[simp]
theorem any_true {o : Option α} : o.any (fun _ => true) = o.isSome := by
cases o <;> simp
@[simp]
theorem any_false {o : Option α} : o.any (fun _ => false) = false := by
cases o <;> simp
@[simp]
theorem all_true {o : Option α} : o.all (fun _ => true) = true := by
cases o <;> simp
@[simp]
theorem all_false {o : Option α} : o.all (fun _ => false) = o.isNone := by
cases o <;> simp
theorem ne_none_iff_exists : o none x, some x = o := by cases o <;> simp
theorem ne_none_iff_exists' : o none x, o = some x :=
@@ -194,6 +176,8 @@ abbrev ball_ne_none := @forall_ne_none
@[simp, grind] theorem bind_eq_bind : bind = @Option.bind α β := rfl
@[simp, grind] theorem orElse_eq_orElse : HOrElse.hOrElse = @Option.orElse α := rfl
@[simp, grind] theorem bind_fun_some (x : Option α) : x.bind some = x := by cases x <;> rfl
@[simp] theorem bind_fun_none (x : Option α) : x.bind (fun _ => none (α := β)) = none := by
@@ -254,18 +238,10 @@ theorem isSome_apply_of_isSome_bind {α β : Type _} {x : Option α} {f : α
(isSome_apply_of_isSome_bind h) := by
cases x <;> trivial
theorem any_bind {p : β Bool} {f : α Option β} {o : Option α} :
(o.bind f).any p = o.any (Option.any p f) := by
cases o <;> simp
theorem all_bind {p : β Bool} {f : α Option β} {o : Option α} :
(o.bind f).all p = o.all (Option.all p f) := by
cases o <;> simp
@[grind] theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl
theorem join_eq_bind_id {x : Option (Option α)} : x.join = x.bind id := rfl
theorem join_eq_some_iff : x.join = some a x = some (some a) := by
simp [ bind_id_eq_join, bind_eq_some_iff]
simp [join_eq_bind_id, bind_eq_some_iff]
@[deprecated join_eq_some_iff (since := "2025-04-10")]
abbrev join_eq_some := @join_eq_some_iff
@@ -277,14 +253,12 @@ theorem join_ne_none' : ¬x.join = none ↔ ∃ z, x = some (some z) :=
join_ne_none
theorem join_eq_none_iff : o.join = none o = none o = some none :=
match o with | none | some none | some (some _) => by simp [bind_id_eq_join]
match o with | none | some none | some (some _) => by simp [join_eq_bind_id]
@[deprecated join_eq_none_iff (since := "2025-04-10")]
abbrev join_eq_none := @join_eq_none_iff
theorem bind_join {f : α Option β} {o : Option (Option α)} :
o.join.bind f = o.bind (·.bind f) := by
cases o <;> simp
@[grind] theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl
@[simp, grind] theorem map_eq_map : Functor.map f = Option.map f := rfl
@@ -421,15 +395,9 @@ theorem mem_filter_iff {p : α → Bool} {a : α} {o : Option α} :
a o.filter p a o p a := by
simp
@[grind]
theorem bind_guard (x : Option α) (p : α Bool) :
x.bind (Option.guard p) = x.filter p := by
cases x <;> rfl
@[deprecated bind_guard (since := "2025-05-15")]
theorem filter_eq_bind (x : Option α) (p : α Bool) :
x.filter p = x.bind (Option.guard p) :=
(bind_guard x p).symm
x.filter p = x.bind (Option.guard p) := by
cases x <;> rfl
@[simp] theorem any_filter : (o : Option α)
(Option.filter p o).any q = Option.any (fun a => p a && q a) o
@@ -531,10 +499,6 @@ theorem any_eq_false_iff_get (p : α → Bool) (x : Option α) :
theorem isSome_of_any {x : Option α} {p : α Bool} (h : x.any p) : x.isSome := by
cases x <;> trivial
theorem get_of_any_eq_true (p : α Bool) (x : Option α) (h : x.any p = true) :
p (x.get (isSome_of_any h)) :=
any_eq_true_iff_get p x |>.1 h |>.2
@[grind]
theorem any_map {α β : Type _} {x : Option α} {f : α β} {p : β Bool} :
(x.map f).any p = x.any (fun a => p (f a)) := by
@@ -563,39 +527,29 @@ theorem bind_map_comm {α β} {x : Option (Option α)} {f : α → β} :
theorem mem_of_mem_join {a : α} {x : Option (Option α)} (h : a x.join) : some a x :=
h.symm join_eq_some_iff.1 h
theorem any_join {p : α Bool} {x : Option (Option α)} :
x.join.any p = x.any (Option.any p) := by
@[deprecated orElse_some (since := "2025-05-03")]
theorem some_orElse (a : α) (f) : (some a).orElse f = some a := rfl
@[deprecated orElse_none (since := "2025-05-03")]
theorem none_orElse (f : Unit Option α) : none.orElse f = f () := rfl
@[simp] theorem orElse_fun_none (x : Option α) : x.orElse (fun _ => none) = x := by cases x <;> rfl
@[simp] theorem orElse_fun_some (x : Option α) (a : α) :
x.orElse (fun _ => some a) = some (x.getD a) := by
cases x <;> simp
theorem all_join {p : α Bool} {x : Option (Option α)} :
x.join.all p = x.all (Option.all p) := by
theorem orElse_eq_some_iff (o : Option α) (f) (x : α) :
(o.orElse f) = some x o = some x o = none f () = some x := by
cases o <;> simp
theorem orElse_eq_none_iff (o : Option α) (f) : (o.orElse f) = none o = none f () = none := by
cases o <;> simp
@[grind] theorem map_orElse {x : Option α} {y} :
(x.orElse y).map f = (x.map f).orElse (fun _ => (y ()).map f) := by
cases x <;> simp
theorem isNone_join {x : Option (Option α)} : x.join.isNone = x.all Option.isNone := by
cases x <;> simp
theorem isSome_join {x : Option (Option α)} : x.join.isSome = x.any Option.isSome := by
cases x <;> simp
theorem get_join {x : Option (Option α)} {h} : x.join.get h =
(x.get (Option.isSome_of_any (Option.isSome_join h))).get (get_of_any_eq_true _ _ (Option.isSome_join h)) := by
cases x with
| none => simp at h
| some _ => simp
theorem join_eq_get {x : Option (Option α)} {h} : x.join = x.get h := by
cases x with
| none => simp at h
| some _ => simp
theorem getD_join {x : Option (Option α)} {default : α} :
x.join.getD default = (x.getD (some default)).getD default := by
cases x <;> simp
theorem get!_join [Inhabited α] {x : Option (Option α)} :
x.join.get! = x.get!.get! := by
cases x <;> simp [default_eq_none]
@[simp] theorem guard_eq_some_iff : guard p a = some b a = b p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@@ -608,9 +562,6 @@ abbrev guard_eq_some := @guard_eq_some_iff
@[deprecated isSome_guard (since := "2025-03-18")]
abbrev guard_isSome := @isSome_guard
@[simp] theorem isNone_guard : (Option.guard p a).isNone = !p a := by
rw [ not_isSome, isSome_guard]
@[simp] theorem guard_eq_none_iff : Option.guard p a = none p a = false :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@@ -636,27 +587,19 @@ theorem guard_comp {p : α → Bool} {f : β → α} :
ext1 b
simp [guard]
theorem get_none (a : α) {h} : none.get h = a := by
simp at h
@[grind] theorem bind_guard (x : Option α) (p : α Bool) :
x.bind (Option.guard p) = x.filter p := by
simp only [Option.filter_eq_bind, decide_eq_true_eq]
@[simp]
theorem get_none_eq_iff_true {h} : (none : Option α).get h = a True := by
simp at h
theorem get_guard : (guard p a).get h = a := by
simp only [guard]
split <;> simp
@[grind]
theorem guard_def (p : α Bool) :
Option.guard p = fun x => if p x then some x else none := rfl
@[deprecated guard_def (since := "2025-05-15")]
theorem guard_eq_map (p : α Bool) :
Option.guard p = fun x => Option.map (fun _ => x) (if p x then some x else none) := by
funext x
simp [Option.guard]
@[grind]
theorem guard_def (p : α Bool) :
Option.guard p = fun x => if p x then some x else none := rfl
theorem guard_eq_ite {p : α Bool} {x : α} :
Option.guard p x = if p x then some x else none := rfl
@@ -668,10 +611,6 @@ theorem guard_eq_filter {p : α → Bool} {x : α} :
rw [guard_eq_ite]
split <;> simp_all [filter_some, guard_eq_ite]
theorem map_guard {p : α Bool} {f : α β} {x : α} :
(Option.guard p x).map f = if p x then some (f x) else none := by
simp [guard_eq_ite]
theorem join_filter {p : Option α Bool} : {o : Option (Option α)}
(o.filter p).join = o.join.filter (fun a => p (some a))
| none => by simp
@@ -741,44 +680,6 @@ instance lawfulIdentity_merge (f : ααα) : Std.LawfulIdentity (merge
left_id a := by cases a <;> simp [merge]
right_id a := by cases a <;> simp [merge]
theorem merge_join {o o' : Option (Option α)} {f : α α α} :
o.join.merge f o'.join = (o.merge (Option.merge f) o').join := by
cases o <;> cases o' <;> simp
theorem merge_eq_some_iff {o o' : Option α} {f : α α α} {a : α} :
o.merge f o' = some a (o = some a o' = none) (o = none o' = some a)
( b c, o = some b o' = some c f b c = a) := by
cases o <;> cases o' <;> simp
@[simp]
theorem merge_eq_none_iff {o o' : Option α} : o.merge f o' = none o = none o' = none := by
cases o <;> cases o' <;> simp
@[simp]
theorem any_merge {p : α Bool} {f : α α α} (hpf : a b, p (f a b) = (p a || p b))
{o o' : Option α} : (o.merge f o').any p = (o.any p || o'.any p) := by
cases o <;> cases o' <;> simp [*]
@[simp]
theorem all_merge {p : α Bool} {f : α α α} (hpf : a b, p (f a b) = (p a && p b))
{o o' : Option α} : (o.merge f o').all p = (o.all p && o'.all p) := by
cases o <;> cases o' <;> simp [*]
@[simp]
theorem isSome_merge {o o' : Option α} {f : α α α} :
(o.merge f o').isSome = (o.isSome || o'.isSome) := by
simp [ any_true]
@[simp]
theorem isNone_merge {o o' : Option α} {f : α α α} :
(o.merge f o').isNone = (o.isNone && o'.isNone) := by
simp [ all_false]
@[simp]
theorem get_merge {o o' : Option α} {f : α α α} {i : α} [Std.LawfulIdentity f i] {h} :
(o.merge f o').get h = f (o.getD i) (o'.getD i) := by
cases o <;> cases o' <;> simp [Std.LawfulLeftIdentity.left_id, Std.LawfulRightIdentity.right_id]
@[simp, grind] theorem elim_none (x : β) (f : α β) : none.elim x f = x := rfl
@[simp, grind] theorem elim_some (x : β) (f : α β) (a : α) : (some a).elim x f = f a := rfl
@@ -792,13 +693,6 @@ theorem elim_filter {o : Option α} {b : β} :
| false => by simp [filter_some_neg h, h]
| true => by simp [filter_some_pos, h]
theorem elim_join {o : Option (Option α)} {b : β} {f : α β} :
o.join.elim b f = o.elim b (·.elim b f) := by
cases o <;> simp
theorem elim_guard : (guard p a).elim b f = if p a then f a else b := by
cases h : p a <;> simp [*, guard]
@[simp, grind] theorem getD_map (f : α β) (x : α) (o : Option α) :
(o.map f).getD (f x) = f (getD o x) := by cases o <;> rfl
@@ -833,29 +727,12 @@ theorem choice_eq_none_iff_not_nonempty : choice α = none ↔ ¬Nonempty α :=
theorem isSome_choice_iff_nonempty : (choice α).isSome Nonempty α :=
fun h => (choice α).get h, fun h => by simp only [choice, dif_pos h, isSome_some]
@[simp]
theorem isSome_choice [Nonempty α] : (choice α).isSome :=
isSome_choice_iff_nonempty.2 inferInstance
@[deprecated isSome_choice_iff_nonempty (since := "2025-03-18")]
abbrev choice_isSome_iff_nonempty := @isSome_choice_iff_nonempty
theorem isNone_choice_iff_not_nonempty : (choice α).isNone ¬Nonempty α := by
rw [isNone_iff_eq_none, choice_eq_none_iff_not_nonempty]
@[simp]
theorem isNone_choice_eq_false [Nonempty α] : (choice α).isNone = false := by
simp [ not_isSome]
@[simp]
theorem getD_choice {a} :
(choice α).getD a = (choice α).get (isSome_choice_iff_nonempty.2 a) := by
rw [get_eq_getD]
@[simp]
theorem get!_choice [Inhabited α] : (choice α).get! = (choice α).get isSome_choice := by
rw [get_eq_get!]
end choice
@[simp, grind] theorem toList_some (a : α) : (some a).toList = [a] := rfl
@@ -919,6 +796,9 @@ theorem or_self : or o o = o := by
cases o <;> rfl
instance : Std.IdempotentOp (or (α := α)) := @or_self _
theorem or_eq_orElse : or o o' = o.orElse (fun _ => o') := by
cases o <;> rfl
@[grind _=_] theorem map_or : (or o o').map f = (o.map f).or (o'.map f) := by
cases o <;> rfl
@@ -938,54 +818,10 @@ theorem getD_or {o o' : Option α} {fallback : α} :
(o.or o').getD fallback = o.getD (o'.getD fallback) := by
cases o <;> simp
@[simp]
theorem get!_or {o o' : Option α} [Inhabited α] : (o.or o').get! = o.getD o'.get! := by
cases o <;> simp
@[simp] theorem filter_or_filter {o o' : Option α} {f : α Bool} :
(o.or (o'.filter f)).filter f = (o.or o').filter f := by
cases o <;> cases o' <;> simp
theorem guard_or_guard : (guard p a).or (guard q a) = guard (fun x => p x || q x) a := by
simp only [guard]
split <;> simp_all
/-! ### `orElse` -/
/-- The `simp` normal form of `o <|> o'` is `o.or o'` via `orElse_eq_orElse` and `orElse_eq_or`. -/
@[simp, grind] theorem orElse_eq_orElse : HOrElse.hOrElse = @Option.orElse α := rfl
theorem or_eq_orElse : or o o' = o.orElse (fun _ => o') := by
cases o <;> rfl
/-- The `simp` normal form of `o.orElse f` is o.or (f ())`. -/
@[simp, grind] theorem orElse_eq_or {o : Option α} {f} : o.orElse f = o.or (f ()) := by
simp [or_eq_orElse]
@[deprecated or_some (since := "2025-05-03")]
theorem some_orElse (a : α) (f) : (some a).orElse f = some a := rfl
@[deprecated or_none (since := "2025-05-03")]
theorem none_orElse (f : Unit Option α) : none.orElse f = f () := rfl
@[deprecated or_none (since := "2025-05-13")]
theorem orElse_fun_none (x : Option α) : x.orElse (fun _ => none) = x := by simp
@[deprecated or_some (since := "2025-05-13")]
theorem orElse_fun_some (x : Option α) (a : α) :
x.orElse (fun _ => some a) = some (x.getD a) := by simp
@[deprecated or_eq_some_iff (since := "2025-05-13")]
theorem orElse_eq_some_iff (o : Option α) (f) (x : α) :
(o.orElse f) = some x o = some x o = none f () = some x := by simp
@[deprecated or_eq_none_iff (since := "2025-05-13")]
theorem orElse_eq_none_iff (o : Option α) (f) : (o.orElse f) = none o = none f () = none := by simp
@[deprecated map_or (since := "2025-05-13")]
theorem map_orElse {x : Option α} {y} :
(x.orElse y).map f = (x.map f).orElse (fun _ => (y ()).map f) := by simp [map_or]
/-! ### beq -/
section beq
@@ -1024,42 +860,6 @@ variable [BEq α]
· intro h
infer_instance
@[simp] theorem beq_none {o : Option α} : (o == none) = o.isNone := by cases o <;> simp
@[simp]
theorem filter_beq_self [ReflBEq α] {p : α Bool} {o : Option α} : (o.filter p == o) = (o.all p) := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [*]
@[simp]
theorem self_beq_filter [ReflBEq α] {p : α Bool} {o : Option α} : (o == o.filter p) = (o.all p) := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [*]
theorem join_beq_some {o : Option (Option α)} {a : α} : (o.join == some a) = (o == some (some a)) := by
cases o <;> simp
theorem join_beq_none {o : Option (Option α)} : (o.join == none) = (o == none || o == some none) := by
cases o <;> simp
@[simp]
theorem guard_beq_some [ReflBEq α] {x : α} {p : α Bool} : (guard p x == some x) = p x := by
simp only [guard_eq_ite]
split <;> simp [*]
theorem guard_beq_none {x : α} {p : α Bool} : (guard p x == none) = !p x := by
simp
theorem merge_beq_none {o o' : Option α} {f : α α α} :
(o.merge f o' == none) = (o == none && o' == none) := by
simp [beq_none]
end beq
/-! ### ite -/
@@ -1097,9 +897,6 @@ section ite
some a = (if p then b else none) p some a = b := by
split <;> simp_all
theorem ite_some_none_eq_some {p : Prop} {_ : Decidable p} {a b : α} :
(if p then some a else none) = some b p a = b := by simp
theorem mem_dite_none_left {x : α} {_ : Decidable p} {l : ¬ p Option α} :
(x if h : p then none else l h) h : ¬ p, x l h := by
simp
@@ -1191,10 +988,6 @@ theorem isSome_pbind_iff {o : Option α} {f : (a : α) → o = some a → Option
(o.pbind f).isSome a h, (f a h).isSome := by
cases o <;> simp
theorem isNone_pbind_iff {o : Option α} {f : (a : α) o = some a Option β} :
(o.pbind f).isNone o = none a h, f a h = none := by
cases o <;> simp
@[deprecated "isSome_pbind_iff" (since := "2025-04-01")]
theorem pbind_isSome {o : Option α} {f : (a : α) o = some a Option β} :
(o.pbind f).isSome = a h, (f a h).isSome := by
@@ -1204,25 +997,6 @@ theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → o = some a → Optio
o.pbind f = some b a h, f a h = some b := by
cases o <;> simp
theorem pbind_join {o : Option (Option α)} {f : (a : α) o.join = some a Option β} :
o.join.pbind f = o.pbind (fun o' ho' => o'.pbind (fun a ha => f a (by simp_all))) := by
cases o <;> simp <;> congr
theorem isSome_of_isSome_pbind {o : Option α} {f : (a : α) o = some a Option β} :
(o.pbind f).isSome o.isSome := by
cases o <;> simp
theorem isSome_get_of_isSome_pbind {o : Option α} {f : (a : α) o = some a Option β}
(h : (o.pbind f).isSome) : (f (o.get (isSome_of_isSome_pbind h)) (by simp)).isSome := by
cases o with
| none => simp at h
| some a => simp [ h]
@[simp]
theorem get_pbind {o : Option α} {f : (a : α) o = some a Option β} {h} :
(o.pbind f).get h = (f (o.get (isSome_of_isSome_pbind h)) (by simp)).get (isSome_get_of_isSome_pbind h) := by
cases o <;> simp
/-! ### pmap -/
@[simp, grind] theorem pmap_none {p : α Prop} {f : (a : α), p a β} {h} :
@@ -1299,18 +1073,6 @@ theorem pmap_congr {α : Type u} {β : Type v}
· dsimp
rw [hf]
theorem pmap_guard {q : α Bool} {p : α Prop} (f : (x : α) p x β) {x : α}
(h : (a : α), guard q x = some a p a) :
(guard q x).pmap f h = if h' : q x then some (f x (h _ (by simp_all))) else none := by
simp only [guard_eq_ite]
split <;> simp_all
@[simp]
theorem get_pmap {p : α Bool} {f : (x : α) p x β} {o : Option α}
{h : a, o = some a p a} {h'} :
(o.pmap f h).get h' = f (o.get (by simpa using h')) (h _ (by simp)) := by
cases o <;> simp
/-! ### pelim -/
@[simp, grind] theorem pelim_none : pelim none b f = b := rfl
@@ -1339,22 +1101,6 @@ theorem pelim_filter {o : Option α} {b : β} {f : (a : α) → a ∈ o.filter p
| false => by simp [pelim_congr_left (filter_some_neg h), h]
| true => by simp [pelim_congr_left (filter_some_pos h), h]
theorem pelim_join {o : Option (Option α)} {b : β} {f : (a : α) a o.join β} :
o.join.pelim b f = o.pelim b (fun o' ho' => o'.pelim b (fun a ha => f a (by simp_all))) := by
cases o <;> simp <;> congr
@[congr]
theorem pelim_congr {o o' : Option α} {b b' : β}
{f : (a : α) o = some a β} {g : (a : α) o' = some a β}
(ho : o = o') (hb : b = b') (hf : a ha, f a (ho.trans ha) = g a ha) :
o.pelim b f = o'.pelim b' g := by
cases ho; cases hb; cases o <;> apply_assumption
theorem pelim_guard {a : α} {f : (a' : α) guard p a = some a' β} :
(guard p a).pelim b f = if h : p a then f a (by simpa) else b := by
simp only [guard]
split <;> simp
/-! ### pfilter -/
@[congr]
@@ -1386,15 +1132,6 @@ theorem isSome_of_isSome_pfilter {α : Type _} {o : Option α} {p : (a : α) →
(h : (o.pfilter p).isSome) : o.isSome :=
(isSome_pfilter_iff_get.mp h).1
theorem isNone_pfilter_iff {o : Option α} {p : (a : α) o = some a Bool} :
(o.pfilter p).isNone (a : α) (ha : o = some a), p a ha = false := by
cases o with
| none => simp
| some a =>
simp only [pfilter_some, isNone_iff_eq_none, ite_eq_right_iff, reduceCtorEq, imp_false,
Bool.not_eq_true, some.injEq]
exact fun h _ h' => h' h, fun h => h _ rfl
@[simp, grind] theorem get_pfilter {α : Type _} {o : Option α} {p : (a : α) o = some a Bool}
(h : (o.pfilter p).isSome) :
(o.pfilter p).get h = o.get (isSome_of_isSome_pfilter h) := by
@@ -1468,53 +1205,6 @@ theorem pfilter_eq_pbind_ite {α : Type _} {o : Option α}
· rfl
· simp only [Option.pfilter, Bool.cond_eq_ite, Option.pbind_some]
theorem filter_pmap {p : α Prop} {f : (a : α) p a β} {h : (a : α), o = some a p a}
{q : β Bool} : (o.pmap f h).filter q = (o.pfilter (fun a h' => q (f a (h _ h')))).pmap f
(fun _ h' => h _ (eq_some_of_pfilter_eq_some h')) := by
cases o with
| none => simp
| some a =>
simp only [pmap_some, filter_some, pfilter_some]
split <;> simp
theorem pfilter_join {o : Option (Option α)} {p : (a : α) o.join = some a Bool} :
o.join.pfilter p = (o.pfilter (fun o' h => o'.pelim false (fun a ha => p a (by simp_all)))).join := by
cases o with
| none => simp
| some o' =>
cases o' with
| none => simp
| some a =>
simp only [join_some, pfilter_some, pelim_some]
split <;> simp
theorem join_pfilter {o : Option (Option α)} {p : (o' : Option α) o = some o' Bool} :
(o.pfilter p).join = o.pbind (fun o' ho' => if p o' ho' then o' else none) := by
cases o <;> simp <;> split <;> simp
theorem elim_pfilter {o : Option α} {b : β} {f : α β} {p : (a : α) o = some a Bool} :
(o.pfilter p).elim b f = o.pelim b (fun a ha => if p a ha then f a else b) := by
cases o with
| none => simp
| some a =>
simp only [pfilter_some, pelim_some]
split <;> simp
theorem pelim_pfilter {o : Option α} {b : β} {p : (a : α) o = some a Bool}
{f : (a : α) o.pfilter p = some a β} :
(o.pfilter p).pelim b f = o.pelim b
(fun a ha => if hp : p a ha then f a (pfilter_eq_some_iff.2 _, hp) else b) := by
cases o with
| none => simp
| some a =>
simp only [pfilter_some, pelim_some]
split <;> simp
theorem pfilter_guard {a : α} {p : α Bool} {q : (a' : α) guard p a = some a' Bool} :
(guard p a).pfilter q = if (h : p a), q a (by simp [h]) then some a else none := by
simp only [guard]
split <;> simp_all
/-! ### LT and LE -/
@[simp, grind] theorem not_lt_none [LT α] {a : Option α} : ¬ a < none := by cases a <;> simp [LT.lt, Option.lt]
@@ -1527,112 +1217,6 @@ theorem pfilter_guard {a : α} {p : α → Bool} {q : (a' : α) → guard p a =
@[simp] theorem le_none [LE α] {a : Option α} : a none a = none := by cases a <;> simp
@[simp, grind] theorem some_le_some [LE α] {a b : α} : some a some b a b := by simp [LE.le, Option.le]
@[simp]
theorem filter_le [LE α] (le_refl : x : α, x x) {o : Option α} {p : α Bool} : o.filter p o := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [le_refl]
@[simp]
theorem filter_lt [LT α] (lt_irrefl : x : α, ¬x < x) {o : Option α} {p : α Bool} : o.filter p < o o.any (fun a => !p a) := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [*]
@[simp]
theorem le_filter [LE α] (le_refl : x : α, x x) {o : Option α} {p : α Bool} : o o.filter p o.all p := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [*]
@[simp]
theorem not_lt_filter [LT α] (lt_irrefl : x : α, ¬x < x) {o : Option α} {p : α Bool} : ¬o < o.filter p := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [lt_irrefl]
@[simp]
theorem pfilter_le [LE α] (le_refl : x : α, x x) {o : Option α} {p : (a : α) o = some a Bool} :
o.pfilter p o := by
cases o with
| none => simp
| some _ =>
simp only [pfilter_some]
split <;> simp [*]
@[simp]
theorem not_lt_pfilter [LT α] (lt_irrefl : x : α, ¬x < x) {o : Option α}
{p : (a : α) o = some a Bool} : ¬o < o.pfilter p := by
cases o with
| none => simp
| some _ =>
simp only [pfilter_some]
split <;> simp [lt_irrefl]
theorem join_le [LE α] {o : Option (Option α)} {o' : Option α} : o.join o' o some o' := by
cases o <;> simp
@[simp]
theorem guard_le_some [LE α] (le_refl : x : α, x x) {x : α} {p : α Bool} : guard p x some x := by
simp only [guard_eq_ite]
split <;> simp [le_refl]
@[simp]
theorem guard_lt_some [LT α] (lt_irrefl : x : α, ¬x < x) {x : α} {p : α Bool} :
guard p x < some x p x = false := by
simp only [guard_eq_ite]
split <;> simp [*]
theorem left_le_of_merge_le [LE α] {f : α α α} (hf : a b c, f a b c a c)
{o₁ o₂ o₃ : Option α} : o₁.merge f o₂ o₃ o₁ o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> try (simp; done)
simpa using hf _ _ _
theorem right_le_of_merge_le [LE α] {f : α α α} (hf : a b c, f a b c b c)
{o₁ o₂ o₃ : Option α} : o₁.merge f o₂ o₃ o₂ o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> try (simp; done)
simpa using hf _ _ _
theorem merge_le [LE α] {f : α α α} {o₁ o₂ o₃ : Option α}
(hf : a b c, a c b c f a b c) : o₁ o₃ o₂ o₃ o₁.merge f o₂ o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> try (simp; done)
simpa using hf _ _ _
@[simp]
theorem merge_le_iff [LE α] {f : α α α} {o₁ o₂ o₃ : Option α}
(hf : a b c, f a b c a c b c) :
o₁.merge f o₂ o₃ o₁ o₃ o₂ o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> simp [*]
theorem left_lt_of_merge_lt [LT α] {f : α α α} (hf : a b c, f a b < c a < c)
{o₁ o₂ o₃ : Option α} : o₁.merge f o₂ < o₃ o₁ < o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> try (simp; done)
simpa using hf _ _ _
theorem right_lt_of_merge_lt [LT α] {f : α α α} (hf : a b c, f a b < c b < c)
{o₁ o₂ o₃ : Option α} : o₁.merge f o₂ < o₃ o₂ < o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> try (simp; done)
simpa using hf _ _ _
theorem merge_lt [LT α] {f : α α α} {o₁ o₂ o₃ : Option α}
(hf : a b c, a < c b < c f a b < c) : o₁ < o₃ o₂ < o₃ o₁.merge f o₂ < o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> try (simp; done)
simpa using hf _ _ _
@[simp]
theorem merge_lt_iff [LT α] {f : α α α} {o₁ o₂ o₃ : Option α}
(hf : a b c, f a b < c a < c b < c) :
o₁.merge f o₂ < o₃ o₁ < o₃ o₂ < o₃ := by
cases o₁ <;> cases o₂ <;> cases o₃ <;> simp [*]
/-! ### Rel -/
@[simp] theorem rel_some_some {r : α β Prop} : Rel r (some a) (some b) r a b :=
@@ -1714,192 +1298,4 @@ theorem merge_max [Max α] : merge (α := α) max = max := by
instance [Max α] : Std.LawfulIdentity (α := Option α) max none := by
rw [ merge_max]; infer_instance
instance [Max α] [Std.IdempotentOp (α := α) max] : Std.IdempotentOp (α := Option α) max where
idempotent o := by cases o <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem max_filter_left [Max α] [Std.IdempotentOp (α := α) max] {p : α Bool} {o : Option α} :
max (o.filter p) o = o := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem max_filter_right [Max α] [Std.IdempotentOp (α := α) max] {p : α Bool} {o : Option α} :
max o (o.filter p) = o := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem min_filter_left [Min α] [Std.IdempotentOp (α := α) min] {p : α Bool} {o : Option α} :
min (o.filter p) o = o.filter p := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem min_filter_right [Min α] [Std.IdempotentOp (α := α) min] {p : α Bool} {o : Option α} :
min o (o.filter p) = o.filter p := by
cases o with
| none => simp
| some _ =>
simp only [filter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem max_pfilter_left [Max α] [Std.IdempotentOp (α := α) max] {o : Option α} {p : (a : α) o = some a Bool} :
max (o.pfilter p) o = o := by
cases o with
| none => simp
| some _ =>
simp only [pfilter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem max_pfilter_right [Max α] [Std.IdempotentOp (α := α) max] {o : Option α} {p : (a : α) o = some a Bool} :
max o (o.pfilter p) = o := by
cases o with
| none => simp
| some _ =>
simp only [pfilter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem min_pfilter_left [Min α] [Std.IdempotentOp (α := α) min] {o : Option α} {p : (a : α) o = some a Bool} :
min (o.pfilter p) o = o.pfilter p := by
cases o with
| none => simp
| some _ =>
simp only [pfilter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem min_pfilter_right [Min α] [Std.IdempotentOp (α := α) min] {o : Option α} {p : (a : α) o = some a Bool} :
min o (o.pfilter p) = o.pfilter p := by
cases o with
| none => simp
| some _ =>
simp only [pfilter_some]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem isSome_max [Max α] {o o' : Option α} : (max o o').isSome = (o.isSome || o'.isSome) := by
cases o <;> cases o' <;> simp
@[simp]
theorem isNone_max [Max α] {o o' : Option α} : (max o o').isNone = (o.isNone && o'.isNone) := by
cases o <;> cases o' <;> simp
@[simp]
theorem isSome_min [Min α] {o o' : Option α} : (min o o').isSome = (o.isSome && o'.isSome) := by
cases o <;> cases o' <;> simp
@[simp]
theorem isNone_min [Min α] {o o' : Option α} : (min o o').isNone = (o.isNone || o'.isNone) := by
cases o <;> cases o' <;> simp
theorem max_join_left [Max α] {o : Option (Option α)} {o' : Option α} :
max o.join o' = (max o (some o')).get (by simp) := by
cases o <;> simp
theorem max_join_right [Max α] {o : Option α} {o' : Option (Option α)} :
max o o'.join = (max (some o) o').get (by simp) := by
cases o' <;> simp
theorem join_max [Max α] {o o' : Option (Option α)} :
(max o o').join = max o.join o'.join := by
cases o <;> cases o' <;> simp
theorem min_join_left [Min α] {o : Option (Option α)} {o' : Option α} :
min o.join o' = (min o (some o')).join := by
cases o <;> simp
theorem min_join_right [Min α] {o : Option α} {o' : Option (Option α)} :
min o o'.join = (min (some o) o').join := by
cases o' <;> simp
theorem join_min [Min α] {o o' : Option (Option α)} :
(min o o').join = min o.join o'.join := by
cases o <;> cases o' <;> simp
@[simp]
theorem min_guard_some [Min α] [Std.IdempotentOp (α := α) min] {x : α} {p : α Bool} :
min (guard p x) (some x) = guard p x := by
simp only [guard_eq_ite]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem min_some_guard [Min α] [Std.IdempotentOp (α := α) min] {x : α} {p : α Bool} :
min (some x) (guard p x) = guard p x := by
simp only [guard_eq_ite]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem max_guard_some [Max α] [Std.IdempotentOp (α := α) max] {x : α} {p : α Bool} :
max (guard p x) (some x) = some x := by
simp only [guard_eq_ite]
split <;> simp [Std.IdempotentOp.idempotent]
@[simp]
theorem max_some_guard [Max α] [Std.IdempotentOp (α := α) max] {x : α} {p : α Bool} :
max (some x) (guard p x) = some x := by
simp only [guard_eq_ite]
split <;> simp [Std.IdempotentOp.idempotent]
theorem max_eq_some_iff [Max α] {o o' : Option α} {a : α} :
max o o' = some a (o = some a o' = none) (o = none o' = some a)
( b c, o = some b o' = some c max b c = a) := by
cases o <;> cases o' <;> simp
@[simp]
theorem max_eq_none_iff [Max α] {o o' : Option α} :
max o o' = none o = none o' = none := by
cases o <;> cases o' <;> simp
@[simp]
theorem min_eq_some_iff [Min α] {o o' : Option α} {a : α} :
min o o' = some a b c, o = some b o' = some c min b c = a := by
cases o <;> cases o' <;> simp
@[simp]
theorem min_eq_none_iff [Min α] {o o' : Option α} :
min o o' = none o = none o' = none := by
cases o <;> cases o' <;> simp
@[simp]
theorem any_max [Max α] {o o' : Option α} {p : α Bool} (hp : a b, p (max a b) = (p a || p b)) :
(max o o').any p = (o.any p || o'.any p) := by
cases o <;> cases o' <;> simp [hp]
@[simp]
theorem all_min [Min α] {o o' : Option α} {p : α Bool} (hp : a b, p (min a b) = (p a || p b)) :
(min o o').all p = (o.all p || o'.all p) := by
cases o <;> cases o' <;> simp [hp]
theorem isSome_left_of_isSome_min [Min α] {o o' : Option α} : (min o o').isSome o.isSome := by
cases o <;> simp
theorem isSome_right_of_isSome_min [Min α] {o o' : Option α} : (min o o').isSome o'.isSome := by
cases o' <;> simp
@[simp]
theorem get_min [Min α] {o o' : Option α} {h} :
(min o o').get h = min (o.get (isSome_left_of_isSome_min h)) (o'.get (isSome_right_of_isSome_min h)) := by
cases o <;> cases o' <;> simp
theorem map_max [Max α] [Max β] {o o' : Option α} {f : α β} (hf : x y, f (max x y) = max (f x) (f y)) :
(max o o').map f = max (o.map f) (o'.map f) := by
cases o <;> cases o' <;> simp [*]
theorem map_min [Min α] [Min β] {o o' : Option α} {f : α β} (hf : x y, f (min x y) = min (f x) (f y)) :
(min o o').map f = min (o.map f) (o'.map f) := by
cases o <;> cases o' <;> simp [*]
end Option

View File

@@ -60,42 +60,6 @@ theorem toList_bind {o : Option α} {f : α → Option β} :
cases o <;> simp
theorem toList_join {o : Option (Option α)} : o.join.toList = o.toList.flatMap Option.toList := by
simp [toList_bind, bind_id_eq_join]
theorem toList_map {o : Option α} {f : α β} : (o.map f).toList = o.toList.map f := by
cases o <;> simp
theorem toList_min [Min α] {o o' : Option α} :
(min o o').toList = o.toList.zipWith min o'.toList := by
cases o <;> cases o' <;> simp
@[simp]
theorem length_toList_le {o : Option α} : o.toList.length 1 := by
cases o <;> simp
theorem length_toList_eq_ite {o : Option α} :
o.toList.length = if o.isSome then 1 else 0 := by
cases o <;> simp
@[simp]
theorem toList_eq_nil_iff {o : Option α} : o.toList = [] o = none := by
cases o <;> simp
@[simp]
theorem toList_eq_singleton_iff {o : Option α} : o.toList = [a] o = some a := by
cases o <;> simp
@[simp]
theorem length_toList_eq_zero_iff {o : Option α} :
o.toList.length = 0 o = none := by
cases o <;> simp
@[simp]
theorem length_toList_eq_one_iff {o : Option α} :
o.toList.length = 1 o.isSome := by
cases o <;> simp
theorem length_toList_choice_eq_one [Nonempty α] : (choice α).toList.length = 1 := by
simp
simp [toList_bind, join_eq_bind_id]
end Option

View File

@@ -13,8 +13,8 @@ import Init.Control.Lawful.Basic
namespace Option
@[simp, grind] theorem bindM_none [Pure m] (f : α m (Option β)) : none.bindM f = pure none := rfl
@[simp, grind] theorem bindM_some [Pure m] (a) (f : α m (Option β)) : (some a).bindM f = f a := by
@[simp, grind] theorem bindM_none [Monad m] (f : α m (Option β)) : none.bindM f = pure none := rfl
@[simp, grind] theorem bindM_some [Monad m] [LawfulMonad m] (a) (f : α m (Option β)) : (some a).bindM f = f a := by
simp [Option.bindM]
-- We simplify `Option.forM` to `forM`.
@@ -30,10 +30,6 @@ namespace Option
forM (o.map g) f = forM o (fun a => f (g a)) := by
cases o <;> simp
theorem forM_join [Monad m] [LawfulMonad m] (o : Option (Option α)) (f : α m PUnit) :
forM o.join f = forM o (forM · f) := by
cases o <;> simp
@[simp, grind] theorem forIn'_none [Monad m] (b : β) (f : (a : α) a none β m (ForInStep β)) :
forIn' none b f = pure b := by
rfl
@@ -101,13 +97,6 @@ theorem forIn'_eq_pelim [Monad m] [LawfulMonad m]
forIn' (o.map g) init f = forIn' o init fun a h y => f (g a) (mem_map_of_mem g h) y := by
cases o <;> simp
theorem forIn'_join [Monad m] [LawfulMonad m] (b : β) (o : Option (Option α))
(f : (a : α) a o.join β m (ForInStep β)) :
forIn' o.join b f = forIn' o b (fun o' ho' b => ForInStep.yield <$> forIn' o' b (fun a ha b' => f a (by simp_all [join_eq_some_iff]) b')) := by
cases o with
| none => simp
| some a => simpa using forIn'_congr rfl rfl (by simp)
theorem forIn_eq_elim [Monad m] [LawfulMonad m]
(o : Option α) (f : (a : α) β m (ForInStep β)) (b : β) :
forIn o b f =
@@ -137,11 +126,6 @@ theorem forIn_eq_elim [Monad m] [LawfulMonad m]
forIn (o.map g) init f = forIn o init fun a y => f (g a) y := by
cases o <;> simp
theorem forIn_join [Monad m] [LawfulMonad m]
(o : Option (Option α)) (f : α β m (ForInStep β)) :
forIn o.join init f = forIn o init (fun o' b => ForInStep.yield <$> forIn o' b f) := by
cases o <;> simp
@[simp] theorem elimM_pure [Monad m] [LawfulMonad m] (x : Option α) (y : m β) (z : α m β) :
Option.elimM (pure x : m (Option α)) y z = x.elim y z := by
simp [Option.elimM]
@@ -154,8 +138,11 @@ theorem forIn_join [Monad m] [LawfulMonad m]
(y : m γ) (z : β m γ) : Option.elimM (f <$> x) y z = (do Option.elim (f ( x)) y z) := by
simp [Option.elimM]
@[simp] theorem tryCatch_eq_or (o : Option α) (alternative : Unit Option α) :
tryCatch o alternative = o.or (alternative ()) := by cases o <;> rfl
@[simp] theorem tryCatch_none (alternative : Unit Option α) :
(tryCatch none alternative) = alternative () := rfl
@[simp] theorem tryCatch_some (a : α) (alternative : Unit Option α) :
(tryCatch (some a) alternative) = some a := rfl
@[simp] theorem throw_eq_none : throw () = (none : Option α) := rfl
@@ -164,21 +151,4 @@ theorem forIn_join [Monad m] [LawfulMonad m]
theorem filterM_some [Applicative m] (p : α m Bool) (a : α) :
(some a).filterM p = (fun b => if b then some a else none) <$> p a := rfl
theorem sequence_join [Applicative m] [LawfulApplicative m] {o : Option (Option (m α))} :
o.join.sequence = join <$> sequence (o.map sequence) := by
cases o <;> simp
theorem bindM_join [Pure m] {f : α m (Option β)} {o : Option (Option α)} :
o.join.bindM f = o.bindM (·.bindM f) := by
cases o <;> simp
theorem mapM_join [Applicative m] [LawfulApplicative m] {f : α m β} {o : Option (Option α)} :
o.join.mapM f = join <$> o.mapM (Option.mapM f) := by
cases o <;> simp
theorem mapM_guard [Applicative m] {x : α} {p : α Bool} {f : α m β} :
(guard p x).mapM f = if p x then some <$> f x else pure none := by
simp only [guard_eq_ite]
split <;> simp
end Option

View File

@@ -849,4 +849,13 @@ comparisons.
protected def lex' (ord₁ ord₂ : Ord α) : Ord α where
compare := compareLex ord₁.compare ord₂.compare
/--
Constructs an order which compares elements of an `Array` in lexicographic order.
-/
protected def arrayOrd [a : Ord α] : Ord (Array α) where
compare x y :=
let _ : LT α := a.toLT
let _ : BEq α := a.toBEq
if List.lex x.toList y.toList then .lt else if x == y then .eq else .gt
end Ord

View File

@@ -429,8 +429,8 @@ Examples:
def Int8.decLe (a b : Int8) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
attribute [instance] Int8.decLt Int8.decLe
instance (a b : Int8) : Decidable (a < b) := Int8.decLt a b
instance (a b : Int8) : Decidable (a b) := Int8.decLe a b
instance : Max Int8 := maxOfLe
instance : Min Int8 := minOfLe
@@ -800,8 +800,8 @@ Examples:
def Int16.decLe (a b : Int16) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
attribute [instance] Int16.decLt Int16.decLe
instance (a b : Int16) : Decidable (a < b) := Int16.decLt a b
instance (a b : Int16) : Decidable (a b) := Int16.decLe a b
instance : Max Int16 := maxOfLe
instance : Min Int16 := minOfLe
@@ -1187,8 +1187,8 @@ Examples:
def Int32.decLe (a b : Int32) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
attribute [instance] Int32.decLt Int32.decLe
instance (a b : Int32) : Decidable (a < b) := Int32.decLt a b
instance (a b : Int32) : Decidable (a b) := Int32.decLe a b
instance : Max Int32 := maxOfLe
instance : Min Int32 := minOfLe
@@ -1593,8 +1593,8 @@ Examples:
def Int64.decLe (a b : Int64) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
attribute [instance] Int64.decLt Int64.decLe
instance (a b : Int64) : Decidable (a < b) := Int64.decLt a b
instance (a b : Int64) : Decidable (a b) := Int64.decLe a b
instance : Max Int64 := maxOfLe
instance : Min Int64 := minOfLe
@@ -1986,7 +1986,7 @@ Examples:
def ISize.decLe (a b : ISize) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
attribute [instance] ISize.decLt ISize.decLe
instance (a b : ISize) : Decidable (a < b) := ISize.decLt a b
instance (a b : ISize) : Decidable (a b) := ISize.decLe a b
instance : Max ISize := maxOfLe
instance : Min ISize := minOfLe

View File

@@ -32,4 +32,22 @@ protected theorem ne_of_lt {a b : String} (h : a < b) : a ≠ b := by
have := String.lt_irrefl a
intro h; subst h; contradiction
instance ltIrrefl : Std.Irrefl (· < · : Char Char Prop) where
irrefl := Char.lt_irrefl
instance leRefl : Std.Refl (· · : Char Char Prop) where
refl := Char.le_refl
instance leTrans : Trans (· · : Char Char Prop) (· ·) (· ·) where
trans := Char.le_trans
instance leAntisymm : Std.Antisymm (· · : Char Char Prop) where
antisymm _ _ := Char.le_antisymm
instance ltAsymm : Std.Asymm (· < · : Char Char Prop) where
asymm _ _ := Char.lt_asymm
instance leTotal : Std.Total (· · : Char Char Prop) where
total := Char.le_total
end String

View File

@@ -44,6 +44,7 @@ universe signature in consequence. The `Prop` version is `Or`.
namespace Sum
deriving instance DecidableEq for Sum
deriving instance BEq for Sum
section get

View File

@@ -222,8 +222,8 @@ Examples:
def UInt8.decLe (a b : UInt8) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
attribute [instance] UInt8.decLt UInt8.decLe
instance (a b : UInt8) : Decidable (a < b) := UInt8.decLt a b
instance (a b : UInt8) : Decidable (a b) := UInt8.decLe a b
instance : Max UInt8 := maxOfLe
instance : Min UInt8 := minOfLe
@@ -438,8 +438,8 @@ Examples:
def UInt16.decLe (a b : UInt16) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
attribute [instance] UInt16.decLt UInt16.decLe
instance (a b : UInt16) : Decidable (a < b) := UInt16.decLt a b
instance (a b : UInt16) : Decidable (a b) := UInt16.decLe a b
instance : Max UInt16 := maxOfLe
instance : Min UInt16 := minOfLe
@@ -586,7 +586,8 @@ set_option linter.deprecated false in
instance : HMod UInt32 Nat UInt32 := UInt32.modn
instance : Div UInt32 := UInt32.div
-- `LT` and `LE` are already defined in `Init.Prelude`
instance : LT UInt32 := UInt32.lt
instance : LE UInt32 := UInt32.le
/--
Bitwise complement, also known as bitwise negation, for 32-bit unsigned integers. Usually accessed
@@ -831,8 +832,8 @@ Examples:
def UInt64.decLe (a b : UInt64) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
attribute [instance] UInt64.decLt UInt64.decLe
instance (a b : UInt64) : Decidable (a < b) := UInt64.decLt a b
instance (a b : UInt64) : Decidable (a b) := UInt64.decLe a b
instance : Max UInt64 := maxOfLe
instance : Min UInt64 := minOfLe

View File

@@ -437,4 +437,5 @@ Examples:
def USize.decLe (a b : USize) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
attribute [instance] USize.decLt USize.decLe
instance (a b : USize) : Decidable (a < b) := USize.decLt a b
instance (a b : USize) : Decidable (a b) := USize.decLe a b

View File

@@ -307,8 +307,6 @@ abbrev zipWithIndex := @zipIdx
@[inline] def ofFn (f : Fin n α) : Vector α n :=
Array.ofFn f, by simp
/-! See also `Vector.ofFnM` defined in `Init.Data.Vector.OfFn`. -/
/--
Swap two elements of a vector using `Fin` indices.

View File

@@ -44,6 +44,12 @@ theorem isEqv_self [DecidableEq α] (xs : Vector α n) : Vector.isEqv xs xs (·
rcases xs with xs, rfl
simp [Array.isEqv_self]
instance [DecidableEq α] : DecidableEq (Vector α n) :=
fun xs ys =>
match h:isEqv xs ys (fun x y => x = y) with
| true => isTrue (eq_of_isEqv xs ys h)
| false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction
theorem beq_eq_decide [BEq α] (xs ys : Vector α n) :
(xs == ys) = decide ( (i : Nat) (h' : i < n), xs[i] == ys[i]) := by
simp [BEq.beq, isEqv_eq_decide]

View File

@@ -53,9 +53,9 @@ theorem toArray_mk {xs : Array α} (h : xs.size = n) : (Vector.mk xs h).toArray
(Vector.mk xs size).contains a = xs.contains a := by
simp [contains]
@[simp] theorem push_mk {xs : Array α} {size : xs.size = n} :
(Vector.mk xs size).push =
fun x => Vector.mk (xs.push x) (by simp [size, Nat.succ_eq_add_one]) := rfl
@[simp] theorem push_mk {xs : Array α} {size : xs.size = n} {x : α} :
(Vector.mk xs size).push x =
Vector.mk (xs.push x) (by simp [size, Nat.succ_eq_add_one]) := rfl
@[simp] theorem pop_mk {xs : Array α} {size : xs.size = n} :
(Vector.mk xs size).pop = Vector.mk xs.pop (by simp [size]) := rfl
@@ -1660,12 +1660,12 @@ theorem forall_mem_append {p : α → Prop} {xs : Vector α n} {ys : Vector α m
( (x) (_ : x xs ++ ys), p x) ( (x) (_ : x xs), p x) ( (x) (_ : x ys), p x) := by
simp only [mem_append, or_imp, forall_and]
@[simp, grind]
@[grind]
theorem empty_append {xs : Vector α n} : (#v[] : Vector α 0) ++ xs = xs.cast (by omega) := by
rcases xs with as, rfl
simp
@[simp, grind]
@[grind]
theorem append_empty {xs : Vector α n} : xs ++ (#v[] : Vector α 0) = xs := by
rw [ toArray_inj, toArray_append, Array.append_empty]

View File

@@ -38,11 +38,6 @@ theorem mapM_pure [Monad m] [LawfulMonad m] {xs : Vector α n} (f : α → β) :
apply map_toArray_inj.mp
simp
@[simp] theorem mapM_map [Monad m] [LawfulMonad m] {f : α β} {g : β m γ} {xs : Vector α n} :
(xs.map f).mapM g = xs.mapM (g f) := by
apply map_toArray_inj.mp
simp
@[congr] theorem mapM_congr [Monad m] {xs ys : Vector α n} (w : xs = ys)
{f : α m β} :
xs.mapM f = ys.mapM f := by

View File

@@ -8,7 +8,6 @@ module
prelude
import all Init.Data.Vector.Basic
import Init.Data.Vector.Lemmas
import Init.Data.Vector.Monadic
import Init.Data.Array.OfFn
/-!
@@ -41,122 +40,4 @@ theorem back_ofFn {n} [NeZero n] {f : Fin n → α} :
(ofFn f).back = f n - 1, by have := NeZero.ne n; omega := by
simp [back]
theorem ofFn_succ {f : Fin (n+1) α} :
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f n, by omega) := by
ext i h
· simp only [getElem_ofFn, getElem_push, Fin.castSucc_mk, left_eq_dite_iff]
intro h'
have : i = n := by omega
simp_all
theorem ofFn_add {n m} {f : Fin (n + m) α} :
ofFn f = (ofFn (fun i => f (i.castLE (Nat.le_add_right n m)))) ++ (ofFn (fun i => f (i.natAdd n))) := by
apply Vector.toArray_inj.mp
simp [Array.ofFn_add]
theorem ofFn_succ' {f : Fin (n+1) α} :
ofFn f = (#v[f 0] ++ ofFn (fun i => f i.succ)).cast (by omega) := by
apply Vector.toArray_inj.mp
simp [Array.ofFn_succ']
/-! ### ofFnM -/
/-- Construct (in a monadic context) a vector by applying a monadic function to each index. -/
def ofFnM {n} [Monad m] (f : Fin n m α) : m (Vector α n) :=
go 0 (by omega) (Array.emptyWithCapacity n) rfl where
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #v[f i, ..., f(n - 1)]` -/
go (i : Nat) (h' : i n) (acc : Array α) (w : acc.size = i) : m (Vector α n) := do
if h : i < n then
go (i+1) (by omega) (acc.push ( f i, h)) (by simp [w])
else
pure acc, by omega
@[simp]
theorem ofFnM_zero [Monad m] {f : Fin 0 m α} : Vector.ofFnM f = pure #v[] := by
simp [ofFnM, ofFnM.go]
private theorem ofFnM_go_succ {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α}
(hi : i n := by omega) {h : xs.size = i} :
ofFnM.go f i (by omega) xs h = (do
let as ofFnM.go (fun i => f i.castSucc) i hi xs h
let a f (Fin.last n)
pure (as.push a)) := by
fun_induction ofFnM.go f i (by omega) xs h
case case1 acc h' h ih =>
if h : acc.size = n then
unfold ofFnM.go
rw [dif_neg (by omega)]
have h : ¬ acc.size + 1 < n + 1 := by omega
have : Fin.last n = acc.size, by omega := by ext; simp; omega
simp [*]
else
have : acc.size + 1 n := by omega
simp only [ih, this]
conv => rhs; unfold ofFnM.go
rw [dif_pos (by omega)]
simp
case case2 =>
omega
theorem ofFnM_succ {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let as ofFnM fun i => f i.castSucc
let a f (Fin.last n)
pure (as.push a)) := by
simp [ofFnM, ofFnM_go_succ]
theorem ofFnM_add {n m} [Monad m] [LawfulMonad m] {f : Fin (n + k) m α} :
ofFnM f = (do
let as ofFnM (fun i => f (i.castLE (Nat.le_add_right n k)))
let bs ofFnM (fun i => f (i.natAdd n))
pure (as ++ bs)) := by
induction k with
| zero => simp
| succ k ih => simp [ofFnM_succ, ih, push_append]
@[simp, grind] theorem toArray_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
toArray <$> ofFnM f = Array.ofFnM f := by
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ, Array.ofFnM_succ, ih]
@[simp, grind] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
toList <$> Vector.ofFnM f = List.ofFnM f := by
unfold toList
suffices Array.toList <$> (toArray <$> ofFnM f) = List.ofFnM f by
simpa [-toArray_ofFnM]
simp
theorem ofFnM_succ' {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let a f 0
let as ofFnM fun i => f i.succ
pure ((#v[a] ++ as).cast (by omega))) := by
apply Vector.map_toArray_inj.mp
simp only [toArray_ofFnM, Array.ofFnM_succ', bind_pure_comp, map_bind, Functor.map_map,
toArray_cast, toArray_append]
congr 1
funext x
have : (fun xs : Vector α n => #[x] ++ xs.toArray) = (#[x] ++ ·) toArray := by funext xs; simp
simp [this, comp_map]
@[simp]
theorem ofFnM_pure_comp [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (pure f) = (pure (ofFn f) : m (Vector α n)) := by
apply Vector.map_toArray_inj.mp
simp
-- Variant of `ofFnM_pure_comp` using a lambda.
-- This is not marked a `@[simp]` as it would match on every occurrence of `ofFnM`.
theorem ofFnM_pure [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (fun i => pure (f i)) = (pure (ofFn f) : m (Vector α n)) :=
ofFnM_pure_comp
@[simp, grind =] theorem idRun_ofFnM {f : Fin n Id α} :
Id.run (ofFnM f) = ofFn (fun i => Id.run (f i)) := by
unfold Id.run
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ', ofFn_succ', ih]
end Vector

View File

@@ -15,6 +15,4 @@ import Init.Grind.Util
import Init.Grind.Offset
import Init.Grind.PP
import Init.Grind.CommRing
import Init.Grind.Module
import Init.Grind.Ordered
import Init.Grind.Ext

View File

@@ -9,5 +9,5 @@ prelude
import Init.Core
import Init.Grind.Tactics
attribute [grind cases eager] And Prod False Empty True PUnit Exists Subtype
attribute [grind cases eager] And Prod False Empty True Unit Exists Subtype
attribute [grind cases] Or

View File

@@ -13,4 +13,3 @@ import Init.Grind.CommRing.SInt
import Init.Grind.CommRing.Fin
import Init.Grind.CommRing.BitVec
import Init.Grind.CommRing.Poly
import Init.Grind.CommRing.Field

View File

@@ -10,7 +10,6 @@ import Init.Data.Zero
import Init.Data.Int.DivMod.Lemmas
import Init.Data.Int.Pow
import Init.TacticsExtra
import Init.Grind.Module.Basic
/-!
# A monolithic commutative ring typeclass for internal use in `grind`.
@@ -104,35 +103,11 @@ theorem ofNat_mul (a b : Nat) : OfNat.ofNat (α := α) (a * b) = OfNat.ofNat a *
theorem natCast_mul (a b : Nat) : ((a * b : Nat) : α) = ((a : α) * (b : α)) := by
rw [ ofNat_eq_natCast, ofNat_mul, ofNat_eq_natCast, ofNat_eq_natCast]
theorem pow_one (a : α) : a ^ 1 = a := by
rw [pow_succ, pow_zero, one_mul]
theorem pow_two (a : α) : a ^ 2 = a * a := by
rw [pow_succ, pow_one]
theorem pow_add (a : α) (k₁ k₂ : Nat) : a ^ (k₁ + k₂) = a^k₁ * a^k₂ := by
induction k₂
next => simp [pow_zero, mul_one]
next k₂ ih => rw [Nat.add_succ, pow_succ, pow_succ, ih, mul_assoc]
instance : NatModule α where
hMul a x := a * x
add_zero := by simp [add_zero]
zero_add := by simp [zero_add]
add_assoc := by simp [add_assoc]
add_comm := by simp [add_comm]
zero_hmul := by simp [natCast_zero, zero_mul]
one_hmul := by simp [natCast_one, one_mul]
add_hmul := by simp [natCast_add, right_distrib]
hmul_zero := by simp [mul_zero]
hmul_add := by simp [left_distrib]
mul_hmul := by simp [natCast_mul, mul_assoc]
theorem hmul_eq_natCast_mul {α} [Semiring α] {k : Nat} {a : α} : HMul.hMul (α := Nat) k a = (k : α) * a := rfl
theorem hmul_eq_ofNat_mul {α} [Semiring α] {k : Nat} {a : α} : HMul.hMul (α := Nat) k a = OfNat.ofNat k * a := by
simp [ofNat_eq_natCast, hmul_eq_natCast_mul]
end Semiring
namespace Ring
@@ -268,23 +243,6 @@ theorem intCast_pow (x : Int) (k : Nat) : ((x ^ k : Int) : α) = (x : α) ^ k :=
next => simp [pow_zero, Int.pow_zero, intCast_one]
next k ih => simp [pow_succ, Int.pow_succ, intCast_mul, *]
instance : IntModule α where
hMul a x := a * x
add_zero := by simp [add_zero]
zero_add := by simp [zero_add]
add_assoc := by simp [add_assoc]
add_comm := by simp [add_comm]
zero_hmul := by simp [intCast_zero, zero_mul]
one_hmul := by simp [intCast_one, one_mul]
add_hmul := by simp [intCast_add, right_distrib]
hmul_zero := by simp [mul_zero]
hmul_add := by simp [left_distrib]
mul_hmul := by simp [intCast_mul, mul_assoc]
neg_add_cancel := by simp [neg_add_cancel]
sub_eq_add_neg := by simp [sub_eq_add_neg]
theorem hmul_eq_intCast_mul {α} [Ring α] {k : Int} {a : α} : HMul.hMul (α := Int) k a = (k : α) * a := rfl
end Ring
namespace CommSemiring
@@ -389,7 +347,14 @@ theorem natCast_eq_iff_of_lt {x y : Nat} (h₁ : x < p) (h₂ : y < p) :
end IsCharP
-- TODO: This should be generalizable to any `IntModule α`, not just `Ring α`.
/--
Special case of Mathlib's `NoZeroSMulDivisors Nat α`.
-/
class NoNatZeroDivisors (α : Type u) [Ring α] where
no_nat_zero_divisors : (k : Nat) (a : α), k 0 OfNat.ofNat (α := α) k * a = 0 a = 0
export NoNatZeroDivisors (no_nat_zero_divisors)
theorem no_int_zero_divisors {α : Type u} [Ring α] [NoNatZeroDivisors α] {k : Int} {a : α}
: k 0 k * a = 0 a = 0 := by
match k with
@@ -397,12 +362,13 @@ theorem no_int_zero_divisors {α : Type u} [Ring α] [NoNatZeroDivisors α] {k :
simp [intCast_natCast]
intro h₁ h₂
replace h₁ : k 0 := by intro h; simp [h] at h₁
rw [ ofNat_eq_natCast] at h₂
exact no_nat_zero_divisors k a h₁ h₂
| -(k+1 : Nat) =>
rw [Int.natCast_add, Int.natCast_add, intCast_neg, intCast_natCast]
intro _ h
replace h := congrArg (-·) h; simp at h
rw [ neg_mul, neg_neg, neg_zero, hmul_eq_natCast_mul] at h
rw [ neg_mul, neg_neg, neg_zero, ofNat_eq_natCast] at h
exact no_nat_zero_divisors (k+1) a (Nat.succ_ne_zero _) h
end Lean.Grind

View File

@@ -1,45 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Grind.CommRing.Basic
namespace Lean.Grind
class Field (α : Type u) extends CommRing α, Inv α, Div α where
div_eq_mul_inv : a b : α, a / b = a * b⁻¹
inv_zero : (0 : α)⁻¹ = 0
inv_one : (1 : α)⁻¹ = 1
mul_inv_cancel : {a : α}, a 0 a * a⁻¹ = 1
attribute [instance 100] Field.toInv Field.toDiv
namespace Field
variable [Field α] {a : α}
theorem inv_mul_cancel (h : a 0) : a⁻¹ * a = 1 := by
rw [CommSemiring.mul_comm, mul_inv_cancel h]
instance [IsCharP α 0] : NoNatZeroDivisors α where
no_nat_zero_divisors := by
intro a b h w
have := IsCharP.natCast_eq_zero_iff (α := α) 0 a
simp only [Nat.mod_zero, h, iff_false] at this
if h : b = 0 then
exact h
else
rw [Semiring.ofNat_eq_natCast] at w
replace w := congrArg (fun x => x * b⁻¹) w
dsimp only [] at w
rw [Semiring.hmul_eq_ofNat_mul, Semiring.mul_assoc, Field.mul_inv_cancel h, Semiring.mul_one,
Semiring.natCast_zero, Semiring.zero_mul, Semiring.ofNat_eq_natCast] at w
contradiction
end Field
end Lean.Grind

View File

@@ -104,11 +104,7 @@ instance (n : Nat) [NeZero n] : CommRing (Fin n) where
intCast_neg := Fin.intCast_neg
instance (n : Nat) [NeZero n] : IsCharP (Fin n) n where
ofNat_eq_zero_iff x := by
change Fin.ofNat' _ _ = Fin.ofNat' _ _ _
simp only [Fin.ofNat']
simp only [Nat.zero_mod]
simp only [Fin.mk.injEq]
ofNat_eq_zero_iff x := by simp only [OfNat.ofNat, Fin.ofNat']; simp
end Fin

View File

@@ -189,7 +189,7 @@ def Mon.grevlex (m₁ m₂ : Mon) : Ordering :=
inductive Poly where
| num (k : Int)
| add (k : Int) (v : Mon) (p : Poly)
deriving BEq, Repr, Inhabited, Hashable
deriving BEq, Inhabited, Hashable
instance : LawfulBEq Poly where
eq_of_beq {a} := by

View File

@@ -1,9 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Grind.Module.Basic

View File

@@ -1,114 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Int.Order
namespace Lean.Grind
class NatModule (M : Type u) extends Zero M, Add M, HMul Nat M M where
add_zero : a : M, a + 0 = a
zero_add : a : M, 0 + a = a
add_comm : a b : M, a + b = b + a
add_assoc : a b c : M, a + b + c = a + (b + c)
zero_hmul : a : M, 0 * a = 0
one_hmul : a : M, 1 * a = a
add_hmul : n m : Nat, a : M, (n + m) * a = n * a + m * a
hmul_zero : n : Nat, n * (0 : M) = 0
hmul_add : n : Nat, a b : M, n * (a + b) = n * a + n * b
mul_hmul : n m : Nat, a : M, (n * m) * a = n * (m * a)
attribute [instance 100] NatModule.toZero NatModule.toAdd NatModule.toHMul
class IntModule (M : Type u) extends Zero M, Add M, Neg M, Sub M, HMul Int M M where
add_zero : a : M, a + 0 = a
zero_add : a : M, 0 + a = a
add_comm : a b : M, a + b = b + a
add_assoc : a b c : M, a + b + c = a + (b + c)
zero_hmul : a : M, (0 : Int) * a = 0
one_hmul : a : M, (1 : Int) * a = a
add_hmul : n m : Int, a : M, (n + m) * a = n * a + m * a
hmul_zero : n : Int, n * (0 : M) = 0
hmul_add : n : Int, a b : M, n * (a + b) = n * a + n * b
mul_hmul : n m : Int, a : M, (n * m) * a = n * (m * a)
neg_add_cancel : a : M, -a + a = 0
sub_eq_add_neg : a b : M, a - b = a + -b
namespace IntModule
attribute [instance 100] IntModule.toZero IntModule.toAdd IntModule.toNeg IntModule.toSub IntModule.toHMul
instance toNatModule (M : Type u) [i : IntModule M] : NatModule M :=
{ i with
hMul a x := (a : Int) * x
hmul_zero := by simp [IntModule.hmul_zero]
add_hmul := by simp [IntModule.add_hmul]
hmul_add := by simp [IntModule.hmul_add]
mul_hmul := by simp [IntModule.mul_hmul] }
variable {M : Type u} [IntModule M]
theorem add_neg_cancel (a : M) : a + -a = 0 := by
rw [add_comm, neg_add_cancel]
theorem add_left_inj {a b : M} (c : M) : a + c = b + c a = b :=
fun h => by simpa [add_assoc, add_neg_cancel, add_zero] using (congrArg (· + -c) h),
fun g => congrArg (· + c) g
theorem add_right_inj (a b c : M) : a + b = a + c b = c := by
rw [add_comm a b, add_comm a c, add_left_inj]
theorem neg_zero : (-0 : M) = 0 := by
rw [ add_left_inj 0, neg_add_cancel, add_zero]
theorem neg_neg (a : M) : -(-a) = a := by
rw [ add_left_inj (-a), neg_add_cancel, add_neg_cancel]
theorem neg_eq_zero (a : M) : -a = 0 a = 0 :=
fun h => by
replace h := congrArg (-·) h
simpa [neg_neg, neg_zero] using h,
fun h => by rw [h, neg_zero]
theorem neg_add (a b : M) : -(a + b) = -a + -b := by
rw [ add_left_inj (a + b), neg_add_cancel, add_assoc (-a), add_comm a b, add_assoc (-b),
neg_add_cancel, zero_add, neg_add_cancel]
theorem neg_sub (a b : M) : -(a - b) = b - a := by
rw [sub_eq_add_neg, neg_add, neg_neg, sub_eq_add_neg, add_comm]
theorem sub_self (a : M) : a - a = 0 := by
rw [sub_eq_add_neg, add_neg_cancel]
theorem sub_eq_iff {a b c : M} : a - b = c a = c + b := by
rw [sub_eq_add_neg]
constructor
next => intro; subst c; rw [add_assoc, neg_add_cancel, add_zero]
next => intro; subst a; rw [add_assoc, add_comm b, neg_add_cancel, add_zero]
theorem sub_eq_zero_iff {a b : M} : a - b = 0 a = b := by
simp [sub_eq_iff, zero_add]
theorem neg_hmul (n : Int) (a : M) : (-n) * a = - (n * a) := by
apply (add_left_inj (n * a)).mp
rw [ add_hmul, Int.add_left_neg, zero_hmul, neg_add_cancel]
theorem hmul_neg (n : Int) (a : M) : n * (-a) = - (n * a) := by
apply (add_left_inj (n * a)).mp
rw [ hmul_add, neg_add_cancel, neg_add_cancel, hmul_zero]
end IntModule
/--
Special case of Mathlib's `NoZeroSMulDivisors Nat α`.
-/
class NoNatZeroDivisors (α : Type u) [Zero α] [HMul Nat α α] where
no_nat_zero_divisors : (k : Nat) (a : α), k 0 k * a = 0 a = 0
export NoNatZeroDivisors (no_nat_zero_divisors)
end Lean.Grind

View File

@@ -104,26 +104,6 @@ theorem flip_bool_eq (a b : Bool) : (a = b) = (b = a) := by
theorem bool_eq_to_prop (a b : Bool) : (a = b) = ((a = true) = (b = true)) := by
simp
theorem forall_or_forall {α : Sort u} {β : α Sort v} (p : α Prop) (q : (a : α) β a Prop)
: ( a : α, p a b : β a, q a b) =
( (a : α) (b : β a), p a q a b) := by
apply propext; constructor
· intro h a b; cases h a <;> simp [*]
· intro h a
apply Classical.byContradiction
intro h'; simp at h'; have h₁, b, h₂ := h'
replace h := h a b; simp [h₁, h₂] at h
theorem forall_forall_or {α : Sort u} {β : α Sort v} (p : α Prop) (q : (a : α) β a Prop)
: ( a : α, ( b : β a, q a b) p a) =
( (a : α) (b : β a), q a b p a) := by
apply propext; constructor
· intro h a b; cases h a <;> simp [*]
· intro h a
apply Classical.byContradiction
intro h'; simp at h'; have b, h₁, h₂ := h'
replace h := h a b; simp [h₁, h₂] at h
init_grind_norm
/- Pre theorems -/
not_and not_or not_ite not_forall not_exists
@@ -133,7 +113,6 @@ init_grind_norm
/- Post theorems -/
Classical.not_not
ne_eq iff_eq eq_self heq_eq_eq
forall_or_forall forall_forall_or
-- Prop equality
eq_true_eq eq_false_eq not_eq_prop
-- True

View File

@@ -1,12 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Grind.Ordered.PartialOrder
import Init.Grind.Ordered.Module
import Init.Grind.Ordered.Ring
import Init.Grind.Ordered.Int

View File

@@ -1,35 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Grind.Ordered.Ring
import Init.Grind.CommRing.Int
import Init.Omega
/-!
# `grind` instances for `Int` as an ordered module.
-/
namespace Lean.Grind
instance : Preorder Int where
le_refl := Int.le_refl
le_trans := Int.le_trans
lt_iff_le_not_le := by omega
instance : IntModule.IsOrdered Int where
neg_le_iff := by omega
add_le_left := by omega
hmul_pos k a ha := fun hk => Int.mul_pos hk ha, fun h => Int.pos_of_mul_pos_left h ha
hmul_nonneg hk ha := Int.mul_nonneg hk ha
instance : Ring.IsOrdered Int where
zero_lt_one := by omega
mul_lt_mul_of_pos_left := Int.mul_lt_mul_of_pos_left
mul_lt_mul_of_pos_right := Int.mul_lt_mul_of_pos_right
end Lean.Grind

View File

@@ -1,69 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Int.Order
import Init.Grind.Module.Basic
import Init.Grind.Ordered.PartialOrder
namespace Lean.Grind
class IntModule.IsOrdered (M : Type u) [Preorder M] [IntModule M] where
neg_le_iff : a b : M, -a b -b a
add_le_left : {a b : M}, a b (c : M) a + c b + c
hmul_pos : (k : Int) {a : M}, 0 < a (0 < k 0 < k * a)
hmul_nonneg : {k : Int} {a : M}, 0 k 0 a 0 k * a
namespace IntModule.IsOrdered
variable {M : Type u} [Preorder M] [IntModule M] [IntModule.IsOrdered M]
theorem le_neg_iff {a b : M} : a -b b -a := by
conv => lhs; rw [ neg_neg a]
rw [neg_le_iff, neg_neg]
theorem neg_lt_iff {a b : M} : -a < b -b < a := by
simp [Preorder.lt_iff_le_not_le]
rw [neg_le_iff, le_neg_iff]
theorem lt_neg_iff {a b : M} : a < -b b < -a := by
conv => lhs; rw [ neg_neg a]
rw [neg_lt_iff, neg_neg]
theorem neg_nonneg_iff {a : M} : 0 -a a 0 := by
rw [le_neg_iff, neg_zero]
theorem neg_pos_iff {a : M} : 0 < -a a < 0 := by
rw [lt_neg_iff, neg_zero]
theorem add_lt_left {a b : M} (h : a < b) (c : M) : a + c < b + c := by
simp [Preorder.lt_iff_le_not_le] at h
constructor
· exact add_le_left h.1 _
· intro w
apply h.2
replace w := add_le_left w (-c)
rw [add_assoc, add_assoc, add_neg_cancel, add_zero, add_zero] at w
exact w
theorem add_le_right (a : M) {b c : M} (h : b c) : a + b a + c := by
rw [add_comm a b, add_comm a c]
exact add_le_left h a
theorem add_lt_right (a : M) {b c : M} (h : b < c) : a + b < a + c := by
rw [add_comm a b, add_comm a c]
exact add_lt_left h a
theorem hmul_neg (k : Int) {a : M} (h : a < 0) : 0 < k k * a < 0 := by
simpa [IntModule.hmul_neg, neg_pos_iff] using hmul_pos k (neg_pos_iff.mpr h)
theorem hmul_nonpos {k : Int} {a : M} (hk : 0 k) (ha : a 0) : k * a 0 := by
simpa [IntModule.hmul_neg, neg_nonneg_iff] using hmul_nonneg hk (neg_nonneg_iff.mpr ha)
end IntModule.IsOrdered
end Lean.Grind

View File

@@ -1,61 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Int.Order
namespace Lean.Grind
/-- A preorder is a reflexive, transitive relation `≤` with `a < b` defined in the obvious way. -/
class Preorder (α : Type u) extends LE α, LT α where
le_refl : a : α, a a
le_trans : {a b c : α}, a b b c a c
lt := fun a b => a b ¬b a
lt_iff_le_not_le : {a b : α}, a < b a b ¬b a := by intros; rfl
namespace Preorder
variable {α : Type u} [Preorder α]
theorem le_of_lt {a b : α} (h : a < b) : a b := (lt_iff_le_not_le.mp h).1
theorem lt_of_lt_of_le {a b c : α} (h₁ : a < b) (h₂ : b c) : a < c := by
simp [lt_iff_le_not_le] at h₁
exact le_trans h₁.1 h₂, fun h => h₁.2 (le_trans h₂ h)
theorem lt_of_le_of_lt {a b c : α} (h₁ : a b) (h₂ : b < c) : a < c := by
simp [lt_iff_le_not_le] at h₂
exact le_trans h₁ h₂.1, fun h => h₂.2 (le_trans h h₁)
theorem lt_trans {a b c : α} (h₁ : a < b) (h₂ : b < c) : a < c :=
lt_of_lt_of_le h₁ (le_of_lt h₂)
theorem lt_irrefl {a : α} (h : a < a) : False := by
simp [lt_iff_le_not_le] at h
end Preorder
class PartialOrder (α : Type u) extends Preorder α where
le_antisymm : {a b : α}, a b b a a = b
namespace PartialOrder
variable {α : Type u} [PartialOrder α]
theorem le_iff_lt_or_eq {a b : α} : a b a < b a = b := by
constructor
· intro h
rw [Preorder.lt_iff_le_not_le, Classical.or_iff_not_imp_right]
exact fun w => h, fun w' => w (le_antisymm h w')
· intro h
cases h with
| inl h => exact Preorder.le_of_lt h
| inr h => subst h; exact Preorder.le_refl a
end PartialOrder
end Lean.Grind

View File

@@ -1,66 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Grind.CommRing.Basic
import Init.Grind.Ordered.Module
namespace Lean.Grind
class Ring.IsOrdered (R : Type u) [Ring R] [Preorder R] extends IntModule.IsOrdered R where
/-- In a strict ordered semiring, we have `0 < 1`. -/
zero_lt_one : 0 < 1
/-- In a strict ordered semiring, we can multiply an inequality `a < b` on the left
by a positive element `0 < c` to obtain `c * a < c * b`. -/
mul_lt_mul_of_pos_left : {a b c : R}, a < b 0 < c c * a < c * b
/-- In a strict ordered semiring, we can multiply an inequality `a < b` on the right
by a positive element `0 < c` to obtain `a * c < b * c`. -/
mul_lt_mul_of_pos_right : {a b c : R}, a < b 0 < c a * c < b * c
namespace Ring.IsOrdered
variable {R : Type u} [Ring R] [PartialOrder R] [Ring.IsOrdered R]
theorem mul_le_mul_of_nonneg_left {a b c : R} (h : a b) (h' : 0 c) : c * a c * b := by
rw [PartialOrder.le_iff_lt_or_eq] at h'
cases h' with
| inl h' =>
have p := mul_lt_mul_of_pos_left (a := a) (b := b) (c := c)
rw [PartialOrder.le_iff_lt_or_eq] at h
cases h with
| inl h => exact Preorder.le_of_lt (p h h')
| inr h => subst h; exact Preorder.le_refl (c * a)
| inr h' => subst h'; simp [Semiring.zero_mul, Preorder.le_refl]
theorem mul_le_mul_of_nonneg_right {a b c : R} (h : a b) (h' : 0 c) : a * c b * c := by
rw [PartialOrder.le_iff_lt_or_eq] at h'
cases h' with
| inl h' =>
have p := mul_lt_mul_of_pos_right (a := a) (b := b) (c := c)
rw [PartialOrder.le_iff_lt_or_eq] at h
cases h with
| inl h => exact Preorder.le_of_lt (p h h')
| inr h => subst h; exact Preorder.le_refl (a * c)
| inr h' => subst h'; simp [Semiring.mul_zero, Preorder.le_refl]
theorem mul_nonneg {a b : R} (h₁ : 0 a) (h₂ : 0 b) : 0 a * b := by
simpa [Semiring.zero_mul] using mul_le_mul_of_nonneg_right h₁ h₂
theorem mul_pos {a b : R} (h₁ : 0 < a) (h₂ : 0 < b) : 0 < a * b := by
simpa [Semiring.zero_mul] using mul_lt_mul_of_pos_right h₁ h₂
theorem sq_nonneg {a : R} (h : 0 a) : 0 a^2 := by
rw [Semiring.pow_two]
apply mul_nonneg h h
theorem sq_pos {a : R} (h : 0 < a) : 0 < a^2 := by
rw [Semiring.pow_two]
apply mul_pos h h
end Ring.IsOrdered
end Lean.Grind

View File

@@ -30,7 +30,6 @@ syntax grindIntro := &"intro "
syntax grindExt := &"ext "
syntax grindMod := grindEqBoth <|> grindEqRhs <|> grindEq <|> grindEqBwd <|> grindBwd <|> grindFwd <|> grindRL <|> grindLR <|> grindUsr <|> grindCasesEager <|> grindCases <|> grindIntro <|> grindExt
syntax (name := grind) "grind" (grindMod)? : attr
syntax (name := grind?) "grind?" (grindMod)? : attr
end Attr
end Lean.Parser
@@ -68,6 +67,8 @@ structure Config where
if the implication is true. Otherwise, it will split only if `p` is an arithmetic predicate.
-/
splitImp : Bool := false
/-- By default, `grind` halts as soon as it encounters a sub-goal where no further progress can be made. -/
failures : Nat := 1
/-- Maximum number of heartbeats (in thousands) the canonicalizer can spend per definitional equality test. -/
canonHeartbeats : Nat := 1000
/-- If `ext` is `true`, `grind` uses extensionality theorems that have been marked with `[grind ext]`. -/

View File

@@ -877,12 +877,6 @@ instance ImplicationOrder.instCompleteLattice : CompleteLattice ImplicationOrder
@monotone _ _ _ ImplicationOrder.instOrder (fun x => (Exists (f x))) :=
fun x y hxy w, hw => w, monotone_apply w f h x y hxy hw
@[partial_fixpoint_monotone] theorem implication_order_monotone_forall
{α} [PartialOrder α] {β} (f : α β ImplicationOrder)
(h : monotone f) :
@monotone _ _ _ ImplicationOrder.instOrder (fun x => y, f x y) :=
fun x y hxy h₂ y₁ => monotone_apply y₁ f h x y hxy (h₂ y₁)
@[partial_fixpoint_monotone] theorem implication_order_monotone_and
{α} [PartialOrder α] (f₁ : α ImplicationOrder) (f₂ : α ImplicationOrder)
(h₁ : @monotone _ _ _ ImplicationOrder.instOrder f₁)
@@ -937,12 +931,6 @@ def ReverseImplicationOrder.instCompleteLattice : CompleteLattice ReverseImplica
@monotone _ _ _ ReverseImplicationOrder.instOrder (fun x => Exists (f x)) :=
fun x y hxy w, hw => w, monotone_apply w f h x y hxy hw
@[partial_fixpoint_monotone] theorem coind_monotone_forall
{α} [PartialOrder α] {β} (f : α β ReverseImplicationOrder)
(h : monotone f) :
@monotone _ _ _ ReverseImplicationOrder.instOrder (fun x => y, f x y) :=
fun x y hxy h₂ y₁ => monotone_apply y₁ f h x y hxy (h₂ y₁)
@[partial_fixpoint_monotone] theorem coind_monotone_and
{α} [PartialOrder α] (f₁ : α Prop) (f₂ : α Prop)
(h₁ : @monotone _ _ _ ReverseImplicationOrder.instOrder f₁)

View File

@@ -82,13 +82,16 @@ theorem SeqRight.monotone_seqRight [LawfulMonad m] (f : γ → m α) (g : γ
namespace Option
omit [MonoBind m] in
@[partial_fixpoint_monotone]
theorem monotone_bindM (f : γ α m (Option β)) (xs : Option α) (hmono : monotone f) :
monotone (fun x => xs.bindM (f x)) := by
cases xs with
| none => apply monotone_const
| some x => apply monotone_apply _ _ hmono
| some x =>
apply monotone_bind
· apply monotone_apply
apply hmono
· apply monotone_const
@[partial_fixpoint_monotone]
theorem monotone_mapM [LawfulMonad m] (f : γ α m β) (xs : Option α) (hmono : monotone f) :

View File

@@ -295,7 +295,6 @@ recommended_spelling "PProd" for "×'" in [PProd, «term_×'_»]
@[inherit_doc] prefix:75 "-" => Neg.neg
@[inherit_doc] prefix:100 "~~~" => Complement.complement
@[inherit_doc] postfix:max "⁻¹" => Inv.inv
@[inherit_doc] infixr:73 "" => HSMul.hSMul
/-!
Remark: the infix commands above ensure a delaborator is generated for each relations.
@@ -313,40 +312,6 @@ macro_rules | `($x % $y) => `(binop% HMod.hMod $x $y)
macro_rules | `($x ^ $y) => `(rightact% HPow.hPow $x $y)
macro_rules | `($x ++ $y) => `(binop% HAppend.hAppend $x $y)
macro_rules | `(- $x) => `(unop% Neg.neg $x)
/-!
We have a macro to make `x • y` notation participate in the expression tree elaborator,
like other arithmetic expressions such as `+`, `*`, `/`, `^`, `=`, inequalities, etc.
The macro is using the `leftact%` elaborator introduced in
[this RFC](https://github.com/leanprover/lean4/issues/2854).
As a concrete example of the effect of this macro, consider
```lean
variable [Ring R] [AddCommMonoid M] [Module R M] (r : R) (N : Submodule R M) (m : M) (n : N)
#check m + r • n
```
Without the macro, the expression would elaborate as `m + ↑(r • n : ↑N) : M`.
With the macro, the expression elaborates as `m + r • (↑n : M) : M`.
To get the first interpretation, one can write `m + (r • n :)`.
Here is a quick review of the expression tree elaborator:
1. It builds up an expression tree of all the immediately accessible operations
that are marked with `binop%`, `unop%`, `leftact%`, `rightact%`, `binrel%`, etc.
2. It elaborates every leaf term of this tree
(without an expected type, so as if it were temporarily wrapped in `(... :)`).
3. Using the types of each elaborated leaf, it computes a supremum type they can all be
coerced to, if such a supremum exists.
4. It inserts coercions around leaf terms wherever needed.
The hypothesis is that individual expression trees tend to be calculations with respect
to a single algebraic structure.
Note(kmill): If we were to remove `HSMul` and switch to using `SMul` directly,
then the expression tree elaborator would not be able to insert coercions within the right operand;
they would likely appear as `↑(x • y)` rather than `x • ↑y`, unlike other arithmetic operations.
-/
@[inherit_doc HSMul.hSMul]
macro_rules | `($x $y) => `(leftact% HSMul.hSMul $x $y)
recommended_spelling "or" for "|||" in [HOr.hOr, «term_|||_»]
recommended_spelling "xor" for "^^^" in [HXor.hXor, «term_^^^_»]
@@ -358,7 +323,6 @@ recommended_spelling "mul" for "*" in [HMul.hMul, «term_*_»]
recommended_spelling "div" for "/" in [HDiv.hDiv, «term_/_»]
recommended_spelling "mod" for "%" in [HMod.hMod, «term_%_»]
recommended_spelling "pow" for "^" in [HPow.hPow, «term_^_»]
recommended_spelling "smul" for "" in [HSMul.hSMul, «term__»]
recommended_spelling "append" for "++" in [HAppend.hAppend, «term_++_»]
/-- when used as a unary operator -/
recommended_spelling "neg" for "-" in [Neg.neg, «term-_»]

View File

@@ -1348,23 +1348,6 @@ class HPow (α : Type u) (β : Type v) (γ : outParam (Type w)) where
The meaning of this notation is type-dependent. -/
hPow : α β γ
/--
The notation typeclass for heterogeneous scalar multiplication.
This enables the notation `a • b : γ` where `a : α`, `b : β`.
It is assumed to represent a left action in some sense.
The notation `a • b` is augmented with a macro (below) to have it elaborate as a left action.
Only the `b` argument participates in the elaboration algorithm: the algorithm uses the type of `b`
when calculating the type of the surrounding arithmetic expression
and it tries to insert coercions into `b` to get some `b'`
such that `a • b'` has the same type as `b'`.
See the module documentation near the macro for more details.
-/
class HSMul (α : Type u) (β : Type v) (γ : outParam (Type w)) where
/-- `a • b` computes the product of `a` and `b`.
The meaning of this notation is type-dependent, but it is intended to be used for left actions. -/
hSMul : α β γ
/--
The notation typeclass for heterogeneous append.
This enables the notation `a ++ b : γ` where `a : α`, `b : β`.
@@ -1527,12 +1510,6 @@ class HomogeneousPow (α : Type u) where
/-- `a ^ b` computes `a` to the power of `b` where `a` and `b` both have the same type. -/
protected pow : α α α
/-- Typeclass for types with a scalar multiplication operation, denoted `•` (`\bu`) -/
class SMul (M : Type u) (α : Type v) where
/-- `a • b` computes the product of `a` and `b`. The meaning of this notation is type-dependent,
but it is intended to be used for left actions. -/
smul : M α α
/-- The homogeneous version of `HAppend`: `a ++ b : α` where `a b : α`. -/
class Append (α : Type u) where
/-- `a ++ b` is the result of concatenation of `a` and `b`. See `HAppend`. -/
@@ -1624,13 +1601,6 @@ instance instPowNat [NatPow α] : Pow α Nat where
instance [HomogeneousPow α] : Pow α α where
pow a b := HomogeneousPow.pow a b
@[default_instance]
instance instHSMul {α β} [SMul α β] : HSMul α β β where
hSMul := SMul.smul
instance (priority := 910) {α : Type u} [Mul α] : SMul α α where
smul x y := Mul.mul x y
@[default_instance]
instance [Append α] : HAppend α α α where
hAppend a b := Append.append a b
@@ -2333,8 +2303,8 @@ Examples:
def UInt32.decLe (a b : UInt32) : Decidable (LE.le a b) :=
inferInstanceAs (Decidable (LE.le a.toBitVec b.toBitVec))
attribute [instance] UInt32.decLt UInt32.decLe
instance (a b : UInt32) : Decidable (LT.lt a b) := UInt32.decLt a b
instance (a b : UInt32) : Decidable (LE.le a b) := UInt32.decLe a b
instance : Max UInt32 := maxOfLe
instance : Min UInt32 := minOfLe

View File

@@ -309,10 +309,6 @@ theorem exists_or : (∃ x, p x q x) ↔ (∃ x, p x) ∃ x, q x :=
theorem Exists.nonempty : ( x, p x) Nonempty α | x, _ => x
@[deprecated Exists.nonempty (since := "2025-05-19")]
theorem nonempty_of_exists {α : Sort u} {p : α Prop} : Exists (fun x => p x) Nonempty α
| w, _ => w
theorem not_forall_of_exists_not {p : α Prop} : ( x, ¬p x) ¬ x, p x
| x, hn, h => hn (h x)

View File

@@ -255,7 +255,8 @@ abbrev measure {α : Sort u} (f : α → Nat) : WellFoundedRelation α :=
abbrev sizeOfWFRel {α : Sort u} [SizeOf α] : WellFoundedRelation α :=
measure sizeOf
attribute [instance low] sizeOfWFRel
instance (priority := low) [SizeOf α] : WellFoundedRelation α :=
sizeOfWFRel
namespace Prod
open WellFounded

View File

@@ -69,6 +69,15 @@ partial def merge (v₁ v₂ : Value) : Value :=
| choice vs, v => choice <| addChoice merge vs v
| v, choice vs => choice <| addChoice merge vs v
protected partial def format : Value Format
| top => "top"
| bot => "bot"
| choice vs => format "@" ++ @List.format _ Value.format vs
| ctor i vs => format "#" ++ if vs.isEmpty then format i.name else Format.paren (format i.name ++ @formatArray _ Value.format vs)
instance : ToFormat Value := Value.format
instance : ToString Value := Format.pretty Value.format
/--
In `truncate`, we approximate a value as `top` if depth > `truncateMaxDepth`.
TODO: add option to control this parameter.

View File

@@ -495,10 +495,7 @@ def emitNumLit (t : IRType) (v : Nat) : M Unit := do
else
emit "lean_cstr_to_nat(\""; emit v; emit "\")"
else
if v < UInt32.size then
emit v
else
emit v; emit "ULL"
emit v
def emitLit (z : VarId) (t : IRType) (v : LitVal) : M Unit := do
emitLhs z;

View File

@@ -67,10 +67,6 @@ def lowerLitValue (v : LCNF.LitValue) : LitVal :=
match v with
| .nat n => .num n
| .str s => .str s
| .uint8 v => .num (UInt8.toNat v)
| .uint16 v => .num (UInt16.toNat v)
| .uint32 v => .num (UInt32.toNat v)
| .uint64 v => .num (UInt64.toNat v)
-- TODO: This should be cached.
def lowerEnumToScalarType (name : Name) : M (Option IRType) := do

View File

@@ -36,20 +36,12 @@ def Param.toExpr (p : Param) : Expr :=
inductive LitValue where
| nat (val : Nat)
| str (val : String)
| uint8 (val : UInt8)
| uint16 (val : UInt16)
| uint32 (val : UInt32)
| uint64 (val : UInt64)
-- TODO: add constructors for `Int`, `Float`, `USize` ...
-- TODO: add constructors for `Int`, `Float`, `UInt` ...
deriving Inhabited, BEq, Hashable
def LitValue.toExpr : LitValue Expr
| .nat v => .lit (.natVal v)
| .str v => .lit (.strVal v)
| .uint8 v => .app (.const ``UInt8.ofNat []) (.lit (.natVal (UInt8.toNat v)))
| .uint16 v => .app (.const ``UInt16.ofNat []) (.lit (.natVal (UInt16.toNat v)))
| .uint32 v => .app (.const ``UInt32.ofNat []) (.lit (.natVal (UInt32.toNat v)))
| .uint64 v => .app (.const ``UInt64.ofNat []) (.lit (.natVal (UInt64.toNat v)))
inductive Arg where
| erased

View File

@@ -174,10 +174,6 @@ def ofLCNFLit : LCNF.LitValue → Value
| .nat n => ofNat n
-- TODO: We could make this much more precise but the payoff is questionable
| .str .. => .top
| .uint8 v => ofNat (UInt8.toNat v)
| .uint16 v => ofNat (UInt16.toNat v)
| .uint32 v => ofNat (UInt32.toNat v)
| .uint64 v => ofNat (UInt64.toNat v)
partial def proj : Value Nat Value
| .ctor _ vs , i => vs.getD i bot

View File

@@ -105,10 +105,6 @@ def inferLitValueType (value : LitValue) : Expr :=
match value with
| .nat .. => mkConst ``Nat
| .str .. => mkConst ``String
| .uint8 .. => mkConst ``UInt8
| .uint16 .. => mkConst ``UInt16
| .uint32 .. => mkConst ``UInt32
| .uint64 .. => mkConst ``UInt64
mutual
partial def inferArgType (arg : Arg) : InferTypeM Expr :=

View File

@@ -56,15 +56,10 @@ def ppArg (e : Arg) : M Format := do
def ppArgs (args : Array Arg) : M Format := do
prefixJoin " " args ppArg
def ppLitValue (lit : LitValue) : M Format := do
match lit with
| .nat v | .uint8 v | .uint16 v | .uint32 v | .uint64 v => return format v
| .str v => return format (repr v)
def ppLetValue (e : LetValue) : M Format := do
match e with
| .erased => return ""
| .lit v => ppLitValue v
| .lit v => ppExpr v.toExpr
| .proj _ i fvarId => return f!"{← ppFVar fvarId} # {i}"
| .fvar fvarId args => return f!"{← ppFVar fvarId}{← ppArgs args}"
| .const declName us args => return f!"{← ppExpr (.const declName us)}{← ppArgs args}"

View File

@@ -308,14 +308,6 @@ def higherOrderLiteralFolders : List (Name × Folder) := [
def Folder.mulShift [Literal α] [BEq α] (shiftLeft : Name) (pow2 : α α) (log2 : α α) : Folder :=
Folder.first #[Folder.mulLhsShift shiftLeft pow2 log2, Folder.mulRhsShift shiftLeft pow2 log2]
/--
Folder for ofNat operations on fixed-sized integer types.
-/
def Folder.ofNat (f : Nat LitValue) (args : Array Arg): FolderM (Option LetValue) := do
let #[.fvar fvarId] := args | return none
let some value getNatLit fvarId | return none
return some (.lit (f value))
/--
All arithmetic folders.
-/
@@ -363,13 +355,6 @@ def relationFolders : List (Name × Folder) := [
(``Bool.decEq, Folder.mkBinaryDecisionProcedure String.decEq)
]
def conversionFolders : List (Name × Folder) := [
(``UInt8.ofNat, Folder.ofNat (fun v => .uint8 (UInt8.ofNat v))),
(``UInt16.ofNat, Folder.ofNat (fun v => .uint16 (UInt16.ofNat v))),
(``UInt32.ofNat, Folder.ofNat (fun v => .uint32 (UInt32.ofNat v))),
(``UInt64.ofNat, Folder.ofNat (fun v => .uint64 (UInt64.ofNat v))),
]
/--
All string folders.
-/
@@ -402,7 +387,7 @@ private def getFolder (declName : Name) : CoreM Folder := do
ofExcept <| getFolderCore ( getEnv) ( getOptions) declName
def builtinFolders : SMap Name Folder :=
(arithmeticFolders ++ relationFolders ++ conversionFolders ++ higherOrderLiteralFolders ++ stringFolders).foldl (init := {}) fun s (declName, folder) =>
(arithmeticFolders ++ relationFolders ++ higherOrderLiteralFolders ++ stringFolders).foldl (init := {}) fun s (declName, folder) =>
s.insert declName folder
structure FolderOleanEntry where

View File

@@ -77,9 +77,12 @@ def lt (a b : JsonNumber) : Bool :=
else if ae > be then false
else am < bm
instance ltProp : LT JsonNumber :=
def ltProp : LT JsonNumber :=
fun a b => lt a b = true
instance : LT JsonNumber :=
ltProp
instance (a b : JsonNumber) : Decidable (a < b) :=
inferInstanceAs (Decidable (lt a b = true))

View File

@@ -9,7 +9,6 @@ import Lean.Elab.Term
import Lean.Elab.BindersUtil
import Lean.Elab.SyntheticMVars
import Lean.Elab.PreDefinition.TerminationHint
import Lean.Elab.Match
namespace Lean.Elab.Term
open Meta
@@ -554,43 +553,6 @@ def expandMatchAltsIntoMatch (ref : Syntax) (matchAlts : Syntax) (useExplicit :=
def expandMatchAltsIntoMatchTactic (ref : Syntax) (matchAlts : Syntax) : MacroM Syntax :=
withRef ref <| expandMatchAltsIntoMatchAux matchAlts (isTactic := true) (useExplicit := false) (getMatchAltsNumPatterns matchAlts) #[] #[]
/--
Sanity-checks the number of patterns in each alternative of a definition by pattern matching.
Specifically, verifies that all alternatives have the same number of patterns and that the number
of patterns is upper-bounded by the number of (dependent) arrows in the expected type.
Note: This function assumes that the number of patterns in the first alternative will be equal to
`numDiscrs` (since we use the first alternative to infer the arity of the generated matcher in
`getMatchAltsNumPatterns`).
-/
private def checkMatchAltPatternCounts (matchAlts : Syntax) (numDiscrs : Nat) (expectedType : Expr)
: MetaM Unit := do
let sepPats (pats : List Syntax) := MessageData.joinSep (pats.map toMessageData) ", "
let maxDiscrs? forallTelescopeReducing expectedType fun xs e =>
if e.getAppFn.isMVar then pure none else pure (some xs.size)
let matchAltViews := matchAlts[0].getArgs.filterMap getMatchAlt
let numPatternsStr (n : Nat) := s!"{n} {if n == 1 then "pattern" else "patterns"}"
if h : matchAltViews.size > 0 then
if let some maxDiscrs := maxDiscrs? then
if numDiscrs > maxDiscrs then
if maxDiscrs == 0 then
throwErrorAt matchAltViews[0].lhs m!"Cannot define a value of type{indentExpr expectedType}\n\
by pattern matching because it is not a function type"
else
throwErrorAt matchAltViews[0].lhs m!"Too many patterns in match alternative: \
At most {numPatternsStr maxDiscrs} expected in a definition of type {indentExpr expectedType}\n\
but found {numDiscrs}:{indentD <| sepPats matchAltViews[0].patterns.toList}"
-- Catch inconsistencies between pattern counts here so that we can report them as "inconsistent"
-- rather than as "too many" or "too few" (as the `match` elaborator does)
for view in matchAltViews do
let numPats := view.patterns.size
if numPats != numDiscrs then
let origPats := sepPats matchAltViews[0].patterns.toList
let pats := sepPats view.patterns.toList
throwErrorAt view.lhs m!"Inconsistent number of patterns in match alternatives: This \
alternative contains {numPatternsStr numPats}:{indentD pats}\n\
but a preceding alternative contains {numDiscrs}:{indentD origPats}"
/--
Similar to `expandMatchAltsIntoMatch`, but supports an optional `where` clause.
@@ -619,21 +581,19 @@ private def checkMatchAltPatternCounts (matchAlts : Syntax) (numDiscrs : Nat) (e
| i, _ => ... f i + g i ...
```
-/
def expandMatchAltsWhereDecls (matchAltsWhereDecls : Syntax) (expectedType : Expr) : TermElabM Syntax :=
def expandMatchAltsWhereDecls (matchAltsWhereDecls : Syntax) : MacroM Syntax :=
let matchAlts := matchAltsWhereDecls[0]
-- matchAltsWhereDecls[1] is the termination hints, collected elsewhere
let whereDeclsOpt := matchAltsWhereDecls[2]
let rec loop (i : Nat) (discrs : Array Syntax) : TermElabM Syntax :=
let rec loop (i : Nat) (discrs : Array Syntax) : MacroM Syntax :=
match i with
| 0 => do
checkMatchAltPatternCounts matchAlts discrs.size expectedType
let matchStx `(match $[@$discrs:term],* with $matchAlts:matchAlts)
liftMacroM do
let matchStx clearInMatch matchStx discrs
if whereDeclsOpt.isNone then
return matchStx
else
expandWhereDeclsOpt whereDeclsOpt matchStx
let matchStx clearInMatch matchStx discrs
if whereDeclsOpt.isNone then
return matchStx
else
expandWhereDeclsOpt whereDeclsOpt matchStx
| n+1 => withFreshMacroScope do
let body loop n (discrs.push ( `(x)))
`(@fun x => $body)

View File

@@ -138,7 +138,7 @@ private def reorderCtorArgs (ctorType : Expr) : MetaM Expr := do
else
let r mkForallFVars (bsPrefix ++ as) type
/- `r` already contains the resulting type.
To be able to produce better error messages, we copy the first `bsPrefix.size` binder names from `C` to `r`.
To be able to produce more better error messages, we copy the first `bsPrefix.size` binder names from `C` to `r`.
This is important when some of constructor parameters were inferred using the auto-bound implicit feature.
For example, in the following declaration.
```
@@ -178,8 +178,7 @@ private def elabCtors (indFVars : Array Expr) (params : Array Expr) (r : ElabHea
match ctorView.type? with
| none =>
if indFamily then
throwError "Missing resulting type for constructor '{ctorView.declName}': \
Its resulting type must be specified because it is part of an inductive family declaration"
throwError "constructor resulting type must be specified in inductive family declaration"
return mkAppN indFVar params
| some ctorType =>
let type Term.elabType ctorType
@@ -189,9 +188,9 @@ private def elabCtors (indFVars : Array Expr) (params : Array Expr) (r : ElabHea
let type checkParamOccs type
forallTelescopeReducing type fun _ resultingType => do
unless resultingType.getAppFn == indFVar do
throwUnexpectedResultingTypeMismatch resultingType indFVar ctorView.declName ctorType
throwError "unexpected constructor resulting type{indentExpr resultingType}"
unless ( isType resultingType) do
throwUnexpectedResultingTypeNotType resultingType ctorView.declName ctorType
throwError "unexpected constructor resulting type, type expected{indentExpr resultingType}"
return type
let type elabCtorType
Term.synthesizeSyntheticMVarsNoPostponing
@@ -230,62 +229,23 @@ private def elabCtors (indFVars : Array Expr) (params : Array Expr) (r : ElabHea
trace[Elab.inductive] "{ctorView.declName} : {type}"
return { name := ctorView.declName, type }
where
/--
Ensures that the arguments to recursive occurrences of the type family being defined match the
parameters from the inductive definition.
-/
checkParamOccs (ctorType : Expr) : MetaM Expr :=
let visit (e : Expr) : StateT (List Expr) MetaM TransformStep := do
let visit (e : Expr) : MetaM TransformStep := do
let f := e.getAppFn
if indFVars.contains f then
let mut args := e.getAppArgs
-- Prefer throwing an "argument mismatch" error rather than a "missing parameter" one
for i in [:min args.size params.size] do
let param := params[i]!
unless args.size params.size do
throwError "unexpected inductive type occurrence{indentExpr e}"
for h : i in [:params.size] do
let param := params[i]
let arg := args[i]!
unless ( isDefEq param arg) do
let (arg, param) addPPExplicitToExposeDiff arg param
let msg := m!"Mismatched inductive type parameter in{indentExpr e}\nThe provided argument\
{indentExpr arg}\nis not definitionally equal to the expected parameter{indentExpr param}"
let noteMsg := m!"The value of parameter '{param}' must be fixed throughout the inductive \
declaration. Consider making this parameter an index if it must vary."
throwError msg ++ .note noteMsg
throwError "inductive datatype parameter mismatch{indentExpr arg}\nexpected{indentExpr param}"
args := args.set! i param
unless args.size params.size do
let expected := mkAppN f params
let containingExprMsg := ( get).head?.map toMessageData |>.getD m!"<missing>"
let msg :=
m!"Missing parameter(s) in occurrence of inductive type: In the expression{indentD containingExprMsg}\n\
found{indentExpr e}\nbut expected all parameters to be specified:{indentExpr expected}"
let noteMsg :=
m!"All occurrences of an inductive type in the types of its constructors must specify its \
fixed parameters. Only indices can be omitted in a partial application of the type constructor."
throwError msg ++ .note noteMsg
return TransformStep.done (mkAppN f args)
else
modify fun es => e :: es
return .continue
let popContainingExpr (e : Expr) : StateT (List Expr) MetaM TransformStep := do
modify fun es => es.drop 1
return .done e
transform ctorType (pre := visit) (post := popContainingExpr) |>.run' [ctorType]
throwUnexpectedResultingTypeMismatch (resultingType indFVar : Expr) (declName : Name) (ctorType : Syntax) := do
let lazyAppMsg := MessageData.ofLazyM do
if let some fvarId := indFVar.fvarId? then
if let some decl := ( getLCtx).find? fvarId then
if ( whnfD decl.type).isForall then
return m!" an application of"
return m!""
throwErrorAt ctorType "Unexpected resulting type for constructor '{declName}': \
Expected{lazyAppMsg}{indentExpr indFVar}\nbut found{indentExpr resultingType}"
throwUnexpectedResultingTypeNotType (resultingType : Expr) (declName : Name) (ctorType : Syntax) := do
let lazyMsg := MessageData.ofLazyM do
let resultingTypeType inferType resultingType
return indentExpr resultingTypeType
throwErrorAt ctorType "Unexpected resulting type for constructor '{declName}': \
Expected a type, but found{indentExpr resultingType}\nof type{lazyMsg}"
transform ctorType (pre := visit)
@[builtin_inductive_elab Lean.Parser.Command.inductive, builtin_inductive_elab Lean.Parser.Command.classInductive]
def elabInductiveCommand : InductiveElabDescr where

View File

@@ -104,7 +104,7 @@ where
matchType := b.instantiate1 discr
discrs := discrs.push { expr := discr }
| _ =>
throwError "Invalid motive provided to match-expression: Function type with arity {discrStxs.size} expected"
throwError "invalid motive provided to match-expression, function type with arity #{discrStxs.size} expected"
return (discrs, isDep)
markIsDep (r : ElabMatchTypeAndDiscrsResult) :=
@@ -156,20 +156,16 @@ private def getMatchGeneralizing? : Syntax → Option Bool
| `(match (generalizing := false) $[$motive]? $_discrs,* with $_alts:matchAlt*) => some false
| _ => none
/-- Given the `stx` of a single match alternative, return a corresponding `MatchAltView`. -/
def getMatchAlt : Syntax Option MatchAltView
| alt@`(matchAltExpr| | $patterns,* => $rhs) => some {
/-- Given `stx` a match-expression, return its alternatives. -/
private def getMatchAlts : Syntax Array MatchAltView
| `(match $[$gen]? $[$motive]? $_discrs,* with $alts:matchAlt*) =>
alts.filterMap fun alt => match alt with
| `(matchAltExpr| | $patterns,* => $rhs) => some {
ref := alt,
patterns := patterns,
lhs := alt[1],
rhs := rhs
}
| _ => none
/-- Given `stx` a match-expression, return its alternatives. -/
def getMatchAlts : Syntax Array MatchAltView
| `(match $[$gen]? $[$motive]? $_discrs,* with $alts:matchAlt*) =>
alts.filterMap getMatchAlt
| _ => none
| _ => #[]
@[builtin_term_elab inaccessible] def elabInaccessible : TermElab := fun stx expectedType? => do
@@ -337,31 +333,16 @@ private partial def eraseIndices (type : Expr) : MetaM Expr := do
let params args[:info.numParams].toArray.mapM eraseIndices
let result := mkAppN type'.getAppFn params
let resultType inferType result
let (newIndices, _, _) forallMetaTelescopeReducing resultType (some (args.size - info.numParams))
let (newIndices, _, _) forallMetaTelescopeReducing resultType (some (args.size - info.numParams))
return mkAppN result newIndices
private def withPatternElabConfig (x : TermElabM α) : TermElabM α :=
withoutErrToSorry <| withReader (fun ctx => { ctx with inPattern := true }) <| x
open Meta.Match (throwIncorrectNumberOfPatternsAt logIncorrectNumberOfPatternsAt)
private def elabPatterns (patternStxs : Array Syntax) (numDiscrs : Nat) (matchType : Expr) : ExceptT PatternElabException TermElabM (Array Expr × Expr) :=
private def elabPatterns (patternStxs : Array Syntax) (matchType : Expr) : ExceptT PatternElabException TermElabM (Array Expr × Expr) :=
withReader (fun ctx => { ctx with implicitLambda := false }) do
let origMatchType := matchType
let mut patterns := #[]
let mut matchType := matchType
let mut patternStxs := patternStxs
if patternStxs.size < numDiscrs then
-- If there are too few patterns, log the error but continue elaborating with holes for the missing patterns
logIncorrectNumberOfPatternsAt ( getRef) "Not enough" numDiscrs patternStxs.size patternStxs.toList
let numHoles := numDiscrs - patternStxs.size
let mut extraStxs := Array.emptyWithCapacity numHoles
for _ in [:numHoles] do
extraStxs := extraStxs.push ( `(_))
patternStxs := patternStxs ++ extraStxs
else if patternStxs.size > numDiscrs then
throwIncorrectNumberOfPatternsAt ( getRef) "Too many" numDiscrs patternStxs.size patternStxs.toList
for h : idx in [:patternStxs.size] do
let patternStx := patternStxs[idx]
matchType whnf matchType
@@ -384,7 +365,7 @@ private def elabPatterns (patternStxs : Array Syntax) (numDiscrs : Nat) (matchTy
| none => throw ex
matchType := b.instantiate1 pattern
patterns := patterns.push pattern
| _ => throwError "Failed to elaborate match expression: Inferred {idx} discriminants, but more were found"
| _ => throwError "unexpected match type"
return (patterns, matchType)
open Meta.Match (Pattern Pattern.var Pattern.inaccessible Pattern.ctor Pattern.as Pattern.val Pattern.arrayLit AltLHS MatcherResult)
@@ -392,7 +373,7 @@ open Meta.Match (Pattern Pattern.var Pattern.inaccessible Pattern.ctor Pattern.a
namespace ToDepElimPattern
private def throwInvalidPattern (e : Expr) : MetaM α :=
throwError "Invalid pattern{indentExpr e}"
throwError "invalid pattern {indentExpr e}"
structure State where
patternVars : Array Expr := #[]
@@ -501,7 +482,7 @@ partial def normalize (e : Expr) : M Expr := do
let p := e.getArg! 2
let h := e.getArg! 3
unless x.consumeMData.isFVar && h.consumeMData.isFVar do
throwError "Unexpected occurrence of auxiliary declaration 'namedPattern'"
throwError "unexpected occurrence of auxiliary declaration 'namedPattern'"
addVar x
let p normalize p
addVar h
@@ -603,7 +584,7 @@ private partial def toPattern (e : Expr) : MetaM Pattern := do
let p toPattern <| e.getArg! 2
match e.getArg! 1, e.getArg! 3 with
| Expr.fvar x, Expr.fvar h => return Pattern.as x p h
| _, _ => throwError "Unexpected occurrence of auxiliary declaration 'namedPattern'"
| _, _ => throwError "unexpected occurrence of auxiliary declaration 'namedPattern'"
else if ( isMatchValue e) then
return Pattern.val ( normLitValue e)
else if e.isFVar then
@@ -701,7 +682,7 @@ partial def main (patternVarDecls : Array PatternVarDecl) (ps : Array Expr) (mat
for explicit in explicitPatternVars do
unless patternVars.any (· == mkFVar explicit) do
withInPattern do
throwError "Invalid pattern(s): `{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")}"
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}"
withErasedFVars explicitPatternVars do
@@ -753,9 +734,9 @@ end ToDepElimPattern
def withDepElimPatterns (patternVarDecls : Array PatternVarDecl) (ps : Array Expr) (matchType : Expr) (k : Array LocalDecl Array Pattern Expr TermElabM α) : TermElabM α := do
ToDepElimPattern.main patternVarDecls ps matchType k
private def withElaboratedLHS {α} (ref : Syntax) (patternVarDecls : Array PatternVarDecl) (patternStxs : Array Syntax) (lhsStx : Syntax) (numDiscrs : Nat) (matchType : Expr)
private def withElaboratedLHS {α} (ref : Syntax) (patternVarDecls : Array PatternVarDecl) (patternStxs : Array Syntax) (matchType : Expr)
(k : AltLHS Expr TermElabM α) : ExceptT PatternElabException TermElabM α := do
let (patterns, matchType) withSynthesize <| withRef lhsStx <| elabPatterns patternStxs numDiscrs matchType
let (patterns, matchType) withSynthesize <| elabPatterns patternStxs matchType
id (α := TermElabM α) do
trace[Elab.match] "patterns: {patterns}"
withDepElimPatterns patternVarDecls patterns matchType fun localDecls patterns matchType => do
@@ -811,7 +792,7 @@ private def elabMatchAltView (discrs : Array Discr) (alt : MatchAltView) (matchT
let (patternVars, alt) collectPatternVars alt
trace[Elab.match] "patternVars: {patternVars}"
withPatternVars patternVars fun patternVarDecls => do
withElaboratedLHS alt.ref patternVarDecls alt.patterns alt.lhs discrs.size matchType fun altLHS matchType =>
withElaboratedLHS alt.ref patternVarDecls alt.patterns matchType fun altLHS matchType =>
withEqs discrs altLHS.patterns fun eqs =>
withLocalInstances altLHS.fvarDecls do
trace[Elab.match] "elabMatchAltView: {matchType}"
@@ -827,7 +808,7 @@ private def elabMatchAltView (discrs : Array Discr) (alt : MatchAltView) (matchT
let rhs elabTermEnsuringType alt.rhs matchType'
-- We use all approximations to ensure the auxiliary type is defeq to the original one.
unless ( fullApproxDefEq <| isDefEq matchType' matchType) do
throwError "Type mismatch: Alternative {← mkHasTypeButIsExpectedMsg matchType' matchType}"
throwError "type mismatch, alternative {← mkHasTypeButIsExpectedMsg matchType' matchType}"
let xs := altLHS.fvarDecls.toArray.map LocalDecl.toExpr ++ eqs
let rhs if xs.isEmpty then pure <| mkSimpleThunk rhs else mkLambdaFVars xs rhs
trace[Elab.match] "rhs: {rhs}"
@@ -1019,29 +1000,16 @@ register_builtin_option match.ignoreUnusedAlts : Bool := {
descr := "if true, do not generate error if an alternative is not used"
}
/--
Constructs a "redundant alternative" error message.
Optionally accepts the name of the constructor (e.g., for use in the `induction` tactic) and/or the
message-data representation of the alternative in question.
-/
def mkRedundantAlternativeMsg (altName? : Option Name) (altMsg? : Option MessageData) : MessageData :=
let altName := altName?.map (m!" '{toMessageData ·}'") |>.getD ""
let altMsg := altMsg?.map (indentD · ++ m!"\n") |>.getD " this pattern "
m!"Redundant alternative{altName}: Any expression matching{altMsg}will match one of the preceding alternatives"
def reportMatcherResultErrors (altLHSS : List AltLHS) (result : MatcherResult) : TermElabM Unit := do
unless result.counterExamples.isEmpty do
withHeadRefOnly <| logError m!"Missing cases:\n{Meta.Match.counterExamplesToMessageData result.counterExamples}"
withHeadRefOnly <| logError m!"missing cases:\n{Meta.Match.counterExamplesToMessageData result.counterExamples}"
return ()
unless match.ignoreUnusedAlts.get ( getOptions) || result.unusedAltIdxs.isEmpty do
let mut i := 0
for alt in altLHSS do
if result.unusedAltIdxs.contains i then
withRef alt.ref do withInPattern do withExistingLocalDecls alt.fvarDecls do
let pats alt.patterns.mapM fun p => return toMessageData ( Pattern.toExpr p)
let pats := MessageData.joinSep pats ", "
logError (mkRedundantAlternativeMsg none pats)
withRef alt.ref do
logError "redundant alternative"
i := i + 1
/--
@@ -1063,7 +1031,7 @@ private def elabMatchAux (generalizing? : Option Bool) (discrStxs : Array Syntax
let mut generalizing? := generalizing?
if !matchOptMotive.isNone then
if generalizing? == some true then
throwError "The '(generalizing := true)' parameter is not supported when the 'match' motive is explicitly provided"
throwError "the '(generalizing := true)' parameter is not supported when the 'match' motive is explicitly provided"
generalizing? := some false
let (discrs, matchType, altLHSS, isDep, rhss) commitIfDidNotPostpone do
let discrs, matchType, isDep, altViews elabMatchTypeAndDiscrs discrStxs matchOptMotive altViews expectedType
@@ -1118,12 +1086,12 @@ private def elabMatchAux (generalizing? : Option Bool) (discrStxs : Array Syntax
withExistingLocalDecls altLHS.fvarDecls do
runPendingTacticsAt d.type
if ( instantiateMVars d.type).hasExprMVar then
throwMVarError m!"Invalid match expression: The type of pattern variable '{d.toExpr}' contains metavariables:{indentExpr d.type}"
throwMVarError m!"invalid match-expression, type of pattern variable '{d.toExpr}' contains metavariables{indentExpr d.type}"
for p in altLHS.patterns do
if ( Match.instantiatePatternMVars p).hasExprMVar then
tryPostpone
withExistingLocalDecls altLHS.fvarDecls do
throwMVarError m!"Invalid match expression: This pattern contains metavariables:{indentExpr (← p.toExpr)}"
throwMVarError m!"invalid match-expression, pattern contains metavariables{indentExpr (← p.toExpr)}"
pure altLHS
return (discrs, matchType, altLHSS, isDep, rhss)
if let some r if isDep then pure none else isMatchUnit? altLHSS rhss then

View File

@@ -20,7 +20,6 @@ def «match» := leading_parser:leadPrec "match " >> sepBy1 matchDiscr ", " >> o
structure MatchAltView where
ref : Syntax
patterns : Array Syntax
lhs : Syntax
rhs : Syntax
deriving Inhabited

View File

@@ -353,17 +353,17 @@ def declVal := declValSimple <|> declValEqns <|> Term.whereDecls
The `Termination.suffix` is ignored here, and extracted in `declValToTerminationHint`.
-/
private def declValToTerm (declVal : Syntax) (expectedType : Expr) : TermElabM Syntax := withRef declVal do
private def declValToTerm (declVal : Syntax) : MacroM Syntax := withRef declVal do
if declVal.isOfKind ``Parser.Command.declValSimple then
liftMacroM <| expandWhereDeclsOpt declVal[3] declVal[1]
expandWhereDeclsOpt declVal[3] declVal[1]
else if declVal.isOfKind ``Parser.Command.declValEqns then
expandMatchAltsWhereDecls declVal[0] expectedType
expandMatchAltsWhereDecls declVal[0]
else if declVal.isOfKind ``Parser.Command.whereStructInst then
liftMacroM <| expandWhereStructInst declVal
expandWhereStructInst declVal
else if declVal.isMissing then
throwErrorAt declVal "declaration body is missing"
Macro.throwErrorAt declVal "declaration body is missing"
else
throwErrorAt declVal "unexpected declaration body"
Macro.throwErrorAt declVal "unexpected declaration body"
/-- Elaborates the termination hints in a `declVal` syntax. -/
private def declValToTerminationHint (declVal : Syntax) : TermElabM TerminationHints :=
@@ -464,7 +464,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
withReuseContext header.value do
withTraceNode `Elab.definition.value (fun _ => pure header.declName) do
withDeclName header.declName <| withLevelNames header.levelNames do
let valStx declValToTerm header.value header.type
let valStx liftMacroM <| declValToTerm header.value
(if header.kind.isTheorem && !deprecated.oldSectionVars.get ( getOptions) then withHeaderSecVars vars sc #[header] else fun x => x #[]) fun vars => do
forallBoundedTelescope header.type header.numParams fun xs type => do
-- Add new info nodes for new fvars. The server will detect all fvars of a binder by the binder's source location.

View File

@@ -219,7 +219,7 @@ private def checkUnsafe (rs : Array PreElabHeaderResult) : TermElabM Unit := do
let isUnsafe := rs[0]!.view.modifiers.isUnsafe
for r in rs do
unless r.view.modifiers.isUnsafe == isUnsafe do
throwErrorAt r.view.ref "invalid inductive type, cannot mix unsafe and safe declarations in mutually inductive datatypes"
throwErrorAt r.view.ref "invalid inductive type, cannot mix unsafe and safe declarations in a mutually inductive datatypes"
private def checkClass (rs : Array PreElabHeaderResult) : TermElabM Unit := do
if rs.size > 1 then

View File

@@ -16,7 +16,7 @@ abbrev PatternVar := Syntax -- TODO: should be `Ident`
/-!
Patterns define new local variables.
This module collects them and preprocesses `_` occurring in patterns.
This module collect them and preprocess `_` occurring in patterns.
Recall that an `_` may represent anonymous variables or inaccessible terms
that are implied by typing constraints. Thus, we represent them with fresh named holes `?x`.
After we elaborate the pattern, if the metavariable remains unassigned, we transform it into
@@ -49,7 +49,7 @@ abbrev M := StateRefT State TermElabM
private def throwCtorExpected {α} (ident : Option Syntax) : M α := do
let message : MessageData :=
"Invalid pattern: Expected a constructor or constant marked with `[match_pattern]`"
"invalid pattern, constructor or constant marked with '[match_pattern]' expected"
let some idStx := ident | throwError message
let name := idStx.getId
if let .anonymous := name then throwError message
@@ -64,7 +64,7 @@ private def throwCtorExpected {α} (ident : Option Syntax) : M α := do
if candidates.size = 0 then
throwError message
else if h : candidates.size = 1 then
throwError message ++ .hint' m!"'{candidates[0]}' is similar"
throwError message ++ m!"\n\nSuggestion: '{candidates[0]}' is similar"
else
let sorted := candidates.qsort (·.toString < ·.toString)
let diff :=
@@ -73,28 +73,28 @@ private def throwCtorExpected {α} (ident : Option Syntax) : M α := do
let suggestions : MessageData := .group <|
.joinSep ((sorted.extract 0 10 |>.toList |>.map (showName env)) ++ diff)
("," ++ Format.line)
throwError message ++ .group (.hint' ("These are similar:" ++ .nestD (Format.line ++ suggestions)))
throwError message ++ .group ("\n\nSuggestions:" ++ .nestD (Format.line ++ suggestions))
where
-- Create some `MessageData` for a name that shows it without an `@`, but with the metadata that
-- makes infoview hovers and the like work. This technique only works because the names are known
-- to be global constants, so we don't need the local context.
showName (env : Environment) (n : Name) : MessageData :=
let params :=
env.constants.find?' n |>.map (·.levelParams.map Level.param) |>.getD []
.ofFormatWithInfos {
fmt := "'" ++ .tag 0 (format n) ++ "'",
infos :=
.fromList [(0, .ofTermInfo {
lctx := .empty,
expr := .const n params,
stx := .ident .none (toString n).toSubstring n [.decl n []],
elaborator := `Delab,
expectedType? := none
})] _
}
let params :=
env.constants.find?' n |>.map (·.levelParams.map Level.param) |>.getD []
.ofFormatWithInfos {
fmt := "'" ++ .tag 0 (format n) ++ "'",
infos :=
.fromList [(0, .ofTermInfo {
lctx := .empty,
expr := .const n params,
stx := .ident .none (toString n).toSubstring n [.decl n []],
elaborator := `Delab,
expectedType? := none
})] _
}
private def throwInvalidPattern {α} : M α :=
throwError "Invalid pattern"
throwError "invalid pattern"
/-!
An application in a pattern can be
@@ -118,26 +118,6 @@ structure Context where
newArgs : Array Term := #[]
deriving Inhabited
private def throwInvalidNamedArgs [Monad m] [MonadError m]
(namedArgs : Array NamedArg) (funId : Term) : m α :=
let names := (namedArgs.map fun narg => m!"'{narg.name}'").toList
let nameStr := if names.length == 1 then "name" else "names"
throwError m!"Invalid argument {nameStr} {.andList names} for function '{funId}'"
private def throwWrongArgCount (ctx : Context) (tooMany : Bool) : M α := do
if !ctx.namedArgs.isEmpty then
throwInvalidNamedArgs ctx.namedArgs ctx.funId
let numExpectedArgs :=
(if ctx.explicit then ctx.paramDecls else ctx.paramDecls.filter (·.2.isExplicit)).size
let argKind := if ctx.explicit then "" else "explicit "
let argWord := if numExpectedArgs == 1 then "argument" else "arguments"
let discrepancyKind := if tooMany then "Too many" else "Not enough"
let mut msg := m!"Invalid pattern: {discrepancyKind} arguments to '{ctx.funId}'; \
expected {numExpectedArgs} {argKind}{argWord}"
if !tooMany then
msg := msg ++ .hint' "To ignore all remaining arguments, use the ellipsis notation `..`"
throwError msg
private def isDone (ctx : Context) : Bool :=
ctx.paramDeclIdx ctx.paramDecls.size
@@ -145,10 +125,8 @@ private def finalize (ctx : Context) : M Syntax := do
if ctx.namedArgs.isEmpty && ctx.args.isEmpty then
let fStx `(@$(ctx.funId):ident)
return Syntax.mkApp fStx ctx.newArgs
else if ctx.args.isEmpty then
throwInvalidNamedArgs ctx.namedArgs ctx.funId
else
throwWrongArgCount ctx true
throwError "too many arguments"
private def isNextArgAccessible (ctx : Context) : Bool :=
let i := ctx.paramDeclIdx
@@ -169,12 +147,12 @@ private def getNextParam (ctx : Context) : (Name × BinderInfo) × Context :=
private def processVar (idStx : Syntax) : M Syntax := do
unless idStx.isIdent do
throwErrorAt idStx "Invalid pattern variable: Identifier expected, but found{indentD idStx}"
throwErrorAt idStx "identifier expected"
let id := idStx.getId
unless id.eraseMacroScopes.isAtomic do
throwError "Invalid pattern variable: Variable name must be atomic, but '{id}' has multiple components"
throwError "invalid pattern variable, must be atomic"
if ( get).found.contains id then
throwError "Invalid pattern variable: Variable name '{id}' was already used"
throwError "invalid pattern, variable '{id}' occurred more than once"
modify fun s => { s with vars := s.vars.push idStx, found := s.found.insert id }
return idStx
@@ -197,7 +175,6 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
let elems stx[1].getArgs.mapSepElemsM collect
return stx.setArg 1 <| mkNullNode elems
else if k == ``Lean.Parser.Term.dotIdent then
-- TODO: validate that `stx` corresponds to a valid constructor or match pattern
return stx
else if k == ``Lean.Parser.Term.hole then
`(.( $stx ))
@@ -210,8 +187,6 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
return stx.setArg 1 t
else if k == ``Lean.Parser.Term.explicitUniv then
processCtor stx[0]
else if k == ``Lean.Parser.Term.explicit then
processCtor stx
else if k == ``Lean.Parser.Term.namedPattern then
/- Recall that
```
@@ -279,13 +254,13 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
set stateSaved
argsNew := argsNew.push ( collect arg)
unless samePatternsVariables stateSaved.vars.size stateNew ( get) do
throwError "Invalid pattern: Overloaded notation is only allowed when all alternatives have the same set of pattern variables"
throwError "invalid pattern, overloaded notation is only allowed when all alternative have the same set of pattern variables"
set stateNew
return mkNode choiceKind argsNew
else match stx with
| `({ $[$srcs?,* with]? $fields,* $[..%$ell?]? $[: $ty?]? }) =>
if let some srcs := srcs? then
throwErrorAt (mkNullNode srcs) "Invalid struct instance pattern: `with` is not allowed in patterns"
throwErrorAt (mkNullNode srcs) "invalid struct instance pattern, 'with' is not allowed in patterns"
let fields fields.getElems.mapM fun
| `(Parser.Term.structInstField| $lval:structInstLVal := $val) => do
let newVal collect val
@@ -341,7 +316,7 @@ where
if ctx.ellipsis then
pushNewArg accessible ctx (Arg.stx ( `(_)))
else
throwWrongArgCount ctx false
throwError "explicit parameter is missing, unused named arguments {ctx.namedArgs.map fun narg => narg.name}"
| arg::args =>
pushNewArg accessible { ctx with args := args } arg
@@ -376,8 +351,7 @@ where
let (fId, explicit) match f with
| `($fId:ident) => pure (fId, false)
| `(@$fId:ident) => pure (fId, true)
| _ =>
throwError "Invalid pattern: Expected an identifier in function position, but found{indentD f}"
| _ => throwError "identifier expected"
let some (Expr.const fName _) resolveId? fId "pattern" (withInfo := true) | throwCtorExpected (some fId)
let fInfo getConstInfo fName
let paramDecls forallTelescopeReducing fInfo.type fun xs _ => xs.mapM fun x => do

View File

@@ -141,7 +141,7 @@ def grind
let type mvarId.getType
let mvar' mkFreshExprSyntheticOpaqueMVar type
let result Grind.main mvar'.mvarId! params fallback
if result.hasFailed then
if result.hasFailures then
throwError "`grind` failed\n{← result.toMessageData}"
-- `grind` proofs are often big
let e if ( isProp type) then

View File

@@ -16,7 +16,6 @@ import Lean.Meta.Tactic.Cases
import Lean.Meta.Tactic.FunIndCollect
import Lean.Meta.GeneralizeVars
import Lean.Elab.App
import Lean.Elab.Match
import Lean.Elab.Tactic.ElabTerm
import Lean.Elab.Tactic.Generalize
@@ -157,7 +156,7 @@ partial def mkElimApp (elimInfo : ElimInfo) (targets : Array Expr) (tag : Name)
let s get
let ctx read
unless s.targetPos < ctx.targets.size do
throwError "Insufficient number of targets for '{elimInfo.elimExpr}'"
throwError "insufficient number of targets for '{elimInfo.elimExpr}'"
let target := ctx.targets[s.targetPos]!
let expectedType getArgExpectedType
let target withAssignableSyntheticOpaque <| Term.ensureHasType expectedType target
@@ -186,7 +185,7 @@ partial def mkElimApp (elimInfo : ElimInfo) (targets : Array Expr) (tag : Name)
let s get
let ctx read
unless s.targetPos = ctx.targets.size do
throwError "Unexpected number of targets for '{elimInfo.elimExpr}'"
throwError "unexpected number of targets for '{elimInfo.elimExpr}'"
pure ()
let (_, s) (loop).run { elimInfo := elimInfo, targets := targets }
|>.run { f := elimInfo.elimExpr, fType := elimInfo.elimType, motive := none }
@@ -201,16 +200,15 @@ partial def mkElimApp (elimInfo : ElimInfo) (targets : Array Expr) (tag : Name)
others := others.push mvarId
let alts s.alts.filterM fun alt => return !( alt.mvarId.isAssigned)
let some motive := s.motive |
throwError "Internal error in mkElimApp: Motive not found"
throwError "mkElimApp: motive not found"
let complexArgs s.fType.withApp fun f motiveArgs => do
unless f == mkMVar motive do
throwError "Internal error in mkElimApp: Expected application of {motive}:{indentExpr s.fType}"
throwError "mkElimApp: Expected application of {motive}:{indentExpr s.fType}"
-- Sanity-checking that the motive is applied to the targets.
-- NB: The motive can take them in a different order than the eliminator itself
for motiveArg in motiveArgs[:targets.size] do
unless targets.contains motiveArg do
throwError "Internal error in mkElimApp: Expected first {targets.size} arguments of motive \
in conclusion to be one of the targets:{indentExpr s.fType}"
throwError "mkElimApp: Expected first {targets.size} arguments of motive in conclusion to be one of the targets:{indentExpr s.fType}"
pure motiveArgs[targets.size:]
let elimApp instantiateMVars s.f
-- `elimArgs` is the argument list that the offsets in `elimInfo` work with
@@ -252,14 +250,14 @@ def setMotiveArg (mvarId : MVarId) (motiveArg : MVarId) (targets : Array FVarId)
let motive mkLambdaFVars (targets.map mkFVar) absType
let motiverInferredType inferType motive
unless ( isDefEqGuarded motiverInferredType motiveType) do
throwError "Type mismatch when assigning motive{indentExpr motive}\n{← mkHasTypeButIsExpectedMsg motiverInferredType motiveType}"
throwError "type mismatch when assigning motive{indentExpr motive}\n{← mkHasTypeButIsExpectedMsg motiverInferredType motiveType}"
motiveArg.assign motive
private def getAltNumFields (elimInfo : ElimInfo) (altName : Name) : TermElabM Nat := do
for altInfo in elimInfo.altsInfo do
if altInfo.name == altName then
return altInfo.numFields
throwError "Unknown alternative name '{altName}'"
throwError "unknown alternative name '{altName}'"
private def isWildcard (altStx : Syntax) : Bool :=
getAltName altStx == `_
@@ -269,21 +267,21 @@ private def checkAltNames (alts : Array Alt) (altsSyntax : Array Syntax) : Tacti
for h : i in [:altsSyntax.size] do
let altStx := altsSyntax[i]
if getAltName altStx == `_ && i != altsSyntax.size - 1 then
withRef altStx <| throwError "Invalid occurrence of the wildcard alternative `| _ => ...`: It must be the last alternative"
withRef altStx <| throwError "invalid occurrence of wildcard alternative, it must be the last alternative"
let altName := getAltName altStx
if altName != `_ then
if seenNames.contains altName then
throwErrorAt altStx s!"Duplicate alternative name '{altName}'"
throwErrorAt altStx s!"duplicate alternative name '{altName}'"
seenNames := seenNames.push altName
unless alts.any (·.name == altName) do
let unhandledAlts := alts.filter fun alt => !seenNames.contains alt.name
let msg :=
if unhandledAlts.isEmpty then
m!"Invalid alternative name '{altName}': There are no unhandled alternatives"
m!"invalid alternative name '{altName}', no unhandled alternatives"
else
let unhandledAltsMessages := unhandledAlts.map (m!"'{·.name}'")
let unhandledAlts := MessageData.orList unhandledAltsMessages.toList
m!"Invalid alternative name '{altName}': Expected {unhandledAlts}"
m!"invalid alternative name '{altName}', expected {unhandledAlts}"
throwErrorAt altStx msg
@@ -297,7 +295,7 @@ private def getNumExplicitFields (altMVarId : MVarId) (numFields : Nat) : MetaM
-- `forallMetaBoundTelescope` will reduce let-bindings, so we don't just count how many
-- explicit binders are in `bis`, but how many implicit ones.
-- If this turns out to be insufficient, then the real (and complicated) logic for which
-- arguments are explicit or implicit can be found in `introNImp`,
-- arguments are explicit or implicit can be found in `introNImp`,
let (_, bis, _) forallMetaBoundedTelescope target numFields
let numImplicits := (bis.filter (!·.isExplicit)).size
return numFields - numImplicits
@@ -315,7 +313,7 @@ private def saveAltVarsInfo (altMVarId : MVarId) (altStx : Syntax) (fvarIds : Ar
open Language in
def evalAlts (elimInfo : ElimInfo) (alts : Array Alt) (optPreTac : Syntax) (altStxs? : Option (Array Syntax))
(initialInfo : Info) (tacStx : Syntax)
(initialInfo : Info)
(numEqs : Nat := 0) (numGeneralized : Nat := 0) (toClear : Array FVarId := #[])
(toTag : Array (Ident × FVarId) := #[]) : TacticM Unit := do
let hasAlts := altStxs?.isSome
@@ -412,7 +410,7 @@ where
applyAltStx tacSnaps altStxs altStxIdx altStx alt
alts := #[]
else
throwErrorAt altStx (Term.mkRedundantAlternativeMsg altName none)
throwErrorAt altStx "unused alternative '{altName}'"
-- now process remaining alternatives; these might either be unreachable or we're in `induction`
-- without `with`. In all other cases, remaining alternatives are flagged as errors.
@@ -436,7 +434,7 @@ where
-- User did not provide alternatives using `|`
setGoals <| ( getGoals) ++ altMVarIds
else if !altMVarIds.isEmpty then
logErrorAt tacStx m!"Alternative '{altName}' has not been provided"
logError m!"alternative '{altName}' has not been provided"
altMVarIds.forM fun mvarId => admitGoal mvarId
/-- Applies syntactic alternative to alternative goal. -/
@@ -447,7 +445,7 @@ where
let altVars := getAltVars altStx
let numFieldsToName if altHasExplicitModifier altStx then pure numFields else getNumExplicitFields altMVarId numFields
if altVars.size > numFieldsToName then
logError m!"Too many variable names provided at alternative '{altName}': {altVars.size} provided, but {numFieldsToName} expected"
logError m!"too many variable names provided at alternative '{altName}', #{altVars.size} provided, but #{numFieldsToName} expected"
let mut (fvarIds, altMVarId) altMVarId.introN numFields (altVars.toList.map getNameOfIdent') (useNamesForExplicitOnly := !altHasExplicitModifier altStx)
-- Delay adding the infos for the pattern LHS because we want them to nest
-- inside tacticInfo for the current alternative (in `evalAlt`)
@@ -459,7 +457,7 @@ where
let unusedAlt := do
addInfo
if !isWildcard altStx then
throwError "Alternative '{altName}' is not needed"
throwError "alternative '{altName}' is not needed"
let some (altMVarId', subst) Cases.unifyEqs? numEqs altMVarId {}
| unusedAlt
altMVarId if info.provesMotive then
@@ -533,9 +531,9 @@ private def generalizeVars (mvarId : MVarId) (stx : Syntax) (targets : Array Exp
let mut s getFVarSetToGeneralize targets forbidden
for userFVarId in userFVarIds do
if forbidden.contains userFVarId then
throwError "Variable '{mkFVar userFVarId}' cannot be generalized because the induction target depends on it"
throwError "variable cannot be generalized because target depends on it{indentExpr (mkFVar userFVarId)}"
if s.contains userFVarId then
throwError "Unnecessary 'generalizing' argument: Variable '{mkFVar userFVarId}' is generalized automatically"
throwError "unnecessary 'generalizing' argument, variable '{mkFVar userFVarId}' is generalized automatically"
s := s.insert userFVarId
let fvarIds sortFVarIds s.toArray
let (fvarIds, mvarId') mvarId.revert fvarIds
@@ -637,7 +635,7 @@ private def inductionAltsPos (stx : Syntax) : Nat :=
else if stx.getKind == ``Lean.Parser.Tactic.funCases then
2
else
panic! s!"inductionAltsSyntaxPos: Unexpected syntax kind {stx.getKind}"
panic! "inductionAltsSyntaxPos: Unexpected syntax kind {stx.getKind}"
/--
Expand
@@ -664,9 +662,9 @@ private def checkAltsOfOptInductionAlts (optInductionAlts : Syntax) : TacticM Un
let n := getAltName alt
if n == `_ then
unless (getAltVars alt).isEmpty do
throwErrorAt alt "The wildcard alternative `| _ => ...` must not specify variable names"
throwErrorAt alt "wildcard alternative must not specify variable names"
if found then
throwErrorAt alt "More than one wildcard alternative `| _ => ...` used"
throwErrorAt alt "more than one wildcard alternative '| _ => ...' used"
found := true
def getInductiveValFromMajor (induction : Bool) (major : Expr) : TacticM InductiveVal :=
@@ -729,7 +727,7 @@ private def getElimNameInfo (optElimId : Syntax) (targets : Array Expr) (inducti
if let some elimName getCustomEliminator? targets induction then
return getElimInfo elimName
unless targets.size == 1 do
throwError "Eliminator must be provided when multiple targets are used (use 'using <eliminator-name>'), and no default eliminator has been registered using attribute `[eliminator]`"
throwError "eliminator must be provided when multiple targets are used (use 'using <eliminator-name>'), and no default eliminator has been registered using attribute `[eliminator]`"
let indVal getInductiveValFromMajor induction targets[0]!
if induction && indVal.all.length != 1 then
throwError "'induction' tactic does not support mutually inductive types, the eliminator '{mkRecName indVal.name}' has multiple motives"
@@ -847,9 +845,9 @@ def checkInductionTargets (targets : Array Expr) : MetaM Unit := do
let mut foundFVars : FVarIdSet := {}
for target in targets do
unless target.isFVar do
throwError "Invalid target: Index in target's type is not a variable (consider using the `cases` tactic instead){indentExpr target}"
throwError "index in target's type is not a variable (consider using the `cases` tactic instead){indentExpr target}"
if foundFVars.contains target.fvarId! then
throwError "Invalid target: Target (or one of its indices) occurs more than once{indentExpr target}"
throwError "target (or one of its indices) occurs more than once{indentExpr target}"
foundFVars := foundFVars.insert target.fvarId!
/--
@@ -878,7 +876,7 @@ private def evalInductionCore (stx : Syntax) (elimInfo : ElimInfo) (targets : Ar
withAltsOfOptInductionAlts optInductionAlts fun alts? => do
let optPreTac := getOptPreTacOfOptInductionAlts optInductionAlts
mvarId.assign result.elimApp
ElimApp.evalAlts elimInfo result.alts optPreTac alts? initInfo stx[0]
ElimApp.evalAlts elimInfo result.alts optPreTac alts? initInfo
(numGeneralized := n) (toClear := targetFVarIds) (toTag := toTag)
appendGoals result.others.toList
@@ -913,12 +911,12 @@ def elabFunTargetCall (cases : Bool) (stx : Syntax) : TacticM Expr := do
let unfolding := tactic.fun_induction.unfolding.get ( getOptions)
let some funIndInfo getFunIndInfo? (cases := cases) (unfolding := unfolding) fnName |
let theoremKind := if cases then "cases" else "induction"
throwError "No functional {theoremKind} theorem for '{.ofConstName fnName}', or function is mutually recursive "
throwError "no functional {theoremKind} theorem for '{.ofConstName fnName}', or function is mutually recursive "
let candidates FunInd.collect funIndInfo ( getMainGoal)
if candidates.isEmpty then
throwError "Could not find suitable call of '{.ofConstName fnName}' in the goal"
throwError "could not find suitable call of '{.ofConstName fnName}' in the goal"
if candidates.size > 1 then
throwError "Found more than one suitable call of '{.ofConstName fnName}' in the goal. \
throwError "found more than one suitable call of '{.ofConstName fnName}' in the goal. \
Please include the desired arguments."
pure candidates[0]!
| _ =>
@@ -932,11 +930,11 @@ private def elabFunTarget (cases : Bool) (stx : Syntax) : TacticM (ElimInfo × A
let funCall elabFunTargetCall cases stx
funCall.withApp fun fn funArgs => do
let .const fnName fnUs := fn |
throwError "Expected application headed by a function constant"
throwError "expected application headed by a function constant"
let unfolding := tactic.fun_induction.unfolding.get ( getOptions)
let some funIndInfo getFunIndInfo? (cases := cases) (unfolding := unfolding) fnName |
let theoremKind := if cases then "cases" else "induction"
throwError "No functional {theoremKind} theorem for '{.ofConstName fnName}', or function is mutually recursive "
throwError "no functional {theoremKind} theorem for '{.ofConstName fnName}', or function is mutually recursive "
if funArgs.size != funIndInfo.params.size then
throwError "Expected fully applied application of '{.ofConstName fnName}' with \
{funIndInfo.params.size} arguments, but found {funArgs.size} arguments"
@@ -961,7 +959,7 @@ private def elabFunTarget (cases : Bool) (stx : Syntax) : TacticM (ElimInfo × A
unless targets.size = elimInfo.targetsPos.size do
let tacName := if cases then "fun_cases" else "fun_induction"
throwError "{tacName} got confused trying to use \
'{.ofConstName funIndInfo.funIndName}'. Does it take {targets.size} or \
{.ofConstName funIndInfo.funIndName}. Does it take {targets.size} or \
{elimInfo.targetsPos.size} targets?"
return (elimInfo, targets)
@@ -1001,7 +999,7 @@ def evalCasesCore (stx : Syntax) (elimInfo : ElimInfo) (targets : Array Expr)
Term.withNarrowedArgTacticReuse (stx := stx) (argIdx := inductionAltsPos stx) fun optInductionAlts => do
withAltsOfOptInductionAlts optInductionAlts fun alts => do
let optPreTac := getOptPreTacOfOptInductionAlts optInductionAlts
ElimApp.evalAlts elimInfo result.alts optPreTac alts initInfo stx[0]
ElimApp.evalAlts elimInfo result.alts optPreTac alts initInfo
(numEqs := targets.size) (toClear := targetsNew) (toTag := toTag)
@[builtin_tactic Lean.Parser.Tactic.cases, builtin_incremental]

View File

@@ -265,7 +265,7 @@ instance : Inhabited (FVarIdMap α) where
/-- Universe metavariable Id -/
structure MVarId where
name : Name
deriving Inhabited, BEq, Hashable
deriving Inhabited, BEq, Hashable, Repr
instance : Repr MVarId where
reprPrec n p := reprPrec n.name p

View File

@@ -236,13 +236,13 @@ _, (Vec.cons _ _ (Vec.cons _ _ _)), _
-/
def checkAndReplaceFVarId (fvarId : FVarId) (v : Expr) (alt : Alt) : MetaM Alt := do
match alt.fvarDecls.find? fun (fvarDecl : LocalDecl) => fvarDecl.fvarId == fvarId with
| none => throwErrorAt alt.ref "Unknown free pattern variable"
| none => throwErrorAt alt.ref "unknown free pattern variable"
| some fvarDecl => do
let vType inferType v
unless ( isDefEqGuarded fvarDecl.type vType) do
withExistingLocalDecls alt.fvarDecls do
let (expectedType, givenType) addPPExplicitToExposeDiff vType fvarDecl.type
throwErrorAt alt.ref "Type mismatch during dependent match-elimination at pattern variable '{mkFVar fvarDecl.fvarId}' with type{indentExpr givenType}\nExpected type{indentExpr expectedType}"
throwErrorAt alt.ref "type mismatch during dependent match-elimination at pattern variable '{mkFVar fvarDecl.fvarId}' with type{indentExpr givenType}\nexpected type{indentExpr expectedType}"
return replaceFVarId fvarId v alt
end Alt
@@ -342,7 +342,7 @@ partial def toPattern (e : Expr) : MetaM Pattern := do
let p toPattern <| e.getArg! 2
match e.getArg! 1, e.getArg! 3 with
| Expr.fvar x, Expr.fvar h => return Pattern.as x p h
| _, _ => throwError "Unexpected occurrence of auxiliary declaration 'namedPattern'"
| _, _ => throwError "unexpected occurrence of auxiliary declaration 'namedPattern'"
else if ( isMatchValue e) then
return Pattern.val e
else if e.isFVar then
@@ -351,10 +351,10 @@ partial def toPattern (e : Expr) : MetaM Pattern := do
let newE whnf e
if newE != e then
toPattern newE
else matchConstCtor e.getAppFn (fun _ => throwError "Unexpected pattern{indentExpr e}") fun v us => do
else matchConstCtor e.getAppFn (fun _ => throwError "unexpected pattern{indentExpr e}") fun v us => do
let args := e.getAppArgs
unless args.size == v.numParams + v.numFields do
throwError "Unexpected pattern{indentExpr e}"
throwError "unexpected pattern{indentExpr e}"
let params := args.extract 0 v.numParams
let fields := args.extract v.numParams args.size
let fields fields.mapM toPattern

View File

@@ -17,44 +17,10 @@ import Lean.Meta.Match.MVarRenaming
namespace Lean.Meta.Match
private def mkIncorrectNumberOfPatternsMsg [ToMessageData α]
(discrepancyKind : String) (expected actual : Nat) (pats : List α) :=
let patternsMsg := MessageData.joinSep (pats.map toMessageData) ", "
m!"{discrepancyKind} patterns in match alternative: Expected {expected}, \
but found {actual}:{indentD patternsMsg}"
/--
Throws an error indicating that the alternative at `ref` contains an unexpected number of patterns.
Remark: we allow `α` to be arbitrary because this error may be thrown before or after elaborating
pattern syntax.
-/
def throwIncorrectNumberOfPatternsAt [ToMessageData α]
(ref : Syntax) (discrepancyKind : String) (expected actual : Nat) (pats : List α)
: MetaM Unit := do
throwErrorAt ref (mkIncorrectNumberOfPatternsMsg discrepancyKind expected actual pats)
/--
Logs an error indicating that the alternative at `ref` contains an unexpected number of patterns.
Remark: we allow `α` to be arbitrary because this error may be thrown before or after elaborating
pattern syntax.
-/
def logIncorrectNumberOfPatternsAt [ToMessageData α]
(ref : Syntax) (discrepancyKind : String) (expected actual : Nat) (pats : List α)
: MetaM Unit :=
logErrorAt ref (mkIncorrectNumberOfPatternsMsg discrepancyKind expected actual pats)
/-- The number of patterns in each AltLHS must be equal to the number of discriminants. -/
private def checkNumPatterns (numDiscrs : Nat) (lhss : List AltLHS) : MetaM Unit := do
for lhs in lhss do
let doThrow (kind : String) := withExistingLocalDecls lhs.fvarDecls do
throwIncorrectNumberOfPatternsAt lhs.ref kind numDiscrs lhs.patterns.length
(lhs.patterns.map Pattern.toMessageData)
if lhs.patterns.length < numDiscrs then
doThrow "Not enough"
else if lhs.patterns.length > numDiscrs then
-- This case should be impossible, as an alternative with too many patterns will cause an
-- error to be thrown in `Lean.Elab.Term.elabPatterns`
doThrow "Too many"
if lhss.any fun lhs => lhs.patterns.length != numDiscrs then
throwError "incorrect number of patterns"
/--
Execute `k hs` where `hs` contains new equalities `h : lhs[i] = rhs[i]` for each `discrInfos[i] = some h`.
@@ -300,7 +266,7 @@ where
let targetType mvarId.getType
unless ( isDefEqGuarded targetType eType) do
trace[Meta.Match.match] "assignGoalOf failed {eType} =?= {targetType}"
throwErrorAt alt.ref "Dependent elimination failed: Type mismatch when solving this alternative: it {← mkHasTypeButIsExpectedMsg eType targetType}"
throwError "dependent elimination failed, type mismatch when solving alternative with type{indentExpr eType}\nbut expected{indentExpr targetType}"
mvarId.assign alt.rhs
modify fun s => { s with used := s.used.insert alt.idx }
return true
@@ -465,7 +431,7 @@ def processInaccessibleAsCtor (alt : Alt) (ctorName : Name) : MetaM (Option Alt)
return some { alt with patterns := fields ++ ps }
else
return none
| _ => throwErrorAt alt.ref "Dependent match elimination failed: Expected a constructor, but found the inaccessible pattern{indentD p.toMessageData}"
| _ => throwErrorAt alt.ref "dependent match elimination failed, inaccessible pattern found{indentD p.toMessageData}\nconstructor expected"
| _ => unreachable!
private def hasNonTrivialExample (p : Problem) : Bool :=
@@ -711,7 +677,7 @@ private def traceState (p : Problem) : MetaM Unit :=
private def throwNonSupported (p : Problem) : MetaM Unit :=
withGoalOf p do
let msg p.toMessageData
throwError "Failed to compile pattern matching: Stuck at{indentD msg}"
throwError "failed to compile pattern matching, stuck at{indentD msg}"
def isCurrVarInductive (p : Problem) : MetaM Bool := do
match p.vars with
@@ -733,7 +699,7 @@ private def checkNextPatternTypes (p : Problem) : MetaM Unit := do
let xType inferType x
let eType inferType e
unless ( isDefEq xType eType) do
throwError "Type mismatch in pattern: Pattern{indentExpr e}\n{← mkHasTypeButIsExpectedMsg eType xType}"
throwError "pattern{indentExpr e}\n{← mkHasTypeButIsExpectedMsg eType xType}"
private partial def process (p : Problem) : StateRefT State MetaM Unit := do
traceState p
@@ -787,7 +753,7 @@ private def getUElimPos? (matcherLevels : List Level) (uElim : Level) : MetaM (O
if uElim == levelZero then
return none
else match matcherLevels.idxOf? uElim with
| none => throwError "Dependent match elimination failed: Universe level not found"
| none => throwError "dependent match elimination failed, universe level not found"
| some pos => return some pos
/- See comment at `mkMatcher` before `mkAuxDefinition` -/
@@ -891,7 +857,7 @@ def mkMatcher (input : MkMatcherInput) (exceptionIfContainsSorry := false) : Met
let type instantiateMVars type
if exceptionIfContainsSorry then
if type.hasSorry || val.hasSorry then
throwError "Failed to create auxiliary match declaration '{matcherName}' because it contains `sorry`"
throwError "failed to create auxiliary match declaration `{matcherName}`, it contains `sorry`"
trace[Meta.Match.debug] "matcher value: {val}\ntype: {type}"
trace[Meta.Match.debug] "minors num params: {minors.map (·.2)}"
/- The option `bootstrap.gen_matcher_code` is a helper hack. It is useful, for example,
@@ -975,8 +941,7 @@ def mkMatcher (input : MkMatcherInput) (exceptionIfContainsSorry := false) : Met
def getMkMatcherInputInContext (matcherApp : MatcherApp) : MetaM MkMatcherInput := do
let matcherName := matcherApp.matcherName
let some matcherInfo getMatcherInfo? matcherName
| throwError "Internal error during match expression elaboration: Could not find a matcher named '{matcherName}'"
let some matcherInfo getMatcherInfo? matcherName | throwError "not a matcher: {matcherName}"
let matcherConst getConstInfo matcherName
let matcherType instantiateForall matcherConst.type <| matcherApp.params ++ #[matcherApp.motive]
let matchType do
@@ -1006,14 +971,12 @@ def getMkMatcherInputInContext (matcherApp : MatcherApp) : MetaM MkMatcherInput
/-- This function is only used for testing purposes -/
def withMkMatcherInput (matcherName : Name) (k : MkMatcherInput MetaM α) : MetaM α := do
let some matcherInfo getMatcherInfo? matcherName
| throwError "Internal error during match expression elaboration: Could not find a matcher named '{matcherName}'"
let some matcherInfo getMatcherInfo? matcherName | throwError "not a matcher: {matcherName}"
let matcherConst getConstInfo matcherName
forallBoundedTelescope matcherConst.type (some matcherInfo.arity) fun xs _ => do
let matcherApp mkConstWithLevelParams matcherConst.name
let matcherApp := mkAppN matcherApp xs
let some matcherApp matchMatcherApp? matcherApp
| throwError "Internal error during match expression elaboration: Could not find a matcher app named '{matcherApp}'"
let some matcherApp matchMatcherApp? matcherApp | throwError "not a matcher app: {matcherApp}"
let mkMatcherInput getMkMatcherInputInContext matcherApp
k mkMatcherInput

View File

@@ -111,13 +111,8 @@ where
| trace_goal[grind.ring] "found instance for{indentExpr charType}\nbut characteristic is not a natural number"; pure none
trace_goal[grind.ring] "characteristic: {n}"
pure <| some (charInst, n)
let noZeroDivInst? withNewMCtxDepth do
let zeroType := mkApp (mkConst ``Zero [u]) type
let .some zeroInst trySynthInstance zeroType | return none
let hmulType := mkApp3 (mkConst ``HMul [0, u, u]) (mkConst ``Nat []) type type
let .some hmulInst trySynthInstance hmulType | return none
let noZeroDivType := mkApp3 (mkConst ``Grind.NoNatZeroDivisors [u]) type zeroInst hmulInst
LOption.toOption <$> trySynthInstance noZeroDivType
let noZeroDivType := mkApp2 (mkConst ``Grind.NoNatZeroDivisors [u]) type ringInst
let noZeroDivInst? := ( trySynthInstance noZeroDivType).toOption
trace_goal[grind.ring] "NoNatZeroDivisors available: {noZeroDivInst?.isSome}"
let addFn getAddFn type u semiringInst
let mulFn getMulFn type u semiringInst

View File

@@ -14,6 +14,8 @@ namespace Lean.Meta.Grind.Arith.CommRing
export Lean.Grind.CommRing (Var Power Mon Poly)
abbrev RingExpr := Grind.CommRing.Expr
deriving instance Repr for Power, Mon, Poly
mutual
structure EqCnstr where

View File

@@ -95,7 +95,7 @@ def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
-- Assign on expressions associated with cutsat terms or interpreted terms
for e in goal.exprs do
let node goal.getENode e
if node.isRoot then
if isSameExpr node.root node.self then
if ( isIntNatENode node) then
if let some v getAssignment? goal node.self then
if v.den == 1 then used := used.insert v.num
@@ -111,7 +111,7 @@ def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
-- Assign the remaining ones with values not used by cutsat
for e in goal.exprs do
let node goal.getENode e
if node.isRoot then
if isSameExpr node.root node.self then
if ( isIntNatENode node) then
if model[node.self]?.isNone then
let v := pickUnusedValue goal model node.self nextVal used

View File

@@ -49,20 +49,11 @@ def getAttrKindFromOpt (stx : Syntax) : CoreM AttrKind := do
def throwInvalidUsrModifier : CoreM α :=
throwError "the modifier `usr` is only relevant in parameters for `grind only`"
/--
Auxiliary function for registering `grind` and `grind?` attributes.
The `grind?` is an alias for `grind` which displays patterns using `logInfo`.
It is just a convenience for users.
-/
private def registerGrindAttr (showInfo : Bool) : IO Unit :=
builtin_initialize
registerBuiltinAttribute {
name := if showInfo then `grind? else `grind
name := `grind
descr :=
let header := if showInfo then
"The `[grind?]` attribute is identical to the `[grind]` attribute, but displays inferred pattern information."
else
"The `[grind]` attribute is used to annotate declarations."
header ++ "\
"The `[grind]` attribute is used to annotate declarations.\
\
When applied to an equational theorem, `[grind =]`, `[grind =_]`, or `[grind _=_]`\
will mark the theorem for use in heuristic instantiations by the `grind` tactic,
@@ -82,12 +73,12 @@ private def registerGrindAttr (showInfo : Bool) : IO Unit :=
add := fun declName stx attrKind => MetaM.run' do
match ( getAttrKindFromOpt stx) with
| .ematch .user => throwInvalidUsrModifier
| .ematch k => addEMatchAttr declName attrKind k (showInfo := showInfo)
| .ematch k => addEMatchAttr declName attrKind k
| .cases eager => addCasesAttr declName eager attrKind
| .intro =>
if let some info isCasesAttrPredicateCandidate? declName false then
for ctor in info.ctors do
addEMatchAttr ctor attrKind .default (showInfo := showInfo)
addEMatchAttr ctor attrKind .default
else
throwError "invalid `[grind intro]`, `{declName}` is not an inductive predicate"
| .ext => addExtAttr declName attrKind
@@ -98,12 +89,10 @@ private def registerGrindAttr (showInfo : Bool) : IO Unit :=
-- If it is an inductive predicate,
-- we also add the constructors (intro rules) as E-matching rules
for ctor in info.ctors do
addEMatchAttr ctor attrKind .default (showInfo := showInfo)
addEMatchAttr ctor attrKind .default
else
addEMatchAttr declName attrKind .default (showInfo := showInfo)
addEMatchAttr declName attrKind .default
erase := fun declName => MetaM.run' do
if showInfo then
throwError "`[grind?]` is a helper attribute for displaying inferred patterns, if you want to remove the attribute, consider using `[grind]` instead"
if ( isCasesAttrCandidate declName false) then
eraseCasesAttr declName
else if ( isExtTheorem declName) then
@@ -112,8 +101,4 @@ private def registerGrindAttr (showInfo : Bool) : IO Unit :=
eraseEMatchAttr declName
}
builtin_initialize
registerGrindAttr true
registerGrindAttr false
end Lean.Meta.Grind

View File

@@ -70,9 +70,20 @@ def getCasesTypes : CoreM CasesTypes :=
def isSplit (declName : Name) : CoreM Bool := do
return ( getCasesTypes).isSplit declName
private def getAlias? (value : Expr) : MetaM (Option Name) :=
lambdaTelescope value fun _ body => do
if let .const declName _ := body.getAppFn' then
return some declName
else
return none
partial def isCasesAttrCandidate? (declName : Name) (eager : Bool) : CoreM (Option Name) := do
match ( getConstInfo declName) with
| .inductInfo info => if !info.isRec || !eager then return some declName else return none
| .defnInfo info =>
let some declName getAlias? info.value |>.run' {} {}
| return none
isCasesAttrCandidate? declName eager
| _ => return none
def isCasesAttrCandidate (declName : Name) (eager : Bool) : CoreM Bool := do

View File

@@ -75,7 +75,7 @@ inductive Origin where
| stx (id : Name) (ref : Syntax)
/-- It is local, but we don't have a local hypothesis for it. -/
| local (id : Name)
deriving Inhabited, Repr
deriving Inhabited, Repr, BEq
/-- A unique identifier corresponding to the origin. -/
def Origin.key : Origin Name
@@ -589,24 +589,18 @@ private def ppParamsAt (proof : Expr) (numParams : Nat) (paramPos : List Nat) :
msg := msg ++ m!"{x} : {← inferType x}"
addMessageContextFull msg
private def logPatternWhen (showInfo : Bool) (origin : Origin) (patterns : List Expr) : MetaM Unit := do
if showInfo then
logInfo m!"{← origin.pp}: {patterns.map ppPattern}"
/--
Creates an E-matching theorem for a theorem with proof `proof`, `numParams` parameters, and the given set of patterns.
Pattern variables are represented using de Bruijn indices.
-/
def mkEMatchTheoremCore (origin : Origin) (levelParams : Array Name) (numParams : Nat) (proof : Expr)
(patterns : List Expr) (kind : EMatchTheoremKind) (showInfo := false) : MetaM EMatchTheorem := do
def mkEMatchTheoremCore (origin : Origin) (levelParams : Array Name) (numParams : Nat) (proof : Expr) (patterns : List Expr) (kind : EMatchTheoremKind) : MetaM EMatchTheorem := do
let (patterns, symbols, bvarFound) NormalizePattern.main patterns
if symbols.isEmpty then
throwError "invalid pattern for `{← origin.pp}`{indentD (patterns.map ppPattern)}\nthe pattern does not contain constant symbols for indexing"
trace[grind.ematch.pattern] "{← origin.pp}: {patterns.map ppPattern}"
trace[grind.ematch.pattern] "{MessageData.ofConst proof}: {patterns.map ppPattern}"
if let .missing pos checkCoverage proof numParams bvarFound then
let pats : MessageData := m!"{patterns.map ppPattern}"
throwError "invalid pattern(s) for `{← origin.pp}`{indentD pats}\nthe following theorem parameters cannot be instantiated:{indentD (← ppParamsAt proof numParams pos)}"
logPatternWhen showInfo origin patterns
return {
proof, patterns, numParams, symbols
levelParams, origin, kind
@@ -633,7 +627,7 @@ Given a theorem with proof `proof` and type of the form `∀ (a_1 ... a_n), lhs
creates an E-matching pattern for it using `addEMatchTheorem n [lhs]`
If `normalizePattern` is true, it applies the `grind` simplification theorems and simprocs to the pattern.
-/
def mkEMatchEqTheoremCore (origin : Origin) (levelParams : Array Name) (proof : Expr) (normalizePattern : Bool) (useLhs : Bool) (showInfo := false) : MetaM EMatchTheorem := do
def mkEMatchEqTheoremCore (origin : Origin) (levelParams : Array Name) (proof : Expr) (normalizePattern : Bool) (useLhs : Bool) : MetaM EMatchTheorem := do
let (numParams, patterns) forallTelescopeReducing ( inferType proof) fun xs type => do
let (lhs, rhs) match_expr type with
| Eq _ lhs rhs => pure (lhs, rhs)
@@ -646,15 +640,15 @@ def mkEMatchEqTheoremCore (origin : Origin) (levelParams : Array Name) (proof :
trace[grind.debug.ematch.pattern] "mkEMatchEqTheoremCore: after preprocessing: {pat}, {← normalize pat normConfig}"
let pats := splitWhileForbidden (pat.abstract xs)
return (xs.size, pats)
mkEMatchTheoremCore origin levelParams numParams proof patterns (if useLhs then .eqLhs else .eqRhs) (showInfo := showInfo)
mkEMatchTheoremCore origin levelParams numParams proof patterns (if useLhs then .eqLhs else .eqRhs)
def mkEMatchEqBwdTheoremCore (origin : Origin) (levelParams : Array Name) (proof : Expr) (showInfo := false) : MetaM EMatchTheorem := do
def mkEMatchEqBwdTheoremCore (origin : Origin) (levelParams : Array Name) (proof : Expr) : MetaM EMatchTheorem := do
let (numParams, patterns) forallTelescopeReducing ( inferType proof) fun xs type => do
let_expr f@Eq α lhs rhs := type
| throwError "invalid E-matching `←=` theorem, conclusion must be an equality{indentExpr type}"
let pat preprocessPattern (mkEqBwdPattern f.constLevels! α lhs rhs)
return (xs.size, [pat.abstract xs])
mkEMatchTheoremCore origin levelParams numParams proof patterns .eqBwd (showInfo := showInfo)
mkEMatchTheoremCore origin levelParams numParams proof patterns .eqBwd
/--
Given theorem with name `declName` and type of the form `∀ (a_1 ... a_n), lhs = rhs`,
@@ -663,8 +657,8 @@ creates an E-matching pattern for it using `addEMatchTheorem n [lhs]`
If `normalizePattern` is true, it applies the `grind` simplification theorems and simprocs to the
pattern.
-/
def mkEMatchEqTheorem (declName : Name) (normalizePattern := true) (useLhs : Bool := true) (showInfo := false) : MetaM EMatchTheorem := do
mkEMatchEqTheoremCore (.decl declName) #[] ( getProofFor declName) normalizePattern useLhs (showInfo := showInfo)
def mkEMatchEqTheorem (declName : Name) (normalizePattern := true) (useLhs : Bool := true) : MetaM EMatchTheorem := do
mkEMatchEqTheoremCore (.decl declName) #[] ( getProofFor declName) normalizePattern useLhs
/--
Adds an E-matching theorem to the environment.
@@ -850,13 +844,13 @@ since the theorem is already in the `grind` state and there is nothing to be ins
-/
def mkEMatchTheoremWithKind?
(origin : Origin) (levelParams : Array Name) (proof : Expr) (kind : EMatchTheoremKind)
(groundPatterns := true) (showInfo := false) : MetaM (Option EMatchTheorem) := do
(groundPatterns := true) : MetaM (Option EMatchTheorem) := do
if kind == .eqLhs then
return ( mkEMatchEqTheoremCore origin levelParams proof (normalizePattern := true) (useLhs := true) (showInfo := showInfo))
return ( mkEMatchEqTheoremCore origin levelParams proof (normalizePattern := true) (useLhs := true))
else if kind == .eqRhs then
return ( mkEMatchEqTheoremCore origin levelParams proof (normalizePattern := true) (useLhs := false) (showInfo := showInfo))
return ( mkEMatchEqTheoremCore origin levelParams proof (normalizePattern := true) (useLhs := false))
else if kind == .eqBwd then
return ( mkEMatchEqBwdTheoremCore origin levelParams proof (showInfo := showInfo))
return ( mkEMatchEqBwdTheoremCore origin levelParams proof)
let type inferType proof
/-
Remark: we should not use `forallTelescopeReducing` (with default reducibility) here
@@ -900,26 +894,25 @@ where
return none
let numParams := xs.size
trace[grind.ematch.pattern] "{← origin.pp}: {patterns.map ppPattern}"
logPatternWhen showInfo origin patterns
return some {
proof, patterns, numParams, symbols
levelParams, origin, kind
}
def mkEMatchTheoremForDecl (declName : Name) (thmKind : EMatchTheoremKind) (showInfo := false) : MetaM EMatchTheorem := do
let some thm mkEMatchTheoremWithKind? (.decl declName) #[] ( getProofFor declName) thmKind (showInfo := showInfo)
def mkEMatchTheoremForDecl (declName : Name) (thmKind : EMatchTheoremKind) : MetaM EMatchTheorem := do
let some thm mkEMatchTheoremWithKind? (.decl declName) #[] ( getProofFor declName) thmKind
| throwError "`@{thmKind.toAttribute} theorem {declName}` {thmKind.explainFailure}, consider using different options or the `grind_pattern` command"
return thm
def mkEMatchEqTheoremsForDef? (declName : Name) (showInfo := false) : MetaM (Option (Array EMatchTheorem)) := do
def mkEMatchEqTheoremsForDef? (declName : Name) : MetaM (Option (Array EMatchTheorem)) := do
let some eqns getEqnsFor? declName | return none
eqns.mapM fun eqn => do
mkEMatchEqTheorem eqn (normalizePattern := true) (showInfo := showInfo)
mkEMatchEqTheorem eqn (normalizePattern := true)
private def addGrindEqAttr (declName : Name) (attrKind : AttributeKind) (thmKind : EMatchTheoremKind) (useLhs := true) (showInfo := false) : MetaM Unit := do
private def addGrindEqAttr (declName : Name) (attrKind : AttributeKind) (thmKind : EMatchTheoremKind) (useLhs := true) : MetaM Unit := do
if wasOriginallyTheorem ( getEnv) declName then
ematchTheoremsExt.add ( mkEMatchEqTheorem declName (normalizePattern := true) (useLhs := useLhs) (showInfo := showInfo)) attrKind
else if let some thms mkEMatchEqTheoremsForDef? declName (showInfo := showInfo) then
ematchTheoremsExt.add ( mkEMatchEqTheorem declName (normalizePattern := true) (useLhs := useLhs)) attrKind
else if let some thms mkEMatchEqTheoremsForDef? declName then
unless useLhs do
throwError "`{declName}` is a definition, you must only use the left-hand side for extracting patterns"
thms.forM (ematchTheoremsExt.add · attrKind)
@@ -942,20 +935,20 @@ def EMatchTheorems.eraseDecl (s : EMatchTheorems) (declName : Name) : MetaM EMat
throwErr
return s.erase <| .decl declName
def addEMatchAttr (declName : Name) (attrKind : AttributeKind) (thmKind : EMatchTheoremKind) (showInfo := false) : MetaM Unit := do
def addEMatchAttr (declName : Name) (attrKind : AttributeKind) (thmKind : EMatchTheoremKind) : MetaM Unit := do
if thmKind == .eqLhs then
addGrindEqAttr declName attrKind thmKind (useLhs := true) (showInfo := showInfo)
addGrindEqAttr declName attrKind thmKind (useLhs := true)
else if thmKind == .eqRhs then
addGrindEqAttr declName attrKind thmKind (useLhs := false) (showInfo := showInfo)
addGrindEqAttr declName attrKind thmKind (useLhs := false)
else if thmKind == .eqBoth then
addGrindEqAttr declName attrKind thmKind (useLhs := true) (showInfo := showInfo)
addGrindEqAttr declName attrKind thmKind (useLhs := false) (showInfo := showInfo)
addGrindEqAttr declName attrKind thmKind (useLhs := true)
addGrindEqAttr declName attrKind thmKind (useLhs := false)
else
let info getConstInfo declName
if !wasOriginallyTheorem ( getEnv) declName && !info.isCtor && !info.isAxiom then
addGrindEqAttr declName attrKind thmKind (showInfo := showInfo)
addGrindEqAttr declName attrKind thmKind
else
let thm mkEMatchTheoremForDecl declName thmKind (showInfo := showInfo)
let thm mkEMatchTheoremForDecl declName thmKind
ematchTheoremsExt.add thm attrKind
def eraseEMatchAttr (declName : Name) : MetaM Unit := do

View File

@@ -102,12 +102,10 @@ private def checkAndAddSplitCandidate (e : Expr) : GoalM Unit := do
if ( isProp d) then
addSplitCandidate (.imp e (h rfl))
else if Arith.isRelevantPred d then
-- TODO: should we keep lookahead after we implement non-chronological backtracking?
if ( getConfig).lookahead then
addLookaheadCandidate (.imp e (h rfl))
-- We used to add the `split` only if `lookahead := false`, but it was counterintuitive
-- to make `grind` "stronger" by disabling a feature.
addSplitCandidate (.imp e (h rfl))
else
addSplitCandidate (.imp e (h rfl))
| _ => pure ()
/--

View File

@@ -123,7 +123,7 @@ def checkInvariants (expensive := false) : GoalM Unit := do
for e in ( getExprs) do
let node getENode e
checkParents node.self
if node.isRoot then
if isSameExpr node.self node.root then
checkEqc node
if expensive then
checkPtrEqImpliesStructEq

View File

@@ -105,7 +105,7 @@ private def initCore (mvarId : MVarId) (params : Params) : GrindM (List Goal) :=
return goals.filter fun goal => !goal.inconsistent
structure Result where
failure? : Option Goal
failures : List Goal
skipped : List Goal
issues : List MessageData
config : Grind.Config
@@ -140,11 +140,11 @@ private def mkGlobalDiag (cs : Counters) (simp : Simp.Stats) : MetaM (Option Mes
else
return some <| .trace { cls := `grind } "Diagnostics" msgs
def Result.hasFailed (r : Result) : Bool :=
r.failure?.isSome
def Result.hasFailures (r : Result) : Bool :=
!r.failures.isEmpty
def Result.toMessageData (result : Result) : MetaM MessageData := do
let mut msgs result.failure?.toList.mapM (goalToMessageData · result.config)
let mut msgs result.failures.mapM (goalToMessageData · result.config)
if result.config.verbose then
let mut issues := result.issues
-- We did not find the following very useful in practice.
@@ -163,23 +163,23 @@ def main (mvarId : MVarId) (params : Params) (fallback : Fallback) : MetaM Resul
if debug.terminalTacticsAsSorry.get ( getOptions) then
mvarId.admit
return {
failure? := none, skipped := [], issues := [], config := params.config, trace := {}, counters := {}, simp := {}
failures := [], skipped := [], issues := [], config := params.config, trace := {}, counters := {}, simp := {}
}
else
let go : GrindM Result := withReducible do
let goals initCore mvarId params
let (failure?, skipped) solve goals fallback
let (failures, skipped) solve goals fallback
trace[grind.debug.final] "{← ppGoals goals}"
let issues := ( get).issues
let trace := ( get).trace
let counters := ( get).counters
let simp := ( get).simpStats
if failure?.isNone then
if failures.isEmpty then
-- If there are no failures and diagnostics are enabled, we still report the performance counters.
if ( isDiagnosticsEnabled) then
if let some msg mkGlobalDiag counters simp then
logInfo msg
return { failure?, skipped, issues, config := params.config, trace, counters, simp }
return { failures, skipped, issues, config := params.config, trace, counters, simp }
go.run params fallback
end Lean.Meta.Grind

View File

@@ -47,7 +47,7 @@ where
return e'
-- Remark: we have to process `Expr.proj` since we only
-- fold projections later during term internalization
unless e.isApp || e.isForall || e.isProj || e.isMData do
unless e.isApp || e.isForall || e.isProj do
return e
-- Check whether it is cached
if let some r := ( get).find? e then
@@ -68,8 +68,6 @@ where
pure e
| .proj _ _ b =>
pure <| e.updateProj! ( visit b)
| .mdata _ b =>
pure <| e.updateMData! ( visit b)
| .forallE _ d b _ =>
-- Recall that we have `ForallProp.lean`.
let d' visit d

View File

@@ -16,7 +16,8 @@ namespace Solve
structure State where
todo : List Goal
failure? : Option Goal := none
failures : List Goal := []
stop : Bool := false
private abbrev M := StateRefT State GrindM
@@ -31,8 +32,10 @@ def pushGoal (goal : Goal) : M Unit :=
def pushGoals (goals : List Goal) : M Unit :=
modify fun s => { s with todo := goals ++ s.todo }
def setFailure (goal : Goal) : M Unit := do
modify fun s => { s with failure? := some goal }
def pushFailure (goal : Goal) : M Unit := do
modify fun s => { s with failures := goal :: s.failures }
if ( get).failures.length ( getConfig).failures then
modify fun s => { s with stop := true }
@[inline] def stepGuard (x : Goal M Bool) (goal : Goal) : M Bool := do
try
@@ -40,7 +43,7 @@ def setFailure (goal : Goal) : M Unit := do
catch ex =>
if ex.isMaxHeartbeat || ex.isMaxRecDepth then
reportIssue! ex.toMessageData
setFailure goal
pushFailure goal
return true
else
throw ex
@@ -64,9 +67,12 @@ def tryLookahead : Goal → M Bool := applyTac lookahead
def tryMBTC : Goal M Bool := applyTac Arith.Cutsat.mbtcTac
def maxNumFailuresReached : M Bool := do
return ( get).failures.length ( getConfig).failures
partial def main (fallback : Fallback) : M Unit := do
repeat do
if ( get).failure?.isSome then
if ( get).stop then
return ()
let some goal getNext? |
return ()
@@ -87,15 +93,15 @@ partial def main (fallback : Fallback) : M Unit := do
let goal GoalM.run' goal fallback
if goal.inconsistent || ( goal.mvarId.isAssigned) then
continue
setFailure goal
pushFailure goal
end Solve
/--
Try to solve/close the given goals, and returns the ones that could not be solved.
-/
def solve (goals : List Goal) (fallback : Fallback) : GrindM (Option Goal × List Goal) := do
def solve (goals : List Goal) (fallback : Fallback) : GrindM (List Goal × List Goal) := do
let (_, s) Solve.main fallback |>.run { todo := goals }
return (s.failure?, s.todo)
return (s.failures.reverse, s.todo)
end Lean.Meta.Grind

View File

@@ -343,9 +343,6 @@ structure ENode where
-- If the number of satellite solvers increases, we may add support for an arbitrary solvers like done in Z3.
deriving Inhabited, Repr
def ENode.isRoot (n : ENode) :=
isSameExpr n.self n.root
def ENode.isCongrRoot (n : ENode) :=
isSameExpr n.self n.congr
@@ -1253,7 +1250,7 @@ def filterENodes (p : ENode → GoalM Bool) : GoalM (Array ENode) := do
def forEachEqcRoot (f : ENode GoalM Unit) : GoalM Unit := do
for e in ( getExprs) do
let n getENode e
if n.isRoot then
if isSameExpr n.self n.root then
f n
abbrev Propagator := Expr GoalM Unit
@@ -1305,7 +1302,7 @@ partial def Goal.getEqcs (goal : Goal) : List (List Expr) := Id.run do
let mut r : List (List Expr) := []
for e in goal.exprs do
let some node := goal.getENode? e | pure ()
if node.isRoot then
if isSameExpr node.root node.self then
r := goal.getEqc node.self :: r
return r

View File

@@ -15,6 +15,7 @@ namespace Lean.Widget
open Elab Server
deriving instance TypeName for InfoWithCtx
deriving instance TypeName for MessageData
deriving instance TypeName for LocalContext
deriving instance TypeName for Elab.ContextInfo
deriving instance TypeName for Elab.TermInfo

View File

@@ -23,5 +23,3 @@ import Std.Data.HashSet.RawLemmas
import Std.Data.DTreeMap.Raw
import Std.Data.TreeMap.Raw
import Std.Data.TreeSet.Raw
import Std.Data.Iterators

View File

@@ -3430,13 +3430,13 @@ theorem get_filterMap [LawfulBEq α]
(isSome_apply_of_mem_filterMap h') :=
Raw₀.get_filterMap m.1, _ m.2
@[simp, grind =]
@[grind =]
theorem get!_filterMap [LawfulBEq α]
{f : (a : α) β a Option (γ a)} {k : α} [Inhabited (γ k)] :
(m.filterMap f).get! k = ((m.get? k).bind (f k)).get! :=
Raw₀.get!_filterMap m.1, _ m.2
@[simp, grind =]
@[grind =]
theorem getD_filterMap [LawfulBEq α]
{f : (a : α) β a Option (γ a)} {k : α} {fallback : γ k} :
(m.filterMap f).getD k fallback = ((m.get? k).bind (f k)).getD fallback :=
@@ -3502,20 +3502,13 @@ theorem size_filterMap_eq_size_iff [EquivBEq α] [LawfulHashable α]
(m.filterMap f).size = m.size k h, (f (m.getKey k h) (Const.get m k h)).isSome :=
Raw₀.Const.size_filterMap_eq_size_iff m.1, _ m.2
@[simp]
@[grind =]
theorem get?_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} :
Const.get? (m.filterMap f) k = (Const.get? m k).pbind (fun x h' =>
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x) :=
Raw₀.Const.get?_filterMap m.1, _ m.2
/-- Simpler variant of `get?_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem get?_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} :
Const.get? (m.filterMap f) k = (Const.get? m k).bind fun x => f k x := by
simp [get?_filterMap]
theorem get?_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k k' : α} (h : m.getKey? k = some k') :
Const.get? (m.filterMap f) k = (Const.get? m k).bind (f k') :=
@@ -3528,7 +3521,7 @@ theorem isSome_apply_of_mem_filterMap [EquivBEq α] [LawfulHashable α]
(Const.get m k (mem_of_mem_filterMap h))).isSome :=
Raw₀.Const.isSome_apply_of_contains_filterMap m.1, _ m.2
@[simp]
@[simp, grind =]
theorem get_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {h} :
Const.get (m.filterMap f) k h =
@@ -3537,14 +3530,7 @@ theorem get_filterMap [EquivBEq α] [LawfulHashable α]
(isSome_apply_of_mem_filterMap h) :=
Raw₀.Const.get_filterMap m.1, _ m.2
/-- Simpler variant of `get_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem get_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {h} :
Const.get (m.filterMap f) k h =
(f k (Const.get m k (mem_of_mem_filterMap h))).get (by simpa using isSome_apply_of_mem_filterMap h) := by
simp [get_filterMap]
theorem get!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k : α} :
Const.get! (m.filterMap f) k =
@@ -3552,18 +3538,12 @@ theorem get!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ]
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).get! :=
Raw₀.Const.get!_filterMap m.1, _ m.2
/-- Simpler variant of `get!_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem get!_filterMap' [LawfulBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k : α} :
Const.get! (m.filterMap f) k = ((Const.get? m k).bind (f k) ).get!:= by
simp [get!_filterMap]
theorem get!_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k k' : α} (h : m.getKey? k = some k') :
Const.get! (m.filterMap f) k = ((Const.get? m k).bind (f k')).get! :=
Raw₀.Const.get!_filterMap_of_getKey?_eq_some m.1, _ m.2 h
@[grind =]
theorem getD_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {fallback : γ} :
Const.getD (m.filterMap f) k fallback =
@@ -3571,13 +3551,6 @@ theorem getD_filterMap [EquivBEq α] [LawfulHashable α]
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback :=
Raw₀.Const.getD_filterMap m.1, _ m.2
/-- Simpler variant of `getD_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem getD_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {fallback : γ} :
Const.getD (m.filterMap f) k fallback = ((Const.get? m k).bind (f k)).getD fallback := by
simp [getD_filterMap]
theorem getD_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k k' : α} {fallback : γ} (h : m.getKey? k = some k') :
Const.getD (m.filterMap f) k fallback = ((Const.get? m k).bind (f k')).getD fallback :=
@@ -3832,19 +3805,13 @@ theorem filter_equiv_self_iff [EquivBEq α] [LawfulHashable α]
fun h => (Raw₀.Const.filter_equiv_self_iff m.1, _ m.2).mp h.1,
fun h => (Raw₀.Const.filter_equiv_self_iff m.1, _ m.2).mpr h
@[grind =]
theorem get?_filter [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} :
Const.get? (m.filter f) k = (Const.get? m k).pfilter (fun x h' =>
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x) :=
Raw₀.Const.get?_filter m.1, _ m.2
/-- Simpler variant of `get?_filter` when `LawfulBEq` is available. -/
@[simp, grind =]
theorem get?_filter' [LawfulBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} :
Const.get? (m.filter f) k = (Const.get? m k).filter (f k) := by
simp [get?_filter]
theorem get?_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k k' : α} :
m.getKey? k = some k'
@@ -3857,6 +3824,7 @@ theorem get_filter [EquivBEq α] [LawfulHashable α]
Const.get (m.filter f) k h' = Const.get m k (mem_of_mem_filter h') :=
Raw₀.Const.get_filter m.1, _ m.2
@[grind =]
theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k : α} :
Const.get! (m.filter f) k =
@@ -3864,32 +3832,19 @@ theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β]
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).get! :=
Raw₀.Const.get!_filter m.1, _ m.2
/-- Simpler variant of `get!_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem get!_filter' [LawfulBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k : α} :
Const.get! (m.filter f) k = ((Const.get? m k).filter (f k)).get! := by
simp [get!_filter]
theorem get!_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k k' : α} :
m.getKey? k = some k'
Const.get! (m.filter f) k = ((Const.get? m k).filter (fun x => f k' x)).get! :=
Raw₀.Const.get!_filter_of_getKey?_eq_some m.1, _ m.2
@[grind =]
theorem getD_filter [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} {fallback : β} :
Const.getD (m.filter f) k fallback = ((Const.get? m k).pfilter (fun x h' =>
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback :=
Raw₀.Const.getD_filter m.1, _ m.2
/-- Simpler variant of `getD_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem getD_filter' [LawfulBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} {fallback : β} :
Const.getD (m.filter f) k fallback = ((Const.get? m k).filter (f k)).getD fallback := by
simp [getD_filter]
theorem getD_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k k' : α} {fallback : β} :
m.getKey? k = some k'

View File

@@ -3702,19 +3702,13 @@ theorem size_filterMap_eq_size_iff [EquivBEq α] [LawfulHashable α]
-- TODO: `size_filterMap_le_size` is missing
@[grind =]
theorem get?_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} (h : m.WF) :
Const.get? (m.filterMap f) k = (Const.get? m k).pbind (fun x h' =>
f (m.getKey k ((mem_iff_isSome_get? h).mpr (Option.isSome_of_eq_some h'))) x) := by
simp_to_raw using Raw₀.Const.get?_filterMap
/-- Simpler variant of `get?_filterMap` when `LawfulBEq` is available. -/
@[simp, grind =]
theorem get?_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} (h : m.WF) :
Const.get? (m.filterMap f) k = (Const.get? m k).bind (f k) := by
simp [get?_filterMap, h]
theorem get?_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k k' : α} (h : m.WF) :
m.getKey? k = some k' Const.get? (m.filterMap f) k = (Const.get? m k).bind (f k') := by
@@ -3737,6 +3731,7 @@ theorem get_filterMap [EquivBEq α] [LawfulHashable α]
(isSome_apply_of_mem_filterMap h h') := by
simp_to_raw using Raw₀.Const.get_filterMap
@[grind =]
theorem get!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k : α} (h : m.WF) :
Const.get! (m.filterMap f) k =
@@ -3745,19 +3740,13 @@ theorem get!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ]
x)).get! := by
simp_to_raw using Raw₀.Const.get!_filterMap
/-- Simpler variant of `get!_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem get!_filterMap' [LawfulBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k : α} (h : m.WF) :
Const.get! (m.filterMap f) k = ((Const.get? m k).bind (f k)).get! := by
simp [get!_filterMap, h]
theorem get!_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k k' : α} (h : m.WF) :
m.getKey? k = some k' Const.get! (m.filterMap f) k = ((Const.get? m k).bind
fun x => f k' x).get! := by
simp_to_raw using Raw₀.Const.get!_filterMap_of_getKey?_eq_some
@[grind =]
theorem getD_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {fallback : γ} (h : m.WF) :
Const.getD (m.filterMap f) k fallback =
@@ -3765,13 +3754,6 @@ theorem getD_filterMap [EquivBEq α] [LawfulHashable α]
f (m.getKey k ((mem_iff_isSome_get? h).mpr (Option.isSome_of_eq_some h'))) x)).getD fallback := by
simp_to_raw using Raw₀.Const.getD_filterMap
/-- Simpler variant of `getD_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem getD_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {fallback : γ} (h : m.WF) :
Const.getD (m.filterMap f) k fallback = ((Const.get? m k).bind (f k)).getD fallback := by
simp [getD_filterMap, h]
theorem getD_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k k' : α} {fallback : γ} (h : m.WF) :
m.getKey? k = some k' Const.getD (m.filterMap f) k fallback = ((Const.get? m k).bind
@@ -4046,20 +4028,13 @@ theorem filter_equiv_self_iff [EquivBEq α] [LawfulHashable α]
simp [ contains_iff_mem]
simp_to_raw using Raw₀.Const.filter_equiv_self_iff
@[simp]
@[grind =]
theorem get?_filter [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} (h : m.WF) :
Const.get? (m.filter f) k = (Const.get? m k).pfilter (fun x h' =>
f (m.getKey k ((mem_iff_isSome_get? h).mpr (Option.isSome_of_eq_some h'))) x) := by
simp_to_raw using Raw₀.Const.get?_filter
/-- Simpler variant of `get?_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem get?_filter' [LawfulBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} (h : m.WF) :
Const.get? (m.filter f) k = (Const.get? m k).filter (f k) := by
simp [get?_filter, h]
theorem get?_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k k' : α} (h : m.WF) :
m.getKey? k = some k'
@@ -4072,6 +4047,7 @@ theorem get_filter [EquivBEq α] [LawfulHashable α]
Const.get (m.filter f) k h' = Const.get m k (mem_of_mem_filter h h') := by
simp_to_raw using Raw₀.Const.get_filter
@[grind =]
theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k : α} (h : m.WF) :
Const.get! (m.filter f) k =
@@ -4079,32 +4055,19 @@ theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β]
f (m.getKey k ((mem_iff_isSome_get? h).mpr (Option.isSome_of_eq_some h'))) x)).get! := by
simp_to_raw using Raw₀.Const.get!_filter
/-- Simpler variant of `get!_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem get!_filter' [LawfulBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k : α} (h : m.WF) :
Const.get! (m.filter f) k = ((Const.get? m k).filter (f k)).get! := by
simp [get!_filter, h]
theorem get!_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k k' : α} (h : m.WF) :
m.getKey? k = some k'
Const.get! (m.filter f) k = ((Const.get? m k).filter (fun x => f k' x)).get! := by
simp_to_raw using Raw₀.Const.get!_filter_of_getKey?_eq_some
@[grind =]
theorem getD_filter [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} {fallback : β} (h : m.WF) :
Const.getD (m.filter f) k fallback = ((Const.get? m k).pfilter (fun x h' =>
f (m.getKey k ((mem_iff_isSome_get? h).mpr (Option.isSome_of_eq_some h'))) x)).getD fallback := by
simp_to_raw using Raw₀.Const.getD_filter
/-- Simpler variant of `getD_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem getD_filter' [LawfulBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} {fallback : β} (h : m.WF) :
Const.getD (m.filter f) k fallback = ((Const.get? m k).filter (f k)).getD fallback := by
simp [getD_filter, h]
theorem getD_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k k' : α} {fallback : β} (h : m.WF) :
m.getKey? k = some k'

View File

@@ -2965,20 +2965,13 @@ theorem size_filterMap_eq_size_iff [EquivBEq α] [LawfulHashable α]
(m.filterMap f).size = m.size k h, (f (m.getKey k h) (Const.get m k h)).isSome :=
m.inductionOn fun _ => DHashMap.Const.size_filterMap_eq_size_iff
@[simp]
@[grind =]
theorem get?_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} :
Const.get? (m.filterMap f) k = (Const.get? m k).pbind (fun x h' =>
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x) :=
m.inductionOn fun _ => DHashMap.Const.get?_filterMap
/-- Simpler variant of `get?_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem get?_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} :
Const.get? (m.filterMap f) k = (Const.get? m k).bind (f k) := by
simp [get?_filterMap]
theorem get?_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k k' : α} (h : m.getKey? k = some k') :
Const.get? (m.filterMap f) k = (Const.get? m k).bind (f k') :=
@@ -2991,7 +2984,7 @@ theorem isSome_apply_of_mem_filterMap [EquivBEq α] [LawfulHashable α]
(Const.get m k (mem_of_mem_filterMap h))).isSome :=
m.inductionOn fun _ => DHashMap.Const.isSome_apply_of_mem_filterMap
@[simp]
@[simp, grind =]
theorem get_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {h} :
Const.get (m.filterMap f) k h =
@@ -3000,13 +2993,7 @@ theorem get_filterMap [EquivBEq α] [LawfulHashable α]
(isSome_apply_of_mem_filterMap h) :=
m.inductionOn (fun _ _ => DHashMap.Const.get_filterMap) h
/-- Simpler variant of `get_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem get_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {h} :
Const.get (m.filterMap f) k h = (f k (Const.get m k (mem_of_mem_filterMap h))).get (by simpa using isSome_apply_of_mem_filterMap h) := by
simp [get_filterMap]
theorem get!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k : α} :
Const.get! (m.filterMap f) k =
@@ -3014,18 +3001,12 @@ theorem get!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ]
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).get! :=
m.inductionOn fun _ => DHashMap.Const.get!_filterMap
/-- Simpler variant of `get!_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem get!_filterMap' [LawfulBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k : α} :
Const.get! (m.filterMap f) k = ((Const.get? m k).bind (f k)).get! := by
simp [get!_filterMap]
theorem get!_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ]
{f : α β Option γ} {k k' : α} (h : m.getKey? k = some k') :
Const.get! (m.filterMap f) k = ((Const.get? m k).bind (f k')).get! :=
m.inductionOn (fun _ h => DHashMap.Const.get!_filterMap_of_getKey?_eq_some h) h
@[grind =]
theorem getD_filterMap [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {fallback : γ} :
Const.getD (m.filterMap f) k fallback =
@@ -3033,13 +3014,6 @@ theorem getD_filterMap [EquivBEq α] [LawfulHashable α]
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback :=
m.inductionOn fun _ => DHashMap.Const.getD_filterMap
/-- Simpler variant of `getD_filterMap` when `LawfulBEq` is available. -/
@[grind =]
theorem getD_filterMap' [LawfulBEq α] [LawfulHashable α]
{f : α β Option γ} {k : α} {fallback : γ} :
Const.getD (m.filterMap f) k fallback = ((Const.get? m k).bind (f k)).getD fallback := by
simp [getD_filterMap]
theorem getD_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Option γ} {k k' : α} {fallback : γ} (h : m.getKey? k = some k') :
Const.getD (m.filterMap f) k fallback = ((Const.get? m k).bind (f k')).getD fallback :=
@@ -3231,19 +3205,12 @@ theorem filter_eq_self_iff [EquivBEq α] [LawfulHashable α] {f : α → β →
m.filter f = m k h, f (m.getKey k h) (Const.get m k h) :=
m.inductionOn fun _ => Iff.trans Quotient.exact, Quotient.sound DHashMap.Const.filter_equiv_self_iff
theorem get?_filter [EquivBEq α] [LawfulHashable α]
@[grind =] theorem get?_filter [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} :
Const.get? (m.filter f) k = (Const.get? m k).pfilter (fun x h' =>
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x) :=
m.inductionOn fun _ => DHashMap.Const.get?_filter
/-- Simpler variant of `get?_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem get?_filter' [LawfulBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} :
Const.get? (m.filter f) k = (Const.get? m k).filter (f k) := by
simp [get?_filter]
theorem get?_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k k' : α} :
m.getKey? k = some k'
@@ -3256,39 +3223,25 @@ theorem get_filter [EquivBEq α] [LawfulHashable α]
Const.get (m.filter f) k h' = Const.get m k (mem_of_mem_filter h') :=
m.inductionOn (fun _ _ => DHashMap.Const.get_filter) h'
theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β]
@[grind =] theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k : α} :
Const.get! (m.filter f) k =
((Const.get? m k).pfilter (fun x h' =>
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).get! :=
m.inductionOn fun _ => DHashMap.Const.get!_filter
/-- Simpler variant of `get!_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem get!_filter' [LawfulBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k : α} :
Const.get! (m.filter f) k = ((Const.get? m k).filter (f k)).get! := by
simp [get!_filter]
theorem get!_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited β]
{f : α β Bool} {k k' : α} :
m.getKey? k = some k'
Const.get! (m.filter f) k = ((Const.get? m k).filter (fun x => f k' x)).get! :=
m.inductionOn fun _ => DHashMap.Const.get!_filter_of_getKey?_eq_some
theorem getD_filter [EquivBEq α] [LawfulHashable α]
@[grind =] theorem getD_filter [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} {fallback : β} :
Const.getD (m.filter f) k fallback = ((Const.get? m k).pfilter (fun x h' =>
f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback :=
m.inductionOn fun _ => DHashMap.Const.getD_filter
/-- Simpler variant of `getD_filter` when `LawfulBEq` is available. -/
@[grind =]
theorem getD_filter' [LawfulBEq α] [LawfulHashable α]
{f : α β Bool} {k : α} {fallback : β} :
Const.getD (m.filter f) k fallback = ((Const.get? m k).filter (f k)).getD fallback := by
simp [getD_filter]
theorem getD_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α]
{f : α β Bool} {k k' : α} {fallback : β} :
m.getKey? k = some k'

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