Compare commits

..

114 Commits

Author SHA1 Message Date
Kim Morrison
5a8b620e46 fix: use GitHub App token for Batteries/Mathlib checkout in PR release
The MATHLIB4_BOT PAT has been deprecated as part of the migration to GitHub Apps.
This PR updates the workflow to use the mathlib-nightly-testing GitHub App instead.

**Required setup:** Add the following secrets to leanprover/lean4:
- MATHLIB_NIGHTLY_TESTING_APP_ID
- MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY

These are the same values used in leanprover-community/mathlib4 and batteries.
2026-02-05 04:47:21 +00:00
Leonardo de Moura
7c350e33a0 feat: improve instantiateExtTheorem in grind (#12319)
This PR leverages the fact that expressions are type correct in `grind`
and the conclusion of extensionality theorems is of the form `?a = ?b`.

This PR is relevant for #12179 because it enables us to use a weaker
`isDefEq` that does not bump the transparency level when processing
implicit arguments.
2026-02-05 02:39:42 +00:00
Kim Morrison
0a431679a4 feat: implements maxSuggestions for grind and simp 2026-02-05 13:41:34 +11:00
Kim Morrison
b1b8e95713 chore: update stage0 2026-02-05 13:41:34 +11:00
Kim Morrison
d49e5d8a3d Revert "chore: temporarily disable proofs for bootstrap"
This reverts commit c56a5732a5.
2026-02-05 13:41:34 +11:00
Kim Morrison
2f2a004c7f chore: update stage0 2026-02-05 13:41:34 +11:00
Kim Morrison
8ca8138f8f feat: add maxSuggestions field to Simp.Config and Grind.Config
This adds a `maxSuggestions : Option Nat := none` field to both
`Simp.Config` and `Grind.Config`. When `suggestions` is enabled, this
field controls the maximum number of library suggestions to use. If
`none`, the default limit is used.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 13:41:34 +11:00
Kim Morrison
9203df205a chore: update stage0 2026-02-05 13:41:34 +11:00
Kim Morrison
7b12b504df chore: temporarily disable proofs for bootstrap
This adds `set_option debug.byAsSorry true` and `decreasing_by sorry` to
various files to allow bootstrapping with Config structure changes. These
changes will be restored after the bootstrap dance is complete.
2026-02-05 13:41:34 +11:00
Kim Morrison
15e4af8f49 chore: add explicit type for robustness under bootstrapping 2026-02-05 13:41:34 +11:00
David Thrane Christiansen
b397d466b4 fix: report Verso docsring parse errors (#12314)
This PR fixes a problem where Verso docstring parse errors were being
swallowed in some circumstances.

Closes #12062.
2026-02-04 18:46:31 +00:00
Wojciech Różowski
ff93bb6b80 feat: add cbv_opaque attribute (#12283)
This PR introduces `cbv_opaque` attribute that allows one to mark
definitions not to be unfolded by the `cbv` tactic.

Stacked on top of #12279
2026-02-04 16:27:32 +00:00
Garmelon
e08cb957f5 chore: fail benchmarks if lakeprof upload fails (#12313) 2026-02-04 15:53:33 +00:00
Wojciech Różowski
bd8c861316 test: add program evaluation benchmark to cbv (#12280)
This PR adds a benchmark based on Xavier Leroy's compiler verification
course to test call-by-value tactic.

Stacked on top of #12279
2026-02-04 15:37:00 +00:00
David Thrane Christiansen
e93287137e doc: review and add docstrings for syntax operators in manual and one range statement (#12271)
This PR adds and updates docstrings for syntax (and one for ranges).

The reference manual's section on syntax operators is gaining more
content, so the resulting docstrings were reviewed and the missing ones
added.
2026-02-04 15:14:59 +00:00
Wojciech Różowski
47a18a4bb4 feat: add experimental cbv tactic (#12279)
This PR adds an experimental `cbv` tactic that can be invoked from
`conv` mode. The tactic is not suitable for production use and an
appropriate warning is displayed.
2026-02-04 14:39:39 +00:00
Michael Sammler
635d5c1f59 fix: enable custom CCPO when using module system (#12311)
This PR exposes the chain and is_sup definitions such that other modules
can declare custom CCPO instances.

Resolves the issue discussed [on Zulip:
](https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/How.20to.20define.20CCPO.20with.20module.20system.20enabled.3F/with/571855195)[#general
> How to define CCPO with module system
enabled?](https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/How.20to.20define.20CCPO.20with.20module.20system.20enabled.3F/with/571855195).
2026-02-04 14:12:38 +00:00
Markus Himmel
35852a9fa9 chore: lemmas for basic types (#12305)
This PR adds various uninteresting lemmas about basic types, extracted
from the KMP verification.
2026-02-04 11:02:17 +00:00
Sebastian Ullrich
1ed3ce2005 chore: remove unused Lean.Data.Xml (#12302)
NB: This module is used in doc-gen for, uh, HTML parsing but should be
moved there or into a separate package for that purpose
2026-02-04 09:54:25 +00:00
Markus Himmel
8a9cb6def0 feat: Slice.posGE and Slice.posGT (#12301)
This PR introduces the functions `(String|Slice).posGE` and
`(String|Slice).posGT` will full verification and deprecates
`Slice.findNextPos` in favor of `Slice.posGT`.

The KMP implementation is adapted to use these two new functions.

Various useful string and order lemmas are added along the way.

Also add a `simp` attribute to `Std.le_refl` and fix the resulting
fallout (yes, this would have been better as a separate PR).
2026-02-04 09:45:44 +00:00
Lean stage0 autoupdater
5ec3b8c9d2 chore: update stage0 2026-02-03 23:07:52 +00:00
Henrik Böving
34620ec1f6 perf: speedup some fvar handling in LCNF (#12298) 2026-02-03 20:52:16 +00:00
Henrik Böving
7ba76bd33e refactor: port push_proj to LCNF (#12294)
This PR ports the `push_proj` pass from IR to LCNF. Notably it cannot
delete it from IR yet as the pass is still used later on.
2026-02-03 19:21:45 +00:00
Sebastian Graf
00c1f0d3a9 test: create aux theorems for backward rules in SymM-based mvcgen (#12295)
This PR improves and simplifies the SymM-based mvcgen prototype by
creating `BackwardRule.apply`-ready auxiliary theorems for spec
theorems. These auxiliary theorems have types that have reducible
definitions unfolded and shared, just like the rest of the SymM world
assumes. Furthermore, in order to aid kernel checking times,
definitional reductions leave behind expected type hints. With #12290,
we get the following numbers:

```
goal_100: 100.671964 ms, kernel: 34.104676 ms
goal_200: 152.650808 ms, kernel: 70.653251 ms
goal_300: 222.973242 ms, kernel: 105.874266 ms
goal_400: 294.032333 ms, kernel: 150.025106 ms
goal_500: 366.748098 ms, kernel: 193.483843 ms
goal_600: 442.509542 ms, kernel: 236.845115 ms
goal_700: 517.527685 ms, kernel: 268.804230 ms
goal_800: 601.657910 ms, kernel: 310.765606 ms
goal_900: 681.020759 ms, kernel: 357.428032 ms
goal_1000: 762.212989 ms, kernel: 403.789517 ms
```

The baseline is `shallow_add_sub_cancel`:

```
goal_100: 62.721757 ms, kernel: 22.109237 ms
goal_200: 140.118652 ms, kernel: 45.219512 ms
goal_300: 241.077690 ms, kernel: 78.779379 ms
goal_400: 363.274462 ms, kernel: 128.951250 ms
goal_500: 517.350791 ms, kernel: 155.498217 ms
goal_600: 678.291416 ms, kernel: 212.325487 ms
goal_700: 881.479043 ms, kernel: 258.690695 ms
goal_800: 1092.357375 ms, kernel: 351.996079 ms
goal_900: 1247.759480 ms, kernel: 319.197608 ms
goal_1000: 1497.203628 ms, kernel: 364.532560 ms
```

The latter is with the main solving loop in interpreter mode, but the
kernel checking times are still representative.
Earlier experiments suggest that the precompiled baseline performs at
roughly 650ms for `goal_1000`, so the new mvcgen is getting close.
2026-02-03 17:43:33 +00:00
Sebastian Graf
d1671aa25b fix: make PredTrans.apply semi-reducible (#12290)
This PR moves the `PredTrans.apply` structure field into a separate
`def`. Doing so improves kernel reduction speed because the kernel is
less likely to unfold definitions compared to structure field
projections. This causes minor shifts in `simp` normal forms.
2026-02-03 16:17:57 +00:00
Henrik Böving
3516b66019 chore: two typos in the impure refactoring (#12288) 2026-02-03 13:34:28 +00:00
Garmelon
a0cec1838e chore: move test suite setup to tests/CMakeLists.txt (#12278)
In preparation for adding the bench suite to the cmake-based test suite,
this PR moves test-related cmake code into the `tests` directory.

It also fixes a warning by removing an obsolete bit of the cmake code.
2026-02-03 13:22:20 +00:00
Sebastian Ullrich
c09700d72a fix: [local simp] on privately imported theorem (#12287)
This PR fixes an issue where `attribute [local simp]` was incorrectly
rejected on a theorem from a private import

Fixes #12198
2026-02-03 12:57:23 +00:00
dependabot[bot]
b0ef99f397 chore: CI: bump actions/cache from 4 to 5 (#11862)
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/cache/releases">actions/cache's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<blockquote>
<p>[!IMPORTANT]
<strong><code>actions/cache@v5</code> runs on the Node.js 24 runtime and
requires a minimum Actions Runner version of
<code>2.327.1</code>.</strong></p>
<p>If you are using self-hosted runners, ensure they are updated before
upgrading.</p>
</blockquote>
<hr />
<h2>What's Changed</h2>
<ul>
<li>Upgrade to use node24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1630">actions/cache#1630</a></li>
<li>Prepare v5.0.0 release by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1684">actions/cache#1684</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4.3.0...v5.0.0">https://github.com/actions/cache/compare/v4.3.0...v5.0.0</a></p>
<h2>v4.3.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Add note on runner versions by <a
href="https://github.com/GhadimiR"><code>@​GhadimiR</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1642">actions/cache#1642</a></li>
<li>Prepare <code>v4.3.0</code> release by <a
href="https://github.com/Link"><code>@​Link</code></a>- in <a
href="https://redirect.github.com/actions/cache/pull/1655">actions/cache#1655</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/GhadimiR"><code>@​GhadimiR</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/cache/pull/1642">actions/cache#1642</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4...v4.3.0">https://github.com/actions/cache/compare/v4...v4.3.0</a></p>
<h2>v4.2.4</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1620">actions/cache#1620</a></li>
<li>Upgrade <code>@actions/cache</code> to <code>4.0.5</code> and move
<code>@protobuf-ts/plugin</code> to dev depdencies by <a
href="https://github.com/Link"><code>@​Link</code></a>- in <a
href="https://redirect.github.com/actions/cache/pull/1634">actions/cache#1634</a></li>
<li>Prepare release <code>4.2.4</code> by <a
href="https://github.com/Link"><code>@​Link</code></a>- in <a
href="https://redirect.github.com/actions/cache/pull/1636">actions/cache#1636</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/nebuk89"><code>@​nebuk89</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/cache/pull/1620">actions/cache#1620</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4...v4.2.4">https://github.com/actions/cache/compare/v4...v4.2.4</a></p>
<h2>v4.2.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Update to use <code>@​actions/cache</code> 4.0.3 package &amp;
prepare for new release by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1577">actions/cache#1577</a>
(SAS tokens for cache entries are now masked in debug logs)</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/cache/pull/1577">actions/cache#1577</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v4.2.2...v4.2.3">https://github.com/actions/cache/compare/v4.2.2...v4.2.3</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/cache/blob/main/RELEASES.md">actions/cache's
changelog</a>.</em></p>
<blockquote>
<h1>Releases</h1>
<h2>Changelog</h2>
<h3>5.0.1</h3>
<ul>
<li>Update <code>@azure/storage-blob</code> to <code>^12.29.1</code> via
<code>@actions/cache@5.0.1</code> <a
href="https://redirect.github.com/actions/cache/pull/1685">#1685</a></li>
</ul>
<h3>5.0.0</h3>
<blockquote>
<p>[!IMPORTANT]
<code>actions/cache@v5</code> runs on the Node.js 24 runtime and
requires a minimum Actions Runner version of <code>2.327.1</code>.
If you are using self-hosted runners, ensure they are updated before
upgrading.</p>
</blockquote>
<h3>4.3.0</h3>
<ul>
<li>Bump <code>@actions/cache</code> to <a
href="https://redirect.github.com/actions/toolkit/pull/2132">v4.1.0</a></li>
</ul>
<h3>4.2.4</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.5</li>
</ul>
<h3>4.2.3</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.3 (obfuscates SAS token in
debug logs for cache entries)</li>
</ul>
<h3>4.2.2</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.2</li>
</ul>
<h3>4.2.1</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.1</li>
</ul>
<h3>4.2.0</h3>
<p>TLDR; The cache backend service has been rewritten from the ground up
for improved performance and reliability. <a
href="https://github.com/actions/cache">actions/cache</a> now integrates
with the new cache service (v2) APIs.</p>
<p>The new service will gradually roll out as of <strong>February 1st,
2025</strong>. The legacy service will also be sunset on the same date.
Changes in these release are <strong>fully backward
compatible</strong>.</p>
<p><strong>We are deprecating some versions of this action</strong>. We
recommend upgrading to version <code>v4</code> or <code>v3</code> as
soon as possible before <strong>February 1st, 2025.</strong> (Upgrade
instructions below).</p>
<p>If you are using pinned SHAs, please use the SHAs of versions
<code>v4.2.0</code> or <code>v3.4.0</code></p>
<p>If you do not upgrade, all workflow runs using any of the deprecated
<a href="https://github.com/actions/cache">actions/cache</a> will
fail.</p>
<p>Upgrading to the recommended versions will not break your
workflows.</p>
<h3>4.1.2</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9255dc7a25"><code>9255dc7</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/cache/issues/1686">#1686</a>
from actions/cache-v5.0.1-release</li>
<li><a
href="8ff5423e8b"><code>8ff5423</code></a>
chore: release v5.0.1</li>
<li><a
href="9233019a15"><code>9233019</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/cache/issues/1685">#1685</a>
from salmanmkc/node24-storage-blob-fix</li>
<li><a
href="b975f2bb84"><code>b975f2b</code></a>
fix: add peer property to package-lock.json for dependencies</li>
<li><a
href="d0a0e18134"><code>d0a0e18</code></a>
fix: update license files for <code>@​actions/cache</code>,
fast-xml-parser, and strnum</li>
<li><a
href="74de208dcf"><code>74de208</code></a>
fix: update <code>@​actions/cache</code> to ^5.0.1 for Node.js 24
punycode fix</li>
<li><a
href="ac7f1152ea"><code>ac7f115</code></a>
peer</li>
<li><a
href="b0f846b50b"><code>b0f846b</code></a>
fix: update <code>@​actions/cache</code> with storage-blob fix for
Node.js 24 punycode depr...</li>
<li><a
href="a783357455"><code>a783357</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/cache/issues/1684">#1684</a>
from actions/prepare-cache-v5-release</li>
<li><a
href="3bb0d78750"><code>3bb0d78</code></a>
docs: highlight v5 runner requirement in releases</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/cache/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/cache&package-manager=github_actions&previous-version=4&new-version=5)](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>
2026-02-03 12:37:56 +00:00
dependabot[bot]
1fe7e50ac2 chore: CI: bump actions/download-artifact from 6 to 7 (#11863)
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 6 to 7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/download-artifact/releases">actions/download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.0</h2>
<h2>v7 - What's new</h2>
<blockquote>
<p>[!IMPORTANT]
actions/download-artifact@v7 now runs on Node.js 24 (<code>runs.using:
node24</code>) and requires a minimum Actions Runner version of 2.327.1.
If you are using self-hosted runners, ensure they are updated before
upgrading.</p>
</blockquote>
<h3>Node.js 24</h3>
<p>This release updates the runtime to Node.js 24. v6 had preliminary
support for Node 24, however this action was by default still running on
Node.js 20. Now this action by default will run on Node.js 24.</p>
<h2>What's Changed</h2>
<ul>
<li>Update GHES guidance to include reference to Node 20 version by <a
href="https://github.com/patrikpolyak"><code>@​patrikpolyak</code></a>
in <a
href="https://redirect.github.com/actions/download-artifact/pull/440">actions/download-artifact#440</a></li>
<li>Download Artifact Node24 support by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/415">actions/download-artifact#415</a></li>
<li>fix: update <code>@​actions/artifact</code> to fix Node.js 24
punycode deprecation by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/451">actions/download-artifact#451</a></li>
<li>prepare release v7.0.0 for Node.js 24 support by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/452">actions/download-artifact#452</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/patrikpolyak"><code>@​patrikpolyak</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/download-artifact/pull/440">actions/download-artifact#440</a></li>
<li><a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/download-artifact/pull/415">actions/download-artifact#415</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/download-artifact/compare/v6.0.0...v7.0.0">https://github.com/actions/download-artifact/compare/v6.0.0...v7.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="37930b1c2a"><code>37930b1</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/452">#452</a>
from actions/download-artifact-v7-release</li>
<li><a
href="72582b9e0a"><code>72582b9</code></a>
doc: update readme</li>
<li><a
href="0d2ec9d4cb"><code>0d2ec9d</code></a>
chore: release v7.0.0 for Node.js 24 support</li>
<li><a
href="fd7ae8fda6"><code>fd7ae8f</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/451">#451</a>
from actions/fix-storage-blob</li>
<li><a
href="d484700543"><code>d484700</code></a>
chore: restore minimatch.dep.yml license file</li>
<li><a
href="03a808050e"><code>03a8080</code></a>
chore: remove obsolete dependency license files</li>
<li><a
href="56fe6d904b"><code>56fe6d9</code></a>
chore: update <code>@​actions/artifact</code> license file to 5.0.1</li>
<li><a
href="8e3ebc4ab4"><code>8e3ebc4</code></a>
chore: update package-lock.json with <code>@​actions/artifact</code><a
href="https://github.com/5"><code>@​5</code></a>.0.1</li>
<li><a
href="1e3c4b4d49"><code>1e3c4b4</code></a>
fix: update <code>@​actions/artifact</code> to ^5.0.0 for Node.js 24
punycode fix</li>
<li><a
href="458627d354"><code>458627d</code></a>
chore: use local <code>@​actions/artifact</code> package for Node.js 24
testing</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/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=actions/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>
2026-02-03 12:37:46 +00:00
Lean stage0 autoupdater
70f4530dfa chore: update stage0 2026-02-03 11:15:37 +00:00
Henrik Böving
26f6bc67ee feat: lambda pure conversion in LCNF (#12272)
This PR shifts the conversion from LCNF mono to lambda pure into the
LCNF impure phase. This is preparatory work for the upcoming refactor of
IR into LCNF impure.

The LCNF impure phase differs from the other LCNF phases in two crucial
ways:
1. I decided to have `Decl.type` be the result type as opposed to an
arrows from the parameter types to the result type. This is done because
impure does not have a notion of arrows anymore so keeping them around
for this one particular purpose would be slightly odd.
2. In order to avoid cluttering up the olean size LCNF impure saves only
the signature persistently to the disk. This is possible because we no
longer have inlining/specialization at this point of compilation so all
we need is typing information (and potentially other environment
extensions) to guide our analyses.
2026-02-03 10:24:59 +00:00
Joachim Breitner
2907df22ec feat: one axiom per native computation (#12217)
This PR implements RFC #12216: native computation (`native_decide`,
`bv_decide`) is represented in the logic as one axiom per computation,
asserting the equality that was obtained from the native computation.
`#print axiom` will no longer show `Lean.trustCompiler`, but rather the
auto-generated names of these axioms (with, for example,
`._native.bv_decide.` in the name). See the RFC for more information.


This PR introduces a common MetaM helper (`nativeEqTrue`) used by
`native_decide` and `bv_decide` alike that runs the computation and then
asserts the result using an axiom.

It also deprecated the `ofReduceBool` axioms etc.

Not included in this PR is infrastructure for enumerating these axioms,
prettier `#print axioms` (should we want his) and tactic concurrency.

Fixes #12216.
2026-02-03 10:15:01 +00:00
Leonardo de Moura
e02a140080 feat: @[instance_reducible] part 2 (#12263)
This PR implements the second part of #12247.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2026-02-03 04:01:13 +00:00
Leonardo de Moura
3deba604bf feat: cache output universe parameter positions (#12285)
This PR implements a cache for the positions of class universe level
parameters that only appear in output parameter types.

During type class resolution, the cache key for a query like
`HAppend.{0, 0, ?u} (BitVec 8) (BitVec 8) ?m` should be independent of
the specific metavariable IDs in output parameter positions. To achieve
this, output parameter arguments are erased from the cache key. However,
universe levels that only appear in output parameter types (e.g., `?u`
corresponding to the result type's universe) must also be erased to
avoid cache misses when the same query is issued with different universe
metavariable IDs.

This function identifies which universe level parameter positions are
"output-only" by collecting all level param names that appear in
non-output parameter domains, then returning the positions of any level
params not in that set.

**Remark**: This PR requires a manual update stage0 because it changes
the structure of our .olean files.
2026-02-02 19:56:33 -08:00
Lean stage0 autoupdater
3ad3bacd97 chore: update stage0 2026-02-03 02:34:19 +00:00
Joachim Breitner
c27ea08450 fix: set isRecursive only after adding the declaration (#12269)
This PR refines upon #12106, by setting the `isRecursive` env extension
after adding the declaration, but before processing attributes like
`macro_inline` that want to look at the flag. Fixes #12268.
2026-02-02 17:13:08 +00:00
Marcelo Lynch
5d41b3bdce fix: avoid deadlock by not throttling workers when the task manager is shutting down (#12052)
This PR avoids a potential deadlock on shutdown of a Lean program when
the number of pooled threads has temporarily been pushed above the
limit.

There's a potential race between the finalizer "waking up everyone"
after setting `m_shutting_down = true` and a worker that is about to be
throttled because of concurrency limits.

- `m_max_std_workers = 1`, `m_std_workers.size() = 2`, and the queue
still has tasks.
- Finalizer sets `m_shutting_down = true` and calls `notify_all()` while
a worker is running a task (outside of the mutex).
- Worker finishes a task, re-enters the loop, sees work, and "should
wait" because `active >= max`.
- Worker then calls `wait()` after the notify and never wakes, so
`join()` in the finalizer hangs.

This PR avoids the worker being blocked by not `wait()`ing if we are
already shutting down. The code is restructured a bit for readability,
where the first section is "there's no work in the queue" and the next
section is "there is some work in the queue"
2026-02-02 16:14:32 +00:00
Lean stage0 autoupdater
240c64b20c chore: update stage0 2026-02-02 14:36:04 +00:00
Wojciech Różowski
a966a192b7 fix: unification issue in proofs generated by Lean.Meta.MkIffOfInductiveProp (#12219)
This PR fixes a unification issue that appeared in
`Lean.Meta.MkIffOfInductiveProp` machinery that was upstreamed from
Mathlib. Inside of `toInductive`, wrong free variables were passed,
which made it impossible to perform a unification in certain cases.

Closes #12215
2026-02-02 10:37:44 +00:00
Paul Reichert
3c64f6a749 feat: lemmas about sums of lists/arrays/vectors (#11994)
This PR provides more lemmas about sums of lists/arrays/vectors,
especially sums of `Nat` or `Int` lists/arrays/vectors.

This change has been motivated by my experience solving
`human-eval-lean` problems. Sums, minima and maxima are frequently
required and the improvements provided in this PR make it easier to
verify such programming tasks.

Changes:
* Added lemmas that `sum` equals `foldl`/`foldr`.
* Generalized `sum_append_nat` and `sum_reverse_nat` lemmas so that they
are polymorphic, requiring only some type class instances about the list
elements' type. The polymorphic lemmas aren't simp- or grind-annotated
because I fear the instance synthesis overhead. However, the `Nat` and
`Int` specializations are annotated (see below). Note that as
`{Array,Vector}.min` do not exist, some lemmas can't be stated and were
omitted.
* Added `List.min_singleton` and `List.max_singleton` lemmas as they
were needed for some proofs.
* `Nat`-related:
* Moved all `{List,Array,Vector}.sum` lemmas that are specific for `Nat`
into their own module: `Init.Data.List.Nat.Sum`, `Init.Data.Array.Nat`
and `Int.Data.Vector.Nat`.
* Notably, moved `Nat.sum_pos_iff_exists_pos` and renamed it to
`List.sum_pos_iff_exists_pos_nat`. This is more consistent and made it
possible to add `Array` and `Vector` variants of this lemma.
* Added lemmas proving that `l.sum / l.length` lies between the minimum
and the maximum of a list.
* Added analogous lemmas for `Int` lists/arrays/vectors to parallel
modules: `Init.Data.List.Int.Sum`, `Init.Data.Array.Int` and
`Int.Data.Vector.Int`.
* Renamed `sum_eq_sum_toList` to `sum_toList`, which better represents
the theorem's content.
2026-02-02 07:52:36 +00:00
Lean stage0 autoupdater
c7a9a4e18c chore: update stage0 2026-02-01 17:04:45 +00:00
Sebastian Ullrich
a7b9a3def6 refactor: move getOriginalConstKind? into its own module to avoid future import cycle (#12265) 2026-02-01 16:18:51 +00:00
Paul Reichert
c25468f057 feat: various small list/array/vector API improvements (#12017)
This PR makes several small improvements to the list/array/vector API:
* It fixes typos in `Init.Core`.
* It adds `List.isSome_min_iff` and `List.isSome_max_iff`.
* It adds `grind` and `simp` annotations to various previously
unannotated lemmas.
* It adds lemmas for characterizing `∃ x ∈ xs, P x` using indices as `∃
(i : Nat), ∃ hi, P (xs[i])`, and similar universally quantified lemmas:
`exists_mem_iff_exists_getElem` and `forall_mem_iff_forall_getElem`.
* It adds `Vector.toList_zip`.
* It adds `map_ofFn` and `ofFn_getElem` for lists/arrays/vectors.
2026-02-01 13:21:38 +00:00
Kim Morrison
a362093730 chore: update grind IndexMap example (#12264)
This PR cleans up the `IndexMap` examples for grind, after #11963.
2026-02-01 09:57:41 +00:00
Lean stage0 autoupdater
ec60620534 chore: update stage0 2026-02-01 03:54:16 +00:00
Leonardo de Moura
4606c35c40 feat: @[instance_reducible] (#12247)
This PR adds the new transparency setting `@[instance_reducible]`. We
used to check whether a declaration had `instance` reducibility by using
the `isInstance` predicate. However, this was not a robust solution
because:

- We have scoped instances, and `isInstance` returns `true` only if the
scope is active.

- We have auxiliary declarations used to construct instances manually,
such as:

```lean
    def lt_wfRel : WellFoundedRelation Nat
```
    
`isInstance` also returns `false` for this kind of declaration.

In both cases, the declaration may be (or may have been) used to
construct an instance, but `isInstance`
returns `false`. Thus, we claim it is a mistake to check the
reducibility status using `isInstance`.
`isInstance` indicates whether a declaration is available for the type
class resolution mechanism,
not its transparency status.

**We are decoupling whether a declaration is available for type class
resolution from its transparency status.**

**Remak**: We need a update stage0 to complete this feature.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2026-02-01 03:03:16 +00:00
Leonardo de Moura
9f4c81342e chore: cleanup test (#12262) 2026-01-31 23:22:13 +00:00
Mac Malone
89c01c9e7e fix: lake: facet names in unknown facet errors (#12261)
This PR fixes a bug in Lake where the facet names printed in unknown
facet errors would contain the internal facet kind.
2026-01-31 20:57:13 +00:00
Mac Malone
ce980895b2 fix: IO.Process.spawn empty env var on Windows (#12220)
This PR fixes a bug on Windows with `IO.Process.spawn` where setting an
environment variable to the empty string would not set the environment
variable on the subprocess.
2026-01-31 19:17:26 +00:00
Wojciech Różowski
6c5de545f9 feat: add orElse combinator to Sym.Simp.Simproc (#12236)
This PR adds `orElse` combinator to simprocs of `Sym.Simp`.
2026-01-31 18:34:19 +00:00
Leonardo de Moura
21a281b496 fix: bug in instantiateRangeS' (#12260)
This PR fixes a bug in the function `instantiateRangeS'` in the `Sym`
framework.
2026-01-31 17:50:03 +00:00
Paul Reichert
7cd6b78a9c feat: Std.Iter.isEmpty (#12212)
This PR adds the function `Std.Iter.isEmpty` and proves the
specification lemmas `Std.Iter.isEmpty_eq_match_step` and
`Std.Iter.isEmpty_toList` if the iterator is productive.

The monadic variant on `Std.IterM` is also provided.
2026-01-31 16:18:35 +00:00
Paul Reichert
b64e5dec1e feat: projected minima and maxima (#11938)
This PR introduces projected minima and maxima, also known as
"argmin/argmax", for lists under the names `List.minOn` and
`List.maxOn`. It also introduces `List.minIdxOn` and `List.maxIdxOn`,
which return the index of the minimal or maximal element. Moreover,
there are variants with `?` suffix that return an `Option`. The change
further introduces new instances for opposite orders, such as
`LE.opposite`, `IsLinearOrder.opposite` etc. The change also adds the
missing `Std.lt_irrefl` lemma.
2026-01-31 16:16:32 +00:00
Leonardo de Moura
d1514f3cec perf: cache unfold_definition in the kernel (#12259)
This PR ensures we cache the result of `unfold_definition` definition in
the kernel type checker. We used to cache this information in a thread
local storage, but it was deleted during the Lean 3 to Lean 4
transition.
2026-01-31 03:44:50 +00:00
Kim Morrison
a972c4f50d fix: include local variable dot notation params in grind? suggestions (#12224)
This PR fixes a bug where `grind?` suggestions would not include
parameters using local variable dot notation (e.g.,
`cs.getD_rightInvSeq` where `cs` is a local variable). These parameters
were incorrectly filtered out because the code assumed all ident params
resolve to global declarations. In fact, local variable dot notation
produces anchors that need the original term to be loaded during replay,
so they must be preserved in the suggestion.

Closes #12185

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 00:34:28 +00:00
Sebastian Graf
7416309805 test: teach SymM mvcgen to recognize specialized theorem applications (#12256)
This PR recognizes certain kinds of composite proof terms of the form
`hpre.trans hspec |> (wp prog).mono _ _ hpost` and abstracts them into
bespoke theorems. This should yield smaller proof terms. Sadly, kernel
checking time is unaffected, even regressing a bit. The number of shared
terms stays almost the same (+- a constant). Hence I deactivate the code
path in this patch. We keep the code, though, because it might be useful
in the future, also there are a few other improvements.
2026-01-30 17:00:59 +00:00
Kim Morrison
bb68f31527 doc: add pp.mvars advice to #guard_msgs docstring (#12253)
This PR adds a "Stabilizing output" section to the `#guard_msgs`
docstring, explaining how to use `pp.mvars.anonymous` and `pp.mvars`
options to stabilize output containing autogenerated metavariable names
like `?m.47`.

This was prompted by discussion on Zulip about improving #mwe
documentation:
https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/JacobiZariski.20is.20slow.2E/near/570739745

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 16:13:24 +00:00
Sebastian Ullrich
85341d02ac feat: immediate noncomputable check (#12028)
This PR gives a simpler semantics to `noncomputable`, improving
predictability as well as preparing codegen to be moved into a separate
build step without breaking immediate generation of error messages.

Specifically, `noncomputable` is now needed whenever an axiom or another
`noncomputable` def is used by a def except for the following special
cases:
* uses inside proofs, types, type formers, and constructor arguments
corresponding to (fixed) inductive parameters are ignored
* uses of functions marked `@[extern]/@[implemented_by]/@[csimp]` are
ignored
* for applications of a function marked `@[macro_inline]`,
noncomputability of the inlining is instead inspected

# Breaking change

After this change, more `noncomputable` annotations than before may be
required in exchange for improved future stability.
2026-01-30 16:07:25 +00:00
Sebastian Graf
59f3abd0bd test: add SymM mvcgen and port the add_sub_cancel benchmark (#12251)
This PR adds a clone of the `mvcgen` tactic based on `SymM` and
evaluates it based on a ported `add_sub_cancel` benchmark. Notably, it
can reuse all the existing `@[spec]`-annotated theorems to generate VCs.
(It doesn't do control-flow splitting, simp rules on the program
expression or handling of lets; we'll get there.)

It is quite fast already, with the kernel being the bottle-neck:

```
goal_50: 69.524305 ms, kernel: 155.327778 ms
goal_100: 93.834221 ms, kernel: 407.370786 ms
goal_150: 131.364098 ms, kernel: 762.936720 ms
goal_200: 169.577172 ms, kernel: 1181.199093 ms
goal_250: 206.421738 ms, kernel: 1707.539380 ms
```

```
goal_200: 169.458637 ms, kernel: 1186.221085 ms
goal_400: 322.819718 ms, kernel: 3791.613854 ms
goal_600: 474.929013 ms, kernel: 7763.373757 ms
goal_800: 634.379422 ms, kernel: 13107.810430 ms
```

It is best compared to the `solveUsingSym <n> false true` measurements
of the SymM `add_sub_cancel` benchmark (`false`: without intermediate
eager simplification). For `n=200`, it reports

```
goal_200: 779.482300 ms, kernel: 742.097404 ms
```

suggesting that the generated proof term could be improved for kernel
reduction. (TODO.)
I'm unsure whether `solveUsingSym` is run in interpreted mode, so take
the >400% speedup with a grain of salt.
We can definitely conclude that VC generation time is currently not a
bottleneck compared to kernel checking time.

Plot for discharging goals of sizes 100..800:
<img width="1000" height="600" alt="Code_Generated_Image(1)"
src="https://github.com/user-attachments/assets/90e76a45-fa46-4d02-912a-c3355e2aa094"
/>

Plot comparing Kernel and Goal time: 
<img width="1000" height="600" alt="Code_Generated_Image(2)"
src="https://github.com/user-attachments/assets/5849ba0f-1d83-4f2d-98dd-fa65b840bb4e"
/>
2026-01-30 15:12:54 +00:00
Sebastian Graf
2f3912df74 feat: define Triple.iff, Triple.iff_conseq etc. and use defeq less (#12250)
This PR introduces the defining equality `Triple.iff` and uses that in
proofs instead of relying on definitional equality. It also introduces
`Triple.iff_conseq` that is useful for backward reasoning and introduces
verification conditions. Similarly, `Triple.entails_wp_*` theorems are
introduced for backward reasoning where the target is an stateful
entailment rather than a triple.
2026-01-30 14:03:22 +00:00
Henrik Böving
5ce756f350 refactor: introduce a phase separation to the IR (#12214)
This PR introduces a phase separation to the LCNF IR. This is a
preparation for the merge of
the old `Lean.Compiler.IR` and the new `Lean.Compiler.LCNF` framework.

The change parametrizes all relevant `LCNF` data structures over a
`Purity` parameter and
additionally carries around proofs that the `Purity` has certain values,
depending on what's
required. This is done as opposed to indexing the types over `Purity`
because we do (almost) never
have to store the `Purity` value for phase generic structures this way.
2026-01-30 09:42:29 +00:00
Lean stage0 autoupdater
6d370ec3c2 chore: update stage0 2026-01-30 09:12:55 +00:00
Henrik Böving
332c1ec46a perf: specializer a little more courageously (#12239)
This PR reverts a lot of the changes done in #8308. We practically
encountered situations such as:
```
fun y (z) :=
  let x := inst
  mkInst x z
f y
```
Where the instance puller turns it into:
```
let x := inst
fun y (z) :=
  mkInst x z
f y
```
The current heuristic now discovers `x` being in scope at the call site
of `f` and being used under a binder in `y` and thus blocks pulling in
`x` to the specialization, abstracting over an instance.

According to @zwarich this was done at the time either due to observed
stack overflows or pulling in computation into loops. With the current
configuration for abstraction in specialization it seems rather unlikely
that we pull in a non trivial computation into a loop with this. We also
practically didn't observe stack overflows in our tests or benchmarks.
Cameron speculates that the issues he observed might've been fixed
otherwise by now.

Crucial note: Deciding not to abstract over ground terms *might* cause
us to pull in computationally intensive ground terms into a loop. We
could decide to weaken this to just instance terms though of course even
computing instances might end up being non-trivial.
2026-01-30 08:23:15 +00:00
Joachim Breitner
4c5e3d73af fix: deriving Ord with indexed data type (#12243)
This PR fixes #12240, where `deriving Ord` failed with `Unknown
identifier a✝`.
2026-01-29 20:50:14 +00:00
Sebastian Ullrich
2b2b72d113 test: more .git cleanup (#12238)
Co-authored-by: Mac Malone <mac@lean-fro.org>
2026-01-29 17:43:31 +00:00
Garmelon
5b0b365406 chore: stop make install from printing every individual file (#12235)
https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_MESSAGE.html
2026-01-29 16:50:21 +00:00
Sebastian Ullrich
892cbe22f8 fix: run @[init] declarations in declaration order (#12221)
Fixes #10175 harder.
2026-01-29 15:32:56 +00:00
Paul Reichert
3883f0f669 feat: min(?)/max(?) for Array (#11936)
This PR provides `Array` operations analogous to `List.min(?)` and
`List.max(?)`.

I had to prove a few auxiliary lemmas. Downstream in Batteries, which
already had `List.min` and `List.max`, I renamed their variants to
`List.rangeMin` and `List.rangeMax` in the PR testing branch. Their
version is more general in the sense that it has `start` and `stop`
autoParams, like `Array.foldl` has, but I think the futore belongs to
`Subarray.min` instead (which I haven't implemented yet).
2026-01-29 14:12:02 +00:00
Marc Huisinga
30c8b39b23 test: fix broken uri test (#12230) 2026-01-29 13:52:36 +00:00
Paul Reichert
e7b6bd6734 refactor: rename Iter(M).count to Iter(M).length (#12210)
This PR renames `Iter(M).count` to `Iter(M).length` and updates lots of
lemmas, adding deprecations.
2026-01-29 07:26:13 +00:00
Paul Reichert
16919852d9 refactor: remove last appearances of allowNontermination (#12211)
This PR updates docstrings and function signatures in order to complete
the transition from `Iter.Partial` to `Iter.Total` (extrinsically
terminating by default). It also deprecates `allowNontermination` and
adds `Iter.Total.atIdxSlow?`.
2026-01-29 07:22:19 +00:00
Leonardo de Moura
29545dcf10 feat: do not dsimp instances (#12195)
This PR ensures `dsimp` does not "simplify" instances by default. The
old behavior can be retrieved by using
```
set_option backward.dsimp.instances true
```
Applying `dsimp` to instances creates non-standard instances, and this
creates all sorts of problems in Mathlib.
This modification is similar to
```
set_option backward.dsimp.proofs true
```

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 05:25:01 +00:00
Kim Morrison
b772852522 fix: verify PR release artifacts before creating tags (#12223)
This PR moves the artifact verification step before tag creation and
release deletion, so we fail early if no artifacts are available rather
than creating side effects that would need to be cleaned up.

Addresses feedback from
https://lean-fro.zulipchat.com/#narrow/channel/399079-infrastructure/topic/PR.20toolchain.20even.20if.20test.20suite.20fails/near/570482678

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 04:41:11 +00:00
Kim Morrison
ebec1b3a16 fix: typo in ExtractLetsConfig doc comment (#12174)
This PR fixes a typo in `ExtractLetsConfig.merge` doc comment.

Reported on Zulip:
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Typo.20in.20Init.2FMetaTypes.2Elean/near/568698828

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 04:40:43 +00:00
Kim Morrison
00c8431cf8 doc: add changelog label instructions to CLAUDE.md (#12227)
This PR documents the available `changelog-*` labels and when to use
them in the project-specific CLAUDE.md instructions.

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 03:24:23 +00:00
Rob23oba
b919cfff30 fix: public section in Dyadic files (#12199)
This PR fixes `Init.Data.Dyadic.Instances` and `Init.Data.Dyadic.Inv`.
Previously, all declarations defined in boths file were private and not
exposed.
2026-01-29 03:05:43 +00:00
Kim Morrison
e441ed8e46 Revert "doc: add changelog label instructions to CLAUDE.md"
This reverts commit 119533d602.
2026-01-29 03:16:10 +00:00
Kim Morrison
119533d602 doc: add changelog label instructions to CLAUDE.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 03:14:38 +00:00
Kim Morrison
9b9ce0c2ac feat: adjust grind annotations for List.drop (#12170)
This PR adjusts the grind annotations for List.take/drop, and adds two
theorems.

This resolves problems @datokrat encountered while working on
https://github.com/leanprover/human-eval-lean/blob/master/HumanEvalLean/HumanEval114.lean.
2026-01-29 00:27:46 +00:00
Leonardo de Moura
3f0acbbb48 fix: use isClass? instead of binder annotation to identify instance parameters (#12172)
This PR fixes how we determine whether a function parameter is an
instance.
Previously, we relied on binder annotations (e.g., `[Ring A]` vs `{_ :
Ring A}`)
to make this determination. This is unreliable because users
legitimately use
`{..}` binders for class types when the instance is already available
from
context. For example:
```lean
structure OrdSet (α : Type) [Hashable α] [BEq α] where
  ...

def OrdSet.insert {_ : Hashable α} {_ : BEq α} (s : OrdSet α) (a : α) : OrdSet α :=
  ...
```

Here, `Hashable` and `BEq` are classes, but the `{..}` binder is
intentional, the
instances come from `OrdSet`'s parameters, so type class resolution is
unnecessary.

The fix checks the parameter's *type* using `isClass?` rather than its
syntax, and
caches this information in `FunInfo`. This affects several subsystems:

- **Discrimination trees**: instance parameters should not be indexed
even if marked with `{..}`
- **Congruence lemma generation**: instances require special treatment
- **`grind` canonicalizer**: must ensure canonical instances

**Potential regressions**: automation may now behave differently in
cases where it
previously misidentified instance parameters. For example, a rewrite
rule in `simp` that was
not firing due to incorrect indexing may now fire.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 20:33:43 +00:00
Garmelon
6dcd6c8f08 chore: reformat all cmake files (#12218)
The script to run for reformatting is `script/fmt`.
2026-01-28 18:23:08 +00:00
Eric Wieser
71be4901c3 fix: do not compile with -fwrapv (#12132)
This PR removes the requirement that libraries compiled against the lean
headers must use `-fwrapv`.

clang
[documents](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#:~:text=Note%20that%20checks%20are%20still%20added%20even%20when%20%2Dfwrapv%20is%20enabled)
that `-fwrapv` does not automatically turn off the integer overflow
sanitizer; and so overflow should still be avoided in normal execution.

This is a retry of #12098 after it was reverted in #12125.
2026-01-28 16:16:15 +00:00
Garmelon
5e13e71a84 chore: fix cmake if conditions (#12213)
Due to the way variable expansion and if interact in cmake, unquoted
variable expansions should essentially never be used inside if and may
lead to unexpected behavior. Also, quoted variable expansions can
usually be replaced by the unquoted variable name.

For more details, see this section in the cmake docs:
https://cmake.org/cmake/help/latest/command/if.html#variable-expansion

As one example of the kinds of issues that can occur with unquoted
variable expansions, consider this check from
`src/shell/CMakeLists.txt`, which tries to ensure that a test is only
run in non-WASM builds.

```cmake
if(NOT ${EMSCRIPTEN})
```

If the variable `EMSCRIPTEN` is empty or not defined (as is the case in
a non-WASM build), `${EMSCRIPTEN}` expands to 0 arguments, meaning the
check becomes

```cmake
if(NOT)
```

Since the `NOT` is unquoted, the if now tries to resolve it as a
variable. Since the variable `NOT` does not exist, the condition is
false and the test is never executed, even in non-WASM builds.
2026-01-28 15:37:18 +00:00
Henrik Böving
08ee91a433 feat: add DecidableEq instances for Sigma and PSimga (#12193)
This PR adds `DecidableEq` instances for `Sigma` and `PSigma`.
2026-01-28 15:00:45 +00:00
Sebastian Ullrich
f790ff1961 chore: remove obsolete repeat macro 2026-01-28 16:27:57 +01:00
Sebastian Ullrich
c4aac5d7c5 chore: update stage0 2026-01-28 16:27:57 +01:00
Sebastian Ullrich
316761c202 perf: make repeat an elaborator 2026-01-28 16:27:57 +01:00
Paul Reichert
b248b13ac2 feat: add useful lemmas about division (#12019)
This PR provides the `Nat`/`Int` lemmas `x ≤ y * z ↔ (x + z - 1) / z ≤
y`, `x ≤ y * z ↔ (x + y - 1) / y ≤ z` and `x / z + y / z ≤ (x + y) / z`.

The PR is inspired by a `human-eval-lean` problem, the solution of which
required these lemmas.
2026-01-28 14:17:47 +00:00
Joachim Breitner
08f43acefb perf: add introSubstEq shortcut (#12190)
This PR adds the `introSubstEq` MetaM tactic, as an optimization over
`intro h; subst h` that avoids introducing `h : a = b` if it can be
avoided,
which is the case when `b` can be reverted without reverting anything
else. Speeds up the generation of `injEq` theorem.
2026-01-28 12:33:14 +00:00
Sebastian Graf
9a37dba765 chore: express SPred lemmas using Iff instead of Eq (#12209) 2026-01-28 10:19:55 +00:00
Henrik Böving
a47eb31076 chore: remove the LCNF testing framework (#12207)
This PR removes the LCNF testing framework. Unfortunately it never got
used much and porting it to
the extended LCNF structure now would be a bit of effort that would
ultimately be in vain.
2026-01-28 10:09:30 +00:00
Marc Huisinga
819fb6a6a8 fix: use windows path separators in System.Uri.fileUriToPath? (#12197)
This PR fixes a bug in `System.Uri.fileUriToPath?` where it wouldn't use
the default Windows path separator in the path it produces.

It also adjusts the URI patching in the interactive test runner to be
more robust.
2026-01-28 09:10:34 +00:00
Leonardo de Moura
9e18eea271 feat: add mkBackwardRuleFromExpr (#12205)
This PR adds `mkBackwardRuleFromExpr` to create backward rules from
expressions, complementing the existing `mkBackwardRuleFromDecl` which
only works with declaration names.

The new function enables creating backward rules from partially applied
terms. For example, `mkBackwardRuleFromExpr (mkApp (mkConst
``Exists.intro [1]) Nat.mkType)` creates a rule for `Exists.intro` with
the type parameter fixed to `Nat`, leaving only the witness and proof as
subgoals.

The `levelParams` parameter supports universe polymorphism: when
creating a rule like `Prod.mk Nat` that should work at multiple universe
levels, the caller specifies which level parameters remain polymorphic.
The pattern's universe variables are then instantiated appropriately at
each application site.

Also refactors `Pattern.lean` to share code between declaration-based
and expression-based pattern creation, extracting `mkPatternFromType`
and `mkEqPatternFromType` as common helpers.
2026-01-28 05:00:15 +00:00
Kim Morrison
fa4cd6d78c feat: add theorems relating find? with findIdx? and findFinIdx? (#12204)
This PR adds theorems showing the consistency between `find?` and the
various index-finding functions. The theorems establish bidirectional
relationships between finding elements and finding their indices.

**Forward direction** (find? in terms of index):
- `find?_eq_map_findFinIdx?_getElem`: `xs.find? p = (xs.findFinIdx?
p).map (xs[·])`
- `find?_eq_bind_findIdx?_getElem?`: `xs.find? p = (xs.findIdx? p).bind
(xs[·]?)`
- `find?_eq_getElem?_findIdx`: `xs.find? p = xs[xs.findIdx p]?`

**Reverse direction** (index in terms of find?):
- `findIdx?_eq_bind_find?_idxOf?`: `xs.findIdx? p = (xs.find? p).bind
(xs.idxOf?)`
- `findFinIdx?_eq_bind_find?_finIdxOf?`: `xs.findFinIdx? p = (xs.find?
p).bind (xs.finIdxOf?)`
- `findIdx_eq_getD_bind_find?_idxOf?`: `xs.findIdx p = ((xs.find?
p).bind (xs.idxOf?)).getD xs.length`

All theorems are provided for `List`, `Array`, and `Vector` (where
applicable).

Requested at
https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/show.20that.20Array.2Efind.3F.20and.20Array.2EfindFinIdx.3F.20consistent/near/567340199

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 04:55:29 +00:00
Kim Morrison
e1b19198a9 feat: another grind_pattern for getElem?_pos (#11963)
This PR activates `getElem?_pos` more aggressively, triggered by `c[i]`.

- [x] depends on: #12176

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 03:45:54 +00:00
Mac Malone
1590c9c9d9 chore: lake: fix tests on non-Linux platforms (#11955)
This PR fixes failures in the Lake tests on non-Linux platforms.
2026-01-28 03:32:49 +00:00
Kim Morrison
cd8280700d fix: create PR releases even when test suite fails (#12202)
This PR fixes an issue where PR releases were not created when the test
suite failed, even though the build artifacts were available. The
workflow now runs whenever a PR's CI completes, regardless of
success/failure, and relies on the artifact verification step to ensure
the necessary build artifacts exist before proceeding.

This allows developers to get PR toolchains and test against Mathlib
even when the Lean test suite has failures, as long as the build jobs
succeeded.

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 03:13:48 +00:00
Kim Morrison
2e779f79de fix: bump numInstances for delayed grind theorem instances (#12176)
This PR fixes a bug where delayed E-match theorem instances could cause
uniqueId collisions in the instance tracking map.

The `uniqueId` for theorem instances is generated using `numInstances`,
but this counter was only bumped for immediately activated instances
(`.ready` case), not for delayed instances (`.next` case). This caused
ID collisions:

1. Theorem A matches, becomes delayed, gets `uniqueId = N`
2. Counter isn't bumped (stays at N)
3. Theorem B matches next, gets `uniqueId = N` (same!)
4. B's entry overwrites A's entry in `instanceMap`
5. A's tracking is lost

This manifested as `grind?` and `finish?` producing `instantiate approx`
(meaning "we couldn't determine which theorems to use") instead of
proper `instantiate only [...]` with specific theorem lists.

The fix bumps `numInstances` for delayed instances too, ensuring each
theorem instance gets a truly unique ID.

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 03:09:27 +00:00
Leonardo de Moura
50bbf101be test: delayed assignment performance issue (#12201)
This PR adds a benchmark that exposes a performance issue at
`instantiateMVars` when there are many nested delayed assignments.
2026-01-28 02:08:39 +00:00
Elazar Gershuni
90ba5f3f40 feat: add prefix and suffix map injectivity lemmas (#12108)
This PR adds `prefix_map_iff_of_injective` and
`suffix_map_iff_of_injective` lemmas to Init.Data.List.Nat.Sublist.

These lemmas establish that if a function `f` is injective, then the
prefix and suffix relations are preserved under mapping (e.g., `l₁.map f
<+: l₂.map f ↔ l₁ <+: l₂`). These additions complement the existing
index-based lemmas in this file and allow for simpler structural proofs
without resorting to `take`, `drop`, or manual index manipulation.
2026-01-27 22:54:16 +00:00
Lean stage0 autoupdater
cedc641fd5 chore: update stage0 2026-01-27 20:33:17 +00:00
Eric Wieser
afee8aa1c1 fix: avoid SIGFPE on x86_64 for ISize division overflow (#12110)
This PR fixes a SIGFPE crash on x86_64 when evaluating `(ISize.minValue
/ -1 : ISize)`, filling an omission from #11624.

Closes #12097.
2026-01-27 19:36:07 +00:00
Henrik Böving
31e4eb62b7 perf: speed up compiler recompilation (#12196) 2026-01-27 18:50:58 +00:00
Marc Huisinga
4bcb3cea42 test: make interactive runner uri patching slightly more robust (#12194) 2026-01-27 16:23:46 +00:00
Joachim Breitner
36bae38e6b test: add big_struct_dep1 benchmark (#12191) 2026-01-27 14:36:09 +00:00
Markus Himmel
ba0e755adc feat: Std.Iter.first? (#12162)
This PR adds the function `Std.Iter.first?` and proves the specification
lemma `Std.Iter.first?_eq_match_step` if the iterator is productive.

The monadic variant on `Std.IterM` is also provided.

We use this new function to fix the default implementation for
`startsWith` and `dropPrefix` on `String` patterns, which used to fail
if the searcher returned a `skip` at the beginning. None of the patterns
we ship out of the box were affected by this, but user-defined patterns
were vulnerable.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2026-01-27 12:10:16 +00:00
Kim Morrison
596827c0e9 test: add regression test for structure-extends-class congr (#12187)
This PR adds regression tests that catch issues where structures/classes
with class-typed fields produce HEq goals in `congr` instead of handling
Prop fields automatically.

Both tests pass on v4.28.0-rc1 (before isInstance detection changes).

## Test 1: Structure extending classes (mirrors Mathlib's GroupTopology)

```lean
structure MyGroupTopology (α : Type) extends MyTopology α, IsContinuousMul α

theorem MyGroupTopology.toMyTopology_injective {α : Type} :
    Function.Injective (MyGroupTopology.toMyTopology : MyGroupTopology α → MyTopology α) := by
  intro f g h
  cases f
  cases g
  congr
```

**Failure mode:** `⊢ toIsContinuousMul✝¹ ≍ toIsContinuousMul✝`

## Test 2: Class with explicit class-typed field (mirrors Mathlib's
PseudoEMetricSpace)

```lean
class MyMetricSpace (α : Type) extends MyDist α where
  dist_self : ∀ x : α, dist x x = 0
  toMyUniformity : MyUniformity α  -- explicit class-typed field (NOT from extends)
  uniformity_dist : toMyUniformity.uniformity (fun x y => dist x y = 0)

protected theorem MyMetricSpace.ext {α : Type} {m m' : MyMetricSpace α}
    (h : m.toMyDist = m'.toMyDist) (hU : m.toMyUniformity = m'.toMyUniformity) : m = m' := by
  cases m
  cases m'
  congr 1 <;> assumption
```

**Failure mode:** `⊢ dist_self✝¹ ≍ dist_self✝` and `⊢ uniformity_dist✝¹
≍ uniformity_dist✝`

## Context

These tests are related to #12172, which changes instance parameter
detection from binder-based to `isClass?`-based. That change can affect
how structure fields are classified in congruence lemma generation.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 11:57:18 +00:00
Marc Huisinga
3666544aad refactor: eliminate $/lean/ileanHeaderInfo (#12107) 2026-01-27 10:08:37 +00:00
Sebastian Graf
4ce04776b6 fix: do not assign synthetic opaque MVars in mspec (#12184)
This PR ensures that the `mspec` tactic does not assign synthetic opaque
MVars occurring in the goal, just like the `apply` tactic.
2026-01-27 10:05:20 +00:00
Marc Huisinga
621fdea272 refactor: eliminate FileIdent.mod (#12089) 2026-01-27 09:55:29 +00:00
Marc Huisinga
fb3aae7509 refactor: remove redundant calls to DocumentMeta.mod (#12085) 2026-01-27 09:02:20 +00:00
Kim Morrison
f11fffb27b doc: clarify release notes title format requirements (#12182)
This PR clarifies the release notes title format in the release
checklist documentation.

**Changes:**
- Add explicit section explaining title format for -rc1, subsequent RCs,
and stable releases
- Make it clear that titles should include both the RC suffix AND the
date (e.g., "Lean 4.7.0-rc1 (2024-03-15)")
- Update example to use realistic date format instead of YYYY-MM-DD
- Clarify that only content is written for -rc1, subsequent releases
just update the title

This addresses confusion about whether RC release notes should include
the date in the title.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 08:08:47 +00:00
Sebastian Graf
7e1f9e24c4 chore: activate two mspec tests (#12183) 2026-01-27 07:52:56 +00:00
1553 changed files with 14110 additions and 7755 deletions

View File

@@ -46,6 +46,21 @@ This PR adds a `num?` parameter to `mkPatternFromTheorem` to control how many
leading quantifiers are stripped when creating a pattern.
```
**Changelog labels:** Add one `changelog-*` label to categorize the PR for release notes:
- `changelog-language` - Language features and metaprograms
- `changelog-tactics` - User facing tactics
- `changelog-server` - Language server, widgets, and IDE extensions
- `changelog-pp` - Pretty printing
- `changelog-library` - Library
- `changelog-compiler` - Compiler, runtime, and FFI
- `changelog-lake` - Lake
- `changelog-doc` - Documentation
- `changelog-ffi` - FFI changes
- `changelog-other` - Other changes
- `changelog-no` - Do not include this PR in the release changelog
If you're unsure which label applies, it's fine to omit the label and let reviewers add it.
## CI Log Retrieval
When CI jobs fail, investigate immediately - don't wait for other jobs to complete. Individual job logs are often available even while other jobs are still running. Try `gh run view <run-id> --log` or `gh run view <run-id> --log-failed`, or use `gh run view <run-id> --job=<job-id>` to target the specific failed job. Sleeping is fine when asked to monitor CI and no failures exist yet, but once any job fails, investigate that failure immediately.

View File

@@ -102,7 +102,7 @@ jobs:
if: matrix.cmultilib
- name: Restore Cache
id: restore-cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
with:
# NOTE: must be in sync with `save` below and with `restore-cache` in `update-stage0.yml`
path: |
@@ -175,7 +175,7 @@ jobs:
# Caching on cancellation created some mysterious issues perhaps related to improper build
# shutdown
if: steps.restore-cache.outputs.cache-hit != 'true' && !cancelled()
uses: actions/cache/save@v4
uses: actions/cache/save@v5
with:
# NOTE: must be in sync with `restore` above
path: |

View File

@@ -433,7 +433,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v7
with:
path: artifacts
- name: Release
@@ -465,7 +465,7 @@ jobs:
# Doesn't seem to be working when additionally fetching from lean4-nightly
#filter: tree:0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v7
with:
path: artifacts
- name: Prepare Nightly Release

View File

@@ -20,7 +20,9 @@ on:
jobs:
on-success:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
# Run even if CI fails, as long as build artifacts are available
# The "Verify release artifacts exist" step will fail if necessary artifacts are missing
if: github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
steps:
- name: Retrieve information about the original workflow
uses: potiuk/get-workflow-origin@v1_1 # https://github.com/marketplace/actions/get-workflow-origin
@@ -41,6 +43,19 @@ jobs:
name: build-.*
name_is_regexp: true
# Verify artifacts were downloaded before any side effects (tag creation, release deletion).
- name: Verify release artifacts exist
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
shopt -s nullglob
files=(artifacts/*/*)
if [ ${#files[@]} -eq 0 ]; then
echo "::error::No artifacts found matching artifacts/*/*"
exit 1
fi
echo "Found ${#files[@]} artifacts to upload:"
printf '%s\n' "${files[@]}"
- name: Push tag
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
@@ -72,18 +87,6 @@ jobs:
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }} -y || true
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
# Verify artifacts were downloaded (equivalent to fail_on_unmatched_files in the old action).
- name: Verify release artifacts exist
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
shopt -s nullglob
files=(artifacts/*/*)
if [ ${#files[@]} -eq 0 ]; then
echo "::error::No artifacts found matching artifacts/*/*"
exit 1
fi
echo "Found ${#files[@]} artifacts to upload:"
printf '%s\n' "${files[@]}"
# We use `gh release create` instead of `softprops/action-gh-release` because
# the latter enumerates all releases to check for existing ones, which fails
# when the repository has more than 10000 releases (GitHub API pagination limit).
@@ -393,6 +396,18 @@ jobs:
# We next automatically create a Batteries branch using this toolchain.
# Batteries doesn't itself have a mechanism to report results of CI from this branch back to Lean
# Instead this is taken care of by Mathlib CI, which will fail if Batteries fails.
# Generate a token from the mathlib-nightly-testing GitHub App for cross-org access
- name: Generate GitHub App token for leanprover-community repos
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
id: mathlib-app-token
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2
with:
app-id: ${{ secrets.MATHLIB_NIGHTLY_TESTING_APP_ID }}
private-key: ${{ secrets.MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY }}
owner: leanprover-community
repositories: batteries,mathlib4-nightly-testing
- name: Cleanup workspace
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
run: |
@@ -404,7 +419,7 @@ jobs:
uses: actions/checkout@v6
with:
repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }}
token: ${{ steps.mathlib-app-token.outputs.token }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
@@ -464,7 +479,7 @@ jobs:
uses: actions/checkout@v6
with:
repository: leanprover-community/mathlib4-nightly-testing
token: ${{ secrets.MATHLIB4_BOT }}
token: ${{ steps.mathlib-app-token.outputs.token }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0

View File

@@ -58,7 +58,7 @@ jobs:
shell: 'nix develop -c bash -euxo pipefail {0}'
- name: Restore Cache
if: env.should_update_stage0 == 'yes'
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
with:
# NOTE: must be in sync with `restore-cache` in `build-template.yml`
path: |

View File

@@ -10,22 +10,22 @@ option(USE_MIMALLOC "use mimalloc" ON)
get_cmake_property(vars CACHE_VARIABLES)
foreach(var ${vars})
get_property(currentHelpString CACHE "${var}" PROPERTY HELPSTRING)
if("${var}" MATCHES "STAGE0_(.*)")
if(var MATCHES "STAGE0_(.*)")
list(APPEND STAGE0_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif("${var}" MATCHES "STAGE1_(.*)")
elseif(var MATCHES "STAGE1_(.*)")
list(APPEND STAGE1_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "")
elseif(currentHelpString MATCHES "No help, variable specified on the command line." OR currentHelpString STREQUAL "")
list(APPEND CL_ARGS "-D${var}=${${var}}")
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION|LEAN_VERSION_.*|LEAN_SPECIAL_VERSION_DESC")
if(var MATCHES "USE_GMP|CHECK_OLEAN_VERSION|LEAN_VERSION_.*|LEAN_SPECIAL_VERSION_DESC")
# must forward options that generate incompatible .olean format
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
elseif("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE|USE_MIMALLOC")
elseif(var MATCHES "LLVM*|PKG_CONFIG|USE_LAKE|USE_MIMALLOC")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
elseif("${var}" MATCHES "USE_MIMALLOC")
elseif(var MATCHES "USE_MIMALLOC")
list(APPEND CL_ARGS "-D${var}=${${var}}")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
elseif((var MATCHES "CMAKE_.*") AND NOT (var MATCHES "CMAKE_BUILD_TYPE") AND NOT (var MATCHES "CMAKE_HOME_DIRECTORY"))
list(APPEND PLATFORM_ARGS "-D${var}=${${var}}")
endif()
endforeach()
@@ -34,15 +34,15 @@ include(ExternalProject)
project(LEAN CXX C)
if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
endif()
# Don't do anything with cadical on wasm
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
find_program(CADICAL cadical)
if(NOT CADICAL)
set(CADICAL_CXX c++)
if (CADICAL_USE_CUSTOM_CXX)
if(CADICAL_USE_CUSTOM_CXX)
set(CADICAL_CXX ${CMAKE_CXX_COMPILER})
# Use same platform flags as for Lean executables, in particular from `prepare-llvm-linux.sh`,
# but not Lean-specific `LEAN_EXTRA_CXX_FLAGS` such as fsanitize.
@@ -54,42 +54,51 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
endif()
# missing stdio locking API on Windows
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif()
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
ExternalProject_add(cadical
ExternalProject_Add(
cadical
PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-2.1.2
CONFIGURE_COMMAND ""
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk
CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
CXX=${CADICAL_CXX}
CXXFLAGS=${CADICAL_CXXFLAGS}
LDFLAGS=${CADICAL_LDFLAGS}
BUILD_COMMAND
$(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS} LDFLAGS=${CADICAL_LDFLAGS}
BUILD_IN_SOURCE ON
INSTALL_COMMAND "")
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
INSTALL_COMMAND ""
)
set(
CADICAL
${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX}
CACHE FILEPATH
"path to cadical binary"
FORCE
)
list(APPEND EXTRA_DEPENDS cadical)
endif()
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
endif()
if (USE_MIMALLOC)
ExternalProject_add(mimalloc
if(USE_MIMALLOC)
ExternalProject_Add(
mimalloc
PREFIX mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc
GIT_TAG v2.2.3
# just download, we compile it as part of each stage as it is small
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
INSTALL_COMMAND ""
)
list(APPEND EXTRA_DEPENDS mimalloc)
endif()
if (NOT STAGE1_PREV_STAGE)
ExternalProject_add(stage0
if(NOT STAGE1_PREV_STAGE)
ExternalProject_Add(
stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src
BINARY_DIR stage0
@@ -97,38 +106,49 @@ if (NOT STAGE1_PREV_STAGE)
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install
BUILD_ALWAYS
ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND
"" # skip install
DEPENDS ${EXTRA_DEPENDS}
)
list(APPEND EXTRA_DEPENDS stage0)
endif()
ExternalProject_add(stage1
ExternalProject_Add(
stage1
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage1
CMAKE_ARGS -DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS} ${STAGE1_ARGS}
CMAKE_ARGS
-DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0
-DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS} ${STAGE1_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS ${EXTRA_DEPENDS}
STEP_TARGETS configure
)
ExternalProject_add(stage2
ExternalProject_Add(
stage2
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage2
CMAKE_ARGS -DSTAGE=2 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage1 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS}
CMAKE_ARGS
-DSTAGE=2 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage1 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
${CL_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage1
EXCLUDE_FROM_ALL ON
STEP_TARGETS configure
)
ExternalProject_add(stage3
ExternalProject_Add(
stage3
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage3
CMAKE_ARGS -DSTAGE=3 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage2 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS}
CMAKE_ARGS
-DSTAGE=3 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage2 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
${CL_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage2
@@ -137,24 +157,14 @@ ExternalProject_add(stage3
# targets forwarded to appropriate stages
add_custom_target(update-stage0
COMMAND $(MAKE) -C stage1 update-stage0
DEPENDS stage1)
add_custom_target(update-stage0 COMMAND $(MAKE) -C stage1 update-stage0 DEPENDS stage1)
add_custom_target(update-stage0-commit
COMMAND $(MAKE) -C stage1 update-stage0-commit
DEPENDS stage1)
add_custom_target(update-stage0-commit COMMAND $(MAKE) -C stage1 update-stage0-commit DEPENDS stage1)
add_custom_target(test
COMMAND $(MAKE) -C stage1 test
DEPENDS stage1)
add_custom_target(test COMMAND $(MAKE) -C stage1 test DEPENDS stage1)
add_custom_target(clean-stdlib
COMMAND $(MAKE) -C stage1 clean-stdlib
DEPENDS stage1)
add_custom_target(clean-stdlib COMMAND $(MAKE) -C stage1 clean-stdlib DEPENDS stage1)
install(CODE "execute_process(COMMAND make -C stage1 install)")
add_custom_target(check-stage3
COMMAND diff "stage2/bin/lean" "stage3/bin/lean"
DEPENDS stage3)
add_custom_target(check-stage3 COMMAND diff "stage2/bin/lean" "stage3/bin/lean" DEPENDS stage3)

View File

@@ -218,8 +218,18 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
# Writing the release notes
Release notes are only needed for the first release candidate (`-rc1`). For subsequent RCs and stable releases,
just update the version number in the title of the existing release notes file.
Release notes content is only written for the first release candidate (`-rc1`). For subsequent RCs and stable releases,
just update the title in the existing release notes file (see "Release notes title format" below).
## Release notes title format
The title in the `#doc (Manual)` line must follow these formats:
- **For -rc1**: `"Lean 4.7.0-rc1 (2024-03-15)"` — Include the RC suffix and the release date
- **For -rc2, -rc3, etc.**: `"Lean 4.7.0-rc2 (2024-03-20)"` — Update the RC number and date
- **For stable release**: `"Lean 4.7.0 (2024-04-01)"` — Remove the RC suffix but keep the date
The date should be the actual date when the tag was pushed (or when CI completed and created the release page).
## Generating the release notes
@@ -273,7 +283,7 @@ open Verso.Genre
open Verso.Genre.Manual
open Verso.Genre.Manual.InlineLean
#doc (Manual) "Lean 4.7.0-rc1 (YYYY-MM-DD)" =>
#doc (Manual) "Lean 4.7.0-rc1 (2024-03-15)" =>
%%%
tag := "release-v4.7.0"
file := "v4.7.0"

13
script/fmt Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
# This script expects to be run from the repo root.
# Format cmake files
find -regex '.*/CMakeLists\.txt\(\.in\)?\|.*\.cmake\(\.in\)?' \
! -path './build/*' \
! -path "./stage0/*" \
-exec \
uvx gersemi --in-place --line-length 120 --indent 2 \
--definitions src/cmake/Modules/ src/CMakeLists.txt \
-- {} +

File diff suppressed because it is too large Load Diff

View File

@@ -142,6 +142,7 @@ is classically true but not constructively. -/
/-- Transfer decidability of `¬ p` to decidability of `p`. -/
-- This can not be an instance as it would be tried everywhere.
@[instance_reducible]
def decidable_of_decidable_not (p : Prop) [h : Decidable (¬ p)] : Decidable p :=
match h with
| isFalse h => isTrue (Classical.not_not.mp h)

View File

@@ -121,6 +121,11 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
@[simp] theorem run_control [Monad m] [LawfulMonad m] (f : ({β : Type u} ExceptT ε m β m (stM m (ExceptT ε m) β)) m (stM m (ExceptT ε m) α)) :
ExceptT.run (control f) = f fun x => x.run := run_controlAt f
@[simp, grind =]
theorem run_adapt [Monad m] (f : ε ε') (x : ExceptT ε m α)
: run (ExceptT.adapt f x : ExceptT ε' m α) = Except.mapError f <$> run x :=
rfl
end ExceptT
/-! # Except -/
@@ -467,15 +472,33 @@ namespace EStateM
@[simp, grind =] theorem run_throw (e : ε) (s : σ):
EStateM.run (throw e : EStateM ε σ PUnit) s = .error e s := rfl
@[simp, grind =] theorem run_bind (x : EStateM ε σ α) (f : α EStateM ε σ β)
: EStateM.run (x >>= f : EStateM ε σ β) s
=
match EStateM.run x s with
| .ok x s => EStateM.run (f x) s
| .error e s => .error e s :=
rfl
@[simp, grind =]
theorem run_adaptExcept (f : ε ε') (x : EStateM ε σ α) (s : σ)
: EStateM.run (EStateM.adaptExcept f x : EStateM ε' σ α) s
=
match EStateM.run x s with
| .ok x s => .ok x s
| .error e s => .error (f e) s := by
simp only [EStateM.run, EStateM.adaptExcept]
cases (x s) <;> rfl
instance : LawfulMonad (EStateM ε σ) := .mk'
(id_map := fun x => funext <| fun s => by
dsimp only [EStateM.instMonad, EStateM.map]
simp only [Functor.map, EStateM.map]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)
(pure_bind := fun _ _ => by rfl)
(bind_assoc := fun x _ _ => funext <| fun s => by
dsimp only [EStateM.instMonad, EStateM.bind]
simp only [bind, EStateM.bind]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)

View File

@@ -70,7 +70,7 @@ information to the return value, except a trivial proof of {name}`True`.
This instance is used whenever no more useful {name}`MonadAttach` instance can be implemented.
It always has a {name}`WeaklyLawfulMonadAttach`, but usually no {name}`LawfulMonadAttach` instance.
-/
@[expose]
@[expose, instance_reducible]
public protected def MonadAttach.trivial {m : Type u Type v} [Monad m] : MonadAttach m where
CanReturn _ _ := True
attach x := (·, .intro) <$> x

View File

@@ -51,6 +51,10 @@ scoped syntax (name := withAnnotateState)
/-- `skip` does nothing. -/
syntax (name := skip) "skip" : conv
/-- `cbv` performs simplification that closely mimics call-by-value evaluation,
using equations associated with definitions and the matchers. -/
syntax (name := cbv) "cbv" : conv
/--
Traverses into the left subterm of a binary operator.

View File

@@ -932,6 +932,14 @@ noncomputable def HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sor
noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : a b) (m : motive a) : motive b :=
h.rec m
/-- `HEq.ndrec` specialized to homogeneous heterogeneous equality -/
noncomputable def HEq.homo_ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : α Sort u1} (m : motive a) {b : α} (h : a b) : motive b :=
(eq_of_heq h).ndrec m
/-- `HEq.ndrec` specialized to homogeneous heterogeneous equality, symmetric variant -/
noncomputable def HEq.homo_ndrec_symm.{u1, u2} {α : Sort u2} {a : α} {motive : α Sort u1} (m : motive a) {b : α} (h : b a) : motive b :=
(eq_of_heq h).ndrec_symm m
/-- `HEq.ndrec` variant -/
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : a b) (h₂ : p a) : p b :=
eq_of_heq h₁ h₂
@@ -1478,6 +1486,29 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
/-! # Dependent products -/
instance {α : Type u} {β : α Type v} [h₁ : DecidableEq α] [h₂ : a, DecidableEq (β a)] :
DecidableEq (Sigma β)
| a₁, b₁, a₂, b₂ =>
match a₁, b₁, a₂, b₂, h₁ a₁ a₂ with
| _, b₁, _, b₂, isTrue (Eq.refl _) =>
match b₁, b₂, h₂ _ b₁ b₂ with
| _, _, isTrue (Eq.refl _) => isTrue rfl
| _, _, isFalse n => isFalse fun h
Sigma.noConfusion rfl .rfl (heq_of_eq h) fun _ e₂ n (eq_of_heq e₂)
| _, _, _, _, isFalse n => isFalse fun h
Sigma.noConfusion rfl .rfl (heq_of_eq h) fun e₁ _ n (eq_of_heq e₁)
instance {α : Sort u} {β : α Sort v} [h₁ : DecidableEq α] [h₂ : a, DecidableEq (β a)] : DecidableEq (PSigma β)
| a₁, b₁, a₂, b₂ =>
match a₁, b₁, a₂, b₂, h₁ a₁ a₂ with
| _, b₁, _, b₂, isTrue (Eq.refl _) =>
match b₁, b₂, h₂ _ b₁ b₂ with
| _, _, isTrue (Eq.refl _) => isTrue rfl
| _, _, isFalse n => isFalse fun h
PSigma.noConfusion rfl .rfl (heq_of_eq h) fun _ e₂ n (eq_of_heq e₂)
| _, _, _, _, isFalse n => isFalse fun h
PSigma.noConfusion rfl .rfl (heq_of_eq h) fun e₁ _ n (eq_of_heq e₁)
theorem Exists.of_psigma_prop {α : Sort u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
| x, hx => x, hx
@@ -2329,8 +2360,10 @@ namespace Lean
/--
Depends on the correctness of the Lean compiler, interpreter, and all `[implemented_by ...]` and `[extern ...]` annotations.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
axiom trustCompiler : True
set_option linter.deprecated false in
/--
When the kernel tries to reduce a term `Lean.reduceBool c`, it will invoke the Lean interpreter to evaluate `c`.
The kernel will not use the interpreter if `c` is not a constant.
@@ -2350,11 +2383,13 @@ Recall that the compiler trusts the correctness of all `[implemented_by ...]` an
If an extern function is executed, then the trusted code base will also include the implementation of the associated
foreign function.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
opaque reduceBool (b : Bool) : Bool :=
-- This ensures that `#print axioms` will track use of `reduceBool`.
have := trustCompiler
b
set_option linter.deprecated false in
/--
Similar to `Lean.reduceBool` for closed `Nat` terms.
@@ -2362,12 +2397,14 @@ Remark: we do not have plans for supporting a generic `reduceValue {α} (a : α)
The main issue is that it is non-trivial to convert an arbitrary runtime object back into a Lean expression.
We believe `Lean.reduceBool` enables most interesting applications (e.g., proof by reflection).
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
opaque reduceNat (n : Nat) : Nat :=
-- This ensures that `#print axioms` will track use of `reduceNat`.
have := trustCompiler
n
set_option linter.deprecated false in
/--
The axiom `ofReduceBool` is used to perform proofs by reflection. See `reduceBool`.
@@ -2381,8 +2418,10 @@ external type checkers that do not implement this feature.
Keep in mind that if you are using Lean as programming language, you are already trusting the Lean compiler and interpreter.
So, you are mainly losing the capability of type checking your development using external checkers.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
axiom ofReduceBool (a b : Bool) (h : reduceBool a = b) : a = b
set_option linter.deprecated false in
/--
The axiom `ofReduceNat` is used to perform proofs by reflection. See `reduceBool`.
@@ -2392,6 +2431,7 @@ external type checkers that do not implement this feature.
Keep in mind that if you are using Lean as programming language, you are already trusting the Lean compiler and interpreter.
So, you are mainly losing the capability of type checking your development using external checkers.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
axiom ofReduceNat (a b : Nat) (h : reduceNat a = b) : a = b
@@ -2442,7 +2482,7 @@ class IdempotentOp (op : ααα) : Prop where
idempotent : (x : α) op x x = x
/--
`LeftIdentify op o` indicates `o` is a left identity of `op`.
`LeftIdentity op o` indicates `o` is a left identity of `op`.
This class does not require a proof that `o` is an identity, and
is used primarily for inferring the identity using class resolution.
@@ -2450,7 +2490,7 @@ is used primarily for inferring the identity using class resolution.
class LeftIdentity (op : α β β) (o : outParam α) : Prop
/--
`LawfulLeftIdentify op o` indicates `o` is a verified left identity of
`LawfulLeftIdentity op o` indicates `o` is a verified left identity of
`op`.
-/
class LawfulLeftIdentity (op : α β β) (o : outParam α) : Prop extends LeftIdentity op o where
@@ -2458,7 +2498,7 @@ class LawfulLeftIdentity (op : α → β → β) (o : outParam α) : Prop extend
left_id : a, op o a = a
/--
`RightIdentify op o` indicates `o` is a right identity `o` of `op`.
`RightIdentity op o` indicates `o` is a right identity `o` of `op`.
This class does not require a proof that `o` is an identity, and is used
primarily for inferring the identity using class resolution.
@@ -2466,7 +2506,7 @@ primarily for inferring the identity using class resolution.
class RightIdentity (op : α β α) (o : outParam β) : Prop
/--
`LawfulRightIdentify op o` indicates `o` is a verified right identity of
`LawfulRightIdentity op o` indicates `o` is a verified right identity of
`op`.
-/
class LawfulRightIdentity (op : α β α) (o : outParam β) : Prop extends RightIdentity op o where

View File

@@ -31,3 +31,5 @@ public import Init.Data.Array.Zip
public import Init.Data.Array.InsertIdx
public import Init.Data.Array.Extract
public import Init.Data.Array.MinMax
public import Init.Data.Array.Nat
public import Init.Data.Array.Int

View File

@@ -9,6 +9,7 @@ prelude
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Count
import Init.Grind.Util
public section
@@ -92,6 +93,18 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
rcases xs with xs
simp
/-- This lemma is only relevant for `grind`. -/
@[grind =]
theorem _root_.Std.Internal.Array.countP_eq_zero_of_forall {xs : Array α} (h : x xs, ¬ p x) : xs.countP p = 0 :=
countP_eq_zero.mpr h
/-- This lemma is only relevant for `grind`. -/
theorem _root_.Std.Internal.Array.not_of_countP_eq_zero_of_mem {xs : Array α} (h : xs.countP p = 0) (h' : x xs) : ¬ p x :=
countP_eq_zero.mp h _ h'
grind_pattern Std.Internal.Array.not_of_countP_eq_zero_of_mem => xs.countP p, x xs where
guard xs.countP p = 0
@[simp] theorem countP_eq_size {p} : countP p xs = xs.size a xs, p a := by
rcases xs with xs
simp

View File

@@ -7,6 +7,7 @@ module
prelude
public import Init.Data.List.Nat.Find
import Init.Data.List.Nat.Sum
import all Init.Data.Array.Basic
public import Init.Data.Array.Range
@@ -114,7 +115,7 @@ theorem getElem_zero_flatten.proof {xss : Array (Array α)} (h : 0 < xss.flatten
simp only [List.findSome?_toArray, List.findSome?_map, Function.comp_def, List.getElem?_toArray,
List.findSome?_isSome_iff, isSome_getElem?]
simp only [flatten_toArray_map_toArray, List.size_toArray, List.length_flatten,
Nat.sum_pos_iff_exists_pos, List.mem_map] at h
List.sum_pos_iff_exists_pos_nat, List.mem_map] at h
obtain _, xs, m, rfl, h := h
exact xs, m, by simpa using h
@@ -674,6 +675,39 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
simp only [Option.map_map, Function.comp_def, Fin.cast_cast]
simp [Array.size]
/-! ### find? and findFinIdx? -/
theorem find?_eq_map_findFinIdx?_getElem {xs : Array α} {p : α Bool} :
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
cases xs
simp [List.find?_eq_map_findFinIdx?_getElem]
rfl
theorem find?_eq_bind_findIdx?_getElem? {xs : Array α} {p : α Bool} :
xs.find? p = (xs.findIdx? p).bind (xs[·]?) := by
cases xs
simp [List.find?_eq_bind_findIdx?_getElem?]
theorem find?_eq_getElem?_findIdx {xs : Array α} {p : α Bool} :
xs.find? p = xs[xs.findIdx p]? := by
cases xs
simp [List.find?_eq_getElem?_findIdx]
theorem findIdx?_eq_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α Bool} :
xs.findIdx? p = (xs.find? p).bind (xs.idxOf? ·) := by
cases xs
simp [List.findIdx?_eq_bind_find?_idxOf?]
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α Bool} :
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf? ·) := by
cases xs
simp [List.findFinIdx?_eq_bind_find?_finIdxOf?]
theorem findIdx_eq_getD_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α Bool} :
xs.findIdx p = ((xs.find? p).bind (xs.idxOf? ·)).getD xs.size := by
cases xs
simp [List.findIdx_eq_getD_bind_find?_idxOf?]
/-! ### idxOf
The verification API for `idxOf` is still incomplete.

View File

@@ -0,0 +1,35 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Graf, Paul Reichert
-/
module
prelude
public import Init.Data.List.Int.Sum
public import Init.Data.Array.Lemmas
public import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.DivMod.Lemmas
import Init.Data.List.MinMax
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array
@[simp] theorem sum_replicate_int {n : Nat} {a : Int} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
theorem sum_append_int {as₁ as₂ : Array Int} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
simp [sum_append]
theorem sum_reverse_int (xs : Array Int) : xs.reverse.sum = xs.sum := by
simp [sum_reverse]
theorem sum_eq_foldl_int {xs : Array Int} : xs.sum = xs.foldl (init := 0) (· + ·) := by
simp only [foldl_eq_foldr_reverse, Int.add_comm, sum_eq_foldr, sum_reverse_int]
end Array

View File

@@ -482,9 +482,18 @@ theorem mem_iff_getElem {a} {xs : Array α} : a ∈ xs ↔ ∃ (i : Nat) (h : i
theorem mem_iff_getElem? {a} {xs : Array α} : a xs i : Nat, xs[i]? = some a := by
simp [getElem?_eq_some_iff, mem_iff_getElem]
theorem exists_mem_iff_exists_getElem {P : α Prop} {xs : Array α} :
( x xs, P x) (i : Nat), (hi : i < xs.size), P (xs[i]) := by
cases xs; simp [List.exists_mem_iff_exists_getElem]
theorem forall_mem_iff_forall_getElem {P : α Prop} {xs : Array α} :
( x xs, P x) (i : Nat) (hi : i < xs.size), P (xs[i]) := by
cases xs; simp [List.forall_mem_iff_forall_getElem]
@[deprecated forall_mem_iff_forall_getElem (since := "2026-01-29")]
theorem forall_getElem {xs : Array α} {p : α Prop} :
( (i : Nat) h, p (xs[i]'h)) a, a xs p a := by
cases xs; simp [List.forall_getElem]
exact forall_mem_iff_forall_getElem.symm
/-! ### isEmpty -/
@@ -1969,6 +1978,14 @@ theorem append_eq_append_iff {ws xs ys zs : Array α} :
· left; exact as.toList, by simp
· right; exact cs.toList, by simp
theorem append_eq_append_iff_of_size_eq_left {ws xs ys zs : Array α} (h : ws.size = xs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ Array.toList_inj] using List.append_eq_append_iff_of_size_eq_left h
theorem append_eq_append_iff_of_size_eq_right {ws xs ys zs : Array α} (h : ys.size = zs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ Array.toList_inj] using List.append_eq_append_iff_of_size_eq_right h
@[grind =] theorem set_append {xs ys : Array α} {i : Nat} {x : α} (h : i < (xs ++ ys).size) :
(xs ++ ys).set i x =
if h' : i < xs.size then
@@ -2500,10 +2517,6 @@ theorem flatMap_replicate {f : α → Array β} : (replicate n a).flatMap f = (r
rw [ List.toArray_replicate, List.isEmpty_toArray]
simp
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
/-! ### Preliminaries about `swap` needed for `reverse`. -/
@[grind =]
@@ -4289,19 +4302,31 @@ theorem getElem?_range {n : Nat} {i : Nat} : (Array.range n)[i]? = if i < n then
/-! ### sum -/
@[simp, grind =] theorem sum_empty [Add α] [Zero α] : (#[] : Array α).sum = 0 := rfl
theorem sum_eq_foldr [Add α] [Zero α] {xs : Array α} :
xs.sum = xs.foldr (init := 0) (· + ·) :=
rfl
-- Without further algebraic hypotheses, there's no useful `sum_push` lemma.
@[simp, grind =]
theorem sum_eq_sum_toList [Add α] [Zero α] {as : Array α} : as.toList.sum = as.sum := by
theorem sum_toList [Add α] [Zero α] {as : Array α} : as.toList.sum = as.sum := by
cases as
simp [Array.sum, List.sum]
@[deprecated sum_toList (since := "2026-01-14")]
def sum_eq_sum_toList := @sum_toList
@[simp, grind =]
theorem sum_append_nat {as₁ as₂ : Array Nat} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
cases as₁
cases as₂
simp [List.sum_append_nat]
theorem sum_append [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
[Std.LeftIdentity (α := α) (· + ·) 0] [Std.LawfulLeftIdentity (α := α) (· + ·) 0]
{as₁ as₂ : Array α} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
simp [ sum_toList, List.sum_append]
@[simp, grind =]
theorem sum_reverse [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
[Std.Commutative (α := α) (· + ·)]
[Std.LawfulLeftIdentity (α := α) (· + ·) 0] (xs : Array α) : xs.reverse.sum = xs.sum := by
simp [ sum_toList, List.sum_reverse]
theorem foldl_toList_eq_flatMap {l : List α} {acc : Array β}
{F : Array β α Array β} {G : α List β}

View File

@@ -0,0 +1,39 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Graf, Paul Reichert
-/
module
prelude
public import Init.Data.Array.Lemmas
import Init.Data.List.Nat.Sum
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array
protected theorem sum_pos_iff_exists_pos_nat {xs : Array Nat} : 0 < xs.sum x xs, 0 < x := by
simp [ sum_toList, List.sum_pos_iff_exists_pos_nat]
protected theorem sum_eq_zero_iff_forall_eq_nat {xs : Array Nat} :
xs.sum = 0 x xs, x = 0 := by
simp [ sum_toList, List.sum_eq_zero_iff_forall_eq_nat]
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
theorem sum_append_nat {as₁ as₂ : Array Nat} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
simp [sum_append]
theorem sum_reverse_nat (xs : Array Nat) : xs.reverse.sum = xs.sum := by
simp [sum_reverse]
theorem sum_eq_foldl_nat {xs : Array Nat} : xs.sum = xs.foldl (init := 0) (· + ·) := by
simp only [foldl_eq_foldr_reverse, Nat.add_comm, sum_eq_foldr, sum_reverse_nat]
end Array

View File

@@ -53,6 +53,11 @@ theorem ofFn_succ' {f : Fin (n+1) → α} :
apply Array.toList_inj.mp
simp [List.ofFn_succ]
@[simp]
theorem ofFn_getElem {xs : Array α} :
Array.ofFn (fun i : Fin xs.size => xs[i.val]) = xs := by
ext <;> simp
@[simp]
theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by
rw [ Array.toList_inj]
@@ -67,6 +72,12 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
· rintro i, rfl
apply mem_of_getElem (i := i) <;> simp
@[simp, grind =]
theorem map_ofFn {f : Fin n α} {g : α β} :
(Array.ofFn f).map g = Array.ofFn (g f) := by
apply Array.ext_getElem?
simp [Array.getElem?_ofFn]
/-! ### ofFnM -/
/-- Construct (in a monadic context) an array by applying a monadic function to each index. -/

View File

@@ -636,7 +636,7 @@ def boolPredToPred : Coe (α → Bool) (α → Prop) where
This should not be turned on globally as an instance because it degrades performance in Mathlib,
but may be used locally.
-/
@[expose] def boolRelToRel : Coe (α α Bool) (α α Prop) where
@[expose, instance_reducible] def boolRelToRel : Coe (α α Bool) (α α Prop) where
coe r := fun a b => Eq (r a b) true
/-! ### subtypes -/

View File

@@ -286,4 +286,21 @@ theorem extract_zero_max_size {a : ByteArray} {i : Nat} : a.extract 0 (max i a.s
ext1
simp [Nat.le_max_right]
theorem append_eq_append_iff_of_size_eq_left {ws xs ys zs : ByteArray} (h : ws.size = xs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ByteArray.ext_iff] using Array.append_eq_append_iff_of_size_eq_left h
theorem append_eq_append_iff_of_size_eq_right {ws xs ys zs : ByteArray} (h : ys.size = zs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ByteArray.ext_iff] using Array.append_eq_append_iff_of_size_eq_right h
@[simp]
theorem size_push {bs : ByteArray} {b : UInt8} : (bs.push b).size = bs.size + 1 := by
rw [ByteArray.size, data_push, Array.size_push, ByteArray.size]
theorem ext_getElem {a b : ByteArray} (h₀ : a.size = b.size) (h : (i : Nat) hi hi', a[i]'hi = b[i]'hi') : a = b := by
rw [ByteArray.ext_iff]
apply Array.ext (by simpa using h₀)
simpa [ ByteArray.getElem_eq_getElem_data]
end ByteArray

View File

@@ -48,6 +48,7 @@ instance ltTrans : Trans (· < · : Char → Char → Prop) (· < ·) (· < ·)
trans := Char.lt_trans
-- This instance is useful while setting up instances for `String`.
@[instance_reducible]
def notLTTrans : Trans (¬ · < · : Char Char Prop) (¬ · < ·) (¬ · < ·) where
trans h₁ h₂ := by simpa using Char.le_trans (by simpa using h₂) (by simpa using h₁)

View File

@@ -11,6 +11,8 @@ public import Init.Grind.Ordered.Ring
/-! # Internal `grind` algebra instances for `Dyadic`. -/
@[expose] public section
open Lean.Grind
namespace Dyadic

View File

@@ -4,7 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Init.Data.Dyadic.Basic
import Init.Data.Dyadic.Round
import Init.Grind.Ordered.Ring
@@ -12,6 +14,8 @@ import Init.Grind.Ordered.Ring
# Inversion for dyadic numbers
-/
@[expose] public section
namespace Dyadic
/--

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Data.Dyadic.Basic
import all Init.Data.Dyadic.Instances
import Init.Data.Dyadic.Instances
import Init.Grind.Ordered.Rat
import Init.Grind.Ordered.Field

View File

@@ -118,7 +118,7 @@ For example, for `x : Fin k` and `n : Nat`,
it causes `x < n` to be elaborated as `x < ↑n` rather than `↑x < n`,
silently introducing wraparound arithmetic.
-/
@[expose]
@[expose, instance_reducible]
def instNatCast (n : Nat) [NeZero n] : NatCast (Fin n) where
natCast a := Fin.ofNat n a
@@ -140,7 +140,7 @@ This is not a global instance, but may be activated locally via `open Fin.IntCas
See the doc-string for `Fin.NatCast.instNatCast` for more details.
-/
@[expose]
@[expose, instance_reducible]
def instIntCast (n : Nat) [NeZero n] : IntCast (Fin n) where
intCast := Fin.intCast

View File

@@ -1153,6 +1153,15 @@ theorem ediv_le_iff_le_mul {k x y : Int} (h : 0 < k) : x / k ≤ y ↔ x < y * k
rw [Int.le_iff_lt_add_one, Int.ediv_lt_iff_lt_mul h, Int.add_mul]
omega
theorem le_mul_iff_le_left {x y z : Int} (hz : 0 < z) :
x y * z (x + z - 1) / z y := by
rw [Int.ediv_le_iff_le_mul hz]
omega
theorem le_mul_iff_le_right {x y z : Int} (hy : 0 < y) :
x y * z (x + y - 1) / y z := by
rw [ le_mul_iff_le_left hy, Int.mul_comm]
protected theorem le_mul_of_ediv_le {a b c : Int} (H1 : 0 b) (H2 : b a) (H3 : a / b c) :
a c * b := by
rw [ Int.ediv_mul_cancel H2]; exact Int.mul_le_mul_of_nonneg_right H3 H1
@@ -1206,6 +1215,11 @@ theorem add_ediv {a b c : Int} (h : c ≠ 0) :
protected theorem ediv_le_ediv {a b c : Int} (H : 0 < c) (H' : a b) : a / c b / c :=
Int.le_ediv_of_mul_le H (Int.le_trans (Int.ediv_mul_le _ (Int.ne_of_gt H)) H')
theorem ediv_add_ediv_le_add_ediv {x y z : Int} (hz : 0 < z) :
x / z + y / z (x + y) / z := by
rw [Int.le_ediv_iff_mul_le hz, Int.add_mul]
apply Int.add_le_add <;> apply Int.ediv_mul_le <;> omega
/-- If `n > 0` then `m` is not divisible by `n` iff it is between `n * k` and `n * (k + 1)`
for some `k`. -/
theorem not_dvd_iff_lt_mul_succ (m : Int) (hn : 0 < n) :
@@ -1783,12 +1797,12 @@ theorem ediv_lt_ediv_iff_of_dvd_of_neg_of_neg {a b c d : Int} (hb : b < 0) (hd :
theorem ediv_lt_ediv_of_lt {a b c : Int} (h : a < b) (hcb : c b) (hc : 0 < c) :
a / c < b / c :=
Int.lt_ediv_of_mul_lt (Int.le_of_lt hc) hcb
Int.lt_ediv_of_mul_lt (Int.le_of_lt hc) hcb
(Int.lt_of_le_of_lt (Int.ediv_mul_le _ (Int.ne_of_gt hc)) h)
theorem ediv_lt_ediv_of_lt_of_neg {a b c : Int} (h : b < a) (hca : c a) (hc : c < 0) :
a / c < b / c :=
(Int.ediv_lt_iff_of_dvd_of_neg hc hca).2
(Int.ediv_lt_iff_of_dvd_of_neg hc hca).2
(Int.lt_of_le_of_lt (Int.mul_ediv_self_le (Int.ne_of_lt hc)) h)
/-! ### `tdiv` and ordering -/

View File

@@ -94,8 +94,9 @@ By convention, the monadic iterator associated with an object can be obtained vi
For example, `List.iterM IO` creates an iterator over a list in the monad `IO`.
See `Init.Data.Iterators.Consumers` for ways to use an iterator. For example, `it.toList` will
convert a provably finite iterator `it` into a list and `it.allowNontermination.toList` will
do so even if finiteness cannot be proved. It is also always possible to manually iterate using
convert an iterator `it` into a list and `it.ensureTermination.toList` guarantees that this
operation will terminate, given a proof that the iterator is finite.
It is also always possible to manually iterate using
`it.step`, relying on the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
See `Iter` for a more convenient interface in case that no monadic effects are needed (`m = Id`).
@@ -139,8 +140,9 @@ By convention, the monadic iterator associated with an object can be obtained vi
For example, `List.iterM IO` creates an iterator over a list in the monad `IO`.
See `Init.Data.Iterators.Consumers` for ways to use an iterator. For example, `it.toList` will
convert a provably finite iterator `it` into a list and `it.allowNontermination.toList` will
do so even if finiteness cannot be proved. It is also always possible to manually iterate using
convert an iterator `it` into a list and `it.ensureTermination.toList` guarantees that this
operation will terminate, given a proof that the iterator is finite.
It is also always possible to manually iterate using
`it.step`, relying on the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
See `IterM` for iterators that operate in a monad.
@@ -754,8 +756,8 @@ def IterM.finitelyManySteps {α : Type w} {m : Type w → Type w'} {β : Type w}
it
/--
Termination measure to be used in well-founded recursive functions recursing over a finite iterator
(see also `Finite`).
Termination measure to be used in recursive functions built with `WellFounded.extrinsicFix`
recursing over a finite iterator without requiring a proof of finiteness (see also `Finite`).
-/
@[expose]
def IterM.finitelyManySteps! {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
@@ -796,6 +798,11 @@ def Iter.finitelyManySteps {α : Type w} {β : Type w} [Iterator α Id β] [Iter
(it : Iter (α := α) β) : IterM.TerminationMeasures.Finite α Id :=
it.toIterM.finitelyManySteps
@[inherit_doc IterM.finitelyManySteps!, expose]
def Iter.finitelyManySteps! {α : Type w} {β : Type w} [Iterator α Id β]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Finite α Id :=
it.toIterM.finitelyManySteps!
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `IterM.finitelyManySteps`.
@@ -902,6 +909,16 @@ def IterM.finitelyManySkips {α : Type w} {m : Type w → Type w'} {β : Type w}
[Iterators.Productive α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
it
/--
Termination measure to be used in recursive functions built with `WellFounded.extrinsicFix`
recursing over a productive iterator without requiring a proof of productiveness
(see also `Productive`).
-/
@[expose]
def IterM.finitelyManySkips! {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
it
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `IterM.finitelyManySkips`.
@@ -922,6 +939,11 @@ def Iter.finitelyManySkips {α : Type w} {β : Type w} [Iterator α Id β] [Iter
(it : Iter (α := α) β) : IterM.TerminationMeasures.Productive α Id :=
it.toIterM.finitelyManySkips
@[inherit_doc IterM.finitelyManySkips!, expose]
def Iter.finitelyManySkips! {α : Type w} {β : Type w} [Iterator α Id β]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Productive α Id :=
it.toIterM.finitelyManySkips!
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `Iter.finitelyManySkips`.

View File

@@ -21,21 +21,70 @@ If possible, takes `n` steps with the iterator `it` and
returns the `n`-th emitted value, or `none` if `it` finished
before emitting `n` values.
This function requires a `Productive` instance proving that the iterator will always emit a value
after a finite number of skips. If the iterator is not productive or such an instance is not
available, consider using `it.allowNontermination.atIdxSlow?` instead of `it.atIdxSlow?`. However,
it is not possible to formally verify the behavior of the partial variant.
If the iterator is not productive, this function might run forever in an endless loop of iterator
steps. The variant `it.ensureTermination.atIdxSlow?` is guaranteed to terminate after finitely many
steps.
-/
@[specialize]
def Iter.atIdxSlow? {α β} [Iterator α Id β] [Productive α Id]
def Iter.atIdxSlow? {α β} [Iterator α Id β]
(n : Nat) (it : Iter (α := α) β) : Option β :=
match it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => it'.atIdxSlow? k
| .skip it' _ => it'.atIdxSlow? n
| .done _ => none
WellFounded.extrinsicFix₂ (C₂ := fun _ _ => Option β) (α := Iter (α := α) β) (β := fun _ => Nat)
(InvImage
(Prod.Lex WellFoundedRelation.rel IterM.TerminationMeasures.Productive.Rel)
(fun p => (p.2, p.1.finitelyManySkips!)))
(fun it n recur =>
match it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => recur it' k (by decreasing_tactic)
| .skip it' _ => recur it' n (by decreasing_tactic)
| .done _ => none) it n
-- We provide the functional induction principle by hand because `atIdxSlow?` is implemented using
-- `extrinsicFix₂` and not using well-founded recursion.
/-
An induction principle for `Iter.atIdxSlow?`.
This lemma provides a functional induction principle for reasoning about `Iter.atIdxSlow? n it`.
The induction follows the structure of iterator steps.
- base case: when we reach the desired index (`n = 0`) and get a `.yield` step
- inductive case: when we have a `.yield` step but need to continue (`n > 0`)
- skip case: when we encounter a `.skip` step and continue with the same index
- done case: when the iterator is exhausted and we return `none`
-/
theorem Iter.atIdxSlow?.induct_unfolding {α β : Type u} [Iterator α Id β] [Productive α Id]
(motive : Nat Iter β Option β Prop)
-- Base case: we have reached index 0 and found a value
(yield_zero : (it it' : Iter (α := α) β) (out : β) (property : it.IsPlausibleStep (IterStep.yield it' out)),
it.step = IterStep.yield it' out, property motive 0 it (some out))
-- Inductive case: we have a yield but need to continue to a higher index
(yield_succ : (it it' : Iter (α := α) β) (out : β) (property : it.IsPlausibleStep (IterStep.yield it' out)),
it.step = IterStep.yield it' out, property
(k : Nat), motive k it' (Iter.atIdxSlow? k it') motive k.succ it (Iter.atIdxSlow? k it'))
-- Skip case: we encounter a skip and continue with the same index
(skip_case : (n : Nat) (it it' : Iter β) (property : it.IsPlausibleStep (IterStep.skip it')),
it.step = IterStep.skip it', property
motive n it' (Iter.atIdxSlow? n it') motive n it (Iter.atIdxSlow? n it'))
-- Done case: the iterator is exhausted, return none
(done_case : (n : Nat) (it : Iter β) (property : it.IsPlausibleStep IterStep.done),
it.step = IterStep.done, property motive n it none)
-- The conclusion: the property holds for all indices and iterators
(n : Nat) (it : Iter β) : motive n it (Iter.atIdxSlow? n it) := by
simp only [atIdxSlow?] at *
rw [WellFounded.extrinsicFix₂_eq_apply]
· split
· split
· apply yield_zero <;> assumption
· apply yield_succ
all_goals try assumption
apply Iter.atIdxSlow?.induct_unfolding <;> assumption
· apply skip_case
all_goals try assumption
apply Iter.atIdxSlow?.induct_unfolding <;> assumption
· apply done_case <;> assumption
· exact InvImage.wf _ WellFoundedRelation.wf
termination_by (n, it.finitelyManySkips)
/--
@@ -43,22 +92,21 @@ If possible, takes `n` steps with the iterator `it` and
returns the `n`-th emitted value, or `none` if `it` finished
before emitting `n` values.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Productive` instance, consider using `Iter.atIdxSlow?` instead.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `Iter.toArray`.
-/
@[specialize]
partial def Iter.Partial.atIdxSlow? {α β} [Iterator α Id β] [Monad Id]
(n : Nat) (it : Iter.Partial (α := α) β) : Option β := do
match it.it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => (it' : Iter.Partial (α := α) β).atIdxSlow? k
| .skip it' _ => (it' : Iter.Partial (α := α) β).atIdxSlow? n
| .done _ => none
@[inline]
def Iter.Total.atIdxSlow? {α β} [Iterator α Id β] [Productive α Id]
(n : Nat) (it : Iter.Total (α := α) β) : Option β :=
it.it.atIdxSlow? n
@[inline, inherit_doc Iter.atIdxSlow?, deprecated Iter.atIdxSlow? (since := "2026-01-28")]
def Iter.Partial.atIdxSlow? {α β} [Iterator α Id β]
(n : Nat) (it : Iter.Partial (α := α) β) : Option β :=
it.it.atIdxSlow? n
@[always_inline, inline, inherit_doc IterM.atIdx?]
def Iter.atIdx? {α β} [Iterator α Id β] [Productive α Id] [IteratorAccess α Id]
def Iter.atIdx? {α β} [Iterator α Id β] [IteratorAccess α Id]
(n : Nat) (it : Iter (α := α) β) : Option β :=
match (IteratorAccess.nextAtIdx? it.toIterM n).run.val with
| .yield _ out => some out

View File

@@ -630,6 +630,79 @@ def Iter.Total.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id
(it : Iter.Total (α := α) β) (f : β Bool) : Option β :=
it.it.find? f
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
If the iterator is not productive, this function might run forever. The variant
`it.ensureTermination.first?` always terminates after finitely many steps.
Examples:
* `[7, 6].iter.first? = some 7`
* `[].iter.first? = none`
-/
@[inline]
def Iter.first? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Option β :=
it.toIterM.first?.run
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. The elements in `it` are examined in order of
iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `Iter.first?`.
Examples:
* `[7, 6].iter.first? = some 7`
* `[].iter.first? = none`
-/
@[inline]
def Iter.Total.first? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id] [Productive α Id]
(it : Iter.Total (α := α) β) : Option β :=
it.it.first?
/--
Returns `true` if the iterator yields no values.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
If the iterator is not productive, this function might run forever. The variant
`it.ensureTermination.isEmpty` always terminates after finitely many steps.
Examples:
* `[].iter.isEmpty = true`
* `[1].iter.isEmpty = false`
-/
@[inline]
def Iter.isEmpty {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Bool :=
it.toIterM.isEmpty.run.down
/--
Returns `true` if the iterator yields no values.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `Iter.isEmpty`.
Examples:
* `[].iter.isEmpty = true`
* `[1].iter.isEmpty = false`
-/
@[inline]
def Iter.Total.isEmpty {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id] [Productive α Id]
(it : Iter.Total (α := α) β) : Bool :=
it.it.isEmpty
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -638,9 +711,15 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose]
def Iter.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
def Iter.length {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.toIterM.count.run.down
it.toIterM.length.run.down
@[inline, inherit_doc Iter.length, deprecated Iter.length (since := "2026-01-28"), expose]
def Iter.count := @Iter.length
@[inline, inherit_doc Iter.length, deprecated Iter.length (since := "2025-10-29"), expose]
def Iter.size := @Iter.length
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -649,22 +728,10 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.count
/--
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-12-04")]
@[always_inline, inline, expose, deprecated Iter.length (since := "2025-12-04")]
def Iter.Partial.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.it.toIterM.count.run.down
it.it.toIterM.length.run.down
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -673,9 +740,9 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
@[always_inline, inline, expose, deprecated Iter.length (since := "2025-10-29")]
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.it.count
it.it.length
end Std

View File

@@ -185,8 +185,8 @@ instance instLawfulIteratorLoopDefaultImplementation (α : Type w) (m : Type w
constructor; simp
theorem IteratorLoop.wellFounded_of_finite {m : Type w Type w'}
{α β : Type w} {γ : Type x} [Iterator α m β] [Finite α m] :
WellFounded α m (γ := γ) fun _ _ _ => True := by
{α β : Type w} {γ : Type x} [Iterator α m β] [Finite α m] {P : β γ ForInStep γ Prop} :
WellFounded α m (γ := γ) P := by
apply Subrelation.wf
(r := InvImage IterM.TerminationMeasures.Finite.Rel (fun p => p.1.finitelyManySteps))
· intro p' p h
@@ -197,6 +197,16 @@ theorem IteratorLoop.wellFounded_of_finite {m : Type w → Type w'}
· apply InvImage.wf
exact WellFoundedRelation.wf
theorem IteratorLoop.wellFounded_of_productive {α β : Type w} {m : Type w Type w'}
[Iterator α m β] [IteratorLoop α m m] [Productive α m] {P : β γ ForInStep γ Prop}
(hp : {b g s}, P b g s s matches ForInStep.done ..) :
WellFounded α m (γ := γ) P := by
rw [WellFounded]
unfold IteratorLoop.rel
have {b g q} : ¬ P b g (ForInStep.yield q) := fun h => by simpa using hp h
simp only [and_false, exists_false, false_or, this]
exact Subrelation.wf And.left (InvImage.wf Prod.fst Productive.wf)
/--
This `ForIn'`-style loop construct traverses a finite iterator using an `IteratorLoop` instance.
-/
@@ -902,6 +912,76 @@ def IterM.Total.find? {α β : Type w} {m : Type w → Type w'} [Monad m] [Itera
m (Option β) :=
it.it.find? f
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
If the iterator is not productive, this function might run forever. The variant
`it.ensureTermination.first?` always terminates after finitely many steps.
Examples:
* `([7, 6].iterM Id).first? = pure (some 7)`
* `([].iterM Id).first? = pure none`
-/
@[inline]
def IterM.first? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] (it : IterM (α := α) m β) : m (Option β) :=
IteratorLoop.forIn (fun _ _ => flip Bind.bind) _ (fun b _ s => s = ForInStep.done (some b)) it
none (fun b _ _ => pure ForInStep.done (some b), rfl)
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. The elements in `it` are examined in order of
iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `IterM.first?`.
Examples:
* `([7, 6].iterM Id).first? = pure (some 7)`
* `([].iterM Id).first? = pure none`
-/
@[inline]
def IterM.Total.first? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [Productive α m] (it : IterM.Total (α := α) m β) : m (Option β) :=
it.it.first?
set_option doc.verso true in
/--
Returns {lean}`ULift.up true` if the iterator {name}`it` yields no values.
{lit}`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of {name}`it` is examined.
If the iterator is not productive, this function might run forever. The variant
{lit}`it.ensureTermination.isEmpty` always terminates after finitely many steps.
-/
@[always_inline]
def IterM.isEmpty {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] (it : IterM (α := α) m β) : m (ULift Bool) :=
IteratorLoop.forIn (fun _ _ => flip Bind.bind) _ (fun _ _ s => s = ForInStep.done (.up false)) it
(.up true) (fun _ _ _ => pure ForInStep.done (.up false), rfl)
set_option doc.verso true in
/--
Returns {lean}`ULift.up true` if the iterator {name}`it` yields no values.
{lit}`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of {name}`it` is examined.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using {name}`IterM.isEmpty`.
-/
@[always_inline, inline]
def IterM.Total.isEmpty {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [Productive α m] (it : IterM.Total (α := α) m β) :
m (ULift Bool) :=
it.it.isEmpty
section Count
/--
@@ -912,21 +992,15 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline]
def IterM.count {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
def IterM.length {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM (α := α) m β) : m (ULift Nat) :=
it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
/--
Steps through the whole iterator, counting the number of outputs emitted.
@[inline, inherit_doc IterM.length, deprecated IterM.length (since := "2026-01-28"), expose]
def IterM.count := @IterM.length
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, deprecated IterM.count (since := "2025-10-29")]
def IterM.size {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM (α := α) m β) : m (ULift Nat) :=
it.count
@[inline, inherit_doc IterM.length, deprecated IterM.length (since := "2025-10-29"), expose]
def IterM.size := @IterM.length
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -935,7 +1009,7 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, deprecated IterM.count (since := "2025-12-04")]
@[always_inline, inline, deprecated IterM.length (since := "2025-12-04")]
def IterM.Partial.count {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
it.it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
@@ -947,10 +1021,10 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, deprecated IterM.Partial.count (since := "2025-10-29")]
@[always_inline, inline, deprecated IterM.length (since := "2025-10-29")]
def IterM.Partial.size {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
it.it.count
it.it.length
end Count

View File

@@ -29,7 +29,7 @@ consumers such as `toList`. They can be used without any proof of termination su
or `Productive`, but as they are implemented with the `partial` declaration modifier, they are
opaque for the kernel and it is impossible to prove anything about them.
-/
@[always_inline, inline]
@[always_inline, inline, deprecated "The consumers on iterators do not require proofs of termination anymore. For example, use `it.toList` instead of `it.allowNontermination.toList`." (since := "2026-01-28")]
def IterM.allowNontermination {α : Type w} {m : Type w Type w'} {β : Type w}
(it : IterM (α := α) m β) : IterM.Partial (α := α) m β :=
it

View File

@@ -29,7 +29,7 @@ consumers such as `toList`. They can be used without any proof of termination su
or `Productive`, but as they are implemented with the `partial` declaration modifier, they are
opaque for the kernel and it is impossible to prove anything about them.
-/
@[always_inline, inline]
@[always_inline, inline, deprecated "The consumers on iterators do not require proofs of termination anymore. For example, use `it.toList` instead of `it.allowNontermination.toList`." (since := "2026-01-28")]
def Iter.allowNontermination {α : Type w} {β : Type w}
(it : Iter (α := α) β) : Iter.Partial (α := α) β :=
it

View File

@@ -111,6 +111,11 @@ instance {n : Type u → Type w} [Monad n] [LawfulMonad n] :
liftBind_pure := by simp
liftBind_bind := by simp
instance {m : Type u Type v} [Monad m] [LawfulMonad m] :
LawfulMonadLiftBindFunction (m := m) (n := m) (fun _ _ => flip Bind.bind) where
liftBind_pure := by simp [flip]
liftBind_bind := by simp [flip]
end LiftBind
end Std.Internal

View File

@@ -79,12 +79,15 @@ theorem Iter.toArray_attachWith [Iterator α Id β]
simp [Iter.toList_toArray]
@[simp]
theorem Iter.count_attachWith [Iterator α Id β]
theorem Iter.length_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorLoop α Id Id]
[LawfulIteratorLoop α Id Id] :
(it.attachWith P hP).count = it.count := by
rw [ Iter.length_toList_eq_count, toList_attachWith]
(it.attachWith P hP).length = it.length := by
rw [ Iter.length_toList_eq_length, toList_attachWith]
simp
@[deprecated Iter.length_attachWith (since := "2026-01-28")]
def Iter.count_attachWith := @Iter.length_attachWith
end Std

View File

@@ -722,11 +722,14 @@ end Fold
section Count
@[simp]
theorem Iter.count_map {α β β' : Type w} [Iterator α Id β]
theorem Iter.length_map {α β β' : Type w} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β β'} :
(it.map f).count = it.count := by
simp [map_eq_toIter_map_toIterM, count_eq_count_toIterM]
(it.map f).length = it.length := by
simp [map_eq_toIter_map_toIterM, length_eq_length_toIterM]
@[deprecated Iter.length_map (since := "2026-01-28")]
def Iter.count_map := @Iter.length_map
end Count

View File

@@ -60,12 +60,15 @@ theorem IterM.map_unattach_toArray_attachWith [Iterator α m β] [Monad m] [Mona
simp [-map_unattach_toList_attachWith, -IterM.toArray_toList]
@[simp]
theorem IterM.count_attachWith [Iterator α m β] [Monad m] [Monad n]
theorem IterM.length_attachWith [Iterator α m β] [Monad m] [Monad n]
{it : IterM (α := α) m β} {hP}
[Finite α m] [IteratorLoop α m m] [LawfulMonad m] [LawfulIteratorLoop α m m] :
(it.attachWith P hP).count = it.count := by
rw [ up_length_toList_eq_count, up_length_toList_eq_count,
(it.attachWith P hP).length = it.length := by
rw [ up_length_toList_eq_length, up_length_toList_eq_length,
map_unattach_toList_attachWith (it := it) (P := P) (hP := hP)]
simp only [Functor.map_map, List.length_unattach]
@[deprecated IterM.length_attachWith (since := "2026-01-28")]
def IterM.count_attachWith := @IterM.length_attachWith
end Std

View File

@@ -1620,18 +1620,21 @@ end Fold
section Count
@[simp]
theorem IterM.count_map {α β β' : Type w} {m : Type w Type w'} [Iterator α m β] [Monad m]
theorem IterM.length_map {α β β' : Type w} {m : Type w Type w'} [Iterator α m β] [Monad m]
[IteratorLoop α m m] [Finite α m] [LawfulMonad m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β β'} :
(it.map f).count = it.count := by
(it.map f).length = it.length := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [count_eq_match_step, count_eq_match_step, step_map, bind_assoc]
rw [length_eq_match_step, length_eq_match_step, step_map, bind_assoc]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy _]
· simp [ihs _]
· simp
@[deprecated IterM.length_map (since := "2026-01-28")]
def IterM.count_map := @IterM.length_map
end Count
section AnyAll

View File

@@ -66,14 +66,14 @@ theorem IterM.toArray_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (
simp
@[simp]
theorem IterM.count_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
theorem IterM.length_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
[MonadLiftT m (ULiftT n)] [Finite α m] [IteratorLoop α m m]
[LawfulMonad m] [LawfulMonad n] [LawfulIteratorLoop α m m]
[LawfulMonadLiftT m (ULiftT n)] :
(it.uLift n).count =
(.up ·.down.down) <$> (monadLift (n := ULiftT n) it.count).run := by
(it.uLift n).length =
(.up ·.down.down) <$> (monadLift (n := ULiftT n) it.length).run := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [count_eq_match_step, count_eq_match_step, monadLift_bind, map_eq_pure_bind, step_uLift]
rw [length_eq_match_step, length_eq_match_step, monadLift_bind, map_eq_pure_bind, step_uLift]
simp only [bind_assoc, ULiftT.run_bind]
apply bind_congr; intro step
cases step.down.inflate using PlausibleIterStep.casesOn
@@ -81,4 +81,7 @@ theorem IterM.count_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α
· simp [ihs _]
· simp
@[deprecated IterM.length_uLift (since := "2026-01-28")]
def IterM.count_uLift := @IterM.length_uLift
end Std

View File

@@ -47,18 +47,18 @@ theorem Iter.atIdxSlow?_take {α β}
[Iterator α Id β] [Productive α Id] {k l : Nat}
{it : Iter (α := α) β} :
(it.take k).atIdxSlow? l = if l < k then it.atIdxSlow? l else none := by
fun_induction it.atIdxSlow? l generalizing k
case case1 it it' out h h' =>
simp only [atIdxSlow?.eq_def (it := it.take k), step_take, h']
induction l, it using Iter.atIdxSlow?.induct_unfolding generalizing k
case yield_zero it it' out h h' =>
simp only [atIdxSlow?_eq_match (it := it.take k), step_take, h']
cases k <;> simp
case case2 it it' out h h' l ih =>
simp only [Nat.succ_eq_add_one, atIdxSlow?.eq_def (it := it.take k), step_take, h']
case yield_succ it it' out h h' l ih =>
simp only [Nat.succ_eq_add_one, atIdxSlow?_eq_match (it := it.take k), step_take, h']
cases k <;> cases l <;> simp [ih]
case case3 l it it' h h' ih =>
simp only [atIdxSlow?.eq_def (it := it.take k), step_take, h']
case skip_case l it it' h h' ih =>
simp only [atIdxSlow?_eq_match (it := it.take k), step_take, h']
cases k <;> cases l <;> simp [ih]
case case4 l it h h' =>
simp only [atIdxSlow?.eq_def (it := it.take k), step_take, h']
case done_case l it h h' =>
simp only [atIdxSlow?_eq_match (it := it.take k), step_take, h']
cases k <;> cases l <;> simp
@[simp]

View File

@@ -57,11 +57,14 @@ theorem Iter.toArray_uLift [Iterator α Id β] {it : Iter (α := α) β}
simp [-toArray_toList]
@[simp]
theorem Iter.count_uLift [Iterator α Id β] {it : Iter (α := α) β}
theorem Iter.length_uLift [Iterator α Id β] {it : Iter (α := α) β}
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id] :
it.uLift.count = it.count := by
simp only [monadLift, uLift_eq_toIter_uLift_toIterM, count_eq_count_toIterM, toIterM_toIter]
rw [IterM.count_uLift]
it.uLift.length = it.length := by
simp only [monadLift, uLift_eq_toIter_uLift_toIterM, length_eq_length_toIterM, toIterM_toIter]
rw [IterM.length_uLift]
simp [monadLift]
@[deprecated Iter.length_uLift (since := "2026-01-28")]
def Iter.count_uLift := @Iter.length_uLift
end Std

View File

@@ -7,6 +7,7 @@ module
prelude
public import Init.Data.Iterators.Consumers.Access
import Init.Data.Iterators.Lemmas.Basic
namespace Std.Iter
open Std.Iterators
@@ -21,6 +22,6 @@ public theorem atIdxSlow?_eq_match [Iterator α Id β] [Productive α Id]
| n + 1 => it'.atIdxSlow? n
| .skip it' => it'.atIdxSlow? n
| .done => none) := by
fun_induction it.atIdxSlow? n <;> simp_all
induction n, it using Iter.atIdxSlow?.induct_unfolding <;> simp_all
end Std.Iter

View File

@@ -19,6 +19,10 @@ public section
namespace Std
open Std.Iterators
@[simp]
theorem IterM.run_toList_mk' {α : Type u} {β : Type u} [Std.Iterator α Id β] (a : α) :
(Std.IterM.mk' (m := Id) a).toList.run = (Std.Iter.mk a).toList := rfl
theorem Iter.toArray_eq_toArray_toIterM {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toArray = it.toIterM.toArray.run :=
@@ -163,12 +167,14 @@ theorem Iter.getElem?_toList_eq_atIdxSlow? {α β}
{it : Iter (α := α) β} {k : Nat} :
it.toList[k]? = it.atIdxSlow? k := by
induction it using Iter.inductSteps generalizing k with | step it ihy ihs
rw [toList_eq_match_step, atIdxSlow?]
obtain step, h := it.step
cases step
· cases k <;> simp [ihy h]
· simp [ihs h]
· simp
rw [toList_eq_match_step, atIdxSlow?, WellFounded.extrinsicFix₂_eq_apply]
· obtain step, h := it.step
cases step
· cases k <;> simp [ihy h, atIdxSlow?]
· simp [ihs h, atIdxSlow?]
· simp
· apply InvImage.wf
exact WellFoundedRelation.wf
theorem Iter.toList_eq_of_atIdxSlow?_eq {α₁ α₂ β}
[Iterator α₁ Id β] [Finite α₁ Id]

View File

@@ -460,69 +460,90 @@ theorem Iter.foldl_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [F
it.toArray.foldl (init := init) f = it.fold (init := init) f := by
rw [fold_eq_foldM, Array.foldl_eq_foldlM, Iter.foldlM_toArray]
theorem Iter.count_eq_count_toIterM {α β : Type w} [Iterator α Id β]
theorem Iter.length_eq_length_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] {it : Iter (α := α) β} :
it.count = it.toIterM.count.run.down :=
it.length = it.toIterM.length.run.down :=
(rfl)
theorem Iter.count_eq_fold {α β : Type w} [Iterator α Id β]
@[deprecated Iter.length_eq_length_toIterM (since := "2026-01-28")]
def Iter.count_eq_count_toIterM := @Iter.length_eq_length_toIterM
theorem Iter.length_eq_fold {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] [LawfulIteratorLoop α Id Id.{w}]
[IteratorLoop α Id Id.{0}] [LawfulIteratorLoop α Id Id.{0}]
{it : Iter (α := α) β} :
it.count = it.fold (γ := Nat) (init := 0) (fun acc _ => acc + 1) := by
rw [count_eq_count_toIterM, IterM.count_eq_fold, fold_eq_fold_toIterM]
it.length = it.fold (γ := Nat) (init := 0) (fun acc _ => acc + 1) := by
rw [length_eq_length_toIterM, IterM.length_eq_fold, fold_eq_fold_toIterM]
rw [ fold_hom (f := ULift.down)]
simp
theorem Iter.count_eq_forIn {α β : Type w} [Iterator α Id β]
@[deprecated Iter.length_eq_fold (since := "2026-01-28")]
def Iter.count_eq_fold := @Iter.length_eq_fold
theorem Iter.length_eq_forIn {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] [LawfulIteratorLoop α Id Id.{w}]
[IteratorLoop α Id Id.{0}] [LawfulIteratorLoop α Id Id.{0}]
{it : Iter (α := α) β} :
it.count = (ForIn.forIn (m := Id) it 0 (fun _ acc => return .yield (acc + 1))).run := by
rw [count_eq_fold, forIn_pure_yield_eq_fold, Id.run_pure]
it.length = (ForIn.forIn (m := Id) it 0 (fun _ acc => return .yield (acc + 1))).run := by
rw [length_eq_fold, forIn_pure_yield_eq_fold, Id.run_pure]
theorem Iter.count_eq_match_step {α β : Type w} [Iterator α Id β]
@[deprecated Iter.length_eq_forIn (since := "2026-01-28")]
def Iter.count_eq_forIn := @Iter.length_eq_forIn
theorem Iter.length_eq_match_step {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.count = (match it.step.val with
| .yield it' _ => it'.count + 1
| .skip it' => it'.count
it.length = (match it.step.val with
| .yield it' _ => it'.length + 1
| .skip it' => it'.length
| .done => 0) := by
simp only [count_eq_count_toIterM]
rw [IterM.count_eq_match_step]
simp only [length_eq_length_toIterM]
rw [IterM.length_eq_match_step]
simp only [bind_pure_comp, id_map', Id.run_bind, Iter.step]
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
@[simp]
theorem Iter.size_toArray_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toArray.size = it.count := by
simp only [toArray_eq_toArray_toIterM, count_eq_count_toIterM, Id.run_map,
IterM.up_size_toArray_eq_count]
@[deprecated Iter.size_toArray_eq_count (since := "2025-10-29")]
def Iter.size_toArray_eq_size := @size_toArray_eq_count
@[deprecated Iter.length_eq_match_step (since := "2026-01-28")]
def Iter.count_eq_match_step := @Iter.length_eq_match_step
@[simp]
theorem Iter.length_toList_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
theorem Iter.size_toArray_eq_length {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toList.length = it.count := by
rw [ toList_toArray, Array.length_toList, size_toArray_eq_count]
it.toArray.size = it.length := by
simp only [toArray_eq_toArray_toIterM, length_eq_length_toIterM, Id.run_map,
IterM.up_size_toArray_eq_length]
@[deprecated Iter.length_toList_eq_count (since := "2025-10-29")]
def Iter.length_toList_eq_size := @length_toList_eq_count
@[deprecated Iter.size_toArray_eq_length (since := "2025-10-29")]
def Iter.size_toArray_eq_size := @size_toArray_eq_length
@[deprecated Iter.size_toArray_eq_length (since := "2026-01-28")]
def Iter.size_toArray_eq_count := @size_toArray_eq_length
@[simp]
theorem Iter.length_toListRev_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
theorem Iter.length_toList_eq_length {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toListRev.length = it.count := by
rw [toListRev_eq, List.length_reverse, length_toList_eq_count]
it.toList.length = it.length := by
rw [ toList_toArray, Array.length_toList, size_toArray_eq_length]
@[deprecated Iter.length_toListRev_eq_count (since := "2025-10-29")]
def Iter.length_toListRev_eq_size := @length_toListRev_eq_count
@[deprecated Iter.length_toList_eq_length (since := "2025-10-29")]
def Iter.length_toList_eq_size := @length_toList_eq_length
@[deprecated Iter.length_toList_eq_length (since := "2026-01-28")]
def Iter.length_toList_eq_count := @length_toList_eq_length
@[simp]
theorem Iter.length_toListRev_eq_length {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toListRev.length = it.length := by
rw [toListRev_eq, List.length_reverse, length_toList_eq_length]
@[deprecated Iter.length_toListRev_eq_length (since := "2025-10-29")]
def Iter.length_toListRev_eq_size := @length_toListRev_eq_length
@[deprecated Iter.length_toListRev_eq_length (since := "2026-01-28")]
def Iter.length_toListRev_eq_count := @length_toListRev_eq_length
theorem Iter.anyM_eq_forIn {α β : Type w} {m : Type Type w'} [Iterator α Id β]
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -915,4 +936,50 @@ theorem Iter.findM?_pure {α β : Type w} {m : Type w → Type w'} [Monad m]
· simp [ihs _]
· simp
theorem Iter.first?_eq_first?_toIterM {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.first? = it.toIterM.first?.run := (rfl)
theorem Iter.first?_eq_match_step {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Productive α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.first? = match it.step.val with
| .yield _ out => some out
| .skip it' => it'.first?
| .done => none := by
rw [Iter.first?_eq_first?_toIterM, IterM.first?_eq_match_step]
simp only [Id.run_bind, step]
generalize it.toIterM.step.run.inflate = s
rcases s with _|_|_, _ <;> simp [Iter.first?_eq_first?_toIterM]
@[simp, grind =]
theorem Iter.head?_toList {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.toList.head? = it.first? := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [first?_eq_match_step, toList_eq_match_step]
cases it.step using PlausibleIterStep.casesOn <;> simp [*]
theorem Iter.isEmpty_eq_isEmpty_toIterM {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.isEmpty = it.toIterM.isEmpty.run.down := (rfl)
theorem Iter.isEmpty_eq_match_step {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Productive α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.isEmpty = match it.step.val with
| .yield _ _ => false
| .skip it' => it'.isEmpty
| .done => true := by
rw [Iter.isEmpty_eq_isEmpty_toIterM, IterM.isEmpty_eq_match_step]
simp only [Id.run_bind, step]
generalize it.toIterM.step.run.inflate = s
rcases s with _|_|_, _ <;> simp [Iter.isEmpty_eq_isEmpty_toIterM]
@[simp, grind =]
theorem Iter.isEmpty_toList {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.toList.isEmpty = it.isEmpty := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [isEmpty_eq_match_step, toList_eq_match_step]
cases it.step using PlausibleIterStep.casesOn <;> simp [*]
end Std

View File

@@ -15,6 +15,15 @@ public section
namespace Std
open Std.Iterators
theorem IterM.DefaultConsumers.forIn_eq {α β : Type w} {m : Type w Type w'}
{n : Type x Type x'} [Monad n] [Iterator α m β]
{lift : (γ : Type w) (δ : Type x) (γ n δ) m γ n δ}
{plausible_forInStep : β γ ForInStep γ Prop} {it : IterM (α := α) m β} {init : γ}
{f : (b : β) it.IsPlausibleIndirectOutput b (c : γ) n (Subtype (plausible_forInStep b c))} :
letI : IteratorLoop α m n := .defaultImplementation
IteratorLoop.forIn lift γ plausible_forInStep it init f =
IterM.DefaultConsumers.forIn' lift γ plausible_forInStep it init _ (fun _ => id) f := rfl
theorem IterM.DefaultConsumers.forIn'_eq_match_step {α β : Type w} {m : Type w Type w'}
[Iterator α m β] {n : Type x Type x'} [Monad n] [LawfulMonad n]
{lift : γ δ, (γ n δ) m γ n δ} {γ : Type x}
@@ -467,27 +476,33 @@ theorem IterM.drain_eq_map_toArray {α β : Type w} {m : Type w → Type w'} [It
it.drain = (fun _ => .unit) <$> it.toList := by
simp [IterM.drain_eq_map_toList]
theorem IterM.count_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
theorem IterM.length_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m]
{it : IterM (α := α) m β} :
it.count = it.fold (init := .up 0) (fun acc _ => .up <| acc.down + 1) :=
it.length = it.fold (init := .up 0) (fun acc _ => .up <| acc.down + 1) :=
(rfl)
theorem IterM.count_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
@[deprecated IterM.length_eq_fold (since := "2026-01-28")]
def IterM.count_eq_fold := @IterM.length_eq_fold
theorem IterM.length_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m]
{it : IterM (α := α) m β} :
it.count = ForIn.forIn it (.up 0) (fun _ acc => return .yield (.up (acc.down + 1))) :=
it.length = ForIn.forIn it (.up 0) (fun _ acc => return .yield (.up (acc.down + 1))) :=
(rfl)
theorem IterM.count_eq_match_step {α β : Type w} {m : Type w Type w'} [Iterator α m β]
@[deprecated IterM.length_eq_forIn (since := "2026-01-28")]
def IterM.count_eq_forIn := @IterM.length_eq_forIn
theorem IterM.length_eq_match_step {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
it.count = (do
it.length = (do
match ( it.step).inflate.val with
| .yield it' _ => return .up (( it'.count).down + 1)
| .skip it' => return .up ( it'.count).down
| .yield it' _ => return .up (( it'.length).down + 1)
| .skip it' => return .up ( it'.length).down
| .done => return .up 0) := by
simp only [count_eq_fold]
simp only [length_eq_fold]
have (acc : Nat) (it' : IterM (α := α) m β) :
it'.fold (init := ULift.up acc) (fun acc _ => .up (acc.down + 1)) =
(ULift.up <| ·.down + acc) <$>
@@ -503,33 +518,45 @@ theorem IterM.count_eq_match_step {α β : Type w} {m : Type w → Type w'} [Ite
· simp
· simp
@[deprecated IterM.length_eq_match_step (since := "2026-01-28")]
def IterM.count_eq_match_step := @IterM.length_eq_match_step
@[simp]
theorem IterM.up_size_toArray_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
theorem IterM.up_size_toArray_eq_length {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.size) <$> it.toArray = it.count := by
rw [toArray_eq_fold, count_eq_fold, fold_hom]
(.up <| ·.size) <$> it.toArray = it.length := by
rw [toArray_eq_fold, length_eq_fold, fold_hom]
· simp only [List.size_toArray, List.length_nil]; rfl
· simp
@[deprecated IterM.up_size_toArray_eq_length (since := "2026-01-28")]
def IterM.up_size_toArray_eq_count := @IterM.up_size_toArray_eq_length
@[simp]
theorem IterM.up_length_toList_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
theorem IterM.up_length_toList_eq_length {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toList = it.count := by
rw [toList_eq_fold, count_eq_fold, fold_hom]
(.up <| ·.length) <$> it.toList = it.length := by
rw [toList_eq_fold, length_eq_fold, fold_hom]
· simp only [List.length_nil]; rfl
· simp
@[deprecated IterM.up_length_toList_eq_length (since := "2026-01-28")]
def IterM.up_length_toList_eq_count := @IterM.up_length_toList_eq_length
@[simp]
theorem IterM.up_length_toListRev_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
theorem IterM.up_length_toListRev_eq_length {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toListRev = it.count := by
simp only [toListRev_eq, Functor.map_map, List.length_reverse, up_length_toList_eq_count]
(.up <| ·.length) <$> it.toListRev = it.length := by
simp only [toListRev_eq, Functor.map_map, List.length_reverse, up_length_toList_eq_length]
@[deprecated IterM.up_length_toListRev_eq_length (since := "2026-01-28")]
def IterM.up_length_toListRev_eq_count := @IterM.up_length_toListRev_eq_length
theorem IterM.anyM_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
@@ -731,7 +758,7 @@ theorem IterM.findSomeM?_eq_match_step {α β γ : Type w} {m : Type w → Type
· simp
theorem IterM.findSome?_eq_findSomeM? {α β γ : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
[Iterator α m β] [IteratorLoop α m m]
{it : IterM (α := α) m β} {f : β Option γ} :
it.findSome? f = it.findSomeM? (pure <| f ·) :=
(rfl)
@@ -832,4 +859,44 @@ theorem IterM.findM?_pure {α β : Type w} {m : Type w → Type w'} [Monad m]
· simp [ihs _]
· simp
theorem IterM.first?_eq_match_step {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Productive α m]
[LawfulIteratorLoop α m m] {it : IterM (α := α) m β} :
it.first? = (do
match ( it.step).inflate.val with
| .yield _ out => return (some out)
| .skip it' => it'.first?
| .done => return none) := by
simp only [first?]
have := IteratorLoop.wellFounded_of_productive (α := α) (β := β) (m := m)
(P := fun b g s => s = ForInStep.done (some b)) (by simp)
simp only [LawfulIteratorLoop.lawful _ _ _ _ _ this]
rw [IterM.DefaultConsumers.forIn_eq, IterM.DefaultConsumers.forIn'_eq_match_step _ this]
simp only [flip, pure_bind]
congr
ext s
split <;> try (simp [*]; done)
simp only [DefaultConsumers.forIn_eq, *]
exact IterM.DefaultConsumers.forIn'_eq_forIn' _ this (by simp)
theorem IterM.isEmpty_eq_match_step {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Productive α m]
[LawfulIteratorLoop α m m] {it : IterM (α := α) m β} :
it.isEmpty = (do
match ( it.step).inflate.val with
| .yield _ _ => return .up false
| .skip it' => it'.isEmpty
| .done => return .up true) := by
simp only [isEmpty]
have := IteratorLoop.wellFounded_of_productive (α := α) (β := β) (m := m)
(P := fun _ _ s => s = ForInStep.done (ULift.up false)) (by simp)
simp only [LawfulIteratorLoop.lawful _ _ _ _ _ this]
rw [IterM.DefaultConsumers.forIn_eq, IterM.DefaultConsumers.forIn'_eq_match_step _ this]
simp only [flip, pure_bind]
congr
ext s
split <;> try (simp [*]; done)
simp only [DefaultConsumers.forIn_eq, *]
exact IterM.DefaultConsumers.forIn'_eq_forIn' _ this (by simp)
end Std

View File

@@ -16,8 +16,11 @@ public import Init.Data.List.Find
public import Init.Data.List.Impl
public import Init.Data.List.Lemmas
public import Init.Data.List.MinMax
public import Init.Data.List.MinMaxIdx
public import Init.Data.List.MinMaxOn
public import Init.Data.List.Monadic
public import Init.Data.List.Nat
public import Init.Data.List.Int
public import Init.Data.List.Notation
public import Init.Data.List.Pairwise
public import Init.Data.List.Sublist

View File

@@ -2017,6 +2017,7 @@ def sum {α} [Add α] [Zero α] : List αα :=
@[simp, grind =] theorem sum_nil [Add α] [Zero α] : ([] : List α).sum = 0 := rfl
@[simp, grind =] theorem sum_cons [Add α] [Zero α] {a : α} {l : List α} : (a::l).sum = a + l.sum := rfl
theorem sum_eq_foldr [Add α] [Zero α] {l : List α} : l.sum = l.foldr (· + ·) 0 := rfl
/-! ### range -/

View File

@@ -7,6 +7,7 @@ module
prelude
public import Init.Data.List.Sublist
import Init.Grind.Util
public section
@@ -97,6 +98,18 @@ theorem countP_le_length : countP p l ≤ l.length := by
@[simp] theorem countP_eq_zero {p} : countP p l = 0 a l, ¬p a := by
simp only [countP_eq_length_filter, length_eq_zero_iff, filter_eq_nil_iff]
/-- This lemma is only relevant for `grind`. -/
@[grind =]
theorem _root_.Std.Internal.List.countP_eq_zero_of_forall {xs : List α} (h : x xs, ¬ p x) : xs.countP p = 0 :=
countP_eq_zero.mpr h
/-- This lemma is only relevant for `grind`. -/
theorem _root_.Std.Internal.List.not_of_countP_eq_zero_of_mem {xs : List α} (h : xs.countP p = 0) (h' : x xs) : ¬ p x :=
countP_eq_zero.mp h _ h'
grind_pattern Std.Internal.List.not_of_countP_eq_zero_of_mem => xs.countP p, x xs where
guard xs.countP p = 0
@[simp] theorem countP_eq_length {p} : countP p l = l.length a l, p a := by
rw [countP_eq_length_filter, length_filter_eq_length_iff]

View File

@@ -1077,6 +1077,37 @@ theorem isNone_findFinIdx? {l : List α} {p : α → Bool} :
simp [hf, findFinIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### find? and findFinIdx? -/
theorem find?_eq_map_findFinIdx?_getElem {xs : List α} {p : α Bool} :
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findFinIdx?_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih, Function.comp_def]
theorem find?_eq_bind_findIdx?_getElem? {xs : List α} {p : α Bool} :
xs.find? p = (xs.findIdx? p).bind (xs[·]?) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findIdx?_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih, Option.bind_map, Function.comp_def]
theorem find?_eq_getElem?_findIdx {xs : List α} {p : α Bool} :
xs.find? p = xs[xs.findIdx p]? := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findIdx_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih]
/-! ### idxOf
@@ -1103,8 +1134,6 @@ theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
· rw [if_neg]
simpa using h
theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a l) : l.idxOf a = l.length := by
induction l with
| nil => rfl
@@ -1113,8 +1142,6 @@ theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.
simp only [idxOf_cons, cond_eq_ite, beq_iff_eq]
split <;> simp_all
theorem idxOf_lt_length_of_mem [BEq α] [EquivBEq α] {l : List α} (h : a l) : l.idxOf a < l.length := by
induction l with
| nil => simp at h
@@ -1143,8 +1170,6 @@ theorem idxOf_lt_length_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
grind_pattern idxOf_lt_length_iff => l.idxOf a, l.length
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
@@ -1214,7 +1239,9 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
· rintro w x h rfl
contradiction
theorem idxOf?_eq_some_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} {i : Nat} :
l.idxOf? a = some i (h : i < l.length), l[i] = a j (_ : j < i), ¬l[j] = a := by
simp [idxOf?, findIdx?_eq_some_iff_getElem]
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
@@ -1230,6 +1257,56 @@ theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isNone = ¬ a l := by
simp
theorem finIdxOf?_eq_pmap_idxOf? {l : List α} {a : α} [BEq α] [LawfulBEq α] :
l.finIdxOf? a =
(l.idxOf? a).pmap
(fun i h => i, (idxOf?_eq_some_iff.mp h).1)
(fun _ h => h) := by
ext i, h
simp only [finIdxOf?_eq_some_iff, Fin.getElem_fin, Fin.forall_iff, Fin.mk_lt_mk,
idxOf?_eq_some_iff, Option.pmap_eq_some_iff, Fin.mk.injEq, exists_and_left, exists_prop,
and_self_left, exists_eq_right', h, exists_true_left, and_congr_right_iff]
intro w
constructor
· intro w j h₁
apply w <;> omega
· intro w j h₁ h₂
apply w <;> omega
/-! ### find? and idxOf? -/
theorem findIdx?_eq_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α Bool} :
xs.findIdx? p = (xs.find? p).bind (xs.idxOf?) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx?_cons, find?_cons]
split <;> rename_i h
· simp [h, idxOf?_cons]
· simp [h, ih, Function.comp_def, idxOf?_cons]
cases w : xs.find? p with
| none => simp
| some x' =>
simp
rintro rfl
have := find?_some w
contradiction
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α Bool} :
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf?) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_eq_bind_find?_idxOf?, finIdxOf?_eq_pmap_idxOf?]
ext i
simp only [Option.bind_eq_some_iff, Option.pmap_eq_some_iff, exists_and_left, and_exists_self]
constructor
· rintro a, h₁, h₂, rfl
exact h₁, by simp [h₂]
· rintro h₁, h₂, a, h₃, rfl
exact a, h₁, h₂, h₃, rfl
theorem findIdx_eq_getD_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α Bool} :
xs.findIdx p = ((xs.find? p).bind (xs.idxOf?)).getD xs.length := by
rw [findIdx_eq_getD_findIdx?, findIdx?_eq_bind_find?_idxOf?]
/-! ### lookup -/
section lookup

View File

@@ -0,0 +1,9 @@
/-
Copyright (c) 2026 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.List.Int.Sum

View File

@@ -0,0 +1,107 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Graf, Paul Reichert
-/
module
prelude
public import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.DivMod.Lemmas
import Init.Data.List.MinMax
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
@[simp]
theorem sum_replicate_int {n : Nat} {a : Int} : (replicate n a).sum = n * a := by
induction n <;> simp_all [replicate_succ, Int.add_mul, Int.add_comm]
theorem sum_append_int {l₁ l₂ : List Int} : (l₁ ++ l₂).sum = l₁.sum + l₂.sum := by
simp [sum_append]
theorem sum_reverse_int (xs : List Int) : xs.reverse.sum = xs.sum := by
simp [sum_reverse]
theorem min_mul_length_le_sum_int {xs : List Int} (h : xs []) :
xs.min h * xs.length xs.sum := by
induction xs
· contradiction
· rename_i x xs ih
cases xs
· simp_all [List.min_singleton]
· simp only [ne_eq, reduceCtorEq, not_false_eq_true, min_eq_get_min?,
List.min?_cons (α := Int), Option.get_some, length_cons, Int.natCast_add, Int.cast_ofNat_Int,
forall_const] at ih
rw [Int.mul_add, Int.mul_one, Int.add_comm]
apply Int.add_le_add
· apply Int.min_le_left
· refine Int.le_trans ?_ ih
rw [Int.mul_le_mul_right (by omega)]
apply Int.min_le_right
theorem mul_length_le_sum_of_min?_eq_some_int {xs : List Int} (h : xs.min? = some x) :
x * xs.length xs.sum := by
cases xs
· simp_all
· simp only [min?_eq_some_min (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using min_mul_length_le_sum_int _
theorem min_le_sum_div_length_int {xs : List Int} (h : xs []) :
xs.min h xs.sum / xs.length := by
have := min_mul_length_le_sum_int h
rwa [Int.le_ediv_iff_mul_le]
simp [List.length_pos_iff, h]
theorem le_sum_div_length_of_min?_eq_some_int {xs : List Int} (h : xs.min? = some x) :
x xs.sum / xs.length := by
cases xs
· simp_all
· simp only [min?_eq_some_min (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using min_le_sum_div_length_int _
theorem sum_le_max_mul_length_int {xs : List Int} (h : xs []) :
xs.sum xs.max h * xs.length := by
induction xs
· contradiction
· rename_i x xs ih
cases xs
· simp_all [List.max_singleton]
· simp only [ne_eq, reduceCtorEq, not_false_eq_true, max_eq_get_max?,
List.max?_cons (α := Int), Option.get_some, length_cons, Int.natCast_add, Int.cast_ofNat_Int,
forall_const] at ih
rw [Int.mul_add, Int.mul_one, Int.add_comm]
apply Int.add_le_add
· apply Int.le_max_left
· refine Int.le_trans ih ?_
rw [Int.mul_le_mul_right (by omega)]
apply Int.le_max_right
theorem sum_le_max_mul_length_of_max?_eq_some_int {xs : List Int} (h : xs.max? = some x) :
xs.sum x * xs.length := by
cases xs
· simp_all
· simp only [max?_eq_some_max (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using sum_le_max_mul_length_int _
theorem sum_div_length_le_max_int {xs : List Int} (h : xs []) :
xs.sum / xs.length xs.max h := by
have := sum_le_max_mul_length_int h
rw [Int.ediv_le_iff_le_mul]
· refine Int.lt_of_le_of_lt this ?_
apply Int.lt_add_of_pos_right
simp [ Nat.ne_zero_iff_zero_lt, h]
· simp [List.length_pos_iff, h]
theorem sum_div_length_le_max_of_max?_eq_some_int {xs : List Int} (h : xs.max? = some x) :
xs.sum / xs.length x := by
cases xs
· simp_all
· simp only [max?_eq_some_max (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using sum_div_length_le_max_int _
end List

View File

@@ -482,25 +482,28 @@ theorem mem_iff_getElem {a} {l : List α} : a ∈ l ↔ ∃ (i : Nat) (h : i < l
theorem mem_iff_getElem? {a} {l : List α} : a l i : Nat, l[i]? = some a := by
simp [getElem?_eq_some_iff, mem_iff_getElem]
theorem exists_mem_iff_exists_getElem {P : α Prop} {l : List α} :
( x l, P x) (i : Nat), hi, P (l[i]) := by
simp only [mem_iff_getElem]
apply Iff.intro
· rintro _, i, hi, rfl, hP
exact i, hi, hP
· rintro i, hi, hP
exact _, i, hi, rfl, hP
theorem forall_mem_iff_forall_getElem {P : α Prop} {l : List α} :
( x l, P x) (i : Nat) hi, P (l[i]) := by
simp only [mem_iff_getElem]
apply Iff.intro
· intro h i hi
exact h l[i] i, hi, rfl
· rintro h _ i, hi, rfl
exact h i hi
@[deprecated forall_mem_iff_forall_getElem (since := "2026-01-29")]
theorem forall_getElem {l : List α} {p : α Prop} :
( (i : Nat) h, p (l[i]'h)) a, a l p a := by
induction l with
| nil => simp
| cons a l ih =>
simp only [length_cons, mem_cons, forall_eq_or_imp]
constructor
· intro w
constructor
· exact w 0 (by simp)
· apply ih.1
intro n h
simpa using w (n+1) (Nat.add_lt_add_right h 1)
· rintro h, w
rintro (_ | n) h
· simpa
· apply w
simp only [getElem_cons_succ]
exact getElem_mem (lt_of_succ_lt_succ h)
( (i : Nat) h, p (l[i]'h)) a, a l p a :=
forall_mem_iff_forall_getElem.symm
@[simp] theorem elem_eq_contains [BEq α] {a : α} {l : List α} :
elem a l = l.contains a := by
@@ -1827,12 +1830,17 @@ theorem append_eq_map_iff {f : α → β} :
rw [eq_comm, map_eq_append_iff]
@[simp, grind =]
theorem sum_append_nat {l₁ l₂ : List Nat} : (l₁ ++ l₂).sum = l₁.sum + l₂.sum := by
induction l₁ generalizing l₂ <;> simp_all [Nat.add_assoc]
theorem sum_append [Add α] [Zero α] [Std.LawfulLeftIdentity (α := α) (· + ·) 0]
[Std.Associative (α := α) (· + ·)] {l₁ l₂ : List α} : (l₁ ++ l₂).sum = l₁.sum + l₂.sum := by
induction l₁ generalizing l₂ <;> simp_all [Std.Associative.assoc, Std.LawfulLeftIdentity.left_id]
@[simp, grind =]
theorem sum_reverse_nat (xs : List Nat) : xs.reverse.sum = xs.sum := by
induction xs <;> simp_all [Nat.add_comm]
theorem sum_reverse [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
[Std.Commutative (α := α) (· + ·)]
[Std.LawfulLeftIdentity (α := α) (· + ·) 0] (xs : List α) : xs.reverse.sum = xs.sum := by
induction xs <;>
simp_all [sum_append, Std.Commutative.comm (α := α) _ 0,
Std.LawfulLeftIdentity.left_id, Std.Commutative.comm]
/-! ### concat
@@ -2363,9 +2371,6 @@ theorem replicateRecOn {α : Type _} {p : List α → Prop} (l : List α)
exact hi _ _ _ _ h hn (replicateRecOn (b :: l') h0 hr hi)
termination_by l.length
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
induction n <;> simp_all [replicate_succ, Nat.add_mul, Nat.add_comm]
/-! ### reverse -/
@[simp, grind =] theorem length_reverse {as : List α} : (as.reverse).length = as.length := by

View File

@@ -85,7 +85,7 @@ theorem cons_lex_cons_iff : Lex r (a :: l₁) (b :: l₂) ↔ r a b a = b
theorem cons_lt_cons_iff [LT α] {a b} {l₁ l₂ : List α} :
(a :: l₁) < (b :: l₂) a < b a = b l₁ < l₂ := by
dsimp only [instLT, List.lt]
simp only [LT.lt, List.lt]
simp [cons_lex_cons_iff]
@[simp] theorem cons_lt_cons_self [LT α] [i₀ : Std.Irrefl (· < · : α α Prop)] {l₁ l₂ : List α} :
@@ -101,7 +101,7 @@ theorem cons_le_cons_iff [LT α]
[i₂ : Std.Trichotomous (· < · : α α Prop)]
{a b} {l₁ l₂ : List α} :
(a :: l₁) (b :: l₂) a < b a = b l₁ l₂ := by
dsimp only [instLE, instLT, List.le, List.lt]
simp only [LE.le, LT.lt, List.le, List.lt]
open Classical in
simp only [not_cons_lex_cons_iff, ne_eq]
constructor

View File

@@ -47,7 +47,7 @@ theorem min?_cons' [Min α] {xs : List α} : (x :: xs).min? = some (foldl min x
cases xs <;> simp [min?]
@[simp, grind =]
public theorem isSome_min?_iff {xs : List α} [Min α] : xs.min?.isSome xs [] := by
public theorem isSome_min?_iff [Min α] {xs : List α} : xs.min?.isSome xs [] := by
cases xs <;> simp [min?]
@[grind .]
@@ -55,8 +55,8 @@ theorem isSome_min?_of_mem {l : List α} [Min α] {a : α} (h : a ∈ l) :
l.min?.isSome := by
cases l <;> simp_all [min?_cons']
theorem isSome_min?_of_ne_nil [Min α] : {l : List α} (hl : l []) l.min?.isSome
| x::xs, h => by simp [min?_cons']
theorem isSome_min?_of_ne_nil [Min α] {l : List α} (hl : l []) : l.min?.isSome := by
rwa [isSome_min?_iff]
theorem min?_eq_head? {α : Type u} [Min α] {l : List α}
(h : l.Pairwise (fun a b => min a b = a)) : l.min? = l.head? := by
@@ -242,7 +242,7 @@ theorem max?_cons' [Max α] {xs : List α} : (x :: xs).max? = some (foldl max x
cases xs <;> simp [max?]
@[simp, grind =]
public theorem isSome_max?_iff {xs : List α} [Max α] : xs.max?.isSome xs [] := by
public theorem isSome_max?_iff [Max α] {xs : List α} : xs.max?.isSome xs [] := by
cases xs <;> simp [max?]
@[grind .]
@@ -250,8 +250,8 @@ theorem isSome_max?_of_mem {l : List α} [Max α] {a : α} (h : a ∈ l) :
l.max?.isSome := by
cases l <;> simp_all [max?_cons']
theorem isSome_max?_of_ne_nil [Max α] : {l : List α} (hl : l []) l.max?.isSome
| x::xs, h => by simp [max?_cons']
theorem isSome_max?_of_ne_nil [Max α] {l : List α} (hl : l []) : l.max?.isSome := by
rwa [isSome_max?_iff]
theorem max?_eq_head? {α : Type u} [Max α] {l : List α}
(h : l.Pairwise (fun a b => max a b = a)) : l.max? = l.head? := by

View File

@@ -0,0 +1,830 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.List.MinMaxOn
import Init.Data.List.MinMaxOn
public import Init.Data.List.Pairwise
public import Init.Data.Subtype.Order
import Init.Data.Order.Lemmas
import Init.Data.List.Nat.TakeDrop
import Init.Data.Order.Opposite
import Init.Data.Nat.Order
public section
open Std
open scoped OppositeOrderInstances
set_option doc.verso true
set_option linter.missingDocs true
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
/--
Returns the index of an element of the non-empty list {name}`xs` that minimizes {name}`f`.
If {given}`x, y` are such that {lean}`f x = f y`, it returns the index of whichever comes first
in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def minIdxOn [LE β] [DecidableLE β] (f : α β) (xs : List α) (h : xs []) : Nat :=
match xs with
| y :: ys => go y 0 1 ys
where
@[specialize]
go (x : α) (i : Nat) (j : Nat) (xs : List α) :=
match xs with
| [] => i
| y :: ys =>
if f x f y then
go x i (j + 1) ys
else
go y j (j + 1) ys
/--
Returns the index of an element of {name}`xs` that minimizes {name}`f`. If {given}`x, y`
are such that {lean}`f x = f y`, it returns the index of whichever comes first in the list.
Returns {name}`none` if the list is empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def minIdxOn? [LE β] [DecidableLE β] (f : α β) (xs : List α) : Option Nat :=
match xs with
| [] => none
| y :: ys => some ((y :: ys).minIdxOn f (nomatch ·))
/--
Returns the index of an element of the non-empty list {name}`xs` that maximizes {name}`f`.
If {given}`x, y` are such that {lean}`f x = f y`, it returns the index of whichever comes first
in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def maxIdxOn [LE β] [DecidableLE β] (f : α β) (xs : List α) (h : xs []) : Nat :=
letI : LE β := LE.opposite inferInstance
xs.minIdxOn f h
/--
Returns the index of an element of {name}`xs` that maximizes {name}`f`. If {given}`x, y`
are such that {lean}`f x = f y`, it returns the index of whichever comes first in the list.
Returns {name}`none` if the list is empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def maxIdxOn? [LE β] [DecidableLE β] (f : α β) (xs : List α) : Option Nat :=
letI : LE β := LE.opposite inferInstance
xs.minIdxOn? f
protected theorem maxIdxOn_eq_minIdxOn {le : LE β} {_ : DecidableLE β} {f : α β}
{xs : List α} {h} :
xs.maxIdxOn f h = (letI := le.opposite; xs.minIdxOn f h) :=
(rfl)
private theorem minIdxOn.go_lt_length_add [LE β] [DecidableLE β] {f : α β} {x : α}
{i j : Nat} {xs : List α} (h : i < j) :
List.minIdxOn.go f x i j xs < xs.length + j := by
induction xs generalizing x i j
· simp [go, h]
· rename_i y ys ih
simp only [go, length_cons, Nat.add_assoc, Nat.add_comm 1]
split
· exact ih (Nat.lt_succ_of_lt i < j)
· exact ih (Nat.lt_succ_self j)
private theorem minIdxOn.go_eq_of_forall_le [LE β] [DecidableLE β] {f : α β}
{x : α} {i j : Nat} {xs : List α} (h : y xs, f x f y) :
List.minIdxOn.go f x i j xs = i := by
induction xs generalizing x i j
· simp [go]
· rename_i y ys ih
simp only [go]
split
· apply ih
simp_all
· simp_all
private theorem exists_getElem_eq_of_drop_eq_cons {xs : List α} {k : Nat} {y : α} {ys : List α}
(h : xs.drop k = y :: ys) : hlt : k < xs.length, xs[k] = y := by
have hlt : k < xs.length := by
false_or_by_contra
have : drop k xs = [] := drop_of_length_le (by omega)
simp [this] at h
refine hlt, ?_
have := take_append_drop k xs
rw [h] at this
simp +singlePass only [ this]
rw [getElem_append_right (length_take_le _ _)]
simp [length_take_of_le (Nat.le_of_lt hlt)]
private theorem take_succ_eq_append_of_drop_eq_cons {xs : List α} {k : Nat} {y : α}
{ys : List α} (h : xs.drop k = y :: ys) : xs.take (k + 1) = xs.take k ++ [y] := by
obtain hlt, rfl := exists_getElem_eq_of_drop_eq_cons h
rw [take_succ_eq_append_getElem hlt]
private theorem minIdxOn_eq_go_drop [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{xs : List α} (h : xs []) {k : Nat} :
(i : Nat) (hlt : i < xs.length), i k xs[i] = (xs.take (k + 1)).minOn f (by simpa)
xs.minIdxOn f h = List.minIdxOn.go f ((xs.take (k + 1)).minOn f (by cases xs <;> simp_all)) i (k + 1) (xs.drop (k + 1)) := by
match xs with
| y :: ys =>
simp only [drop_succ_cons]
induction k
· simp [minIdxOn]
· rename_i k ih
specialize ih
obtain i, hlt, hi, ih := ih
simp only [ih, drop_drop]
simp only [length_cons] at hlt
match h : drop k ys with
| [] =>
have : ys.length k := by simp_all
simp [drop_nil, minIdxOn.go, take_of_length_le, hi, ih, hlt, this, Nat.le_succ_of_le]
| z :: zs =>
simp only [minIdxOn.go]
have : take (k + 1 + 1) (y :: ys) = take (k + 1) (y :: ys) ++ [z] := by apply take_succ_eq_append_of_drop_eq_cons _
simp only [this, List.minOn_append (xs := take (k + 1) (y :: ys)) (by simp) (cons_ne_nil _ _)]
simp only [take_succ_cons] at this
split
· simp only [List.minOn_singleton, minOn_eq_left, length_cons, *]
exact i, by omega, Nat.le_succ_of_le i k, by simp [ih], rfl
· simp only [List.minOn_singleton, not_false_eq_true, minOn_eq_right, length_cons, *]
obtain hlt, rfl := exists_getElem_eq_of_drop_eq_cons h
exact k + 1, by omega, Nat.le_refl _, by simp, rfl
@[simp]
protected theorem minIdxOn_nil_eq_iff_true [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).minIdxOn f h = x True :=
nomatch h
protected theorem minIdxOn_nil_eq_iff_false [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).minIdxOn f h = x False :=
nomatch h
@[simp]
protected theorem minIdxOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minIdxOn f (of_decide_eq_false rfl) = 0 := by
rw [minIdxOn, minIdxOn.go]
@[simp]
protected theorem minIdxOn_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minIdxOn f h < xs.length := by
rw [minIdxOn.eq_def]
split
simp [minIdxOn.go_lt_length_add]
protected theorem minIdxOn_le_of_apply_getElem_le_apply_minOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hi : k < xs.length) (hle : f xs[k] f (xs.minOn f h)) :
xs.minIdxOn f h k := by
obtain i, _, hi, _, h' := minIdxOn_eq_go_drop (f := f) h (k := k)
rw [h']
refine Nat.le_trans ?_ hi
apply Nat.le_of_eq
apply minIdxOn.go_eq_of_forall_le
intro y hy
refine le_trans (List.apply_minOn_le_of_mem (y := xs[k]) (by rw [mem_take_iff_getElem]; exact k, by omega, rfl)) ?_
refine le_trans hle ?_
apply List.apply_minOn_le_of_mem
apply mem_of_mem_drop
exact hy
protected theorem apply_minOn_lt_apply_getElem_of_lt_minIdxOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hk : k < xs.minIdxOn f h) :
f (xs.minOn f h) < f (xs[k]'(by haveI := List.minIdxOn_lt_length (f := f) h; omega)) := by
simp only [ not_le] at hk
apply hk.imp
apply List.minIdxOn_le_of_apply_getElem_le_apply_minOn
@[simp]
protected theorem getElem_minIdxOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) :
xs[xs.minIdxOn f h] = xs.minOn f h := by
obtain i, hlt, hi, heq, h' := minIdxOn_eq_go_drop (f := f) h (k := xs.length)
simp only [drop_eq_nil_of_le (as := xs) (i := xs.length + 1) (by omega), minIdxOn.go] at h'
simp [h', heq, take_of_length_le (l := xs) (i := xs.length + 1) (by omega)]
protected theorem le_minIdxOn_of_apply_getElem_lt_apply_getElem [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat} (hi : i < xs.length)
(hi' : j, (_ : j < i) f xs[i] < f xs[j]) :
i xs.minIdxOn f h := by
false_or_by_contra; rename_i hgt
simp only [not_le] at hgt
specialize hi' _ hgt
simp only [List.getElem_minIdxOn] at hi'
apply (not_le.mpr hi').elim
apply List.apply_minOn_le_of_mem
simp
protected theorem minIdxOn_le_of_apply_getElem_le_apply_getElem [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} (hi : i < xs.length)
(hi' : j, (_ : j < xs.length) f xs[i] f xs[j]) :
xs.minIdxOn f h i := by
apply List.minIdxOn_le_of_apply_getElem_le_apply_minOn h hi
simp only [List.le_apply_minOn_iff, List.mem_iff_getElem]
rintro _ j, hj, rfl
exact hi' _ hj
protected theorem minIdxOn_eq_iff [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.minIdxOn f h = i (h : i < xs.length),
( j, (_ : j < xs.length) f xs[i] f xs[j])
( j, (_ : j < i) f xs[i] < f xs[j]) := by
apply Iff.intro
· rintro rfl
simp only [List.getElem_minIdxOn]
refine List.minIdxOn_lt_length h, ?_, ?_
· simp [List.apply_minOn_le_of_mem]
· exact fun j hj => List.apply_minOn_lt_apply_getElem_of_lt_minIdxOn h hj
· rintro hi, h₁, h₂
apply le_antisymm
· apply List.minIdxOn_le_of_apply_getElem_le_apply_getElem h hi h₁
· apply List.le_minIdxOn_of_apply_getElem_lt_apply_getElem h hi h₂
protected theorem minIdxOn_eq_iff_eq_minOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.minIdxOn f h = i hi : i < xs.length, xs[i] = xs.minOn f h
(j : Nat) (hj : j < i), f (xs.minOn f h) < f xs[j] := by
apply Iff.intro
· rintro rfl
refine List.minIdxOn_lt_length h, List.getElem_minIdxOn h, ?_
intro j hj
exact List.apply_minOn_lt_apply_getElem_of_lt_minIdxOn h hj
· rintro hlt, heq, h'
specialize h' (xs.minIdxOn f h)
simp only [List.getElem_minIdxOn] at h'
apply le_antisymm
· apply List.minIdxOn_le_of_apply_getElem_le_apply_minOn h hlt
simp [heq, le_refl]
· simpa [lt_irrefl] using h'
private theorem minIdxOn.go_eq
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
List.minIdxOn.go f x i j xs =
if h : xs = [] then i
else if f x f (xs.minOn f h) then i
else (xs.minIdxOn f h) + j := by
open scoped Classical.Order in
induction xs generalizing x i j
· simp [go]
· rename_i y ys ih
simp only [go, reduceCtorEq, reduceDIte]
split
· rw [ih]
split
· simp [*]
· simp only [List.minOn_cons, reduceDIte, le_apply_minOn_iff, true_and, *]
split
· rfl
· rename_i hlt
simp only [minIdxOn]
split
simp only [ih, reduceCtorEq, reduceDIte]
rw [if_neg]
· simp [minIdxOn, Nat.add_assoc, Nat.add_comm 1]
· simp only [not_le] at hlt
exact lt_of_lt_of_le hlt _
· rename_i hlt
rw [if_neg]
· rw [minIdxOn, ih]
split
· simp [*, go]
· simp only [reduceDIte, *]
split
· simp
· simp only [Nat.add_assoc, Nat.add_comm 1]
· simp only [not_le] at hlt
exact lt_of_le_of_lt (List.apply_minOn_le_of_mem mem_cons_self) hlt
protected theorem minIdxOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).minIdxOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then 0
else if f x f (xs.minOn f h) then 0
else (xs.minIdxOn f h) + 1 := by
simpa [List.minIdxOn] using minIdxOn.go_eq
protected theorem minIdxOn_eq_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) :
xs.minIdxOn f h = 0 x xs, f (xs.head h) f x := by
rw [minIdxOn.eq_def]
split
rename_i y ys _
simp only [mem_cons, head_cons, forall_eq_or_imp, le_refl, true_and]
apply Iff.intro
· intro h
cases ys
· simp
· intro a ha
refine le_trans ?_ (List.apply_minOn_le_of_mem ha)
simpa [minIdxOn.go_eq] using h
· intro h
cases ys
· simp [minIdxOn.go]
· simpa [minIdxOn.go_eq, List.le_apply_minOn_iff] using h
section Append
/-!
The proof of {name}`List.minOn_append` uses associativity of {name}`minOn` and applies {name}`foldl_assoc`.
The proof of {name (scope := "Init.Data.List.MinMaxIdx")}`minIdxOn_append` is analogous, but the
aggregation operation, {name (scope := "Init.Data.List.MinMaxIdx")}`combineMinIdxOn`, depends on
the length of the lists to combine. After proving associativity of the aggregation operation,
the proof closely follows the proof of {name}`foldl_assoc`.
-/
private def combineMinIdxOn [LE β] [DecidableLE β]
(f : α β) {xs ys : List α} (i j : Nat) (hi : i < xs.length) (hj : j < ys.length) : Nat :=
if f xs[i] f ys[j] then
i
else
xs.length + j
private theorem combineMinIdxOn_lt [LE β] [DecidableLE β]
(f : α β) {xs ys : List α} {i j : Nat} (hi : i < xs.length) (hj : j < ys.length) :
combineMinIdxOn f i j hi hj < (xs ++ ys).length := by
simp only [combineMinIdxOn]
split <;> (simp; omega)
private theorem combineMinIdxOn_assoc [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys zs : List α} {i j k : Nat} {f : α β} (hi : i < xs.length) (hj : j < ys.length)
(hk : k < zs.length) :
combineMinIdxOn f (combineMinIdxOn f i j _ _) k
(combineMinIdxOn_lt f hi hj) hk = combineMinIdxOn f i (combineMinIdxOn f j k _ _) hi (combineMinIdxOn_lt f hj hk) := by
open scoped Classical.Order in
simp only [combineMinIdxOn]
split
· rw [getElem_append_left (by omega)]
split
· split
· rw [getElem_append_left (by omega)]
simp [*]
· rw [getElem_append_right (by omega)]
simp [*]
· split
· have := le_trans f xs[i] f ys[j] f ys[j] f zs[k]
contradiction
· rw [getElem_append_right (by omega)]
simp [*, Nat.add_assoc]
· rw [getElem_append_right (by omega)]
simp only [Nat.add_sub_cancel_left]
split
· rw [getElem_append_left (by omega), if_neg _]
· rename_i h₁ h₂
rw [not_le] at h₁ h₂
rw [getElem_append_right (by omega)]
simp only [Nat.add_sub_cancel_left]
have := not_le.mpr <| lt_trans h₂ h₁
simp [*, Nat.add_assoc]
private theorem minIdxOn_cons_aux [LE β] [DecidableLE β]
[IsLinearPreorder β] {x : α} {xs : List α} {f : α β} (hxs : xs []) :
(x :: xs).minIdxOn f (by simp) =
combineMinIdxOn f _ _
(List.minIdxOn_lt_length (f := f) (cons_ne_nil x []))
(List.minIdxOn_lt_length (f := f) hxs) := by
rw [minIdxOn, combineMinIdxOn]
simp [minIdxOn.go_eq, hxs, List.getElem_minIdxOn, Nat.add_comm 1]
private theorem minIdxOn_append_aux [LE β] [DecidableLE β]
[IsLinearPreorder β] {xs ys : List α} {f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).minIdxOn f (by simp [hxs]) =
combineMinIdxOn f _ _
(List.minIdxOn_lt_length (f := f) hxs)
(List.minIdxOn_lt_length (f := f) hys) := by
induction xs
· contradiction
· rename_i x xs ih
match xs with
| [] => simp [minIdxOn_cons_aux (xs := ys) _]
| z :: zs =>
simp +singlePass only [cons_append]
simp only [minIdxOn_cons_aux (xs := z :: zs ++ ys) (by simp), ih (by simp),
minIdxOn_cons_aux (xs := z :: zs) (by simp), combineMinIdxOn_assoc]
protected theorem minIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).minIdxOn f (by simp [hxs]) =
if f (xs.minOn f hxs) f (ys.minOn f hys) then
xs.minIdxOn f hxs
else
xs.length + ys.minIdxOn f hys := by
simp [minIdxOn_append_aux hxs hys, combineMinIdxOn, List.getElem_minIdxOn]
end Append
protected theorem left_le_minIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
xs.minIdxOn f h (xs ++ ys).minIdxOn f (by simp [h]) := by
by_cases hys : ys = []
· simp [hys]
· rw [List.minIdxOn_append h hys]
split
· apply Nat.le_refl
· have := List.minIdxOn_lt_length (f := f) h
omega
protected theorem minIdxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
(xs.take i).minIdxOn f h xs.minIdxOn f (List.ne_nil_of_take_ne_nil h) := by
have := take_append_drop i xs
conv => rhs; simp +singlePass only [ this]
apply List.left_le_minIdxOn_append
@[simp]
protected theorem minIdxOn_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).minIdxOn f h = 0 := by
match n with
| 0 => simp at h
| n + 1 =>
simp only [minIdxOn, replicate_succ]
generalize 1 = j
induction n generalizing j
· simp [minIdxOn.go]
· simp only [replicate_succ, minIdxOn.go] at *
split
· simp [*]
· have := le_refl (f a)
contradiction
@[simp]
protected theorem maxIdxOn_nil_eq_iff_true [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).maxIdxOn f h = x True :=
nomatch h
protected theorem maxIdxOn_nil_eq_iff_false [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).maxIdxOn f h = x False :=
nomatch h
@[simp]
protected theorem maxIdxOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxIdxOn f (of_decide_eq_false rfl) = 0 :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_singleton
@[simp]
protected theorem maxIdxOn_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxIdxOn f h < xs.length :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_lt_length h
protected theorem maxIdxOn_le_of_apply_getElem_le_apply_maxOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hi : k < xs.length) (hle : f (xs.maxOn f h) f xs[k]) :
xs.maxIdxOn f h k := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn] at hle
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minIdxOn_le_of_apply_getElem_le_apply_minOn h hi (by simpa [LE.le_opposite_iff] using hle)
protected theorem apply_maxOn_lt_apply_getElem_of_lt_maxIdxOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hk : k < xs.maxIdxOn f h) :
f (xs[k]'(by haveI := List.maxIdxOn_lt_length (f := f) h; omega)) < f (xs.maxOn f h) := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn] at hk
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LT.lt_opposite_iff] using List.apply_minOn_lt_apply_getElem_of_lt_minIdxOn (f := f) h hk
@[simp]
protected theorem getElem_maxIdxOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) :
xs[xs.maxIdxOn f h] = xs.maxOn f h := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.getElem_minIdxOn h
protected theorem le_maxIdxOn_of_apply_getElem_lt_apply_getElem [LE β] [DecidableLE β] [LT β]
[IsLinearPreorder β] [LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat}
(hi : i < xs.length) (hi' : j, (_ : j < i) f xs[j] < f xs[i]) :
i xs.maxIdxOn f h := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LE.le_opposite_iff] using List.le_minIdxOn_of_apply_getElem_lt_apply_getElem h hi
(by simpa [LT.lt_opposite_iff] using hi')
protected theorem maxIdxOn_le_of_apply_getElem_le_apply_getElem [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} (hi : i < xs.length)
(hi' : j, (_ : j < xs.length) f xs[j] f xs[i]) :
xs.maxIdxOn f h i := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := LE.opposite inferInstance
simpa [LE.le_opposite_iff] using List.minIdxOn_le_of_apply_getElem_le_apply_getElem (f := f) h hi
(by simpa [LE.le_opposite_iff] using hi')
protected theorem maxIdxOn_eq_iff [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.maxIdxOn f h = i (h : i < xs.length),
( j, (_ : j < xs.length) f xs[j] f xs[i])
( j, (_ : j < i) f xs[j] < f xs[i]) := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LE.le_opposite_iff, LT.lt_opposite_iff] using List.minIdxOn_eq_iff (f := f) h
protected theorem maxIdxOn_eq_iff_eq_maxOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.maxIdxOn f h = i hi : i < xs.length, xs[i] = xs.maxOn f h
(j : Nat) (hj : j < i), f xs[j] < f (xs.maxOn f h) := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LT.lt_opposite_iff] using List.minIdxOn_eq_iff_eq_minOn (f := f) h
protected theorem maxIdxOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).maxIdxOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then 0
else if f (xs.maxOn f h) f x then 0
else (xs.maxIdxOn f h) + 1 := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minIdxOn_cons (f := f)
protected theorem maxIdxOn_eq_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) :
xs.maxIdxOn f h = 0 x xs, f x f (xs.head h) := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minIdxOn_eq_zero_iff h (f := f)
protected theorem maxIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).maxIdxOn f (by simp [hxs]) =
if f (ys.maxOn f hys) f (xs.maxOn f hxs) then
xs.maxIdxOn f hxs
else
xs.length + ys.maxIdxOn f hys := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minIdxOn_append hxs hys (f := f)
protected theorem left_le_maxIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
xs.maxIdxOn f h (xs ++ ys).maxIdxOn f (by simp [h]) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.left_le_minIdxOn_append h
protected theorem maxIdxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
(xs.take i).maxIdxOn f h xs.maxIdxOn f (List.ne_nil_of_take_ne_nil h) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_take_le h
@[simp]
protected theorem maxIdxOn_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).maxIdxOn f h = 0 :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_replicate h
@[simp]
protected theorem minIdxOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).minIdxOn? f = none :=
(rfl)
@[simp]
protected theorem minIdxOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minIdxOn? f = some 0 :=
(rfl)
@[simp]
protected theorem isSome_minIdxOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.minIdxOn? f).isSome xs [] := by
cases xs <;> simp [minIdxOn?]
protected theorem minIdxOn_eq_get_minIdxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minIdxOn f h = (xs.minIdxOn? f).get (List.isSome_minIdxOn?_iff.mpr h) := by
match xs with
| [] => contradiction
| _ :: _ => simp [minIdxOn?]
@[simp]
protected theorem get_minIdxOn?_eq_minIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.minIdxOn? f).isSome) :
(xs.minIdxOn? f).get h = xs.minIdxOn f (List.isSome_minIdxOn?_iff.mp h) := by
rw [List.minIdxOn_eq_get_minIdxOn?]
protected theorem minIdxOn?_eq_some_minIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minIdxOn? f = some (xs.minIdxOn f h) := by
match xs with
| [] => contradiction
| _ :: _ => simp [minIdxOn?]
protected theorem minIdxOn_eq_of_minIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {i : Nat} (h : xs.minIdxOn? f = some i) :
xs.minIdxOn f (List.isSome_minIdxOn?_iff.mp (Option.isSome_of_eq_some h)) = i := by
have h' := List.isSome_minIdxOn?_iff.mp (Option.isSome_of_eq_some h)
rwa [List.minIdxOn?_eq_some_minIdxOn h', Option.some.injEq] at h
protected theorem isSome_minIdxOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.minIdxOn? f).isSome := by
apply List.isSome_minIdxOn?_iff.mpr
exact ne_nil_of_mem h
protected theorem minIdxOn?_cons_eq_some_minIdxOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minIdxOn? f = some ((x :: xs).minIdxOn f (nomatch ·)) := by
simp [List.minIdxOn?_eq_some_minIdxOn]
protected theorem minIdxOn?_eq_if
[LE β] [DecidableLE β] {f : α β} {xs : List α} :
xs.minIdxOn? f =
if h : xs [] then
some (xs.minIdxOn f h)
else
none := by
cases xs <;> simp [List.minIdxOn?_cons_eq_some_minIdxOn]
protected theorem minIdxOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minIdxOn? f = some
(if h : xs = [] then 0
else if f x f (xs.minOn f h) then 0
else (xs.minIdxOn f h) + 1) := by
simp [List.minIdxOn?_eq_some_minIdxOn, List.minIdxOn_cons]
protected theorem ne_nil_of_minIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {k : Nat} {xs : List α} (h : xs.minIdxOn? f = some k) :
xs [] := by
rintro rfl
simp at h
protected theorem lt_length_of_minIdxOn?_eq_some [LE β] [DecidableLE β] {f : α β}
{xs : List α} (h : xs.minIdxOn? f = some i) : i < xs.length := by
have hne : xs [] := List.ne_nil_of_minIdxOn?_eq_some h
rw [List.minIdxOn?_eq_some_minIdxOn hne] at h
have := List.minIdxOn_lt_length (f := f) hne
simp_all
@[simp]
protected theorem get_minIdxOn?_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.minIdxOn? f).isSome) : (xs.minIdxOn? f).get h < xs.length := by
rw [List.get_minIdxOn?_eq_minIdxOn]
apply List.minIdxOn_lt_length
@[simp]
protected theorem getElem_get_minIdxOn? [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : (xs.minIdxOn? f).isSome) :
xs[(xs.minIdxOn? f).get h] = xs.minOn f (List.isSome_minIdxOn?_iff.mp h) := by
rw [getElem_congr rfl (List.get_minIdxOn?_eq_minIdxOn _), List.getElem_minIdxOn]
protected theorem minIdxOn?_eq_some_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} :
xs.minIdxOn? f = some 0 h : xs [], x xs, f (xs.head h) f x := by
simp [Option.eq_some_iff_get_eq, List.minIdxOn_eq_zero_iff]
protected theorem minIdxOn?_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} :
(replicate n a).minIdxOn? f = if n = 0 then none else some 0 := by
simp [List.minIdxOn?_eq_if]
@[simp]
protected theorem minIdxOn?_replicate_of_pos [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).minIdxOn? f = some 0 := by
simp [List.minIdxOn?_replicate, Nat.ne_zero_of_lt h]
/-! ### maxIdxOn? -/
protected theorem maxIdxOn?_eq_minIdxOn? {le : LE β} {_ : DecidableLE β} {f : α β}
{xs : List α} :
xs.maxIdxOn? f = (letI := le.opposite; xs.minIdxOn? f) :=
(rfl)
@[simp]
protected theorem maxIdxOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).maxIdxOn? f = none :=
letI : LE β := LE.opposite inferInstance
List.minIdxOn?_nil
@[simp]
protected theorem maxIdxOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxIdxOn? f = some 0 :=
letI : LE β := LE.opposite inferInstance
List.minIdxOn?_singleton
@[simp]
protected theorem isSome_maxIdxOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.maxIdxOn? f).isSome xs [] := by
letI : LE β := LE.opposite inferInstance
exact List.isSome_minIdxOn?_iff
protected theorem maxIdxOn_eq_get_maxIdxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxIdxOn f h = (xs.maxIdxOn? f).get (List.isSome_maxIdxOn?_iff.mpr h) := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn_eq_get_minIdxOn? h
@[simp]
protected theorem get_maxIdxOn?_eq_maxIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.maxIdxOn? f).isSome) :
(xs.maxIdxOn? f).get h = xs.maxIdxOn f (List.isSome_maxIdxOn?_iff.mp h) := by
letI : LE β := LE.opposite inferInstance
exact List.get_minIdxOn?_eq_minIdxOn h
protected theorem maxIdxOn?_eq_some_maxIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxIdxOn? f = some (xs.maxIdxOn f h) := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_eq_some_minIdxOn h
protected theorem maxIdxOn_eq_of_maxIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {i : Nat} (h : xs.maxIdxOn? f = some i) :
xs.maxIdxOn f (List.isSome_maxIdxOn?_iff.mp (Option.isSome_of_eq_some h)) = i := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn_eq_of_minIdxOn?_eq_some h
protected theorem isSome_maxIdxOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.maxIdxOn? f).isSome := by
letI : LE β := LE.opposite inferInstance
exact List.isSome_minIdxOn?_of_mem h
protected theorem maxIdxOn?_cons_eq_some_maxIdxOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxIdxOn? f = some ((x :: xs).maxIdxOn f (nomatch ·)) := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_cons_eq_some_minIdxOn
protected theorem maxIdxOn?_eq_if
[LE β] [DecidableLE β] {f : α β} {xs : List α} :
xs.maxIdxOn? f =
if h : xs [] then
some (xs.maxIdxOn f h)
else
none := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_eq_if
protected theorem maxIdxOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxIdxOn? f = some
(if h : xs = [] then 0
else if f (xs.maxOn f h) f x then 0
else (xs.maxIdxOn f h) + 1) := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := LE.opposite inferInstance
simpa [LE.le_opposite_iff] using List.minIdxOn?_cons (f := f)
protected theorem ne_nil_of_maxIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {k : Nat} {xs : List α} (h : xs.maxIdxOn? f = some k) :
xs [] := by
letI : LE β := LE.opposite inferInstance
exact List.ne_nil_of_minIdxOn?_eq_some (by simpa only [List.maxIdxOn?_eq_minIdxOn?] using h)
protected theorem lt_length_of_maxIdxOn?_eq_some [LE β] [DecidableLE β] {f : α β}
{xs : List α} (h : xs.maxIdxOn? f = some i) : i < xs.length := by
letI : LE β := LE.opposite inferInstance
exact List.lt_length_of_minIdxOn?_eq_some h
@[simp]
protected theorem get_maxIdxOn?_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.maxIdxOn? f).isSome) : (xs.maxIdxOn? f).get h < xs.length := by
letI : LE β := LE.opposite inferInstance
exact List.get_minIdxOn?_lt_length h
@[simp]
protected theorem getElem_get_maxIdxOn? [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : (xs.maxIdxOn? f).isSome) :
xs[(xs.maxIdxOn? f).get h] = xs.maxOn f (List.isSome_maxIdxOn?_iff.mp h) := by
simp only [List.maxIdxOn?_eq_minIdxOn?, List.maxOn_eq_minOn]
letI : LE β := LE.opposite inferInstance
exact List.getElem_get_minIdxOn? h
protected theorem maxIdxOn?_eq_some_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} :
xs.maxIdxOn? f = some 0 h : xs [], x xs, f x f (xs.head h) := by
simp only [List.maxIdxOn?_eq_minIdxOn?]
letI : LE β := LE.opposite inferInstance
simpa [LE.le_opposite_iff] using List.minIdxOn?_eq_some_zero_iff (f := f)
protected theorem maxIdxOn?_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} :
(replicate n a).maxIdxOn? f = if n = 0 then none else some 0 := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_replicate
@[simp]
protected theorem maxIdxOn?_replicate_of_pos [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).maxIdxOn? f = some 0 := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_replicate_of_pos h
end List

View File

@@ -0,0 +1,623 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Order.MinMaxOn
public import Init.Data.Int.OfNat
public import Init.Data.List.Lemmas
public import Init.Data.List.TakeDrop
import Init.Data.Order.Lemmas
import Init.Data.List.Sublist
import Init.Data.List.MinMax
import Init.Data.Order.Opposite
set_option doc.verso true
set_option linter.missingDocs true
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
public section
open Std
open scoped OppositeOrderInstances
namespace List
/--
Returns an element of the non-empty list {name}`l` that minimizes {name}`f`. If {given}`x, y` are
such that {lean}`f x = f y`, it returns whichever comes first in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.minOn` is the first minimizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_minIdxOn`.
-/
@[inline, suggest_for List.argmin]
protected def minOn [LE β] [DecidableLE β] (f : α β) (l : List α) (h : l []) : α :=
match l with
| x :: xs => xs.foldl (init := x) (minOn f)
/--
Returns an element of the non-empty list {name}`l` that maximizes {name}`f`. If {given}`x, y` are
such that {lean}`f x = f y`, it returns whichever comes first in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.maxOn` is the first maximizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_maxIdxOn`.
-/
@[inline, suggest_for List.argmax]
protected def maxOn [i : LE β] [DecidableLE β] (f : α β) (l : List α) (h : l []) : α :=
letI : LE β := i.opposite
l.minOn f h
/--
Returns an element of {name}`l` that minimizes {name}`f`. If {given}`x, y` are such that
{lean}`f x = f y`, it returns whichever comes first in the list. Returns {name}`none` if the list is
empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.minOn?` is the first minimizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_get_minIdxOn?`
-/
@[inline, suggest_for List.argmin? List.argmin] -- Mathlib's `List.argmin` returns an `Option α`
protected def minOn? [LE β] [DecidableLE β] (f : α β) (l : List α) : Option α :=
match l with
| [] => none
| x :: xs => some (xs.foldl (init := x) (minOn f))
/--
Returns an element of {name}`l` that maximizes {name}`f`. If {given}`x, y` are such that
{lean}`f x = f y`, it returns whichever comes first in the list. Returns {name}`none` if the list is
empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.maxOn?` is the first minimizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_get_maxIdxOn?`.
-/
@[inline, suggest_for List.argmax? List.argmax] -- Mathlib's `List.argmax` returns an `Option α`
protected def maxOn? [i : LE β] [DecidableLE β] (f : α β) (l : List α) : Option α :=
letI : LE β := i.opposite
l.minOn? f
/-! ### minOn -/
@[simp]
protected theorem minOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minOn f (of_decide_eq_false rfl) = x := by
simp [List.minOn]
protected theorem minOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).minOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then x else minOn f x (xs.minOn f h) := by
simp only [List.minOn]
match xs with
| [] => simp
| y :: xs => simp [foldl_assoc]
@[simp]
protected theorem minOn_id [Min α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMin α]
{xs : List α} (h : xs []) :
xs.minOn id h = xs.min h := by
have : minOn (α := α) id = min := by ext; apply minOn_id
simp only [List.minOn, List.min, this]
match xs with
| _ :: _ => simp
@[simp]
protected theorem minOn_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} {h : xs []} : xs.minOn f h xs := by
simp only [List.minOn]
match xs with
| x :: xs =>
fun_induction xs.foldl (init := x) (_root_.minOn f)
· simp
· rename_i x y _ ih
simp only [ne_eq, reduceCtorEq, not_false_eq_true, mem_cons, forall_const, foldl_cons] at ih
cases ih <;> rename_i heq
· cases minOn_eq_or (f := f) (x := x) (y := y) <;> simp_all
· simp [*]
protected theorem apply_minOn_le_of_mem [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {y : α} (hx : y xs) :
f (xs.minOn f (List.ne_nil_of_mem hx)) f y := by
have h : xs [] := List.ne_nil_of_mem hx
simp only [List.minOn]
match xs with
| x :: xs =>
fun_induction xs.foldl (init := x) (_root_.minOn f) generalizing y
· simp only [mem_cons] at hx
simp_all
· rename_i x y _ ih
simp at ih
rcases mem_cons.mp hx with rfl | hx
· exact le_trans ih.1 apply_minOn_le_left
· rcases mem_cons.mp hx with rfl | hx
· exact le_trans ih.1 apply_minOn_le_right
· apply ih.2
assumption
protected theorem le_apply_minOn_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
b f (xs.minOn f h) x xs, b f x := by
match xs with
| x :: xs =>
rw [List.minOn]
induction xs generalizing x
· simp
· rw [foldl_cons, foldl_assoc, le_apply_minOn_iff]
simp_all
protected theorem apply_minOn_le_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
f (xs.minOn f h) b x xs, f x b := by
apply Iff.intro
· intro h
match xs with
| x :: xs =>
rw [List.minOn] at h
induction xs generalizing x
· simpa using h
· rename_i y ys ih _
rw [foldl_cons] at h
specialize ih (minOn f x y) (by simp) h
obtain z, hm, hle := ih
rcases mem_cons.mp hm with rfl | hm
· cases minOn_eq_or (f := f) (x := x) (y := y)
· exact x, by simp_all
· exact y, by simp_all
· exact z, by simp_all
· rintro x, hm, hx
exact le_trans (List.apply_minOn_le_of_mem hm) hx
protected theorem lt_apply_minOn_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
b < f (xs.minOn f h) x xs, b < f x := by
simpa [not_le] using not_congr <| xs.apply_minOn_le_iff (f := f) h (b := b)
protected theorem apply_minOn_lt_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
f (xs.minOn f h) < b x xs, f x < b := by
simpa [not_le] using not_congr <| xs.le_apply_minOn_iff (f := f) h (b := b)
protected theorem apply_minOn_le_apply_minOn_of_subset [LE β] [DecidableLE β]
[IsLinearPreorder β] {xs ys : List α} {f : α β} (hxs : ys xs) (hys : ys []) :
haveI : xs [] := by intro h; rw [h] at hxs; simp_all [subset_nil]
f (xs.minOn f this) f (ys.minOn f hys) := by
rw [List.le_apply_minOn_iff]
intro x hx
exact List.apply_minOn_le_of_mem (hxs hx)
protected theorem le_apply_minOn_take [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
f (xs.minOn f (List.ne_nil_of_take_ne_nil h)) f ((xs.take i).minOn f h) := by
apply List.apply_minOn_le_apply_minOn_of_subset
apply take_subset
protected theorem apply_minOn_append_le_left [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
f ((xs ++ ys).minOn f (append_ne_nil_of_left_ne_nil h ys))
f (xs.minOn f h) := by
apply List.apply_minOn_le_apply_minOn_of_subset
apply subset_append_left
protected theorem apply_minOn_append_le_right [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : ys []) :
f ((xs ++ ys).minOn f (append_ne_nil_of_right_ne_nil xs h))
f (ys.minOn f h) := by
apply List.apply_minOn_le_apply_minOn_of_subset
apply subset_append_right
@[simp]
protected theorem minOn_append [LE β] [DecidableLE β] [IsLinearPreorder β] {xs ys : List α}
{f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).minOn f (by simp [hxs]) = minOn f (xs.minOn f hxs) (ys.minOn f hys) := by
match xs, ys with
| x :: xs, y :: ys => simp [List.minOn, foldl_assoc]
protected theorem minOn_eq_head [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) (h' : x xs, f (xs.head h) f x) :
xs.minOn f h = xs.head h := by
match xs with
| x :: xs =>
simp only [List.minOn]
induction xs
· simp
· simp only [foldl_cons, head_cons]
rw [minOn_eq_left] <;> simp_all
protected theorem min_map
[LE β] [DecidableLE β] [Min β] [IsLinearPreorder β] [LawfulOrderLeftLeaningMin β] {xs : List α}
{f : α β} (h : xs []) :
(xs.map f).min (by simpa) = f (xs.minOn f h) := by
match xs with
| x :: xs =>
simp only [List.minOn, map_cons, List.min, foldl_map]
rw [foldl_hom]
simp [min_apply]
@[simp]
protected theorem minOn_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).minOn f h = a := by
induction n
· simp at h
· rename_i n ih
simp only [ne_eq, replicate_eq_nil_iff] at ih
simp +contextual [List.replicate, List.minOn_cons, ih]
/-! ### maxOn -/
protected theorem maxOn_eq_minOn {le : LE β} {dle : DecidableLE β} {xs : List α} {f : α β} {h} :
xs.maxOn f h = (letI := le.opposite; xs.minOn f h) :=
(rfl)
@[simp]
protected theorem maxOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxOn f (of_decide_eq_false rfl) = x := by
simp [List.maxOn]
protected theorem maxOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).maxOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then x else maxOn f x (xs.maxOn f h) := by
simp only [maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minOn_cons (f := f)
protected theorem min_eq_max {min : Min α} {xs : List α} {h} :
xs.min h = (letI := min.oppositeMax; xs.max h) := by
simp only [List.min, List.max]
rw [Min.oppositeMax_def]
simp
protected theorem max_eq_min {max : Max α} {xs : List α} {h} :
xs.max h = (letI := max.oppositeMin; xs.min h) := by
simp only [List.min, List.max]
rw [Max.oppositeMin_def]
simp
protected theorem max?_eq_min? {max : Max α} {xs : List α} :
xs.max? = (letI := max.oppositeMin; xs.min?) := by
simp only [List.min?, List.max?]
rw [Max.oppositeMin_def]
simp
@[simp]
protected theorem maxOn_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α]
{xs : List α} (h : xs []) :
xs.maxOn id h = xs.max h := by
simp only [List.maxOn_eq_minOn]
letI : LE α := (inferInstanceAs (LE α)).opposite
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
simpa only [List.max_eq_min] using List.minOn_id h
@[simp]
protected theorem maxOn_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} {h : xs []} : xs.maxOn f h xs :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_mem (f := f)
protected theorem le_apply_maxOn_of_mem [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {y : α} (hx : y xs) :
f y f (xs.maxOn f (List.ne_nil_of_mem hx)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_le_of_mem (f := f) hx
protected theorem apply_maxOn_le_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
{f : α β} (h : xs []) {b : β} :
f (xs.maxOn f h) b x xs, f x b := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.le_apply_minOn_iff (f := f) h
protected theorem le_apply_maxOn_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
{f : α β} (h : xs []) {b : β} :
b f (xs.maxOn f h) x xs, b f x := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_le_iff (f := f) h
protected theorem apply_maxOn_lt_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
f (xs.maxOn f h) < b x xs, f x < b := by
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : LT β := (inferInstanceAs (LT β)).opposite
simpa [LT.lt_opposite_iff] using List.lt_apply_minOn_iff (f := f) h
protected theorem lt_apply_maxOn_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
b < f (xs.maxOn f h) x xs, b < f x := by
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : LT β := (inferInstanceAs (LT β)).opposite
simpa [LT.lt_opposite_iff] using List.apply_minOn_lt_iff (f := f) h
protected theorem apply_maxOn_le_apply_maxOn_of_subset [LE β] [DecidableLE β]
[IsLinearPreorder β] {xs ys : List α} {f : α β} (hxs : ys xs) (hys : ys []) :
haveI : xs [] := by intro h; rw [h] at hxs; simp_all [subset_nil]
f (ys.maxOn f hys) f (xs.maxOn f this) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_le_apply_minOn_of_subset (f := f) hxs hys
protected theorem apply_maxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
f ((xs.take i).maxOn f h) f (xs.maxOn f (List.ne_nil_of_take_ne_nil h)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.le_apply_minOn_take (f := f) h
protected theorem le_apply_maxOn_append_left [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
f (xs.maxOn f h)
f ((xs ++ ys).maxOn f (append_ne_nil_of_left_ne_nil h ys)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_append_le_left (f := f) h
protected theorem le_apply_maxOn_append_right [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : ys []) :
f (ys.maxOn f h)
f ((xs ++ ys).maxOn f (append_ne_nil_of_right_ne_nil xs h)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_append_le_right (f := f) h
@[simp]
protected theorem maxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β] {xs ys : List α}
{f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).maxOn f (by simp [hxs]) = maxOn f (xs.maxOn f hxs) (ys.maxOn f hys) := by
simp only [List.maxOn_eq_minOn, maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minOn_append (f := f) hxs hys
protected theorem maxOn_eq_head [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
{f : α β} (h : xs []) (h' : x xs, f x f (xs.head h)) :
xs.maxOn f h = xs.head h := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minOn_eq_head (f := f) h (by simpa [LE.le_opposite_iff] using h')
protected theorem max_map
[LE β] [DecidableLE β] [Max β] [IsLinearPreorder β] [LawfulOrderLeftLeaningMax β] {xs : List α}
{f : α β} (h : xs []) : (xs.map f).max (by simpa) = f (xs.maxOn f h) := by
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : Min β := (inferInstanceAs (Max β)).oppositeMin
simpa [List.max_eq_min] using List.min_map (f := f) h
@[simp]
protected theorem maxOn_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).maxOn f h = a :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_replicate (f := f) h
/-! ### minOn? -/
/-- {lit}`List.minOn?` returns {name}`none` when applied to an empty list. -/
@[simp]
protected theorem minOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).minOn? f = none := by
simp [List.minOn?]
protected theorem minOn?_cons_eq_some_minOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minOn? f = some ((x :: xs).minOn f (fun h => nomatch h)) := by
simp [List.minOn?, List.minOn]
protected theorem minOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minOn? f = some ((xs.minOn? f).elim x (minOn f x)) := by
simp only [List.minOn?]
split <;> simp [foldl_assoc]
@[simp]
protected theorem minOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minOn? f = some x := by
simp [List.minOn?_cons_eq_some_minOn]
@[simp]
protected theorem minOn?_id [Min α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMin α]
{xs : List α} : xs.minOn? id = xs.min? := by
cases xs
· simp
· simp only [List.minOn?_cons_eq_some_minOn, List.minOn_id, List.min?_eq_some_min (List.cons_ne_nil _ _)]
protected theorem minOn?_eq_if
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} :
xs.minOn? f =
if h : xs [] then
some (xs.minOn f h)
else
none := by
fun_cases xs.minOn? f <;> simp [List.minOn]
@[simp]
protected theorem isSome_minOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.minOn? f).isSome xs [] := by
fun_cases xs.minOn? f <;> simp
protected theorem minOn_eq_get_minOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minOn f h = (xs.minOn? f).get (List.isSome_minOn?_iff.mpr h) := by
fun_cases xs.minOn? f
· contradiction
· simp [List.minOn?, List.minOn]
protected theorem minOn?_eq_some_minOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minOn? f = some (xs.minOn f h) := by
simp [List.minOn_eq_get_minOn? h]
@[simp]
protected theorem get_minOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : (xs.minOn? f).get (List.isSome_minOn?_iff.mpr h) = xs.minOn f h := by
rw [List.minOn_eq_get_minOn?]
protected theorem minOn_eq_of_minOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : xs.minOn? f = some x) :
xs.minOn f (List.isSome_minOn?_iff.mp (Option.isSome_of_eq_some h)) = x := by
have h' := List.isSome_minOn?_iff.mp (Option.isSome_of_eq_some h)
rwa [List.minOn?_eq_some_minOn h', Option.some.injEq] at h
protected theorem isSome_minOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.minOn? f).isSome := by
apply List.isSome_minOn?_iff.mpr
exact ne_nil_of_mem h
protected theorem apply_get_minOn?_le_of_mem
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} {x : α} (h : x xs) :
f ((xs.minOn? f).get (List.isSome_minOn?_of_mem h)) f x := by
rw [List.get_minOn? (ne_nil_of_mem h)]
apply List.apply_minOn_le_of_mem h
protected theorem minOn?_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} (h : xs.minOn? f = some a) : a xs := by
rw [ List.minOn_eq_of_minOn?_eq_some h]
apply List.minOn_mem
protected theorem minOn?_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} :
(replicate n a).minOn? f = if n = 0 then none else some a := by
split
· simp [*]
· rw [List.minOn?_eq_some_minOn, List.minOn_replicate]
simp [*]
@[simp]
protected theorem minOn?_replicate_of_pos [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).minOn? f = some a := by
simp [List.minOn?_replicate, show n 0 from Nat.ne_zero_of_lt h]
@[simp]
protected theorem minOn?_append [LE β] [DecidableLE β] [IsLinearPreorder β]
(xs ys : List α) (f : α β) :
(xs ++ ys).minOn? f =
(xs.minOn? f).merge (_root_.minOn f) (ys.minOn? f) := by
by_cases xs = [] <;> by_cases ys = [] <;> simp [*, List.minOn?_eq_if, List.minOn_append]
/-! ### maxOn? -/
protected theorem maxOn?_eq_minOn? {le : LE β} {dle : DecidableLE β} {xs : List α} {f : α β} :
xs.maxOn? f = (letI := le.opposite; xs.minOn? f) :=
(rfl)
@[simp]
protected theorem maxOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).maxOn? f = none :=
List.minOn?_nil (f := f)
protected theorem maxOn?_cons_eq_some_maxOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxOn? f = some ((x :: xs).maxOn f (fun h => nomatch h)) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_cons_eq_some_minOn
protected theorem maxOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxOn? f = some ((xs.maxOn? f).elim x (maxOn f x)) := by
have : maxOn f x = (letI : LE β := LE.opposite inferInstance; minOn f x) := by
ext; simp only [maxOn_eq_minOn]
simp only [List.maxOn?_eq_minOn?, this]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minOn?_cons
@[simp]
protected theorem maxOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxOn? f = some x :=
List.minOn?_singleton (f := f)
@[simp]
protected theorem maxOn?_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α]
{xs : List α} : xs.maxOn? id = xs.max? := by
letI : LE α := (inferInstanceAs (LE α)).opposite
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
simpa only [List.maxOn?_eq_minOn?, List.max?_eq_min?] using List.minOn?_id (α := α)
protected theorem maxOn?_eq_if
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} :
xs.maxOn? f =
if h : xs [] then
some (xs.maxOn f h)
else
none :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_eq_if
@[simp]
protected theorem isSome_maxOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.maxOn? f).isSome xs [] := by
fun_cases xs.maxOn? f <;> simp
protected theorem maxOn_eq_get_maxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxOn f h = (xs.maxOn? f).get (List.isSome_maxOn?_iff.mpr h) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_eq_get_minOn? (f := f) h
protected theorem maxOn?_eq_some_maxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxOn? f = some (xs.maxOn f h) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_eq_some_minOn (f := f) h
@[simp]
protected theorem get_maxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : (xs.maxOn? f).get (List.isSome_maxOn?_iff.mpr h) = xs.maxOn f h :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.get_minOn? (f := f) h
protected theorem maxOn_eq_of_maxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : xs.maxOn? f = some x) :
xs.maxOn f (List.isSome_maxOn?_iff.mp (Option.isSome_of_eq_some h)) = x :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_eq_of_minOn?_eq_some (f := f) h
protected theorem isSome_maxOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.maxOn? f).isSome :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.isSome_minOn?_of_mem (f := f) h
protected theorem le_apply_get_maxOn?_of_mem
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} {x : α} (h : x xs) :
f x f ((xs.maxOn? f).get (List.isSome_maxOn?_of_mem h)) := by
simp only [List.maxOn?_eq_minOn?]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_get_minOn?_le_of_mem (f := f) h
protected theorem maxOn?_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} (h : xs.maxOn? f = some a) : a xs :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_mem (f := f) h
protected theorem maxOn?_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} :
(replicate n a).maxOn? f = if n = 0 then none else some a :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_replicate
@[simp]
protected theorem maxOn?_replicate_of_pos [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).maxOn? f = some a :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_replicate_of_pos (f := f) h
@[simp]
protected theorem maxOn?_append [LE β] [DecidableLE β] [IsLinearPreorder β]
(xs ys : List α) (f : α β) : (xs ++ ys).maxOn? f =
(xs.maxOn? f).merge (_root_.maxOn f) (ys.maxOn? f) := by
have : maxOn f = (letI : LE β := LE.opposite inferInstance; minOn f) := by
ext; simp only [maxOn_eq_minOn]
simp only [List.maxOn?_eq_minOn?, this]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minOn?_append xs ys f
end List

View File

@@ -12,6 +12,7 @@ public import Init.Data.List.Nat.Range
public import Init.Data.List.Nat.Sublist
public import Init.Data.List.Nat.TakeDrop
public import Init.Data.List.Nat.Count
public import Init.Data.List.Nat.Sum
public import Init.Data.List.Nat.Erase
public import Init.Data.List.Nat.Find
public import Init.Data.List.Nat.BEq

View File

@@ -254,4 +254,18 @@ theorem le_max?_getD_of_mem {l : List Nat} {a k : Nat} (h : a ∈ l) :
a l.max?.getD k :=
Option.get_eq_getD _ le_max?_get_of_mem h
/-! #### append -/
theorem append_eq_append_iff_of_size_eq_left {ws xs ys zs : List α}
(h : ws.length = xs.length) : ws ++ ys = xs ++ zs ws = xs ys = zs := by
rw [append_eq_append_iff]
refine ?_, ?_
· rintro (as, rfl, rfl|as, rfl, rfl) <;> simp_all
· rintro rfl, rfl <;> simp_all
theorem append_eq_append_iff_of_size_eq_right {ws xs ys zs : List α}
(h : ys.length = zs.length) : ws ++ ys = xs ++ zs ws = xs ys = zs := by
rw [ reverse_inj, reverse_append, reverse_append,
append_eq_append_iff_of_size_eq_left (by simpa), reverse_inj, reverse_inj, and_comm]
end List

View File

@@ -13,13 +13,6 @@ public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
protected theorem Nat.sum_pos_iff_exists_pos {l : List Nat} : 0 < l.sum x l, 0 < x := by
induction l with
| nil => simp
| cons x xs ih =>
simp [ ih]
omega
namespace List
open Nat

View File

@@ -131,6 +131,14 @@ theorem suffix_iff_eq_drop : l₁ <:+ l₂ ↔ l₁ = drop (length l₂ - length
fun h => append_cancel_left <| (suffix_iff_eq_append.1 h).trans (take_append_drop _ _).symm,
fun e => e.symm drop_suffix _ _
theorem prefix_map_iff_of_injective {f : α β} (hf : Function.Injective f) :
l₁.map f <+: l₂.map f l₁ <+: l₂ := by
simp [prefix_iff_eq_take, map_take, map_inj_right hf]
theorem suffix_map_iff_of_injective {f : α β} (hf : Function.Injective f) :
l₁.map f <:+ l₂.map f l₁ <:+ l₂ := by
simp [suffix_iff_eq_drop, map_drop, map_inj_right hf]
@[grind =] theorem prefix_take_le_iff {xs : List α} (hm : i < xs.length) :
xs.take i <+: xs.take j i j := by
simp only [prefix_iff_eq_take, length_take]

View File

@@ -0,0 +1,128 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Graf, Paul Reichert
-/
module
prelude
public import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.DivMod.Lemmas
import Init.Data.List.MinMax
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
protected theorem sum_pos_iff_exists_pos_nat {l : List Nat} : 0 < l.sum x l, 0 < x := by
induction l with
| nil => simp
| cons x xs ih =>
simp [ ih]
omega
@[deprecated List.sum_pos_iff_exists_pos_nat (since := "2025-01-15")]
protected def _root_.Nat.sum_pos_iff_exists_pos := @List.sum_pos_iff_exists_pos_nat
protected theorem sum_eq_zero_iff_forall_eq_nat {xs : List Nat} :
xs.sum = 0 x xs, x = 0 := by
rw [ Decidable.not_iff_not]
simp [ Nat.pos_iff_ne_zero, List.sum_pos_iff_exists_pos_nat]
@[deprecated List.sum_pos_iff_exists_pos_nat (since := "2025-01-15")]
protected def _root_.Nat.sum_eq_zero_iff_forall_eq := @List.sum_eq_zero_iff_forall_eq_nat
@[simp]
theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
induction n <;> simp_all [replicate_succ, Nat.add_mul, Nat.add_comm]
theorem sum_append_nat {l₁ l₂ : List Nat} : (l₁ ++ l₂).sum = l₁.sum + l₂.sum := by
simp [sum_append]
theorem sum_reverse_nat (xs : List Nat) : xs.reverse.sum = xs.sum := by
simp [sum_reverse]
theorem sum_eq_foldl_nat {xs : List Nat} : xs.sum = xs.foldl (init := 0) (· + ·) := by
simp only [foldl_eq_foldr_reverse, Nat.add_comm, sum_eq_foldr, sum_reverse_nat]
theorem min_mul_length_le_sum_nat {xs : List Nat} (h : xs []) :
xs.min h * xs.length xs.sum := by
induction xs
· contradiction
· rename_i x xs ih
cases xs
· simp_all [List.min_singleton]
· simp only [ne_eq, reduceCtorEq, not_false_eq_true, min_eq_get_min?,
List.min?_cons (α := Nat), Option.get_some, length_cons,
forall_const] at ih
rw [Nat.mul_add, Nat.mul_one, Nat.add_comm]
apply Nat.add_le_add
· apply Nat.min_le_left
· refine Nat.le_trans ?_ ih
rw [Nat.mul_le_mul_right_iff (by omega)]
apply Nat.min_le_right
theorem mul_length_le_sum_of_min?_eq_some_nat {xs : List Nat} (h : xs.min? = some x) :
x * xs.length xs.sum := by
cases xs
· simp_all
· simp only [min?_eq_some_min (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using min_mul_length_le_sum_nat _
theorem min_le_sum_div_length_nat {xs : List Nat} (h : xs []) :
xs.min h xs.sum / xs.length := by
have := min_mul_length_le_sum_nat h
rwa [Nat.le_div_iff_mul_le]
simp [List.length_pos_iff, h]
theorem le_sum_div_length_of_min?_eq_some_nat {xs : List Nat} (h : xs.min? = some x) :
x xs.sum / xs.length := by
cases xs
· simp_all
· simp only [min?_eq_some_min (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using min_le_sum_div_length_nat _
theorem sum_le_max_mul_length_nat {xs : List Nat} (h : xs []) :
xs.sum xs.max h * xs.length := by
induction xs
· contradiction
· rename_i x xs ih
cases xs
· simp_all [List.max_singleton]
· simp only [ne_eq, reduceCtorEq, not_false_eq_true, max_eq_get_max?,
List.max?_cons (α := Nat), Option.get_some, length_cons,
forall_const, Option.elim_some] at ih
rw [Nat.mul_add, Nat.mul_one, Nat.add_comm]
apply Nat.add_le_add
· apply Nat.le_max_left
· refine Nat.le_trans ih ?_
rw [Nat.mul_le_mul_right_iff (by omega)]
apply Nat.le_max_right
theorem sum_le_max_mul_length_of_max?_eq_some_nat {xs : List Nat} (h : xs.max? = some x) :
xs.sum x * xs.length := by
cases xs
· simp_all
· simp only [max?_eq_some_max (cons_ne_nil _ _), Option.some.injEq] at h
simp only [ h]
apply sum_le_max_mul_length_nat
theorem sum_div_length_le_max_nat {xs : List Nat} (h : xs []) :
xs.sum / xs.length xs.max h := by
have := sum_le_max_mul_length_nat h
rw [Nat.div_le_iff_le_mul, Nat.add_sub_assoc]
· exact Nat.le_trans this (Nat.le_add_right _ _)
· simp [Nat.one_le_iff_ne_zero, h]
· simp [ Nat.ne_zero_iff_zero_lt, h]
theorem sum_div_length_le_max_of_max?_eq_some_nat {xs : List Nat} (h : xs.max? = some x) :
xs.sum / xs.length x := by
cases xs
· simp_all
· simp only [max?_eq_some_max (cons_ne_nil _ _), Option.some.injEq] at h
simpa only [ h] using sum_div_length_le_max_nat _
end List

View File

@@ -137,10 +137,15 @@ theorem take_append {l₁ l₂ : List α} {i : Nat} :
congr 1
omega
@[grind =]
theorem take_append_of_le_length {l₁ l₂ : List α} {i : Nat} (h : i l₁.length) :
(l₁ ++ l₂).take i = l₁.take i := by
simp [take_append, Nat.sub_eq_zero_of_le h]
@[grind =]
theorem take_append_length {l₁ l₂ : List α} : (l₁ ++ l₂).take l₁.length = l₁ := by
simp
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
`i` elements of `l₂` to `l₁`. -/
theorem take_length_add_append {l₁ l₂ : List α} (i : Nat) :
@@ -304,7 +309,6 @@ theorem drop_length_cons {l : List α} (h : l ≠ []) (a : α) :
/-- Dropping the elements up to `i` in `l₁ ++ l₂` is the same as dropping the elements up to `i`
in `l₁`, dropping the elements up to `i - l₁.length` in `l₂`, and appending them. -/
@[grind =]
theorem drop_append {l₁ l₂ : List α} {i : Nat} :
drop i (l₁ ++ l₂) = drop i l₁ ++ drop (i - l₁.length) l₂ := by
induction l₁ generalizing i
@@ -315,10 +319,15 @@ theorem drop_append {l₁ l₂ : List α} {i : Nat} :
congr 1
omega
@[grind =]
theorem drop_append_of_le_length {l₁ l₂ : List α} {i : Nat} (h : i l₁.length) :
(l₁ ++ l₂).drop i = l₁.drop i ++ l₂ := by
simp [drop_append, Nat.sub_eq_zero_of_le h]
@[grind =]
theorem drop_append_length {l₁ l₂ : List α} : (l₁ ++ l₂).drop l₁.length = l₂ := by
simp [List.drop_append_of_le_length (Nat.le_refl _)]
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
up to `i` in `l₂`. -/
@[simp]
@@ -364,7 +373,7 @@ theorem drop_take : ∀ {i j : Nat} {l : List α}, drop i (take j l) = take (j -
simp only [take_succ_cons, drop_succ_cons, drop_take, take_eq_take_iff, length_drop]
omega
@[simp] theorem drop_take_self : drop i (take i l) = [] := by
@[simp, grind =] theorem drop_take_self : drop i (take i l) = [] := by
rw [drop_take]
simp

View File

@@ -100,6 +100,11 @@ theorem ofFn_add {n m} {f : Fin (n + m) → α} :
theorem ofFn_eq_nil_iff {f : Fin n α} : ofFn f = [] n = 0 := by
cases n <;> simp only [ofFn_zero, ofFn_succ, Nat.succ_ne_zero, reduceCtorEq]
@[simp]
theorem ofFn_getElem {xs : List α} :
List.ofFn (fun i : Fin xs.length => xs[i.val]) = xs := by
apply ext_getElem <;> simp
@[simp 500, grind =]
theorem mem_ofFn {n} {f : Fin n α} {a : α} : a ofFn f i, f i = a := by
constructor
@@ -109,6 +114,12 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
· rintro i, rfl
apply mem_of_getElem (i := i) <;> simp
@[simp, grind =]
theorem map_ofFn {f : Fin n α} {g : α β} :
(List.ofFn f).map g = List.ofFn (g f) := by
apply List.ext_getElem?
simp [List.getElem?_ofFn]
@[grind =] theorem head_ofFn {n} {f : Fin n α} (h : ofFn f []) :
(ofFn f).head h = f 0, Nat.pos_of_ne_zero (mt ofFn_eq_nil_iff.2 h) := by
rw [ getElem_zero (length_ofFn Nat.pos_of_ne_zero (mt ofFn_eq_nil_iff.2 h)),

View File

@@ -580,7 +580,7 @@ theorem sublist_flatten_iff {L : List (List α)} {l} :
· rintro L', rfl, h
simp only [flatten_nil, sublist_nil, flatten_eq_nil_iff]
simp only [getElem?_nil, Option.getD_none, sublist_nil] at h
exact (forall_getElem (p := (· = []))).1 h
exact (forall_mem_iff_forall_getElem (P := (· = []))).2 h
| cons l' L ih =>
simp only [flatten_cons, sublist_append_iff, ih]
constructor
@@ -1124,9 +1124,11 @@ theorem drop_subset (i) (l : List α) : drop i l ⊆ l :=
grind_pattern drop_subset => drop i l l
@[grind ]
theorem mem_of_mem_take {l : List α} (h : a l.take i) : a l :=
take_subset _ _ h
@[grind ]
theorem mem_of_mem_drop {i} {l : List α} (h : a l.drop i) : a l :=
drop_subset _ _ h

View File

@@ -54,6 +54,15 @@ theorem div_le_iff_le_mul (h : 0 < k) : x / k ≤ y ↔ x ≤ y * k + k - 1 := b
rw [le_iff_lt_add_one, Nat.div_lt_iff_lt_mul h, Nat.add_one_mul]
omega
theorem le_mul_iff_le_left (hz : 0 < z) :
x y * z (x + z - 1) / z y := by
rw [Nat.div_le_iff_le_mul hz]
omega
theorem le_mul_iff_le_right (hy : 0 < y) :
x y * z (x + y - 1) / y z := by
rw [ le_mul_iff_le_left hy, Nat.mul_comm]
-- TODO: reprove `div_eq_of_lt_le` in terms of this:
protected theorem div_eq_iff (h : 0 < k) : x / k = y y * k x x y * k + k - 1 := by
rw [Nat.eq_iff_le_and_ge, and_comm, le_div_iff_mul_le h, Nat.div_le_iff_le_mul h]
@@ -95,6 +104,12 @@ theorem div_add_le_right {z : Nat} (h : 0 < z) (x y : Nat) :
x / (y + z) x / z :=
div_le_div_left (Nat.le_add_left z y) h
theorem div_add_div_le_add_div {x y z : Nat} : x / z + y / z (x + y) / z := by
by_cases hc : z > 0
· rw [Nat.le_div_iff_mul_le hc, Nat.add_mul]
apply Nat.add_le_add <;> apply Nat.div_mul_le_self
· simp_all
theorem succ_div_of_dvd {a b : Nat} (h : b a + 1) :
(a + 1) / b = a / b + 1 := by
replace h := mod_eq_zero_of_dvd h

View File

@@ -883,6 +883,10 @@ theorem guard_or_guard : (guard p a).or (guard q a) = guard (fun x => p x || q x
simp only [guard]
split <;> simp_all
theorem any_or_of_any_left {o₁ o₂ : Option α} {f : α Bool} (h : o₁.any f) :
(o₁.or o₂).any f := by
cases o₁ <;> simp_all
/-! ### `orElse` -/
/-- The `simp` normal form of `o <|> o'` is `o.or o'` via `orElse_eq_orElse` and `orElse_eq_or`. -/

View File

@@ -609,21 +609,22 @@ protected theorem compare_nil_right_eq_eq {α} [Ord α] {xs : List α} :
end List
/-- The lexicographic order on pairs. -/
@[expose] def lexOrd [Ord α] [Ord β] : Ord (α × β) where
@[expose, instance_reducible]
def lexOrd [Ord α] [Ord β] : Ord (α × β) where
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
/--
Constructs an `BEq` instance from an `Ord` instance that asserts that the result of `compare` is
`Ordering.eq`.
-/
@[expose] def beqOfOrd [Ord α] : BEq α where
@[expose, instance_reducible] def beqOfOrd [Ord α] : BEq α where
beq a b := (compare a b).isEq
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare` is
`Ordering.lt`.
-/
@[expose] def ltOfOrd [Ord α] : LT α where
@[expose, instance_reducible] def ltOfOrd [Ord α] : LT α where
lt a b := compare a b = Ordering.lt
@[inline]

View File

@@ -13,4 +13,6 @@ public import Init.Data.Order.Lemmas
public import Init.Data.Order.LemmasExtra
public import Init.Data.Order.Factories
public import Init.Data.Order.FactoriesExtra
public import Init.Data.Order.MinMaxOn
public import Init.Data.Order.Opposite
public import Init.Data.Order.PackageFactories

View File

@@ -23,7 +23,7 @@ preferring `a` over `b` when in doubt.
Has a `LawfulOrderLeftLeaningMin α` instance.
-/
@[inline]
@[inline, instance_reducible]
public def _root_.Min.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Min α where
min a b := if a b then a else b
@@ -33,7 +33,7 @@ preferring `a` over `b` when in doubt.
Has a `LawfulOrderLeftLeaningMax α` instance.
-/
@[inline]
@[inline, instance_reducible]
public def _root_.Max.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Max α where
max a b := if b a then a else b

View File

@@ -18,7 +18,7 @@ Creates an `LE α` instance from an `Ord α` instance.
`OrientedOrd α` must be satisfied so that the resulting `LE α` instance faithfully represents
the `Ord α` instance.
-/
@[inline, expose]
@[inline, expose, instance_reducible]
public def _root_.LE.ofOrd (α : Type u) [Ord α] : LE α where
le a b := (compare a b).isLE
@@ -38,7 +38,7 @@ Creates an `LT α` instance from an `Ord α` instance.
`OrientedOrd α` must be satisfied so that the resulting `LT α` instance faithfully represents
the `Ord α` instance.
-/
@[inline, expose]
@[inline, expose, instance_reducible]
public def _root_.LT.ofOrd (α : Type u) [Ord α] :
LT α where
lt a b := compare a b = .lt
@@ -82,7 +82,7 @@ public def _root_.DecidableLT.ofOrd (α : Type u) [LE α] [LT α] [Ord α] [Lawf
/--
Creates a `BEq α` instance from an `Ord α` instance. -/
@[inline, expose]
@[inline, expose, instance_reducible]
public def _root_.BEq.ofOrd (α : Type u) [Ord α] :
BEq α where
beq a b := compare a b = .eq

View File

@@ -90,9 +90,13 @@ end AxiomaticInstances
section LE
@[simp]
public theorem le_refl {α : Type u} [LE α] [Refl (α := α) (· ·)] (a : α) : a a := by
simp [Refl.refl]
public theorem le_of_eq [LE α] [Refl (α := α) (· ·)] {a b : α} : a = b a b :=
(· le_refl _)
public theorem le_antisymm {α : Type u} [LE α] [Std.Antisymm (α := α) (· ·)] {a b : α}
(hab : a b) (hba : b a) : a = b :=
Antisymm.antisymm _ _ hab hba
@@ -142,6 +146,10 @@ public theorem not_gt_of_lt {α : Type u} [LT α] [i : Std.Asymm (α := α) (·
(h : a < b) : ¬ b < a :=
i.asymm a b h
public theorem lt_irrefl {α : Type u} [LT α] [i : Std.Irrefl (α := α) (· < ·)] {a : α} :
¬ a < a :=
i.irrefl a
public theorem le_of_lt {α : Type u} [LT α] [LE α] [LawfulOrderLT α] {a b : α} (h : a < b) :
a b := (lt_iff_le_and_not_ge.1 h).1
@@ -222,6 +230,18 @@ public theorem lt_of_le_of_ne {α : Type u} [LE α] [LT α]
intro hge
exact hne.elim <| Std.Antisymm.antisymm a b hle hge
public theorem ne_of_lt {α : Type u} [LT α] [Std.Irrefl (α := α) (· < ·)] {a b : α} : a < b a b :=
fun h h' => absurd (h' h) (h' lt_irrefl)
public theorem le_iff_lt_or_eq [LE α] [LT α] [LawfulOrderLT α] [IsPartialOrder α] {a b : α} :
a b a < b a = b := by
refine fun h => ?_, fun h => h.elim le_of_lt le_of_eq
simp [Classical.or_iff_not_imp_left, Std.lt_iff_le_and_not_ge, h, Std.le_antisymm_iff]
public theorem lt_iff_le_and_ne [LE α] [LT α] [LawfulOrderLT α] [IsPartialOrder α] {a b : α} :
a < b a b a b := by
simpa [le_iff_lt_or_eq, or_and_right] using Std.ne_of_lt
end LT
end Std

View File

@@ -66,7 +66,7 @@ public theorem compare_eq_eq_iff_eq {α : Type u} [Ord α] [LawfulEqOrd α] {a b
public theorem IsLinearPreorder.of_ord {α : Type u} [LE α] [Ord α] [LawfulOrderOrd α]
[TransOrd α] : IsLinearPreorder α where
le_refl a := by simp [ isLE_compare]
le_refl a := by simp
le_trans a b c := by simpa [ isLE_compare] using TransOrd.isLE_trans
le_total a b := Total.total a b

View File

@@ -0,0 +1,198 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.NotationExtra
public import Init.Data.Order.Lemmas
public import Init.Data.Order.Opposite
open Std
open scoped OppositeOrderInstances
/-! ## Definitions -/
/--
Returns either `x` or `y`, the one with the smaller value under `f`.
If `f x ≤ f y`, it returns `x`, and otherwise returns `y`.
-/
public def minOn [LE β] [DecidableLE β] (f : α β) (x y : α) :=
if f x f y then x else y
/--
Returns either `x` or `y`, the one with the greater value under `f`.
If `f y ≤ f x`, it returns `x`, and otherwise returns `y`.
-/
public def maxOn [i : LE β] [DecidableLE β] (f : α β) (x y : α) :=
letI := i.opposite
minOn f x y
/-! ## `minOn` Lemmas -/
public theorem minOn_id [Min α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMin α] {x y : α} :
minOn id x y = min x y := by
simp [minOn, min_eq_if]
public theorem maxOn_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α] {x y : α} :
maxOn id x y = max x y := by
letI : LE α := (inferInstanceAs (LE α)).opposite
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
simp [maxOn, minOn_id, Max.min_oppositeMin, this]
public theorem minOn_eq_or [LE β] [DecidableLE β] {f : α β} {x y : α} :
minOn f x y = x minOn f x y = y := by
rw [minOn]
split
· exact Or.inl rfl
· exact Or.inr rfl
@[simp]
public theorem minOn_self [LE β] [DecidableLE β] {f : α β} {x : α} :
minOn f x x = x := by
cases minOn_eq_or (f := f) (x := x) (y := x) <;> assumption
public theorem minOn_eq_left [LE β] [DecidableLE β] {f : α β} {x y : α} (h : f x f y) :
minOn f x y = x := by
simp [minOn, h]
public theorem minOn_eq_right [LE β] [DecidableLE β] {f : α β} {x y : α} (h : ¬ f x f y) :
minOn f x y = y := by
simp [minOn, h]
public theorem minOn_eq_right_of_lt
[LE β] [DecidableLE β] [LT β] [Total (α := β) (· ·)] [LawfulOrderLT β]
{f : α β} {x y : α} (h : f y < f x) :
minOn f x y = y := by
apply minOn_eq_right
simpa [not_le] using h
public theorem apply_minOn_le_left [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y : α} : f (minOn f x y) f x := by
rw [minOn]
split
· apply le_refl
· exact le_of_not_ge _
public theorem apply_minOn_le_right [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y : α} : f (minOn f x y) f y := by
rw [minOn]
split
· assumption
· apply le_refl
public theorem le_apply_minOn_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y : α} {b : β} :
b f (minOn f x y) b f x b f y := by
apply Iff.intro
· intro h
exact le_trans h apply_minOn_le_left, le_trans h apply_minOn_le_right
· intro h
cases minOn_eq_or (f := f) (x := x) (y := y) <;> simp_all
public theorem minOn_assoc [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y z : α} : minOn f (minOn f x y) z = minOn f x (minOn f y z) := by
open scoped Classical.Order in
simp only [minOn]
split
· split
· split
· rfl
· rfl
· split
· have : ¬ f x f z := by assumption
have : f x f z := le_trans f x f y f y f z
contradiction
· rfl
· split
· rfl
· have : f z < f y := not_le.mp ¬ f y f z
have : f y < f x := not_le.mp ¬ f x f y
have : f z < f x := lt_trans _ _
rw [if_neg]
exact not_le.mpr _
public instance [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} :
Associative (minOn f) where
assoc := by apply minOn_assoc
public theorem min_apply [LE β] [DecidableLE β] [Min β] [LawfulOrderLeftLeaningMin β]
{f : α β} {x y : α} : min (f x) (f y) = f (minOn f x y) := by
rw [min_eq_if, minOn]
split <;> rfl
/-! ## `maxOn` Lemmas -/
public theorem maxOn_eq_minOn [le : LE β] [DecidableLE β] {f : α β} {x y : α} :
maxOn f x y = (letI := le.opposite; minOn f x y) :=
(rfl)
public theorem maxOn_eq_or [LE β] [DecidableLE β] {f : α β} {x y : α} :
maxOn f x y = x maxOn f x y = y :=
@minOn_eq_or ..
@[simp]
public theorem maxOn_self [LE β] [DecidableLE β] {f : α β} {x : α} :
maxOn f x x = x :=
@minOn_self ..
public theorem maxOn_eq_left [le : LE β] [DecidableLE β] {f : α β} {x y : α} (h : f y f x) :
maxOn f x y = x := by
simp only [maxOn_eq_minOn]
exact @minOn_eq_left (h := by simpa [LE.opposite_def]) ..
public theorem maxOn_eq_right [LE β] [DecidableLE β] {f : α β} {x y : α} (h : ¬ f y f x) :
maxOn f x y = y := by
simp only [maxOn_eq_minOn]
exact @minOn_eq_right (h := by simpa [LE.opposite_def]) ..
public theorem maxOn_eq_right_of_lt
[LE β] [DecidableLE β] [LT β] [Total (α := β) (· ·)] [LawfulOrderLT β]
{f : α β} {x y : α} (h : f x < f y) :
maxOn f x y = y :=
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : LT β := (inferInstanceAs (LT β)).opposite
minOn_eq_right_of_lt (h := by simpa [LT.lt_opposite_iff] using h) ..
public theorem left_le_apply_maxOn [le : LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y : α} : f x f (maxOn f x y) := by
rw [maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa only [LE.le_opposite_iff] using apply_minOn_le_left (f := f) ..
public theorem right_le_apply_maxOn [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y : α} : f y f (maxOn f x y) := by
rw [maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa only [LE.le_opposite_iff] using apply_minOn_le_right (f := f)
public theorem apply_maxOn_le_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y : α} {b : β} :
f (maxOn f x y) b f x b f y b := by
rw [maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa only [LE.le_opposite_iff] using le_apply_minOn_iff (f := f)
public theorem maxOn_assoc [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{x y z : α} : maxOn f (maxOn f x y) z = maxOn f x (maxOn f y z) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
minOn_assoc (f := f)
public instance [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} :
Associative (maxOn f) where
assoc := by
apply maxOn_assoc
public theorem max_apply [LE β] [DecidableLE β] [Max β] [LawfulOrderLeftLeaningMax β]
{f : α β} {x y : α} : max (f x) (f y) = f (maxOn f x y) := by
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : Min β := (inferInstanceAs (Max β)).oppositeMin
simpa [Max.min_oppositeMin] using min_apply (f := f)
public theorem apply_maxOn [LE β] [DecidableLE β] [Max β] [LawfulOrderLeftLeaningMax β]
{f : α β} {x y : α} : f (maxOn f x y) = max (f x) (f y) :=
max_apply.symm

View File

@@ -0,0 +1,407 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Order.ClassesExtra
public import Init.Data.Order.LemmasExtra
public section
open Std
set_option linter.missingDocs true
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
/-
Note: We're having verso docstrings disabled because the examples depend on instances that
are provided later in the module. They will be converted into verso docstrings at the end
of the module.
-/
/--
Inverts an {name}`LE` instance.
The result is an {lean}`LE α` instance where {lit}`a ≤ b` holds when {name}`le` would have
{lit}`b ≤ a` hold.
If {name}`le` obeys laws, then {lean}`le.opposite` obeys the opposite laws. For example, if
{name}`le` encodes a linear order, then {lean}`le.opposite` also encodes a linear order.
To automatically derive these laws, use {lit}`open Std.OppositeOrderInstances`.
For example, {name}`LE.opposite` can be used to derive maximum operations from minimum operations,
since finding the minimum in the opposite order is the same as finding the maximum in the original order:
```lean +warning
def min' [LE α] [DecidableLE α] (a b : α) : α :=
if a ≤ b then a else b
open scoped Std.OppositeOrderInstances in
def max' [LE α] [DecidableLE α] (a b : α) : α :=
letI : LE α := (inferInstanceAs (LE α)).opposite
-- `DecidableLE` for the opposite order is derived automatically via `OppositeOrderInstances`
min' a b
```
Without the `open scoped` command, Lean would not find the required {lit}`DecidableLE α`
instance for the opposite order.
-/
def LE.opposite (le : LE α) : LE α where
le a b := b a
theorem LE.opposite_def {le : LE α} :
le.opposite = fun a b => b a :=
(rfl)
theorem LE.le_opposite_iff {le : LE α} {a b : α} :
(haveI := le.opposite; a b) b a := by
exact Iff.rfl
/--
Inverts an {name}`LT` instance.
The result is an {lean}`LT α` instance where {lit}`a < b` holds when {name}`lt` would have
{lit}`b < a` hold.
If {name}`lt` obeys laws, then {lean}`lt.opposite` obeys the opposite laws.
To automatically derive these laws, use {lit}`open scoped Std.OppositeOrderInstances`.
For example, one can use the derived instances to prove properties about the opposite {name}`LT`
instance:
```lean
open scoped Std.OppositeOrderInstances in
example [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsLinearOrder α] {x y : α} :
letI : LE α := LE.opposite inferInstance
letI : LT α := LT.opposite inferInstance
¬ y ≤ x ↔ x < y :=
letI : LE α := LE.opposite inferInstance
letI : LT α := LT.opposite inferInstance
Std.not_le
```
Without the `open scoped` command, Lean would not find the {lit}`LawfulOrderLT α`
and {lit}`IsLinearOrder α` instances for the opposite order that are required by {name}`not_le`.
-/
def LT.opposite (lt : LT α) : LT α where
lt a b := b < a
theorem LT.opposite_def {lt : LT α} :
lt.opposite = fun a b => b < a :=
(rfl)
theorem LT.lt_opposite_iff {lt : LT α} {a b : α} :
(haveI := lt.opposite; a < b) b < a := by
exact Iff.rfl
/--
Creates a {name}`Max` instance from a {name}`Min` instance.
The result is a {lean}`Max α` instance that uses {lean}`min.min` as its {name}`max` operation.
If {name}`min` obeys laws, then {lean}`min.oppositeMax` obeys the corresponding laws for {name}`Max`.
To automatically derive these laws, use {lit}`open scoped Std.OppositeOrderInstances`.
For example, one can use the derived instances to prove properties about the opposite {name}`Max`
instance:
```lean
open scoped Std.OppositeOrderInstances in
example [LE α] [DecidableLE α] [Min α] [Std.LawfulOrderLeftLeaningMin α] {a b : α} :
letI : LE α := LE.opposite inferInstance
letI : Max α := (inferInstance : Min α).oppositeMax
max a b = if b ≤ a then a else b :=
letI : LE α := LE.opposite inferInstance
letI : Max α := (inferInstance : Min α).oppositeMax
Std.max_eq_if
```
Without the `open scoped` command, Lean would not find the {lit}`LawfulOrderLeftLeaningMax α`
instance for the opposite order that is required by {name}`max_eq_if`.
-/
def Min.oppositeMax (min : Min α) : Max α where
max a b := Min.min a b
theorem Min.oppositeMax_def {min : Min α} :
min.oppositeMax = Min.min :=
(rfl)
theorem Min.max_oppositeMax {min : Min α} {a b : α} :
(haveI := min.oppositeMax; Max.max a b) = Min.min a b :=
(rfl)
/--
Creates a {name}`Min` instance from a {name}`Max` instance.
The result is a {lean}`Min α` instance that uses {lean}`max.max` as its {name}`min` operation.
If {name}`max` obeys laws, then {lean}`max.oppositeMin` obeys the corresponding laws for {name}`Min`.
To automatically derive these laws, use {lit}`open scoped Std.OppositeOrderInstances`.
For example, one can use the derived instances to prove properties about the opposite {name}`Min`
instance:
```lean
open scoped Std.OppositeOrderInstances in
example [LE α] [DecidableLE α] [Max α] [Std.LawfulOrderLeftLeaningMax α] {a b : α} :
letI : LE α := LE.opposite inferInstance
letI : Min α := (inferInstance : Max α).oppositeMin
min a b = if a ≤ b then a else b :=
letI : LE α := LE.opposite inferInstance
letI : Min α := (inferInstance : Max α).oppositeMin
Std.min_eq_if
```
Without the `open scoped` command, Lean would not find the {lit}`LawfulOrderLeftLeaningMin α`
instance for the opposite order that is required by {name}`min_eq_if`.
-/
def Max.oppositeMin (max : Max α) : Min α where
min a b := Max.max a b
theorem Max.oppositeMin_def {min : Max α} :
min.oppositeMin = Max.max :=
(rfl)
theorem Max.min_oppositeMin {max : Max α} {a b : α} :
(haveI := max.oppositeMin; Min.min a b) = Max.max a b :=
(rfl)
namespace Std.OppositeOrderInstances
@[no_expose]
scoped instance (priority := low) instDecidableLEOpposite {i : LE α} [id : DecidableLE α] :
haveI := i.opposite
DecidableLE α :=
fun a b => id b a
@[no_expose]
scoped instance (priority := low) instDecidableLTOpposite {i : LT α} [id : DecidableLT α] :
haveI := i.opposite
DecidableLT α :=
fun a b => id b a
scoped instance (priority := low) instLEReflOpposite {i : LE α} [Refl (α := α) (· ·)] :
haveI := i.opposite
Refl (α := α) (· ·) :=
letI := i.opposite
{ refl a := letI := i; le_refl a }
scoped instance (priority := low) instLESymmOpposite {i : LE α} [Symm (α := α) (· ·)] :
haveI := i.opposite
Symm (α := α) (· ·) :=
letI := i.opposite
{ symm a b hab := by
simp only [LE.opposite] at *
letI := i
exact Symm.symm b a hab }
scoped instance (priority := low) instLEAntisymmOpposite {i : LE α} [Antisymm (α := α) (· ·)] :
haveI := i.opposite
Antisymm (α := α) (· ·) :=
letI := i.opposite
{ antisymm a b hab hba := by
simp only [LE.opposite] at *
letI := i
exact le_antisymm hba hab }
scoped instance (priority := low) instLEAsymmOpposite {i : LE α} [Asymm (α := α) (· ·)] :
haveI := i.opposite
Asymm (α := α) (· ·) :=
letI := i.opposite
{ asymm a b hab := by
simp only [LE.opposite] at *
letI := i
exact Asymm.asymm b a hab }
scoped instance (priority := low) instLETransOpposite {i : LE α}
[Trans (· ·) (· ·) (· · : α α Prop)] :
haveI := i.opposite
Trans (· ·) (· ·) (· · : α α Prop) :=
letI := i.opposite
{ trans hab hbc := by
simp only [LE.opposite] at *
letI := i
exact Trans.trans hbc hab }
scoped instance (priority := low) instLETotalOpposite {i : LE α} [Total (α := α) (· ·)] :
haveI := i.opposite
Total (α := α) (· ·) :=
letI := i.opposite
{ total a b := letI := i; le_total (a := b) (b := a) }
scoped instance (priority := low) instLEIrreflOpposite {i : LE α} [Irrefl (α := α) (· ·)] :
haveI := i.opposite
Irrefl (α := α) (· ·) :=
letI := i.opposite
{ irrefl a := letI := i; Irrefl.irrefl (r := (· ·)) a }
scoped instance (priority := low) instIsPreorderOpposite {i : LE α} [IsPreorder α] :
haveI := i.opposite
IsPreorder α :=
letI := i.opposite
{ le_refl a := le_refl a
le_trans _ _ _ := le_trans }
scoped instance (priority := low) instIsPartialOrderOpposite {i : LE α} [IsPartialOrder α] :
haveI := i.opposite
IsPartialOrder α :=
letI := i.opposite
{ le_antisymm _ _ := le_antisymm }
scoped instance (priority := low) instIsLinearPreorderOpposite {i : LE α} [IsLinearPreorder α] :
haveI := i.opposite
IsLinearPreorder α :=
letI := i.opposite
{ le_total _ _ := le_total }
scoped instance (priority := low) instIsLinearOrderOpposite {i : LE α} [IsLinearOrder α] :
haveI := i.opposite
IsLinearOrder α :=
letI := i.opposite; {}
scoped instance (priority := low) instLawfulOrderOrdOpposite {il : LE α} {io : Ord α}
[LawfulOrderOrd α] :
haveI := il.opposite
haveI := io.opposite
LawfulOrderOrd α :=
letI := il.opposite
letI := io.opposite
{ isLE_compare a b := by
simp only [LE.opposite, Ord.opposite]
letI := il; letI := io
apply isLE_compare
isGE_compare a b := by
simp only [LE.opposite, Ord.opposite]
letI := il; letI := io
apply isGE_compare }
scoped instance (priority := low) instLawfulOrderLTOpposite {il : LE α} {it : LT α}
[LawfulOrderLT α] :
haveI := il.opposite
haveI := it.opposite
LawfulOrderLT α :=
letI := il.opposite
letI := it.opposite
{ lt_iff a b := by
simp only [LE.opposite, LT.opposite]
letI := il; letI := it
exact LawfulOrderLT.lt_iff b a }
scoped instance (priority := low) instLawfulOrderBEqOpposite {il : LE α} {ib : BEq α}
[LawfulOrderBEq α] :
haveI := il.opposite
LawfulOrderBEq α :=
letI := il.opposite
{ beq_iff_le_and_ge a b := by
simp only [LE.opposite]
letI := il; letI := ib
rw [LawfulOrderBEq.beq_iff_le_and_ge]
exact and_comm }
scoped instance (priority := low) instLawfulOrderInfOpposite {il : LE α} {im : Min α}
[LawfulOrderInf α] :
haveI := il.opposite
haveI := im.oppositeMax
LawfulOrderSup α :=
letI := il.opposite
letI := im.oppositeMax
{ max_le_iff a b c := by
simp only [LE.opposite, Min.oppositeMax]
letI := il; letI := im
exact LawfulOrderInf.le_min_iff c a b }
scoped instance (priority := low) instLawfulOrderMinOpposite {il : LE α} {im : Min α}
[LawfulOrderMin α] :
haveI := il.opposite
haveI := im.oppositeMax
LawfulOrderMax α :=
letI := il.opposite
letI := im.oppositeMax
{ max_eq_or a b := by
simp only [Min.oppositeMax]
letI := il; letI := im
exact MinEqOr.min_eq_or a b
max_le_iff a b c := by
simp only [LE.opposite, Min.oppositeMax]
letI := il; letI := im
exact LawfulOrderInf.le_min_iff c a b }
scoped instance (priority := low) instLawfulOrderSupOpposite {il : LE α} {im : Max α}
[LawfulOrderSup α] :
haveI := il.opposite
haveI := im.oppositeMin
LawfulOrderInf α :=
letI := il.opposite
letI := im.oppositeMin
{ le_min_iff a b c := by
simp only [LE.opposite, Max.oppositeMin]
letI := il; letI := im
exact LawfulOrderSup.max_le_iff b c a }
scoped instance (priority := low) instLawfulOrderMaxOpposite {il : LE α} {im : Max α}
[LawfulOrderMax α] :
haveI := il.opposite
haveI := im.oppositeMin
LawfulOrderMin α :=
letI := il.opposite
letI := im.oppositeMin
{ min_eq_or a b := by
simp only [Max.oppositeMin]
letI := il; letI := im
exact MaxEqOr.max_eq_or a b
le_min_iff a b c := by
simp only [LE.opposite, Max.oppositeMin]
letI := il; letI := im
exact LawfulOrderSup.max_le_iff b c a }
scoped instance (priority := low) instLawfulOrderLeftLeaningMinOpposite {il : LE α} {im : Min α}
[LawfulOrderLeftLeaningMin α] :
haveI := il.opposite
haveI := im.oppositeMax
LawfulOrderLeftLeaningMax α :=
letI := il.opposite
letI := im.oppositeMax
{ max_eq_left a b hab := by
simp only [Min.oppositeMax]
letI := il; letI := im
exact LawfulOrderLeftLeaningMin.min_eq_left a b hab
max_eq_right a b hab := by
simp only [Min.oppositeMax]
letI := il; letI := im
exact LawfulOrderLeftLeaningMin.min_eq_right a b hab }
scoped instance (priority := low) instLawfulOrderLeftLeaningMaxOpposite {il : LE α} {im : Max α}
[LawfulOrderLeftLeaningMax α] :
haveI := il.opposite
haveI := im.oppositeMin
LawfulOrderLeftLeaningMin α :=
letI := il.opposite
letI := im.oppositeMin
{ min_eq_left a b hab := by
simp only [Max.oppositeMin]
letI := il; letI := im
exact LawfulOrderLeftLeaningMax.max_eq_left a b hab
min_eq_right a b hab := by
simp only [Max.oppositeMin]
letI := il; letI := im
exact LawfulOrderLeftLeaningMax.max_eq_right a b hab }
end OppositeOrderInstances
-- When imported from a non-module, these instances are exposed, and reducing them during
-- type class resolution is too inefficient.
attribute [irreducible] LE.opposite LT.opposite Min.oppositeMax Max.oppositeMin
section DocsToVerso
set_option linter.unusedVariables false -- Otherwise, we get warnings about Verso code blocks.
docs_to_verso LE.opposite
docs_to_verso LT.opposite
docs_to_verso Min.oppositeMax
docs_to_verso Max.oppositeMin
end DocsToVerso

View File

@@ -663,7 +663,7 @@ public def LinearPreorderPackage.ofOrd (α : Type u)
isGE_compare]
decidableLE := args.decidableLE
decidableLT := args.decidableLT
le_refl a := by simp [ isLE_compare]
le_refl a := by simp
le_total a b := by cases h : compare a b <;> simp [h, isLE_compare (a := a), isGE_compare (a := a)]
le_trans a b c := by simpa [ isLE_compare] using TransOrd.isLE_trans }

View File

@@ -240,6 +240,9 @@ This propositional typeclass ensures that `UpwardEnumerable.succ?` will never re
In other words, it ensures that there will always be a successor.
-/
class InfinitelyUpwardEnumerable (α : Type u) [UpwardEnumerable α] where
/--
Every element of `α` has a successor.
-/
isSome_succ? : a : α, (UpwardEnumerable.succ? a).isSome
/--

View File

@@ -409,7 +409,7 @@ Examples:
* `(if (5 : Int8) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : Int8) < 7) by decide`
-/
@[extern "lean_int8_dec_lt"]
@[extern "lean_int8_dec_lt", instance_reducible]
def Int8.decLt (a b : Int8) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
@@ -425,7 +425,7 @@ Examples:
* `(if (15 : Int8) ≤ 5 then "yes" else "no") = "no"`
* `show (7 : Int8) ≤ 7 by decide`
-/
@[extern "lean_int8_dec_le"]
@[extern "lean_int8_dec_le", instance_reducible]
def Int8.decLe (a b : Int8) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
@@ -778,7 +778,7 @@ Examples:
* `(if (5 : Int16) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : Int16) < 7) by decide`
-/
@[extern "lean_int16_dec_lt"]
@[extern "lean_int16_dec_lt", instance_reducible]
def Int16.decLt (a b : Int16) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
@@ -794,7 +794,7 @@ Examples:
* `(if (15 : Int16) ≤ 5 then "yes" else "no") = "no"`
* `show (7 : Int16) ≤ 7 by decide`
-/
@[extern "lean_int16_dec_le"]
@[extern "lean_int16_dec_le", instance_reducible]
def Int16.decLe (a b : Int16) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
@@ -1163,7 +1163,7 @@ Examples:
* `(if (5 : Int32) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : Int32) < 7) by decide`
-/
@[extern "lean_int32_dec_lt"]
@[extern "lean_int32_dec_lt", instance_reducible]
def Int32.decLt (a b : Int32) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
@@ -1179,7 +1179,7 @@ Examples:
* `(if (15 : Int32) ≤ 5 then "yes" else "no") = "no"`
* `show (7 : Int32) ≤ 7 by decide`
-/
@[extern "lean_int32_dec_le"]
@[extern "lean_int32_dec_le", instance_reducible]
def Int32.decLe (a b : Int32) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
@@ -1568,7 +1568,7 @@ Examples:
* `(if (5 : Int64) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : Int64) < 7) by decide`
-/
@[extern "lean_int64_dec_lt"]
@[extern "lean_int64_dec_lt", instance_reducible]
def Int64.decLt (a b : Int64) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
/--
@@ -1583,7 +1583,7 @@ Examples:
* `(if (15 : Int64) ≤ 5 then "yes" else "no") = "no"`
* `show (7 : Int64) ≤ 7 by decide`
-/
@[extern "lean_int64_dec_le"]
@[extern "lean_int64_dec_le", instance_reducible]
def Int64.decLe (a b : Int64) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))
@@ -1958,7 +1958,7 @@ Examples:
* `(if (5 : ISize) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : ISize) < 7) by decide`
-/
@[extern "lean_isize_dec_lt"]
@[extern "lean_isize_dec_lt", instance_reducible]
def ISize.decLt (a b : ISize) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec.slt b.toBitVec))
@@ -1974,7 +1974,7 @@ Examples:
* `(if (15 : ISize) ≤ 5 then "yes" else "no") = "no"`
* `show (7 : ISize) ≤ 7 by decide`
-/
@[extern "lean_isize_dec_le"]
@[extern "lean_isize_dec_le", instance_reducible]
def ISize.decLe (a b : ISize) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec.sle b.toBitVec))

View File

@@ -61,7 +61,7 @@ instance SubarrayIterator.instFinite : Finite (SubarrayIterator α) Id :=
instance [Monad m] : IteratorLoop (SubarrayIterator α) Id m := .defaultImplementation
@[inline, expose]
@[inline, expose, instance_reducible]
def Subarray.instToIterator :=
ToIterator.of (γ := Slice (Internal.SubarrayData α)) (β := α) (SubarrayIterator α) (·)
attribute [instance] Subarray.instToIterator

View File

@@ -85,9 +85,12 @@ theorem toList_eq {α : Type u} {it : Iter (α := SubarrayIterator α) α} :
· rw [dif_neg]; rotate_left; exact h
simp_all [it.internalState.xs.stop_le_array_size]
theorem count_eq {α : Type u} {it : Iter (α := SubarrayIterator α) α} :
it.count = it.internalState.xs.stop - it.internalState.xs.start := by
simp [ Iter.length_toList_eq_count, toList_eq, it.internalState.xs.stop_le_array_size]
theorem length_eq {α : Type u} {it : Iter (α := SubarrayIterator α) α} :
it.length = it.internalState.xs.stop - it.internalState.xs.start := by
simp [ Iter.length_toList_eq_length, toList_eq, it.internalState.xs.stop_le_array_size]
@[deprecated length_eq (since := "2026-01-28")]
def count_eq := @length_eq
end SubarrayIterator
@@ -105,7 +108,7 @@ theorem toList_internalIter {α : Type u} {s : Subarray α} :
public instance : LawfulSliceSize (Internal.SubarrayData α) where
lawful s := by
simp [SliceSize.size, ToIterator.iter_eq, Iter.toIter_toIterM,
Iter.length_toList_eq_count, SubarrayIterator.toList_eq,
Iter.length_toList_eq_length, SubarrayIterator.toList_eq,
s.internalRepresentation.stop_le_array_size, start, stop, array]
public theorem toArray_eq_sliceToArray {α : Type u} {s : Subarray α} :

View File

@@ -60,12 +60,15 @@ public theorem forIn_toArray {γ : Type u} {β : Type v}
ForIn.forIn s.toArray init f = ForIn.forIn s init f := by
rw [ forIn_internalIter, Iter.forIn_toArray, Slice.toArray]
theorem Internal.size_eq_count_iter [ToIterator (Slice γ) Id α β]
theorem Internal.size_eq_length_iter [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{s : Slice γ} [SliceSize γ] [LawfulSliceSize γ] :
s.size = (Internal.iter s).count := by
simp only [Slice.size, iter, LawfulSliceSize.lawful, Iter.length_toList_eq_count]
s.size = (Internal.iter s).length := by
simp only [Slice.size, iter, LawfulSliceSize.lawful, Iter.length_toList_eq_length]
@[deprecated Internal.size_eq_length_iter (since := "2026-01-28")]
def Internal.size_eq_count_iter := @Internal.size_eq_length_iter
theorem Internal.toArray_eq_toArray_iter {s : Slice γ} [ToIterator (Slice γ) Id α β]
[Iterator α Id β]
@@ -91,7 +94,7 @@ theorem size_toArray_eq_size [ToIterator (Slice γ) Id α β]
{s : Slice γ} :
s.toArray.size = s.size := by
letI : IteratorLoop α Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toArray_eq_toArray_iter, Iter.size_toArray_eq_count]
rw [Internal.size_eq_length_iter, Internal.toArray_eq_toArray_iter, Iter.size_toArray_eq_length]
@[simp]
theorem length_toList_eq_size [ToIterator (Slice γ) Id α β]
@@ -100,7 +103,7 @@ theorem length_toList_eq_size [ToIterator (Slice γ) Id α β]
[Finite α Id] :
s.toList.length = s.size := by
letI : IteratorLoop α Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toList_eq_toList_iter, Iter.length_toList_eq_count]
rw [Internal.size_eq_length_iter, Internal.toList_eq_toList_iter, Iter.length_toList_eq_length]
@[simp]
theorem length_toListRev_eq_size [ToIterator (Slice γ) Id α β]
@@ -109,7 +112,7 @@ theorem length_toListRev_eq_size [ToIterator (Slice γ) Id α β]
[Finite α Id]
[LawfulIteratorLoop α Id Id] :
s.toListRev.length = s.size := by
rw [Internal.size_eq_count_iter, Internal.toListRev_eq_toListRev_iter,
Iter.length_toListRev_eq_count]
rw [Internal.size_eq_length_iter, Internal.toListRev_eq_toListRev_iter,
Iter.length_toListRev_eq_length]
end Std.Slice

View File

@@ -24,7 +24,7 @@ open Std Slice PRange Iterators
variable {α : Type u}
@[inline, expose]
@[inline, expose, instance_reducible]
def ListSlice.instToIterator :=
ToIterator.of (γ := Slice (Internal.ListSliceData α)) _ (fun s => match s.internalRepresentation.stop with
| some n => s.internalRepresentation.list.iter.take n
@@ -34,7 +34,7 @@ attribute [instance] ListSlice.instToIterator
universe v w
instance : SliceSize (Internal.ListSliceData α) where
size s := (Internal.iter s).count
size s := (Internal.iter s).length
@[no_expose]
instance {α : Type u} {m : Type v Type w} [Monad m] :

View File

@@ -60,7 +60,7 @@ public theorem toList_toArray {xs : ListSlice α} :
@[simp, grind =]
public theorem length_toList {xs : ListSlice α} :
xs.toList.length = xs.size := by
simp [ListSlice.toList_eq, Std.Slice.size, Std.Slice.SliceSize.size, Iter.length_toList_eq_count,
simp [ListSlice.toList_eq, Std.Slice.size, Std.Slice.SliceSize.size, Iter.length_toList_eq_length,
toList_internalIter]; rfl
@[grind =]

View File

@@ -45,7 +45,7 @@ class LawfulSliceSize (γ : Type u) [SliceSize γ] [ToIterator (Slice γ) Id α
/-- The iterator of a slice `s` of type `Slice γ` emits exactly `SliceSize.size s` elements. -/
lawful :
letI : IteratorLoop α Id Id := .defaultImplementation
s : Slice γ, SliceSize.size s = (ToIterator.iter (γ := Slice γ) s).count
s : Slice γ, SliceSize.size s = (ToIterator.iter (γ := Slice γ) s).length
/--
Returns the number of elements with distinct indices in the given slice.

View File

@@ -25,4 +25,5 @@ public import Init.Data.String.Termination
public import Init.Data.String.ToSlice
public import Init.Data.String.Search
public import Init.Data.String.Legacy
public import Init.Data.String.Grind
public import Init.Data.String.OrderInstances
public import Init.Data.String.FindPos

View File

@@ -1740,38 +1740,6 @@ theorem Pos.copy_toSlice_eq_cast {s : String} (p : s.Pos) :
p.toSlice.copy = p.cast copy_toSlice.symm :=
Pos.ext (by simp)
/-- Given a byte position within a string slice, obtains the smallest valid position that is
strictly greater than the given byte position. -/
@[inline]
def Slice.findNextPos (offset : String.Pos.Raw) (s : Slice) (_h : offset < s.rawEndPos) : s.Pos :=
go offset.inc
where
go (offset : String.Pos.Raw) : s.Pos :=
if h : offset < s.rawEndPos then
if h' : (s.getUTF8Byte offset h).IsUTF8FirstByte then
s.pos offset (Pos.Raw.isValidForSlice_iff_isUTF8FirstByte.2 (Or.inr _, h'))
else
go offset.inc
else
s.endPos
termination_by s.utf8ByteSize - offset.byteIdx
decreasing_by
simp only [Pos.Raw.lt_iff, byteIdx_rawEndPos, utf8ByteSize_eq, Pos.Raw.byteIdx_inc] at h
omega
private theorem Slice.le_offset_findNextPosGo {s : Slice} {o : String.Pos.Raw} (h : o s.rawEndPos) :
o (findNextPos.go s o).offset := by
fun_induction findNextPos.go with
| case1 => simp
| case2 x h₁ h₂ ih =>
refine Pos.Raw.le_of_lt (Pos.Raw.lt_of_lt_of_le Pos.Raw.lt_inc (ih ?_))
rw [Pos.Raw.le_iff, Pos.Raw.byteIdx_inc]
exact Nat.succ_le_iff.2 h₁
| case3 x h => exact h
theorem Slice.lt_offset_findNextPos {s : Slice} {o : String.Pos.Raw} (h) : o < (s.findNextPos o h).offset :=
Pos.Raw.lt_of_lt_of_le Pos.Raw.lt_inc (le_offset_findNextPosGo (Pos.Raw.inc_le.2 h))
theorem Slice.Pos.prevAuxGo_le_self {s : Slice} {p : Nat} {h : p < s.utf8ByteSize} :
prevAux.go p h p := by
induction p with
@@ -2044,6 +2012,15 @@ theorem Slice.Pos.next_le_of_lt {s : Slice} {p q : s.Pos} {h} : p < q → p.next
theorem Pos.ofToSlice_le_iff {s : String} {p : s.toSlice.Pos} {q : s.Pos} :
ofToSlice p q p q.toSlice := Iff.rfl
theorem Pos.ofToSlice_lt_iff {s : String} {p : s.toSlice.Pos} {q : s.Pos} :
ofToSlice p < q p < q.toSlice := Iff.rfl
theorem Pos.lt_ofToSlice_iff {s : String} {p : s.Pos} {q : s.toSlice.Pos} :
p < ofToSlice q p.toSlice < q := Iff.rfl
theorem Pos.le_ofToSlice_iff {s : String} {p : s.Pos} {q : s.toSlice.Pos} :
p ofToSlice q p.toSlice q := Iff.rfl
@[simp]
theorem Pos.toSlice_lt_toSlice_iff {s : String} {p q : s.Pos} :
p.toSlice < q.toSlice p < q := Iff.rfl

View File

@@ -0,0 +1,62 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
public import Init.Data.String.Basic
set_option doc.verso true
/-!
# Finding positions in a string relative to a given raw position
-/
public section
namespace String
/--
Obtains the smallest valid position that is greater than or equal to the given byte position.
-/
def Slice.posGE (s : Slice) (offset : String.Pos.Raw) (_h : offset s.rawEndPos) : s.Pos :=
if h : offset < s.rawEndPos then
if h' : (s.getUTF8Byte offset h).IsUTF8FirstByte then
s.pos offset (Pos.Raw.isValidForSlice_iff_isUTF8FirstByte.2 (Or.inr _, h'))
else
s.posGE offset.inc (by simpa)
else
s.endPos
termination_by s.utf8ByteSize - offset.byteIdx
decreasing_by
simp only [Pos.Raw.lt_iff, byteIdx_rawEndPos, utf8ByteSize_eq, Pos.Raw.byteIdx_inc] at h
omega
/--
Obtains the smallest valid position that is strictly greater than the given byte position.
-/
@[inline]
def Slice.posGT (s : Slice) (offset : String.Pos.Raw) (h : offset < s.rawEndPos) : s.Pos :=
s.posGE offset.inc (by simpa)
@[deprecated Slice.posGT (since := "2026-02-03")]
def Slice.findNextPos (offset : String.Pos.Raw) (s : Slice) (h : offset < s.rawEndPos) : s.Pos :=
s.posGT offset h
/--
Obtains the smallest valid position that is greater than or equal to the given byte position.
-/
@[inline]
def posGE (s : String) (offset : String.Pos.Raw) (h : offset s.rawEndPos) : s.Pos :=
Pos.ofToSlice (s.toSlice.posGE offset (by simpa))
/--
Obtains the smallest valid position that is strictly greater than the given byte position.
-/
@[inline]
def posGT (s : String) (offset : String.Pos.Raw) (h : offset < s.rawEndPos) : s.Pos :=
Pos.ofToSlice (s.toSlice.posGT offset (by simpa))
end String

View File

@@ -9,6 +9,9 @@ prelude
public import Init.Data.String.Lemmas.Splits
public import Init.Data.String.Lemmas.Modify
public import Init.Data.String.Lemmas.Search
public import Init.Data.String.Lemmas.FindPos
public import Init.Data.String.Lemmas.Basic
public import Init.Data.String.Lemmas.Order
public import Init.Data.Char.Order
public import Init.Data.Char.Lemmas
public import Init.Data.List.Lex

View File

@@ -55,4 +55,21 @@ theorem Slice.Pos.startPos_le {s : Slice} (p : s.Pos) : s.startPos ≤ p := by
theorem Slice.Pos.le_endPos {s : Slice} (p : s.Pos) : p s.endPos :=
p.isValidForSlice.le_rawEndPos
theorem getUTF8Byte_eq_getUTF8Byte_toSlice {s : String} {p : String.Pos.Raw} {h} :
s.getUTF8Byte p h = s.toSlice.getUTF8Byte p (by simpa) := by
simp [Slice.getUTF8Byte]
theorem getUTF8Byte_toSlice {s : String} {p : String.Pos.Raw} {h} :
s.toSlice.getUTF8Byte p h = s.getUTF8Byte p (by simpa) := by
simp [Slice.getUTF8Byte]
@[simp]
theorem Pos.byte_toSlice {s : String} {p : s.Pos} {h} :
p.toSlice.byte h = p.byte (ne_of_apply_ne Pos.toSlice (by simpa)) := by
simp [byte]
theorem Pos.byte_eq_byte_toSlice {s : String} {p : s.Pos} {h} :
p.byte h = p.toSlice.byte (ne_of_apply_ne Pos.ofToSlice (by simpa)) := by
simp
end String

View File

@@ -0,0 +1,207 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
public import Init.Data.String.FindPos
import all Init.Data.String.FindPos
import Init.Data.String.OrderInstances
import Init.Data.String.Lemmas.Basic
import Init.Data.String.Lemmas.Order
public section
namespace String
namespace Slice
@[simp]
theorem le_offset_posGE {s : Slice} {p : Pos.Raw} {h : p s.rawEndPos} :
p (s.posGE p h).offset := by
fun_induction posGE with
| case1 => simp
| case2 => exact Std.le_trans (Std.le_of_lt (Pos.Raw.lt_inc)) _
| case3 => assumption
@[simp]
theorem posGE_le_iff {s : Slice} {p : Pos.Raw} {h : p s.rawEndPos} {q : s.Pos} :
s.posGE p h q p q.offset := by
fun_induction posGE with
| case1 => simp [Pos.le_iff]
| case2 r h₁ h₂ h₃ ih =>
suffices r q.offset by simp [ih, Pos.Raw.inc_le, Std.le_iff_lt_or_eq (a := r), this]
exact fun h => h₃ (h q.isUTF8FirstByte_getUTF8Byte_offset)
| case3 r h₁ h₂ =>
obtain rfl : r = s.rawEndPos := Std.le_antisymm h₁ (Std.not_lt.1 h₂)
simp only [Pos.endPos_le, offset_endPos, Pos.le_iff]
@[simp]
theorem lt_posGE_iff {s : Slice} {p : Pos.Raw} {h : p s.rawEndPos} {q : s.Pos} :
q < s.posGE p h q.offset < p := by
rw [ Std.not_le, posGE_le_iff, Std.not_le]
theorem posGE_eq_iff {s : Slice} {p : Pos.Raw} {h : p s.rawEndPos} {q : s.Pos} :
s.posGE p h = q p q.offset q', p q'.offset q q' :=
by rintro rfl; simp, fun h₁, h₂ => Std.le_antisymm (by simpa) (h₂ _ (by simp))
theorem posGT_eq_posGE {s : Slice} {p : Pos.Raw} {h : p < s.rawEndPos} :
s.posGT p h = s.posGE p.inc (by simpa) :=
(rfl)
@[simp]
theorem posGE_inc {s : Slice} {p : Pos.Raw} {h : p.inc s.rawEndPos} :
s.posGE p.inc h = s.posGT p (by simpa) :=
(rfl)
@[simp]
theorem lt_offset_posGT {s : Slice} {p : Pos.Raw} {h : p < s.rawEndPos} :
p < (s.posGT p h).offset :=
Std.lt_of_lt_of_le p.lt_inc (by simp [posGT_eq_posGE, -posGE_inc])
@[simp]
theorem posGT_le_iff {s : Slice} {p : Pos.Raw} {h : p < s.rawEndPos} {q : s.Pos} :
s.posGT p h q p < q.offset := by
rw [posGT_eq_posGE, posGE_le_iff, Pos.Raw.inc_le]
@[simp]
theorem lt_posGT_iff {s : Slice} {p : Pos.Raw} {h : p < s.rawEndPos} {q : s.Pos} :
q < s.posGT p h q.offset p := by
rw [posGT_eq_posGE, lt_posGE_iff, Pos.Raw.lt_inc_iff]
theorem posGT_eq_iff {s : Slice} {p : Pos.Raw} {h : p < s.rawEndPos} {q : s.Pos} :
s.posGT p h = q p < q.offset q', p < q'.offset q q' := by
simp [posGT_eq_posGE, -posGE_inc, posGE_eq_iff]
@[simp]
theorem Pos.posGE_offset {s : Slice} {p : s.Pos} : s.posGE p.offset (by simp) = p := by
simp [posGE_eq_iff, Pos.le_iff]
@[simp]
theorem offset_posGE_eq_self_iff {s : Slice} {p : String.Pos.Raw} {h} :
(s.posGE p h).offset = p p.IsValidForSlice s :=
fun h' => by simpa [h'] using (s.posGE p h).isValidForSlice,
fun h' => by simpa using congrArg Pos.offset (Pos.posGE_offset (p := s.pos p h'))
theorem posGE_eq_pos {s : Slice} {p : String.Pos.Raw} (h : p.IsValidForSlice s) :
s.posGE p h.le_rawEndPos = s.pos p h := by
simpa using Pos.posGE_offset (p := s.pos p h)
theorem pos_eq_posGE {s : Slice} {p : String.Pos.Raw} {h} :
s.pos p h = s.posGE p h.le_rawEndPos := by
simp [posGE_eq_pos h]
@[simp]
theorem Pos.posGT_offset {s : Slice} {p : s.Pos} {h} :
s.posGT p.offset h = p.next (by simpa using h) := by
rw [posGT_eq_iff, Pos.lt_iff]
simp [ Pos.lt_iff]
theorem posGT_eq_next {s : Slice} {p : String.Pos.Raw} {h} (h' : p.IsValidForSlice s) :
s.posGT p h = (s.pos p h').next (by simpa [Pos.ext_iff] using Pos.Raw.ne_of_lt h) := by
simpa using Pos.posGT_offset (h := h) (p := s.pos p h')
theorem next_eq_posGT {s : Slice} {p : s.Pos} {h} :
p.next h = s.posGT p.offset (by simpa) := by
simp
end Slice
@[simp]
theorem le_offset_posGE {s : String} {p : Pos.Raw} {h : p s.rawEndPos} :
p (s.posGE p h).offset := by
simp [posGE]
@[simp]
theorem posGE_le_iff {s : String} {p : Pos.Raw} {h : p s.rawEndPos} {q : s.Pos} :
s.posGE p h q p q.offset := by
simp [posGE, Pos.ofToSlice_le_iff]
@[simp]
theorem lt_posGE_iff {s : String} {p : Pos.Raw} {h : p s.rawEndPos} {q : s.Pos} :
q < s.posGE p h q.offset < p := by
simp [posGE, Pos.lt_ofToSlice_iff]
theorem posGE_eq_iff {s : String} {p : Pos.Raw} {h : p s.rawEndPos} {q : s.Pos} :
s.posGE p h = q p q.offset q', p q'.offset q q' :=
by rintro rfl; simp, fun h₁, h₂ => Std.le_antisymm (by simpa) (h₂ _ (by simp))
theorem posGT_eq_posGE {s : String} {p : Pos.Raw} {h : p < s.rawEndPos} :
s.posGT p h = s.posGE p.inc (by simpa) :=
(rfl)
@[simp]
theorem posGE_inc {s : String} {p : Pos.Raw} {h : p.inc s.rawEndPos} :
s.posGE p.inc h = s.posGT p (by simpa) :=
(rfl)
@[simp]
theorem lt_offset_posGT {s : String} {p : Pos.Raw} {h : p < s.rawEndPos} :
p < (s.posGT p h).offset :=
Std.lt_of_lt_of_le p.lt_inc (by simp [posGT_eq_posGE, -posGE_inc])
@[simp]
theorem posGT_le_iff {s : String} {p : Pos.Raw} {h : p < s.rawEndPos} {q : s.Pos} :
s.posGT p h q p < q.offset := by
rw [posGT_eq_posGE, posGE_le_iff, Pos.Raw.inc_le]
@[simp]
theorem lt_posGT_iff {s : String} {p : Pos.Raw} {h : p < s.rawEndPos} {q : s.Pos} :
q < s.posGT p h q.offset p := by
rw [posGT_eq_posGE, lt_posGE_iff, Pos.Raw.lt_inc_iff]
theorem posGT_eq_iff {s : String} {p : Pos.Raw} {h : p < s.rawEndPos} {q : s.Pos} :
s.posGT p h = q p < q.offset q', p < q'.offset q q' := by
simp [posGT_eq_posGE, -posGE_inc, posGE_eq_iff]
theorem posGE_toSlice {s : String} {p : Pos.Raw} (h : p s.toSlice.rawEndPos) :
s.toSlice.posGE p h = (s.posGE p (by simpa)).toSlice := by
simp [posGE]
theorem posGE_eq_posGE_toSlice {s : String} {p : Pos.Raw} (h : p s.rawEndPos) :
s.posGE p h = Pos.ofToSlice (s.toSlice.posGE p (by simpa)) := by
simp [posGE]
theorem posGT_toSlice {s : String} {p : Pos.Raw} (h : p < s.toSlice.rawEndPos) :
s.toSlice.posGT p h = (s.posGT p (by simpa)).toSlice := by
simp [posGT]
theorem posGT_eq_posGT_toSlice {s : String} {p : Pos.Raw} (h : p < s.rawEndPos) :
s.posGT p h = Pos.ofToSlice (s.toSlice.posGT p (by simpa)) := by
simp [posGT]
@[simp]
theorem Pos.posGE_offset {s : String} {p : s.Pos} : s.posGE p.offset (by simp) = p := by
simp [posGE_eq_iff, Pos.le_iff]
@[simp]
theorem offset_posGE_eq_self_iff {s : String} {p : String.Pos.Raw} {h} :
(s.posGE p h).offset = p p.IsValid s :=
fun h' => by simpa [h'] using (s.posGE p h).isValid,
fun h' => by simpa using congrArg Pos.offset (Pos.posGE_offset (p := s.pos p h'))
theorem posGE_eq_pos {s : String} {p : String.Pos.Raw} (h : p.IsValid s) :
s.posGE p h.le_rawEndPos = s.pos p h := by
simpa using Pos.posGE_offset (p := s.pos p h)
theorem pos_eq_posGE {s : String} {p : String.Pos.Raw} {h} :
s.pos p h = s.posGE p h.le_rawEndPos := by
simp [posGE_eq_pos h]
@[simp]
theorem Pos.posGT_offset {s : String} {p : s.Pos} {h} :
s.posGT p.offset h = p.next (by simpa using h) := by
rw [posGT_eq_iff, Pos.lt_iff]
simp [ Pos.lt_iff]
theorem posGT_eq_next {s : String} {p : String.Pos.Raw} {h} (h' : p.IsValid s) :
s.posGT p h = (s.pos p h').next (by simpa [Pos.ext_iff] using Pos.Raw.ne_of_lt h) := by
simpa using Pos.posGT_offset (h := h) (p := s.pos p h')
theorem next_eq_posGT {s : String} {p : s.Pos} {h} :
p.next h = s.posGT p.offset (by simpa) := by
simp
end String

View File

@@ -0,0 +1,109 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.String.Basic
import Init.Data.String.OrderInstances
import Init.Data.String.Lemmas.Basic
public section
namespace String
@[simp]
theorem Slice.Pos.next_le_iff_lt {s : Slice} {p q : s.Pos} {h} : p.next h q p < q :=
fun h => Std.lt_of_lt_of_le lt_next h, next_le_of_lt
@[simp]
theorem Slice.Pos.lt_next_iff_le {s : Slice} {p q : s.Pos} {h} : p < q.next h p q := by
rw [ Decidable.not_iff_not, Std.not_lt, next_le_iff_lt, Std.not_le]
@[simp]
theorem Pos.next_le_iff_lt {s : String} {p q : s.Pos} {h} : p.next h q p < q := by
rw [next, Pos.ofToSlice_le_iff, Pos.toSlice_lt_toSlice_iff]
exact Slice.Pos.next_le_iff_lt
@[simp]
theorem Slice.Pos.le_startPos {s : Slice} (p : s.Pos) : p s.startPos p = s.startPos :=
fun h => Std.le_antisymm h (startPos_le _), by simp +contextual
@[simp]
theorem Slice.Pos.startPos_lt_iff {s : Slice} (p : s.Pos) : s.startPos < p p s.startPos := by
simp [ le_startPos, Std.not_le]
@[simp]
theorem Slice.Pos.endPos_le {s : Slice} (p : s.Pos) : s.endPos p p = s.endPos :=
fun h => Std.le_antisymm (le_endPos _) h, by simp +contextual
@[simp]
theorem Pos.le_startPos {s : String} (p : s.Pos) : p s.startPos p = s.startPos :=
fun h => Std.le_antisymm h (startPos_le _), by simp +contextual
@[simp]
theorem Pos.endPos_le {s : String} (p : s.Pos) : s.endPos p p = s.endPos :=
fun h => Std.le_antisymm (le_endPos _) h, by simp +contextual [Std.le_refl]
@[simp]
theorem Slice.Pos.not_lt_startPos {s : Slice} {p : s.Pos} : ¬ p < s.startPos :=
fun h => Std.lt_irrefl (Std.lt_of_lt_of_le h (Slice.Pos.startPos_le _))
theorem Slice.Pos.ne_startPos_of_lt {s : Slice} {p q : s.Pos} : p < q q s.startPos := by
rintro h rfl
simp at h
@[simp]
theorem Slice.Pos.next_ne_startPos {s : Slice} {p : s.Pos} {h} :
p.next h s.startPos :=
ne_startPos_of_lt lt_next
theorem Slice.Pos.ofSliceFrom_lt_ofSliceFrom_iff {s : Slice} {p : s.Pos}
{q r : (s.sliceFrom p).Pos} : Slice.Pos.ofSliceFrom q < Slice.Pos.ofSliceFrom r q < r := by
simp [Slice.Pos.lt_iff, Pos.Raw.lt_iff]
theorem Slice.Pos.ofSliceFrom_le_ofSliceFrom_iff {s : Slice} {p : s.Pos}
{q r : (s.sliceFrom p).Pos} : Slice.Pos.ofSliceFrom q Slice.Pos.ofSliceFrom r q r := by
simp [Slice.Pos.le_iff, Pos.Raw.le_iff]
@[simp]
theorem Slice.Pos.offset_le_rawEndPos {s : Slice} {p : s.Pos} :
p.offset s.rawEndPos :=
p.isValidForSlice.le_rawEndPos
@[simp]
theorem Pos.offset_le_rawEndPos {s : String} {p : s.Pos} :
p.offset s.rawEndPos :=
p.isValid.le_rawEndPos
@[simp]
theorem Slice.Pos.offset_lt_rawEndPos_iff {s : Slice} {p : s.Pos} :
p.offset < s.rawEndPos p s.endPos := by
simp [Std.lt_iff_le_and_ne, p.offset_le_rawEndPos, Pos.ext_iff]
@[simp]
theorem Pos.offset_lt_rawEndPos_iff {s : String} {p : s.Pos} :
p.offset < s.rawEndPos p s.endPos := by
simp [Std.lt_iff_le_and_ne, p.offset_le_rawEndPos, Pos.ext_iff]
@[simp]
theorem Slice.Pos.getUTF8Byte_offset {s : Slice} {p : s.Pos} {h} :
s.getUTF8Byte p.offset h = p.byte (by simpa using h) := by
simp [Pos.byte]
theorem Slice.Pos.isUTF8FirstByte_getUTF8Byte_offset {s : Slice} {p : s.Pos} {h} :
(s.getUTF8Byte p.offset h).IsUTF8FirstByte := by
simpa [getUTF8Byte_offset] using isUTF8FirstByte_byte
theorem Pos.getUTF8Byte_offset {s : String} {p : s.Pos} {h} :
s.getUTF8Byte p.offset h = p.byte (by simpa using h) := by
simp only [getUTF8Byte_eq_getUTF8Byte_toSlice, Pos.offset_toSlice,
Slice.Pos.getUTF8Byte_offset, byte_toSlice]
theorem Pos.isUTF8FirstByte_getUTF8Byte_offset {s : String} {p : s.Pos} {h} :
(s.getUTF8Byte p.offset h).IsUTF8FirstByte := by
simpa [getUTF8Byte_offset] using isUTF8FirstByte_byte
end String

View File

@@ -8,6 +8,7 @@ module
prelude
public import Init.Data.String.Basic
public import Init.Data.Iterators.Basic
public import Init.Data.Iterators.Consumers.Loop
set_option doc.verso true
@@ -129,23 +130,24 @@ variable [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
variable (pat : ρ) [ToForwardSearcher pat σ]
@[specialize pat]
def defaultStartsWith (s : Slice) : Bool :=
def defaultStartsWith (s : Slice) [Std.IteratorLoop (σ s) Id Id] : Bool :=
let searcher := ToForwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched start ..) _ => s.startPos = start
match searcher.first? with
| some (.matched start ..) => s.startPos = start
| _ => false
@[specialize pat]
def defaultDropPrefix? (s : Slice) : Option s.Pos :=
def defaultDropPrefix? (s : Slice) [Std.IteratorLoop (σ s) Id Id] : Option s.Pos :=
let searcher := ToForwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched _ endPos) _ => some endPos
match searcher.first? with
| some (.matched _ endPos) => some endPos
| _ => none
@[always_inline, inline]
def defaultImplementation {pat : ρ} [ToForwardSearcher pat σ] : ForwardPattern pat where
startsWith := defaultStartsWith pat
dropPrefix? := defaultDropPrefix? pat
def defaultImplementation {pat : ρ} [ToForwardSearcher pat σ] [ s, Std.IteratorLoop (σ s) Id Id] :
ForwardPattern pat where
startsWith s := defaultStartsWith pat s
dropPrefix? s := defaultDropPrefix? pat s
end ForwardPattern
@@ -188,23 +190,24 @@ variable [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
variable (pat : ρ) [ToBackwardSearcher pat σ]
@[specialize pat]
def defaultEndsWith (s : Slice) : Bool :=
def defaultEndsWith (s : Slice) [Std.IteratorLoop (σ s) Id Id] : Bool :=
let searcher := ToBackwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched _ endPos) _ => s.endPos = endPos
match searcher.first? with
| some (.matched _ endPos) => s.endPos = endPos
| _ => false
@[specialize pat]
def defaultDropSuffix? (s : Slice) : Option s.Pos :=
def defaultDropSuffix? (s : Slice) [Std.IteratorLoop (σ s) Id Id] : Option s.Pos :=
let searcher := ToBackwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched startPos _) _ => some startPos
match searcher.first? with
| some (.matched startPos _) => some startPos
| _ => none
@[always_inline, inline]
def defaultImplementation {pat : ρ} [ToBackwardSearcher pat σ] : BackwardPattern pat where
endsWith := defaultEndsWith pat
dropSuffix? := defaultDropSuffix? pat
def defaultImplementation {pat : ρ} [ToBackwardSearcher pat σ] [ s, Std.IteratorLoop (σ s) Id Id] :
BackwardPattern pat where
endsWith s := defaultEndsWith pat s
dropSuffix? s := defaultDropSuffix? pat s
end ToBackwardSearcher

View File

@@ -8,8 +8,10 @@ module
prelude
public import Init.Data.String.Pattern.Basic
public import Init.Data.Iterators.Consumers.Monadic.Loop
import Init.Data.String.Termination
public import Init.Data.Vector.Basic
public import Init.Data.String.FindPos
import Init.Data.String.Termination
import Init.Data.String.Lemmas.FindPos
set_option doc.verso true
@@ -148,15 +150,15 @@ instance (s : Slice) : Std.Iterator (ForwardSliceSearcher s) Id (SearchStep s) w
let basePos := s.pos! stackPos
-- Since we report (mis)matches by code point and not by byte, missing in the first byte
-- means that we should skip ahead to the next code point.
let nextStackPos := s.findNextPos stackPos h₁
let nextStackPos := s.posGT stackPos h₁
let res := .rejected basePos nextStackPos
-- Invariants still satisfied
pure (.deflate .yield .proper needle table htable nextStackPos.offset 0
(by simp [Pos.Raw.lt_iff] at hn ; omega) res,
by simpa using _, _, rfl, rfl, by simp [Pos.Raw.lt_iff] at hn ; omega,
Or.inl (by
have := lt_offset_findNextPos h₁
have t₀ := (findNextPos _ _ h₁).isValidForSlice.le_utf8ByteSize
have := lt_offset_posGT (h := h₁)
have t₀ := (posGT _ _ h₁).isValidForSlice.le_utf8ByteSize
simp [nextStackPos, Pos.Raw.lt_iff] at this ; omega))
else
let newNeedlePos := table[needlePos.byteIdx - 1]'(by simp [Pos.Raw.lt_iff] at hn; omega)
@@ -165,19 +167,16 @@ instance (s : Slice) : Std.Iterator (ForwardSliceSearcher s) Id (SearchStep s) w
let basePos := s.pos! (stackPos.unoffsetBy needlePos)
-- Since we report (mis)matches by code point and not by byte, missing in the first byte
-- means that we should skip ahead to the next code point.
let nextStackPos := (s.pos? stackPos).getD (s.findNextPos stackPos h₁)
let nextStackPos := s.posGE stackPos (Pos.Raw.le_of_lt h₁)
let res := .rejected basePos nextStackPos
-- Invariants still satisfied
pure (.deflate .yield .proper needle table htable nextStackPos.offset 0
(by simp [Pos.Raw.lt_iff] at hn ; omega) res,
by simpa using _, _, rfl, rfl, by simp [Pos.Raw.lt_iff] at hn ; omega, by
simp only [pos?, Pos.Raw.isValidForSlice_eq_true_iff, nextStackPos]
split
· exact Or.inr (by simp [Pos.Raw.lt_iff]; omega)
· refine Or.inl ?_
have := lt_offset_findNextPos h₁
have t₀ := (findNextPos _ _ h₁).isValidForSlice.le_utf8ByteSize
simp [Pos.Raw.lt_iff] at this ; omega)
have h₂ := le_offset_posGE (h := Pos.Raw.le_of_lt h₁)
have h₃ := (s.posGE _ (Pos.Raw.le_of_lt h₁)).isValidForSlice.le_utf8ByteSize
simp [Pos.Raw.le_iff, Pos.Raw.lt_iff, Pos.Raw.ext_iff, nextStackPos] at h₂
omega)
else
let oldBasePos := s.pos! (stackPos.decreaseBy needlePos.byteIdx)
let newBasePos := s.pos! (stackPos.decreaseBy newNeedlePos)
@@ -264,8 +263,7 @@ def startsWith (pat : Slice) (s : Slice) : Bool :=
have hs := by
simp [Pos.Raw.le_iff] at h
omega
have hp := by
simp [Pos.Raw.le_iff]
have hp := by simp
Internal.memcmpSlice s pat s.startPos.offset pat.startPos.offset pat.rawEndPos hs hp
else
false
@@ -301,7 +299,7 @@ def endsWith (pat : Slice) (s : Slice) : Bool :=
simp [sStart, Pos.Raw.le_iff] at h
omega
have hp := by
simp [patStart, Pos.Raw.le_iff] at h
simp [patStart] at h
Internal.memcmpSlice s pat sStart patStart pat.rawEndPos hs hp
else
false

View File

@@ -144,8 +144,11 @@ theorem Pos.Raw.offsetBy_assoc {p q r : Pos.Raw} :
@[simp]
theorem Pos.Raw.offsetBy_zero_left {p : Pos.Raw} : (0 : Pos.Raw).offsetBy p = p := by
ext
simp
simp [Pos.Raw.ext_iff]
@[simp]
theorem Pos.Raw.offsetBy_zero {p : Pos.Raw} : p.offsetBy 0 = p := by
simp [Pos.Raw.ext_iff]
/--
Decreases `p` by `offset`. This is not an `HSub` instance because it should be a relatively
@@ -174,6 +177,14 @@ theorem Pos.Raw.unoffsetBy_offsetBy {p q : Pos.Raw} : (p.offsetBy q).unoffsetBy
ext
simp
@[simp]
theorem Pos.Raw.unoffsetBy_zero {p : Pos.Raw} : p.unoffsetBy 0 = p := by
simp [Pos.Raw.ext_iff]
@[simp]
theorem Pos.Raw.unoffsetBy_le_self {p q : Pos.Raw} : p.unoffsetBy q p := by
simp [Pos.Raw.le_iff]
@[simp]
theorem Pos.Raw.eq_zero_iff {p : Pos.Raw} : p = 0 p.byteIdx = 0 :=
Pos.Raw.ext_iff
@@ -195,6 +206,10 @@ def Pos.Raw.increaseBy (p : Pos.Raw) (n : Nat) : Pos.Raw where
theorem Pos.Raw.byteIdx_increaseBy {p : Pos.Raw} {n : Nat} :
(p.increaseBy n).byteIdx = p.byteIdx + n := (rfl)
@[simp]
theorem Pos.Raw.increaseBy_zero {p : Pos.Raw} : p.increaseBy 0 = p := by
simp [Pos.Raw.ext_iff]
/--
Move the position `p` back by `n` bytes. This is not an `HSub` instance because it should be a
relatively rare operation, so we use a name to make accidental use less likely. To remove the size
@@ -212,6 +227,10 @@ def Pos.Raw.decreaseBy (p : Pos.Raw) (n : Nat) : Pos.Raw where
theorem Pos.Raw.byteIdx_decreaseBy {p : Pos.Raw} {n : Nat} :
(p.decreaseBy n).byteIdx = p.byteIdx - n := (rfl)
@[simp]
theorem Pos.Raw.decreaseBy_zero {p : Pos.Raw} : p.decreaseBy 0 = p := by
simp [Pos.Raw.ext_iff]
theorem Pos.Raw.increaseBy_charUtf8Size {p : Pos.Raw} {c : Char} :
p.increaseBy c.utf8Size = p + c := by
simp [Pos.Raw.ext_iff]
@@ -224,6 +243,10 @@ def Pos.Raw.inc (p : Pos.Raw) : Pos.Raw :=
@[simp]
theorem Pos.Raw.byteIdx_inc {p : Pos.Raw} : p.inc.byteIdx = p.byteIdx + 1 := (rfl)
@[simp]
theorem Pos.Raw.inc_unoffsetBy_inc {p q : Pos.Raw} : p.inc.unoffsetBy q.inc = p.unoffsetBy q := by
simp [Pos.Raw.ext_iff]
/-- Decreases the byte offset of the position by `1`. Not to be confused with `Pos.prev`. -/
@[inline, expose]
def Pos.Raw.dec (p : Pos.Raw) : Pos.Raw :=
@@ -235,12 +258,17 @@ theorem Pos.Raw.byteIdx_dec {p : Pos.Raw} : p.dec.byteIdx = p.byteIdx - 1 := (rf
@[simp]
theorem Pos.Raw.le_refl {p : Pos.Raw} : p p := by simp [le_iff]
@[simp]
theorem Pos.Raw.lt_inc {p : Pos.Raw} : p < p.inc := by simp [lt_iff]
theorem Pos.Raw.le_of_lt {p q : Pos.Raw} : p < q p q := by simpa [lt_iff, le_iff] using Nat.le_of_lt
@[simp]
theorem Pos.Raw.inc_le {p q : Pos.Raw} : p.inc q p < q := by simpa [lt_iff, le_iff] using Nat.succ_le_iff
@[simp]
theorem Pos.Raw.lt_inc_iff {p q : Pos.Raw} : p < q.inc p q := by simpa [lt_iff, le_iff] using Nat.lt_add_one_iff
theorem Pos.Raw.lt_of_le_of_lt {a b c : Pos.Raw} : a b b < c a < c := by
simpa [le_iff, lt_iff] using Nat.lt_of_le_of_lt

View File

@@ -66,7 +66,7 @@ The implementation is an efficient equivalent of {lean}`s1.copy == s2.copy`
-/
def beq (s1 s2 : Slice) : Bool :=
if h : s1.utf8ByteSize = s2.utf8ByteSize then
have h1 := by simp [h, String.Pos.Raw.le_iff]
have h1 := by simp
have h2 := by simp [h, String.Pos.Raw.le_iff]
Internal.memcmpSlice s1 s2 s1.startPos.offset s2.startPos.offset s1.rawEndPos h1 h2
else
@@ -905,9 +905,9 @@ Examples:
def chars (s : Slice) :=
Std.Iter.map (fun pos, h => pos.get h) (positions s)
@[deprecated "There is no constant-time length function on slices. Use `s.positions.count` instead, or `isEmpty` if you only need to know whether the slice is empty." (since := "2025-11-20")]
@[deprecated "There is no constant-time length function on slices. Use `s.positions.length` instead, or `isEmpty` if you only need to know whether the slice is empty." (since := "2025-11-20")]
def length (s : Slice) : Nat :=
s.positions.count
s.positions.length
structure RevPosIterator (s : Slice) where
currPos : s.Pos

View File

@@ -373,7 +373,7 @@ Examples:
* `(if (5 : UInt16) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : UInt16) < 7) by decide`
-/
@[extern "lean_uint16_dec_lt"]
@[extern "lean_uint16_dec_lt", instance_reducible]
def UInt16.decLt (a b : UInt16) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
@@ -390,7 +390,7 @@ Examples:
* `(if (5 : UInt16) ≤ 15 then "yes" else "no") = "yes"`
* `show (7 : UInt16) ≤ 7 by decide`
-/
@[extern "lean_uint16_dec_le"]
@[extern "lean_uint16_dec_le", instance_reducible]
def UInt16.decLe (a b : UInt16) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
@@ -737,7 +737,7 @@ Examples:
* `(if (5 : UInt64) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : UInt64) < 7) by decide`
-/
@[extern "lean_uint64_dec_lt"]
@[extern "lean_uint64_dec_lt", instance_reducible]
def UInt64.decLt (a b : UInt64) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
@@ -753,7 +753,7 @@ Examples:
* `(if (5 : UInt64) ≤ 15 then "yes" else "no") = "yes"`
* `show (7 : UInt64) ≤ 7 by decide`
-/
@[extern "lean_uint64_dec_le"]
@[extern "lean_uint64_dec_le", instance_reducible]
def UInt64.decLe (a b : UInt64) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))

View File

@@ -397,7 +397,7 @@ Examples:
* `(if (5 : USize) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : USize) < 7) by decide`
-/
@[extern "lean_usize_dec_lt"]
@[extern "lean_usize_dec_lt", instance_reducible]
def USize.decLt (a b : USize) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
@@ -413,7 +413,7 @@ Examples:
* `(if (5 : USize) ≤ 15 then "yes" else "no") = "yes"`
* `show (7 : USize) ≤ 7 by decide`
-/
@[extern "lean_usize_dec_le"]
@[extern "lean_usize_dec_le", instance_reducible]
def USize.decLe (a b : USize) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))

View File

@@ -24,3 +24,5 @@ public import Init.Data.Vector.Perm
public import Init.Data.Vector.Find
public import Init.Data.Vector.Algebra
public import Init.Data.Vector.Stream
public import Init.Data.Vector.Nat
public import Init.Data.Vector.Int

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