Compare commits

..

5 Commits

Author SHA1 Message Date
Kyle Miller
4224d416bf release note 2024-02-27 14:21:03 -08:00
Kyle Miller
a06010fa1a suggestion 2024-02-27 14:21:03 -08:00
Kyle Miller
234eef85e8 doc fixes 2024-02-27 14:21:03 -08:00
Kyle Miller
67da377715 wording refinements 2024-02-27 14:21:03 -08:00
Kyle Miller
60b598d425 feat: improve error messages and docstring for decide tactic
The `decide` tactic produces error messages that users find to be obscure. Now:
1.  If the `Decidable` instance reduces to `isFalse`, it reports that `decide` failed because the proposition is false.
2. If the `Decidable` instance fails to reduce, it explains what proposition it failed for, and it shows the reduced `Decidable` instance rather than the `Decidable.decide` expression, which tends to be less useful since it shows the unreduced `Decidable` argument (plus it's a lot longer!)

Examples:
```lean
example : 1 ≠ 1 := by decide
/-
tactic 'decide' proved that the proposition
  1 ≠ 1
is false
-/

opaque unknownProp : Prop

open scoped Classical in
example : unknownProp := by decide
/-
tactic 'decide' failed for proposition
  unknownProp
since its 'Decidable' instance did not reduce to the 'isTrue' constructor:
  Classical.choice ⋯
-/
```

In the future, it would be useful if `decide` would locate and report what `Decidable` instance inside the reported term is not being fully reduced.
2024-02-27 14:21:03 -08:00
345 changed files with 1518 additions and 9834 deletions

View File

@@ -140,8 +140,7 @@ jobs:
"shell": "msys2 {0}",
"CMAKE_OPTIONS": "-G \"Unix Makefiles\" -DUSE_GMP=OFF",
// for reasons unknown, interactivetests are flaky on Windows
// also, the liasolver test hits “too many exported symbols”
"CTEST_OPTIONS": "--repeat until-pass:2 -E 'leanbenchtest_liasolver.lean'",
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd"

View File

@@ -6,7 +6,6 @@ on:
tags:
- '*'
pull_request:
types: [opened, synchronize, reopened, labeled]
merge_group:
concurrency:

View File

@@ -8,38 +8,9 @@ This file contains work-in-progress notes for the upcoming release, as well as p
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
of each version.
v4.8.0 (development in progress)
v4.7.0 (development in progress)
---------
* New command `derive_functinal_induction`:
Derived from the definition of a (possibly mutually) recursive function
defined by well-founded recursion, a **functional induction principle** is
tailored to proofs about that function. For example from:
```
def ackermann : Nat → Nat → Nat
| 0, m => m + 1
| n+1, 0 => ackermann n 1
| n+1, m+1 => ackermann n (ackermann (n + 1) m)
derive_functional_induction ackermann
```
we get
```
ackermann.induct (motive : Nat → Nat → Prop) (case1 : ∀ (m : Nat), motive 0 m)
(case2 : ∀ (n : Nat), motive n 1 → motive (Nat.succ n) 0)
(case3 : ∀ (n m : Nat), motive (n + 1) m → motive n (ackermann (n + 1) m) → motive (Nat.succ n) (Nat.succ m))
(x x : Nat) : motive x x
```
v4.7.0
---------
* `simp` and `rw` now use instance arguments found by unification,
rather than always resynthesizing. For backwards compatibility, the original behaviour is
available via `set_option tactic.skipAssignedInstances false`.
[#3507](https://github.com/leanprover/lean4/pull/3507) and
[#3509](https://github.com/leanprover/lean4/pull/3509).
* When the `pp.proofs` is false, now omitted proofs use `⋯` rather than `_`,
which gives a more helpful error message when copied from the Infoview.
The `pp.proofs.threshold` option lets small proofs always be pretty printed.
@@ -47,10 +18,6 @@ v4.7.0
* `pp.proofs.withType` is now set to false by default to reduce noise in the info view.
* The pretty printer for applications now handles the case of over-application itself when applying app unexpanders.
In particular, the ``| `($_ $a $b $xs*) => `(($a + $b) $xs*)`` case of an `app_unexpander` is no longer necessary.
[#3495](https://github.com/leanprover/lean4/pull/3495).
* New `simp` (and `dsimp`) configuration option: `zetaDelta`. It is `false` by default.
The `zeta` option is still `true` by default, but their meaning has changed.
- When `zeta := true`, `simp` and `dsimp` reduce terms of the form
@@ -59,7 +26,7 @@ v4.7.0
the context. For example, suppose the context contains `x := val`. Then,
any occurrence of `x` is replaced with `val`.
See [issue #2682](https://github.com/leanprover/lean4/pull/2682) for additional details. Here are some examples:
See issue [#2682](https://github.com/leanprover/lean4/pull/2682) for additional details. Here are some examples:
```
example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
intro x
@@ -100,7 +67,7 @@ v4.7.0
```
* When adding new local theorems to `simp`, the system assumes that the function application arguments
have been annotated with `no_index`. This modification, which addresses [issue #2670](https://github.com/leanprover/lean4/issues/2670),
have been annotated with `no_index`. This modification, which addresses issue [#2670](https://github.com/leanprover/lean4/issues/2670),
restores the Lean 3 behavior that users expect. With this modification, the following examples are now operational:
```
example {α β : Type} {f : α × β → β → β} (h : ∀ p : α × β, f p p.2 = p.2)
@@ -116,178 +83,76 @@ v4.7.0
* Improved the error messages produced by the `decide` tactic. [#3422](https://github.com/leanprover/lean4/pull/3422)
* Improved auto-completion performance. [#3460](https://github.com/leanprover/lean4/pull/3460)
* Improved initial language server startup performance. [#3552](https://github.com/leanprover/lean4/pull/3552)
* Changed call hierarchy to sort entries and strip private header from names displayed in the call hierarchy. [#3482](https://github.com/leanprover/lean4/pull/3482)
* There is now a low-level error recovery combinator in the parsing framework, primarily intended for DSLs. [#3413](https://github.com/leanprover/lean4/pull/3413)
* You can now write `termination_by?` after a declaration to see the automatically inferred
termination argument, and turn it into a `termination_by …` clause using the “Try this” widget or a code action. [#3514](https://github.com/leanprover/lean4/pull/3514)
* A large fraction of `Std` has been moved into the Lean repository.
This was motivated by:
1. Making universally useful tactics such as `ext`, `by_cases`, `change at`,
`norm_cast`, `rcases`, `simpa`, `simp?`, `omega`, and `exact?`
available to all users of Lean, without imports.
2. Minimizing the syntactic changes between plain Lean and Lean with `import Std`.
3. Simplifying the development process for the basic data types
`Nat`, `Int`, `Fin` (and variants such as `UInt64`), `List`, `Array`,
and `BitVec` as we begin making the APIs and simp normal forms for these types
more complete and consistent.
4. Laying the groundwork for the Std roadmap, as a library focused on
essential datatypes not provided by the core langauge (e.g. `RBMap`)
and utilities such as basic IO.
While we have achieved most of our initial aims in `v4.7.0-rc1`,
some upstreaming will continue over the coming months.
* The `/` and `%` notations in `Int` now use `Int.ediv` and `Int.emod`
(i.e. the rounding conventions have changed).
Previously `Std` overrode these notations, so this is no change for users of `Std`.
There is now kernel support for these functions.
[#3376](https://github.com/leanprover/lean4/pull/3376).
* `omega`, our integer linear arithmetic tactic, is now availabe in the core langauge.
* It is supplemented by a preprocessing tactic `bv_omega` which can solve goals about `BitVec`
which naturally translate into linear arithmetic problems.
[#3435](https://github.com/leanprover/lean4/pull/3435).
* `omega` now has support for `Fin` [#3427](https://github.com/leanprover/lean4/pull/3427),
the `<<<` operator [#3433](https://github.com/leanprover/lean4/pull/3433).
* During the port `omega` was modified to no longer identify atoms up to definitional equality
(so in particular it can no longer prove `id x ≤ x`). [#3525](https://github.com/leanprover/lean4/pull/3525).
This may cause some regressions.
We plan to provide a general purpose preprocessing tactic later, or an `omega!` mode.
* `omega` is now invoked in Lean's automation for termination proofs
[#3503](https://github.com/leanprover/lean4/pull/3503) as well as in
array indexing proofs [#3515](https://github.com/leanprover/lean4/pull/3515).
This automation will be substantially revised in the medium term,
and while `omega` does help automate some proofs, we plan to make this much more robust.
* The library search tactics `exact?` and `apply?` that were originally in
Mathlib are now available in Lean itself. These use the implementation using
lazy discrimination trees from `Std`, and thus do not require a disk cache but
have a slightly longer startup time. The order used for selection lemmas has
changed as well to favor goals purely based on how many terms in the head
pattern match the current goal.
* The `solve_by_elim` tactic has been ported from `Std` to Lean so that library
search can use it.
* New `#check_tactic` and `#check_simp` commands have been added. These are
useful for checking tactics (particularly `simp`) behave as expected in test
suites.
* Previously, app unexpanders would only be applied to entire applications. However, some notations produce
functions, and these functions can be given additional arguments. The solution so far has been to write app unexpanders so that they can take an arbitrary number of additional arguments. However this leads to misleading hover information in the Infoview. For example, while `HAdd.hAdd f g 1` pretty prints as `(f + g) 1`, hovering over `f + g` shows `f`. There is no way to fix the situation from within an app unexpander; the expression position for `HAdd.hAdd f g` is absent, and app unexpanders cannot register TermInfo.
This commit changes the app delaborator to try running app unexpanders on every prefix of an application, from longest to shortest prefix. For efficiency, it is careful to only try this when app delaborators do in fact exist for the head constant, and it also ensures arguments are only delaborated once. Then, in `(f + g) 1`, the `f + g` gets TermInfo registered for that subexpression, making it properly hoverable.
[#3375](https://github.com/leanprover/lean4/pull/3375)
Breaking changes:
* `Lean.withTraceNode` and variants got a stronger `MonadAlwaysExcept` assumption to
fix trace trees not being built on elaboration runtime exceptions. Instances for most elaboration
monads built on `EIO Exception` should be synthesized automatically.
* The `match ... with.` and `fun.` notations previously in Std have been replaced by
`nomatch ...` and `nofun`. [#3279](https://github.com/leanprover/lean4/pull/3279) and [#3286](https://github.com/leanprover/lean4/pull/3286)
Other improvements:
* several bug fixes for `simp`:
* we should not crash when `simp` loops [#3269](https://github.com/leanprover/lean4/pull/3269)
* `simp` gets stuck on `autoParam` [#3315](https://github.com/leanprover/lean4/pull/3315)
* `simp` fails when custom discharger makes no progress [#3317](https://github.com/leanprover/lean4/pull/3317)
* `simp` fails to discharge `autoParam` premises even when it can reduce them to `True` [#3314](https://github.com/leanprover/lean4/pull/3314)
* `simp?` suggests generated equations lemma names, fixes [#3547](https://github.com/leanprover/lean4/pull/3547) [#3573](https://github.com/leanprover/lean4/pull/3573)
* fixes for `match` expressions:
* fix regression with builtin literals [#3521](https://github.com/leanprover/lean4/pull/3521)
* accept `match` when patterns cover all cases of a `BitVec` finite type [#3538](https://github.com/leanprover/lean4/pull/3538)
* fix matching `Int` literals [#3504](https://github.com/leanprover/lean4/pull/3504)
* patterns containing int values and constructors [#3496](https://github.com/leanprover/lean4/pull/3496)
* improve `termination_by` error messages [#3255](https://github.com/leanprover/lean4/pull/3255)
* fix `rename_i` in macros, fixes [#3553](https://github.com/leanprover/lean4/pull/3553) [#3581](https://github.com/leanprover/lean4/pull/3581)
* fix excessive resource usage in `generalize`, fixes [#3524](https://github.com/leanprover/lean4/pull/3524) [#3575](https://github.com/leanprover/lean4/pull/3575)
* an equation lemma with autoParam arguments fails to rewrite, fixing [#2243](https://github.com/leanprover/lean4/pull/2243) [#3316](https://github.com/leanprover/lean4/pull/3316)
* `add_decl_doc` should check that declarations are local [#3311](https://github.com/leanprover/lean4/pull/3311)
* instantiate the types of inductives with the right parameters, closing [#3242](https://github.com/leanprover/lean4/pull/3242) [#3246](https://github.com/leanprover/lean4/pull/3246)
* New simprocs for many basic types. [#3407](https://github.com/leanprover/lean4/pull/3407)
Lake fixes:
* Warn on fetch cloud release failure [#3401](https://github.com/leanprover/lean4/pull/3401)
* Cloud release trace & `lake build :release` errors [#3248](https://github.com/leanprover/lean4/pull/3248)
v4.6.1
---------
* Backport of [#3552](https://github.com/leanprover/lean4/pull/3552) fixing a performance regression
in server startup.
v4.6.0
---------
* Add custom simplification procedures (aka `simproc`s) to `simp`. Simprocs can be triggered by the simplifier on a specified term-pattern. Here is an small example:
```lean
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
```lean
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
def foo (x : Nat) : Nat :=
x + 10
def foo (x : Nat) : Nat :=
x + 10
/--
The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`.
-/
simproc reduceFoo (foo _) :=
/- A term of type `Expr → SimpM Step -/
fun e => do
/-
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
* The constructor `.done` instructs `simp` that the result does
not need to be simplied further.
* The constructor `.visit` instructs `simp` to visit the resulting expression.
* The constructor `.continue` instructs `simp` to try other simplification procedures.
All three constructors take a `Result`. The `.continue` contructor may also take `none`.
`Result` has two fields `expr` (the new expression), and `proof?` (an optional proof).
If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`.
-/
/- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/
unless e.isAppOfArity ``foo 1 do
return .continue
/- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/
let some n ← Nat.fromExpr? e.appArg!
| return .continue
return .done { expr := Lean.mkNatLit (n+10) }
```
We disable simprocs support by using the command `set_option simprocs false`. This command is particularly useful when porting files to v4.6.0.
Simprocs can be scoped, manually added to `simp` commands, and suppressed using `-`. They are also supported by `simp?`. `simp only` does not execute any `simproc`. Here are some examples for the `simproc` defined above.
```lean
example : x + foo 2 = 12 + x := by
set_option simprocs false in
/- This `simp` command does not make progress since `simproc`s are disabled. -/
fail_if_success simp
simp_arith
example : x + foo 2 = 12 + x := by
/- `simp only` must not use the default simproc set. -/
fail_if_success simp only
simp_arith
example : x + foo 2 = 12 + x := by
/--
The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`.
-/
simproc reduceFoo (foo _) :=
/- A term of type `Expr → SimpM Step -/
fun e => do
/-
`simp only` does not use the default simproc set,
but we can provide simprocs as arguments. -/
simp only [reduceFoo]
simp_arith
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
* The constructor `.done` instructs `simp` that the result does
not need to be simplied further.
* The constructor `.visit` instructs `simp` to visit the resulting expression.
* The constructor `.continue` instructs `simp` to try other simplification procedures.
example : x + foo 2 = 12 + x := by
/- We can use `-` to disable `simproc`s. -/
fail_if_success simp [-reduceFoo]
simp_arith
```
The command `register_simp_attr <id>` now creates a `simp` **and** a `simproc` set with the name `<id>`. The following command instructs Lean to insert the `reduceFoo` simplification procedure into the set `my_simp`. If no set is specified, Lean uses the default `simp` set.
```lean
simproc [my_simp] reduceFoo (foo _) := ...
```
All three constructors take a `Result`. The `.continue` contructor may also take `none`.
`Result` has two fields `expr` (the new expression), and `proof?` (an optional proof).
If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`.
-/
/- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/
unless e.isAppOfArity ``foo 1 do
return .continue
/- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/
let some n ← Nat.fromExpr? e.appArg!
| return .continue
return .done { expr := Lean.mkNatLit (n+10) }
```
We disable simprocs support by using the command `set_option simprocs false`. This command is particularly useful when porting files to v4.6.0.
Simprocs can be scoped, manually added to `simp` commands, and suppressed using `-`. They are also supported by `simp?`. `simp only` does not execute any `simproc`. Here are some examples for the `simproc` defined above.
```lean
example : x + foo 2 = 12 + x := by
set_option simprocs false in
/- This `simp` command does not make progress since `simproc`s are disabled. -/
fail_if_success simp
simp_arith
example : x + foo 2 = 12 + x := by
/- `simp only` must not use the default simproc set. -/
fail_if_success simp only
simp_arith
example : x + foo 2 = 12 + x := by
/-
`simp only` does not use the default simproc set,
but we can provide simprocs as arguments. -/
simp only [reduceFoo]
simp_arith
example : x + foo 2 = 12 + x := by
/- We can use `-` to disable `simproc`s. -/
fail_if_success simp [-reduceFoo]
simp_arith
```
The command `register_simp_attr <id>` now creates a `simp` **and** a `simproc` set with the name `<id>`. The following command instructs Lean to insert the `reduceFoo` simplification procedure into the set `my_simp`. If no set is specified, Lean uses the default `simp` set.
```lean
simproc [my_simp] reduceFoo (foo _) := ...
```
* The syntax of the `termination_by` and `decreasing_by` termination hints is overhauled:
@@ -426,7 +291,7 @@ v4.6.0
and hence greatly reduces the reliance on costly structure eta reduction. This has a large impact on mathlib,
reducing total CPU instructions by 3% and enabling impactful refactors like leanprover-community/mathlib4#8386
which reduces the build time by almost 20%.
See [PR #2478](https://github.com/leanprover/lean4/pull/2478) and [RFC #2451](https://github.com/leanprover/lean4/issues/2451).
See PR [#2478](https://github.com/leanprover/lean4/pull/2478) and RFC [#2451](https://github.com/leanprover/lean4/issues/2451).
* Add pretty printer settings to omit deeply nested terms (`pp.deepTerms false` and `pp.deepTerms.threshold`) ([PR #3201](https://github.com/leanprover/lean4/pull/3201))
@@ -445,7 +310,7 @@ Other improvements:
* produce simpler proof terms in `rw` [#3121](https://github.com/leanprover/lean4/pull/3121)
* fuse nested `mkCongrArg` calls in proofs generated by `simp` [#3203](https://github.com/leanprover/lean4/pull/3203)
* `induction using` followed by a general term [#3188](https://github.com/leanprover/lean4/pull/3188)
* allow generalization in `let` [#3060](https://github.com/leanprover/lean4/pull/3060), fixing [#3065](https://github.com/leanprover/lean4/issues/3065)
* allow generalization in `let` [#3060](https://github.com/leanprover/lean4/pull/3060, fixing [#3065](https://github.com/leanprover/lean4/issues/3065)
* reducing out-of-bounds `swap!` should return `a`, not `default`` [#3197](https://github.com/leanprover/lean4/pull/3197), fixing [#3196](https://github.com/leanprover/lean4/issues/3196)
* derive `BEq` on structure with `Prop`-fields [#3191](https://github.com/leanprover/lean4/pull/3191), fixing [#3140](https://github.com/leanprover/lean4/issues/3140)
* refine through more `casesOnApp`/`matcherApp` [#3176](https://github.com/leanprover/lean4/pull/3176), fixing [#3175](https://github.com/leanprover/lean4/pull/3175)

View File

@@ -89,6 +89,5 @@
- [Testing](./dev/testing.md)
- [Debugging](./dev/debugging.md)
- [Commit Convention](./dev/commit_convention.md)
- [Release checklist](./dev/release_checklist.md)
- [Building This Manual](./dev/mdbook.md)
- [Foreign Function Interface](./dev/ffi.md)

View File

@@ -1,201 +0,0 @@
# Releasing a stable version
This checklist walks you through releasing a stable version.
See below for the checklist for release candidates.
We'll use `v4.6.0` as the intended release version as a running example.
- One week before the planned release, ensure that someone has written the first draft of the release blog post
- `git checkout releases/v4.6.0`
(This branch should already exist, from the release candidates.)
- `git pull`
- In `src/CMakeLists.txt`, verify you see
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
- `set(LEAN_VERSION_IS_RELEASE 1)`
- (both of these should already be in place from the release candidates)
- It is possible that the `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Run `git diff master RELEASES.md`.
- You should expect to see additons on `master` in the `v4.7.0-rc1` section; ignore these.
(i.e. the new release notes for the upcoming release candidate).
- Reconcile discrepancies in the `v4.6.0` section,
usually via copy and paste and a commit to `releases/v4.6.0`.
- `git tag v4.6.0`
- `git push origin v4.6.0`
- Now wait, while CI runs.
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`,
looking for the `v4.6.0` tag.
- This step can take up to an hour.
- If you are intending to cut the next release candidate on the same day,
you may want to start on the release candidate checklist now.
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
- Edit the release notes on Github to select the "Set as the latest release".
- Copy and paste the Github release notes from the previous releases candidate for this version
(e.g. `v4.6.0-rc1`), and quickly sanity check.
- Next, we will move a curated list of downstream repos to the latest stable release.
- For each of the repositories listed below:
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`.
The PR title should be "chore: bump toolchain to v4.6.0".
Since the `v4.6.0` release should be functionally identical to the last release candidate,
which the repository should already be on, this PR is a no-op besides changing the toolchain.
- Once this is merged, create the tag `v4.6.0` from `master`/`main` and push it.
- Merge the tag `v4.6.0` into the stable branch.
- We do this for the repositories:
- [lean4checker](https://github.com/leanprover/lean4checker)
- `lean4checker` uses a different version tagging scheme: use `toolchain/v4.6.0` rather than `v4.6.0`.
- [Std](https://github.com/leanprover-community/repl)
- [ProofWidgets4](https://github.com/leanprover-community/ProofWidgets4)
- `ProofWidgets` uses a sequential version tagging scheme, e.g. `v0.0.29`,
which does not refer to the toolchain being used.
- Make a new release in this sequence after merging the toolchain bump PR.
- `ProofWidgets` does not maintain a `stable` branch.
- [Aesop](https://github.com/leanprover-community/aesop)
- [Mathlib](https://github.com/leanprover-community/mathlib4)
- In addition to updating the `lean-toolchain` and `lakefile.lean`,
in `.github/workflows/build.yml.in` in the `lean4checker` section update the line
`git checkout toolchain/v4.6.0` to the appropriate tag,
and then run `.github/workflows/mk_build_yml.sh`.
- [REPL](https://github.com/leanprover-community/repl)
- Note that there are two copies of `lean-toolchain`/`lakefile.lean`:
in the root, and in `test/Mathlib/`.
- Note that there are dependencies between these packages:
you should update the lakefile so that you are using the `v4.6.0` tag of upstream repositories
(or the sequential tag for `ProofWidgets4`), and run `lake update` before committing.
- This means that this process is sequential; each repository must have its bump PR merged,
and the new tag pushed, before you can make the PR for the downstream repositories.
- `lean4checker` has no dependencies
- `Std` has no dependencies
- `Aesop` depends on `Std`
- `ProofWidgets4` depends on `Std`
- `Mathlib` depends on `Aesop`, `ProofWidgets4`, and `lean4checker` (and transitively on `Std`)
- `REPL` depends on `Mathlib` (this dependency is only for testing).
- Merge the release announcement PR for the Lean website - it will be deployed automatically
- Finally, make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
Please see previous announcements for suggested language.
You will want a few bullet points for main topics from the release notes.
Link to the blog post from the Zulip announcement.
Please also make sure that whoever is handling social media knows the release is out.
## Optimistic(?) time estimates:
- Initial checks and push the tag: 30 minutes.
- Note that if `RELEASES.md` has discrepancies this could take longer!
- Waiting for the release: 60 minutes.
- Fixing release notes: 10 minutes.
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
- Waiting for Mathlib CI and bors: 120 minutes.
- Finalizing Mathlib tags and stable branch, and updating REPL: 15 minutes.
- Posting announcement and/or blog post: 20 minutes.
# Creating a release candidate.
This checklist walks you through creating the first release candidate for a version of Lean.
We'll use `v4.7.0-rc1` as the intended release version in this example.
- Decide which nightly release you want to turn into a release candidate.
We will use `nightly-2024-02-29` in this example.
- It is essential that Std and Mathlib already have reviewed branches compatible with this nightly.
- Check that both Std and Mathlib's `bump/v4.7.0` branch contain `nightly-2024-02-29`
in their `lean-toolchain`.
- The steps required to reach that state are beyond the scope of this checklist, but see below!
- Create the release branch from this nightly tag:
```
git remote add nightly https://github.com/leanprover/lean4-nightly.git
git fetch nightly tag nightly-2024-02-29
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
```
- In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
- Our current goal is to have written release notes only about major language features or breaking changes,
and to rely on automatically generated release notes for bugfixes and minor changes.
- Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
- Commit your changes to `RELEASES.md`, and push.
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
- In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
- Commit your changes to `src/CMakeLists.txt`, and push.
- `git tag v4.7.0-rc1`
- `git push origin v4.7.0-rc1`
- Now wait, while CI runs.
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
- This step can take up to an hour.
- Once the release appears at https://github.com/leanprover/lean4/releases/
- Edit the release notes on Github to select the "Set as a pre-release box".
- Copy the section of `RELEASES.md` for this version into the Github release notes.
- Use the title "Changes since v4.6.0 (from RELEASES.md)"
- Then in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
- This will add a list of all the commits since the last stable version.
- Delete anything already mentioned in the hand-written release notes above.
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
but for minor items don't put any work in expanding on commit messages.
- (How we want to release notes to look is evolving: please update this section if it looks wrong!)
- Next, we will move a curated list of downstream repos to the release candidate.
- This assumes that there is already a *reviewed* branch `bump/v4.7.0` on each repository
containing the required adaptations (or no adaptations are required).
The preparation of this branch is beyond the scope of this document.
- For each of the target repositories:
- Checkout the `bump/v4.7.0` branch.
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
- `git merge origin/master`
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
back to `master` or `main`, and run `lake update` for those dependencies.
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
- `git commit`
- `git push`
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
or notify the maintainers that it is ready to go.
- Once this PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
- We do this for the same list of repositories as for stable releases, see above.
As above, there are dependencies between these, and so the process above is iterative.
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
- For Std/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
- Make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.7.0-rc1`.
Please see previous announcements for suggested language.
You will want a few bullet points for main topics from the release notes.
Please also make sure that whoever is handling social media knows the release is out.
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
- Removes `(in development)` from the section heading in `RELEASES.md` for `v4.7.0`,
and creates a new `v4.8.0 (in development)` section heading.
## Time estimates:
Slightly longer than the corresponding steps for a stable release.
Similar process, but more things go wrong.
In particular, updating the downstream repositories is significantly more work
(because we need to merge existing `bump/v4.7.0` branches, not just update a toolchain).
# Preparing `bump/v4.7.0` branches
While not part of the release process per se,
this is a brief summary of the work that goes into updating Std/Aesop/Mathlib to new versions.
Please read https://leanprover-community.github.io/contribute/tags_and_branches.html
* Each repo has an unreviewed `nightly-testing` branch that
receives commits automatically from `master`, and
has its toolchain updated automatically for every nightly.
(Note: the aesop branch is not automated, and is updated on an as needed basis.)
As a consequence this branch is often broken.
A bot posts in the (private!) "Mathlib reviewers" stream on Zulip about the status of these branches.
* We fix the breakages by committing directly to `nightly-testing`: there is no PR process.
* This can either be done by the person managing this process directly,
or by soliciting assistance from authors of files, or generally helpful people on Zulip!
* Each repo has a `bump/v4.7.0` which accumulates reviewed changes adapting to new versions.
* Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we:
* Make sure `bump/v4.7.0` is up to date with `master` (by merging `master`, no PR necessary)
* Create from `bump/v4.7.0` a `bump/nightly-2024-02-15` branch.
* In that branch, `git merge --squash nightly-testing` to bring across changes from `nightly-testing`.
* Sanity check changes, commit, and make a PR to `bump/v4.7.0` from the `bump/nightly-2024-02-15` branch.
* Solicit review, merge the PR into `bump/v4,7,0`.
* It is always okay to merge in the following directions:
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
Please remember to push any merges you make to intermediate steps!

View File

@@ -277,13 +277,14 @@ theorem BinTree.find_insert (b : BinTree β) (k : Nat) (v : β)
. by_cases' key < k
cases h; apply ihr; assumption
theorem BinTree.find_insert_of_ne (b : BinTree β) (ne : k k') (v : β)
theorem BinTree.find_insert_of_ne (b : BinTree β) (h : k k') (v : β)
: (b.insert k v).find? k' = b.find? k' := by
let t, h := b; simp
induction t with simp
| leaf =>
intros le
exact Nat.lt_of_le_of_ne le ne
intros
have_eq k k'
contradiction
| node left key value right ihl ihr =>
let .node hl hr bl br := h
specialize ihl bl

View File

@@ -9,7 +9,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 8)
set(LEAN_VERSION_MINOR 7)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -501,18 +501,24 @@ string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
# (also looks nicer in the build log)
file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib)
# set up libInit_shared only on Windows; see also stdlib.make.in
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND INIT_SHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a")
else()
string(APPEND INIT_SHARED_LINKER_FLAGS " -Wl,--whole-archive -lInit ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND INIT_SHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
endif()
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive -lInit_shared -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
else()
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
endif()
endif()
string(APPEND LEANSHARED_LINKER_FLAGS " -lInit_shared")
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# We do not use dynamic linking via leanshared for Emscripten to keep things

View File

@@ -37,6 +37,15 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
f (ite P x y) = ite P (f x) (f y) :=
apply_dite f P (fun _ => x) (fun _ => y)
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
@[simp] theorem dite_not (P : Prop) {_ : Decidable P} (x : ¬P α) (y : ¬¬P α) :
dite (¬P) x y = dite P (fun h => y (not_not_intro h)) x := by
by_cases h : P <;> simp [h]
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
@[simp] theorem ite_not (P : Prop) {_ : Decidable P} (x y : α) : ite (¬P) x y = ite P y x :=
dite_not P (fun _ => x) (fun _ => y)
@[simp] theorem dite_eq_left_iff {P : Prop} [Decidable P] {B : ¬ P α} :
dite P (fun _ => a) B = a h, B h = a := by
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]

View File

@@ -125,15 +125,16 @@ theorem byContradiction {p : Prop} (h : ¬p → False) : p :=
/-- The Double Negation Theorem: `¬¬P` is equivalent to `P`.
The left-to-right direction, double negation elimination (DNE),
is classically true but not constructively. -/
@[simp] theorem not_not : ¬¬a a := Decidable.not_not
@[scoped simp] theorem not_not : ¬¬a a := Decidable.not_not
@[simp low] theorem not_forall {p : α Prop} : (¬ x, p x) x, ¬p x := Decidable.not_forall
@[simp] theorem not_forall {p : α Prop} : (¬ x, p x) x, ¬p x := Decidable.not_forall
theorem not_forall_not {p : α Prop} : (¬ x, ¬p x) x, p x := Decidable.not_forall_not
theorem not_exists_not {p : α Prop} : (¬ x, ¬p x) x, p x := Decidable.not_exists_not
theorem forall_or_exists_not (P : α Prop) : ( a, P a) a, ¬ P a := by
rw [ not_forall]; exact em _
theorem exists_or_forall_not (P : α Prop) : ( a, P a) a, ¬ P a := by
rw [ not_exists]; exact em _
@@ -146,22 +147,8 @@ theorem not_and_iff_or_not_not : ¬(a ∧ b) ↔ ¬a ¬b := Decidable.not_an
theorem not_iff : ¬(a b) (¬a b) := Decidable.not_iff
@[simp] theorem imp_iff_left_iff : (b a b) a b := Decidable.imp_iff_left_iff
@[simp] theorem imp_iff_right_iff : (a b b) a b := Decidable.imp_iff_right_iff
@[simp] theorem and_or_imp : a b (a c) a b c := Decidable.and_or_imp
@[simp] theorem not_imp : ¬(a b) a ¬b := Decidable.not_imp_iff_and_not
@[simp] theorem imp_and_neg_imp_iff (p q : Prop) : (p q) (¬p q) q :=
Iff.intro (fun (a : _ _) => (Classical.em p).rec a.left a.right)
(fun a => And.intro (fun _ => a) (fun _ => a))
end Classical
/- Export for Mathlib compat. -/
export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
/-- Extract an element from a existential statement, using `Classical.choose`. -/
-- This enables projection notation.
@[reducible] noncomputable def Exists.choose {p : α Prop} (P : a, p a) : α := Classical.choose P

View File

@@ -321,7 +321,7 @@ Helper definition used by the elaborator. It is not meant to be used directly by
This is used for coercions between monads, in the case where we want to apply
a monad lift and a coercion on the result type at the same time.
-/
@[coe_decl] abbrev Lean.Internal.liftCoeM {m : Type u Type v} {n : Type u Type w} {α β : Type u}
@[inline, coe_decl] def Lean.Internal.liftCoeM {m : Type u Type v} {n : Type u Type w} {α β : Type u}
[MonadLiftT m n] [ a, CoeT α a β] [Monad n] (x : m α) : n β := do
let a liftM x
pure (CoeT.coe a)
@@ -331,7 +331,7 @@ Helper definition used by the elaborator. It is not meant to be used directly by
This is used for coercing the result type under a monad.
-/
@[coe_decl] abbrev Lean.Internal.coeM {m : Type u Type v} {α β : Type u}
@[inline, coe_decl] def Lean.Internal.coeM {m : Type u Type v} {α β : Type u}
[ a, CoeT α a β] [Monad m] (x : m α) : m β := do
let a x
pure (CoeT.coe a)

View File

@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful
/-!
The Exception monad transformer using CPS style.

View File

@@ -4,5 +4,373 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful.Instances
import Init.SimpLemmas
import Init.Control.Except
import Init.Control.StateRef
open Function
@[simp] theorem monadLift_self [Monad m] (x : m α) : monadLift x = x :=
rfl
class LawfulFunctor (f : Type u Type v) [Functor f] : Prop where
map_const : (Functor.mapConst : α f β f α) = Functor.map const β
id_map (x : f α) : id <$> x = x
comp_map (g : α β) (h : β γ) (x : f α) : (h g) <$> x = h <$> g <$> x
export LawfulFunctor (map_const id_map comp_map)
attribute [simp] id_map
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
id_map x
class LawfulApplicative (f : Type u Type v) [Applicative f] extends LawfulFunctor f : Prop where
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
pure_seq (g : α β) (x : f α) : pure g <*> x = g <$> x
map_pure (g : α β) (x : α) : g <$> (pure x : f α) = pure (g x)
seq_pure {α β : Type u} (g : f (α β)) (x : α) : g <*> pure x = (fun h => h x) <$> g
seq_assoc {α β γ : Type u} (x : f α) (g : f (α β)) (h : f (β γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
comp_map g h x := (by
repeat rw [ pure_seq]
simp [seq_assoc, map_pure, seq_pure])
export LawfulApplicative (seqLeft_eq seqRight_eq pure_seq map_pure seq_pure seq_assoc)
attribute [simp] map_pure seq_pure
@[simp] theorem pure_id_seq [Applicative f] [LawfulApplicative f] (x : f α) : pure id <*> x = x := by
simp [pure_seq]
class LawfulMonad (m : Type u Type v) [Monad m] extends LawfulApplicative m : Prop where
bind_pure_comp (f : α β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
bind_map {α β : Type u} (f : m (α β)) (x : m α) : f >>= (. <$> x) = f <*> x
pure_bind (x : α) (f : α m β) : pure x >>= f = f x
bind_assoc (x : m α) (f : α m β) (g : β m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
map_pure g x := (by rw [ bind_pure_comp, pure_bind])
seq_pure g x := (by rw [ bind_map]; simp [map_pure, bind_pure_comp])
seq_assoc x g h := (by simp [ bind_pure_comp, bind_map, bind_assoc, pure_bind])
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
attribute [simp] pure_bind bind_assoc
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x
rw [bind_pure_comp, id_map]
theorem map_eq_pure_bind [Monad m] [LawfulMonad m] (f : α β) (x : m α) : f <$> x = x >>= fun a => pure (f a) := by
rw [ bind_pure_comp]
theorem seq_eq_bind_map {α β : Type u} [Monad m] [LawfulMonad m] (f : m (α β)) (x : m α) : f <*> x = f >>= (. <$> x) := by
rw [ bind_map]
theorem bind_congr [Bind m] {x : m α} {f g : α m β} (h : a, f a = g a) : x >>= f = x >>= g := by
simp [funext h]
@[simp] theorem bind_pure_unit [Monad m] [LawfulMonad m] {x : m PUnit} : (x >>= fun _ => pure ) = x := by
rw [bind_pure]
theorem map_congr [Functor m] {x : m α} {f g : α β} (h : a, f a = g a) : (f <$> x : m β) = g <$> x := by
simp [funext h]
theorem seq_eq_bind {α β : Type u} [Monad m] [LawfulMonad m] (mf : m (α β)) (x : m α) : mf <*> x = mf >>= fun f => f <$> x := by
rw [bind_map]
theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *> y = x >>= fun _ => y := by
rw [seqRight_eq]
simp [map_eq_pure_bind, seq_eq_bind_map, const]
theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y = x >>= fun a => y >>= fun _ => pure a := by
rw [seqLeft_eq]; simp [map_eq_pure_bind, seq_eq_bind_map]
/--
An alternative constructor for `LawfulMonad` which has more
defaultable fields in the common case.
-/
theorem LawfulMonad.mk' (m : Type u Type v) [Monad m]
(id_map : {α} (x : m α), id <$> x = x)
(pure_bind : {α β} (x : α) (f : α m β), pure x >>= f = f x)
(bind_assoc : {α β γ} (x : m α) (f : α m β) (g : β m γ),
x >>= f >>= g = x >>= fun x => f x >>= g)
(map_const : {α β} (x : α) (y : m β),
Functor.mapConst x y = Function.const β x <$> y := by intros; rfl)
(seqLeft_eq : {α β} (x : m α) (y : m β),
x <* y = (x >>= fun a => y >>= fun _ => pure a) := by intros; rfl)
(seqRight_eq : {α β} (x : m α) (y : m β), x *> y = (x >>= fun _ => y) := by intros; rfl)
(bind_pure_comp : {α β} (f : α β) (x : m α),
x >>= (fun y => pure (f y)) = f <$> x := by intros; rfl)
(bind_map : {α β} (f : m (α β)) (x : m α), f >>= (. <$> x) = f <*> x := by intros; rfl)
: LawfulMonad m :=
have map_pure {α β} (g : α β) (x : α) : g <$> (pure x : m α) = pure (g x) := by
rw [ bind_pure_comp]; simp [pure_bind]
{ id_map, bind_pure_comp, bind_map, pure_bind, bind_assoc, map_pure,
comp_map := by simp [ bind_pure_comp, bind_assoc, pure_bind]
pure_seq := by intros; rw [ bind_map]; simp [pure_bind]
seq_pure := by intros; rw [ bind_map]; simp [map_pure, bind_pure_comp]
seq_assoc := by simp [ bind_pure_comp, bind_map, bind_assoc, pure_bind]
map_const := funext fun x => funext (map_const x)
seqLeft_eq := by simp [seqLeft_eq, bind_map, bind_pure_comp, pure_bind, bind_assoc]
seqRight_eq := fun x y => by
rw [seqRight_eq, bind_map, bind_pure_comp, bind_assoc]; simp [pure_bind, id_map] }
/-! # Id -/
namespace Id
@[simp] theorem map_eq (x : Id α) (f : α β) : f <$> x = f x := rfl
@[simp] theorem bind_eq (x : Id α) (f : α id β) : x >>= f = f x := rfl
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
instance : LawfulMonad Id := by
refine' { .. } <;> intros <;> rfl
end Id
/-! # ExceptT -/
namespace ExceptT
theorem ext [Monad m] {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
simp [run] at h
assumption
@[simp] theorem run_pure [Monad m] (x : α) : run (pure x : ExceptT ε m α) = pure (Except.ok x) := rfl
@[simp] theorem run_lift [Monad.{u, v} m] (x : m α) : run (ExceptT.lift x : ExceptT ε m α) = (Except.ok <$> x : m (Except ε α)) := rfl
@[simp] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl
@[simp] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by
simp[ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont, map_eq_pure_bind]
@[simp] theorem bind_throw [Monad m] [LawfulMonad m] (f : α ExceptT ε m β) : (throw e >>= f) = throw e := by
simp [throw, throwThe, MonadExceptOf.throw, bind, ExceptT.bind, ExceptT.bindCont, ExceptT.mk]
theorem run_bind [Monad m] (x : ExceptT ε m α)
: run (x >>= f : ExceptT ε m β)
=
run x >>= fun
| Except.ok x => run (f x)
| Except.error e => pure (Except.error e) :=
rfl
@[simp] theorem lift_pure [Monad m] [LawfulMonad m] (a : α) : ExceptT.lift (pure a) = (pure a : ExceptT ε m α) := by
simp [ExceptT.lift, pure, ExceptT.pure]
@[simp] theorem run_map [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α)
: (f <$> x).run = Except.map f <$> x.run := by
simp [Functor.map, ExceptT.map, map_eq_pure_bind]
apply bind_congr
intro a; cases a <;> simp [Except.map]
protected theorem seq_eq {α β ε : Type u} [Monad m] (mf : ExceptT ε m (α β)) (x : ExceptT ε m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl
protected theorem bind_pure_comp [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by
intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
show (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
apply bind_congr
intro
| Except.error _ => simp
| Except.ok _ =>
simp [map_eq_pure_bind]; apply bind_congr; intro b;
cases b <;> simp [comp, Except.map, const]
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
show (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
apply bind_congr
intro a; cases a <;> simp
instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
id_map := by intros; apply ext; simp
map_const := by intros; rfl
seqLeft_eq := ExceptT.seqLeft_eq
seqRight_eq := ExceptT.seqRight_eq
pure_seq := by intros; apply ext; simp [ExceptT.seq_eq, run_bind]
bind_pure_comp := ExceptT.bind_pure_comp
bind_map := by intros; rfl
pure_bind := by intros; apply ext; simp [run_bind]
bind_assoc := by intros; apply ext; simp [run_bind]; apply bind_congr; intro a; cases a <;> simp
end ExceptT
/-! # Except -/
instance : LawfulMonad (Except ε) := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun a f => rfl)
(bind_assoc := fun a f g => by cases a <;> rfl)
instance : LawfulApplicative (Except ε) := inferInstance
instance : LawfulFunctor (Except ε) := inferInstance
/-! # ReaderT -/
namespace ReaderT
theorem ext {x y : ReaderT ρ m α} (h : ctx, x.run ctx = y.run ctx) : x = y := by
simp [run] at h
exact funext h
@[simp] theorem run_pure [Monad m] (a : α) (ctx : ρ) : (pure a : ReaderT ρ m α).run ctx = pure a := rfl
@[simp] theorem run_bind [Monad m] (x : ReaderT ρ m α) (f : α ReaderT ρ m β) (ctx : ρ)
: (x >>= f).run ctx = x.run ctx >>= λ a => (f a).run ctx := rfl
@[simp] theorem run_mapConst [Monad m] (a : α) (x : ReaderT ρ m β) (ctx : ρ)
: (Functor.mapConst a x).run ctx = Functor.mapConst a (x.run ctx) := rfl
@[simp] theorem run_map [Monad m] (f : α β) (x : ReaderT ρ m α) (ctx : ρ)
: (f <$> x).run ctx = f <$> x.run ctx := rfl
@[simp] theorem run_monadLift [MonadLiftT n m] (x : n α) (ctx : ρ)
: (monadLift x : ReaderT ρ m α).run ctx = (monadLift x : m α) := rfl
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} n β n β) (x : ReaderT ρ m α) (ctx : ρ)
: (monadMap @f x : ReaderT ρ m α).run ctx = monadMap @f (x.run ctx) := rfl
@[simp] theorem run_read [Monad m] (ctx : ρ) : (ReaderT.read : ReaderT ρ m ρ).run ctx = pure ctx := rfl
@[simp] theorem run_seq {α β : Type u} [Monad m] (f : ReaderT ρ m (α β)) (x : ReaderT ρ m α) (ctx : ρ)
: (f <*> x).run ctx = (f.run ctx <*> x.run ctx) := rfl
@[simp] theorem run_seqRight [Monad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ)
: (x *> y).run ctx = (x.run ctx *> y.run ctx) := rfl
@[simp] theorem run_seqLeft [Monad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ)
: (x <* y).run ctx = (x.run ctx <* y.run ctx) := rfl
instance [Monad m] [LawfulFunctor m] : LawfulFunctor (ReaderT ρ m) where
id_map := by intros; apply ext; simp
map_const := by intros; funext a b; apply ext; intros; simp [map_const]
comp_map := by intros; apply ext; intros; simp [comp_map]
instance [Monad m] [LawfulApplicative m] : LawfulApplicative (ReaderT ρ m) where
seqLeft_eq := by intros; apply ext; intros; simp [seqLeft_eq]
seqRight_eq := by intros; apply ext; intros; simp [seqRight_eq]
pure_seq := by intros; apply ext; intros; simp [pure_seq]
map_pure := by intros; apply ext; intros; simp [map_pure]
seq_pure := by intros; apply ext; intros; simp [seq_pure]
seq_assoc := by intros; apply ext; intros; simp [seq_assoc]
instance [Monad m] [LawfulMonad m] : LawfulMonad (ReaderT ρ m) where
bind_pure_comp := by intros; apply ext; intros; simp [LawfulMonad.bind_pure_comp]
bind_map := by intros; apply ext; intros; simp [bind_map]
pure_bind := by intros; apply ext; intros; simp
bind_assoc := by intros; apply ext; intros; simp
end ReaderT
/-! # StateRefT -/
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateRefT' ω σ m) :=
inferInstanceAs (LawfulMonad (ReaderT (ST.Ref ω σ) m))
/-! # StateT -/
namespace StateT
theorem ext {x y : StateT σ m α} (h : s, x.run s = y.run s) : x = y :=
funext h
@[simp] theorem run'_eq [Monad m] (x : StateT σ m α) (s : σ) : run' x s = (·.1) <$> run x s :=
rfl
@[simp] theorem run_pure [Monad m] (a : α) (s : σ) : (pure a : StateT σ m α).run s = pure (a, s) := rfl
@[simp] theorem run_bind [Monad m] (x : StateT σ m α) (f : α StateT σ m β) (s : σ)
: (x >>= f).run s = x.run s >>= λ p => (f p.1).run p.2 := by
simp [bind, StateT.bind, run]
@[simp] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
simp [Functor.map, StateT.map, run, map_eq_pure_bind]
@[simp] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
@[simp] theorem run_set [Monad m] (s s' : σ) : (set s' : StateT σ m PUnit).run s = pure (, s') := rfl
@[simp] theorem run_modify [Monad m] (f : σ σ) (s : σ) : (modify f : StateT σ m PUnit).run s = pure (, f s) := rfl
@[simp] theorem run_modifyGet [Monad m] (f : σ α × σ) (s : σ) : (modifyGet f : StateT σ m α).run s = pure ((f s).1, (f s).2) := by
simp [modifyGet, MonadStateOf.modifyGet, StateT.modifyGet, run]
@[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
@[simp] theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp] theorem run_monadMap [Monad m] [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ)
: (monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
show (f >>= fun g => g <$> x).run s = _
simp
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _
simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _
simp
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by
apply ext; intro s
simp [map_eq_pure_bind, const]
apply bind_congr; intro p; cases p
simp [Prod.eta]
theorem seqLeft_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x <* y = const β <$> x <*> y := by
apply ext; intro s
simp [map_eq_pure_bind]
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
id_map := by intros; apply ext; intros; simp[Prod.eta]
map_const := by intros; rfl
seqLeft_eq := seqLeft_eq
seqRight_eq := seqRight_eq
pure_seq := by intros; apply ext; intros; simp
bind_pure_comp := by intros; apply ext; intros; simp; apply LawfulMonad.bind_pure_comp
bind_map := by intros; rfl
pure_bind := by intros; apply ext; intros; simp
bind_assoc := by intros; apply ext; intros; simp
end StateT
/-! # EStateM -/
instance : LawfulMonad (EStateM ε σ) := .mk'
(id_map := fun x => funext <| fun s => by
dsimp only [EStateM.instMonadEStateM, EStateM.map]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)
(pure_bind := fun _ _ => rfl)
(bind_assoc := fun x _ _ => funext <| fun s => by
dsimp only [EStateM.instMonadEStateM, EStateM.bind]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)
(map_const := fun _ _ => rfl)
/-! # Option -/
instance : LawfulMonad Option := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun x f => rfl)
(bind_assoc := fun x f g => by cases x <;> rfl)
(bind_pure_comp := fun f x => by cases x <;> rfl)
instance : LawfulApplicative Option := inferInstance
instance : LawfulFunctor Option := inferInstance

View File

@@ -1,139 +0,0 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
prelude
import Init.SimpLemmas
import Init.Meta
import Init.Data.Ord
open Function
@[simp] theorem monadLift_self [Monad m] (x : m α) : monadLift x = x :=
rfl
class LawfulFunctor (f : Type u Type v) [Functor f] : Prop where
map_const : (Functor.mapConst : α f β f α) = Functor.map const β
id_map (x : f α) : id <$> x = x
comp_map (g : α β) (h : β γ) (x : f α) : (h g) <$> x = h <$> g <$> x
export LawfulFunctor (map_const id_map comp_map)
attribute [simp] id_map
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
id_map x
class LawfulApplicative (f : Type u Type v) [Applicative f] extends LawfulFunctor f : Prop where
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
pure_seq (g : α β) (x : f α) : pure g <*> x = g <$> x
map_pure (g : α β) (x : α) : g <$> (pure x : f α) = pure (g x)
seq_pure {α β : Type u} (g : f (α β)) (x : α) : g <*> pure x = (fun h => h x) <$> g
seq_assoc {α β γ : Type u} (x : f α) (g : f (α β)) (h : f (β γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
comp_map g h x := (by
repeat rw [ pure_seq]
simp [seq_assoc, map_pure, seq_pure])
export LawfulApplicative (seqLeft_eq seqRight_eq pure_seq map_pure seq_pure seq_assoc)
attribute [simp] map_pure seq_pure
@[simp] theorem pure_id_seq [Applicative f] [LawfulApplicative f] (x : f α) : pure id <*> x = x := by
simp [pure_seq]
class LawfulMonad (m : Type u Type v) [Monad m] extends LawfulApplicative m : Prop where
bind_pure_comp (f : α β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
bind_map {α β : Type u} (f : m (α β)) (x : m α) : f >>= (. <$> x) = f <*> x
pure_bind (x : α) (f : α m β) : pure x >>= f = f x
bind_assoc (x : m α) (f : α m β) (g : β m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
map_pure g x := (by rw [ bind_pure_comp, pure_bind])
seq_pure g x := (by rw [ bind_map]; simp [map_pure, bind_pure_comp])
seq_assoc x g h := (by simp [ bind_pure_comp, bind_map, bind_assoc, pure_bind])
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
attribute [simp] pure_bind bind_assoc
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x
rw [bind_pure_comp, id_map]
theorem map_eq_pure_bind [Monad m] [LawfulMonad m] (f : α β) (x : m α) : f <$> x = x >>= fun a => pure (f a) := by
rw [ bind_pure_comp]
theorem seq_eq_bind_map {α β : Type u} [Monad m] [LawfulMonad m] (f : m (α β)) (x : m α) : f <*> x = f >>= (. <$> x) := by
rw [ bind_map]
theorem bind_congr [Bind m] {x : m α} {f g : α m β} (h : a, f a = g a) : x >>= f = x >>= g := by
simp [funext h]
@[simp] theorem bind_pure_unit [Monad m] [LawfulMonad m] {x : m PUnit} : (x >>= fun _ => pure ) = x := by
rw [bind_pure]
theorem map_congr [Functor m] {x : m α} {f g : α β} (h : a, f a = g a) : (f <$> x : m β) = g <$> x := by
simp [funext h]
theorem seq_eq_bind {α β : Type u} [Monad m] [LawfulMonad m] (mf : m (α β)) (x : m α) : mf <*> x = mf >>= fun f => f <$> x := by
rw [bind_map]
theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *> y = x >>= fun _ => y := by
rw [seqRight_eq]
simp [map_eq_pure_bind, seq_eq_bind_map, const]
theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y = x >>= fun a => y >>= fun _ => pure a := by
rw [seqLeft_eq]; simp [map_eq_pure_bind, seq_eq_bind_map]
/--
An alternative constructor for `LawfulMonad` which has more
defaultable fields in the common case.
-/
theorem LawfulMonad.mk' (m : Type u Type v) [Monad m]
(id_map : {α} (x : m α), id <$> x = x)
(pure_bind : {α β} (x : α) (f : α m β), pure x >>= f = f x)
(bind_assoc : {α β γ} (x : m α) (f : α m β) (g : β m γ),
x >>= f >>= g = x >>= fun x => f x >>= g)
(map_const : {α β} (x : α) (y : m β),
Functor.mapConst x y = Function.const β x <$> y := by intros; rfl)
(seqLeft_eq : {α β} (x : m α) (y : m β),
x <* y = (x >>= fun a => y >>= fun _ => pure a) := by intros; rfl)
(seqRight_eq : {α β} (x : m α) (y : m β), x *> y = (x >>= fun _ => y) := by intros; rfl)
(bind_pure_comp : {α β} (f : α β) (x : m α),
x >>= (fun y => pure (f y)) = f <$> x := by intros; rfl)
(bind_map : {α β} (f : m (α β)) (x : m α), f >>= (. <$> x) = f <*> x := by intros; rfl)
: LawfulMonad m :=
have map_pure {α β} (g : α β) (x : α) : g <$> (pure x : m α) = pure (g x) := by
rw [ bind_pure_comp]; simp [pure_bind]
{ id_map, bind_pure_comp, bind_map, pure_bind, bind_assoc, map_pure,
comp_map := by simp [ bind_pure_comp, bind_assoc, pure_bind]
pure_seq := by intros; rw [ bind_map]; simp [pure_bind]
seq_pure := by intros; rw [ bind_map]; simp [map_pure, bind_pure_comp]
seq_assoc := by simp [ bind_pure_comp, bind_map, bind_assoc, pure_bind]
map_const := funext fun x => funext (map_const x)
seqLeft_eq := by simp [seqLeft_eq, bind_map, bind_pure_comp, pure_bind, bind_assoc]
seqRight_eq := fun x y => by
rw [seqRight_eq, bind_map, bind_pure_comp, bind_assoc]; simp [pure_bind, id_map] }
/-! # Id -/
namespace Id
@[simp] theorem map_eq (x : Id α) (f : α β) : f <$> x = f x := rfl
@[simp] theorem bind_eq (x : Id α) (f : α id β) : x >>= f = f x := rfl
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
instance : LawfulMonad Id := by
refine' { .. } <;> intros <;> rfl
end Id
/-! # Option -/
instance : LawfulMonad Option := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun x f => rfl)
(bind_assoc := fun x f g => by cases x <;> rfl)
(bind_pure_comp := fun f x => by cases x <;> rfl)
instance : LawfulApplicative Option := inferInstance
instance : LawfulFunctor Option := inferInstance

View File

@@ -1,248 +0,0 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
prelude
import Init.Control.Lawful.Basic
import Init.Control.Except
import Init.Control.StateRef
open Function
/-! # ExceptT -/
namespace ExceptT
theorem ext [Monad m] {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
simp [run] at h
assumption
@[simp] theorem run_pure [Monad m] (x : α) : run (pure x : ExceptT ε m α) = pure (Except.ok x) := rfl
@[simp] theorem run_lift [Monad.{u, v} m] (x : m α) : run (ExceptT.lift x : ExceptT ε m α) = (Except.ok <$> x : m (Except ε α)) := rfl
@[simp] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl
@[simp] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by
simp[ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont, map_eq_pure_bind]
@[simp] theorem bind_throw [Monad m] [LawfulMonad m] (f : α ExceptT ε m β) : (throw e >>= f) = throw e := by
simp [throw, throwThe, MonadExceptOf.throw, bind, ExceptT.bind, ExceptT.bindCont, ExceptT.mk]
theorem run_bind [Monad m] (x : ExceptT ε m α)
: run (x >>= f : ExceptT ε m β)
=
run x >>= fun
| Except.ok x => run (f x)
| Except.error e => pure (Except.error e) :=
rfl
@[simp] theorem lift_pure [Monad m] [LawfulMonad m] (a : α) : ExceptT.lift (pure a) = (pure a : ExceptT ε m α) := by
simp [ExceptT.lift, pure, ExceptT.pure]
@[simp] theorem run_map [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α)
: (f <$> x).run = Except.map f <$> x.run := by
simp [Functor.map, ExceptT.map, map_eq_pure_bind]
apply bind_congr
intro a; cases a <;> simp [Except.map]
protected theorem seq_eq {α β ε : Type u} [Monad m] (mf : ExceptT ε m (α β)) (x : ExceptT ε m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl
protected theorem bind_pure_comp [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by
intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
show (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
apply bind_congr
intro
| Except.error _ => simp
| Except.ok _ =>
simp [map_eq_pure_bind]; apply bind_congr; intro b;
cases b <;> simp [comp, Except.map, const]
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
show (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
apply bind_congr
intro a; cases a <;> simp
instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
id_map := by intros; apply ext; simp
map_const := by intros; rfl
seqLeft_eq := ExceptT.seqLeft_eq
seqRight_eq := ExceptT.seqRight_eq
pure_seq := by intros; apply ext; simp [ExceptT.seq_eq, run_bind]
bind_pure_comp := ExceptT.bind_pure_comp
bind_map := by intros; rfl
pure_bind := by intros; apply ext; simp [run_bind]
bind_assoc := by intros; apply ext; simp [run_bind]; apply bind_congr; intro a; cases a <;> simp
end ExceptT
/-! # Except -/
instance : LawfulMonad (Except ε) := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun a f => rfl)
(bind_assoc := fun a f g => by cases a <;> rfl)
instance : LawfulApplicative (Except ε) := inferInstance
instance : LawfulFunctor (Except ε) := inferInstance
/-! # ReaderT -/
namespace ReaderT
theorem ext {x y : ReaderT ρ m α} (h : ctx, x.run ctx = y.run ctx) : x = y := by
simp [run] at h
exact funext h
@[simp] theorem run_pure [Monad m] (a : α) (ctx : ρ) : (pure a : ReaderT ρ m α).run ctx = pure a := rfl
@[simp] theorem run_bind [Monad m] (x : ReaderT ρ m α) (f : α ReaderT ρ m β) (ctx : ρ)
: (x >>= f).run ctx = x.run ctx >>= λ a => (f a).run ctx := rfl
@[simp] theorem run_mapConst [Monad m] (a : α) (x : ReaderT ρ m β) (ctx : ρ)
: (Functor.mapConst a x).run ctx = Functor.mapConst a (x.run ctx) := rfl
@[simp] theorem run_map [Monad m] (f : α β) (x : ReaderT ρ m α) (ctx : ρ)
: (f <$> x).run ctx = f <$> x.run ctx := rfl
@[simp] theorem run_monadLift [MonadLiftT n m] (x : n α) (ctx : ρ)
: (monadLift x : ReaderT ρ m α).run ctx = (monadLift x : m α) := rfl
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} n β n β) (x : ReaderT ρ m α) (ctx : ρ)
: (monadMap @f x : ReaderT ρ m α).run ctx = monadMap @f (x.run ctx) := rfl
@[simp] theorem run_read [Monad m] (ctx : ρ) : (ReaderT.read : ReaderT ρ m ρ).run ctx = pure ctx := rfl
@[simp] theorem run_seq {α β : Type u} [Monad m] (f : ReaderT ρ m (α β)) (x : ReaderT ρ m α) (ctx : ρ)
: (f <*> x).run ctx = (f.run ctx <*> x.run ctx) := rfl
@[simp] theorem run_seqRight [Monad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ)
: (x *> y).run ctx = (x.run ctx *> y.run ctx) := rfl
@[simp] theorem run_seqLeft [Monad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ)
: (x <* y).run ctx = (x.run ctx <* y.run ctx) := rfl
instance [Monad m] [LawfulFunctor m] : LawfulFunctor (ReaderT ρ m) where
id_map := by intros; apply ext; simp
map_const := by intros; funext a b; apply ext; intros; simp [map_const]
comp_map := by intros; apply ext; intros; simp [comp_map]
instance [Monad m] [LawfulApplicative m] : LawfulApplicative (ReaderT ρ m) where
seqLeft_eq := by intros; apply ext; intros; simp [seqLeft_eq]
seqRight_eq := by intros; apply ext; intros; simp [seqRight_eq]
pure_seq := by intros; apply ext; intros; simp [pure_seq]
map_pure := by intros; apply ext; intros; simp [map_pure]
seq_pure := by intros; apply ext; intros; simp [seq_pure]
seq_assoc := by intros; apply ext; intros; simp [seq_assoc]
instance [Monad m] [LawfulMonad m] : LawfulMonad (ReaderT ρ m) where
bind_pure_comp := by intros; apply ext; intros; simp [LawfulMonad.bind_pure_comp]
bind_map := by intros; apply ext; intros; simp [bind_map]
pure_bind := by intros; apply ext; intros; simp
bind_assoc := by intros; apply ext; intros; simp
end ReaderT
/-! # StateRefT -/
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateRefT' ω σ m) :=
inferInstanceAs (LawfulMonad (ReaderT (ST.Ref ω σ) m))
/-! # StateT -/
namespace StateT
theorem ext {x y : StateT σ m α} (h : s, x.run s = y.run s) : x = y :=
funext h
@[simp] theorem run'_eq [Monad m] (x : StateT σ m α) (s : σ) : run' x s = (·.1) <$> run x s :=
rfl
@[simp] theorem run_pure [Monad m] (a : α) (s : σ) : (pure a : StateT σ m α).run s = pure (a, s) := rfl
@[simp] theorem run_bind [Monad m] (x : StateT σ m α) (f : α StateT σ m β) (s : σ)
: (x >>= f).run s = x.run s >>= λ p => (f p.1).run p.2 := by
simp [bind, StateT.bind, run]
@[simp] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
simp [Functor.map, StateT.map, run, map_eq_pure_bind]
@[simp] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
@[simp] theorem run_set [Monad m] (s s' : σ) : (set s' : StateT σ m PUnit).run s = pure (, s') := rfl
@[simp] theorem run_modify [Monad m] (f : σ σ) (s : σ) : (modify f : StateT σ m PUnit).run s = pure (, f s) := rfl
@[simp] theorem run_modifyGet [Monad m] (f : σ α × σ) (s : σ) : (modifyGet f : StateT σ m α).run s = pure ((f s).1, (f s).2) := by
simp [modifyGet, MonadStateOf.modifyGet, StateT.modifyGet, run]
@[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
@[simp] theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp] theorem run_monadMap [Monad m] [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ)
: (monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
show (f >>= fun g => g <$> x).run s = _
simp
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _
simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _
simp
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by
apply ext; intro s
simp [map_eq_pure_bind, const]
apply bind_congr; intro p; cases p
simp [Prod.eta]
theorem seqLeft_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x <* y = const β <$> x <*> y := by
apply ext; intro s
simp [map_eq_pure_bind]
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
id_map := by intros; apply ext; intros; simp[Prod.eta]
map_const := by intros; rfl
seqLeft_eq := seqLeft_eq
seqRight_eq := seqRight_eq
pure_seq := by intros; apply ext; intros; simp
bind_pure_comp := by intros; apply ext; intros; simp; apply LawfulMonad.bind_pure_comp
bind_map := by intros; rfl
pure_bind := by intros; apply ext; intros; simp
bind_assoc := by intros; apply ext; intros; simp
end StateT
/-! # EStateM -/
instance : LawfulMonad (EStateM ε σ) := .mk'
(id_map := fun x => funext <| fun s => by
dsimp only [EStateM.instMonadEStateM, EStateM.map]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)
(pure_bind := fun _ _ => rfl)
(bind_assoc := fun x _ _ => funext <| fun s => by
dsimp only [EStateM.instMonadEStateM, EStateM.bind]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)
(map_const := fun _ _ => rfl)

View File

@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful
/-!
The State monad transformer using CPS style.

View File

@@ -677,7 +677,7 @@ You can prove theorems about the resulting element by induction on `h`, since
theorem Eq.substr {α : Sort u} {p : α Prop} {a b : α} (h₁ : b = a) (h₂ : p a) : p b :=
h₁ h₂
@[simp] theorem cast_eq {α : Sort u} (h : α = α) (a : α) : cast h a = a :=
theorem cast_eq {α : Sort u} (h : α = α) (a : α) : cast h a = a :=
rfl
/--
@@ -1403,9 +1403,9 @@ theorem false_imp_iff (a : Prop) : (False → a) ↔ True := iff_true_intro Fals
theorem true_imp_iff (α : Prop) : (True α) α := imp_iff_right True.intro
@[simp high] theorem imp_self : (a a) True := iff_true_intro id
@[simp] theorem imp_self : (a a) True := iff_true_intro id
@[simp] theorem imp_false : (a False) ¬a := Iff.rfl
theorem imp_false : (a False) ¬a := Iff.rfl
theorem imp.swap : (a b c) (b a c) := Iff.intro flip flip

View File

@@ -8,7 +8,6 @@ import Init.Data.Nat.MinMax
import Init.Data.List.Lemmas
import Init.Data.Fin.Basic
import Init.Data.Array.Mem
import Init.TacticsExtra
/-!
## Bootstrapping theorems about arrays
@@ -186,84 +185,3 @@ theorem anyM_stop_le_start [Monad m] (p : α → m Bool) (as : Array α) (start
theorem mem_def (a : α) (as : Array α) : a as a as.data :=
fun | .mk h => h, Array.Mem.mk
/-- # get -/
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
theorem getElem?_lt
(a : Array α) {i : Nat} (h : i < a.size) : a[i]? = some (a[i]) := dif_pos h
theorem getElem?_ge
(a : Array α) {i : Nat} (h : i a.size) : a[i]? = none := dif_neg (Nat.not_lt_of_le h)
@[simp] theorem get?_eq_getElem? (a : Array α) (i : Nat) : a.get? i = a[i]? := rfl
theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size i) : a[i]? = none := by
simp [getElem?_ge, h]
theorem getD_get? (a : Array α) (i : Nat) (d : α) :
Option.getD a[i]? d = if p : i < a.size then a[i]'p else d := by
if h : i < a.size then
simp [setD, h, getElem?]
else
have p : i a.size := Nat.le_of_not_gt h
simp [setD, getElem?_len_le _ p, h]
@[simp] theorem getD_eq_get? (a : Array α) (n d) : a.getD n d = (a[n]?).getD d := by
simp only [getD, get_eq_getElem, get?_eq_getElem?]; split <;> simp [getD_get?, *]
theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default := rfl
@[simp] theorem get!_eq_getElem? [Inhabited α] (a : Array α) (i : Nat) : a.get! i = (a.get? i).getD default := by
by_cases p : i < a.size <;> simp [getD_get?, get!_eq_getD, p]
/-- # set -/
@[simp] theorem getElem_set_eq (a : Array α) (i : Fin a.size) (v : α) {j : Nat}
(eq : i.val = j) (p : j < (a.set i v).size) :
(a.set i v)[j]'p = v := by
simp [set, getElem_eq_data_get, eq]
@[simp] theorem getElem_set_ne (a : Array α) (i : Fin a.size) (v : α) {j : Nat} (pj : j < (a.set i v).size)
(h : i.val j) : (a.set i v)[j]'pj = a[j]'(size_set a i v pj) := by
simp only [set, getElem_eq_data_get, List.get_set_ne _ h]
theorem getElem_set (a : Array α) (i : Fin a.size) (v : α) (j : Nat)
(h : j < (a.set i v).size) :
(a.set i v)[j]'h = if i = j then v else a[j]'(size_set a i v h) := by
by_cases p : i.1 = j <;> simp [p]
@[simp] theorem getElem?_set_eq (a : Array α) (i : Fin a.size) (v : α) :
(a.set i v)[i.1]? = v := by simp [getElem?_lt, i.2]
@[simp] theorem getElem?_set_ne (a : Array α) (i : Fin a.size) {j : Nat} (v : α)
(ne : i.val j) : (a.set i v)[j]? = a[j]? := by
by_cases h : j < a.size <;> simp [getElem?_lt, getElem?_ge, Nat.ge_of_not_lt, ne, h]
/- # setD -/
@[simp] theorem set!_is_setD : @set! = @setD := rfl
@[simp] theorem size_setD (a : Array α) (index : Nat) (val : α) :
(Array.setD a index val).size = a.size := by
if h : index < a.size then
simp [setD, h]
else
simp [setD, h]
@[simp] theorem getElem_setD_eq (a : Array α) {i : Nat} (v : α) (h : _) :
(setD a i v)[i]'h = v := by
simp at h
simp only [setD, h, dite_true, getElem_set, ite_true]
@[simp]
theorem getElem?_setD_eq (a : Array α) {i : Nat} (p : i < a.size) (v : α) : (a.setD i v)[i]? = some v := by
simp [getElem?_lt, p]
/-- Simplifies a normal form from `get!` -/
@[simp] theorem getD_get?_setD (a : Array α) (i : Nat) (v d : α) :
Option.getD (setD a i v)[i]? d = if i < a.size then v else d := by
by_cases h : i < a.size <;>
simp [setD, Nat.not_lt_of_le, h, getD_get?]
end Array

View File

@@ -7,7 +7,6 @@ prelude
import Init.Data.Fin.Basic
import Init.Data.Nat.Bitwise.Lemmas
import Init.Data.Nat.Power2
import Init.Data.Int.Bitwise
/-!
We define bitvectors. We choose the `Fin` representation over others for its relative efficiency
@@ -125,20 +124,13 @@ section Int
/-- Interpret the bitvector as an integer stored in two's complement form. -/
protected def toInt (a : BitVec n) : Int :=
if 2 * a.toNat < 2^n then
a.toNat
else
(a.toNat : Int) - (2^n : Nat)
if a.msb then Int.ofNat a.toNat - Int.ofNat (2^n) else a.toNat
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
apply (Int.toNat_lt _).mpr
· apply Int.emod_lt_of_pos
exact Int.ofNat_pos.mpr (Nat.two_pow_pos _)
· apply Int.emod_nonneg
intro eq
apply Nat.ne_of_gt (Nat.two_pow_pos n)
exact Int.ofNat_inj.mp eq)
protected def ofInt (n : Nat) (i : Int) : BitVec n :=
match i with
| Int.ofNat x => .ofNat n x
| Int.negSucc x => BitVec.ofNatLt (2^n - x % 2^n - 1) (by omega)
instance : IntCast (BitVec w) := BitVec.ofInt w

View File

@@ -5,7 +5,6 @@ Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix
-/
prelude
import Init.Data.BitVec.Folds
import Init.Data.Nat.Mod
/-!
# Bitblasting of bitvectors
@@ -71,8 +70,24 @@ private theorem testBit_limit {x i : Nat} (x_lt_succ : x < 2^(i+1)) :
_ x := testBit_implies_ge jp
private theorem mod_two_pow_succ (x i : Nat) :
x % 2^(i+1) = 2^i*(x.testBit i).toNat + x % (2 ^ i):= by
rw [Nat.mod_pow_succ, Nat.add_comm, Nat.toNat_testBit]
x % 2^(i+1) = 2^i*(x.testBit i).toNat + x % (2 ^ i):= by
apply Nat.eq_of_testBit_eq
intro j
simp only [Nat.mul_add_lt_is_or, testBit_or, testBit_mod_two_pow, testBit_shiftLeft,
Nat.testBit_bool_to_nat, Nat.sub_eq_zero_iff_le, Nat.mod_lt, Nat.two_pow_pos,
testBit_mul_pow_two]
rcases Nat.lt_trichotomy i j with i_lt_j | i_eq_j | j_lt_i
· have i_le_j : i j := Nat.le_of_lt i_lt_j
have not_j_le_i : ¬(j i) := Nat.not_le_of_lt i_lt_j
have not_j_lt_i : ¬(j < i) := Nat.not_lt_of_le i_le_j
have not_j_lt_i_succ : ¬(j < i + 1) :=
Nat.not_le_of_lt (Nat.succ_lt_succ i_lt_j)
simp [i_le_j, not_j_le_i, not_j_lt_i, not_j_lt_i_succ]
· simp [i_eq_j]
· have j_le_i : j i := Nat.le_of_lt j_lt_i
have j_le_i_succ : j < i + 1 := Nat.succ_le_succ j_le_i
have not_j_ge_i : ¬(j i) := Nat.not_le_of_lt j_lt_i
simp [j_lt_i, j_le_i, not_j_ge_i, j_le_i_succ]
private theorem mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ
(x y i : Nat) (c : Bool) : x % 2^i + (y % 2^i + c.toNat) < 2^(i+1) := by

View File

@@ -36,7 +36,7 @@ theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsb i := rfl
@[simp] theorem getLsb_ofFin (x : Fin (2^n)) (i : Nat) :
getLsb (BitVec.ofFin x) i = x.val.testBit i := rfl
@[simp] theorem getLsb_ge (x : BitVec w) (i : Nat) (ge : w i) : getLsb x i = false := by
@[simp] theorem getLsb_ge (x : BitVec w) (i : Nat) (ge : i w) : getLsb x i = false := by
let x, x_lt := x
simp
apply Nat.testBit_lt_two_pow
@@ -89,9 +89,6 @@ theorem eq_of_toFin_eq : ∀ {x y : BitVec w}, x.toFin = y.toFin → x = y
@[simp] theorem toNat_ofBool (b : Bool) : (ofBool b).toNat = b.toNat := by
cases b <;> rfl
@[simp] theorem msb_ofBool (b : Bool) : (ofBool b).msb = b := by
cases b <;> simp [BitVec.msb]
theorem ofNat_one (n : Nat) : BitVec.ofNat 1 n = BitVec.ofBool (n % 2 = 1) := by
rcases (Nat.mod_two_eq_zero_or_one n) with h | h <;> simp [h, BitVec.ofNat, Fin.ofNat']
@@ -119,8 +116,6 @@ theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
@[simp] theorem getLsb_zero : (0#w).getLsb i = false := by simp [getLsb]
@[simp] theorem getMsb_zero : (0#w).getMsb i = false := by simp [getMsb]
@[simp] theorem toNat_mod_cancel (x : BitVec n) : x.toNat % (2^n) = x.toNat :=
Nat.mod_eq_of_lt x.isLt
@@ -138,35 +133,21 @@ theorem msb_eq_getLsb_last (x : BitVec w) :
· simp [BitVec.eq_nil x]
· simp
@[bv_toNat] theorem getLsb_last (x : BitVec w) :
x.getLsb (w-1) = decide (2 ^ (w-1) x.toNat) := by
rcases w with rfl | w
· simp
· simp only [Nat.zero_lt_succ, decide_True, getLsb, Nat.testBit, Nat.succ_sub_succ_eq_sub,
@[bv_toNat] theorem getLsb_last (x : BitVec (w + 1)) :
x.getLsb w = decide (2 ^ w x.toNat) := by
simp only [Nat.zero_lt_succ, decide_True, getLsb, Nat.testBit, Nat.succ_sub_succ_eq_sub,
Nat.sub_zero, Nat.and_one_is_mod, Bool.true_and, Nat.shiftRight_eq_div_pow]
rcases (Nat.lt_or_ge (BitVec.toNat x) (2 ^ w)) with h | h
· simp [Nat.div_eq_of_lt h, h]
· simp only [h]
rw [Nat.div_eq_sub_div (Nat.two_pow_pos w) h, Nat.div_eq_of_lt]
· decide
· have : BitVec.toNat x < 2^w + 2^w := by simpa [Nat.pow_succ, Nat.mul_two] using x.isLt
omega
rcases (Nat.lt_or_ge (BitVec.toNat x) (2 ^ w)) with h | h
· simp [Nat.div_eq_of_lt h, h]
· simp only [h]
rw [Nat.div_eq_sub_div (Nat.two_pow_pos w) h, Nat.div_eq_of_lt]
· decide
· have : BitVec.toNat x < 2^w + 2^w := by simpa [Nat.pow_succ, Nat.mul_two] using x.isLt
omega
@[bv_toNat] theorem getLsb_succ_last (x : BitVec (w + 1)) :
x.getLsb w = decide (2 ^ w x.toNat) := getLsb_last x
@[bv_toNat] theorem msb_eq_decide (x : BitVec w) : BitVec.msb x = decide (2 ^ (w-1) x.toNat) := by
@[bv_toNat] theorem msb_eq_decide (x : BitVec (w + 1)) : BitVec.msb x = decide (2 ^ w x.toNat) := by
simp [msb_eq_getLsb_last, getLsb_last]
theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat 2^(n-1) := by
match n with
| 0 =>
simp [BitVec.msb, BitVec.getMsb] at p
| n + 1 =>
simp [BitVec.msb_eq_decide] at p
simp only [Nat.add_sub_cancel]
exact p
/-! ### cast -/
@[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (cast h x).toNat = x.toNat := rfl
@@ -182,53 +163,6 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
@[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (cast h x).msb = x.msb := by
simp [BitVec.msb]
/-! ### toInt/ofInt -/
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem toInt_eq_toNat_cond (i : BitVec n) :
i.toInt =
if 2*i.toNat < 2^n then
(i.toNat : Int)
else
(i.toNat : Int) - (2^n : Nat) := by
unfold BitVec.toInt
split <;> omega
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
simp only [toInt_eq_toNat_cond]
split
case inl g =>
rw [Int.bmod_pos] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
omega
case inr g =>
rw [Int.bmod_neg] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
omega
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toInt_eq {i j : BitVec n} : i.toInt = j.toInt i = j := by
intro eq
simp [toInt_eq_toNat_cond] at eq
apply eq_of_toNat_eq
revert eq
have _ilt := i.isLt
have _jlt := j.isLt
split <;> split <;> omega
@[simp] theorem toNat_ofInt {n : Nat} (i : Int) :
(BitVec.ofInt n i).toNat = (i % (2^n : Nat)).toNat := by
unfold BitVec.ofInt
simp
theorem toInt_ofNat {n : Nat} (x : Nat) :
(BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
simp [toInt_eq_toNat_bmod]
@[simp] theorem toInt_ofInt {n : Nat} (i : Int) :
(BitVec.ofInt n i).toInt = i.bmod (2^n) := by
have _ := Nat.two_pow_pos n
have p : 0 i % (2^n : Nat) := by omega
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
/-! ### zeroExtend and truncate -/
@[simp, bv_toNat] theorem toNat_zeroExtend' {m n : Nat} (p : m n) (x : BitVec m) :
@@ -246,12 +180,6 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
else
simp [n_le_i, toNat_ofNat]
theorem zeroExtend'_eq {x : BitVec w} (h : w v) : x.zeroExtend' h = x.zeroExtend v := by
apply eq_of_toNat_eq
rw [toNat_zeroExtend, toNat_zeroExtend']
rw [Nat.mod_eq_of_lt]
exact Nat.lt_of_lt_of_le x.isLt (Nat.pow_le_pow_right (Nat.zero_lt_two) h)
@[simp, bv_toNat] theorem toNat_truncate (x : BitVec n) : (truncate i x).toNat = x.toNat % 2^i :=
toNat_zeroExtend i x
@@ -270,24 +198,6 @@ theorem zeroExtend'_eq {x : BitVec w} (h : w ≤ v) : x.zeroExtend' h = x.zeroEx
apply eq_of_toNat_eq
simp
/-- Moves one-sided left toNat equality to BitVec equality. -/
theorem toNat_eq_nat (x : BitVec w) (y : Nat)
: (x.toNat = y) (y < 2^w (x = y#w)) := by
apply Iff.intro
· intro eq
simp at eq
have lt := x.isLt
simp [eq] at lt
simp [eq, lt, x.isLt]
· intro eq
simp [Nat.mod_eq_of_lt, eq]
/-- Moves one-sided right toNat equality to BitVec equality. -/
theorem nat_eq_toNat (x : BitVec w) (y : Nat)
: (y = x.toNat) (y < 2^w (x = y#w)) := by
rw [@eq_comm _ _ x.toNat]
apply toNat_eq_nat
@[simp] theorem getLsb_zeroExtend' (ge : m n) (x : BitVec n) (i : Nat) :
getLsb (zeroExtend' ge x) i = getLsb x i := by
simp [getLsb, toNat_zeroExtend']
@@ -296,25 +206,10 @@ theorem nat_eq_toNat (x : BitVec w) (y : Nat)
getLsb (zeroExtend m x) i = (decide (i < m) && getLsb x i) := by
simp [getLsb, toNat_zeroExtend, Nat.testBit_mod_two_pow]
@[simp] theorem getMsb_zeroExtend_add {x : BitVec w} (h : k i) :
(x.zeroExtend (w + k)).getMsb i = x.getMsb (i - k) := by
by_cases h : w = 0
· subst h; simp
simp only [getMsb, getLsb_zeroExtend]
by_cases h₁ : i < w + k <;> by_cases h₂ : i - k < w <;> by_cases h₃ : w + k - 1 - i < w + k
<;> simp [h₁, h₂, h₃]
· congr 1
omega
all_goals (first | apply getLsb_ge | apply Eq.symm; apply getLsb_ge)
<;> omega
@[simp] theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
getLsb (truncate m x) i = (decide (i < m) && getLsb x i) :=
getLsb_zeroExtend m x i
theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k := by
simp [BitVec.msb, getMsb]
@[simp] theorem zeroExtend_zeroExtend_of_le (x : BitVec w) (h : k l) :
(x.zeroExtend l).zeroExtend k = x.zeroExtend k := by
ext i
@@ -327,18 +222,11 @@ theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k := b
(x.truncate l).truncate k = x.truncate k :=
zeroExtend_zeroExtend_of_le x h
@[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k := by
apply eq_of_getLsb_eq
simp
theorem msb_zeroExtend (x : BitVec w) : (x.zeroExtend v).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
rw [msb_eq_getLsb_last]
simp only [getLsb_zeroExtend]
cases getLsb x (v - 1) <;> simp; omega
theorem msb_zeroExtend' (x : BitVec w) (h : w v) : (x.zeroExtend' h).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
rw [zeroExtend'_eq, msb_zeroExtend]
/-! ## extractLsb -/
@[simp]
@@ -386,18 +274,6 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
rw [ testBit_toNat, getLsb, getLsb]
simp
@[simp] theorem getMsb_or {x y : BitVec w} : (x ||| y).getMsb i = (x.getMsb i || y.getMsb i) := by
simp only [getMsb]
by_cases h : i < w <;> simp [h]
@[simp] theorem msb_or {x y : BitVec w} : (x ||| y).msb = (x.msb || y.msb) := by
simp [BitVec.msb]
@[simp] theorem truncate_or {x y : BitVec w} :
(x ||| y).truncate k = x.truncate k ||| y.truncate k := by
ext
simp
/-! ### and -/
@[simp] theorem toNat_and (x y : BitVec v) :
@@ -412,18 +288,6 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
rw [ testBit_toNat, getLsb, getLsb]
simp
@[simp] theorem getMsb_and {x y : BitVec w} : (x &&& y).getMsb i = (x.getMsb i && y.getMsb i) := by
simp only [getMsb]
by_cases h : i < w <;> simp [h]
@[simp] theorem msb_and {x y : BitVec w} : (x &&& y).msb = (x.msb && y.msb) := by
simp [BitVec.msb]
@[simp] theorem truncate_and {x y : BitVec w} :
(x &&& y).truncate k = x.truncate k &&& y.truncate k := by
ext
simp
/-! ### xor -/
@[simp] theorem toNat_xor (x y : BitVec v) :
@@ -439,11 +303,6 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
rw [ testBit_toNat, getLsb, getLsb]
simp
@[simp] theorem truncate_xor {x y : BitVec w} :
(x ^^^ y).truncate k = x.truncate k ^^^ y.truncate k := by
ext
simp
/-! ### not -/
theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@@ -476,12 +335,6 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem getLsb_not {x : BitVec v} : (~~~x).getLsb i = (decide (i < v) && ! x.getLsb i) := by
by_cases h' : i < v <;> simp_all [not_def]
@[simp] theorem truncate_not {x : BitVec w} (h : k w) :
(~~~x).truncate k = ~~~(x.truncate k) := by
ext
simp [h]
omega
/-! ### shiftLeft -/
@[simp, bv_toNat] theorem toNat_shiftLeft {x : BitVec v} :
@@ -499,19 +352,6 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
cases h₁ : decide (i < m) <;> cases h₂ : decide (n i) <;> cases h₃ : decide (i < n)
all_goals { simp_all <;> omega }
@[simp] theorem getMsb_shiftLeft (x : BitVec w) (i) :
(x <<< i).getMsb k = x.getMsb (k + i) := by
simp only [getMsb, getLsb_shiftLeft]
by_cases h : w = 0
· subst h; simp
have t : w - 1 - k < w := by omega
simp only [t]
simp only [decide_True, Nat.sub_sub, Bool.true_and, Nat.add_assoc]
by_cases h₁ : k < w <;> by_cases h₂ : w - (1 + k) < i <;> by_cases h₃ : k + i < w
<;> simp [h₁, h₂, h₃]
<;> (first | apply getLsb_ge | apply Eq.symm; apply getLsb_ge)
<;> omega
theorem shiftLeftZeroExtend_eq {x : BitVec w} :
shiftLeftZeroExtend x n = zeroExtend (w+n) x <<< n := by
apply eq_of_toNat_eq
@@ -531,10 +371,6 @@ theorem shiftLeftZeroExtend_eq {x : BitVec w} :
<;> simp_all
<;> (rw [getLsb_ge]; omega)
@[simp] theorem msb_shiftLeftZeroExtend (x : BitVec w) (i : Nat) :
(shiftLeftZeroExtend x i).msb = x.msb := by
simp [shiftLeftZeroExtend_eq, BitVec.msb]
/-! ### ushiftRight -/
@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
@@ -560,34 +396,6 @@ theorem append_def (x : BitVec v) (y : BitVec w) :
· simp [h]
· simp [h]; simp_all
theorem msb_append {x : BitVec w} {y : BitVec v} :
(x ++ y).msb = bif (w == 0) then (y.msb) else (x.msb) := by
rw [ append_eq, append]
simp [msb_zeroExtend']
by_cases h : w = 0
· subst h
simp [BitVec.msb, getMsb]
· rw [cond_eq_if]
have q : 0 < w + v := by omega
have t : y.getLsb (w + v - 1) = false := getLsb_ge _ _ (by omega)
simp [h, q, t, BitVec.msb, getMsb]
@[simp] theorem truncate_append {x : BitVec w} {y : BitVec v} :
(x ++ y).truncate k = if h : k v then y.truncate k else (x.truncate (k - v) ++ y).cast (by omega) := by
apply eq_of_getLsb_eq
intro i
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, getLsb_append, Bool.true_and]
split
· have t : i < v := by omega
simp [t]
· by_cases t : i < v
· simp [t]
· have t' : i - v < k - v := by omega
simp [t, t']
@[simp] theorem truncate_cons {x : BitVec w} : (cons a x).truncate w = x := by
simp [cons]
/-! ### rev -/
theorem getLsb_rev (x : BitVec w) (i : Fin w) :
@@ -610,11 +418,6 @@ theorem getMsb_rev (x : BitVec w) (i : Fin w) :
let x, _ := x
simp [cons, toNat_append, toNat_ofBool]
/-- Variant of `toNat_cons` using `+` instead of `|||`. -/
theorem toNat_cons' {x : BitVec w} :
(cons a x).toNat = (a.toNat <<< w) + x.toNat := by
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.mul_add_lt_is_or, x.isLt]
@[simp] theorem getLsb_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
getLsb (cons b x) i = if i = n then b else getLsb x i := by
simp only [getLsb, toNat_cons, Nat.testBit_or]
@@ -629,9 +432,6 @@ theorem toNat_cons' {x : BitVec w} :
have p2 : i - n 0 := by omega
simp [p1, p2, Nat.testBit_bool_to_nat]
@[simp] theorem msb_cons : (cons a x).msb = a := by
simp [cons, msb_cast, msb_append]
theorem truncate_succ (x : BitVec w) :
truncate (i+1) x = cons (getLsb x i) (truncate i x) := by
apply eq_of_getLsb_eq
@@ -643,15 +443,6 @@ theorem truncate_succ (x : BitVec w) :
have j_lt : j.val < i := Nat.lt_of_le_of_ne (Nat.le_of_succ_le_succ j.isLt) j_eq
simp [j_eq, j_lt]
theorem eq_msb_cons_truncate (x : BitVec (w+1)) : x = (cons x.msb (x.truncate w)) := by
ext i
simp
split <;> rename_i h
· simp [BitVec.msb, getMsb, h]
· by_cases h' : i < w
· simp_all
· omega
/-! ### concat -/
@[simp] theorem toNat_concat (x : BitVec w) (b : Bool) :
@@ -676,21 +467,6 @@ theorem getLsb_concat (x : BitVec w) (b : Bool) (i : Nat) :
@[simp] theorem getLsb_concat_succ : (concat x b).getLsb (i + 1) = x.getLsb i := by
simp [getLsb_concat]
@[simp] theorem not_concat (x : BitVec w) (b : Bool) : ~~~(concat x b) = concat (~~~x) !b := by
ext i; cases i using Fin.succRecOn <;> simp [*, Nat.succ_lt_succ]
@[simp] theorem concat_or_concat (x y : BitVec w) (a b : Bool) :
(concat x a) ||| (concat y b) = concat (x ||| y) (a || b) := by
ext i; cases i using Fin.succRecOn <;> simp
@[simp] theorem concat_and_concat (x y : BitVec w) (a b : Bool) :
(concat x a) &&& (concat y b) = concat (x &&& y) (a && b) := by
ext i; cases i using Fin.succRecOn <;> simp
@[simp] theorem concat_xor_concat (x y : BitVec w) (a b : Bool) :
(concat x a) ^^^ (concat y b) = concat (x ^^^ y) (xor a b) := by
ext i; cases i using Fin.succRecOn <;> simp
/-! ### add -/
theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := rfl
@@ -717,10 +493,6 @@ protected theorem add_comm (x y : BitVec n) : x + y = y + x := by
@[simp] protected theorem zero_add (x : BitVec n) : 0#n + x = x := by simp [add_def]
theorem truncate_add (x y : BitVec w) (h : i w) :
(x + y).truncate i = x.truncate i + y.truncate i := by
have dvd : 2^i 2^w := Nat.pow_dvd_pow _ h
simp [bv_toNat, h, Nat.mod_mod_of_dvd _ dvd]
/-! ### sub/neg -/
@@ -827,18 +599,4 @@ protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x
simp
exact Nat.lt_of_le_of_ne
/- ! ### intMax -/
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) : BitVec w := (2^w - 1)#w
theorem getLsb_intMax_eq (w : Nat) : (intMax w).getLsb i = decide (i < w) := by
simp [intMax, getLsb]
theorem toNat_intMax_eq : (intMax w).toNat = 2^w - 1 := by
have h : 2^w - 1 < 2^w := by
have pos : 2^w > 0 := Nat.pow_pos (by decide)
omega
simp [intMax, Nat.shiftLeft_eq, Nat.one_mul, natCast_eq_ofNat, toNat_ofNat, Nat.mod_eq_of_lt h]
end BitVec

View File

@@ -29,8 +29,6 @@ instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∃ x, p x) :
| _, isTrue hf => isTrue _, hf
| isFalse ht, isFalse hf => isFalse fun | true, h => absurd h ht | false, h => absurd h hf
@[simp] theorem default_bool : default = false := rfl
instance : LE Bool := (. .)
instance : LT Bool := (!. && .)
@@ -50,205 +48,85 @@ theorem ne_false_iff : {b : Bool} → b ≠ false ↔ b = true := by decide
theorem eq_iff_iff {a b : Bool} : a = b (a b) := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} [Decidable (b = true)] : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} [Decidable (b = false)] : decide (b = false) = !b := by cases b <;> simp
@[simp] theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} : decide (b = false) = !b := by cases b <;> simp
@[simp] theorem decide_true_eq {b : Bool} : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} : decide (false = b) = !b := by cases b <;> simp
/-! ### and -/
@[simp] theorem and_self_left : (a b : Bool), (a && (a && b)) = (a && b) := by decide
@[simp] theorem and_self_right : (a b : Bool), ((a && b) && b) = (a && b) := by decide
@[simp] theorem not_and_self : (x : Bool), (!x && x) = false := by decide
@[simp] theorem and_not_self : (x : Bool), (x && !x) = false := by decide
/-
Added for confluence with `not_and_self` `and_not_self` on term
`(b && !b) = true` due to reductions:
1. `(b = true !b = true)` via `Bool.and_eq_true`
2. `false = true` via `Bool.and_not_self`
-/
@[simp] theorem eq_true_and_eq_false_self : (b : Bool), (b = true b = false) False := by decide
@[simp] theorem eq_false_and_eq_true_self : (b : Bool), (b = false b = true) False := by decide
theorem and_comm : (x y : Bool), (x && y) = (y && x) := by decide
theorem and_left_comm : (x y z : Bool), (x && (y && z)) = (y && (x && z)) := by decide
theorem and_right_comm : (x y z : Bool), ((x && y) && z) = ((x && z) && y) := by decide
/-
Bool version `and_iff_left_iff_imp`.
theorem and_or_distrib_left : (x y z : Bool), (x && (y || z)) = ((x && y) || (x && z)) := by
decide
Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` via
`Bool.coe_iff_coe` and `a → b` via `Bool.and_eq_true` and
`and_iff_left_iff_imp`.
-/
@[simp] theorem and_iff_left_iff_imp : (a b : Bool), ((a && b) = a) (a b) := by decide
@[simp] theorem and_iff_right_iff_imp : (a b : Bool), ((a && b) = b) (b a) := by decide
@[simp] theorem iff_self_and : (a b : Bool), (a = (a && b)) (a b) := by decide
@[simp] theorem iff_and_self : (a b : Bool), (b = (a && b)) (b a) := by decide
theorem and_or_distrib_right : (x y z : Bool), ((x || y) && z) = ((x && z) || (y && z)) := by
decide
theorem and_xor_distrib_left : (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
theorem and_xor_distrib_right : (x y z : Bool), (xor x y && z) = xor (x && z) (y && z) := by
decide
/-- De Morgan's law for boolean and -/
theorem not_and : (x y : Bool), (!(x && y)) = (!x || !y) := by decide
theorem and_eq_true_iff : (x y : Bool), (x && y) = true x = true y = true := by decide
theorem and_eq_false_iff : (x y : Bool), (x && y) = false x = false y = false := by decide
/-! ### or -/
@[simp] theorem or_self_left : (a b : Bool), (a || (a || b)) = (a || b) := by decide
@[simp] theorem or_self_right : (a b : Bool), ((a || b) || b) = (a || b) := by decide
@[simp] theorem not_or_self : (x : Bool), (!x || x) = true := by decide
@[simp] theorem or_not_self : (x : Bool), (x || !x) = true := by decide
/-
Added for confluence with `not_or_self` `or_not_self` on term
`(b || !b) = true` due to reductions:
1. `(b = true !b = true)` via `Bool.or_eq_true`
2. `true = true` via `Bool.or_not_self`
-/
@[simp] theorem eq_true_or_eq_false_self : (b : Bool), (b = true b = false) True := by decide
@[simp] theorem eq_false_or_eq_true_self : (b : Bool), (b = false b = true) True := by decide
/-
Bool version `or_iff_left_iff_imp`.
Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` via
`Bool.coe_iff_coe` and `a → b` via `Bool.or_eq_true` and
`and_iff_left_iff_imp`.
-/
@[simp] theorem or_iff_left_iff_imp : (a b : Bool), ((a || b) = a) (b a) := by decide
@[simp] theorem or_iff_right_iff_imp : (a b : Bool), ((a || b) = b) (a b) := by decide
@[simp] theorem iff_self_or : (a b : Bool), (a = (a || b)) (b a) := by decide
@[simp] theorem iff_or_self : (a b : Bool), (b = (a || b)) (a b) := by decide
theorem or_comm : (x y : Bool), (x || y) = (y || x) := by decide
theorem or_left_comm : (x y z : Bool), (x || (y || z)) = (y || (x || z)) := by decide
theorem or_right_comm : (x y z : Bool), ((x || y) || z) = ((x || z) || y) := by decide
/-! ### distributivity -/
theorem or_and_distrib_left : (x y z : Bool), (x || (y && z)) = ((x || y) && (x || z)) := by
decide
theorem and_or_distrib_left : (x y z : Bool), (x && (y || z)) = (x && y || x && z) := by decide
theorem and_or_distrib_right : (x y z : Bool), ((x || y) && z) = (x && z || y && z) := by decide
theorem or_and_distrib_left : (x y z : Bool), (x || y && z) = ((x || y) && (x || z)) := by decide
theorem or_and_distrib_right : (x y z : Bool), (x && y || z) = ((x || z) && (y || z)) := by decide
theorem and_xor_distrib_left : (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
theorem and_xor_distrib_right : (x y z : Bool), (xor x y && z) = xor (x && z) (y && z) := by decide
/-- De Morgan's law for boolean and -/
@[simp] theorem not_and : (x y : Bool), (!(x && y)) = (!x || !y) := by decide
theorem or_and_distrib_right : (x y z : Bool), ((x && y) || z) = ((x || z) && (y || z)) := by
decide
/-- De Morgan's law for boolean or -/
@[simp] theorem not_or : (x y : Bool), (!(x || y)) = (!x && !y) := by decide
theorem not_or : (x y : Bool), (!(x || y)) = (!x && !y) := by decide
theorem and_eq_true_iff (x y : Bool) : (x && y) = true x = true y = true :=
Iff.of_eq (and_eq_true x y)
theorem or_eq_true_iff : (x y : Bool), (x || y) = true x = true y = true := by decide
theorem and_eq_false_iff : (x y : Bool), (x && y) = false x = false y = false := by decide
/-
New simp rule that replaces `Bool.and_eq_false_eq_eq_false_or_eq_false` in
Mathlib due to confluence:
Consider the term: `¬((b && c) = true)`:
1. Reduces to `((b && c) = false)` via `Bool.not_eq_true`
2. Reduces to `¬(b = true ∧ c = true)` via `Bool.and_eq_true`.
1. Further reduces to `b = false c = false` via `Bool.and_eq_false_eq_eq_false_or_eq_false`.
2. Further reduces to `b = true → c = false` via `not_and` and `Bool.not_eq_true`.
-/
@[simp] theorem and_eq_false_imp : (x y : Bool), (x && y) = false (x = true y = false) := by decide
@[simp] theorem or_eq_true_iff : (x y : Bool), (x || y) = true x = true y = true := by decide
@[simp] theorem or_eq_false_iff : (x y : Bool), (x || y) = false x = false y = false := by decide
/-! ### eq/beq/bne -/
/--
These two rules follow trivially by simp, but are needed to avoid non-termination
in false_eq and true_eq.
-/
@[simp] theorem false_eq_true : (false = true) = False := by simp
@[simp] theorem true_eq_false : (true = false) = False := by simp
-- The two lemmas below normalize terms with a constant to the
-- right-hand side but risk non-termination if `false_eq_true` and
-- `true_eq_false` are disabled.
@[simp low] theorem false_eq (b : Bool) : (false = b) = (b = false) := by
cases b <;> simp
@[simp low] theorem true_eq (b : Bool) : (true = b) = (b = true) := by
cases b <;> simp
@[simp] theorem true_beq : b, (true == b) = b := by decide
@[simp] theorem false_beq : b, (false == b) = !b := by decide
@[simp] theorem beq_true : b, (b == true) = b := by decide
@[simp] theorem beq_false : b, (b == false) = !b := by decide
@[simp] theorem true_bne : (b : Bool), (true != b) = !b := by decide
@[simp] theorem false_bne : (b : Bool), (false != b) = b := by decide
@[simp] theorem bne_true : (b : Bool), (b != true) = !b := by decide
@[simp] theorem bne_false : (b : Bool), (b != false) = b := by decide
@[simp] theorem not_beq_self : (x : Bool), ((!x) == x) = false := by decide
@[simp] theorem beq_not_self : (x : Bool), (x == !x) = false := by decide
@[simp] theorem not_bne_self : (x : Bool), ((!x) != x) = true := by decide
@[simp] theorem bne_not_self : (x : Bool), (x != !x) = true := by decide
/-
Added for equivalence with `Bool.not_beq_self` and needed for confluence
due to `beq_iff_eq`.
-/
@[simp] theorem not_eq_self : (b : Bool), ((!b) = b) False := by decide
@[simp] theorem eq_not_self : (b : Bool), (b = (!b)) False := by decide
@[simp] theorem beq_self_left : (a b : Bool), (a == (a == b)) = b := by decide
@[simp] theorem beq_self_right : (a b : Bool), ((a == b) == b) = a := by decide
@[simp] theorem bne_self_left : (a b : Bool), (a != (a != b)) = b := by decide
@[simp] theorem bne_self_right : (a b : Bool), ((a != b) != b) = a := by decide
@[simp] theorem not_bne_not : (x y : Bool), ((!x) != (!y)) = (x != y) := by decide
@[simp] theorem bne_assoc : (x y z : Bool), ((x != y) != z) = (x != (y != z)) := by decide
@[simp] theorem bne_left_inj : (x y z : Bool), (x != y) = (x != z) y = z := by decide
@[simp] theorem bne_right_inj : (x y z : Bool), (x != z) = (y != z) x = y := by decide
/-! ### coercision related normal forms -/
@[simp] theorem not_eq_not : {a b : Bool}, ¬a = !b a = b := by decide
@[simp] theorem not_not_eq : {a b : Bool}, ¬(!a) = b a = b := by decide
@[simp] theorem coe_iff_coe : (a b : Bool), (a b) a = b := by decide
@[simp] theorem coe_true_iff_false : (a b : Bool), (a b = false) a = (!b) := by decide
@[simp] theorem coe_false_iff_true : (a b : Bool), (a = false b) (!a) = b := by decide
@[simp] theorem coe_false_iff_false : (a b : Bool), (a = false b = false) (!a) = (!b) := by decide
theorem or_eq_false_iff : (x y : Bool), (x || y) = false x = false y = false := by decide
/-! ### xor -/
theorem false_xor : (x : Bool), xor false x = x := false_bne
@[simp] theorem false_xor : (x : Bool), xor false x = x := by decide
theorem xor_false : (x : Bool), xor x false = x := bne_false
@[simp] theorem xor_false : (x : Bool), xor x false = x := by decide
theorem true_xor : (x : Bool), xor true x = !x := true_bne
@[simp] theorem true_xor : (x : Bool), xor true x = !x := by decide
theorem xor_true : (x : Bool), xor x true = !x := bne_true
@[simp] theorem xor_true : (x : Bool), xor x true = !x := by decide
theorem not_xor_self : (x : Bool), xor (!x) x = true := not_bne_self
@[simp] theorem not_xor_self : (x : Bool), xor (!x) x = true := by decide
theorem xor_not_self : (x : Bool), xor x (!x) = true := bne_not_self
@[simp] theorem xor_not_self : (x : Bool), xor x (!x) = true := by decide
theorem not_xor : (x y : Bool), xor (!x) y = !(xor x y) := by decide
theorem xor_not : (x y : Bool), xor x (!y) = !(xor x y) := by decide
theorem not_xor_not : (x y : Bool), xor (!x) (!y) = (xor x y) := not_bne_not
@[simp] theorem not_xor_not : (x y : Bool), xor (!x) (!y) = (xor x y) := by decide
theorem xor_self : (x : Bool), xor x x = false := by decide
@@ -258,11 +136,13 @@ theorem xor_left_comm : ∀ (x y z : Bool), xor x (xor y z) = xor y (xor x z) :=
theorem xor_right_comm : (x y z : Bool), xor (xor x y) z = xor (xor x z) y := by decide
theorem xor_assoc : (x y z : Bool), xor (xor x y) z = xor x (xor y z) := bne_assoc
theorem xor_assoc : (x y z : Bool), xor (xor x y) z = xor x (xor y z) := by decide
theorem xor_left_inj : (x y z : Bool), xor x y = xor x z y = z := bne_left_inj
@[simp]
theorem xor_left_inj : (x y z : Bool), xor x y = xor x z y = z := by decide
theorem xor_right_inj : (x y z : Bool), xor x z = xor y z x = y := bne_right_inj
@[simp]
theorem xor_right_inj : (x y z : Bool), xor x z = xor y z x = y := by decide
/-! ### le/lt -/
@@ -347,146 +227,15 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
@[simp] theorem toNat_eq_zero (b : Bool) : b.toNat = 0 b = false := by
cases b <;> simp
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 b = true := by
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 b = true := by
cases b <;> simp
/-! ### ite -/
@[simp] theorem if_true_left (p : Prop) [h : Decidable p] (f : Bool) :
(ite p true f) = (p || f) := by cases h with | _ p => simp [p]
@[simp] theorem if_false_left (p : Prop) [h : Decidable p] (f : Bool) :
(ite p false f) = (!p && f) := by cases h with | _ p => simp [p]
@[simp] theorem if_true_right (p : Prop) [h : Decidable p] (t : Bool) :
(ite p t true) = (!(p : Bool) || t) := by cases h with | _ p => simp [p]
@[simp] theorem if_false_right (p : Prop) [h : Decidable p] (t : Bool) :
(ite p t false) = (p && t) := by cases h with | _ p => simp [p]
@[simp] theorem ite_eq_true_distrib (p : Prop) [h : Decidable p] (t f : Bool) :
(ite p t f = true) = ite p (t = true) (f = true) := by
cases h with | _ p => simp [p]
@[simp] theorem ite_eq_false_distrib (p : Prop) [h : Decidable p] (t f : Bool) :
(ite p t f = false) = ite p (t = false) (f = false) := by
cases h with | _ p => simp [p]
/-
`not_ite_eq_true_eq_true` and related theorems below are added for
non-confluence. A motivating example is
`¬((if u then b else c) = true)`.
This reduces to:
1. `¬((if u then (b = true) else (c = true))` via `ite_eq_true_distrib`
2. `(if u then b c) = false)` via `Bool.not_eq_true`.
Similar logic holds for `¬((if u then b else c) = false)` and related
lemmas.
-/
@[simp]
theorem not_ite_eq_true_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = true) (c = true)) (ite p (b = false) (c = false)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_false_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = false) (c = false)) (ite p (b = true) (c = true)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_true_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = true) (c = false)) (ite p (b = false) (c = true)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = false) (c = true)) (ite p (b = true) (c = false)) := by
cases h with | _ p => simp [p]
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = true then True else b = true`
-/
@[simp] theorem eq_false_imp_eq_true : (b:Bool), (b = false b = true) (b = true) := by decide
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = false then True else b = false`
-/
@[simp] theorem eq_true_imp_eq_false : (b:Bool), (b = true b = false) (b = false) := by decide
/-! ### cond -/
theorem cond_eq_ite {α} (b : Bool) (t e : α) : cond b t e = if b then t else e := by
cases b <;> simp
theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := cond_eq_ite b x y
@[simp] theorem cond_not (b : Bool) (t e : α) : cond (!b) t e = cond b e t := by
cases b <;> rfl
@[simp] theorem cond_self (c : Bool) (t : α) : cond c t t = t := by cases c <;> rfl
/-
This is a simp rule in Mathlib, but results in non-confluence that is
difficult to fix as decide distributes over propositions.
A possible fix would be to completely simplify away `cond`, but that
is not taken since it could result in major rewriting of code that is
otherwise purely about `Bool`.
-/
theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
cond (decide p) t e = if p then t else e := by
simp [cond_eq_ite]
@[simp] theorem cond_eq_ite_iff (a : Bool) (p : Prop) [h : Decidable p] (x y u v : α) :
(cond a x y = ite p u v) ite a x y = ite p u v := by
simp [Bool.cond_eq_ite]
@[simp] theorem ite_eq_cond_iff (p : Prop) [h : Decidable p] (a : Bool) (x y u v : α) :
(ite p x y = cond a u v) ite p x y = ite a u v := by
simp [Bool.cond_eq_ite]
@[simp] theorem cond_eq_true_distrib : (c t f : Bool),
(cond c t f = true) = ite (c = true) (t = true) (f = true) := by
decide
@[simp] theorem cond_eq_false_distrib : (c t f : Bool),
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
@[simp] theorem cond_true_left : (c f : Bool), cond c true f = ( c || f) := by decide
@[simp] theorem cond_false_left : (c f : Bool), cond c false f = (!c && f) := by decide
@[simp] theorem cond_true_right : (c t : Bool), cond c t true = (!c || t) := by decide
@[simp] theorem cond_false_right : (c t : Bool), cond c t false = ( c && t) := by decide
@[simp] theorem cond_true_same : (c b : Bool), cond c c b = (c || b) := by decide
@[simp] theorem cond_false_same : (c b : Bool), cond c b c = (c && b) := by decide
/-# decidability -/
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true
@[simp] theorem decide_and (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
decide (p q) = (p && q) := by
cases dp with | _ p => simp [p]
@[simp] theorem decide_or (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
decide (p q) = (p || q) := by
cases dp with | _ p => simp [p]
@[simp] theorem decide_iff_dist (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
decide (p q) = (decide p == decide q) := by
cases dp with | _ p => simp [p]
end Bool
export Bool (cond_eq_if)
/-! ### cond -/
theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := by
cases b <;> simp
/-! ### decide -/

View File

@@ -687,7 +687,7 @@ decreasing_by decreasing_with
@[simp] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ} :
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
rw [reverseInduction]; simp
rw [reverseInduction]; simp; rfl
@[simp] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ}
(i : Fin n) : reverseInduction (motive := motive) zero succ (castSucc i) =

View File

@@ -158,44 +158,4 @@ instance : Div Int where
instance : Mod Int where
mod := Int.emod
/-!
# `bmod` ("balanced" mod)
Balanced mod (and balanced div) are a division and modulus pair such
that `b * (Int.bdiv a b) + Int.bmod a b = a` and `b/2 ≤ Int.bmod a b <
b/2` for all `a : Int` and `b > 0`.
This is used in Omega as well as signed bitvectors.
-/
/--
Balanced modulus. This version of Integer modulus uses the
balanced rounding convention, which guarantees that
`m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
to `x` modulo `m`.
If `m = 0`, then `bmod x m = x`.
-/
def bmod (x : Int) (m : Nat) : Int :=
let r := x % m
if r < (m + 1) / 2 then
r
else
r - m
/--
Balanced division. This returns the unique integer so that
`b * (Int.bdiv a b) + Int.bmod a b = a`.
-/
def bdiv (x : Int) (m : Nat) : Int :=
if m = 0 then
0
else
let q := x / m
let r := x % m
if r < (m + 1) / 2 then
q
else
q + 1
end Int

View File

@@ -325,78 +325,23 @@ theorem sub_ediv_of_dvd (a : Int) {b c : Int}
rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg, Int.add_ediv_of_dvd_right (Int.dvd_neg.2 hcb)]
congr; exact Int.neg_ediv_of_dvd hcb
@[simp] theorem ediv_one : a : Int, a / 1 = a
| (_:Nat) => congrArg Nat.cast (Nat.div_one _)
| -[_+1] => congrArg negSucc (Nat.div_one _)
/-!
# `bmod` ("balanced" mod)
@[simp] theorem emod_one (a : Int) : a % 1 = 0 := by
simp [emod_def, Int.one_mul, Int.sub_self]
We use balanced mod in the omega algorithm,
to make ±1 coefficients appear in equations without them.
-/
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp]
theorem Int.emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
if h : y = 0 then
simp [h]
/--
Balanced mod, taking values in the range [- m/2, (m - 1)/2].
-/
def bmod (x : Int) (m : Nat) : Int :=
let r := x % m
if r < (m + 1) / 2 then
r
else
simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
/-! bmod -/
r - m
@[simp] theorem bmod_emod : bmod x m % m = x % m := by
dsimp [bmod]
split <;> simp [Int.sub_emod]
@[simp]
theorem emod_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n) n = Int.bmod x n := by
simp [bmod, Int.emod_emod]
theorem bmod_def (x : Int) (m : Nat) : bmod x m =
if (x % m) < (m + 1) / 2 then
x % m
else
(x % m) - m :=
rfl
theorem bmod_pos (x : Int) (m : Nat) (p : x % m < (m + 1) / 2) : bmod x m = x % m := by
simp [bmod_def, p]
theorem bmod_neg (x : Int) (m : Nat) (p : x % m (m + 1) / 2) : bmod x m = (x % m) - m := by
simp [bmod_def, Int.not_lt.mpr p]
@[simp]
theorem bmod_one_is_zero (x : Int) : Int.bmod x 1 = 0 := by
simp [Int.bmod]
@[simp]
theorem bmod_add_cancel (x : Int) (n : Nat) : Int.bmod (x + n) n = Int.bmod x n := by
simp [bmod_def]
@[simp]
theorem bmod_add_mul_cancel (x : Int) (n : Nat) (k : Int) : Int.bmod (x + n * k) n = Int.bmod x n := by
simp [bmod_def]
@[simp]
theorem bmod_sub_cancel (x : Int) (n : Nat) : Int.bmod (x - n) n = Int.bmod x n := by
simp [bmod_def]
@[simp]
theorem emod_add_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n + y) n = Int.bmod (x + y) n := by
simp [Int.emod_def, Int.sub_eq_add_neg]
rw [Int.mul_neg, Int.add_right_comm, Int.bmod_add_mul_cancel]
@[simp]
theorem bmod_add_bmod_congr : Int.bmod (Int.bmod x n + y) n = Int.bmod (x + y) n := by
rw [bmod_def x n]
split
case inl p =>
simp
case inr p =>
rw [Int.sub_eq_add_neg, Int.add_right_comm, Int.sub_eq_add_neg]
simp
@[simp]
theorem add_bmod_bmod : Int.bmod (x + Int.bmod y n) n = Int.bmod (x + y) n := by
rw [Int.add_comm x, Int.bmod_add_bmod_congr, Int.add_comm y]

View File

@@ -321,27 +321,6 @@ theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
· exact (Nat.add_sub_cancel_left ..).symm
· dsimp; rw [Nat.add_assoc, Nat.sub_eq_zero_of_le (Nat.le_add_right ..)]; rfl
/- ## add/sub injectivity -/
@[simp]
protected theorem add_right_inj (i j k : Int) : (i + k = j + k) i = j := by
apply Iff.intro
· intro p
rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p]
· exact congrArg (· + k)
@[simp]
protected theorem add_left_inj (i j k : Int) : (k + i = k + j) i = j := by
simp [Int.add_comm k]
@[simp]
protected theorem sub_left_inj (i j k : Int) : (k - i = k - j) i = j := by
simp [Int.sub_eq_add_neg, Int.neg_inj]
@[simp]
protected theorem sub_right_inj (i j k : Int) : (i - k = j - k) i = j := by
simp [Int.sub_eq_add_neg]
/- ## Ring properties -/
@[simp] theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -(m * succ n) := rfl
@@ -499,33 +478,10 @@ theorem eq_one_of_mul_eq_self_left {a b : Int} (Hpos : a ≠ 0) (H : b * a = a)
theorem eq_one_of_mul_eq_self_right {a b : Int} (Hpos : b 0) (H : b * a = b) : a = 1 :=
Int.eq_of_mul_eq_mul_left Hpos <| by rw [Int.mul_one, H]
/-! # pow -/
protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
rw [Int.mul_comm, Int.pow_succ]
theorem pow_le_pow_of_le_left {n m : Nat} (h : n m) : (i : Nat), n^i m^i
| 0 => Nat.le_refl _
| succ i => Nat.mul_le_mul (pow_le_pow_of_le_left h i) h
theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : {j}, i j n^i n^j
| 0, h =>
have : i = 0 := eq_zero_of_le_zero h
this.symm Nat.le_refl _
| succ j, h =>
match le_or_eq_of_le_succ h with
| Or.inl h => show n^i n^j * n from
have : n^i * 1 n^j * n := Nat.mul_le_mul (pow_le_pow_of_le_right hx h) hx
Nat.mul_one (n^i) this
| Or.inr h =>
h.symm Nat.le_refl _
theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
pow_le_pow_of_le_right h (Nat.zero_le _)
/-! NatCast lemmas -/
/-!
@@ -545,10 +501,4 @@ theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
@[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
simp
theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
match n with
| 0 => rfl
| n + 1 =>
simp only [Nat.pow_succ, Int.pow_succ, natCast_mul, natCast_pow _ n]
end Int

View File

@@ -192,11 +192,6 @@ protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def
protected theorem min_le_left (a b : Int) : min a b a := Int.min_comm .. Int.min_le_right ..
protected theorem min_eq_left {a b : Int} (h : a b) : min a b = a := by simp [Int.min_def, h]
protected theorem min_eq_right {a b : Int} (h : b a) : min a b = b := by
rw [Int.min_comm a b]; exact Int.min_eq_left h
protected theorem le_min {a b c : Int} : a min b c a b a c :=
fun h => Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..),
fun h₁, h₂ => by rw [Int.min_def]; split <;> assumption
@@ -215,12 +210,6 @@ protected theorem max_le {a b c : Int} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :
fun h => Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h,
fun h₁, h₂ => by rw [Int.max_def]; split <;> assumption
protected theorem max_eq_right {a b : Int} (h : a b) : max a b = b := by
simp [Int.max_def, h, Int.not_lt.2 h]
protected theorem max_eq_left {a b : Int} (h : b a) : max a b = a := by
rw [ Int.max_comm b a]; exact Int.max_eq_right h
theorem eq_natAbs_of_zero_le {a : Int} (h : 0 a) : a = natAbs a := by
let n, e := eq_ofNat_of_zero_le h
rw [e]; rfl
@@ -447,54 +436,3 @@ theorem natAbs_of_nonneg {a : Int} (H : 0 ≤ a) : (natAbs a : Int) = a :=
theorem ofNat_natAbs_of_nonpos {a : Int} (H : a 0) : (natAbs a : Int) = -a := by
rw [ natAbs_neg, natAbs_of_nonneg (Int.neg_nonneg_of_nonpos H)]
/-! ### toNat -/
theorem toNat_eq_max : a : Int, (toNat a : Int) = max a 0
| (n : Nat) => (Int.max_eq_left (ofNat_zero_le n)).symm
| -[n+1] => (Int.max_eq_right (Int.le_of_lt (negSucc_lt_zero n))).symm
@[simp] theorem toNat_zero : (0 : Int).toNat = 0 := rfl
@[simp] theorem toNat_one : (1 : Int).toNat = 1 := rfl
@[simp] theorem toNat_of_nonneg {a : Int} (h : 0 a) : (toNat a : Int) = a := by
rw [toNat_eq_max, Int.max_eq_left h]
@[simp] theorem toNat_ofNat (n : Nat) : toNat n = n := rfl
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
theorem self_le_toNat (a : Int) : a toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
@[simp] theorem le_toNat {n : Nat} {z : Int} (h : 0 z) : n z.toNat (n : Int) z := by
rw [ Int.ofNat_le, Int.toNat_of_nonneg h]
@[simp] theorem toNat_lt {n : Nat} {z : Int} (h : 0 z) : z.toNat < n z < (n : Int) := by
rw [ Int.not_le, Nat.not_le, Int.le_toNat h]
theorem toNat_add {a b : Int} (ha : 0 a) (hb : 0 b) : (a + b).toNat = a.toNat + b.toNat :=
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
| _, _, _, rfl, _, rfl => rfl
theorem toNat_add_nat {a : Int} (ha : 0 a) (n : Nat) : (a + n).toNat = a.toNat + n :=
match a, eq_ofNat_of_zero_le ha with | _, _, rfl => rfl
@[simp] theorem pred_toNat : i : Int, (i - 1).toNat = i.toNat - 1
| 0 => rfl
| (n+1:Nat) => by simp [ofNat_add]
| -[n+1] => rfl
@[simp] theorem toNat_sub_toNat_neg : n : Int, n.toNat - (-n).toNat = n
| 0 => rfl
| (_+1:Nat) => Int.sub_zero _
| -[_+1] => Int.zero_sub _
@[simp] theorem toNat_add_toNat_neg_eq_natAbs : n : Int, n.toNat + (-n).toNat = n.natAbs
| 0 => rfl
| (_+1:Nat) => Nat.add_zero _
| -[_+1] => Nat.zero_add _
@[simp] theorem toNat_neg_nat : n : Nat, (-(n : Int)).toNat = 0
| 0 => rfl
| _+1 => rfl

View File

@@ -727,9 +727,9 @@ inductive lt [LT α] : List α → List α → Prop where
instance [LT α] : LT (List α) := List.lt
instance hasDecidableLt [LT α] [h : DecidableRel (α:=α) (·<·)] : (l₁ l₂ : List α) Decidable (l₁ < l₂)
| [], [] => isFalse nofun
| [], [] => isFalse (fun h => nomatch h)
| [], _::_ => isTrue (List.lt.nil _ _)
| _::_, [] => isFalse nofun
| _::_, [] => isFalse (fun h => nomatch h)
| a::as, b::bs =>
match h a b with
| isTrue h₁ => isTrue (List.lt.head _ _ h₁)

View File

@@ -227,23 +227,4 @@ where
else
go xs acc₁ (acc₂.push x)
/--
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
whilst partitioning the result it into a pair of lists, `List β × List γ`,
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
```
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
```
-/
@[inline] def partitionMap (f : α β γ) (l : List α) : List β × List γ := go l #[] #[] where
/-- Auxiliary for `partitionMap`:
`partitionMap.go f l acc₁ acc₂ = (acc₁.toList ++ left, acc₂.toList ++ right)`
if `partitionMap f l = (left, right)`. -/
@[specialize] go : List α Array β Array γ List β × List γ
| [], acc₁, acc₂ => (acc₁.toList, acc₂.toList)
| x :: xs, acc₁, acc₂ =>
match f x with
| .inl a => go xs (acc₁.push a) acc₂
| .inr b => go xs acc₁ (acc₂.push b)
end List

View File

@@ -8,7 +8,7 @@ import Init.Data.List.BasicAux
import Init.Data.List.Control
import Init.Data.Nat.Lemmas
import Init.PropLemmas
import Init.Control.Lawful.Basic
import Init.Control.Lawful
import Init.Hints
namespace List
@@ -69,7 +69,7 @@ theorem mem_cons_self (a : α) (l : List α) : a ∈ a :: l := .head ..
theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a l a y :: l := .tail _
theorem eq_nil_iff_forall_not_mem {l : List α} : l = [] a, a l := by
cases l <;> simp [-not_or]
cases l <;> simp
/-! ### append -/
@@ -451,9 +451,9 @@ theorem mem_filter : x ∈ filter p as ↔ x ∈ as ∧ p x := by
induction as with
| nil => simp [filter]
| cons a as ih =>
by_cases h : p a
· simp_all [or_and_left]
· simp_all [or_and_right]
by_cases h : p a <;> simp [*, or_and_right]
· exact or_congr_left (and_iff_left_of_imp fun | rfl => h).symm
· exact (or_iff_right fun rfl, h' => h h').symm
theorem filter_eq_nil {l} : filter p l = [] a, a l ¬p a := by
simp only [eq_nil_iff_forall_not_mem, mem_filter, not_and]
@@ -665,44 +665,3 @@ theorem minimum?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·
exact congrArg some <| anti.1
((le_minimum?_iff le_min_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
@[simp] theorem get_cons_succ {as : List α} {h : i + 1 < (a :: as).length} :
(a :: as).get i+1, h = as.get i, Nat.lt_of_succ_lt_succ h := rfl
@[simp] theorem get_cons_succ' {as : List α} {i : Fin as.length} :
(a :: as).get i.succ = as.get i := rfl
@[simp] theorem set_nil (n : Nat) (a : α) : [].set n a = [] := rfl
@[simp] theorem set_zero (x : α) (xs : List α) (a : α) :
(x :: xs).set 0 a = a :: xs := rfl
@[simp] theorem set_succ (x : α) (xs : List α) (n : Nat) (a : α) :
(x :: xs).set n.succ a = x :: xs.set n a := rfl
@[simp] theorem get_set_eq (l : List α) (i : Nat) (a : α) (h : i < (l.set i a).length) :
(l.set i a).get i, h = a :=
match l, i with
| [], _ => by
simp at h
contradiction
| _ :: _, 0 => by
simp
| _ :: l, i + 1 => by
simp [get_set_eq l]
@[simp] theorem get_set_ne (l : List α) {i j : Nat} (h : i j) (a : α)
(hj : j < (l.set i a).length) :
(l.set i a).get j, hj = l.get j, by simp at hj; exact hj :=
match l, i, j with
| [], _, _ => by
simp
| _ :: _, 0, 0 => by
contradiction
| _ :: _, 0, _ + 1 => by
simp
| _ :: _, _ + 1, 0 => by
simp
| _ :: l, i + 1, j + 1 => by
have g : i j := h congrArg (· + 1)
simp [get_set_ne l g]

View File

@@ -16,4 +16,3 @@ import Init.Data.Nat.Power2
import Init.Data.Nat.Linear
import Init.Data.Nat.SOM
import Init.Data.Nat.Lemmas
import Init.Data.Nat.Mod

View File

@@ -189,7 +189,7 @@ protected theorem mul_comm : ∀ (n m : Nat), n * m = m * n
Nat.mul_comm n 1 Nat.mul_one n
protected theorem left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := by
induction n with
induction n generalizing m k with
| zero => repeat rw [Nat.zero_mul]
| succ n ih => simp [succ_mul, ih]; rw [Nat.add_assoc, Nat.add_assoc (n*m)]; apply congrArg; apply Nat.add_left_comm
@@ -451,72 +451,6 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
protected theorem add_le_add_iff_right {n : Nat} : m + n k + n m k :=
Nat.le_of_add_le_add_right, fun h => Nat.add_le_add_right h _
/-! ### le/lt -/
protected theorem lt_asymm {a b : Nat} (h : a < b) : ¬ b < a := Nat.not_lt.2 (Nat.le_of_lt h)
/-- Alias for `Nat.lt_asymm`. -/
protected abbrev not_lt_of_gt := @Nat.lt_asymm
/-- Alias for `Nat.lt_asymm`. -/
protected abbrev not_lt_of_lt := @Nat.lt_asymm
protected theorem lt_iff_le_not_le {m n : Nat} : m < n m n ¬ n m :=
fun h => Nat.le_of_lt h, Nat.not_le_of_gt h, fun _, h => Nat.lt_of_not_ge h
/-- Alias for `Nat.lt_iff_le_not_le`. -/
protected abbrev lt_iff_le_and_not_ge := @Nat.lt_iff_le_not_le
protected theorem lt_iff_le_and_ne {m n : Nat} : m < n m n m n :=
fun h => Nat.le_of_lt h, Nat.ne_of_lt h, fun h => Nat.lt_of_le_of_ne h.1 h.2
protected theorem ne_iff_lt_or_gt {a b : Nat} : a b a < b b < a :=
Nat.lt_or_gt_of_ne, fun | .inl h => Nat.ne_of_lt h | .inr h => Nat.ne_of_gt h
/-- Alias for `Nat.ne_iff_lt_or_gt`. -/
protected abbrev lt_or_gt := @Nat.ne_iff_lt_or_gt
/-- Alias for `Nat.le_total`. -/
protected abbrev le_or_ge := @Nat.le_total
/-- Alias for `Nat.le_total`. -/
protected abbrev le_or_le := @Nat.le_total
protected theorem eq_or_lt_of_not_lt {a b : Nat} (hnlt : ¬ a < b) : a = b b < a :=
(Nat.lt_trichotomy ..).resolve_left hnlt
protected theorem lt_or_eq_of_le {n m : Nat} (h : n m) : n < m n = m :=
(Nat.lt_or_ge ..).imp_right (Nat.le_antisymm h)
protected theorem le_iff_lt_or_eq {n m : Nat} : n m n < m n = m :=
Nat.lt_or_eq_of_le, fun | .inl h => Nat.le_of_lt h | .inr rfl => Nat.le_refl _
protected theorem lt_succ_iff : m < succ n m n := le_of_lt_succ, lt_succ_of_le
protected theorem lt_succ_iff_lt_or_eq : m < succ n m < n m = n :=
Nat.lt_succ_iff.trans Nat.le_iff_lt_or_eq
protected theorem eq_of_lt_succ_of_not_lt (hmn : m < n + 1) (h : ¬ m < n) : m = n :=
(Nat.lt_succ_iff_lt_or_eq.1 hmn).resolve_left h
protected theorem eq_of_le_of_lt_succ (h₁ : n m) (h₂ : m < n + 1) : m = n :=
Nat.le_antisymm (le_of_succ_le_succ h₂) h₁
/-! ## zero/one/two -/
theorem le_zero : i 0 i = 0 := Nat.eq_zero_of_le_zero, fun | rfl => Nat.le_refl _
/-- Alias for `Nat.zero_lt_one`. -/
protected abbrev one_pos := @Nat.zero_lt_one
protected theorem two_pos : 0 < 2 := Nat.zero_lt_succ _
protected theorem ne_zero_iff_zero_lt : n 0 0 < n := Nat.pos_iff_ne_zero.symm
protected theorem zero_lt_two : 0 < 2 := Nat.zero_lt_succ _
protected theorem one_lt_two : 1 < 2 := Nat.succ_lt_succ Nat.zero_lt_one
protected theorem eq_zero_of_not_pos (h : ¬0 < n) : n = 0 :=
Nat.eq_zero_of_le_zero (Nat.not_lt.1 h)
/-! # Basic theorems for comparing numerals -/
theorem ctor_eq_zero : Nat.zero = 0 :=
@@ -531,8 +465,6 @@ protected theorem zero_ne_one : 0 ≠ (1 : Nat) :=
theorem succ_ne_zero (n : Nat) : succ n 0 :=
fun h => Nat.noConfusion h
theorem add_one_ne_zero (n) : n + 1 0 := succ_ne_zero _
/-! # mul + order -/
theorem mul_le_mul_left {n m : Nat} (k : Nat) (h : n m) : k * n k * m :=
@@ -571,10 +503,10 @@ theorem eq_of_mul_eq_mul_right {n m k : Nat} (hm : 0 < m) (h : n * m = k * m) :
/-! # power -/
protected theorem pow_succ (n m : Nat) : n^(succ m) = n^m * n :=
theorem pow_succ (n m : Nat) : n^(succ m) = n^m * n :=
rfl
protected theorem pow_zero (n : Nat) : n^0 = 1 := rfl
theorem pow_zero (n : Nat) : n^0 = 1 := rfl
theorem pow_le_pow_of_le_left {n m : Nat} (h : n m) : (i : Nat), n^i m^i
| 0 => Nat.le_refl _

View File

@@ -51,26 +51,6 @@ instance : Xor Nat := ⟨Nat.xor⟩
instance : ShiftLeft Nat := Nat.shiftLeft
instance : ShiftRight Nat := Nat.shiftRight
theorem shiftLeft_eq (a b : Nat) : a <<< b = a * 2 ^ b :=
match b with
| 0 => (Nat.mul_one _).symm
| b+1 => (shiftLeft_eq _ b).trans <| by
simp [Nat.pow_succ, Nat.mul_assoc, Nat.mul_left_comm, Nat.mul_comm]
@[simp] theorem shiftRight_zero : n >>> 0 = n := rfl
theorem shiftRight_succ (m n) : m >>> (n + 1) = (m >>> n) / 2 := rfl
theorem shiftRight_add (m n : Nat) : k, m >>> (n + k) = (m >>> n) >>> k
| 0 => rfl
| k + 1 => by simp [add_succ, shiftRight_add, shiftRight_succ]
theorem shiftRight_eq_div_pow (m : Nat) : n, m >>> n = m / 2 ^ n
| 0 => (Nat.div_one _).symm
| k + 1 => by
rw [shiftRight_add, shiftRight_eq_div_pow m k]
simp [Nat.div_div_eq_div_mul, Nat.pow_succ, shiftRight_succ]
/-!
### testBit
We define an operation for testing individual bits in the binary representation

View File

@@ -23,13 +23,26 @@ namespace Nat
private theorem one_div_two : 1/2 = 0 := by trivial
private theorem two_pow_succ_sub_succ_div_two : (2 ^ (n+1) - (x + 1)) / 2 = 2^n - (x/2 + 1) := by
omega
if h : x + 1 2 ^ (n + 1) then
apply fun x => (Nat.sub_eq_of_eq_add x).symm
apply Eq.trans _
apply Nat.add_mul_div_left _ _ Nat.zero_lt_two
rw [ Nat.sub_add_comm h]
rw [Nat.add_sub_assoc (by omega)]
rw [Nat.pow_succ']
rw [Nat.mul_add_div Nat.zero_lt_two]
simp [show (2 * (x / 2 + 1) - (x + 1)) / 2 = 0 by omega]
else
rw [Nat.pow_succ'] at *
omega
private theorem two_pow_succ_sub_one_div_two : (2 ^ (n+1) - 1) / 2 = 2^n - 1 :=
two_pow_succ_sub_succ_div_two
private theorem two_mul_sub_one {n : Nat} (n_pos : n > 0) : (2*n - 1) % 2 = 1 := by
omega
match n with
| 0 => contradiction
| n + 1 => simp [Nat.mul_succ, Nat.mul_add_mod, mod_eq_of_lt]
/-! ### Preliminaries -/
@@ -86,11 +99,6 @@ theorem testBit_to_div_mod {x : Nat} : testBit x i = decide (x / 2^i % 2 = 1) :=
| succ i hyp =>
simp [hyp, Nat.div_div_eq_div_mul, Nat.pow_succ']
theorem toNat_testBit (x i : Nat) :
(x.testBit i).toNat = x / 2 ^ i % 2 := by
rw [Nat.testBit_to_div_mod]
rcases Nat.mod_two_eq_zero_or_one (x / 2^i) <;> simp_all
theorem ne_zero_implies_bit_true {x : Nat} (xnz : x 0) : i, testBit x i := by
induction x using div2Induction with
| ind x hyp =>
@@ -231,7 +239,7 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
rw [Nat.sub_eq_zero_iff_le] at i_sub_j_eq
exact Nat.not_le_of_gt j_lt_i i_sub_j_eq
| d+1 =>
simp [Nat.pow_succ, Nat.mul_comm _ 2, Nat.mul_add_mod]
simp [pow_succ, Nat.mul_comm _ 2, Nat.mul_add_mod]
@[simp] theorem testBit_mod_two_pow (x j i : Nat) :
testBit (x % 2^j) i = (decide (i < j) && testBit x i) := by
@@ -261,28 +269,31 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
theorem testBit_one_zero : testBit 1 0 = true := by trivial
theorem not_decide_mod_two_eq_one (x : Nat)
: (!decide (x % 2 = 1)) = decide (x % 2 = 0) := by
cases Nat.mod_two_eq_zero_or_one x <;> (rename_i p; simp [p])
theorem testBit_two_pow_sub_succ (h₂ : x < 2 ^ n) (i : Nat) :
testBit (2^n - (x + 1)) i = (decide (i < n) && ! testBit x i) := by
induction i generalizing n x with
| zero =>
simp only [testBit_zero, zero_eq, Bool.and_eq_true, decide_eq_true_eq,
Bool.not_eq_true']
match n with
| 0 => simp
| n+1 =>
simp [not_decide_mod_two_eq_one]
-- just logic + omega:
simp only [zero_lt_succ, decide_True, Bool.true_and]
rw [Nat.pow_succ', decide_not, decide_eq_decide]
rw [Nat.pow_succ'] at h₂
omega
| succ i ih =>
simp only [testBit_succ]
match n with
| 0 =>
simp [decide_eq_false]
simp only [pow_zero, succ_sub_succ_eq_sub, Nat.zero_sub, Nat.zero_div, zero_testBit]
rw [decide_eq_false] <;> simp
| n+1 =>
rw [Nat.two_pow_succ_sub_succ_div_two, ih]
· simp [Nat.succ_lt_succ_iff]
· omega
· rw [Nat.pow_succ'] at h₂
omega
@[simp] theorem testBit_two_pow_sub_one (n i : Nat) : testBit (2^n-1) i = decide (i < n) := by
rw [testBit_two_pow_sub_succ]
@@ -341,7 +352,7 @@ private theorem eq_0_of_lt (x : Nat) : x < 2^ 0 ↔ x = 0 := eq_0_of_lt_one x
private theorem zero_lt_pow (n : Nat) : 0 < 2^n := by
induction n
case zero => simp [eq_0_of_lt]
case succ n hyp => simpa [Nat.pow_succ]
case succ n hyp => simpa [pow_succ]
private theorem div_two_le_of_lt_two {m n : Nat} (p : m < 2 ^ succ n) : m / 2 < 2^n := by
simp [div_lt_iff_lt_mul Nat.zero_lt_two]
@@ -366,7 +377,7 @@ theorem bitwise_lt_two_pow (left : x < 2^n) (right : y < 2^n) : (Nat.bitwise f x
simp only [x_zero, y_zero, if_neg]
have hyp1 := hyp (div_two_le_of_lt_two left) (div_two_le_of_lt_two right)
by_cases p : f (decide (x % 2 = 1)) (decide (y % 2 = 1)) = true <;>
simp [p, Nat.pow_succ, mul_succ, Nat.add_assoc]
simp [p, pow_succ, mul_succ, Nat.add_assoc]
case pos =>
apply lt_of_succ_le
simp only [ Nat.succ_add]
@@ -436,8 +447,12 @@ theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat)
cases Nat.lt_or_ge j i with
| inl j_lt =>
simp only [j_lt]
have i_def : i = j + succ (pred (i-j)) := by
rw [succ_pred_eq_of_pos] <;> omega
have i_ge := Nat.le_of_lt j_lt
have i_sub_j_nez : i-j 0 := Nat.sub_ne_zero_of_lt j_lt
have i_def : i = j + succ (pred (i-j)) :=
calc i = j + (i-j) := (Nat.add_sub_cancel' i_ge).symm
_ = j + succ (pred (i-j)) := by
rw [ congrArg (j+·) (Nat.succ_pred i_sub_j_nez)]
rw [i_def]
simp only [testBit_to_div_mod, Nat.pow_add, Nat.mul_assoc]
simp only [Nat.mul_add_div (Nat.two_pow_pos _), Nat.mul_add_mod]

View File

@@ -205,26 +205,6 @@ theorem le_div_iff_mul_le (k0 : 0 < k) : x ≤ y / k ↔ x * k ≤ y := by
rw [ add_one, Nat.add_le_add_iff_right, IH k0, succ_mul,
Nat.add_sub_cancel (x*k) k, Nat.sub_le_sub_iff_right h.2, Nat.add_sub_cancel]
protected theorem div_div_eq_div_mul (m n k : Nat) : m / n / k = m / (n * k) := by
cases eq_zero_or_pos k with
| inl k0 => rw [k0, Nat.mul_zero, Nat.div_zero, Nat.div_zero] | inr kpos => ?_
cases eq_zero_or_pos n with
| inl n0 => rw [n0, Nat.zero_mul, Nat.div_zero, Nat.zero_div] | inr npos => ?_
apply Nat.le_antisymm
apply (le_div_iff_mul_le (Nat.mul_pos npos kpos)).2
rw [Nat.mul_comm n k, Nat.mul_assoc]
apply (le_div_iff_mul_le npos).1
apply (le_div_iff_mul_le kpos).1
(apply Nat.le_refl)
apply (le_div_iff_mul_le kpos).2
apply (le_div_iff_mul_le npos).2
rw [Nat.mul_assoc, Nat.mul_comm n k]
apply (le_div_iff_mul_le (Nat.mul_pos kpos npos)).1
apply Nat.le_refl
theorem div_mul_le_self : (m n : Nat), m / n * n m
| m, 0 => by simp
| m, n+1 => (le_div_iff_mul_le (Nat.succ_pos _)).1 (Nat.le_refl _)

View File

@@ -20,6 +20,66 @@ and later these lemmas should be organised into other files more systematically.
namespace Nat
/-! ### le/lt -/
protected theorem lt_asymm {a b : Nat} (h : a < b) : ¬ b < a := Nat.not_lt.2 (Nat.le_of_lt h)
protected abbrev not_lt_of_gt := @Nat.lt_asymm
protected abbrev not_lt_of_lt := @Nat.lt_asymm
protected theorem lt_iff_le_not_le {m n : Nat} : m < n m n ¬ n m :=
fun h => Nat.le_of_lt h, Nat.not_le_of_gt h, fun _, h => Nat.lt_of_not_ge h
protected abbrev lt_iff_le_and_not_ge := @Nat.lt_iff_le_not_le
protected theorem lt_iff_le_and_ne {m n : Nat} : m < n m n m n :=
fun h => Nat.le_of_lt h, Nat.ne_of_lt h, fun h => Nat.lt_of_le_of_ne h.1 h.2
protected theorem ne_iff_lt_or_gt {a b : Nat} : a b a < b b < a :=
Nat.lt_or_gt_of_ne, fun | .inl h => Nat.ne_of_lt h | .inr h => Nat.ne_of_gt h
protected abbrev lt_or_gt := @Nat.ne_iff_lt_or_gt
protected abbrev le_or_ge := @Nat.le_total
protected abbrev le_or_le := @Nat.le_total
protected theorem eq_or_lt_of_not_lt {a b : Nat} (hnlt : ¬ a < b) : a = b b < a :=
(Nat.lt_trichotomy ..).resolve_left hnlt
protected theorem lt_or_eq_of_le {n m : Nat} (h : n m) : n < m n = m :=
(Nat.lt_or_ge ..).imp_right (Nat.le_antisymm h)
protected theorem le_iff_lt_or_eq {n m : Nat} : n m n < m n = m :=
Nat.lt_or_eq_of_le, fun | .inl h => Nat.le_of_lt h | .inr rfl => Nat.le_refl _
protected theorem lt_succ_iff : m < succ n m n := le_of_lt_succ, lt_succ_of_le
protected theorem lt_succ_iff_lt_or_eq : m < succ n m < n m = n :=
Nat.lt_succ_iff.trans Nat.le_iff_lt_or_eq
protected theorem eq_of_lt_succ_of_not_lt (hmn : m < n + 1) (h : ¬ m < n) : m = n :=
(Nat.lt_succ_iff_lt_or_eq.1 hmn).resolve_left h
protected theorem eq_of_le_of_lt_succ (h₁ : n m) (h₂ : m < n + 1) : m = n :=
Nat.le_antisymm (le_of_succ_le_succ h₂) h₁
/-! ## zero/one/two -/
theorem le_zero : i 0 i = 0 := Nat.eq_zero_of_le_zero, fun | rfl => Nat.le_refl _
protected abbrev one_pos := @Nat.zero_lt_one
protected theorem two_pos : 0 < 2 := Nat.zero_lt_succ _
theorem add_one_ne_zero (n) : n + 1 0 := succ_ne_zero _
protected theorem ne_zero_iff_zero_lt : n 0 0 < n := Nat.pos_iff_ne_zero.symm
protected theorem zero_lt_two : 0 < 2 := Nat.zero_lt_succ _
protected theorem one_lt_two : 1 < 2 := Nat.succ_lt_succ Nat.zero_lt_one
protected theorem eq_zero_of_not_pos (h : ¬0 < n) : n = 0 :=
Nat.eq_zero_of_le_zero (Nat.not_lt.1 h)
/-! ## succ/pred -/
attribute [simp] succ_ne_zero zero_lt_succ lt_succ_self Nat.pred_zero Nat.pred_succ Nat.pred_le
@@ -593,6 +653,23 @@ by rw [H2, Nat.mul_div_cancel _ H1]
protected theorem div_eq_of_eq_mul_right (H1 : 0 < n) (H2 : m = n * k) : m / n = k :=
by rw [H2, Nat.mul_div_cancel_left _ H1]
protected theorem div_div_eq_div_mul (m n k : Nat) : m / n / k = m / (n * k) := by
cases eq_zero_or_pos k with
| inl k0 => rw [k0, Nat.mul_zero, Nat.div_zero, Nat.div_zero] | inr kpos => ?_
cases eq_zero_or_pos n with
| inl n0 => rw [n0, Nat.zero_mul, Nat.div_zero, Nat.zero_div] | inr npos => ?_
apply Nat.le_antisymm
· apply (le_div_iff_mul_le (Nat.mul_pos npos kpos)).2
rw [Nat.mul_comm n k, Nat.mul_assoc]
apply (le_div_iff_mul_le npos).1
apply (le_div_iff_mul_le kpos).1
(apply Nat.le_refl)
· apply (le_div_iff_mul_le kpos).2
apply (le_div_iff_mul_le npos).2
rw [Nat.mul_assoc, Nat.mul_comm n k]
apply (le_div_iff_mul_le (Nat.mul_pos kpos npos)).1
apply Nat.le_refl
protected theorem mul_div_mul_left {m : Nat} (n k : Nat) (H : 0 < m) :
m * n / (m * k) = n / k := by rw [ Nat.div_div_eq_div_mul, Nat.mul_div_cancel_left _ H]
@@ -661,6 +738,12 @@ theorem pow_succ' {m n : Nat} : m ^ n.succ = m * m ^ n := by
@[simp] theorem pow_eq {m n : Nat} : m.pow n = m ^ n := rfl
theorem shiftLeft_eq (a b : Nat) : a <<< b = a * 2 ^ b :=
match b with
| 0 => (Nat.mul_one _).symm
| b+1 => (shiftLeft_eq _ b).trans <| by
simp [pow_succ, Nat.mul_assoc, Nat.mul_left_comm, Nat.mul_comm]
theorem one_shiftLeft (n : Nat) : 1 <<< n = 2 ^ n := by rw [shiftLeft_eq, Nat.one_mul]
attribute [simp] Nat.pow_zero
@@ -900,6 +983,10 @@ theorem shiftLeft_succ : ∀(m n), m <<< (n + 1) = 2 * (m <<< n)
rw [shiftLeft_succ_inside _ (k+1)]
rw [shiftLeft_succ _ k, shiftLeft_succ_inside]
@[simp] theorem shiftRight_zero : n >>> 0 = n := rfl
theorem shiftRight_succ (m n) : m >>> (n + 1) = (m >>> n) / 2 := rfl
/-- Shiftright on successor with division moved inside. -/
theorem shiftRight_succ_inside : m n, m >>> (n+1) = (m/2) >>> n
| m, 0 => rfl
@@ -915,10 +1002,20 @@ theorem shiftRight_succ_inside : ∀m n, m >>> (n+1) = (m/2) >>> n
| 0 => by simp [shiftRight]
| n + 1 => by simp [shiftRight, zero_shiftRight n, shiftRight_succ]
theorem shiftRight_add (m n : Nat) : k, m >>> (n + k) = (m >>> n) >>> k
| 0 => rfl
| k + 1 => by simp [add_succ, shiftRight_add, shiftRight_succ]
theorem shiftLeft_shiftLeft (m n : Nat) : k, (m <<< n) <<< k = m <<< (n + k)
| 0 => rfl
| k + 1 => by simp [add_succ, shiftLeft_shiftLeft _ _ k, shiftLeft_succ]
theorem shiftRight_eq_div_pow (m : Nat) : n, m >>> n = m / 2 ^ n
| 0 => (Nat.div_one _).symm
| k + 1 => by
rw [shiftRight_add, shiftRight_eq_div_pow m k]
simp [Nat.div_div_eq_div_mul, Nat.pow_succ, shiftRight_succ]
theorem mul_add_div {m : Nat} (m_pos : m > 0) (x y : Nat) : (m * x + y) / m = x + y / m := by
match x with
| 0 => simp

View File

@@ -1,76 +0,0 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Omega
/-!
# Further results about `mod`.
This file proves some results about `mod` that are useful for bitblasting,
in particular
`Nat.mod_mul : x % (a * b) = x % a + a * (x / a % b)`
and its corollary
`Nat.mod_pow_succ : x % b ^ (k + 1) = x % b ^ k + b ^ k * ((x / b ^ k) % b)`.
It contains the necesssary preliminary results relating order and `*` and `/`,
which should probably be moved to their own file.
-/
namespace Nat
@[simp] protected theorem mul_lt_mul_left (a0 : 0 < a) : a * b < a * c b < c := by
induction a with
| zero => simp_all
| succ a ih =>
cases a
· simp
· simp_all [succ_eq_add_one, Nat.right_distrib]
omega
@[simp] protected theorem mul_lt_mul_right (a0 : 0 < a) : b * a < c * a b < c := by
rw [Nat.mul_comm b a, Nat.mul_comm c a, Nat.mul_lt_mul_left a0]
protected theorem lt_of_mul_lt_mul_left {a b c : Nat} (h : a * b < a * c) : b < c := by
cases a <;> simp_all
protected theorem lt_of_mul_lt_mul_right {a b c : Nat} (h : b * a < c * a) : b < c := by
rw [Nat.mul_comm b a, Nat.mul_comm c a] at h
exact Nat.lt_of_mul_lt_mul_left h
protected theorem div_lt_of_lt_mul {m n k : Nat} (h : m < n * k) : m / n < k :=
Nat.lt_of_mul_lt_mul_left <|
calc
n * (m / n) m % n + n * (m / n) := Nat.le_add_left _ _
_ = m := mod_add_div _ _
_ < n * k := h
theorem mod_mul_right_div_self (m n k : Nat) : m % (n * k) / n = m / n % k := by
rcases Nat.eq_zero_or_pos n with (rfl | hn); simp [mod_zero]
rcases Nat.eq_zero_or_pos k with (rfl | hk); simp [mod_zero]
conv => rhs; rw [ mod_add_div m (n * k)]
rw [Nat.mul_assoc, add_mul_div_left _ _ hn, add_mul_mod_self_left,
mod_eq_of_lt (Nat.div_lt_of_lt_mul (mod_lt _ (Nat.mul_pos hn hk)))]
theorem mod_mul_left_div_self (m n k : Nat) : m % (k * n) / n = m / n % k := by
rw [Nat.mul_comm k n, mod_mul_right_div_self]
@[simp 1100]
theorem mod_mul_right_mod (a b c : Nat) : a % (b * c) % b = a % b :=
Nat.mod_mod_of_dvd a (Nat.dvd_mul_right b c)
@[simp 1100]
theorem mod_mul_left_mod (a b c : Nat) : a % (b * c) % c = a % c :=
Nat.mod_mod_of_dvd a (Nat.mul_comm _ _ Nat.dvd_mul_left c b)
theorem mod_mul {a b x : Nat} : x % (a * b) = x % a + a * (x / a % b) := by
rw [Nat.add_comm, Nat.div_add_mod (x % (a*b)) a, Nat.mod_mul_right_mod,
Nat.mod_mul_right_div_self]
theorem mod_pow_succ {x b k : Nat} :
x % b ^ (k + 1) = x % b ^ k + b ^ k * ((x / b ^ k) % b) := by
rw [Nat.pow_succ, Nat.mod_mul]
end Nat

View File

@@ -5,6 +5,7 @@ Authors: Dany Fabian, Sebastian Ullrich
-/
prelude
import Init.Data.Int
import Init.Data.String
inductive Ordering where

View File

@@ -1362,19 +1362,6 @@ structure OmegaConfig where
end Omega
namespace CheckTactic
/--
Type used to lift an arbitrary value into a type parameter so it can
appear in a proof goal.
It is used by the #check_tactic command.
-/
inductive CheckGoalType {α : Sort u} : (val : α) → Prop where
| intro : (val : α) → CheckGoalType val
end CheckTactic
end Meta
namespace Parser

View File

@@ -613,30 +613,3 @@ everything else.
-/
syntax (name := guardMsgsCmd)
(docComment)? "#guard_msgs" (ppSpace guardMsgsSpec)? " in" ppLine command : command
namespace Parser
/--
`#check_tactic t ~> r by commands` runs the tactic sequence `commands`
on a goal with `t` and sees if the resulting expression has reduced it
to `r`.
-/
syntax (name := checkTactic) "#check_tactic " term "~>" term "by" tactic : command
/--
`#check_tactic_failure t by tac` runs the tactic `tac`
on a goal with `t` and verifies it fails.
-/
syntax (name := checkTacticFailure) "#check_tactic_failure " term "by" tactic : command
/--
`#check_simp t ~> r` checks `simp` reduces `t` to `r`.
-/
syntax (name := checkSimp) "#check_simp " term "~>" term : command
/--
`#check_simp t !~>` checks `simp` fails on reducing `t`.
-/
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
end Parser

View File

@@ -170,6 +170,19 @@ See [Theorem Proving in Lean 4][tpil4] for more information.
-/
syntax (name := calcTactic) "calc" calcSteps : tactic
/--
Denotes a term that was omitted by the pretty printer.
This is only used for pretty printing, and it cannot be elaborated.
The presence of `⋯` is controlled by the `pp.deepTerms` and `pp.proofs` options.
-/
syntax "" : term
macro_rules | `() => Macro.throwError "\
Error: The '⋯' token is used by the pretty printer to indicate omitted terms, \
and it cannot be elaborated.\
\n\nIts presence in pretty printing output is controlled by the 'pp.deepTerms' and `pp.proofs` options. \
These options can be further adjusted using `pp.deepTerms.threshold` and `pp.proofs.threshold`."
@[app_unexpander Unit.unit] def unexpandUnit : Lean.PrettyPrinter.Unexpander
| `($(_)) => `(())

View File

@@ -4,8 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Data.Int.Order
import Init.Data.Int.DivModLemmas
import Init.Data.Nat.Basic
import Init.Data.Nat.Lemmas
/-!
# Lemmas about `Nat`, `Int`, and `Fin` needed internally by `omega`.

View File

@@ -5,8 +5,6 @@ Authors: Scott Morrison
-/
prelude
import Init.Data.List.Lemmas
import Init.Data.Int.DivModLemmas
import Init.Data.Int.Gcd
namespace Lean.Omega

View File

@@ -1635,8 +1635,8 @@ instance : LT Nat where
lt := Nat.lt
theorem Nat.not_succ_le_zero : (n : Nat), LE.le (succ n) 0 False
| 0 => nofun
| succ _ => nofun
| 0, h => nomatch h
| succ _, h => nomatch h
theorem Nat.not_lt_zero (n : Nat) : Not (LT.lt n 0) :=
not_succ_le_zero n

View File

@@ -11,18 +11,6 @@ import Init.Core
import Init.NotationExtra
set_option linter.missingDocs true -- keep it documented
/-! ## cast and equality -/
@[simp] theorem eq_mp_eq_cast (h : α = β) : Eq.mp h = cast h := rfl
@[simp] theorem eq_mpr_eq_cast (h : α = β) : Eq.mpr h = cast h.symm := rfl
@[simp] theorem cast_cast : (ha : α = β) (hb : β = γ) (a : α),
cast hb (cast ha a) = cast (ha.trans hb) a
| rfl, rfl, _ => rfl
@[simp] theorem eq_true_eq_id : Eq True = id := by
funext _; simp only [true_iff, id.def, eq_iff_iff]
/-! ## not -/
theorem not_not_em (a : Prop) : ¬¬(a ¬a) := fun h => h (.inr (h .inl))
@@ -116,62 +104,10 @@ theorem and_or_right : (a ∧ b) c ↔ (a c) ∧ (b c) := by rw [@or
theorem or_imp : (a b c) (a c) (b c) :=
Iff.intro (fun h => h .inl, h .inr) (fun ha, hb => Or.rec ha hb)
/-
`not_or` is made simp for confluence with `¬((b || c) = true)`:
Critical pair:
1. `¬(b = true c = true)` via `Bool.or_eq_true`.
2. `(b || c = false)` via `Bool.not_eq_true` which then
reduces to `b = false ∧ c = false` via Mathlib simp lemma
`Bool.or_eq_false_eq_eq_false_and_eq_false`.
Both reduce to `b = false ∧ c = false` via `not_or`.
-/
@[simp] theorem not_or : ¬(p q) ¬p ¬q := or_imp
theorem not_or : ¬(p q) ¬p ¬q := or_imp
theorem not_and_of_not_or_not (h : ¬a ¬b) : ¬(a b) := h.elim (mt (·.1)) (mt (·.2))
/-! ## Ite -/
@[simp]
theorem if_false_left [h : Decidable p] :
ite p False q ¬p q := by cases h <;> (rename_i g; simp [g])
@[simp]
theorem if_false_right [h : Decidable p] :
ite p q False p q := by cases h <;> (rename_i g; simp [g])
/-
`if_true_left` and `if_true_right` are lower priority because
they introduce disjunctions and we prefer `if_false_left` and
`if_false_right` if they overlap.
-/
@[simp low]
theorem if_true_left [h : Decidable p] :
ite p True q ¬p q := by cases h <;> (rename_i g; simp [g])
@[simp low]
theorem if_true_right [h : Decidable p] :
ite p q True p q := by cases h <;> (rename_i g; simp [g])
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
@[simp] theorem dite_not [hn : Decidable (¬p)] [h : Decidable p] (x : ¬p α) (y : ¬¬p α) :
dite (¬p) x y = dite p (fun h => y (not_not_intro h)) x := by
cases h <;> (rename_i g; simp [g])
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
@[simp] theorem ite_not (p : Prop) [Decidable p] (x y : α) : ite (¬p) x y = ite p y x :=
dite_not (fun _ => x) (fun _ => y)
@[simp] theorem ite_true_same (p q : Prop) [h : Decidable p] : (if p then p else q) = (¬p q) := by
cases h <;> (rename_i g; simp [g])
@[simp] theorem ite_false_same (p q : Prop) [h : Decidable p] : (if p then q else p) = (p q) := by
cases h <;> (rename_i g; simp [g])
/-! ## exists and forall -/
section quantifiers
@@ -332,14 +268,7 @@ end quantifiers
/-! ## decidable -/
@[simp] theorem Decidable.not_not [Decidable p] : ¬¬p p := of_not_not, not_not_intro
/-- Excluded middle. Added as alias for Decidable.em -/
abbrev Decidable.or_not_self := em
/-- Excluded middle commuted. Added as alias for Decidable.em -/
theorem Decidable.not_or_self (p : Prop) [h : Decidable p] : ¬p p := by
cases h <;> simp [*]
theorem Decidable.not_not [Decidable p] : ¬¬p p := of_not_not, not_not_intro
theorem Decidable.by_contra [Decidable p] : (¬p False) p := of_not_not
@@ -381,7 +310,7 @@ theorem Decidable.not_imp_symm [Decidable a] (h : ¬a → b) (hb : ¬b) : a :=
theorem Decidable.not_imp_comm [Decidable a] [Decidable b] : (¬a b) (¬b a) :=
not_imp_symm, not_imp_symm
@[simp] theorem Decidable.not_imp_self [Decidable a] : (¬a a) a := by
theorem Decidable.not_imp_self [Decidable a] : (¬a a) a := by
have := @imp_not_self (¬a); rwa [not_not] at this
theorem Decidable.or_iff_not_imp_left [Decidable a] : a b (¬a b) :=
@@ -460,12 +389,8 @@ theorem Decidable.and_iff_not_or_not [Decidable a] [Decidable b] : a ∧ b ↔
rw [ not_and_iff_or_not_not, not_not]
theorem Decidable.imp_iff_right_iff [Decidable a] : (a b b) a b :=
Iff.intro
(fun h => (Decidable.em a).imp_right fun ha' => h.mp fun ha => (ha' ha).elim)
(fun ab => ab.elim imp_iff_right fun hb => iff_of_true (fun _ => hb) hb)
theorem Decidable.imp_iff_left_iff [Decidable a] : (b a b) a b :=
propext (@Iff.comm (a b) b) (@Decidable.imp_iff_right_iff a b _)
fun H => (Decidable.em a).imp_right fun ha' => H.1 fun ha => (ha' ha).elim,
fun H => H.elim imp_iff_right fun hb => iff_of_true (fun _ => hb) hb
theorem Decidable.and_or_imp [Decidable a] : a b (a c) a b c :=
if ha : a then by simp only [ha, true_and, true_imp_iff]
@@ -510,53 +435,3 @@ protected theorem Decidable.not_forall_not {p : α → Prop} [Decidable (∃ x,
protected theorem Decidable.not_exists_not {p : α Prop} [ x, Decidable (p x)] :
(¬ x, ¬p x) x, p x := by
simp only [not_exists, Decidable.not_not]
export Decidable (not_imp_self)
/-
`decide_implies` simp justification.
We have a critical pair from `decide (¬(p ∧ q))`:
1. `decide (p → ¬q)` via `not_and`
2. `!decide (p ∧ q)` via `decide_not` This further refines to
`!(decide p) || !(decide q)` via `Bool.decide_and` (in Mathlib) and
`Bool.not_and` (made simp in Mathlib).
We introduce `decide_implies` below and then both normalize to
`!(decide p) || !(decide q)`.
-/
@[simp]
theorem decide_implies (u v : Prop)
[duv : Decidable (u v)] [du : Decidable u] {dv : u Decidable v}
: decide (u v) = dite u (fun h => @decide v (dv h)) (fun _ => true) :=
if h : u then by
simp [h]
else by
simp [h]
/-
`decide_ite` is needed to resolve critical pair with
We have a critical pair from `decide (ite p b c = true)`:
1. `ite p b c` via `decide_coe`
2. `decide (ite p (b = true) (c = true))` via `Bool.ite_eq_true_distrib`.
We introduce `decide_ite` so both normalize to `ite p b c`.
-/
@[simp]
theorem decide_ite (u : Prop) [du : Decidable u] (p q : Prop)
[dpq : Decidable (ite u p q)] [dp : Decidable p] [dq : Decidable q] :
decide (ite u p q) = ite u (decide p) (decide q) := by
cases du <;> simp [*]
/- Confluence for `ite_true_same` and `decide_ite`. -/
@[simp] theorem ite_true_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
(if p then decide p else b) = (decide p || b) := by
cases h <;> (rename_i pt; simp [pt])
/- Confluence for `ite_false_same` and `decide_ite`. -/
@[simp] theorem ite_false_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
(if p then b else decide p) = (decide p && b) := by
cases h <;> (rename_i pt; simp [pt])

View File

@@ -15,15 +15,12 @@ theorem of_eq_false (h : p = False) : ¬ p := fun hp => False.elim (h.mp hp)
theorem eq_true (h : p) : p = True :=
propext fun _ => trivial, fun _ => h
-- Adding this attribute needs `eq_true`.
attribute [simp] cast_heq
theorem eq_false (h : ¬ p) : p = False :=
propext fun h' => absurd h' h, fun h' => False.elim h'
theorem eq_false' (h : p False) : p = False := eq_false h
theorem eq_true_of_decide {p : Prop} [Decidable p] (h : decide p = true) : p = True :=
theorem eq_true_of_decide {p : Prop} {_ : Decidable p} (h : decide p = true) : p = True :=
eq_true (of_decide_eq_true h)
theorem eq_false_of_decide {p : Prop} {_ : Decidable p} (h : decide p = false) : p = False :=
@@ -127,7 +124,6 @@ end SimprocHelperLemmas
@[simp] theorem not_true_eq_false : (¬ True) = False := by decide
@[simp] theorem not_iff_self : ¬(¬a a) | H => iff_not_self H.symm
attribute [simp] iff_not_self
/-! ## and -/
@@ -177,11 +173,6 @@ theorem or_iff_left_of_imp (hb : b → a) : (a b) ↔ a := Iff.intro (Or.r
@[simp] theorem or_iff_left_iff_imp : (a b a) (b a) := Iff.intro (·.mp Or.inr) or_iff_left_of_imp
@[simp] theorem or_iff_right_iff_imp : (a b b) (a b) := by rw [or_comm, or_iff_left_iff_imp]
@[simp] theorem iff_self_or (a b : Prop) : (a a b) (b a) :=
propext (@Iff.comm _ a) @or_iff_left_iff_imp a b
@[simp] theorem iff_or_self (a b : Prop) : (b a b) (a b) :=
propext (@Iff.comm _ b) @or_iff_right_iff_imp a b
/-# Bool -/
@[simp] theorem Bool.or_false (b : Bool) : (b || false) = b := by cases b <;> rfl
@@ -208,9 +199,9 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem Bool.not_not (b : Bool) : (!!b) = b := by cases b <;> rfl
@[simp] theorem Bool.not_true : (!true) = false := by decide
@[simp] theorem Bool.not_false : (!false) = true := by decide
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
@[simp] theorem Bool.not_beq_false (b : Bool) : (!(b == false)) = (b == true) := by cases b <;> rfl
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
@[simp] theorem Bool.not_eq_false' (b : Bool) : ((!b) = false) = (b = true) := by cases b <;> simp
@[simp] theorem Bool.beq_to_eq (a b : Bool) :
@@ -221,14 +212,11 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem Bool.not_eq_true (b : Bool) : (¬(b = true)) = (b = false) := by cases b <;> decide
@[simp] theorem Bool.not_eq_false (b : Bool) : (¬(b = false)) = (b = true) := by cases b <;> decide
@[simp] theorem decide_eq_true_eq [Decidable p] : (decide p = true) = p :=
propext <| Iff.intro of_decide_eq_true decide_eq_true
@[simp] theorem decide_not [g : Decidable p] [h : Decidable (Not p)] : decide (Not p) = !(decide p) := by
cases g <;> (rename_i gp; simp [gp]; rfl)
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by
cases h <;> (rename_i hp; simp [decide, hp])
@[simp] theorem decide_eq_true_eq {_ : Decidable p} : (decide p = true) = p := propext <| Iff.intro of_decide_eq_true decide_eq_true
@[simp] theorem decide_not {h : Decidable p} : decide (¬ p) = !decide p := by cases h <;> rfl
@[simp] theorem not_decide_eq_true {h : Decidable p} : ((!decide p) = true) = ¬ p := by cases h <;> simp [decide, *]
@[simp] theorem heq_eq_eq (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
@[simp] theorem heq_eq_eq {α : Sort u} (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
@[simp] theorem cond_true (a b : α) : cond true a b = a := rfl
@[simp] theorem cond_false (a b : α) : cond false a b = b := rfl
@@ -240,29 +228,11 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp [bne]
@[simp] theorem decide_False : decide False = false := rfl
@[simp] theorem decide_True : decide True = true := rfl
@[simp] theorem decide_True : decide True = true := rfl
@[simp] theorem bne_iff_ne [BEq α] [LawfulBEq α] (a b : α) : a != b a b := by
simp [bne]; rw [ beq_iff_eq a b]; simp [-beq_iff_eq]
/-
Added for critical pair for `¬((a != b) = true)`
1. `(a != b) = false` via `Bool.not_eq_true`
2. `¬(a ≠ b)` via `bne_iff_ne`
These will both normalize to `a = b` with the first via `bne_eq_false_iff_eq`.
-/
@[simp] theorem beq_eq_false_iff_ne [BEq α] [LawfulBEq α]
(a b : α) : (a == b) = false a b := by
rw [ne_eq, beq_iff_eq a b]
cases a == b <;> decide
@[simp] theorem bne_eq_false_iff_eq [BEq α] [LawfulBEq α] (a b : α) :
(a != b) = false a = b := by
rw [bne, beq_iff_eq a b]
cases a == b <;> decide
/-# Nat -/
@[simp] theorem Nat.le_zero_eq (a : Nat) : (a 0) = (a = 0) :=

View File

@@ -31,43 +31,22 @@ Simplification procedures can be also scoped or local.
-/
syntax (docComment)? attrKind "simproc " (Tactic.simpPre <|> Tactic.simpPost)? ("[" ident,* "]")? ident " (" term ")" " := " term : command
/--
Similar to `simproc`, but resulting expression must be definitionally equal to the input one.
-/
syntax (docComment)? attrKind "dsimproc " (Tactic.simpPre <|> Tactic.simpPost)? ("[" ident,* "]")? ident " (" term ")" " := " term : command
/--
A user-defined simplification procedure declaration. To activate this procedure in `simp` tactic,
we must provide it as an argument, or use the command `attribute` to set its `[simproc]` attribute.
-/
syntax (docComment)? "simproc_decl " ident " (" term ")" " := " term : command
/--
A user-defined defeq simplification procedure declaration. To activate this procedure in `simp` tactic,
we must provide it as an argument, or use the command `attribute` to set its `[simproc]` attribute.
-/
syntax (docComment)? "dsimproc_decl " ident " (" term ")" " := " term : command
/--
A builtin simplification procedure.
-/
syntax (docComment)? attrKind "builtin_simproc " (Tactic.simpPre <|> Tactic.simpPost)? ("[" ident,* "]")? ident " (" term ")" " := " term : command
/--
A builtin defeq simplification procedure.
-/
syntax (docComment)? attrKind "builtin_dsimproc " (Tactic.simpPre <|> Tactic.simpPost)? ("[" ident,* "]")? ident " (" term ")" " := " term : command
/--
A builtin simplification procedure declaration.
-/
syntax (docComment)? "builtin_simproc_decl " ident " (" term ")" " := " term : command
/--
A builtin defeq simplification procedure declaration.
-/
syntax (docComment)? "builtin_dsimproc_decl " ident " (" term ")" " := " term : command
/--
Auxiliary command for associating a pattern with a simplification procedure.
-/
@@ -107,60 +86,33 @@ macro_rules
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
simproc_pattern% $pattern => $n)
macro_rules
| `($[$doc?:docComment]? dsimproc_decl $n:ident ($pattern:term) := $body) => do
let simprocType := `Lean.Meta.Simp.DSimproc
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
simproc_pattern% $pattern => $n)
macro_rules
| `($[$doc?:docComment]? builtin_simproc_decl $n:ident ($pattern:term) := $body) => do
let simprocType := `Lean.Meta.Simp.Simproc
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
builtin_simproc_pattern% $pattern => $n)
macro_rules
| `($[$doc?:docComment]? builtin_dsimproc_decl $n:ident ($pattern:term) := $body) => do
let simprocType := `Lean.Meta.Simp.DSimproc
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
builtin_simproc_pattern% $pattern => $n)
private def mkAttributeCmds
(kind : TSyntax `Lean.Parser.Term.attrKind)
(pre? : Option (TSyntax [`Lean.Parser.Tactic.simpPre, `Lean.Parser.Tactic.simpPost]))
(ids? : Option (Syntax.TSepArray `ident ","))
(n : Ident) : MacroM (Array Syntax) := do
let mut cmds := #[]
let pushDefault (cmds : Array (TSyntax `command)) : MacroM (Array (TSyntax `command)) := do
return cmds.push ( `(attribute [$kind simproc $[$pre?]?] $n))
if let some ids := ids? then
for id in ids.getElems do
let idName := id.getId
let (attrName, attrKey) :=
if idName == `simp then
(`simprocAttr, "simproc")
else if idName == `seval then
(`sevalprocAttr, "sevalproc")
else
let idName := idName.appendAfter "_proc"
(`Parser.Attr ++ idName, idName.toString)
let attrStx : TSyntax `attr := mkNode attrName #[mkAtom attrKey, mkOptionalNode pre?]
cmds := cmds.push ( `(attribute [$kind $attrStx] $n))
else
cmds pushDefault cmds
return cmds
macro_rules
| `($[$doc?:docComment]? $kind:attrKind simproc $[$pre?]? $[ [ $ids?:ident,* ] ]? $n:ident ($pattern:term) := $body) => do
return mkNullNode <|
#[( `($[$doc?:docComment]? simproc_decl $n ($pattern) := $body))]
++ ( mkAttributeCmds kind pre? ids? n)
macro_rules
| `($[$doc?:docComment]? $kind:attrKind dsimproc $[$pre?]? $[ [ $ids?:ident,* ] ]? $n:ident ($pattern:term) := $body) => do
return mkNullNode <|
#[( `($[$doc?:docComment]? dsimproc_decl $n ($pattern) := $body))]
++ ( mkAttributeCmds kind pre? ids? n)
let mut cmds := #[( `($[$doc?:docComment]? simproc_decl $n ($pattern) := $body))]
let pushDefault (cmds : Array (TSyntax `command)) : MacroM (Array (TSyntax `command)) := do
return cmds.push ( `(attribute [$kind simproc $[$pre?]?] $n))
if let some ids := ids? then
for id in ids.getElems do
let idName := id.getId
let (attrName, attrKey) :=
if idName == `simp then
(`simprocAttr, "simproc")
else if idName == `seval then
(`sevalprocAttr, "sevalproc")
else
let idName := idName.appendAfter "_proc"
(`Parser.Attr ++ idName, idName.toString)
let attrStx : TSyntax `attr := mkNode attrName #[mkAtom attrKey, mkOptionalNode pre?]
cmds := cmds.push ( `(attribute [$kind $attrStx] $n))
else
cmds pushDefault cmds
return mkNullNode cmds
macro_rules
| `($[$doc?:docComment]? $kind:attrKind builtin_simproc $[$pre?]? $n:ident ($pattern:term) := $body) => do
@@ -174,16 +126,4 @@ macro_rules
attribute [$kind builtin_simproc $[$pre?]?] $n
attribute [$kind builtin_sevalproc $[$pre?]?] $n)
macro_rules
| `($[$doc?:docComment]? $kind:attrKind builtin_dsimproc $[$pre?]? $n:ident ($pattern:term) := $body) => do
`($[$doc?:docComment]? builtin_dsimproc_decl $n ($pattern) := $body
attribute [$kind builtin_simproc $[$pre?]?] $n)
| `($[$doc?:docComment]? $kind:attrKind builtin_dsimproc $[$pre?]? [seval] $n:ident ($pattern:term) := $body) => do
`($[$doc?:docComment]? builtin_dsimproc_decl $n ($pattern) := $body
attribute [$kind builtin_sevalproc $[$pre?]?] $n)
| `($[$doc?:docComment]? $kind:attrKind builtin_dsimproc $[$pre?]? [simp, seval] $n:ident ($pattern:term) := $body) => do
`($[$doc?:docComment]? builtin_dsimproc_decl $n ($pattern) := $body
attribute [$kind builtin_simproc $[$pre?]?] $n
attribute [$kind builtin_sevalproc $[$pre?]?] $n)
end Lean.Parser

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Luke Nelson, Jared Roesch, Leonardo de Moura, Sebastian Ullrich, Mac Malone
-/
prelude
import Init.Control.EState
import Init.Control.Reader
import Init.Data.String
import Init.Data.ByteArray

View File

@@ -673,13 +673,12 @@ It makes sure the "continuation" `?_` is the main goal after refining.
macro "refine_lift " e:term : tactic => `(tactic| focus (refine no_implicit_lambda% $e; rotate_right))
/--
The `have` tactic is for adding hypotheses to the local context of the main goal.
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
* `have h := e` uses the type of `e` for `t`.
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that have only one applicable constructor.
`have h : t := e` adds the hypothesis `h : t` to the current goal if `e` a term
of type `t`.
* If `t` is omitted, it will be inferred.
* If `h` is omitted, the name `this` is used.
* The variant `have pattern := e` is equivalent to `match e with | pattern => _`,
and it is convenient for types that have only one applicable constructor.
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
-/
@@ -694,15 +693,12 @@ If `h :` is omitted, the name `this` is used.
-/
macro "suffices " d:sufficesDecl : tactic => `(tactic| refine_lift suffices $d; ?_)
/--
The `let` tactic is for adding definitions to the local context of the main goal.
* `let x : t := e` adds the definition `x : t := e` if `e` is a term of type `t`.
* `let x := e` uses the type of `e` for `t`.
* `let : t := e` and `let := e` use `this` for the name of the hypothesis.
* `let pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
where `_` stands for the tactics that follow this one.
It is convenient for types that let only one applicable constructor.
For example, given `p : α × β × γ`, `let ⟨x, y, z⟩ := p` produces the
local variables `x : α`, `y : β`, and `z : γ`.
`let h : t := e` adds the hypothesis `h : t := e` to the current goal if `e` a term of type `t`.
If `t` is omitted, it will be inferred.
The variant `let pattern := e` is equivalent to `match e with | pattern => _`,
and it is convenient for types that have only applicable constructor.
Example: given `h : p ∧ q ∧ r`, `let ⟨h₁, h₂, h₃⟩ := h` produces the hypotheses
`h₁ : p`, `h₂ : q`, and `h₃ : r`.
-/
macro "let " d:letDecl : tactic => `(tactic| refine_lift let $d:letDecl; ?_)
/--
@@ -1310,26 +1306,6 @@ used when closing the goal.
-/
syntax (name := apply?) "apply?" (" using " (colGt term),+)? : tactic
/--
`show_term tac` runs `tac`, then prints the generated term in the form
"exact X Y Z" or "refine X ?_ Z" if there are remaining subgoals.
(For some tactics, the printed term will not be human readable.)
-/
syntax (name := showTerm) "show_term " tacticSeq : tactic
/--
`show_term e` elaborates `e`, then prints the generated term.
-/
macro (name := showTermElab) tk:"show_term " t:term : term =>
`(term| no_implicit_lambda% (show_term_elab%$tk $t))
/--
The command `by?` will print a suggestion for replacing the proof block with a proof term
using `show_term`.
-/
macro (name := by?) tk:"by?" t:tacticSeq : term => `(show_term%$tk by%$tk $t)
end Tactic
namespace Attr
@@ -1454,9 +1430,9 @@ where `i < arr.size` is in the context) and `simp_arith` and `omega`
-/
syntax "get_elem_tactic_trivial" : tactic
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| omega)
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| simp (config := { arith := true }); done)
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| trivial)
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| simp (config := { arith := true }); done)
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| omega)
/--
`get_elem_tactic` is the tactic automatically called by the notation `arr[i]`
@@ -1467,24 +1443,6 @@ users are encouraged to extend `get_elem_tactic_trivial` instead of this tactic.
-/
macro "get_elem_tactic" : tactic =>
`(tactic| first
/-
Recall that `macro_rules` are tried in reverse order.
We want `assumption` to be tried first.
This is important for theorems such as
```
[simp] theorem getElem_pop (a : Array α) (i : Nat) (hi : i < a.pop.size) :
a.pop[i] = a[i]'(Nat.lt_of_lt_of_le (a.size_pop ▸ hi) (Nat.sub_le _ _)) :=
```
There is a proof embedded in the right-hand-side, and we want it to be just `hi`.
If `omega` is used to "fill" this proof, we will have a more complex proof term that
cannot be inferred by unification.
We hardcoded `assumption` here to ensure users cannot accidentaly break this IF
they add new `macro_rules` for `get_elem_tactic_trivial`.
TODO: Implement priorities for `macro_rules`.
TODO: Ensure we have a **high-priority** macro_rules for `get_elem_tactic_trivial` which is just `assumption`.
-/
| assumption
| get_elem_tactic_trivial
| fail "failed to prove index is valid, possible solutions:
- Use `have`-expressions to prove the index is valid

View File

@@ -289,9 +289,6 @@ def Exception.isMaxHeartbeat (ex : Exception) : Bool :=
def mkArrow (d b : Expr) : CoreM Expr :=
return Lean.mkForall ( mkFreshUserName `x) BinderInfo.default d b
/-- Iterated `mkArrow`, creates the expression `a₁ → a₂ → … → aₙ → b`. Also see `arrowDomainsN`. -/
def mkArrowN (ds : Array Expr) (e : Expr) : CoreM Expr := ds.foldrM mkArrow e
def addDecl (decl : Declaration) : CoreM Unit := do
profileitM Exception "type checking" ( getOptions) do
withTraceNode `Kernel (fun _ => return m!"typechecking declaration") do

View File

@@ -84,14 +84,14 @@ partial def insertAtCollisionNodeAux [BEq α] : CollisionNode α β → Nat →
else insertAtCollisionNodeAux n (i+1) k v
else
Node.collision (keys.push k) (vals.push v) (size_push heq k v), IsCollisionNode.mk _ _ _
| Node.entries _, h, _, _, _ => nomatch h
| Node.entries _, h, _, _, _ => False.elim (nomatch h)
def insertAtCollisionNode [BEq α] : CollisionNode α β α β CollisionNode α β :=
fun n k v => insertAtCollisionNodeAux n 0 k v
def getCollisionNodeSize : CollisionNode α β Nat
| Node.collision keys _ _, _ => keys.size
| Node.entries _, h => nomatch h
| Node.entries _, h => False.elim (nomatch h)
def mkCollisionNode (k₁ : α) (v₁ : β) (k₂ : α) (v₂ : β) : Node α β :=
let ks : Array α := Array.mkEmpty maxCollisions
@@ -105,7 +105,7 @@ partial def insertAux [BEq α] [Hashable α] : Node α β → USize → USize
let newNode := insertAtCollisionNode Node.collision keys vals heq, IsCollisionNode.mk _ _ _ k v
if depth >= maxDepth || getCollisionNodeSize newNode < maxCollisions then newNode.val
else match newNode with
| Node.entries _, h => nomatch h
| Node.entries _, h => False.elim (nomatch h)
| Node.collision keys vals heq, _ =>
let rec traverse (i : Nat) (entries : Node α β) : Node α β :=
if h : i < keys.size then

View File

@@ -48,5 +48,3 @@ import Lean.Elab.Calc
import Lean.Elab.InheritDoc
import Lean.Elab.ParseImportsFast
import Lean.Elab.GuardMsgs
import Lean.Elab.CheckTactic
import Lean.Elab.MatchExpr

View File

@@ -99,14 +99,6 @@ private def elabOptLevel (stx : Syntax) : TermElabM Level :=
else
throwError "synthetic hole has already been defined with an incompatible local context"
@[builtin_term_elab Lean.Parser.Term.omission] def elabOmission : TermElab := fun stx expectedType? => do
logWarning m!"\
The '⋯' token is used by the pretty printer to indicate omitted terms, and it should not be used directly. \
It logs this warning and then elaborates like `_`.\
\n\nThe presence of `⋯` in pretty printing output is controlled by the 'pp.deepTerms' and `pp.proofs` options. \
These options can be further adjusted using `pp.deepTerms.threshold` and `pp.proofs.threshold`."
elabHole stx expectedType?
@[builtin_term_elab «letMVar»] def elabLetMVar : TermElab := fun stx expectedType? => do
match stx with
| `(let_mvar% ? $n := $e; $b) =>

View File

@@ -1,84 +0,0 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix
-/
prelude
import Lean.Elab.Tactic.ElabTerm
import Lean.Elab.Command
import Lean.Elab.Tactic.Meta
import Lean.Meta.CheckTactic
/-!
Commands to validate tactic results.
-/
namespace Lean.Elab.CheckTactic
open Lean.Meta CheckTactic
open Lean.Elab.Tactic
open Lean.Elab.Command
@[builtin_command_elab Lean.Parser.checkTactic]
def elabCheckTactic : CommandElab := fun stx => do
let `(#check_tactic $t ~> $result by $tac) := stx | throwUnsupportedSyntax
withoutModifyingEnv $ do
runTermElabM $ fun _vars => do
let u Lean.Elab.Term.elabTerm t none
let type inferType u
let checkGoalType mkCheckGoalType u type
let mvar mkFreshExprMVar (.some checkGoalType)
let expTerm Lean.Elab.Term.elabTerm result (.some type)
let (goals, _) Lean.Elab.runTactic mvar.mvarId! tac.raw
match goals with
| [] =>
throwErrorAt stx
m!"{tac} closed goal, but is expected to reduce to {indentExpr expTerm}."
| [next] => do
let (val, _, _) matchCheckGoalType stx (next.getType)
if !( Meta.withReducible <| isDefEq val expTerm) then
throwErrorAt stx
m!"Term reduces to{indentExpr val}\nbut is expected to reduce to {indentExpr expTerm}"
| _ => do
throwErrorAt stx
m!"{tac} produced multiple goals, but is expected to reduce to {indentExpr expTerm}."
@[builtin_command_elab Lean.Parser.checkTacticFailure]
def elabCheckTacticFailure : CommandElab := fun stx => do
let `(#check_tactic_failure $t by $tactic) := stx | throwUnsupportedSyntax
withoutModifyingEnv $ do
runTermElabM $ fun _vars => do
let val Lean.Elab.Term.elabTerm t none
let type inferType val
let checkGoalType mkCheckGoalType val type
let mvar mkFreshExprMVar (.some checkGoalType)
let act := Lean.Elab.runTactic mvar.mvarId! tactic.raw
match try (Term.withoutErrToSorry (some <$> act)) catch _ => pure none with
| none =>
pure ()
| some (gls, _) =>
let ppGoal (g : MVarId) := do
let (val, _type, _u) matchCheckGoalType stx ( g.getType)
pure m!"{indentExpr val}"
let msg
match gls with
| [] => pure m!"{tactic} expected to fail on {t}, but closed goal."
| [g] =>
pure <| m!"{tactic} expected to fail on {t}, but returned: {←ppGoal g}"
| gls =>
let app m g := do pure <| m ++ (ppGoal g)
let init := m!"{tactic} expected to fail on {t}, but returned goals:"
gls.foldlM (init := init) app
throwErrorAt stx msg
@[builtin_macro Lean.Parser.checkSimp]
def expandCheckSimp : Macro := fun stx => do
let `(#check_simp $t ~> $exp) := stx | Macro.throwUnsupported
`(command|#check_tactic $t ~> $exp by simp)
@[builtin_macro Lean.Parser.checkSimpFailure]
def expandCheckSimpFailure : Macro := fun stx => do
let `(#check_simp $t !~>) := stx | Macro.throwUnsupported
`(command|#check_tactic_failure $t by simp)
end Lean.Elab.CheckTactic

View File

@@ -131,31 +131,12 @@ abbrev Var := Syntax -- TODO: should be `Ident`
/-- A `doMatch` alternative. `vars` is the array of variables declared by `patterns`. -/
structure Alt (σ : Type) where
ref : Syntax
vars : Array Var
ref : Syntax
vars : Array Var
patterns : Syntax
rhs : σ
rhs : σ
deriving Inhabited
/-- A `doMatchExpr` alternative. -/
structure AltExpr (σ : Type) where
ref : Syntax
var? : Option Var
funName : Syntax
pvars : Array Syntax
rhs : σ
deriving Inhabited
def AltExpr.vars (alt : AltExpr σ) : Array Var := Id.run do
let mut vars := #[]
if let some var := alt.var? then
vars := vars.push var
for pvar in alt.pvars do
match pvar with
| `(_) => pure ()
| _ => vars := vars.push pvar
return vars
/--
Auxiliary datastructure for representing a `do` code block, and compiling "reassignments" (e.g., `x := x + 1`).
We convert `Code` into a `Syntax` term representing the:
@@ -217,7 +198,6 @@ inductive Code where
/-- Recall that an if-then-else may declare a variable using `optIdent` for the branches `thenBranch` and `elseBranch`. We store the variable name at `var?`. -/
| ite (ref : Syntax) (h? : Option Var) (optIdent : Syntax) (cond : Syntax) (thenBranch : Code) (elseBranch : Code)
| match (ref : Syntax) (gen : Syntax) (discrs : Syntax) (optMotive : Syntax) (alts : Array (Alt Code))
| matchExpr (ref : Syntax) (meta : Bool) (discr : Syntax) (alts : Array (AltExpr Code)) (elseBranch : Code)
| jmp (ref : Syntax) (jpName : Name) (args : Array Syntax)
deriving Inhabited
@@ -232,7 +212,6 @@ def Code.getRef? : Code → Option Syntax
| .return ref _ => ref
| .ite ref .. => ref
| .match ref .. => ref
| .matchExpr ref .. => ref
| .jmp ref .. => ref
abbrev VarSet := RBMap Name Syntax Name.cmp
@@ -264,28 +243,19 @@ partial def CodeBlocl.toMessageData (codeBlock : CodeBlock) : MessageData :=
| .match _ _ ds _ alts =>
m!"match {ds} with"
++ alts.foldl (init := m!"") fun acc alt => acc ++ m!"\n| {alt.patterns} => {loop alt.rhs}"
| .matchExpr _ meta d alts elseCode =>
let r := m!"match_expr {if meta then "" else "(meta := false)"} {d} with"
let r := r ++ alts.foldl (init := m!"") fun acc alt =>
let acc := acc ++ m!"\n| {if let some var := alt.var? then m!"{var}@" else ""}"
let acc := acc ++ m!"{alt.funName}"
let acc := acc ++ alt.pvars.foldl (init := m!"") fun acc pvar => acc ++ m!" {pvar}"
acc ++ m!" => {loop alt.rhs}"
r ++ m!"| _ => {loop elseCode}"
loop codeBlock.code
/-- Return true if the give code contains an exit point that satisfies `p` -/
partial def hasExitPointPred (c : Code) (p : Code Bool) : Bool :=
let rec loop : Code Bool
| .decl _ _ k => loop k
| .reassign _ _ k => loop k
| .joinpoint _ _ b k => loop b || loop k
| .seq _ k => loop k
| .ite _ _ _ _ t e => loop t || loop e
| .match _ _ _ _ alts => alts.any (loop ·.rhs)
| .matchExpr _ _ _ alts e => alts.any (loop ·.rhs) || loop e
| .jmp .. => false
| c => p c
| .decl _ _ k => loop k
| .reassign _ _ k => loop k
| .joinpoint _ _ b k => loop b || loop k
| .seq _ k => loop k
| .ite _ _ _ _ t e => loop t || loop e
| .match _ _ _ _ alts => alts.any (loop ·.rhs)
| .jmp .. => false
| c => p c
loop c
def hasExitPoint (c : Code) : Bool :=
@@ -330,18 +300,13 @@ partial def convertTerminalActionIntoJmp (code : Code) (jp : Name) (xs : Array V
| .joinpoint n ps b k => return .joinpoint n ps ( loop b) ( loop k)
| .seq e k => return .seq e ( loop k)
| .ite ref x? h c t e => return .ite ref x? h c ( loop t) ( loop e)
| .match ref g ds t alts => return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( loop alt.rhs) })
| .action e => mkAuxDeclFor e fun y =>
let ref := e
-- We jump to `jp` with xs **and** y
let jmpArgs := xs.push y
return Code.jmp ref jp jmpArgs
| .match ref g ds t alts =>
return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( loop alt.rhs) })
| .matchExpr ref meta d alts e => do
let alts alts.mapM fun alt => do pure { alt with rhs := ( loop alt.rhs) }
let e loop e
return .matchExpr ref meta d alts e
| c => return c
| c => return c
loop code
structure JPDecl where
@@ -407,13 +372,14 @@ def mkJmp (ref : Syntax) (rs : VarSet) (val : Syntax) (mkJPBody : Syntax → Mac
return Code.jmp ref jp args
/-- `pullExitPointsAux rs c` auxiliary method for `pullExitPoints`, `rs` is the set of update variable in the current path. -/
partial def pullExitPointsAux (rs : VarSet) (c : Code) : StateRefT (Array JPDecl) TermElabM Code := do
partial def pullExitPointsAux (rs : VarSet) (c : Code) : StateRefT (Array JPDecl) TermElabM Code :=
match c with
| .decl xs stx k => return .decl xs stx ( pullExitPointsAux (eraseVars rs xs) k)
| .reassign xs stx k => return .reassign xs stx ( pullExitPointsAux (insertVars rs xs) k)
| .joinpoint j ps b k => return .joinpoint j ps ( pullExitPointsAux rs b) ( pullExitPointsAux rs k)
| .seq e k => return .seq e ( pullExitPointsAux rs k)
| .ite ref x? o c t e => return .ite ref x? o c ( pullExitPointsAux (eraseOptVar rs x?) t) ( pullExitPointsAux (eraseOptVar rs x?) e)
| .match ref g ds t alts => return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) })
| .jmp .. => return c
| .break ref => mkSimpleJmp ref rs (.break ref)
| .continue ref => mkSimpleJmp ref rs (.continue ref)
@@ -423,13 +389,6 @@ partial def pullExitPointsAux (rs : VarSet) (c : Code) : StateRefT (Array JPDecl
mkAuxDeclFor e fun y =>
let ref := e
mkJmp ref rs y (fun yFresh => return .action ( ``(Pure.pure $yFresh)))
| .match ref g ds t alts =>
let alts alts.mapM fun alt => do pure { alt with rhs := ( pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) }
return .match ref g ds t alts
| .matchExpr ref meta d alts e =>
let alts alts.mapM fun alt => do pure { alt with rhs := ( pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) }
let e pullExitPointsAux rs e
return .matchExpr ref meta d alts e
/--
Auxiliary operation for adding new variables to the collection of updated variables in a CodeBlock.
@@ -498,14 +457,6 @@ partial def extendUpdatedVarsAux (c : Code) (ws : VarSet) : TermElabM Code :=
pullExitPoints c
else
return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( update alt.rhs) })
| .matchExpr ref meta d alts e =>
if alts.any fun alt => alt.vars.any fun x => ws.contains x.getId then
-- If a pattern variable is shadowing a variable in ws, we `pullExitPoints`
pullExitPoints c
else
let alts alts.mapM fun alt => do pure { alt with rhs := ( update alt.rhs) }
let e update e
return .matchExpr ref meta d alts e
| .ite ref none o c t e => return .ite ref none o c ( update t) ( update e)
| .ite ref (some h) o cond t e =>
if ws.contains h.getId then
@@ -619,16 +570,6 @@ def mkMatch (ref : Syntax) (genParam : Syntax) (discrs : Syntax) (optMotive : Sy
return { ref := alt.ref, vars := alt.vars, patterns := alt.patterns, rhs := rhs.code : Alt Code }
return { code := .match ref genParam discrs optMotive alts, uvars := ws }
def mkMatchExpr (ref : Syntax) (meta : Bool) (discr : Syntax) (alts : Array (AltExpr CodeBlock)) (elseBranch : CodeBlock) : TermElabM CodeBlock := do
-- nary version of homogenize
let ws := alts.foldl (union · ·.rhs.uvars) {}
let ws := union ws elseBranch.uvars
let alts alts.mapM fun alt => do
let rhs extendUpdatedVars alt.rhs ws
return { alt with rhs := rhs.code : AltExpr Code }
let elseBranch extendUpdatedVars elseBranch ws
return { code := .matchExpr ref meta discr alts elseBranch.code, uvars := ws }
/-- Return a code block that executes `terminal` and then `k` with the value produced by `terminal`.
This method assumes `terminal` is a terminal -/
def concat (terminal : CodeBlock) (kRef : Syntax) (y? : Option Var) (k : CodeBlock) : TermElabM CodeBlock := do
@@ -765,19 +706,6 @@ private def expandDoIf? (stx : Syntax) : MacroM (Option Syntax) := match stx wit
return some e
| _ => pure none
/--
If the given syntax is a `doLetExpr` or `doLetMetaExpr`, return an equivalent `doIf` that has an `else` but no `else if`s or `if let`s. -/
private def expandDoLetExpr? (stx : Syntax) (doElems : List Syntax) : MacroM (Option Syntax) := match stx with
| `(doElem| let_expr $pat:matchExprPat := $discr:term | $elseBranch:doSeq) =>
return some ( `(doElem| match_expr (meta := false) $discr:term with
| $pat:matchExprPat => $(mkDoSeq doElems.toArray)
| _ => $elseBranch))
| `(doElem| let_expr $pat:matchExprPat $discr:term | $elseBranch:doSeq) =>
return some ( `(doElem| match_expr $discr:term with
| $pat:matchExprPat => $(mkDoSeq doElems.toArray)
| _ => $elseBranch))
| _ => return none
structure DoIfView where
ref : Syntax
optIdent : Syntax
@@ -1149,26 +1077,10 @@ where
let mut termAlts := #[]
for alt in alts do
let rhs toTerm alt.rhs
let termAlt := mkNode ``Parser.Term.matchAlt #[mkAtomFrom alt.ref "|", mkNullNode #[alt.patterns], mkAtomFrom alt.ref "=>", rhs]
let termAlt := mkNode `Lean.Parser.Term.matchAlt #[mkAtomFrom alt.ref "|", mkNullNode #[alt.patterns], mkAtomFrom alt.ref "=>", rhs]
termAlts := termAlts.push termAlt
let termMatchAlts := mkNode ``Parser.Term.matchAlts #[mkNullNode termAlts]
return mkNode ``Parser.Term.«match» #[mkAtomFrom ref "match", genParam, optMotive, discrs, mkAtomFrom ref "with", termMatchAlts]
| .matchExpr ref meta d alts elseBranch => withFreshMacroScope do
let d' `(discr)
let mut termAlts := #[]
for alt in alts do
let rhs `(($( toTerm alt.rhs) : $(( read).m) _))
let optVar := if let some var := alt.var? then mkNullNode #[var, mkAtomFrom var "@"] else mkNullNode #[]
let pat := mkNode ``Parser.Term.matchExprPat #[optVar, alt.funName, mkNullNode alt.pvars]
let termAlt := mkNode ``Parser.Term.matchExprAlt #[mkAtomFrom alt.ref "|", pat, mkAtomFrom alt.ref "=>", rhs]
termAlts := termAlts.push termAlt
let elseBranch := mkNode ``Parser.Term.matchExprElseAlt #[mkAtomFrom ref "|", mkHole ref, mkAtomFrom ref "=>", ( toTerm elseBranch)]
let termMatchExprAlts := mkNode ``Parser.Term.matchExprAlts #[mkNullNode termAlts, elseBranch]
let body := mkNode ``Parser.Term.matchExpr #[mkAtomFrom ref "match_expr", d', mkAtomFrom ref "with", termMatchExprAlts]
if meta then
`(Bind.bind (instantiateMVarsIfMVarApp $d) fun discr => $body)
else
`(let discr := $d; $body)
let termMatchAlts := mkNode `Lean.Parser.Term.matchAlts #[mkNullNode termAlts]
return mkNode `Lean.Parser.Term.«match» #[mkAtomFrom ref "match", genParam, optMotive, discrs, mkAtomFrom ref "with", termMatchAlts]
def run (code : Code) (m : Syntax) (returnType : Syntax) (uvars : Array Var := #[]) (kind := Kind.regular) : MacroM Syntax :=
toTerm code { m, returnType, kind, uvars }
@@ -1621,24 +1533,6 @@ mutual
let matchCode mkMatch ref genParam discrs optMotive alts
concatWith matchCode doElems
/-- Generate `CodeBlock` for `doMatchExpr; doElems` -/
partial def doMatchExprToCode (doMatchExpr : Syntax) (doElems: List Syntax) : M CodeBlock := do
let ref := doMatchExpr
let meta := doMatchExpr[1].isNone
let discr := doMatchExpr[2]
let alts := doMatchExpr[4][0].getArgs -- Array of `doMatchExprAlt`
let alts alts.mapM fun alt => do
let pat := alt[1]
let var? := if pat[0].isNone then none else some pat[0][0]
let funName := pat[1]
let pvars := pat[2].getArgs
let rhs := alt[3]
let rhs doSeqToCode (getDoSeqElems rhs)
pure { ref, var?, funName, pvars, rhs }
let elseBranch doSeqToCode (getDoSeqElems doMatchExpr[4][1][3])
let matchCode mkMatchExpr ref meta discr alts elseBranch
concatWith matchCode doElems
/--
Generate `CodeBlock` for `doTry; doElems`
```
@@ -1708,9 +1602,6 @@ mutual
| none =>
match ( liftMacroM <| expandDoIf? doElem) with
| some doElem => doSeqToCode (doElem::doElems)
| none =>
match ( liftMacroM <| expandDoLetExpr? doElem doElems) with
| some doElem => doSeqToCode [doElem]
| none =>
let (liftedDoElems, doElem) expandLiftMethod doElem
if !liftedDoElems.isEmpty then
@@ -1749,8 +1640,6 @@ mutual
doForToCode doElem doElems
else if k == ``Parser.Term.doMatch then
doMatchToCode doElem doElems
else if k == ``Parser.Term.doMatchExpr then
doMatchExprToCode doElem doElems
else if k == ``Parser.Term.doTry then
doTryToCode doElem doElems
else if k == ``Parser.Term.doBreak then

View File

@@ -488,10 +488,8 @@ def elabBinRelCore (noProp : Bool) (stx : Syntax) (expectedType? : Option Expr)
```
We can improve this failure in the future by applying default instances before reporting a type mismatch.
-/
let lhsStx := stx[2]
let rhsStx := stx[3]
let lhs withRef lhsStx <| toTree lhsStx
let rhs withRef rhsStx <| toTree rhsStx
let lhs withRef stx[2] <| toTree stx[2]
let rhs withRef stx[3] <| toTree stx[3]
let tree := .binop stx .regular f lhs rhs
let r analyze tree none
trace[Elab.binrel] "hasUncomparable: {r.hasUncomparable}, maxType: {r.max?}"
@@ -499,10 +497,10 @@ def elabBinRelCore (noProp : Bool) (stx : Syntax) (expectedType? : Option Expr)
-- Use default elaboration strategy + `toBoolIfNecessary`
let lhs toExprCore lhs
let rhs toExprCore rhs
let lhs withRef lhsStx <| toBoolIfNecessary lhs
let rhs withRef rhsStx <| toBoolIfNecessary rhs
let lhs toBoolIfNecessary lhs
let rhs toBoolIfNecessary rhs
let lhsType inferType lhs
let rhs withRef rhsStx <| ensureHasType lhsType rhs
let rhs ensureHasType lhsType rhs
elabAppArgs f #[] #[Arg.expr lhs, Arg.expr rhs] expectedType? (explicit := false) (ellipsis := false) (resultIsOutParamSupport := false)
else
let mut maxType := r.max?.get!

View File

@@ -1,217 +0,0 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Elab.Term
namespace Lean.Elab.Term
namespace MatchExpr
/--
`match_expr` alternative. Recall that it has the following structure.
```
| (ident "@")? ident bindeIdent* => rhs
```
Example:
```
| c@Eq _ a b => f c a b
```
-/
structure Alt where
/--
`some c` if there is a variable binding to the function symbol being matched.
`c` is the variable name.
-/
var? : Option Ident
/-- Function being matched. -/
funName : Ident
/-- Pattern variables. The list uses `none` for representing `_`, and `some a` for pattern variable `a`. -/
pvars : List (Option Ident)
/-- right-hand-side for the alternative. -/
rhs : Syntax
/-- Store the auxliary continuation function for each right-hand-side. -/
k : Ident := .missing
/-- Actual value to be passed as an argument. -/
actuals : List Term := []
/--
`match_expr` else-alternative. Recall that it has the following structure.
```
| _ => rhs
```
-/
structure ElseAlt where
rhs : Syntax
open Parser Term
/--
Converts syntax representing a `match_expr` else-alternative into an `ElseAlt`.
-/
def toElseAlt? (stx : Syntax) : Option ElseAlt :=
if !stx.isOfKind ``matchExprElseAlt then none else
some { rhs := stx[3] }
/--
Converts syntax representing a `match_expr` alternative into an `Alt`.
-/
def toAlt? (stx : Syntax) : Option Alt :=
if !stx.isOfKind ``matchExprAlt then none else
match stx[1] with
| `(matchExprPat| $[$var? @]? $funName:ident $pvars*) =>
let pvars := pvars.toList.reverse.map fun arg =>
match arg.raw with
| `(_) => none
| _ => some arg
let rhs := stx[3]
some { var?, funName, pvars, rhs }
| _ => none
/--
Returns the function names of alternatives that do not have any pattern variable left.
-/
def getFunNamesToMatch (alts : List Alt) : List Ident := Id.run do
let mut funNames := #[]
for alt in alts do
if alt.pvars.isEmpty then
if Option.isNone <| funNames.find? fun funName => funName.getId == alt.funName.getId then
funNames := funNames.push alt.funName
return funNames.toList
/--
Returns `true` if there is at least one alternative whose next pattern variable is not a `_`.
-/
def shouldSaveActual (alts : List Alt) : Bool :=
alts.any fun alt => alt.pvars matches some _ :: _
/--
Returns the first alternative whose function name is `funName` **and**
does not have pattern variables left to match.
-/
def getAltFor? (alts : List Alt) (funName : Ident) : Option Alt :=
alts.find? fun alt => alt.funName.getId == funName.getId && alt.pvars.isEmpty
/--
Removes alternatives that do not have any pattern variable left to be matched.
For the ones that still have pattern variables, remove the first one, and
save `actual` if the removed pattern variable is not a `_`.
-/
def next (alts : List Alt) (actual : Term) : List Alt :=
alts.filterMap fun alt =>
if let some _ :: pvars := alt.pvars then
some { alt with pvars, actuals := actual :: alt.actuals }
else if let none :: pvars := alt.pvars then
some { alt with pvars }
else
none
/--
Creates a fresh identifier for representing the continuation function used to
execute the RHS of the given alternative, and stores it in the field `k`.
-/
def initK (alt : Alt) : MacroM Alt := withFreshMacroScope do
-- Remark: the compiler frontend implemented in C++ currently detects jointpoints created by
-- the "do" notation by testing the name. See hack at method `visit_let` at `lcnf.cpp`
-- We will remove this hack when we re-implement the compiler frontend in Lean.
let k : Ident `(__do_jp)
return { alt with k }
/--
Generates parameters for the continuation function used to execute
the RHS of the given alternative.
-/
def getParams (alt : Alt) : MacroM (Array (TSyntax ``bracketedBinder)) := do
let mut params := #[]
if let some var := alt.var? then
params := params.push ( `(bracketedBinderF| ($var : Expr)))
params := params ++ ( alt.pvars.toArray.reverse.filterMapM fun
| none => return none
| some arg => return some ( `(bracketedBinderF| ($arg : Expr))))
if params.isEmpty then
return #[( `(bracketedBinderF| (_ : Unit)))]
return params
/--
Generates the actual arguments for invoking the auxiliary continuation function
associated with the given alternative. The arguments are the actuals stored in `alt`.
`discr` is also an argument if `alt.var?` is not none.
-/
def getActuals (discr : Term) (alt : Alt) : MacroM (Array Term) := do
let mut actuals := #[]
if alt.var?.isSome then
actuals := actuals.push discr
actuals := actuals ++ alt.actuals.toArray
if actuals.isEmpty then
return #[ `(())]
return actuals
def toDoubleQuotedName (ident : Ident) : Term :=
mkNode ``Parser.Term.doubleQuotedName #[mkAtom "`", mkAtom "`", ident]
/--
Generates an `if-then-else` tree for implementing a `match_expr` with discriminant `discr`,
alternatives `alts`, and else-alternative `elseAlt`.
-/
partial def generate (discr : Term) (alts : List Alt) (elseAlt : ElseAlt) : MacroM Syntax := do
let alts alts.mapM initK
let discr' `(__discr)
-- Remark: the compiler frontend implemented in C++ currently detects jointpoints created by
-- the "do" notation by testing the name. See hack at method `visit_let` at `lcnf.cpp`
-- We will remove this hack when we re-implement the compiler frontend in Lean.
let kElse `(__do_jp)
let rec loop (discr : Term) (alts : List Alt) : MacroM Term := withFreshMacroScope do
let funNamesToMatch := getFunNamesToMatch alts
let saveActual := shouldSaveActual alts
let actual if saveActual then `(a) else `(_)
let altsNext := next alts actual
let body if altsNext.isEmpty then
`($kElse ())
else
let discr' `(__discr)
let body loop discr' altsNext
if saveActual then
`(if h : ($discr).isApp then let a := Expr.appArg $discr h; let __discr := Expr.appFnCleanup $discr h; $body else $kElse ())
else
`(if h : ($discr).isApp then let __discr := Expr.appFnCleanup $discr h; $body else $kElse ())
let mut result := body
for funName in funNamesToMatch do
if let some alt := getAltFor? alts funName then
let actuals getActuals discr alt
result `(if ($discr).isConstOf $(toDoubleQuotedName funName) then $alt.k $actuals* else $result)
return result
let body loop discr' alts
let mut result `(let_delayed __do_jp (_ : Unit) := $(elseAlt.rhs):term; let __discr := Expr.cleanupAnnotations $discr:term; $body:term)
for alt in alts do
let params getParams alt
result `(let_delayed $alt.k:ident $params:bracketedBinder* := $(alt.rhs):term; $result:term)
return result
def main (discr : Term) (alts : Array Syntax) (elseAlt : Syntax) : MacroM Syntax := do
let alts alts.toList.mapM fun alt =>
if let some alt := toAlt? alt then
pure alt
else
Macro.throwErrorAt alt "unexpected `match_expr` alternative"
let some elseAlt := toElseAlt? elseAlt
| Macro.throwErrorAt elseAlt "unexpected `match_expr` else-alternative"
generate discr alts elseAlt
end MatchExpr
@[builtin_macro Lean.Parser.Term.matchExpr] def expandMatchExpr : Macro := fun stx =>
match stx with
| `(match_expr $discr:term with $alts) =>
MatchExpr.main discr alts.raw[0].getArgs alts.raw[1]
| _ => Macro.throwUnsupported
@[builtin_macro Lean.Parser.Term.letExpr] def expandLetExpr : Macro := fun stx =>
match stx with
| `(let_expr $pat:matchExprPat := $discr:term | $elseBranch:term; $body:term) =>
`(match_expr $discr with
| $pat:matchExprPat => $body
| _ => $elseBranch)
| _ => Macro.throwUnsupported
end Lean.Elab.Term

View File

@@ -121,7 +121,8 @@ def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLC
preDefs.forM (·.termination.ensureNone "partial")
else
try
let hasHints := preDefs.any fun preDef => preDef.termination.isNotNone
let hasHints := preDefs.any fun preDef =>
preDef.termination.decreasing_by?.isSome || preDef.termination.termination_by?.isSome
if hasHints then
wfRecursion preDefs
else

View File

@@ -8,7 +8,6 @@ import Lean.Util.HasConstCache
import Lean.Meta.Match.MatcherApp.Transform
import Lean.Meta.Tactic.Cleanup
import Lean.Meta.Tactic.Refl
import Lean.Meta.Tactic.TryThis
import Lean.Elab.Quotation
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
@@ -703,19 +702,17 @@ def guessLex (preDefs : Array PreDefinition) (unaryPreDef : PreDefinition)
-- Collect all recursive calls and extract their context
let recCalls collectRecCalls unaryPreDef fixedPrefixSize arities
let recCalls := filterSubsumed recCalls
let rcs recCalls.mapM (RecCallCache.mk (preDefs.map (·.termination.decreasingBy?)) ·)
let rcs recCalls.mapM (RecCallCache.mk (preDefs.map (·.termination.decreasing_by?)) ·)
let callMatrix := rcs.map (inspectCall ·)
match liftMetaM <| solve measures callMatrix with
| .some solution => do
let wf buildTermWF originalVarNamess varNamess solution
let wf' := trimTermWF extraParamss wf
for preDef in preDefs, term in wf' do
if showInferredTerminationBy.get ( getOptions) then
logInfoAt preDef.ref m!"Inferred termination argument:\n{← term.unexpand}"
if let some ref := preDef.termination.terminationBy?? then
Tactic.TryThis.addSuggestion ref ( term.unexpand)
if showInferredTerminationBy.get ( getOptions) then
let wf' := trimTermWF extraParamss wf
for preDef in preDefs, term in wf' do
logInfoAt preDef.ref m!"Inferred termination argument: {← term.unexpand}"
return wf
| .none =>

View File

@@ -94,12 +94,12 @@ def wfRecursion (preDefs : Array PreDefinition) : TermElabM Unit := do
return ( packMutual fixedPrefixSize preDefs unaryPreDefs, fixedPrefixSize)
let wf do
let (preDefsWith, preDefsWithout) := preDefs.partition (·.termination.terminationBy?.isSome)
let (preDefsWith, preDefsWithout) := preDefs.partition (·.termination.termination_by?.isSome)
if preDefsWith.isEmpty then
-- No termination_by anywhere, so guess one
guessLex preDefs unaryPreDef fixedPrefixSize
else if preDefsWithout.isEmpty then
pure <| preDefsWith.map (·.termination.terminationBy?.get!)
pure <| preDefsWith.map (·.termination.termination_by?.get!)
else
-- Some have, some do not, so report errors
preDefsWithout.forM fun preDef => do
@@ -114,7 +114,7 @@ def wfRecursion (preDefs : Array PreDefinition) : TermElabM Unit := do
trace[Elab.definition.wf] "wfRel: {wfRel}"
let (value, envNew) withoutModifyingEnv' do
addAsAxiom unaryPreDef
let value mkFix unaryPreDef prefixArgs wfRel (preDefs.map (·.termination.decreasingBy?))
let value mkFix unaryPreDef prefixArgs wfRel (preDefs.map (·.termination.decreasing_by?))
eraseRecAppSyntaxExpr value
/- `mkFix` invokes `decreasing_tactic` which may add auxiliary theorems to the environment. -/
let value unfoldDeclsFrom envNew value

View File

@@ -27,7 +27,7 @@ structure TerminationBy where
deriving Inhabited
open Parser.Termination in
def TerminationBy.unexpand (wf : TerminationBy) : MetaM (TSyntax ``terminationBy) := do
def TerminationBy.unexpand (wf : TerminationBy) : MetaM Syntax := do
-- TODO: Why can I not just use $wf.vars in the quotation below?
let vars : TSyntaxArray `ident := wf.vars.map (·.raw)
if vars.isEmpty then
@@ -50,9 +50,8 @@ is what `Term.runTactic` expects.
-/
structure TerminationHints where
ref : Syntax
terminationBy?? : Option Syntax
terminationBy? : Option TerminationBy
decreasingBy? : Option DecreasingBy
termination_by? : Option TerminationBy
decreasing_by? : Option DecreasingBy
/-- Here we record the number of parameters past the `:`. It is set by
`TerminationHints.rememberExtraParams` and used as folows:
@@ -64,27 +63,19 @@ structure TerminationHints where
extraParams : Nat
deriving Inhabited
def TerminationHints.none : TerminationHints := .missing, .none, .none, .none, 0
def TerminationHints.none : TerminationHints := .missing, .none, .none, 0
/-- Logs warnings when the `TerminationHints` are present. -/
def TerminationHints.ensureNone (hints : TerminationHints) (reason : String): CoreM Unit := do
match hints.terminationBy??, hints.terminationBy?, hints.decreasingBy? with
| .none, .none, .none => pure ()
| .none, .none, .some dec_by =>
match hints.termination_by?, hints.decreasing_by? with
| .none, .none => pure ()
| .none, .some dec_by =>
logErrorAt dec_by.ref m!"unused `decreasing_by`, function is {reason}"
| .some term_by?, .none, .none =>
logErrorAt term_by? m!"unused `termination_by?`, function is {reason}"
| .none, .some term_by, .none =>
| .some term_by, .none =>
logErrorAt term_by.ref m!"unused `termination_by`, function is {reason}"
| _, _, _ =>
| .some _, .some _ =>
logErrorAt hints.ref m!"unused termination hints, function is {reason}"
/-- True if any form of termination hint is present. -/
def TerminationHints.isNotNone (hints : TerminationHints) : Bool :=
hints.terminationBy??.isSome ||
hints.terminationBy?.isSome ||
hints.decreasingBy?.isSome
/--
Remembers `extraParams` for later use. Needs to happen early enough where we still know
how many parameters came from the function header (`headerParams`).
@@ -120,23 +111,19 @@ def elabTerminationHints {m} [Monad m] [MonadError m] (stx : TSyntax ``suffix) :
if let .missing := stx.raw then
return { TerminationHints.none with ref := stx }
match stx with
| `(suffix| $[$t?]? $[$d?:decreasingBy]? ) => do
let terminationBy?? : Option Syntax if let some t := t? then match t with
| `(terminationBy?|termination_by?) => pure (some t)
| _ => pure none
else pure none
let terminationBy? : Option TerminationBy if let some t := t? then match t with
| `(terminationBy|termination_by => $_body) =>
throwErrorAt t "no extra parameters bounds, please omit the `=>`"
| `(terminationBy|termination_by $vars* => $body) => pure (some {ref := t, vars, body})
| `(terminationBy|termination_by $body:term) => pure (some {ref := t, vars := #[], body})
| `(terminationBy?|termination_by?) => pure none
| `(suffix| $[$t?:terminationBy]? $[$d?:decreasingBy]? ) => do
let termination_by? t?.mapM fun t => match t with
| `(terminationBy|termination_by $vars* => $body) =>
if vars.isEmpty then
throwErrorAt t "no extra parameters bounds, please omit the `=>`"
else
pure {ref := t, vars, body}
| `(terminationBy|termination_by $body:term) => pure {ref := t, vars := #[], body}
| _ => throwErrorAt t "unexpected `termination_by` syntax"
else pure none
let decreasingBy? d?.mapM fun d => match d with
let decreasing_by? d?.mapM fun d => match d with
| `(decreasingBy|decreasing_by $tactic) => pure {ref := d, tactic}
| _ => throwErrorAt d "unexpected `decreasing_by` syntax"
return { ref := stx, terminationBy??, terminationBy?, decreasingBy?, extraParams := 0 }
return { ref := stx, termination_by?, decreasing_by?, extraParams := 0 }
| _ => throwErrorAt stx s!"Unexpected Termination.suffix syntax: {stx} of kind {stx.raw.getKind}"
end Lean.Elab.WF

View File

@@ -5,7 +5,6 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Util.FoldConsts
import Lean.Meta.Eqns
import Lean.Elab.Command
namespace Lean.Elab.Command
@@ -129,18 +128,4 @@ private def printAxiomsOf (constName : Name) : CommandElabM Unit := do
cs.forM printAxiomsOf
| _ => throwUnsupportedSyntax
private def printEqnsOf (constName : Name) : CommandElabM Unit := do
let some eqns liftTermElabM <| Meta.getEqnsFor? constName (nonRec := true) |
logInfo m!"'{constName}' does not have equations"
let mut m := m!"equations:"
for eq in eqns do
let cinfo getConstInfo eq
m := m ++ Format.line ++ ( mkHeader "theorem" eq cinfo.levelParams cinfo.type .safe)
logInfo m
@[builtin_command_elab «printEqns»] def elabPrintEqns : CommandElab := fun stx => do
let id := stx[2]
let cs resolveGlobalConstWithInfos id
cs.forM printEqnsOf
end Lean.Elab.Command

View File

@@ -802,8 +802,10 @@ partial def mkDefaultValueAux? (struct : Struct) : Expr → TermElabM (Option Ex
let arg mkFreshExprMVar d
mkDefaultValueAux? struct (b.instantiate1 arg)
| e =>
let_expr id _ a := e | return some e
return some a
if e.isAppOfArity ``id 2 then
return some e.appArg!
else
return some e
def mkDefaultValue? (struct : Struct) (cinfo : ConstantInfo) : TermElabM (Option Expr) :=
withRef struct.ref do

View File

@@ -37,4 +37,3 @@ import Lean.Elab.Tactic.NormCast
import Lean.Elab.Tactic.Symm
import Lean.Elab.Tactic.SolveByElim
import Lean.Elab.Tactic.LibrarySearch
import Lean.Elab.Tactic.ShowTerm

View File

@@ -352,21 +352,12 @@ def renameInaccessibles (mvarId : MVarId) (hs : TSyntaxArray ``binderIdent) : Ta
let mut info := #[]
let mut found : NameSet := {}
let n := lctx.numIndices
-- hypotheses are inaccessible if their scopes are different from the caller's (we assume that
-- the scopes are the same for all the hypotheses in `hs`, which is reasonable to expect in
-- practice and otherwise the expected semantics of `rename_i` really are not clear)
let some callerScopes := hs.findSome? (fun
| `(binderIdent| $h:ident) => some <| extractMacroScopes h.getId
| _ => none)
| return mvarId
for i in [:n] do
let j := n - i - 1
match lctx.getAt? j with
| none => pure ()
| some localDecl =>
let inaccessible := !(extractMacroScopes localDecl.userName |>.equalScope callerScopes)
let shadowed := found.contains localDecl.userName
if inaccessible || shadowed then
if localDecl.userName.hasMacroScopes || found.contains localDecl.userName then
if let `(binderIdent| $h:ident) := hs.back then
let newName := h.getId
lctx := lctx.setUserName localDecl.fvarId newName

View File

@@ -68,7 +68,7 @@ def mkEvalRflProof (e : Expr) (lc : LinearCombo) : OmegaM Expr := do
`e = (coordinate n).eval atoms`. -/
def mkCoordinateEvalAtomsEq (e : Expr) (n : Nat) : OmegaM Expr := do
if n < 10 then
let atoms atoms
let atoms := ( getThe State).atoms
let tail mkListLit (.const ``Int []) atoms[n+1:].toArray.toList
let lem := .str ``LinearCombo s!"coordinate_eval_{n}"
mkEqSymm (mkAppN (.const lem []) (atoms[:n+1].toArray.push tail))
@@ -358,7 +358,6 @@ def addIntInequality (p : MetaProblem) (h y : Expr) : OmegaM MetaProblem := do
/-- Given a fact `h` with type `¬ P`, return a more useful fact obtained by pushing the negation. -/
def pushNot (h P : Expr) : MetaM (Option Expr) := do
let P whnfR P
trace[omega] "pushing negation: {P}"
match P with
| .forallE _ t b _ =>
if ( isProp t) && ( isProp b) then
@@ -367,42 +366,43 @@ def pushNot (h P : Expr) : MetaM (Option Expr) := do
else
return none
| .app _ _ =>
match_expr P with
| LT.lt α _ x y => match_expr α with
| Nat => return some (mkApp3 (.const ``Nat.le_of_not_lt []) x y h)
| Int => return some (mkApp3 (.const ``Int.le_of_not_lt []) x y h)
| Fin n => return some (mkApp4 (.const ``Fin.le_of_not_lt []) n x y h)
| _ => return none
| LE.le α _ x y => match_expr α with
| Nat => return some (mkApp3 (.const ``Nat.lt_of_not_le []) x y h)
| Int => return some (mkApp3 (.const ``Int.lt_of_not_le []) x y h)
| Fin n => return some (mkApp4 (.const ``Fin.lt_of_not_le []) n x y h)
| _ => return none
| Eq α x y => match_expr α with
| Nat => return some (mkApp3 (.const ``Nat.lt_or_gt_of_ne []) x y h)
| Int => return some (mkApp3 (.const ``Int.lt_or_gt_of_ne []) x y h)
| Fin n => return some (mkApp4 (.const ``Fin.lt_or_gt_of_ne []) n x y h)
| _ => return none
| Dvd.dvd α _ k x => match_expr α with
| Nat => return some (mkApp3 (.const ``Nat.emod_pos_of_not_dvd []) k x h)
| Int =>
-- This introduces a disjunction that could be avoided by checking `k ≠ 0`.
return some (mkApp3 (.const ``Int.emod_pos_of_not_dvd []) k x h)
| _ => return none
| Prod.Lex _ _ _ _ _ _ => return some ( mkAppM ``Prod.of_not_lex #[h])
| Not P =>
return some (mkApp3 (.const ``Decidable.of_not_not []) P
(.app (.const ``Classical.propDecidable []) P) h)
| And P Q =>
return some (mkApp5 (.const ``Decidable.or_not_not_of_not_and []) P Q
(.app (.const ``Classical.propDecidable []) P)
(.app (.const ``Classical.propDecidable []) Q) h)
| Or P Q =>
return some (mkApp3 (.const ``and_not_not_of_not_or []) P Q h)
| Iff P Q =>
return some (mkApp5 (.const ``Decidable.and_not_or_not_and_of_not_iff []) P Q
(.app (.const ``Classical.propDecidable []) P)
(.app (.const ``Classical.propDecidable []) Q) h)
match P.getAppFnArgs with
| (``LT.lt, #[.const ``Int [], _, x, y]) =>
return some (mkApp3 (.const ``Int.le_of_not_lt []) x y h)
| (``LE.le, #[.const ``Int [], _, x, y]) =>
return some (mkApp3 (.const ``Int.lt_of_not_le []) x y h)
| (``LT.lt, #[.const ``Nat [], _, x, y]) =>
return some (mkApp3 (.const ``Nat.le_of_not_lt []) x y h)
| (``LE.le, #[.const ``Nat [], _, x, y]) =>
return some (mkApp3 (.const ``Nat.lt_of_not_le []) x y h)
| (``LT.lt, #[.app (.const ``Fin []) n, _, x, y]) =>
return some (mkApp4 (.const ``Fin.le_of_not_lt []) n x y h)
| (``LE.le, #[.app (.const ``Fin []) n, _, x, y]) =>
return some (mkApp4 (.const ``Fin.lt_of_not_le []) n x y h)
| (``Eq, #[.const ``Nat [], x, y]) =>
return some (mkApp3 (.const ``Nat.lt_or_gt_of_ne []) x y h)
| (``Eq, #[.const ``Int [], x, y]) =>
return some (mkApp3 (.const ``Int.lt_or_gt_of_ne []) x y h)
| (``Prod.Lex, _) => return some ( mkAppM ``Prod.of_not_lex #[h])
| (``Eq, #[.app (.const ``Fin []) n, x, y]) =>
return some (mkApp4 (.const ``Fin.lt_or_gt_of_ne []) n x y h)
| (``Dvd.dvd, #[.const ``Nat [], _, k, x]) =>
return some (mkApp3 (.const ``Nat.emod_pos_of_not_dvd []) k x h)
| (``Dvd.dvd, #[.const ``Int [], _, k, x]) =>
-- This introduces a disjunction that could be avoided by checking `k ≠ 0`.
return some (mkApp3 (.const ``Int.emod_pos_of_not_dvd []) k x h)
| (``Or, #[P₁, P₂]) => return some (mkApp3 (.const ``and_not_not_of_not_or []) P P₂ h)
| (``And, #[P₁, P₂]) =>
return some (mkApp5 (.const ``Decidable.or_not_not_of_not_and []) P P₂
(.app (.const ``Classical.propDecidable []) P)
(.app (.const ``Classical.propDecidable []) P₂) h)
| (``Not, #[P']) =>
return some (mkApp3 (.const ``Decidable.of_not_not []) P'
(.app (.const ``Classical.propDecidable []) P') h)
| (``Iff, #[P₁, P]) =>
return some (mkApp5 (.const ``Decidable.and_not_or_not_and_of_not_iff []) P P₂
(.app (.const ``Classical.propDecidable []) P₁)
(.app (.const ``Classical.propDecidable []) P₂) h)
| _ => return none
| _ => return none

View File

@@ -5,8 +5,8 @@ Authors: Scott Morrison
-/
prelude
import Init.BinderPredicates
import Init.Data.Option.Lemmas
import Init.Data.Nat.Bitwise.Lemmas
import Init.Data.List
import Init.Data.Option
/-!
# `List.nonzeroMinimum`, `List.minNatAbs`, `List.maxNatAbs`

View File

@@ -51,7 +51,7 @@ structure Context where
/-- The internal state for the `OmegaM` monad, recording previously encountered atoms. -/
structure State where
/-- The atoms up-to-defeq encountered so far. -/
atoms : HashMap Expr Nat := {}
atoms : Array Expr := #[]
/-- An intermediate layer in the `OmegaM` monad. -/
abbrev OmegaM' := StateRefT State (ReaderT Context MetaM)
@@ -76,11 +76,10 @@ def OmegaM.run (m : OmegaM α) (cfg : OmegaConfig) : MetaM α :=
def cfg : OmegaM OmegaConfig := do pure ( read).cfg
/-- Retrieve the list of atoms. -/
def atoms : OmegaM (Array Expr) := do
return ( getThe State).atoms.toArray.qsort (·.2 < ·.2) |>.map (·.1)
def atoms : OmegaM (List Expr) := return ( getThe State).atoms.toList
/-- Return the `Expr` representing the list of atoms. -/
def atomsList : OmegaM Expr := do mkListLit (.const ``Int []) ( atoms).toList
def atomsList : OmegaM Expr := do mkListLit (.const ``Int []) ( atoms)
/-- Return the `Expr` representing the list of atoms as a `Coeffs`. -/
def atomsCoeffs : OmegaM Expr := do
@@ -244,16 +243,15 @@ Return its index, and, if it is new, a collection of interesting facts about the
-/
def lookup (e : Expr) : OmegaM (Nat × Option (HashSet Expr)) := do
let c getThe State
match c.atoms.find? e with
| some i => return (i, none)
| none =>
for h : i in [:c.atoms.size] do
if isDefEq e c.atoms[i] then
return (i, none)
trace[omega] "New atom: {e}"
let facts analyzeAtom e
if isTracingEnabledFor `omega then
unless facts.isEmpty do
trace[omega] "New facts: {← facts.toList.mapM fun e => inferType e}"
let i modifyGetThe State fun c =>
(c.atoms.size, { c with atoms := c.atoms.insert e c.atoms.size })
let i modifyGetThe State fun c => (c.atoms.size, { c with atoms := c.atoms.push e })
return (i, some facts)
end Omega

View File

@@ -1,28 +0,0 @@
/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Mario Carneiro
-/
prelude
import Lean.Elab.ElabRules
import Lean.Meta.Tactic.TryThis
namespace Std.Tactic
open Lean Elab Term Tactic Meta.Tactic.TryThis Parser.Tactic
@[builtin_tactic showTerm] def evalShowTerm : Tactic := fun stx =>
match stx with
| `(tactic| show_term%$tk $t) => withMainContext do
let g getMainGoal
evalTactic t
addExactSuggestion tk ( instantiateMVars (mkMVar g)).headBeta (origSpan? := getRef)
| _ => throwUnsupportedSyntax
/-- Implementation of `show_term` term elaborator. -/
@[builtin_term_elab showTermElabImpl] def elabShowTerm : TermElab
| `(show_term_elab%$tk $t), ty => do
let e Term.elabTermEnsuringType t ty
Term.synthesizeSyntheticMVarsNoPostponing
addTermSuggestion tk ( instantiateMVars e).headBeta (origSpan? := getRef)
pure e
| _, _ => throwUnsupportedSyntax

View File

@@ -353,13 +353,14 @@ def mkSimpOnly (stx : Syntax) (usedSimps : UsedSimps) : MetaM Syntax := do
| true => `(Parser.Tactic.simpLemma| $decl:term)
| false => `(Parser.Tactic.simpLemma| $decl:term)
args := args.push arg
| .fvar fvarId =>
-- local hypotheses in the context
| .fvar fvarId => -- local hypotheses in the context
-- `simp_all` always uses all propositional hypotheses (and it can't use
-- any others). So `simp_all only [h]`, where `h` is a hypothesis, would
-- be redundant. It would also be confusing since it suggests that only
-- `h` is used.
if isSimpAll then
continue
if let some ldecl := lctx.find? fvarId then
-- `simp_all` always uses all propositional hypotheses.
-- So `simp_all only [x]`, only makes sense if `ldecl` is a let-variable.
if isSimpAll && !ldecl.hasValue then
continue
localsOrStar := localsOrStar.bind fun locals =>
if !ldecl.userName.isInaccessibleUserName && !ldecl.userName.hasMacroScopes &&
(lctx.findFromUserName? ldecl.userName).get!.fvarId == ldecl.fvarId then
@@ -434,7 +435,7 @@ where
if tactic.simp.trace.get ( getOptions) then
traceSimpCall stx usedSimps
def dsimpLocation (ctx : Simp.Context) (simprocs : Simp.SimprocsArray) (loc : Location) : TacticM Unit := do
def dsimpLocation (ctx : Simp.Context) (loc : Location) : TacticM Unit := do
match loc with
| Location.targets hyps simplifyTarget =>
withMainContext do
@@ -446,7 +447,7 @@ def dsimpLocation (ctx : Simp.Context) (simprocs : Simp.SimprocsArray) (loc : Lo
where
go (fvarIdsToSimp : Array FVarId) (simplifyTarget : Bool) : TacticM Unit := do
let mvarId getMainGoal
let (result?, usedSimps) dsimpGoal mvarId ctx simprocs (simplifyTarget := simplifyTarget) (fvarIdsToSimp := fvarIdsToSimp)
let (result?, usedSimps) dsimpGoal mvarId ctx (simplifyTarget := simplifyTarget) (fvarIdsToSimp := fvarIdsToSimp)
match result? with
| none => replaceMainGoal []
| some mvarId => replaceMainGoal [mvarId]
@@ -454,8 +455,8 @@ where
mvarId.withContext <| traceSimpCall ( getRef) usedSimps
@[builtin_tactic Lean.Parser.Tactic.dsimp] def evalDSimp : Tactic := fun stx => do
let { ctx, simprocs, .. } withMainContext <| mkSimpContext stx (eraseLocal := false) (kind := .dsimp)
dsimpLocation ctx simprocs (expandOptLocation stx[5])
let { ctx, .. } withMainContext <| mkSimpContext stx (eraseLocal := false) (kind := .dsimp)
dsimpLocation ctx (expandOptLocation stx[5])
end Lean.Elab.Tactic

View File

@@ -25,12 +25,13 @@ def mkSimpCallStx (stx : Syntax) (usedSimps : UsedSimps) : MetaM (TSyntax `tacti
@[builtin_tactic simpTrace] def evalSimpTrace : Tactic := fun stx =>
match stx with
| `(tactic|
simp?%$tk $[!%$bang]? $(config)? $(discharger)? $[only%$o]? $[[$args,*]]? $(loc)?) => withMainContext do
simp?%$tk $[!%$bang]? $(config)? $(discharger)? $[only%$o]? $[[$args,*]]? $(loc)?) => do
let stx if bang.isSome then
`(tactic| simp!%$tk $(config)? $(discharger)? $[only%$o]? $[[$args,*]]? $(loc)?)
else
`(tactic| simp%$tk $(config)? $(discharger)? $[only%$o]? $[[$args,*]]? $(loc)?)
let { ctx, simprocs, dischargeWrapper } mkSimpContext stx (eraseLocal := false)
let { ctx, simprocs, dischargeWrapper }
withMainContext <| mkSimpContext stx (eraseLocal := false)
let usedSimps dischargeWrapper.with fun discharge? =>
simpLocation ctx (simprocs := simprocs) discharge? <|
(loc.map expandLocation).getD (.targets #[] true)

View File

@@ -26,11 +26,10 @@ def elabSimprocKeys (stx : Syntax) : MetaM (Array Meta.SimpTheoremKey) := do
let pattern elabSimprocPattern stx
DiscrTree.mkPath pattern simpDtConfig
def checkSimprocType (declName : Name) : CoreM Bool := do
def checkSimprocType (declName : Name) : CoreM Unit := do
let decl getConstInfo declName
match decl.type with
| .const ``Simproc _ => pure false
| .const ``DSimproc _ => pure true
| .const ``Simproc _ => pure ()
| _ => throwError "unexpected type at '{declName}', 'Simproc' expected"
namespace Command
@@ -39,7 +38,7 @@ namespace Command
let `(simproc_pattern% $pattern => $declName) := stx | throwUnsupportedSyntax
let declName resolveGlobalConstNoOverload declName
liftTermElabM do
discard <| checkSimprocType declName
checkSimprocType declName
let keys elabSimprocKeys pattern
registerSimproc declName keys
@@ -47,10 +46,9 @@ namespace Command
let `(builtin_simproc_pattern% $pattern => $declName) := stx | throwUnsupportedSyntax
let declName resolveGlobalConstNoOverload declName
liftTermElabM do
let dsimp checkSimprocType declName
checkSimprocType declName
let keys elabSimprocKeys pattern
let registerProcName := if dsimp then ``registerBuiltinDSimproc else ``registerBuiltinSimproc
let val := mkAppN (mkConst registerProcName) #[toExpr declName, toExpr keys, mkConst declName]
let val := mkAppN (mkConst ``registerBuiltinSimproc) #[toExpr declName, toExpr keys, mkConst declName]
let initDeclName mkFreshUserName (declName ++ `declare)
declareBuiltin initDeclName val

View File

@@ -24,13 +24,6 @@ def MacroScopesView.format (view : MacroScopesView) (mainModule : Name) : Format
else
view.scopes.foldl Name.mkNum (view.name ++ view.imported ++ view.mainModule)
/--
Two names are from the same lexical scope if their scoping information modulo `MacroScopesView.name`
is equal.
-/
def MacroScopesView.equalScope (a b : MacroScopesView) : Bool :=
a.scopes == b.scopes && a.mainModule == b.mainModule && a.imported == b.imported
namespace Elab
def expandOptNamedPrio (stx : Syntax) : MacroM Nat :=

View File

@@ -69,7 +69,7 @@ protected def throwError [Monad m] [MonadError m] (msg : MessageData) : m α :=
let (ref, msg) AddErrorMessageContext.add ref msg
throw <| Exception.error ref msg
/-- Throw an unknown constant error message. -/
/-- Thrown an unknown constant error message. -/
def throwUnknownConstant [Monad m] [MonadError m] (constName : Name) : m α :=
Lean.throwError m!"unknown constant '{mkConst constName}'"

View File

@@ -801,7 +801,7 @@ def isType0 : Expr → Bool
/-- Return `true` if the given expression is `.sort .zero` -/
def isProp : Expr Bool
| sort .zero => true
| sort (.zero ..) => true
| _ => false
/-- Return `true` if the given expression is a bound variable. -/
@@ -904,14 +904,6 @@ def appArg!' : Expr → Expr
| app _ a => a
| _ => panic! "application expected"
def appArg (e : Expr) (h : e.isApp) : Expr :=
match e, h with
| .app _ a, _ => a
def appFn (e : Expr) (h : e.isApp) : Expr :=
match e, h with
| .app f _, _ => f
def sortLevel! : Expr Level
| sort u => u
| _ => panic! "sort expected"
@@ -1075,6 +1067,33 @@ def isAppOfArity' : Expr → Name → Nat → Bool
| app f _, n, a+1 => isAppOfArity' f n a
| _, _, _ => false
/--
Checks if an expression is a "natural number numeral in normal form",
i.e. of type `Nat`, and explicitly of the form `OfNat.ofNat n`
where `n` matches `.lit (.natVal n)` for some literal natural number `n`.
and if so returns `n`.
-/
-- Note that `Expr.lit (.natVal n)` is not considered in normal form!
def nat? (e : Expr) : Option Nat := do
guard <| e.isAppOfArity ``OfNat.ofNat 3
let lit (.natVal n) := e.appFn!.appArg! | none
n
/--
Checks if an expression is an "integer numeral in normal form",
i.e. of type `Nat` or `Int`, and either a natural number numeral in normal form (as specified by `nat?`),
or the negation of a positive natural number numberal in normal form,
and if so returns the integer.
-/
def int? (e : Expr) : Option Int :=
if e.isAppOfArity ``Neg.neg 3 then
match e.appArg!.nat? with
| none => none
| some 0 => none
| some n => some (-n)
else
e.nat?
private def getAppNumArgsAux : Expr Nat Nat
| app f _, n => getAppNumArgsAux f (n+1)
| _, n => n
@@ -1597,45 +1616,12 @@ partial def cleanupAnnotations (e : Expr) : Expr :=
let e' := e.consumeMData.consumeTypeAnnotations
if e' == e then e else cleanupAnnotations e'
/--
Similar to `appFn`, but also applies `cleanupAnnotations` to resulting function.
This function is used compile the `match_expr` term.
-/
def appFnCleanup (e : Expr) (h : e.isApp) : Expr :=
match e, h with
| .app f _, _ => f.cleanupAnnotations
def isFalse (e : Expr) : Bool :=
e.cleanupAnnotations.isConstOf ``False
def isTrue (e : Expr) : Bool :=
e.cleanupAnnotations.isConstOf ``True
/--
Checks if an expression is a "natural number numeral in normal form",
i.e. of type `Nat`, and explicitly of the form `OfNat.ofNat n`
where `n` matches `.lit (.natVal n)` for some literal natural number `n`.
and if so returns `n`.
-/
-- Note that `Expr.lit (.natVal n)` is not considered in normal form!
def nat? (e : Expr) : Option Nat := do
let_expr OfNat.ofNat _ n _ := e | failure
let lit (.natVal n) := n | failure
n
/--
Checks if an expression is an "integer numeral in normal form",
i.e. of type `Nat` or `Int`, and either a natural number numeral in normal form (as specified by `nat?`),
or the negation of a positive natural number numberal in normal form,
and if so returns the integer.
-/
def int? (e : Expr) : Option Int :=
let_expr Neg.neg _ _ a := e | e.nat?
match a.nat? with
| none => none
| some 0 => none
| some n => some (-n)
/-- Return true iff `e` contains a free variable which satisfies `p`. -/
@[inline] def hasAnyFVar (e : Expr) (p : FVarId Bool) : Bool :=
let rec @[specialize] visit (e : Expr) := if !e.hasFVar then false else

View File

@@ -47,4 +47,3 @@ import Lean.Meta.CoeAttr
import Lean.Meta.Iterator
import Lean.Meta.LazyDiscrTree
import Lean.Meta.LitValues
import Lean.Meta.CheckTactic

View File

@@ -254,7 +254,7 @@ structure PostponedEntry where
ref : Syntax
lhs : Level
rhs : Level
/-- Context for the surrounding `isDefEq` call when the entry was created. -/
/-- Context for the surrounding `isDefEq` call when entry was created. -/
ctx? : Option DefEqContext
deriving Inhabited
@@ -264,7 +264,7 @@ structure PostponedEntry where
structure State where
mctx : MetavarContext := {}
cache : Cache := {}
/-- When `trackZetaDelta == true`, then any let-decl free variable that is zetaDelta-expanded by `MetaM` is stored in `zetaDeltaFVarIds`. -/
/-- When `trackZetaDelta == true`, then any let-decl free variable that is zetaDelta expansion performed by `MetaM` is stored in `zetaDeltaFVarIds`. -/
zetaDeltaFVarIds : FVarIdSet := {}
/-- Array of postponed universe level constraints -/
postponed : PersistentArray PostponedEntry := {}
@@ -1347,16 +1347,6 @@ private def withNewMCtxDepthImp (allowLevelAssignments : Bool) (x : MetaM α) :
finally
modify fun s => { s with mctx := saved.mctx, postponed := saved.postponed }
/--
Removes `fvarId` from the local context, and replaces occurrences of it with `e`.
It is the responsibility of the caller to ensure that `e` is well-typed in the context
of any occurrence of `fvarId`.
-/
def withReplaceFVarId {α} (fvarId : FVarId) (e : Expr) : MetaM α MetaM α :=
withReader fun ctx => { ctx with
lctx := ctx.lctx.replaceFVarId fvarId e
localInstances := ctx.localInstances.erase fvarId }
/--
`withNewMCtxDepth k` executes `k` with a higher metavariable context depth,
where metavariables created outside the `withNewMCtxDepth` (with a lower depth) cannot be assigned.
@@ -1747,15 +1737,6 @@ def isDefEqNoConstantApprox (t s : Expr) : MetaM Bool :=
def etaExpand (e : Expr) : MetaM Expr :=
withDefault do forallTelescopeReducing ( inferType e) fun xs _ => mkLambdaFVars xs (mkAppN e xs)
/--
If `e` is of the form `?m ...` instantiate metavars
-/
def instantiateMVarsIfMVarApp (e : Expr) : MetaM Expr := do
if e.getAppFn.isMVar then
instantiateMVars e
else
return e
end Meta
builtin_initialize

View File

@@ -1,24 +0,0 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix
-/
prelude
import Lean.Meta.Basic
namespace Lean.Meta.CheckTactic
def mkCheckGoalType (val type : Expr) : MetaM Expr := do
let lvl mkFreshLevelMVar
pure <| mkApp2 (mkConst ``CheckGoalType [lvl]) type val
def matchCheckGoalType (stx : Syntax) (goalType : Expr) : MetaM (Expr × Expr × Level) := do
let u mkFreshLevelMVar
let type mkFreshExprMVar (some (.sort u))
let val mkFreshExprMVar (some type)
let extType := mkAppN (.const ``CheckGoalType [u]) #[type, val]
if !( isDefEq goalType extType) then
throwErrorAt stx "Goal{indentExpr goalType}\nis expected to match {indentExpr extType}"
pure (val, type, u)
end Lean.Meta.CheckTactic

View File

@@ -51,8 +51,7 @@ private def shouldGenerateEqnThms (declName : Name) : MetaM Bool := do
return false
structure EqnsExtState where
map : PHashMap Name (Array Name) := {}
mapInv : PHashMap Name Name := {}
map : PHashMap Name (Array Name) := {}
deriving Inhabited
/- We generate the equations on demand, and do not save them on .olean files. -/
@@ -78,22 +77,7 @@ private def mkSimpleEqThm (declName : Name) : MetaM (Option Name) := do
return none
/--
Returns `some declName` if `thmName` is an equational theorem for `declName`.
-/
def isEqnThm? (thmName : Name) : CoreM (Option Name) := do
return eqnsExt.getState ( getEnv) |>.mapInv.find? thmName
/--
Stores in the `eqnsExt` environment extension that `eqThms` are the equational theorems for `declName`
-/
private def registerEqnThms (declName : Name) (eqThms : Array Name) : CoreM Unit := do
modifyEnv fun env => eqnsExt.modifyState env fun s => { s with
map := s.map.insert declName eqThms
mapInv := eqThms.foldl (init := s.mapInv) fun mapInv eqThm => mapInv.insert eqThm declName
}
/--
Returns equation theorems for the given declaration.
Return equation theorems for the given declaration.
By default, we do not create equation theorems for nonrecursive definitions.
You can use `nonRec := true` to override this behavior, a dummy `rfl` proof is created on the fly.
-/
@@ -103,12 +87,12 @@ def getEqnsFor? (declName : Name) (nonRec := false) : MetaM (Option (Array Name)
else if ( shouldGenerateEqnThms declName) then
for f in ( getEqnsFnsRef.get) do
if let some r f declName then
registerEqnThms declName r
modifyEnv fun env => eqnsExt.modifyState env fun s => { s with map := s.map.insert declName r }
return some r
if nonRec then
let some eqThm mkSimpleEqThm declName | return none
let r := #[eqThm]
registerEqnThms declName r
modifyEnv fun env => eqnsExt.modifyState env fun s => { s with map := s.map.insert declName r }
return some r
return none
@@ -147,8 +131,8 @@ def registerGetUnfoldEqnFn (f : GetUnfoldEqnFn) : IO Unit := do
getUnfoldEqnFnsRef.modify (f :: ·)
/--
Return an "unfold" theorem for the given declaration.
By default, we do not create unfold theorems for nonrecursive definitions.
Return a "unfold" theorem for the given declaration.
By default, we not create unfold theorems for nonrecursive definitions.
You can use `nonRec := true` to override this behavior.
-/
def getUnfoldEqnFor? (declName : Name) (nonRec := false) : MetaM (Option Name) := withLCtx {} {} do

View File

@@ -138,11 +138,11 @@ private def viewCoordRaw: Expr → Nat → M Expr
| e , c => throwError "Bad coordinate {c} for {e}"
/-- Given a valid `SubExpr`, return the raw current expression without performing any instantiation.
If the given `SubExpr` has a type subexpression coordinate, then throw an error.
/-- Given a valid SubExpr, will return the raw current expression without performing any instantiation.
If the SubExpr has a type subexpression coordinate then will error.
This is a cheaper version of `Lean.Meta.viewSubexpr` and can be used to quickly view the
subexpression at a position. Note that because the resulting expression may contain
subexpression at a position. Note that because the resulting expression will contain
loose bound variables it can't be used in any `MetaM` methods. -/
def viewSubexpr (p : Pos) (root : Expr) : M Expr :=
p.foldlM viewCoordRaw root
@@ -172,3 +172,5 @@ def numBinders (p : Pos) (e : Expr) : M Nat :=
end ViewRaw
end Lean.Core

View File

@@ -26,7 +26,7 @@ private def mkAnd? (args : Array Expr) : Option Expr := Id.run do
def elimOptParam (type : Expr) : CoreM Expr := do
Core.transform type fun e =>
if e.isAppOfArity ``optParam 2 then
if e.isAppOfArity ``optParam 2 then
return TransformStep.visit (e.getArg! 0)
else
return .continue

View File

@@ -27,10 +27,11 @@ def getRawNatValue? (e : Expr) : Option Nat :=
/-- Return `some (n, type)` if `e` is an `OfNat.ofNat`-application encoding `n` for a type with name `typeDeclName`. -/
def getOfNatValue? (e : Expr) (typeDeclName : Name) : MetaM (Option (Nat × Expr)) := OptionT.run do
let_expr OfNat.ofNat type n _ e | failure
let type whnfD type
let e := e.consumeMData
guard <| e.isAppOfArity' ``OfNat.ofNat 3
let type whnfD (e.getArg!' 0)
guard <| type.getAppFn.isConstOf typeDeclName
let .lit (.natVal n) := n.consumeMData | failure
let .lit (.natVal n) := (e.getArg!' 1).consumeMData | failure
return (n, type)
/-- Return `some n` if `e` is a raw natural number or an `OfNat.ofNat`-application encoding `n`. -/
@@ -45,15 +46,16 @@ def getNatValue? (e : Expr) : MetaM (Option Nat) := do
def getIntValue? (e : Expr) : MetaM (Option Int) := do
if let some (n, _) getOfNatValue? e ``Int then
return some n
let_expr Neg.neg _ _ a e | return none
let some (n, _) getOfNatValue? a ``Int | return none
return some (-n)
if e.isAppOfArity' ``Neg.neg 3 then
let some (n, _) getOfNatValue? (e.getArg!' 2) ``Int | return none
return some (-n)
return none
/-- Return `some c` if `e` is a `Char.ofNat`-application encoding character `c`. -/
def getCharValue? (e : Expr) : MetaM (Option Char) := do
let_expr Char.ofNat n e | return none
let some n getNatValue? n | return none
return some (Char.ofNat n)
def getCharValue? (e : Expr) : MetaM (Option Char) := OptionT.run do
guard <| e.isAppOfArity' ``Char.ofNat 1
let n getNatValue? (e.getArg!' 0)
return Char.ofNat n
/-- Return `some s` if `e` is of the form `.lit (.strVal s)`. -/
def getStringValue? (e : Expr) : (Option String) :=
@@ -76,6 +78,7 @@ def getBitVecValue? (e : Expr) : MetaM (Option ((n : Nat) × BitVec n)) := Optio
let v getNatValue? (e.getArg!' 1)
return n, BitVec.ofNat n v
let (v, type) getOfNatValue? e ``BitVec
IO.println v
let n getNatValue? ( whnfD type.appArg!)
return n, BitVec.ofNat n v

View File

@@ -19,7 +19,7 @@ structure CaseArraySizesSubgoal where
def getArrayArgType (a : Expr) : MetaM Expr := do
let aType inferType a
let aType whnfD aType
unless aType.isAppOfArity ``Array 1 do
unless aType.isAppOfArity `Array 1 do
throwError "array expected{indentExpr a}"
pure aType.appArg!

View File

@@ -139,21 +139,15 @@ private def isValueTransition (p : Problem) : Bool :=
| .var _ :: _ => true
| _ => false
private def isValueOnlyTransitionCore (p : Problem) (isValue : Expr MetaM Bool) : MetaM Bool := do
private def isFinValueTransition (p : Problem) : MetaM Bool := do
if hasVarPattern p then return false
if !hasValPattern p then return false
p.alts.allM fun alt => do
match alt.patterns with
| .val v :: _ => isValue v
| .val v :: _ => return ( getFinValue? v).isSome
| .ctor .. :: _ => return true
| _ => return false
private def isFinValueTransition (p : Problem) : MetaM Bool :=
isValueOnlyTransitionCore p fun e => return ( getFinValue? e).isSome
private def isBitVecValueTransition (p : Problem) : MetaM Bool :=
isValueOnlyTransitionCore p fun e => return ( getBitVecValue? e).isSome
private def isArrayLitTransition (p : Problem) : Bool :=
hasArrayLitPattern p && hasVarPattern p
&& p.alts.all fun alt => match alt.patterns with
@@ -653,18 +647,15 @@ private def expandIntValuePattern (p : Problem) : MetaM Problem := do
private def expandFinValuePattern (p : Problem) : MetaM Problem := do
let alts p.alts.mapM fun alt => do
let .val n :: ps := alt.patterns | return alt
let some n, v getFinValue? n | return alt
let p mkLt (toExpr v.val) (toExpr n)
let h mkDecideProof p
return { alt with patterns := .ctor ``Fin.mk [] [toExpr n] [.val (toExpr v.val), .inaccessible h] :: ps }
return { p with alts := alts }
private def expandBitVecValuePattern (p : Problem) : MetaM Problem := do
let alts p.alts.mapM fun alt => do
let .val n :: ps := alt.patterns | return alt
let some _, v getBitVecValue? n | return alt
return { alt with patterns := .ctor ``BitVec.ofFin [] [] [.val (toExpr v.toFin)] :: ps }
match alt.patterns with
| .val n :: ps =>
match ( getFinValue? n) with
| some n, v =>
let p mkLt (toExpr v.val) (toExpr n)
let h mkDecideProof p
return { alt with patterns := .ctor ``Fin.mk [] [toExpr n] [.val (toExpr v.val), .inaccessible h] :: ps }
| _ => return alt
| _ => return alt
return { p with alts := alts }
private def traceStep (msg : String) : StateRefT State MetaM Unit := do
@@ -719,9 +710,6 @@ private partial def process (p : Problem) : StateRefT State MetaM Unit := do
else if ( isFinValueTransition p) then
traceStep ("fin value to constructor")
process ( expandFinValuePattern p)
else if ( isBitVecValueTransition p) then
traceStep ("bitvec value to constructor")
process ( expandBitVecValuePattern p)
else if !isNextVar p then
traceStep ("non variable")
let p processNonVariable p

View File

@@ -110,8 +110,6 @@ def unfoldNamedPattern (e : Expr) : MetaM Expr := do
- `type` is the resulting type for `altType`.
We use the `mask` to build the splitter proof. See `mkSplitterProof`.
This can be used to use the alternative of a match expression in its splitter.
-/
partial def forallAltTelescope (altType : Expr) (altNumParams numDiscrEqs : Nat)
(k : (ys : Array Expr) (eqs : Array Expr) (args : Array Expr) (mask : Array Bool) (type : Expr) MetaM α)
@@ -134,11 +132,9 @@ where
let some k := args.getIdx? lhs | unreachable!
let mask := mask.set! k false
let args := args.map fun arg => if arg == lhs then rhs else arg
let arg mkEqRefl rhs
let args := args.push ( mkEqRefl rhs)
let typeNew := typeNew.replaceFVar lhs rhs
return withReplaceFVarId lhs.fvarId! rhs do
withReplaceFVarId y.fvarId! arg do
go ys eqs (args.push arg) (mask.push false) (i+1) typeNew
return ( go ys eqs args (mask.push false) (i+1) typeNew)
go (ys.push y) eqs (args.push y) (mask.push true) (i+1) typeNew
else
let arg if let some (_, _, rhs) matchEq? d then
@@ -156,9 +152,7 @@ where
they are not eagerly evaluated. -/
if ys.size == 1 then
if ( inferType ys[0]!).isConstOf ``Unit && !( dependsOn type ys[0]!.fvarId!) then
let rhs := mkConst ``Unit.unit
return withReplaceFVarId ys[0]!.fvarId! rhs do
return ( k #[] #[] #[rhs] #[false] type)
return ( k #[] #[] #[mkConst ``Unit.unit] #[false] type)
k ys eqs args mask type
isNamedPatternProof (type : Expr) (h : Expr) : Bool :=

View File

@@ -156,259 +156,4 @@ def refineThrough? (matcherApp : MatcherApp) (e : Expr) :
catch _ =>
return none
/--
Given `n` and a non-dependent function type `α₁ → α₂ → ... → αₙ → Sort u`, returns the
types `α₁, α₂, ..., αₙ`. Throws an error if there are not at least `n` argument types or if a
later argument type depends on a prior one (i.e., it's a dependent function type).
This can be used to infer the expected type of the alternatives when constructing a `MatcherApp`.
-/
-- TODO: Which is the natural module for this?
def arrowDomainsN (n : Nat) (type : Expr) : MetaM (Array Expr) := do
let mut type := type
let mut ts := #[]
for i in [:n] do
type whnfForall type
let Expr.forallE _ α β _ pure type | throwError "expected {n} arguments, got {i}"
if β.hasLooseBVars then throwError "unexpected dependent type"
ts := ts.push α
type := β
return ts
/--
Performs a possibly type-changing transformation to a `MatcherApp`.
* `onParams` is run on each parameter and discriminant
* `onMotive` runs on the body of the motive, and is passed the motive parameters
(one for each `MatcherApp.discrs`)
* `onAlt` runs on each alternative, and is passed the expected type of the alternative,
as inferred from the motive
* `onRemaining` runs on the remaining arguments (and may change their number)
If `useSplitter` is true, the matcher is replaced with the splitter.
NB: Not all operations on `MatcherApp` can handle one `matcherName` is a splitter.
The array `addEqualities`, if provided, indicates for which of the discriminants an equality
connecting the discriminant to the parameters of the alternative (like in `match h : x with …`)
should be added (if it is isn't already there).
This function works even if the the type of alternatives do *not* fit the inferred type. This
allows you to post-process the `MatcherApp` with `MatcherApp.inferMatchType`, which will
infer a type, given all the alternatives.
-/
def transform (matcherApp : MatcherApp)
(useSplitter := false)
(addEqualities : Array Bool := mkArray matcherApp.discrs.size false)
(onParams : Expr MetaM Expr := pure)
(onMotive : Array Expr Expr MetaM Expr := fun _ e => pure e)
(onAlt : Expr Expr MetaM Expr := fun _ e => pure e)
(onRemaining : Array Expr MetaM (Array Expr) := pure) :
MetaM MatcherApp := do
if addEqualities.size != matcherApp.discrs.size then
throwError "MatcherApp.transform: addEqualities has wrong size"
-- Do not add equalities when the matcher already does so
let addEqualities := Array.zipWith addEqualities matcherApp.discrInfos fun b di =>
if di.hName?.isSome then false else b
-- We also handle CasesOn applications here, and need to treat them specially in a
-- few places.
-- TODO: Expand MatcherApp with the necessary fields to make this more uniform
-- (in particular, include discrEq and whether there is a splitter)
let isCasesOn := isCasesOnRecursor ( getEnv) matcherApp.matcherName
let numDiscrEqs
if isCasesOn then pure 0 else
match getMatcherInfo? matcherApp.matcherName with
| some info => pure info.getNumDiscrEqs
| none => throwError "matcher {matcherApp.matcherName} has no MatchInfo found"
let params' matcherApp.params.mapM onParams
let discrs' matcherApp.discrs.mapM onParams
let (motive', uElim) lambdaTelescope matcherApp.motive fun motiveArgs motiveBody => do
unless motiveArgs.size == matcherApp.discrs.size do
throwError "unexpected matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
let mut motiveBody' onMotive motiveArgs motiveBody
-- Prepend (x = e) → to the motive when an equality is requested
for arg in motiveArgs, discr in discrs', b in addEqualities do if b then
motiveBody' mkArrow ( mkEq discr arg) motiveBody'
return ( mkLambdaFVars motiveArgs motiveBody', getLevel motiveBody')
let matcherLevels match matcherApp.uElimPos? with
| none => pure matcherApp.matcherLevels
| some pos => pure <| matcherApp.matcherLevels.set! pos uElim
-- We pass `Eq.refl`s for all the equations we added as extra arguments
-- (and count them along the way)
let mut remaining' := #[]
let mut extraEqualities : Nat := 0
for discr in discrs'.reverse, b in addEqualities.reverse do if b then
remaining' := remaining'.push ( mkEqRefl discr)
extraEqualities := extraEqualities + 1
if useSplitter && !isCasesOn then
-- We replace the matcher with the splitter
let matchEqns Match.getEquationsFor matcherApp.matcherName
let splitter := matchEqns.splitterName
let aux1 := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) params'
let aux1 := mkApp aux1 motive'
let aux1 := mkAppN aux1 discrs'
unless ( isTypeCorrect aux1) do
logError m!"failed to transform matcher, type error when constructing new motive:{indentExpr aux1}"
check aux1
let origAltTypes arrowDomainsN matcherApp.alts.size ( inferType aux1)
let aux2 := mkAppN (mkConst splitter matcherLevels.toList) params'
let aux2 := mkApp aux2 motive'
let aux2 := mkAppN aux2 discrs'
unless ( isTypeCorrect aux2) do
logError m!"failed to transform matcher, type error when constructing new motive:{indentExpr aux2}"
check aux2
let altTypes arrowDomainsN matcherApp.alts.size ( inferType aux2)
let mut alts' := #[]
for alt in matcherApp.alts,
numParams in matcherApp.altNumParams,
splitterNumParams in matchEqns.splitterAltNumParams,
origAltType in origAltTypes,
altType in altTypes do
let alt' Match.forallAltTelescope origAltType (numParams - numDiscrEqs) 0 fun ys _eqs args _mask _bodyType => do
let altType instantiateForall altType ys
-- The splitter inserts its extra paramters after the first ys.size parameters, before
-- the parameters for the numDiscrEqs
forallBoundedTelescope altType (splitterNumParams - ys.size) fun ys2 altType => do
forallBoundedTelescope altType numDiscrEqs fun ys3 altType => do
forallBoundedTelescope altType extraEqualities fun ys4 altType => do
let alt try instantiateLambda alt (args ++ ys3)
catch _ => throwError "unexpected matcher application, insufficient number of parameters in alternative"
let alt' onAlt altType alt
mkLambdaFVars (ys ++ ys2 ++ ys3 ++ ys4) alt'
alts' := alts'.push alt'
remaining' := remaining' ++ ( onRemaining matcherApp.remaining)
return { matcherApp with
matcherName := splitter
matcherLevels := matcherLevels
params := params'
motive := motive'
discrs := discrs'
altNumParams := matchEqns.splitterAltNumParams.map (· + extraEqualities)
alts := alts'
remaining := remaining'
}
else
let aux := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) params'
let aux := mkApp aux motive'
let aux := mkAppN aux discrs'
unless ( isTypeCorrect aux) do
-- check aux
logError m!"failed to transform matcher, type error when constructing new motive:{indentExpr aux}"
check aux
let altTypes arrowDomainsN matcherApp.alts.size ( inferType aux)
let mut alts' := #[]
for alt in matcherApp.alts,
numParams in matcherApp.altNumParams,
altType in altTypes do
let alt' forallBoundedTelescope altType numParams fun xs altType => do
forallBoundedTelescope altType extraEqualities fun ys4 altType => do
let alt instantiateLambda alt xs
let alt' onAlt altType alt
mkLambdaFVars (xs ++ ys4) alt'
alts' := alts'.push alt'
remaining' := remaining' ++ ( onRemaining matcherApp.remaining)
return { matcherApp with
matcherLevels := matcherLevels
params := params'
motive := motive'
discrs := discrs'
altNumParams := matcherApp.altNumParams.map (· + extraEqualities)
alts := alts'
remaining := remaining'
}
/--
Given a `MatcherApp`, replaces the motive with one that is inferred from the actual types of the
alternatives.
For example, given
```
(match (motive := Nat → Unit → ?) n with
0 => 1
_ => true) ()
```
(for any `?`; the motives result type be ignored) will give this type
```
(match n with
| 0 => Nat
| _ => Bool)
```
The given `MatcherApp` must not use a splitter in `matcherName`.
The resulting expression *will* use the splitter corresponding to `matcherName` (this is necessary
for the construction).
Interally, this needs to reduce the matcher in a given branch; this is done using
`Split.simpMatchTarget`.
-/
def inferMatchType (matcherApp : MatcherApp) : MetaM MatcherApp := do
-- In matcherApp.motive, replace the (dummy) matcher body with a type
-- derived from the inferred types of the alterantives
let nExtra := matcherApp.remaining.size
matcherApp.transform (useSplitter := true)
(onMotive := fun motiveArgs body => do
let extraParams arrowDomainsN nExtra body
let propMotive mkLambdaFVars motiveArgs (.sort levelZero)
let propAlts matcherApp.alts.mapM fun termAlt =>
lambdaTelescope termAlt fun xs termAltBody => do
-- We have alt parameters and parameters corresponding to the extra args
let xs1 := xs[0 : xs.size - nExtra]
let xs2 := xs[xs.size - nExtra : xs.size]
-- logInfo m!"altIH: {xs} => {altIH}"
let altType inferType termAltBody
for x in xs2 do
if altType.hasAnyFVar (· == x.fvarId!) then
throwError "Type {altType} of alternative {termAlt} still depends on {x}"
-- logInfo m!"altIH type: {altType}"
mkLambdaFVars xs1 altType
let matcherLevels match matcherApp.uElimPos? with
| none => pure matcherApp.matcherLevels
| some pos => pure <| matcherApp.matcherLevels.set! pos levelOne
let typeMatcherApp := { matcherApp with
motive := propMotive
matcherLevels := matcherLevels
discrs := motiveArgs
alts := propAlts
remaining := #[]
}
mkArrowN extraParams typeMatcherApp.toExpr
)
(onAlt := fun expAltType alt => do
let altType inferType alt
let eq mkEq expAltType altType
let proof mkFreshExprSyntheticOpaqueMVar eq
let goal := proof.mvarId!
-- logInfo m!"Goal: {goal}"
let goal Split.simpMatchTarget goal
-- logInfo m!"Goal after splitting: {goal}"
try
goal.refl
catch _ =>
logInfo m!"Cannot close goal after splitting: {goal}"
goal.admit
mkEqMPR proof alt
)
end Lean.Meta.MatcherApp

View File

@@ -18,10 +18,9 @@ private abbrev withInstantiatedMVars (e : Expr) (k : Expr → OptionT MetaM α)
k eNew
def isNatProjInst (declName : Name) (numArgs : Nat) : Bool :=
(numArgs == 4 && (declName == ``Add.add || declName == ``Sub.sub || declName == ``Mul.mul || declName == ``Div.div || declName == ``Mod.mod || declName == ``NatPow.pow))
|| (numArgs == 5 && (declName == ``Pow.pow))
|| (numArgs == 6 && (declName == ``HAdd.hAdd || declName == ``HSub.hSub || declName == ``HMul.hMul || declName == ``HDiv.hDiv || declName == ``HMod.hMod || declName == ``HPow.hPow))
|| (numArgs == 3 && declName == ``OfNat.ofNat)
(numArgs == 4 && (declName == ``Add.add || declName == ``Sub.sub || declName == ``Mul.mul))
|| (numArgs == 6 && (declName == ``HAdd.hAdd || declName == ``HSub.hSub || declName == ``HMul.hMul))
|| (numArgs == 3 && declName == ``OfNat.ofNat)
/--
Evaluate simple `Nat` expressions.
@@ -36,21 +35,31 @@ partial def evalNat (e : Expr) : OptionT MetaM Nat := do
| _ => failure
where
visit e := do
match_expr e with
| Nat.succ a => return ( evalNat a) + 1
| Nat.add a b => return ( evalNat a) + ( evalNat b)
| Nat.sub a b => return ( evalNat a) - ( evalNat b)
| Nat.mul a b => return ( evalNat a) * ( evalNat b)
| Nat.div a b => return ( evalNat a) / ( evalNat b)
| Nat.mod a b => return ( evalNat a) % ( evalNat b)
| Nat.pow a b => return ( evalNat a) ^ ( evalNat b)
| _ =>
let e instantiateMVarsIfMVarApp e
let f := e.getAppFn
if f.isConst && isNatProjInst f.constName! e.getAppNumArgs then
let f := e.getAppFn
match f with
| .mvar .. => withInstantiatedMVars e evalNat
| .const c _ =>
let nargs := e.getAppNumArgs
if c == ``Nat.succ && nargs == 1 then
let v evalNat (e.getArg! 0)
return v+1
else if c == ``Nat.add && nargs == 2 then
let v₁ evalNat (e.getArg! 0)
let v₂ evalNat (e.getArg! 1)
return v₁ + v₂
else if c == ``Nat.sub && nargs == 2 then
let v₁ evalNat (e.getArg! 0)
let v₂ evalNat (e.getArg! 1)
return v₁ - v₂
else if c == ``Nat.mul && nargs == 2 then
let v₁ evalNat (e.getArg! 0)
let v₂ evalNat (e.getArg! 1)
return v₁ * v₂
else if isNatProjInst c nargs then
evalNat ( unfoldProjInst? e)
else
failure
| _ => failure
mutual

View File

@@ -37,4 +37,3 @@ import Lean.Meta.Tactic.IndependentOf
import Lean.Meta.Tactic.Symm
import Lean.Meta.Tactic.Backtrack
import Lean.Meta.Tactic.SolveByElim
import Lean.Meta.Tactic.FunInd

View File

@@ -37,15 +37,6 @@ structure ElimInfo where
altsInfo : Array ElimAltInfo := #[]
deriving Repr, Inhabited
/-- Given the type `t` of an alternative, determines the number of parameters
(.forall and .let)-bound, and whether the conclusion is a `motive`-application. -/
def altArity (motive : Expr) (n : Nat) : Expr Nat × Bool
| .forallE _ _ b _ => altArity motive (n+1) b
| .letE _ _ _ b _ => altArity motive (n+1) b
| conclusion => (n, conclusion.getAppFn == motive)
def getElimExprInfo (elimExpr : Expr) (baseDeclName? : Option Name := none) : MetaM ElimInfo := do
let elimType inferType elimExpr
trace[Elab.induction] "eliminator {indentExpr elimExpr}\nhas type{indentExpr elimType}"
@@ -73,7 +64,8 @@ def getElimExprInfo (elimExpr : Expr) (baseDeclName? : Option Name := none) : Me
if x != motive && !targets.contains x then
let xDecl x.fvarId!.getDecl
if xDecl.binderInfo.isExplicit then
let (numFields, provesMotive) := altArity motive 0 xDecl.type
let (numFields, provesMotive) forallTelescopeReducing xDecl.type fun args concl =>
pure (args.size, concl.getAppFn == motive)
let name := xDecl.userName
let declName? := do
let base baseDeclName?

View File

@@ -1,924 +0,0 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
prelude
import Lean.Meta.Basic
import Lean.Meta.Match.MatcherApp.Transform
import Lean.Meta.Check
import Lean.Meta.Tactic.Cleanup
import Lean.Meta.Tactic.Subst
import Lean.Meta.Injective -- for elimOptParam
import Lean.Elab.PreDefinition.WF.Eqns
import Lean.Elab.PreDefinition.WF.PackMutual
import Lean.Elab.Command
/-!
This module contains code to derive, from the definition of a recursive function
(or mutually recursive functions) defined by well-founded recursion, a
**functional induction principle** tailored to proofs about that function(s). For
example from:
```
def ackermann : Nat → Nat → Nat
| 0, m => m + 1
| n+1, 0 => ackermann n 1
| n+1, m+1 => ackermann n (ackermann (n + 1) m)
derive_functional_induction ackermann
```
we get
```
ackermann.induct (motive : Nat → Nat → Prop) (case1 : ∀ (m : Nat), motive 0 m)
(case2 : ∀ (n : Nat), motive n 1 → motive (Nat.succ n) 0)
(case3 : ∀ (n m : Nat), motive (n + 1) m → motive n (ackermann (n + 1) m) → motive (Nat.succ n) (Nat.succ m))
(x x : Nat) : motive x x
```
## Specification
The functional induction principle takes the same fixed parameters as the function, and
the motive takes the same non-fixed parameters as the original function.
For each branch of the original function, there is a case in the induction principle.
Here "branch" roughly corresponds to tail-call positions: branches of top-level
`if`-`then`-`else` and of `match` expressions.
For every recursive call in that branch, an induction hypothesis asserting the
motive for the arguments of the recursive call is provided.
If the recursive call is under binders and it, or its proof of termination,
depend on the the bound values, then these become assumptions of the inductive
hypothesis.
Additionally, the local context of the branch (e.g. the condition of an
if-then-else; a let-binding, a have-binding) is provided as assumptions in the
corresponding induction case, if they are likely to be useful (as determined
by `MVarId.cleanup`).
Mutual recursion is supported and results in multiple motives.
## Implementation overview
For a non-mutual, unary function `foo` (or else for the `_unary` function), we
1. expect its definition, possibly after some `whnf`ing, to be of the form
```
def foo := fun x₁ … xₙ (y : a) => WellFounded.fix (fun y' oldIH => body) y
```
where `xᵢ…` are the fixed parameter prefix and `y` is the varying parameter of
the function.
2. From this structure we derive the type of the motive, and start assembling the induction
principle:
```
def foo.induct := fun x₁ … xₙ (motive : (y : a) → Prop) =>
fix (fun y' newIH => T[body])
```
3. The first phase, transformation `T1[body]` (implemented in) `buildInductionBody`,
mirrors the branching structure of `foo`, i.e. replicates `dite` and some matcher applications,
while adjusting their motive. It also unfolds calls to `oldIH` and collects induction hypotheses
in conditions (see below).
In particular, when translating a `match` it is prepared to recognize the idiom
as introduced by `mkFix` via `Lean.Meta.MatcherApp.addArg?`, which refines the type of `oldIH`
throughout the match. The transformation will replace `oldIH` with `newIH` here.
```
T[(match (motive := fun oldIH => …) y with | … => fun oldIH' => body) oldIH]
==> (match (motive := fun newIH => …) y with | … => fun newIH' => T[body]) newIH
```
In addition, the information gathered from the match is preserved, so that when performing the
proof by induction, the user can reliably enter the right case. To achieve this
* the matcher is replaced by its splitter, which brings extra assumptions into scope when
patterns are overlapping
* simple discriminants that are mentioned in the goal (i.e plain parameters) are instantiated
in the code.
* for discriminants that are not instantiated that way, equalities connecting the discriminant
to the instantiation are added (just as if the user wrote `match h : x with …`)
4. When a tail position (no more branching) is found, function `buildInductionCase` assembles the
type of the case: a fresh `MVar` asserts the current goal, unwanted values from the local context
are cleared, and the current `body` is searched for recursive calls using `collectIHs`,
which are then asserted as inductive hyptheses in the `MVar`.
5. The function `collectIHs` walks the term and collects the induction hypotheses for the current case
(with proofs). When it encounters a saturated application of `oldIH x proof`, it returns
`newIH x proof : motive x`.
Since `x` and `proof` can contain further recursive calls, it uses
`foldCalls` to replace these with calls to `foo`. This assumes that the
termination proof `proof` works nevertheless.
Again, `collectIHs` may encounter the `Lean.Meta.Matcherapp.addArg?` idiom, and again it threads `newIH`
through, replacing the extra argument. The resulting type of this induction hypothesis is now
itself a `match` statement (cf. `Lean.Meta.MatcherApp.inferMatchType`)
The termination proof of `foo` may have abstracted over some proofs; these proofs must be transferred, so
auxillary lemmas are unfolded if needed.
6. The function `foldCalls` replaces calls to `oldIH` with calls to `foo` that
make sense to the user.
At the end of this transformation, no mention of `oldIH` must remain.
7. After this construction, the MVars introduced by `buildInductionCase` are turned into parameters.
The resulting term then becomes `foo.induct` at its inferred type.
If `foo` is not unary and/or part of a mutual reduction, then the induction theorem for `foo._unary`
(i.e. the unary non-mutual recursion function produced by the equation compiler)
of the form
```
foo._unary.induct : {motive : (a ⊗' b) ⊕' c → Prop} →
(case1 : ∀ …, motive (PSum.inl (x,y)) → …) → … →
(x : (a ⊗' b) ⊕' c) → motive x
```
will first in `unpackMutualInduction` be turned into a joint induction theorem of the form
```
foo.mutual_induct : {motive1 : a → b → Prop} {motive2 : c → Prop} →
(case1 : ∀ …, motive1 x y → …) → … →
((x : a) → (y : b) → motive1 x y) ∧ ((z : c) → motive2 z)
```
where all the `PSum`/`PSigma` encoding has been resolved. This theorem is attached to the
name of the first function in the mutual group, like the `._unary` definition.
Finally, in `deriveUnpackedInduction`, for each of the funtions in the mutual group, a simple
projection yields the final `foo.induct` theorem:
```
foo.induct : {motive1 : a → b → Prop} {motive2 : c → Prop} →
(case1 : ∀ …, motive1 x y → …) → … →
(x : a) → (y : b) → motive1 x y
```
-/
set_option autoImplicit false
namespace Lean.Tactic.FunInd
open Lean Elab Meta
/-- Opens the body of a lambda, _without_ putting the free variable into the local context.
This is used when replacing parameters with different expressions.
This way it will not be picked up by metavariables.
-/
def removeLamda {α} (e : Expr) (k : FVarId Expr MetaM α) : MetaM α := do
let .lam _n _d b _bi := whnfD e | throwError "removeLamda: expected lambda, got {e}"
let x mkFreshFVarId
let b := b.instantiate1 (.fvar x)
k x b
/-- Replace calls to oldIH back to calls to the original function. At the end, if `oldIH` occurs, an error is thrown. -/
partial def foldCalls (fn : Expr) (oldIH : FVarId) (e : Expr) : MetaM Expr := do
unless e.containsFVar oldIH do
return e
if e.getAppNumArgs = 2 && e.getAppFn.isFVarOf oldIH then
let #[arg, _proof] := e.getAppArgs | unreachable!
let arg' foldCalls fn oldIH arg
return .app fn arg'
if let some matcherApp matchMatcherApp? e (alsoCasesOn := true) then
if matcherApp.remaining.size == 1 && matcherApp.remaining[0]!.isFVarOf oldIH then
let matcherApp' matcherApp.transform
(onParams := foldCalls fn oldIH)
(onMotive := fun _motiveArgs motiveBody => do
let some (_extra, body) := motiveBody.arrow? | throwError "motive not an arrow"
foldCalls fn oldIH body)
(onAlt := fun _altType alt => do
removeLamda alt fun oldIH alt => do
foldCalls fn oldIH alt)
(onRemaining := fun _ => pure #[])
return matcherApp'.toExpr
if e.getAppArgs.any (·.isFVarOf oldIH) then
-- Sometimes Fix.lean abstracts over oldIH in a proof definition.
-- So beta-reduce that definition.
-- Need to look through theorems here!
let e' withTransparency .all do whnf e
if e == e' then
throwError "foldCalls: cannot reduce application of {e.getAppFn} in {indentExpr e} "
return foldCalls fn oldIH e'
if let some (n, t, v, b) := e.letFun? then
let t' foldCalls fn oldIH t
let v' foldCalls fn oldIH v
return withLocalDecl n .default t' fun x => do
let b' foldCalls fn oldIH (b.instantiate1 x)
mkLetFun x v' b'
match e with
| .app e1 e2 =>
return .app ( foldCalls fn oldIH e1) ( foldCalls fn oldIH e2)
| .lam n t body bi =>
let t' foldCalls fn oldIH t
return withLocalDecl n bi t' fun x => do
let body' foldCalls fn oldIH (body.instantiate1 x)
mkLambdaFVars #[x] body'
| .forallE n t body bi =>
let t' foldCalls fn oldIH t
return withLocalDecl n bi t' fun x => do
let body' foldCalls fn oldIH (body.instantiate1 x)
mkForallFVars #[x] body'
| .letE n t v b _ =>
let t' foldCalls fn oldIH t
let v' foldCalls fn oldIH v
return withLetDecl n t' v' fun x => do
let b' foldCalls fn oldIH (b.instantiate1 x)
mkLetFVars #[x] b'
| .mdata m b =>
return .mdata m ( foldCalls fn oldIH b)
| .proj t i e =>
return .proj t i ( foldCalls fn oldIH e)
| .sort .. | .lit .. | .const .. | .mvar .. | .bvar .. =>
unreachable! -- cannot contain free variables, so early exit above kicks in
| .fvar .. =>
throwError m!"collectIHs: cannot eliminate unsaturated call to induction hypothesis"
/--
Given proofs of `P₁`, …, `Pₙ`, returns a proof of `P₁ ∧ … ∧ Pₙ`.
If `n = 0` returns a proof of `True`.
If `n = 1` returns the proof of `P₁`.
-/
def mkAndIntroN : Array Expr MetaM Expr
| #[] => return mkConst ``True.intro []
| #[e] => return e
| es => es.foldrM (start := es.size - 1) (fun a b => mkAppM ``And.intro #[a,b]) es.back
/-- Given a proof of `P₁ ∧ … ∧ Pᵢ ∧ … ∧ Pₙ`, return the proof of `Pᵢ` -/
def mkProjAndN (n i : Nat) (e : Expr) : Expr := Id.run do
let mut value := e
for _ in [:i] do
value := mkProj ``And 1 value
if i + 1 < n then
value := mkProj ``And 0 value
return value
-- Non-tail-positions: Collect induction hypotheses
-- (TODO: Worth folding with `foldCalls`, like before?)
-- (TODO: Accumulated with a left fold)
partial def collectIHs (fn : Expr) (oldIH newIH : FVarId) (e : Expr) : MetaM (Array Expr) := do
unless e.containsFVar oldIH do
return #[]
if e.getAppNumArgs = 2 && e.getAppFn.isFVarOf oldIH then
let #[arg, proof] := e.getAppArgs | unreachable!
let arg' foldCalls fn oldIH arg
let proof' foldCalls fn oldIH proof
let ihs collectIHs fn oldIH newIH arg
return ihs.push (mkApp2 (.fvar newIH) arg' proof')
if let some (n, t, v, b) := e.letFun? then
let ihs1 collectIHs fn oldIH newIH v
let v' foldCalls fn oldIH v
return withLetDecl n t v' fun x => do
let ihs2 collectIHs fn oldIH newIH (b.instantiate1 x)
let ihs2 ihs2.mapM (mkLetFVars (usedLetOnly := true) #[x] ·)
return ihs1 ++ ihs2
if let some matcherApp matchMatcherApp? e (alsoCasesOn := true) then
if matcherApp.remaining.size == 1 && matcherApp.remaining[0]!.isFVarOf oldIH then
let matcherApp' matcherApp.transform
(onParams := foldCalls fn oldIH)
(onMotive := fun xs _body => do
-- Remove the old IH that was added in mkFix
let eType newIH.getType
let eTypeAbst matcherApp.discrs.size.foldRevM (init := eType) fun i eTypeAbst => do
let motiveArg := xs[i]!
let discr := matcherApp.discrs[i]!
let eTypeAbst kabstract eTypeAbst discr
return eTypeAbst.instantiate1 motiveArg
-- Will later be overriden with a type thats itself a match
-- statement and the infered alt types
let dummyGoal := mkConst ``True []
mkArrow eTypeAbst dummyGoal)
(onAlt := fun altType alt => do
removeLamda alt fun oldIH' alt => do
forallBoundedTelescope altType (some 1) fun newIH' _goal' => do
let #[newIH'] := newIH' | unreachable!
let altIHs collectIHs fn oldIH' newIH'.fvarId! alt
let altIH mkAndIntroN altIHs
mkLambdaFVars #[newIH'] altIH)
(onRemaining := fun _ => pure #[mkFVar newIH])
let matcherApp'' matcherApp'.inferMatchType
return #[ matcherApp''.toExpr ]
if e.getAppArgs.any (·.isFVarOf oldIH) then
-- Sometimes Fix.lean abstracts over oldIH in a proof definition.
-- So beta-reduce that definition.
-- Need to look through theorems here!
let e' withTransparency .all do whnf e
if e == e' then
throwError "collectIHs: cannot reduce application of {e.getAppFn} in {indentExpr e} "
return collectIHs fn oldIH newIH e'
if e.getAppArgs.any (·.isFVarOf oldIH) then
throwError "collectIHs: could not collect recursive calls from call {indentExpr e}"
match e with
| .letE n t v b _ =>
let ihs1 collectIHs fn oldIH newIH v
let v' foldCalls fn oldIH v
return withLetDecl n t v' fun x => do
let ihs2 collectIHs fn oldIH newIH (b.instantiate1 x)
let ihs2 ihs2.mapM (mkLetFVars (usedLetOnly := true) #[x] ·)
return ihs1 ++ ihs2
| .app e1 e2 =>
return ( collectIHs fn oldIH newIH e1) ++ ( collectIHs fn oldIH newIH e2)
| .proj _ _ e =>
return collectIHs fn oldIH newIH e
| .forallE n t body bi =>
let t' foldCalls fn oldIH t
return withLocalDecl n bi t' fun x => do
let ihs collectIHs fn oldIH newIH (body.instantiate1 x)
ihs.mapM (mkLambdaFVars (usedOnly := true) #[x])
| .lam n t body bi =>
let t' foldCalls fn oldIH t
return withLocalDecl n bi t' fun x => do
let ihs collectIHs fn oldIH newIH (body.instantiate1 x)
ihs.mapM (mkLambdaFVars (usedOnly := true) #[x])
| .mdata _m b =>
return collectIHs fn oldIH newIH b
| .sort .. | .lit .. | .const .. | .mvar .. | .bvar .. =>
unreachable! -- cannot contain free variables, so early exit above kicks in
| .fvar _ =>
throwError "collectIHs: could not collect recursive calls, unsaturated application of old induction hypothesis"
-- Because of term duplications we might encounter the same IH multiple times.
-- We deduplicate them (by type, not proof term) here.
-- This could be improved and catch cases where the same IH is used in different contexts.
-- (Cf. `assignSubsumed` in `WF.Fix`)
def deduplicateIHs (vals : Array Expr) : MetaM (Array Expr) := do
let mut vals' := #[]
let mut types := #[]
for v in vals do
let t inferType v
unless types.contains t do
vals' := vals'.push v
types := types.push t
return vals'
def assertIHs (vals : Array Expr) (mvarid : MVarId) : MetaM MVarId := do
let mut mvarid := mvarid
for v in vals.reverse, i in [0:vals.size] do
mvarid mvarid.assert s!"ih{i+1}" ( inferType v) v
return mvarid
/-- Base case of `buildInductionBody`: Construct a case for the final induction hypthesis. -/
def buildInductionCase (fn : Expr) (oldIH newIH : FVarId) (toClear toPreserve : Array FVarId)
(goal : Expr) (IHs : Array Expr) (e : Expr) : MetaM Expr := do
let IHs := IHs ++ ( collectIHs fn oldIH newIH e)
let IHs deduplicateIHs IHs
let mvar mkFreshExprSyntheticOpaqueMVar goal (tag := `hyp)
let mut mvarId := mvar.mvarId!
mvarId assertIHs IHs mvarId
for fvarId in toClear do
mvarId mvarId.clear fvarId
mvarId mvarId.cleanup (toPreserve := toPreserve)
mvarId substVars mvarId
let mvar instantiateMVars mvar
pure mvar
/--
Like `mkLambdaFVars (usedOnly := true)`, but
* silently skips expression in `xs` that are not `.isFVar`
* returns a mask (same size as `xs`) indicating which variables have been abstracted
(`true` means was abstracted).
The result `r` can be applied with `r.beta (maskArray mask args)`.
We use this when generating the functional induction principle to refine the goal through a `match`,
here `xs` are the discriminans of the `match`.
We do not expect non-trivial discriminants to appear in the goal (and if they do, the user will
get a helpful equality into the context).
-/
def mkLambdaFVarsMasked (xs : Array Expr) (e : Expr) : MetaM (Array Bool × Expr) := do
let mut e := e
let mut xs := xs
let mut mask := #[]
while ! xs.isEmpty do
let discr := xs.back
if discr.isFVar && e.containsFVar discr.fvarId! then
e mkLambdaFVars #[discr] e
mask := mask.push true
else
mask := mask.push false
xs := xs.pop
return (mask.reverse, e)
/-- `maskArray mask xs` keeps those `x` where the corresponding entry in `mask` is `true` -/
def maskArray {α} (mask : Array Bool) (xs : Array α) : Array α := Id.run do
let mut ys := #[]
for b in mask, x in xs do
if b then ys := ys.push x
return ys
partial def buildInductionBody (fn : Expr) (toClear toPreserve : Array FVarId)
(goal : Expr) (oldIH newIH : FVarId) (IHs : Array Expr) (e : Expr) : MetaM Expr := do
if e.isDIte then
let #[_α, c, h, t, f] := e.getAppArgs | unreachable!
let IHs := IHs ++ ( collectIHs fn oldIH newIH c)
let c' foldCalls fn oldIH c
let h' foldCalls fn oldIH h
let t' withLocalDecl `h .default c' fun h => do
let t instantiateLambda t #[h]
let t' buildInductionBody fn toClear (toPreserve.push h.fvarId!) goal oldIH newIH IHs t
mkLambdaFVars #[h] t'
let f' withLocalDecl `h .default (mkNot c') fun h => do
let f instantiateLambda f #[h]
let f' buildInductionBody fn toClear (toPreserve.push h.fvarId!) goal oldIH newIH IHs f
mkLambdaFVars #[h] f'
let u getLevel goal
return mkApp5 (mkConst ``dite [u]) goal c' h' t' f'
if let some matcherApp matchMatcherApp? e (alsoCasesOn := true) then
-- Collect IHs from the parameters and discrs of the matcher
let paramsAndDiscrs := matcherApp.params ++ matcherApp.discrs
let IHs := IHs ++ ( paramsAndDiscrs.concatMapM (collectIHs fn oldIH newIH))
-- Calculate motive
let eType newIH.getType
let motiveBody mkArrow eType goal
let (mask, absMotiveBody) mkLambdaFVarsMasked matcherApp.discrs motiveBody
-- A match that refines the parameter has been modified by `Fix.lean` to refine the IH,
-- so we need to replace that IH
if matcherApp.remaining.size == 1 && matcherApp.remaining[0]!.isFVarOf oldIH then
let matcherApp' matcherApp.transform (useSplitter := true)
(addEqualities := mask.map not)
(onParams := foldCalls fn oldIH)
(onMotive := fun xs _body => pure (absMotiveBody.beta (maskArray mask xs)))
(onAlt := fun expAltType alt => do
removeLamda alt fun oldIH' alt => do
forallBoundedTelescope expAltType (some 1) fun newIH' goal' => do
let #[newIH'] := newIH' | unreachable!
let alt' buildInductionBody fn (toClear.push newIH'.fvarId!) toPreserve goal' oldIH' newIH'.fvarId! IHs alt
mkLambdaFVars #[newIH'] alt')
(onRemaining := fun _ => pure #[.fvar newIH])
return matcherApp'.toExpr
-- A match that does not refine the parameter, but that we still want to split into separate
-- cases
if matcherApp.remaining.isEmpty then
-- Calculate motive
let (mask, absMotiveBody) mkLambdaFVarsMasked matcherApp.discrs goal
let matcherApp' matcherApp.transform (useSplitter := true)
(addEqualities := mask.map not)
(onParams := foldCalls fn oldIH)
(onMotive := fun xs _body => pure (absMotiveBody.beta (maskArray mask xs)))
(onAlt := fun expAltType alt => do
buildInductionBody fn toClear toPreserve expAltType oldIH newIH IHs alt)
return matcherApp'.toExpr
if let .letE n t v b _ := e then
let IHs := IHs ++ ( collectIHs fn oldIH newIH v)
let t' foldCalls fn oldIH t
let v' foldCalls fn oldIH v
return withLetDecl n t' v' fun x => do
let b' buildInductionBody fn toClear toPreserve goal oldIH newIH IHs (b.instantiate1 x)
mkLetFVars #[x] b'
if let some (n, t, v, b) := e.letFun? then
let IHs := IHs ++ ( collectIHs fn oldIH newIH v)
let t' foldCalls fn oldIH t
let v' foldCalls fn oldIH v
return withLocalDecl n .default t' fun x => do
let b' buildInductionBody fn toClear toPreserve goal oldIH newIH IHs (b.instantiate1 x)
mkLetFun x v' b'
buildInductionCase fn oldIH newIH toClear toPreserve goal IHs e
partial def findFixF {α} (name : Name) (e : Expr) (k : Array Expr Expr MetaM α) : MetaM α := do
lambdaTelescope e fun params body => do
if body.isAppOf ``WellFounded.fixF then
k params body
else if body.isAppOf ``WellFounded.fix then
findFixF name ( unfoldDefinition body) fun args e' => k (params ++ args) e'
else
throwError m!"Function {name} does not look like a function defined by well-founded " ++
m!"recursion.\nNB: If {name} is not itself recursive, but contains an inner recursive " ++
m!"function (via `let rec` or `where`), try `{name}.go` where `go` is name of the inner " ++
"function."
/--
Given a definition `foo` defined via `WellFounded.fixF`, derive a suitable induction principle
`foo.induct` for it. See module doc for details.
-/
def deriveUnaryInduction (name : Name) : MetaM Name := do
let inductName := .append name `induct
if hasConst inductName then return inductName
let info getConstInfoDefn name
findFixF name info.value fun params body => body.withApp fun f fixArgs => do
-- logInfo f!"{fixArgs}"
unless params.size > 0 do
throwError "Value of {name} is not a lambda application"
unless f.isConstOf ``WellFounded.fixF do
throwError "Term isnt application of {``WellFounded.fixF}, but of {f}"
let #[argType, rel, _motive, body, arg, acc] := fixArgs |
throwError "Application of WellFounded.fixF has wrong arity {fixArgs.size}"
unless isDefEq arg params.back do
throwError "fixF application argument {arg} is not function argument "
let [argLevel, _motiveLevel] := f.constLevels! | unreachable!
let motiveType mkArrow argType (.sort levelZero)
withLocalDecl `motive .default motiveType fun motive => do
let e' := mkApp3 (.const ``WellFounded.fixF [argLevel, levelZero]) argType rel motive
let fn := mkAppN (.const name (info.levelParams.map mkLevelParam)) params.pop
let body' forallTelescope ( inferType e').bindingDomain! fun xs _ => do
let #[param, genIH] := xs | unreachable!
-- open body with the same arg
let body instantiateLambda body #[param]
removeLamda body fun oldIH body => do
let body' buildInductionBody fn #[genIH.fvarId!] #[] (.app motive param) oldIH genIH.fvarId! #[] body
if body'.containsFVar oldIH then
throwError m!"Did not fully eliminate {mkFVar oldIH} from induction principle body:{indentExpr body}"
mkLambdaFVars #[param, genIH] body'
let e' := mkApp3 e' body' arg acc
let e' mkLambdaFVars #[params.back] e'
let mvars getMVarsNoDelayed e'
let mvars mvars.mapM fun mvar => do
let (_, mvar) mvar.revertAfter motive.fvarId!
pure mvar
-- Using `mkLambdaFVars` on mvars directly does not reliably replace
-- the mvars with the parameter, in the presence of delayed assignemnts.
-- Also `abstractMVars` does not handle delayed assignments correctly (as of now).
-- So instead we bring suitable fvars into scope and use `assign`; this handles
-- delayed assignemnts correctly.
-- NB: This idiom only works because
-- * we know that the `MVars` have the right local context (thanks to `mvarId.revertAfter`)
-- * the MVars are independent (so we dont need to reorder them)
-- * we do no need the mvars in their unassigned form later
let e' Meta.withLocalDecls
(mvars.mapIdx (fun i mvar => (s!"case{i.val+1}", .default, (fun _ => mvar.getType))))
fun xs => do
for mvar in mvars, x in xs do
mvar.assign x
let e' instantiateMVars e'
mkLambdaFVars xs e'
-- We could pass (usedOnly := true) below, and get nicer induction principles that
-- do do not mention odd unused parameters.
-- But the downside is that automatic instantiation of the principle (e.g. in a tactic
-- that derives them from an function application in the goal) is harder, as
-- one would have to infer or keep track of which parameters to pass.
-- So for now lets just keep them around.
let e' mkLambdaFVars (binderInfoForMVars := .default) (params.pop ++ #[motive]) e'
let e' instantiateMVars e'
let eTyp inferType e'
let eTyp elimOptParam eTyp
-- logInfo m!"eTyp: {eTyp}"
unless ( isTypeCorrect e') do
logError m!"failed to derive induction priciple:{indentExpr e'}"
check e'
addDecl <| Declaration.thmDecl
{ name := inductName, levelParams := info.levelParams, type := eTyp, value := e' }
return inductName
/--
In the type of `value`, reduces
* Beta-redexes
* `PSigma.casesOn (PSigma.mk a b) (fun x y => k x y) --> k a b`
* `PSum.casesOn (PSum.inl x) k₁ k₂ --> k₁ x`
* `foo._unary (PSum.inl (PSigma.mk a b)) --> foo a b`
and then wraps `value` in an appropriate type hint.
-/
def cleanPackedArgs (eqnInfo : WF.EqnInfo) (value : Expr) : MetaM Expr := do
-- TODO: Make arities (or varnames) part of eqnInfo
let arities eqnInfo.declNames.mapM fun name => do
let ci getConstInfoDefn name
lambdaTelescope ci.value fun xs _body => return xs.size - eqnInfo.fixedPrefixSize
let t Meta.transform ( inferType value) (skipConstInApp := true) (pre := fun e => do
-- Need to beta-reduce first
let e' := e.headBeta
if e' != e then
return .visit e'
-- Look for PSigma redexes
if e.isAppOf ``PSigma.casesOn then
let args := e.getAppArgs
if 5 args.size then
let scrut := args[3]!
let k := args[4]!
let extra := args[5:]
if scrut.isAppOfArity ``PSigma.mk 4 then
let #[_, _, x, y] := scrut.getAppArgs | unreachable!
let e' := (k.beta #[x, y]).beta extra
return .visit e'
-- Look for PSum redexes
if e.isAppOf ``PSum.casesOn then
let args := e.getAppArgs
if 6 args.size then
let scrut := args[3]!
let k₁ := args[4]!
let k₂ := args[5]!
let extra := args[6:]
if scrut.isAppOfArity ``PSum.inl 3 then
let e' := (k₁.beta #[scrut.appArg!]).beta extra
return .visit e'
if scrut.isAppOfArity ``PSum.inr 3 then
let e' := (k₂.beta #[scrut.appArg!]).beta extra
return .visit e'
-- Look for _unary redexes
if e.isAppOf eqnInfo.declNameNonRec then
let args := e.getAppArgs
if eqnInfo.fixedPrefixSize + 1 args.size then
let packedArg := args.back
let (i, unpackedArgs) WF.unpackArg arities packedArg
let e' := .const eqnInfo.declNames[i]! e.getAppFn.constLevels!
let e' := mkAppN e' args.pop
let e' := mkAppN e' unpackedArgs
let e' := mkAppN e' args[eqnInfo.fixedPrefixSize+1:]
return .continue e'
return .continue e)
mkExpectedTypeHint value t
/-- Given type `A ⊕' B ⊕' … ⊕' D`, return `[A, B, …, D]` -/
partial def unpackPSum (type : Expr) : List Expr :=
if type.isAppOfArity ``PSum 2 then
if let #[a, b] := type.getAppArgs then
a :: unpackPSum b
else unreachable!
else
[type]
/-- Given `A ⊗' B ⊗' … ⊗' D` and `R`, return `A → B → … → D → R` -/
partial def uncurryPSumArrow (domain : Expr) (codomain : Expr) : MetaM Expr := do
if domain.isAppOfArity ``PSigma 2 then
let #[a, b] := domain.getAppArgs | unreachable!
withLocalDecl `x .default a fun x => do
mkForallFVars #[x] ( uncurryPSumArrow (b.beta #[x]) codomain)
else
mkArrow domain codomain
/--
Given expression `e` with type `(x : A ⊗' B ⊗' … ⊗' D) → R[x]`
return expression of type `(x : A) → (y : B) → … → (z : D) → R[(x,y,z)]`
-/
partial def uncurryPSigma (e : Expr) : MetaM Expr := do
let packedDomain := ( inferType e).bindingDomain!
go packedDomain packedDomain #[]
where
go (packedDomain domain : Expr) args : MetaM Expr := do
if domain.isAppOfArity ``PSigma 2 then
let #[a, b] := domain.getAppArgs | unreachable!
withLocalDecl `x .default a fun x => do
mkLambdaFVars #[x] ( go packedDomain (b.beta #[x]) (args.push x))
else
withLocalDecl `x .default domain fun x => do
let args := args.push x
let packedArg WF.mkUnaryArg packedDomain args
mkLambdaFVars #[x] (e.beta #[packedArg])
/--
Iterated `PSigma.casesOn`: Given `y : a ⊗' b ⊗' …` and a type `codomain`,
and `alt : (x : a) → (y : b) → codomain`, uses `PSigma.casesOn` to invoke `alt` on `y`.
This very is similar to `Lean.Predefinition.WF.mkPSigmaCasesOn`, but takes a lambda rather than
free variables.
-/
partial def mkPSigmaNCasesOn (y : FVarId) (codomain : Expr) (alt : Expr) : MetaM Expr := do
let mvar mkFreshExprSyntheticOpaqueMVar codomain
let rec go (mvarId : MVarId) (y : FVarId) (ys : Array Expr) : MetaM Unit := mvarId.withContext do
if ( inferType (mkFVar y)).isAppOfArity ``PSigma 2 then
let #[s] mvarId.cases y | unreachable!
go s.mvarId s.fields[1]!.fvarId! (ys.push s.fields[0]!)
else
let ys := ys.push (mkFVar y)
mvarId.assign (alt.beta ys)
go mvar.mvarId! y #[]
instantiateMVars mvar
/--
Given expression `e` with type `(x : A) → (y : B[x]) → … → (z : D[x,y]) → R`
returns an expression of type `(x : A ⊗' B ⊗' … ⊗' D) → R`.
-/
partial def curryPSigma (e : Expr) : MetaM Expr := do
let (d, codomain) forallTelescope ( inferType e) fun xs codomain => do
if xs.any (codomain.containsFVar ·.fvarId!) then
throwError "curryPSum: codomain depends on domain variables"
let mut d inferType xs.back
for x in xs.pop.reverse do
d mkLambdaFVars #[x] d
d mkAppOptM ``PSigma #[some ( inferType x), some d]
return (d, codomain)
withLocalDecl `x .default d fun x => do
let value mkPSigmaNCasesOn x.fvarId! codomain e
mkLambdaFVars #[x] value
/--
Given type `(a ⊗' b ⊕' c ⊗' d) → e`, brings `a → b → e` and `c → d → e`
into scope as fresh local declarations and passes their FVars to the continuation `k`.
The `name` is used to form the variable names; uses `name1`, `name2`, … if there are multiple.
-/
def withCurriedDecl {α} (name : String) (type : Expr) (k : Array Expr MetaM α) : MetaM α := do
let some (d,c) := type.arrow? | throwError "withCurriedDecl: Expected arrow"
let motiveTypes (unpackPSum d).mapM (uncurryPSumArrow · c)
if let [t] := motiveTypes then
-- If a singleton, do not number the names.
withLocalDecl name .default t fun x => do k #[x]
else
go motiveTypes #[]
where
go : List Expr Array Expr MetaM α
| [], acc => k acc
| t::ts, acc => do
let name := s!"{name}{acc.size+1}"
withLocalDecl name .default t fun x => do
go ts (acc.push x)
/--
Given expression `e` of type `(x : a ⊗' b ⊕' c ⊗' d) → e[x]`, wraps that expression
to produce an expression of the isomorphic type
```
((x: a) → (y : b) → e[.inl (x,y)]) ∧ ((x : c) → (y : d) → e[.inr (x,y)])
```
-/
def deMorganPSumPSigma (e : Expr) : MetaM Expr := do
let packedDomain := ( inferType e).bindingDomain!
let unaryTypes := unpackPSum packedDomain
shareIf (unaryTypes.length > 1) e fun e => do
let mut es := #[]
for unaryType in unaryTypes, i in [:unaryTypes.length] do
-- unary : (x : a ⊗ b) → e[inl x]
let unary withLocalDecl `x .default unaryType fun x => do
let packedArg WF.mkMutualArg unaryTypes.length packedDomain i x
mkLambdaFVars #[x] (e.beta #[packedArg])
-- nary : (x : a) → (y : b) → e[inl (x,y)]
let nary uncurryPSigma unary
es := es.push nary
mkAndIntroN es
where
shareIf (b : Bool) (e : Expr) (k : Expr MetaM Expr) : MetaM Expr := do
if b then
withLetDecl `packed ( inferType e) e fun e => do mkLetFVars #[e] ( k e)
else
k e
-- Adapted from PackMutual: TODO: Compare and unify
/--
Combine/pack the values of the different definitions in a single value
`x` is `PSum`, and we use `PSum.casesOn` to select the appropriate `preDefs.value`.
See: `packMutual`.
Remark: this method does not replace the nested recursive `preDefValues` applications.
This step is performed by `transform` with the following `post` method.
-/
private def packValues (x : Expr) (codomain : Expr) (preDefValues : Array Expr) : MetaM Expr := do
let varNames := preDefValues.map fun val =>
if val.isLambda then val.bindingName! else `x
let mvar mkFreshExprSyntheticOpaqueMVar codomain
let rec go (mvarId : MVarId) (x : FVarId) (i : Nat) : MetaM Unit := do
if i < preDefValues.size - 1 then
/-
Names for the `cases` tactics. The names are important to preserve the user provided names (unary functions).
-/
let givenNames : Array AltVarNames :=
if i == preDefValues.size - 2 then
#[{ varNames := [varNames[i]!] }, { varNames := [varNames[i+1]!] }]
else
#[{ varNames := [varNames[i]!] }]
let #[s₁, s₂] mvarId.cases x (givenNames := givenNames) | unreachable!
s₁.mvarId.assign (mkApp preDefValues[i]! s₁.fields[0]!).headBeta
go s₂.mvarId s₂.fields[0]!.fvarId! (i+1)
else
mvarId.assign (mkApp preDefValues[i]! (mkFVar x)).headBeta
termination_by preDefValues.size - 1 - i
go mvar.mvarId! x.fvarId! 0
instantiateMVars mvar
/--
Takes an induction principle where the motive is a `PSigma`/`PSum` type and
unpacks it into a n-ary and (possibly) joint induction principle.
-/
def unpackMutualInduction (eqnInfo : WF.EqnInfo) (unaryInductName : Name) : MetaM Name := do
let inductName := if eqnInfo.declNames.size > 1 then
.append eqnInfo.declNames[0]! `mutual_induct
else
-- If there is no mutual recursion, generate the `foo.induct` directly.
.append eqnInfo.declNames[0]! `induct
if hasConst inductName then return inductName
let ci getConstInfo unaryInductName
let us := ci.levelParams
let value := .const ci.name (us.map mkLevelParam)
let motivePos forallTelescope ci.type fun xs concl => concl.withApp fun motive targets => do
unless motive.isFVar && targets.size = 1 && targets.all (·.isFVar) do
throwError "conclusion {concl} does not look like a packed motive application"
let packedTarget := targets[0]!
unless xs.back == packedTarget do
throwError "packed target not last argument to {unaryInductName}"
let some motivePos := xs.findIdx? (· == motive)
| throwError "could not find motive {motive} in {xs}"
pure motivePos
let value forallBoundedTelescope ci.type motivePos fun params type => do
let value := mkAppN value params
-- Next parameter is the motive (motive : a ⊗' b ⊕' c ⊗' d → Prop).
let packedMotiveType := type.bindingDomain!
-- Bring unpacked motives (motive1 : a → b → Prop and motive2 : c → d → Prop) into scope
withCurriedDecl "motive" packedMotiveType fun motives => do
-- Combine them into a packed motive (motive : a ⊗' b ⊕' c ⊗' d → Prop), and use that
let motive forallBoundedTelescope packedMotiveType (some 1) fun xs motiveCodomain => do
let #[x] := xs | throwError "packedMotiveType is not a forall: {packedMotiveType}"
let packedMotives motives.mapM curryPSigma
let motiveBody packValues x motiveCodomain packedMotives
mkLambdaFVars xs motiveBody
let type instantiateForall type #[motive]
let value := mkApp value motive
-- Bring the rest into scope
forallTelescope type fun xs _concl => do
let alts := xs.pop
let value := mkAppN value alts
let value deMorganPSumPSigma value
let value mkLambdaFVars alts value
let value mkLambdaFVars motives value
let value mkLambdaFVars params value
check value
let value cleanPackedArgs eqnInfo value
return value
unless isTypeCorrect value do
logError m!"failed to unpack induction priciple:{indentExpr value}"
check value
let type inferType value
let type elimOptParam type
addDecl <| Declaration.thmDecl
{ name := inductName, levelParams := ci.levelParams, type, value }
return inductName
/-- Given `foo._unary.induct`, define `foo.mutual_induct` and then `foo.induct`, `bar.induct`, … -/
def deriveUnpackedInduction (eqnInfo : WF.EqnInfo) (unaryInductName : Name): MetaM Unit := do
let unpackedInductName unpackMutualInduction eqnInfo unaryInductName
let ci getConstInfo unpackedInductName
let levelParams := ci.levelParams
for name in eqnInfo.declNames, idx in [:eqnInfo.declNames.size] do
let inductName := .append name `induct
unless hasConst inductName do
let value forallTelescope ci.type fun xs _body => do
let value := .const ci.name (levelParams.map mkLevelParam)
let value := mkAppN value xs
let value := mkProjAndN eqnInfo.declNames.size idx value
mkLambdaFVars xs value
let type inferType value
addDecl <| Declaration.thmDecl { name := inductName, levelParams, type, value }
/--
Given a recursively defined function `foo`, derives `foo.induct`. See the module doc for details.
-/
def deriveInduction (name : Name) : MetaM Unit := do
if let some eqnInfo := WF.eqnInfoExt.find? ( getEnv) name then
let unaryInductName deriveUnaryInduction eqnInfo.declNameNonRec
unless eqnInfo.declNameNonRec = name do
deriveUnpackedInduction eqnInfo unaryInductName
else
_ deriveUnaryInduction name
@[builtin_command_elab Parser.Command.deriveInduction]
def elabDeriveInduction : Command.CommandElab := fun stx => Command.runTermElabM fun _xs => do
let ident := stx[1]
let name resolveGlobalConstNoOverloadWithInfo ident
deriveInduction name
end Lean.Tactic.FunInd

View File

@@ -23,8 +23,7 @@ structure GeneralizeArg where
Telescopic `generalize` tactic. It can simultaneously generalize many terms.
It uses `kabstract` to occurrences of the terms that need to be generalized.
-/
private partial def generalizeCore (mvarId : MVarId) (args : Array GeneralizeArg)
(transparency : TransparencyMode) : MetaM (Array FVarId × MVarId) :=
private partial def generalizeCore (mvarId : MVarId) (args : Array GeneralizeArg) : MetaM (Array FVarId × MVarId) :=
mvarId.withContext do
mvarId.checkNotAssigned `generalize
let tag mvarId.getTag
@@ -36,8 +35,7 @@ private partial def generalizeCore (mvarId : MVarId) (args : Array GeneralizeArg
let eType instantiateMVars ( inferType e)
let type go (i+1)
let xName if let some xName := arg.xName? then pure xName else mkFreshUserName `x
return Lean.mkForall xName BinderInfo.default eType
( withTransparency transparency <| kabstract type e)
return Lean.mkForall xName BinderInfo.default eType ( kabstract type e)
else
return target
let targetNew go 0
@@ -73,62 +71,13 @@ private partial def generalizeCore (mvarId : MVarId) (args : Array GeneralizeArg
mvarId.assign (mkAppN (mkAppN mvarNew es) rfls.toArray)
mvarNew.mvarId!.introNP (args.size + rfls.length)
/-
Remark: we use `TransparencyMode.instances` as the default setting at `generalize`
and `generalizeHyp` to avoid excessive resource usage.
**Motivation:**
The `kabstract e p` operation is widely used, for instance, in the `generalize` tactic.
It operates by taking an expression `e` and a pattern (i.e., an expression containing metavariables)
and employs keyed-matching to identify and abstract instances of `p` within `e`.
For example, if `e` is `a + (2 * (b + c))` and `p` is `2 * ?m`, the resultant expression
would be `a + #0`, where `#0` represents a loose bound variable.
This matching process is not merely syntactic; it also considers reduction. It's impractical
to attempt matching each sub-term with `p`; therefore, only sub-terms sharing the same "root"
symbol are evaluated. For instance, with the pattern `2 * ?m`, only sub-terms with the
root `*` are considered. Matching is executed using the definitionally equality test
(i.e., `isDefEq`).
The `generalize` tactic employs `kabstract` and defaults to standard reducibility.
Hence, the `isDefEq` operations invoked by `kabstract` can become highly resource-intensive
and potentially trigger "max recursion depth reached" errors, as observed in issue #3524.
This issue was isolated by @**Scott Morrison** with the following example:
```
example (a : Nat) : ((2 ^ 7) + a) - 2 ^ 7 = 0 := by
generalize 0 - 0 = x
```
In this scenario, `kabstract` triggers a "max recursion depth reached" error while
testing whether `((2 ^ 7) + a) - 2 ^ 7` is definitionally equal to `0 - 0`.
Note that the term `((2 ^ 7) + a) - 2 ^ 7` is not ground.
We believe most users find the error message to be uninformative and unexpected.
To fix this issue, we decided to use `TransparencyMode.instances` as the default setting.
Kyle Miller has performed the following analysis on the potential impact of the
changes on Mathlib (2024-03-02).
There seem to be just 130 cases of generalize in Mathlib, and after looking through a
good number of them, they seem to come in just two types:
- Ones where it looks like reducible+instance transparency should work, where in
particular there is nothing obvious being reduced, and
- Ones that don't make use of the `kabstract` feature at all (it's being used like a
`have` that introduces an equality for rewriting).
That wasn't a systematic review of generalize though. It's possible changing the
transparency settings would break things, but in my opinion it would be better
if generalize weren't used for unfolding things.
-/
@[inherit_doc generalizeCore]
def _root_.Lean.MVarId.generalize (mvarId : MVarId) (args : Array GeneralizeArg)
(transparency := TransparencyMode.instances) : MetaM (Array FVarId × MVarId) :=
generalizeCore mvarId args transparency
def _root_.Lean.MVarId.generalize (mvarId : MVarId) (args : Array GeneralizeArg) : MetaM (Array FVarId × MVarId) :=
generalizeCore mvarId args
@[inherit_doc generalizeCore, deprecated MVarId.generalize]
def generalize (mvarId : MVarId) (args : Array GeneralizeArg)
(transparency := TransparencyMode.instances) : MetaM (Array FVarId × MVarId) :=
generalizeCore mvarId args transparency
def generalize (mvarId : MVarId) (args : Array GeneralizeArg) : MetaM (Array FVarId × MVarId) :=
generalizeCore mvarId args
/--
Extension of `generalize` to support generalizing within specified hypotheses.
@@ -136,16 +85,16 @@ The `hyps` array contains the list of hypotheses within which to look for occurr
of the generalizing expressions.
-/
def _root_.Lean.MVarId.generalizeHyp (mvarId : MVarId) (args : Array GeneralizeArg) (hyps : Array FVarId := #[])
(fvarSubst : FVarSubst := {}) (transparency := TransparencyMode.instances) : MetaM (FVarSubst × Array FVarId × MVarId) := do
(fvarSubst : FVarSubst := {}) : MetaM (FVarSubst × Array FVarId × MVarId) := do
if hyps.isEmpty then
-- trivial case
return (fvarSubst, mvarId.generalize args transparency)
return (fvarSubst, mvarId.generalize args)
let args args.mapM fun arg => return { arg with expr := instantiateMVars arg.expr }
let hyps hyps.filterM fun h => do
let type instantiateMVars ( h.getType)
args.anyM fun arg => return ( withTransparency transparency <| kabstract type arg.expr).hasLooseBVars
args.anyM fun arg => return ( kabstract type arg.expr).hasLooseBVars
let (reverted, mvarId) mvarId.revert hyps true
let (newVars, mvarId) mvarId.generalize args transparency
let (newVars, mvarId) mvarId.generalize args
let (reintros, mvarId) mvarId.introNP reverted.size
let fvarSubst := Id.run do
let mut subst : FVarSubst := fvarSubst

View File

@@ -57,6 +57,9 @@ inductive DeclMod
| /-- the backward direction of an `iff` -/ mpr
deriving DecidableEq, Inhabited, Ord
instance : ToString DeclMod where
toString m := match m with | .none => "" | .mp => "mp" | .mpr => "mpr"
/--
LibrarySearch has an extension mechanism for replacing the function used
to find candidate lemmas.
@@ -282,26 +285,6 @@ def mkLibrarySearchLemma (lem : Name) (mod : DeclMod) : MetaM Expr := do
| .mp => mapForallTelescope (fun e => mkAppM ``Iff.mp #[e]) lem
| .mpr => mapForallTelescope (fun e => mkAppM ``Iff.mpr #[e]) lem
private def isVar (e : Expr) : Bool :=
match e with
| .bvar _ => true
| .fvar _ => true
| .mvar _ => true
| _ => false
private def isNonspecific (type : Expr) : MetaM Bool := do
forallTelescope type fun _ tp =>
match tp.getAppFn with
| .bvar _ => pure true
| .fvar _ => pure true
| .mvar _ => pure true
| .const nm _ =>
if nm = ``Eq then
pure (tp.getAppArgsN 3 |>.all isVar)
else
pure false
| _ => pure false
/--
Tries to apply the given lemma (with symmetry modifier) to the goal,
then tries to close subsequent goals using `solveByElim`.
@@ -311,14 +294,9 @@ otherwise the full list of subgoals is returned.
private def librarySearchLemma (cfg : ApplyConfig) (act : List MVarId MetaM (List MVarId))
(allowFailure : MVarId MetaM Bool) (cand : Candidate) : MetaM (List MVarId) := do
let ((goal, mctx), (name, mod)) := cand
let ppMod (mod : DeclMod) : MessageData :=
match mod with | .none => "" | .mp => " with mp" | .mpr => " with mpr"
withTraceNode `Tactic.librarySearch (return m!"{emoji ·} trying {name}{ppMod mod} ") do
withTraceNode `Tactic.librarySearch (return m!"{emoji ·} trying {name} with {mod} ") do
setMCtx mctx
let lem mkLibrarySearchLemma name mod
let lemType instantiateMVars ( inferType lem)
if isNonspecific lemType then
failure
let newGoals goal.apply lem cfg
try
act newGoals

View File

@@ -74,31 +74,42 @@ def addAsVar (e : Expr) : M LinearExpr := do
partial def toLinearExpr (e : Expr) : M LinearExpr := do
match e with
| .lit (.natVal n) => return num n
| .mdata _ e => toLinearExpr e
| .const ``Nat.zero .. => return num 0
| .app .. => visit e
| .mvar .. => visit e
| _ => addAsVar e
| Expr.lit (Literal.natVal n) => return num n
| Expr.mdata _ e => toLinearExpr e
| Expr.const ``Nat.zero .. => return num 0
| Expr.app .. => visit e
| Expr.mvar .. => visit e
| _ => addAsVar e
where
visit (e : Expr) : M LinearExpr := do
match_expr e with
| Nat.succ a => return inc ( toLinearExpr a)
| Nat.add a b => return add ( toLinearExpr a) ( toLinearExpr b)
| Nat.mul a b =>
match ( evalNat a |>.run) with
| some k => return mulL k ( toLinearExpr b)
| none => match ( evalNat b |>.run) with
| some k => return mulR ( toLinearExpr a) k
| none => addAsVar e
| _ =>
let e instantiateMVarsIfMVarApp e
let f := e.getAppFn
if f.isConst && isNatProjInst f.constName! e.getAppNumArgs then
let some e unfoldProjInst? e | addAsVar e
toLinearExpr e
let f := e.getAppFn
match f with
| Expr.mvar .. =>
let eNew instantiateMVars e
if eNew != e then
toLinearExpr eNew
else
addAsVar e
| Expr.const declName .. =>
let numArgs := e.getAppNumArgs
if declName == ``Nat.succ && numArgs == 1 then
return inc ( toLinearExpr e.appArg!)
else if declName == ``Nat.add && numArgs == 2 then
return add ( toLinearExpr (e.getArg! 0)) ( toLinearExpr (e.getArg! 1))
else if declName == ``Nat.mul && numArgs == 2 then
match ( evalNat (e.getArg! 0) |>.run) with
| some k => return mulL k ( toLinearExpr (e.getArg! 1))
| none => match ( evalNat (e.getArg! 1) |>.run) with
| some k => return mulR ( toLinearExpr (e.getArg! 0)) k
| none => addAsVar e
else if isNatProjInst declName numArgs then
if let some e unfoldProjInst? e then
toLinearExpr e
else
addAsVar e
else
addAsVar e
| _ => addAsVar e
partial def toLinearCnstr? (e : Expr) : M (Option LinearCnstr) := do
let f := e.getAppFn

View File

@@ -8,7 +8,6 @@ import Lean.Meta.AppBuilder
import Lean.Meta.MatchUtil
import Lean.Meta.KAbstract
import Lean.Meta.Check
import Lean.Meta.Tactic.Util
import Lean.Meta.Tactic.Apply
namespace Lean.Meta
@@ -53,8 +52,7 @@ def _root_.Lean.MVarId.rewrite (mvarId : MVarId) (e : Expr) (heq : Expr)
let u1 getLevel α
let u2 getLevel eType
let eqPrf := mkApp6 (.const ``congrArg [u1, u2]) α eType lhs rhs motive heq
postprocessAppMVars `rewrite mvarId newMVars binderInfos
(synthAssignedInstances := !tactic.skipAssignedInstances.get ( getOptions))
postprocessAppMVars `rewrite mvarId newMVars binderInfos (synthAssignedInstances := false)
let newMVarIds newMVars.map Expr.mvarId! |>.filterM fun mvarId => not <$> mvarId.isAssigned
let otherMVarIds getMVarsNoDelayed eqPrf
let otherMVarIds := otherMVarIds.filter (!newMVarIds.contains ·)

View File

@@ -31,185 +31,176 @@ def fromExpr? (e : Expr) : SimpM (Option Literal) := do
Helper function for reducing homogenous unary bitvector operators.
-/
@[inline] def reduceUnary (declName : Name) (arity : Nat)
(op : {n : Nat} BitVec n BitVec n) (e : Expr) : SimpM DStep := do
(op : {n : Nat} BitVec n BitVec n) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some v fromExpr? e.appArg! | return .continue
return .done <| toExpr (op v.value)
return .done { expr := toExpr (op v.value) }
/--
Helper function for reducing homogenous binary bitvector operators.
-/
@[inline] def reduceBin (declName : Name) (arity : Nat)
(op : {n : Nat} BitVec n BitVec n BitVec n) (e : Expr) : SimpM DStep := do
(op : {n : Nat} BitVec n BitVec n BitVec n) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ fromExpr? e.appArg! | return .continue
if h : v₁.n = v₂.n then
trace[Meta.debug] "reduce [{declName}] {v₁.value}, {v₂.value}"
return .done <| toExpr (op v₁.value (h v₂.value))
return .done { expr := toExpr (op v₁.value (h v₂.value)) }
else
return .continue
/-- Simplification procedure for `zeroExtend` and `signExtend` on `BitVec`s. -/
@[inline] def reduceExtend (declName : Name)
(op : {n : Nat} (m : Nat) BitVec n BitVec m) (e : Expr) : SimpM DStep := do
(op : {n : Nat} (m : Nat) BitVec n BitVec m) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName 3 do return .continue
let some v fromExpr? e.appArg! | return .continue
let some n Nat.fromExpr? e.appFn!.appArg! | return .continue
return .done <| toExpr (op n v.value)
return .done { expr := toExpr (op n v.value) }
/--
Helper function for reducing bitvector functions such as `getLsb` and `getMsb`.
-/
@[inline] def reduceGetBit (declName : Name) (op : {n : Nat} BitVec n Nat Bool) (e : Expr)
: SimpM DStep := do
: SimpM Step := do
unless e.isAppOfArity declName 3 do return .continue
let some v fromExpr? e.appFn!.appArg! | return .continue
let some i Nat.fromExpr? e.appArg! | return .continue
let b := op v.value i
return .done <| toExpr b
return .done { expr := toExpr b }
/--
Helper function for reducing bitvector functions such as `shiftLeft` and `rotateRight`.
-/
@[inline] def reduceShift (declName : Name) (arity : Nat)
(op : {n : Nat} BitVec n Nat BitVec n) (e : Expr) : SimpM DStep := do
(op : {n : Nat} BitVec n Nat BitVec n) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some v fromExpr? e.appFn!.appArg! | return .continue
let some i Nat.fromExpr? e.appArg! | return .continue
return .done <| toExpr (op v.value i)
return .done { expr := toExpr (op v.value i) }
/--
Helper function for reducing bitvector predicates.
-/
@[inline] def reduceBinPred (declName : Name) (arity : Nat)
(op : {n : Nat} BitVec n BitVec n Bool) (e : Expr) : SimpM Step := do
(op : {n : Nat} BitVec n BitVec n Bool) (e : Expr) (isProp := true) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ fromExpr? e.appArg! | return .continue
if h : v₁.n = v₂.n then
let b := op v₁.value (h v₂.value)
evalPropStep e b
if isProp then
evalPropStep e b
else
return .done { expr := toExpr b }
else
return .continue
@[inline] def reduceBoolPred (declName : Name) (arity : Nat)
(op : {n : Nat} BitVec n BitVec n Bool) (e : Expr) : SimpM DStep := do
unless e.isAppOfArity declName arity do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ fromExpr? e.appArg! | return .continue
if h : v₁.n = v₂.n then
let b := op v₁.value (h v₂.value)
return .done <| toExpr b
else
return .continue
/-- Simplification procedure for negation of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceNeg ((- _ : BitVec _)) := reduceUnary ``Neg.neg 3 (- ·)
builtin_simproc [simp, seval] reduceNeg ((- _ : BitVec _)) := reduceUnary ``Neg.neg 3 (- ·)
/-- Simplification procedure for bitwise not of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceNot ((~~~ _ : BitVec _)) :=
builtin_simproc [simp, seval] reduceNot ((~~~ _ : BitVec _)) :=
reduceUnary ``Complement.complement 3 (~~~ ·)
/-- Simplification procedure for absolute value of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceAbs (BitVec.abs _) := reduceUnary ``BitVec.abs 2 BitVec.abs
builtin_simproc [simp, seval] reduceAbs (BitVec.abs _) := reduceUnary ``BitVec.abs 2 BitVec.abs
/-- Simplification procedure for bitwise and of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceAnd ((_ &&& _ : BitVec _)) := reduceBin ``HAnd.hAnd 6 (· &&& ·)
builtin_simproc [simp, seval] reduceAnd ((_ &&& _ : BitVec _)) := reduceBin ``HAnd.hAnd 6 (· &&& ·)
/-- Simplification procedure for bitwise or of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceOr ((_ ||| _ : BitVec _)) := reduceBin ``HOr.hOr 6 (· ||| ·)
builtin_simproc [simp, seval] reduceOr ((_ ||| _ : BitVec _)) := reduceBin ``HOr.hOr 6 (· ||| ·)
/-- Simplification procedure for bitwise xor of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceXOr ((_ ^^^ _ : BitVec _)) := reduceBin ``HXor.hXor 6 (· ^^^ ·)
builtin_simproc [simp, seval] reduceXOr ((_ ^^^ _ : BitVec _)) := reduceBin ``HXor.hXor 6 (· ^^^ ·)
/-- Simplification procedure for addition of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceAdd ((_ + _ : BitVec _)) := reduceBin ``HAdd.hAdd 6 (· + ·)
builtin_simproc [simp, seval] reduceAdd ((_ + _ : BitVec _)) := reduceBin ``HAdd.hAdd 6 (· + ·)
/-- Simplification procedure for multiplication of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceMul ((_ * _ : BitVec _)) := reduceBin ``HMul.hMul 6 (· * ·)
builtin_simproc [simp, seval] reduceMul ((_ * _ : BitVec _)) := reduceBin ``HMul.hMul 6 (· * ·)
/-- Simplification procedure for subtraction of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceSub ((_ - _ : BitVec _)) := reduceBin ``HSub.hSub 6 (· - ·)
builtin_simproc [simp, seval] reduceSub ((_ - _ : BitVec _)) := reduceBin ``HSub.hSub 6 (· - ·)
/-- Simplification procedure for division of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceDiv ((_ / _ : BitVec _)) := reduceBin ``HDiv.hDiv 6 (· / ·)
builtin_simproc [simp, seval] reduceDiv ((_ / _ : BitVec _)) := reduceBin ``HDiv.hDiv 6 (· / ·)
/-- Simplification procedure for the modulo operation on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceMod ((_ % _ : BitVec _)) := reduceBin ``HMod.hMod 6 (· % ·)
builtin_simproc [simp, seval] reduceMod ((_ % _ : BitVec _)) := reduceBin ``HMod.hMod 6 (· % ·)
/-- Simplification procedure for for the unsigned modulo operation on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceUMod ((umod _ _ : BitVec _)) := reduceBin ``umod 3 umod
builtin_simproc [simp, seval] reduceUMod ((umod _ _ : BitVec _)) := reduceBin ``umod 3 umod
/-- Simplification procedure for unsigned division of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceUDiv ((udiv _ _ : BitVec _)) := reduceBin ``udiv 3 udiv
builtin_simproc [simp, seval] reduceUDiv ((udiv _ _ : BitVec _)) := reduceBin ``udiv 3 udiv
/-- Simplification procedure for division of `BitVec`s using the SMT-Lib conventions. -/
builtin_dsimproc [simp, seval] reduceSMTUDiv ((smtUDiv _ _ : BitVec _)) := reduceBin ``smtUDiv 3 smtUDiv
builtin_simproc [simp, seval] reduceSMTUDiv ((smtUDiv _ _ : BitVec _)) := reduceBin ``smtUDiv 3 smtUDiv
/-- Simplification procedure for the signed modulo operation on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceSMod ((smod _ _ : BitVec _)) := reduceBin ``smod 3 smod
builtin_simproc [simp, seval] reduceSMod ((smod _ _ : BitVec _)) := reduceBin ``smod 3 smod
/-- Simplification procedure for signed remainder of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceSRem ((srem _ _ : BitVec _)) := reduceBin ``srem 3 srem
builtin_simproc [simp, seval] reduceSRem ((srem _ _ : BitVec _)) := reduceBin ``srem 3 srem
/-- Simplification procedure for signed t-division of `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceSDiv ((sdiv _ _ : BitVec _)) := reduceBin ``sdiv 3 sdiv
builtin_simproc [simp, seval] reduceSDiv ((sdiv _ _ : BitVec _)) := reduceBin ``sdiv 3 sdiv
/-- Simplification procedure for signed division of `BitVec`s using the SMT-Lib conventions. -/
builtin_dsimproc [simp, seval] reduceSMTSDiv ((smtSDiv _ _ : BitVec _)) := reduceBin ``smtSDiv 3 smtSDiv
builtin_simproc [simp, seval] reduceSMTSDiv ((smtSDiv _ _ : BitVec _)) := reduceBin ``smtSDiv 3 smtSDiv
/-- Simplification procedure for `getLsb` (lowest significant bit) on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceGetLsb (getLsb _ _) := reduceGetBit ``getLsb getLsb
builtin_simproc [simp, seval] reduceGetLsb (getLsb _ _) := reduceGetBit ``getLsb getLsb
/-- Simplification procedure for `getMsb` (most significant bit) on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceGetMsb (getMsb _ _) := reduceGetBit ``getMsb getMsb
builtin_simproc [simp, seval] reduceGetMsb (getMsb _ _) := reduceGetBit ``getMsb getMsb
/-- Simplification procedure for shift left on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceShiftLeft (BitVec.shiftLeft _ _) :=
builtin_simproc [simp, seval] reduceShiftLeft (BitVec.shiftLeft _ _) :=
reduceShift ``BitVec.shiftLeft 3 BitVec.shiftLeft
/-- Simplification procedure for unsigned shift right on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceUShiftRight (BitVec.ushiftRight _ _) :=
builtin_simproc [simp, seval] reduceUShiftRight (BitVec.ushiftRight _ _) :=
reduceShift ``BitVec.ushiftRight 3 BitVec.ushiftRight
/-- Simplification procedure for signed shift right on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceSShiftRight (BitVec.sshiftRight _ _) :=
builtin_simproc [simp, seval] reduceSShiftRight (BitVec.sshiftRight _ _) :=
reduceShift ``BitVec.sshiftRight 3 BitVec.sshiftRight
/-- Simplification procedure for shift left on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceHShiftLeft ((_ <<< _ : BitVec _)) :=
builtin_simproc [simp, seval] reduceHShiftLeft ((_ <<< _ : BitVec _)) :=
reduceShift ``HShiftLeft.hShiftLeft 6 (· <<< ·)
/-- Simplification procedure for shift right on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceHShiftRight ((_ >>> _ : BitVec _)) :=
builtin_simproc [simp, seval] reduceHShiftRight ((_ >>> _ : BitVec _)) :=
reduceShift ``HShiftRight.hShiftRight 6 (· >>> ·)
/-- Simplification procedure for rotate left on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceRotateLeft (BitVec.rotateLeft _ _) :=
builtin_simproc [simp, seval] reduceRotateLeft (BitVec.rotateLeft _ _) :=
reduceShift ``BitVec.rotateLeft 3 BitVec.rotateLeft
/-- Simplification procedure for rotate right on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceRotateRight (BitVec.rotateRight _ _) :=
builtin_simproc [simp, seval] reduceRotateRight (BitVec.rotateRight _ _) :=
reduceShift ``BitVec.rotateRight 3 BitVec.rotateRight
/-- Simplification procedure for append on `BitVec`. -/
builtin_dsimproc [simp, seval] reduceAppend ((_ ++ _ : BitVec _)) := fun e => do
let_expr HAppend.hAppend _ _ _ _ a b e | return .continue
let some v₁ fromExpr? a | return .continue
let some v₂ fromExpr? b | return .continue
return .done <| toExpr (v₁.value ++ v₂.value)
builtin_simproc [simp, seval] reduceAppend ((_ ++ _ : BitVec _)) := fun e => do
unless e.isAppOfArity ``HAppend.hAppend 6 do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ fromExpr? e.appArg! | return .continue
return .done { expr := toExpr (v₁.value ++ v₂.value) }
/-- Simplification procedure for casting `BitVec`s along an equality of the size. -/
builtin_dsimproc [simp, seval] reduceCast (cast _ _) := fun e => do
let_expr cast _ m _ v e | return .continue
let some v fromExpr? v | return .continue
let some m Nat.fromExpr? m | return .continue
return .done <| toExpr (BitVec.ofNat m v.value.toNat)
builtin_simproc [simp, seval] reduceCast (cast _ _) := fun e => do
unless e.isAppOfArity ``cast 4 do return .continue
let some v fromExpr? e.appArg! | return .continue
let some m Nat.fromExpr? e.appFn!.appFn!.appArg! | return .continue
return .done { expr := toExpr (BitVec.ofNat m v.value.toNat) }
/-- Simplification procedure for `BitVec.toNat`. -/
builtin_dsimproc [simp, seval] reduceToNat (BitVec.toNat _) := fun e => do
let_expr BitVec.toNat _ v e | return .continue
let some v fromExpr? v | return .continue
return .done <| mkNatLit v.value.toNat
builtin_simproc [simp, seval] reduceToNat (BitVec.toNat _) := fun e => do
unless e.isAppOfArity ``BitVec.toNat 2 do return .continue
let some v fromExpr? e.appArg! | return .continue
return .done { expr := mkNatLit v.value.toNat }
/-- Simplification procedure for `BitVec.toInt`. -/
builtin_dsimproc [simp, seval] reduceToInt (BitVec.toInt _) := fun e => do
let_expr BitVec.toInt _ v e | return .continue
let some v fromExpr? v | return .continue
return .done <| toExpr v.value.toInt
builtin_simproc [simp, seval] reduceToInt (BitVec.toInt _) := fun e => do
unless e.isAppOfArity ``BitVec.toInt 2 do return .continue
let some v fromExpr? e.appArg! | return .continue
return .done { expr := toExpr v.value.toInt }
/-- Simplification procedure for `BitVec.ofInt`. -/
builtin_dsimproc [simp, seval] reduceOfInt (BitVec.ofInt _ _) := fun e => do
let_expr BitVec.ofInt n i e | return .continue
let some n Nat.fromExpr? n | return .continue
let some i Int.fromExpr? i | return .continue
return .done <| toExpr (BitVec.ofInt n i)
builtin_simproc [simp, seval] reduceOfInt (BitVec.ofInt _ _) := fun e => do
unless e.isAppOfArity ``BitVec.ofInt 2 do return .continue
let some n Nat.fromExpr? e.appFn!.appArg! | return .continue
let some i Int.fromExpr? e.appArg! | return .continue
return .done { expr := toExpr (BitVec.ofInt n i) }
/-- Simplification procedure for ensuring `BitVec.ofNat` literals are normalized. -/
builtin_dsimproc [simp, seval] reduceOfNat (BitVec.ofNat _ _) := fun e => do
let_expr BitVec.ofNat n v e | return .continue
let some n Nat.fromExpr? n | return .continue
let some v Nat.fromExpr? v | return .continue
builtin_simproc [simp, seval] reduceOfNat (BitVec.ofNat _ _) := fun e => do
unless e.isAppOfArity ``BitVec.ofNat 2 do return .continue
let some n Nat.fromExpr? e.appFn!.appArg! | return .continue
let some v Nat.fromExpr? e.appArg! | return .continue
let bv := BitVec.ofNat n v
if bv.toNat == v then return .continue -- already normalized
return .done <| toExpr (BitVec.ofNat n v)
return .done { expr := toExpr (BitVec.ofNat n v) }
/-- Simplification procedure for `<` on `BitVec`s. -/
builtin_simproc [simp, seval] reduceLT (( _ : BitVec _) < _) := reduceBinPred ``LT.lt 4 (· < ·)
@@ -221,71 +212,60 @@ builtin_simproc [simp, seval] reduceGT (( _ : BitVec _) > _) := reduceBinPred `
builtin_simproc [simp, seval] reduceGE (( _ : BitVec _) _) := reduceBinPred ``GE.ge 4 (. .)
/-- Simplification procedure for unsigned less than `ult` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceULT (BitVec.ult _ _) :=
reduceBoolPred ``BitVec.ult 3 BitVec.ult
builtin_simproc [simp, seval] reduceULT (BitVec.ult _ _) :=
reduceBinPred ``BitVec.ult 3 BitVec.ult (isProp := false)
/-- Simplification procedure for unsigned less than or equal `ule` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceULE (BitVec.ule _ _) :=
reduceBoolPred ``BitVec.ule 3 BitVec.ule
builtin_simproc [simp, seval] reduceULE (BitVec.ule _ _) :=
reduceBinPred ``BitVec.ule 3 BitVec.ule (isProp := false)
/-- Simplification procedure for signed less than `slt` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceSLT (BitVec.slt _ _) :=
reduceBoolPred ``BitVec.slt 3 BitVec.slt
builtin_simproc [simp, seval] reduceSLT (BitVec.slt _ _) :=
reduceBinPred ``BitVec.slt 3 BitVec.slt (isProp := false)
/-- Simplification procedure for signed less than or equal `sle` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceSLE (BitVec.sle _ _) :=
reduceBoolPred ``BitVec.sle 3 BitVec.sle
builtin_simproc [simp, seval] reduceSLE (BitVec.sle _ _) :=
reduceBinPred ``BitVec.sle 3 BitVec.sle (isProp := false)
/-- Simplification procedure for `zeroExtend'` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceZeroExtend' (zeroExtend' _ _) := fun e => do
let_expr zeroExtend' _ w _ v e | return .continue
let some v fromExpr? v | return .continue
let some w Nat.fromExpr? w | return .continue
builtin_simproc [simp, seval] reduceZeroExtend' (zeroExtend' _ _) := fun e => do
unless e.isAppOfArity ``zeroExtend' 4 do return .continue
let some v fromExpr? e.appArg! | return .continue
let some w Nat.fromExpr? e.appFn!.appFn!.appArg! | return .continue
if h : v.n w then
return .done <| toExpr (v.value.zeroExtend' h)
return .done { expr := toExpr (v.value.zeroExtend' h) }
else
return .continue
/-- Simplification procedure for `shiftLeftZeroExtend` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceShiftLeftZeroExtend (shiftLeftZeroExtend _ _) := fun e => do
let_expr shiftLeftZeroExtend _ v m e | return .continue
let some v fromExpr? v | return .continue
let some m Nat.fromExpr? m | return .continue
return .done <| toExpr (v.value.shiftLeftZeroExtend m)
builtin_simproc [simp, seval] reduceShiftLeftZeroExtend (shiftLeftZeroExtend _ _) := fun e => do
unless e.isAppOfArity ``shiftLeftZeroExtend 3 do return .continue
let some v fromExpr? e.appFn!.appArg! | return .continue
let some m Nat.fromExpr? e.appArg! | return .continue
return .done { expr := toExpr (v.value.shiftLeftZeroExtend m) }
/-- Simplification procedure for `extractLsb'` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceExtracLsb' (extractLsb' _ _ _) := fun e => do
let_expr extractLsb' _ start len v e | return .continue
let some v fromExpr? v | return .continue
let some start Nat.fromExpr? start | return .continue
let some len Nat.fromExpr? len | return .continue
return .done <| toExpr (v.value.extractLsb' start len)
builtin_simproc [simp, seval] reduceExtracLsb' (extractLsb' _ _ _) := fun e => do
unless e.isAppOfArity ``extractLsb' 4 do return .continue
let some v fromExpr? e.appArg! | return .continue
let some start Nat.fromExpr? e.appFn!.appFn!.appArg! | return .continue
let some len Nat.fromExpr? e.appFn!.appArg! | return .continue
return .done { expr := toExpr (v.value.extractLsb' start len) }
/-- Simplification procedure for `replicate` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceReplicate (replicate _ _) := fun e => do
let_expr replicate _ i v e | return .continue
let some v fromExpr? v | return .continue
let some i Nat.fromExpr? i | return .continue
return .done <| toExpr (v.value.replicate i)
builtin_simproc [simp, seval] reduceReplicate (replicate _ _) := fun e => do
unless e.isAppOfArity ``replicate 3 do return .continue
let some v fromExpr? e.appArg! | return .continue
let some w Nat.fromExpr? e.appFn!.appArg! | return .continue
return .done { expr := toExpr (v.value.replicate w) }
/-- Simplification procedure for `zeroExtend` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceZeroExtend (zeroExtend _ _) := reduceExtend ``zeroExtend zeroExtend
builtin_simproc [simp, seval] reduceZeroExtend (zeroExtend _ _) := reduceExtend ``zeroExtend zeroExtend
/-- Simplification procedure for `signExtend` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceSignExtend (signExtend _ _) := reduceExtend ``signExtend signExtend
builtin_simproc [simp, seval] reduceSignExtend (signExtend _ _) := reduceExtend ``signExtend signExtend
/-- Simplification procedure for `allOnes` -/
builtin_dsimproc [simp, seval] reduceAllOnes (allOnes _) := fun e => do
let_expr allOnes n e | return .continue
let some n Nat.fromExpr? n | return .continue
return .done <| toExpr (allOnes n)
builtin_dsimproc [simp, seval] reduceBitVecOfFin (BitVec.ofFin _) := fun e => do
let_expr BitVec.ofFin w v e | return .continue
let some w evalNat w |>.run | return .continue
let some _, v getFinValue? v | return .continue
return .done <| toExpr (BitVec.ofNat w v.val)
builtin_dsimproc [simp, seval] reduceBitVecToFin (BitVec.toFin _) := fun e => do
let_expr BitVec.toFin _ v e | return .continue
let some _, v getBitVecValue? v | return .continue
return .done <| toExpr v.toFin
builtin_simproc [simp, seval] reduceAllOnes (allOnes _) := fun e => do
unless e.isAppOfArity ``allOnes 1 do return .continue
let some n Nat.fromExpr? e.appArg! | return .continue
return .done { expr := toExpr (allOnes n) }
end BitVec

View File

@@ -14,10 +14,10 @@ open Lean Meta Simp
def fromExpr? (e : Expr) : SimpM (Option Char) :=
getCharValue? e
@[inline] def reduceUnary [ToExpr α] (declName : Name) (op : Char α) (arity : Nat := 1) (e : Expr) : SimpM DStep := do
@[inline] def reduceUnary [ToExpr α] (declName : Name) (op : Char α) (arity : Nat := 1) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some c fromExpr? e.appArg! | return .continue
return .done <| toExpr (op c)
return .done { expr := toExpr (op c) }
@[inline] def reduceBinPred (declName : Name) (arity : Nat) (op : Char Char Bool) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
@@ -25,47 +25,47 @@ def fromExpr? (e : Expr) : SimpM (Option Char) :=
let some m fromExpr? e.appArg! | return .continue
evalPropStep e (op n m)
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Char Char Bool) (e : Expr) : SimpM DStep := do
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Char Char Bool) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some n fromExpr? e.appFn!.appArg! | return .continue
let some m fromExpr? e.appArg! | return .continue
return .done <| toExpr (op n m)
return .done { expr := toExpr (op n m) }
builtin_dsimproc [simp, seval] reduceToLower (Char.toLower _) := reduceUnary ``Char.toLower Char.toLower
builtin_dsimproc [simp, seval] reduceToUpper (Char.toUpper _) := reduceUnary ``Char.toUpper Char.toUpper
builtin_dsimproc [simp, seval] reduceToNat (Char.toNat _) := reduceUnary ``Char.toNat Char.toNat
builtin_dsimproc [simp, seval] reduceIsWhitespace (Char.isWhitespace _) := reduceUnary ``Char.isWhitespace Char.isWhitespace
builtin_dsimproc [simp, seval] reduceIsUpper (Char.isUpper _) := reduceUnary ``Char.isUpper Char.isUpper
builtin_dsimproc [simp, seval] reduceIsLower (Char.isLower _) := reduceUnary ``Char.isLower Char.isLower
builtin_dsimproc [simp, seval] reduceIsAlpha (Char.isAlpha _) := reduceUnary ``Char.isAlpha Char.isAlpha
builtin_dsimproc [simp, seval] reduceIsDigit (Char.isDigit _) := reduceUnary ``Char.isDigit Char.isDigit
builtin_dsimproc [simp, seval] reduceIsAlphaNum (Char.isAlphanum _) := reduceUnary ``Char.isAlphanum Char.isAlphanum
builtin_dsimproc [simp, seval] reduceToString (toString (_ : Char)) := reduceUnary ``toString toString 3
builtin_dsimproc [simp, seval] reduceVal (Char.val _) := fun e => do
let_expr Char.val arg e | return .continue
let some c fromExpr? arg | return .continue
return .done <| toExpr c.val
builtin_simproc [simp, seval] reduceToLower (Char.toLower _) := reduceUnary ``Char.toLower Char.toLower
builtin_simproc [simp, seval] reduceToUpper (Char.toUpper _) := reduceUnary ``Char.toUpper Char.toUpper
builtin_simproc [simp, seval] reduceToNat (Char.toNat _) := reduceUnary ``Char.toNat Char.toNat
builtin_simproc [simp, seval] reduceIsWhitespace (Char.isWhitespace _) := reduceUnary ``Char.isWhitespace Char.isWhitespace
builtin_simproc [simp, seval] reduceIsUpper (Char.isUpper _) := reduceUnary ``Char.isUpper Char.isUpper
builtin_simproc [simp, seval] reduceIsLower (Char.isLower _) := reduceUnary ``Char.isLower Char.isLower
builtin_simproc [simp, seval] reduceIsAlpha (Char.isAlpha _) := reduceUnary ``Char.isAlpha Char.isAlpha
builtin_simproc [simp, seval] reduceIsDigit (Char.isDigit _) := reduceUnary ``Char.isDigit Char.isDigit
builtin_simproc [simp, seval] reduceIsAlphaNum (Char.isAlphanum _) := reduceUnary ``Char.isAlphanum Char.isAlphanum
builtin_simproc [simp, seval] reduceToString (toString (_ : Char)) := reduceUnary ``toString toString 3
builtin_simproc [simp, seval] reduceVal (Char.val _) := fun e => do
unless e.isAppOfArity ``Char.val 1 do return .continue
let some c fromExpr? e.appArg! | return .continue
return .done { expr := toExpr c.val }
builtin_simproc [simp, seval] reduceEq (( _ : Char) = _) := reduceBinPred ``Eq 3 (. = .)
builtin_simproc [simp, seval] reduceNe (( _ : Char) _) := reduceBinPred ``Ne 3 (. .)
builtin_dsimproc [simp, seval] reduceBEq (( _ : Char) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_dsimproc [simp, seval] reduceBNe (( _ : Char) != _) := reduceBoolPred ``bne 4 (. != .)
builtin_simproc [simp, seval] reduceBEq (( _ : Char) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_simproc [simp, seval] reduceBNe (( _ : Char) != _) := reduceBoolPred ``bne 4 (. != .)
/--
Return `.done` for Char values. We don't want to unfold in the symbolic evaluator.
In regular `simp`, we want to prevent the nested raw literal from being converted into
a `OfNat.ofNat` application. TODO: cleanup
-/
builtin_dsimproc [simp, seval] isValue (Char.ofNat _ ) := fun e => do
builtin_simproc [simp, seval] isValue (Char.ofNat _ ) := fun e => do
unless ( fromExpr? e).isSome do return .continue
return .done e
return .done { expr := e }
builtin_dsimproc [simp, seval] reduceOfNatAux (Char.ofNatAux _ _) := fun e => do
let_expr Char.ofNatAux n _ e | return .continue
let some n Nat.fromExpr? n | return .continue
return .done <| toExpr (Char.ofNat n)
builtin_simproc [simp, seval] reduceOfNatAux (Char.ofNatAux _ _) := fun e => do
unless e.isAppOfArity ``Char.ofNatAux 2 do return .continue
let some n Nat.fromExpr? e.appFn!.appArg! | return .continue
return .done { expr := toExpr (Char.ofNat n) }
builtin_dsimproc [simp, seval] reduceDefault ((default : Char)) := fun e => do
let_expr default _ _ e | return .continue
return .done <| toExpr (default : Char)
builtin_simproc [simp, seval] reduceDefault ((default : Char)) := fun e => do
unless e.isAppOfArity ``default 2 do return .continue
return .done { expr := toExpr (default : Char) }
end Char

View File

@@ -10,29 +10,33 @@ import Lean.Meta.Tactic.Simp.Simproc
open Lean Meta Simp
builtin_simproc [simp, seval] reduceIte (ite _ _ _) := fun e => do
let_expr f@ite α c i tb eb e | return .continue
unless e.isAppOfArity ``ite 5 do return .continue
let c := e.getArg! 1
let r simp c
if r.expr.isTrue then
let pr := mkApp (mkApp5 (mkConst ``ite_cond_eq_true f.constLevels!) α c i tb eb) ( r.getProof)
return .visit { expr := tb, proof? := pr }
let eNew := e.getArg! 3
let pr := mkApp (mkAppN (mkConst ``ite_cond_eq_true e.getAppFn.constLevels!) e.getAppArgs) ( r.getProof)
return .visit { expr := eNew, proof? := pr }
if r.expr.isFalse then
let pr := mkApp (mkApp5 (mkConst ``ite_cond_eq_false f.constLevels!) α c i tb eb) ( r.getProof)
return .visit { expr := eb, proof? := pr }
let eNew := e.getArg! 4
let pr := mkApp (mkAppN (mkConst ``ite_cond_eq_false e.getAppFn.constLevels!) e.getAppArgs) ( r.getProof)
return .visit { expr := eNew, proof? := pr }
return .continue
builtin_simproc [simp, seval] reduceDite (dite _ _ _) := fun e => do
let_expr f@dite α c i tb eb e | return .continue
unless e.isAppOfArity ``dite 5 do return .continue
let c := e.getArg! 1
let r simp c
if r.expr.isTrue then
let pr r.getProof
let h := mkApp2 (mkConst ``of_eq_true) c pr
let eNew := mkApp tb h |>.headBeta
let prNew := mkApp (mkApp5 (mkConst ``dite_cond_eq_true f.constLevels!) α c i tb eb) pr
let eNew := mkApp (e.getArg! 3) h |>.headBeta
let prNew := mkApp (mkAppN (mkConst ``dite_cond_eq_true e.getAppFn.constLevels!) e.getAppArgs) pr
return .visit { expr := eNew, proof? := prNew }
if r.expr.isFalse then
let pr r.getProof
let h := mkApp2 (mkConst ``of_eq_false) c pr
let eNew := mkApp eb h |>.headBeta
let prNew := mkApp (mkApp5 (mkConst ``dite_cond_eq_false f.constLevels!) α c i tb eb) pr
let eNew := mkApp (e.getArg! 4) h |>.headBeta
let prNew := mkApp (mkAppN (mkConst ``dite_cond_eq_false e.getAppFn.constLevels!) e.getAppArgs) pr
return .visit { expr := eNew, proof? := prNew }
return .continue

View File

@@ -19,13 +19,13 @@ def fromExpr? (e : Expr) : SimpM (Option Value) := do
let some n, value getFinValue? e | return none
return some { n, value }
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : {n : Nat} Fin n Fin n Fin n) (e : Expr) : SimpM DStep := do
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : {n : Nat} Fin n Fin n Fin n) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ fromExpr? e.appArg! | return .continue
if h : v₁.n = v₂.n then
let v := op v₁.value (h v₂.value)
return .done <| toExpr v
return .done { expr := toExpr v }
else
return .continue
@@ -35,22 +35,22 @@ def fromExpr? (e : Expr) : SimpM (Option Value) := do
let some v₂ fromExpr? e.appArg! | return .continue
evalPropStep e (op v₁.value v₂.value)
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Nat Nat Bool) (e : Expr) : SimpM DStep := do
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Nat Nat Bool) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ fromExpr? e.appArg! | return .continue
return .done <| toExpr (op v₁.value v₂.value)
return .done { expr := Lean.toExpr (op v₁.value v₂.value) }
/-
The following code assumes users did not override the `Fin n` instances for the arithmetic operators.
If they do, they must disable the following `simprocs`.
-/
builtin_dsimproc [simp, seval] reduceAdd ((_ + _ : Fin _)) := reduceBin ``HAdd.hAdd 6 (· + ·)
builtin_dsimproc [simp, seval] reduceMul ((_ * _ : Fin _)) := reduceBin ``HMul.hMul 6 (· * ·)
builtin_dsimproc [simp, seval] reduceSub ((_ - _ : Fin _)) := reduceBin ``HSub.hSub 6 (· - ·)
builtin_dsimproc [simp, seval] reduceDiv ((_ / _ : Fin _)) := reduceBin ``HDiv.hDiv 6 (· / ·)
builtin_dsimproc [simp, seval] reduceMod ((_ % _ : Fin _)) := reduceBin ``HMod.hMod 6 (· % ·)
builtin_simproc [simp, seval] reduceAdd ((_ + _ : Fin _)) := reduceBin ``HAdd.hAdd 6 (· + ·)
builtin_simproc [simp, seval] reduceMul ((_ * _ : Fin _)) := reduceBin ``HMul.hMul 6 (· * ·)
builtin_simproc [simp, seval] reduceSub ((_ - _ : Fin _)) := reduceBin ``HSub.hSub 6 (· - ·)
builtin_simproc [simp, seval] reduceDiv ((_ / _ : Fin _)) := reduceBin ``HDiv.hDiv 6 (· / ·)
builtin_simproc [simp, seval] reduceMod ((_ % _ : Fin _)) := reduceBin ``HMod.hMod 6 (· % ·)
builtin_simproc [simp, seval] reduceLT (( _ : Fin _) < _) := reduceBinPred ``LT.lt 4 (. < .)
builtin_simproc [simp, seval] reduceLE (( _ : Fin _) _) := reduceBinPred ``LE.le 4 (. .)
@@ -58,26 +58,17 @@ builtin_simproc [simp, seval] reduceGT (( _ : Fin _) > _) := reduceBinPred ``G
builtin_simproc [simp, seval] reduceGE (( _ : Fin _) _) := reduceBinPred ``GE.ge 4 (. .)
builtin_simproc [simp, seval] reduceEq (( _ : Fin _) = _) := reduceBinPred ``Eq 3 (. = .)
builtin_simproc [simp, seval] reduceNe (( _ : Fin _) _) := reduceBinPred ``Ne 3 (. .)
builtin_dsimproc [simp, seval] reduceBEq (( _ : Fin _) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_dsimproc [simp, seval] reduceBNe (( _ : Fin _) != _) := reduceBoolPred ``bne 4 (. != .)
builtin_simproc [simp, seval] reduceBEq (( _ : Fin _) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_simproc [simp, seval] reduceBNe (( _ : Fin _) != _) := reduceBoolPred ``bne 4 (. != .)
/-- Simplification procedure for ensuring `Fin` literals are normalized. -/
builtin_dsimproc [simp, seval] isValue ((OfNat.ofNat _ : Fin _)) := fun e => do
builtin_simproc [simp, seval] isValue ((OfNat.ofNat _ : Fin _)) := fun e => do
let some n, v getFinValue? e | return .continue
let some m getNatValue? e.appFn!.appArg! | return .continue
if n == m then
-- Design decision: should we return `.continue` instead of `.done` when simplifying.
-- In the symbolic evaluator, we must return `.done`, otherwise it will unfold the `OfNat.ofNat`
return .done e
return .done <| toExpr v
builtin_dsimproc [simp, seval] reduceFinMk (Fin.mk _ _) := fun e => do
let_expr Fin.mk n v _ e | return .continue
let some n evalNat n |>.run | return .continue
let some v getNatValue? v | return .continue
if h : n > 0 then
return .done <| toExpr (Fin.ofNat' v h)
else
return .continue
return .done { expr := e }
return .done { expr := toExpr v }
end Fin

View File

@@ -14,16 +14,16 @@ open Lean Meta Simp
def fromExpr? (e : Expr) : SimpM (Option Int) :=
getIntValue? e
@[inline] def reduceUnary (declName : Name) (arity : Nat) (op : Int Int) (e : Expr) : SimpM DStep := do
@[inline] def reduceUnary (declName : Name) (arity : Nat) (op : Int Int) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some n fromExpr? e.appArg! | return .continue
return .done <| toExpr (op n)
return .done { expr := toExpr (op n) }
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : Int Int Int) (e : Expr) : SimpM DStep := do
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : Int Int Int) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ fromExpr? e.appArg! | return .continue
return .done <| toExpr (op v₁ v₂)
return .done { expr := toExpr (op v₁ v₂) }
@[inline] def reduceBinPred (declName : Name) (arity : Nat) (op : Int Int Bool) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
@@ -31,46 +31,46 @@ def fromExpr? (e : Expr) : SimpM (Option Int) :=
let some v₂ fromExpr? e.appArg! | return .continue
evalPropStep e (op v₁ v₂)
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Int Int Bool) (e : Expr) : SimpM DStep := do
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Int Int Bool) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some n fromExpr? e.appFn!.appArg! | return .continue
let some m fromExpr? e.appArg! | return .continue
return .done <| toExpr (op n m)
return .done { expr := Lean.toExpr (op n m) }
/-
The following code assumes users did not override the `Int` instances for the arithmetic operators.
If they do, they must disable the following `simprocs`.
-/
builtin_dsimproc [simp, seval] reduceNeg ((- _ : Int)) := fun e => do
builtin_simproc [simp, seval] reduceNeg ((- _ : Int)) := fun e => do
unless e.isAppOfArity ``Neg.neg 3 do return .continue
let arg := e.appArg!
if arg.isAppOfArity ``OfNat.ofNat 3 then
-- We return .done to ensure `Neg.neg` is not unfolded even when `ground := true`.
return .done e
return .done { expr := e }
else
let some v fromExpr? arg | return .continue
if v < 0 then
return .done <| toExpr (- v)
return .done { expr := toExpr (- v) }
else
return .done <| toExpr v
return .done { expr := toExpr v }
/-- Return `.done` for positive Int values. We don't want to unfold in the symbolic evaluator. -/
builtin_dsimproc [seval] isPosValue ((OfNat.ofNat _ : Int)) := fun e => do
let_expr OfNat.ofNat _ _ _ e | return .continue
return .done e
builtin_simproc [seval] isPosValue ((OfNat.ofNat _ : Int)) := fun e => do
unless e.isAppOfArity ``OfNat.ofNat 3 do return .continue
return .done { expr := e }
builtin_dsimproc [simp, seval] reduceAdd ((_ + _ : Int)) := reduceBin ``HAdd.hAdd 6 (· + ·)
builtin_dsimproc [simp, seval] reduceMul ((_ * _ : Int)) := reduceBin ``HMul.hMul 6 (· * ·)
builtin_dsimproc [simp, seval] reduceSub ((_ - _ : Int)) := reduceBin ``HSub.hSub 6 (· - ·)
builtin_dsimproc [simp, seval] reduceDiv ((_ / _ : Int)) := reduceBin ``HDiv.hDiv 6 (· / ·)
builtin_dsimproc [simp, seval] reduceMod ((_ % _ : Int)) := reduceBin ``HMod.hMod 6 (· % ·)
builtin_simproc [simp, seval] reduceAdd ((_ + _ : Int)) := reduceBin ``HAdd.hAdd 6 (· + ·)
builtin_simproc [simp, seval] reduceMul ((_ * _ : Int)) := reduceBin ``HMul.hMul 6 (· * ·)
builtin_simproc [simp, seval] reduceSub ((_ - _ : Int)) := reduceBin ``HSub.hSub 6 (· - ·)
builtin_simproc [simp, seval] reduceDiv ((_ / _ : Int)) := reduceBin ``HDiv.hDiv 6 (· / ·)
builtin_simproc [simp, seval] reduceMod ((_ % _ : Int)) := reduceBin ``HMod.hMod 6 (· % ·)
builtin_dsimproc [simp, seval] reducePow ((_ : Int) ^ (_ : Nat)) := fun e => do
let_expr HPow.hPow _ _ _ _ a b e | return .continue
let some v₁ fromExpr? a | return .continue
let some v₂ Nat.fromExpr? b | return .continue
return .done <| toExpr (v₁ ^ v₂)
builtin_simproc [simp, seval] reducePow ((_ : Int) ^ (_ : Nat)) := fun e => do
unless e.isAppOfArity ``HPow.hPow 6 do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
let some v₂ Nat.fromExpr? e.appArg! | return .continue
return .done { expr := toExpr (v₁ ^ v₂) }
builtin_simproc [simp, seval] reduceLT (( _ : Int) < _) := reduceBinPred ``LT.lt 4 (. < .)
builtin_simproc [simp, seval] reduceLE (( _ : Int) _) := reduceBinPred ``LE.le 4 (. .)
@@ -78,25 +78,15 @@ builtin_simproc [simp, seval] reduceGT (( _ : Int) > _) := reduceBinPred ``GT.
builtin_simproc [simp, seval] reduceGE (( _ : Int) _) := reduceBinPred ``GE.ge 4 (. .)
builtin_simproc [simp, seval] reduceEq (( _ : Int) = _) := reduceBinPred ``Eq 3 (. = .)
builtin_simproc [simp, seval] reduceNe (( _ : Int) _) := reduceBinPred ``Ne 3 (. .)
builtin_dsimproc [simp, seval] reduceBEq (( _ : Int) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_dsimproc [simp, seval] reduceBNe (( _ : Int) != _) := reduceBoolPred ``bne 4 (. != .)
builtin_simproc [simp, seval] reduceBEq (( _ : Int) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_simproc [simp, seval] reduceBNe (( _ : Int) != _) := reduceBoolPred ``bne 4 (. != .)
@[inline] def reduceNatCore (declName : Name) (op : Int Nat) (e : Expr) : SimpM DStep := do
@[inline] def reduceNatCore (declName : Name) (op : Int Nat) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName 1 do return .continue
let some v fromExpr? e.appArg! | return .continue
return .done <| mkNatLit (op v)
return .done { expr := mkNatLit (op v) }
builtin_dsimproc [simp, seval] reduceAbs (natAbs _) := reduceNatCore ``natAbs natAbs
builtin_dsimproc [simp, seval] reduceToNat (Int.toNat _) := reduceNatCore ``Int.toNat Int.toNat
builtin_dsimproc [simp, seval] reduceNegSucc (Int.negSucc _) := fun e => do
let_expr Int.negSucc a e | return .continue
let some a getNatValue? a | return .continue
return .done <| toExpr (-(Int.ofNat a + 1))
builtin_dsimproc [simp, seval] reduceOfNat (Int.ofNat _) := fun e => do
let_expr Int.ofNat a e | return .continue
let some a getNatValue? a | return .continue
return .done <| toExpr (Int.ofNat a)
builtin_simproc [simp, seval] reduceAbs (natAbs _) := reduceNatCore ``natAbs natAbs
builtin_simproc [simp, seval] reduceToNat (Int.toNat _) := reduceNatCore ``Int.toNat Int.toNat
end Int

View File

@@ -16,16 +16,16 @@ open Lean Meta Simp
def fromExpr? (e : Expr) : SimpM (Option Nat) :=
getNatValue? e
@[inline] def reduceUnary (declName : Name) (arity : Nat) (op : Nat Nat) (e : Expr) : SimpM DStep := do
@[inline] def reduceUnary (declName : Name) (arity : Nat) (op : Nat Nat) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some n fromExpr? e.appArg! | return .continue
return .done <| toExpr (op n)
return .done { expr := toExpr (op n) }
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : Nat Nat Nat) (e : Expr) : SimpM DStep := do
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : Nat Nat Nat) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some n fromExpr? e.appFn!.appArg! | return .continue
let some m fromExpr? e.appArg! | return .continue
return .done <| toExpr (op n m)
return .done { expr := toExpr (op n m) }
@[inline] def reduceBinPred (declName : Name) (arity : Nat) (op : Nat Nat Bool) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
@@ -33,26 +33,26 @@ def fromExpr? (e : Expr) : SimpM (Option Nat) :=
let some m fromExpr? e.appArg! | return .continue
evalPropStep e (op n m)
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Nat Nat Bool) (e : Expr) : SimpM DStep := do
@[inline] def reduceBoolPred (declName : Name) (arity : Nat) (op : Nat Nat Bool) (e : Expr) : SimpM Step := do
unless e.isAppOfArity declName arity do return .continue
let some n fromExpr? e.appFn!.appArg! | return .continue
let some m fromExpr? e.appArg! | return .continue
return .done <| toExpr (op n m)
return .done { expr := toExpr (op n m) }
builtin_dsimproc [simp, seval] reduceSucc (Nat.succ _) := reduceUnary ``Nat.succ 1 (· + 1)
builtin_simproc [simp, seval] reduceSucc (Nat.succ _) := reduceUnary ``Nat.succ 1 (· + 1)
/-
The following code assumes users did not override the `Nat` instances for the arithmetic operators.
If they do, they must disable the following `simprocs`.
-/
builtin_dsimproc [simp, seval] reduceAdd ((_ + _ : Nat)) := reduceBin ``HAdd.hAdd 6 (· + ·)
builtin_dsimproc [simp, seval] reduceMul ((_ * _ : Nat)) := reduceBin ``HMul.hMul 6 (· * ·)
builtin_dsimproc [simp, seval] reduceSub ((_ - _ : Nat)) := reduceBin ``HSub.hSub 6 (· - ·)
builtin_dsimproc [simp, seval] reduceDiv ((_ / _ : Nat)) := reduceBin ``HDiv.hDiv 6 (· / ·)
builtin_dsimproc [simp, seval] reduceMod ((_ % _ : Nat)) := reduceBin ``HMod.hMod 6 (· % ·)
builtin_dsimproc [simp, seval] reducePow ((_ ^ _ : Nat)) := reduceBin ``HPow.hPow 6 (· ^ ·)
builtin_dsimproc [simp, seval] reduceGcd (gcd _ _) := reduceBin ``gcd 2 gcd
builtin_simproc [simp, seval] reduceAdd ((_ + _ : Nat)) := reduceBin ``HAdd.hAdd 6 (· + ·)
builtin_simproc [simp, seval] reduceMul ((_ * _ : Nat)) := reduceBin ``HMul.hMul 6 (· * ·)
builtin_simproc [simp, seval] reduceSub ((_ - _ : Nat)) := reduceBin ``HSub.hSub 6 (· - ·)
builtin_simproc [simp, seval] reduceDiv ((_ / _ : Nat)) := reduceBin ``HDiv.hDiv 6 (· / ·)
builtin_simproc [simp, seval] reduceMod ((_ % _ : Nat)) := reduceBin ``HMod.hMod 6 (· % ·)
builtin_simproc [simp, seval] reducePow ((_ ^ _ : Nat)) := reduceBin ``HPow.hPow 6 (· ^ ·)
builtin_simproc [simp, seval] reduceGcd (gcd _ _) := reduceBin ``gcd 2 gcd
builtin_simproc [simp, seval] reduceLT (( _ : Nat) < _) := reduceBinPred ``LT.lt 4 (. < .)
builtin_simproc [simp, seval] reduceLE (( _ : Nat) _) := reduceBinPred ``LE.le 4 (. .)
@@ -60,12 +60,12 @@ builtin_simproc [simp, seval] reduceGT (( _ : Nat) > _) := reduceBinPred ``GT.
builtin_simproc [simp, seval] reduceGE (( _ : Nat) _) := reduceBinPred ``GE.ge 4 (. .)
builtin_simproc [simp, seval] reduceEq (( _ : Nat) = _) := reduceBinPred ``Eq 3 (. = .)
builtin_simproc [simp, seval] reduceNe (( _ : Nat) _) := reduceBinPred ``Ne 3 (. .)
builtin_dsimproc [simp, seval] reduceBEq (( _ : Nat) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_dsimproc [simp, seval] reduceBNe (( _ : Nat) != _) := reduceBoolPred ``bne 4 (. != .)
builtin_simproc [simp, seval] reduceBEq (( _ : Nat) == _) := reduceBoolPred ``BEq.beq 4 (. == .)
builtin_simproc [simp, seval] reduceBNe (( _ : Nat) != _) := reduceBoolPred ``bne 4 (. != .)
/-- Return `.done` for Nat values. We don't want to unfold in the symbolic evaluator. -/
builtin_dsimproc [seval] isValue ((OfNat.ofNat _ : Nat)) := fun e => do
let_expr OfNat.ofNat _ _ _ e | return .continue
return .done e
builtin_simproc [seval] isValue ((OfNat.ofNat _ : Nat)) := fun e => do
unless e.isAppOfArity ``OfNat.ofNat 3 do return .continue
return .done { expr := e }
end Nat

View File

@@ -13,23 +13,23 @@ open Lean Meta Simp
def fromExpr? (e : Expr) : SimpM (Option String) := do
return getStringValue? e
builtin_dsimproc [simp, seval] reduceAppend ((_ ++ _ : String)) := fun e => do
builtin_simproc [simp, seval] reduceAppend ((_ ++ _ : String)) := fun e => do
unless e.isAppOfArity ``HAppend.hAppend 6 do return .continue
let some a fromExpr? e.appFn!.appArg! | return .continue
let some b fromExpr? e.appArg! | return .continue
return .done <| toExpr (a ++ b)
return .done { expr := toExpr (a ++ b) }
private partial def reduceListChar (e : Expr) (s : String) : SimpM DStep := do
private partial def reduceListChar (e : Expr) (s : String) : SimpM Step := do
trace[Meta.debug] "reduceListChar {e}, {s}"
if e.isAppOfArity ``List.nil 1 then
return .done <| toExpr s
return .done { expr := toExpr s }
else if e.isAppOfArity ``List.cons 3 then
let some c Char.fromExpr? e.appFn!.appArg! | return .continue
reduceListChar e.appArg! (s.push c)
else
return .continue
builtin_dsimproc [simp, seval] reduceMk (String.mk _) := fun e => do
builtin_simproc [simp, seval] reduceMk (String.mk _) := fun e => do
unless e.isAppOfArity ``String.mk 1 do return .continue
reduceListChar e.appArg! ""

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