Compare commits

..

85 Commits

Author SHA1 Message Date
Kim Morrison
8534409834 chore: DecidableRel allows a heterogeneous relation 2024-12-09 16:58:58 +11:00
Kim Morrison
4dd182c554 chore: remove deprecated aliases for Int.tdiv and Int.tmod (#6322)
This PR removes the deprecated aliases `Int.div := Int.tdiv` and
`Int.mod := Int.tmod`. Later we will rename `Int.ediv` to `Int.div` and
`Int.emod` to `Int.mod`.
2024-12-08 05:19:42 +00:00
jsr-p
762c5758f5 doc: missing (type := true) in reader monad example (#6196)
This PR adds missing `(types := true)` to `#reduce` example in [Readers
example](https://lean-lang.org/lean4/doc/monads/readers.lean.html).
Since [4.10](https://lean-lang.org/blog/2024-8-1-lean-4100/) the `(types
:= true)` is necessary for the `ReaderM Environment String` type to be
reduced into `Environment → String`.
2024-12-07 15:59:36 +00:00
Joachim Breitner
6447fda253 feat: FunInd: omit unused parameters (#6330)
This PR removes unnecessary parameters from the funcion induction
principles. This is a breaking change; broken code can typically be adjusted
simply by passing fewer parameters.

Part 2, adjusting after stage0 update.

Closes #6320
2024-12-07 04:19:21 +01:00
Joachim Breitner
279f36b4cc chore: update stage0 2024-12-07 04:19:21 +01:00
Joachim Breitner
d2853ecbc4 feat: FunInd: omit unused parameters (#6330)
This PR removes unnecessary parameters from the funcion induction
principles. This is a breaking change; broken code can typically be adjusted
simply by passing fewer parameters.

Part 1, before stage0 update.

Closes #6320
2024-12-07 04:19:21 +01:00
Kim Morrison
6e60d13084 feat: getElem lemmas for Vector operations (#6324)
This PR adds `GetElem` lemmas for the basic `Vector` operations.

The `Vector` API is still very sparse, but I'm hoping to infill rapidly.
2024-12-06 01:45:19 +00:00
Kim Morrison
019f8e175f chore: protect Fin.cast and BitVec.cast (#6315)
This PR adds `protected` to `Fin.cast` and `BitVec.cast`, to avoid
confusion with `_root_.cast`. These should mostly be used via
dot-notation in any case.
2024-12-05 06:11:45 +00:00
Kim Morrison
c366a291ca chore: generalize universe in Array.find? (#6318)
This PR generalizes the universe level for `Array.find?`, by giving it a
separate implementation from `Array.findM?`.
2024-12-05 06:11:40 +00:00
Alex Keizer
1400b95ffb feat: upstream ToLevel from mathlib (#6285)
This PR upstreams the `ToLevel` typeclass from mathlib and uses it to
fix the existing `ToExpr` instances so that they are truly universe
polymorphic (previously it generated malformed expressions when the
universe level was nonzero). We improve on the mathlib definition of
`ToLevel` to ensure the class always lives in `Type`, irrespective of
the universe parameter.

This implements part one of the plan to upstream a derive handler for
`ToExpr`, as discussed in #5906 and #5909.

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-12-05 05:50:32 +00:00
Kim Morrison
00c7b85261 feat: lemmas about for loops over Option (#6316)
This PR adds lemmas simplifying `for` loops over `Option` into
`Option.pelim`, giving parity with lemmas simplifying `for` loops of
`List` into `List.fold`.
2024-12-05 05:09:07 +00:00
Leonardo de Moura
f6e88e5a05 fix: missing HEq support at ToLCNF (#6311)
This PR adds support for `HEq` to the new code generator.
2024-12-04 19:49:16 +00:00
Sebastian Ullrich
88573c802d test: do not filter output for non-diff tests (#6308) 2024-12-04 17:49:35 +00:00
Henrik Böving
faf07e58db chore: remove unused imports (#6305)
This PR removes an unused import in the time library that can yield to
import cycles when building stuff that gets imported by `Std.Internal`
but also wants to import `Std.Time`.
2024-12-04 12:46:08 +00:00
Tobias Grosser
c5181569f9 feat: BitVec.[toInt|toFin]_concat and Bool.toInt (#6182)
This PR adds `BitVec.[toInt|toFin]_concat` and moves a couple of
theorems into the concat section, as `BitVec.msb_concat` is needed for
the `toInt_concat` proof.

We also add `Bool.toInt`.
2024-12-04 01:53:30 +00:00
Siddharth
77211029da feat: BitVec.toFin/ToInt BitVec.ushiftRight (#6238)
This PR adds theorems characterizing the value of the unsigned shift
right of a bitvector in terms of its 2s complement interpretation as an
integer.
Unsigned shift right by at least one bit makes the value of the
bitvector less than or equal to `2^(w-1)`,
makes the interpretation of the bitvector `Int` and `Nat` agree.
In the case when `n = 0`, then the shift right value equals the integer
interpretation.

```lean
theorem toInt_ushiftRight_eq_ite {x : BitVec w} {n : Nat} :
  (x >>> n).toInt = if n = 0 then x.toInt else x.toNat >>> n
```

```lean
theorem toFin_uShiftRight {x : BitVec w} {n : Nat} :
  (x >>> n).toFin = x.toFin / (Fin.ofNat' (2^w) (2^n))
```

---------

Co-authored-by: Harun Khan <harun19@stanford.edu>
Co-authored-by: Tobias Grosser <github@grosser.es>
2024-12-04 01:49:58 +00:00
Lean stage0 autoupdater
da9a0c4190 chore: update stage0 2024-12-04 00:04:00 +00:00
Leonardo de Moura
b9bf94313a feat: add debug.proofAsSorry (#6300)
This PR adds the `debug.proofAsSorry` option. When enabled, the proofs
of theorems are ignored and replaced with `sorry`.
2024-12-03 23:21:38 +00:00
Sebastian Ullrich
2a891a3889 chore: CMAKE_CXX_SYSROOT_FLAG is also needed for linking (#6297)
Fixes #6296
2024-12-03 16:14:22 +00:00
Sebastian Ullrich
00718c3959 chore: clean up Elab.async handling (#6299)
* Make sure metaprogramming users cannot be surprised by its
introduction
* Make `#guard_msgs` compatible with its use
2024-12-03 12:42:02 +00:00
Sebastian Ullrich
473274f145 chore: update stage0 2024-12-03 13:59:37 +01:00
Kim Morrison
7b98fbece4 feat: reverse HashMap.toList, so it agrees with HashMap.toArray (#6244)
This PR changes the implementation of `HashMap.toList`, so the ordering
agrees with `HashMap.toArray`.

Currently there are no verification lemmas about `HashMap.toList`, so no
contract is being broken yet!
2024-12-03 12:25:35 +00:00
Henrik Böving
24b412ebe3 refactor: move IO.Channel and IO.Mutex to Std.Sync (#6282)
This PR moves `IO.Channel` and `IO.Mutex` from `Init` to `Std.Sync` and
renames them to `Std.Channel` and `Std.Mutex`.

Note that the original files are retained and the deprecation is written
manually as we cannot import `Std` from `Init` so this is the only way
to deprecate without a hard breaking change. In particular we do not yet
move `Std.Queue` from `Init` to `Std` both because it needs to be
retained for this deprecation to work but also because it is already
within the `Std` namespace and as such we cannot maintain two copies of
the file at once. After the deprecation period is finished `Std.Queue`
will find a new home in `Std.Data.Queue`.
2024-12-03 09:36:50 +00:00
Kim Morrison
cb600ed9b4 chore: restore broken proofs
This reverts commit d099f560f72b5f18695c7fb586a9da93af0cb17e.
2024-12-03 17:59:23 +11:00
Kim Morrison
57d83c835e feat: add simp configuration to norm_cast macros 2024-12-03 17:59:23 +11:00
Kim Morrison
ce27d49e31 chore: update stage0 2024-12-03 17:59:23 +11:00
Kim Morrison
8a7889d602 chore: temporarily sorry broken proofs 2024-12-03 17:59:23 +11:00
Kim Morrison
69340297be chore: add simp configuration to norm_cast syntax
chore: define NormCastConfig earlier
2024-12-03 17:59:23 +11:00
Kim Morrison
222abdd43d feat: simprocs for other Fin operations (#6295)
This PR sets up simprocs for all the remaining operations defined in
`Init.Data.Fin.Basic`
2024-12-03 04:42:17 +00:00
François G. Dorais
490be9282e chore: specialize fold loops (#6293)
This PR adds `specialize` and `semireducible` attributes to loops for
`Fin.fold[lr]M?`
2024-12-03 02:44:19 +00:00
Kim Morrison
cda6d5c67a chore: upstream List.length_flatMap (#6294)
This PR upstreams `List.length_flatMap`, `countP_flatMap` and
`count_flatMap` from Mathlib. These were not possible to state before we
upstreamed `List.sum`.
2024-12-03 01:59:32 +00:00
Kim Morrison
904404303b chore: robustify for byAsSorry (#6287)
This PR makes some proofs more robust so they will still work with
`byAsSorry`. Unfortunately, they are not a complete fix and there are
remaining problems building with `byAsSorry`.
2024-12-02 23:53:16 +00:00
Mac Malone
f6bc6b2eb1 fix: lake: properly prepend job log in ensureJob (#6291)
This PR ensures the the log error position is properly preserved when
prepending stray log entries to the job log. It also adds comparison
support for `Log.Pos`.
2024-12-02 23:43:12 +00:00
Mac Malone
d9d54c1f99 chore: lake: use & check prelude (#6289)
This PR adapts Lake modules to use `prelude` and includes them in the
`check-prelude` CI.
2024-12-02 19:55:05 +00:00
Henrik Böving
b2336fd980 perf: speed up bv_decide reflection using Lean.RArray (#6288)
This PR uses Lean.RArray in bv_decide's reflection proofs. Giving
speedups on problems with lots of variables.

Implement like #6068, speedup:
```
# before
λ hyperfine "lean +nightly-2024-12-02 tests/lean/run/bv_reflection_stress.lean"
Benchmark 1: lean +nightly-2024-12-02 tests/lean/run/bv_reflection_stress.lean
  Time (mean ± σ):      1.939 s ±  0.007 s    [User: 1.549 s, System: 0.104 s]
  Range (min … max):    1.928 s …  1.947 s    10 runs
# after
λ hyperfine "lean tests/lean/run/bv_reflection_stress.lean"                                                                                                                                                                                                                        
Benchmark 1: lean tests/lean/run/bv_reflection_stress.lean
  Time (mean ± σ):      1.409 s ±  0.006 s    [User: 1.058 s, System: 0.073 s]
  Range (min … max):    1.401 s …  1.419 s    10 runs
```
2024-12-02 17:44:58 +00:00
Mac Malone
f156f22d7c feat: lake: build without leanc (#6176)
This PR changes Lake's build process to no longer use `leanc` for
compiling C files or linking shared libraries and executables. Instead,
it directly invokes the bundled compiler (or the native compiler if
none) using the necessary flags.
2024-12-02 17:11:27 +00:00
dependabot[bot]
3c348d4526 chore: CI: bump dawidd6/action-download-artifact from 6 to 7 (#6274)
Bumps
[dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact)
from 6 to 7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dawidd6/action-download-artifact/releases">dawidd6/action-download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v7</h2>
<h2>What's Changed</h2>
<ul>
<li>build(deps): bump fast-xml-parser from 4.4.0 to 4.4.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/299">dawidd6/action-download-artifact#299</a></li>
<li>build(deps): bump <code>@​actions/artifact</code> from 2.1.7 to
2.1.9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/300">dawidd6/action-download-artifact#300</a></li>
<li>build(deps): bump adm-zip from 0.5.14 to 0.5.15 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/301">dawidd6/action-download-artifact#301</a></li>
<li>build(deps): bump adm-zip from 0.5.15 to 0.5.16 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/306">dawidd6/action-download-artifact#306</a></li>
<li>build(deps): bump path-to-regexp from 6.2.2 to 6.3.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/307">dawidd6/action-download-artifact#307</a></li>
<li>build(deps): bump <code>@​actions/artifact</code> from 2.1.9 to
2.1.10 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/311">dawidd6/action-download-artifact#311</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.10.1 to 1.11.0
by <a href="https://github.com/dependabot"><code>@​dependabot</code></a>
in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/310">dawidd6/action-download-artifact#310</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.11.0 to 1.11.1
by <a href="https://github.com/dependabot"><code>@​dependabot</code></a>
in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/312">dawidd6/action-download-artifact#312</a></li>
<li>build(deps): bump <code>@​actions/artifact</code> from 2.1.10 to
2.1.11 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/313">dawidd6/action-download-artifact#313</a></li>
<li>build(deps): Fix cross-spawn &gt;=7.0.0 &lt;= 7.0.5 vulnerability by
<a href="https://github.com/alexcouret"><code>@​alexcouret</code></a> in
<a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/317">dawidd6/action-download-artifact#317</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/alexcouret"><code>@​alexcouret</code></a> made
their first contribution in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/317">dawidd6/action-download-artifact#317</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dawidd6/action-download-artifact/compare/v6...v7">https://github.com/dawidd6/action-download-artifact/compare/v6...v7</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="80620a5d27"><code>80620a5</code></a>
node_modules: update</li>
<li><a
href="b15e003f46"><code>b15e003</code></a>
node_modules: install</li>
<li><a
href="1ee9a455fd"><code>1ee9a45</code></a>
build(deps): Fix cross-spawn &gt;=7.0.0 &lt;= 7.0.5 vulnerability (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/317">#317</a>)</li>
<li><a
href="b2f2706ac4"><code>b2f2706</code></a>
build(deps): bump <code>@​actions/artifact</code> from 2.1.10 to 2.1.11
(<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/313">#313</a>)</li>
<li><a
href="fdbeba027c"><code>fdbeba0</code></a>
build(deps): bump <code>@​actions/core</code> from 1.11.0 to 1.11.1 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/312">#312</a>)</li>
<li><a
href="a74b42987a"><code>a74b429</code></a>
build(deps): bump <code>@​actions/core</code> from 1.10.1 to 1.11.0 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/310">#310</a>)</li>
<li><a
href="24e807a70c"><code>24e807a</code></a>
build(deps): bump <code>@​actions/artifact</code> from 2.1.9 to 2.1.10
(<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/311">#311</a>)</li>
<li><a
href="9592e3c4ab"><code>9592e3c</code></a>
build(deps): bump path-to-regexp from 6.2.2 to 6.3.0 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/307">#307</a>)</li>
<li><a
href="5f966b63eb"><code>5f966b6</code></a>
build(deps): bump adm-zip from 0.5.15 to 0.5.16 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/306">#306</a>)</li>
<li><a
href="db9477a3eb"><code>db9477a</code></a>
build(deps): bump adm-zip from 0.5.14 to 0.5.15 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/301">#301</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/dawidd6/action-download-artifact/compare/v6...v7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dawidd6/action-download-artifact&package-manager=github_actions&previous-version=6&new-version=7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 16:09:50 +00:00
Sebastian Ullrich
0b8f50f78d feat: async linting (#4460)
This PR runs all linters for a single command (together) on a separate
thread from further elaboration, making a first step towards
parallelizing the elaborator.
2024-12-02 14:37:03 +00:00
Henrik Böving
0d89f0194b perf: bv_decide uses rfl in reflection if possible (#6286)
This PR ensure `bv_decide` uses definitional equality in its reflection
procedure as much as possible. Previously it would build up explicit
congruence proofs for the kernel to check. This reduces the size of
proof terms passed to kernel speeds up checking of large reflection
proofs.
2024-12-02 14:27:49 +00:00
Kim Morrison
e157fcbcd1 chore: missing Array/Vector injectivity lemmas (#6284) 2024-12-02 11:00:03 +00:00
Henrik Böving
95dbac26cf chore: shake Std.Time (#6283)
This PR reduces the import closure of `Std.Time` such that it doesn't
have to be rebuilt on every change in `Init.Data`.

Noticed while working on `Init` refactorings.
2024-12-02 10:52:43 +00:00
dependabot[bot]
be63c8280e chore: CI: bump dcarbone/install-jq-action from 2.1.0 to 3.0.1 (#6275)
Bumps
[dcarbone/install-jq-action](https://github.com/dcarbone/install-jq-action)
from 2.1.0 to 3.0.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dcarbone/install-jq-action/releases">dcarbone/install-jq-action's
releases</a>.</em></p>
<blockquote>
<h2>v3.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Use sh in action by <a
href="https://github.com/dcarbone"><code>@​dcarbone</code></a> in <a
href="https://redirect.github.com/dcarbone/install-jq-action/pull/15">dcarbone/install-jq-action#15</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dcarbone/install-jq-action/compare/v3.0.0...v3.0.1">https://github.com/dcarbone/install-jq-action/compare/v3.0.0...v3.0.1</a></p>
<h2>v3.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>updating matrix versions by <a
href="https://github.com/dcarbone"><code>@​dcarbone</code></a> in <a
href="https://redirect.github.com/dcarbone/install-jq-action/pull/12">dcarbone/install-jq-action#12</a></li>
<li>trying out posix sh by <a
href="https://github.com/dcarbone"><code>@​dcarbone</code></a> in <a
href="https://redirect.github.com/dcarbone/install-jq-action/pull/14">dcarbone/install-jq-action#14</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dcarbone/install-jq-action/compare/v2...v3.0.0">https://github.com/dcarbone/install-jq-action/compare/v2...v3.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e397bd8743"><code>e397bd8</code></a>
Use sh in action (<a
href="https://redirect.github.com/dcarbone/install-jq-action/issues/15">#15</a>)</li>
<li><a
href="36b228ee68"><code>36b228e</code></a>
Add dependabot for github-actions</li>
<li><a
href="d5935278d5"><code>d593527</code></a>
updating examples</li>
<li><a
href="ca8101273e"><code>ca81012</code></a>
trying out posix sh (<a
href="https://redirect.github.com/dcarbone/install-jq-action/issues/14">#14</a>)</li>
<li><a
href="de7c0d1fb1"><code>de7c0d1</code></a>
updating matrix versions (<a
href="https://redirect.github.com/dcarbone/install-jq-action/issues/12">#12</a>)</li>
<li>See full diff in <a
href="https://github.com/dcarbone/install-jq-action/compare/v2.1.0...v3.0.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dcarbone/install-jq-action&package-manager=github_actions&previous-version=2.1.0&new-version=3.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:33:27 +00:00
Sebastian Ullrich
6fcf35e930 chore: script/mathlib-bench (#6280)
A simple approach to benchmarking lean4 PRs against Mathlib
2024-12-02 10:00:57 +00:00
Marc Huisinga
b3e0c9c3fa fix: use sensible notion of indentation in structure instance field completion (#6279)
This PR fixes a bug in structure instance field completion that caused
it to not function correctly for bracketed structure instances written
in Mathlib style.
2024-12-02 09:37:12 +00:00
Kim Morrison
3c5e612dc5 chore: begin development cycle for v4.16.0 (#6277) 2024-12-02 04:11:10 +00:00
Kim Morrison
29e84fa7ea feat: omega doesn't get stuck on bare Int.negSucc (#6276)
This PR ensures `omega` doesn't get stuck on bare `Int.negSucc` terms in
goals.

This came up in https://github.com/ImperialCollegeLondon/FLT/pull/260.
2024-12-01 23:57:15 +00:00
Mac Malone
6bf8ff32f0 feat: more UInt bitwise theorems (#6188)
This PR completes the `toNat` theorems for the bitwise operations
(`and`, `or`, `xor`, `shiftLeft`, `shiftRight`) of the UInt types and
adds `toBitVec` theorems as well. It also renames `and_toNat` to
`toNat_and` to fit with the current naming convention.
2024-12-01 22:38:49 +00:00
Henrik Böving
62b8238782 chore: remove accidentally added file (#6262)
This PR removes an accidentally comitted file.
2024-12-01 21:11:44 +00:00
Kyle Miller
0a2a8e8aa4 feat: make "foo has been deprecated" warning be hoverable (#6273)
This PR modifies the "foo has been deprecated: use betterFoo instead"
warning so that foo and betterFoo are hoverable.
2024-12-01 19:12:42 +00:00
Kyle Miller
23236ef520 fix: have Lean.Meta.isConstructorApp'? be aware of n + k Nat offsets (#6270)
This PR fixes a bug that could cause the `injectivity` tactic to fail in
reducible mode, which could cause unfolding lemma generation to fail
(used by tactics such as `unfold`). In particular,
`Lean.Meta.isConstructorApp'?` was not aware that `n + 1` is equivalent
to `Nat.succ n`.

Closes #5064
2024-12-01 18:04:32 +00:00
Kim Morrison
b2f70dad52 feat: Array.swap_perm (#6272)
This PR introduces the basic theory of permutations of `Array`s and
proves `Array.swap_perm`.

The API falls well short of what is available for `List` at this point.
2024-12-01 08:35:28 +00:00
Kim Morrison
819cb879e1 chore: upstream Vector lemmas (#6271)
This PR upstreams existing lemmas about `Vector` from Batteries.

Thanks to @fgdorais for preparing these in
https://github.com/leanprover-community/batteries/pull/1062. Further
contributions to the `Vector` API welcome via PR here.
2024-12-01 06:44:14 +00:00
Kim Morrison
3ee2842e77 feat: remove runtime bounds checks and partial from qsort (#6241)
This PR refactors `Array.qsort` to remove runtime array bounds checks,
and avoids the use of `partial`. We use the `Vector` API, along with
auto_params, to avoid having to write any proofs. The new code
benchmarks indistinguishably from the old.
2024-12-01 06:26:00 +00:00
Kyle Miller
7b8504cf06 chore: post-stage0 cleanup for #6165 (#6268)
This PR puts code in terms of syntax quotations now that there has been
a stage0 update. Fixes a lingering bug in StructInst where some
intermediate syntax was malformed, but this had no observable effects
outside of some debug messages.
2024-12-01 00:20:59 +00:00
Lean stage0 autoupdater
ca96922b4b chore: update stage0 2024-11-30 21:22:30 +00:00
Kyle Miller
a1c3a36433 feat: parity between structure instance notation and where notation (#6165)
This PR modifies structure instance notation and `where` notation to use
the same notation for fields. Structure instance notation now admits
binders, type ascriptions, and equations, and `where` notation admits
full structure lvals. Examples of these for structure instance notation:
```lean
structure PosFun where
  f : Nat → Nat
  pos : ∀ n, 0 < f n

def p : PosFun :=
  { f n := n + 1
    pos := by simp }

def p' : PosFun :=
  { f | 0 => 1
      | n + 1 => n + 1
    pos := by rintro (_|_) <;> simp }
```
Just like for the structure `where` notation, a field `f x y z : ty :=
val` expands to `f := fun x y z => (val : ty)`. The type ascription is
optional.

The PR also is setting things up for future expansion. Pending some
discussion, in the future structure/`where` notation could have have
embedded `where` clauses; rather than `{ a := { x := 1, y := z } }` one
could write `{ a where x := 1; y := z }`.
2024-11-30 20:27:25 +00:00
Lean stage0 autoupdater
734ea3071d chore: update stage0 2024-11-30 12:58:59 +00:00
Kyle Miller
f3f00451c8 feat: add structInstFieldDecl syntax category (#6265)
This PR is preparation for changes to structure instance notation in
#6165. It adds a syntax category that will be used for field syntax.
2024-11-30 12:12:53 +00:00
Leonardo de Moura
27df5e968a feat: Simp.Config.implicitDefEqProofs (#4595)
This PR implements `Simp.Config.implicitDefEqsProofs`. When `true`
(default: `true`), `simp` will **not** create a proof term for a
rewriting rule associated with an `rfl`-theorem. Rewriting rules are
provided by users by annotating theorems with the attribute `@[simp]`.
If the proof of the theorem is just `rfl` (reflexivity), and
`implicitDefEqProofs := true`, `simp` will **not** create a proof term
which is an application of the annotated theorem.

The default setting does change the existing behavior. Users can use
`simp -implicitDefEqProofs` to force `simp` to create a proof term for
`rfl`-theorems. This can positively impact proof checking time in the
kernel.

This PR also fixes an issue in the `split` tactic that has been exposed
by this feature. It was looking for `split` candidates in proofs and
implicit arguments. See new test for issue exposed by the previous
feature.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-11-29 22:29:27 +00:00
Lean stage0 autoupdater
3752241edd chore: update stage0 2024-11-29 21:32:14 +00:00
Marc Huisinga
39bffb6fda fix: don't walk full project file tree on every file save (#6246)
This PR fixes a performance issue where the Lean language server would
walk the full project file tree every time a file was saved, blocking
the processing of all other requests and notifications and significantly
increasing overall language server latency after saving.

This issue was originally reported at
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Compiling.20too.20slow/near/484386515
and uncovered in a lengthy investigation. The performance bug that
causes the Lean language server to walk the full project file tree when
the file watcher for .ilean files is triggered was introduced when the
.ileans were first introduced, whereas the specific issue of file saving
also triggering the walk was introduced by #3247 in 4.8.0 and the use of
the file watcher for .lean files, which would then also trigger the
directory walk. Combining this with VS Code's auto-save feature causes
the language server to walk the full project file tree on every change
of the document.

It somehow hasn't really been much of an issue until now, but we still
do way too much work in the watchdog main loop. I'll look into resolving
that more general issue in the future.
2024-11-29 15:34:44 +00:00
Sebastian Ullrich
86f303774a chore: harden markPersistent uses (#6257)
This API may or may not have been a footgun, better to be safe than
`sorry`
2024-11-29 14:33:33 +00:00
Sebastian Ullrich
d97af36867 feat: simpler trace timing annotation logic (#6259)
This PR ensures that nesting trace nodes are annotated with timing
information iff `trace.profiler` is active.

The previous connection to the otherwise unrelated `profiler` option was
a remnant from before `trace.profiler` existed; if users want to
annotate explicitly activated trace classes only, they can instead
increase `trace.profiler.threshold`.
2024-11-29 14:03:01 +00:00
Mac Malone
27cc0c8039 feat: USize.reduceToNat (#6190)
This PR adds the builtin simproc `USize.reduceToNat` which reduces the
`USize.toNat` operation on literals less than `UInt32.size` (i.e.,
`4294967296`).
2024-11-29 08:24:40 +00:00
Mac Malone
c9ee66fb1f chore: lake: fix typo in materialize error (#6250) 2024-11-29 06:20:51 +00:00
Kim Morrison
5c7e027b03 chore: cleanup of List/Array lemmas (#6249)
This PR performs further cleanup of `List/Lemmas.lean` and
`Array/Lemmas.lean`, trying to make them more parallel.

Still a long way to go.
2024-11-29 06:12:38 +00:00
Mac Malone
4969ec9cdb feat: more UInt lemmas (#6205)
This PR upstreams some UInt theorems from Batteries and adds more
`toNat`-related theorems. It also adds the missing `UInt8` and `UInt16`
to/from `USize` conversions so that the the interface is uniform across
the UInt types.

**Summary of all changes:**

* Upstreamed and added `toNat` constructors lemmas: `toNat_mk`,
`ofNat_toNat`, `toNat_ofNat`, `toNat_ofNatCore`, and
`USize.toNat_ofNat32`
* Upstreamed and added `toNat` canonicalization; `val_val_eq_toNat` and
`toNat_toBitVec_eq_toNat`
* Added injectivity iffs: `toBitVec_inj`, `toNat_inj`, and `val_inj`
* Added inequality iffs: `le_iff_toNat_le` and `lt_iff_toNat_lt`
* Upstreamed antisymmetry lemmas: `le_antisymm` and `le_antisymm_iff`
* Upstreamed missing `toNat` lemmas on arithmetic operations:
`toNat_add`, `toNat_sub`, `toNat_mul`
* Upstreamed and added missing conversion lemmas: `toNat_toUInt*` and
`toNat_USize`
* Added missing `USize` conversions: `USize.toUInt8`, `UInt8.toUSize`,
`USize.toUInt16`, `UInt16.toUSize`
2024-11-29 02:08:52 +00:00
Mac Malone
827062f807 feat: System.Platform.numBits inequalities (#6247)
This PR adds the theorems `numBits_pos`, `le_numBits`, `numBits_le` ,
which make proving inequalities about `System.Platform.numBits` easier.
2024-11-28 21:20:47 +00:00
Kim Morrison
6d495586a1 chore: deprecate Fin.ofNat (replaced by Fin.ofNat', subsequently to be renamed) (#6242)
This PR deprecates `Fin.ofNat` in favour of `Fin.ofNat'` (which takes an
`[NeZero]` instance, rather than returning an element of `Fin (n+1)`).

After leaving the deprecation warning in place for some time, we will
then rename `ofNat'` back to `ofNat`.
2024-11-28 05:23:23 +00:00
Kim Morrison
10d1d2cc25 chore: cleanup in Array/Lemmas (#6243) 2024-11-28 03:50:41 +00:00
Wojciech Nawrocki
2325f5c7b9 doc: explain abstraction order (#6239)
This PR explains the order in which `Expr.abstract` introduces de Bruijn
indices.
2024-11-28 01:37:30 +00:00
Sofia Rodrigues
88e3a2b1ab fix: improve directory fallback on Linux and trim local time identifier (#6221)
This PR fixes:
- Problems in other linux distributions that the default `tzdata`
directory is not the same as previously defined by ensuring it with a
fallback behavior when directory is missing.
- Trim unnecessary characters from local time identifier.
2024-11-27 14:52:35 +00:00
Lean stage0 autoupdater
b378fe98a7 chore: update stage0 2024-11-27 14:20:47 +00:00
Sebastian Ullrich
5f1ff42a15 fix: Runtime.markPersistent is unsafe (#6209)
This PR documents under which conditions `Runtime.markPersistent` is
unsafe and adjusts the elaborator accordingly
2024-11-27 13:32:05 +00:00
Jens Petersen
30d01f7a9a fix: add cmake COPY_CADICAL option to allow turning off install copy (#5931)
This PR adds a cmake knob to allow turning off installing a copy of
`cadical`.
This can be useful for custom builds/installs where cadical is already
available in the system.

Closes: #5603
2024-11-27 13:21:20 +00:00
Sebastian Ullrich
81b85d8e2f fix: reparsing may need to backtrack two commands (#6236)
This PR fixes an issue where edits to a command containing a nested
docstring fail to reparse the entire command.

Fixes #6227
2024-11-27 13:06:57 +00:00
Sebastian Ullrich
5982a6d230 chore: default parseQuotWithCurrentStage to true in stage 0 (#6212)
Use the default that solves bootstrapping issues in exchange for an
insignificant(?) perf overhead
2024-11-27 12:58:44 +00:00
Mac Malone
ac1197ff59 feat: Lean.loadPlugin (#6130)
This PR adds `Lean.loadPlugin` which exposes functionality similar to
the `lean` executable's `--plugin` option to Lean code.

This will allow custom Lean frontends (e.g., Lake, the Lean language
server) to also load plugins.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-11-27 09:56:31 +00:00
Kim Morrison
609346f5e0 feat: relate Nat.fold/foldRev/any/all to List.finRange (#6235)
This PR relates that operations `Nat.fold`/`foldRev`/`any`/`all` to the
corresponding List operations over `List.finRange`.
2024-11-27 05:38:18 +00:00
Mac Malone
04f80a1f9f feat: lake: detailed Reservoir fetch error (#6231)
This PR improves the errors Lake produces when it fails to fetch a
dependency from Reservoir. If the package is not indexed, it will
produce a suggestion about how to require it from GitHub.

Closes #5330.
2024-11-27 05:07:09 +00:00
Kim Morrison
7e9dd5668b feat: upstream List.finRange from Batteries (#6234)
This PR upstreams the definition and basic lemmas about `List.finRange`
from Batteries.

Thanks for contributors to Batteries and Mathlib who've previously
worked on this material. Further PRs are welcome here. I'll be adding
more API later.
2024-11-27 04:27:22 +00:00
Kim Morrison
79f050b816 feat: upstream Vector lemmas (#6233)
This PR upstreams lemmas about `Vector` from Batteries.

I'll be adding more soon, and PRs are welcome, particularly from those
who have previously contributed to `Vector` in Batteries.
2024-11-27 04:19:30 +00:00
Siddharth
af4a3f2251 feat: BitVec.toInt_abs (#6154)
This PR implements `BitVec.toInt_abs`.


The absolute value of `x : BitVec w` is naively a case split on the sign
of `x`.
However, recall that when `x = intMin w`, `-x = x`.
Thus, the full value of `abs x` is computed by the case split:
- If `x : BitVec w` is `intMin`, then its absolute value is also `intMin
w`, and
  thus `toInt` will equal `intMin.toInt`.
- Otherwise, if `x` is negative, then `x.abs.toInt = (-x).toInt`.
- Finally, when `x` is nonnegative, then `x.abs.toInt = x.toInt`.

```lean
theorem toInt_abs {x : BitVec w} :
  x.abs.toInt =
    if x = intMin w then (intMin w).toInt
    else if x.msb then -x.toInt
    else x.toInt
```

We also provide a variant of `toInt_abs` that
hides the case split for `x` being positive or negative by using
`natAbs`.
```lean
theorem toInt_abs_eq_natAbs {x : BitVec w} : x.abs.toInt =
    if x = intMin w then (intMin w).toInt else x.toInt.natAbs
```

Supercedes https://github.com/leanprover/lean4/pull/5787

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2024-11-27 03:50:55 +00:00
Siddharth
7692343720 feat: BitVec.toNat BitVec.signExtend (#6155)
This PR adds `toNat` theorems for `BitVec.signExtend.`

Sign extending to a larger bitwidth depends on the msb. If the msb is
false, then the result equals the original value. If the msb is true,
then we add a value of `(2^v - 2^w)`, which arises from the sign
extension.

```lean
theorem toNat_signExtend (x : BitVec w) {v : Nat} :
    (x.signExtend v).toNat = (x.setWidth v).toNat + if x.msb then 2^v - 2^w else 0
```

Co-authored-by: Harun Khan <harun19@stanford.edu>
2024-11-27 03:50:15 +00:00
Luisa Cicolini
597ef8cfee feat: add Nat.mod_eq_sub and fix dependencies from Nat.sub_mul_eq_mod_of_lt_of_le (#6160)
This PR adds theorem `mod_eq_sub`, makes theorem
`sub_mul_eq_mod_of_lt_of_le` not private anymore and moves its location
within the `rotate*` section to use it in other proofs.
2024-11-27 03:48:59 +00:00
832 changed files with 4828 additions and 1581 deletions

View File

@@ -14,6 +14,7 @@ jobs:
sparse-checkout: |
src/Lean
src/Std
src/lake/Lake
- name: Check Prelude
run: |
failed_files=""
@@ -21,7 +22,7 @@ jobs:
if ! grep -q "^prelude$" "$file"; then
failed_files="$failed_files$file\n"
fi
done < <(find src/Lean src/Std -name '*.lean' -print0)
done < <(find src/Lean src/Std src/lake/Lake -name '*.lean' -print0)
if [ -n "$failed_files" ]; then
echo -e "The following files should use 'prelude':\n$failed_files"
exit 1

View File

@@ -34,7 +34,7 @@ jobs:
- name: Download artifact from the previous workflow.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: download-artifact
uses: dawidd6/action-download-artifact@v6 # https://github.com/marketplace/actions/download-workflow-artifact
uses: dawidd6/action-download-artifact@v7 # https://github.com/marketplace/actions/download-workflow-artifact
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts
@@ -111,7 +111,7 @@ jobs:
- name: 'Setup jq'
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: dcarbone/install-jq-action@v2.1.0
uses: dcarbone/install-jq-action@v3.0.1
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
- name: Check merge-base and nightly-testing-YYYY-MM-DD

View File

@@ -8,11 +8,16 @@ 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.15.0
v4.16.0
----------
Development in progress.
v4.15.0
----------
Release candidate, release notes will be copied from the branch `releases/v4.15.0` once completed.
v4.14.0
----------
@@ -88,7 +93,7 @@ v4.13.0
* [#4768](https://github.com/leanprover/lean4/pull/4768) fixes a parse error when `..` appears with a `.` on the next line
* Metaprogramming
* [#3090](https://github.com/leanprover/lean4/pull/3090) handles level parameters in `Meta.evalExpr` (@eric-wieser)
* [#3090](https://github.com/leanprover/lean4/pull/3090) handles level parameters in `Meta.evalExpr` (@eric-wieser)
* [#5401](https://github.com/leanprover/lean4/pull/5401) instance for `Inhabited (TacticM α)` (@alexkeizer)
* [#5412](https://github.com/leanprover/lean4/pull/5412) expose Kernel.check for debugging purposes
* [#5556](https://github.com/leanprover/lean4/pull/5556) improves the "invalid projection" type inference error in `inferType`.

View File

@@ -1 +0,0 @@
[0829/202002.254:ERROR:crashpad_client_win.cc(868)] not connected

View File

@@ -103,10 +103,21 @@ your PR using rebase merge, bypassing the merge queue.
As written above, changes in meta code in the current stage usually will only
affect later stages. This is an issue in two specific cases.
* For the special case of *quotations*, it is desirable to have changes in builtin parsers affect them immediately: when the changes in the parser become active in the next stage, builtin macros implemented via quotations should generate syntax trees compatible with the new parser, and quotation patterns in builtin macros and elaborators should be able to match syntax created by the new parser and macros.
Since quotations capture the syntax tree structure during execution of the current stage and turn it into code for the next stage, we need to run the current stage's builtin parsers in quotations via the interpreter for this to work.
Caveats:
* We activate this behavior by default when building stage 1 by setting `-Dinternal.parseQuotWithCurrentStage=true`.
We force-disable it inside `macro/macro_rules/elab/elab_rules` via `suppressInsideQuot` as they are guaranteed not to run in the next stage and may need to be run in the current one, so the stage 0 parser is the correct one to use for them.
It may be necessary to extend this disabling to functions that contain quotations and are (exclusively) used by one of the mentioned commands. A function using quotations should never be used by both builtin and non-builtin macros/elaborators. Example: https://github.com/leanprover/lean4/blob/f70b7e5722da6101572869d87832494e2f8534b7/src/Lean/Elab/Tactic/Config.lean#L118-L122
* The parser needs to be reachable via an `import` statement, otherwise the version of the previous stage will silently be used.
* Only the parser code (`Parser.fn`) is affected; all metadata such as leading tokens is taken from the previous stage.
For an example, see https://github.com/leanprover/lean4/commit/f9dcbbddc48ccab22c7674ba20c5f409823b4cc1#diff-371387aed38bb02bf7761084fd9460e4168ae16d1ffe5de041b47d3ad2d22422R13
* For *non-builtin* meta code such as `notation`s or `macro`s in
`Notation.lean`, we expect changes to affect the current file and all later
files of the same stage immediately, just like outside the stdlib. To ensure
this, we need to build the stage using `-Dinterpreter.prefer_native=false` -
this, we build stage 1 using `-Dinterpreter.prefer_native=false` -
otherwise, when executing a macro, the interpreter would notice that there is
already a native symbol available for this function and run it instead of the
new IR, but the symbol is from the previous stage!
@@ -124,26 +135,11 @@ affect later stages. This is an issue in two specific cases.
further stages (e.g. after an `update-stage0`) will then need to be compiled
with the flag set to `false` again since they will expect the new signature.
For an example, see https://github.com/leanprover/lean4/commit/da4c46370d85add64ef7ca5e7cc4638b62823fbb.
When enabling `prefer_native`, we usually want to *disable* `parseQuotWithCurrentStage` as it would otherwise make quotations use the interpreter after all.
However, there is a specific case where we want to set both options to `true`: when we make changes to a non-builtin parser like `simp` that has a builtin elaborator, we cannot have the new parser be active outside of quotations in stage 1 as the builtin elaborator from stage 0 would not understand them; on the other hand, we need quotations in e.g. the builtin `simp` elaborator to produce the new syntax in the next stage.
As this issue usually affects only tactics, enabling `debug.byAsSorry` instead of `prefer_native` can be a simpler solution.
* For the special case of *quotations*, it is desirable to have changes in
built-in parsers affect them immediately: when the changes in the parser
become active in the next stage, macros implemented via quotations should
generate syntax trees compatible with the new parser, and quotation patterns
in macro and elaborators should be able to match syntax created by the new
parser and macros. Since quotations capture the syntax tree structure during
execution of the current stage and turn it into code for the next stage, we
need to run the current stage's built-in parsers in quotation via the
interpreter for this to work. Caveats:
* Since interpreting full parsers is not nearly as cheap and we rarely change
built-in syntax, this needs to be opted in using `-Dinternal.parseQuotWithCurrentStage=true`.
* The parser needs to be reachable via an `import` statement, otherwise the
version of the previous stage will silently be used.
* Only the parser code (`Parser.fn`) is affected; all metadata such as leading
tokens is taken from the previous stage.
For an example, see https://github.com/leanprover/lean4/commit/f9dcbbddc48ccab22c7674ba20c5f409823b4cc1#diff-371387aed38bb02bf7761084fd9460e4168ae16d1ffe5de041b47d3ad2d22422
(from before the flag defaulted to `false`).
For a `prefer_native` example, see https://github.com/leanprover/lean4/commit/da4c46370d85add64ef7ca5e7cc4638b62823fbb.
To modify either of these flags both for building and editing the stdlib, adjust
the code in `stage0/src/stdlib_flags.h`. The flags will automatically be reset

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
source ../../tests/common.sh
exec_check lean -Dlinter.all=false "$f"
exec_check_raw lean -Dlinter.all=false "$f"

View File

@@ -139,7 +139,7 @@ You might be wondering, how does the context actually move through the `ReaderM`
add an input argument to a function by modifying its return type? There is a special command in
Lean that will show you the reduced types:
-/
#reduce ReaderM Environment String -- Environment → String
#reduce (types := true) ReaderM Environment String -- Environment → String
/-!
And you can see here that this type is actually a function! It's a function that takes an
`Environment` as input and returns a `String`.
@@ -196,4 +196,4 @@ entirely.
Now it's time to move on to [StateM Monad](states.lean.md) which is like a `ReaderM` that is
also updatable.
-/
-/

12
script/mathlib-bench Executable file
View File

@@ -0,0 +1,12 @@
#! /bin/env bash
# Open a Mathlib4 PR for benchmarking a given Lean 4 PR
set -euo pipefail
[ $# -eq 1 ] || (echo "usage: $0 <lean4 PR #>"; exit 1)
LEAN_PR=$1
PR_RESPONSE=$(gh api repos/leanprover-community/mathlib4/pulls -X POST -f head=lean-pr-testing-$LEAN_PR -f base=nightly-testing -f title="leanprover/lean4#$LEAN_PR benchmarking" -f draft=true -f body="ignore me")
PR_NUMBER=$(echo "$PR_RESPONSE" | jq '.number')
echo "opened https://github.com/leanprover-community/mathlib4/pull/$PR_NUMBER"
gh api repos/leanprover-community/mathlib4/issues/$PR_NUMBER/comments -X POST -f body="!bench" > /dev/null

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 15)
set(LEAN_VERSION_MINOR 16)
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'")
@@ -51,6 +51,8 @@ option(LLVM "LLVM" OFF)
option(USE_GITHASH "GIT_HASH" ON)
# When ON we install LICENSE files to CMAKE_INSTALL_PREFIX
option(INSTALL_LICENSE "INSTALL_LICENSE" ON)
# When ON we install a copy of cadical
option(INSTALL_CADICAL "Install a copy of cadical" ON)
# When ON thread storage is automatically finalized, it assumes platform support pthreads.
# This option is important when using Lean as library that is invoked from a different programming language (e.g., Haskell).
option(AUTO_THREAD_FINALIZATION "AUTO_THREAD_FINALIZATION" ON)
@@ -120,7 +122,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# From https://emscripten.org/docs/compiling/WebAssembly.html#backends:
# > The simple and safe thing is to pass all -s flags at both compile and link time.
set(EMSCRIPTEN_SETTINGS "-s ALLOW_MEMORY_GROWTH=1 -fwasm-exceptions -pthread -flto")
string(APPEND LEANC_EXTRA_FLAGS " -pthread")
string(APPEND LEANC_EXTRA_CC_FLAGS " -pthread")
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_EMSCRIPTEN ${EMSCRIPTEN_SETTINGS}")
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${EMSCRIPTEN_SETTINGS}")
endif()
@@ -155,11 +157,11 @@ if ((${MULTI_THREAD} MATCHES "ON") AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin"))
endif ()
# We want explicit stack probes in huge Lean stack frames for robust stack overflow detection
string(APPEND LEANC_EXTRA_FLAGS " -fstack-clash-protection")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fstack-clash-protection")
# This makes signed integer overflow guaranteed to match 2's complement.
string(APPEND CMAKE_CXX_FLAGS " -fwrapv")
string(APPEND LEANC_EXTRA_FLAGS " -fwrapv")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fwrapv")
if(NOT MULTI_THREAD)
message(STATUS "Disabled multi-thread support, it will not be safe to run multiple threads in parallel")
@@ -449,7 +451,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
endif()
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
@@ -462,7 +464,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
endif()
@@ -477,7 +479,7 @@ if(NOT(${CMAKE_SYSTEM_NAME} MATCHES "Windows") AND NOT(${CMAKE_SYSTEM_NAME} MATC
string(APPEND CMAKE_EXE_LINKER_FLAGS " -rdynamic")
# hide all other symbols
string(APPEND CMAKE_CXX_FLAGS " -fvisibility=hidden -fvisibility-inlines-hidden")
string(APPEND LEANC_EXTRA_FLAGS " -fvisibility=hidden")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fvisibility=hidden")
endif()
# On Windows, add bcrypt for random number generation
@@ -542,9 +544,10 @@ include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" head
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
# Do embed flag for finding system libraries in dev builds
# Do embed flag for finding system headers and libraries in dev builds
if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE)
string(APPEND LEANC_EXTRA_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
string(APPEND LEANC_EXTRA_CC_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
endif()
add_subdirectory(initialize)
@@ -616,7 +619,7 @@ else()
OUTPUT_NAME leancpp)
endif()
if((${STAGE} GREATER 0) AND CADICAL)
if((${STAGE} GREATER 0) AND CADICAL AND INSTALL_CADICAL)
add_custom_target(copy-cadical
COMMAND cmake -E copy_if_different "${CADICAL}" "${CMAKE_BINARY_DIR}/bin/cadical${CMAKE_EXECUTABLE_SUFFIX}")
add_dependencies(leancpp copy-cadical)
@@ -738,7 +741,7 @@ file(COPY ${LEAN_SOURCE_DIR}/bin/leanmake DESTINATION ${CMAKE_BINARY_DIR}/bin)
install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION bin)
if (${STAGE} GREATER 0 AND CADICAL)
if (${STAGE} GREATER 0 AND CADICAL AND INSTALL_CADICAL)
install(PROGRAMS "${CADICAL}" DESTINATION bin)
endif()

View File

@@ -20,3 +20,5 @@ import Init.Data.Array.MapIdx
import Init.Data.Array.Set
import Init.Data.Array.Monadic
import Init.Data.Array.FinRange
import Init.Data.Array.Perm
import Init.Data.Array.Find

View File

@@ -251,7 +251,7 @@ theorem getElem?_attach {xs : Array α} {i : Nat} :
theorem getElem_attachWith {xs : Array α} {P : α Prop} {H : a xs, P a}
{i : Nat} (h : i < (xs.attachWith P H).size) :
(xs.attachWith P H)[i] = xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h)) :=
getElem_pmap ..
getElem_pmap _ _ h
@[simp]
theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :

View File

@@ -474,6 +474,10 @@ def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f
| _ => pure
return none
/--
Note that the universe level is contrained to `Type` here,
to avoid having to have the predicate live in `p : α → m (ULift Bool)`.
-/
@[inline]
def findM? {α : Type} {m : Type Type} [Monad m] (p : α m Bool) (as : Array α) : m (Option α) := do
for a in as do
@@ -585,8 +589,12 @@ def zipWithIndex (arr : Array α) : Array (α × Nat) :=
arr.mapIdx fun i a => (a, i)
@[inline]
def find? {α : Type} (p : α Bool) (as : Array α) : Option α :=
Id.run <| as.findM? p
def find? {α : Type u} (p : α Bool) (as : Array α) : Option α :=
Id.run do
for a in as do
if p a then
return a
return none
@[inline]
def findSome? {α : Type u} {β : Type v} (f : α Option β) (as : Array α) : Option β :=

View File

@@ -21,15 +21,14 @@ import Init.TacticsExtra
## Theorems about `Array`.
-/
/-! ### Preliminaries about `Array` needed for `List.toArray` lemmas.
This section contains only the bare minimum lemmas about `Array`
that we need to write lemmas about `List.toArray`.
-/
namespace Array
@[simp] theorem mem_toArray {a : α} {l : List α} : a l.toArray a l := by
simp [mem_def]
@[simp] theorem getElem_mk {xs : List α} {i : Nat} (h : i < xs.length) : (Array.mk xs)[i] = xs[i] := rfl
theorem getElem_eq_getElem_toList {a : Array α} (h : i < a.size) : a[i] = a.toList[i] := rfl
theorem getElem?_eq_getElem {a : Array α} {i : Nat} (h : i < a.size) : a[i]? = some a[i] :=
getElem?_pos ..
@@ -39,96 +38,34 @@ theorem getElem?_eq_getElem {a : Array α} {i : Nat} (h : i < a.size) : a[i]? =
· rw [getElem?_neg a i h]
simp_all
@[simp] theorem none_eq_getElem?_iff {a : Array α} {i : Nat} : none = a[i]? a.size i := by
simp [eq_comm (a := none)]
theorem getElem?_eq {a : Array α} {i : Nat} :
a[i]? = if h : i < a.size then some a[i] else none := by
split
· simp_all [getElem?_eq_getElem]
· simp_all
theorem getElem?_eq_some_iff {a : Array α} : a[i]? = some b h : i < a.size, a[i] = b := by
simp [getElem?_eq]
theorem some_eq_getElem?_iff {a : Array α} : some b = a[i]? h : i < a.size, a[i] = b := by
rw [eq_comm, getElem?_eq_some_iff]
theorem getElem?_eq_getElem?_toList (a : Array α) (i : Nat) : a[i]? = a.toList[i]? := by
rw [getElem?_eq]
split <;> simp_all
theorem getElem_push_lt (a : Array α) (x : α) (i : Nat) (h : i < a.size) :
have : i < (a.push x).size := by simp [*, Nat.lt_succ_of_le, Nat.le_of_lt]
(a.push x)[i] = a[i] := by
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append, List.getElem_append_left, h]
@[simp] theorem getElem_push_eq (a : Array α) (x : α) : (a.push x)[a.size] = x := by
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append]
rw [List.getElem_append_right] <;> simp [getElem_eq_getElem_toList, Nat.zero_lt_one]
theorem getElem_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size) :
(a.push x)[i] = if h : i < a.size then a[i] else x := by
by_cases h' : i < a.size
· simp [getElem_push_lt, h']
· simp at h
simp [getElem_push_lt, Nat.le_antisymm (Nat.le_of_lt_succ h) (Nat.ge_of_not_lt h')]
@[deprecated getElem_push (since := "2024-10-21")] abbrev get_push := @getElem_push
@[deprecated getElem_push_lt (since := "2024-10-21")] abbrev get_push_lt := @getElem_push_lt
@[deprecated getElem_push_eq (since := "2024-10-21")] abbrev get_push_eq := @getElem_push_eq
@[simp] theorem mem_push {a : Array α} {x y : α} : x a.push y x a x = y := by
simp [mem_def]
theorem mem_push_self {a : Array α} {x : α} : x a.push x :=
mem_push.2 (Or.inr rfl)
theorem mem_push_of_mem {a : Array α} {x : α} (y : α) (h : x a) : x a.push y :=
mem_push.2 (Or.inl h)
theorem getElem_of_mem {a} {l : Array α} (h : a l) : (n : Nat) (h : n < l.size), l[n]'h = a := by
cases l
simp [List.getElem_of_mem (by simpa using h)]
theorem getElem?_of_mem {a} {l : Array α} (h : a l) : n : Nat, l[n]? = some a :=
let n, _, e := getElem_of_mem h; n, e getElem?_eq_getElem _
theorem mem_of_getElem? {l : Array α} {n : Nat} {a : α} (e : l[n]? = some a) : a l :=
let _, e := getElem?_eq_some_iff.1 e; e getElem_mem ..
theorem mem_iff_getElem {a} {l : Array α} : a l (n : Nat) (h : n < l.size), l[n]'h = a :=
getElem_of_mem, fun _, _, e => e getElem_mem ..
theorem mem_iff_getElem? {a} {l : Array α} : a l n : Nat, l[n]? = some a := by
simp [getElem?_eq_some_iff, mem_iff_getElem]
theorem forall_getElem {l : Array α} {p : α Prop} :
( (n : Nat) h, p (l[n]'h)) a, a l p a := by
cases l; simp [List.forall_getElem]
@[simp] theorem get_eq_getElem (a : Array α) (i : Nat) (h) : a.get i h = a[i] := rfl
@[simp] theorem get!_eq_getElem! [Inhabited α] (a : Array α) (i : Nat) : a.get! i = a[i]! := by
simp [getElem!_def, get!, getD]
split <;> rename_i h
· simp [getElem?_eq_getElem h]
rfl
· simp [getElem?_eq_none_iff.2 (by simpa using h)]
theorem singleton_inj : #[a] = #[b] a = b := by
simp
theorem singleton_eq_toArray_singleton (a : α) : #[a] = [a].toArray := rfl
@[simp] theorem mem_toArray {a : α} {l : List α} : a l.toArray a l := by
simp [mem_def]
end Array
namespace List
open Array
/-! ### Lemmas about `List.toArray`.
We prefer to pull `List.toArray` outwards.
-/
namespace List
open Array
theorem toArray_inj {a b : List α} (h : a.toArray = b.toArray) : a = b := by
cases a with
| nil => simpa using h
| cons a as =>
cases b with
| nil => simp at h
| cons b bs => simpa using h
@[simp] theorem size_toArrayAux {a : List α} {b : Array α} :
(a.toArrayAux b).size = b.size + a.length := by
@@ -293,7 +230,13 @@ theorem findRevM?_toArray [Monad m] [LawfulMonad m] (f : α → m Bool) (l : Lis
@[simp] theorem find?_toArray (f : α Bool) (l : List α) :
l.toArray.find? f = l.find? f := by
rw [Array.find?, findM?_id, findM?_toArray, Id.run]
rw [Array.find?]
simp only [Id.run, Id, Id.pure_eq, Id.bind_eq, forIn_toArray]
induction l with
| nil => simp
| cons a l ih =>
simp only [forIn_cons, Id.pure_eq, Id.bind_eq, find?]
by_cases f a <;> simp_all
theorem isPrefixOfAux_toArray_succ [BEq α] (l₁ l₂ : List α) (hle : l₁.length l₂.length) (i : Nat) :
Array.isPrefixOfAux l₁.toArray l₂.toArray hle (i + 1) =
@@ -419,10 +362,248 @@ theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α → O
Array.zipWithAll as.toArray bs.toArray f = (List.zipWithAll f as bs).toArray := by
simp [Array.zipWithAll, zipWithAll_go_toArray]
@[simp] theorem toArray_appendList (l₁ l₂ : List α) :
l₁.toArray ++ l₂ = (l₁ ++ l₂).toArray := by
apply ext'
simp
@[simp] theorem pop_toArray (l : List α) : l.toArray.pop = l.dropLast.toArray := by
apply ext'
simp
theorem takeWhile_go_succ (p : α Bool) (a : α) (l : List α) (i : Nat) :
takeWhile.go p (a :: l).toArray (i+1) r = takeWhile.go p l.toArray i r := by
rw [takeWhile.go, takeWhile.go]
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
getElem_toArray, getElem_cons_succ]
split
rw [takeWhile_go_succ]
rfl
theorem takeWhile_go_toArray (p : α Bool) (l : List α) (i : Nat) :
Array.takeWhile.go p l.toArray i r = r ++ (takeWhile p (l.drop i)).toArray := by
induction l generalizing i r with
| nil => simp [takeWhile.go]
| cons a l ih =>
rw [takeWhile.go]
cases i with
| zero =>
simp [takeWhile_go_succ, ih, takeWhile_cons]
split <;> simp
| succ i =>
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
getElem_toArray, getElem_cons_succ, drop_succ_cons]
split <;> rename_i h₁
· rw [takeWhile_go_succ, ih]
rw [ getElem_cons_drop_succ_eq_drop h₁, takeWhile_cons]
split <;> simp_all
· simp_all [drop_eq_nil_of_le]
@[simp] theorem takeWhile_toArray (p : α Bool) (l : List α) :
l.toArray.takeWhile p = (l.takeWhile p).toArray := by
simp [Array.takeWhile, takeWhile_go_toArray]
end List
namespace Array
/-! ## Preliminaries -/
/-! ### toList -/
theorem toList_inj {a b : Array α} (h : a.toList = b.toList) : a = b := by
cases a; cases b; simpa using h
/-! ### empty -/
@[simp] theorem empty_eq {xs : Array α} : #[] = xs xs = #[] := by
cases xs <;> simp
/-! ### size -/
theorem eq_empty_of_size_eq_zero (h : l.size = 0) : l = #[] := by
cases l
simp_all
theorem ne_empty_of_size_eq_add_one (h : l.size = n + 1) : l #[] := by
cases l
simpa using List.ne_nil_of_length_eq_add_one h
theorem ne_empty_of_size_pos (h : 0 < l.size) : l #[] := by
cases l
simpa using List.ne_nil_of_length_pos h
@[simp] theorem size_eq_zero : l.size = 0 l = #[] :=
eq_empty_of_size_eq_zero, fun h => h rfl
theorem size_pos_of_mem {a : α} {l : Array α} (h : a l) : 0 < l.size := by
cases l
simp only [mem_toArray] at h
simpa using List.length_pos_of_mem h
theorem exists_mem_of_size_pos {l : Array α} (h : 0 < l.size) : a, a l := by
cases l
simpa using List.exists_mem_of_length_pos h
theorem size_pos_iff_exists_mem {l : Array α} : 0 < l.size a, a l :=
exists_mem_of_size_pos, fun _, h => size_pos_of_mem h
theorem exists_mem_of_size_eq_add_one {l : Array α} (h : l.size = n + 1) : a, a l := by
cases l
simpa using List.exists_mem_of_length_eq_add_one h
theorem size_pos {l : Array α} : 0 < l.size l #[] :=
Nat.pos_iff_ne_zero.trans (not_congr size_eq_zero)
theorem size_eq_one {l : Array α} : l.size = 1 a, l = #[a] := by
cases l
simpa using List.length_eq_one
/-! ### push -/
theorem push_ne_empty {a : α} {xs : Array α} : xs.push a #[] := by
cases xs
simp
@[simp] theorem push_ne_self {a : α} {xs : Array α} : xs.push a xs := by
cases xs
simp
@[simp] theorem ne_push_self {a : α} {xs : Array α} : xs xs.push a := by
rw [ne_eq, eq_comm]
simp
theorem back_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : a = b := by
cases xs
cases ys
simp only [List.push_toArray, mk.injEq] at h
replace h := List.append_inj_right' h (by simp)
simpa using h
theorem pop_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : xs = ys := by
cases xs
cases ys
simp at h
replace h := List.append_inj_left' h (by simp)
simp [h]
theorem push_inj_left {a : α} {xs ys : Array α} : xs.push a = ys.push a xs = ys :=
pop_eq_of_push_eq, fun h => by simp [h]
theorem push_inj_right {a b : α} {xs : Array α} : xs.push a = xs.push b a = b :=
back_eq_of_push_eq, fun h => by simp [h]
theorem push_eq_push {a b : α} {xs ys : Array α} : xs.push a = ys.push b a = b xs = ys := by
constructor
· intro h
exact back_eq_of_push_eq h, pop_eq_of_push_eq h
· rintro rfl, rfl
rfl
theorem exists_push_of_ne_empty {xs : Array α} (h : xs #[]) :
(ys : Array α) (a : α), xs = ys.push a := by
rcases xs with xs
simp only [ne_eq, mk.injEq] at h
exact (xs.take (xs.length - 1)).toArray, xs.getLast h, by simp
theorem ne_empty_iff_exists_push {xs : Array α} :
xs #[] (ys : Array α) (a : α), xs = ys.push a :=
exists_push_of_ne_empty, fun _, _, eq => eq.symm push_ne_empty
theorem exists_push_of_size_pos {xs : Array α} (h : 0 < xs.size) :
(ys : Array α) (a : α), xs = ys.push a := by
replace h : xs #[] := size_pos.mp h
exact exists_push_of_ne_empty h
theorem size_pos_iff_exists_push {xs : Array α} :
0 < xs.size (ys : Array α) (a : α), xs = ys.push a :=
exists_push_of_size_pos, fun _, _, eq => by simp [eq]
theorem exists_push_of_size_eq_add_one {xs : Array α} (h : xs.size = n + 1) :
(ys : Array α) (a : α), xs = ys.push a :=
exists_push_of_size_pos (by simp [h])
/-! ## L[i] and L[i]? -/
@[deprecated List.getElem_toArray (since := "2024-11-29")]
theorem getElem_mk {xs : List α} {i : Nat} (h : i < xs.length) : (Array.mk xs)[i] = xs[i] := rfl
theorem getElem_eq_getElem_toList {a : Array α} (h : i < a.size) : a[i] = a.toList[i] := rfl
@[simp] theorem none_eq_getElem?_iff {a : Array α} {i : Nat} : none = a[i]? a.size i := by
simp [eq_comm (a := none)]
theorem getElem?_eq {a : Array α} {i : Nat} :
a[i]? = if h : i < a.size then some a[i] else none := by
split
· simp_all [getElem?_eq_getElem]
· simp_all
theorem getElem?_eq_some_iff {a : Array α} : a[i]? = some b h : i < a.size, a[i] = b := by
simp [getElem?_eq]
theorem some_eq_getElem?_iff {a : Array α} : some b = a[i]? h : i < a.size, a[i] = b := by
rw [eq_comm, getElem?_eq_some_iff]
theorem getElem?_eq_getElem?_toList (a : Array α) (i : Nat) : a[i]? = a.toList[i]? := by
rw [getElem?_eq]
split <;> simp_all
theorem getElem_push_lt (a : Array α) (x : α) (i : Nat) (h : i < a.size) :
have : i < (a.push x).size := by simp [*, Nat.lt_succ_of_le, Nat.le_of_lt]
(a.push x)[i] = a[i] := by
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append, List.getElem_append_left, h]
@[simp] theorem getElem_push_eq (a : Array α) (x : α) : (a.push x)[a.size] = x := by
simp only [push, getElem_eq_getElem_toList, List.concat_eq_append]
rw [List.getElem_append_right] <;> simp [getElem_eq_getElem_toList, Nat.zero_lt_one]
theorem getElem_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size) :
(a.push x)[i] = if h : i < a.size then a[i] else x := by
by_cases h' : i < a.size
· simp [getElem_push_lt, h']
· simp at h
simp [getElem_push_lt, Nat.le_antisymm (Nat.le_of_lt_succ h) (Nat.ge_of_not_lt h')]
@[deprecated getElem_push (since := "2024-10-21")] abbrev get_push := @getElem_push
@[deprecated getElem_push_lt (since := "2024-10-21")] abbrev get_push_lt := @getElem_push_lt
@[deprecated getElem_push_eq (since := "2024-10-21")] abbrev get_push_eq := @getElem_push_eq
@[simp] theorem mem_push {a : Array α} {x y : α} : x a.push y x a x = y := by
simp [mem_def]
theorem mem_push_self {a : Array α} {x : α} : x a.push x :=
mem_push.2 (Or.inr rfl)
theorem mem_push_of_mem {a : Array α} {x : α} (y : α) (h : x a) : x a.push y :=
mem_push.2 (Or.inl h)
theorem getElem_of_mem {a} {l : Array α} (h : a l) : (n : Nat) (h : n < l.size), l[n]'h = a := by
cases l
simp [List.getElem_of_mem (by simpa using h)]
theorem getElem?_of_mem {a} {l : Array α} (h : a l) : n : Nat, l[n]? = some a :=
let n, _, e := getElem_of_mem h; n, e getElem?_eq_getElem _
theorem mem_of_getElem? {l : Array α} {n : Nat} {a : α} (e : l[n]? = some a) : a l :=
let _, e := getElem?_eq_some_iff.1 e; e getElem_mem ..
theorem mem_iff_getElem {a} {l : Array α} : a l (n : Nat) (h : n < l.size), l[n]'h = a :=
getElem_of_mem, fun _, _, e => e getElem_mem ..
theorem mem_iff_getElem? {a} {l : Array α} : a l n : Nat, l[n]? = some a := by
simp [getElem?_eq_some_iff, mem_iff_getElem]
theorem forall_getElem {l : Array α} {p : α Prop} :
( (n : Nat) h, p (l[n]'h)) a, a l p a := by
cases l; simp [List.forall_getElem]
theorem singleton_inj : #[a] = #[b] a = b := by
simp
theorem singleton_eq_toArray_singleton (a : α) : #[a] = [a].toArray := rfl
@[simp] theorem singleton_def (v : α) : singleton v = #[v] := rfl
-- This is a duplicate of `List.toArray_toList`.
@@ -496,6 +677,11 @@ where
simp only [ length_toList]
simp
@[simp] theorem mapM_empty [Monad m] (f : α m β) : mapM f #[] = pure #[] := by
rw [mapM, mapM.map]; rfl
@[simp] theorem map_empty (f : α β) : map f #[] = #[] := mapM_empty f
@[simp] theorem appendList_nil (arr : Array α) : arr ++ ([] : List α) = arr := Array.ext' (by simp)
@[simp] theorem appendList_cons (arr : Array α) (a : α) (l : List α) :
@@ -535,8 +721,6 @@ theorem size_uset (a : Array α) (v i h) : (uset a i v h).size = a.size := by si
/-! # get -/
@[simp] theorem get_eq_getElem (a : Array α) (i : Nat) (h) : a.get i h = a[i] := rfl
theorem getElem?_lt
(a : Array α) {i : Nat} (h : i < a.size) : a[i]? = some a[i] := dif_pos h
@@ -601,11 +785,26 @@ theorem getElem_set (a : Array α) (i : Nat) (h' : i < a.size) (v : α) (j : Nat
else
simp [setIfInBounds, h]
theorem getElem_setIfInBounds (a : Array α) (i : Nat) (v : α) (j : Nat)
(hj : j < (setIfInBounds a i v).size) :
(setIfInBounds a i v)[j]'hj = if i = j then v else a[j]'(by simpa using hj) := by
simp only [setIfInBounds]
split
· simp [getElem_set]
· simp only [size_setIfInBounds] at hj
rw [if_neg]
omega
@[simp] theorem getElem_setIfInBounds_eq (a : Array α) {i : Nat} (v : α) (h : _) :
(setIfInBounds a i v)[i]'h = v := by
simp at h
simp only [setIfInBounds, h, reduceDIte, getElem_set_eq]
@[simp] theorem getElem_setIfInBounds_ne (a : Array α) {i : Nat} (v : α) {j : Nat}
(hj : j < (setIfInBounds a i v).size) (h : i j) :
(setIfInBounds a i v)[j]'hj = a[j]'(by simpa using hj) := by
simp [getElem_setIfInBounds, h]
@[simp]
theorem getElem?_setIfInBounds_eq (a : Array α) {i : Nat} (p : i < a.size) (v : α) :
(a.setIfInBounds i v)[i]? = some v := by
@@ -807,11 +1006,6 @@ theorem get_set (a : Array α) (i : Nat) (hi : i < a.size) (j : Nat) (hj : j < a
(h : i j) : (a.set i v)[j]'(by simp [*]) = a[j] := by
simp only [set, getElem_eq_getElem_toList, List.getElem_set_ne h]
theorem getElem_setIfInBounds (a : Array α) (i : Nat) (v : α) (h : i < (setIfInBounds a i v).size) :
(setIfInBounds a i v)[i] = v := by
simp at h
simp only [setIfInBounds, h, reduceDIte, getElem_set_eq]
theorem set_set (a : Array α) (i : Nat) (h) (v v' : α) :
(a.set i v h).set i v' (by simp [h]) = a.set i v' := by simp [set, List.set_set]
@@ -855,11 +1049,6 @@ theorem swapAt!_def (a : Array α) (i : Nat) (v : α) (h : i < a.size) :
a.pop[i] = a[i]'(Nat.lt_of_lt_of_le (a.size_pop hi) (Nat.sub_le _ _)) :=
List.getElem_dropLast ..
theorem eq_empty_of_size_eq_zero {as : Array α} (h : as.size = 0) : as = #[] := by
apply ext
· simp [h]
· intros; contradiction
theorem eq_push_pop_back!_of_size_ne_zero [Inhabited α] {as : Array α} (h : as.size 0) :
as = as.pop.push as.back! := by
apply ext
@@ -1376,6 +1565,18 @@ theorem getElem?_append {as bs : Array α} {n : Nat} :
· exact getElem?_append_left h
· exact getElem?_append_right (by simpa using h)
@[simp] theorem toArray_eq_append_iff {xs : List α} {as bs : Array α} :
xs.toArray = as ++ bs xs = as.toList ++ bs.toList := by
cases as
cases bs
simp
@[simp] theorem append_eq_toArray_iff {as bs : Array α} {xs : List α} :
as ++ bs = xs.toArray as.toList ++ bs.toList = xs := by
cases as
cases bs
simp
/-! ### flatten -/
@[simp] theorem toList_flatten {l : Array (Array α)} :
@@ -1670,8 +1871,6 @@ instance [DecidableEq α] (a : α) (as : Array α) : Decidable (a ∈ as) :=
/-! ### swap -/
open Fin
@[simp] theorem getElem_swap_right (a : Array α) {i j : Nat} {hi hj} :
(a.swap i j hi hj)[j]'(by simpa using hj) = a[i] := by
simp [swap_def, getElem_set]
@@ -1690,7 +1889,7 @@ theorem getElem_swap' (a : Array α) (i j : Nat) {hi hj} (k : Nat) (hk : k < a.s
· simp_all only [getElem_swap_left]
· split <;> simp_all
theorem getElem_swap (a : Array α) (i j : Nat) {hi hj}(k : Nat) (hk : k < (a.swap i j).size) :
theorem getElem_swap (a : Array α) (i j : Nat) {hi hj} (k : Nat) (hk : k < (a.swap i j).size) :
(a.swap i j hi hj)[k] = if k = i then a[j] else if k = j then a[i] else a[k]'(by simp_all) := by
apply getElem_swap'
@@ -1753,6 +1952,13 @@ theorem eraseIdx_eq_eraseIdxIfInBounds {a : Array α} {i : Nat} (h : i < a.size)
(as.zip bs).size = min as.size bs.size :=
as.size_zipWith bs Prod.mk
@[simp] theorem getElem_zipWith (as : Array α) (bs : Array β) (f : α β γ) (i : Nat)
(hi : i < (as.zipWith bs f).size) :
(as.zipWith bs f)[i] = f (as[i]'(by simp at hi; omega)) (bs[i]'(by simp at hi; omega)) := by
cases as
cases bs
simp
/-! ### findSomeM?, findM?, findSome?, find? -/
@[simp] theorem findSomeM?_toList [Monad m] [LawfulMonad m] (p : α m (Option β)) (as : Array α) :
@@ -1812,11 +2018,6 @@ Our goal is to have `simp` "pull `List.toArray` outwards" as much as possible.
apply ext'
simp
@[simp] theorem toArray_appendList (l₁ l₂ : List α) :
l₁.toArray ++ l₂ = (l₁ ++ l₂).toArray := by
apply ext'
simp
@[simp] theorem set_toArray (l : List α) (i : Fin l.toArray.size) (a : α) :
l.toArray.set i a = (l.set i a).toArray := by
apply ext'
@@ -1880,10 +2081,6 @@ theorem all_toArray (p : α → Bool) (l : List α) : l.toArray.all p = l.all p
apply ext'
simp
@[simp] theorem pop_toArray (l : List α) : l.toArray.pop = l.dropLast.toArray := by
apply ext'
simp
@[simp] theorem reverse_toArray (l : List α) : l.toArray.reverse = l.reverse.toArray := by
apply ext'
simp
@@ -1929,38 +2126,6 @@ theorem filterMap_toArray (f : α → Option β) (l : List α) :
@[simp] theorem toArray_ofFn (f : Fin n α) : (ofFn f).toArray = Array.ofFn f := by
ext <;> simp
theorem takeWhile_go_succ (p : α Bool) (a : α) (l : List α) (i : Nat) :
takeWhile.go p (a :: l).toArray (i+1) r = takeWhile.go p l.toArray i r := by
rw [takeWhile.go, takeWhile.go]
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
getElem_toArray, getElem_cons_succ]
split
rw [takeWhile_go_succ]
rfl
theorem takeWhile_go_toArray (p : α Bool) (l : List α) (i : Nat) :
Array.takeWhile.go p l.toArray i r = r ++ (takeWhile p (l.drop i)).toArray := by
induction l generalizing i r with
| nil => simp [takeWhile.go]
| cons a l ih =>
rw [takeWhile.go]
cases i with
| zero =>
simp [takeWhile_go_succ, ih, takeWhile_cons]
split <;> simp
| succ i =>
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right, Array.get_eq_getElem,
getElem_toArray, getElem_cons_succ, drop_succ_cons]
split <;> rename_i h₁
· rw [takeWhile_go_succ, ih]
rw [ getElem_cons_drop_succ_eq_drop h₁, takeWhile_cons]
split <;> simp_all
· simp_all [drop_eq_nil_of_le]
@[simp] theorem takeWhile_toArray (p : α Bool) (l : List α) :
l.toArray.takeWhile p = (l.takeWhile p).toArray := by
simp [Array.takeWhile, takeWhile_go_toArray]
@[simp] theorem eraseIdx_toArray (l : List α) (i : Nat) (h : i < l.toArray.size) :
l.toArray.eraseIdx i h = (l.eraseIdx i).toArray := by
rw [Array.eraseIdx]
@@ -2094,6 +2259,11 @@ theorem foldr_map' (g : α → β) (f : ααα) (f' : β → β → β
cases as
simp
@[simp] theorem getElem_reverse (as : Array α) (i : Nat) (hi : i < as.reverse.size) :
(as.reverse)[i] = as[as.size - 1 - i]'(by simp at hi; omega) := by
cases as
simp [Array.getElem_reverse]
/-! ### findSomeRevM?, findRevM?, findSomeRev?, findRev? -/
@[simp] theorem findSomeRevM?_eq_findSomeM?_reverse

View File

@@ -0,0 +1,65 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Nat.Perm
import Init.Data.Array.Lemmas
namespace Array
open List
/--
`Perm as bs` asserts that `as` and `bs` are permutations of each other.
This is a wrapper around `List.Perm`, and for now has much less API.
For more complicated verification, use `perm_iff_toList_perm` and the `List` API.
-/
def Perm (as bs : Array α) : Prop :=
as.toList ~ bs.toList
@[inherit_doc] scoped infixl:50 " ~ " => Perm
theorem perm_iff_toList_perm {as bs : Array α} : as ~ bs as.toList ~ bs.toList := Iff.rfl
@[simp] theorem perm_toArray (as bs : List α) : as.toArray ~ bs.toArray as ~ bs := by
simp [perm_iff_toList_perm]
@[simp, refl] protected theorem Perm.refl (l : Array α) : l ~ l := by
cases l
simp
protected theorem Perm.rfl {l : List α} : l ~ l := .refl _
theorem Perm.of_eq {l₁ l₂ : Array α} (h : l₁ = l₂) : l₁ ~ l₂ := h .rfl
protected theorem Perm.symm {l₁ l₂ : Array α} (h : l₁ ~ l₂) : l₂ ~ l₁ := by
cases l₁; cases l₂
simp only [perm_toArray] at h
simpa using h.symm
protected theorem Perm.trans {l₁ l₂ l₃ : Array α} (h₁ : l₁ ~ l₂) (h₂ : l₂ ~ l₃) : l₁ ~ l₃ := by
cases l₁; cases l₂; cases l₃
simp only [perm_toArray] at h₁ h₂
simpa using h₁.trans h₂
instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
trans h₁ h₂ := Perm.trans h₁ h₂
theorem perm_comm {l₁ l₂ : Array α} : l₁ ~ l₂ l₂ ~ l₁ := Perm.symm, Perm.symm
theorem Perm.push (x y : α) {l₁ l₂ : Array α} (p : l₁ ~ l₂) :
(l₁.push x).push y ~ (l₂.push y).push x := by
cases l₁; cases l₂
simp only [perm_toArray] at p
simp only [push_toArray, List.append_assoc, singleton_append, perm_toArray]
exact p.append (Perm.swap' _ _ Perm.nil)
theorem swap_perm {as : Array α} {i j : Nat} (h₁ : i < as.size) (h₂ : j < as.size) :
as.swap i j ~ as := by
simp only [swap, perm_iff_toList_perm, toList_set]
apply set_set_perm
end Array

View File

@@ -4,46 +4,46 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.Array.Basic
import Init.Data.Vector.Basic
import Init.Data.Ord
namespace Array
-- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
def qpartition (as : Array α) (lt : α α Bool) (lo hi : Nat) : Nat × Array α :=
if h : as.size = 0 then (0, as) else have : Inhabited α := as[0]'(by revert h; cases as.size <;> simp) -- TODO: remove
private def qpartition {n} (as : Vector α n) (lt : α α Bool) (lo hi : Nat)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {n : Nat // lo n} × Vector α n :=
let mid := (lo + hi) / 2
let as := if lt (as.get! mid) (as.get! lo) then as.swapIfInBounds lo mid else as
let as := if lt (as.get! hi) (as.get! lo) then as.swapIfInBounds lo hi else as
let as := if lt (as.get! mid) (as.get! hi) then as.swapIfInBounds mid hi else as
let pivot := as.get! hi
let rec loop (as : Array α) (i j : Nat) :=
let as := if lt as[mid] as[lo] then as.swap lo mid else as
let as := if lt as[hi] as[lo] then as.swap lo hi else as
let as := if lt as[mid] as[hi] then as.swap mid hi else as
let pivot := as[hi]
let rec loop (as : Vector α n) (i j : Nat)
(ilo : lo i := by omega) (jh : j < n := by omega) (w : i j := by omega) :=
if h : j < hi then
if lt (as.get! j) pivot then
let as := as.swapIfInBounds i j
loop as (i+1) (j+1)
if lt as[j] pivot then
loop (as.swap i j) (i+1) (j+1)
else
loop as i (j+1)
else
let as := as.swapIfInBounds i hi
(i, as)
termination_by hi - j
decreasing_by all_goals simp_wf; decreasing_trivial_pre_omega
(i, ilo, as.swap i hi)
loop as lo lo
@[inline] partial def qsort (as : Array α) (lt : α α Bool) (low := 0) (high := as.size - 1) : Array α :=
let rec @[specialize] sort (as : Array α) (low high : Nat) :=
if low < high then
let p := qpartition as lt low high;
-- TODO: fix `partial` support in the equation compiler, it breaks if we use `let (mid, as) := partition as lt low high`
let mid := p.1
let as := p.2
if mid >= high then as
@[inline] def qsort (as : Array α) (lt : α α Bool := by exact (· < ·))
(low := 0) (high := as.size - 1) : Array α :=
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) :=
if h₁ : lo < hi then
let mid, hmid, as := qpartition as lt lo hi
if h₂ : mid hi then
as
else
let as := sort as low mid
sort as (mid+1) high
sort (sort as lo mid) (mid+1) hi
else as
sort as low high
if h : as.size = 0 then
as
else
let low := min low (as.size - 1)
let high := min high (as.size - 1)
sort as, rfl low high |>.toArray
set_option linter.unusedVariables.funArgs false in
/--

View File

@@ -351,17 +351,17 @@ end relations
section cast
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
@[inline] def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq x.isLt)
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq x.isLt)
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
cast h (BitVec.ofNat n x) = BitVec.ofNat m x := by
(BitVec.ofNat n x).cast h = BitVec.ofNat m x := by
subst h; rfl
@[simp] theorem cast_cast {n m k : Nat} (h₁ : n = m) (h₂ : m = k) (x : BitVec n) :
cast h (cast h x) = cast (h₁ h₂) x :=
(x.cast h).cast h = x.cast (h₁ h₂) :=
rfl
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : cast h x = x := rfl
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : x.cast h = x := rfl
/--
Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to yield a

View File

@@ -410,21 +410,21 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
/-! ### cast -/
@[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (cast h x).toNat = x.toNat := rfl
@[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (x.cast h).toNat = x.toNat := rfl
@[simp] theorem toFin_cast (h : w = v) (x : BitVec w) :
(cast h x).toFin = x.toFin.cast (by rw [h]) :=
(x.cast h).toFin = x.toFin.cast (by rw [h]) :=
rfl
@[simp] theorem getLsbD_cast (h : w = v) (x : BitVec w) : (cast h x).getLsbD i = x.getLsbD i := by
@[simp] theorem getLsbD_cast (h : w = v) (x : BitVec w) : (x.cast h).getLsbD i = x.getLsbD i := by
subst h; simp
@[simp] theorem getMsbD_cast (h : w = v) (x : BitVec w) : (cast h x).getMsbD i = x.getMsbD i := by
@[simp] theorem getMsbD_cast (h : w = v) (x : BitVec w) : (x.cast h).getMsbD i = x.getMsbD i := by
subst h; simp
@[simp] theorem getElem_cast (h : w = v) (x : BitVec w) (p : i < v) : (cast h x)[i] = x[i] := by
@[simp] theorem getElem_cast (h : w = v) (x : BitVec w) (p : i < v) : (x.cast h)[i] = x[i] := by
subst h; simp
@[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (cast h x).msb = x.msb := by
@[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (x.cast h).msb = x.msb := by
simp [BitVec.msb]
/-! ### toInt/ofInt -/
@@ -658,7 +658,7 @@ theorem getElem?_setWidth (m : Nat) (x : BitVec n) (i : Nat) :
<;> omega
@[simp] theorem cast_setWidth (h : v = v') (x : BitVec w) :
cast h (setWidth v x) = setWidth v' x := by
(x.setWidth v).cast h = x.setWidth v' := by
subst h
ext
simp
@@ -671,7 +671,7 @@ theorem getElem?_setWidth (m : Nat) (x : BitVec n) (i : Nat) :
revert p
cases getLsbD x i <;> simp; omega
@[simp] theorem setWidth_cast {h : w = v} : (cast h x).setWidth k = x.setWidth k := by
@[simp] theorem setWidth_cast {x : BitVec w} {h : w = v} : (x.cast h).setWidth k = x.setWidth k := by
apply eq_of_getLsbD_eq
simp
@@ -1102,19 +1102,19 @@ theorem not_eq_comm {x y : BitVec w} : ~~~ x = y ↔ x = ~~~ y := by
/-! ### cast -/
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(cast h x) = cast h (~~~x) := by
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
ext
simp_all [lt_of_getLsbD]
@[simp] theorem and_cast {x y : BitVec w} (h : w = w') : cast h x &&& cast h y = cast h (x &&& y) := by
@[simp] theorem and_cast {x y : BitVec w} (h : w = w') : x.cast h &&& y.cast h = (x &&& y).cast h := by
ext
simp_all [lt_of_getLsbD]
@[simp] theorem or_cast {x y : BitVec w} (h : w = w') : cast h x ||| cast h y = cast h (x ||| y) := by
@[simp] theorem or_cast {x y : BitVec w} (h : w = w') : x.cast h ||| y.cast h = (x ||| y).cast h := by
ext
simp_all [lt_of_getLsbD]
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x ^^^ cast h y = cast h (x ^^^ y) := by
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : x.cast h ^^^ y.cast h = (x ^^^ y).cast h := by
ext
simp_all [lt_of_getLsbD]
@@ -1316,6 +1316,61 @@ theorem toNat_ushiftRight_lt (x : BitVec w) (n : Nat) (hn : n ≤ w) :
· apply hn
· apply Nat.pow_pos (by decide)
/-- Shifting right by `n`, which is larger than the bitwidth `w` produces `0. -/
theorem ushiftRight_eq_zero {x : BitVec w} {n : Nat} (hn : w n) :
x >>> n = 0#w := by
simp only [toNat_eq, toNat_ushiftRight, toNat_ofNat, Nat.zero_mod]
have : 2^w 2^n := Nat.pow_le_pow_of_le Nat.one_lt_two hn
rw [Nat.shiftRight_eq_div_pow, Nat.div_eq_of_lt (by omega)]
/--
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
because it makes the value of the bitvector less than or equal to `2^(w-1)`.
-/
theorem toInt_ushiftRight_of_lt {x : BitVec w} {n : Nat} (hn : 0 < n) :
(x >>> n).toInt = x.toNat >>> n := by
rw [toInt_eq_toNat_cond]
simp only [toNat_ushiftRight, ite_eq_left_iff, Nat.not_lt]
intros h
by_cases hn : n w
· have h1 := Nat.mul_lt_mul_of_pos_left (toNat_ushiftRight_lt x n hn) Nat.two_pos
simp only [toNat_ushiftRight, Nat.zero_lt_succ, Nat.mul_lt_mul_left] at h1
have : 2 ^ (w - n).succ 2^ w := Nat.pow_le_pow_of_le (by decide) (by omega)
have := show 2 * x.toNat >>> n < 2 ^ w by
omega
omega
· have : x.toNat >>> n = 0 := by
apply Nat.shiftRight_eq_zero
have : 2^w 2^n := Nat.pow_le_pow_of_le (by decide) (by omega)
omega
simp [this] at h
omega
/--
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
because it makes the value of the bitvector less than or equal to `2^(w-1)`.
In the case when `n = 0`, then the shift right value equals the integer interpretation.
-/
@[simp]
theorem toInt_ushiftRight {x : BitVec w} {n : Nat} :
(x >>> n).toInt = if n = 0 then x.toInt else x.toNat >>> n := by
by_cases hn : n = 0
· simp [hn]
· rw [toInt_ushiftRight_of_lt (by omega), toInt_eq_toNat_cond]
simp [hn]
@[simp]
theorem toFin_uShiftRight {x : BitVec w} {n : Nat} :
(x >>> n).toFin = x.toFin / (Fin.ofNat' (2^w) (2^n)) := by
apply Fin.eq_of_val_eq
by_cases hn : n < w
· simp [Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt (Nat.pow_lt_pow_of_lt Nat.one_lt_two hn)]
· simp only [Nat.not_lt] at hn
rw [ushiftRight_eq_zero (by omega)]
simp [Nat.dvd_iff_mod_eq_zero.mp (Nat.pow_dvd_pow 2 hn)]
@[simp]
theorem getMsbD_ushiftRight {x : BitVec w} {i n : Nat} :
(x >>> n).getMsbD i = (decide (i < w) && (!decide (i < n) && x.getMsbD (i - n))) := by
@@ -1622,14 +1677,16 @@ theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
/-- Sign extending to a larger bitwidth depends on the msb.
If the msb is false, then the result equals the original value.
If the msb is true, then we add a value of `(2^v - 2^w)`, which arises from the sign extension. -/
theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w v) :
private theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w v) :
(x.signExtend v).toNat = x.toNat + if x.msb then 2^v - 2^w else 0 := by
apply Nat.eq_of_testBit_eq
intro i
have k, hk := Nat.exists_eq_add_of_le hv
rw [hk, testBit_toNat, getLsbD_signExtend, Nat.pow_add, Nat.mul_sub_one, Nat.add_comm (x.toNat)]
by_cases hx : x.msb
· simp [hx, Nat.testBit_mul_pow_two_add _ x.isLt, testBit_toNat]
· simp only [hx, Bool.if_true_right, reduceIte,
Nat.testBit_mul_pow_two_add _ x.isLt,
testBit_toNat, Nat.testBit_two_pow_sub_one]
-- Case analysis on i being in the intervals [0..w), [w..w + k), [w+k..∞)
have hi : i < w (w i i < w + k) w + k i := by omega
rcases hi with hi | hi | hi
@@ -1637,7 +1694,8 @@ theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
· simp [hi]; omega
· simp [hi, show ¬ (i < w + k) by omega, show ¬ (i < w) by omega]
omega
· simp [hx, Nat.testBit_mul_pow_two_add _ x.isLt, testBit_toNat]
· simp only [hx, Bool.if_false_right,
Bool.false_eq_true, reduceIte, Nat.zero_add, testBit_toNat]
have hi : i < w (w i i < w + k) w + k i := by omega
rcases hi with hi | hi | hi
· simp [hi]; omega
@@ -1739,7 +1797,7 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
rw [getLsbD_append] -- Why does this not work with `simp [getLsbD_append]`?
simp
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = cast (by omega) y := by
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = y.cast (by omega) := by
ext
rw [getLsbD_append]
simpa using lt_of_getLsbD
@@ -1749,7 +1807,7 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
simp only [getLsbD_append, getLsbD_zero, Bool.cond_self]
@[simp] theorem cast_append_right (h : w + v = w + v') (x : BitVec w) (y : BitVec v) :
cast h (x ++ y) = x ++ cast (by omega) y := by
(x ++ y).cast h = x ++ y.cast (by omega) := by
ext
simp only [getLsbD_cast, getLsbD_append, cond_eq_if, decide_eq_true_eq]
split <;> split
@@ -1760,7 +1818,7 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
omega
@[simp] theorem cast_append_left (h : w + v = w' + v) (x : BitVec w) (y : BitVec v) :
cast h (x ++ y) = cast (by omega) x ++ y := by
(x ++ y).cast h = x.cast (by omega) ++ y := by
ext
simp [getLsbD_append]
@@ -1999,6 +2057,46 @@ theorem getElem_concat (x : BitVec w) (b : Bool) (i : Nat) (h : i < w + 1) :
(concat x b)[i + 1] = x[i] := by
simp [getElem_concat, h, getLsbD_eq_getElem]
@[simp]
theorem getMsbD_concat {i w : Nat} {b : Bool} {x : BitVec w} :
(x.concat b).getMsbD i = if i < w then x.getMsbD i else decide (i = w) && b := by
simp only [getMsbD_eq_getLsbD, Nat.add_sub_cancel, getLsbD_concat]
by_cases h₀ : i = w
· simp [h₀]
· by_cases h₁ : i < w
· simp [h₀, h₁, show ¬ w - i = 0 by omega, show i < w + 1 by omega, Nat.sub_sub, Nat.add_comm]
· simp only [show w - i = 0 by omega, reduceIte, h₁, h₀, decide_false, Bool.false_and,
Bool.and_eq_false_imp, decide_eq_true_eq]
intro
omega
@[simp]
theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
(x.concat b).msb = if 0 < w then x.msb else b := by
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.zero_lt_succ, decide_true, Nat.add_one_sub_one,
Nat.sub_zero, Bool.true_and]
by_cases h₀ : 0 < w
· simp only [Nat.lt_add_one, getLsbD_eq_getElem, getElem_concat, h₀, reduceIte, decide_true,
Bool.true_and, ite_eq_right_iff]
intro
omega
· simp [h₀, show w = 0 by omega]
@[simp] theorem toInt_concat (x : BitVec w) (b : Bool) :
(concat x b).toInt = if w = 0 then -b.toInt else x.toInt * 2 + b.toInt := by
simp only [BitVec.toInt, toNat_concat]
cases w
· cases b <;> simp [eq_nil x]
· cases b <;> simp <;> omega
@[simp] theorem toFin_concat (x : BitVec w) (b : Bool) :
(concat x b).toFin = Fin.mk (x.toNat * 2 + b.toNat) (by
have := Bool.toNat_lt b
simp [ Nat.two_pow_pred_add_two_pow_pred, Bool.toNat_lt b]
omega
) := by
simp [ Fin.val_inj]
@[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]
@@ -2014,6 +2112,10 @@ theorem getElem_concat (x : BitVec w) (b : Bool) (i : Nat) (h : i < w + 1) :
(concat x a) ^^^ (concat y b) = concat (x ^^^ y) (a ^^ b) := by
ext i; cases i using Fin.succRecOn <;> simp
@[simp] theorem zero_concat_false : concat 0#w false = 0#(w + 1) := by
ext
simp [getLsbD_concat]
/-! ### shiftConcat -/
theorem getLsbD_shiftConcat (x : BitVec w) (b : Bool) (i : Nat) :
@@ -2059,35 +2161,6 @@ theorem toNat_shiftConcat_lt_of_lt {x : BitVec w} {b : Bool} {k : Nat}
have := Bool.toNat_lt b
omega
@[simp] theorem zero_concat_false : concat 0#w false = 0#(w + 1) := by
ext
simp [getLsbD_concat]
@[simp]
theorem getMsbD_concat {i w : Nat} {b : Bool} {x : BitVec w} :
(x.concat b).getMsbD i = if i < w then x.getMsbD i else decide (i = w) && b := by
simp only [getMsbD_eq_getLsbD, Nat.add_sub_cancel, getLsbD_concat]
by_cases h₀ : i = w
· simp [h₀]
· by_cases h₁ : i < w
· simp [h₀, h₁, show ¬ w - i = 0 by omega, show i < w + 1 by omega, Nat.sub_sub, Nat.add_comm]
· simp only [show w - i = 0 by omega, reduceIte, h₁, h₀, decide_false, Bool.false_and,
Bool.and_eq_false_imp, decide_eq_true_eq]
intro
omega
@[simp]
theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
(x.concat b).msb = if 0 < w then x.msb else b := by
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.zero_lt_succ, decide_true, Nat.add_one_sub_one,
Nat.sub_zero, Bool.true_and]
by_cases h₀ : 0 < w
· simp only [Nat.lt_add_one, getLsbD_eq_getElem, getElem_concat, h₀, reduceIte, decide_true,
Bool.true_and, ite_eq_right_iff]
intro
omega
· simp [h₀, show w = 0 by omega]
/-! ### add -/
theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := rfl
@@ -2758,12 +2831,6 @@ theorem getElem_rotateLeft {x : BitVec w} {r i : Nat} (h : i < w) :
if h' : i < r % w then x[(w - (r % w) + i)] else x[i - (r % w)] := by
simp [ BitVec.getLsbD_eq_getElem, h]
/-- If `w ≤ x < 2 * w`, then `x % w = x - w` -/
theorem mod_eq_sub_of_le_of_lt {x w : Nat} (x_le : w x) (x_lt : x < 2 * w) :
x % w = x - w := by
rw [Nat.mod_eq_sub_mod, Nat.mod_eq_of_lt (by omega)]
omega
theorem getMsbD_rotateLeftAux_of_lt {x : BitVec w} {r : Nat} {i : Nat} (hi : i < w - r) :
(x.rotateLeftAux r).getMsbD i = x.getMsbD (r + i) := by
rw [rotateLeftAux, getMsbD_or]
@@ -2773,6 +2840,20 @@ theorem getMsbD_rotateLeftAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i
(x.rotateLeftAux r).getMsbD i = (decide (i < w) && x.getMsbD (i - (w - r))) := by
simp [rotateLeftAux, getMsbD_or, show i + r w by omega, show ¬i < w - r by omega]
/--
If a number `w * n ≤ i < w * (n + 1)`, then `i - w * n` equals `i % w`.
This is true by subtracting `w * n` from the inequality, giving
`0 ≤ i - w * n < w`, which uniquely identifies `i % w`.
-/
private theorem Nat.sub_mul_eq_mod_of_lt_of_le (hlo : w * n i) (hhi : i < w * (n + 1)) :
i - w * n = i % w := by
rw [Nat.mod_def]
congr
symm
apply Nat.div_eq_of_lt_le
(by rw [Nat.mul_comm]; omega)
(by rw [Nat.mul_comm]; omega)
/-- When `r < w`, we give a formula for `(x.rotateLeft r).getMsbD i`. -/
theorem getMsbD_rotateLeft_of_lt {n w : Nat} {x : BitVec w} (hi : r < w):
(x.rotateLeft r).getMsbD n = (decide (n < w) && x.getMsbD ((r + n) % w)) := by
@@ -2785,8 +2866,8 @@ theorem getMsbD_rotateLeft_of_lt {n w : Nat} {x : BitVec w} (hi : r < w):
by_cases h₁ : n < w + 1
· simp only [h₁, decide_true, Bool.true_and]
have h₂ : (r + n) < 2 * (w + 1) := by omega
rw [mod_eq_sub_of_le_of_lt (by omega) (by omega)]
congr 1
rw [ Nat.sub_mul_eq_mod_of_lt_of_le (n := 1) (by omega) (by omega), Nat.mul_one]
omega
· simp [h₁]
@@ -3103,20 +3184,6 @@ theorem replicate_succ_eq {x : BitVec w} :
(x ++ replicate n x).cast (by rw [Nat.mul_succ]; omega) := by
simp [replicate]
/--
If a number `w * n ≤ i < w * (n + 1)`, then `i - w * n` equals `i % w`.
This is true by subtracting `w * n` from the inequality, giving
`0 ≤ i - w * n < w`, which uniquely identifies `i % w`.
-/
private theorem Nat.sub_mul_eq_mod_of_lt_of_le (hlo : w * n i) (hhi : i < w * (n + 1)) :
i - w * n = i % w := by
rw [Nat.mod_def]
congr
symm
apply Nat.div_eq_of_lt_le
(by rw [Nat.mul_comm]; omega)
(by rw [Nat.mul_comm]; omega)
@[simp]
theorem getLsbD_replicate {n w : Nat} (x : BitVec w) :
(x.replicate n).getLsbD i =
@@ -3222,6 +3289,11 @@ theorem toInt_neg_of_ne_intMin {x : BitVec w} (rs : x ≠ intMin w) :
have := @Nat.two_pow_pred_mul_two w (by omega)
split <;> split <;> omega
theorem toInt_neg_eq_ite {x : BitVec w} :
(-x).toInt = if x = intMin w then x.toInt else -(x.toInt) := by
by_cases hx : x = intMin w <;>
simp [hx, neg_intMin, toInt_neg_of_ne_intMin]
theorem msb_intMin {w : Nat} : (intMin w).msb = decide (0 < w) := by
simp only [msb_eq_decide, toNat_intMin, decide_eq_decide]
by_cases h : 0 < w <;> simp_all
@@ -3355,6 +3427,73 @@ theorem getMsbD_abs {i : Nat} {x : BitVec w} :
getMsbD (x.abs) i = if x.msb then getMsbD (-x) i else getMsbD x i := by
by_cases h : x.msb <;> simp [BitVec.abs, h]
/-
The absolute value of `x : BitVec w` is naively a case split on the sign of `x`.
However, recall that when `x = intMin w`, `-x = x`.
Thus, the full value of `abs x` is computed by the case split:
- If `x : BitVec w` is `intMin`, then its absolute value is also `intMin w`, and
thus `toInt` will equal `intMin.toInt`.
- Otherwise, if `x` is negative, then `x.abs.toInt = (-x).toInt`.
- If `x` is positive, then it is equal to `x.abs.toInt = x.toInt`.
-/
theorem toInt_abs_eq_ite {x : BitVec w} :
x.abs.toInt =
if x = intMin w then (intMin w).toInt
else if x.msb then -x.toInt
else x.toInt := by
by_cases hx : x = intMin w
· simp [hx]
· simp [hx]
by_cases hx₂ : x.msb
· simp [hx₂, abs_eq, toInt_neg_of_ne_intMin hx]
· simp [hx₂, abs_eq]
/-
The absolute value of `x : BitVec w` is a case split on the sign of `x`, when `x ≠ intMin w`.
This is a variant of `toInt_abs_eq_ite`.
-/
theorem toInt_abs_eq_ite_of_ne_intMin {x : BitVec w} (hx : x intMin w) :
x.abs.toInt = if x.msb then -x.toInt else x.toInt := by
simp [toInt_abs_eq_ite, hx]
/--
The absolute value of `x : BitVec w`, interpreted as an integer, is a case split:
- When `x = intMin w`, then `x.abs = intMin w`
- Otherwise, `x.abs.toInt` equals the absolute value (`x.toInt.natAbs`).
This is a simpler version of `BitVec.toInt_abs_eq_ite`, which hides a case split on `x.msb`.
-/
theorem toInt_abs_eq_natAbs {x : BitVec w} : x.abs.toInt =
if x = intMin w then (intMin w).toInt else x.toInt.natAbs := by
rw [toInt_abs_eq_ite]
by_cases hx : x = intMin w
· simp [hx]
· simp [hx]
by_cases h : x.msb
· simp only [h, reduceIte]
have : x.toInt < 0 := by
rw [toInt_neg_iff]
have := msb_eq_true_iff_two_mul_ge.mp h
omega
omega
· simp only [h, Bool.false_eq_true, reduceIte]
have : 0 x.toInt := by
rw [toInt_pos_iff]
exact msb_eq_false_iff_two_mul_lt.mp (by simp [h])
omega
/-
The absolute value of `(x : BitVec w)`, when interpreted as an integer,
is the absolute value of `x.toInt` when `(x ≠ intMin)`.
-/
theorem toInt_abs_eq_natAbs_of_ne_intMin {x : BitVec w} (hx : x intMin w) :
x.abs.toInt = x.toInt.natAbs := by
simp [toInt_abs_eq_natAbs, hx]
/-! ### Decidable quantifiers -/
theorem forall_zero_iff {P : BitVec 0 Prop} :

View File

@@ -384,6 +384,15 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
@[simp] theorem toNat_eq_one {b : Bool} : b.toNat = 1 b = true := by
cases b <;> simp
/-! ## toInt -/
/-- convert a `Bool` to an `Int`, `false -> 0`, `true -> 1` -/
def toInt (b : Bool) : Int := cond b 1 0
@[simp] theorem toInt_false : false.toInt = 0 := rfl
@[simp] theorem toInt_true : true.toInt = 1 := rfl
/-! ### ite -/
@[simp] theorem if_true_left (p : Prop) [h : Decidable p] (f : Bool) :

View File

@@ -8,6 +8,8 @@ import Init.Data.Queue
import Init.System.Promise
import Init.System.Mutex
set_option linter.deprecated false
namespace IO
/--
@@ -15,6 +17,7 @@ Internal state of an `Channel`.
We maintain the invariant that at all times either `consumers` or `values` is empty.
-/
@[deprecated "Use Std.Channel.State from Std.Sync.Channel instead" (since := "2024-12-02")]
structure Channel.State (α : Type) where
values : Std.Queue α :=
consumers : Std.Queue (Promise (Option α)) :=
@@ -27,12 +30,14 @@ FIFO channel with unbounded buffer, where `recv?` returns a `Task`.
A channel can be closed. Once it is closed, all `send`s are ignored, and
`recv?` returns `none` once the queue is empty.
-/
@[deprecated "Use Std.Channel from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel (α : Type) : Type := Mutex (Channel.State α)
instance : Nonempty (Channel α) :=
inferInstanceAs (Nonempty (Mutex _))
/-- Creates a new `Channel`. -/
@[deprecated "Use Std.Channel.new from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.new : BaseIO (Channel α) :=
Mutex.new {}
@@ -41,6 +46,7 @@ Sends a message on an `Channel`.
This function does not block.
-/
@[deprecated "Use Std.Channel.send from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.send (ch : Channel α) (v : α) : BaseIO Unit :=
ch.atomically do
let st get
@@ -54,6 +60,7 @@ def Channel.send (ch : Channel α) (v : α) : BaseIO Unit :=
/--
Closes an `Channel`.
-/
@[deprecated "Use Std.Channel.close from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.close (ch : Channel α) : BaseIO Unit :=
ch.atomically do
let st get
@@ -67,6 +74,7 @@ Every message is only received once.
Returns `none` if the channel is closed and the queue is empty.
-/
@[deprecated "Use Std.Channel.recv? from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.recv? (ch : Channel α) : BaseIO (Task (Option α)) :=
ch.atomically do
let st get
@@ -85,6 +93,7 @@ def Channel.recv? (ch : Channel α) : BaseIO (Task (Option α)) :=
Note that if this function is called twice, each `forAsync` only gets half the messages.
-/
@[deprecated "Use Std.Channel.forAsync from Std.Sync.Channel instead" (since := "2024-12-02")]
partial def Channel.forAsync (f : α BaseIO Unit) (ch : Channel α)
(prio : Task.Priority := .default) : BaseIO (Task Unit) := do
BaseIO.bindTask (prio := prio) ( ch.recv?) fun
@@ -96,11 +105,13 @@ Receives all currently queued messages from the channel.
Those messages are dequeued and will not be returned by `recv?`.
-/
@[deprecated "Use Std.Channel.recvAllCurrent from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.recvAllCurrent (ch : Channel α) : BaseIO (Array α) :=
ch.atomically do
modifyGet fun st => (st.values.toArray, { st with values := })
/-- Type tag for synchronous (blocking) operations on a `Channel`. -/
@[deprecated "Use Std.Channel.Sync from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.Sync := Channel
/--
@@ -110,6 +121,7 @@ For example, `ch.sync.recv?` blocks until the next message,
and `for msg in ch.sync do ...` iterates synchronously over the channel.
These functions should only be used in dedicated threads.
-/
@[deprecated "Use Std.Channel.sync from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.sync (ch : Channel α) : Channel.Sync α := ch
/--
@@ -118,9 +130,11 @@ Synchronously receives a message from the channel.
Every message is only received once.
Returns `none` if the channel is closed and the queue is empty.
-/
@[deprecated "Use Std.Channel.Sync.recv? from Std.Sync.Channel instead" (since := "2024-12-02")]
def Channel.Sync.recv? (ch : Channel.Sync α) : BaseIO (Option α) := do
IO.wait ( Channel.recv? ch)
@[deprecated "Use Std.Channel.Sync.forIn from Std.Sync.Channel instead" (since := "2024-12-02")]
private partial def Channel.Sync.forIn [Monad m] [MonadLiftT BaseIO m]
(ch : Channel.Sync α) (f : α β m (ForInStep β)) : β m β := fun b => do
match ch.recv? with

View File

@@ -36,12 +36,6 @@ def succ : Fin n → Fin (n + 1)
variable {n : Nat}
/--
Returns `a` modulo `n + 1` as a `Fin n.succ`.
-/
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)
/--
Returns `a` modulo `n` as a `Fin n`.
@@ -50,9 +44,12 @@ The assumption `NeZero n` ensures that `Fin n` is nonempty.
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
a % n, Nat.mod_lt _ (pos_of_neZero n)
-- We intend to deprecate `Fin.ofNat` in favor of `Fin.ofNat'` (and later rename).
-- This is waiting on https://github.com/leanprover/lean4/pull/5323
-- attribute [deprecated Fin.ofNat' (since := "2024-09-16")] Fin.ofNat
/--
Returns `a` modulo `n + 1` as a `Fin n.succ`.
-/
@[deprecated Fin.ofNat' (since := "2024-11-27")]
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)
private theorem mlt {b : Nat} : {a : Nat} a < n b % n < n
| 0, h => Nat.mod_lt _ h
@@ -179,7 +176,7 @@ protected theorem pos (i : Fin n) : 0 < n :=
@[inline] def castLE (h : n m) (i : Fin n) : Fin m := i, Nat.lt_of_lt_of_le i.2 h
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
@[inline] def cast (eq : n = m) (i : Fin n) : Fin m := i, eq i.2
@[inline] protected def cast (eq : n = m) (i : Fin n) : Fin m := i, eq i.2
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
@[inline] def castAdd (m) : Fin n Fin (n + m) :=

View File

@@ -13,14 +13,14 @@ namespace Fin
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
@[inline] def foldl (n) (f : α Fin n α) (init : α) : α := loop init 0 where
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
@[semireducible] loop (x : α) (i : Nat) : α :=
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
if h : i < n then loop (f x i, h) (i+1) else x
termination_by n - i
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
@[inline] def foldr (n) (f : Fin n α α) (init : α) : α := loop n (Nat.le_refl n) init where
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
loop : (i : _) i n α α
@[specialize] loop : (i : _) i n α α
| 0, _, x => x
| i+1, h, x => loop i (Nat.le_of_lt h) (f i, h x)
termination_by structural i => i
@@ -47,7 +47,7 @@ Fin.foldlM n f x₀ = do
pure xₙ
```
-/
loop (x : α) (i : Nat) : m α := do
@[semireducible, specialize] loop (x : α) (i : Nat) : m α := do
if h : i < n then f x i, h >>= (loop · (i+1)) else pure x
termination_by n - i
decreasing_by decreasing_trivial_pre_omega
@@ -76,7 +76,7 @@ Fin.foldrM n f xₙ = do
pure x₀
```
-/
loop : {i // i n} α m α
@[semireducible, specialize] loop : {i // i n} α m α
| 0, _, x => pure x
| i+1, h, x => f i, h x >>= loop i, Nat.le_of_lt h
@@ -125,7 +125,7 @@ theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) → α → m α) (x
| zero =>
rw [foldrM_loop_zero, foldrM_loop_succ, pure_bind]
conv => rhs; rw [bind_pure (f 0 x)]
congr; funext; exact foldrM_loop_zero ..
congr; funext
| succ i ih =>
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
congr; funext; exact ih ..

View File

@@ -370,25 +370,25 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
Fin.castLE mn Fin.castLE km = Fin.castLE (Nat.le_trans km mn) :=
funext (castLE_castLE km mn)
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : (last n).cast h = last n' :=
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h i, hn = i, h hn := rfl
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : Fin.cast h i, hn = i, h hn := rfl
@[simp] theorem cast_refl (n : Nat) (h : n = n) : cast h = id := by
@[simp] theorem cast_refl (n : Nat) (h : n = n) : Fin.cast h = id := by
ext
simp
@[simp] theorem cast_trans {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
cast h' (cast h i) = cast (Eq.trans h h') i := rfl
(i.cast h).cast h' = i.cast (Eq.trans h h') := rfl
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m n} : castLE h' = Fin.cast h := rfl
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n Fin (n + 0)) = cast rfl := rfl
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n Fin (n + 0)) = Fin.cast rfl := rfl
theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m := by simp
@@ -406,37 +406,37 @@ theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := Fin.ext rfl
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
(i.castAdd m).cast h = (i.cast (Nat.add_right_cancel h)).castAdd m := rfl
@[simp] theorem cast_castAdd_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
cast h (castAdd m' i) = castAdd m i := rfl
(i.castAdd m').cast h = i.castAdd m := rfl
theorem castAdd_castAdd {m n p : Nat} (i : Fin m) :
castAdd p (castAdd n i) = cast (Nat.add_assoc ..).symm (castAdd (n + p) i) := rfl
(i.castAdd n).castAdd p = (i.castAdd (n + p)).cast (Nat.add_assoc ..).symm := rfl
/-- The cast of the successor is the successor of the cast. See `Fin.succ_cast_eq` for rewriting in
the reverse direction. -/
@[simp] theorem cast_succ_eq {n' : Nat} (i : Fin n) (h : n.succ = n'.succ) :
cast h i.succ = (cast (Nat.succ.inj h) i).succ := rfl
i.succ.cast h = (i.cast (Nat.succ.inj h)).succ := rfl
theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
(cast h i).succ = cast (by rw [h]) i.succ := rfl
(i.cast h).succ = i.succ.cast (by rw [h]) := rfl
@[simp] theorem coe_castSucc (i : Fin n) : (Fin.castSucc i : Nat) = i := rfl
@[simp] theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[simp] theorem castSucc_mk (n i : Nat) (h : i < n) : castSucc i, h = i, Nat.lt.step h := rfl
@[simp] theorem cast_castSucc {n' : Nat} {h : n + 1 = n' + 1} {i : Fin n} :
cast h (castSucc i) = castSucc (cast (Nat.succ.inj h) i) := rfl
i.castSucc.cast h = (i.cast (Nat.succ.inj h)).castSucc := rfl
theorem castSucc_lt_succ (i : Fin n) : Fin.castSucc i < i.succ :=
theorem castSucc_lt_succ (i : Fin n) : i.castSucc < i.succ :=
lt_def.2 <| by simp only [coe_castSucc, val_succ, Nat.lt_succ_self]
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i Fin.castSucc j i < j.succ := by
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i j.castSucc i < j.succ := by
simpa only [lt_def, le_def] using Nat.add_one_le_add_one_iff.symm
theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
Fin.castSucc i < j i.succ j := .rfl
i.castSucc < j i.succ j := .rfl
@[simp] theorem succ_last (n : Nat) : (last n).succ = last n.succ := rfl
@@ -444,48 +444,48 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
i.succ = last (n + 1) i = last n := by rw [ succ_last, succ_inj]
@[simp] theorem castSucc_castLT (i : Fin (n + 1)) (h : (i : Nat) < n) :
castSucc (castLT i h) = i := rfl
(castLT i h).castSucc = i := rfl
@[simp] theorem castLT_castSucc {n : Nat} (a : Fin n) (h : (a : Nat) < n) :
castLT (castSucc a) h = a := rfl
castLT a.castSucc h = a := rfl
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
Fin.castSucc a < Fin.castSucc b a < b := .rfl
a.castSucc < b.castSucc a < b := .rfl
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [Fin.ext_iff]
theorem castSucc_inj {a b : Fin n} : a.castSucc = b.castSucc a = b := by simp [Fin.ext_iff]
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
theorem castSucc_lt_last (a : Fin n) : a.castSucc < last n := a.is_lt
@[simp] theorem castSucc_zero : castSucc (0 : Fin (n + 1)) = 0 := rfl
@[simp] theorem castSucc_one {n : Nat} : castSucc (1 : Fin (n + 2)) = 1 := rfl
/-- `castSucc i` is positive when `i` is positive -/
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < i.castSucc := by
simpa [lt_def] using h
@[simp] theorem castSucc_eq_zero_iff {a : Fin (n + 1)} : castSucc a = 0 a = 0 := by simp [Fin.ext_iff]
@[simp] theorem castSucc_eq_zero_iff {a : Fin (n + 1)} : a.castSucc = 0 a = 0 := by simp [Fin.ext_iff]
theorem castSucc_ne_zero_iff {a : Fin (n + 1)} : castSucc a 0 a 0 :=
theorem castSucc_ne_zero_iff {a : Fin (n + 1)} : a.castSucc 0 a 0 :=
not_congr <| castSucc_eq_zero_iff
theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
castSucc (Fin.succ j) = Fin.succ (castSucc j) := by simp [Fin.ext_iff]
j.succ.castSucc = (j.castSucc).succ := by simp [Fin.ext_iff]
@[simp]
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
theorem coeSucc_eq_succ {a : Fin n} : a.castSucc + 1 = a.succ := by
cases n
· exact a.elim0
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
theorem lt_succ {a : Fin n} : a.castSucc < a.succ := by
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : ( j, castSucc j = i) i last n :=
fun j, hj => hj Fin.ne_of_lt j.castSucc_lt_last,
fun hi => i.castLT <| Fin.val_lt_last hi, rfl
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = castSucc i.succ := rfl
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = i.succ.castSucc := rfl
@[simp] theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@@ -502,17 +502,17 @@ theorem le_coe_addNat (m : Nat) (i : Fin n) : m ≤ addNat i m :=
addNat i, hi n = i + n, Nat.add_lt_add_right hi n := rfl
@[simp] theorem cast_addNat_zero {n n' : Nat} (i : Fin n) (h : n + 0 = n') :
cast h (addNat i 0) = cast ((Nat.add_zero _).symm.trans h) i := rfl
(addNat i 0).cast h = i.cast ((Nat.add_zero _).symm.trans h) := rfl
/-- For rewriting in the reverse direction, see `Fin.cast_addNat_left`. -/
theorem addNat_cast {n n' m : Nat} (i : Fin n') (h : n' = n) :
addNat (cast h i) m = cast (congrArg (. + m) h) (addNat i m) := rfl
addNat (i.cast h) m = (addNat i m).cast (congrArg (. + m) h) := rfl
theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
cast h (addNat i m) = addNat (cast (Nat.add_right_cancel h) i) m := rfl
(addNat i m).cast h = addNat (i.cast (Nat.add_right_cancel h)) m := rfl
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
cast h (addNat i m') = addNat i m :=
(addNat i m').cast h = addNat i m :=
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@@ -522,46 +522,46 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
theorem le_coe_natAdd (m : Nat) (i : Fin n) : m natAdd m i := Nat.le_add_right ..
@[simp] theorem natAdd_zero {n : Nat} : natAdd 0 = cast (Nat.zero_add n).symm := by ext; simp
@[simp] theorem natAdd_zero {n : Nat} : natAdd 0 = Fin.cast (Nat.zero_add n).symm := by ext; simp
/-- For rewriting in the reverse direction, see `Fin.cast_natAdd_right`. -/
theorem natAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
natAdd m (cast h i) = cast (congrArg _ h) (natAdd m i) := rfl
natAdd m (i.cast h) = (natAdd m i).cast (congrArg _ h) := rfl
theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
cast h (natAdd m i) = natAdd m (cast (Nat.add_left_cancel h) i) := rfl
(natAdd m i).cast h = natAdd m (i.cast (Nat.add_left_cancel h)) := rfl
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
cast h (natAdd m' i) = natAdd m i :=
(natAdd m' i).cast h = natAdd m i :=
Fin.ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
castAdd p (natAdd m i) = (natAdd m (castAdd p i)).cast (Nat.add_assoc ..).symm := rfl
theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
natAdd m (castAdd p i) = cast (Nat.add_assoc ..) (castAdd p (natAdd m i)) := rfl
natAdd m (castAdd p i) = (castAdd p (natAdd m i)).cast (Nat.add_assoc ..) := rfl
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
natAdd m (natAdd n i) = (natAdd (m + n) i).cast (Nat.add_assoc ..) :=
Fin.ext <| (Nat.add_assoc ..).symm
@[simp]
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
(natAdd 0 i).cast h = i.cast ((Nat.zero_add _).symm.trans h) :=
Fin.ext <| Nat.zero_add _
@[simp]
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := Fin.ext <| Nat.add_comm ..
(natAdd n i).cast (Nat.add_comm ..) = addNat i n := Fin.ext <| Nat.add_comm ..
@[simp]
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := Fin.ext <| Nat.add_comm ..
(addNat i m).cast (Nat.add_comm ..) = natAdd m i := Fin.ext <| Nat.add_comm ..
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
@[simp] theorem addNat_last (n : Nat) :
addNat (last n) m = cast (by omega) (last (n + m)) := by
addNat (last n) m = (last (n + m)).cast (by omega) := by
ext
simp
@@ -657,7 +657,7 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n i) :
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [ cast_addNat]
natAdd n (subNat n (i.cast (Nat.add_comm ..)) h) = i := by simp [ cast_addNat]
/-! ### recursion and induction principles -/

View File

@@ -34,4 +34,8 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
simp [Int.shiftRight_eq_div_pow]
@[simp]
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow]
end Int

View File

@@ -29,6 +29,8 @@ At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with
In September 2024, we decided to do this rename (with deprecations in place),
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
ever need to use these functions and their associated lemmas.
In December 2024, we removed `tdiv` and `tmod`, but have not yet renamed `ediv` and `emod`.
-/
/-! ### T-rounding division -/
@@ -71,8 +73,6 @@ def tdiv : (@& Int) → (@& Int) → Int
| -[m +1], ofNat n => -ofNat (succ m / n)
| -[m +1], -[n +1] => ofNat (succ m / succ n)
@[deprecated tdiv (since := "2024-09-11")] abbrev div := tdiv
/-- Integer modulo. This function uses the
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
to pair with `Int.tdiv`, meaning that `tmod a b + b * (tdiv a b) = a`
@@ -107,8 +107,6 @@ def tmod : (@& Int) → (@& Int) → Int
| -[m +1], ofNat n => -ofNat (succ m % n)
| -[m +1], -[n +1] => -ofNat (succ m % succ n)
@[deprecated tmod (since := "2024-09-11")] abbrev mod := tmod
/-! ### F-rounding division
This pair satisfies `fdiv x y = floor (x / y)`.
-/
@@ -251,8 +249,6 @@ instance : Mod Int where
theorem ofNat_tdiv (m n : Nat) : (m / n) = tdiv m n := rfl
@[deprecated ofNat_tdiv (since := "2024-09-11")] abbrev ofNat_div := ofNat_tdiv
theorem ofNat_fdiv : m n : Nat, (m / n) = fdiv m n
| 0, _ => by simp [fdiv]
| succ _, _ => rfl

View File

@@ -125,7 +125,7 @@ theorem eq_one_of_mul_eq_one_right {a b : Int} (H : 0 ≤ a) (H' : a * b = 1) :
eq_one_of_dvd_one H b, H'.symm
theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 b) (H' : a * b = 1) : b = 1 :=
eq_one_of_mul_eq_one_right H <| by rw [Int.mul_comm, H']
eq_one_of_mul_eq_one_right (b := a) H <| by rw [Int.mul_comm, H']
/-! ### *div zero -/
@@ -1315,65 +1315,3 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
all_goals decide
· exact ofNat_nonneg x
· exact succ_ofNat_pos (x + 1)
/-! ### Deprecations -/
@[deprecated Int.zero_tdiv (since := "2024-09-11")] protected abbrev zero_div := @Int.zero_tdiv
@[deprecated Int.tdiv_zero (since := "2024-09-11")] protected abbrev div_zero := @Int.tdiv_zero
@[deprecated tdiv_eq_ediv (since := "2024-09-11")] abbrev div_eq_ediv := @tdiv_eq_ediv
@[deprecated fdiv_eq_tdiv (since := "2024-09-11")] abbrev fdiv_eq_div := @fdiv_eq_tdiv
@[deprecated zero_tmod (since := "2024-09-11")] abbrev zero_mod := @zero_tmod
@[deprecated tmod_zero (since := "2024-09-11")] abbrev mod_zero := @tmod_zero
@[deprecated tmod_add_tdiv (since := "2024-09-11")] abbrev mod_add_div := @tmod_add_tdiv
@[deprecated tdiv_add_tmod (since := "2024-09-11")] abbrev div_add_mod := @tdiv_add_tmod
@[deprecated tmod_add_tdiv' (since := "2024-09-11")] abbrev mod_add_div' := @tmod_add_tdiv'
@[deprecated tdiv_add_tmod' (since := "2024-09-11")] abbrev div_add_mod' := @tdiv_add_tmod'
@[deprecated tmod_def (since := "2024-09-11")] abbrev mod_def := @tmod_def
@[deprecated tmod_eq_emod (since := "2024-09-11")] abbrev mod_eq_emod := @tmod_eq_emod
@[deprecated fmod_eq_tmod (since := "2024-09-11")] abbrev fmod_eq_mod := @fmod_eq_tmod
@[deprecated Int.tdiv_one (since := "2024-09-11")] protected abbrev div_one := @Int.tdiv_one
@[deprecated Int.tdiv_neg (since := "2024-09-11")] protected abbrev div_neg := @Int.tdiv_neg
@[deprecated Int.neg_tdiv (since := "2024-09-11")] protected abbrev neg_div := @Int.neg_tdiv
@[deprecated Int.neg_tdiv_neg (since := "2024-09-11")] protected abbrev neg_div_neg := @Int.neg_tdiv_neg
@[deprecated Int.tdiv_nonneg (since := "2024-09-11")] protected abbrev div_nonneg := @Int.tdiv_nonneg
@[deprecated Int.tdiv_nonpos (since := "2024-09-11")] protected abbrev div_nonpos := @Int.tdiv_nonpos
@[deprecated Int.tdiv_eq_zero_of_lt (since := "2024-09-11")] abbrev div_eq_zero_of_lt := @Int.tdiv_eq_zero_of_lt
@[deprecated Int.mul_tdiv_cancel (since := "2024-09-11")] protected abbrev mul_div_cancel := @Int.mul_tdiv_cancel
@[deprecated Int.mul_tdiv_cancel_left (since := "2024-09-11")] protected abbrev mul_div_cancel_left := @Int.mul_tdiv_cancel_left
@[deprecated Int.tdiv_self (since := "2024-09-11")] protected abbrev div_self := @Int.tdiv_self
@[deprecated Int.mul_tdiv_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev mul_div_cancel_of_mod_eq_zero := @Int.mul_tdiv_cancel_of_tmod_eq_zero
@[deprecated Int.tdiv_mul_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev div_mul_cancel_of_mod_eq_zero := @Int.tdiv_mul_cancel_of_tmod_eq_zero
@[deprecated Int.dvd_of_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_of_mod_eq_zero := @Int.dvd_of_tmod_eq_zero
@[deprecated Int.mul_tdiv_assoc (since := "2024-09-11")] protected abbrev mul_div_assoc := @Int.mul_tdiv_assoc
@[deprecated Int.mul_tdiv_assoc' (since := "2024-09-11")] protected abbrev mul_div_assoc' := @Int.mul_tdiv_assoc'
@[deprecated Int.tdiv_dvd_tdiv (since := "2024-09-11")] abbrev div_dvd_div := @Int.tdiv_dvd_tdiv
@[deprecated Int.natAbs_tdiv (since := "2024-09-11")] abbrev natAbs_div := @Int.natAbs_tdiv
@[deprecated Int.tdiv_eq_of_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_right := @Int.tdiv_eq_of_eq_mul_right
@[deprecated Int.eq_tdiv_of_mul_eq_right (since := "2024-09-11")] protected abbrev eq_div_of_mul_eq_right := @Int.eq_tdiv_of_mul_eq_right
@[deprecated Int.ofNat_tmod (since := "2024-09-11")] abbrev ofNat_mod := @Int.ofNat_tmod
@[deprecated Int.tmod_one (since := "2024-09-11")] abbrev mod_one := @Int.tmod_one
@[deprecated Int.tmod_eq_of_lt (since := "2024-09-11")] abbrev mod_eq_of_lt := @Int.tmod_eq_of_lt
@[deprecated Int.tmod_lt_of_pos (since := "2024-09-11")] abbrev mod_lt_of_pos := @Int.tmod_lt_of_pos
@[deprecated Int.tmod_nonneg (since := "2024-09-11")] abbrev mod_nonneg := @Int.tmod_nonneg
@[deprecated Int.tmod_neg (since := "2024-09-11")] abbrev mod_neg := @Int.tmod_neg
@[deprecated Int.mul_tmod_left (since := "2024-09-11")] abbrev mul_mod_left := @Int.mul_tmod_left
@[deprecated Int.mul_tmod_right (since := "2024-09-11")] abbrev mul_mod_right := @Int.mul_tmod_right
@[deprecated Int.tmod_eq_zero_of_dvd (since := "2024-09-11")] abbrev mod_eq_zero_of_dvd := @Int.tmod_eq_zero_of_dvd
@[deprecated Int.dvd_iff_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_iff_mod_eq_zero := @Int.dvd_iff_tmod_eq_zero
@[deprecated Int.neg_mul_tmod_right (since := "2024-09-11")] abbrev neg_mul_mod_right := @Int.neg_mul_tmod_right
@[deprecated Int.neg_mul_tmod_left (since := "2024-09-11")] abbrev neg_mul_mod_left := @Int.neg_mul_tmod_left
@[deprecated Int.tdiv_mul_cancel (since := "2024-09-11")] protected abbrev div_mul_cancel := @Int.tdiv_mul_cancel
@[deprecated Int.mul_tdiv_cancel' (since := "2024-09-11")] protected abbrev mul_div_cancel' := @Int.mul_tdiv_cancel'
@[deprecated Int.eq_mul_of_tdiv_eq_right (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_right := @Int.eq_mul_of_tdiv_eq_right
@[deprecated Int.tmod_self (since := "2024-09-11")] abbrev mod_self := @Int.tmod_self
@[deprecated Int.neg_tmod_self (since := "2024-09-11")] abbrev neg_mod_self := @Int.neg_tmod_self
@[deprecated Int.lt_tdiv_add_one_mul_self (since := "2024-09-11")] abbrev lt_div_add_one_mul_self := @Int.lt_tdiv_add_one_mul_self
@[deprecated Int.tdiv_eq_iff_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_right := @Int.tdiv_eq_iff_eq_mul_right
@[deprecated Int.tdiv_eq_iff_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_left := @Int.tdiv_eq_iff_eq_mul_left
@[deprecated Int.eq_mul_of_tdiv_eq_left (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_left := @Int.eq_mul_of_tdiv_eq_left
@[deprecated Int.tdiv_eq_of_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_left := @Int.tdiv_eq_of_eq_mul_left
@[deprecated Int.eq_zero_of_tdiv_eq_zero (since := "2024-09-11")] protected abbrev eq_zero_of_div_eq_zero := @Int.eq_zero_of_tdiv_eq_zero
@[deprecated Int.tdiv_left_inj (since := "2024-09-11")] protected abbrev div_left_inj := @Int.tdiv_left_inj
@[deprecated Int.tdiv_sign (since := "2024-09-11")] abbrev div_sign := @Int.tdiv_sign
@[deprecated Int.sign_eq_tdiv_abs (since := "2024-09-11")] protected abbrev sign_eq_div_abs := @Int.sign_eq_tdiv_abs
@[deprecated Int.tdiv_eq_ediv_of_dvd (since := "2024-09-11")] abbrev div_eq_ediv_of_dvd := @Int.tdiv_eq_ediv_of_dvd

View File

@@ -155,7 +155,8 @@ def mapMono (as : List α) (f : αα) : List α :=
/-! ## Additional lemmas required for bootstrapping `Array`. -/
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h'} : (as ++ bs)[i] = as[i] := by
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} :
(as ++ bs)[i] = as[i] := by
induction as generalizing i with
| nil => trivial
| cons a as ih =>

View File

@@ -162,6 +162,10 @@ theorem countP_filterMap (p : β → Bool) (f : α → Option β) (l : List α)
@[deprecated countP_flatten (since := "2024-10-14")] abbrev countP_join := @countP_flatten
theorem countP_flatMap (p : β Bool) (l : List α) (f : α List β) :
countP p (l.flatMap f) = sum (map (countP p f) l) := by
rw [List.flatMap, countP_flatten, map_map]
@[simp] theorem countP_reverse (l : List α) : countP p l.reverse = countP p l := by
simp [countP_eq_length_filter, filter_reverse]
@@ -326,6 +330,9 @@ theorem count_filterMap {α} [BEq β] (b : β) (f : α → Option β) (l : List
· simp
· simp
theorem count_flatMap {α} [BEq β] (l : List α) (f : α List β) (x : β) :
count x (l.flatMap f) = sum (map (count x f) l) := countP_flatMap _ _ _
theorem count_erase (a b : α) :
l : List α, count a (l.erase b) = count a l - if b == a then 1 else 0
| [] => by simp

View File

@@ -83,44 +83,12 @@ open Nat
@[simp] theorem nil_eq {α} {xs : List α} : [] = xs xs = [] := by
cases xs <;> simp
/-! ### cons -/
theorem cons_ne_nil (a : α) (l : List α) : a :: l [] := nofun
@[simp]
theorem cons_ne_self (a : α) (l : List α) : a :: l l := mt (congrArg length) (Nat.succ_ne_self _)
@[simp] theorem ne_cons_self {a : α} {l : List α} : l a :: l := by
rw [ne_eq, eq_comm]
simp
theorem head_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : h₁ = h₂ := (cons.inj H).1
theorem tail_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : t₁ = t₂ := (cons.inj H).2
theorem cons_inj_right (a : α) {l l' : List α} : a :: l = a :: l' l = l' :=
tail_eq_of_cons_eq, congrArg _
@[deprecated cons_inj_right (since := "2024-06-15")] abbrev cons_inj := @cons_inj_right
theorem cons_eq_cons {a b : α} {l l' : List α} : a :: l = b :: l' a = b l = l' :=
List.cons.injEq .. .rfl
theorem exists_cons_of_ne_nil : {l : List α}, l [] b L, l = b :: L
| c :: l', _ => c, l', rfl
theorem singleton_inj {α : Type _} {a b : α} : [a] = [b] a = b := by
simp
/-! ### length -/
theorem eq_nil_of_length_eq_zero (_ : length l = 0) : l = [] := match l with | [] => rfl
theorem ne_nil_of_length_eq_add_one (_ : length l = n + 1) : l [] := fun _ => nomatch l
@[deprecated ne_nil_of_length_eq_add_one (since := "2024-06-16")]
abbrev ne_nil_of_length_eq_succ := @ne_nil_of_length_eq_add_one
theorem ne_nil_of_length_pos (_ : 0 < length l) : l [] := fun _ => nomatch l
@[simp] theorem length_eq_zero : length l = 0 l = [] :=
@@ -156,6 +124,36 @@ theorem length_pos {l : List α} : 0 < length l ↔ l ≠ [] :=
theorem length_eq_one {l : List α} : length l = 1 a, l = [a] :=
fun h => match l, h with | [_], _ => _, rfl, fun _, h => by simp [h]
/-! ### cons -/
theorem cons_ne_nil (a : α) (l : List α) : a :: l [] := nofun
@[simp]
theorem cons_ne_self (a : α) (l : List α) : a :: l l := mt (congrArg length) (Nat.succ_ne_self _)
@[simp] theorem ne_cons_self {a : α} {l : List α} : l a :: l := by
rw [ne_eq, eq_comm]
simp
theorem head_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : h₁ = h₂ := (cons.inj H).1
theorem tail_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : t₁ = t₂ := (cons.inj H).2
theorem cons_inj_right (a : α) {l l' : List α} : a :: l = a :: l' l = l' :=
tail_eq_of_cons_eq, congrArg _
theorem cons_eq_cons {a b : α} {l l' : List α} : a :: l = b :: l' a = b l = l' :=
List.cons.injEq .. .rfl
theorem exists_cons_of_ne_nil : {l : List α}, l [] b L, l = b :: L
| c :: l', _ => c, l', rfl
theorem ne_nil_iff_exists_cons {l : List α} : l [] b L, l = b :: L :=
exists_cons_of_ne_nil, fun _, _, eq => eq.symm cons_ne_nil _ _
theorem singleton_inj {α : Type _} {a b : α} : [a] = [b] a = b := by
simp
/-! ## L[i] and L[i]? -/
/-! ### `get` and `get?`.
@@ -163,57 +161,29 @@ theorem length_eq_one {l : List α} : length l = 1 ↔ ∃ a, l = [a] :=
We simplify `l.get i` to `l[i.1]'i.2` and `l.get? i` to `l[i]?`.
-/
theorem get_cons_zero : get (a::l) (0 : Fin (l.length + 1)) = a := rfl
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
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
theorem get_cons_succ' {as : List α} {i : Fin as.length} :
(a :: as).get i.succ = as.get i := rfl
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
theorem get_mk_zero : {l : List α} (h : 0 < l.length), l.get 0, h = l.head (length_pos.mp h)
| _::_, _ => rfl
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
theorem get?_len_le : {l : List α} {n}, length l n l.get? n = none
theorem get?_eq_none : {l : List α} {n}, length l n l.get? n = none
| [], _, _ => rfl
| _ :: l, _+1, h => get?_len_le (l := l) <| Nat.le_of_succ_le_succ h
| _ :: l, _+1, h => get?_eq_none (l := l) <| Nat.le_of_succ_le_succ h
theorem get?_eq_get : {l : List α} {n} (h : n < l.length), l.get? n = some (get l n, h)
| _ :: _, 0, _ => rfl
| _ :: l, _+1, _ => get?_eq_get (l := l) _
theorem get?_eq_some : l.get? n = some a h, get l n, h = a :=
theorem get?_eq_some_iff : l.get? n = some a h, get l n, h = a :=
fun e =>
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_len_le hn e
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_eq_none hn e
this, by rwa [get?_eq_get this, Option.some.injEq] at e,
fun _, e => e get?_eq_get _
theorem get?_eq_none : l.get? n = none length l n :=
fun e => Nat.ge_of_not_lt (fun h' => by cases e get?_eq_some.2 h', rfl), get?_len_le
theorem get?_eq_none_iff : l.get? n = none length l n :=
fun e => Nat.ge_of_not_lt (fun h' => by cases e get?_eq_some_iff.2 h', rfl), get?_eq_none
@[simp] theorem get?_eq_getElem? (l : List α) (i : Nat) : l.get? i = l[i]? := by
simp only [getElem?, decidableGetElem?]; split
simp only [getElem?_def]; split
· exact (get?_eq_get _)
· exact (get?_eq_none.2 <| Nat.not_lt.1 _)
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
theorem getElem?_eq_some {l : List α} : l[i]? = some a h : i < l.length, l[i]'h = a := by
simpa using get?_eq_some
/--
If one has `l.get i` in an expression (with `i : Fin l.length`) and `h : l = l'`,
`rw [h]` will give a "motive it not type correct" error, as it cannot rewrite the
`i : Fin l.length` to `Fin l'.length` directly. The theorem `get_of_eq` can be used to make
such a rewrite, with `rw [get_of_eq h]`.
-/
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
get l i = get l' i, h i.2 := by cases h; rfl
· exact (get?_eq_none_iff.2 <| Nat.not_lt.1 _)
/-! ### getD
@@ -224,42 +194,29 @@ Because of this, there is only minimal API for `getD`.
@[simp] theorem getD_eq_getElem?_getD (l) (n) (a : α) : getD l n a = (l[n]?).getD a := by
simp [getD]
@[deprecated getD_eq_getElem?_getD (since := "2024-06-12")]
theorem getD_eq_get? : l n (a : α), getD l n a = (get? l n).getD a := by simp
/-! ### get!
We simplify `l.get! n` to `l[n]!`.
-/
theorem get!_of_get? [Inhabited α] : {l : List α} {n}, get? l n = some a get! l n = a
| _a::_, 0, rfl => rfl
| _::l, _+1, e => get!_of_get? (l := l) e
theorem get!_eq_getD [Inhabited α] : (l : List α) n, l.get! n = l.getD n default
| [], _ => rfl
| _a::_, 0 => rfl
| _a::l, n+1 => get!_eq_getD l n
theorem get!_len_le [Inhabited α] : {l : List α} {n}, length l n l.get! n = (default : α)
| [], _, _ => rfl
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
@[simp] theorem get!_eq_getElem! [Inhabited α] (l : List α) (n) : l.get! n = l[n]! := by
simp [get!_eq_getD]
rfl
/-! ### getElem! -/
/-! ### getElem!
@[simp] theorem getElem!_nil [Inhabited α] {n : Nat} : ([] : List α)[n]! = default := rfl
We simplify `l[n]!` to `(l[n]?).getD default`.
-/
@[simp] theorem getElem!_cons_zero [Inhabited α] {l : List α} : (a::l)[0]! = a := by
rw [getElem!_pos] <;> simp
@[simp] theorem getElem!_cons_succ [Inhabited α] {l : List α} : (a::l)[n+1]! = l[n]! := by
by_cases h : n < l.length
· rw [getElem!_pos, getElem!_pos] <;> simp_all [Nat.succ_lt_succ_iff]
· rw [getElem!_neg, getElem!_neg] <;> simp_all [Nat.succ_lt_succ_iff]
@[simp] theorem getElem!_eq_getElem?_getD [Inhabited α] (l : List α) (n : Nat) :
l[n]! = (l[n]?).getD (default : α) := by
simp only [getElem!_def]
split <;> simp_all
/-! ### getElem? and getElem -/
@@ -267,23 +224,19 @@ theorem get!_len_le [Inhabited α] : ∀ {l : List α} {n}, length l ≤ n → l
simp only [getElem?_def, h, reduceDIte]
theorem getElem?_eq_some_iff {l : List α} : l[n]? = some a h : n < l.length, l[n] = a := by
simp only [ get?_eq_getElem?, get?_eq_some, get_eq_getElem]
simp only [ get?_eq_getElem?, get?_eq_some_iff, get_eq_getElem]
theorem some_eq_getElem?_iff {l : List α} : some a = l[n]? h : n < l.length, l[n] = a := by
rw [eq_comm, getElem?_eq_some_iff]
@[simp] theorem getElem?_eq_none_iff : l[n]? = none length l n := by
simp only [ get?_eq_getElem?, get?_eq_none]
simp only [ get?_eq_getElem?, get?_eq_none_iff]
@[simp] theorem none_eq_getElem?_iff {l : List α} {n : Nat} : none = l[n]? length l n := by
simp [eq_comm (a := none)]
theorem getElem?_eq_none (h : length l n) : l[n]? = none := getElem?_eq_none_iff.mpr h
theorem getElem?_eq (l : List α) (i : Nat) :
l[i]? = if h : i < l.length then some l[i] else none := by
split <;> simp_all
@[simp] theorem some_getElem_eq_getElem?_iff {α} (xs : List α) (i : Nat) (h : i < xs.length) :
(some xs[i] = xs[i]?) True := by
simp [h]
@@ -300,9 +253,6 @@ theorem getElem_eq_getElem?_get (l : List α) (i : Nat) (h : i < l.length) :
l[i] = l[i]?.get (by simp [getElem?_eq_getElem, h]) := by
simp [getElem_eq_iff]
@[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? :=
@getElem_eq_getElem?_get
@[simp] theorem getElem?_nil {n : Nat} : ([] : List α)[n]? = none := rfl
theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
@@ -314,11 +264,6 @@ theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
theorem getElem?_cons : (a :: l)[i]? = if i = 0 then some a else l[i-1]? := by
cases i <;> simp
theorem getElem?_len_le : {l : List α} {n}, length l n l[n]? = none
| [], _, _ => rfl
| _ :: l, _+1, h => by
rw [getElem?_cons_succ, getElem?_len_le (l := l) <| Nat.le_of_succ_le_succ h]
/--
If one has `l[i]` in an expression and `h : l = l'`,
`rw [h]` will give a "motive it not type correct" error, as it cannot rewrite the
@@ -332,20 +277,10 @@ theorem getElem_of_eq {l l' : List α} (h : l = l') {i : Nat} (w : i < l.length)
match i, h with
| 0, _ => rfl
@[deprecated getElem_singleton (since := "2024-06-12")]
theorem get_singleton (a : α) (n : Fin 1) : get [a] n = a := by simp
theorem getElem_zero {l : List α} (h : 0 < l.length) : l[0] = l.head (length_pos.mp h) :=
match l, h with
| _ :: _, _ => rfl
theorem getElem!_of_getElem? [Inhabited α] : {l : List α} {n : Nat}, l[n]? = some a l[n]! = a
| _a::_, 0, _ => by
rw [getElem!_pos] <;> simp_all
| _::l, _+1, e => by
simp at e
simp_all [getElem!_of_getElem? (l := l) e]
@[ext] theorem ext_getElem? {l₁ l₂ : List α} (h : n : Nat, l₁[n]? = l₂[n]?) : l₁ = l₂ :=
ext_get? fun n => by simp_all
@@ -356,11 +291,7 @@ theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
simp_all [getElem?_eq_getElem]
else by
have h₁ := Nat.le_of_not_lt h₁
rw [getElem?_len_le h₁, getElem?_len_le]; rwa [ hl]
theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
(h : n h₁ h₂, get l₁ n, h₁ = get l₂ n, h₂) : l₁ = l₂ :=
ext_getElem hl (by simp_all)
rw [getElem?_eq_none h₁, getElem?_eq_none]; rwa [ hl]
@[simp] theorem getElem_concat_length : (l : List α) (a : α) (i) (_ : i = l.length) (w), (l ++ [a])[i]'w = a
| [], a, _, h, _ => by subst h; simp
@@ -369,19 +300,11 @@ theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
theorem getElem?_concat_length (l : List α) (a : α) : (l ++ [a])[l.length]? = some a := by
simp
@[deprecated getElem?_concat_length (since := "2024-06-12")]
theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = some a := by simp
theorem isSome_getElem? {l : List α} {n : Nat} : l[n]?.isSome n < l.length := by
simp
@[simp] theorem isSome_getElem? {l : List α} {n : Nat} : l[n]?.isSome n < l.length := by
by_cases h : n < l.length
· simp_all
· simp [h]
simp_all
@[simp] theorem isNone_getElem? {l : List α} {n : Nat} : l[n]?.isNone l.length n := by
by_cases h : n < l.length
· simp_all
· simp [h]
theorem isNone_getElem? {l : List α} {n : Nat} : l[n]?.isNone l.length n := by
simp
/-! ### mem -/
@@ -493,42 +416,19 @@ theorem getElem_of_mem : ∀ {a} {l : List α}, a ∈ l → ∃ (n : Nat) (h : n
| _, _ :: _, .head .. => 0, Nat.succ_pos _, rfl
| _, _ :: _, .tail _ m => let n, h, e := getElem_of_mem m; n+1, Nat.succ_lt_succ h, e
theorem get_of_mem {a} {l : List α} (h : a l) : n, get l n = a := by
obtain n, h, e := getElem_of_mem h
exact n, h, e
theorem getElem?_of_mem {a} {l : List α} (h : a l) : n : Nat, l[n]? = some a :=
let n, _, e := getElem_of_mem h; n, e getElem?_eq_getElem _
theorem get?_of_mem {a} {l : List α} (h : a l) : n, l.get? n = some a :=
let n, _, e := get_of_mem h; n, e get?_eq_get _
theorem get_mem : (l : List α) n, get l n l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (get_mem l ..)
theorem getElem?_of_mem {a} {l : List α} (h : a l) : n : Nat, l[n]? = some a := by
let n, _, e := getElem_of_mem h
exact n, e getElem?_eq_getElem _
theorem mem_of_getElem? {l : List α} {n : Nat} {a : α} (e : l[n]? = some a) : a l :=
let _, e := getElem?_eq_some_iff.1 e; e getElem_mem ..
@[deprecated mem_of_getElem? (since := "2024-09-06")] abbrev getElem?_mem := @mem_of_getElem?
theorem mem_of_get? {l : List α} {n a} (e : l.get? n = some a) : a l :=
let _, e := get?_eq_some.1 e; e get_mem ..
@[deprecated mem_of_get? (since := "2024-09-06")] abbrev get?_mem := @mem_of_get?
theorem mem_iff_getElem {a} {l : List α} : a l (n : Nat) (h : n < l.length), l[n]'h = a :=
getElem_of_mem, fun _, _, e => e getElem_mem ..
theorem mem_iff_get {a} {l : List α} : a l n, get l n = a :=
get_of_mem, fun _, e => e get_mem ..
theorem mem_iff_getElem? {a} {l : List α} : a l n : Nat, l[n]? = some a := by
simp [getElem?_eq_some_iff, mem_iff_getElem]
theorem mem_iff_get? {a} {l : List α} : a l n, l.get? n = some a := by
simp [getElem?_eq_some_iff, Fin.exists_iff, mem_iff_get]
theorem forall_getElem {l : List α} {p : α Prop} :
( (n : Nat) h, p (l[n]'h)) a, a l p a := by
induction l with
@@ -579,18 +479,6 @@ theorem isEmpty_iff_length_eq_zero {l : List α} : l.isEmpty ↔ l.length = 0 :=
/-! ### any / all -/
theorem any_beq [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => a == x) a l := by
induction l <;> simp_all
theorem any_beq' [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => x == a) a l := by
induction l <;> simp_all [eq_comm (a := a)]
theorem all_bne [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => a != x) a l := by
induction l <;> simp_all
theorem all_bne' [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => x != a) a l := by
induction l <;> simp_all [eq_comm (a := a)]
theorem any_eq {l : List α} : l.any p = decide ( x, x l p x) := by induction l <;> simp [*]
theorem all_eq {l : List α} : l.all p = decide ( x, x l p x) := by induction l <;> simp [*]
@@ -615,6 +503,18 @@ theorem decide_forall_mem {l : List α} {p : α → Prop} [DecidablePred p] :
@[simp] theorem all_eq_false {l : List α} : l.all p = false x, x l ¬p x := by
simp [all_eq]
theorem any_beq [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => a == x) a l := by
simp
theorem any_beq' [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => x == a) a l := by
simp
theorem all_bne [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => a != x) a l := by
induction l <;> simp_all
theorem all_bne' [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => x != a) a l := by
induction l <;> simp_all [eq_comm (a := a)]
/-! ### set -/
-- As `List.set` is defined in `Init.Prelude`, we write the basic simplification lemmas here.
@@ -632,19 +532,10 @@ theorem decide_forall_mem {l : List α} {p : α → Prop} [DecidablePred p] :
| _ :: _, 0 => by simp
| _ :: l, i + 1 => by simp [getElem_set_self]
@[deprecated getElem_set_self (since := "2024-09-04")] abbrev getElem_set_eq := @getElem_set_self
@[deprecated getElem_set_self (since := "2024-06-12")]
theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
(l.set i a).get i, h = a := by
simp
@[simp] theorem getElem?_set_self {l : List α} {i : Nat} {a : α} (h : i < l.length) :
(l.set i a)[i]? = some a := by
simp_all [getElem?_eq_some_iff]
@[deprecated getElem?_set_self (since := "2024-09-04")] abbrev getElem?_set_eq := @getElem?_set_self
/-- This differs from `getElem?_set_self` by monadically mapping `Function.const _ a` over the `Option`
returned by `l[i]?`. -/
theorem getElem?_set_self' {l : List α} {i : Nat} {a : α} :
@@ -666,12 +557,6 @@ theorem getElem?_set_self' {l : List α} {i : Nat} {a : α} :
have g : i j := h congrArg (· + 1)
simp [getElem_set_ne g]
@[deprecated getElem_set_ne (since := "2024-06-12")]
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 := by
simp [h]
@[simp] theorem getElem?_set_ne {l : List α} {i j : Nat} (h : i j) {a : α} :
(l.set i a)[j]? = l[j]? := by
by_cases hj : j < (l.set i a).length
@@ -686,11 +571,6 @@ theorem getElem_set {l : List α} {m n} {a} (h) :
else
simp [h]
@[deprecated getElem_set (since := "2024-06-12")]
theorem get_set {l : List α} {m n} {a : α} (h) :
(set l m a).get n, h = if m = n then a else l.get n, length_set .. h := by
simp [getElem_set]
theorem getElem?_set {l : List α} {i j : Nat} {a : α} :
(l.set i a)[j]? = if i = j then if i < l.length then some a else none else l[j]? := by
if h : i = j then
@@ -710,6 +590,14 @@ theorem getElem?_set' {l : List α} {i j : Nat} {a : α} :
· simp only [getElem?_set_self', Option.map_eq_map, reduceIte, *]
· simp only [ne_eq, not_false_eq_true, getElem?_set_ne, reduceIte, *]
@[simp] theorem set_getElem_self {as : List α} {i : Nat} (h : i < as.length) :
as.set i as[i] = as := by
apply ext_getElem
· simp
· intro n h₁ h₂
rw [getElem_set]
split <;> simp_all
theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length n) {a : α} :
l.set n a = l := by
induction l generalizing n with
@@ -724,8 +612,6 @@ theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α
@[simp] theorem set_eq_nil_iff {l : List α} (n : Nat) (a : α) : l.set n a = [] l = [] := by
cases l <;> cases n <;> simp [set]
@[deprecated set_eq_nil_iff (since := "2024-09-05")] abbrev set_eq_nil := @set_eq_nil_iff
theorem set_comm (a b : α) : {n m : Nat} (l : List α), n m
(l.set n a).set m b = (l.set m b).set n a
| _, _, [], _ => by simp
@@ -1064,7 +950,7 @@ theorem getLast_eq_getElem : ∀ (l : List α) (h : l ≠ []),
| _ :: _ :: _, _ => by
simp [getLast, get, Nat.succ_sub_succ, getLast_eq_getElem]
theorem getElem_length_sub_one_eq_getLast (l : List α) (h) :
theorem getElem_length_sub_one_eq_getLast (l : List α) (h : l.length - 1 < l.length) :
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
rw [ getLast_eq_getElem]
@@ -1192,7 +1078,8 @@ theorem head_eq_getElem (l : List α) (h : l ≠ []) : head l h = l[0]'(length_p
| nil => simp at h
| cons _ _ => simp
theorem getElem_zero_eq_head (l : List α) (h) : l[0] = head l (by simpa [length_pos] using h) := by
theorem getElem_zero_eq_head (l : List α) (h : 0 < l.length) :
l[0] = head l (by simpa [length_pos] using h) := by
cases l with
| nil => simp at h
| cons _ _ => simp
@@ -1784,7 +1671,7 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
@[simp] theorem cons_append_fun (a : α) (as : List α) :
(fun bs => ((a :: as) ++ bs)) = fun bs => a :: (as ++ bs) := rfl
theorem getElem_append {l₁ l₂ : List α} (n : Nat) (h) :
theorem getElem_append {l₁ l₂ : List α} (n : Nat) (h : n < (l₁ ++ l₂).length) :
(l₁ ++ l₂)[n] = if h' : n < l₁.length then l₁[n] else l₂[n - l₁.length]'(by simp at h h'; exact Nat.sub_lt_left_of_lt_add h' h) := by
split <;> rename_i h'
· rw [getElem_append_left h']
@@ -2326,6 +2213,11 @@ theorem flatMap_def (l : List α) (f : α → List β) : l.flatMap f = flatten (
@[simp] theorem flatMap_id (l : List (List α)) : List.flatMap l id = l.flatten := by simp [flatMap_def]
@[simp]
theorem length_flatMap (l : List α) (f : α List β) :
length (l.flatMap f) = sum (map (length f) l) := by
rw [List.flatMap, length_flatten, map_map]
@[simp] theorem mem_flatMap {f : α List β} {b} {l : List α} : b l.flatMap f a, a l b f a := by
simp [flatMap_def, mem_flatten]
exact fun _, a, h₁, rfl, h₂ => a, h₁, h₂, fun a, h₁, h₂ => _, a, h₁, rfl, h₂
@@ -2982,7 +2874,7 @@ are often used for theorems about `Array.pop`.
@[simp] theorem getElem_dropLast : (xs : List α) (i : Nat) (h : i < xs.dropLast.length),
xs.dropLast[i] = xs[i]'(Nat.lt_of_lt_of_le h (length_dropLast .. Nat.pred_le _))
| _::_::_, 0, _ => rfl
| _::_::_, i+1, _ => getElem_dropLast _ i _
| _::_::_, i+1, h => getElem_dropLast _ i (Nat.add_one_lt_add_one_iff.mp h)
@[deprecated getElem_dropLast (since := "2024-06-12")]
theorem get_dropLast (xs : List α) (i : Fin xs.dropLast.length) :
@@ -3357,10 +3249,10 @@ theorem any_eq_not_all_not (l : List α) (p : α → Bool) : l.any p = !l.all (!
theorem all_eq_not_any_not (l : List α) (p : α Bool) : l.all p = !l.any (!p .) := by
simp only [not_any_eq_all_not, Bool.not_not]
@[simp] theorem any_map {l : List α} {p : α Bool} : (l.map f).any p = l.any (p f) := by
@[simp] theorem any_map {l : List α} {p : β Bool} : (l.map f).any p = l.any (p f) := by
induction l with simp | cons _ _ ih => rw [ih]
@[simp] theorem all_map {l : List α} {p : α Bool} : (l.map f).all p = l.all (p f) := by
@[simp] theorem all_map {l : List α} {p : β Bool} : (l.map f).all p = l.all (p f) := by
induction l with simp | cons _ _ ih => rw [ih]
@[simp] theorem any_filter {l : List α} {p q : α Bool} :
@@ -3445,17 +3337,137 @@ theorem all_eq_not_any_not (l : List α) (p : α → Bool) : l.all p = !l.any (!
(l.insert a).all f = (f a && l.all f) := by
simp [all_eq]
/-! ### Legacy lemmas about `get`, `get?`, and `get!`.
Hopefully these should not be needed, in favour of lemmas about `xs[i]`, `xs[i]?`, and `xs[i]!`,
to which these simplify.
We may consider deprecating or downstreaming these lemmas.
-/
theorem get_cons_zero : get (a::l) (0 : Fin (l.length + 1)) = a := rfl
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
theorem get_cons_succ' {as : List α} {i : Fin as.length} :
(a :: as).get i.succ = as.get i := rfl
theorem get_mk_zero : {l : List α} (h : 0 < l.length), l.get 0, h = l.head (length_pos.mp h)
| _::_, _ => rfl
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
/--
If one has `l.get i` in an expression (with `i : Fin l.length`) and `h : l = l'`,
`rw [h]` will give a "motive is not type correct" error, as it cannot rewrite the
`i : Fin l.length` to `Fin l'.length` directly. The theorem `get_of_eq` can be used to make
such a rewrite, with `rw [get_of_eq h]`.
-/
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
get l i = get l' i, h i.2 := by cases h; rfl
theorem get!_of_get? [Inhabited α] : {l : List α} {n}, get? l n = some a get! l n = a
| _a::_, 0, rfl => rfl
| _::l, _+1, e => get!_of_get? (l := l) e
theorem get!_len_le [Inhabited α] : {l : List α} {n}, length l n l.get! n = (default : α)
| [], _, _ => rfl
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
theorem getElem!_nil [Inhabited α] {n : Nat} : ([] : List α)[n]! = default := rfl
theorem getElem!_cons_zero [Inhabited α] {l : List α} : (a::l)[0]! = a := by
rw [getElem!_pos] <;> simp
theorem getElem!_cons_succ [Inhabited α] {l : List α} : (a::l)[n+1]! = l[n]! := by
by_cases h : n < l.length
· rw [getElem!_pos, getElem!_pos] <;> simp_all [Nat.succ_lt_succ_iff]
· rw [getElem!_neg, getElem!_neg] <;> simp_all [Nat.succ_lt_succ_iff]
theorem getElem!_of_getElem? [Inhabited α] : {l : List α} {n : Nat}, l[n]? = some a l[n]! = a
| _a::_, 0, _ => by
rw [getElem!_pos] <;> simp_all
| _::l, _+1, e => by
simp at e
simp_all [getElem!_of_getElem? (l := l) e]
theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
(h : n h₁ h₂, get l₁ n, h₁ = get l₂ n, h₂) : l₁ = l₂ :=
ext_getElem hl (by simp_all)
theorem get_of_mem {a} {l : List α} (h : a l) : n, get l n = a := by
obtain n, h, e := getElem_of_mem h
exact n, h, e
theorem get?_of_mem {a} {l : List α} (h : a l) : n, l.get? n = some a :=
let n, _, e := get_of_mem h; n, e get?_eq_get _
theorem get_mem : (l : List α) n, get l n l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (get_mem l ..)
theorem mem_of_get? {l : List α} {n a} (e : l.get? n = some a) : a l :=
let _, e := get?_eq_some_iff.1 e; e get_mem ..
theorem mem_iff_get {a} {l : List α} : a l n, get l n = a :=
get_of_mem, fun _, e => e get_mem ..
theorem mem_iff_get? {a} {l : List α} : a l n, l.get? n = some a := by
simp [getElem?_eq_some_iff, Fin.exists_iff, mem_iff_get]
/-! ### Deprecations -/
@[deprecated getD_eq_getElem?_getD (since := "2024-06-12")]
theorem getD_eq_get? : l n (a : α), getD l n a = (get? l n).getD a := by simp
@[deprecated getElem_singleton (since := "2024-06-12")]
theorem get_singleton (a : α) (n : Fin 1) : get [a] n = a := by simp
@[deprecated getElem?_concat_length (since := "2024-06-12")]
theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = some a := by simp
@[deprecated getElem_set_self (since := "2024-06-12")]
theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
(l.set i a).get i, h = a := by
simp
@[deprecated getElem_set_ne (since := "2024-06-12")]
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 := by
simp [h]
@[deprecated getElem_set (since := "2024-06-12")]
theorem get_set {l : List α} {m n} {a : α} (h) :
(set l m a).get n, h = if m = n then a else l.get n, length_set .. h := by
simp [getElem_set]
@[deprecated cons_inj_right (since := "2024-06-15")] abbrev cons_inj := @cons_inj_right
@[deprecated ne_nil_of_length_eq_add_one (since := "2024-06-16")]
abbrev ne_nil_of_length_eq_succ := @ne_nil_of_length_eq_add_one
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
@[deprecated filter_flatten (since := "2024-08-26")]
theorem join_map_filter (p : α Bool) (l : List (List α)) :
(l.map (filter p)).flatten = (l.flatten).filter p := by
rw [filter_flatten]
@[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? :=
@getElem_eq_getElem?_get
@[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff
@[deprecated flatten_ne_nil_iff (since := "2024-09-05")] abbrev join_ne_nil := @flatten_ne_nil_iff
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons_iff := @flatten_eq_cons_iff
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons := @flatten_eq_cons_iff
@[deprecated flatten_eq_append_iff (since := "2024-09-05")] abbrev join_eq_append := @flatten_eq_append_iff
@[deprecated mem_of_getElem? (since := "2024-09-06")] abbrev getElem?_mem := @mem_of_getElem?
@[deprecated mem_of_get? (since := "2024-09-06")] abbrev get?_mem := @mem_of_get?
@[deprecated getElem_set_self (since := "2024-09-04")] abbrev getElem_set_eq := @getElem_set_self
@[deprecated getElem?_set_self (since := "2024-09-04")] abbrev getElem?_set_eq := @getElem?_set_self
@[deprecated set_eq_nil_iff (since := "2024-09-05")] abbrev set_eq_nil := @set_eq_nil_iff
@[deprecated flatten_nil (since := "2024-10-14")] abbrev join_nil := @flatten_nil
@[deprecated flatten_cons (since := "2024-10-14")] abbrev join_cons := @flatten_cons
@[deprecated length_flatten (since := "2024-10-14")] abbrev length_join := @length_flatten
@[deprecated flatten_singleton (since := "2024-10-14")] abbrev join_singleton := @flatten_singleton
@[deprecated mem_flatten (since := "2024-10-14")] abbrev mem_join := @mem_flatten
@[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff
@[deprecated flatten_eq_nil_iff (since := "2024-10-14")] abbrev join_eq_nil_iff := @flatten_eq_nil_iff
@[deprecated flatten_ne_nil_iff (since := "2024-09-05")] abbrev join_ne_nil := @flatten_ne_nil_iff
@[deprecated flatten_ne_nil_iff (since := "2024-10-14")] abbrev join_ne_nil_iff := @flatten_ne_nil_iff
@[deprecated exists_of_mem_flatten (since := "2024-10-14")] abbrev exists_of_mem_join := @exists_of_mem_flatten
@[deprecated mem_flatten_of_mem (since := "2024-10-14")] abbrev mem_join_of_mem := @mem_flatten_of_mem
@@ -3469,16 +3481,9 @@ theorem all_eq_not_any_not (l : List α) (p : α → Bool) : l.all p = !l.any (!
@[deprecated filter_flatten (since := "2024-10-14")] abbrev filter_join := @filter_flatten
@[deprecated flatten_filter_not_isEmpty (since := "2024-10-14")] abbrev join_filter_not_isEmpty := @flatten_filter_not_isEmpty
@[deprecated flatten_filter_ne_nil (since := "2024-10-14")] abbrev join_filter_ne_nil := @flatten_filter_ne_nil
@[deprecated filter_flatten (since := "2024-08-26")]
theorem join_map_filter (p : α Bool) (l : List (List α)) :
(l.map (filter p)).flatten = (l.flatten).filter p := by
rw [filter_flatten]
@[deprecated flatten_append (since := "2024-10-14")] abbrev join_append := @flatten_append
@[deprecated flatten_concat (since := "2024-10-14")] abbrev join_concat := @flatten_concat
@[deprecated flatten_flatten (since := "2024-10-14")] abbrev join_join := @flatten_flatten
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons_iff := @flatten_eq_cons_iff
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons := @flatten_eq_cons_iff
@[deprecated flatten_eq_append_iff (since := "2024-09-05")] abbrev join_eq_append := @flatten_eq_append_iff
@[deprecated flatten_eq_append_iff (since := "2024-10-14")] abbrev join_eq_append_iff := @flatten_eq_append_iff
@[deprecated eq_iff_flatten_eq (since := "2024-10-14")] abbrev eq_iff_join_eq := @eq_iff_flatten_eq
@[deprecated flatten_replicate_nil (since := "2024-10-14")] abbrev join_replicate_nil := @flatten_replicate_nil
@@ -3513,4 +3518,18 @@ theorem join_map_filter (p : α → Bool) (l : List (List α)) :
@[deprecated any_flatMap (since := "2024-10-16")] abbrev any_bind := @any_flatMap
@[deprecated all_flatMap (since := "2024-10-16")] abbrev all_bind := @all_flatMap
@[deprecated get?_eq_none (since := "2024-11-29")] abbrev get?_len_le := @get?_eq_none
@[deprecated getElem?_eq_some_iff (since := "2024-11-29")]
abbrev getElem?_eq_some := @getElem?_eq_some_iff
@[deprecated get?_eq_some_iff (since := "2024-11-29")]
abbrev get?_eq_some := @get?_eq_some_iff
@[deprecated LawfulGetElem.getElem?_def (since := "2024-11-29")]
theorem getElem?_eq (l : List α) (i : Nat) :
l[i]? = if h : i < l.length then some l[i] else none :=
getElem?_def _ _
@[deprecated getElem?_eq_none (since := "2024-11-29")] abbrev getElem?_len_le := @getElem?_eq_none
end List

View File

@@ -87,8 +87,8 @@ theorem mapFinIdx_eq_ofFn {as : List α} {f : Fin as.length → α → β} :
apply ext_getElem <;> simp
@[simp] theorem getElem?_mapFinIdx {l : List α} {f : Fin l.length α β} {i : Nat} :
(l.mapFinIdx f)[i]? = l[i]?.pbind fun x m => f i, by simp [getElem?_eq_some] at m; exact m.1 x := by
simp only [getElem?_eq, length_mapFinIdx, getElem_mapFinIdx]
(l.mapFinIdx f)[i]? = l[i]?.pbind fun x m => f i, by simp [getElem?_eq_some_iff] at m; exact m.1 x := by
simp only [getElem?_def, length_mapFinIdx, getElem_mapFinIdx]
split <;> simp
@[simp]
@@ -126,7 +126,8 @@ theorem mapFinIdx_singleton {a : α} {f : Fin 1 → α → β} :
theorem mapFinIdx_eq_enum_map {l : List α} {f : Fin l.length α β} :
l.mapFinIdx f = l.enum.attach.map
fun i, x, m => f i, by rw [mk_mem_enum_iff_getElem?, getElem?_eq_some] at m; exact m.1 x := by
fun i, x, m =>
f i, by rw [mk_mem_enum_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1 x := by
apply ext_getElem <;> simp
@[simp]
@@ -235,7 +236,7 @@ theorem getElem?_mapIdx_go : ∀ {l : List α} {arr : Array β} {i : Nat},
(mapIdx.go f l arr)[i]? =
if h : i < arr.size then some arr[i] else Option.map (f i) l[i - arr.size]?
| [], arr, i => by
simp only [mapIdx.go, Array.toListImpl_eq, getElem?_eq, Array.length_toList,
simp only [mapIdx.go, Array.toListImpl_eq, getElem?_def, Array.length_toList,
Array.getElem_eq_getElem_toList, length_nil, Nat.not_lt_zero, reduceDIte, Option.map_none']
| a :: l, arr, i => by
rw [mapIdx.go, getElem?_mapIdx_go]

View File

@@ -15,3 +15,4 @@ import Init.Data.List.Nat.Find
import Init.Data.List.Nat.BEq
import Init.Data.List.Nat.Modify
import Init.Data.List.Nat.InsertIdx
import Init.Data.List.Nat.Perm

View File

@@ -0,0 +1,54 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Nat.TakeDrop
import Init.Data.List.Perm
namespace List
/-- Helper lemma for `set_set_perm`-/
private theorem set_set_perm' {as : List α} {i j : Nat} (h₁ : i < as.length) (h₂ : i + j < as.length)
(hj : 0 < j) :
(as.set i as[i + j]).set (i + j) as[i] ~ as := by
have : as =
as.take i ++ as[i] :: (as.take (i + j)).drop (i + 1) ++ as[i + j] :: as.drop (i + j + 1) := by
simp only [getElem_cons_drop, append_assoc, cons_append]
rw [ drop_append_of_le_length]
· simp
· simp; omega
conv => lhs; congr; congr; rw [this]
conv => rhs; rw [this]
rw [set_append_left _ _ (by simp; omega)]
rw [set_append_right _ _ (by simp; omega)]
rw [set_append_right _ _ (by simp; omega)]
simp only [length_append, length_take, length_set, length_cons, length_drop]
rw [(show i - min i as.length = 0 by omega)]
rw [(show i + j - (min i as.length + (min (i + j) as.length - (i + 1) + 1)) = 0 by omega)]
simp only [set_cons_zero]
simp only [append_assoc]
apply Perm.append_left
apply cons_append_cons_perm
theorem set_set_perm {as : List α} {i j : Nat} (h₁ : i < as.length) (h₂ : j < as.length) :
(as.set i as[j]).set j as[i] ~ as := by
if h₃ : i = j then
simp [h₃]
else
if h₃ : i < j then
let j' := j - i
have t : j = i + j' := by omega
generalize j' = j' at t
subst t
exact set_set_perm' _ _ (by omega)
else
rw [set_comm _ _ _ (by omega)]
let i' := i - j
have t : i = j + i' := by omega
generalize i' = i' at t
subst t
apply set_set_perm' _ _ (by omega)
end List

View File

@@ -345,7 +345,7 @@ theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
theorem set_eq_take_append_cons_drop (l : List α) (n : Nat) (a : α) :
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
split <;> rename_i h
· ext1 m

View File

@@ -39,6 +39,9 @@ protected theorem Perm.symm {l₁ l₂ : List α} (h : l₁ ~ l₂) : l₂ ~ l
| swap => exact swap ..
| trans _ _ ih₁ ih₂ => exact trans ih₂ ih₁
instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
trans h₁ h₂ := Perm.trans h₁ h₂
theorem perm_comm {l₁ l₂ : List α} : l₁ ~ l₂ l₂ ~ l₁ := Perm.symm, Perm.symm
theorem Perm.swap' (x y : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : y :: x :: l₁ ~ x :: y :: l₂ :=
@@ -102,7 +105,7 @@ theorem perm_append_comm : ∀ {l₁ l₂ : List α}, l₁ ++ l₂ ~ l₂ ++ l
| _ :: _, _ => (perm_append_comm.cons _).trans perm_middle.symm
theorem perm_append_comm_assoc (l₁ l₂ l₃ : List α) :
Perm (l₁ ++ (l₂ ++ l₃)) (l₂ ++ (l₁ ++ l₃)) := by
(l₁ ++ (l₂ ++ l₃)) ~ (l₂ ++ (l₁ ++ l₃)) := by
simpa only [List.append_assoc] using perm_append_comm.append_right _
theorem concat_perm (l : List α) (a : α) : concat l a ~ a :: l := by simp
@@ -133,7 +136,7 @@ theorem Perm.nil_eq {l : List α} (p : [] ~ l) : [] = l := p.symm.eq_nil.symm
theorem not_perm_nil_cons (x : α) (l : List α) : ¬[] ~ x :: l := (nomatch ·.symm.eq_nil)
theorem not_perm_cons_nil {l : List α} {a : α} : ¬(Perm (a::l) []) :=
theorem not_perm_cons_nil {l : List α} {a : α} : ¬((a::l) ~ []) :=
fun h => by simpa using h.length_eq
theorem Perm.isEmpty_eq {l l' : List α} (h : Perm l l') : l.isEmpty = l'.isEmpty := by
@@ -478,6 +481,15 @@ theorem Perm.flatten {l₁ l₂ : List (List α)} (h : l₁ ~ l₂) : l₁.flatt
@[deprecated Perm.flatten (since := "2024-10-14")] abbrev Perm.join := @Perm.flatten
theorem cons_append_cons_perm {a b : α} {as bs : List α} :
a :: as ++ b :: bs ~ b :: as ++ a :: bs := by
suffices [[a], as, [b], bs].flatten ~ [[b], as, [a], bs].flatten by simpa
apply Perm.flatten
calc
[[a], as, [b], bs] ~ [as, [a], [b], bs] := Perm.swap as [a] _
_ ~ [as, [b], [a], bs] := Perm.cons _ (Perm.swap [b] [a] _)
_ ~ [[b], as, [a], bs] := Perm.swap [b] as _
theorem Perm.flatMap_right {l₁ l₂ : List α} (f : α List β) (p : l₁ ~ l₂) : l₁.flatMap f ~ l₂.flatMap f :=
(p.map _).flatten

View File

@@ -841,7 +841,7 @@ theorem isPrefix_iff : l₁ <+: l₂ ↔ ∀ i (h : i < l₁.length), l₂[i]? =
theorem isPrefix_iff_getElem {l₁ l₂ : List α} :
l₁ <+: l₂ (h : l₁.length l₂.length), x (hx : x < l₁.length),
l₁[x] = l₂[x]'(Nat.lt_of_lt_of_le hx h) where
mp h := h.length_le, fun _ _ h.getElem _
mp h := h.length_le, fun _ h' h.getElem h'
mpr h := by
obtain hl, h := h
induction l₂ generalizing l₁ with

View File

@@ -65,13 +65,13 @@ theorem lt_length_of_take_ne_self {l : List α} {n} (h : l.take n ≠ l) : n < l
theorem getElem_cons_drop : (l : List α) (i : Nat) (h : i < l.length),
l[i] :: drop (i + 1) l = drop i l
| _::_, 0, _ => rfl
| _::_, i+1, _ => getElem_cons_drop _ i _
| _::_, i+1, h => getElem_cons_drop _ i (Nat.add_one_lt_add_one_iff.mp h)
@[deprecated getElem_cons_drop (since := "2024-06-12")]
theorem get_cons_drop (l : List α) (i) : get l i :: drop (i + 1) l = drop i l := by
simp
theorem drop_eq_getElem_cons {n} {l : List α} (h) : drop n l = l[n] :: drop (n + 1) l :=
theorem drop_eq_getElem_cons {n} {l : List α} (h : n < l.length) : drop n l = l[n] :: drop (n + 1) l :=
(getElem_cons_drop _ n h).symm
@[deprecated drop_eq_getElem_cons (since := "2024-06-12")]
@@ -192,6 +192,24 @@ theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
Eq.symm <| (append_left_inj _).1 <| (take_append_drop (i+1) l).trans <| by
rw [concat_eq_append, append_assoc, singleton_append, getElem_cons_drop_succ_eq_drop, take_append_drop]
@[simp] theorem take_append_getElem (l : List α) (i : Nat) (h : i < l.length) :
(l.take i) ++ [l[i]] = l.take (i+1) := by
simpa using take_concat_get l i h
@[simp] theorem take_append_getLast (l : List α) (h : l []) :
(l.take (l.length - 1)) ++ [l.getLast h] = l := by
rw [getLast_eq_getElem]
cases l
· contradiction
· simp
@[simp] theorem take_append_getLast? (l : List α) :
(l.take (l.length - 1)) ++ l.getLast?.toList = l := by
match l with
| [] => simp
| x :: xs =>
simpa using take_append_getLast (x :: xs) (by simp)
@[deprecated take_succ_cons (since := "2024-07-25")]
theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl

View File

@@ -71,6 +71,9 @@ theorem shiftRight_eq_div_pow (m : Nat) : ∀ n, m >>> n = m / 2 ^ n
rw [shiftRight_add, shiftRight_eq_div_pow m k]
simp [Nat.div_div_eq_div_mul, Nat.pow_succ, shiftRight_succ]
theorem shiftRight_eq_zero (m n : Nat) (hn : m < 2^n) : m >>> n = 0 := by
simp [Nat.shiftRight_eq_div_pow, Nat.div_eq_of_lt hn]
/-!
### testBit
We define an operation for testing individual bits in the binary representation

View File

@@ -39,9 +39,9 @@ protected theorem dvd_add_iff_right {k m n : Nat} (h : k m) : k n ↔ k
protected theorem dvd_add_iff_left {k m n : Nat} (h : k n) : k m k m + n := by
rw [Nat.add_comm]; exact Nat.dvd_add_iff_right h
theorem dvd_mod_iff {k m n : Nat} (h: k n) : k m % n k m :=
have := Nat.dvd_add_iff_left <| Nat.dvd_trans h <| Nat.dvd_mul_right n (m / n)
by rwa [mod_add_div] at this
theorem dvd_mod_iff {k m n : Nat} (h: k n) : k m % n k m := by
have := Nat.dvd_add_iff_left (m := m % n) <| Nat.dvd_trans h <| Nat.dvd_mul_right n (m / n)
rwa [mod_add_div] at this
theorem le_of_dvd {m n : Nat} (h : 0 < n) : m n m n
| k, e => by
@@ -77,7 +77,7 @@ theorem dvd_of_mod_eq_zero {m n : Nat} (H : n % m = 0) : m n := by
theorem dvd_iff_mod_eq_zero {m n : Nat} : m n n % m = 0 :=
mod_eq_zero_of_dvd, dvd_of_mod_eq_zero
instance decidable_dvd : @DecidableRel Nat (··) :=
instance decidable_dvd : @DecidableRel Nat Nat (··) :=
fun _ _ => decidable_of_decidable_of_iff dvd_iff_mod_eq_zero.symm
theorem emod_pos_of_not_dvd {a b : Nat} (h : ¬ a b) : 0 < b % a := by

View File

@@ -5,6 +5,7 @@ Authors: Floris van Doorn, Leonardo de Moura, Kim Morrison
-/
prelude
import Init.Omega
import Init.Data.List.FinRange
set_option linter.missingDocs true -- keep it documented
universe u
@@ -137,6 +138,54 @@ theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bo
omega
go n 0 f
@[simp] theorem fold_zero {α : Type u} (f : (i : Nat) i < 0 α α) (init : α) :
fold 0 f init = init := by simp [fold]
@[simp] theorem fold_succ {α : Type u} (n : Nat) (f : (i : Nat) i < n + 1 α α) (init : α) :
fold (n + 1) f init = f n (by omega) (fold n (fun i h => f i (by omega)) init) := by simp [fold]
theorem fold_eq_finRange_foldl {α : Type u} (n : Nat) (f : (i : Nat) i < n α α) (init : α) :
fold n f init = (List.finRange n).foldl (fun acc i, h => f i h acc) init := by
induction n with
| zero => simp
| succ n ih =>
simp [ih, List.finRange_succ_last, List.foldl_map]
@[simp] theorem foldRev_zero {α : Type u} (f : (i : Nat) i < 0 α α) (init : α) :
foldRev 0 f init = init := by simp [foldRev]
@[simp] theorem foldRev_succ {α : Type u} (n : Nat) (f : (i : Nat) i < n + 1 α α) (init : α) :
foldRev (n + 1) f init = foldRev n (fun i h => f i (by omega)) (f n (by omega) init) := by
simp [foldRev]
theorem foldRev_eq_finRange_foldr {α : Type u} (n : Nat) (f : (i : Nat) i < n α α) (init : α) :
foldRev n f init = (List.finRange n).foldr (fun i, h acc => f i h acc) init := by
induction n generalizing init with
| zero => simp
| succ n ih => simp [ih, List.finRange_succ_last, List.foldr_map]
@[simp] theorem any_zero {f : (i : Nat) i < 0 Bool} : any 0 f = false := by simp [any]
@[simp] theorem any_succ {n : Nat} (f : (i : Nat) i < n + 1 Bool) :
any (n + 1) f = (any n (fun i h => f i (by omega)) || f n (by omega)) := by simp [any]
theorem any_eq_finRange_any {n : Nat} (f : (i : Nat) i < n Bool) :
any n f = (List.finRange n).any (fun i, h => f i h) := by
induction n with
| zero => simp
| succ n ih => simp [ih, List.finRange_succ_last, List.any_map, Function.comp_def]
@[simp] theorem all_zero {f : (i : Nat) i < 0 Bool} : all 0 f = true := by simp [all]
@[simp] theorem all_succ {n : Nat} (f : (i : Nat) i < n + 1 Bool) :
all (n + 1) f = (all n (fun i h => f i (by omega)) && f n (by omega)) := by simp [all]
theorem all_eq_finRange_all {n : Nat} (f : (i : Nat) i < n Bool) :
all n f = (List.finRange n).all (fun i, h => f i h) := by
induction n with
| zero => simp
| succ n ih => simp [ih, List.finRange_succ_last, List.all_map, Function.comp_def]
end Nat
namespace Prod

View File

@@ -679,6 +679,10 @@ theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
@[simp] theorem mod_mul_mod {a b c : Nat} : (a % c * b) % c = a * b % c := by
rw [mul_mod, mod_mod, mul_mod]
theorem mod_eq_sub (x w : Nat) : x % w = x - w * (x / w) := by
conv => rhs; congr; rw [ mod_add_div x w]
simp
/-! ### pow -/
theorem pow_succ' {m n : Nat} : m ^ n.succ = m * m ^ n := by

View File

@@ -36,3 +36,7 @@ theorem neZero_iff {n : R} : NeZero n ↔ n ≠ 0 :=
@[simp] theorem neZero_zero_iff_false {α : Type _} [Zero α] : NeZero (0 : α) False :=
fun _ NeZero.ne (0 : α) rfl, fun h h.elim
instance {p : Prop} [Decidable p] {n m : Nat} [NeZero n] [NeZero m] :
NeZero (if p then n else m) := by
split <;> infer_instance

View File

@@ -10,3 +10,4 @@ import Init.Data.Option.Instances
import Init.Data.Option.Lemmas
import Init.Data.Option.Attach
import Init.Data.Option.List
import Init.Data.Option.Monadic

View File

@@ -119,10 +119,14 @@ theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a
· simp at h
· simp [get_some]
@[simp] theorem toList_attach (o : Option α) :
theorem toList_attach (o : Option α) :
o.attach.toList = o.toList.attach.map fun x, h => x, by simpa using h := by
cases o <;> simp
@[simp] theorem attach_toList (o : Option α) :
o.toList.attach = (o.attach.map fun a, h => a, by simpa using h).toList := by
cases o <;> simp
theorem attach_map {o : Option α} (f : α β) :
(o.map f).attach = o.attach.map (fun x, h => f x, mem_map_of_mem f h) := by
cases o <;> simp

View File

@@ -96,12 +96,12 @@ This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
| some a, _ => some a
| none, b => b
@[inline] protected def lt (r : α α Prop) : Option α Option α Prop
@[inline] protected def lt (r : α β Prop) : Option α Option β Prop
| none, some _ => True
| some x, some y => r x y
| _, _ => False
instance (r : α α Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
instance (r : α β Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
| none, some _ => isTrue trivial
| some x, some y => s x y
| some _, none => isFalse not_false

View File

@@ -70,6 +70,13 @@ satisfy `p`, using the proof to apply `f`.
| none, _ => none
| some a, H => f a (H a rfl)
/-- Partial elimination. If `o : Option α` and `f : (a : α) → a ∈ o → β`, then `o.pelim b f` is
the same as `o.elim b f` but `f` is passed the proof that `a ∈ o`. -/
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) a o β) : β :=
match o with
| none => b
| some a => f a rfl
/-- Map a monadic function which returns `Unit` over an `Option`. -/
@[inline] protected def forM [Pure m] : Option α (α m PUnit) m PUnit
| none , _ => pure

View File

@@ -629,4 +629,12 @@ theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → a ∈ o → Option
· rintro h, rfl
rfl
/-! ### pelim -/
@[simp] theorem pelim_none : pelim none b f = b := rfl
@[simp] theorem pelim_some : pelim (some a) b f = f a rfl := rfl
@[simp] theorem pelim_eq_elim : pelim o b (fun a _ => f a) = o.elim b f := by
cases o <;> simp
end Option

View File

@@ -15,17 +15,25 @@ namespace Option
forIn' none b f = pure b := by
rfl
@[simp] theorem forIn'_some [Monad m] (a : α) (b : β) (f : (a' : α) a' some a β m (ForInStep β)) :
forIn' (some a) b f = bind (f a rfl b) (fun | .done r | .yield r => pure r) := by
rfl
@[simp] theorem forIn'_some [Monad m] [LawfulMonad m] (a : α) (b : β) (f : (a' : α) a' some a β m (ForInStep β)) :
forIn' (some a) b f = bind (f a rfl b) (fun r => pure (ForInStep.value r)) := by
simp only [forIn', bind_pure_comp]
rw [map_eq_pure_bind]
congr
funext x
split <;> rfl
@[simp] theorem forIn_none [Monad m] (b : β) (f : α β m (ForInStep β)) :
forIn none b f = pure b := by
rfl
@[simp] theorem forIn_some [Monad m] (a : α) (b : β) (f : α β m (ForInStep β)) :
forIn (some a) b f = bind (f a b) (fun | .done r | .yield r => pure r) := by
rfl
@[simp] theorem forIn_some [Monad m] [LawfulMonad m] (a : α) (b : β) (f : α β m (ForInStep β)) :
forIn (some a) b f = bind (f a b) (fun r => pure (ForInStep.value r)) := by
simp only [forIn, forIn', bind_pure_comp]
rw [map_eq_pure_bind]
congr
funext x
split <;> rfl
@[simp] theorem forIn'_toList [Monad m] (o : Option α) (b : β) (f : (a : α) a o.toList β m (ForInStep β)) :
forIn' o.toList b f = forIn' o b fun a m b => f a (by simpa using m) b := by
@@ -35,4 +43,20 @@ namespace Option
forIn o.toList b f = forIn o b f := by
cases o <;> rfl
@[simp] theorem foldlM_toList [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : α β m α) :
o.toList.foldlM f a = o.elim (pure a) (fun b => f a b) := by
cases o <;> simp
@[simp] theorem foldrM_toList [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : β α m α) :
o.toList.foldrM f a = o.elim (pure a) (fun b => f b a) := by
cases o <;> simp
@[simp] theorem foldl_toList (o : Option β) (a : α) (f : α β α) :
o.toList.foldl f a = o.elim a (fun b => f a b) := by
cases o <;> simp
@[simp] theorem foldr_toList (o : Option β) (a : α) (f : β α α) :
o.toList.foldr f a = o.elim a (fun b => f b a) := by
cases o <;> simp
end Option

View File

@@ -0,0 +1,75 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Option.Attach
import Init.Control.Lawful.Basic
namespace Option
@[congr] theorem forIn'_congr [Monad m] [LawfulMonad m]{as bs : Option α} (w : as = bs)
{b b' : β} (hb : b = b')
{f : (a' : α) a' as β m (ForInStep β)}
{g : (a' : α) a' bs β m (ForInStep β)}
(h : a m b, f a (by simpa [w] using m) b = g a m b) :
forIn' as b f = forIn' bs b' g := by
cases as <;> cases bs
· simp [hb]
· simp at w
· simp at w
· simp only [some.injEq] at w
subst w
simp [hb, h]
theorem forIn'_eq_pelim [Monad m] [LawfulMonad m]
(o : Option α) (f : (a : α) a o β m (ForInStep β)) (b : β) :
forIn' o b f =
o.pelim (pure b) (fun a h => ForInStep.value <$> f a h b) := by
cases o <;> simp
@[simp] theorem forIn'_yield_eq_pelim [Monad m] [LawfulMonad m] (o : Option α)
(f : (a : α) a o β m γ) (g : (a : α) a o β γ β) (b : β) :
forIn' o b (fun a m b => (fun c => .yield (g a m b c)) <$> f a m b) =
o.pelim (pure b) (fun a h => g a h b <$> f a h b) := by
cases o <;> simp
theorem forIn'_pure_yield_eq_pelim [Monad m] [LawfulMonad m]
(o : Option α) (f : (a : α) a o β β) (b : β) :
forIn' o b (fun a m b => pure (.yield (f a m b))) =
pure (f := m) (o.pelim b (fun a h => f a h b)) := by
cases o <;> simp
@[simp] theorem forIn'_id_yield_eq_pelim
(o : Option α) (f : (a : α) a o β β) (b : β) :
forIn' (m := Id) o b (fun a m b => .yield (f a m b)) =
o.pelim b (fun a h => f a h b) := by
cases o <;> simp
theorem forIn_eq_elim [Monad m] [LawfulMonad m]
(o : Option α) (f : (a : α) β m (ForInStep β)) (b : β) :
forIn o b f =
o.elim (pure b) (fun a => ForInStep.value <$> f a b) := by
cases o <;> simp
@[simp] theorem forIn_yield_eq_elim [Monad m] [LawfulMonad m] (o : Option α)
(f : (a : α) β m γ) (g : (a : α) β γ β) (b : β) :
forIn o b (fun a b => (fun c => .yield (g a b c)) <$> f a b) =
o.elim (pure b) (fun a => g a b <$> f a b) := by
cases o <;> simp
theorem forIn_pure_yield_eq_elim [Monad m] [LawfulMonad m]
(o : Option α) (f : (a : α) β β) (b : β) :
forIn o b (fun a b => pure (.yield (f a b))) =
pure (f := m) (o.elim b (fun a => f a b)) := by
cases o <;> simp
@[simp] theorem forIn_id_yield_eq_elim
(o : Option α) (f : (a : α) β β) (b : β) :
forIn (m := Id) o b (fun a b => .yield (f a b)) =
o.elim b (fun a => f a b) := by
cases o <;> simp
end Option

View File

@@ -278,6 +278,16 @@ This function is overridden with a native implementation.
@[extern "lean_usize_of_nat"]
def USize.ofNat32 (n : @& Nat) (h : n < 4294967296) : USize :=
USize.ofNatCore n (Nat.lt_of_lt_of_le h le_usize_size)
@[extern "lean_uint8_to_usize"]
def UInt8.toUSize (a : UInt8) : USize :=
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
@[extern "lean_usize_to_uint8"]
def USize.toUInt8 (a : USize) : UInt8 := a.toNat.toUInt8
@[extern "lean_uint16_to_usize"]
def UInt16.toUSize (a : UInt16) : USize :=
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
@[extern "lean_usize_to_uint16"]
def USize.toUInt16 (a : USize) : UInt16 := a.toNat.toUInt16
@[extern "lean_uint32_to_usize"]
def UInt32.toUSize (a : UInt32) : USize := USize.ofNat32 a.toBitVec.toNat a.toBitVec.isLt
@[extern "lean_usize_to_uint32"]

View File

@@ -1,25 +1,39 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
Authors: Markus Himmel, Mac Malone
-/
prelude
import Init.Data.UInt.Basic
import Init.Data.UInt.Lemmas
import Init.Data.Fin.Bitwise
import Init.Data.BitVec.Lemmas
set_option hygiene false in
macro "declare_bitwise_uint_theorems" typeName:ident : command =>
macro "declare_bitwise_uint_theorems" typeName:ident bits:term:arg : command =>
`(
namespace $typeName
@[simp] protected theorem and_toNat (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := BitVec.toNat_and ..
@[simp] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
@[simp] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
@[simp] protected theorem toBitVec_xor (a b : $typeName) : (a ^^^ b).toBitVec = a.toBitVec ^^^ b.toBitVec := rfl
@[simp] protected theorem toBitVec_shiftLeft (a b : $typeName) : (a <<< b).toBitVec = a.toBitVec <<< (b.toBitVec % $bits) := rfl
@[simp] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec >>> (b.toBitVec % $bits) := rfl
@[simp] protected theorem toNat_and (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := by simp [toNat]
@[simp] protected theorem toNat_or (a b : $typeName) : (a ||| b).toNat = a.toNat ||| b.toNat := by simp [toNat]
@[simp] protected theorem toNat_xor (a b : $typeName) : (a ^^^ b).toNat = a.toNat ^^^ b.toNat := by simp [toNat]
@[simp] protected theorem toNat_shiftLeft (a b : $typeName) : (a <<< b).toNat = a.toNat <<< (b.toNat % $bits) % 2 ^ $bits := by simp [toNat]
@[simp] protected theorem toNat_shiftRight (a b : $typeName) : (a >>> b).toNat = a.toNat >>> (b.toNat % $bits) := by simp [toNat]
open $typeName (toNat_and) in
@[deprecated toNat_and (since := "2024-11-28")]
protected theorem and_toNat (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := BitVec.toNat_and ..
end $typeName
)
declare_bitwise_uint_theorems UInt8
declare_bitwise_uint_theorems UInt16
declare_bitwise_uint_theorems UInt32
declare_bitwise_uint_theorems UInt64
declare_bitwise_uint_theorems USize
declare_bitwise_uint_theorems UInt8 8
declare_bitwise_uint_theorems UInt16 16
declare_bitwise_uint_theorems UInt32 32
declare_bitwise_uint_theorems UInt64 64
declare_bitwise_uint_theorems USize System.Platform.numBits

View File

@@ -1,7 +1,7 @@
/-
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
Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone
-/
prelude
import Init.Data.UInt.Basic
@@ -9,129 +9,202 @@ import Init.Data.Fin.Lemmas
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Bitblast
open Lean in
set_option hygiene false in
macro "declare_uint_theorems" typeName:ident : command =>
`(
namespace $typeName
macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
let mut cmds Syntax.getArgs <$> `(
namespace $typeName
instance : Inhabited $typeName where
default := 0
theorem zero_def : (0 : $typeName) = 0 := rfl
theorem one_def : (1 : $typeName) = 1 := rfl
theorem sub_def (a b : $typeName) : a - b = a.toBitVec - b.toBitVec := rfl
theorem mul_def (a b : $typeName) : a * b = a.toBitVec * b.toBitVec := rfl
theorem mod_def (a b : $typeName) : a % b = a.toBitVec % b.toBitVec := rfl
theorem add_def (a b : $typeName) : a + b = a.toBitVec + b.toBitVec := rfl
theorem zero_def : (0 : $typeName) = 0 := rfl
theorem one_def : (1 : $typeName) = 1 := rfl
theorem sub_def (a b : $typeName) : a - b = a.toBitVec - b.toBitVec := rfl
theorem mul_def (a b : $typeName) : a * b = a.toBitVec * b.toBitVec := rfl
theorem mod_def (a b : $typeName) : a % b = a.toBitVec % b.toBitVec := rfl
theorem add_def (a b : $typeName) : a + b = a.toBitVec + b.toBitVec := rfl
@[simp] theorem toNat_mk : (mk a).toNat = a.toNat := rfl
@[simp] theorem mk_toBitVec_eq : (a : $typeName), mk a.toBitVec = a
| _, _ => rfl
@[simp] theorem toNat_ofNat {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
theorem toBitVec_eq_of_lt {a : Nat} : a < size (ofNat a).toBitVec.toNat = a :=
Nat.mod_eq_of_lt
@[simp] theorem toNat_ofNatCore {n : Nat} {h : n < size} : (ofNatCore n h).toNat = n := BitVec.toNat_ofNatLt ..
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, toBitVec_eq_of_lt h]
@[simp] theorem val_val_eq_toNat (x : $typeName) : x.val.val = x.toNat := rfl
theorem le_def {a b : $typeName} : a b a.toBitVec b.toBitVec := .rfl
theorem toNat_toBitVec_eq_toNat (x : $typeName) : x.toBitVec.toNat = x.toNat := rfl
theorem lt_def {a b : $typeName} : a < b a.toBitVec < b.toBitVec := .rfl
@[simp] theorem mk_toBitVec_eq : (a : $typeName), mk a.toBitVec = a
| _, _ => rfl
@[simp] protected theorem not_le {a b : $typeName} : ¬ a b b < a := by simp [le_def, lt_def]
theorem toBitVec_eq_of_lt {a : Nat} : a < size (ofNat a).toBitVec.toNat = a :=
Nat.mod_eq_of_lt
@[simp] protected theorem not_lt {a b : $typeName} : ¬ a < b b a := by simp [le_def, lt_def]
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, toBitVec_eq_of_lt h]
@[simp] protected theorem le_refl (a : $typeName) : a a := by simp [le_def]
theorem le_def {a b : $typeName} : a b a.toBitVec b.toBitVec := .rfl
@[simp] protected theorem lt_irrefl (a : $typeName) : ¬ a < a := by simp
theorem lt_def {a b : $typeName} : a < b a.toBitVec < b.toBitVec := .rfl
protected theorem le_trans {a b c : $typeName} : a b b c a c := BitVec.le_trans
theorem le_iff_toNat_le {a b : $typeName} : a b a.toNat b.toNat := .rfl
protected theorem lt_trans {a b c : $typeName} : a < b b < c a < c := BitVec.lt_trans
theorem lt_iff_toNat_lt {a b : $typeName} : a < b a.toNat < b.toNat := .rfl
protected theorem le_total (a b : $typeName) : a b b a := BitVec.le_total ..
@[simp] protected theorem not_le {a b : $typeName} : ¬ a b b < a := by simp [le_def, lt_def]
protected theorem lt_asymm {a b : $typeName} : a < b ¬ b < a := BitVec.lt_asymm
@[simp] protected theorem not_lt {a b : $typeName} : ¬ a < b b a := by simp [le_def, lt_def]
protected theorem toBitVec_eq_of_eq {a b : $typeName} (h : a = b) : a.toBitVec = b.toBitVec := h rfl
@[simp] protected theorem le_refl (a : $typeName) : a a := by simp [le_def]
protected theorem eq_of_toBitVec_eq {a b : $typeName} (h : a.toBitVec = b.toBitVec) : a = b := by
cases a; cases b; simp_all
@[simp] protected theorem lt_irrefl (a : $typeName) : ¬ a < a := by simp
open $typeName (eq_of_toBitVec_eq) in
protected theorem eq_of_val_eq {a b : $typeName} (h : a.val = b.val) : a = b := by
rcases a with _; rcases b with _; simp_all [val]
protected theorem le_trans {a b c : $typeName} : a b b c a c := BitVec.le_trans
open $typeName (toBitVec_eq_of_eq) in
protected theorem ne_of_toBitVec_ne {a b : $typeName} (h : a.toBitVec b.toBitVec) : a b :=
fun h' => absurd (toBitVec_eq_of_eq h') h
protected theorem lt_trans {a b c : $typeName} : a < b b < c a < c := BitVec.lt_trans
open $typeName (ne_of_toBitVec_ne) in
protected theorem ne_of_lt {a b : $typeName} (h : a < b) : a b := by
apply ne_of_toBitVec_ne
apply BitVec.ne_of_lt
simpa [lt_def] using h
protected theorem le_total (a b : $typeName) : a b b a := BitVec.le_total ..
@[simp] protected theorem toNat_zero : (0 : $typeName).toNat = 0 := Nat.zero_mod _
protected theorem lt_asymm {a b : $typeName} : a < b ¬ b < a := BitVec.lt_asymm
@[simp] protected theorem toNat_mod (a b : $typeName) : (a % b).toNat = a.toNat % b.toNat := BitVec.toNat_umod ..
protected theorem toBitVec_eq_of_eq {a b : $typeName} (h : a = b) : a.toBitVec = b.toBitVec := h rfl
@[simp] protected theorem toNat_div (a b : $typeName) : (a / b).toNat = a.toNat / b.toNat := BitVec.toNat_udiv ..
protected theorem eq_of_toBitVec_eq {a b : $typeName} (h : a.toBitVec = b.toBitVec) : a = b := by
cases a; cases b; simp_all
@[simp] protected theorem toNat_sub_of_le (a b : $typeName) : b a (a - b).toNat = a.toNat - b.toNat := BitVec.toNat_sub_of_le
open $typeName (eq_of_toBitVec_eq toBitVec_eq_of_eq) in
protected theorem toBitVec_inj {a b : $typeName} : a.toBitVec = b.toBitVec a = b :=
Iff.intro eq_of_toBitVec_eq toBitVec_eq_of_eq
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.toBitVec.isLt
open $typeName (eq_of_toBitVec_eq) in
protected theorem eq_of_val_eq {a b : $typeName} (h : a.val = b.val) : a = b := by
rcases a with _; rcases b with _; simp_all [val]
open $typeName (toNat_mod toNat_lt_size) in
protected theorem toNat_mod_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % ofNat m) < m := by
intro u h1
by_cases h2 : m < size
· rw [toNat_mod, toNat_ofNat_of_lt h2]
apply Nat.mod_lt _ h1
· apply Nat.lt_of_lt_of_le
· apply toNat_lt_size
· simpa using h2
open $typeName (eq_of_val_eq) in
protected theorem val_inj {a b : $typeName} : a.val = b.val a = b :=
Iff.intro eq_of_val_eq (congrArg val)
open $typeName (toNat_mod_lt) in
set_option linter.deprecated false in
@[deprecated toNat_mod_lt (since := "2024-09-24")]
protected theorem modn_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % m) < m := by
intro u
simp only [(· % ·)]
simp only [gt_iff_lt, toNat, modn, Fin.modn_val, BitVec.natCast_eq_ofNat, BitVec.toNat_ofNat,
Nat.reducePow]
rw [Nat.mod_eq_of_lt]
· apply Nat.mod_lt
· apply Nat.lt_of_le_of_lt
· apply Nat.mod_le
· apply Fin.is_lt
open $typeName (toBitVec_eq_of_eq) in
protected theorem ne_of_toBitVec_ne {a b : $typeName} (h : a.toBitVec b.toBitVec) : a b :=
fun h' => absurd (toBitVec_eq_of_eq h') h
protected theorem mod_lt (a : $typeName) {b : $typeName} : 0 < b a % b < b := by
simp only [lt_def, mod_def]
apply BitVec.umod_lt
open $typeName (ne_of_toBitVec_ne) in
protected theorem ne_of_lt {a b : $typeName} (h : a < b) : a b := by
apply ne_of_toBitVec_ne
apply BitVec.ne_of_lt
simpa [lt_def] using h
protected theorem toNat.inj : {a b : $typeName}, a.toNat = b.toNat a = b
| _, _, _, _, rfl => rfl
@[simp] protected theorem toNat_zero : (0 : $typeName).toNat = 0 := Nat.zero_mod _
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := rfl
@[simp] protected theorem toNat_add (a b : $typeName) : (a + b).toNat = (a.toNat + b.toNat) % 2 ^ $bits := BitVec.toNat_add ..
@[simp]
theorem val_ofNat (n : Nat) : val (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
protected theorem toNat_sub (a b : $typeName) : (a - b).toNat = (2 ^ $bits - b.toNat + a.toNat) % 2 ^ $bits := BitVec.toNat_sub ..
@[simp]
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := rfl
@[simp] protected theorem toNat_mul (a b : $typeName) : (a * b).toNat = a.toNat * b.toNat % 2 ^ $bits := BitVec.toNat_mul ..
@[simp]
theorem mk_ofNat (n : Nat) : mk (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
@[simp] protected theorem toNat_mod (a b : $typeName) : (a % b).toNat = a.toNat % b.toNat := BitVec.toNat_umod ..
end $typeName
)
@[simp] protected theorem toNat_div (a b : $typeName) : (a / b).toNat = a.toNat / b.toNat := BitVec.toNat_udiv ..
declare_uint_theorems UInt8
declare_uint_theorems UInt16
declare_uint_theorems UInt32
declare_uint_theorems UInt64
declare_uint_theorems USize
@[simp] protected theorem toNat_sub_of_le (a b : $typeName) : b a (a - b).toNat = a.toNat - b.toNat := BitVec.toNat_sub_of_le
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.toBitVec.isLt
open $typeName (toNat_mod toNat_lt_size) in
protected theorem toNat_mod_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % ofNat m) < m := by
intro u h1
by_cases h2 : m < size
· rw [toNat_mod, toNat_ofNat_of_lt h2]
apply Nat.mod_lt _ h1
· apply Nat.lt_of_lt_of_le
· apply toNat_lt_size
· simpa using h2
open $typeName (toNat_mod_lt) in
set_option linter.deprecated false in
@[deprecated toNat_mod_lt (since := "2024-09-24")]
protected theorem modn_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % m) < m := by
intro u
simp only [(· % ·)]
simp only [gt_iff_lt, toNat, modn, Fin.modn_val, BitVec.natCast_eq_ofNat, BitVec.toNat_ofNat,
Nat.reducePow]
rw [Nat.mod_eq_of_lt]
· apply Nat.mod_lt
· apply Nat.lt_of_le_of_lt
· apply Nat.mod_le
· apply Fin.is_lt
protected theorem mod_lt (a : $typeName) {b : $typeName} : 0 < b a % b < b := by
simp only [lt_def, mod_def]
apply BitVec.umod_lt
protected theorem toNat.inj : {a b : $typeName}, a.toNat = b.toNat a = b
| _, _, _, _, rfl => rfl
protected theorem toNat_inj : {a b : $typeName}, a.toNat = b.toNat a = b :=
Iff.intro toNat.inj (congrArg toNat)
open $typeName (toNat_inj) in
protected theorem le_antisymm_iff {a b : $typeName} : a = b a b b a :=
toNat_inj.symm.trans Nat.le_antisymm_iff
open $typeName (le_antisymm_iff) in
protected theorem le_antisymm {a b : $typeName} (h₁ : a b) (h₂ : b a) : a = b :=
le_antisymm_iff.2 h₁, h₂
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := rfl
@[simp] protected theorem ofNat_toNat {x : $typeName} : ofNat x.toNat = x := by
apply toNat.inj
simp [Nat.mod_eq_of_lt x.toNat_lt_size]
@[simp]
theorem val_ofNat (n : Nat) : val (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp]
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := rfl
@[simp]
theorem mk_ofNat (n : Nat) : mk (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
)
if let some nbits := bits.raw.isNatLit? then
if nbits > 8 then
cmds := cmds.push <|
`(@[simp] theorem toNat_toUInt8 (x : $typeName) : x.toUInt8.toNat = x.toNat % 2 ^ 8 := rfl)
if nbits < 16 then
cmds := cmds.push <|
`(@[simp] theorem toNat_toUInt16 (x : $typeName) : x.toUInt16.toNat = x.toNat := rfl)
else if nbits > 16 then
cmds := cmds.push <|
`(@[simp] theorem toNat_toUInt16 (x : $typeName) : x.toUInt16.toNat = x.toNat % 2 ^ 16 := rfl)
if nbits < 32 then
cmds := cmds.push <|
`(@[simp] theorem toNat_toUInt32 (x : $typeName) : x.toUInt32.toNat = x.toNat := rfl)
else if nbits > 32 then
cmds := cmds.push <|
`(@[simp] theorem toNat_toUInt32 (x : $typeName) : x.toUInt32.toNat = x.toNat % 2 ^ 32 := rfl)
if nbits 32 then
cmds := cmds.push <|
`(@[simp] theorem toNat_toUSize (x : $typeName) : x.toUSize.toNat = x.toNat := rfl)
else
cmds := cmds.push <|
`(@[simp] theorem toNat_toUSize (x : $typeName) : x.toUSize.toNat = x.toNat % 2 ^ System.Platform.numBits := rfl)
if nbits < 64 then
cmds := cmds.push <|
`(@[simp] theorem toNat_toUInt64 (x : $typeName) : x.toUInt64.toNat = x.toNat := rfl)
cmds := cmds.push <| `(end $typeName)
return mkNullNode cmds
declare_uint_theorems UInt8 8
declare_uint_theorems UInt16 16
declare_uint_theorems UInt32 32
declare_uint_theorems UInt64 64
declare_uint_theorems USize System.Platform.numBits
@[simp] theorem USize.toNat_ofNat32 {n : Nat} {h : n < 4294967296} : (ofNat32 n h).toNat = n := rfl
@[simp] theorem USize.toNat_toUInt32 (x : USize) : x.toUInt32.toNat = x.toNat % 2 ^ 32 := rfl
@[simp] theorem USize.toNat_toUInt64 (x : USize) : x.toUInt64.toNat = x.toNat := rfl
theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat n) = n :=
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h le_usize_size)

View File

@@ -21,6 +21,9 @@ deriving Repr, DecidableEq
attribute [simp] Vector.size_toArray
/-- Convert `xs : Array α` to `Vector α xs.size`. -/
abbrev Array.toVector (xs : Array α) : Vector α xs.size := .mk xs rfl
namespace Vector
/-- Syntax for `Vector α n` -/
@@ -44,9 +47,6 @@ def elimAsList {motive : Vector α n → Sort u}
(v : Vector α n) motive v
| a, ha => mk a ha
/-- The empty vector. -/
@[inline] def empty : Vector α 0 := .empty, rfl
/-- Make an empty vector with pre-allocated capacity. -/
@[inline] def mkEmpty (capacity : Nat) : Vector α 0 := .mkEmpty capacity, rfl

View File

@@ -0,0 +1,426 @@
/-
Copyright (c) 2024 Shreyas Srinivas. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Shreyas Srinivas, Francois Dorais
-/
prelude
import Init.Data.Vector.Basic
/-!
## Vectors
Lemmas about `Vector α n`
-/
namespace Array
theorem toVector_inj {a b : Array α} (h₁ : a.size = b.size) (h₂ : a.toVector.cast h₁ = b.toVector) : a = b := by
ext i ih₁ ih₂
· exact h₁
· simpa using congrArg (fun a => a[i]) h₂
end Array
namespace Vector
@[simp] theorem getElem_mk {data : Array α} {size : data.size = n} {i : Nat} (h : i < n) :
(Vector.mk data size)[i] = data[i] := rfl
@[simp] theorem getElem_toArray {α n} (xs : Vector α n) (i : Nat) (h : i < xs.toArray.size) :
xs.toArray[i] = xs[i]'(by simpa using h) := by
cases xs
simp
@[simp] theorem getElem_ofFn {α n} (f : Fin n α) (i : Nat) (h : i < n) :
(Vector.ofFn f)[i] = f i, by simpa using h := by
simp [ofFn]
/-- The empty vector maps to the empty vector. -/
@[simp]
theorem map_empty (f : α β) : map f #v[] = #v[] := by
rw [map, mk.injEq]
exact Array.map_empty f
theorem toArray_inj : {v w : Vector α n}, v.toArray = w.toArray v = w
| {..}, {..}, rfl => rfl
/-- A vector of length `0` is the empty vector. -/
protected theorem eq_empty (v : Vector α 0) : v = #v[] := by
apply Vector.toArray_inj
apply Array.eq_empty_of_size_eq_zero v.2
/--
`Vector.ext` is an extensionality theorem.
Vectors `a` and `b` are equal to each other if their elements are equal for each valid index.
-/
@[ext]
protected theorem ext {a b : Vector α n} (h : (i : Nat) (_ : i < n) a[i] = b[i]) : a = b := by
apply Vector.toArray_inj
apply Array.ext
· rw [a.size_toArray, b.size_toArray]
· intro i hi _
rw [a.size_toArray] at hi
exact h i hi
@[simp] theorem push_mk {data : Array α} {size : data.size = n} {x : α} :
(Vector.mk data size).push x =
Vector.mk (data.push x) (by simp [size, Nat.succ_eq_add_one]) := rfl
@[simp] theorem pop_mk {data : Array α} {size : data.size = n} :
(Vector.mk data size).pop = Vector.mk data.pop (by simp [size]) := rfl
@[simp] theorem getElem_push_last {v : Vector α n} {x : α} : (v.push x)[n] = x := by
rcases v with data, rfl
simp
@[simp] theorem getElem_push_lt {v : Vector α n} {x : α} {i : Nat} (h : i < n) :
(v.push x)[i] = v[i] := by
rcases v with data, rfl
simp [Array.getElem_push_lt, h]
@[simp] theorem getElem_pop {v : Vector α n} {i : Nat} (h : i < n - 1) : (v.pop)[i] = v[i] := by
rcases v with data, rfl
simp
/--
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
defeq issues in the implicit size argument.
-/
@[simp] theorem getElem_pop' (v : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt v.pop i h = v[i] :=
getElem_pop h
@[simp] theorem push_pop_back (v : Vector α (n + 1)) : v.pop.push v.back = v := by
ext i
by_cases h : i < n
· simp [h]
· replace h : i = v.size - 1 := by rw [size_toArray]; omega
subst h
simp [pop, back, back!, Array.eq_push_pop_back!_of_size_ne_zero]
/-! ### mk lemmas -/
theorem toArray_mk (a : Array α) (h : a.size = n) : (Vector.mk a h).toArray = a := rfl
@[simp] theorem allDiff_mk [BEq α] (a : Array α) (h : a.size = n) :
(Vector.mk a h).allDiff = a.allDiff := rfl
@[simp] theorem mk_append_mk (a b : Array α) (ha : a.size = n) (hb : b.size = m) :
Vector.mk a ha ++ Vector.mk b hb = Vector.mk (a ++ b) (by simp [ha, hb]) := rfl
@[simp] theorem back!_mk [Inhabited α] (a : Array α) (h : a.size = n) :
(Vector.mk a h).back! = a.back! := rfl
@[simp] theorem back?_mk (a : Array α) (h : a.size = n) :
(Vector.mk a h).back? = a.back? := rfl
@[simp] theorem drop_mk (a : Array α) (h : a.size = n) (m) :
(Vector.mk a h).drop m = Vector.mk (a.extract m a.size) (by simp [h]) := rfl
@[simp] theorem eraseIdx_mk (a : Array α) (h : a.size = n) (i) (h') :
(Vector.mk a h).eraseIdx i h' = Vector.mk (a.eraseIdx i) (by simp [h]) := rfl
@[simp] theorem eraseIdx!_mk (a : Array α) (h : a.size = n) (i) (hi : i < n) :
(Vector.mk a h).eraseIdx! i = Vector.mk (a.eraseIdx i) (by simp [h, hi]) := by
simp [Vector.eraseIdx!, hi]
@[simp] theorem cast_mk (a : Array α) (h : a.size = n) (h' : n = m) :
(Vector.mk a h).cast h' = Vector.mk a (by simp [h, h']) := rfl
@[simp] theorem extract_mk (a : Array α) (h : a.size = n) (start stop) :
(Vector.mk a h).extract start stop = Vector.mk (a.extract start stop) (by simp [h]) := rfl
@[simp] theorem indexOf?_mk [BEq α] (a : Array α) (h : a.size = n) (x : α) :
(Vector.mk a h).indexOf? x = (a.indexOf? x).map (Fin.cast h) := rfl
@[simp] theorem mk_isEqv_mk (r : α α Bool) (a b : Array α) (ha : a.size = n) (hb : b.size = n) :
Vector.isEqv (Vector.mk a ha) (Vector.mk b hb) r = Array.isEqv a b r := by
simp [Vector.isEqv, Array.isEqv, ha, hb]
@[simp] theorem mk_isPrefixOf_mk [BEq α] (a b : Array α) (ha : a.size = n) (hb : b.size = m) :
(Vector.mk a ha).isPrefixOf (Vector.mk b hb) = a.isPrefixOf b := rfl
@[simp] theorem map_mk (a : Array α) (h : a.size = n) (f : α β) :
(Vector.mk a h).map f = Vector.mk (a.map f) (by simp [h]) := rfl
@[simp] theorem reverse_mk (a : Array α) (h : a.size = n) :
(Vector.mk a h).reverse = Vector.mk a.reverse (by simp [h]) := rfl
@[simp] theorem set_mk (a : Array α) (h : a.size = n) (i x w) :
(Vector.mk a h).set i x = Vector.mk (a.set i x) (by simp [h]) := rfl
@[simp] theorem set!_mk (a : Array α) (h : a.size = n) (i x) :
(Vector.mk a h).set! i x = Vector.mk (a.set! i x) (by simp [h]) := rfl
@[simp] theorem setIfInBounds_mk (a : Array α) (h : a.size = n) (i x) :
(Vector.mk a h).setIfInBounds i x = Vector.mk (a.setIfInBounds i x) (by simp [h]) := rfl
@[simp] theorem swap_mk (a : Array α) (h : a.size = n) (i j) (hi hj) :
(Vector.mk a h).swap i j = Vector.mk (a.swap i j) (by simp [h]) :=
rfl
@[simp] theorem swapIfInBounds_mk (a : Array α) (h : a.size = n) (i j) :
(Vector.mk a h).swapIfInBounds i j = Vector.mk (a.swapIfInBounds i j) (by simp [h]) := rfl
@[simp] theorem swapAt_mk (a : Array α) (h : a.size = n) (i x) (hi) :
(Vector.mk a h).swapAt i x =
((a.swapAt i x).fst, Vector.mk (a.swapAt i x).snd (by simp [h])) :=
rfl
@[simp] theorem swapAt!_mk (a : Array α) (h : a.size = n) (i x) : (Vector.mk a h).swapAt! i x =
((a.swapAt! i x).fst, Vector.mk (a.swapAt! i x).snd (by simp [h])) := rfl
@[simp] theorem take_mk (a : Array α) (h : a.size = n) (m) :
(Vector.mk a h).take m = Vector.mk (a.take m) (by simp [h]) := rfl
@[simp] theorem mk_zipWith_mk (f : α β γ) (a : Array α) (b : Array β)
(ha : a.size = n) (hb : b.size = n) : zipWith (Vector.mk a ha) (Vector.mk b hb) f =
Vector.mk (Array.zipWith a b f) (by simp [ha, hb]) := rfl
/-! ### toArray lemmas -/
@[simp] theorem toArray_append (a : Vector α m) (b : Vector α n) :
(a ++ b).toArray = a.toArray ++ b.toArray := rfl
@[simp] theorem toArray_drop (a : Vector α n) (m) :
(a.drop m).toArray = a.toArray.extract m a.size := rfl
@[simp] theorem toArray_empty : (#v[] : Vector α 0).toArray = #[] := rfl
@[simp] theorem toArray_mkEmpty (cap) :
(Vector.mkEmpty (α := α) cap).toArray = Array.mkEmpty cap := rfl
@[simp] theorem toArray_eraseIdx (a : Vector α n) (i) (h) :
(a.eraseIdx i h).toArray = a.toArray.eraseIdx i (by simp [h]) := rfl
@[simp] theorem toArray_eraseIdx! (a : Vector α n) (i) (hi : i < n) :
(a.eraseIdx! i).toArray = a.toArray.eraseIdx! i := by
cases a; simp_all [Array.eraseIdx!]
@[simp] theorem toArray_cast (a : Vector α n) (h : n = m) :
(a.cast h).toArray = a.toArray := rfl
@[simp] theorem toArray_extract (a : Vector α n) (start stop) :
(a.extract start stop).toArray = a.toArray.extract start stop := rfl
@[simp] theorem toArray_map (f : α β) (a : Vector α n) :
(a.map f).toArray = a.toArray.map f := rfl
@[simp] theorem toArray_ofFn (f : Fin n α) : (Vector.ofFn f).toArray = Array.ofFn f := rfl
@[simp] theorem toArray_pop (a : Vector α n) : a.pop.toArray = a.toArray.pop := rfl
@[simp] theorem toArray_push (a : Vector α n) (x) : (a.push x).toArray = a.toArray.push x := rfl
@[simp] theorem toArray_range : (Vector.range n).toArray = Array.range n := rfl
@[simp] theorem toArray_reverse (a : Vector α n) : a.reverse.toArray = a.toArray.reverse := rfl
@[simp] theorem toArray_set (a : Vector α n) (i x h) :
(a.set i x).toArray = a.toArray.set i x (by simpa using h):= rfl
@[simp] theorem toArray_set! (a : Vector α n) (i x) :
(a.set! i x).toArray = a.toArray.set! i x := rfl
@[simp] theorem toArray_setIfInBounds (a : Vector α n) (i x) :
(a.setIfInBounds i x).toArray = a.toArray.setIfInBounds i x := rfl
@[simp] theorem toArray_singleton (x : α) : (Vector.singleton x).toArray = #[x] := rfl
@[simp] theorem toArray_swap (a : Vector α n) (i j) (hi hj) : (a.swap i j).toArray =
a.toArray.swap i j (by simp [hi, hj]) (by simp [hi, hj]) := rfl
@[simp] theorem toArray_swapIfInBounds (a : Vector α n) (i j) :
(a.swapIfInBounds i j).toArray = a.toArray.swapIfInBounds i j := rfl
@[simp] theorem toArray_swapAt (a : Vector α n) (i x h) :
((a.swapAt i x).fst, (a.swapAt i x).snd.toArray) =
((a.toArray.swapAt i x (by simpa using h)).fst,
(a.toArray.swapAt i x (by simpa using h)).snd) := rfl
@[simp] theorem toArray_swapAt! (a : Vector α n) (i x) :
((a.swapAt! i x).fst, (a.swapAt! i x).snd.toArray) =
((a.toArray.swapAt! i x).fst, (a.toArray.swapAt! i x).snd) := rfl
@[simp] theorem toArray_take (a : Vector α n) (m) : (a.take m).toArray = a.toArray.take m := rfl
@[simp] theorem toArray_zipWith (f : α β γ) (a : Vector α n) (b : Vector β n) :
(Vector.zipWith a b f).toArray = Array.zipWith a.toArray b.toArray f := rfl
/-! ### toList lemmas -/
theorem length_toList {α n} (xs : Vector α n) : xs.toList.length = n := by simp
theorem getElem_toList {α n} (xs : Vector α n) (i : Nat) (h : i < xs.toList.length) :
xs.toList[i] = xs[i]'(by simpa using h) := by simp
theorem toList_inj {a b : Vector α n} (h : a.toList = b.toList) : a = b := by
rcases a with a, ha
rcases b with b, hb
simpa using h
/-! ### set -/
theorem getElem_set (a : Vector α n) (i : Nat) (x : α) (hi : i < n) (j : Nat) (hj : j < n) :
(a.set i x hi)[j] = if i = j then x else a[j] := by
cases a
split <;> simp_all [Array.getElem_set]
@[simp] theorem getElem_set_eq (a : Vector α n) (i : Nat) (x : α) (hi : i < n) :
(a.set i x hi)[i] = x := by simp [getElem_set]
@[simp] theorem getElem_set_ne (a : Vector α n) (i : Nat) (x : α) (hi : i < n) (j : Nat)
(hj : j < n) (h : i j) : (a.set i x hi)[j] = a[j] := by simp [getElem_set, h]
/-! ### setIfInBounds -/
theorem getElem_setIfInBounds (a : Vector α n) (i : Nat) (x : α) (j : Nat)
(hj : j < n) : (a.setIfInBounds i x)[j] = if i = j then x else a[j] := by
cases a
split <;> simp_all [Array.getElem_setIfInBounds]
@[simp] theorem getElem_setIfInBounds_eq (a : Vector α n) (i : Nat) (x : α) (hj : i < n) :
(a.setIfInBounds i x)[i] = x := by simp [getElem_setIfInBounds]
@[simp] theorem getElem_setIfInBounds_ne (a : Vector α n) (i : Nat) (x : α) (j : Nat)
(hj : j < n) (h : i j) : (a.setIfInBounds i x)[j] = a[j] := by simp [getElem_setIfInBounds, h]
/-! ### append -/
theorem getElem_append (a : Vector α n) (b : Vector α m) (i : Nat) (hi : i < n + m) :
(a ++ b)[i] = if h : i < n then a[i] else b[i - n] := by
rcases a with a, rfl
rcases b with b, rfl
simp [Array.getElem_append, hi]
theorem getElem_append_left {a : Vector α n} {b : Vector α m} {i : Nat} (hi : i < n) :
(a ++ b)[i] = a[i] := by simp [getElem_append, hi]
theorem getElem_append_right {a : Vector α n} {b : Vector α m} {i : Nat} (h : i < n + m) (hi : n i) :
(a ++ b)[i] = b[i - n] := by
rw [getElem_append, dif_neg (by omega)]
/-! ### cast -/
@[simp] theorem getElem_cast (a : Vector α n) (h : n = m) (i : Nat) (hi : i < m) :
(a.cast h)[i] = a[i] := by
cases a
simp
/-! ### extract -/
@[simp] theorem getElem_extract (a : Vector α n) (start stop) (i : Nat) (hi : i < min stop n - start) :
(a.extract start stop)[i] = a[start + i] := by
cases a
simp
/-! ### map -/
@[simp] theorem getElem_map (f : α β) (a : Vector α n) (i : Nat) (hi : i < n) :
(a.map f)[i] = f a[i] := by
cases a
simp
/-! ### zipWith -/
@[simp] theorem getElem_zipWith (f : α β γ) (a : Vector α n) (b : Vector β n) (i : Nat)
(hi : i < n) : (zipWith a b f)[i] = f a[i] b[i] := by
cases a
cases b
simp
/-! ### swap -/
theorem getElem_swap (a : Vector α n) (i j : Nat) {hi hj} (k : Nat) (hk : k < n) :
(a.swap i j hi hj)[k] = if k = i then a[j] else if k = j then a[i] else a[k] := by
cases a
simp_all [Array.getElem_swap]
@[simp] theorem getElem_swap_right (a : Vector α n) {i j : Nat} {hi hj} :
(a.swap i j hi hj)[j]'(by simpa using hj) = a[i] := by
simp +contextual [getElem_swap]
@[simp] theorem getElem_swap_left (a : Vector α n) {i j : Nat} {hi hj} :
(a.swap i j hi hj)[i]'(by simpa using hi) = a[j] := by
simp [getElem_swap]
@[simp] theorem getElem_swap_of_ne (a : Vector α n) {i j : Nat} {hi hj} (hp : p < n)
(hi' : p i) (hj' : p j) : (a.swap i j hi hj)[p] = a[p] := by
simp_all [getElem_swap]
@[simp] theorem swap_swap (a : Vector α n) {i j : Nat} {hi hj} :
(a.swap i j hi hj).swap i j hi hj = a := by
cases a
simp_all [Array.swap_swap]
theorem swap_comm (a : Vector α n) {i j : Nat} {hi hj} :
a.swap i j hi hj = a.swap j i hj hi := by
cases a
simp only [swap_mk, mk.injEq]
rw [Array.swap_comm]
/-! ### range -/
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
simp [Vector.range]
/-! ### take -/
@[simp] theorem getElem_take (a : Vector α n) (m : Nat) (hi : i < min n m) :
(a.take m)[i] = a[i] := by
cases a
simp
/-! ### drop -/
@[simp] theorem getElem_drop (a : Vector α n) (m : Nat) (hi : i < n - m) :
(a.drop m)[i] = a[m + i] := by
cases a
simp
/-! ### reverse -/
@[simp] theorem getElem_reverse (a : Vector α n) (i : Nat) (hi : i < n) :
(a.reverse)[i] = a[n - 1 - i] := by
rcases a with a, rfl
simp
/-! ### Decidable quantifiers. -/
theorem forall_zero_iff {P : Vector α 0 Prop} :
( v, P v) P #v[] := by
constructor
· intro h
apply h
· intro h v
obtain (rfl : v = #v[]) := (by ext i h; simp at h)
apply h
theorem forall_cons_iff {P : Vector α (n + 1) Prop} :
( v : Vector α (n + 1), P v) ( (x : α) (v : Vector α n), P (v.push x)) := by
constructor
· intro h _ _
apply h
· intro h v
have w : v = v.pop.push v.back := by simp
rw [w]
apply h
instance instDecidableForallVectorZero (P : Vector α 0 Prop) :
[Decidable (P #v[])], Decidable ( v, P v)
| .isTrue h => .isTrue fun v, s => by
obtain (rfl : v = .empty) := (by ext i h₁ h₂; exact s; cases h₂)
exact h
| .isFalse h => .isFalse (fun w => h (w _))
instance instDecidableForallVectorSucc (P : Vector α (n+1) Prop)
[Decidable ( (x : α) (v : Vector α n), P (v.push x))] : Decidable ( v, P v) :=
decidable_of_iff' ( x (v : Vector α n), P (v.push x)) forall_cons_iff
instance instDecidableExistsVectorZero (P : Vector α 0 Prop) [Decidable (P #v[])] :
Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
instance instDecidableExistsVectorSucc (P : Vector α (n+1) Prop)
[Decidable ( (x : α) (v : Vector α n), ¬ P (v.push x))] : Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not

View File

@@ -172,6 +172,16 @@ theorem getElem!_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem d
simp only [getElem?_def] at h
split <;> simp_all
@[simp] theorem isNone_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?.isNone = ¬dom c i := by
simp only [getElem?_def]
split <;> simp_all
@[simp] theorem isSome_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?.isSome = dom c i := by
simp only [getElem?_def]
split <;> simp_all
namespace Fin
instance instGetElemFinVal [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
@@ -221,7 +231,7 @@ theorem getElem_cons_drop_succ_eq_drop {as : List α} {i : Nat} (h : i < as.leng
as[i] :: as.drop (i+1) = as.drop i :=
match as, i with
| _::_, 0 => rfl
| _::_, i+1 => getElem_cons_drop_succ_eq_drop (i := i) _
| _::_, i+1 => getElem_cons_drop_succ_eq_drop (i := i) (Nat.add_one_lt_add_one_iff.mp h)
@[deprecated getElem_cons_drop_succ_eq_drop (since := "2024-11-05")]
abbrev get_drop_eq_drop := @getElem_cons_drop_succ_eq_drop

View File

@@ -224,7 +224,8 @@ structure Config where
-/
index : Bool := true
/--
This option does not have any effect (yet).
If `implicitDefEqProofs := true`, `simp` does not create proof terms when the
input and output terms are definitionally equal.
-/
implicitDefEqProofs : Bool := true
deriving Inhabited, BEq
@@ -249,6 +250,13 @@ def neutralConfig : Simp.Config := {
zetaDelta := false
}
structure NormCastConfig extends Simp.Config where
zeta := false
beta := false
eta := false
proj := false
iota := false
end Simp
/-- Configuration for which occurrences that match an expression should be rewritten. -/

View File

@@ -48,6 +48,10 @@ def tactic : Category := {}
For example, `let x ← e` is a `doElem`, and a `do` block consists of a list of `doElem`s. -/
def doElem : Category := {}
/-- `structInstFieldDecl` is the syntax category for value declarations for fields in structure instance notation.
For example, the `:= 1` and `| 0 => 0 | n + 1 => n` in `{ x := 1, f | 0 => 0 | n + 1 => n }` are in the `structInstFieldDecl` class. -/
def structInstFieldDecl : Category := {}
/-- `level` is a builtin syntax category for universe levels.
This is the `u` in `Sort u`: it can contain `max` and `imax`, addition with
constants, and variables. -/

View File

@@ -32,13 +32,9 @@ theorem get_map {xs : IntList} (h : f 0 = 0) : get (xs.map f) i = f (xs.get i) :
cases xs[i]? <;> simp_all
theorem get_of_length_le {xs : IntList} (h : xs.length i) : xs.get i = 0 := by
rw [get, List.get?_eq_none.mpr h]
rw [get, List.get?_eq_none_iff.mpr h]
rfl
-- theorem lt_length_of_get_nonzero {xs : IntList} (h : xs.get i ≠ 0) : i < xs.length := by
-- revert h
-- simpa using mt get_of_length_le
/-- Like `List.set`, but right-pad with zeroes as necessary first. -/
def set (xs : IntList) (i : Nat) (y : Int) : IntList :=
match xs, i with

View File

@@ -860,8 +860,8 @@ abbrev DecidablePred {α : Sort u} (r : α → Prop) :=
(a : α) Decidable (r a)
/-- A decidable relation. See `Decidable`. -/
abbrev DecidableRel {α : Sort u} (r : α α Prop) :=
(a b : α) Decidable (r a b)
abbrev DecidableRel {α : Sort u} {β : Sort v} (r : α β Prop) :=
(a : α) (b : β) Decidable (r a b)
/--
Asserts that `α` has decidable equality, that is, `a = b` is decidable

View File

@@ -959,3 +959,36 @@ syntax "println! " (interpolatedStr(term) <|> term) : term
macro_rules
| `(println! $msg:interpolatedStr) => `((IO.println (s! $msg) : IO Unit))
| `(println! $msg:term) => `((IO.println $msg : IO Unit))
/--
Marks given value and its object graph closure as multi-threaded if currently
marked single-threaded. This will make reference counter updates atomic and
thus more costly. It can still be useful to do eagerly when the value will be
shared between threads later anyway and there is available time budget to mark
it now. -/
@[extern "lean_runtime_mark_multi_threaded"]
def Runtime.markMultiThreaded (a : α) : BaseIO α := return a
/--
Marks given value and its object graph closure as persistent. This will remove
reference counter updates but prevent the closure from being deallocated until
the end of the process! It can still be useful to do eagerly when the value
will be marked persistent later anyway and there is available time budget to
mark it now or it would be unnecessarily marked multi-threaded in between.
This function is only safe to use on objects (in the full closure) which are
not used concurrently or which are already persistent.
-/
@[extern "lean_runtime_mark_persistent"]
unsafe def Runtime.markPersistent (a : α) : BaseIO α := return a
set_option linter.unusedVariables false in
/--
Discards the passed owned reference. This leads to `a` any any object reachable from it never being
freed. This can be a useful optimization for eliding deallocation time of big object graphs that are
kept alive close to the end of the process anyway (in which case calling `Runtime.markPersistent`
would be similarly costly to deallocation). It is still considered a safe operation as it cannot
lead to undefined behavior.
-/
@[extern "lean_runtime_forget"]
def Runtime.forget (a : α) : BaseIO Unit := return

View File

@@ -7,6 +7,9 @@ prelude
import Init.System.IO
import Init.Control.StateRef
set_option linter.deprecated false
namespace IO
private opaque BaseMutexImpl : NonemptyType.{0}
@@ -16,12 +19,13 @@ Mutual exclusion primitive (a lock).
If you want to guard shared state, use `Mutex α` instead.
-/
@[deprecated "Use Std.BaseMutex from Std.Sync.Mutex instead" (since := "2024-12-02")]
def BaseMutex : Type := BaseMutexImpl.type
instance : Nonempty BaseMutex := BaseMutexImpl.property
/-- Creates a new `BaseMutex`. -/
@[extern "lean_io_basemutex_new"]
@[extern "lean_io_basemutex_new", deprecated "Use Std.BaseMutex.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque BaseMutex.new : BaseIO BaseMutex
/--
@@ -30,7 +34,7 @@ Locks a `BaseMutex`. Waits until no other thread has locked the mutex.
The current thread must not have already locked the mutex.
Reentrant locking is undefined behavior (inherited from the C++ implementation).
-/
@[extern "lean_io_basemutex_lock"]
@[extern "lean_io_basemutex_lock", deprecated "Use Std.BaseMutex.lock from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque BaseMutex.lock (mutex : @& BaseMutex) : BaseIO Unit
/--
@@ -39,33 +43,35 @@ Unlocks a `BaseMutex`.
The current thread must have already locked the mutex.
Unlocking an unlocked mutex is undefined behavior (inherited from the C++ implementation).
-/
@[extern "lean_io_basemutex_unlock"]
@[extern "lean_io_basemutex_unlock", deprecated "Use Std.BaseMutex.unlock from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque BaseMutex.unlock (mutex : @& BaseMutex) : BaseIO Unit
private opaque CondvarImpl : NonemptyType.{0}
/-- Condition variable. -/
@[deprecated "Use Std.Condvar from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Condvar : Type := CondvarImpl.type
instance : Nonempty Condvar := CondvarImpl.property
/-- Creates a new condition variable. -/
@[extern "lean_io_condvar_new"]
@[extern "lean_io_condvar_new", deprecated "Use Std.Condvar.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.new : BaseIO Condvar
/-- Waits until another thread calls `notifyOne` or `notifyAll`. -/
@[extern "lean_io_condvar_wait"]
@[extern "lean_io_condvar_wait", deprecated "Use Std.Condvar.wait from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.wait (condvar : @& Condvar) (mutex : @& BaseMutex) : BaseIO Unit
/-- Wakes up a single other thread executing `wait`. -/
@[extern "lean_io_condvar_notify_one"]
@[extern "lean_io_condvar_notify_one", deprecated "Use Std.Condvar.notifyOne from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.notifyOne (condvar : @& Condvar) : BaseIO Unit
/-- Wakes up all other threads executing `wait`. -/
@[extern "lean_io_condvar_notify_all"]
@[extern "lean_io_condvar_notify_all", deprecated "Use Std.Condvar.notifyAll from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.notifyAll (condvar : @& Condvar) : BaseIO Unit
/-- Waits on the condition variable until the predicate is true. -/
@[deprecated "Use Std.Condvar.waitUntil from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Condvar.waitUntil [Monad m] [MonadLift BaseIO m]
(condvar : Condvar) (mutex : BaseMutex) (pred : m Bool) : m Unit := do
while !( pred) do
@@ -78,6 +84,7 @@ The type `Mutex α` is similar to `IO.Ref α`,
except that concurrent accesses are guarded by a mutex
instead of atomic pointer operations and busy-waiting.
-/
@[deprecated "Use Std.Mutex from Std.Sync.Mutex instead" (since := "2024-12-02")]
structure Mutex (α : Type) where private mk ::
private ref : IO.Ref α
mutex : BaseMutex
@@ -86,6 +93,7 @@ structure Mutex (α : Type) where private mk ::
instance : CoeOut (Mutex α) BaseMutex where coe := Mutex.mutex
/-- Creates a new mutex. -/
@[deprecated "Use Std.Mutex.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Mutex.new (a : α) : BaseIO (Mutex α) :=
return { ref := mkRef a, mutex := BaseMutex.new }
@@ -94,9 +102,11 @@ def Mutex.new (a : α) : BaseIO (Mutex α) :=
with outside monad `m`.
The action has access to the state `α` of the mutex (via `get` and `set`).
-/
@[deprecated "Use Std.AtomicT from Std.Sync.Mutex instead" (since := "2024-12-02")]
abbrev AtomicT := StateRefT' IO.RealWorld
/-- `mutex.atomically k` runs `k` with access to the mutex's state while locking the mutex. -/
@[deprecated "Use Std.Mutex.atomically from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Mutex.atomically [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
(mutex : Mutex α) (k : AtomicT α m β) : m β := do
try
@@ -110,6 +120,7 @@ def Mutex.atomically [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
waiting on `condvar` until `pred` returns true.
Both `k` and `pred` have access to the mutex's state.
-/
@[deprecated "Use Std.Mutex.atomicallyOnce from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Mutex.atomicallyOnce [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
(mutex : Mutex α) (condvar : Condvar)
(pred : AtomicT α m Bool) (k : AtomicT α m β) : m β :=

View File

@@ -23,5 +23,14 @@ def isEmscripten : Bool := getIsEmscripten ()
/-- The LLVM target triple of the current platform. Empty if missing at Lean compile time. -/
def target : String := getTarget ()
theorem numBits_pos : 0 < numBits := by
cases numBits_eq <;> next h => simp [h]
theorem le_numBits : 32 numBits := by
cases numBits_eq <;> next h => simp [h]
theorem numBits_le : numBits 64 := by
cases numBits_eq <;> next h => simp [h]
end Platform
end System

View File

@@ -1309,7 +1309,7 @@ macro "bv_omega" : tactic => `(tactic| (try simp only [bv_toNat] at *) <;> omega
syntax (name := acNf0) "ac_nf0" (location)? : tactic
/-- Implementation of `norm_cast` (the full `norm_cast` calls `trivial` afterwards). -/
syntax (name := normCast0) "norm_cast0" (location)? : tactic
syntax (name := normCast0) "norm_cast0" optConfig (location)? : tactic
/-- `assumption_mod_cast` is a variant of `assumption` that solves the goal
using a hypothesis. Unlike `assumption`, it first pre-processes the goal and
@@ -1318,7 +1318,7 @@ in more situations.
Concretely, it runs `norm_cast` on the goal. For each local hypothesis `h`, it also
normalizes `h` with `norm_cast` and tries to use that to close the goal. -/
macro "assumption_mod_cast" : tactic => `(tactic| norm_cast0 at * <;> assumption)
macro "assumption_mod_cast" cfg:optConfig : tactic => `(tactic| norm_cast0 $cfg at * <;> assumption)
/--
The `norm_cast` family of tactics is used to normalize certain coercions (*casts*) in expressions.
@@ -1355,26 +1355,9 @@ their operation, to make them more flexible about the expressions they accept
See also `push_cast`, which moves casts inwards rather than lifting them outwards.
-/
macro "norm_cast" loc:(location)? : tactic =>
`(tactic| norm_cast0 $[$loc]? <;> try trivial)
macro "norm_cast" cfg:optConfig loc:(location)? : tactic =>
`(tactic| norm_cast0 $cfg $[$loc]? <;> try trivial)
/--
`ac_nf` normalizes equalities up to application of an associative and commutative operator.
- `ac_nf` normalizes all hypotheses and the goal target of the goal.
- `ac_nf at l` normalizes at location(s) `l`, where `l` is either `*` or a
list of hypotheses in the local context. In the latter case, a turnstile `⊢` or `|-`
can also be used, to signify the target of the goal.
```
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by
ac_nf
-- goal: a + (b + (c + d)) = a + (b + (c + d))
```
-/
macro "ac_nf" loc:(location)? : tactic =>
`(tactic| ac_nf0 $[$loc]? <;> try trivial)
/--
`push_cast` rewrites the goal to move certain coercions (*casts*) inward, toward the leaf nodes.
@@ -1417,6 +1400,24 @@ syntax (name := pushCast) "push_cast" optConfig (discharger)? (&" only")?
-/
syntax (name := normCastAddElim) "norm_cast_add_elim" ident : command
/--
`ac_nf` normalizes equalities up to application of an associative and commutative operator.
- `ac_nf` normalizes all hypotheses and the goal target of the goal.
- `ac_nf at l` normalizes at location(s) `l`, where `l` is either `*` or a
list of hypotheses in the local context. In the latter case, a turnstile `⊢` or `|-`
can also be used, to signify the target of the goal.
```
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by
ac_nf
-- goal: a + (b + (c + d)) = a + (b + (c + d))
```
-/
macro "ac_nf" loc:(location)? : tactic =>
`(tactic| ac_nf0 $[$loc]? <;> try trivial)
/--
* `symm` applies to a goal whose target has the form `t ~ u` where `~` is a symmetric relation,
that is, a relation which has a symmetry lemma tagged with the attribute [symm].

View File

@@ -79,21 +79,3 @@ def withPtrEq {α : Type u} (a b : α) (k : Unit → Bool) (h : a = b → k () =
@[implemented_by withPtrAddrUnsafe]
def withPtrAddr {α : Type u} {β : Type v} (a : α) (k : USize β) (h : u₁ u₂, k u₁ = k u₂) : β := k 0
/--
Marks given value and its object graph closure as multi-threaded if currently
marked single-threaded. This will make reference counter updates atomic and
thus more costly. It can still be useful to do eagerly when the value will be
shared between threads later anyway and there is available time budget to mark
it now. -/
@[extern "lean_runtime_mark_multi_threaded"]
def Runtime.markMultiThreaded (a : α) : α := a
/--
Marks given value and its object graph closure as persistent. This will remove
reference counter updates but prevent the closure from being deallocated until
the end of the process! It can still be useful to do eagerly when the value
will be marked persistent later anyway and there is available time budget to
mark it now or it would be unnecessarily marked multi-threaded in between. -/
@[extern "lean_runtime_mark_persistent"]
def Runtime.markPersistent (a : α) : α := a

View File

@@ -593,6 +593,14 @@ where
let minor visit minor
mkOverApplication minor args arity
visitHEqRec (e : Expr) : M Arg :=
let arity := 7
etaIfUnderApplied e arity do
let args := e.getAppArgs
let minor := if e.isAppOf ``HEq.rec || e.isAppOf ``HEq.ndrec then args[3]! else args[6]!
let minor visit minor
mkOverApplication minor args arity
visitFalseRec (e : Expr) : M Arg :=
let arity := 2
etaIfUnderApplied e arity do
@@ -669,6 +677,8 @@ where
visitCtor 3 e
else if declName == ``Eq.casesOn || declName == ``Eq.rec || declName == ``Eq.ndrec then
visitEqRec e
else if declName == ``HEq.casesOn || declName == ``HEq.rec || declName == ``HEq.ndrec then
visitHEqRec e
else if declName == ``And.rec || declName == ``Iff.rec then
visitAndIffRecCore e (minorPos := 3)
else if declName == ``And.casesOn || declName == ``Iff.casesOn then

View File

@@ -31,6 +31,19 @@ register_builtin_option maxHeartbeats : Nat := {
descr := "maximum amount of heartbeats per command. A heartbeat is number of (small) memory allocations (in thousands), 0 means no limit"
}
register_builtin_option Elab.async : Bool := {
defValue := false
descr := "perform elaboration using multiple threads where possible\
\n\
\nThis option defaults to `false` but (when not explicitly set) is overridden to `true` in \
`Lean.Language.Lean.process` as used by the cmdline driver and language server. \
Metaprogramming users driving elaboration directly via e.g. \
`Lean.Elab.Command.elabCommandTopLevel` can opt into asynchronous elaboration by setting \
this option but then are responsible for processing messages and other data not only in the \
resulting command state but also from async tasks in `Lean.Command.Context.snap?` and \
`Lean.Command.State.snapshotTasks`."
}
/--
If the `diagnostics` option is not already set, gives a message explaining this option.
Begins with a `\n`, so an error message can look like `m!"some error occurred{useDiagnosticMsg}"`.
@@ -351,9 +364,7 @@ Returns the current log and then resets its messages while adjusting `MessageLog
for incremental reporting during elaboration of a single command.
-/
def getAndEmptyMessageLog : CoreM MessageLog :=
modifyGet fun s => (s.messages, { s with
messages.unreported := {}
messages.hadErrors := s.messages.hasErrors })
modifyGet fun s => (s.messages, { s with messages := s.messages.markAllReported })
instance : MonadLog CoreM where
getRef := getRef
@@ -412,7 +423,7 @@ def wrapAsyncAsSnapshot (act : Unit → CoreM Unit) (desc : String := by exact d
IO.FS.withIsolatedStreams (isolateStderr := stderrAsMessages.get ( getOptions)) do
let tid IO.getTID
-- reset trace state and message log so as not to report them twice
modify ({ · with messages := {}, traceState := { tid } })
modify fun st => { st with messages := st.messages.markAllReported, traceState := { tid } }
try
withTraceNode `Elab.async (fun _ => return desc) do
act ()

View File

@@ -35,7 +35,7 @@ theorem RArray.get_ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) (i : Fin n) :
go 0 n h (Nat.le_refl _) (Nat.zero_le _) i.2
where
go lb ub h1 h2 (h3 : lb i.val) (h3 : i.val < ub) : (ofFn.go f lb ub h1 h2).get i = f i := by
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (f := f) (n := n)
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (n := n)
case case1 =>
simp [ofFn.go, RArray.get_eq_getImpl, RArray.getImpl]
congr
@@ -53,7 +53,7 @@ theorem RArray.size_ofFn {n : Nat} (f : Fin n → α) (h : 0 < n) :
go 0 n h (Nat.le_refl _)
where
go lb ub h1 h2 : (ofFn.go f lb ub h1 h2).size = ub - lb := by
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (f := f) (n := n)
induction lb, ub, h1, h2 using RArray.ofFn.go.induct (n := n)
case case1 => simp [ofFn.go, size]; omega
case case2 ih1 ih2 hiu => rw [ofFn.go]; simp [size, *]; omega

View File

@@ -488,6 +488,9 @@ where
let mut lines : Array MessageData := #[]
let decls getOptionDecls
for (name, val) in opts do
-- `#guard_msgs` sets this option internally, we don't want it to end up in its output
if name == `Elab.async then
continue
let (isSet, isUnknown) :=
match decls.find? name with
| some decl => (decl.defValue != val, false)

View File

@@ -101,7 +101,7 @@ structure Context where
(mutual) defs and contained tactics, in which case the `DynamicSnapshot` is a
`HeadersParsedSnapshot`.
Definitely resolved in `Language.Lean.process.doElab`.
Definitely resolved in `Lean.Elab.Command.elabCommandTopLevel`.
Invariant: if the bundle's `old?` is set, the context and state at the beginning of current and
old elaboration are identical.
@@ -287,7 +287,9 @@ def runLinters (stx : Syntax) : CommandElabM Unit := do
| Exception.internal _ _ =>
logException ex
finally
modify fun s => { savedState with messages := s.messages }
-- TODO: it would be good to preserve even more state (#4363) but preserving info
-- trees currently breaks from linters adding context-less info nodes
modify fun s => { savedState with messages := s.messages, traceState := s.traceState }
/--
Catches and logs exceptions occurring in `x`. Unlike `try catch` in `CommandElabM`, this function
@@ -311,7 +313,7 @@ def wrapAsyncAsSnapshot (act : Unit → CommandElabM Unit)
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get ( getOptions)) do
let tid IO.getTID
-- reset trace state and message log so as not to report them twice
modify ({ · with messages := {}, traceState := { tid } })
modify fun st => { st with messages := st.messages.markAllReported, traceState := { tid } }
try
withTraceNode `Elab.async (fun _ => return desc) do
act ()
@@ -344,6 +346,17 @@ def wrapAsyncAsSnapshot (act : Unit → CommandElabM Unit)
def logSnapshotTask (task : Language.SnapshotTask Language.SnapshotTree) : CommandElabM Unit :=
modify fun s => { s with snapshotTasks := s.snapshotTasks.push task }
def runLintersAsync (stx : Syntax) : CommandElabM Unit := do
if !Elab.async.get ( getOptions) then
withoutModifyingEnv do
runLinters stx
return
-- We only start one task for all linters for now as most linters are fast and we simply want
-- to unblock elaboration of the next command
let lintAct wrapAsyncAsSnapshot fun _ => runLinters stx
logSnapshotTask { range? := none, task := ( BaseIO.asTask lintAct) }
protected def getCurrMacroScope : CommandElabM Nat := do pure ( read).currMacroScope
protected def getMainModule : CommandElabM Name := do pure ( getEnv).mainModule
@@ -547,8 +560,13 @@ def elabCommandTopLevel (stx : Syntax) : CommandElabM Unit := withRef stx do pro
-- rather than engineer a general solution.
unless (stx.find? (·.isOfKind ``Lean.guardMsgsCmd)).isSome do
withLogging do
runLinters stx
runLintersAsync stx
finally
-- Make sure `snap?` is definitely resolved; we do not use it for reporting as `#guard_msgs` may
-- be the caller of this function and add new messages and info trees
if let some snap := ( read).snap? then
snap.new.resolve default
-- note the order: first process current messages & info trees, then add back old messages & trees,
-- then convert new traces to messages
let mut msgs := ( get).messages

View File

@@ -169,6 +169,8 @@ def runFrontend
IO.FS.writeFile out <| Json.compress <| toJson profile
let hasErrors := snaps.getAll.any (·.diagnostics.msgLog.hasErrors)
-- no point in freeing the snapshot graph and all referenced data this close to process exit
Runtime.forget snaps
pure (cmdState.env, !hasErrors)

View File

@@ -140,9 +140,15 @@ def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List S
|>.trim |> removeTrailingWhitespaceMarker
let (whitespace, ordering, specFn) parseGuardMsgsSpec spec?
let initMsgs modifyGet fun st => (st.messages, { st with messages := {} })
-- The `#guard_msgs` command is special-cased in `elabCommandTopLevel` to ensure linters only run once.
elabCommandTopLevel cmd
let msgs := ( get).messages
-- do not forward snapshot as we don't want messages assigned to it to leak outside
withReader ({ · with snap? := none }) do
-- The `#guard_msgs` command is special-cased in `elabCommandTopLevel` to ensure linters only run once.
elabCommandTopLevel cmd
-- collect sync and async messages
let msgs := ( get).messages ++
( get).snapshotTasks.foldl (· ++ ·.get.getAll.foldl (· ++ ·.diagnostics.msgLog) {}) {}
-- clear async messages as we don't want them to leak outside
modify ({ · with snapshotTasks := #[] })
let mut toCheck : MessageLog := .empty
let mut toPassthrough : MessageLog := .empty
for msg in msgs.toList do

View File

@@ -282,52 +282,36 @@ private partial def withFunLocalDecls {α} (headers : Array DefViewElabHeader) (
k fvars
loop 0 #[]
private def expandWhereStructInst : Macro
| whereStx@`(Parser.Command.whereStructInst|where%$whereTk $[$decls:letDecl];* $[$whereDecls?:whereDecls]?) => do
let letIdDecls decls.mapM fun stx => match stx with
| `(letDecl|$_decl:letPatDecl) => Macro.throwErrorAt stx "patterns are not allowed here"
| `(letDecl|$decl:letEqnsDecl) => expandLetEqnsDecl decl (useExplicit := false)
| `(letDecl|$decl:letIdDecl) => pure decl
| _ => Macro.throwUnsupported
let structInstFields letIdDecls.mapM fun
| stx@`(letIdDecl|$id:ident $binders* $[: $ty?]? := $val) => withRef stx do
let mut val := val
if let some ty := ty? then
val `(($val : $ty))
-- HACK: this produces invalid syntax, but the fun elaborator supports letIdBinders as well
have : Coe (TSyntax ``letIdBinder) (TSyntax ``funBinder) := (·)
val if binders.size > 0 then `(fun $binders* => $val) else pure val
`(structInstField|$id:ident := $val)
| stx@`(letIdDecl|_ $_* $[: $_]? := $_) => Macro.throwErrorAt stx "'_' is not allowed here"
| _ => Macro.throwUnsupported
private def expandWhereStructInst : Macro := fun whereStx => do
let `(Parser.Command.whereStructInst| where%$whereTk $[$structInstFields];* $[$whereDecls?:whereDecls]?) := whereStx
| Macro.throwUnsupported
let startOfStructureTkInfo : SourceInfo :=
match whereTk.getPos? with
| some pos => .synthetic pos pos.byteIdx + 1 true
| none => .none
-- Position the closing `}` at the end of the trailing whitespace of `where $[$_:letDecl];*`.
-- We need an accurate range of the generated structure instance in the generated `TermInfo`
-- so that we can determine the expected type in structure field completion.
let structureStxTailInfo :=
whereStx[1].getTailInfo?
<|> whereStx[0].getTailInfo?
let endOfStructureTkInfo : SourceInfo :=
match structureStxTailInfo with
| some (SourceInfo.original _ _ trailing _) =>
let tokenPos := trailing.str.prev trailing.stopPos
let tokenEndPos := trailing.stopPos
.synthetic tokenPos tokenEndPos true
| _ => .none
let startOfStructureTkInfo : SourceInfo :=
match whereTk.getPos? with
| some pos => .synthetic pos pos.byteIdx + 1 true
| none => .none
-- Position the closing `}` at the end of the trailing whitespace of `where $[$_:letDecl];*`.
-- We need an accurate range of the generated structure instance in the generated `TermInfo`
-- so that we can determine the expected type in structure field completion.
let structureStxTailInfo :=
whereStx[1].getTailInfo?
<|> whereStx[0].getTailInfo?
let endOfStructureTkInfo : SourceInfo :=
match structureStxTailInfo with
| some (SourceInfo.original _ _ trailing _) =>
let tokenPos := trailing.str.prev trailing.stopPos
let tokenEndPos := trailing.stopPos
.synthetic tokenPos tokenEndPos true
| _ => .none
let body `(structInst| { $structInstFields,* })
let body := body.raw.setInfo <|
match startOfStructureTkInfo.getPos?, endOfStructureTkInfo.getTailPos? with
| some startPos, some endPos => .synthetic startPos endPos true
| _, _ => .none
match whereDecls? with
| some whereDecls => expandWhereDecls whereDecls body
| none => return body
| _ => Macro.throwUnsupported
let body `(structInst| { $structInstFields,* })
let body := body.raw.setInfo <|
match startOfStructureTkInfo.getPos?, endOfStructureTkInfo.getTailPos? with
| some startPos, some endPos => .synthetic startPos endPos true
| _, _ => .none
match whereDecls? with
| some whereDecls => expandWhereDecls whereDecls body
| none => return body
/-
Recall that
@@ -415,6 +399,20 @@ register_builtin_option linter.unusedSectionVars : Bool := {
descr := "enable the 'unused section variables in theorem body' linter"
}
register_builtin_option debug.proofAsSorry : Bool := {
defValue := false
group := "debug"
descr := "replace the bodies (proofs) of theorems with `sorry`"
}
/-- Returns true if `k` is a theorem, option `debug.proofAsSorry` is set to true, and the environment contains the axiom `sorryAx`. -/
private def useProofAsSorry (k : DefKind) : CoreM Bool := do
if k.isTheorem then
if debug.proofAsSorry.get ( getOptions) then
if ( getEnv).contains ``sorryAx then
return true
return false
private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr) (sc : Command.Scope) : TermElabM (Array Expr) :=
headers.mapM fun header => do
let mut reusableResult? := none
@@ -436,7 +434,9 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
for h : i in [0:header.binderIds.size] do
-- skip auto-bound prefix in `xs`
addLocalVarInfo header.binderIds[i] xs[header.numParams - header.binderIds.size + i]!
let val withReader ({ · with tacSnap? := header.tacSnap? }) do
let val if ( useProofAsSorry header.kind) then
mkSorry type false
else withReader ({ · with tacSnap? := header.tacSnap? }) do
-- Store instantiated body in info tree for the benefit of the unused variables linter
-- and other metaprograms that may want to inspect it without paying for the instantiation
-- again

View File

@@ -265,7 +265,7 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
| `(Parser.Term.structInstField| $lval:structInstLVal := $val) => do
let newVal collect val
`(Parser.Term.structInstField| $lval:structInstLVal := $newVal)
| _ => throwInvalidPattern -- `structInstFieldAbbrev` should be expanded at this point
| _ => throwInvalidPattern -- `structInstField` should be expanded at this point
`({ $[$srcs?,* with]? $fields,* $[..%$ell?]? $[: $ty?]? })
| _ => throwInvalidPattern

View File

@@ -31,13 +31,32 @@ open Meta
open TSyntax.Compat
/-!
Recall that structure instances are of the form:
```
"{" >> optional (atomic (sepBy1 termParser ", " >> " with "))
>> manyIndent (group ((structInstFieldAbbrev <|> structInstField) >> optional ", "))
Recall that structure instances are (after removing parsing and pretty printing hints):
```lean
def structInst := leading_parser
"{ " >> optional (sepBy1 termParser ", " >> " with ")
>> structInstFields (sepByIndent structInstField ", " (allowTrailingSep := true))
>> optEllipsis
>> optional (" : " >> termParser)
>> " }"
>> optional (" : " >> termParser) >> " }"
def structInstField := leading_parser
structInstLVal >> optional (many structInstFieldBinder >> optType >> structInstFieldDecl)
@[builtin_structInstFieldDecl_parser]
def structInstFieldDef := leading_parser
" := " >> termParser
@[builtin_structInstFieldDecl_parser]
def structInstFieldEqns := leading_parser
matchAlts
def structInstWhereBody := leading_parser
structInstFields (sepByIndent structInstField "; " (allowTrailingSep := true))
@[builtin_structInstFieldDecl_parser]
def structInstFieldWhere := leading_parser
"where" >> structInstWhereBody
```
-/
@@ -54,22 +73,57 @@ Structure instance notation makes use of the expected type.
let stxNew := stx.setArg 4 mkNullNode
`(($stxNew : $expected))
def mkStructInstField (lval : TSyntax ``Parser.Term.structInstLVal) (binders : TSyntaxArray ``Parser.Term.structInstFieldBinder)
(type? : Option Term) (val : Term) : MacroM (TSyntax ``Parser.Term.structInstField) := do
let mut val := val
if let some type := type? then
val `(($val : $type))
if !binders.isEmpty then
-- HACK: this produces invalid syntax, but the fun elaborator supports structInstFieldBinder as well
val `(fun $binders* => $val)
`(Parser.Term.structInstField| $lval := $val)
/--
Expands field abbreviation notation.
Example: `{ x, y := 0 }` expands to `{ x := x, y := 0 }`.
Takes an arbitrary `structInstField` and expands it to be a `structInstFieldDef` without any binders or type ascription.
-/
@[builtin_macro Lean.Parser.Term.structInst] def expandStructInstFieldAbbrev : Macro
| `({ $[$srcs,* with]? $fields,* $[..%$ell]? $[: $ty]? }) =>
if fields.getElems.raw.any (·.getKind == ``Lean.Parser.Term.structInstFieldAbbrev) then do
let fieldsNew fields.getElems.mapM fun
| `(Parser.Term.structInstFieldAbbrev| $id:ident) =>
`(Parser.Term.structInstField| $id:ident := $id:ident)
| field => return field
`({ $[$srcs,* with]? $fieldsNew,* $[..%$ell]? $[: $ty]? })
else
Macro.throwUnsupported
private def expandStructInstField (stx : Syntax) : MacroM (Option Syntax) := withRef stx do
match stx with
| `(Parser.Term.structInstField| $_:structInstLVal := $_) =>
-- Already expanded.
return none
| `(Parser.Term.structInstField| $lval:structInstLVal $[$binders]* $[: $ty?]? $decl:structInstFieldDecl) =>
match decl with
| `(Parser.Term.structInstFieldDef| := $val) =>
mkStructInstField lval binders ty? val
| `(Parser.Term.structInstFieldEqns| $alts:matchAlts) =>
let val expandMatchAltsIntoMatch stx alts (useExplicit := false)
mkStructInstField lval binders ty? val
| _ => Macro.throwUnsupported
| `(Parser.Term.structInstField| $lval:structInstLVal) =>
-- Abbreviation
match lval with
| `(Parser.Term.structInstLVal| $id:ident) =>
mkStructInstField lval #[] none id
| _ =>
Macro.throwErrorAt lval "unsupported structure instance field abbreviation, expecting identifier"
| _ => Macro.throwUnsupported
/--
Expands fields.
* Abbrevations. Example: `{ x }` expands to `{ x := x }`.
* Equations. Example: `{ f | 0 => 0 | n + 1 => n }` expands to `{ f := fun x => match x with | 0 => 0 | n + 1 => n }`.
* Binders and types. Example: `{ f n : Nat := n + 1 }` expands to `{ f := fun n => (n + 1 : Nat) }`.
-/
@[builtin_macro Lean.Parser.Term.structInst] def expandStructInstFields : Macro | stx => do
let structInstFields := stx[2]
let fields := structInstFields[0].getSepArgs
let fields? fields.mapM expandStructInstField
if fields?.all (·.isNone) then
Macro.throwUnsupported
let fields := fields?.zipWith fields Option.getD
let structInstFields := structInstFields.setArg 0 <| Syntax.mkSep fields (mkAtomFrom stx ", ")
return stx.setArg 2 structInstFields
/--
If `stx` is of the form `{ s₁, ..., sₙ with ... }` and `sᵢ` is not a local variable,
expands into `let __src := sᵢ; { ..., __src, ... with ... }`.
@@ -187,12 +241,13 @@ def structInstArrayRef := leading_parser "[" >> termParser >>"]"
-/
private def isModifyOp? (stx : Syntax) : TermElabM (Option Syntax) := do
let s? stx[2][0].getSepArgs.foldlM (init := none) fun s? arg => do
/- arg is of the form `structInstFieldAbbrev <|> structInstField` -/
if arg.getKind == ``Lean.Parser.Term.structInstField then
/- Remark: the syntax for `structInstField` is
/- arg is of the form `structInstField`. It should be macro expanded at this point, but we make sure it's the case. -/
if arg[1][2].getKind == ``Lean.Parser.Term.structInstFieldDef then
/- Remark: the syntax for `structInstField` after macro expansion is
```
def structInstLVal := leading_parser (ident <|> numLit <|> structInstArrayRef) >> many (group ("." >> (ident <|> numLit)) <|> structInstArrayRef)
def structInstField := leading_parser structInstLVal >> " := " >> termParser
def structInstFieldDef := leading_parser
structInstLVal >> group (null >> null >> group (" := " >> termParser))
```
-/
let lval := arg[0]
@@ -235,7 +290,7 @@ private def elabModifyOp (stx modifyOp : Syntax) (sources : Array ExplicitSource
withMacroExpansion stx stxNew <| elabTerm stxNew expectedType?
let rest := modifyOp[0][1]
if rest.isNone then
cont modifyOp[2]
cont modifyOp[1][2][1]
else
let s `(s)
let valFirst := rest[0]
@@ -388,7 +443,7 @@ Converts a `FieldLHS` back into syntax. This assumes the `ref` fields have the c
Recall that `structInstField` elements have the form
```lean
def structInstField := leading_parser structInstLVal >> " := " >> termParser
def structInstField := leading_parser structInstLVal >> group (null >> null >> group (" := " >> termParser))
def structInstLVal := leading_parser (ident <|> numLit <|> structInstArrayRef) >> many (("." >> (ident <|> numLit)) <|> structInstArrayRef)
def structInstArrayRef := leading_parser "[" >> termParser >>"]"
```
@@ -412,9 +467,9 @@ Converts a `Field StructInstView` back into syntax. Used to construct synthetic
private def Field.toSyntax : Field Syntax
| field =>
let stx := field.ref
let stx := stx.setArg 2 field.val.toSyntax
let stx := stx.setArg 1 <| stx[1].setArg 2 <| stx[1][2].setArg 1 field.val.toSyntax
match field.lhs with
| first::rest => stx.setArg 0 <| mkNullNode #[first.toSyntax true, mkNullNode <| rest.toArray.map (FieldLHS.toSyntax false) ]
| first::rest => stx.setArg 0 <| mkNode ``Parser.Term.structInstLVal #[first.toSyntax true, mkNullNode <| rest.toArray.map (FieldLHS.toSyntax false) ]
| _ => unreachable!
/-- Creates a view of a field left-hand side. -/
@@ -428,7 +483,7 @@ private def toFieldLHS (stx : Syntax) : MacroM FieldLHS :=
return FieldLHS.fieldName stx stx.getId.eraseMacroScopes
else match stx.isFieldIdx? with
| some idx => return FieldLHS.fieldIndex stx idx
| none => Macro.throwError "unexpected structure syntax"
| none => Macro.throwErrorAt stx "unexpected structure syntax"
/--
Creates a structure instance view from structure instance notation
@@ -436,21 +491,21 @@ and the computed structure name (from `Lean.Elab.Term.StructInst.getStructName`)
and structure source view (from `Lean.Elab.Term.StructInst.getStructSources`).
-/
private def mkStructView (stx : Syntax) (structName : Name) (sources : SourcesView) : MacroM StructInstView := do
/- Recall that `stx` is of the form
```
leading_parser "{" >> optional (atomic (sepBy1 termParser ", " >> " with "))
>> structInstFields (sepByIndent (structInstFieldAbbrev <|> structInstField) ...)
>> optional ".."
>> optional (" : " >> termParser)
>> " }"
```
This method assumes that `structInstFieldAbbrev` had already been expanded.
/-
Recall that `stx` is of the form
```
leading_parser "{" >> optional (atomic (sepBy1 termParser ", " >> " with "))
>> structInstFields (sepByIndent structInstField ...)
>> optional ".."
>> optional (" : " >> termParser)
>> " }"
```
This method assumes that `structInstField` had already been expanded by the macro `expandStructInstFields`.
-/
let fields stx[2][0].getSepArgs.toList.mapM fun fieldStx => do
let val := fieldStx[2]
let first toFieldLHS fieldStx[0][0]
let rest fieldStx[0][1].getArgs.toList.mapM toFieldLHS
let `(Parser.Term.structInstField| $lval:structInstLVal := $val) := fieldStx | Macro.throwUnsupported
let first toFieldLHS lval.raw[0]
let rest lval.raw[1].getArgs.toList.mapM toFieldLHS
return { ref := fieldStx, lhs := first :: rest, val := FieldVal.term val : Field }
return { ref := stx, structName, params := #[], fields, sources }
@@ -596,7 +651,7 @@ mutual
let updateSource (structStx : Syntax) : TermElabM Syntax := do
let sourcesNew s.sources.explicit.filterMapM fun source => mkProjStx? source.stx source.structName fieldName
let explicitSourceStx := if sourcesNew.isEmpty then mkNullNode else mkSourcesWithSyntax sourcesNew
let implicitSourceStx := s.sources.implicit.getD mkNullNode
let implicitSourceStx := s.sources.implicit.getD (mkNode ``Parser.Term.optEllipsis #[mkNullNode])
return (structStx.setArg 1 explicitSourceStx).setArg 3 implicitSourceStx
let valStx := s.ref -- construct substructure syntax using s.ref as template
let valStx := valStx.setArg 4 mkNullNode -- erase optional expected type

View File

@@ -8,6 +8,7 @@ import Std.Data.HashMap
import Std.Tactic.BVDecide.Bitblast.BVExpr.Basic
import Lean.Meta.AppBuilder
import Lean.ToExpr
import Lean.Data.RArray
/-!
This module contains the implementation of the reflection monad, used by all other components of this
@@ -138,9 +139,11 @@ structure State where
-/
atoms : Std.HashMap Expr Atom := {}
/--
A cache for `atomsAssignment`.
A cache for `atomsAssignment`. We maintain the invariant that this value is only used if
`atoms` is non empty. The reason for not using an `Option` is that it would pollute a lot of code
with error handling that is never hit as this invariant is enforced before all of this code.
-/
atomsAssignmentCache : Expr := mkConst ``List.nil [.zero]
atomsAssignmentCache : Expr := mkConst `illegal
/--
The reflection monad, used to track `BitVec` variables that we see as we traverse the context.
@@ -157,9 +160,9 @@ structure ReifiedBVExpr where
-/
bvExpr : BVExpr width
/--
A proof that `bvExpr.eval atomsAssignment = originalBVExpr`.
A proof that `bvExpr.eval atomsAssignment = originalBVExpr`, none if it holds by `rfl`.
-/
evalsAtAtoms : M Expr
evalsAtAtoms : M (Option Expr)
/--
A cache for `toExpr bvExpr`.
-/
@@ -174,9 +177,9 @@ structure ReifiedBVPred where
-/
bvPred : BVPred
/--
A proof that `bvPred.eval atomsAssignment = originalBVPredExpr`.
A proof that `bvPred.eval atomsAssignment = originalBVPredExpr`, none if it holds by `rfl`.
-/
evalsAtAtoms : M Expr
evalsAtAtoms : M (Option Expr)
/--
A cache for `toExpr bvPred`
-/
@@ -191,9 +194,9 @@ structure ReifiedBVLogical where
-/
bvExpr : BVLogicalExpr
/--
A proof that `bvExpr.eval atomsAssignment = originalBVLogicalExpr`.
A proof that `bvExpr.eval atomsAssignment = originalBVLogicalExpr`, none if it holds by `rfl`.
-/
evalsAtAtoms : M Expr
evalsAtAtoms : M (Option Expr)
/--
A cache for `toExpr bvExpr`
-/
@@ -228,9 +231,9 @@ def run (m : M α) : MetaM α :=
/--
Retrieve the atoms as pairs of their width and expression.
-/
def atoms : M (List (Nat × Expr)) := do
def atoms : M (Array (Nat × Expr)) := do
let sortedAtoms := ( getThe State).atoms.toArray.qsort (·.2.atomNumber < ·.2.atomNumber)
return sortedAtoms.map (fun (expr, {width, ..}) => (width, expr)) |>.toList
return sortedAtoms.map (fun (expr, {width, ..}) => (width, expr))
/--
Retrieve a `BitVec.Assignment` representing the atoms we found so far.
@@ -257,11 +260,37 @@ def lookup (e : Expr) (width : Nat) (synthetic : Bool) : M Nat := do
where
updateAtomsAssignment : M Unit := do
let as atoms
let packed :=
as.map (fun (width, expr) => mkApp2 (mkConst ``BVExpr.PackedBitVec.mk) (toExpr width) expr)
let packedType := mkConst ``BVExpr.PackedBitVec
let newAtomsAssignment mkListLit packedType packed
modify fun s => { s with atomsAssignmentCache := newAtomsAssignment }
if h : 0 < as.size then
let ras := Lean.RArray.ofArray as h
let packedType := mkConst ``BVExpr.PackedBitVec
let pack := fun (width, expr) => mkApp2 (mkConst ``BVExpr.PackedBitVec.mk) (toExpr width) expr
let newAtomsAssignment := ras.toExpr packedType pack
modify fun s => { s with atomsAssignmentCache := newAtomsAssignment }
else
throwError "updateAtomsAssignment should only be called when there is an atom"
@[specialize]
def simplifyBinaryProof' (mkFRefl : Expr Expr) (fst : Expr) (fproof : Option Expr)
(mkSRefl : Expr Expr) (snd : Expr) (sproof : Option Expr) : Option (Expr × Expr) := do
match fproof, sproof with
| some fproof, some sproof => some (fproof, sproof)
| some fproof, none => some (fproof, mkSRefl snd)
| none, some sproof => some (mkFRefl fst, sproof)
| none, none => none
@[specialize]
def simplifyBinaryProof (mkRefl : Expr Expr) (fst : Expr) (fproof : Option Expr) (snd : Expr)
(sproof : Option Expr) : Option (Expr × Expr) := do
simplifyBinaryProof' mkRefl fst fproof mkRefl snd sproof
@[specialize]
def simplifyTernaryProof (mkRefl : Expr Expr) (fst : Expr) (fproof : Option Expr) (snd : Expr)
(sproof : Option Expr) (thd : Expr) (tproof : Option Expr) : Option (Expr × Expr × Expr) := do
match fproof, simplifyBinaryProof mkRefl snd sproof thd tproof with
| some fproof, some stproof => some (fproof, stproof)
| some fproof, none => some (fproof, mkRefl snd, mkRefl thd)
| none, some stproof => some (mkRefl fst, stproof)
| none, none => none
end M

View File

@@ -37,9 +37,8 @@ Register `e` as an atom of `width` that might potentially be `synthetic`.
def mkAtom (e : Expr) (width : Nat) (synthetic : Bool) : M ReifiedBVExpr := do
let ident M.lookup e width synthetic
let expr := mkApp2 (mkConst ``BVExpr.var) (toExpr width) (toExpr ident)
let proof := do
let evalExpr mkEvalExpr width expr
return mkBVRefl width evalExpr
-- This is safe because this proof always holds definitionally.
let proof := pure none
return width, .var ident, proof, expr
/--
@@ -70,9 +69,8 @@ Build a reified version of the constant `val`.
def mkBVConst (val : BitVec w) : M ReifiedBVExpr := do
let bvExpr : BVExpr w := .const val
let expr := mkApp2 (mkConst ``BVExpr.const) (toExpr w) (toExpr val)
let proof := do
let evalExpr ReifiedBVExpr.mkEvalExpr w expr
return ReifiedBVExpr.mkBVRefl w evalExpr
-- This is safe because this proof always holds definitionally.
let proof := pure none
return w, bvExpr, proof, expr
end ReifiedBVExpr

View File

@@ -49,7 +49,8 @@ Build a reified version of the constant `val`.
def mkBoolConst (val : Bool) : M ReifiedBVLogical := do
let boolExpr := .const val
let expr := mkApp2 (mkConst ``BoolExpr.const) (mkConst ``BVPred) (toExpr val)
let proof := pure <| ReifiedBVLogical.mkRefl (toExpr val)
-- This is safe because this proof always holds definitionally.
let proof := pure none
return boolExpr, proof, expr
/--
@@ -71,8 +72,13 @@ def mkGate (lhs rhs : ReifiedBVLogical) (lhsExpr rhsExpr : Expr) (gate : Gate) :
let proof := do
let lhsEvalExpr ReifiedBVLogical.mkEvalExpr lhs.expr
let rhsEvalExpr ReifiedBVLogical.mkEvalExpr rhs.expr
let lhsProof lhs.evalsAtAtoms
let rhsProof rhs.evalsAtAtoms
let lhsProof? lhs.evalsAtAtoms
let rhsProof? rhs.evalsAtAtoms
let some (lhsProof, rhsProof) :=
M.simplifyBinaryProof
ReifiedBVLogical.mkRefl
lhsEvalExpr lhsProof?
rhsEvalExpr rhsProof? | return none
return mkApp6
(mkConst congrThm)
lhsExpr rhsExpr
@@ -95,8 +101,9 @@ def mkNot (sub : ReifiedBVLogical) (subExpr : Expr) : M ReifiedBVLogical := do
let boolExpr := .not sub.bvExpr
let expr := mkApp2 (mkConst ``BoolExpr.not) (mkConst ``BVPred) sub.expr
let proof := do
-- This is safe as `not_congr` holds definitionally if the arguments are defeq.
let some subProof sub.evalsAtAtoms | return none
let subEvalExpr ReifiedBVLogical.mkEvalExpr sub.expr
let subProof sub.evalsAtAtoms
return mkApp3 (mkConst ``Std.Tactic.BVDecide.Reflect.Bool.not_congr) subExpr subEvalExpr subProof
return boolExpr, proof, expr
@@ -119,9 +126,15 @@ def mkIte (discr lhs rhs : ReifiedBVLogical) (discrExpr lhsExpr rhsExpr : Expr)
let discrEvalExpr ReifiedBVLogical.mkEvalExpr discr.expr
let lhsEvalExpr ReifiedBVLogical.mkEvalExpr lhs.expr
let rhsEvalExpr ReifiedBVLogical.mkEvalExpr rhs.expr
let discrProof discr.evalsAtAtoms
let lhsProof lhs.evalsAtAtoms
let rhsProof rhs.evalsAtAtoms
let discrProof? discr.evalsAtAtoms
let lhsProof? lhs.evalsAtAtoms
let rhsProof? rhs.evalsAtAtoms
let some (discrProof, lhsProof, rhsProof) :=
M.simplifyTernaryProof
ReifiedBVLogical.mkRefl
discrEvalExpr discrProof?
lhsEvalExpr lhsProof?
rhsEvalExpr rhsProof? | return none
return mkApp9
(mkConst ``Std.Tactic.BVDecide.Reflect.Bool.ite_congr)
discrExpr lhsExpr rhsExpr

View File

@@ -35,8 +35,10 @@ def boolAtom (t : Expr) : M (Option ReifiedBVPred) := do
let bvExpr : BVPred := .getLsbD atom.bvExpr 0
let expr := mkApp3 (mkConst ``BVPred.getLsbD) (toExpr 1) atom.expr (toExpr 0)
let proof := do
-- ofBool_congr does not hold definitionally, if this ever becomes an issue we need to find
-- a more clever encoding for boolean atoms
let atomEval ReifiedBVExpr.mkEvalExpr atom.width atom.expr
let atomProof atom.evalsAtAtoms
let atomProof := ( atom.evalsAtAtoms).getD (ReifiedBVExpr.mkBVRefl atom.width atomEval)
return mkApp3
(mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.ofBool_congr)
t
@@ -63,9 +65,14 @@ def mkBinPred (lhs rhs : ReifiedBVExpr) (lhsExpr rhsExpr : Expr) (pred : BVBinPr
rhs.expr
let proof := do
let lhsEval ReifiedBVExpr.mkEvalExpr lhs.width lhs.expr
let lhsProof lhs.evalsAtAtoms
let rhsEval ReifiedBVExpr.mkEvalExpr rhs.width rhs.expr
let rhsProof rhs.evalsAtAtoms
let lhsProof? lhs.evalsAtAtoms
let rhsProof? rhs.evalsAtAtoms
let some (lhsProof, rhsProof) :=
M.simplifyBinaryProof
(ReifiedBVExpr.mkBVRefl lhs.width)
lhsEval lhsProof?
rhsEval rhsProof? | return none
return mkApp7
(mkConst congrThm)
(toExpr lhs.width)
@@ -90,8 +97,9 @@ def mkGetLsbD (sub : ReifiedBVExpr) (subExpr : Expr) (idx : Nat) : M ReifiedBVPr
let idxExpr := toExpr idx
let expr := mkApp3 (mkConst ``BVPred.getLsbD) (toExpr sub.width) sub.expr idxExpr
let proof := do
-- This is safe as `getLsbD_congr` holds definitionally if the arguments are defeq.
let some subProof sub.evalsAtAtoms | return none
let subEval ReifiedBVExpr.mkEvalExpr sub.width sub.expr
let subProof sub.evalsAtAtoms
return mkApp5
(mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.getLsbD_congr)
idxExpr

View File

@@ -62,7 +62,7 @@ where
let proof := do
let evalExpr ReifiedBVLogical.mkEvalExpr imp.expr
let congrProof imp.evalsAtAtoms
let congrProof := ( imp.evalsAtAtoms).getD (ReifiedBVLogical.mkRefl evalExpr)
let lemmaProof := mkApp4 (mkConst lemmaName) (toExpr lhs.width) discrExpr lhsExpr rhsExpr
let trueExpr := mkConst ``Bool.true

View File

@@ -112,7 +112,8 @@ where
inner.expr
let proof := do
let innerEval ReifiedBVExpr.mkEvalExpr inner.width inner.expr
let innerProof inner.evalsAtAtoms
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
let some innerProof inner.evalsAtAtoms | return none
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.zeroExtend_congr)
newWidthExpr
(toExpr inner.width)
@@ -132,7 +133,8 @@ where
inner.expr
let proof := do
let innerEval ReifiedBVExpr.mkEvalExpr inner.width inner.expr
let innerProof inner.evalsAtAtoms
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
let some innerProof inner.evalsAtAtoms | return none
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.signExtend_congr)
newWidthExpr
(toExpr inner.width)
@@ -150,9 +152,13 @@ where
lhs.expr rhs.expr
let proof := do
let lhsEval ReifiedBVExpr.mkEvalExpr lhs.width lhs.expr
let lhsProof lhs.evalsAtAtoms
let rhsProof rhs.evalsAtAtoms
let rhsEval ReifiedBVExpr.mkEvalExpr rhs.width rhs.expr
let lhsProof? lhs.evalsAtAtoms
let rhsProof? rhs.evalsAtAtoms
let some (lhsProof, rhsProof) :=
M.simplifyBinaryProof'
(ReifiedBVExpr.mkBVRefl lhs.width) lhsEval lhsProof?
(ReifiedBVExpr.mkBVRefl rhs.width) rhsEval rhsProof? | return none
return mkApp8 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.append_congr)
(toExpr lhs.width) (toExpr rhs.width)
lhsExpr lhsEval
@@ -169,7 +175,8 @@ where
inner.expr
let proof := do
let innerEval ReifiedBVExpr.mkEvalExpr inner.width inner.expr
let innerProof inner.evalsAtAtoms
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
let some innerProof inner.evalsAtAtoms | return none
return mkApp5 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.replicate_congr)
(toExpr n)
(toExpr inner.width)
@@ -189,7 +196,8 @@ where
inner.expr
let proof := do
let innerEval ReifiedBVExpr.mkEvalExpr inner.width inner.expr
let innerProof inner.evalsAtAtoms
-- This is safe as `zeroExtend_congr` holds definitionally if the arguments are defeq.
let some innerProof inner.evalsAtAtoms | return none
return mkApp6 (mkConst ``Std.Tactic.BVDecide.Reflect.BitVec.extract_congr)
startExpr
lenExpr
@@ -301,11 +309,16 @@ where
return none
binaryCongrProof (lhs rhs : ReifiedBVExpr) (lhsExpr rhsExpr : Expr) (congrThm : Expr) :
M Expr := do
M (Option Expr) := do
let lhsEval ReifiedBVExpr.mkEvalExpr lhs.width lhs.expr
let lhsProof lhs.evalsAtAtoms
let rhsProof rhs.evalsAtAtoms
let rhsEval ReifiedBVExpr.mkEvalExpr rhs.width rhs.expr
let lhsProof? lhs.evalsAtAtoms
let rhsProof? rhs.evalsAtAtoms
let some (lhsProof, rhsProof) :=
M.simplifyBinaryProof
(ReifiedBVExpr.mkBVRefl lhs.width)
lhsEval lhsProof?
rhsEval rhsProof? | return none
return mkApp6 congrThm lhsExpr rhsExpr lhsEval rhsEval lhsProof rhsProof
unaryReflection (innerExpr : Expr) (op : BVUnOp) (congrThm : Name) :
@@ -316,9 +329,9 @@ where
let proof := unaryCongrProof inner innerExpr (mkConst congrThm)
return some inner.width, bvExpr, proof, expr
unaryCongrProof (inner : ReifiedBVExpr) (innerExpr : Expr) (congrProof : Expr) : M Expr := do
unaryCongrProof (inner : ReifiedBVExpr) (innerExpr : Expr) (congrProof : Expr) : M (Option Expr) := do
let innerEval ReifiedBVExpr.mkEvalExpr inner.width inner.expr
let innerProof inner.evalsAtAtoms
let some innerProof inner.evalsAtAtoms | return none
return mkApp4 congrProof (toExpr inner.width) innerExpr innerEval innerProof
goBvLit (x : Expr) : M (Option ReifiedBVExpr) := do

View File

@@ -37,7 +37,7 @@ partial def of (h : Expr) : LemmaM (Option SatAtBVLogical) := do
let proof := do
let evalLogic ReifiedBVLogical.mkEvalExpr bvLogical.expr
-- this is evalLogic = lhsExpr
let evalProof bvLogical.evalsAtAtoms
let evalProof := ( bvLogical.evalsAtAtoms).getD (ReifiedBVLogical.mkRefl evalLogic)
-- h is lhsExpr = true
-- we prove evalLogic = true by evalLogic = lhsExpr = true
return ReifiedBVLogical.mkTrans evalLogic lhsExpr (mkConst ``Bool.true) evalProof h
@@ -61,13 +61,16 @@ def and (x y : SatAtBVLogical) : SatAtBVLogical where
/-- Given a proof that `x.expr.Unsat`, produce a proof of `False`. -/
def proveFalse (x : SatAtBVLogical) (h : Expr) : M Expr := do
let atomsList M.atomsAssignment
let evalExpr := mkApp2 (mkConst ``BVLogicalExpr.eval) atomsList x.expr
return mkApp3
(mkConst ``Std.Tactic.BVDecide.Reflect.Bool.false_of_eq_true_of_eq_false)
evalExpr
( x.satAtAtoms)
(.app h atomsList)
if ( get).atoms.isEmpty then
throwError "Unable to identify any relevant atoms."
else
let atomsList M.atomsAssignment
let evalExpr := mkApp2 (mkConst ``BVLogicalExpr.eval) atomsList x.expr
return mkApp3
(mkConst ``Std.Tactic.BVDecide.Reflect.Bool.false_of_eq_true_of_eq_false)
evalExpr
( x.satAtAtoms)
(.app h atomsList)
end SatAtBVLogical

View File

@@ -168,20 +168,16 @@ def numeralToCoe (e : Expr) : MetaM Simp.Result := do
let some pr proveEqUsingDown e newE | failure
return pr
declare_config_elab elabNormCastConfig NormCastConfig
/--
The core simplification routine of `normCast`.
-/
def derive (e : Expr) : MetaM Simp.Result := do
def derive (e : Expr) (config : NormCastConfig := {}) : MetaM Simp.Result := do
withTraceNode `Tactic.norm_cast (fun _ => return m!"{e}") do
let e instantiateMVars e
let config : Simp.Config := {
zeta := false
beta := false
eta := false
proj := false
iota := false
}
let config := config.toConfig
let congrTheorems Meta.getSimpCongrTheorems
let r : Simp.Result := { expr := e }
@@ -193,13 +189,13 @@ def derive (e : Expr) : MetaM Simp.Result := do
-- step 1: pre-processing of numerals
let r withTrace "pre-processing numerals" do
let post e := return Simp.Step.done ( try numeralToCoe e catch _ => pure {expr := e})
let ctx Simp.mkContext (config := config) (congrTheorems := congrTheorems)
let ctx Simp.mkContext config (congrTheorems := congrTheorems)
r.mkEqTrans ( Simp.main r.expr ctx (methods := { post })).1
-- step 2: casts are moved upwards and eliminated
let r withTrace "moving upward, splitting and eliminating" do
let post := upwardAndElim ( normCastExt.up.getTheorems)
let ctx Simp.mkContext (config := config) (congrTheorems := congrTheorems)
let ctx Simp.mkContext config (congrTheorems := congrTheorems)
r.mkEqTrans ( Simp.main r.expr ctx (methods := { post })).1
let simprocs ({} : Simp.SimprocsArray).add `reduceCtorEq false
@@ -234,32 +230,33 @@ open Term
| _ => throwUnsupportedSyntax
/-- Implementation of the `norm_cast` tactic when operating on the main goal. -/
def normCastTarget : TacticM Unit :=
def normCastTarget (cfg : NormCastConfig) : TacticM Unit :=
liftMetaTactic1 fun goal => do
let tgt instantiateMVars ( goal.getType)
let prf derive tgt
let prf derive tgt cfg
applySimpResultToTarget goal tgt prf
/-- Implementation of the `norm_cast` tactic when operating on a hypothesis. -/
def normCastHyp (fvarId : FVarId) : TacticM Unit :=
def normCastHyp (cfg : NormCastConfig) (fvarId : FVarId) : TacticM Unit :=
liftMetaTactic1 fun goal => do
let hyp instantiateMVars ( fvarId.getDecl).type
let prf derive hyp
let prf derive hyp cfg
return ( applySimpResultToLocalDecl goal fvarId prf false).map (·.snd)
@[builtin_tactic normCast0]
def evalNormCast0 : Tactic := fun stx => do
match stx with
| `(tactic| norm_cast0 $[$loc?]?) =>
| `(tactic| norm_cast0 $cfg $[$loc?]?) =>
let loc := if let some loc := loc? then expandLocation loc else Location.targets #[] true
let cfg elabNormCastConfig cfg
withMainContext do
match loc with
| Location.targets hyps target =>
if target then normCastTarget
( getFVarIds hyps).forM normCastHyp
if target then (normCastTarget cfg)
( getFVarIds hyps).forM (normCastHyp cfg)
| Location.wildcard =>
normCastTarget
( ( getMainGoal).getNondepPropHyps).forM normCastHyp
normCastTarget cfg
( ( getMainGoal).getNondepPropHyps).forM (normCastHyp cfg)
| _ => throwUnsupportedSyntax
@[builtin_tactic Lean.Parser.Tactic.Conv.normCast]

View File

@@ -228,6 +228,7 @@ partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr ×
| .app (.app (.app (.app (.const ``Prod.mk [u, v]) _) _) x) y =>
rewrite e (mkApp4 (.const ``Prod.snd_mk [u, v]) α x β y)
| _ => mkAtomLinearCombo e
| (``Int.negSucc, #[n]) => rewrite e (mkApp (.const ``Int.negSucc_eq []) n)
| _ => mkAtomLinearCombo e
where
/--

View File

@@ -897,13 +897,18 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
initialized constant. We have seen significant savings in `open Mathlib`
timings, where we have both a big environment and interpreted environment
extensions, from this. There is no significant extra cost to calling
`markPersistent` multiple times like this. -/
env := Runtime.markPersistent env
`markPersistent` multiple times like this.
Safety: There are no concurrent accesses to `env` at this point. -/
env unsafe Runtime.markPersistent env
env finalizePersistentExtensions env s.moduleData opts
if leakEnv then
/- Ensure the final environment including environment extension states is
marked persistent as documented. -/
env := Runtime.markPersistent env
marked persistent as documented.
Safety: There are no concurrent accesses to `env` at this point, assuming
extensions' `addImportFn`s did not spawn any unbound tasks. -/
env unsafe Runtime.markPersistent env
pure env
@[export lean_import_modules]

View File

@@ -1366,7 +1366,11 @@ See also `Lean.Expr.instantiateRange`, which instantiates with the "backwards" i
@[extern "lean_expr_instantiate_rev_range"]
opaque instantiateRevRange (e : @& Expr) (beginIdx endIdx : @& Nat) (subst : @& Array Expr) : Expr
/-- Replace free (or meta) variables `xs` with loose bound variables. -/
/-- Replace free (or meta) variables `xs` with loose bound variables,
with `xs` ordered from outermost to innermost de Bruijn index.
For example, `e := f x y` with `xs := #[x, y]` goes to `f #1 #0`,
whereas `e := f x y` with `xs := #[y, x]` goes to `f #0 #1`. -/
@[extern "lean_expr_abstract"]
opaque abstract (e : @& Expr) (xs : @& Array Expr) : Expr

View File

@@ -46,17 +46,33 @@ delete the space after private, it becomes a syntactically correct structure wit
privateaxiom! So clearly, because of uses of atomic in the grammar, an edit can affect a command
syntax tree even across multiple tokens.
Now, what we do today, and have done since Lean 3, is to always reparse the last command completely
preceding the edit location. If its syntax tree is unchanged, we preserve its data and reprocess all
following commands only, otherwise we reprocess it fully as well. This seems to have worked well so
far but it does seem a bit arbitrary given that even if it works for our current grammar, it can
certainly be extended in ways that break the assumption.
What we did in Lean 3 was to always reparse the last command completely preceding the edit location.
If its syntax tree is unchanged, we preserve its data and reprocess all following commands only,
otherwise we reprocess it fully as well. This worked well but did seem a bit arbitrary given that
even if it works for a grammar at some point, it can certainly be extended in ways that break the
assumption.
With grammar changes in Lean 4, we found that the following example indeed breaks this assumption:
```
structure Signature where
/-- a docstring -/
Sort : Type
--^ insert: "s"
```
As the keyword `Sort` is not a valid start of a structure field and the parser backtracks across the
docstring in that case, this is parsed as the complete command `structure Signature where` followed
by the partial command `/-- a docstring -/ <missing>`. If we insert an `s` after the `t`, the last
command completely preceding the edit location is the partial command containing the docstring. Thus
we need to go up two commands to ensure we reparse the `structure` command as well. This kind of
nested docstring is the only part of the grammar to our knowledge that requires going up at least
two commands; as we never backtrack across more than one docstring, going up two commands should
also be sufficient.
Finally, a more actually principled and generic solution would be to invalidate a syntax tree when
the parser has reached the edit location during parsing. If it did not, surely the edit cannot have
an effect on the syntax tree in question. Sadly such a "high-water mark" parser position does not
exist currently and likely it could at best be approximated by e.g. "furthest `tokenFn` parse". Thus
we remain at "go two commands up" at this point.
we remain at "go up two commands" at this point.
-/
/-!
@@ -231,7 +247,7 @@ structure SetupImportsResult where
/-- Performance option used by cmdline driver. -/
register_builtin_option internal.cmdlineSnapshots : Bool := {
defValue := false
descr := "mark persistent and reduce information stored in snapshots to the minimum necessary \
descr := "reduce information stored in snapshots to the minimum necessary \
for the cmdline driver: diagnostics per command and final full snapshot"
}
@@ -340,11 +356,12 @@ where
if let some old := old? then
if let some oldSuccess := old.result? then
if let some (some processed) old.processedResult.get? then
-- ...and the edit location is after the next command (see note [Incremental Parsing])...
-- ...and the edit is after the second-next command (see note [Incremental Parsing])...
if let some nextCom processed.firstCmdSnap.get? then
if ( isBeforeEditPos nextCom.parserState.pos) then
-- ...go immediately to next snapshot
return ( unchanged old old.stx oldSuccess.parserState)
if let some nextNextCom processed.firstCmdSnap.get? then
if ( isBeforeEditPos nextNextCom.parserState.pos) then
-- ...go immediately to next snapshot
return ( unchanged old old.stx oldSuccess.parserState)
withHeaderExceptions ({ · with
ictx, stx := .missing, result? := none, cancelTk? := none }) do
@@ -416,6 +433,8 @@ where
}
-- now that imports have been loaded, check options again
let opts reparseOptions setup.opts
-- default to async elaboration; see also `Elab.async` docs
let opts := Elab.async.setIfNotSet opts true
let cmdState := Elab.Command.mkState headerEnv msgLog opts
let cmdState := { cmdState with
infoState := {
@@ -437,11 +456,6 @@ where
traceState
}
let prom IO.Promise.new
-- The speedup of these `markPersistent`s is negligible but they help in making unexpected
-- `inc_ref_cold`s more visible
let parserState := Runtime.markPersistent parserState
let cmdState := Runtime.markPersistent cmdState
let ctx := Runtime.markPersistent ctx
parseCmd none parserState cmdState prom (sync := true) ctx
return {
diagnostics
@@ -473,11 +487,12 @@ where
prom.resolve <| { old with nextCmdSnap? := some { range? := none, task := newProm.result } }
else prom.resolve old -- terminal command, we're done!
-- fast path, do not even start new task for this snapshot
-- fast path, do not even start new task for this snapshot (see [Incremental Parsing])
if let some old := old? then
if let some nextCom old.nextCmdSnap?.bindM (·.get?) then
if ( isBeforeEditPos nextCom.parserState.pos) then
return ( unchanged old old.parserState)
if let some nextNextCom nextCom.nextCmdSnap?.bindM (·.get?) then
if ( isBeforeEditPos nextNextCom.parserState.pos) then
return ( unchanged old old.parserState)
let beginPos := parserState.pos
let scope := cmdState.scopes.head!
@@ -626,22 +641,21 @@ where
pos := ctx.fileMap.toPosition beginPos
data := output
}
let cmdState := { cmdState with messages }
let cmdState : Command.State := { cmdState with messages }
let mut reportedCmdState := cmdState
-- definitely resolve eventually
snap.new.resolve <| .ofTyped { diagnostics := .empty : SnapshotLeaf }
let mut infoTree := cmdState.infoState.trees[0]!
let infoTree : InfoTree := cmdState.infoState.trees[0]!
let cmdline := internal.cmdlineSnapshots.get scope.opts && !Parser.isTerminalCommand stx
if cmdline then
infoTree := Runtime.markPersistent infoTree
-- discard all metadata apart from the environment; see `internal.cmdlineSnapshots`
reportedCmdState := { env := reportedCmdState.env, maxRecDepth := 0 }
finishedPromise.resolve {
diagnostics := ( Snapshot.Diagnostics.ofMessageLog cmdState.messages)
infoTree? := infoTree
traces := cmdState.traceState
cmdState := if cmdline then {
env := Runtime.markPersistent cmdState.env
maxRecDepth := 0
} else cmdState
cmdState := reportedCmdState
}
-- The reported `cmdState` in the snapshot may be minimized as seen above, so we return the full
-- state here for further processing on the same thread

View File

@@ -51,8 +51,8 @@ def checkDeprecated [Monad m] [MonadEnv m] [MonadLog m] [AddMessageContext m] [M
if getLinterValue linter.deprecated ( getOptions) then
let some attr := deprecatedAttr.getParam? ( getEnv) declName | pure ()
logWarning <| .tagged ``deprecatedAttr <|
s!"`{declName}` has been deprecated" ++ match attr.text? with
m!"`{.ofConstName declName true}` has been deprecated" ++ match attr.text? with
| some text => s!": {text}"
| none => match attr.newName? with
| some newName => s!": use `{newName}` instead"
| some newName => m!": use `{.ofConstName newName true}` instead"
| none => ""

View File

@@ -248,7 +248,7 @@ builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack opts =>
!getLinterUnusedVariablesFunArgs opts &&
stack.matches [`null, none, `null, ``Lean.Parser.Term.letIdDecl, none] &&
(stack.get? 3 |>.any fun (_, pos) => pos == 1) &&
(stack.get? 5 |>.any fun (stx, _) => !stx.isOfKind ``Lean.Parser.Command.whereStructField))
(stack.get? 5 |>.any fun (stx, _) => !stx.isOfKind ``Lean.Parser.Term.structInstField))
/--
Function argument in declaration signature (when `linter.unusedVariables.funArgs` is false)

View File

@@ -5,14 +5,40 @@ Authors: Mac Malone
-/
prelude
import Init.System.IO
namespace Lean
/--
Dynamically loads a shared library so that its symbols can be used by
the Lean interpreter (e.g., for interpreting `@[extern]` declarations).
Equivalent to passing `--load-dynlib=lib` to `lean`.
Equivalent to passing `--load-dynlib=path` to `lean`.
Note that Lean never unloads libraries.
**Lean never unloads libraries.** Attempting to load a library that defines
symbols shared with a previously loaded library (including itself) will error.
If multiple libraries share common symbols, those symbols should be linked
and loaded as separate libraries.
-/
@[extern "lean_load_dynlib"]
opaque loadDynlib (path : @& System.FilePath) : IO Unit
/--
Loads a Lean plugin and runs its initializers.
A Lean plugin is a shared library built from a Lean module.
This means it has an `initialize_<module-name>` symbol that runs the
module's initializers (including its imports' initializers). Initializers
are declared with the `initialize` or `builtin_initialize` commands.
This is similar to passing `--plugin=path` to `lean`.
Lean environment initializers, such as definitions calling
`registerEnvExtension`, also require `Lean.initializing` to be `true`.
To enable them, use `loadPlugin` within a `withImporting` block. This will
set `Lean.initializing` (but not `IO.initializing`).
**Lean never unloads plugins.** Attempting to load a plugin that defines
symbols shared with a previously loaded plugin (including itself) will error.
If multiple plugins share common symbols (e.g., imports), those symbols
should be linked and loaded separately.
-/
@[extern "lean_load_plugin"]
opaque loadPlugin (path : @& System.FilePath) : IO Unit

View File

@@ -441,6 +441,10 @@ instance : Append MessageLog :=
def hasErrors (log : MessageLog) : Bool :=
log.hadErrors || log.unreported.any (·.severity matches .error)
/-- Clears unreported messages while preserving `hasErrors`. -/
def markAllReported (log : MessageLog) : MessageLog :=
{ log with unreported := {}, hadErrors := log.hasErrors }
def errorsToWarnings (log : MessageLog) : MessageLog :=
{ unreported := log.unreported.map (fun m => match m.severity with | MessageSeverity.error => { m with severity := MessageSeverity.warning } | _ => m) }

View File

@@ -229,7 +229,7 @@ structure ParamInfo where
hasFwdDeps : Bool := false
/-- `backDeps` contains the backwards dependencies. That is, the (0-indexed) position of previous parameters that this one depends on. -/
backDeps : Array Nat := #[]
/-- `isProp` is true if the parameter is always a proposition. -/
/-- `isProp` is true if the parameter type is always a proposition. -/
isProp : Bool := false
/--
`isDecInst` is true if the parameter's type is of the form `Decidable ...`.

View File

@@ -35,11 +35,27 @@ def isConstructorApp? (e : Expr) : MetaM (Option ConstructorVal) := do
/--
Similar to `isConstructorApp?`, but uses `whnf`.
It also uses `isOffset?` for `Nat`.
See also `Lean.Meta.constructorApp'?`.
-/
def isConstructorApp'? (e : Expr) : MetaM (Option ConstructorVal) := do
if let some r isConstructorApp? e then
if let some (_, k) isOffset? e then
if k = 0 then
return none
else
let .ctorInfo val getConstInfo ``Nat.succ | return none
return some val
else if let some r isConstructorApp? e then
return r
isConstructorApp? ( whnf e)
else try
/-
We added the `try` block here because `whnf` fails at terms `n ^ m`
when `m` is a big numeral, and `n` is a numeral. This is a little bit hackish.
-/
isConstructorApp? ( whnf e)
catch _ =>
return none
/--
Returns `true`, if `e` is constructor application of builtin literal defeq to
@@ -70,7 +86,9 @@ def constructorApp? (e : Expr) : MetaM (Option (ConstructorVal × Array Expr)) :
/--
Similar to `constructorApp?`, but on failure it puts `e` in WHNF and tries again.
It also `isOffset?`
It also uses `isOffset?` for `Nat`.
See also `Lean.Meta.isConstructorApp'?`.
-/
def constructorApp'? (e : Expr) : MetaM (Option (ConstructorVal × Array Expr)) := do
if let some (e, k) isOffset? e then

View File

@@ -62,13 +62,13 @@ def getStringValue? (e : Expr) : (Option String) :=
| .lit (.strVal s) => some s
| _ => none
/-- Return `some ⟨n, v⟩` if `e` is af `OfNat.ofNat` application encoding a `Fin n` with value `v` -/
/-- Return `some ⟨n, v⟩` if `e` is an `OfNat.ofNat` application encoding a `Fin n` with value `v` -/
def getFinValue? (e : Expr) : MetaM (Option ((n : Nat) × Fin n)) := OptionT.run do
let (v, type) getOfNatValue? e ``Fin
let n getNatValue? ( whnfD type.appArg!)
match n with
| 0 => failure
| m+1 => return m+1, Fin.ofNat v
| m+1 => return m+1, Fin.ofNat' _ v
/--
Return `some ⟨n, v⟩` if `e` is:

View File

@@ -719,13 +719,11 @@ def deriveUnaryInduction (name : Name) : MetaM Name := do
let e' abstractIndependentMVars mvars ( motive.fvarId!.getDecl).index e'
let e' mkLambdaFVars #[motive] e'
-- We could pass (usedOnly := true) below, and get nicer induction principles that
-- 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) fixedParams e'
-- We used to pass (usedOnly := false) below in the hope that the types of the
-- induction principle match the type of the function better.
-- But this leads to avoidable parameters that make functional induction strictly less
-- useful (e.g. when the unsued parameter mentions bound variables in the users' goal)
let e' mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) fixedParams e'
instantiateMVars e'
| _ =>
if funBody.isAppOf ``WellFounded.fix then
@@ -1062,13 +1060,11 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
let e' abstractIndependentMVars mvars ( motives.back!.fvarId!.getDecl).index e'
let e' mkLambdaFVars motives e'
-- We could pass (usedOnly := true) below, and get nicer induction principles that
-- 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) xs e'
-- We used to pass (usedOnly := false) below in the hope that the types of the
-- induction principle match the type of the function better.
-- But this leads to avoidable parameters that make functional induction strictly less
-- useful (e.g. when the unsued parameter mentions bound variables in the users' goal)
let e' mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) xs e'
let e' instantiateMVars e'
trace[Meta.FunInd] "complete body of mutual induction principle:{indentExpr e'}"
pure e'

View File

@@ -20,6 +20,18 @@ def fromExpr? (e : Expr) : SimpM (Option Value) := do
let some n, value getFinValue? e | return none
return some { n, value }
@[inline] def reduceOp (declName : Name) (arity : Nat) (f : Nat Nat) (op : {n : Nat} Fin n Fin (f n)) (e : Expr) : SimpM DStep := do
unless e.isAppOfArity declName arity do return .continue
let some v fromExpr? e.appArg! | return .continue
let v' := op v.value
return .done <| toExpr v'
@[inline] def reduceNatOp (declName : Name) (arity : Nat) (f : Nat Nat) (op : (n : Nat) Fin (f n)) (e : Expr) : SimpM DStep := do
unless e.isAppOfArity declName arity do return .continue
let some v getNatValue? e.appArg! | return .continue
let v' := op v
return .done <| toExpr v'
@[inline] def reduceBin (declName : Name) (arity : Nat) (op : {n : Nat} Fin n Fin n Fin n) (e : Expr) : SimpM DStep := do
unless e.isAppOfArity declName arity do return .continue
let some v₁ fromExpr? e.appFn!.appArg! | return .continue
@@ -47,12 +59,23 @@ The following code assumes users did not override the `Fin n` instances for the
If they do, they must disable the following `simprocs`.
-/
builtin_dsimproc [simp, seval] reduceSucc (Fin.succ _) := reduceOp ``Fin.succ 2 (· + 1) Fin.succ
builtin_dsimproc [simp, seval] reduceRev (Fin.rev _) := reduceOp ``Fin.rev 2 (·) Fin.rev
builtin_dsimproc [simp, seval] reduceLast (Fin.last _) := reduceNatOp ``Fin.last 1 (· + 1) Fin.last
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_dsimproc [simp, seval] reduceAnd ((_ &&& _ : Fin _)) := reduceBin ``HAnd.hAnd 6 (· &&& ·)
builtin_dsimproc [simp, seval] reduceOr ((_ ||| _ : Fin _)) := reduceBin ``HOr.hOr 6 (· ||| ·)
builtin_dsimproc [simp, seval] reduceXor ((_ ^^^ _ : Fin _)) := reduceBin ``HXor.hXor 6 (· ^^^ ·)
builtin_dsimproc [simp, seval] reduceShiftLeft ((_ <<< _ : Fin _)) := reduceBin ``HShiftLeft.hShiftLeft 6 (· <<< ·)
builtin_dsimproc [simp, seval] reduceShiftRight ((_ >>> _ : Fin _)) := reduceBin ``HShiftRight.hShiftRight 6 (· >>> ·)
builtin_simproc [simp, seval] reduceLT (( _ : Fin _) < _) := reduceBinPred ``LT.lt 4 (. < .)
builtin_simproc [simp, seval] reduceLE (( _ : Fin _) _) := reduceBinPred ``LE.le 4 (. .)
builtin_simproc [simp, seval] reduceGT (( _ : Fin _) > _) := reduceBinPred ``GT.gt 4 (. > .)
@@ -83,4 +106,70 @@ builtin_dsimproc [simp, seval] reduceFinMk (Fin.mk _ _) := fun e => do
else
return .continue
builtin_dsimproc [simp, seval] reduceOfNat' (Fin.ofNat' _ _) := fun e => do
unless e.isAppOfArity ``Fin.ofNat' 3 do return .continue
let some (n + 1) getNatValue? e.appFn!.appFn!.appArg! | return .continue
let some k getNatValue? e.appArg! | return .continue
return .done <| toExpr (Fin.ofNat' (n + 1) k)
builtin_dsimproc [simp, seval] reduceCastSucc (Fin.castSucc _) := fun e => do
unless e.isAppOfArity ``Fin.castSucc 2 do return .continue
let some k fromExpr? e.appArg! | return .continue
return .done <| toExpr (castSucc k.value)
builtin_dsimproc [simp, seval] reduceCastAdd (Fin.castAdd _ _) := fun e => do
unless e.isAppOfArity ``Fin.castAdd 3 do return .continue
let some m getNatValue? e.appFn!.appArg! | return .continue
let some k fromExpr? e.appArg! | return .continue
return .done <| toExpr (castAdd m k.value)
builtin_dsimproc [simp, seval] reduceAddNat (Fin.addNat _ _) := fun e => do
unless e.isAppOfArity ``Fin.addNat 3 do return .continue
let some k fromExpr? e.appFn!.appArg! | return .continue
let some m getNatValue? e.appArg! | return .continue
return .done <| toExpr (addNat k.value m)
builtin_dsimproc [simp, seval] reduceNatAdd (Fin.natAdd _ _) := fun e => do
unless e.isAppOfArity ``Fin.natAdd 3 do return .continue
let some m getNatValue? e.appFn!.appArg! | return .continue
let some k fromExpr? e.appArg! | return .continue
return .done <| toExpr (natAdd m k.value)
builtin_dsimproc [simp, seval] reduceCastLT (Fin.castLT _ _) := fun e => do
unless e.isAppOfArity ``Fin.castLT 4 do return .continue
let some n getNatValue? e.appFn!.appFn!.appFn!.appArg! | return .continue
let some i fromExpr? e.appFn!.appArg! | return .continue
if h : i.value < n then
return .done <| toExpr (castLT i.value h)
else
return .continue
builtin_dsimproc [simp, seval] reduceCastLE (Fin.castLE _ _) := fun e => do
unless e.isAppOfArity ``Fin.castLE 4 do return .continue
let some m getNatValue? e.appFn!.appFn!.appArg! | return .continue
let some i fromExpr? e.appArg! | return .continue
if h : i.n m then
return .done <| toExpr (castLE h i.value)
else
return .continue
-- No simproc is needed for `Fin.cast`, as for explicit numbers `Fin.cast_refl` will apply.
builtin_dsimproc [simp, seval] reduceSubNat (Fin.subNat _ _ _) := fun e => do
unless e.isAppOfArity ``Fin.subNat 4 do return .continue
let some m getNatValue? e.appFn!.appFn!.appArg! | return .continue
let some i fromExpr? e.appFn!.appArg! | return .continue
if h : m i.value then
return .done <| toExpr (subNat m (i.value.cast (by omega : i.n = (i.n - m) + m)) h)
else
return .continue
builtin_dsimproc [simp, seval] reducePred (Fin.pred _ _) := fun e => do
unless e.isAppOfArity ``Fin.pred 3 do return .continue
let some (_ + 1), i fromExpr? e.appFn!.appArg! | return .continue
if h : i 0 then
return .done <| toExpr (pred i h)
else
return .continue
end Fin

View File

@@ -71,8 +71,8 @@ builtin_dsimproc [simp, seval] reduceMul ((_ * _ : Int)) := reduceBin ``HMul.hMu
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_dsimproc [simp, seval] reduceTDiv (tdiv _ _) := reduceBin ``Int.div 2 Int.tdiv
builtin_dsimproc [simp, seval] reduceTMod (tmod _ _) := reduceBin ``Int.mod 2 Int.tmod
builtin_dsimproc [simp, seval] reduceTDiv (tdiv _ _) := reduceBin ``Int.tdiv 2 Int.tdiv
builtin_dsimproc [simp, seval] reduceTMod (tmod _ _) := reduceBin ``Int.tmod 2 Int.tmod
builtin_dsimproc [simp, seval] reduceFDiv (fdiv _ _) := reduceBin ``Int.fdiv 2 Int.fdiv
builtin_dsimproc [simp, seval] reduceFMod (fmod _ _) := reduceBin ``Int.fmod 2 Int.fmod
builtin_dsimproc [simp, seval] reduceBdiv (bdiv _ _) := reduceBinIntNatOp ``bdiv bdiv

View File

@@ -84,8 +84,22 @@ declare_uint_simprocs UInt8
declare_uint_simprocs UInt16
declare_uint_simprocs UInt32
declare_uint_simprocs UInt64
/-
We disabled the simprocs for USize since the result of most operations depend on an opaque value: `System.Platform.numBits`.
We could reduce some cases using the fact that this opaque value is `32` or `64`, but it is unclear whether it would be useful in practice.
We do not use the normal simprocs for `USize` since the result of most operations depend on an opaque value: `System.Platform.numBits`.
However, we do reduce natural literals using the fact this opaque value is at least `32`.
-/
-- declare_uint_simprocs USize
namespace USize
def fromExpr (e : Expr) : SimpM (Option USize) := do
let some (n, _) getOfNatValue? e ``USize | return none
return USize.ofNat n
builtin_simproc [simp, seval] reduceToNat (USize.toNat _) := fun e => do
let_expr USize.toNat e e | return .continue
let some (n, _) getOfNatValue? e ``USize | return .continue
unless n < UInt32.size do return .continue
let e := toExpr n
let p mkDecideProof ( mkLT e (mkNatLit UInt32.size))
let p := mkApp2 (mkConst ``USize.toNat_ofNat_of_lt_32) e p
return .done { expr := e, proof? := p }

View File

@@ -108,13 +108,19 @@ where
trace[Meta.Tactic.simp.discharge] "{← ppOrigin thmId}, failed to synthesize instance{indentExpr type}"
return false
private def useImplicitDefEqProof (thm : SimpTheorem) : SimpM Bool := do
if thm.rfl then
return ( getConfig).implicitDefEqProofs
else
return false
private def tryTheoremCore (lhs : Expr) (xs : Array Expr) (bis : Array BinderInfo) (val : Expr) (type : Expr) (e : Expr) (thm : SimpTheorem) (numExtraArgs : Nat) : SimpM (Option Result) := do
recordTriedSimpTheorem thm.origin
let rec go (e : Expr) : SimpM (Option Result) := do
if ( isDefEq lhs e) then
unless ( synthesizeArgs thm.origin bis xs) do
return none
let proof? if thm.rfl then
let proof? if ( useImplicitDefEqProof thm) then
pure none
else
let proof instantiateMVars (mkAppN val xs)

View File

@@ -269,7 +269,7 @@ def mkDiscrGenErrorMsg (e : Expr) : MessageData :=
def throwDiscrGenError (e : Expr) : MetaM α :=
throwError (mkDiscrGenErrorMsg e)
def splitMatch (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := do
def splitMatch (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := mvarId.withContext do
let some app matchMatcherApp? e | throwError "internal error in `split` tactic: match application expected{indentExpr e}\nthis error typically occurs when the `split` tactic internal functions have been used in a new meta-program"
let matchEqns Match.getEquationsFor app.matcherName
let mvarIds applyMatchSplitter mvarId app.matcherName app.matcherLevels app.params app.discrs
@@ -278,43 +278,14 @@ def splitMatch (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := do
return (i+1, mvarId::mvarIds)
return mvarIds.reverse
/-- Return an `if-then-else` or `match-expr` to split. -/
partial def findSplit? (env : Environment) (e : Expr) (splitIte := true) (exceptionSet : ExprSet := {}) : Option Expr :=
go e
where
go (e : Expr) : Option Expr :=
if let some target := e.find? isCandidate then
if e.isIte || e.isDIte then
let cond := target.getArg! 1 5
-- Try to find a nested `if` in `cond`
go cond |>.getD target
else
some target
else
none
isCandidate (e : Expr) : Bool := Id.run do
if exceptionSet.contains e then
false
else if splitIte && (e.isIte || e.isDIte) then
!(e.getArg! 1 5).hasLooseBVars
else if let some info := isMatcherAppCore? env e then
let args := e.getAppArgs
for i in [info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs] do
if args[i]!.hasLooseBVars then
return false
return true
else
false
end Split
open Split
partial def splitTarget? (mvarId : MVarId) (splitIte := true) : MetaM (Option (List MVarId)) := commitWhenSome? do
partial def splitTarget? (mvarId : MVarId) (splitIte := true) : MetaM (Option (List MVarId)) := commitWhenSome? do mvarId.withContext do
let target instantiateMVars ( mvarId.getType)
let rec go (badCases : ExprSet) : MetaM (Option (List MVarId)) := do
if let some e := findSplit? ( getEnv) target splitIte badCases then
if let some e findSplit? target (if splitIte then .both else .match) badCases then
if e.isIte || e.isDIte then
return ( splitIfTarget? mvarId).map fun (s₁, s₂) => [s₁.mvarId, s₂.mvarId]
else
@@ -333,7 +304,7 @@ partial def splitTarget? (mvarId : MVarId) (splitIte := true) : MetaM (Option (L
def splitLocalDecl? (mvarId : MVarId) (fvarId : FVarId) : MetaM (Option (List MVarId)) := commitWhenSome? do
mvarId.withContext do
if let some e := findSplit? ( getEnv) ( instantiateMVars ( inferType (mkFVar fvarId))) then
if let some e findSplit? ( instantiateMVars ( inferType (mkFVar fvarId))) then
if e.isIte || e.isDIte then
return ( splitIfLocalDecl? mvarId fvarId).map fun (mvarId₁, mvarId₂) => [mvarId₁, mvarId₂]
else

View File

@@ -8,6 +8,124 @@ import Lean.Meta.Tactic.Cases
import Lean.Meta.Tactic.Simp.Main
namespace Lean.Meta
inductive SplitKind where
| ite | match | both
def SplitKind.considerIte : SplitKind Bool
| .ite | .both => true
| _ => false
def SplitKind.considerMatch : SplitKind Bool
| .match | .both => true
| _ => false
namespace FindSplitImpl
structure Context where
exceptionSet : ExprSet := {}
kind : SplitKind := .both
unsafe abbrev FindM := ReaderT Context $ StateT (PtrSet Expr) MetaM
/--
Checks whether `e` is a candidate for `split`.
Returns `some e'` if a prefix is a candidate.
Example: suppose `e` is `(if b then f else g) x`, then
the result is `some e'` where `e'` is the subterm `(if b then f else g)`
-/
private def isCandidate? (env : Environment) (ctx : Context) (e : Expr) : Option Expr := Id.run do
let ret (e : Expr) : Option Expr :=
if ctx.exceptionSet.contains e then none else some e
if ctx.kind.considerIte then
if e.isAppOf ``ite || e.isAppOf ``dite then
let numArgs := e.getAppNumArgs
if numArgs >= 5 && !(e.getArg! 1 5).hasLooseBVars then
return ret (e.getBoundedAppFn (numArgs - 5))
if ctx.kind.considerMatch then
if let some info := isMatcherAppCore? env e then
let args := e.getAppArgs
for i in [info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs] do
if args[i]!.hasLooseBVars then
return none
return ret (e.getBoundedAppFn (args.size - info.arity))
return none
@[inline] unsafe def checkVisited (e : Expr) : OptionT FindM Unit := do
if ( get).contains e then
failure
modify fun s => s.insert e
unsafe def visit (e : Expr) : OptionT FindM Expr := do
checkVisited e
if let some e := isCandidate? ( getEnv) ( read) e then
return e
else
-- We do not look for split candidates in proofs.
unless e.hasLooseBVars do
if ( isProof e) then
failure
match e with
| .lam _ _ b _ | .proj _ _ b -- We do not look for split candidates in the binder of lambdas.
| .mdata _ b => visit b
| .forallE _ d b _ => visit d <|> visit b -- We want to look for candidates at `A → B`
| .letE _ _ v b _ => visit v <|> visit b
| .app .. => visitApp? e
| _ => failure
where
visitApp? (e : Expr) : FindM (Option Expr) :=
e.withApp fun f args => do
-- See comment at `Canonicalizer.lean` regarding the case where
-- `f` has loose bound variables.
let info if f.hasLooseBVars then
pure {}
else
getFunInfo f
for u : i in [0:args.size] do
let arg := args[i]
if h : i < info.paramInfo.size then
let info := info.paramInfo[i]
unless info.isProp do
if info.isExplicit then
let some found visit arg | pure ()
return found
else
let some found visit arg | pure ()
return found
visit f
end FindSplitImpl
/-- Return an `if-then-else` or `match-expr` to split. -/
partial def findSplit? (e : Expr) (kind : SplitKind := .both) (exceptionSet : ExprSet := {}) : MetaM (Option Expr) := do
go ( instantiateMVars e)
where
go (e : Expr) : MetaM (Option Expr) := do
if let some target find? e then
if target.isIte || target.isDIte then
let cond := target.getArg! 1 5
-- Try to find a nested `if` in `cond`
return ( go cond).getD target
else
return some target
else
return none
find? (e : Expr) : MetaM (Option Expr) := do
let some candidate unsafe FindSplitImpl.visit e { kind, exceptionSet } |>.run' mkPtrSet
| return none
trace[split.debug] "candidate:{indentExpr candidate}"
return some candidate
/-- Return the condition and decidable instance of an `if` expression to case split. -/
private partial def findIfToSplit? (e : Expr) : MetaM (Option (Expr × Expr)) := do
if let some iteApp findSplit? e .ite then
let cond := iteApp.getArg! 1 5
let dec := iteApp.getArg! 2 5
return (cond, dec)
else
return none
namespace SplitIf
/--
@@ -62,19 +180,9 @@ private def discharge? (numIndices : Nat) (useDecide : Bool) : Simp.Discharge :=
def mkDischarge? (useDecide := false) : MetaM Simp.Discharge :=
return discharge? ( getLCtx).numIndices useDecide
/-- Return the condition and decidable instance of an `if` expression to case split. -/
private partial def findIfToSplit? (e : Expr) : Option (Expr × Expr) :=
if let some iteApp := e.find? fun e => (e.isIte || e.isDIte) && !(e.getArg! 1 5).hasLooseBVars then
let cond := iteApp.getArg! 1 5
let dec := iteApp.getArg! 2 5
-- Try to find a nested `if` in `cond`
findIfToSplit? cond |>.getD (cond, dec)
else
none
def splitIfAt? (mvarId : MVarId) (e : Expr) (hName? : Option Name) : MetaM (Option (ByCasesSubgoal × ByCasesSubgoal)) := do
def splitIfAt? (mvarId : MVarId) (e : Expr) (hName? : Option Name) : MetaM (Option (ByCasesSubgoal × ByCasesSubgoal)) := mvarId.withContext do
let e instantiateMVars e
if let some (cond, decInst) := findIfToSplit? e then
if let some (cond, decInst) findIfToSplit? e then
let hName match hName? with
| none => mkFreshUserName `h
| some hName => pure hName
@@ -106,6 +214,7 @@ def splitIfTarget? (mvarId : MVarId) (hName? : Option Name := none) : MetaM (Opt
let mvarId₁ simpIfTarget s₁.mvarId
let mvarId₂ simpIfTarget s₂.mvarId
if s₁.mvarId == mvarId₁ && s₂.mvarId == mvarId₂ then
trace[split.failure] "`split` tactic failed to simplify target using new hypotheses Goals:\n{mvarId₁}\n{mvarId₂}"
return none
else
return some ({ s₁ with mvarId := mvarId₁ }, { s₂ with mvarId := mvarId₂ })
@@ -118,6 +227,7 @@ def splitIfLocalDecl? (mvarId : MVarId) (fvarId : FVarId) (hName? : Option Name
let mvarId₁ simpIfLocalDecl s₁.mvarId fvarId
let mvarId₂ simpIfLocalDecl s₂.mvarId fvarId
if s₁.mvarId == mvarId₁ && s₂.mvarId == mvarId₂ then
trace[split.failure] "`split` tactic failed to simplify target using new hypotheses Goals:\n{mvarId₁}\n{mvarId₂}"
return none
else
return some (mvarId₁, mvarId₂)

View File

@@ -134,10 +134,8 @@ def declValSimple := leading_parser
" :=" >> ppHardLineUnlessUngrouped >> declBody >> Termination.suffix >> optional Term.whereDecls
def declValEqns := leading_parser
Term.matchAltsWhereDecls
def whereStructField := leading_parser
Term.letDecl
def whereStructInst := leading_parser
ppIndent ppSpace >> "where" >> Term.structInstFields (sepByIndent (ppGroup whereStructField) "; " (allowTrailingSep := true)) >>
ppIndent ppSpace >> "where" >> Term.structInstFields (sepByIndent Term.structInstField "; " (allowTrailingSep := true)) >>
optional Term.whereDecls
/-- `declVal` matches the right-hand side of a declaration, one of:
* `:= expr` (a "simple declaration")

View File

@@ -269,38 +269,6 @@ an optional `x :`, then a term `ty`, then `from val` or `by tac`. -/
@[builtin_term_parser] def «suffices» := leading_parser:leadPrec
withPosition ("suffices " >> sufficesDecl) >> optSemicolon termParser
@[builtin_term_parser] def «show» := leading_parser:leadPrec "show " >> termParser >> ppSpace >> showRhs
def structInstArrayRef := leading_parser
"[" >> withoutPosition termParser >> "]"
def structInstLVal := leading_parser
(ident <|> fieldIdx <|> structInstArrayRef) >>
many (group ("." >> (ident <|> fieldIdx)) <|> structInstArrayRef)
def structInstField := ppGroup $ leading_parser
structInstLVal >> " := " >> termParser
def structInstFieldAbbrev := leading_parser
-- `x` is an abbreviation for `x := x`
atomic (ident >> notFollowedBy ("." <|> ":=" <|> symbol "[") "invalid field abbreviation")
def optEllipsis := leading_parser
optional " .."
/-
Tags the structure instance field syntax with a `Lean.Parser.Term.structInstFields` syntax node.
This node is used to enable structure instance field completion in the whitespace
of a structure instance notation.
-/
def structInstFields (p : Parser) : Parser := node `Lean.Parser.Term.structInstFields p
/--
Structure instance. `{ x := e, ... }` assigns `e` to field `x`, which may be
inherited. If `e` is itself a variable called `x`, it can be elided:
`fun y => { x := 1, y }`.
A *structure update* of an existing value can be given via `with`:
`{ point with x := 1 }`.
The structure type can be specified if not inferable:
`{ x := 1, y := 2 : Point }`.
-/
@[builtin_term_parser] def structInst := leading_parser
"{ " >> withoutPosition (optional (atomic (sepBy1 termParser ", " >> " with "))
>> structInstFields (sepByIndent (structInstFieldAbbrev <|> structInstField) ", " (allowTrailingSep := true))
>> optEllipsis
>> optional (" : " >> termParser)) >> " }"
def typeSpec := leading_parser " : " >> termParser
def optType : Parser := optional typeSpec
/--
@@ -488,6 +456,56 @@ e.g. because it has no constructors.
@[builtin_term_parser] def «nofun» := leading_parser "nofun"
/-
Syntax category for structure instance notation fields.
Does not initialize `registerBuiltinDynamicParserAttribute` since this category is not meant to be user-extensible.
-/
builtin_initialize
registerBuiltinParserAttribute `builtin_structInstFieldDecl_parser ``Category.structInstFieldDecl
@[inline] def structInstFieldDeclParser (rbp : Nat := 0) : Parser :=
categoryParser `structInstFieldDecl rbp
def optEllipsis := leading_parser
optional " .."
def structInstArrayRef := leading_parser
"[" >> withoutPosition termParser >> "]"
def structInstLVal := leading_parser
(ident <|> fieldIdx <|> structInstArrayRef) >>
many (group ("." >> (ident <|> fieldIdx)) <|> structInstArrayRef)
def structInstFieldBinder :=
withAntiquot (mkAntiquot "structInstFieldBinder" decl_name% (isPseudoKind := true)) <|
binderIdent <|> bracketedBinder
def optTypeForStructInst : Parser := optional (atomic (typeSpec >> notFollowedBy "}" "}"))
/- `x` is an abbreviation for `x := x` -/
def structInstField := ppGroup <| leading_parser
structInstLVal >> optional (many (checkColGt >> structInstFieldBinder) >> optTypeForStructInst >> ppDedent structInstFieldDeclParser)
/-
Tags the structure instance field syntax with a `Lean.Parser.Term.structInstFields` syntax node.
This node is used to enable structure instance field completion in the whitespace
of a structure instance notation.
-/
def structInstFields (p : Parser) : Parser := node `Lean.Parser.Term.structInstFields p
/--
Structure instance. `{ x := e, ... }` assigns `e` to field `x`, which may be
inherited. If `e` is itself a variable called `x`, it can be elided:
`fun y => { x := 1, y }`.
A *structure update* of an existing value can be given via `with`:
`{ point with x := 1 }`.
The structure type can be specified if not inferable:
`{ x := 1, y := 2 : Point }`.
-/
@[builtin_term_parser] def structInst := leading_parser
"{ " >> withoutPosition (optional (atomic (sepBy1 termParser ", " >> " with "))
>> structInstFields (sepByIndent structInstField ", " (allowTrailingSep := true))
>> optEllipsis
>> optional (" : " >> termParser)) >> " }"
@[builtin_structInstFieldDecl_parser]
def structInstFieldDef := leading_parser
" := " >> termParser
@[builtin_structInstFieldDecl_parser]
def structInstFieldEqns := leading_parser
matchAlts
def funImplicitBinder := withAntiquot (mkAntiquot "implicitBinder" ``implicitBinder) <|
atomic (lookahead ("{" >> many1 binderIdent >> (symbol " : " <|> "}"))) >> implicitBinder
def funStrictImplicitBinder :=

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