mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-29 16:24:08 +00:00
Compare commits
5 Commits
dsimproc
...
improve_de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4224d416bf | ||
|
|
a06010fa1a | ||
|
|
234eef85e8 | ||
|
|
67da377715 | ||
|
|
60b598d425 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -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"
|
||||
|
||||
1
.github/workflows/nix-ci.yml
vendored
1
.github/workflows/nix-ci.yml
vendored
@@ -6,7 +6,6 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
|
||||
259
RELEASES.md
259
RELEASES.md
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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!
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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⟩
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 -/
|
||||
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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₁)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 _
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 _)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -5,6 +5,7 @@ Authors: Dany Fabian, Sebastian Ullrich
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Int
|
||||
import Init.Data.String
|
||||
|
||||
inductive Ordering where
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
| `($(_)) => `(())
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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) :=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 :=
|
||||
|
||||
@@ -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}'"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -47,4 +47,3 @@ import Lean.Meta.CoeAttr
|
||||
import Lean.Meta.Iterator
|
||||
import Lean.Meta.LazyDiscrTree
|
||||
import Lean.Meta.LitValues
|
||||
import Lean.Meta.CheckTactic
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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⟩
|
||||
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 :=
|
||||
|
||||
@@ -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 motive’s 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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 that’s 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 isn’t 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 don’t 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ·)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user