Compare commits

...

205 Commits

Author SHA1 Message Date
Leonardo de Moura
b3b07f1b13 test: 2026-01-11 18:14:27 -08:00
Leonardo de Moura
1e01ed29c9 fix: type check pattern vars 2026-01-11 18:11:00 -08:00
Leonardo de Moura
9cf9829ae9 fix: missing check 2026-01-11 16:25:29 -08:00
Leonardo de Moura
14cf40d6d0 feat: helper functions for debugging 2026-01-11 16:25:03 -08:00
Leonardo de Moura
58e599f2f9 perf: optimize congruence proof construction in Sym.simp (#11974)
This PR optimizes congruence proof construction in `Sym.simp` by
avoiding
`inferType` calls on expressions that are less likely to be cached.
Instead of
inferring types of expressions like `@HAdd.hAdd Nat Nat Nat instAdd 5`,
we infer
the type of the function prefix `@HAdd.hAdd Nat Nat Nat instAdd` and
traverse
the forall telescope.

The key insight is that function prefixes are more likely shared across
many call sites
(e.g., all `Nat` additions use the same `@HAdd.hAdd Nat Nat Nat
instAdd`), so they
benefit from `inferType` caching. 

Benchmark results show improvements on workloads with shared function
prefixes:
- `many_rewrites_5000`: 48.8ms → 43.1ms (-12%)
- `term_tree_5000`: 53.4ms → 30.5ms (-43%)
2026-01-11 23:00:19 +00:00
Henrik Böving
c91a2c63c2 perf: fast paths for forEachWhere Expr.isFVar (#11973)
Add a fast path for the pattern `forEachWhere Expr.isFVar` to avoid
setting up the expression
traversal etc.

Pattern initially noticed by @Rob23oba
2026-01-11 22:38:16 +00:00
Leonardo de Moura
d7cbdebf0b chore: cleanup simp benchmark (#11971) 2026-01-11 19:55:39 +00:00
Sebastian Ullrich
28a5e9f93c chore: revert "fix: avoid panic in async elaboration for theorems with docstrings in where" (#11969)
Reverts leanprover/lean4#11896 as it is not a principled fix
2026-01-11 10:26:10 +00:00
Leonardo de Moura
470498cc06 chore: cleanup Sym.simp (#11968) 2026-01-11 04:11:31 +00:00
Leonardo de Moura
d57f71c1c0 perf: optimize kernel type-checking for have-telescope simplification in Sym.simp (#11967)
This PR implements a new strategy for simplifying `have`-telescopes in
`Sym.simp` that achieves linear kernel type-checking time instead of
quadratic.

## Problem

When simplifying deep `have`-telescopes, the previous approach using
`have_congr'` produced proofs that type-checked in quadratic time. The
simplifier itself was fast, but the kernel became the bottleneck for
large telescopes.

For example, at n=100:
- **Before**: simp = 2.4ms, kernel = **225ms**
- **After**: simp = 3.5ms, kernel = **10ms**

The quadratic behavior occurred because the kernel creates fresh free
variables for each binder when type-checking, destroying sharing and
producing O(n²) intermediate terms.

## Solution

We transform sequential `have`-telescopes into a parallel
beta-application form:

```
have x₁ := v₁; have x₂ := v₂[x₁]; b[x₁, x₂]
  ↓ (definitionally equal)
(fun x₁ x₂' => b[x₁, x₂' x₁]) v₁ (fun x₁ => v₂[x₁])
```

This parallel form leverages the efficient simplifier for lambdas in
`Sym.simp`. This form enables:
1. Independent simplification of each argument
2. Proof construction using standard congruence lemmas
3. Linear kernel type-checking time

The algorithm has three phases:
1. **`toBetaApp`**: Transform telescope → parallel beta-application
2. **`simpBetaApp`**: Simplify using `congr`/`congrArg`/`congrFun'` and
`simpLambda`
3. **`toHave`**: Convert back to `have` form

## Benchmark Results

### Benchmark 1: Chain with all variables used in body

| n | Before (simp) | Before (kernel) | After (simp) | After (kernel) |
|---|---------------|-----------------|--------------|----------------|
| 50 | 1.2ms | 32ms | 1.6ms | 4.4ms |
| 100 | 2.4ms | **225ms** | 3.5ms | **10ms** |
| 200 | 4.5ms | — | 8.4ms | 27ms |
| 500 | 11.7ms | — | 33.6ms | 128ms |

### Benchmark 3: Parallel declarations (simplified values)

| n | Before (simp) | Before (kernel) | After (simp) | After (kernel) |
|---|---------------|-----------------|--------------|----------------|
| 50 | 0.5ms | 24ms | 0.8ms | 1.8ms |
| 100 | 1.2ms | **169ms** | 1.8ms | **5.3ms** |
| 200 | 2.2ms | — | 3.9ms | 17ms |
| 500 | 5.9ms | — | 12.3ms | 93ms |

### Benchmark 5: Chain with single dependency

| n | Before (simp) | Before (kernel) | After (simp) | After (kernel) |
|---|---------------|-----------------|--------------|----------------|
| 100 | 1.6ms | 6.2ms | 1.8ms | 6.2ms |
| 200 | 2.8ms | 21.6ms | 4.4ms | 16.5ms |
| 500 | 7.3ms | **125ms** | 12.8ms | **72ms** |

Key observations:
- Kernel time is now **linear** in telescope depth (previously
quadratic)
- Simp time increases slightly due to the transformation overhead
- Total time (simp + kernel) is dramatically reduced for large
telescopes
- The improvement is most pronounced when the body depends on many
variables

## Trade-offs

- Proof sizes are larger (more congruence lemma applications)
- Simp time has ~1.5x overhead from the transformation
- For very small telescopes (n < 10), the overhead may not pay off

The optimization targets the critical path: kernel type-checking was the
bottleneck preventing scaling to realistic symbolic simulation
workloads.
2026-01-11 02:20:47 +00:00
Sebastian Ullrich
eaf8cf15ff test: add leanchecker benchmark (#11959) 2026-01-10 20:52:11 +00:00
Leonardo de Moura
cae739c27c test: implies vs Arrow Sym.simp benchmark (#11966) 2026-01-10 18:51:54 +00:00
Kim Morrison
9280a0ba9e fix: avoid panic in async elaboration for theorems with docstrings in where (#11896)
This PR fixes a panic that occurred when a theorem had a docstring on an
auxiliary definition within a `where` clause.

Reproducer:
```lean
theorem foo : True := aux where /-- -/ aux := True.intro
```

The issue was that `asyncMayModify` used `.any` to check if a nested
declaration could have its extension state modified, which returned
`false` when the declaration wasn't yet in `asyncConsts`. Using `.all`
instead returns `true` for `none` (vacuously true), allowing
modification
of extension state for nested declarations that haven't been added to
`asyncConsts` yet.

Closes #11799

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 09:39:31 +00:00
Kim Morrison
e42262e397 fix: allow private proof-valued structure fields in library suggestions (#11962)
This PR fixes library suggestions to include private proof-valued
structure fields.

Private proof-valued structure fields (like `private size_keys' :
keys.size = values.size`) generate projections with `_private.*` mangled
names. These were being filtered out by `isDeniedPremise` because
`isInternalDetail` returns true for names starting with `_`.

The fix allows private names through by checking `!isPrivateName name`,
following the pattern from #11946. This enables `grind +suggestions` to
discover and use private proof-valued structure fields from the current
module.

Soon I would like to fix the semantics of `isInternalDetail`, as the
current behaviour is clearly wrong, but as there are many call sites, I
would like to get the behaviour of tactics correct first.

Also switches `currentFile` to use `wasOriginallyTheorem` instead of
matching on `.thmInfo`, which correctly identifies both theorems and
proof-valued projections.

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 08:40:54 +00:00
Kim Morrison
a96ae4bb12 chore: add CI log retrieval guidance to CLAUDE.md (#11964)
This PR adds guidance for investigating CI failures promptly rather than
waiting for other jobs to complete.

---
🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 07:41:41 +00:00
Kim Morrison
14039942f3 refactor: derive BEq for Option earlier in import chain (#11960)
This PR moves the `deriving instance BEq for Option` from
`Init.Data.Option.Basic` to `Init.Core`, making `BEq (Option α)`
available earlier in the import chain.

This is preparatory work for adding `maxSuggestions : Option Nat` fields
to `Grind.Config` and `Simp.Config`, which need `BEq (Option Nat)` for
the `deriving BEq` clause.

The duplicate derivation in `Init.Data.Option.Basic` is kept because
proofs there need the definition to be exposed.

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 03:50:15 +00:00
Kim Morrison
5bb7f37645 feat: add first_par combinator for try? with grind +locals (#11949)
This PR adds a new `first_par` tactic combinator that runs multiple
tactics in parallel and returns the first successful result (cancelling
the others).

The `try?` tactic's `atomicSuggestions` step now uses `first_par` to try
three grind variants in parallel:
- `grind? +suggestions` - uses library suggestion engine  
- `grind? +locals` - unfolds local definitions from current file
- `grind? +locals +suggestions` - combines both

This leverages `TacticM.parFirst` which already provides the "first
success wins" parallel execution with cancellation.

### Depends on
- [x] depends on: #11946

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 21:09:41 +00:00
Sebastian Graf
15a719cb36 chore: extract shared match splitting impl from FunInd and mvcgen (#11953)
This was an indepedently viable refactoring proposed by Joachim. It
fixes a bug in `mvcgen` exposed by the now reverted #11696.
2026-01-09 15:53:26 +00:00
Sebastian Graf
0f1eb1d0e5 chore: remove superfluous (generalizing := false) after revert of #11696 (#11952) 2026-01-09 10:14:18 +00:00
Lean stage0 autoupdater
e766839345 chore: update stage0 2026-01-09 09:33:31 +00:00
Sebastian Graf
22bef1c45a chore: revert "feat: abstract metavariables when generalizing match motives (#8099)" (#11941)
This PR reverts #11696.

Reopens #8099.
2026-01-09 08:24:03 +00:00
George Rennie
b771d12072 feat: Decidable instance for Nat.isPowerOfTwo (#11905)
This PR provides a `Decidable` instance for `Nat.isPowerOfTwo` based on
the formula `(n ≠ 0) ∧ (n &&& (n - 1)) = 0`.

To do this it includes theorems about `Nat.testBit` to show that the
`n.log2`th bit is set in `n` and `n - 1` for non powers of two.

Bitwise lemmas are needed to reason about the `&&&` so the file
`Init.Data.Nat.Power2` is renamed to `Init.Data.Nat.Power2.Basic` and
`Init.Data.Nat.Power2.Lemmas` introduced that depends on
`Init.Data.Nat.Bitwise.Lemmas` to prevent circular includes.

---------

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2026-01-09 07:51:41 +00:00
dependabot[bot]
214abb7eb2 chore: CI: bump actions/checkout from 5 to 6 (#11459)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to
6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>v6-beta by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2298">actions/checkout#2298</a></li>
<li>update readme/changelog for v6 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2311">actions/checkout#2311</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v5.0.0...v6.0.0">https://github.com/actions/checkout/compare/v5.0.0...v6.0.0</a></p>
<h2>v6-beta</h2>
<h2>What's Changed</h2>
<p>Updated persist-credentials to store the credentials under
<code>$RUNNER_TEMP</code> instead of directly in the local git
config.</p>
<p>This requires a minimum Actions Runner version of <a
href="https://github.com/actions/runner/releases/tag/v2.329.0">v2.329.0</a>
to access the persisted credentials for <a
href="https://docs.github.com/en/actions/tutorials/use-containerized-services/create-a-docker-container-action">Docker
container action</a> scenarios.</p>
<h2>v5.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v5...v5.0.1">https://github.com/actions/checkout/compare/v5...v5.0.1</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>V6.0.0</h2>
<ul>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
</ul>
<h2>V5.0.1</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<h2>V5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>V4.3.1</h2>
<ul>
<li>Port v6 cleanup to v4 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
</ul>
<h2>V4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable
version. by <a href="https://github.com/jww3"><code>@​jww3</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkout#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkout#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkout#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkout#1695</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1af3b93b68"><code>1af3b93</code></a>
update readme/changelog for v6 (<a
href="https://redirect.github.com/actions/checkout/issues/2311">#2311</a>)</li>
<li><a
href="71cf2267d8"><code>71cf226</code></a>
v6-beta (<a
href="https://redirect.github.com/actions/checkout/issues/2298">#2298</a>)</li>
<li><a
href="069c695914"><code>069c695</code></a>
Persist creds to a separate file (<a
href="https://redirect.github.com/actions/checkout/issues/2286">#2286</a>)</li>
<li><a
href="ff7abcd0c3"><code>ff7abcd</code></a>
Update README to include Node.js 24 support details and requirements (<a
href="https://redirect.github.com/actions/checkout/issues/2248">#2248</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/checkout/compare/v5...v6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=5&new-version=6)](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-01-09 07:43:13 +00:00
dependabot[bot]
76a734c907 chore: CI: bump softprops/action-gh-release from 2.4.1 to 2.5.0 (#11458)
Bumps
[softprops/action-gh-release](https://github.com/softprops/action-gh-release)
from 2.4.1 to 2.5.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/softprops/action-gh-release/releases">softprops/action-gh-release's
releases</a>.</em></p>
<blockquote>
<h2>v2.5.0</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: mark release as draft until all artifacts are uploaded by <a
href="https://github.com/dumbmoron"><code>@​dumbmoron</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/692">softprops/action-gh-release#692</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>chore(deps): bump the npm group across 1 directory with 5 updates by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/697">softprops/action-gh-release#697</a></li>
<li>chore(deps): bump actions/checkout from 5.0.0 to 5.0.1 in the
github-actions group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/689">softprops/action-gh-release#689</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/dumbmoron"><code>@​dumbmoron</code></a>
made their first contribution in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/692">softprops/action-gh-release#692</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/softprops/action-gh-release/compare/v2.4.2...v2.5.0">https://github.com/softprops/action-gh-release/compare/v2.4.2...v2.5.0</a></p>
<h2>v2.4.2</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: Ensure generated release notes cannot be over 125000
characters by <a
href="https://github.com/BeryJu"><code>@​BeryJu</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/684">softprops/action-gh-release#684</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>dependency updates</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/BeryJu"><code>@​BeryJu</code></a> made
their first contribution in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/684">softprops/action-gh-release#684</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/softprops/action-gh-release/compare/v2.4.1...v2.4.2">https://github.com/softprops/action-gh-release/compare/v2.4.1...v2.4.2</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md">softprops/action-gh-release's
changelog</a>.</em></p>
<blockquote>
<h2>2.5.0</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: mark release as draft until all artifacts are uploaded by <a
href="https://github.com/dumbmoron"><code>@​dumbmoron</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/692">softprops/action-gh-release#692</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>dependency updates</li>
</ul>
<h2>2.4.2</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: Ensure generated release notes cannot be over 125000
characters by <a
href="https://github.com/BeryJu"><code>@​BeryJu</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/684">softprops/action-gh-release#684</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>dependency updates</li>
</ul>
<h2>2.4.1</h2>
<h2>What's Changed</h2>
<h3>Other Changes 🔄</h3>
<ul>
<li>fix(util): support brace expansion globs containing commas in
parseInputFiles by <a
href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/672">softprops/action-gh-release#672</a></li>
<li>fix: gracefully fallback to body when body_path cannot be read by <a
href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/671">softprops/action-gh-release#671</a></li>
</ul>
<h2>2.4.0</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat(action): respect working_directory for files globs by <a
href="https://github.com/stephenway"><code>@​stephenway</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/667">softprops/action-gh-release#667</a></li>
</ul>
<h2>2.3.4</h2>
<h2>What's Changed</h2>
<h3>Bug fixes 🐛</h3>
<ul>
<li>fix(action): handle 422 already_exists race condition by <a
href="https://github.com/stephenway"><code>@​stephenway</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/665">softprops/action-gh-release#665</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a06a81a03e"><code>a06a81a</code></a>
release 2.5.0</li>
<li><a
href="7da8983734"><code>7da8983</code></a>
feat: mark release as draft until all artifacts are uploaded (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/692">#692</a>)</li>
<li><a
href="87973286a4"><code>8797328</code></a>
chore(deps): bump actions/checkout in the github-actions group (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/689">#689</a>)</li>
<li><a
href="1bfc62a71b"><code>1bfc62a</code></a>
chore(deps): bump the npm group across 1 directory with 5 updates (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/697">#697</a>)</li>
<li><a
href="5be0e66d93"><code>5be0e66</code></a>
release 2.4.2</li>
<li><a
href="af658b4d5d"><code>af658b4</code></a>
feat: Ensure generated release notes cannot be over 125000 characters
(<a
href="https://redirect.github.com/softprops/action-gh-release/issues/684">#684</a>)</li>
<li><a
href="237aaccf71"><code>237aacc</code></a>
chore: bump node to 24.11.0</li>
<li><a
href="00362bea6f"><code>00362be</code></a>
chore(deps): bump the npm group with 5 updates (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/687">#687</a>)</li>
<li><a
href="0adea5aa98"><code>0adea5a</code></a>
chore(deps): bump the npm group with 3 updates (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/686">#686</a>)</li>
<li><a
href="aa05f9d779"><code>aa05f9d</code></a>
chore(deps): bump actions/setup-node from 5.0.0 to 6.0.0 in the
github-action...</li>
<li>Additional commits viewable in <a
href="6da8fa9354...a06a81a03e">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=softprops/action-gh-release&package-manager=github_actions&previous-version=2.4.1&new-version=2.5.0)](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-01-09 07:42:17 +00:00
dependabot[bot]
f65fe51630 chore: CI: bump namespacelabs/nscloud-checkout-action from 7 to 8 (#11457)
Bumps
[namespacelabs/nscloud-checkout-action](https://github.com/namespacelabs/nscloud-checkout-action)
from 7 to 8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/namespacelabs/nscloud-checkout-action/releases">namespacelabs/nscloud-checkout-action's
releases</a>.</em></p>
<blockquote>
<h2>v8.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Rework checkout flow to precisely control what is fetched and
checked out. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/22">namespacelabs/nscloud-checkout-action#22</a></li>
<li>Log all mirrored refs in debug mode. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/23">namespacelabs/nscloud-checkout-action#23</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/namespacelabs/nscloud-checkout-action/compare/v7.1.0...v8.0.0">https://github.com/namespacelabs/nscloud-checkout-action/compare/v7.1.0...v8.0.0</a></p>
<h2>v7.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Correctly check out merge branches for PRs. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/21">namespacelabs/nscloud-checkout-action#21</a></li>
<li>Set up remote.origin.fetch to allow fetching any branch. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/21">namespacelabs/nscloud-checkout-action#21</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
made their first contribution in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/21">namespacelabs/nscloud-checkout-action#21</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/namespacelabs/nscloud-checkout-action/compare/v7...v7.1.0">https://github.com/namespacelabs/nscloud-checkout-action/compare/v7...v7.1.0</a></p>
<h2>V7.0.1</h2>
<p>No release notes provided.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0c5e6ce59a"><code>0c5e6ce</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/25">#25</a>
from namespacelabs/fix-doing-lfs-checkouts-on-macos</li>
<li><a
href="135cb5f92a"><code>135cb5f</code></a>
Fix doing lfs checkouts on macos</li>
<li><a
href="2716e107fb"><code>2716e10</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/24">#24</a>
from namespacelabs/kirill/prune</li>
<li><a
href="700dc55af6"><code>700dc55</code></a>
Prune references in the mirror when fetching.</li>
<li><a
href="28e5665c3c"><code>28e5665</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/23">#23</a>
from namespacelabs/kirill/all</li>
<li><a
href="e03456f7c8"><code>e03456f</code></a>
Log all mirrored refs in debug mode.</li>
<li><a
href="253d47cfc0"><code>253d47c</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/22">#22</a>
from namespacelabs/kirill/init</li>
<li><a
href="f2df08a5f7"><code>f2df08a</code></a>
Rework checkout flow to precisely control what is fetched and checked
out.</li>
<li><a
href="65866b8ec2"><code>65866b8</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/21">#21</a>
from namespacelabs/NSL-6774</li>
<li><a
href="ef7d6fcaeb"><code>ef7d6fc</code></a>
Correctly check out merge branches for PRs.</li>
<li>See full diff in <a
href="https://github.com/namespacelabs/nscloud-checkout-action/compare/v7...v8">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=namespacelabs/nscloud-checkout-action&package-manager=github_actions&previous-version=7&new-version=8)](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-01-09 07:41:50 +00:00
Rob23oba
e6d021967e fix: ensure that decide (xs = #[]) gets compiled to efficient code (#11945)
This PR changes the runtime implementation of the `Decidable (xs = #[])`
and `Decidable (#[] = xs)` instances to use `Array.isEmpty`. Previously,
`decide (xs = #[])` would first convert `xs` into a list and then
compare it against `List.nil`.
2026-01-09 07:30:37 +00:00
Alok Singh
4c360d50fa style: fix typos in Init/ and Std/ docstrings (#11864)
Typos in `Init/` and `Std/`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 07:24:07 +00:00
Alok Singh
821218aabd style: fix typos in Lake docstrings (#11867)
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 07:21:35 +00:00
Kim Morrison
7b1fb7ac9e feat: add simp +locals to include local definitions (#11947)
This PR adds a `+locals` configuration option to the `simp`, `simp_all`,
and `dsimp` tactics that automatically adds all definitions from the
current file to unfold.

Example usage:
```lean
def foo (n : Nat) : Nat := n + 1

-- Without +locals, simp doesn't know about foo
example (n : Nat) : foo n = n + 1 := by simp  -- fails

-- With +locals, simp can unfold foo
example (n : Nat) : foo n = n + 1 := by simp +locals  -- succeeds
```

The implementation iterates over `env.constants.map₂` (which contains
constants defined in the current module) and adds definitions to unfold.
Instance definitions and internal details are filtered out.

**Note:** For local theorems, use `+suggestions` instead, which will
include relevant local theorems via the library suggestion engine.

🤖 Prepared with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 07:19:40 +00:00
Kim Morrison
cd632b033d feat: add grind +locals to include local definitions (#11946)
This PR adds a `+locals` configuration option to the `grind` tactic that
automatically adds all definitions from the current file as e-match
theorems. This provides a convenient alternative to manually adding
`[local grind]` attributes to each definition. In the form `grind?
+locals`, it is also helpful for discovering which local declarations it
may be useful to add `[local grind]` attributes to.

Example usage:
```lean
def foo (n : Nat) : Nat := n + 1

-- Without +locals, grind doesn't know about foo
example (n : Nat) : foo n = n + 1 := by grind  -- fails

-- With +locals, grind can use the equation
example (n : Nat) : foo n = n + 1 := by grind +locals  -- succeeds
```

Instance definitions and internal details are filtered out.

🤖 Prepared with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 07:19:32 +00:00
Leonardo de Moura
d92cdae8e9 feat: simpForall and simpArrow in Sym.simp (#11950)
This PR implements `simpForall` and `simpArrow` in `Sym.simp`.
2026-01-09 06:20:04 +00:00
David Thrane Christiansen
7d5a96941e doc: add missing docstrings to iterator library (#11912)
This PR adds missing docstrings for parts of the iterator library, which
removes warnings and empty content in the manual.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2026-01-08 19:25:39 +00:00
Sebastian Ullrich
b4cf6b02b9 chore: shake: explain indirect uses as well (#11944) 2026-01-08 16:42:13 +00:00
Sebastian Ullrich
ea7c740ad4 fix: ctor visibility in mutual public inductives (#11940)
This PR fixes module system visibiltity issues when trying to declare a
public inductive inside a mutual block.

Fixes #11115
2026-01-08 14:25:25 +00:00
Sebastian Ullrich
aa8fa47321 chore: attributes do not have to be tracked as public uses (#11939) 2026-01-08 13:39:10 +00:00
Henrik Böving
7e6365567f refactor: preparatory change from structure to inductive on LCNF (#11934) 2026-01-08 09:56:41 +00:00
Sebastian Ullrich
1361d733a6 feat: re-integrate lean4checker as leanchecker (#11887)
This PR makes the external checker lean4checker available as the
existing `leanchecker` binary already known to elan, allowing for
out-of-the-box access to it.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 09:41:33 +00:00
Kim Morrison
0ad15fe982 refactor: add message log capture helpers for tactic evaluation (#11933)
This PR adds utility functions for managing the message log during
tactic
evaluation, and refactors existing code to use them.

**New helpers in `Lean.Elab.Tactic`:**
- `withSuppressedMessages`: executes an action while suppressing new
messages
- `withCapturedMessages`: executes an action and returns any new
messages
- `hasErrorMessages`: checks if a message list contains errors

**Refactored to use these helpers:**
- `LibrarySearch.tryDischarger`: now uses `withSuppressedMessages`
- `Try.evalAndSuggest`: now uses `withSuppressedMessages`
- `Try.evalAndSuggestWithBy`: now uses `withSuppressedMessages`

These helpers provide a standard pattern for tactic validation that
needs to
inspect error messages (e.g., filtering out suggestions that produce
errors).

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 05:25:36 +00:00
Leonardo de Moura
531dbf0e1b perf: mkFunExtFor (#11932)
This PR eliminates super-linear kernel type checking overhead when
simplifying lambda expressions. I improved the proof term produced by
`mkFunext`. This function is used in `Sym.simp` when simplifying lambda
expressions.

 ### Lambda benchmark: before vs after optimization

| Lambda | Before simp (ms) | After simp (ms) | Simp speedup | Before
kernel (ms) | After kernel (ms) | Kernel speedup | Before proof | After
proof | Proof reduction |

|--------|------------------|-----------------|--------------|--------------------|-------------------|----------------|--------------|-------------|-----------------|
| 10 | 0.269 | 0.208 | 1.29× | 0.521 | 0.390 | 1.34× | 583 | 498 | 1.17×
|
| 20 | 0.457 | 0.382 | 1.20× | 1.126 | 0.651 | 1.73× | 1323 | 918 |
1.44× |
| 30 | 0.747 | 0.536 | 1.39× | 1.733 | 0.789 | 2.20× | 2263 | 1338 |
1.69× |
| 40 | 0.819 | 0.697 | 1.18× | 2.696 | 1.065 | 2.53× | 3403 | 1758 |
1.94× |
| 50 | 1.035 | 0.901 | 1.15× | 3.918 | 1.304 | 3.01× | 4743 | 2178 |
2.18× |
| 100 | 2.351 | 1.823 | 1.29× | 20.073 | 2.927 | 6.86× | 14443 | 4278 |
3.38× |
| 150 | 3.920 | 2.873 | 1.36× | 60.266 | 5.290 | 11.39× | 29143 | 6378 |
4.57× |
| 200 | 5.869 | 3.819 | 1.54× | 148.681 | 6.903 | 21.54× | 48843 | 8478
| 5.76× |

We can now handle much larger lambda expressions. For example:

lambda_1000: 20.869250ms, kernel: 98.637875ms, proof_size=42078

This new approach will be implemented in `Meta.simp` in the future. Here
is the table with the `Meta.simp` numbers.

 ### Old `Meta.simp` lambda benchmark

| Lambda | Simp time (ms) | Kernel time (ms) | Proof size |
|--------|----------------|------------------|------------|
| 10  | 2.308 | 0.667 | 1273 |
| 20  | 5.739 | 1.817 | 3323 |
| 30  | 10.687 | 3.320 | 6173 |
| 40  | 17.607 | 6.326 | 9823 |
| 50  | 28.336 | 9.024 | 14273 |
| 100 | 137.878 | 34.344 | 48523 |
| 150 | 395.429 | 77.329 | 102773 |
| 200 | 866.097 | 143.020 | 177023 |
2026-01-08 04:28:58 +00:00
Michael Rothgang
2e649e16f0 fix: pretty-printing of unification hints (#11780)
This PR ensures that pretty-printing of unification hints inserts a
space after |- resp. ⊢.

All uses in Lean core and mathlib add a space after the |- or ⊢ symbol;
this makes the output match usage in practice.
This was discovered in leanprover-community/mathlib4#30658, adding a
formatting linter using pretty-printing as initial guide.
2026-01-08 01:46:25 +00:00
Leonardo de Moura
0e4794a1a9 test: benchmarks for lambda-telescopes (#11929) 2026-01-08 00:20:03 +00:00
Kim Morrison
975a81cdb8 feat: filter out deprecated lemmas from suggestions in exact?/rw? (#11918)
This PR filters deprecated lemmas from `exact?` and `rw?` suggestions.

Previously, both tactics would suggest deprecated lemmas, which could be
confusing for users since using the suggestion would trigger a
deprecation warning.

Now, lemmas marked with `@[deprecated]` are filtered out in the
`addImport` functions that populate the discrimination trees used by
these tactics.

**Example (before this PR):**
```lean
import Mathlib.Logic.Basic

example (h : ∃ n : Nat, n > 0) : True := by
  choose (n : Nat) (hn : n > 0 + 0) using h
  guard_hyp hn : n > 0  -- `rw?` would suggest `Eq.rec_eq_cast` which is deprecated
```

Zulip discussion:
https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/deprecated.20lemma.20from.20rw.3F/near/554106870

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:45:04 +00:00
Kim Morrison
f7de0c408f fix: improve error message for initialize with missing Nonempty instance (#11919)
This PR improves the error message when `initialize` (or `opaque`) fails
to find an `Inhabited` or `Nonempty` instance.

**Before:**
```
failed to synthesize
  Inhabited Foo
```

**After:**
```
failed to synthesize 'Inhabited' or 'Nonempty' instance for
  Foo

If this type is defined using the 'structure' or 'inductive' command, you can try adding a 'deriving Nonempty' clause to it.
```

Prompted by
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/initialize.20structure.20with.20IO.2ERef/near/564936030

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:44:33 +00:00
Kim Morrison
60cdda3c1e refactor: move simp/grind attributes for leftpad/rightpad to definition (#11928)
This PR moves the `@[simp, grind =]` attributes for `List.leftpad` and
`List.rightpad` from `Init.Data.List.Lemmas` to the point of definition
in `Init.Data.List.Basic`.

This makes the simp behavior discoverable at the definition site,
addressing the discoverability issue raised in [this Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/Finding.20the.20.60.40.5Bsimp.5D.60.20attribute/near/566714920).

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:43:05 +00:00
Leonardo de Moura
8484dbad5d test: benchmarks for have-telescopes (#11927) 2026-01-07 23:24:46 +00:00
Kim Morrison
b0ebfaa812 fix: ensure use of unsafeEIO is marked as unsafe (#11926)
This PR adds an `unsafe` modifier to an existing helper function user
`unsafeEIO`, and also leaves the function private.
2026-01-07 21:38:37 +00:00
Henrik Böving
c3cc61cdb4 feat: add a symbol gadget for non linear Array copies (#11916)
This PR adds a symbol to the runtime for marking `Array`
non-linearities. This should allow users to
spot them more easily in profiles or hunt them down using a debugger.
2026-01-07 13:08:45 +00:00
Leonardo de Moura
ff87bcb8e5 feat: add option for simplifying have decls in two passes (#11923)
This PR adds a new option to the function `simpHaveTelescope` in which
the `have` telescope is simplified in two passes:

* In the first pass, only the values and the body are simplified.
* In the second pass, unused declarations are eliminated.

This new mode eliminates **superlinear** behavior in the benchmark
`simp_3.lean`. Note that the kernel type checker still **exhibits**
quadratic behavior in this example, because it **does not have support**
for expanding a `have`/`let` telescope in a single step.
2026-01-07 01:58:36 +00:00
Kim Morrison
a6ed0d640d feat: add #guard_panic command and substring option for #guard_msgs (#11908)
This PR adds two features to the message testing commands:

## `#guard_panic` command

A new `#guard_panic` command that succeeds if the nested command
produces a panic message. Unlike `#guard_msgs`, it does not check the
exact message content, only that a panic occurred.

This is useful for testing commands that are expected to panic, where
the exact panic message text may be volatile. It is particularly useful
when minimizing a panic discovered "in the wild", while ensuring the
panic behaviour is preserved.

## `substring := true` option for `#guard_msgs`

Adds a `substring := true` option to `#guard_msgs` that checks if the
docstring appears as a substring of the output (after whitespace
normalization), rather than requiring an exact match. This is useful
when you only care about part of the message.

Example:
```lean
/-- Unknown identifier -/
#guard_msgs (substring := true) in
example : α := x
```

## Refactoring

Also refactors `runAndCollectMessages` as a shared helper function used
by both `#guard_msgs` and `#guard_panic`.

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 01:34:49 +00:00
Leonardo de Moura
8154453bb5 feat: simplify have blocks in Sym.simp (#11920)
This PR implements support for simplifying `have` telescopes in
`Sym.simp`.
2026-01-07 00:10:47 +00:00
Lean stage0 autoupdater
11e4e44be0 chore: update stage0 2026-01-06 21:42:26 +00:00
Leonardo de Moura
c871f66cfa refactor: have telescope support (#11914)
This PR factors out the `have`-telescope support used in `simp`, and
implements it using the `MonadSimp` interface. The goal is to
use this nice infrastructure for both `Meta.simp` and `Sym.simp`.
2026-01-06 19:20:25 +00:00
Anne Baanen
0f866236c7 doc: write a guideline for tactic docstrings (#11406)
This PR covers tactic docstrings in the documentation style guide.

At the Mathlib Initiative we want to ensure that tactics have good
documentation. Since this will involve adding documentation to tactics
built into core Lean, I discussed with David that we should write a
shared set of documentation guidelines that allow me to do my work both
on the Lean and on the Mathlib repositories.

I have already shown an earlier version of this guideline to David who
made some helpful suggestions but would be away for a few days. So to
make sure the discussion doesn't get lost, I've made a PR with the
version I ended up with after the first round of comments.

---------

Co-authored-by: Robert J. Simmons <442315+robsimmons@users.noreply.github.com>
2026-01-06 04:40:20 +00:00
Leonardo de Moura
f6c8b8d974 perf: replaceS and instantiateRevBetaS (#11911)
This PR minimizes the number of expression allocations performed by
`replaceS` and `instantiateRevBetaS`.
2026-01-06 03:03:01 +00:00
Leonardo de Moura
175661b6c3 refactor: reorganize SymM and GrindM monad hierarchy (#11909)
This PR reorganizes the monad hierarchy for symbolic computation in
Lean.

## Motivation

We want a clean layering where:
1. A foundational monad (`SymM`) provides maximally shared terms and
structural/syntactic `isDefEq`
2. `GrindM` builds on this foundation, adding E-graphs, congruence
closure, and decision procedures
3. Symbolic execution / VCGen uses `GrindM` directly without introducing
a third monad

## Changes

The core symbolic computation layer still lives in `Lean.Meta.Sym`. This
monad (`SymM`) provides:
- Maximally shared terms with pointer-based equality
- Structural/syntactic `isDefEq` and matching (no reduction, predictable
cost)
- Monotonic local contexts (no `revert` or `clear`), enabling O(1)
metavariable validation
- Efficient `intro`, `apply`, and `simp` implementations

The name "Sym" reflects that this is infrastructure for symbolic
computation: symbolic simulation, verification condition generation, and
decision procedures.

### Updated hierarchy

```
Lean.Meta.Sym   -- SymM: shared terms, syntactic isDefEq, intro, apply, simp
Lean.Meta.Grind -- GrindM: E-graphs, congruence closure (extends SymM)
```

Symbolic execution is a usage pattern of `GrindM` operating on
`Grind.Goal`, not a separate monad. This keeps the API surface minimal:
users learn two monads, and VCGen is "how you use `GrindM`" (for users
that want to use `grind`) rather than a third abstraction to understand.
2026-01-06 01:12:07 +00:00
Leonardo de Moura
fd88637948 perf: add PersistentHashMap.findKeyD and PersistentHashSet.findD (#11907)
This PR implements `PersistentHashMap.findKeyD` and
`PersistentHashSet.findD`. The motivation is avoid two memory
allocations (`Prod.mk` and `Option.some`) when the collections contains
the key.
2026-01-05 20:04:49 +00:00
Leonardo de Moura
7376772cbd perf: use update.*! at AlphaShareCommon (#11906)
This PR tries to minimize the number of expressions created at
`AlphaShareCommon`.
2026-01-05 19:11:01 +00:00
Kim Morrison
c358b0c734 feat: add guards for grind patterns for getElem?_eq_none theorems (#11761)
This PR adds some `grind_pattern` `guard` conditions to potentially
expensive theorems.
2026-01-05 08:55:02 +00:00
Kim Morrison
8207919728 chore: cleanup grind List tests (#11903)
Some of these tests were last investigated a long time ago: happily many
of the failing tests now work due to subsequent improvements to grind.
2026-01-05 05:02:33 +00:00
Kim Morrison
06b7b022b3 chore: cleanup some grind tests about palindromes (#11902) 2026-01-05 03:55:17 +00:00
Kim Morrison
460b3c3e43 fix: grind propagates 0 * a = 0 for CommSemiring (#11881)
This PR fixes an issue where `grind` failed to prove `f ≠ 0` from `f * r
≠ 0` when using `Lean.Grind.CommSemiring`, but succeeded with
`Lean.Grind.Semiring`.

The `propagateMul` propagator handles `0 * a = 0` and `a * 0 = 0` rules
for semirings that don't have full ring support in grind. Previously,
`CommSemiring` was excluded because it uses a ring envelope for
normalization, but that approach doesn't propagate these equalities back
to the original terms. Now `CommSemiring` also uses `propagateMul`.

Reported as
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Grind.20failure.20for.20CommSemiring.2C.20not.20Semiring

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 03:14:35 +00:00
Kim Morrison
b46688d683 feat: add Nat.gcd_left_comm and Int.gcd_left_comm (#11901)
This PR adds `gcd_left_comm` lemmas for both `Nat` and `Int`:

- `Nat.gcd_left_comm`: `gcd m (gcd n k) = gcd n (gcd m k)`
- `Int.gcd_left_comm`: `gcd a (gcd b c) = gcd b (gcd a c)`

These lemmas establish the left-commutativity property for gcd,
complementing the existing `gcd_comm` and `gcd_assoc` lemmas.

Upstreamed from
https://github.com/leanprover-community/mathlib4/pull/33235

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 03:12:50 +00:00
Leonardo de Moura
82f60a7ff3 feat: pre and post may return "done" in Sym.simp (#11900)
This PR adds a `done` flag to the result returned by `Simproc`s in
`Sym.simp`.

The `done` flag controls whether simplification should continue after
the result:
- `done = false` (default): Continue with subsequent simplification
steps
- `done = true`: Stop processing, return this result as final

## Use cases for `done = true`

### In `pre` simprocs
Skip simplification of certain subterms entirely:
```
def skipLambdas : Simproc := fun e =>
  if e.isLambda then return .rfl (done := true)
  else return .rfl
```

### In `post` simprocs
Perform single-pass normalization without recursive simplification:
```
def singlePassNormalize : Simproc := fun e =>
  if let some (e', h) ← tryNormalize e then
    return .step e' h (done := true)
  else return .rfl
```
With `done = true`, the result `e'` won't be recursively simplified.
2026-01-05 02:10:06 +00:00
Kim Morrison
6bf2486e13 chore: include comparator and lean4export in release process (#11899)
This PR includes https://github.com/leanprover/lean4export/ and
https://github.com/leanprover/comparator/ in the monthly release
workflow.
2026-01-05 01:08:50 +00:00
Leonardo de Moura
f1c903ca65 feat: simplify lambdas in Sym.simp (#11898)
This PR adds support for simplifying lambda expressions in `Sym.simp`.
It is much more efficient than standard simp for very large lambda
expressions with many binders. The key idea is to generate a custom
function extensionality theorem for the type of the lambda being
simplified.

This technique is compatible with the standard `simp` tactic, and will
be ported in a separate PR.

<img width="581" height="455" alt="image"
src="https://github.com/user-attachments/assets/5911dc6c-03f0-48ed-843b-b8cb4f67ee61"
/>

### `lambda` benchmark summary

| Lambda size | MetaM (ms) | SymM (ms) | Speedup |
|-------------|------------|-----------|---------|
| 50          | 22.7       | 0.74      | ~31×    |
| 100         | 120.5      | 1.75      | ~69×    |
| 150         | 359.6      | 2.90      | ~124×   |
| 200         | 809.5      | 4.51      | ~180×   |
2026-01-05 01:00:30 +00:00
Kim Morrison
35d8925c50 fix: avoid panic in TagDeclarationExtension.tag on partial elaboration (#11882)
This PR adds a guard to `TagDeclarationExtension.tag` to check if the
declaration name is anonymous and return early if so. This prevents a
panic that could occur when modifiers like `meta` or `noncomputable` are
used in combination with syntax errors.

Reproducer:
```lean
public meta section
def private
```

Previously this would panic with:
```
PANIC at Lean.EnvExtension.modifyState: called on `async` extension,
must set `asyncDecl` in that case
```

This follows the same pattern as the fix in #10131 for `addDocString`
and the existing guard in `markNotMeta`.

See
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/panic.20on.20doc-string/near/566110399

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 00:36:58 +00:00
Leonardo de Moura
9f404d8fbe chore: remove leftover (#11895) 2026-01-04 21:42:13 +00:00
Sebastian Ullrich
81c93aeae8 perf: ensure withTraceNodeBefore message is created lazily (#11893) 2026-01-04 20:38:39 +00:00
Leonardo de Moura
cf36ac986d perf: optimize simp congruence proofs (#11892)
This PR optimizes the construction on congruence proofs in `simp`.
It uses some of the ideas used in `Sym.simp`.
2026-01-04 19:37:21 +00:00
Leonardo de Moura
609d99e860 chore: include free variables (#11894)
This PR includes free variable in a `simp` benchmark to stress the
default `simp` matching procedure.
2026-01-04 18:51:18 +00:00
Leonardo de Moura
78c9a01bb2 feat: check Sym.simp thresholds (#11890)
This PR ensures that `Sym.simp` checks thresholds for maximum recursion
depth and maximum number of steps. It also invokes `checkSystem`.
Additionally, this PR simplifies the main loop. Assigned metavariables
and `zetaDelta` reduction are now handled by installing `pre`/`post`
methods.
2026-01-04 04:27:46 +00:00
Leonardo de Moura
a2cf78ac4a perf: Sym.Simp.DiscrTree retrieval (#11889)
This PR improves the discrimination tree retrieval performance used by
`Sym.simp`.
2026-01-04 03:51:56 +00:00
Leonardo de Moura
bc72487aed refactor: Sym.simp (#11888)
This PR refactors `Sym.simp` to make it more general and customizable.
It also moves the code
to its own subdirectory `Meta/Sym/Simp`.
2026-01-04 02:17:23 +00:00
Leonardo de Moura
b40dabdecd feat: add discrimination tree retrieval for Sym (#11886)
This PR adds `getMatch` and `getMatchWithExtra` for retrieving patterns
from
discrimination trees in the symbolic simulation framework. 
The PR also adds uses `DiscrTree` to implement indexing in `Sym.simp`.
2026-01-03 20:28:07 +00:00
Leonardo de Moura
19df2c41b3 feat: add insertPattern for discrimination tree insertion in Sym (#11884)
This PR adds discrimination tree support for the symbolic simulation
framework.
The new `DiscrTree.lean` module converts `Pattern` values into
discrimination
tree keys, treating proof/instance arguments and pattern variables as
wildcards
(`Key.star`). Motivation: efficient pattern retrieval during rewriting.
2026-01-03 19:27:43 +00:00
Henrik Böving
ce8fdb1aa7 chore: fix typo (#11883) 2026-01-03 11:36:50 +00:00
Kim Morrison
fab1897f28 feat: add with_unfolding_none tactic (#11880)
This PR adds a `with_unfolding_none` tactic that sets the transparency
mode to `.none`, in which no definitions are unfolded. This complements
the existing `with_unfolding_all` tactic and provides tactic-level
access to the `TransparencyMode.none` added in
https://github.com/leanprover/lean4/pull/11810.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 08:36:51 +00:00
Leonardo de Moura
3804a1df8d doc: structural matching and definitional equality (#11878)
This PR documents assumptions made by the symbolic simulation framework
regarding structural matching and definitional equality.
2026-01-02 21:47:16 +00:00
Leonardo de Moura
514a5fddc6 refactor: DiscrTree (#11875)
This PR adds the directory `Meta/DiscrTree` and reorganizes the code
into different files. Motivation: we are going to have new functions for
retrieving simplification theorems for the new structural simplifier.
2026-01-02 19:53:45 +00:00
Henrik Böving
d8f0507d2a perf: faster getLine (#11874)
This PR improves the performance of `getLine` by coalescing the locking
of the underlying `FILE*`.

Unfortunately we cannot use `getline` or `fgets` for this as our code
needs to handle `\0` chars
and Windows.
2026-01-02 19:08:18 +00:00
Henrik Böving
4eb5b5776d perf: inline IsUTF8FirstByte (#11872)
This PR marks IsUTF8FirstByte as inline.

I have a use case where it shows up significantly in the profile.
2026-01-02 11:21:54 +00:00
Sebastian Graf
6642061623 fix: make mvcgen with tac fail if tac fails on one of the VCs (#11871)
This PR makes `mvcgen with tac` fail if `tac` fails on one of the VCs,
just as `induction ... with tac` fails if `tac` fails on one of the
goals. The old behavior can be recovered by writing `mvcgen with try
tac` instead.
2026-01-02 10:52:25 +00:00
Leonardo de Moura
4e8b5cfc46 test: benchmark Sym and Meta simplifiers (#11870)
This PR adds simple benchmarks for comparing the `MetaM` and `SymM`
simplifiers. The `SymM` simplifier is still working in progress.

### Big picture across benchmarks

| Benchmark | MetaM scaling | SymM scaling | Speedup (approx.) |

|-------------------------|-------------------|--------------|-------------------|
| `trans_chain` | Linear | Linear | ~8–9× |
| `congr_arg_explosion` | Super-linear | Linear | ~100× |
| `many_rewrites` | Super-linear | Linear | ~10–16× |

<img width="598" height="455" alt="image"
src="https://github.com/user-attachments/assets/8bd9021b-b9cf-4fc0-aab4-3118d87f7c22"
/>

<img width="644" height="455" alt="image"
src="https://github.com/user-attachments/assets/0234dc11-0be7-441a-83b6-c309d20a2663"
/>

<img width="611" height="455" alt="image"
src="https://github.com/user-attachments/assets/df79d057-25ed-49d9-a8f3-5285e5fc7013"
/>
2026-01-02 03:59:54 +00:00
Leonardo de Moura
c07ee77d33 feat: add Meta.Context.cacheInferType (#11869)
This PR adds configuration flag `Meta.Context.cacheInferType`. You can
use it to disable the `inferType` cache at `MetaM`. We use this flag to
implement `SymM` because it has its own cache based on pointer equality.
2026-01-02 03:21:43 +00:00
Leonardo de Moura
b82f969e5b feat: add Sym.Simp.Theorem.rewrite? (#11868)
This PR implements `Sym.Simp.Theorem.rewrite?` for rewriting terms using
equational theorems in `Sym`.
2026-01-02 02:23:37 +00:00
Leonardo de Moura
97c23abf8e feat: main loop for Sym.simp (#11866)
This PR implements the core simplification loop for the `Sym` framework,
with efficient congruence-based argument rewriting.
2026-01-01 23:21:22 +00:00
Leonardo de Moura
ef9777ec0d feat: add getCongrInfo to Sym (#11860)
This PR adds `CongrInfo` analysis for function applications in the
symbolic simulator framework. `CongrInfo` determines how to build
congruence proofs for rewriting subterms efficiently, categorizing
functions into:

- `none`: no arguments can be rewritten (e.g., proofs)
- `fixedPrefix`: common case where implicit/instance arguments form a
fixed prefix and explicit arguments can be rewritten (e.g., `HAdd.hAdd`,
`Eq`)
- `interlaced`: rewritable and non-rewritable arguments alternate (e.g.,
`HEq`)
- `congrTheorem`: uses auto-generated congruence theorems for functions
with dependent proof arguments (e.g., `Array.eraseIdx`)
2026-01-01 17:27:08 +00:00
Henrik Böving
b7360969ed feat: bv_decide can handle structure fields with parametric width (#11858)
This PR changes `bv_decide`'s heuristic for what kinds of structures to
split on to also allow
splitting on structures where the fields have dependently typed widths.
For example:
```lean
structure Byte (w : Nat) where
  /-- A two's complement integer value of width `w`. -/
  val : BitVec w
  /-- A per-bit poison mask of width `w`. -/
  poison : BitVec w
```
This is to allow handling situations such as `(x : Byte 8)` where the
width becomes concrete after
splitting is done.
2026-01-01 13:36:33 +00:00
Leonardo de Moura
9b1b932242 feat: add shareCommonInc (#11857)
This PR adds an incremental variant of `shareCommon` for expressions
constructed from already-shared subterms. We use this when an expression
`e` was produced by a Lean API (e.g., `inferType`, `mkApp4`) that does
not preserve maximal sharing, but the inputs to that API were already
maximally shared. Unlike `shareCommon`, this function does not use a
local `Std.HashMap ExprPtr Expr` to track visited nodes. This is more
efficient when the number of new (unshared) nodes is small, which is the
common case when wrapping API calls that build a few constructor nodes
around shared inputs.
2026-01-01 05:40:33 +00:00
Leonardo de Moura
d4563a818f feat: simplifier for Sym (#11856)
This PR adds the basic infrastructure for the structural simplifier used
by the symbolic simulation (`Sym`) framework.
2026-01-01 04:34:50 +00:00
Paul Reichert
e8781f12c0 feat: use MonadAttach in the takeWhileM and dropWhileM iterator combinators (#11852)
This PR changes the definition of the iterator combinators `takeWhileM`
and `dropWhileM` so that they use `MonadAttach`. This is only relevant
in rare cases, but makes it sometimes possible to prove such combinators
finite when the finiteness depends on properties of the monadic
predicate.
2025-12-31 12:38:21 +00:00
Leonardo de Moura
1ca4faae18 fix: Sym.intro for have-declarations (#11851)
This PR fixes `Sym/Intro.lean` support for `have`-declarations.
2025-12-31 01:36:23 +00:00
Leonardo de Moura
3a5887276c fix: handle assigned metavariables during pattern matching (#11850)
This PR fixes a bug in the new pattern matching procedure for the Sym
framework. It was not correctly handling assigned metavariables during
pattern matching.

It also improves the support for free variables.
2025-12-31 00:50:55 +00:00
Leonardo de Moura
e086b9b5c6 fix: zetaDelta at Sym/Pattern.lean (#11849)
This PR fixes missing zetaDelta support at the pattern
matching/unification procedure in the new Sym framework.
2025-12-30 23:47:22 +00:00
Leonardo de Moura
16ae74e98e fix: bug at Name.beq (#11848)
This PR fixes a bug at `Name.beq` reported by
gasstationcodemanager@gmail.com
2025-12-30 18:22:47 +00:00
Henrik Böving
2a28cd98fc feat: allow bv_decide users to configure the SAT solver (#11847)
This PR adds a new `solverMode` field to `bv_decide`'s configuration,
allowing users to configure
the SAT solver for different kinds of workloads.
2025-12-30 13:17:20 +00:00
Leonardo de Moura
bba35e4532 perf: add performance comparison tests for SymM vs MetaM (#11838)
This PR adds performance comparison tests between the new `SymM` monad
and the standard `MetaM` for `intros`/`apply` operations.

The tests solve problems of the form:
```lean
let z := 0; ∀ x, ∃ y, x = z + y ∧ let z := z + x; ∀ x, ∃ y, x = z + y ∧ ... ∧ True
```
using repeated `intros` and `apply` with `Exists.intro`, `And.intro`,
`Eq.refl`, and `True.intro`.

**Results show 10-20x speedup:**

| Size | MetaM | SymM | Speedup |
|------|-------|------|---------|
| 1000 | 226ms | 21ms | 10.8x |
| 2000 | 582ms | 44ms | 13.2x |
| 3000 | 1.08s | 72ms | 15.0x |
| 4000 | 1.72s | 101ms | 17.0x |
| 5000 | 2.49s | 125ms | 19.9x |
| 6000 | 3.45s | 157ms | 22.0x |
2025-12-30 02:42:04 +00:00
Leonardo de Moura
17581a2628 feat: add backward chaining rule application to Sym (#11837)
This PR adds `BackwardRule` for efficient goal transformation via
backward chaining in `SymM`.

`BackwardRule` stores a theorem expression, precomputed pattern for
fast unification, and argument indices that become new subgoals. The
subgoal ordering lists non-dependent goals first to match the behavior
of `MetaM.apply`.

`BackwardRule.apply` unifies the goal type with the rule's pattern,
assigns the goal metavariable to the theorem application, and returns
new subgoals for unassigned arguments.
2025-12-30 00:23:08 +00:00
Paul Reichert
05664b15a3 fix: update naming of FinitenessRelation fields in the sigmaIterator.lean benchmark (#11836)
This PR fixes a broken benchmark that uses an outdated naming of
`FinitenessRelation` and `ProductivenessRelation`'s fields.
2025-12-29 23:13:13 +00:00
Paul Reichert
1590a72913 feat: make FinitenessRelation part of the public API (#11789)
This PR makes the `FinitenessRelation` structure, which is helpful when
proving the finiteness of iterators, part of the public API. Previously,
it was marked internal and experimental.
2025-12-29 20:45:41 +00:00
Leonardo de Moura
f77ce8c669 doc: expand PR creation guidelines in CLAUDE.md (#11835)
This PR expands the pull request creation section with detailed
formatting guidelines including title format (type prefixes, imperative
present tense) and body format requirements (starting with "This PR").
Adds a concrete example for reference.

All modifications were suggested by Claude.
2025-12-29 17:42:28 +00:00
Leonardo de Moura
4e1a2487b7 feat: add optional binder limit to mkPatternFromTheorem (#11834)
This PR adds `num?` parameter to `mkPatternFromTheorem` to control how
many leading quantifiers are stripped when creating a pattern. This
enables matching theorems where only some quantifiers should be
converted to pattern variables.

For example, to match `mk_forall_and : (∀ x, P x) → (∀ x, Q x) → (∀ x, P
x ∧ Q x)` against a goal `∀ x, q x 0 ∧ q (f (f x)) y`, we use
`mkPatternFromTheorem ``mk_forall_and (some 5)` to create the pattern `∀
x, ?P x ∧ ?Q x`, keeping the outermost `∀` in the pattern rather than
converting it to a pattern variable.
2025-12-29 17:38:50 +00:00
Leonardo de Moura
b60556af4e chore: Sym cleanup (#11833)
This PR fixes a few typos, adds missing docstrings, and adds a (simple)
missing optimization.
2025-12-29 17:07:56 +00:00
Leonardo de Moura
2bca310bea feat: efficient pattern matching and unification for the symbolic simulation framework (#11825)
This PR completes the new pattern matching and unification procedures
for the symbolic simulation framework using a two-phase approach.

**Phase 1 (Syntactic Matching):**
- Patterns use de Bruijn indices for expression variables and renamed
level params for universe variables
- Purely structural matching after reducible definitions are unfolded
- Universe levels treat `max`/`imax` as uninterpreted functions
- Proof arguments skipped via proof irrelevance
- Instance and binder constraints deferred to Phase 2

**Phase 2 (Pending Constraints):**
- Level constraints: structural equality with mvar assignment
- Instance constraints: `isDefEqI` (full `isDefEq` for TC synthesis)
- Expression constraints: `isDefEqS` with Miller pattern support
- Unassigned instance pattern variables synthesized via
`trySynthInstance`

**`isDefEqS` (Structural DefEq):**
- Miller pattern detection and assignment (`?m x y z := rhs` → `?m :=
fun x y z => rhs`)
- Scope checking via `maxFVar` to prevent out-of-scope assignments
- Optional zeta-delta reduction for let-declarations
- Proof irrelevance and instance delegation to `isDefEqI`

**Key optimizations:**
- `abstractFVars` skips metavariables and uses `maxFVar` for early
cutoff
- Per-pattern `ProofInstInfo` cache for fast argument classification
- Maximal sharing.
2025-12-29 05:18:16 +00:00
Leonardo de Moura
5042c8cc37 feat: isDefEqS, a lightweight structural definitional equality for the symbolic simulation framework (#11824)
This PR implements `isDefEqS`, a lightweight structural definitional
equality for the symbolic simulation framework. Unlike the full
`isDefEq`, it avoids expensive operations while still supporting Miller
pattern unification.

**Key features:**
- Structural matching with optional zeta-delta reduction for
let-declarations
- Miller pattern detection and assignment (`?m x y z := rhs` → `?m :=
fun x y z => rhs`)
- Scope checking via `maxFVar` to prevent out-of-scope assignments
- Proof arguments skipped via proof irrelevance
- Instance arguments delegated to full `isDefEq` (need TC machinery)
- Universe levels treated structurally (`max`/`imax` as uninterpreted)
2025-12-29 03:17:18 +00:00
Leonardo de Moura
1e99ff1dba feat: optimized abstractFVars and abstractFVarsRange (#11820)
This PR adds optimized `abstractFVars` and `abstractFVarsRange` for
converting free variables to de Bruijn indices during pattern
matching/unification.

**Optimizations:**
- Metavariables are skipped (their contexts must not include abstracted
fvars)
- Subterms whose `maxFVar` is below the minimal abstracted fvar are
skipped via early cutoff
- Results are maximally shared via `AlphaShareBuilderM`

These optimizations are sound for Miller pattern matching where
metavariables are created before entering binders.
2025-12-28 23:12:21 +00:00
Leonardo de Moura
48bb954e4e feat: structural isDefEq for Sym (#11819)
This PR adds some basic infrastructure for a structural (and cheaper)
`isDefEq` predicate for pattern matching and unification in `Sym`.
2025-12-28 22:37:21 +00:00
Leonardo de Moura
96160e553a feat: skip proof and instance arguments during pattern matching (#11815)
This PR optimizes pattern matching by skipping proof and instance
arguments during Phase 1 (syntactic matching).
2025-12-28 05:23:32 +00:00
Leonardo de Moura
18702bdd47 feat: add instantiateRevBetaS (#11814)
This PR implements `instantiateRevBetaS`, which is similar to
`instantiateRevS` but beta-reduces nested applications whose function
becomes a lambda after substitution.

For example, if `e` contains a subterm `#0 a` and we apply the
substitution `#0 := fun x => x + 1`, then `instantiateRevBetaS` produces
`a + 1` instead of `(fun x => x + 1) a`.

This is useful when applying theorems. For example, when applying
`Exists.intro`:
```lean
Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists p
```
to a goal of the form `∃ x : Nat, p x ∧ q x`, we create metavariables
`?w` and `?h`. With `instantiateRevBetaS`, the type of `?h` becomes `p
?w ∧ q ?w` instead of `(fun x => p x ∧ q x) ?w`.
2025-12-28 03:28:15 +00:00
Leonardo de Moura
4eaaadf1c1 feat: add pattern matching/unification for symbolic simulation (#11813)
This PR introduces a fast pattern matching and unification module for
the symbolic simulation framework (`Sym`). The design prioritizes
performance by using a two-phase approach:

**Phase 1 (Syntactic Matching)**
- Patterns use de Bruijn indices for expression variables and renamed
level params (`_uvar.0`, `_uvar.1`, ...) for universe variables
- Matching is purely structural after reducible definitions are unfolded
during preprocessing
- Universe levels treat `max` and `imax` as uninterpreted functions (no
AC reasoning)
- Binders and term metavariables are deferred to Phase 2

**Phase 2 (Pending Constraints)** [WIP]
- Handles binders (Miller patterns) and metavariable unification
- Converts remaining de Bruijn variables to metavariables
- Falls back to `isDefEq` when necessary

**Key design decisions:**
- Preprocessing unfolds reducible definitions and performs beta/zeta
reduction
- Kernel projections are expected to be folded as projection
applications before matching
- Assignment conflicts are deferred to pending rather than invoking
`isDefEq` inline
- `instantiateRevS` ensures maximal sharing of result expressions

**TODO:**
- Skip instance arguments during matching, synthesize later
- Skip proof arguments (proof irrelevance)
- Implement `processPending` for Phase 2 constraints
2025-12-28 01:44:36 +00:00
Leonardo de Moura
2234c91163 feat: add TransparencyMode.none (#11810)
This PR adds a new transparency mode `.none` in which no definitions are
unfolded.
2025-12-27 03:10:17 +00:00
Lean stage0 autoupdater
4f7ba5eb09 chore: update stage0 2025-12-27 03:18:33 +00:00
Sofia Rodrigues
da70626e64 fix: Signal.Handler segmentation fault with Selector (#11724)
This PR adds more `event_loop_lock`s to fix race conditions.
2025-12-27 02:07:00 +00:00
Leonardo de Moura
214acc921c refactor: Goal in grind (#11806)
This PR refactors the `Goal` type used in `grind`. The new
representation allows multiple goals with different metavariables to
share the same `GoalState`. This is useful for automation such as
symbolic simulator, where applying theorems create multiple goals that
inherit the same E-graph, congruence closure and solvers state, and
other accumulated facts.
2025-12-26 23:39:13 +00:00
Robert J. Simmons
f483c6c10f refactor: move error explanation text to the manual (#11688)
This PR removes error explanation text from the manual, as this content
is now directly incorporated in the manual by
leanprover/reference-manual#704.
2025-12-26 17:14:58 +00:00
Leonardo de Moura
c0d5e8bc2c feat: intro tactic for SymM (#11803)
This PR implements `intro` (and its variants) for `SymM`. These versions
do not use reduction or infer types, and ensure expressions are
maximally shared.
2025-12-26 03:45:33 +00:00
Leonardo de Moura
c02f570b76 feat: add instantiateS and variants (#11802)
This PR adds the function `Sym.instantiateS` and its variants, which are
similar to `Expr.instantiate` but assumes the input is maximally shared
and ensures the output is also maximally shared.
2025-12-25 23:02:16 +00:00
Leonardo de Moura
19d16ff9b7 feat: add replaceS, liftLooseBVarsS, and lowerBVarsS (#11800)
This PR adds the function `Sym.replaceS`, which is similar to
`replace_fn` available in the kernel but assumes the input is maximally
shared and ensures the output is also maximally shared. The PR also
generalizes the `AlphaShareBuilder` API.
2025-12-25 20:16:45 +00:00
Leonardo de Moura
58420f9416 refactor: simplify AlphaShareCommon.State (#11797)
This PR simplifies `AlphaShareCommon.State` by separating the persistent
and transient parts of the state.

The `map` field caches visited sub-expressions during a single
`shareCommonAlpha` call to handle DAGs efficiently, the input expression
may contain shared sub-expressions that are not yet maximally shared.
However, this cache does not need to persist between different
`shareCommonAlpha` calls.

**Changes:**
- Moved `map` from the persistent `AlphaShareCommon.State` to a private
`State` used only within individual `shareCommonAlpha` calls.
- Replaced `PHashMap ExprPtr Expr` with (the more efficient)
`Std.HashMap ExprPtr Expr` for `map`, since it is now local to each call
and does not need persistence.
- The public `AlphaShareCommon.State` now only contains the `set` of
alpha-equivalent expressions that should persist
2025-12-25 18:06:34 +00:00
Leonardo de Moura
b3b33e85d3 feat: add Sym.getMaxFVar? (#11794)
This PR implements the function `getMaxFVar?` for implementing `SymM`
primitives.
2025-12-25 02:24:00 +00:00
Leonardo de Moura
723acce2a7 feat: add AlphaShareBuilder (#11793)
This PR adds functions for creating maximally shared terms from
maximally shared terms. It is more efficient than creating an expression
and then invoking `shareCommon`. We are going to use these functions for
implementing the symbolic simulation primitives.
2025-12-25 00:05:03 +00:00
Leonardo de Moura
e765138bb4 chore: add isDebugEnabled to grind (#11792)
This PR adds `isDebugEnabled` for checking whether `grind.debug` is set
to `true` when `grind` was initialized.
2025-12-24 23:32:27 +00:00
Leonardo de Moura
501375f340 feat: add SymM monad (#11788)
This PR introduces `SymM`, a new monad for implementing symbolic
simulators (e.g., verification condition generators) in Lean. The monad
addresses performance issues found in symbolic simulators built on top
of user-facing tactics like `apply` and `intros`.

**Key features:**
- Goals are represented by `Grind.Goal` objects, enabling incremental
hypothesis processing
- No `revert` or `clear` operations, allowing O(1) local context checks
instead of O(n log n)
- Carries `GrindM` state across goals to avoid reprocessing shared
hypotheses
- Provides `mkGoal` for creating new goals within the monad

This is the foundational infrastructure for `SymM`. Future PRs will add
operations like `intro`, `apply`, and the optimized definitional
equality test.
2025-12-24 04:05:14 +00:00
Leonardo de Moura
ce56e2139e feat: support for incrementally processing hypotheses in grind (#11787)
This PR adds support for incrementally processing local declarations in
`grind`. Instead of processing all hypotheses at once during goal
initialization, `grind` now tracks which local declarations have been
processed via `Goal.nextDeclIdx` and provides APIs to process new
hypotheses incrementally.
This feature will be used by the new `SymM` monad for efficient symbolic
simulation.
2025-12-24 02:50:22 +00:00
Henrik Böving
c34e4cf0f7 perf: disable closed term extraction in bv_decide (#11785)
This PR disables closed term extraction in the reflection terms used by
`bv_decide`. These terms do
not profit at all from closed term extraction but can in practice cause
thousands of new closed term
declarations which in turn slows down the compiler.
2025-12-23 23:22:12 +00:00
Leonardo de Moura
f2c9fcc0b2 feat: add optional start position to PersistentArray.forM (#11784)
This PR just adds an optional start position argument to
`PersistentArray.forM`
2025-12-23 22:12:02 +00:00
Sebastian Ullrich
950a2b7896 chore: ensure every pkg/ test has a correct lean-toolchain file (#11782) 2025-12-23 17:17:22 +00:00
Henrik Böving
88f17dee71 perf: tune the behavior of and flattening in bv_decide (#11781)
This PR improves the performance of and flattening in `bv_decide`.

The two main insights of this PR are:
1. When embedded constraint substitution is disabled it makes no sense
to have and flattening on in
   the first place, given that we do not profit from it in any way.
2. The new fvars produced by and flattening can also be inserted into
the rewriting caches of the
preprocessing pipeline if the fvar they were derived from is already in
the cache. This
drastically decreases the amount of work we have to do in the second
rewriting pass after running
   and flattening.
2025-12-23 13:08:31 +00:00
Henrik Böving
4d2647f9c7 fix: foldlM mismatch part 2 (#11779)
This PR fixes an oversight in the initial #11772 PR.

Closes #11778.
2025-12-23 10:29:20 +00:00
Leonardo de Moura
a471f005d6 feat: add [grind norm] and [grind unfold] attributes (#11776)
This PR adds the attributes `[grind norm]` and `[grind unfold]` for
controlling the `grind` normalizer/preprocessor.

The `norm` modifier instructs `grind` to use a theorem as a
normalization rule. That is, the theorem is applied during the
preprocessing step. This feature is meant for advanced users who
understand how the preprocessor and `grind`'s search procedure interact
with each other.
New users can still benefit from this feature by restricting its use to
theorems that completely eliminate a symbol from the goal. Example:
```lean
theorem max_def : max n m = if n ≤ m then m else n
```
For a negative example, consider:
```lean
opaque f : Int → Int → Int → Int
theorem fax1 : f x 0 1 = 1 := sorry
theorem fax2 : f 1 x 1 = 1 := sorry
attribute [grind norm] fax1
attribute [grind =] fax2

example (h : c = 1) : f c 0 c = 1 := by
  grind -- fails
```
In this example, `fax1` is a normalization rule, but it is not
applicable to the input goal since `f c 0 c` is not an instance of `f x
0 1`. However, `f c 0 c` matches the pattern `f 1 x 1` modulo the
equality `c = 1`. Thus, `grind` instantiates `fax2` with `x := 0`,
producing the equality `f 1 0 1 = 1`, which the normalizer simplifies to
`True`. As a result, nothing useful is learned. In the future, we plan
to include linters to automatically detect issues like these. Example:
```lean
opaque f : Nat → Nat
opaque g : Nat → Nat

@[grind norm] axiom fax : f x = x + 2
@[grind norm ←] axiom fg : f x = g x

example : f x ≥ 2 := by grind
example : f x ≥ g x := by grind
example : f x + g x ≥ 4 := by grind
```

The `unfold` modifier instructs `grind` to unfold the given definition
during the preprocessing step. Example:
```lean
@[grind unfold] def h (x : Nat) := 2 * x
example : 6 ∣ 3*h x := by grind
```
2025-12-23 03:54:35 +00:00
Leonardo de Moura
f6a25b13b9 chore: grind cleanup (#11775) 2025-12-22 23:49:14 +00:00
Henrik Böving
a847b13b1a fix: implemented_by Array.foldlM behavior when stop > start (#11774)
This PR fixes a mismatch between the behavior of `foldlM` and
`foldlMUnsafe` in the three array
types. This mismatch is only exposed when manually specifying a `stop`
value greater than the size
of the array and only exploitable through `native_decide`.

The mismatch was introduced as part of
4ba21ea10c which introduced
`foldlMUnsafe` and thus likely a mistake when building the `unsafe`
implementation instead of a
specification mistake.

Closes #11773
2025-12-22 23:46:45 +00:00
Leonardo de Moura
186a81627b fix: Array.foldlMUnsafe bug (#11772)
This PR a bug in the optimized and unsafe implementation of
`Array.foldlM`.

Issue was reported here:

https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/Array.2Efoldl.20bug.20.28can.20prove.20False.29/near/565077432
2025-12-22 23:00:16 +00:00
Lean stage0 autoupdater
0df74178d8 chore: update stage0 2025-12-22 20:55:00 +00:00
Leonardo de Moura
72f9b725aa feat: user attribute at grind_pattern (#11770)
This PR implements support for user-defined attributes at
`grind_pattern`. Suppose we have declared the `grind` attribute

```lean
register_grind_attr my_grind
```

Then, we can now write

```lean
opaque f : Nat → Nat
opaque g : Nat → Nat
axiom fg : g (f x) = x

grind_pattern [my_grind] fg => g (f x)
```
2025-12-22 20:07:02 +00:00
Leonardo de Moura
dc53fac626 chore: use extensible grind attribute framework to implement [grind] itself (#11769)
This PR uses the new support for user-defined `grind` attributes to
implement the default `[grind]` attribute.

A manual update-stage0 is required because it affects the .olean files.
2025-12-22 10:07:30 -08:00
Lean stage0 autoupdater
13c88f960f chore: update stage0 2025-12-22 03:42:18 +00:00
Leonardo de Moura
0d2a574f96 feat: user-defined grind attributes (#11765)
This PR implements user-defined `grind` attributes. They are useful for
users that want to implement tactics using the `grind` infrastructure
(e.g., `progress*` in Aeneas). New `grind` attributes are declared using
the command
```lean
register_grind_attr my_grind
```
The command is similar to `register_simp_attr`. After the new attribute
is declared. Recall that similar to `register_simp_attr`, the new
attribute cannot be used in the same file it is declared.
```lean
opaque f : Nat → Nat
opaque g : Nat → Nat

@[my_grind] theorem fax : f (f x) = f x := sorry

example theorem fax2 : f (f (f x)) = f x := by
  fail_if_success grind
  grind [my_grind]
```

TODO: remove leftovers after update stage0
2025-12-22 02:57:25 +00:00
Kim Morrison
a7562bc578 feat: add guarded grind_pattern to List.eq_nil_of_length_eq_zero (#11760)
This PR allows `grind` to use `List.eq_nil_of_length_eq_zero` (and
`Array.eq_empty_of_size_eq_zero`), but only when it has already proved
the length is zero.
2025-12-22 00:05:58 +00:00
Kim Morrison
c86b10d141 chore: add grind pattern guide for Sublist.eq_of_length_le (#11762)
This PR moves the grind pattern from `Sublist.eq_of_length` to the
slightly more general `Sublist.eq_of_length_le`, and adds a grind
pattern guard so it only activates if we have a proof of the hypothesis.
2025-12-22 00:01:33 +00:00
Kim Morrison
54a88e941f chore: followup tests for #11745 (#11764)
This PR adds additional test coverage for #11758 (fix for #11745:
nonstandard instances in grind and simp +arith).

The existing test `grind_11745.lean` only covers Int LE with `grind
-order` and `lia -order`. This adds tests for:

- LT instances (Int and Nat)
- Nat LE instances
- Mixed canonical and non-canonical instances in the same goal
- Equality derived from two LE constraints
- `simp +arith` with non-canonical instances

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-21 22:31:53 +00:00
Kim Morrison
b87d2c0fb9 feat: add lean-bisect script for bisecting toolchain regressions (#11727)
This PR adds a Python script that helps find which commit introduced a
behavior change in Lean. It supports multiple bisection modes and
automatically downloads CI artifacts when available.

- [x] depends on: #11735

## Usage

```
usage: lean-bisect [-h] [--timeout SEC] [--ignore-messages] [--verbose]
                   [--selftest] [--clear-cache] [--nightly-only]
                   [file] [RANGE]

Bisect Lean toolchain versions to find where behavior changes.

positional arguments:
  file               Lean file to test (must only import Lean.* or Std.*)
  RANGE              Range to bisect: FROM..TO, FROM, or ..TO

options:
  -h, --help         show this help message and exit
  --timeout SEC      Timeout in seconds for each test run
  --ignore-messages  Compare only exit codes, ignore stdout/stderr differences
  --verbose, -v      Show stdout/stderr from each test
  --selftest         Run built-in selftest to verify lean-bisect works
  --clear-cache      Clear CI artifact cache (~600MB per commit) and exit
  --nightly-only     Stop after finding nightly range (don't bisect individual
                     commits)

Range Syntax:

  FROM..TO                Bisect between FROM and TO
  FROM                    Start from FROM, bisect to latest nightly
  ..TO                    Bisect to TO, search backwards for regression start

  If no range given, searches backwards from latest nightly to find regression.

Identifier Formats:

  nightly-YYYY-MM-DD    Nightly build date (e.g., nightly-2024-06-15)
                        Uses pre-built toolchains from leanprover/lean4-nightly.
                        Fast: downloads via elan (~30s each).

  v4.X.Y or v4.X.Y-rcN  Version tag (e.g., v4.8.0, v4.9.0-rc1)
                        Converts to equivalent nightly range.

  Commit SHA            Git commit hash (short or full, e.g., abc123def)
                        Bisects individual commits between two points.
                        Tries CI artifacts first (~30s), falls back to building (~2-5min).
                        Commits with failed CI builds are automatically skipped.
                        Artifacts cached in ~/.cache/lean-bisect/artifacts/

Bisection Modes:

  Nightly mode:   Both endpoints are nightly dates.
                  Binary search through nightlies to find the day behavior changed.
                  Then automatically continues to bisect individual commits.
                  Use --nightly-only to stop after finding the nightly range.

  Version mode:   Either endpoint is a version tag.
                  Converts to equivalent nightly range and bisects.

  Commit mode:    Both endpoints are commit SHAs.
                  Binary search through individual commits on master.
                  Output: "Behavior change introduced in commit abc123"

Examples:

  # Simplest: just provide the file, finds the regression automatically
  lean-bisect test.lean

  # Specify an endpoint if you know roughly when it broke
  lean-bisect test.lean ..nightly-2024-06-01

  # Full manual control over the range
  lean-bisect test.lean nightly-2024-01-01..nightly-2024-06-01

  # Only find the nightly range, don't continue to commit bisection
  lean-bisect test.lean nightly-2024-01-01..nightly-2024-06-01 --nightly-only

  # Add a timeout (kills slow/hanging tests)
  lean-bisect test.lean --timeout 30

  # Bisect commits directly (if you already know the commit range)
  lean-bisect test.lean abc1234..def5678

  # Only compare exit codes, ignore output differences
  lean-bisect test.lean --ignore-messages

  # Clear downloaded CI artifacts to free disk space
  lean-bisect --clear-cache
```

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-21 20:04:47 +00:00
Kim Morrison
eb990538ae fix: allow exact? to suggest local private declarations (part 2) (#11759)
This PR contains changes that were meant to be part of #11736, but I
accidentally merged without pushing my final local changes.
2025-12-21 20:03:10 +00:00
Joachim Breitner
4c0765fc07 fix: grind using congr equation of private imported matcher (#11756)
This PR fixes an issue where `grind` fails when trying to unfold a
definition by pattern matching imported by `import all` (or from a
non-`module`).

Fixes #11715

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-12-21 17:59:52 +00:00
Leonardo de Moura
5e24120dba fix: nonstandard instances in grind and simp +arith (#11758)
This PR improves support for nonstandard `Int`/`Nat` instances in
`grind` and `simp +arith`.

Closes #11745
2025-12-21 17:56:49 +00:00
Sebastian Ullrich
f317e28d84 fix: realizeValue should default to the private scope (#11748)
This PR fixes an edge case where some tactics did not allow access to
private declarations inside private proofs under the module system

Fixes #11747
2025-12-21 01:22:19 +00:00
Eric Paul
bb8e6801f0 chore: fix typo in parser docstring (#11753)
Fix a typo in the docstring for checking the `lhsPrec`
2025-12-20 23:17:47 +00:00
Leonardo de Moura
5440bf724d fix: case-splitting selection in grind (#11749)
This PR fixes a bug in the function `selectNextSplit?` used in `grind`.
It was incorrectly computing the generation of each candidate.

Closes #11697
2025-12-20 20:17:09 +00:00
Henrik Böving
c88ec35c0d perf: turn more commonly used bv_decide theorems into simprocs (#11739)
This PR turns even more commonly used bv_decide theorems that require
unification into fast simprocs
using syntactic equality. This pushes the overall performance across
sage/app7 to <= 1min10s for
every problem.
2025-12-19 18:09:32 +00:00
David Thrane Christiansen
73ff198d11 doc: replace ffi.md with links to the reference manual (#11737)
This PR replaces `ffi.md` with links to the corresponding sections of
the manual, so we don't have to keep two documents up to date.

A corresponding reference manual PR re-synchronizes them:
https://github.com/leanprover/reference-manual/pull/714
2025-12-19 07:23:06 +00:00
Kim Morrison
cee149cc1f feat: add #import_path, assert_not_exists, assert_not_imported commands (#11726)
This PR upstreams dependency-management commands from Mathlib:

- `#import_path Foo` prints the transitive import chain that brings
`Foo` into scope
- `assert_not_exists Foo` errors if declaration `Foo` exists (for
dependency management)
- `assert_not_imported Module` warns if `Module` is transitively
imported
- `#check_assertions` verifies all pending assertions are eventually
satisfied

These commands help maintain the independence of different parts of a
library by catching unintended transitive dependencies early.

### Example usage

```lean
-- Find out how Nat got into scope
#import_path Nat
-- Declaration Nat is imported via
-- Init.Prelude,
--   which is imported by Init.Coe,
--   which is imported by Init.Notation,
--   ...
--   which is imported by this file.

-- Assert that a declaration should not be in scope yet
assert_not_exists SomeAdvancedType

-- Assert that a module should not be imported
assert_not_imported Some.Heavy.Module

-- Verify all assertions are eventually satisfied
#check_assertions
```

Addresses
https://lean-fro.zulipchat.com/#narrow/channel/398861-general/topic/path.20of.20an.20import

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-19 04:09:33 +00:00
Kim Morrison
2236122411 feat: add build_artifact.py for downloading CI artifacts (#11735)
This PR adds a standalone script to download pre-built CI artifacts from
GitHub Actions. This allows us to quickly switch commits without
rebuilding.

**Features:**
- Downloads artifacts for current HEAD or specified commit (`--sha`)
- Caches in `~/.cache/lean_build_artifact/` for reuse
- Platform detection (Linux/macOS, x86_64/aarch64)

**Usage:**
```
build_artifact.py                   # Download for current HEAD
build_artifact.py --sha abc1234     # Download for specific commit
build_artifact.py --clear-cache     # Clear cache
```

This is extracted to be shared with `lean-bisect`.

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-19 04:09:23 +00:00
Kim Morrison
c74d24aaaa fix: allow exact? to suggest local private declarations (#11736)
This PR fixes an issue where `exact?` would not suggest private
declarations defined in the current module.

## Problem

When using `exact?` in a file with private declarations, those private
declarations were not being suggested even though they are valid and
accessible:

```lean
module

axiom P : Prop
private axiom p : P
example : P := by exact? -- error: could not find lemma
```

The problem was that `blacklistInsertion` in `LazyDiscrTree` was
filtering out all declarations whose names matched `isInternalDetail`,
which includes private names due to their `_private.Module.0.name`
structure.

## Solution

The fix adds a helper function `isPrivateNameOf` that checks if a
private declaration belongs to a specific module. The
`blacklistInsertion` function now allows private declarations belonging
to the current module (`env.header.mainModule`) to pass through the
filter.

Private declarations from imported modules are still filtered out, as
they may reference internal declarations that aren't accessible (which
would cause processing errors).

Zulip discussion:
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/.60exact.3F.60.20and.20private.20declarations/near/564586152

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-19 04:05:54 +00:00
Henrik Böving
34d619bf93 perf: use lean::unordered_set for expr_eq_fn (#11731)
This PR makes the cache in expr_eq_fn use mimalloc for a small
performance win across the board.
2025-12-18 14:24:50 +00:00
Luisa Cicolini
eb11ccb234 feat: lemmas around BitVec.extractLsb' and BitVec.extractLsb (#11728)
This PR introduces some additional lemmas around `BitVec.extractLsb'`
and `BitVec.extractLsb`.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
2025-12-18 11:27:27 +00:00
Henrik Böving
2db0a98b7c fix: internalize all arguments to Quot.lift during LCNF conversion (#11729)
This PR internalizes all arguments of Quot.lift during LCNF conversion,
preventing panics in certain
non trivial programs that use quotients.

Fixes #11719.
2025-12-18 09:31:48 +00:00
Henrik Böving
6cabf59099 perf: avoid locally nameless overhead in congruence functions (#11721)
This PR improves the performance of the functions for generating
congruence lemmas, used by `simp`
and a few other components.

It is a followup to (though not dependent on) #11717 and improves the
performance of `bv_decide` on the benchmark
in question further down to 20 seconds (from 1min 23s in #11717 and 8min
originally). We are thus at approximately a 24x speedup from the
original run.
2025-12-18 08:29:08 +00:00
Henrik Böving
89bbe804a5 perf: turn more bv_normalize rules into simprocs (#11717)
This PR improves the performance of `bv_decide`'s rewriter on large
problems.

The baseline for this PR is `QF_BV/sage/app7/bench_1222.smt2` on
`chonk3` at 8 minutes. After this
PR it takes about 1min and 23 seconds. This improvement is achieved by
turning frequently used simp
rules into simprocs in order to avoid spending time performing
unification to see if they are
applicable.
2025-12-18 08:20:16 +00:00
Paul Reichert
4e656ea8e9 refactor: move Std.Range to Std.Legacy.Range (#11438)
This PR renames the namespace `Std.Range` to `Std.Legacy.Range`. Instead
of using `Std.Range` and `[a:b]` notation, the new range type `Std.Rco`
and its corresponding `a...b` notation should be used. There are also
other ranges with open/closed/infinite boundary shapes in
`Std.Data.Range.Polymorphic` and the new range notation also works for
`Int`, `Int8`, `UInt8`, `Fin` etc.
2025-12-18 02:07:33 +00:00
Lean stage0 autoupdater
aa9f7ab14b chore: update stage0 2025-12-17 23:51:56 +00:00
Paul Reichert
5ef0207a85 refactor: remove IteratorCollect (#11706)
This PR removes the `IteratorCollect` type class and hereby simplifies
the iterator API. Its limited advantages did not justify the complexity
cost.
2025-12-17 23:02:33 +00:00
Paul Reichert
a1b8ffe31b feat: improve MPL support for loops over iterators, fix MPL spec priorities (#11716)
This PR adds more MPL spec lemmas for all combinations of `for` loops,
`fold(M)` and the `filter(M)/filterMap(M)/map(M)` iterator combinators.
These kinds of loops over these combinators (e.g. `it.mapM`) are first
transformed into loops over their base iterators (`it`), and if the base
iterator is of type `Iter _` or `IterM Id _`, then another spec lemma
exists for proving Hoare triples about it using an invariant and the
underlying list (`it.toList`). The PR also fixes a bug that MPL always
assigns the default priority to spec lemmas if `Std.Tactic.Do.Syntax` is
not imported and a bug that low-priority lemmas are preferred about
high-priority ones.

For context, the MPL bug was related to the fact that the `Attr.spec`
syntax is not built-in. Therefore, Lean falls back to the `Attr.simple`
syntax, which *basically* also works, but which stores the priority at a
different position. The routine to extract the priority does not
consider this and so it falls back to the default priority given an
`Attr.simple` syntax object.
2025-12-17 22:49:42 +00:00
Henrik Böving
f21f8d96f9 perf: improve auto completion and fuzzy matching (#11630)
This PR improves the performance of autocompletion and fuzzy matching by
introducing an ASCII fast path into one of their core loops and making
Char.toLower/toUpper more efficient.

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-12-17 16:04:05 +00:00
Joachim Breitner
1918d4f0dc chore: add test for #11655 (#11718)
This PR adds a test for issue #11655, which it seems was fixed by #11695

Fixes #11655
2025-12-17 15:54:16 +00:00
Robert J. Simmons
08c87b2ad3 feat: focused error messages for named examples (#11714)
This PR gives a focused error message when a user tries to name an
example, and tweaks error messages for attempts to define multiple
opaque names at once.

## Example errors

```
example x : 1 == 1 := by grind
```

Current message:
```
Failed to infer type of binder `x`

Note: Because this declaration's type has been explicitly provided, all parameter types and holes (e.g., `_`) in its header are resolved before its body is processed; information from the declaration body cannot be used to infer what these values should be
```

New message:
```
Failed to infer type of binder `x`

Note: Examples don't have names. The identifier `x` is being interpreted as a parameter `(x : _)`.
```

## Plural-aware identifier lists

Both the example errors and opaque errors understand pluralization and
use oxford commas.

```
opaque a b c : Nat
```

Current message:
```
Failed to infer type of binder `c`

Note: Multiple constants cannot be declared in a single declaration. The identifier(s) `b`, `c` are being interpreted as parameters `(b : _)`, `(c : _)`.
```

New message:
```
Failed to infer type of binder `c`

Note: Multiple constants cannot be declared in a single declaration. The identifiers `b` and `c` are being interpreted as parameters `(b : _)` and `(c : _)`.```
2025-12-17 14:54:41 +00:00
Paul Reichert
489f8acd77 feat: get-elem tactic support for subarrays (#11710)
This PR extends the get-elem tactic for ranges so that it supports
subarrays. Example:
```lean
example {a : Array Nat} (h : a.size = 28) : Id Unit := do
  let mut x := 0
  for h : i in *...(3 : Nat) do
    x := a[1...4][i]
```
2025-12-17 13:44:17 +00:00
Henrik Böving
3e61514ce4 perf: partially evaluate bv_decide simprocs to avoid instance synthesis (#11712)
This PR avoids invoking TC synthesis and other inference mechanisms in
the simprocs of bv_decide. This can give significant speedups on
problems that pressure these simprocs.
2025-12-17 11:52:57 +00:00
Lean stage0 autoupdater
f63c2363ee chore: update stage0 2025-12-17 11:51:52 +00:00
Henrik Böving
fe96911368 feat: proper recursive specialization (#11479)
This PR enables the specializer to also recursively specialize in some
non trivial higher order situations.

The main motivation for this change is the upcoming changes to do
notation by sgraf. In there he uses combinators such as
```lean
@[specialize, expose]
def List.newForIn {α β γ} (l : List α) (b : β) (kcons : α → (β → γ) → β → γ) (knil : β → γ) : γ :=
  match l with
  | []     => knil b
  | a :: l => kcons a (l.newForIn · kcons knil) b
```
in programs such as
```lean
def testing :=
  let x := 42;
  List.newForIn (β := Nat) (γ := Id Nat)
    [1,2,3]
    x
    (fun i kcontinue s =>
      let x := s;
      List.newForIn
        [i:10].toList x
        (fun j kcontinue s =>
          let x := s;
          let x := x + i + j;
          kcontinue x)
        kcontinue)
    pure
```
inspecting this IR right before we get to the specializer in the current
compiler we get:
```
[Compiler.eagerLambdaLifting] size: 22
    def testing : Nat :=
      fun _f.1 _y.2 : Nat :=
        return _y.2;
      let x := 42;
      let _x.3 := 1;
      fun _f.4 i kcontinue s : Nat :=
        fun _f.5 j kcontinue s : Nat :=
          let _x.6 := Nat.add s i;
          let x := Nat.add _x.6 j;
          let _x.7 := kcontinue x;
          return _x.7;
        let _x.8 := 10;
        let _x.9 := Nat.sub _x.8 i;
        let _x.10 := Nat.add _x.9 _x.3;
        let _x.11 := 1;
        let _x.12 := Nat.sub _x.10 _x.11;
        let _x.13 := Nat.mul _x.3 _x.12;
        let _x.14 := Nat.add i _x.13;
        let _x.15 := @List.nil _;
        let _x.16 := List.range'TR.go _x.3 _x.12 _x.14 _x.15;
        let _x.17 := @List.newForIn _ _ _ _x.16 s _f.5 kcontinue;
        return _x.17;
      let _x.18 := 2;
      let _x.19 := 3;
      let _x.20 := @List.nil _;
      let _x.21 := @List.cons _ _x.19 _x.20;
      let _x.22 := @List.cons _ _x.18 _x.21;
      let _x.23 := @List.cons _ _x.3 _x.22;
      let _x.24 := @List.newForIn _ _ _ _x.23 x _f.4 _f.1;
      return _x.24 
```
Here the `kcontinue` higher order functions pose a special challenge
because they delay the discovery of new specialization opportunities.
Inspecting the IR after the current specializer (and a cleanup simp
step) we get functions that look as follows:
```
 [simp] size: 7
      def List.newForIn._at_.testing.spec_0 i kcontinue l b : Nat :=
        cases l : Nat
        | List.nil =>
          let _x.1 := kcontinue b;
          return _x.1
        | List.cons head.2 tail.3 =>
          let _x.4 := Nat.add b i;
          let x := Nat.add _x.4 head.2;
          let _x.5 := List.newForIn._at_.testing.spec_0 i kcontinue tail.3 x;
          return _x.5 
  [simp] size: 14
      def List.newForIn._at_.List.newForIn._at_.testing.spec_1.spec_1 _x.1 l b : Nat :=
        cases l : Nat
        | List.nil =>
          return b
        | List.cons head.2 tail.3 =>
          fun _f.4 x.5 : Nat :=
            let _x.6 := List.newForIn._at_.List.newForIn._at_.testing.spec_1.spec_1 _x.1 tail.3 x.5;
            return _x.6;
          let _x.7 := 10;
          let _x.8 := Nat.sub _x.7 head.2;
          let _x.9 := Nat.add _x.8 _x.1;
          let _x.10 := 1;
          let _x.11 := Nat.sub _x.9 _x.10;
          let _x.12 := Nat.mul _x.1 _x.11;
          let _x.13 := Nat.add head.2 _x.12;
          let _x.14 := @List.nil _;
          let _x.15 := List.range'TR.go _x.1 _x.11 _x.13 _x.14;
          let _x.16 := List.newForIn._at_.testing.spec_0 head.2 _f.4 _x.15 b;
          return _x.16
```
Observe that the specializer decided to abstract over `kcontinue`
instead of specializing further recursively. Thus this tight loop is now
going through an indirect call.

This PR now changes the specializer somewhat fundamentally to handle
situations like this. The most notable change is going to a fixpoint
loop of:
1. Specialize all current declarations in the worklist
2. If a declaration
- succeeded in specializing run the simplifier on it and put it back
onto the worklist
    - if it didn't don't put it back onto the worklist anymore
3. Put all newly generated specialisations on the worklist
4. Recompute fixed parameters for the current SCC
5. Repeat until the worklist is empty

Furthermore, declarations that were already specialized:
- only consider `fixedHO` parameters for specialization, in order to
avoid termination issues with repeated specialization and abstraction of
type class parameters under binders
- recursively specialized declarations only allow specialization if at
least one of their fixedHO arguments is not a parameter itself. The
reason for allowing this in first generation specialization is that we
refrain from specializing inside the body of a declaration marked as
`@[specialize]`. Thus we need to specialize them even if their arguments
don't actually contain anything of interest in order to ensure that type
classes etc. are correctly cleaned up within their bodies.

There is one last trade-off to consider. When specializing code
generated by the new do elaborator we sometimes generate intermediate
specializations that are not actually part of any call graph after we
are done specializing. We could in principle detect these functions and
delete them but having them in cache is potentially helpful for further
specializations later. Once the new do elaborator lands we plan to test
this trade-off.

Closes #10924
2025-12-17 11:05:24 +00:00
Paul Reichert
08f0d12ffb feat: add lemmas about Int ranges (#11705)
This PR provides many lemmas about `Int` ranges, in analogy to those
about `Nat` ranges. A few necessary basic `Int` lemmas are added. The PR
also removes `simp` annotations on `Rcc.toList_eq_toList_rco`,
`Nat.toList_rcc_eq_toList_rco` and consorts.
2025-12-17 10:04:28 +00:00
Luisa Cicolini
06d2390fb3 feat: add BitVec.cpop and lemmas (#11257)
This PR adds the definition of `BitVec.cpop`, which relies on the more
general `BitVec.cpopNatRec`, and build some theory around it. The name
`cpop` aligns with the [RISCV ISA
nomenclature](https://msyksphinz-self.github.io/riscv-isadoc/#_cpop).

Co-authored-by: @tobiasgrosser, @bollu

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-12-17 09:51:24 +00:00
Paul Reichert
3ac9bbb3d8 feat: MPL specs for loops over iterators (#11693)
This PR makes it possible to verify loops over iterators. It provides
MPL spec lemmas about `for` loops over pure iterators. It also provides
spec lemmas that rewrite loops over `mapM`, `filterMapM` or `filterM`
iterator combinators into loops over their base iterator.
2025-12-17 09:36:44 +00:00
Joachim Breitner
118160bf07 refactor: handle irrefutable patterns in match compilation individually (#11695)
This PR refactors match compilation, to handle “side-effect free”
patterns (`.var`, `.inaccessible`, `.as`) eagerly and for each
alternative separately. The idea is that there should be less interplay
between different alternatives, and prepares the ground for #11105.

This may cause some corner case match statements to compiler or fail
compile that behaved differently before. For example, it can now use a
sparse case where previously was using a full case, and pattern
completeness may not be clear to lean now. On the other hand, using a
sparse case can mean that match statements mixing matching in indicies
with matching on the indexed datatype can work.
2025-12-17 09:02:17 +00:00
Kim Morrison
c1bc886d98 fix: remove batteries tag check from PR mathlib CI (#11707)
This PR removes the unnecessary check for the batteries
`nightly-testing-YYYY-MM-DD` tag that blocks mathlib CI from running.

## Problem

Currently, when fixing mathlib's nightly-testing branch, the workflow
requires BOTH batteries and mathlib to have `nightly-testing-YYYY-MM-DD`
tags before mathlib CI can run on lean4 PRs. This creates a false
dependency:

1. Fix mathlib nightly-testing (including fixing batteries build)
2. Mathlib CI succeeds → creates mathlib tag → advances
`nightly-with-mathlib`
3. But batteries test suite fails → no batteries tag created
4. lean4 PR can't run mathlib CI because batteries tag doesn't exist
5. Bot suggests rebasing onto `nightly-with-mathlib`, but this doesn't
help

## Solution

Remove the batteries tag check because:
- Mathlib CI already depends on batteries (builds it as a dependency)
- If batteries is broken, mathlib CI will detect it
- The batteries testing branch creation already has fallback logic
(falls back to `nightly-testing` branch if tag doesn't exist)

This allows mathlib CI to run as soon as mathlib is ready, which is the
actual blocker.

See discussion at
https://leanprover.zulipchat.com/#narrow/channel/428973-nightly-testing/topic/Mathlib.20status.20updates/near/564136025

🤖 Prepared with Claude Code

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 08:53:04 +00:00
Kim Morrison
0708024c46 fix: support dot notation on declarations in grind lemma list (#11691)
This PR fixes `grind` to support dot notation on declarations in the
lemma list.

When using `grind only [foo.le]` where `foo.le` is dot notation applying
`LT.lt.le` to a theorem `foo`, grind previously failed with "Unknown
constant `foo.le`" because it tried to look up `foo.le` as a constant
name rather than elaborating it as a term.

The fix adds a fallback in `processParam`: when constant lookup fails,
it now falls back to `processTermParam` which elaborates the identifier
as a term. This allows dot notation expressions like `log_two_lt_d9.le`
to work correctly.

Closes #11690

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-17 03:17:46 +00:00
Kim Morrison
2d9571563a chore: add missing repositories to release_repos.yml (#11668)
This PR adds the following repositories to the release configuration:
- lean4-unicode-basic
- BibtexQuery (depends on lean4-unicode-basic)
- verso-web-components (depends on verso)

It also updates dependencies:
- doc-gen4 now depends on BibtexQuery
- lean-fro.org now depends on verso-web-components

🤖 Prepared with Claude Code
2025-12-17 02:33:53 +00:00
Paul Reichert
e2617903f8 feat: MonadAttach (#11532)
This PR adds the new operation `MonadAttach.attach` that attaches a
proof that a postcondition holds to the return value of a monadic
operation. Most non-CPS monads in the standard library support this
operation in a nontrivial way. The PR also changes the `filterMapM`,
`mapM` and `flatMapM` combinators so that they attach postconditions to
the user-provided monadic functions passed to them. This makes it
possible to prove termination for some of these for which it wasn't
possible before. Additionally, the PR adds many missing lemmas about
`filterMap(M)` and `map(M)` that were needed in the course of this PR.
2025-12-16 18:57:00 +00:00
Markus Himmel
7ba21c4d1b fix: incorrect inherit_doc (#11704)
This PR fixes an incorrect `inherit_doc` in `Init.System.IO`.
2025-12-16 17:30:01 +00:00
Sebastian Ullrich
b7f1cf9ba7 chore: shake: fix handling of meta structure etc (#11701) 2025-12-16 16:28:39 +00:00
Leonardo de Moura
12c282b1e9 doc: add link to reference manual in grind docstring (#11700)
This PR adds a link to the `grind` docstring. The link directs users to
the section describing `grind` in the reference manual.

<img width="840" height="354" alt="image"
src="https://github.com/user-attachments/assets/8efedc53-26cd-4f2c-8d47-a9d3a324e579"
/>
2025-12-16 15:35:55 +00:00
Sebastian Graf
5f4d724c2d feat: abstract metavariables when generalizing match motives (#8099) (#11696)
This PR improves `match` generalization such that it abstracts
metavariables in types of local variables and in the result type of the
match over the match discriminants. Previously, a metavariable in the
result type would silently default to the behavior of `generalizing :=
false`, and a metavariable in the type of a free variable would lead to
an error (#8099). Example of a `match` that elaborates now but
previously wouldn't:
```lean
example (a : Nat) (ha : a = 37) :=
    (match a with | 42 => by contradiction | n => n) = 37
```
This is because the result type of the `match` is a metavariable that
was not abstracted over `a` and hence generalization failed; the result
is that `contradiction` cannot pick up the proof `ha : 42 = 37`.
The old behavior can be recovered by passing `(generalizing := false)`
to the `match`.

Furthermore, programs such as the following can now be elaborated:
```lean
example (n : Nat) : Id (Fin (n + 1)) :=
  have jp : ?m := ?rhs
  match n with
  | 0 => ?jmp1
  | n + 1 => ?jmp2
  where finally
  case m => exact Fin (n + 1) → Id (Fin (n + 1))
  case jmp1 => exact jp ⟨0, by decide⟩
  case jmp2 => exact jp ⟨n, by omega⟩
  case rhs => exact pure
```
This is useful for the `do` elaborator.

Fixes #8099.
2025-12-16 14:34:29 +00:00
Sebastian Graf
98616529fd fix: early return after simplifying discriminants in mvcgen (#11687) (#11698)
This PR makes `mvcgen` early return after simplifying discriminants,
avoiding a rewrite on an ill-formed `match`.

Closes #11687.
2025-12-16 11:36:45 +00:00
maxwell3025
8f80d2c2e0 fix: add table variant for require.git field in lakefile.toml schema (#11536)
This PR corrects the JSON Schema at
`src/lake/schemas/lakefile-toml-schema.json` to allow the table variant
of the `require.git` field in `lakefile.toml` as specified in the
[reference](https://lean-lang.org/doc/reference/latest/Build-Tools-and-Distribution/Lake/#Lake___Dependency-git).

Closes #11535
2025-12-16 10:47:33 +00:00
Joachim Breitner
fd0a65f312 refactor: make simpH proof-producing (#11553)
This PR makes `simpH`, used in the match equation generator, produce a
proof term. This is in preparation for a bigger refactoring in #11512.

This removes some cases, these are no longer necessary since #11196.
2025-12-16 09:37:17 +00:00
Henrik Böving
bd5d750780 chore: fix BitVec docstring typo (#11694)
Closes #11680
2025-12-16 08:54:14 +00:00
Mac Malone
49d4752bfd fix: lake: meta import transitivity (#11683)
This PR fixes an inconsistency in the way Lake and Lean view the
transitivity of a `meta import`. Lake now works as Lean expects and
includes the meta segment of all transitive imports of a `meta import`
in its transitive trace.
2025-12-16 08:28:52 +00:00
Sofia Rodrigues
95a7c769d8 feat: introduce CancellationContext type for cancellation with context propagation (#11499)
This PR adds the `Context` type for cancellation with context
propagation. It works by storing a tree of forks of the main context,
providing a way to control cancellation.
2025-12-15 21:20:11 +00:00
Robert J. Simmons
7b8e51e025 fix: missing word in inductionWithNoAlts error message (#11684)
This PR adds a missing word ("be") to the error message catching
natural-numbers-game-like uses of induction that was introduced in
#11347.
2025-12-15 17:23:25 +00:00
Alok Singh
949cf69246 chore: use backticks for sorry in diagnostic messages (#11608)
This PR changes the "declaration uses 'sorry'" warning to use backticks
instead of single quotes, consistent with Lean's conventions for
formatting code identifiers in diagnostic messages.
2025-12-15 14:30:21 +00:00
Alok Singh
e02f229305 chore: fix typo "Unkown" -> "Unknown" in role error message (#11682)
Fix a typo in the error message when an unknown role is used in a
docstring.

- Changes "Unkown role" to "Unknown role" in
`src/Lean/Elab/DocString.lean`
2025-12-15 14:29:11 +00:00
Joachim Breitner
9b49b6b68d fix: let grind handle Nat.ctorIdx (#11670)
This PR fixes the `grind` support for `Nat.ctorIdx`. Nat constructors
appear in `grind` as offsets or literals, and not as a node marked
`.constr`, so handle that case as well.
2025-12-15 10:26:16 +00:00
Paul Reichert
eb20c07b4a fix: fix broken benchmarks from #11446 (#11681)
This PR fixes benchmarks that were broken by #11446.
2025-12-15 09:35:42 +00:00
Lean stage0 autoupdater
3fdde57e7b chore: update stage0 2025-12-15 08:59:34 +00:00
Paul Reichert
c79d74d9a1 refactor: move Iter and others from Std.Iterators to Std (#11446)
This PR moves many constants of the iterator API from `Std.Iterators` to
the `Std` namespace in order to make them more convenient to use. These
constants include, but are not limited to, `Iter`, `IterM` and
`IteratorLoop`. This is a breaking change. If something breaks, try
adding `open Std` in order to make these constants available again. If
some constants in the `Std.Iterators` namespace cannot be found, they
can be found directly in `Std` now.
2025-12-15 08:24:12 +00:00
Markus Himmel
082c65f226 chore: ci: check for changes to src/stdlib_flags.h (#11679)
This PR adds a CI step that fails if the `src/stdlib_flags.h` file was
modified, to alert PR authors that they most likely wanted to modify
`stage0/src/stdlib_flags.h` instead.
2025-12-15 07:17:12 +00:00
Leonardo de Moura
6a0b0c8273 fix: grind lia distracted by nonlinearity (#11678)
This PR fixes a bug in `registerNonlinearOccsAt` used to implement
`grind lia`. This issue was originally reported at:
https://leanprover.zulipchat.com/#narrow/channel/113489-new-members/topic/Weirdness.20with.20cutsat/near/562099515

Example that was failing:
```lean
example {a : Nat} (ha : 1 ≤ a) (H : a ^ 2 = 2 ^ a)
    : a = 1 ∨ a = 2 ∨ 3 ≤ a := by
  grind
```
2025-12-14 23:18:12 +00:00
Leonardo de Moura
62b900e8ef feat: basic equality propagation for IntModule in grind (#11677)
This PR adds basic support for equality propagation in `grind linarith`
for the `IntModule` case. This covers only the basic case. See note in
the code.
We remark this feature is irrelevant for `CommRing` since `grind ring`
already has much better support for equality propagation.
2025-12-14 22:40:11 +00:00
Evan Chen
429e09cd82 doc: trivial typo in Grind/Attr.lean (#11676)
This PR fixes typo "the the custom pattern" -> "the custom pattern".
2025-12-14 20:12:47 +00:00
Sebastian Ullrich
c4d67c22e6 chore: CI: disable problematic bv_decide tests under fsanitize (#11675) 2025-12-14 19:02:20 +00:00
Sebastian Ullrich
923d7e1ed6 fix: ensure by uses expected instead of given type for modsys aux decl (#11673)
This PR fixes an issue where a `by` in the public scope could create an
auxiliary theorem for the proof whose type does not match the expected
type in the public scope.

Fixes #11672
2025-12-14 17:44:38 +00:00
Joachim Breitner
5db865ea2f fix: make grind support for ctorIdx debug.grind-safe (#11669)
This PR makes sure that proofs about `ctorIdx` passed to `grind` pass
the `debug.grind` checks, despite reducing a `semireducible` definition.
2025-12-14 14:59:57 +00:00
Joachim Breitner
0f2ac0b099 feat: sparse sparse casesOn splitting in match equations (#11666)
This PR makes sure that when a matcher is compiled using a sparse cases,
that equation generation also uses sparse cases to split.
This fixes #11665.
2025-12-14 14:59:45 +00:00
Kim Morrison
b7ff463358 chore: begin development cycle for v4.28.0 (#11667)
This PR bumps the version from v4.27.0 to v4.28.0-pre to begin the next
development cycle.

🤖 Prepared with Claude Code
2025-12-14 12:42:12 +00:00
Leonardo de Moura
799c6b5ff8 feat: add support for OrderedRing.natCast_nonneg in grind (#11664)
This PR adds support for `Nat.cast` in `grind linarith`. It now uses
`Grind.OrderedRing.natCast_nonneg`. Example:
```lean
open Lean Grind Std
attribute [instance] Semiring.natCast

variable [Lean.Grind.CommRing R] [LE R] [LT R] [LawfulOrderLT R] [IsLinearOrder R] [OrderedRing R]

example (a : Nat) : 0 ≤ (a : R) := by grind
example (a b : Nat) : 0 ≤ (a : R) + (b : R) := by grind
example (a : Nat) : 0 ≤ 2 * (a : R) := by grind
example (a : Nat) : 0 ≥ -3 * (a : R) := by grind
```
2025-12-14 09:09:42 +00:00
Leonardo de Moura
2d0c62c767 fix: grind pattern validation (#11663)
This PR fixes the `grind` pattern validator. It covers the case where an
instance is not tagged with the implicit instance binder. This happens
in declarations such as
```lean
ZeroMemClass.zero_mem {S : Type} {M : outParam Type} {inst1 : Zero M} {inst2 : SetLike S M}
  [self : @ZeroMemClass S M inst1 inst2] (s : S) : 0 ∈ s
```
2025-12-14 07:41:19 +00:00
1841 changed files with 25782 additions and 9186 deletions

View File

@@ -29,6 +29,23 @@ After rebuilding, LSP diagnostics may be stale until the user interacts with fil
If the user expresses frustration with you, stop and ask them to help update this `.claude/CLAUDE.md` file with missing guidance.
## Creating pull requests.
## Creating pull requests
All PRs must have a first paragraph starting with "This PR". This paragraph is automatically incorporated into release notes. Read `lean4/doc/dev/commit_convention.md` when making PRs.
Follow the commit convention in `doc/dev/commit_convention.md`.
**Title format:** `<type>: <subject>` where type is one of: `feat`, `fix`, `doc`, `style`, `refactor`, `test`, `chore`, `perf`.
Subject should use imperative present tense ("add" not "added"), no capitalization, no trailing period.
**Body format:** The first paragraph must start with "This PR". This paragraph is automatically incorporated into release notes. Use imperative present tense. Include motivation and contrast with previous behavior when relevant.
Example:
```
feat: add optional binder limit to `mkPatternFromTheorem`
This PR adds a `num?` parameter to `mkPatternFromTheorem` to control how many
leading quantifiers are stripped when creating a pattern.
```
## 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

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: actionlint
uses: raven-actions/actionlint@v2
with:

View File

@@ -67,13 +67,13 @@ jobs:
if: runner.os == 'macOS'
- name: Checkout
if: (!endsWith(matrix.os, '-with-cache'))
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Namespace Checkout
if: endsWith(matrix.os, '-with-cache')
uses: namespacelabs/nscloud-checkout-action@v7
uses: namespacelabs/nscloud-checkout-action@v8
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Open Nix shell once

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}

View File

@@ -8,7 +8,7 @@ jobs:
check-stage0-on-queue:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

View File

@@ -0,0 +1,57 @@
name: Check stdlib_flags.h modifications
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
check-stdlib-flags:
runs-on: ubuntu-latest
steps:
- name: Check if stdlib_flags.h was modified
uses: actions/github-script@v8
with:
script: |
// Get the list of files changed in this PR
const files = await github.paginate(
github.rest.pulls.listFiles,
{
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
}
);
// Check if stdlib_flags.h was modified
const stdlibFlagsModified = files.some(file =>
file.filename === 'src/stdlib_flags.h'
);
if (stdlibFlagsModified) {
console.log('src/stdlib_flags.h was modified in this PR');
// Check if the unlock label is present
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const hasUnlockLabel = pr.labels.some(label =>
label.name === 'unlock-upstream-stdlib-flags'
);
if (!hasUnlockLabel) {
core.setFailed(
'src/stdlib_flags.h was modified. This is likely a mistake. If you would like to change ' +
'bootstrapping settings or request a stage0 update, you should modify stage0/src/stdlib_flags.h. ' +
'If you really want to change src/stdlib_flags.h (which should be extremely rare), set the ' +
'unlock-upstream-stdlib-flags label.'
);
} else {
console.log('Found unlock-upstream-stdlib-flags');
}
} else {
console.log('src/stdlib_flags.h was not modified');
}

View File

@@ -50,7 +50,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly' || (startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4')
- name: Set Nightly
@@ -267,12 +267,14 @@ jobs:
"test": true,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_PRESET": "sanitize",
// `StackOverflow*` correctly triggers ubsan
// `reverse-ffi` fails to link in sanitizers
// `StackOverflow*` correctly triggers ubsan.
// `reverse-ffi` fails to link in sanitizers.
// `interactive` and `async_select_channel` fail nondeterministically, would need to
// be investigated.
// 9366 is too close to timeout
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel|9366'"
// be investigated..
// 9366 is too close to timeout.
// `bv_` sometimes times out calling into cadical even though we should be using the
// standard compile flags for it.
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel|9366|run/bv_'"
},
{
"name": "macOS",
@@ -432,7 +434,7 @@ jobs:
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -453,7 +455,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
# needed for tagging
fetch-depth: 0
@@ -478,7 +480,7 @@ jobs:
echo -e "\n*Full commit log*\n" >> diff.md
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
- name: Release Nightly
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
body_path: diff.md
prerelease: true

View File

@@ -6,7 +6,7 @@ jobs:
check-lean-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Verify .lean files start with a copyright header.
run: |

View File

@@ -71,7 +71,7 @@ jobs:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -86,7 +86,7 @@ jobs:
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -166,22 +166,14 @@ jobs:
if [ "$NIGHTLY_SHA" = "$MERGE_BASE_SHA" ]; then
echo "The merge base of this PR coincides with the nightly release"
BATTERIES_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/batteries.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
MATHLIB_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/mathlib4-nightly-testing.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
if [[ -n "$BATTERIES_REMOTE_TAGS" ]]; then
echo "... and Batteries has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE=""
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
else
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
fi
else
echo "... but Batteries does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Batteries CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Batteries CI should run now."
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
fi
else
echo "The most recently nightly tag on this branch has SHA: $NIGHTLY_SHA"
@@ -395,7 +387,7 @@ jobs:
# Checkout the Batteries repository with all branches
- name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }}
@@ -455,7 +447,7 @@ jobs:
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: leanprover-community/mathlib4-nightly-testing
token: ${{ secrets.MATHLIB4_BOT }}
@@ -538,7 +530,7 @@ jobs:
# Checkout the reference manual repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: leanprover/reference-manual
token: ${{ secrets.MANUAL_PR_BOT }}

View File

@@ -27,7 +27,7 @@ jobs:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"

View File

@@ -1,189 +1,9 @@
# Foreign Function Interface
NOTE: The current interface was designed for internal use in Lean and should be considered **unstable**.
It will be refined and extended in the future.
The Lean FFI documentation is now part of the [Lean language reference](https://lean-lang.org/doc/reference/latest/).
As Lean is written partially in Lean itself and partially in C++, it offers efficient interoperability between the two languages (or rather, between Lean and any language supporting C interfaces).
This support is however currently limited to transferring Lean data types; in particular, it is not possible yet to pass or return compound data structures such as C `struct`s by value from or to Lean.
* [General FFI](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=ffi)
* [Representation of inductive types](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=inductive-types-ffi)
* [String](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=string-ffi)
* [Array](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=array-ffi)
There are two primary attributes for interoperating with other languages:
* `@[extern "sym"] constant leanSym : ...` binds a Lean declaration to the external symbol `sym`.
It can also be used with `def` to provide an internal definition, but ensuring consistency of both definitions is up to the user.
* `@[export sym] def leanSym : ...` exports `leanSym` under the unmangled symbol name `sym`.
For simple examples of how to call foreign code from Lean and vice versa, see <https://github.com/leanprover/lean4/blob/master/src/lake/examples/ffi> and <https://github.com/leanprover/lean4/blob/master/src/lake/examples/reverse-ffi>, respectively.
## The Lean ABI
The Lean Application Binary Interface (ABI) describes how the signature of a Lean declaration is encoded as a native calling convention.
It is based on the standard C ABI and calling convention of the target platform.
For a Lean declaration marked with either `@[extern "sym"]` or `@[export sym]` for some symbol name `sym`, let `α₁ → ... → αₙ → β` be the normalized declaration's type.
If `n` is 0, the corresponding C declaration is
```c
extern s sym;
```
where `s` is the C translation of `β` as specified in the next section.
In the case of an `@[extern]` definition, the symbol's value is guaranteed to be initialized only after calling the Lean module's initializer or that of an importing module; see [Initialization](#initialization).
If `n` is greater than 0, the corresponding C declaration is
```c
s sym(t, ..., tₘ);
```
where the parameter types `tᵢ` are the C translation of the `αᵢ` as in the next section.
In the case of `@[extern]` all *irrelevant* types are removed first; see next section.
### Translating Types from Lean to C
* The integer types `UInt8`, ..., `UInt64`, `USize` are represented by the C types `uint8_t`, ..., `uint64_t`, `size_t`, respectively
* `Char` is represented by `uint32_t`
* `Float` is represented by `double`
* An *enum* inductive type of at least 2 and at most 2^32 constructors, each of which with no parameters, is represented by the first type of `uint8_t`, `uint16_t`, `uint32_t` that is sufficient to represent all constructor indices.
For example, the type `Bool` is represented as `uint8_t` with values `0` for `false` and `1` for `true`.
* `Decidable α` is represented the same way as `Bool`
* An inductive type with a *trivial structure*, that is,
* it is none of the types described above
* it is not marked `unsafe`
* it has a single constructor with a single parameter of *relevant* type
is represented by the representation of that parameter's type.
For example, `{ x : α // p }`, the `Subtype` structure of a value of type `α` and an irrelevant proof, is represented by the representation of `α`.
Similarly, the signed integer types `Int8`, ..., `Int64`, `ISize` are also represented by the unsigned C types `uint8_t`, ..., `uint64_t`, `size_t`, respectively, because they have a trivial structure.
* `Nat` and `Int` are represented by `lean_object *`.
Their runtime values is either a pointer to an opaque bignum object or, if the lowest bit of the "pointer" is 1 (`lean_is_scalar`), an encoded unboxed natural number or integer (`lean_box`/`lean_unbox`).
* A universe `Sort u`, type constructor `... → Sort u`, `Void α` or proposition `p : Prop` is *irrelevant* and is either statically erased (see above) or represented as a `lean_object *` with the runtime value `lean_box(0)`
* Any other type is represented by `lean_object *`.
Its runtime value is a pointer to an object of a subtype of `lean_object` (see the "Inductive types" section below) or the unboxed value `lean_box(cidx)` for the `cidx`th constructor of an inductive type if this constructor does not have any relevant parameters.
Example: the runtime value of `u : Unit` is always `lean_box(0)`.
#### Inductive types
For inductive types which are in the fallback `lean_object *` case above and not trivial constructors, the type is stored as a `lean_ctor_object`, and `lean_is_ctor` will return true. A `lean_ctor_object` stores the constructor index in the header, and the fields are stored in the `m_objs` portion of the object.
The memory order of the fields is derived from the types and order of the fields in the declaration. They are ordered as follows:
* Non-scalar fields stored as `lean_object *`
* Fields of type `USize`
* Other scalar fields, in decreasing order by size
Within each group the fields are ordered in declaration order. Trivial wrapper types count as their underlying wrapped type for this purpose.
* To access fields of the first kind, use `lean_ctor_get(val, i)` to get the `i`th non-scalar field.
* To access `USize` fields, use `lean_ctor_get_usize(val, n+i)` to get the `i`th usize field and `n` is the total number of fields of the first kind.
* To access other scalar fields, use `lean_ctor_get_uintN(val, off)` or `lean_ctor_get_usize(val, off)` as appropriate. Here `off` is the byte offset of the field in the structure, starting at `n*sizeof(void*)` where `n` is the number of fields of the first two kinds.
For example, a structure such as
```lean
structure S where
ptr_1 : Array Nat
usize_1 : USize
sc64_1 : UInt64
sc64_2 : { x : UInt64 // x > 0 } -- wrappers of scalars count as scalars
sc64_3 : Float -- `Float` is 64 bit
sc8_1 : Bool
sc16_1 : UInt16
sc8_2 : UInt8
sc64_4 : UInt64
usize_2 : USize
sc32_1 : Char -- trivial wrapper around `UInt32`
sc32_2 : UInt32
sc16_2 : UInt16
```
would get re-sorted into the following memory order:
* `S.ptr_1` - `lean_ctor_get(val, 0)`
* `S.usize_1` - `lean_ctor_get_usize(val, 1)`
* `S.usize_2` - `lean_ctor_get_usize(val, 2)`
* `S.sc64_1` - `lean_ctor_get_uint64(val, sizeof(void*)*3)`
* `S.sc64_2` - `lean_ctor_get_uint64(val, sizeof(void*)*3 + 8)`
* `S.sc64_3` - `lean_ctor_get_float(val, sizeof(void*)*3 + 16)`
* `S.sc64_4` - `lean_ctor_get_uint64(val, sizeof(void*)*3 + 24)`
* `S.sc32_1` - `lean_ctor_get_uint32(val, sizeof(void*)*3 + 32)`
* `S.sc32_2` - `lean_ctor_get_uint32(val, sizeof(void*)*3 + 36)`
* `S.sc16_1` - `lean_ctor_get_uint16(val, sizeof(void*)*3 + 40)`
* `S.sc16_2` - `lean_ctor_get_uint16(val, sizeof(void*)*3 + 42)`
* `S.sc8_1` - `lean_ctor_get_uint8(val, sizeof(void*)*3 + 44)`
* `S.sc8_2` - `lean_ctor_get_uint8(val, sizeof(void*)*3 + 45)`
### Borrowing
By default, all `lean_object *` parameters of an `@[extern]` function are considered *owned*, i.e. the external code is passed a "virtual RC token" and is responsible for passing this token along to another consuming function (exactly once) or freeing it via `lean_dec`.
To reduce reference counting overhead, parameters can be marked as *borrowed* by prefixing their type with `@&`.
Borrowed objects must only be passed to other non-consuming functions (arbitrarily often) or converted to owned values using `lean_inc`.
In `lean.h`, the `lean_object *` aliases `lean_obj_arg` and `b_lean_obj_arg` are used to mark this difference on the C side.
Return values and `@[export]` parameters are always owned at the moment.
## Initialization
When including Lean code as part of a larger program, modules must be *initialized* before accessing any of their declarations.
Module initialization entails
* initialization of all "constants" (nullary functions), including closed terms lifted out of other functions
* execution of all `[init]` functions
* execution of all `[builtin_init]` functions, if the `builtin` parameter of the module initializer has been set
The module initializer is automatically run with the `builtin` flag for executables compiled from Lean code and for "plugins" loaded with `lean --plugin`.
For all other modules imported by `lean`, the initializer is run without `builtin`.
Thus `[init]` functions are run iff their module is imported, regardless of whether they have native code available or not, while `[builtin_init]` functions are only run for native executable or plugins, regardless of whether their module is imported or not.
`lean` uses built-in initializers for e.g. registering basic parsers that should be available even without importing their module (which is necessary for bootstrapping).
The initializer for module `A.B` in a package `foo` is called `initialize_foo_A_B`. For modules in the Lean core (e.g., `Init.Prelude`), the initializer is called `initialize_Init_Prelude`. Module initializers will automatically initialize any imported modules. They are also idempotent (when run with the same `builtin` flag), but not thread-safe.
**Important for process-related functionality**: If your application needs to use process-related functions from libuv, such as `Std.Internal.IO.Process.getProcessTitle` and `Std.Internal.IO.Process.setProcessTitle`, you must call `lean_setup_args(argc, argv)` (which returns a potentially modified `argv` that must be used in place of the original) **before** calling `lean_initialize()` or `lean_initialize_runtime_module()`. This sets up process handling capabilities correctly, which is essential for certain system-level operations that Lean's runtime may depend on.
Together with initialization of the Lean runtime, you should execute code like the following exactly once before accessing any Lean declarations:
```c
void lean_initialize_runtime_module();
void lean_initialize();
char ** lean_setup_args(int argc, char ** argv);
lean_object * initialize_A_B(uint8_t builtin);
lean_object * initialize_C(uint8_t builtin);
...
argv = lean_setup_args(argc, argv); // if using process-related functionality
lean_initialize_runtime_module();
//lean_initialize(); // necessary (and replaces `lean_initialize_runtime_module`) if you (indirectly) access the `Lean` package
lean_object * res;
// use same default as for Lean executables
uint8_t builtin = 1;
res = initialize_A_B(builtin);
if (lean_io_result_is_ok(res)) {
lean_dec_ref(res);
} else {
lean_io_result_show_error(res);
lean_dec(res);
return ...; // do not access Lean declarations if initialization failed
}
res = initialize_C(builtin);
if (lean_io_result_is_ok(res)) {
...
//lean_init_task_manager(); // necessary if you (indirectly) use `Task`
lean_io_mark_end_initialization();
```
In addition, any other thread not spawned by the Lean runtime itself must be initialized for Lean use by calling
```c
void lean_initialize_thread();
```
and should be finalized in order to free all thread-local resources by calling
```c
void lean_finalize_thread();
```
## `@[extern]` in the Interpreter
The interpreter can run Lean declarations for which symbols are available in loaded shared libraries, which includes `@[extern]` declarations.
Thus to e.g. run `#eval` on such a declaration, you need to
1. compile (at least) the module containing the declaration and its dependencies into a shared library, and then
1. pass this library to `lean --load-dynlib=` to run code `import`ing this module.
Note that it is not sufficient to load the foreign library containing the external symbol because the interpreter depends on code that is emitted for each `@[extern]` declaration.
Thus it is not possible to interpret an `@[extern]` declaration in the same file.
See [`tests/compiler/foreign`](https://github.com/leanprover/lean4/tree/master/tests/compiler/foreign/) for an example.

View File

@@ -810,7 +810,7 @@ Docstrings for constants should have the following structure:
The **short summary** should be 13 sentences (ideally 1) and provide
enough information for most readers to quickly decide whether the
docstring is relevant to their task. The first (or only) sentence of
constant is relevant to their task. The first (or only) sentence of
the short summary should be a *sentence fragment* in which the subject
is implied to be the documented item, written in present tense
indicative, or a *noun phrase* that characterizes the documented
@@ -1123,6 +1123,110 @@ infix:50 " ⇔ " => Bijection
recommended_spelling "bij" for "⇔" in [Bijection, «term_⇔_»]
```
#### Tactics
Docstrings for tactics should have the following structure:
* Short summary
* Details
* Variants
* Examples
Sometimes more than one declaration is needed to implement what the user
sees as a single tactic. In that case, only one declaration should have
the associated docstring, and the others should have the `tactic_alt`
attribute to mark them as an implementation detail.
The **short summary** should be 13 sentences (ideally 1) and provide
enough information for most readers to quickly decide whether the
tactic is relevant to their task. The first (or only) sentence of
the short summary should be a full sentence in which the subject
is an example invocation of the tactic, written in present tense
indicative. If the example tactic invocation names parameters, then the
short summary may refer to them. For the example invocation, prefer the
simplest or most typical example. Explain more complicated forms in the
variants section. If needed, abbreviate the invocation by naming part of
the syntax and expanding it in the next sentence. The summary should be
written as a single paragraph.
**Details**, if needed, may be 1-3 paragraphs that describe further
relevant information. They may insert links as needed. This section
should fully explain the scope of the tactic: its syntax format,
on which goals it works and what the resulting goal(s) look like. It
should be clear whether the tactic fails if it does not close the main
goal and whether it creates any side goals. The details may include
explanatory examples that cant necessarily be machine checked and
dont fit the format.
If the tactic is extensible using `macro_rules`, mention this in the
details, with a link to `lean-manual://section/tactic-macro-extension`
and give a one-line example. If the tactic provides an attribute or a
command that allows the user to extend its behavior, the documentation
on how to extend the tactic belongs to that attribute or command. In the
tactic docstring, use a single sentence to refer the reader to this
further documentation.
**Variants**, if needed, should be a bulleted list describing different
options and forms of the same tactic. The reader should be able to parse
and understand the parts of a tactic invocation they are hovering over,
using this list. Each list item should describe an individual variant
and take one of two formats: the **short summary** as above, or a
**named list item**. A named list item consists of a title in bold
followed by an indented short paragraph.
Variants should be explained from the perspective of the tactic's users, not
their implementers. A tactic that is implemented as a single Lean parser may
have multiple variants from the perspective of users, while a tactic that is
implemented as multiple parsers may have no variants, but merely an optional
part of the syntax.
**Examples** should start with the line `Examples:` (or `Example:` if
theres exactly one). The section should consist of a sequence of code
blocks, each showing a Lean declaration (usually with the `example`
keyword) that invokes the tactic. When the effect of the tactic is not
clear from the code, you can use code comments to describe this. Do
not include text between examples, because it can be unclear whether
the text refers to the code before or after the example.
##### Example
````
`rw [e]` uses the expression `e` as a rewrite rule on the main goal,
then tries to close the goal by "cheap" (reducible) `rfl`.
If `e` is a defined constant, then the equational theorems associated with `e`
are used. This provides a convenient way to unfold `e`. If `e` has parameters,
the tactic will try to fill these in by unification with the matching part of
the target. Parameters are only filled in once per rule, restricting which
later rewrites can be found. Parameters that are not filled in after
unification will create side goals. If the `rfl` fails to close the main goal,
no error is raised.
`rw` may fail to rewrite terms "under binders", such as `∀ x, ...` or `∃ x,
...`. `rw` can also fail with a "motive is type incorrect" error in the context
of dependent types. In these cases, consider using `simp only`.
* `rw [e₁, ... eₙ]` applies the given rules sequentially.
* `rw [← e]` or `rw [<- e]` applies the rewrite in the reverse direction.
* `rw [e] at l` rewrites with `e` at location(s) `l`.
* `rw (occs := .pos L) [e]`, where `L` is a literal list of natural numbers,
only rewrites the given occurrences in the target. Occurrences count from 1.
* `rw (occs := .neg L) [e]`, where `L` is a literal list of natural numbers,
skips rewriting the given occurrences in the target. Occurrences count from 1.
Examples:
```lean
example {a b : Nat} (h : a + a = b) : (a + a) + (a + a) = b + b := by rw [h]
```
```lean
example {f : Nat -> Nat} (h : ∀ x, f x = 1) (a b : Nat) : f a = f b := by
rw [h] -- `rw` instantiates `h` only once, so this is equivalent to: `rw [h a]`
-- goal: ⊢ 1 = f b
rw [h] -- equivalent to: `rw [h b]`
```
````
## Dictionary

View File

@@ -285,7 +285,9 @@ def isDeclMeta' (env : Environment) (declName : Name) : Bool :=
-- references from any other context as compatible with both phases.
let inferFor :=
if declName.isStr && (declName.getString!.startsWith "match_" || declName.getString! == "_unsafe_rec") then declName.getPrefix else declName
isDeclMeta env inferFor
-- `isMarkedMeta` knows about non-defs such as `meta structure`, isDeclMeta knows about decls
-- implicitly marked meta
isMarkedMeta env inferFor || isDeclMeta env inferFor
/--
Given an `Expr` reference, returns the declaration name that should be considered the reference, if
@@ -336,12 +338,14 @@ where
deps := deps.union k {indMod}
return deps
abbrev Explanations := Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name))
/--
Calculates the same as `calcNeeds` but tracing each module to a use-def declaration pair or
`none` if merely a recorded extra use.
-/
def getExplanations (env : Environment) (i : ModuleIdx) :
Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name)) := Id.run do
def getExplanations (s : State) (i : ModuleIdx) : Explanations := Id.run do
let env := s.env
let mut deps := default
for ci in env.header.moduleData[i]!.constants do
-- Added guard for cases like `structure` that are still exported even if private
@@ -362,18 +366,25 @@ def getExplanations (env : Environment) (i : ModuleIdx) :
where
/-- Accumulate the results from expression `e` into `deps`. -/
visitExpr (k : NeedsKind) name e deps :=
let env := s.env
Lean.Expr.foldConsts e deps fun c deps => Id.run do
let mut deps := deps
if let some c := getDepConstName? env c then
if let some j := env.getModuleIdxFor? c then
let k := { k with isMeta := k.isMeta && !isDeclMeta' env c }
if
if let some (some (name', _)) := deps[(j, k)]? then
decide (name.toString.length < name'.toString.length)
else true
then
deps := deps.insert (j, k) (name, c)
deps := addExplanation j k name c deps
for indMod in (indirectModUseExt.getState env)[c]?.getD #[] do
if s.transDeps[i]!.has k indMod then
deps := addExplanation indMod k name (`_indirect ++ c) deps
return deps
addExplanation (j : ModuleIdx) (k : NeedsKind) (use def_ : Name) (deps : Explanations) : Explanations :=
if
if let some (some (name', _)) := deps[(j, k)]? then
decide (use.toString.length < name'.toString.length)
else true
then
deps.insert (j, k) (use, def_)
else deps
partial def initStateFromEnv (env : Environment) : State := Id.run do
let mut s := { env }
@@ -540,7 +551,7 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
let mut imp : Import := { k with module := s.modNames[j]! }
let mut j := j
if args.trace then
IO.eprintln s!"`{imp}` is needed"
IO.eprintln s!"`{imp}` is needed{if needs.has k j then " (calculated)" else ""}"
if args.addPublic && !k.isExported &&
-- also add as public if previously `public meta`, which could be from automatic porting
(s.transDepsOrig[i]!.has { k with isExported := true } j || s.transDepsOrig[i]!.has { k with isExported := true, isMeta := true } j) then
@@ -658,7 +669,7 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
modify fun s => { s with transDeps := s.transDeps.set! i newTransDepsI }
if args.explain then
let explanation := getExplanations s.env i
let explanation := getExplanations s i
let sanitize n := if n.hasMacroScopes then (sanitizeName n).run' { options := {} } else n
let run (imp : Import) := do
let j := s.env.getModuleIdx? imp.module |>.get!

441
script/build_artifact.py Executable file
View File

@@ -0,0 +1,441 @@
#!/usr/bin/env python3
"""
build_artifact.py: Download pre-built CI artifacts for a Lean commit.
Usage:
build_artifact.py # Download artifact for current HEAD
build_artifact.py --sha abc1234 # Download artifact for specific commit
build_artifact.py --clear-cache # Clear artifact cache
This script downloads pre-built binaries from GitHub Actions CI runs,
which is much faster than building from source (~30s vs 2-5min).
Artifacts are cached in ~/.cache/lean_build_artifact/ for reuse.
"""
import argparse
import json
import os
import platform
import shutil
import subprocess
import sys
import urllib.request
import urllib.error
from pathlib import Path
from typing import Optional
# Constants
GITHUB_API_BASE = "https://api.github.com"
LEAN4_REPO = "leanprover/lean4"
# CI artifact cache
CACHE_DIR = Path.home() / '.cache' / 'lean_build_artifact'
ARTIFACT_CACHE = CACHE_DIR
# Sentinel value indicating CI failed (don't bother building locally)
CI_FAILED = object()
# ANSI colors for terminal output
class Colors:
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
BOLD = '\033[1m'
RESET = '\033[0m'
def color(text: str, c: str) -> str:
"""Apply color to text if stdout is a tty."""
if sys.stdout.isatty():
return f"{c}{text}{Colors.RESET}"
return text
def error(msg: str) -> None:
"""Print error message and exit."""
print(color(f"Error: {msg}", Colors.RED), file=sys.stderr)
sys.exit(1)
def warn(msg: str) -> None:
"""Print warning message."""
print(color(f"Warning: {msg}", Colors.YELLOW), file=sys.stderr)
def info(msg: str) -> None:
"""Print info message."""
print(color(msg, Colors.BLUE), file=sys.stderr)
def success(msg: str) -> None:
"""Print success message."""
print(color(msg, Colors.GREEN), file=sys.stderr)
# -----------------------------------------------------------------------------
# Platform detection
# -----------------------------------------------------------------------------
def get_artifact_name() -> Optional[str]:
"""Get CI artifact name for current platform."""
system = platform.system()
machine = platform.machine()
if system == 'Darwin':
if machine == 'arm64':
return 'build-macOS aarch64'
return 'build-macOS' # Intel
elif system == 'Linux':
if machine == 'aarch64':
return 'build-Linux aarch64'
return 'build-Linux release'
# Windows not supported for CI artifact download
return None
# -----------------------------------------------------------------------------
# GitHub API helpers
# -----------------------------------------------------------------------------
_github_token_warning_shown = False
def get_github_token() -> Optional[str]:
"""Get GitHub token from environment or gh CLI."""
global _github_token_warning_shown
# Check environment variable first
token = os.environ.get('GITHUB_TOKEN')
if token:
return token
# Try to get token from gh CLI
try:
result = subprocess.run(
['gh', 'auth', 'token'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
except (FileNotFoundError, subprocess.TimeoutExpired):
pass
# Warn once if no token available
if not _github_token_warning_shown:
_github_token_warning_shown = True
warn("No GitHub authentication found. API rate limits may apply.")
warn("Run 'gh auth login' or set GITHUB_TOKEN to avoid rate limiting.")
return None
def github_api_request(url: str) -> dict:
"""Make a GitHub API request and return JSON response."""
headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'build-artifact'
}
token = get_github_token()
if token:
headers['Authorization'] = f'token {token}'
req = urllib.request.Request(url, headers=headers)
try:
with urllib.request.urlopen(req, timeout=30) as response:
return json.loads(response.read().decode())
except urllib.error.HTTPError as e:
if e.code == 403:
error(f"GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable to increase limit.")
elif e.code == 404:
error(f"GitHub resource not found: {url}")
else:
error(f"GitHub API error: {e.code} {e.reason}")
except urllib.error.URLError as e:
error(f"Network error accessing GitHub API: {e.reason}")
# -----------------------------------------------------------------------------
# CI artifact cache functions
# -----------------------------------------------------------------------------
def get_cache_path(sha: str) -> Path:
"""Get cache directory for a commit's artifact."""
return ARTIFACT_CACHE / sha[:12]
def is_cached(sha: str) -> bool:
"""Check if artifact for this commit is already cached and valid."""
cache_path = get_cache_path(sha)
return cache_path.exists() and (cache_path / 'bin' / 'lean').exists()
def check_zstd_support() -> bool:
"""Check if tar supports zstd compression."""
try:
result = subprocess.run(
['tar', '--zstd', '--version'],
capture_output=True,
timeout=5
)
return result.returncode == 0
except (subprocess.TimeoutExpired, FileNotFoundError):
return False
def check_gh_available() -> bool:
"""Check if gh CLI is available and authenticated."""
try:
result = subprocess.run(
['gh', 'auth', 'status'],
capture_output=True,
timeout=10
)
return result.returncode == 0
except (subprocess.TimeoutExpired, FileNotFoundError):
return False
def download_ci_artifact(sha: str, quiet: bool = False):
"""
Try to download CI artifact for a commit.
Returns:
- Path to extracted toolchain directory if available
- CI_FAILED sentinel if CI run failed (don't bother building locally)
- None if no artifact available but local build might work
"""
# Check cache first
if is_cached(sha):
return get_cache_path(sha)
artifact_name = get_artifact_name()
if artifact_name is None:
return None # Unsupported platform
cache_path = get_cache_path(sha)
try:
# Query for CI workflow run for this commit, including status
# Note: Query parameters must be in the URL for GET requests
result = subprocess.run(
['gh', 'api', f'repos/{LEAN4_REPO}/actions/runs?head_sha={sha}&per_page=100',
'--jq', r'.workflow_runs[] | select(.name == "CI") | "\(.id) \(.conclusion // "null")"'],
capture_output=True,
text=True,
timeout=30
)
if result.returncode != 0 or not result.stdout.strip():
return None # No CI run found (old commit?)
# Parse "run_id conclusion" format
line = result.stdout.strip().split('\n')[0]
parts = line.split(' ', 1)
run_id = parts[0]
conclusion = parts[1] if len(parts) > 1 else "null"
# Check if the desired artifact exists for this run
result = subprocess.run(
['gh', 'api', f'repos/{LEAN4_REPO}/actions/runs/{run_id}/artifacts',
'--jq', f'.artifacts[] | select(.name == "{artifact_name}") | .id'],
capture_output=True,
text=True,
timeout=30
)
if result.returncode != 0 or not result.stdout.strip():
# No artifact available
# If CI failed and no artifact, the build itself likely failed - skip
if conclusion == "failure":
return CI_FAILED
# Otherwise (in progress, expired, etc.) - fall back to local build
return None
# Download artifact
cache_path.mkdir(parents=True, exist_ok=True)
if not quiet:
print("downloading CI artifact... ", end='', flush=True)
result = subprocess.run(
['gh', 'run', 'download', run_id,
'-n', artifact_name,
'-R', LEAN4_REPO,
'-D', str(cache_path)],
capture_output=True,
text=True,
timeout=600 # 10 minutes for large downloads
)
if result.returncode != 0:
shutil.rmtree(cache_path, ignore_errors=True)
return None
# Extract tar.zst - find the file (name varies by platform/version)
tar_files = list(cache_path.glob('*.tar.zst'))
if not tar_files:
shutil.rmtree(cache_path, ignore_errors=True)
return None
tar_file = tar_files[0]
if not quiet:
print("extracting... ", end='', flush=True)
result = subprocess.run(
['tar', '--zstd', '-xf', tar_file.name],
cwd=cache_path,
capture_output=True,
timeout=300
)
if result.returncode != 0:
shutil.rmtree(cache_path, ignore_errors=True)
return None
# Move contents up from lean-VERSION-PLATFORM/ to cache_path/
# The extracted directory name varies (e.g., lean-4.15.0-linux, lean-4.15.0-darwin_aarch64)
extracted_dirs = [d for d in cache_path.iterdir() if d.is_dir() and d.name.startswith('lean-')]
if extracted_dirs:
extracted = extracted_dirs[0]
for item in extracted.iterdir():
dest = cache_path / item.name
if dest.exists():
if dest.is_dir():
shutil.rmtree(dest)
else:
dest.unlink()
shutil.move(str(item), str(cache_path / item.name))
extracted.rmdir()
# Clean up tar file
tar_file.unlink()
# Verify the extraction worked
if not (cache_path / 'bin' / 'lean').exists():
shutil.rmtree(cache_path, ignore_errors=True)
return None
return cache_path
except (subprocess.TimeoutExpired, FileNotFoundError):
shutil.rmtree(cache_path, ignore_errors=True)
return None
# -----------------------------------------------------------------------------
# Git helpers
# -----------------------------------------------------------------------------
def get_current_commit() -> str:
"""Get the current git HEAD commit SHA."""
try:
result = subprocess.run(
['git', 'rev-parse', 'HEAD'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
return result.stdout.strip()
error(f"Failed to get current commit: {result.stderr.strip()}")
except subprocess.TimeoutExpired:
error("Timeout getting current commit")
except FileNotFoundError:
error("git not found")
def resolve_sha(short_sha: str) -> str:
"""Resolve a (possibly short) SHA to full 40-character SHA using git rev-parse."""
if len(short_sha) == 40:
return short_sha
try:
result = subprocess.run(
['git', 'rev-parse', short_sha],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
full_sha = result.stdout.strip()
if len(full_sha) == 40:
return full_sha
error(f"Cannot resolve SHA '{short_sha}': {result.stderr.strip() or 'not found in repository'}")
except subprocess.TimeoutExpired:
error(f"Timeout resolving SHA '{short_sha}'")
except FileNotFoundError:
error("git not found - required for SHA resolution")
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description='Download pre-built CI artifacts for a Lean commit.',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
This script downloads pre-built binaries from GitHub Actions CI runs,
which is much faster than building from source (~30s vs 2-5min).
Artifacts are cached in ~/.cache/lean_build_artifact/ for reuse.
Examples:
build_artifact.py # Download for current HEAD
build_artifact.py --sha abc1234 # Download for specific commit
build_artifact.py --clear-cache # Clear cache to free disk space
"""
)
parser.add_argument('--sha', metavar='SHA',
help='Commit SHA to download artifact for (default: current HEAD)')
parser.add_argument('--clear-cache', action='store_true',
help='Clear artifact cache and exit')
parser.add_argument('--quiet', '-q', action='store_true',
help='Suppress progress messages (still prints result path)')
args = parser.parse_args()
# Handle cache clearing
if args.clear_cache:
if ARTIFACT_CACHE.exists():
size = sum(f.stat().st_size for f in ARTIFACT_CACHE.rglob('*') if f.is_file())
shutil.rmtree(ARTIFACT_CACHE)
info(f"Cleared cache at {ARTIFACT_CACHE} ({size / 1024 / 1024:.1f} MB)")
else:
info(f"Cache directory does not exist: {ARTIFACT_CACHE}")
return
# Get commit SHA
if args.sha:
sha = resolve_sha(args.sha)
else:
sha = get_current_commit()
if not args.quiet:
info(f"Commit: {sha[:12]}")
# Check prerequisites
if not check_gh_available():
error("gh CLI not available or not authenticated. Run 'gh auth login' first.")
if not check_zstd_support():
error("tar does not support zstd compression. Install zstd or a newer tar.")
artifact_name = get_artifact_name()
if artifact_name is None:
error(f"No CI artifacts available for this platform ({platform.system()} {platform.machine()})")
if not args.quiet:
info(f"Platform: {artifact_name}")
# Check cache
if is_cached(sha):
path = get_cache_path(sha)
if not args.quiet:
success("Using cached artifact")
print(path)
return
# Download artifact
result = download_ci_artifact(sha, quiet=args.quiet)
if result is CI_FAILED:
if not args.quiet:
print() # End the "downloading..." line
error(f"CI build failed for commit {sha[:12]}")
elif result is None:
if not args.quiet:
print() # End the "downloading..." line
error(f"No CI artifact available for commit {sha[:12]}")
else:
if not args.quiet:
print(color("done", Colors.GREEN))
print(result)
if __name__ == '__main__':
main()

1290
script/lean-bisect Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,307 @@
/-
Copyright Strata Contributors
SPDX-License-Identifier: Apache-2.0 OR MIT
-/
namespace Strata
namespace Python
/-
Parser and translator for some basic regular expression patterns supported by
Python's `re` library
Ref.: https://docs.python.org/3/library/re.html
Also see
https://github.com/python/cpython/blob/759a048d4bea522fda2fe929be0fba1650c62b0e/Lib/re/_parser.py
for a reference implementation.
-/
-------------------------------------------------------------------------------
inductive ParseError where
/--
`patternError` is raised when Python's `re.patternError` exception is
raised.
[Reference: Python's re exceptions](https://docs.python.org/3/library/re.html#exceptions):
"Exception raised when a string passed to one of the functions here is not a
valid regular expression (for example, it might contain unmatched
parentheses) or when some other error occurs during compilation or matching.
It is never an error if a string contains no match for a pattern."
-/
| patternError (message : String) (pattern : String) (pos : String.Pos.Raw)
/--
`unimplemented` is raised whenever we don't support some regex operations
(e.g., lookahead assertions).
-/
| unimplemented (message : String) (pattern : String) (pos : String.Pos.Raw)
deriving Repr
def ParseError.toString : ParseError String
| .patternError msg pat pos => s!"Pattern error at position {pos.byteIdx}: {msg} in pattern '{pat}'"
| .unimplemented msg pat pos => s!"Unimplemented at position {pos.byteIdx}: {msg} in pattern '{pat}'"
instance : ToString ParseError where
toString := ParseError.toString
-------------------------------------------------------------------------------
/--
Regular Expression Nodes
-/
inductive RegexAST where
/-- Single literal character: `a` -/
| char : Char RegexAST
/-- Character range: `[a-z]` -/
| range : Char Char RegexAST
/-- Alternation: `a|b` -/
| union : RegexAST RegexAST RegexAST
/-- Concatenation: `ab` -/
| concat : RegexAST RegexAST RegexAST
/-- Any character: `.` -/
| anychar : RegexAST
/-- Zero or more: `a*` -/
| star : RegexAST RegexAST
/-- One or more: `a+` -/
| plus : RegexAST RegexAST
/-- Zero or one: `a?` -/
| optional : RegexAST RegexAST
/-- Bounded repetition: `a{n,m}` -/
| loop : RegexAST Nat Nat RegexAST
/-- Start of string: `^` -/
| anchor_start : RegexAST
/-- End of string: `$` -/
| anchor_end : RegexAST
/-- Grouping: `(abc)` -/
| group : RegexAST RegexAST
/-- Empty string: `()` or `""` -/
| empty : RegexAST
/-- Complement: `[^a-z]` -/
| complement : RegexAST RegexAST
deriving Inhabited, Repr
-------------------------------------------------------------------------------
/-- Parse character class like [a-z], [0-9], etc. into union of ranges and
chars. Note that this parses `|` as a character. -/
def parseCharClass (s : String) (pos : String.Pos.Raw) : Except ParseError (RegexAST × String.Pos.Raw) := do
if pos.get? s != some '[' then throw (.patternError "Expected '[' at start of character class" s pos)
let mut i := pos.next s
-- Check for complement (negation) with leading ^
let isComplement := !i.atEnd s && i.get? s == some '^'
if isComplement then
i := i.next s
let mut result : Option RegexAST := none
-- Process each element in the character class.
while !i.atEnd s && i.get? s != some ']' do
-- Uncommenting this makes the code stop
--dbg_trace "Working" (pure ())
let some c1 := i.get? s | throw (.patternError "Invalid character in class" s i)
let i1 := i.next s
-- Check for range pattern: c1-c2.
if !i1.atEnd s && i1.get? s == some '-' then
let i2 := i1.next s
if !i2.atEnd s && i2.get? s != some ']' then
let some c2 := i2.get? s | throw (.patternError "Invalid character in range" s i2)
if c1 > c2 then
throw (.patternError s!"Invalid character range [{c1}-{c2}]: \
start character '{c1}' is greater than end character '{c2}'" s i)
let r := RegexAST.range c1 c2
-- Union with previous elements.
result := some (match result with | none => r | some prev => RegexAST.union prev r)
i := i2.next s
continue
-- Single character.
let r := RegexAST.char c1
result := some (match result with | none => r | some prev => RegexAST.union prev r)
i := i.next s
let some ast := result | throw (.patternError "Unterminated character set" s pos)
let finalAst := if isComplement then RegexAST.complement ast else ast
pure (finalAst, i.next s)
-------------------------------------------------------------------------------
/-- Parse numeric repeats like `{10}` or `{1,10}` into min and max bounds. -/
def parseBounds (s : String) (pos : String.Pos.Raw) : Except ParseError (Nat × Nat × String.Pos.Raw) := do
if pos.get? s != some '{' then throw (.patternError "Expected '{' at start of bounds" s pos)
let mut i := pos.next s
let mut numStr := ""
-- Parse first number.
while !i.atEnd s && (i.get? s).any Char.isDigit do
numStr := numStr.push ((i.get? s).get!)
i := i.next s
let some n := numStr.toNat? | throw (.patternError "Invalid minimum bound" s pos)
-- Check for comma (range) or closing brace (exact count).
match i.get? s with
| some '}' => pure (n, n, i.next s) -- {n} means exactly n times.
| some ',' =>
i := i.next s
-- Parse maximum bound
numStr := ""
while !i.atEnd s && (i.get? s).any Char.isDigit do
numStr := numStr.push ((i.get? s).get!)
i := i.next s
let some max := numStr.toNat? | throw (.patternError "Invalid maximum bound" s i)
if i.get? s != some '}' then throw (.patternError "Expected '}' at end of bounds" s i)
-- Validate bounds order
if max < n then
throw (.patternError s!"Invalid repeat bounds \{{n},{max}}: \
maximum {max} is less than minimum {n}" s pos)
pure (n, max, i.next s)
| _ => throw (.patternError "Invalid bounds syntax" s i)
-------------------------------------------------------------------------------
mutual
/--
Parse atom: single element (char, class, anchor, group) with optional
quantifier. Stops at the first `|`.
-/
partial def parseAtom (s : String) (pos : String.Pos.Raw) : Except ParseError (RegexAST × String.Pos.Raw) := do
if pos.atEnd s then throw (.patternError "Unexpected end of regex" s pos)
let some c := pos.get? s | throw (.patternError "Invalid position" s pos)
-- Detect invalid quantifier at start
if c == '*' || c == '+' || c == '{' || c == '?' then
throw (.patternError s!"Quantifier '{c}' at position {pos} has nothing to quantify" s pos)
-- Detect unbalanced closing parenthesis
if c == ')' then
throw (.patternError "Unbalanced parenthesis" s pos)
-- Parse base element (anchor, char class, group, anychar, escape, or single char).
let (base, nextPos) match c with
| '^' => pure (RegexAST.anchor_start, pos.next s)
| '$' => pure (RegexAST.anchor_end, pos.next s)
| '[' => parseCharClass s pos
| '(' => parseExplicitGroup s pos
| '.' => pure (RegexAST.anychar, pos.next s)
| '\\' =>
-- Handle escape sequence.
-- Note: Python uses a single backslash as an escape character, but Lean
-- strings need to escape that. After DDMification, we will see two
-- backslashes in Strata for every Python backslash.
let nextPos := pos.next s
if nextPos.atEnd s then throw (.patternError "Incomplete escape sequence at end of regex" s pos)
let some escapedChar := nextPos.get? s | throw (.patternError "Invalid escape position" s nextPos)
-- Check for special sequences (unsupported right now).
match escapedChar with
| 'A' | 'b' | 'B' | 'd' | 'D' | 's' | 'S' | 'w' | 'W' | 'z' | 'Z' =>
throw (.unimplemented s!"Special sequence \\{escapedChar} is not supported" s pos)
| 'a' | 'f' | 'n' | 'N' | 'r' | 't' | 'u' | 'U' | 'v' | 'x' =>
throw (.unimplemented s!"Escape sequence \\{escapedChar} is not supported" s pos)
| c =>
if c.isDigit then
throw (.unimplemented s!"Backreference \\{c} is not supported" s pos)
else
pure (RegexAST.char escapedChar, nextPos.next s)
| _ => pure (RegexAST.char c, pos.next s)
-- Check for numeric repeat suffix on base element (but not on anchors)
match base with
| .anchor_start | .anchor_end => pure (base, nextPos)
| _ =>
if !nextPos.atEnd s then
match nextPos.get? s with
| some '{' =>
let (min, max, finalPos) parseBounds s nextPos
pure (RegexAST.loop base min max, finalPos)
| some '*' =>
let afterStar := nextPos.next s
if !afterStar.atEnd s then
match afterStar.get? s with
| some '?' => throw (.unimplemented "Non-greedy quantifier *? is not supported" s nextPos)
| some '+' => throw (.unimplemented "Possessive quantifier *+ is not supported" s nextPos)
| _ => pure (RegexAST.star base, afterStar)
else pure (RegexAST.star base, afterStar)
| some '+' =>
let afterPlus := nextPos.next s
if !afterPlus.atEnd s then
match afterPlus.get? s with
| some '?' => throw (.unimplemented "Non-greedy quantifier +? is not supported" s nextPos)
| some '+' => throw (.unimplemented "Possessive quantifier ++ is not supported" s nextPos)
| _ => pure (RegexAST.plus base, afterPlus)
else pure (RegexAST.plus base, afterPlus)
| some '?' =>
let afterQuestion := nextPos.next s
if !afterQuestion.atEnd s then
match afterQuestion.get? s with
| some '?' => throw (.unimplemented "Non-greedy quantifier ?? is not supported" s nextPos)
| some '+' => throw (.unimplemented "Possessive quantifier ?+ is not supported" s nextPos)
| _ => pure (RegexAST.optional base, afterQuestion)
else pure (RegexAST.optional base, afterQuestion)
| _ => pure (base, nextPos)
else
pure (base, nextPos)
/-- Parse explicit group with parentheses. -/
partial def parseExplicitGroup (s : String) (pos : String.Pos.Raw) : Except ParseError (RegexAST × String.Pos.Raw) := do
if pos.get? s != some '(' then throw (.patternError "Expected '(' at start of group" s pos)
let mut i := pos.next s
-- Check for extension notation (?...
if !i.atEnd s && i.get? s == some '?' then
let i1 := i.next s
if !i1.atEnd s then
match i1.get? s with
| some '=' => throw (.unimplemented "Positive lookahead (?=...) is not supported" s pos)
| some '!' => throw (.unimplemented "Negative lookahead (?!...) is not supported" s pos)
| _ => throw (.unimplemented "Extension notation (?...) is not supported" s pos)
let (inner, finalPos) parseGroup s i (some ')')
pure (.group inner, finalPos)
/-- Parse group: handles alternation and concatenation at current scope. -/
partial def parseGroup (s : String) (pos : String.Pos.Raw) (endChar : Option Char) :
Except ParseError (RegexAST × String.Pos.Raw) := do
let mut alternatives : List (List RegexAST) := [[]]
let mut i := pos
-- Parse until end of string or `endChar`.
while !i.atEnd s && (endChar.isNone || i.get? s != endChar) do
if i.get? s == some '|' then
-- Push a new scope to `alternatives`.
alternatives := [] :: alternatives
i := i.next s
else
let (ast, nextPos) parseAtom s i
alternatives := match alternatives with
| [] => [[ast]]
| head :: tail => (ast :: head) :: tail
i := nextPos
-- Check for expected end character.
if let some ec := endChar then
if i.get? s != some ec then
throw (.patternError s!"Expected '{ec}'" s i)
i := i.next s
-- Build result: concatenate each alternative, then union them.
let concatAlts := alternatives.reverse.filterMap fun alt =>
match alt.reverse with
| [] => -- Empty regex.
some (.empty)
| [single] => some single
| head :: tail => some (tail.foldl RegexAST.concat head)
match concatAlts with
| [] => pure (.empty, i)
| [single] => pure (single, i)
| head :: tail => pure (tail.foldl RegexAST.union head, i)
end
/-- info: Except.ok (Strata.Python.RegexAST.range 'A' 'z', { byteIdx := 5 }) -/
#guard_msgs in
#eval parseCharClass "[A-z]" 0
-- Test code: Print done
#print "Done!"

View File

@@ -50,12 +50,26 @@ repositories:
dependencies:
- lean4-cli
- name: lean4-unicode-basic
url: https://github.com/fgdorais/lean4-unicode-basic
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: BibtexQuery
url: https://github.com/dupuisf/BibtexQuery
toolchain-tag: true
stable-branch: false
branch: master
dependencies: [lean4-unicode-basic]
- name: doc-gen4
url: https://github.com/leanprover/doc-gen4
toolchain-tag: true
stable-branch: false
branch: main
dependencies: [lean4-cli]
dependencies: [lean4-cli, BibtexQuery]
- name: reference-manual
url: https://github.com/leanprover/reference-manual
@@ -113,10 +127,30 @@ repositories:
dependencies:
- mathlib4
- name: verso-web-components
url: https://github.com/leanprover/verso-web-components
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- verso
- name: lean-fro.org
url: https://github.com/leanprover/lean-fro.org
toolchain-tag: false
stable-branch: false
branch: master
dependencies:
- verso
- verso-web-components
- name: comparator
url: https://github.com/leanprover/comparator
toolchain-tag: true
stable-branch: false
branch: master
- name: lean4export
url: https://github.com/leanprover/lean4export
toolchain-tag: true
stable-branch: false
branch: master

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 27)
set(LEAN_VERSION_MINOR 28)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -695,7 +695,7 @@ endif()
set(STDLIBS Init Std Lean Leanc)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
list(APPEND STDLIBS Lake)
list(APPEND STDLIBS Lake LeanChecker)
endif()
add_custom_target(make_stdlib ALL
@@ -758,6 +758,12 @@ if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
DEPENDS lake_shared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make lake
VERBATIM)
add_custom_target(leanchecker ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_shared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanchecker
VERBATIM)
endif()
if(PREV_STAGE)

View File

@@ -102,7 +102,7 @@ noncomputable def strongIndefiniteDescription {α : Sort u} (p : α → Prop) (h
xp.val, fun _ => xp.property)
(fun hp => choice h, fun h => absurd h hp)
/-- the Hilbert epsilon Function -/
/-- The Hilbert epsilon function. -/
noncomputable def epsilon {α : Sort u} [h : Nonempty α] (p : α Prop) : α :=
(strongIndefiniteDescription p h).val

View File

@@ -16,3 +16,4 @@ public import Init.Control.Option
public import Init.Control.Lawful
public import Init.Control.StateCps
public import Init.Control.ExceptCps
public import Init.Control.MonadAttach

View File

@@ -144,7 +144,7 @@ instance : ToBool Bool where
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns it and ignores
`y`; otherwise, runs `y` and returns its result.
This a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
This is a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
operator.
-/
@[macro_inline] def orM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
@@ -161,7 +161,7 @@ recommended_spelling "orM" for "<||>" in [orM, «term_<||>_»]
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns `y`; otherwise,
returns the original result of `x`.
This a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
This is a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
operator.
-/
@[macro_inline] def andM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do

View File

@@ -25,6 +25,12 @@ instance [Repr ε] [Repr α] : Repr (Result ε σ α) where
| Result.error e _, prec => Repr.addAppParen ("EStateM.Result.error " ++ reprArg e) prec
| Result.ok a _, prec => Repr.addAppParen ("EStateM.Result.ok " ++ reprArg a) prec
instance : MonadAttach (EStateM ε σ) where
CanReturn x a := Exists fun s => Exists fun s' => x.run s = .ok a s'
attach x s := match h : x s with
| .ok a s' => .ok a, s, s', h s'
| .error e s' => .error e s'
end EStateM
namespace EStateM

View File

@@ -329,3 +329,8 @@ instance ExceptT.finally {m : Type u → Type v} {ε : Type u} [MonadFinally m]
| (.ok a, .ok b) => pure (.ok (a, b))
| (_, .error e) => pure (.error e) -- second error has precedence
| (.error e, _) => pure (.error e)
instance [Monad m] [MonadAttach m] : MonadAttach (ExceptT ε m) where
CanReturn x a := MonadAttach.CanReturn (m := m) x (.ok a)
attach x := show m (Except ε _) from
(fun a, h => match a with | .ok a => .ok a, h | .error e => .error e) <$> MonadAttach.attach (m := m) x

View File

@@ -75,6 +75,13 @@ instance [Monad m] : MonadLift m (ExceptCpsT σ m) where
instance [Inhabited ε] : Inhabited (ExceptCpsT ε m α) where
default := fun _ _ k₂ => k₂ default
/--
For continuation monads, it is not possible to provide a computable `MonadAttach` instance that
actually adds information about the return value. Therefore, this instance always attaches a proof
of `True`.
-/
instance : MonadAttach (ExceptCpsT ε m) := .trivial
@[simp] theorem run_pure [Monad m] : run (pure x : ExceptCpsT ε m α) = pure (Except.ok x) := rfl
@[simp] theorem run_lift {α ε : Type u} [Monad m] (x : m α) : run (ExceptCpsT.lift x : ExceptCpsT ε m α) = (x >>= fun a => pure (Except.ok a) : m (Except ε α)) := rfl

View File

@@ -9,6 +9,7 @@ module
prelude
public import Init.Core
public import Init.Control.MonadAttach
public section
@@ -67,4 +68,15 @@ instance [OfNat α n] : OfNat (Id α) n :=
instance {m : Type u Type v} [Pure m] : MonadLiftT Id m where
monadLift x := pure x.run
instance : MonadAttach Id where
CanReturn x a := x.run = a
attach x := pure x.run, rfl
instance : LawfulMonadAttach Id where
map_attach := rfl
canReturn_map_imp := by
intro _ _ x _ h
cases h
exact x.run.2
end Id

View File

@@ -10,3 +10,4 @@ public import Init.Control.Lawful.Basic
public import Init.Control.Lawful.Instances
public import Init.Control.Lawful.Lemmas
public import Init.Control.Lawful.MonadLift
public import Init.Control.Lawful.MonadAttach

View File

@@ -248,10 +248,10 @@ namespace Id
instance : LawfulMonad Id := by
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
@[simp] theorem run_map (x : Id α) (f : α β) : (f <$> x).run = f x.run := rfl
@[simp] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp] theorem pure_run (a : Id α) : pure a.run = a := rfl
@[simp, grind =] theorem run_map (x : Id α) (f : α β) : (f <$> x).run = f x.run := rfl
@[simp, grind =] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp, grind =] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp, grind =] theorem pure_run (a : Id α) : pure a.run = a := rfl
@[simp] theorem run_seqRight (x y : Id α) : (x *> y).run = y.run := rfl
@[simp] theorem run_seqLeft (x y : Id α) : (x <* y).run = x.run := rfl
@[simp] theorem run_seq (f : Id (α β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl

View File

@@ -0,0 +1,10 @@
/-
Copyright (c) 2025 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.Control.Lawful.MonadAttach.Lemmas
public import Init.Control.Lawful.MonadAttach.Instances

View File

@@ -0,0 +1,86 @@
/-
Copyright (c) 2025 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.Control.Reader
public import Init.Control.Lawful.Instances
import Init.Control.Lawful.MonadAttach.Lemmas
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (ReaderT ρ m) where
map_attach := by
simp only [Functor.map, MonadAttach.attach, Functor.map_map, WeaklyLawfulMonadAttach.map_attach]
intros; rfl
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [LawfulMonadAttach m] :
LawfulMonadAttach (ReaderT ρ m) where
canReturn_map_imp := by
simp only [Functor.map, MonadAttach.CanReturn, ReaderT.run]
rintro _ _ x a r, h
apply LawfulMonadAttach.canReturn_map_imp h
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (StateT σ m) where
map_attach := by
intro α x
simp only [Functor.map, StateT, funext_iff, StateT.map, bind_pure_comp, MonadAttach.attach,
Functor.map_map]
exact fun s => WeaklyLawfulMonadAttach.map_attach
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [LawfulMonadAttach m] :
LawfulMonadAttach (StateT σ m) where
canReturn_map_imp := by
simp only [Functor.map, MonadAttach.CanReturn, StateT.run, StateT.map, bind_pure_comp]
rintro _ _ x a s, s', h
obtain a, h, h' := LawfulMonadAttach.canReturn_map_imp' h
cases h'
exact a.1.2
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (ExceptT ε m) where
map_attach {α} x := by
simp only [Functor.map, MonadAttach.attach, ExceptT.map]
simp
conv => rhs; rw [ WeaklyLawfulMonadAttach.map_attach (m := m) (x := x)]
simp only [map_eq_pure_bind]
apply bind_congr; intro a
match a with
| .ok _, _ => simp
| .error _, _ => simp
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [LawfulMonadAttach m] :
LawfulMonadAttach (ExceptT ε m) where
canReturn_map_imp {α P x a} := by
simp only [Functor.map, MonadAttach.CanReturn, ExceptT.map, ExceptT.mk]
let x' := (fun a => show Subtype (fun a : Except _ _ => match a with | .ok a => P a | .error e => True) from match a with | .ok a => .ok a.1 | .error e => .error e, by cases a <;> simp [Subtype.property]) <$> show m _ from x
have := LawfulMonadAttach.canReturn_map_imp (m := m) (x := x') (a := .ok a)
simp only at this
intro h
apply this
simp only [x', map_eq_pure_bind, bind_assoc]
refine cast ?_ h
congr 1
apply bind_congr; intro a
split <;> simp
public instance [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (StateRefT' ω σ m) :=
inferInstanceAs (WeaklyLawfulMonadAttach (ReaderT _ _))
public instance [Monad m] [MonadAttach m] [LawfulMonad m] [LawfulMonadAttach m] :
LawfulMonadAttach (StateRefT' ω σ m) :=
inferInstanceAs (LawfulMonadAttach (ReaderT _ _))
section
attribute [local instance] MonadAttach.trivial
public instance [Monad m] [LawfulMonad m] :
WeaklyLawfulMonadAttach m where
map_attach := by simp [MonadAttach.attach]
end

View File

@@ -0,0 +1,90 @@
/-
Copyright (c) 2025 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.Control.MonadAttach
import all Init.Control.MonadAttach
public import Init.Control.Lawful.Lemmas
public import Init.Control.Lawful.MonadLift.Lemmas
public theorem LawfulMonadAttach.canReturn_bind_imp' [Monad m] [LawfulMonad m]
[MonadAttach m] [LawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.CanReturn (x >>= f) b Exists fun a => MonadAttach.CanReturn x a MonadAttach.CanReturn (f a) b := by
intro h
let P (b : β) := Exists fun a => MonadAttach.CanReturn x a MonadAttach.CanReturn (f a) b
have h' : (x >>= f) = Subtype.val <$> (MonadAttach.attach x >>= (fun a => (do
let b MonadAttach.attach (f a)
return b.1, a.1, a.2, b.2 : m (Subtype P)))) := by
simp only [map_bind, map_pure]
simp only [bind_pure_comp, WeaklyLawfulMonadAttach.map_attach]
rw (occs := [1]) [ WeaklyLawfulMonadAttach.map_attach (x := x)]
simp
rw [h'] at h
have := LawfulMonadAttach.canReturn_map_imp h
exact this
public theorem LawfulMonadAttach.eq_of_canReturn_pure [Monad m] [MonadAttach m]
[LawfulMonad m] [LawfulMonadAttach m] {a b : α}
(h : MonadAttach.CanReturn (m := m) (pure a) b) :
a = b := by
let x : m (Subtype (a = ·)) := pure a, rfl
have : pure a = Subtype.val <$> x := by simp [x]
rw [this] at h
exact LawfulMonadAttach.canReturn_map_imp h
public theorem LawfulMonadAttach.canReturn_map_imp' [Monad m] [LawfulMonad m]
[MonadAttach m] [LawfulMonadAttach m]
{x : m α} {f : α β} :
MonadAttach.CanReturn (f <$> x) b Exists fun a => MonadAttach.CanReturn x a f a = b := by
rw [map_eq_pure_bind]
intro h
obtain a, h, h' := canReturn_bind_imp' h
exact a, h, eq_of_canReturn_pure h'
public theorem LawfulMonadAttach.canReturn_liftM_imp'
[Monad m] [MonadAttach m] [LawfulMonad m] [LawfulMonadAttach m]
[Monad n] [MonadAttach n] [LawfulMonad n] [LawfulMonadAttach n]
[MonadLiftT m n] [LawfulMonadLiftT m n] {x : m α} {a : α} :
MonadAttach.CanReturn (liftM (n := n) x) a MonadAttach.CanReturn x a := by
intro h
simp only [ WeaklyLawfulMonadAttach.map_attach (x := x), liftM_map] at h
exact canReturn_map_imp h
public theorem WeaklyLawfulMonadAttach.attach_bind_val
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.attach x >>= (fun a => f a.val) = x >>= f := by
conv => rhs; simp only [ map_attach (x := x), bind_map_left]
public theorem WeaklyLawfulMonadAttach.bind_attach_of_nonempty
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [Nonempty (m β)]
{x : m α} {f : Subtype (MonadAttach.CanReturn x) m β} :
open scoped Classical in
MonadAttach.attach x >>= f = x >>= (fun a => if ha : MonadAttach.CanReturn x a then f a, ha else Classical.ofNonempty) := by
conv => rhs; simp +singlePass only [ map_attach (x := x)]
simp [Subtype.property]
public theorem MonadAttach.attach_bind_eq_pbind
[Monad m] [MonadAttach m]
{x : m α} {f : Subtype (MonadAttach.CanReturn x) m β} :
MonadAttach.attach x >>= f = MonadAttach.pbind x (fun a ha => f a, ha) := by
simp [MonadAttach.pbind]
public theorem WeaklyLawfulMonadAttach.pbind_eq_bind
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.pbind x (fun a _ => f a) = x >>= f := by
conv => rhs; rw [ map_attach (x := x)]
simp [MonadAttach.pbind]
public theorem WeaklyLawfulMonadAttach.pbind_eq_bind'
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.pbind x (fun a _ => f a) = x >>= f := by
conv => rhs; rw [ map_attach (x := x)]
simp [MonadAttach.pbind]

View File

@@ -6,6 +6,7 @@ Authors: Quang Dao
module
prelude
public import Init.Control.Id
public import Init.Control.Lawful.Basic
public import Init.Control.Lawful.MonadLift.Basic
@@ -13,6 +14,14 @@ public section
universe u v w
theorem instMonadLiftTOfMonadLift_instMonadLiftTOfPure [Monad m] [Monad n] {_ : MonadLift m n}
[LawfulMonadLift m n] : instMonadLiftTOfMonadLift Id m n = Id.instMonadLiftTOfPure := by
have hext {a b : MonadLiftT Id n} (h : @a.monadLift = @b.monadLift) : a = b := by
cases a <;> cases b <;> simp_all
apply hext
ext α x
simp [monadLift, LawfulMonadLift.monadLift_pure]
variable {m : Type u Type v} {n : Type u Type w} [Monad m] [Monad n] [MonadLiftT m n]
[LawfulMonadLiftT m n] {α β : Type u}

View File

@@ -0,0 +1,126 @@
/-
Copyright (c) 2025 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.Control.Basic
set_option linter.all true
set_option doc.verso true
/-!
# {name (scope := "Init.Control.MonadAttach")}`MonadAttach`
This module provides a mechanism for attaching proofs to the return values of monadic computations,
producing a new monadic computation returning a {name}`Subtype`.
This function is primarily used to allow definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion) that sequence computations using
{name}`Bind.bind` (`>>=`) to prove properties about the return values of prior computations when
a recursive call happens.
This allows the well-founded recursion mechanism to prove that the function terminates.
-/
-- verso docstring is added below
set_option linter.missingDocs false in
public class MonadAttach (m : Type u Type v) where
/--
A predicate that can be assumed to be true for all return values {name}`a` of actions {name}`x`
in {name}`m`, in all situations.
-/
CanReturn {α : Type u} : (x : m α) (a : α) Prop
/--
Attaches a proof of {name}`MonadAttach.CanReturn` to the return value of {name}`x`. This proof
can be used to prove the termination of well-founded recursive functions.
-/
attach {α : Type u} (x : m α) : m (Subtype (CanReturn x))
-- verso docstring is added below
set_option linter.missingDocs false in
public class WeaklyLawfulMonadAttach (m : Type u Type v) [Monad m] [MonadAttach m] where
map_attach {α : Type u} {x : m α} : Subtype.val <$> MonadAttach.attach x = x
/--
This type class ensures that {name}`MonadAttach.CanReturn` is the unique strongest possible
postcondition.
-/
public class LawfulMonadAttach (m : Type u Type v) [Monad m] [MonadAttach m] extends
WeaklyLawfulMonadAttach m where
canReturn_map_imp {α : Type u} {P : α Prop} {x : m (Subtype P)} {a : α} :
MonadAttach.CanReturn (Subtype.val <$> x) a P a
/--
Like {name}`Bind.bind`, {name}`pbind` sequences two computations {lean}`x : m α` and {lean}`f`,
allowing the second to depend on the value computed by the first.
But other than with {name}`Bind.bind`, the second computation can also depend on a proof that
the return value {given}`a` of {name}`x` satisfies {lean}`MonadAttach.CanReturn x a`.
-/
public def MonadAttach.pbind [Monad m] [MonadAttach m]
(x : m α) (f : (a : α) MonadAttach.CanReturn x a m β) : m β :=
MonadAttach.attach x >>= (fun a, ha => f a ha)
/--
A {lean}`MonadAttach` instance where all return values are possible and {name}`attach` adds no
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]
public protected def MonadAttach.trivial {m : Type u Type v} [Monad m] : MonadAttach m where
CanReturn _ _ := True
attach x := (·, .intro) <$> x
section
variable (α : Type u) [ m, Monad m] [ m, MonadAttach m]
set_option doc.verso true
/--
For every {given}`x : m α`, this type class provides a predicate {lean}`MonadAttach.CanReturn x`
and a way to attach a proof of this predicate to the return values of {name}`x` by providing
an element {lean}`MonadAttach.attach x` of {lean}`m { a : α // MonadAttach.CanReturn x a }`.
Instances should abide the law {lean}`Subtype.val <$> MonadAttach.attach x = x`, which is encoded by
the {name}`WeaklyLawfulMonadAttach` type class. The stronger type class {name}`LawfulMonadAttach`
ensures that {lean}`MonadAttach.CanReturn x` is the _unique_ strongest possible predicate.
Similarly to {name (scope := "Init.Data.List.Attach")}`List.attach`, the purpose of
{name}`MonadAttach` is to attach proof terms necessary for well-founded termination proofs.
The iterator library relies on {name}`MonadAttach` for combinators such as
{name (scope := "Init.Data.Iterators")}`Std.Iter.filterM` in order to automatically attach
information about the monadic predicate's behavior that could be relevant for the termination
behavior of the iterator.
*Limitations*:
For many monads, there is a strongly lawful {lean}`MonadAttach` instance, but there are exceptions.
For example, there is no way to provide a computable {lean}`MonadAttach` instance for the CPS monad
transformers
{name (scope := "Init.Control.StateCps")}`StateCpsT` and
{name (scope := "Init.Control.StateCps")}`ExceptCpsT` with a predicate that is not always
{name}`True`. Therefore, such CPS monads only provide the trivial {lean}`MonadAttach` instance
{lean}`MonadAttach.trivial` together with {name}`WeaklyLawfulMonadAttach`, but without
{name}`LawfulMonadAttach`.
For most monads with side effects, {lean}`MonadAttach` is too weak to fully capture the behavior of
computations because the postcondition represented by {name}`MonadAttach.CanReturn` neither depends
on the prior internal state of the monad, nor does it contain information about how the state of the
monad changes with the computation.
-/
add_decl_doc MonadAttach
/--
This type class ensures that every monadic action {given}`x : m α` can be recovered by stripping the
proof component from the subtypes returned by
{lean}`(MonadAttach.attach x) : m { a : α // MonadAttach.CanReturn x a }` . In other words,
the type class ensures that {lean}`Subtype.val <$> MonadAttach.attach x = x`.
-/
add_decl_doc WeaklyLawfulMonadAttach
end

View File

@@ -112,6 +112,12 @@ instance (ε : Type u) [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where
throw e := OptionT.mk <| throwThe ε e
tryCatch x handle := OptionT.mk <| tryCatchThe ε x handle
instance [MonadAttach m] : MonadAttach (OptionT m) where
CanReturn x a := MonadAttach.CanReturn x.run (some a)
attach x := .mk ((fun
| some a, h => some a, h
| none, _ => none) <$> MonadAttach.attach x.run)
end OptionT
instance [Monad m] : MonadControl m (OptionT m) where

View File

@@ -51,3 +51,7 @@ A monad with access to a read-only value of type `ρ`. The value can be locally
`withReader`, but it cannot be mutated.
-/
abbrev ReaderM (ρ : Type u) := ReaderT ρ Id
instance [Monad m] [MonadAttach m] : MonadAttach (ReaderT ρ m) where
CanReturn x a := Exists (fun r => MonadAttach.CanReturn (x.run r) a)
attach x := fun r => (fun a, h => a, r, h) <$> MonadAttach.attach (x.run r)

View File

@@ -204,3 +204,7 @@ instance StateT.tryFinally {m : Type u → Type v} {σ : Type u} [MonadFinally m
| some (a, s') => h (some a) s'
| none => h none s
pure ((a, b), s'')
instance [Monad m] [MonadAttach m] : MonadAttach (StateT σ m) where
CanReturn x a := Exists fun s => Exists fun s' => MonadAttach.CanReturn (x.run s) (a, s')
attach x := fun s => (fun a, s', h => a, s, s', h, s') <$> MonadAttach.attach (x.run s)

View File

@@ -68,6 +68,13 @@ instance : MonadStateOf σ (StateCpsT σ m) where
set s := fun _ _ k => k s
modifyGet f := fun _ s k => let (a, s) := f s; k a s
/--
For continuation monads, it is not possible to provide a computable `MonadAttach` instance that
actually adds information about the return value. Therefore, this instance always attaches a proof
of `True`.
-/
instance : MonadAttach (StateCpsT ε m) := .trivial
/--
Runs an action from the underlying monad in the monad with state. The state is not modified.

View File

@@ -64,6 +64,7 @@ instance [Monad m] : Monad (StateRefT' ω σ m) := inferInstanceAs (Monad (Reade
instance : MonadLift m (StateRefT' ω σ m) := StateRefT'.lift
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
instance [Monad m] [MonadAttach m] : MonadAttach (StateRefT' ω σ m) := inferInstanceAs (MonadAttach (ReaderT _ _))
/--
Retrieves the current value of the monad's mutable state.

View File

@@ -13,6 +13,10 @@ public import Init.SizeOf
public section
set_option linter.missingDocs true -- keep it documented
-- BEq instance for Option defined here so it's available early in the import chain
-- (before Init.Grind.Config and Init.MetaTypes which need BEq (Option Nat))
deriving instance BEq for Option
@[expose] section
universe u v w
@@ -337,7 +341,7 @@ inductive Exists {α : Sort u} (p : α → Prop) : Prop where
An indication of whether a loop's body terminated early that's used to compile the `for x in xs`
notation.
A collection's `ForIn` or `ForIn'` instance describe's how to iterate over its elements. The monadic
A collection's `ForIn` or `ForIn'` instance describes how to iterate over its elements. The monadic
action that represents the body of the loop returns a `ForInStep α`, where `α` is the local state
used to implement features such as `let mut`.
-/
@@ -510,12 +514,12 @@ abbrev SSuperset [HasSSubset α] (a b : α) := SSubset b a
/-- Notation type class for the union operation ``. -/
class Union (α : Type u) where
/-- `a b` is the union of`a` and `b`. -/
/-- `a b` is the union of `a` and `b`. -/
union : α α α
/-- Notation type class for the intersection operation `∩`. -/
class Inter (α : Type u) where
/-- `a ∩ b` is the intersection of`a` and `b`. -/
/-- `a ∩ b` is the intersection of `a` and `b`. -/
inter : α α α
/-- Notation type class for the set difference `\`. -/
@@ -538,10 +542,10 @@ infix:50 " ⊇ " => Superset
/-- Strict superset relation: `a ⊃ b` -/
infix:50 "" => SSuperset
/-- `a b` is the union of`a` and `b`. -/
/-- `a b` is the union of `a` and `b`. -/
infixl:65 " " => Union.union
/-- `a ∩ b` is the intersection of`a` and `b`. -/
/-- `a ∩ b` is the intersection of `a` and `b`. -/
infixl:70 "" => Inter.inter
/--

View File

@@ -589,6 +589,8 @@ unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Mon
if start < stop then
if stop as.size then
fold (USize.ofNat start) (USize.ofNat stop) init
else if start < as.size then
fold (USize.ofNat start) (USize.ofNat as.size) init
else
pure init
else

View File

@@ -125,6 +125,22 @@ instance instDecidableEmpEq (ys : Array α) : Decidable (#[] = ys) :=
| [] => isTrue rfl
| _ :: _ => isFalse (fun h => Array.noConfusion rfl (heq_of_eq h) (fun h => List.noConfusion rfl h))
@[inline]
def instDecidableEqEmpImpl (xs : Array α) : Decidable (xs = #[]) :=
decidable_of_iff xs.isEmpty <| by rcases xs with <;> simp [Array.isEmpty]
@[inline]
def instDecidableEmpEqImpl (xs : Array α) : Decidable (#[] = xs) :=
decidable_of_iff xs.isEmpty <| by rcases xs with <;> simp [Array.isEmpty]
@[csimp]
theorem instDecidableEqEmp_csimp : @instDecidableEqEmp = @instDecidableEqEmpImpl :=
Subsingleton.allEq _ _
@[csimp]
theorem instDecidableEmpEq_csimp : @instDecidableEmpEq = @instDecidableEmpEqImpl :=
Subsingleton.allEq _ _
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
(xs == ys) = if h : xs.size = ys.size then
decide ( (i : Nat) (h' : i < xs.size), xs[i] == ys[i]'(h h')) else false := by

View File

@@ -62,6 +62,9 @@ theorem eq_empty_of_size_eq_zero (h : xs.size = 0) : xs = #[] := by
cases xs
simp_all
grind_pattern eq_empty_of_size_eq_zero => xs.size where
guard xs.size = 0
theorem ne_empty_of_size_eq_add_one (h : xs.size = n + 1) : xs #[] := by
cases xs
simpa using List.ne_nil_of_length_eq_add_one h
@@ -112,7 +115,8 @@ theorem none_eq_getElem?_iff {xs : Array α} {i : Nat} : none = xs[i]? ↔ xs.si
theorem getElem?_eq_none {xs : Array α} (h : xs.size i) : xs[i]? = none := by
simp [h]
grind_pattern Array.getElem?_eq_none => xs.size, xs[i]?
grind_pattern Array.getElem?_eq_none => xs.size, xs[i]? where
guard xs.size i
@[simp] theorem getElem?_eq_getElem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i]? = some xs[i] :=
getElem?_pos ..

View File

@@ -290,7 +290,7 @@ Lean convention that division by zero returns zero.
Examples:
* `(7#4).sdiv 2 = 3#4`
* `(-9#4).sdiv 2 = -4#4`
* `(-8#4).sdiv 2 = -4#4`
* `(5#4).sdiv -2 = -2#4`
* `(-7#4).sdiv (-2) = 3#4`
-/
@@ -864,4 +864,17 @@ def clz (x : BitVec w) : BitVec w := clzAuxRec x (w - 1)
/-- Count the number of trailing zeros. -/
def ctz (x : BitVec w) : BitVec w := (x.reverse).clz
/-- Count the number of bits with value `1` downward from the `pos`-th bit to the
`0`-th bit of `x`, storing the result in `acc`. -/
def cpopNatRec (x : BitVec w) (pos acc : Nat) : Nat :=
match pos with
| 0 => acc
| n + 1 => x.cpopNatRec n (acc + (x.getLsbD n).toNat)
/-- Population count operation, to count the number of bits with value `1` in `x`.
Also known as `popcount`, `popcnt`.
-/
@[suggest_for BitVec.popcount BitVec.popcnt]
def cpop (x : BitVec w) : BitVec w := BitVec.ofNat w (cpopNatRec x w 0)
end BitVec

View File

@@ -67,6 +67,9 @@ theorem none_eq_getElem?_iff {l : BitVec w} : none = l[n]? ↔ w ≤ n := by
@[simp]
theorem getElem?_eq_none {l : BitVec w} (h : w n) : l[n]? = none := getElem?_eq_none_iff.mpr h
grind_pattern BitVec.getElem?_eq_none => l[n]? where
guard w n
theorem getElem?_eq (l : BitVec w) (i : Nat) :
l[i]? = if h : i < w then some l[i] else none := by
split <;> simp_all
@@ -1019,6 +1022,14 @@ theorem setWidth_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
rw [Nat.mod_mod_of_dvd]
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
@[simp]
theorem setWidth_ofNat_of_le_of_lt {x : Nat} (h : w v) (h' : x < 2 ^ w) :
setWidth v (BitVec.ofNat w x) = BitVec.ofNat v x := by
apply BitVec.eq_of_toNat_eq
have := Nat.pow_le_pow_of_le (a := 2) (m := v) (n := w) (by omega) h
simp only [toNat_setWidth, toNat_ofNat]
rw [Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt (by omega)]
/--
Iterated `setWidth` agrees with the second `setWidth`
except in the case the first `setWidth` is a non-trivial truncation,
@@ -1252,11 +1263,31 @@ theorem extractLsb'_setWidth_of_le {b : BitVec w} {start len w' : Nat} (h : star
simp
omega
@[simp]
theorem extractLsb_setWidth_of_lt {x : BitVec w} {hi lo v : Nat} (h : lo + hi < v) :
(x.setWidth v).extractLsb hi lo = x.extractLsb hi lo := by
simp only [BitVec.extractLsb]
ext k hk
simp
omega
theorem setWidth_extractLsb'_of_le {c : BitVec w} (h : len₁ len₂) :
(c.extractLsb' start len₂).setWidth len₁ = c.extractLsb' start len₁ := by
ext i hi
simp [show i < len₂ by omega]
theorem extractLsb'_cast {x : BitVec w} :
(x.cast hcast).extractLsb' start len = x.extractLsb' start len := by
ext k hk
simp
@[simp]
theorem extractLsb'_extractLsb'_of_le {x : BitVec w} (hlt : start + len len') :
(x.extractLsb' 0 len').extractLsb' start len = x.extractLsb' start len := by
ext k hk
simp
omega
/-! ### allOnes -/
@[simp, grind =] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
@@ -2913,6 +2944,15 @@ theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v ≤ w) :
omega
· simp [hiv, getLsbD_of_ge x i (by omega)]
@[simp]
theorem extractLsb'_append_extractLsb' {x : BitVec (w + len)} :
(x.extractLsb' len w ++ x.extractLsb' 0 len) = x := by
ext i hi
simp only [getElem_append, getElem_extractLsb', Nat.zero_add, dite_eq_ite]
split
· rw [ getLsbD_eq_getElem]
· simp [show len + (i - len) = i by omega, getLsbD_eq_getElem]
theorem setWidth_eq_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} (h : w v) :
x.setWidth w = x.extractLsb' 0 w := by
rw [setWidth_eq_append_extractLsb']
@@ -3210,6 +3250,11 @@ theorem cons_append_append (x : BitVec w₁) (y : BitVec w₂) (z : BitVec w₃)
· simp [h₂]; omega
· simp [h₂]; omega
@[simp]
theorem extractLsb'_cons {x : BitVec w} :
(x.cons y).extractLsb' 0 w = x := by
simp [BitVec.toNat_eq, Nat.or_mod_two_pow, Nat.shiftLeft_eq]
/-! ### concat -/
@[simp, grind =] theorem toNat_concat (x : BitVec w) (b : Bool) :
@@ -3308,6 +3353,15 @@ theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
ext
simp [getElem_concat]
theorem extractLsb'_concat {x : BitVec (w + 1)} {y : Bool} :
(x.concat y).extractLsb' 0 (t + 1) = (x.extractLsb' 0 t).concat y := by
ext i hi
simp only [ getLsbD_eq_getElem, getLsbD_extractLsb', hi, decide_true, Nat.zero_add,
getLsbD_concat, Bool.true_and]
split
· simp
· simp [show i - 1 < t by omega]
/-! ### shiftConcat -/
@[grind =]
@@ -5812,6 +5866,16 @@ theorem reverse_reverse_eq {x : BitVec w} :
ext k hk
rw [getElem_reverse, getMsbD_reverse, getLsbD_eq_getElem]
@[simp]
theorem concat_reverse_setWidth_msb_eq_reverse {x : BitVec (w + 1)} :
concat ((x.setWidth w).reverse) x.msb = x.reverse := by
ext i hi
simp only [getElem_reverse, BitVec.msb, getElem_concat, getMsbD_setWidth, Nat.le_add_right,
Nat.sub_eq_zero_of_le, Nat.zero_le, decide_true, Bool.true_and, dite_eq_ite]
by_cases hzero : i = 0
· simp [hzero]
· simp [hzero, show i - 1 + (w + 1) - w = i by omega]
/-! ### Inequalities (le / lt) -/
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
@@ -6287,4 +6351,241 @@ theorem two_pow_ctz_le_toNat_of_ne_zero {x : BitVec w} (hx : x ≠ 0#w) :
have hclz := getLsbD_true_ctz_of_ne_zero (x := x) hx
exact Nat.ge_two_pow_of_testBit hclz
/-! ### Population Count -/
@[simp]
theorem cpopNatRec_zero_self {x : BitVec w} :
x.cpopNatRec 0 acc = acc := rfl
@[simp]
theorem cpopNatRec_succ {n : Nat} {x : BitVec w} :
x.cpopNatRec (n + 1) acc = x.cpopNatRec n (acc + (x.getLsbD n).toNat) := rfl
@[simp]
theorem cpopNatRec_zero :
(0#w).cpopNatRec n acc = acc := by
induction n
· case zero =>
simp
· case succ n ihn =>
simp [ihn]
theorem cpopNatRec_eq {x : BitVec w} {n : Nat} (acc : Nat):
x.cpopNatRec n acc = x.cpopNatRec n 0 + acc := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
simp [ihn (acc := acc + (x.getLsbD n).toNat), ihn (acc := (x.getLsbD n).toNat)]
omega
theorem cpopNatRec_add {x : BitVec w} {acc n : Nat} :
x.cpopNatRec n (acc + acc') = x.cpopNatRec n acc + acc' := by
rw [cpopNatRec_eq (acc := acc + acc'), cpopNatRec_eq (acc := acc), Nat.add_assoc]
theorem cpopNatRec_le {x : BitVec w} (n : Nat) :
x.cpopNatRec n acc acc + n := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
have : (x.getLsbD n).toNat 1 := by cases x.getLsbD n <;> simp
specialize ihn (acc := acc + (x.getLsbD n).toNat)
simp
omega
@[simp]
theorem cpopNatRec_of_le {x : BitVec w} (k n : Nat) (hn : w n) :
x.cpopNatRec (n + k) acc = x.cpopNatRec n acc := by
induction k
· case zero =>
simp
· case succ k ihk =>
simp [show n + (k + 1) = (n + k) + 1 by omega, ihk, show w n + k by omega]
theorem cpopNatRec_zero_le (x : BitVec w) (n : Nat) :
x.cpopNatRec n 0 w := by
induction n
· case zero =>
simp
· case succ n ihn =>
by_cases hle : n w
· by_cases hx : x.getLsbD n
· have := cpopNatRec_le (x := x) (acc := 1) (by omega)
have := lt_of_getLsbD hx
simp [hx]
omega
· have := cpopNatRec_le (x := x) (acc := 0) (by omega)
simp [hx]
omega
· simp [show w n by omega]
omega
@[simp]
theorem cpopNatRec_allOnes (h : n w) :
(allOnes w).cpopNatRec n acc = acc + n := by
induction n
· case zero =>
simp
· case succ n ihn =>
specialize ihn (by omega)
simp [show n < w by omega, ihn,
cpopNatRec_add (acc := acc) (acc' := 1)]
omega
@[simp]
theorem cpop_allOnes :
(allOnes w).cpop = BitVec.ofNat w w := by
simp [cpop, cpopNatRec_allOnes]
@[simp]
theorem cpop_zero :
(0#w).cpop = 0#w := by
simp [cpop]
theorem toNat_cpop_le (x : BitVec w) :
x.cpop.toNat w := by
have hlt := Nat.lt_two_pow_self (n := w)
have hle := cpopNatRec_zero_le (x := x) (n := w)
simp only [cpop, toNat_ofNat, ge_iff_le]
rw [Nat.mod_eq_of_lt (by omega)]
exact hle
@[simp]
theorem cpopNatRec_cons_of_le {x : BitVec w} {b : Bool} (hn : n w) :
(cons b x).cpopNatRec n acc = x.cpopNatRec n acc := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
specialize ihn (acc := acc + ((cons b x).getLsbD n).toNat) (by omega)
rw [cpopNatRec_succ, ihn, getLsbD_cons]
simp [show ¬ n = w by omega]
@[simp]
theorem cpopNatRec_cons_of_lt {x : BitVec w} {b : Bool} (hn : w < n) :
(cons b x).cpopNatRec n acc = b.toNat + x.cpopNatRec n acc := by
induction n generalizing acc
· case zero =>
omega
· case succ n ihn =>
by_cases hlt : w < n
· rw [cpopNatRec_succ, ihn (acc := acc + ((cons b x).getLsbD n).toNat) (by omega)]
simp [getLsbD_cons, show ¬ n = w by omega]
· simp [show w = n by omega, getElem_cons,
cpopNatRec_add (acc := acc) (acc' := b.toNat), Nat.add_comm]
theorem cpopNatRec_concat_of_lt {x : BitVec w} {b : Bool} (hn : 0 < n) :
(concat x b).cpopNatRec n acc = b.toNat + x.cpopNatRec (n - 1) acc := by
induction n generalizing acc
· case zero =>
omega
· case succ n ihn =>
by_cases hn0 : 0 < n
· specialize ihn (acc := (acc + ((x.concat b).getLsbD n).toNat)) (by omega)
rw [cpopNatRec_succ, ihn, cpopNatRec_add (acc := acc)]
simp [getLsbD_concat, show ¬ n = 0 by omega, show n + 1 - 1 = n - 1 + 1 by omega, cpopNatRec_add]
· simp [show n = 0 by omega]
omega
theorem toNat_cpop (x : BitVec w) :
x.cpop.toNat = x.cpopNatRec w 0 := by
have := cpopNatRec_zero_le x w
have := toNat_cpop_le x
have := Nat.lt_two_pow_self (n := w)
rw [cpop, toNat_ofNat, Nat.mod_eq_of_lt]
omega
@[simp]
theorem toNat_cpop_cons {x : BitVec w} {b : Bool} :
(x.cons b).cpop.toNat = b.toNat + x.cpop.toNat := by
simp [toNat_cpop, getElem_cons, cpopNatRec_eq (acc := b.toNat), Nat.add_comm]
@[simp]
theorem cpopNatRec_setWidth_of_le (x : BitVec w) (h : pos v) :
(setWidth v x).cpopNatRec pos acc = x.cpopNatRec pos acc := by
induction pos generalizing acc
· case zero =>
simp
· case succ pos ih =>
simp only [cpopNatRec_succ, getLsbD_setWidth]
rw [ih]
· congr
by_cases h : pos < v
<;> simp [h]
omega
· omega
theorem cpop_cons {x : BitVec w} {b : Bool} :
(x.cons b).cpop = b.toNat + x.cpop.setWidth (w + 1) := by
have := toNat_cpop_le x
have := Bool.toNat_lt b
simp only [natCast_eq_ofNat, toNat_eq, toNat_add, toNat_ofNat, toNat_setWidth, Nat.lt_add_one,
toNat_mod_cancel_of_lt, Nat.mod_add_mod]
rw [toNat_cpop_cons, Nat.mod_eq_of_lt]
omega
theorem cpop_concat {x : BitVec w} {b : Bool} :
(x.concat b).cpop = b.toNat + x.cpop.setWidth (w + 1) := by
have := cpopNatRec_zero_le (x := x) (n := w)
have := Nat.lt_two_pow_self (n := w)
rw [cpop, cpop, cpopNatRec_concat_of_lt,
Nat.add_one_sub_one, natCast_eq_ofNat, ofNat_add]
congr
rw [setWidth_ofNat_of_le_of_lt (x := x.cpopNatRec w 0) (by omega) (by omega)]
omega
@[simp]
theorem toNat_cpop_concat {x : BitVec w} {b : Bool} :
(x.concat b).cpop.toNat = b.toNat + x.cpop.toNat := by
have := toNat_cpop_le (x := x)
have := Nat.lt_two_pow_self (n := w + 1)
simp only [cpop_concat, natCast_eq_ofNat, toNat_add, toNat_ofNat, toNat_setWidth, Nat.lt_add_one,
toNat_mod_cancel_of_lt, Nat.mod_add_mod]
rw [Nat.mod_eq_of_lt]
cases b <;> (simp; omega)
theorem cpop_cons_eq_cpop_concat (x : BitVec w) :
(x.cons y).cpop = (x.concat y).cpop := by
rw [cpop_cons, cpop_concat]
@[simp]
theorem cpop_reverse (x : BitVec w) :
x.reverse.cpop = x.cpop := by
induction w
· case zero =>
simp [cpop, reverse]
· case succ w ihw =>
rw [ concat_reverse_setWidth_msb_eq_reverse, cpop_concat, ihw, cpop_cons]
simp
@[simp]
theorem cpopNatRec_cast_eq_of_eq {x : BitVec w} (p : w = v) :
(x.cast p).cpopNatRec n = x.cpopNatRec n := by
subst p; simp
@[simp]
theorem cpop_cast (x : BitVec w) (h : w = v) :
(x.cast h).cpop = x.cpop.cast h := by
simp [cpop, cpopNatRec_cast_eq_of_eq, h]
@[simp]
theorem toNat_cpop_append {x : BitVec w} {y : BitVec u} :
(x ++ y).cpop.toNat = x.cpop.toNat + y.cpop.toNat := by
induction w generalizing u
· case zero =>
simp [cpop]
· case succ w ihw =>
rw [ cons_msb_setWidth x, toNat_cpop_cons, cons_append, cpop_cast, toNat_cast,
toNat_cpop_cons, ihw, Nat.add_assoc]
theorem cpop_append {x : BitVec w} {y : BitVec u} :
(x ++ y).cpop = x.cpop.setWidth (w + u) + y.cpop.setWidth (w + u) := by
apply eq_of_toNat_eq
have := toNat_cpop_le x
have := toNat_cpop_le y
have := Nat.lt_two_pow_self (n := w + u)
simp only [toNat_cpop_append, toNat_add, toNat_setWidth, Nat.add_mod_mod, Nat.mod_add_mod]
rw [Nat.mod_eq_of_lt (by omega)]
end BitVec

View File

@@ -269,6 +269,8 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
if start < stop then
if stop as.size then
fold (USize.ofNat start) (USize.ofNat stop) init
else if start < as.size then
fold (USize.ofNat start) (USize.ofNat as.size) init
else
pure init
else

View File

@@ -102,7 +102,7 @@ Returns `true` if the character is a uppercase ASCII letter.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
-/
@[inline] def isUpper (c : Char) : Bool :=
c.val 65 && c.val 90
c.val 'A'.val c.val 'Z'.val
/--
Returns `true` if the character is a lowercase ASCII letter.
@@ -110,7 +110,7 @@ Returns `true` if the character is a lowercase ASCII letter.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
-/
@[inline] def isLower (c : Char) : Bool :=
c.val 97 && c.val 122
c.val 'a'.val && c.val 'z'.val
/--
Returns `true` if the character is an ASCII letter.
@@ -126,7 +126,7 @@ Returns `true` if the character is an ASCII digit.
The ASCII digits are the following: `0123456789`.
-/
@[inline] def isDigit (c : Char) : Bool :=
c.val 48 && c.val 57
c.val '0'.val && c.val '9'.val
/--
Returns `true` if the character is an ASCII letter or digit.
@@ -143,9 +143,16 @@ alphabet are returned unchanged.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
-/
@[inline]
def toLower (c : Char) : Char :=
let n := toNat c;
if n >= 65 n <= 90 then ofNat (n + 32) else c
if h : c.val 'A'.val c.val 'Z'.val then
c.val + ('a'.val - 'A'.val), ?_
else
c
where finally
have h : c.val.toBitVec.toNat + ('a'.val - 'A'.val).toBitVec.toNat < 0xd800 :=
Nat.add_lt_add_right (Nat.lt_of_le_of_lt h.2 (by decide)) _
exact .inl (lt_of_eq_of_lt (Nat.mod_eq_of_lt (Nat.lt_trans h (by decide))) h)
/--
Converts a lowercase ASCII letter to the corresponding uppercase letter. Letters outside the ASCII
@@ -153,8 +160,20 @@ alphabet are returned unchanged.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
-/
@[inline]
def toUpper (c : Char) : Char :=
let n := toNat c;
if n >= 97 n <= 122 then ofNat (n - 32) else c
if h : c.val 'a'.val c.val 'z'.val then
c.val + ('A'.val - 'a'.val), ?_
else
c
where finally
have h₁ : 2^32 c.val.toNat + ('A'.val - 'a'.val).toNat :=
@Nat.add_le_add 'a'.val.toNat _ (2^32 - 'a'.val.toNat) _ h.1 (by decide)
have h₂ : c.val.toBitVec.toNat + ('A'.val - 'a'.val).toNat < 2^32 + 0xd800 :=
Nat.add_lt_add_right (Nat.lt_of_le_of_lt h.2 (by decide)) _
have add_eq {x y : UInt32} : (x + y).toNat = (x.toNat + y.toNat) % 2^32 := rfl
replace h₂ := Nat.sub_lt_left_of_lt_add h₁ h₂
exact .inl <| lt_of_eq_of_lt (add_eq.trans (Nat.mod_eq_sub_mod h₁) |>.trans
(Nat.mod_eq_of_lt (Nat.lt_trans h₂ (by decide)))) h₂
end Char

View File

@@ -144,6 +144,8 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
if start < stop then
if stop as.size then
fold (USize.ofNat start) (USize.ofNat stop) init
else if start < as.size then
fold (USize.ofNat start) (USize.ofNat as.size) init
else
pure init
else

View File

@@ -113,6 +113,8 @@ theorem gcd_eq_right_iff_dvd (hb : 0 ≤ b) : gcd a b = b ↔ b a := by
theorem gcd_assoc (a b c : Int) : gcd (gcd a b) c = gcd a (gcd b c) := Nat.gcd_assoc ..
theorem gcd_left_comm (a b c : Int) : gcd a (gcd b c) = gcd b (gcd a c) := Nat.gcd_left_comm ..
theorem gcd_mul_left (m n k : Int) : gcd (m * n) (m * k) = m.natAbs * gcd n k := by
simp [gcd_eq_natAbs_gcd_natAbs, Nat.gcd_mul_left, natAbs_mul]

View File

@@ -333,6 +333,12 @@ protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
@[simp] protected theorem add_sub_cancel (a b : Int) : a + b - b = a :=
Int.add_neg_cancel_right a b
protected theorem add_sub_add_right (n k m : Int) : (n + k) - (m + k) = n - m := by
rw [Int.add_comm m, Int.sub_sub, Int.add_sub_cancel]
protected theorem add_sub_add_left (k n m : Int) : (k + n) - (k + m) = n - m := by
rw [Int.add_comm k, Int.add_comm k, Int.add_sub_add_right]
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_neg_eq_sub]
@@ -546,6 +552,7 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 b = 0 := by
| .ofNat 0, _, _ => by simp
| _, .ofNat 0, _ => by simp
| .ofNat (_+1), .negSucc _, h => by cases h
| .negSucc _, .negSucc _, h => by cases h
protected theorem mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 :=
Or.rec a0 b0 Int.mul_eq_zero.mp

View File

@@ -474,6 +474,20 @@ protected theorem max_lt {a b c : Int} : max a b < c ↔ a < c ∧ b < c := by
simp only [Int.lt_iff_add_one_le]
simpa using Int.max_le (a := a + 1) (b := b + 1) (c := c)
protected theorem max_eq_right_iff {a b : Int} : max a b = b a b := by
apply Iff.intro
· intro h
rw [ h]
apply Int.le_max_left
· apply Int.max_eq_right
protected theorem max_eq_left_iff {a b : Int} : max a b = a b a := by
apply Iff.intro
· intro h
rw [ h]
apply Int.le_max_right
· apply Int.max_eq_left
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (natCast_nonneg n)]
@@ -912,6 +926,16 @@ protected theorem sub_right_le_of_le_add {a b c : Int} (h : a ≤ b + c) : a - c
have h := Int.add_le_add_right h (-c)
rwa [Int.add_neg_cancel_right] at h
protected theorem sub_right_le_iff_le_add {a b c : Int} : a - c b a b + c :=
Int.le_add_of_sub_right_le, Int.sub_right_le_of_le_add
theorem toNat_sub_eq_zero_iff (m n : Int) : toNat (m - n) = 0 m n := by
rw [ ofNat_inj, ofNat_toNat, cast_ofNat_Int, Int.max_eq_right_iff, Int.sub_right_le_iff_le_add,
Int.zero_add]
theorem zero_eq_toNat_sub_iff (m n : Int) : 0 = toNat (m - n) m n := by
rw [eq_comm (a := 0), toNat_sub_eq_zero_iff]
protected theorem le_add_of_neg_add_le_left {a b c : Int} (h : -b + a c) : a b + c := by
rw [Int.add_comm] at h
exact Int.le_add_of_sub_left_le h
@@ -989,6 +1013,10 @@ protected theorem lt_sub_right_of_add_lt {a b c : Int} (h : a + b < c) : a < c -
have h := Int.add_lt_add_right h (-b)
rwa [Int.add_neg_cancel_right] at h
protected theorem lt_sub_right_iff_add_lt {a b c : Int} :
a < c - b a + b < c :=
Int.add_lt_of_lt_sub_right, Int.lt_sub_right_of_add_lt
protected theorem lt_add_of_neg_add_lt {a b c : Int} (h : -b + a < c) : a < b + c := by
have h := Int.add_lt_add_left h b
rwa [Int.add_neg_cancel_left] at h

View File

@@ -10,6 +10,7 @@ public import Init.Classical
public import Init.Ext
set_option doc.verso true
set_option linter.missingDocs true
public section
@@ -77,8 +78,6 @@ public theorem Shrink.deflate_inj {α} {x y : α} :
· rintro rfl
rfl
namespace Iterators
-- It is not fruitful to move the following docstrings to verso right now because there are lots of
-- forward references that cannot be realized nicely.
set_option doc.verso false
@@ -124,6 +123,7 @@ def x := ([1, 2, 3].iterM IO : IterM IO Nat)
-/
@[ext]
structure IterM {α : Type w} (m : Type w Type w') (β : Type w) where
mk' ::
/-- Internal implementation detail of the iterator. -/
internalState : α
@@ -293,6 +293,11 @@ theorem IterStep.mapIterator_id {step : IterStep α β} :
step.mapIterator id = step := by
cases step <;> rfl
@[simp]
theorem IterStep.mapIterator_id' {step : IterStep α β} :
step.mapIterator (fun x => x) = step := by
cases step <;> rfl
/--
A variant of `IterStep` that bundles the step together with a proof that it is "plausible".
The plausibility predicate will later be chosen to assert that a state is a plausible successor
@@ -306,7 +311,7 @@ def PlausibleIterStep (IsPlausibleStep : IterStep α β → Prop) := Subtype IsP
/--
Match pattern for the `yield` case. See also `IterStep.yield`.
-/
@[match_pattern, simp, expose]
@[match_pattern, simp, spec, expose]
def PlausibleIterStep.yield {IsPlausibleStep : IterStep α β Prop}
(it' : α) (out : β) (h : IsPlausibleStep (.yield it' out)) :
PlausibleIterStep IsPlausibleStep :=
@@ -315,7 +320,7 @@ def PlausibleIterStep.yield {IsPlausibleStep : IterStep α β → Prop}
/--
Match pattern for the `skip` case. See also `IterStep.skip`.
-/
@[match_pattern, simp, expose]
@[match_pattern, simp, grind =, expose]
def PlausibleIterStep.skip {IsPlausibleStep : IterStep α β Prop}
(it' : α) (h : IsPlausibleStep (.skip it')) : PlausibleIterStep IsPlausibleStep :=
.skip it', h
@@ -323,7 +328,7 @@ def PlausibleIterStep.skip {IsPlausibleStep : IterStep α β → Prop}
/--
Match pattern for the `done` case. See also `IterStep.done`.
-/
@[match_pattern, simp, expose]
@[match_pattern, simp, grind =, expose]
def PlausibleIterStep.done {IsPlausibleStep : IterStep α β Prop}
(h : IsPlausibleStep .done) : PlausibleIterStep IsPlausibleStep :=
.done, h
@@ -345,34 +350,51 @@ abbrev PlausibleIterStep.casesOn {IsPlausibleStep : IterStep α β → Prop}
end IterStep
/--
The typeclass providing the step function of an iterator in `Iter (α := α) β` or
`IterM (α := α) m β`.
The step function of an iterator in `Iter (α := α) β` or `IterM (α := α) m β`.
In order to allow intrinsic termination proofs when iterating with the `step` function, the
step object is bundled with a proof that it is a "plausible" step for the given current iterator.
-/
class Iterator (α : Type w) (m : Type w Type w') (β : outParam (Type w)) where
/--
A relation that governs the allowed steps from a given iterator.
The "plausible" steps are those which make sense for a given state; plausibility can ensure
properties such as the successor iterator being drawn from the same collection, that an iterator
resulting from a skip will return the same next value, or that the next item yielded is next one
in the original collection.
-/
IsPlausibleStep : IterM (α := α) m β IterStep (IterM (α := α) m β) β Prop
/--
Carries out a step of iteration.
-/
step : (it : IterM (α := α) m β) m (Shrink <| PlausibleIterStep <| IsPlausibleStep it)
section Monadic
/--
Converts wraps the state of an iterator into an `IterM` object.
Wraps the state of an iterator into an `IterM` object.
-/
@[always_inline, inline, expose]
def toIterM {α : Type w} (it : α) (m : Type w Type w') (β : Type w) :
def IterM.mk {α : Type w} (it : α) (m : Type w Type w') (β : Type w) :
IterM (α := α) m β :=
it
@[deprecated IterM.mk (since := "2025-12-01"), inline, expose, inherit_doc IterM.mk]
def Iterators.toIterM := @IterM.mk
@[simp]
theorem toIterM_internalState {α m β} (it : IterM (α := α) m β) :
toIterM it.internalState m β = it :=
theorem IterM.mk_internalState {α m β} (it : IterM (α := α) m β) :
.mk it.internalState m β = it :=
rfl
set_option linter.missingDocs false in
@[deprecated IterM.mk_internalState (since := "2025-12-01")]
def Iterators.toIterM_internalState := @IterM.mk_internalState
@[simp]
theorem internalState_toIterM {α m β} (it : α) :
(toIterM it m β).internalState = it :=
(IterM.mk it m β).internalState = it :=
rfl
/--
@@ -449,8 +471,10 @@ number of steps.
-/
inductive IterM.IsPlausibleIndirectOutput {α β : Type w} {m : Type w Type w'} [Iterator α m β]
: IterM (α := α) m β β Prop where
/-- The output value could plausibly be emitted in the next step. -/
| direct {it : IterM (α := α) m β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
/-- The output value could plausibly be emitted in a step after the next step. -/
| indirect {it it' : IterM (α := α) m β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
@@ -460,7 +484,9 @@ finitely many steps. This relation is reflexive.
-/
inductive IterM.IsPlausibleIndirectSuccessorOf {α β : Type w} {m : Type w Type w'}
[Iterator α m β] : IterM (α := α) m β IterM (α := α) m β Prop where
/-- Every iterator is a plausible indirect successor of itself. -/
| refl (it : IterM (α := α) m β) : it.IsPlausibleIndirectSuccessorOf it
/-- The iterator is a plausible successor of one of the current iterator's successors. -/
| cons_right {it'' it' it : IterM (α := α) m β} (h' : it''.IsPlausibleIndirectSuccessorOf it')
(h : it'.IsPlausibleSuccessorOf it) : it''.IsPlausibleIndirectSuccessorOf it
@@ -585,8 +611,10 @@ number of steps.
-/
inductive Iter.IsPlausibleIndirectOutput {α β : Type w} [Iterator α Id β] :
Iter (α := α) β β Prop where
/-- The output value could plausibly be emitted in the next step. -/
| direct {it : Iter (α := α) β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
/-- The output value could plausibly be emitted in a step after the next step. -/
| indirect {it it' : Iter (α := α) β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
@@ -617,7 +645,9 @@ finitely many steps. This relation is reflexive.
-/
inductive Iter.IsPlausibleIndirectSuccessorOf {α : Type w} {β : Type w} [Iterator α Id β] :
Iter (α := α) β Iter (α := α) β Prop where
/-- Every iterator is a plausible indirect successor of itself. -/
| refl (it : Iter (α := α) β) : IsPlausibleIndirectSuccessorOf it it
/-- The iterator is a plausible indirect successor of one of the current iterator's successors. -/
| cons_right {it'' it' it : Iter (α := α) β} (h' : it''.IsPlausibleIndirectSuccessorOf it')
(h : it'.IsPlausibleSuccessorOf it) : it''.IsPlausibleIndirectSuccessorOf it
@@ -677,11 +707,11 @@ this means that the relation of plausible successors is well-founded.
Given this typeclass, termination proofs for well-founded recursion over an iterator `it` can use
`it.finitelyManySteps` as a termination measure.
-/
class Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
class Iterators.Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
/-- The relation of plausible successors is well-founded. -/
wf : WellFounded (IterM.IsPlausibleSuccessorOf (α := α) (m := m))
theorem Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
theorem Iterators.Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
WellFounded (Iter.IsPlausibleSuccessorOf (α := α)) := by
simpa [Iter.isPlausibleSuccessorOf_eq_invImage] using InvImage.wf _ Finite.wf
@@ -691,6 +721,11 @@ recursion over finite iterators. See also `IterM.finitelyManySteps` and `Iter.fi
-/
structure IterM.TerminationMeasures.Finite
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
The wrapped iterator.
In the wrapper, its finiteness is used as a termination measure.
-/
it : IterM (α := α) m β
/--
@@ -703,10 +738,11 @@ def IterM.TerminationMeasures.Finite.Rel
TerminationMeasures.Finite α m TerminationMeasures.Finite α m Prop :=
Relation.TransGen <| InvImage IterM.IsPlausibleSuccessorOf IterM.TerminationMeasures.Finite.it
instance {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Finite α m] : WellFoundedRelation (IterM.TerminationMeasures.Finite α m) where
instance IterM.TerminationMeasures.instWellFoundedRelationFinite {α : Type w} {m : Type w Type w'}
{β : Type w} [Iterator α m β] [Iterators.Finite α m] :
WellFoundedRelation (IterM.TerminationMeasures.Finite α m) where
rel := IterM.TerminationMeasures.Finite.Rel
wf := by exact (InvImage.wf _ Finite.wf).transGen
wf := by exact (InvImage.wf _ Iterators.Finite.wf).transGen
/--
Termination measure to be used in well-founded recursive functions recursing over a finite iterator
@@ -714,7 +750,7 @@ Termination measure to be used in well-founded recursive functions recursing ove
-/
@[expose]
def IterM.finitelyManySteps {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Finite α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
[Iterators.Finite α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
it
/--
@@ -756,7 +792,7 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
| fail)
@[inherit_doc IterM.finitelyManySteps, expose]
def Iter.finitelyManySteps {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id]
def Iter.finitelyManySteps {α : Type w} {β : Type w} [Iterator α Id β] [Iterators.Finite α Id]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Finite α Id :=
it.toIterM.finitelyManySteps
@@ -806,7 +842,7 @@ well-founded.
Given this typeclass, termination proofs for well-founded recursion over an iterator `it` can use
`it.finitelyManySkips` as a termination measure.
-/
class Productive (α m) {β} [Iterator α m β] : Prop where
class Iterators.Productive (α m) {β} [Iterator α m β] : Prop where
/-- The relation of plausible successors during skips is well-founded. -/
wf : WellFounded (IterM.IsPlausibleSkipSuccessorOf (α := α) (m := m))
@@ -816,6 +852,11 @@ recursion over productive iterators. See also `IterM.finitelyManySkips` and `Ite
-/
structure IterM.TerminationMeasures.Productive
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
The wrapped iterator.
In the wrapper, its productivity is used as a termination measure.
-/
it : IterM (α := α) m β
/--
@@ -846,10 +887,11 @@ theorem IterM.TerminationMeasures.Finite.Rel.of_productive
refine .trans ih ?_
exact .single _, rfl, hab
instance {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Productive α m] : WellFoundedRelation (IterM.TerminationMeasures.Productive α m) where
instance IterM.TerminationMeasures.instWellFoundedRelationProductive {α : Type w}
{m : Type w Type w'} {β : Type w} [Iterator α m β] [Iterators.Productive α m] :
WellFoundedRelation (IterM.TerminationMeasures.Productive α m) where
rel := IterM.TerminationMeasures.Productive.Rel
wf := by exact (InvImage.wf _ Productive.wf).transGen
wf := by exact (InvImage.wf _ Iterators.Productive.wf).transGen
/--
Termination measure to be used in well-founded recursive functions recursing over a productive
@@ -857,7 +899,7 @@ iterator (see also `Productive`).
-/
@[expose]
def IterM.finitelyManySkips {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Productive α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
[Iterators.Productive α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
it
/--
@@ -876,7 +918,7 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
| fail)
@[inherit_doc IterM.finitelyManySkips, expose]
def Iter.finitelyManySkips {α : Type w} {β : Type w} [Iterator α Id β] [Productive α Id]
def Iter.finitelyManySkips {α : Type w} {β : Type w} [Iterator α Id β] [Iterators.Productive α Id]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Productive α Id :=
it.toIterM.finitelyManySkips
@@ -895,12 +937,13 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
| exact Iter.TerminationMeasures.Productive.rel_of_skip _
| fail)
instance [Iterator α m β] [Finite α m] : Productive α m where
instance Iterators.instProductiveOfFinte [Iterator α m β] [Iterators.Finite α m] :
Iterators.Productive α m where
wf := by
apply Subrelation.wf (r := IterM.IsPlausibleSuccessorOf)
· intro it' it h
exact IterM.isPlausibleSuccessorOf_of_skip h
· exact Finite.wf
· exact Iterators.Finite.wf
end Productive
@@ -917,10 +960,63 @@ library.
-/
class LawfulDeterministicIterator (α : Type w) (m : Type w Type w') [Iterator α m β]
where
/--
Every iterator with state `α` in monad `m` has exactly one plausible step.
-/
isPlausibleStep_eq_eq : it : IterM (α := α) m β, step, it.IsPlausibleStep = (· = step)
end Iterators
namespace Iterators
export Iterators (Iter IterM)
/--
This structure provides a more convenient way to define `Finite α m` instances using
`Finite.of_finitenessRelation : FinitenessRelation α m → Finite α m`.
-/
structure FinitenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/--
A well-founded relation such that if `it'` is a successor iterator of `it`, then `Rel it' it`.
-/
Rel (it' it : IterM (α := α) m β) : Prop
/-- `Rel` is well-founded. -/
wf : WellFounded Rel
/-- If `it'` is a successor iterator of `it`, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSuccessorOf it Rel it' it
end Std
theorem Finite.of_finitenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : FinitenessRelation α m) : Finite α m where
wf := by
refine Subrelation.wf (r := r.Rel) ?_ ?_
· intro x y h
apply FinitenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
/--
This structure provides a more convenient way to define `Productive α m` instances using
`Productive.of_productivenessRelation : ProductivenessRelation α m → Productive α m`.
-/
structure ProductivenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/--
A well-founded relation such that if `it'` is obtained from `it` by skipping, then `Rel it' it`.
-/
Rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
/-- `Rel` is well-founded. -/
wf : WellFounded Rel
/-- If `it'` is obtained from `it` by skipping, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSkipSuccessorOf it Rel it' it
theorem Productive.of_productivenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : ProductivenessRelation α m) : Productive α m where
wf := by
refine Subrelation.wf (r := r.Rel) ?_ ?_
· intro x y h
apply ProductivenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
end Std.Iterators

View File

@@ -11,7 +11,8 @@ public import Init.Data.Iterators.Combinators.FilterMap
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
@[always_inline, inline, expose, inherit_doc IterM.attachWith]
def Iter.attachWith {α β : Type w}
@@ -24,4 +25,4 @@ where finally
simp only [ isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM]
exact h
end Std.Iterators
end Std

View File

@@ -30,7 +30,8 @@ Several variants of these combinators are provided:
iterator, and particularly for specialized termination proofs. If possible, avoid this.
-/
namespace Std.Iterators
namespace Std
open Std.Iterators
-- We cannot use `inherit_doc` because the docstring for `IterM` states that a `MonadLiftT` instance
-- is needed.
@@ -197,12 +198,8 @@ it.filterMapM ---a'-----c'-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` never returns `none`, then
this combinator will preserve productiveness. If `f` is an `ExceptT` monad and will always fail,
then `it.filterMapM` will be finite even if `it` isn't. In the first case, consider
using the `map`/`mapM`/`mapWithPostcondition` combinators instead, which provide more instances out
of the box.
If that does not help, the more general combinator `it.filterMapWithPostcondition f` makes it
possible to manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
then `it.filterMapM` will be finite even if `it` isn't. In such cases, the termination proof needs
to be done manually.
**Performance:**
@@ -211,7 +208,7 @@ returned `Option` value.
-/
@[always_inline, inline, expose]
def Iter.filterMapM {α β γ : Type w} [Iterator α Id β] {m : Type w Type w'}
[Monad m] (f : β m (Option γ)) (it : Iter (α := α) β) :=
[Monad m] [MonadAttach m] (f : β m (Option γ)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := pure; it.toIterM.filterMapM f : IterM m γ)
/--
@@ -237,10 +234,7 @@ it.filterM ---a-----c-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.filterWithPostcondition` will be finite -- and productive -- even if `it`
isn't.
In such situations, the more general combinator `it.filterWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
isn't. In such cases, the termination proof needs to be done manually.
**Performance:**
@@ -248,7 +242,7 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline, expose]
def Iter.filterM {α β : Type w} [Iterator α Id β] {m : Type w Type w'}
[Monad m] (f : β m (ULift Bool)) (it : Iter (α := α) β) :=
[Monad m] [MonadAttach m] (f : β m (ULift Bool)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := pure; it.toIterM.filterM f : IterM m β)
/--
@@ -276,10 +270,8 @@ it.mapM ---a'--b'--c'--d'-e'----⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.mapM` will be finite even if `it` isn't.
If that does not help, the more general combinator `it.mapWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
will always fail, then `it.mapM` will be finite even if `it` isn't. In such cases, the termination
proof needs to be done manually.
**Performance:**
@@ -287,7 +279,7 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline, expose]
def Iter.mapM {α β γ : Type w} [Iterator α Id β] {m : Type w Type w'}
[Monad m] (f : β m γ) (it : Iter (α := α) β) :=
[Monad m] [MonadAttach m] (f : β m γ) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := pure; it.toIterM.mapM f : IterM m γ)
@[always_inline, inline, inherit_doc IterM.filterMap, expose]
@@ -305,4 +297,4 @@ def Iter.map {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
(f : β γ) (it : Iter (α := α) β) :=
((it.toIterM.map f).toIter : Iter γ)
end Std.Iterators
end Std

View File

@@ -24,17 +24,17 @@ and so on. In other words, {lit}`it` flattens the iterator of iterators obtained
{lit}`f`.
-/
namespace Std.Iterators
namespace Std
@[always_inline, inherit_doc IterM.flatMapAfterM]
public def Iter.flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α Id β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it₁ : Iter (α := α) β) (it₂ : Option (IterM (α := α₂) m γ)) :=
((it₁.mapM pure).flatMapAfterM f it₂ : IterM m γ)
((it₁.mapWithPostcondition pure).flatMapAfterM f it₂ : IterM m γ)
@[always_inline, expose, inherit_doc IterM.flatMapM]
public def Iter.flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α Id β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it : Iter (α := α) β) :=
(it.flatMapAfterM f none : IterM m γ)
@@ -49,5 +49,3 @@ public def Iter.flatMap {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
(f : β Iter (α := α₂) γ) (it : Iter (α := α) β) :=
(it.flatMapAfter f none : Iter γ)
end Std.Iterators

View File

@@ -6,7 +6,6 @@ Authors: Paul Reichert
module
prelude
public import Init.Data.Iterators.Internal.Termination
public import Init.Data.Iterators.Consumers.Loop
public section
@@ -47,7 +46,7 @@ instance Attach.instIterator {α β : Type w} {m : Type w → Type w'} [Monad m]
def Attach.instFinitenessRelation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Finite α m] {P : β Prop} :
FinitenessRelation (Attach α m P) m where
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySteps
Rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySteps
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single
@@ -68,7 +67,7 @@ instance Attach.instFinite {α β : Type w} {m : Type w → Type w'} [Monad m]
def Attach.instProductivenessRelation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Productive α m] {P : β Prop} :
ProductivenessRelation (Attach α m P) m where
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySkips
Rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySkips
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single
@@ -86,17 +85,12 @@ instance Attach.instProductive {α β : Type w} {m : Type w → Type w'} [Monad
Productive (Attach α m P) m :=
.of_productivenessRelation instProductivenessRelation
instance Attach.instIteratorCollect {α β : Type w} {m : Type w Type w'} [Monad m] [Monad n]
{P : β Prop} [Iterator α m β] :
IteratorCollect (Attach α m P) m n :=
.defaultImplementation
instance Attach.instIteratorLoop {α β : Type w} {m : Type w Type w'} [Monad m]
{n : Type x Type x'} [Monad n] {P : β Prop} [Iterator α m β] :
IteratorLoop (Attach α m P) m n :=
.defaultImplementation
end Types
end Iterators.Types
/--
“Attaches” individual proofs to an iterator of values that satisfy a predicate `P`, returning an
@@ -111,7 +105,7 @@ iterator with values in the corresponding subtype `{ x // P x }`.
def IterM.attachWith {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] (it : IterM (α := α) m β) (P : β Prop)
(h : out, it.IsPlausibleIndirectOutput out P out) :
IterM (α := Types.Attach α m P) m { out : β // P out } :=
IterM (α := Iterators.Types.Attach α m P) m { out : β // P out } :=
it, h
end Std.Iterators
end Std

View File

@@ -8,7 +8,6 @@ module
prelude
public import Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.PostconditionMonad
public import Init.Data.Iterators.Internal.Termination
public section
@@ -32,7 +31,9 @@ Several variants of these combinators are provided:
iterator, and particularly for specialized termination proofs. If possible, avoid this.
-/
namespace Std.Iterators
namespace Std
namespace Iterators.Types
/--
Internal state of the `filterMap` combinator. Do not depend on its internals.
@@ -53,19 +54,23 @@ def Map (α : Type w) {β γ : Type w} (m : Type w → Type w') (n : Type w →
(f : β PostconditionT n γ) :=
FilterMap α m n lift (fun b => PostconditionT.map some (f b))
end Iterators.Types
open Std.Iterators Std.Iterators.Types
@[always_inline, inline, expose]
def IterM.InternalCombinators.filterMap {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} (lift : α : Type w m α n α)
[Iterator α m β] (f : β PostconditionT n (Option γ))
(it : IterM (α := α) m β) : IterM (α := FilterMap α m n lift f) n γ :=
toIterM it n γ
.mk it n γ
@[always_inline, inline, expose]
def IterM.InternalCombinators.map {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] (lift : α : Type w m α n α)
[Iterator α m β] (f : β PostconditionT n γ)
(it : IterM (α := α) m β) : IterM (α := Map α m n lift f) n γ :=
toIterM it n γ
.mk it n γ
/--
*Note: This is a very general combinator that requires an advanced understanding of monads,
@@ -117,16 +122,18 @@ returned `Option` value.
def IterM.filterMapWithPostcondition {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
[MonadLiftT m n] [Iterator α m β] (f : β PostconditionT n (Option γ))
(it : IterM (α := α) m β) : IterM (α := FilterMap α m n (fun _ => monadLift) f) n γ :=
IterM.InternalCombinators.filterMap (fun _ => monadLift) f it
IterM.InternalCombinators.filterMap (n := n) (fun _ => monadLift) f it
namespace Iterators.Types
/--
`it.PlausibleStep step` is the proposition that `step` is a possible next step from the
`filterMap` iterator `it`. This is mostly internally relevant, except if one needs to manually
prove termination (`Finite` or `Productive` instances, for example) of a `filterMap` iterator.
-/
inductive FilterMap.PlausibleStep {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
{lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)} [Iterator α m β]
(it : IterM (α := FilterMap α m n lift f) n γ) :
inductive FilterMap.PlausibleStep {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)}
[Iterator α m β] (it : IterM (α := FilterMap α m n lift f) n γ) :
IterStep (IterM (α := FilterMap α m n lift f) n γ) γ Prop where
| yieldNone : {it' out},
it.internalState.inner.IsPlausibleStep (.yield it' out)
@@ -139,8 +146,8 @@ inductive FilterMap.PlausibleStep {α β γ : Type w} {m : Type w → Type w'} {
PlausibleStep it (.skip (IterM.InternalCombinators.filterMap lift f it'))
| done : it.internalState.inner.IsPlausibleStep .done PlausibleStep it .done
instance FilterMap.instIterator {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
{lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)}
instance FilterMap.instIterator {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)}
[Iterator α m β] [Monad n] :
Iterator (FilterMap α m n lift f) n γ where
IsPlausibleStep := FilterMap.PlausibleStep (m := m) (n := n)
@@ -155,9 +162,8 @@ instance FilterMap.instIterator {α β γ : Type w} {m : Type w → Type w'} {n
| .skip it' h => pure <| .deflate <| .skip (it'.filterMapWithPostcondition f) (by exact .skip h)
| .done h => pure <| .deflate <| .done (.done h)
instance {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n γ} :
instance Map.instIterator {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n]
[Iterator α m β] {lift : α : Type w m α n α} {f : β PostconditionT n γ} :
Iterator (Map α m n lift f) n γ :=
inferInstanceAs <| Iterator (FilterMap α m n lift _) n γ
@@ -165,7 +171,7 @@ private def FilterMap.instFinitenessRelation {α β γ : Type w} {m : Type w →
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} [Finite α m] :
FinitenessRelation (FilterMap α m n lift f) n where
rel := InvImage IterM.IsPlausibleSuccessorOf (FilterMap.inner IterM.internalState)
Rel := InvImage IterM.IsPlausibleSuccessorOf (FilterMap.inner IterM.internalState)
wf := InvImage.wf _ Finite.wf
subrelation {it it'} h := by
obtain step, h, h' := h
@@ -189,8 +195,8 @@ instance FilterMap.instFinite {α β γ : Type w} {m : Type w → Type w'}
Finite.of_finitenessRelation FilterMap.instFinitenessRelation
@[no_expose]
instance {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n] [Iterator α m β]
{lift : α : Type w m α n α} {f : β PostconditionT n γ} [Finite α m] :
instance Map.instFinite {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n]
[Iterator α m β] {lift : α : Type w m α n α} {f : β PostconditionT n γ} [Finite α m] :
Finite (Map α m n lift f) n :=
Finite.of_finitenessRelation FilterMap.instFinitenessRelation
@@ -198,7 +204,7 @@ private def Map.instProductivenessRelation {α β γ : Type w} {m : Type w → T
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n γ} [Productive α m] :
ProductivenessRelation (Map α m n lift f) n where
rel := InvImage IterM.IsPlausibleSkipSuccessorOf (FilterMap.inner IterM.internalState)
Rel := InvImage IterM.IsPlausibleSkipSuccessorOf (FilterMap.inner IterM.internalState)
wf := InvImage.wf _ Productive.wf
subrelation {it it'} h := by
cases h
@@ -214,13 +220,6 @@ instance Map.instProductive {α β γ : Type w} {m : Type w → Type w'}
Productive (Map α m n lift f) n :=
Productive.of_productivenessRelation Map.instProductivenessRelation
instance {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type w Type x} [Monad n] [Monad o] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} :
IteratorCollect (FilterMap α m n lift f) n o :=
.defaultImplementation
instance FilterMap.instIteratorLoop {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type x Type x'}
[Monad n] [Monad o] [Iterator α m β] {lift : α : Type w m α n α}
@@ -228,23 +227,6 @@ instance FilterMap.instIteratorLoop {α β γ : Type w} {m : Type w → Type w'}
IteratorLoop (FilterMap α m n lift f) n o :=
.defaultImplementation
/--
`map` operations allow for a more efficient implementation of `toArray`. For example,
`array.iter.map f |>.toArray happens in-place if possible.
-/
instance Map.instIteratorCollect {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type w Type x} [Monad n] [Monad o] [Iterator α m β]
{lift₁ : α : Type w m α n α}
{f : β PostconditionT n γ} [IteratorCollect α m o] :
IteratorCollect (Map α m n lift₁ f) n o where
toArrayMapped lift₂ _ g it :=
letI : MonadLift m n := lift₁ (α := _)
letI : MonadLift n o := lift₂ (δ := _)
IteratorCollect.toArrayMapped
(lift := fun _ => monadLift)
(fun x => do g ( (f x).operation))
it.internalState.inner (m := m)
instance Map.instIteratorLoop {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type x Type x'} [Monad n] [Monad o] [Iterator α m β]
{lift : α : Type w m α n α}
@@ -252,6 +234,8 @@ instance Map.instIteratorLoop {α β γ : Type w} {m : Type w → Type w'}
IteratorLoop (Map α m n lift f) n o :=
.defaultImplementation
end Iterators.Types
/--
*Note: This is a very general combinator that requires an advanced understanding of monads, dependent
types and termination proofs. The variants `map` and `mapM` are easier to use and sufficient
@@ -373,12 +357,8 @@ it.filterMapM ---a'-----c'-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` never returns `none`, then
this combinator will preserve productiveness. If `f` is an `ExceptT` monad and will always fail,
then `it.filterMapM` will be finite even if `it` isn't. In the first case, consider
using the `map`/`mapM`/`mapWithPostcondition` combinators instead, which provide more instances out of
the box.
If that does not help, the more general combinator `it.filterMapWithPostcondition f` makes it
possible to manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
then `it.filterMapM` will be finite even if `it` isn't. In such cases, the termination proof needs
to be done manually.
**Performance:**
@@ -387,9 +367,9 @@ returned `Option` value.
-/
@[inline, expose]
def IterM.filterMapM {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
[Iterator α m β] [Monad n] [MonadLiftT m n]
[Iterator α m β] [Monad n] [MonadAttach n] [MonadLiftT m n]
(f : β n (Option γ)) (it : IterM (α := α) m β) :=
(it.filterMapWithPostcondition (fun b => PostconditionT.lift (f b)) : IterM n γ)
(it.filterMapWithPostcondition (fun b => PostconditionT.attachLift (f b)) : IterM n γ)
/--
If `it` is an iterator, then `it.mapM f` is another iterator that applies a monadic
@@ -416,10 +396,8 @@ it.mapM ---a'--b'--c'--d'-e'----⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.mapM` will be finite even if `it` isn't.
If that does not help, the more general combinator `it.mapWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
will always fail, then `it.mapM` will be finite even if `it` isn't. In such cases, the termination
proof needs to be done manually.
**Performance:**
@@ -427,8 +405,8 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline, expose]
def IterM.mapM {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Iterator α m β]
[Monad n] [MonadLiftT m n] (f : β n γ) (it : IterM (α := α) m β) :=
(it.mapWithPostcondition (fun b => PostconditionT.lift (f b)) : IterM n γ)
[Monad n] [MonadAttach n] [MonadLiftT m n] (f : β n γ) (it : IterM (α := α) m β) :=
(it.mapWithPostcondition (fun b => PostconditionT.attachLift (f b)) : IterM n γ)
/--
If `it` is an iterator, then `it.filterM f` is another iterator that applies a monadic
@@ -456,10 +434,7 @@ it.filterM ---a-----c-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.filterWithPostcondition` will be finite -- and productive -- even if `it`
isn't.
In such situations, the more general combinator `it.filterWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
isn't. In such cases, the termination proof needs to be done manually.
**Performance:**
@@ -467,9 +442,9 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline, expose]
def IterM.filterM {α β : Type w} {m : Type w Type w'} {n : Type w Type w''} [Iterator α m β]
[Monad n] [MonadLiftT m n] (f : β n (ULift Bool)) (it : IterM (α := α) m β) :=
[Monad n] [MonadAttach n] [MonadLiftT m n] (f : β n (ULift Bool)) (it : IterM (α := α) m β) :=
(it.filterMapWithPostcondition
(fun b => (PostconditionT.lift (f b)).map (if ·.down = true then some b else none)) : IterM n β)
(fun b => (PostconditionT.attachLift (f b)).map (if ·.down = true then some b else none)) : IterM n β)
/--
If `it` is an iterator, then `it.filterMap f` is another iterator that applies a function `f` to all
@@ -571,4 +546,4 @@ def IterM.filter {α β : Type w} {m : Type w → Type w'} [Iterator α m β] [M
(f : β Bool) (it : IterM (α := α) m β) :=
(it.filterMap (fun b => if f b then some b else none) : IterM m β)
end Std.Iterators
end Std

View File

@@ -22,11 +22,12 @@ and so on. In other words, `it` flattens the iterator of iterators obtained by m
`f`.
-/
namespace Std.Iterators
namespace Std
open Iterators.Types
/-- Internal implementation detail of the `flatMap` combinator -/
@[ext, unbox]
public structure Flatten (α α₂ β : Type w) (m) where
public structure Iterators.Types.Flatten (α α₂ β : Type w) (m) where
it₁ : IterM (α := α) m (IterM (α := α₂) m β)
it₂ : Option (IterM (α := α₂) m β)
@@ -37,7 +38,7 @@ Internal iterator combinator that is used to implement all `flatMap` variants
def IterM.flattenAfter {α α₂ β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
(it₁ : IterM (α := α) m (IterM (α := α₂) m β)) (it₂ : Option (IterM (α := α₂) m β)) :=
(toIterM (α := Flatten α α₂ β m) it₁, it₂ m β : IterM m β)
(.mk (α := Flatten α α₂ β m) it₁, it₂ m β : IterM m β)
/--
Let `it₁` and `it₂` be iterators and `f` a monadic function mapping `it₁`'s outputs to iterators
@@ -77,7 +78,7 @@ For each value emitted by the outer iterator `it₁`, this combinator calls `f`.
-/
@[always_inline, inline]
public def IterM.flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α m β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it₁ : IterM (α := α) m β) (it₂ : Option (IterM (α := α₂) m γ)) :=
((it₁.mapM f).flattenAfter it₂ : IterM m γ)
@@ -116,7 +117,7 @@ For each value emitted by the outer iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline, expose]
public def IterM.flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α m β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it : IterM (α := α) m β) :=
(it.flatMapAfterM f none : IterM m γ)
@@ -201,23 +202,25 @@ public def IterM.flatMap {α : Type w} {β : Type w} {α₂ : Type w}
(f : β IterM (α := α₂) m γ) (it : IterM (α := α) m β) :=
(it.flatMapAfter f none : IterM m γ)
namespace Iterators.Types
variable {α α₂ β : Type w} {m : Type w Type w'}
/-- The plausible-step predicate for `Flatten` iterators -/
public inductive Flatten.IsPlausibleStep [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] :
(it : IterM (α := Flatten α α₂ β m) m β) (step : IterStep (IterM (α := Flatten α α₂ β m) m β) β) Prop where
| outerYield : {it₁ it₁' it₂'}, it₁.IsPlausibleStep (.yield it₁' it₂')
IsPlausibleStep (toIterM it₁, none m β) (.skip (toIterM it₁', some it₂' m β))
IsPlausibleStep (.mk it₁, none m β) (.skip (.mk it₁', some it₂' m β))
| outerSkip : {it₁ it₁'}, it₁.IsPlausibleStep (.skip it₁')
IsPlausibleStep (toIterM it₁, none m β) (.skip (toIterM it₁', none m β))
IsPlausibleStep (.mk it₁, none m β) (.skip (.mk it₁', none m β))
| outerDone : {it₁}, it₁.IsPlausibleStep .done
IsPlausibleStep (toIterM it₁, none m β) .done
IsPlausibleStep (.mk it₁, none m β) .done
| innerYield : {it₁ it₂ it₂' b}, it₂.IsPlausibleStep (.yield it₂' b)
IsPlausibleStep (toIterM it₁, some it₂ m β) (.yield (toIterM it₁, some it₂' m β) b)
IsPlausibleStep (.mk it₁, some it₂ m β) (.yield (.mk it₁, some it₂' m β) b)
| innerSkip : {it₁ it₂ it₂'}, it₂.IsPlausibleStep (.skip it₂')
IsPlausibleStep (toIterM it₁, some it₂ m β) (.skip (toIterM it₁, some it₂' m β))
IsPlausibleStep (.mk it₁, some it₂ m β) (.skip (.mk it₁, some it₂' m β))
| innerDone : {it₁ it₂}, it₂.IsPlausibleStep .done
IsPlausibleStep (toIterM it₁, some it₂ m β) (.skip (toIterM it₁, none m β))
IsPlausibleStep (.mk it₁, some it₂ m β) (.skip (.mk it₁, none m β))
public instance Flatten.instIterator [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] :
Iterator (Flatten α α₂ β m) m β where
@@ -246,7 +249,7 @@ section Finite
variable {α : Type w} {α₂ : Type w} {β : Type w} {m : Type w Type w'}
variable (α m β) in
def Rel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m] [Finite α₂ m] :
def Flatten.Rel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m] [Finite α₂ m] :
IterM (α := Flatten α α₂ β m) m β IterM (α := Flatten α α₂ β m) m β Prop :=
InvImage
(Prod.Lex
@@ -271,10 +274,10 @@ theorem Flatten.rel_of_right₂ [Monad m] [Iterator α m (IterM (α := α₂) m
Rel α β m it₁, none it₁, some it₂ :=
Prod.Lex.right _ True.intro
instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
def Flatten.instFinitenessRelation [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Finite α₂ m] :
FinitenessRelation (Flatten α α₂ β m) m where
rel := Rel α β m
Rel := Rel α β m
wf := by
apply InvImage.wf
refine fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b
@@ -299,9 +302,9 @@ instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m
apply Flatten.rel_of_right₂
@[no_expose]
public instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
public instance Flatten.instFinite [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Finite α₂ m] : Finite (Flatten α α₂ β m) m :=
.of_finitenessRelation instFinitenessRelationFlattenOfIterMOfFinite
.of_finitenessRelation instFinitenessRelation
end Finite
@@ -310,7 +313,7 @@ section Productive
variable {α : Type w} {α₂ : Type w} {β : Type w} {m : Type w Type w'}
variable (α m β) in
def ProductiveRel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m]
def Flatten.ProductiveRel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m]
[Productive α₂ m] :
IterM (α := Flatten α α₂ β m) m β IterM (α := Flatten α α₂ β m) m β Prop :=
InvImage
@@ -336,10 +339,10 @@ theorem Flatten.productiveRel_of_right₂ [Monad m] [Iterator α m (IterM (α :=
ProductiveRel α β m it₁, none it₁, some it₂ :=
Prod.Lex.right _ True.intro
instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Productive α₂ m] :
def Flatten.instProductivenessRelation [Monad m] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] [Finite α m] [Productive α₂ m] :
ProductivenessRelation (Flatten α α₂ β m) m where
rel := ProductiveRel α β m
Rel := ProductiveRel α β m
wf := by
apply InvImage.wf
refine fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b
@@ -360,18 +363,14 @@ instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m
apply Flatten.productiveRel_of_right₂
@[no_expose]
public instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
public def Flatten.instProductive [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Productive α₂ m] : Productive (Flatten α α₂ β m) m :=
.of_productivenessRelation instProductivenessRelationFlattenOfFiniteIterMOfProductive
.of_productivenessRelation instProductivenessRelation
end Productive
public instance Flatten.instIteratorCollect [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] : IteratorCollect (Flatten α α₂ β m) m n :=
.defaultImplementation
public instance Flatten.instIteratorLoop [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] : IteratorLoop (Flatten α α₂ β m) m n :=
.defaultImplementation
end Std.Iterators
end Std.Iterators.Types

View File

@@ -9,7 +9,6 @@ prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Loop
public import Init.Data.Iterators.Internal.Termination
@[expose] public section
@@ -17,7 +16,7 @@ public import Init.Data.Iterators.Internal.Termination
This module provides the iterator combinator `IterM.take`.
-/
namespace Std.Iterators
namespace Std
variable {α : Type w} {m : Type w Type w'} {β : Type w}
@@ -25,7 +24,7 @@ variable {α : Type w} {m : Type w → Type w'} {β : Type w}
The internal state of the `IterM.take` iterator combinator.
-/
@[unbox]
structure Take (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
structure Iterators.Types.Take (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
Internal implementation detail of the iterator library.
Caution: For `take n`, `countdown` is `n + 1`.
@@ -40,6 +39,8 @@ structure Take (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α
-/
finite : countdown > 0 Finite α m
open Std.Iterators Std.Iterators.Types
/--
Given an iterator `it` and a natural number `n`, `it.take n` is an iterator that outputs
up to the first `n` of `it`'s values in order and then terminates.
@@ -65,7 +66,7 @@ This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def IterM.take [Iterator α m β] (n : Nat) (it : IterM (α := α) m β) :=
toIterM (Take.mk (n + 1) it (Or.inl <| Nat.zero_lt_succ _)) m β
IterM.mk (Take.mk (n + 1) it (Or.inl <| Nat.zero_lt_succ _)) m β
/--
This combinator is only useful for advanced use cases.
@@ -91,7 +92,7 @@ This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def IterM.toTake [Iterator α m β] [Finite α m] (it : IterM (α := α) m β) :=
toIterM (Take.mk 0 it (Or.inr inferInstance)) m β
IterM.mk (Take.mk 0 it (Or.inr inferInstance)) m β
theorem IterM.take.surjective_of_zero_lt {α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (it : IterM (α := Take α m) m β) (h : 0 < it.internalState.countdown) :
@@ -100,6 +101,8 @@ theorem IterM.take.surjective_of_zero_lt {α : Type w} {m : Type w → Type w'}
simp only [take, Nat.sub_add_cancel (m := 1) (n := it.internalState.countdown) (by omega)]
rfl
namespace Iterators.Types
inductive Take.PlausibleStep [Iterator α m β] (it : IterM (α := Take α m) m β) :
(step : IterStep (IterM (α := Take α m) m β) β) Prop where
| yield : {it' out}, it.internalState.inner.IsPlausibleStep (.yield it' out)
@@ -161,7 +164,7 @@ theorem Take.rel_of_zero_of_inner [Monad m] [Iterator α m β]
private def Take.instFinitenessRelation [Monad m] [Iterator α m β]
[Productive α m] :
FinitenessRelation (Take α m) m where
rel := Take.Rel m
Rel := Take.Rel m
wf := by
rw [Rel]
split
@@ -204,12 +207,8 @@ instance Take.instFinite [Monad m] [Iterator α m β] [Productive α m] :
Finite (Take α m) m :=
by exact Finite.of_finitenessRelation instFinitenessRelation
instance Take.instIteratorCollect {n : Type w Type w'} [Monad m] [Monad n] [Iterator α m β] :
IteratorCollect (Take α m) m n :=
.defaultImplementation
instance Take.instIteratorLoop {n : Type x Type x'} [Monad m] [Monad n] [Iterator α m β] :
IteratorLoop (Take α m) m n :=
.defaultImplementation
end Std.Iterators
end Std.Iterators.Types

View File

@@ -6,16 +6,16 @@ Authors: Paul Reichert
module
prelude
public import Init.Data.Iterators.Internal.Termination
public import Init.Data.Iterators.Consumers.Monadic
public section
namespace Std.Iterators
namespace Std
universe v u v' u'
section ULiftT
namespace Iterators
/-- `ULiftT.{v, u}` shrinks a monad on `Type max u v` to a monad on `Type u`. -/
@[expose] -- for codegen
@@ -60,11 +60,14 @@ theorem ULiftT.run_map {n : Type max u v → Type v'} [Monad n] {α β : Type u}
(f <$> x).run = x.run >>= (fun a => pure <| .up (f a.down)) :=
(rfl)
end Iterators
end ULiftT
namespace Iterators.Types
/-- Internal state of the `uLift` iterator combinator. Do not depend on its internals. -/
@[unbox]
structure Types.ULiftIterator (α : Type u) (m : Type u Type u') (n : Type max u v Type v')
structure ULiftIterator (α : Type u) (m : Type u Type u') (n : Type max u v Type v')
(β : Type u) (lift : γ : Type u, m γ ULiftT n γ) : Type max u v where
inner : IterM (α := α) m β
@@ -75,14 +78,14 @@ variable {α : Type u} {m : Type u → Type u'} {n : Type max u v → Type v'}
Transforms a step of the base iterator into a step of the `uLift` iterator.
-/
@[always_inline, inline, expose]
def Types.ULiftIterator.Monadic.modifyStep (step : IterStep (IterM (α := α) m β) β) :
def ULiftIterator.Monadic.modifyStep (step : IterStep (IterM (α := α) m β) β) :
IterStep (IterM (α := ULiftIterator.{v} α m n β lift) n (ULift.{v} β)) (ULift.{v} β) :=
match step with
| .yield it' out => .yield it' (.up out)
| .skip it' => .skip it'
| .done => .done
instance Types.ULiftIterator.instIterator [Iterator α m β] [Monad n] :
instance ULiftIterator.instIterator [Iterator α m β] [Monad n] :
Iterator (ULiftIterator α m n β lift) n (ULift β) where
IsPlausibleStep it step :=
step', it.internalState.inner.IsPlausibleStep step'
@@ -93,9 +96,9 @@ instance Types.ULiftIterator.instIterator [Iterator α m β] [Monad n] :
where finally
case hp => exact step.inflate.val, step.inflate.property, rfl
def Types.ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m] [Monad n] :
private def ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m] [Monad n] :
FinitenessRelation (ULiftIterator α m n β lift) n where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySteps)
Rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySteps)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation h := by
rcases h with _, hs, step, hp, rfl
@@ -105,13 +108,13 @@ def Types.ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m]
· apply IterM.TerminationMeasures.Finite.rel_of_skip
exact hp
instance Types.ULiftIterator.instFinite [Iterator α m β] [Finite α m] [Monad n] :
instance ULiftIterator.instFinite [Iterator α m β] [Finite α m] [Monad n] :
Finite (ULiftIterator α m n β lift) n :=
.of_finitenessRelation instFinitenessRelation
def Types.ULiftIterator.instProductivenessRelation [Iterator α m β] [Productive α m] [Monad n] :
private def ULiftIterator.instProductivenessRelation [Iterator α m β] [Productive α m] [Monad n] :
ProductivenessRelation (ULiftIterator α m n β lift) n where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySkips)
Rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySkips)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation h := by
rcases h with step, hp, hs
@@ -119,18 +122,18 @@ def Types.ULiftIterator.instProductivenessRelation [Iterator α m β] [Productiv
apply IterM.TerminationMeasures.Productive.rel_of_skip
exact hp
instance Types.ULiftIterator.instProductive [Iterator α m β] [Productive α m] [Monad n] :
instance ULiftIterator.instProductive [Iterator α m β] [Productive α m] [Monad n] :
Productive (ULiftIterator α m n β lift) n :=
.of_productivenessRelation instProductivenessRelation
instance Types.ULiftIterator.instIteratorLoop {o : Type x Type x'} [Monad n] [Monad o]
instance ULiftIterator.instIteratorLoop {o : Type x Type x'} [Monad n] [Monad o]
[Iterator α m β] :
IteratorLoop (ULiftIterator α m n β lift) n o :=
.defaultImplementation
instance Types.ULiftIterator.instIteratorCollect [Monad n] [Monad o] [Iterator α m β] :
IteratorCollect (ULiftIterator α m n β lift) n o :=
.defaultImplementation
end Iterators.Types
open Std.Iterators Std.Iterators.Types
/--
Transforms an `m`-monadic iterator with values in `β` into an `n`-monadic iterator with
@@ -149,9 +152,9 @@ it.uLift n ---.up a----.up b---.up c--.up d---⊥
* `Productive`: only if the original iterator is productive
-/
@[always_inline, inline, expose]
def IterM.uLift (it : IterM (α := α) m β) (n : Type max u v Type v')
[lift : MonadLiftT m (ULiftT n)] :
IterM (α := Types.ULiftIterator α m n β (fun _ => lift.monadLift)) n (ULift β) :=
def IterM.uLift {α β : Type u} {m : Type u Type u'} (it : IterM (α := α) m β)
(n : Type max u v Type v') [lift : MonadLiftT m (ULiftT n)] :
IterM (α := ULiftIterator α m n β (fun _ => lift.monadLift)) n (ULift β) :=
it
end Std.Iterators
end Std

View File

@@ -10,7 +10,8 @@ public import Init.Data.Iterators.Combinators.Monadic.Take
@[expose] public section
namespace Std.Iterators
namespace Std
open Std.Iterators Std.Iterators.Types
/--
Given an iterator `it` and a natural number `n`, `it.take n` is an iterator that outputs
@@ -67,4 +68,4 @@ def Iter.toTake {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] (
Iter (α := Take α Id) β :=
it.toIterM.toTake.toIter
end Std.Iterators
end Std

View File

@@ -10,7 +10,8 @@ public import Init.Data.Iterators.Combinators.Monadic.ULift
public section
namespace Std.Iterators
namespace Std
open Std.Iterators Std.Iterators.Types
universe v u v' u'
@@ -20,10 +21,10 @@ variable {α : Type u} {β : Type u}
Transforms a step of the base iterator into a step of the `uLift` iterator.
-/
@[always_inline, inline]
def Types.ULiftIterator.modifyStep (step : IterStep (Iter (α := α) β) β) :
def Iterators.Types.ULiftIterator.modifyStep (step : IterStep (Iter (α := α) β) β) :
IterStep (Iter (α := ULiftIterator.{v} α Id Id β (fun _ => monadLift)) (ULift.{v} β))
(ULift.{v} β) :=
(Monadic.modifyStep (step.mapIterator Iter.toIterM)).mapIterator IterM.toIter
(ULiftIterator.Monadic.modifyStep (step.mapIterator Iter.toIterM)).mapIterator IterM.toIter
/--
Transforms an iterator with values in `β` into one with values in `ULift β`.
@@ -48,4 +49,4 @@ def Iter.uLift (it : Iter (α := α) β) :
Iter (α := Types.ULiftIterator.{v} α Id Id β (fun _ => monadLift)) (ULift β) :=
(it.toIterM.uLift Id).toIter
end Std.Iterators
end Std

View File

@@ -9,9 +9,12 @@ prelude
public import Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.Consumers.Monadic.Access
set_option linter.missingDocs true
@[expose] public section
namespace Std.Iterators
namespace Std
open Std.Iterators
/--
If possible, takes `n` steps with the iterator `it` and
@@ -62,4 +65,4 @@ def Iter.atIdx? {α β} [Iterator α Id β] [Productive α Id] [IteratorAccess
| .skip _ => none
| .done => none
end Std.Iterators
end Std

View File

@@ -21,11 +21,10 @@ Concretely, the following operations are provided:
* `Iter.toList`, collecting the values in a list
* `Iter.toListRev`, collecting the values in a list in reverse order but more efficiently
* `Iter.toArray`, collecting the values in an array
Some operations are implemented using the `IteratorCollect` type class.
-/
namespace Std.Iterators
namespace Std
open Std.Iterators
/--
Traverses the given iterator and stores the emitted values in an array.
@@ -35,7 +34,7 @@ If the iterator is not finite, this function might run forever. The variant
-/
@[always_inline, inline]
def Iter.toArray {α : Type w} {β : Type w}
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter (α := α) β) : Array β :=
[Iterator α Id β] (it : Iter (α := α) β) : Array β :=
it.toIterM.toArray.run
/--
@@ -45,7 +44,7 @@ This function is deprecated. Instead of `it.allowNontermination.toArray`, use `i
-/
@[always_inline, inline, deprecated Iter.toArray (since := "2025-12-04")]
def Iter.Partial.toArray {α : Type w} {β : Type w}
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter.Partial (α := α) β) : Array β :=
[Iterator α Id β] (it : Iter.Partial (α := α) β) : Array β :=
it.it.toArray
/--
@@ -56,7 +55,7 @@ finite. If such a proof is not available, consider using `Iter.toArray`.
-/
@[always_inline, inline]
def Iter.Total.toArray {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter.Total (α := α) β) :
[Iterator α Id β] [Finite α Id] (it : Iter.Total (α := α) β) :
Array β :=
it.it.toArray
@@ -104,7 +103,7 @@ If the iterator is not finite, this function might run forever. The variant
-/
@[always_inline, inline]
def Iter.toList {α : Type w} {β : Type w}
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter (α := α) β) : List β :=
[Iterator α Id β] (it : Iter (α := α) β) : List β :=
it.toIterM.toList.run
/--
@@ -115,7 +114,7 @@ This function is deprecated. Instead of `it.allowNontermination.toList`, use `it
-/
@[always_inline, deprecated Iter.toList (since := "2025-12-04")]
def Iter.Partial.toList {α : Type w} {β : Type w}
[Iterator α Id β] [IteratorCollect α Id Id] (it : Iter.Partial (α := α) β) : List β :=
[Iterator α Id β] (it : Iter.Partial (α := α) β) : List β :=
it.it.toList
/--
@@ -127,8 +126,8 @@ finite. If such a proof is not available, consider using `Iter.toList`.
-/
@[always_inline, inline]
def Iter.Total.toList {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter.Total (α := α) β) :
[Iterator α Id β] [Finite α Id] (it : Iter.Total (α := α) β) :
List β :=
it.it.toList
end Std.Iterators
end Std

View File

@@ -26,7 +26,8 @@ function in every iteration. Concretely, the following operations are provided:
These operations are implemented using the `IteratorLoop` type class.
-/
namespace Std.Iterators
namespace Std
open Std.Iterators
/--
A `ForIn'` instance for iterators. Its generic membership relation is not easy to use,
@@ -677,4 +678,4 @@ def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorL
(it : Iter.Partial (α := α) β) : Nat :=
it.it.count
end Std.Iterators
end Std

View File

@@ -8,9 +8,12 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
/--
`it.IsPlausibleNthOutputStep n step` is the proposition that according to the
@@ -56,8 +59,8 @@ theorem IterM.not_isPlausibleNthOutputStep_yield {α β : Type w} {m : Type w
/--
`IteratorAccess α m` provides efficient implementations for random access or iterators that support
it. `it.nextAtIdx? n` either returns the step in which the `n`-th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`-th
it. `it.nextAtIdx? n` either returns the step in which the `n`th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`th
value.
For monadic iterators, the monadic effects of this operation may differ from manually iterating
@@ -67,6 +70,11 @@ is guaranteed to plausible in the sense of `IterM.IsPlausibleNthOutputStep`.
This class is experimental and users of the iterator API should not explicitly depend on it.
-/
class IteratorAccess (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
`nextAtIdx? it n` either returns the step in which the `n`th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`th
value.
-/
nextAtIdx? (it : IterM (α := α) m β) (n : Nat) :
m (PlausibleIterStep (it.IsPlausibleNthOutputStep n))
@@ -105,4 +113,4 @@ def IterM.atIdx? [Iterator α m β] [IteratorAccess α m] [Monad m] (it : IterM
| .skip _ => return none
| .done => return none
end Std.Iterators
end Std

View File

@@ -11,6 +11,8 @@ public import Init.Data.Iterators.Consumers.Monadic.Total
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.WFExtrinsicFix
set_option linter.missingDocs true
@[expose] public section
/-!
@@ -22,113 +24,23 @@ Concretely, the following operations are provided:
* `IterM.toList`, collecting the values in a list
* `IterM.toListRev`, collecting the values in a list in reverse order but more efficiently
* `IterM.toArray`, collecting the values in an array
Some producers and combinators provide specialized implementations. These are captured by the
`IteratorCollect` type class. They should be implemented by all types of iterators. A default
implementation is provided. The typeclass `LawfulIteratorCollect` asserts that an `IteratorCollect`
instance equals the default implementation.
-/
namespace Std.Iterators
open Std.Internal
section Typeclasses
/--
`IteratorCollect α m` provides efficient implementations of collectors for `α`-based
iterators. Right now, it is limited to a potentially optimized `toArray` implementation.
This class is experimental and users of the iterator API should not explicitly depend on it.
They can, however, assume that consumers that require an instance will work for all iterators
provided by the standard library.
Note: For this to be compositional enough to be useful, `toArrayMapped` would need to accept a
termination proof for the specific mapping function used instead of the blanket `Finite α m`
instance. Otherwise, most combinators like `map` cannot implement their own instance relying on
the instance of their base iterators. However, fixing this is currently low priority.
-/
class IteratorCollect (α : Type w) (m : Type w Type w') (n : Type w Type w'')
{β : Type w} [Iterator α m β] where
/--
Maps the emitted values of an iterator using the given function and collects the results in an
`Array`. This is an internal implementation detail. Consider using `it.map f |>.toArray` instead.
-/
toArrayMapped :
(lift : δ : Type w m δ n δ) {γ : Type w} (β n γ) IterM (α := α) m β n (Array γ)
end Typeclasses
namespace Std
open Std.Internal Std.Iterators
section ToArray
def IterM.DefaultConsumers.toArrayMapped.RecursionRel {α β : Type w} {m : Type w Type w'}
/--
If this relation is well-founded, then `IterM.toArray`, `IterM.toList` and `IterM.toListRev` are
guaranteed to finish after finitely many steps. If all of the iterator's steps terminate
individually, `IterM.toArray` is guaranteed to terminate.
-/
def IterM.toArray.RecursionRel {α β : Type w} {m : Type w Type w'}
[Iterator α m β] {γ : Type w} (x' x : (_ : IterM (α := α) m β) ×' Array γ) : Prop :=
( out, x.1.IsPlausibleStep (.yield x'.1 out) fx, x'.2 = x.2.push fx)
( out, x.1.IsPlausibleStep (.yield x'.1 out) a, x'.2 = x.2.push a)
(x.1.IsPlausibleStep (.skip x'.1) x'.2 = x.2)
/--
This is an internal function used in `IteratorCollect.defaultImplementation`.
It iterates over an iterator and applies `f` whenever a value is emitted before inserting the result
of `f` into an array.
-/
@[always_inline, no_expose]
def IterM.DefaultConsumers.toArrayMapped {α β : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β]
(lift : α : Type w m α n α) {γ : Type w} (f : β n γ)
(it : IterM (α := α) m β) : n (Array γ) :=
letI : MonadLift m n := lift (α := _)
go it #[]
where
@[always_inline]
go it (acc : Array γ) : n (Array γ) :=
letI : MonadLift m n := lift (α := _)
WellFounded.extrinsicFix₂ (C₂ := fun _ _ => n (Array γ)) (InvImage TerminationMeasures.Finite.Rel (·.1.finitelyManySteps!))
(fun (it : IterM (α := α) m β) acc recur => do
match ( it.step).inflate with
| .yield it' out h =>
recur it' (acc.push ( f out)) (by exact TerminationMeasures.Finite.rel_of_yield _)
| .skip it' h => recur it' acc (by exact TerminationMeasures.Finite.rel_of_skip _)
| .done _ => return acc) it acc
/--
This is the default implementation of the `IteratorCollect` class.
It simply iterates through the iterator using `IterM.step`, incrementally building up the desired
data structure. For certain iterators, more efficient implementations are possible and should be
used instead.
-/
@[always_inline]
def IteratorCollect.defaultImplementation {α β : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β] :
IteratorCollect α m n where
toArrayMapped := IterM.DefaultConsumers.toArrayMapped
/--
Asserts that a given `IteratorCollect` instance is equal to `IteratorCollect.defaultImplementation`
*if the underlying iterator is finite*.
(Even though equal, the given instance might be vastly more efficient.)
-/
class LawfulIteratorCollect (α : Type w) (m : Type w Type w') (n : Type w Type w'')
{β : Type w} [Monad m] [Monad n] [Iterator α m β] [i : IteratorCollect α m n] where
lawful_toArrayMapped : lift [LawfulMonadLiftFunction lift] [Finite α m],
i.toArrayMapped lift (α := α) (γ := γ)
= IteratorCollect.defaultImplementation.toArrayMapped lift
theorem LawfulIteratorCollect.toArrayMapped_eq {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad m] [Monad n] [Iterator α m β] [Finite α m] [IteratorCollect α m n]
[hl : LawfulIteratorCollect α m n] {lift : δ : Type w m δ n δ}
[LawfulMonadLiftFunction lift]
{f : β n γ} {it : IterM (α := α) m β} :
IteratorCollect.toArrayMapped lift f it (m := m) =
IterM.DefaultConsumers.toArrayMapped lift f it (m := m) := by
rw [lawful_toArrayMapped]; rfl
instance (α β : Type w) (m : Type w Type w') (n : Type w Type w'') [Monad n]
[Iterator α m β] [Monad m] [Iterator α m β] [Finite α m] :
haveI : IteratorCollect α m n := .defaultImplementation
LawfulIteratorCollect α m n :=
letI : IteratorCollect α m n := .defaultImplementation
fun _ => rfl
/--
Traverses the given iterator and stores the emitted values in an array.
@@ -137,8 +49,18 @@ If the iterator is not finite, this function might run forever. The variant
-/
@[always_inline, inline]
def IterM.toArray {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorCollect α m m] (it : IterM (α := α) m β) : m (Array β) :=
IteratorCollect.toArrayMapped (fun _ => id) pure it
(it : IterM (α := α) m β) : m (Array β) :=
go it #[]
where
@[always_inline]
go it (acc : Array β) : m (Array β) :=
WellFounded.extrinsicFix₂ (C₂ := fun _ _ => m (Array β)) (InvImage TerminationMeasures.Finite.Rel (·.1.finitelyManySteps!))
(fun (it : IterM (α := α) m β) acc recur => do
match ( it.step).inflate with
| .yield it' out h =>
recur it' (acc.push out) (by exact TerminationMeasures.Finite.rel_of_yield _)
| .skip it' h => recur it' acc (by exact TerminationMeasures.Finite.rel_of_skip _)
| .done _ => return acc) it acc
/--
Traverses the given iterator and stores the emitted values in an array.
@@ -147,7 +69,7 @@ This function is deprecated. Instead of `it.allowNontermination.toArray`, use `i
-/
@[always_inline, inline, deprecated IterM.toArray (since := "2025-10-23")]
def IterM.Partial.toArray {α : Type w} {m : Type w Type w'} {β : Type w} [Monad m]
[Iterator α m β] (it : IterM.Partial (α := α) m β) [IteratorCollect α m m] : m (Array β) :=
[Iterator α m β] (it : IterM.Partial (α := α) m β) : m (Array β) :=
it.it.toArray
/--
@@ -158,7 +80,7 @@ finite. If such a proof is not available, consider using `IterM.toArray`.
-/
@[always_inline, inline]
def IterM.Total.toArray {α : Type w} {m : Type w Type w'} {β : Type w} [Monad m]
[Iterator α m β] [Finite α m] (it : IterM.Total (α := α) m β) [IteratorCollect α m m] :
[Iterator α m β] [Finite α m] (it : IterM.Total (α := α) m β) :
m (Array β) :=
it.it.toArray
@@ -218,7 +140,7 @@ If the iterator is not finite, this function might run forever. The variant
-/
@[always_inline, inline]
def IterM.toList {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] [IteratorCollect α m m] (it : IterM (α := α) m β) : m (List β) :=
[Iterator α m β] (it : IterM (α := α) m β) : m (List β) :=
Array.toList <$> IterM.toArray it
/--
@@ -229,7 +151,7 @@ This function is deprecated. Instead of `it.allowNontermination.toList`, use `it
-/
@[always_inline, inline, deprecated IterM.toList (since := "2025-10-23")]
def IterM.Partial.toList {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] (it : IterM.Partial (α := α) m β) [IteratorCollect α m m] :
[Iterator α m β] (it : IterM.Partial (α := α) m β) :
m (List β) :=
Array.toList <$> it.it.toArray
@@ -242,8 +164,8 @@ finite. If such a proof is not available, consider using `IterM.toList`.
-/
@[always_inline, inline]
def IterM.Total.toList {α : Type w} {m : Type w Type w'} {β : Type w} [Monad m]
[Iterator α m β] [Finite α m] (it : IterM.Total (α := α) m β) [IteratorCollect α m m] :
[Iterator α m β] [Finite α m] (it : IterM.Total (α := α) m β) :
m (List β) :=
it.it.toList
end Std.Iterators
end Std

View File

@@ -11,6 +11,8 @@ public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.WFExtrinsicFix
public import Init.Data.Iterators.Consumers.Monadic.Total
set_option linter.missingDocs true
public section
/-!
@@ -31,9 +33,9 @@ types of iterators. A default implementation is provided. The typeclass `LawfulI
asserts that an `IteratorLoop` instance equals the default implementation.
-/
namespace Std.Iterators
namespace Std
open Std.Internal
open Std.Internal Std.Iterators
section Typeclasses
@@ -70,6 +72,9 @@ provided by the standard library.
@[ext]
class IteratorLoop (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
(n : Type x Type x') where
/--
Iteration over the iterator `it` in the manner expected by `for` loops.
-/
forIn : (_liftBind : (γ : Type w) (δ : Type x) (γ n δ) m γ n δ) (γ : Type x),
(plausible_forInStep : β γ ForInStep γ Prop)
(it : IterM (α := α) m β) γ
@@ -82,7 +87,9 @@ end Typeclasses
structure IteratorLoop.WithWF (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
{γ : Type x} (PlausibleForInStep : β γ ForInStep γ Prop)
(hwf : IteratorLoop.WellFounded α m PlausibleForInStep) where
/-- Internal implementation detail of the iterator library. -/
it : IterM (α := α) m β
/-- Internal implementation detail of the iterator library. -/
acc : γ
instance IteratorLoop.WithWF.instWellFoundedRelation
@@ -163,14 +170,15 @@ Asserts that a given `IteratorLoop` instance is equal to `IteratorLoop.defaultIm
-/
class LawfulIteratorLoop (α : Type w) (m : Type w Type w') (n : Type x Type x')
[Monad m] [Monad n] [Iterator α m β] [i : IteratorLoop α m n] where
/-- The implementation of `IteratorLoop.forIn` in `i` is equal to the default implementation. -/
lawful lift [LawfulMonadLiftBindFunction lift] γ it init
(Pl : β γ ForInStep γ Prop) (wf : IteratorLoop.WellFounded α m Pl)
(f : (b : β) it.IsPlausibleIndirectOutput b (c : γ) n (Subtype (Pl b c))) :
i.forIn lift γ Pl it init f =
IteratorLoop.defaultImplementation.forIn lift γ Pl it init f
instance (α : Type w) (m : Type w Type w') (n : Type x Type x')
[Monad m] [Monad n] [Iterator α m β] [Finite α m] :
instance instLawfulIteratorLoopDefaultImplementation (α : Type w) (m : Type w Type w')
(n : Type x Type x') [Monad m] [Monad n] [Iterator α m β] [Finite α m] :
letI : IteratorLoop α m n := .defaultImplementation
LawfulIteratorLoop α m n := by
letI : IteratorLoop α m n := .defaultImplementation
@@ -212,13 +220,14 @@ def IterM.instForIn' {m : Type w → Type w'} {n : Type w → Type w''}
ForIn' n (IterM (α := α) m β) β fun it out => it.IsPlausibleIndirectOutput out :=
IteratorLoop.finiteForIn' (fun _ _ f x => monadLift x >>= f)
instance {m : Type w Type w'} {n : Type w Type w''}
instance IterM.instForInOfIteratorLoop {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n]
[MonadLiftT m n] [Monad n] :
ForIn n (IterM (α := α) m β) β :=
haveI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn'
instForInOfForIn'
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
def IterM.Partial.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n] :
@@ -226,6 +235,7 @@ def IterM.Partial.instForIn' {m : Type w → Type w'} {n : Type w → Type w''}
forIn' it init f :=
haveI := @IterM.instForIn'; forIn' it.it init f
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
def IterM.Total.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n]
@@ -233,7 +243,7 @@ def IterM.Total.instForIn' {m : Type w → Type w'} {n : Type w → Type w''}
ForIn' n (IterM.Total (α := α) m β) β fun it out => it.it.IsPlausibleIndirectOutput out where
forIn' it init f := IterM.instForIn'.forIn' it.it init f
instance {m : Type w Type w'} {n : Type w Type w''}
instance IterM.Partial.instForInOfIteratorLoop {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n] :
ForIn n (IterM.Partial (α := α) m β) β :=
haveI : ForIn' n (IterM.Partial (α := α) m β) β _ := IterM.Partial.instForIn'
@@ -246,12 +256,13 @@ instance {m : Type w → Type w'} {n : Type w → Type w''}
haveI : ForIn' n (IterM.Total (α := α) m β) β _ := IterM.Total.instForIn'
instForInOfForIn'
instance {m : Type w Type w'} {n : Type w Type w''} {α : Type w} {β : Type w} [Iterator α m β]
[IteratorLoop α m n] [Monad n] [MonadLiftT m n] : ForM n (IterM (α := α) m β) β where
instance IterM.instForMOfIteratorLoop {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [Monad n] [MonadLiftT m n] :
ForM n (IterM (α := α) m β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type w Type w'} {n : Type w Type w''} {α : Type w} {β : Type w} [Monad n]
[Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] :
instance IterM.Partial.instForMOfItreratorLoop {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Monad n] [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] :
ForM n (IterM.Partial (α := α) m β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
@@ -943,4 +954,4 @@ def IterM.Partial.size {α : Type w} {m : Type w → Type w'} {β : Type w} [Ite
end Count
end Std.Iterators
end Std

View File

@@ -8,14 +8,19 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std.Iterators
namespace Std
/--
A wrapper around an iterator that provides partial consumers. See `IterM.allowNontermination`.
-/
structure IterM.Partial {α : Type w} (m : Type w Type w') (β : Type w) where
/--
The wrapped iterator, which was wrapped by `IterM.allowNontermination`.
-/
it : IterM (α := α) m β
/--
@@ -29,4 +34,4 @@ def IterM.allowNontermination {α : Type w} {m : Type w → Type w'} {β : Type
(it : IterM (α := α) m β) : IterM.Partial (α := α) m β :=
it
end Std.Iterators
end Std

View File

@@ -9,12 +9,19 @@ prelude
public import Init.Data.Iterators.Basic
set_option doc.verso true
set_option linter.missingDocs true
public section
namespace Std.Iterators
namespace Std
/--
A wrapper around an iterator that provides total consumers. See `IterM.ensureTermination`.
-/
structure IterM.Total {α : Type w} (m : Type w Type w') (β : Type w) where
/--
The wrapped iterator, which was wrapped by `IterM.ensureTermination`.
-/
it : IterM (α := α) m β
/--
@@ -33,4 +40,4 @@ A wrapper around an iterator that provides strictly terminating consumers. See
-/
add_decl_doc IterM.Total
end Std.Iterators
end Std

View File

@@ -8,14 +8,19 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std.Iterators
namespace Std
/--
A wrapper around an iterator that provides partial consumers. See `Iter.allowNontermination`.
-/
structure Iter.Partial {α : Type w} (β : Type w) where
/--
The wrapped iterator, which was wrapped by `Iter.allowNontermination`.
-/
it : Iter (α := α) β
/--
@@ -29,4 +34,4 @@ def Iter.allowNontermination {α : Type w} {β : Type w}
(it : Iter (α := α) β) : Iter.Partial (α := α) β :=
it
end Std.Iterators
end Std

View File

@@ -9,9 +9,12 @@ prelude
public import Init.Data.Stream
public import Init.Data.Iterators.Consumers.Access
set_option linter.missingDocs true
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
instance {α β} [Iterator α Id β] [Productive α Id] [IteratorAccess α Id] :
Stream (Iter (α := α) β) β where
@@ -24,4 +27,4 @@ instance {α β} [Iterator α Id β] [Productive α Id] [IteratorAccess α Id] :
revert h
exact IterM.not_isPlausibleNthOutputStep_yield
end Std.Iterators
end Std

View File

@@ -9,12 +9,19 @@ prelude
public import Init.Data.Iterators.Basic
set_option doc.verso true
set_option linter.missingDocs true
public section
namespace Std.Iterators
namespace Std
/--
A wrapper around an iterator that provides total consumers. See `Iter.ensureTermination`.
-/
structure Iter.Total {α : Type w} (β : Type w) where
/--
The wrapped iterator, which was wrapped by `Iter.ensureTermination`.
-/
it : Iter (α := α) β
/--
@@ -33,4 +40,4 @@ A wrapper around an iterator that provides strictly terminating consumers. See
-/
add_decl_doc Iter.Total
end Std.Iterators
end Std

View File

@@ -7,4 +7,3 @@ module
prelude
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.Data.Iterators.Internal.Termination

View File

@@ -1,63 +0,0 @@
/-
Copyright (c) 2025 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.Iterators.Basic
public section
/-!
This is an internal module used by iterator implementations.
-/
namespace Std.Iterators
/--
Internal implementation detail of the iterator library.
The purpose of this class is that it implies a `Finite` instance but
it is more convenient to implement.
-/
structure FinitenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
wf : WellFounded rel
subrelation : {it it'}, it'.IsPlausibleSuccessorOf it rel it' it
theorem Finite.of_finitenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : FinitenessRelation α m) : Finite α m where
wf := by
refine Subrelation.wf (r := r.rel) ?_ ?_
· intro x y h
apply FinitenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
/--
Internal implementation detail of the iterator library.
The purpose of this class is that it implies a `Productive` instance but
it is more convenient to implement.
-/
structure ProductivenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
wf : WellFounded rel
subrelation : {it it'}, it'.IsPlausibleSkipSuccessorOf it rel it' it
theorem Productive.of_productivenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : ProductivenessRelation α m) : Productive α m where
wf := by
refine Subrelation.wf (r := r.rel) ?_ ?_
· intro x y h
apply ProductivenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
end Std.Iterators

View File

@@ -10,7 +10,8 @@ public import Init.Data.Iterators.Basic
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
/--
Induction principle for finite iterators: One can define a function `f` that maps every
@@ -46,4 +47,4 @@ def Iter.inductSkips {α β} [Iterator α Id β] [Productive α Id]
step it (fun {it'} _ => inductSkips motive step it')
termination_by it.finitelyManySkips
end Std.Iterators
end Std

View File

@@ -16,7 +16,8 @@ public import Init.Data.Array.Attach
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
theorem Iter.unattach_eq_toIter_unattach_toIterM [Iterator α Id β] {it : Iter (α := α) β} {hP} :
it.attachWith P hP =
@@ -26,8 +27,7 @@ theorem Iter.unattach_eq_toIter_unattach_toIterM [Iterator α Id β] {it : Iter
theorem Iter.unattach_toList_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
(it.attachWith P hP).toList.unattach = it.toList := by
simp [Iter.unattach_eq_toIter_unattach_toIterM,
Id.run_map (f := List.unattach), IterM.map_unattach_toList_attachWith,
@@ -36,8 +36,7 @@ theorem Iter.unattach_toList_attachWith [Iterator α Id β]
@[simp]
theorem Iter.toList_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
(it.attachWith P hP).toList = it.toList.attachWith P
(fun out h => hP out (isPlausibleIndirectOutput_of_mem_toList h)) := by
apply List.ext_getElem
@@ -49,16 +48,14 @@ theorem Iter.toList_attachWith [Iterator α Id β]
theorem Iter.unattach_toListRev_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
(it.attachWith P hP).toListRev.unattach = it.toListRev := by
simp [toListRev_eq]
@[simp]
theorem Iter.toListRev_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
(it.attachWith P hP).toListRev = it.toListRev.attachWith P
(fun out h => hP out (isPlausibleIndirectOutput_of_mem_toListRev h)) := by
simp [toListRev_eq]
@@ -66,16 +63,14 @@ theorem Iter.toListRev_attachWith [Iterator α Id β]
@[simp]
theorem Iter.unattach_toArray_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
(it.attachWith P hP).toListRev.unattach = it.toListRev := by
simp [toListRev_eq]
@[simp]
theorem Iter.toArray_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
(it.attachWith P hP).toArray = it.toArray.attachWith P
(fun out h => hP out (isPlausibleIndirectOutput_of_mem_toArray h)) := by
suffices (it.attachWith P hP).toArray.toList = (it.toArray.attachWith P
@@ -89,8 +84,7 @@ theorem Iter.count_attachWith [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id]
[LawfulIteratorLoop α Id Id] :
(it.attachWith P hP).count = it.count := by
letI : IteratorCollect α Id Id := .defaultImplementation
rw [ Iter.length_toList_eq_count, toList_attachWith]
simp
end Std.Iterators
end Std

View File

@@ -9,10 +9,12 @@ prelude
public import Init.Data.Iterators.Lemmas.Consumers
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap
public import Init.Data.Iterators.Combinators.FilterMap
import Init.Control.Lawful.MonadAttach.Lemmas
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
variable {α β γ : Type w} [Iterator α Id β] {it : Iter (α := α) β}
{m : Type w Type w'} {n : Type w Type w''}
@@ -31,15 +33,15 @@ theorem Iter.mapWithPostcondition_eq_toIter_mapWithPostcondition_toIterM [Monad
it.mapWithPostcondition f = (letI : MonadLift Id m := pure; it.toIterM.mapWithPostcondition f) :=
rfl
theorem Iter.filterMapM_eq_toIter_filterMapM_toIterM [Monad m] {f : β m (Option γ)} :
theorem Iter.filterMapM_eq_toIter_filterMapM_toIterM [Monad m] [MonadAttach m] {f : β m (Option γ)} :
it.filterMapM f = (letI : MonadLift Id m := pure; it.toIterM.filterMapM f) :=
rfl
theorem Iter.filterM_eq_toIter_filterM_toIterM [Monad m] {f : β m (ULift Bool)} :
theorem Iter.filterM_eq_toIter_filterM_toIterM [Monad m] [MonadAttach m] {f : β m (ULift Bool)} :
it.filterM f = (letI : MonadLift Id m := pure; it.toIterM.filterM f) :=
rfl
theorem Iter.mapM_eq_toIter_mapM_toIterM [Monad m] {f : β m γ} :
theorem Iter.mapM_eq_toIter_mapM_toIterM [Monad m] [MonadAttach m] {f : β m γ} :
it.mapM f = (letI : MonadLift Id m := pure; it.toIterM.mapM f) :=
rfl
@@ -107,7 +109,7 @@ theorem Iter.step_filterWithPostcondition {f : β → PostconditionT n (ULift Bo
| .done h => rfl
theorem Iter.step_mapWithPostcondition {f : β PostconditionT n γ}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
[Monad n] [LawfulMonad n] :
(it.mapWithPostcondition f).step = (do
match it.step with
| .yield it' out h => do
@@ -128,15 +130,15 @@ theorem Iter.step_mapWithPostcondition {f : β → PostconditionT n γ}
| .done h => rfl
theorem Iter.step_filterMapM {β' : Type w} {f : β n (Option β')}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
[Monad n] [MonadAttach n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterMapM f).step = (do
match it.step with
| .yield it' out h => do
match f out with
| none =>
pure <| .deflate <| .skip (it'.filterMapM f) (.yieldNone (out := out) h .intro)
| some out' =>
pure <| .deflate <| .yield (it'.filterMapM f) out' (.yieldSome (out := out) h .intro)
match MonadAttach.attach (f out) with
| none, hf =>
pure <| .deflate <| .skip (it'.filterMapM f) (.yieldNone (out := out) h hf)
| some out', hf =>
pure <| .deflate <| .yield (it'.filterMapM f) out' (.yieldSome (out := out) h hf)
| .skip it' h =>
pure <| .deflate <| .skip (it'.filterMapM f) (.skip h)
| .done h =>
@@ -153,15 +155,15 @@ theorem Iter.step_filterMapM {β' : Type w} {f : β → n (Option β')}
| .done h => rfl
theorem Iter.step_filterM {f : β n (ULift Bool)}
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
[Monad n] [MonadAttach n] [LawfulMonad n] [MonadLiftT m n] :
(it.filterM f).step = (do
match it.step with
| .yield it' out h => do
match f out with
| .up false =>
pure <| .deflate <| .skip (it'.filterM f) (.yieldNone (out := out) h .up false, .intro, rfl)
| .up true =>
pure <| .deflate <| .yield (it'.filterM f) out (.yieldSome (out := out) h .up true, .intro, rfl)
match MonadAttach.attach (f out) with
| .up false, hf =>
pure <| .deflate <| .skip (it'.filterM f) (.yieldNone (out := out) h .up false, hf, rfl)
| .up true, hf =>
pure <| .deflate <| .yield (it'.filterM f) out (.yieldSome (out := out) h .up true, hf, rfl)
| .skip it' h =>
pure <| .deflate <| .skip (it'.filterM f) (.skip h)
| .done h =>
@@ -171,20 +173,19 @@ theorem Iter.step_filterM {f : β → n (ULift Bool)}
generalize it.toIterM.step = step
match step.inflate with
| .yield it' out h =>
simp [PostconditionT.lift]
apply bind_congr
intro step
simp only
apply bind_congr; intro step
rcases step with _ | _ <;> rfl
| .skip it' h => rfl
| .done h => rfl
theorem Iter.step_mapM {f : β n γ}
[Monad n] [LawfulMonad n] :
[Monad n] [MonadAttach n] [LawfulMonad n] :
(it.mapM f).step = (do
match it.step with
| .yield it' out h => do
let out' f out
pure <| .deflate <| .yield (it'.mapM f) out' (.yieldSome h out', True.intro, rfl)
let out' MonadAttach.attach (f out)
pure <| .deflate <| .yield (it'.mapM f) out'.val (.yieldSome h out', out'.property, rfl)
| .skip it' h =>
pure <| .deflate <| .skip (it'.mapM f) (.skip h)
| .done h =>
@@ -290,174 +291,417 @@ def Iter.val_step_filter {f : β → Bool} :
· simp
@[simp]
theorem Iter.toList_filterMap
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
theorem Iter.toList_filterMap [Finite α Id]
{f : β Option γ} :
(it.filterMap f).toList = it.toList.filterMap f := by
simp [filterMap_eq_toIter_filterMap_toIterM, toList_eq_toList_toIterM, IterM.toList_filterMap]
@[simp]
theorem Iter.toList_map
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
{f : β γ} :
theorem Iter.toList_mapWithPostcondition [Monad m] [LawfulMonad m] [Finite α Id]
{f : β PostconditionT m γ} :
(it.mapWithPostcondition f).toList = it.toList.mapM (fun x => (f x).run) := by
simp [Iter.mapWithPostcondition, IterM.toList_mapWithPostcondition, Iter.toList_eq_toList_toIterM]
@[simp]
theorem Iter.toList_mapM [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Finite α Id] {f : β m γ} :
(it.mapM f).toList = it.toList.mapM f := by
simp [Iter.mapM_eq_toIter_mapM_toIterM, IterM.toList_mapM, Iter.toList_eq_toList_toIterM]
@[simp]
theorem Iter.toList_map [Finite α Id] {f : β γ} :
(it.map f).toList = it.toList.map f := by
simp [map_eq_toIter_map_toIterM, IterM.toList_map, Iter.toList_eq_toList_toIterM]
@[simp]
theorem Iter.toList_filter
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
{f : β Bool} :
theorem Iter.toList_filter [Finite α Id] {f : β Bool} :
(it.filter f).toList = it.toList.filter f := by
simp [filter_eq_toIter_filter_toIterM, IterM.toList_filter, Iter.toList_eq_toList_toIterM]
@[simp]
theorem Iter.toListRev_filterMap
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
theorem Iter.toList_filterMapWithPostcondition_filterMapWithPostcondition
[Monad m] [LawfulMonad m] [Monad n] [LawfulMonad n] [MonadLiftT m n] [LawfulMonadLiftT m n]
[Finite α Id]
{f : β PostconditionT m (Option γ)} {g : γ PostconditionT n (Option δ)} :
((it.filterMapWithPostcondition f).filterMapWithPostcondition g).toList =
(it.filterMapWithPostcondition (m := n) (fun b => do
match (haveI : MonadLift m n := monadLift; f b) with
| none => return none
| some fb => g fb)).toList := by
simp only [Iter.filterMapWithPostcondition]
rw [IterM.toList_filterMapWithPostcondition_filterMapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
rfl
@[simp]
theorem Iter.toList_mapWithPostcondition_mapWithPostcondition
[Monad m] [LawfulMonad m] [Monad n] [LawfulMonad n] [MonadLiftT m n] [LawfulMonadLiftT m n]
[Finite α Id]
{f : β PostconditionT m γ} {g : γ PostconditionT n δ} :
((it.mapWithPostcondition f).mapWithPostcondition g).toList =
(it.mapWithPostcondition (m := n) (haveI : MonadLift m n := monadLift; fun b => f b >>= g)).toList := by
simp only [Iter.mapWithPostcondition]
rw [IterM.toList_mapWithPostcondition_mapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
@[simp]
theorem Iter.toListRev_filterMap [Finite α Id]
{f : β Option γ} :
(it.filterMap f).toListRev = it.toListRev.filterMap f := by
simp [filterMap_eq_toIter_filterMap_toIterM, toListRev_eq_toListRev_toIterM, IterM.toListRev_filterMap]
@[simp]
theorem Iter.toListRev_map
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
theorem Iter.toListRev_map [Finite α Id]
{f : β γ} :
(it.map f).toListRev = it.toListRev.map f := by
simp [map_eq_toIter_map_toIterM, IterM.toListRev_map, Iter.toListRev_eq_toListRev_toIterM]
@[simp]
theorem Iter.toListRev_filter
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
theorem Iter.toListRev_filter [Finite α Id]
{f : β Bool} :
(it.filter f).toListRev = it.toListRev.filter f := by
simp [filter_eq_toIter_filter_toIterM, IterM.toListRev_filter, Iter.toListRev_eq_toListRev_toIterM]
@[simp]
theorem Iter.toArray_filterMap
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
theorem Iter.toArray_filterMap [Finite α Id]
{f : β Option γ} :
(it.filterMap f).toArray = it.toArray.filterMap f := by
simp [filterMap_eq_toIter_filterMap_toIterM, toArray_eq_toArray_toIterM, IterM.toArray_filterMap]
@[simp]
theorem Iter.toArray_map
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
{f : β γ} :
theorem Iter.toArray_mapWithPostcondition [Monad m] [LawfulMonad m] [Finite α Id]
{f : β PostconditionT m γ} :
(it.mapWithPostcondition f).toArray = it.toArray.mapM (fun x => (f x).run) := by
simp [Iter.mapWithPostcondition, IterM.toArray_mapWithPostcondition, Iter.toArray_eq_toArray_toIterM]
@[simp]
theorem Iter.toArray_mapM [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Finite α Id] {f : β m γ} :
(it.mapM f).toArray = it.toArray.mapM f := by
simp [Iter.mapM_eq_toIter_mapM_toIterM, IterM.toArray_mapM, Iter.toArray_eq_toArray_toIterM]
@[simp]
theorem Iter.toArray_map [Finite α Id] {f : β γ} :
(it.map f).toArray = it.toArray.map f := by
simp [map_eq_toIter_map_toIterM, IterM.toArray_map, Iter.toArray_eq_toArray_toIterM]
@[simp]
theorem Iter.toArray_filter
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] [Finite α Id]
{f : β Bool} :
theorem Iter.toArray_filter[Finite α Id] {f : β Bool} :
(it.filter f).toArray = it.toArray.filter f := by
simp [filter_eq_toIter_filter_toIterM, IterM.toArray_filter, Iter.toArray_eq_toArray_toIterM]
@[simp]
theorem Iter.toArray_filterMapWithPostcondition_filterMapWithPostcondition
[Monad m] [LawfulMonad m] [Monad n] [LawfulMonad n] [MonadLiftT m n] [LawfulMonadLiftT m n]
[Finite α Id]
{f : β PostconditionT m (Option γ)} {g : γ PostconditionT n (Option δ)} :
((it.filterMapWithPostcondition f).filterMapWithPostcondition g).toArray =
(it.filterMapWithPostcondition (m := n) (fun b => do
match (haveI : MonadLift m n := monadLift; f b) with
| none => return none
| some fb => g fb)).toArray := by
simp only [Iter.filterMapWithPostcondition]
rw [IterM.toArray_filterMapWithPostcondition_filterMapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
rfl
@[simp]
theorem Iter.toArray_mapWithPostcondition_mapWithPostcondition
[Monad m] [LawfulMonad m] [Monad n] [LawfulMonad n] [MonadLiftT m n] [LawfulMonadLiftT m n]
[Finite α Id]
{f : β PostconditionT m γ} {g : γ PostconditionT n δ} :
((it.mapWithPostcondition f).mapWithPostcondition g).toArray =
(it.mapWithPostcondition (m := n) (haveI : MonadLift m n := monadLift; fun b => f b >>= g)).toArray := by
simp only [Iter.mapWithPostcondition]
rw [IterM.toArray_mapWithPostcondition_mapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
section ForIn
theorem Iter.forIn_filterMapWithPostcondition
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
[MonadLiftT n o] [LawfulMonadLiftT n o] [Finite α Id]
[IteratorLoop α Id o] [LawfulIteratorLoop α Id o]
{it : Iter (α := α) β} {f : β PostconditionT n (Option β₂)} {init : γ}
{g : β₂ γ o (ForInStep γ)} :
forIn (it.filterMapWithPostcondition f) init g = forIn it init (fun out acc => do
match (f out).run with
| some c => g c acc
| none => return .yield acc) := by
simp [Iter.forIn_eq_forIn_toIterM, filterMapWithPostcondition, IterM.forIn_filterMapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]; rfl
theorem Iter.forIn_filterMapM
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
[MonadAttach n] [WeaklyLawfulMonadAttach n]
[MonadLiftT n o] [LawfulMonadLiftT n o]
[Finite α Id] [IteratorLoop α Id o] [LawfulIteratorLoop α Id o]
{it : Iter (α := α) β} {f : β n (Option β₂)} {init : γ} {g : β₂ γ o (ForInStep γ)} :
forIn (it.filterMapM f) init g = forIn it init (fun out acc => do
match f out with
| some c => g c acc
| none => return .yield acc) := by
simp [filterMapM, forIn_eq_forIn_toIterM, IterM.forIn_filterMapM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]; rfl
theorem Iter.forIn_filterMap
[Monad n] [LawfulMonad n] [Finite α Id]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{it : Iter (α := α) β} {f : β Option β₂} {init : γ} {g : β₂ γ n (ForInStep γ)} :
forIn (it.filterMap f) init g = forIn it init (fun out acc => do
match f out with
| some c => g c acc
| none => return .yield acc) := by
simp [filterMap, forIn_eq_forIn_toIterM, IterM.forIn_filterMap]; rfl
theorem Iter.forIn_mapWithPostcondition
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
[MonadLiftT n o] [LawfulMonadLiftT n o] [Finite α Id]
[IteratorLoop α Id o] [LawfulIteratorLoop α Id o]
{it : Iter (α := α) β} {f : β PostconditionT n β₂} {init : γ}
{g : β₂ γ o (ForInStep γ)} :
forIn (it.mapWithPostcondition f) init g =
forIn it init (fun out acc => do g ( (f out).run) acc) := by
simp [mapWithPostcondition, forIn_eq_forIn_toIterM, IterM.forIn_mapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.forIn_mapM
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
[MonadAttach n] [WeaklyLawfulMonadAttach n]
[MonadLiftT n o] [LawfulMonadLiftT n o]
[Finite α Id]
[IteratorLoop α Id o] [LawfulIteratorLoop α Id o]
{it : Iter (α := α) β} {f : β n β₂} {init : γ} {g : β₂ γ o (ForInStep γ)} :
forIn (it.mapM f) init g = forIn it init (fun out acc => do g ( f out) acc) := by
rw [mapM, forIn_eq_forIn_toIterM, IterM.forIn_mapM, instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.forIn_map
[Monad n] [LawfulMonad n]
[Finite α Id] [IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{it : Iter (α := α) β} {f : β β₂} {init : γ} {g : β₂ γ n (ForInStep γ)} :
forIn (it.map f) init g = forIn it init (fun out acc => do g (f out) acc) := by
simp [map, forIn_eq_forIn_toIterM, IterM.forIn_map]
theorem Iter.forIn_filterWithPostcondition
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
[MonadLiftT n o] [LawfulMonadLiftT n o]
[Finite α Id] [IteratorLoop α Id o] [LawfulIteratorLoop α Id o]
{it : Iter (α := α) β} {f : β PostconditionT n (ULift Bool)} {init : γ}
{g : β γ o (ForInStep γ)} :
haveI : MonadLift n o := monadLift
forIn (it.filterWithPostcondition f) init g =
forIn it init (fun out acc => do if ( (f out).run).down then g out acc else return .yield acc) := by
simp [filterWithPostcondition, forIn_eq_forIn_toIterM, IterM.forIn_filterWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.forIn_filterM
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
[MonadAttach n] [WeaklyLawfulMonadAttach n]
[MonadLiftT n o] [LawfulMonadLiftT n o] [Finite α Id]
[IteratorLoop α Id o] [LawfulIteratorLoop α Id o]
{it : Iter (α := α) β} {f : β n (ULift Bool)} {init : γ} {g : β γ o (ForInStep γ)} :
forIn (it.filterM f) init g = forIn it init (fun out acc => do if ( f out).down then g out acc else return .yield acc) := by
simp [filterM, forIn_eq_forIn_toIterM, IterM.forIn_filterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.forIn_filter
[Monad n] [LawfulMonad n]
[Finite α Id] [IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{it : Iter (α := α) β} {f : β Bool} {init : γ} {g : β γ n (ForInStep γ)} :
forIn (it.filter f) init g = forIn it init (fun out acc => do if f out then g out acc else return .yield acc) := by
simp [filter, forIn_eq_forIn_toIterM, IterM.forIn_filter]
end ForIn
section Fold
theorem Iter.foldM_filterMapM {α β γ δ : Type w} {m : Type w Type w'} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id] [Monad m] [Monad n] [LawfulMonad m] [LawfulMonad n]
[IteratorLoop α Id Id] [IteratorLoop α Id m] [IteratorLoop α Id n]
[MonadLiftT m n] [LawfulMonadLiftT m n]
[LawfulIteratorLoop α Id Id] [LawfulIteratorLoop α Id m] [LawfulIteratorLoop α Id n]
{f : β m (Option γ)} {g : δ γ n δ} {init : δ} {it : Iter (α := α) β} :
theorem Iter.foldM_filterMapWithPostcondition {α β γ δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
[Iterator α Id β] [Finite α Id]
[Monad n] [Monad o] [LawfulMonad n] [LawfulMonad o]
[IteratorLoop α Id n] [IteratorLoop α Id o]
[LawfulIteratorLoop α Id n] [LawfulIteratorLoop α Id o]
[MonadLiftT n o] [LawfulMonadLiftT n o]
{f : β PostconditionT n (Option γ)} {g : δ γ o δ} {init : δ} {it : Iter (α := α) β} :
(it.filterMapWithPostcondition f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do
let some c (f b).run | pure d
g d c) := by
rw [filterMapWithPostcondition, IterM.foldM_filterMapWithPostcondition, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]; rfl
theorem Iter.foldM_filterMapM {α β γ δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
[Iterator α Id β] [Finite α Id]
[Monad n] [MonadAttach n] [LawfulMonad n] [WeaklyLawfulMonadAttach n]
[Monad o] [LawfulMonad o]
[IteratorLoop α Id n] [IteratorLoop α Id o]
[LawfulIteratorLoop α Id n] [LawfulIteratorLoop α Id o]
[MonadLiftT n o] [LawfulMonadLiftT n o]
{f : β n (Option γ)} {g : δ γ o δ} {init : δ} {it : Iter (α := α) β} :
(it.filterMapM f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do
let some c f b | pure d
g d c) := by
rw [foldM_eq_foldM_toIterM, filterMapM_eq_toIter_filterMapM_toIterM, IterM.foldM_filterMapM]
congr
simp [instMonadLiftTOfMonadLift, Id.instMonadLiftTOfPure]
simp [filterMapM, IterM.foldM_filterMapM, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]; rfl
theorem Iter.foldM_mapM {α β γ δ : Type w} {m : Type w Type w'} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id] [Monad m] [Monad n] [LawfulMonad m] [LawfulMonad n]
[IteratorLoop α Id m] [IteratorLoop α Id n]
[LawfulIteratorLoop α Id m] [LawfulIteratorLoop α Id n]
[MonadLiftT m n] [LawfulMonadLiftT m n]
{f : β m γ} {g : δ γ n δ} {init : δ} {it : Iter (α := α) β} :
theorem Iter.foldM_mapWithPostcondition {α β γ δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
[Iterator α Id β] [Finite α Id]
[Monad m] [Monad n] [Monad o] [LawfulMonad m][LawfulMonad n] [LawfulMonad o]
[IteratorLoop α Id n] [IteratorLoop α Id o]
[LawfulIteratorLoop α Id n] [LawfulIteratorLoop α Id o]
[MonadLiftT n o] [LawfulMonadLiftT n o]
{f : β PostconditionT n γ} {g : δ γ o δ} {init : δ} {it : Iter (α := α) β} :
(it.mapWithPostcondition f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do let c (f b).run; g d c) := by
simp [mapWithPostcondition, IterM.foldM_mapWithPostcondition, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_mapM {α β γ δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
[Iterator α Id β] [Finite α Id]
[Monad n] [MonadAttach n] [LawfulMonad n] [WeaklyLawfulMonadAttach n]
[Monad o] [LawfulMonad o]
[IteratorLoop α Id n] [IteratorLoop α Id o]
[LawfulIteratorLoop α Id n] [LawfulIteratorLoop α Id o]
[MonadLiftT n o] [LawfulMonadLiftT n o]
{f : β n γ} {g : δ γ o δ} {init : δ} {it : Iter (α := α) β} :
haveI : MonadLift n o := MonadLiftT.monadLift
(it.mapM f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do let c f b; g d c) := by
rw [foldM_eq_foldM_toIterM, mapM_eq_toIter_mapM_toIterM, IterM.foldM_mapM]
congr
simp [instMonadLiftTOfMonadLift, Id.instMonadLiftTOfPure]
simp [mapM, IterM.foldM_mapM, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_filterMap {α β γ : Type w} {δ : Type x} {m : Type x Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{f : β Option γ} {g : δ γ m δ} {init : δ} {it : Iter (α := α) β} :
theorem Iter.foldM_filterWithPostcondition {α β δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
[Iterator α Id β] [Finite α Id]
[Monad n] [Monad o] [LawfulMonad n] [LawfulMonad o]
[IteratorLoop α Id n] [IteratorLoop α Id o]
[LawfulIteratorLoop α Id n] [LawfulIteratorLoop α Id o]
[MonadLiftT n o] [LawfulMonadLiftT n o]
{f : β PostconditionT n (ULift Bool)} {g : δ β o δ} {init : δ} {it : Iter (α := α) β} :
(it.filterWithPostcondition f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do if ( (f b).run).down then g d b else pure d) := by
simp [filterWithPostcondition, IterM.foldM_filterWithPostcondition, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_filterM {α β δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
[Iterator α Id β] [Finite α Id]
[Monad n] [MonadAttach n] [LawfulMonad n] [WeaklyLawfulMonadAttach n]
[Monad o] [LawfulMonad o]
[IteratorLoop α Id n] [IteratorLoop α Id o]
[LawfulIteratorLoop α Id n] [LawfulIteratorLoop α Id o]
[MonadLiftT n o] [LawfulMonadLiftT n o]
{f : β n (ULift Bool)} {g : δ β o δ} {init : δ} {it : Iter (α := α) β} :
(it.filterM f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do if ( f b).down then g d b else pure d) := by
simp [filterM, IterM.foldM_filterM, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_filterMap {α β γ δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id] [Monad n] [LawfulMonad n]
[IteratorLoop α Id n]
[LawfulIteratorLoop α Id n]
{f : β Option γ} {g : δ γ n δ} {init : δ} {it : Iter (α := α) β} :
(it.filterMap f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do
let some c := f b | pure d
g d c) := by
induction it using Iter.inductSteps generalizing init with | step it ihy ihs
rw [foldM_eq_match_step, foldM_eq_match_step, step_filterMap]
-- There seem to be some type dependencies that, combined with nested match expressions,
-- force us to split a lot.
split <;> rename_i h
· split at h
· split at h
· cases h
· cases h; simp [*, ihy _]
· cases h
· cases h
· split at h
· split at h
· cases h; simp [*, ihy _]
· cases h
· cases h; simp [*, ihs _]
· cases h
· split at h
· split at h
· cases h
· cases h
· cases h
· simp [*]
simp [filterMap, IterM.foldM_filterMap, foldM_eq_foldM_toIterM]; rfl
theorem Iter.foldM_map {α β γ : Type w} {δ : Type x} {m : Type x Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{f : β γ} {g : δ γ m δ} {init : δ} {it : Iter (α := α) β} :
theorem Iter.foldM_map {α β γ δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id] [Monad n] [LawfulMonad n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β γ} {g : δ γ n δ} {init : δ} {it : Iter (α := α) β} :
(it.map f).foldM (init := init) g =
it.foldM (init := init) (fun d b => g d (f b)) := by
induction it using Iter.inductSteps generalizing init with | step it ihy ihs
rw [foldM_eq_match_step, foldM_eq_match_step, step_map]
cases it.step using PlausibleIterStep.casesOn
· simp [*, ihy _]
· simp [*, ihs _]
· simp
it.foldM (init := init) (fun d b => do g d (f b)) := by
simp [foldM_eq_forIn, forIn_map]
theorem Iter.fold_filterMapM {α β γ δ : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[IteratorLoop α Id Id.{w}] [IteratorLoop α Id m]
[LawfulIteratorLoop α Id Id] [LawfulIteratorLoop α Id m]
{f : β m (Option γ)} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
theorem Iter.foldM_filter {α β δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id] [Monad n] [LawfulMonad n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β Bool} {g : δ β n δ} {init : δ} {it : Iter (α := α) β} :
(it.filter f).foldM (init := init) g =
it.foldM (init := init) (fun d b => if f b then g d b else pure d) := by
simp only [foldM_eq_forIn, forIn_filter]
congr 1; ext out acc
cases f out <;> simp
theorem Iter.fold_filterMapWithPostcondition {α β γ δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id]
[Monad n] [LawfulMonad n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β PostconditionT n (Option γ)} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
(it.filterMapWithPostcondition f).fold (init := init) g =
it.foldM (init := init) (fun d b => do
let some c (f b).run | pure d
return g d c) := by
simp [filterMapWithPostcondition, IterM.fold_filterMapWithPostcondition, foldM_eq_foldM_toIterM]
rfl
theorem Iter.fold_filterMapM {α β γ δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id]
[Monad n] [MonadAttach n] [LawfulMonad n] [WeaklyLawfulMonadAttach n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β n (Option γ)} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
(it.filterMapM f).fold (init := init) g =
it.foldM (init := init) (fun d b => do
let some c f b | pure d
return g d c) := by
rw [foldM_eq_foldM_toIterM, filterMapM_eq_toIter_filterMapM_toIterM, IterM.fold_filterMapM]
rfl
simp [filterMapM, IterM.fold_filterMapM, foldM_eq_foldM_toIterM]; rfl
theorem Iter.fold_mapM {α β γ δ : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[IteratorLoop α Id Id.{w}] [IteratorLoop α Id m]
[LawfulIteratorLoop α Id Id] [LawfulIteratorLoop α Id m]
{f : β m γ} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
theorem Iter.fold_mapWithPostcondition {α β γ δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id]
[Monad n] [LawfulMonad n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β PostconditionT n γ} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
(it.mapWithPostcondition f).fold (init := init) g =
it.foldM (init := init) (fun d b => do let c (f b).run; return g d c) := by
simp [mapWithPostcondition, IterM.fold_mapWithPostcondition, foldM_eq_foldM_toIterM]
theorem Iter.fold_mapM {α β γ δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id]
[Monad n] [MonadAttach n] [LawfulMonad n] [WeaklyLawfulMonadAttach n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β n γ} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
(it.mapM f).fold (init := init) g =
it.foldM (init := init) (fun d b => do return g d ( f b)) := by
rw [foldM_eq_foldM_toIterM, mapM_eq_toIter_mapM_toIterM, IterM.fold_mapM]
it.foldM (init := init) (fun d b => do let c f b; return g d c) := by
simp [mapM, IterM.fold_mapM, foldM_eq_foldM_toIterM]
theorem Iter.fold_filterMap {α β γ : Type w} {δ : Type x}
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
theorem Iter.fold_filterWithPostcondition {α β δ : Type w}
{n : Type w Type w''}
[Iterator α Id β] [Finite α Id]
[Monad n] [LawfulMonad n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β PostconditionT n (ULift Bool)} {g : δ β δ} {init : δ} {it : Iter (α := α) β} :
(it.filterWithPostcondition f).fold (init := init) g =
it.foldM (init := init) (fun d b => return if ( (f b).run).down then g d b else d) := by
simp [filterWithPostcondition, IterM.fold_filterWithPostcondition, foldM_eq_foldM_toIterM]
theorem Iter.fold_filterM {α β δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id]
[Monad n] [MonadAttach n] [LawfulMonad n] [WeaklyLawfulMonadAttach n]
[IteratorLoop α Id n] [LawfulIteratorLoop α Id n]
{f : β n (ULift Bool)} {g : δ β δ} {init : δ} {it : Iter (α := α) β} :
(it.filterM f).fold (init := init) g =
it.foldM (init := init) (fun d b => return if ( f b).down then g d b else d) := by
simp [filterM, IterM.fold_filterM, foldM_eq_foldM_toIterM]
theorem Iter.fold_filterMap {α β γ δ : Type w}
[Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{f : β Option γ} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
(it.filterMap f).fold (init := init) g =
it.fold (init := init) (fun d b =>
match f b with
| some c => g d c
| _ => d) := by
simp only [fold_eq_foldM, foldM_filterMap]
rfl
simp [filterMap, IterM.fold_filterMap, fold_eq_fold_toIterM]; rfl
theorem Iter.fold_map {α β γ : Type w} {δ : Type x}
theorem Iter.fold_map {α β γ δ : Type w}
[Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{f : β γ} {g : δ γ δ} {init : δ} {it : Iter (α := α) β} :
@@ -465,6 +709,14 @@ theorem Iter.fold_map {α β γ : Type w} {δ : Type x}
it.fold (init := init) (fun d b => g d (f b)) := by
simp [fold_eq_foldM, foldM_map]
theorem Iter.fold_filter {α β δ : Type w}
[Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{f : β Bool} {g : δ β δ} {init : δ} {it : Iter (α := α) β} :
(it.filter f).fold (init := init) g =
it.fold (init := init) (fun d b => if f b then g d b else d) := by
simp [filter, IterM.fold_filter, fold_eq_fold_toIterM]
end Fold
section Count
@@ -479,7 +731,7 @@ theorem Iter.count_map {α β β' : Type w} [Iterator α Id β]
end Count
theorem Iter.anyM_filterMapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[Iterator α Id β] [Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{it : Iter (α := α) β} {f : β m (Option β')} {p : β' m (ULift Bool)} :
(it.filterMapM f).anyM p = (it.mapM (pure (f := m))).anyM (fun x => do
match f x with
@@ -494,14 +746,24 @@ This lemma expresses `Iter.anyM` in terms of `IterM.anyM`.
It requires all involved types to live in `Type 0`.
-/
theorem Iter.anyM_eq_anyM_mapM_pure {α β : Type} {m : Type Type w'} [Iterator α Id β]
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {p : β m Bool} :
it.anyM p = ULift.down <$> (it.mapM (α := α) (pure (f := m))).anyM (fun x => ULift.up <$> p x) := by
rw [anyM_eq_forIn, IterM.anyM_eq_forIn, map_eq_pure_bind]
induction it using Iter.inductSteps with | step it ihy ihs =>
rw [forIn_eq_match_step, IterM.forIn_eq_match_step, bind_assoc, step_mapM]
cases it.step using PlausibleIterStep.casesOn
· simp only [bind_assoc, liftM_pure, pure_bind, map_eq_pure_bind, Shrink.inflate_deflate]
· rename_i out _
simp only [bind_assoc, pure_bind, map_eq_pure_bind, Shrink.inflate_deflate,
liftM, monadLift]
have {x : m Bool} : x = MonadAttach.attach (pure out) >>= (fun _ => x) := by
rw (occs := [1]) [show x = pure out >>= (fun _ => x) by simp]
conv => lhs; rw [ WeaklyLawfulMonadAttach.map_attach (x := pure out)]
simp
refine Eq.trans this ?_
simp only [WeaklyLawfulMonadAttach.bind_attach_of_nonempty (x := pure out), pure_bind]
split; rotate_left; rfl
apply bind_congr; intro px
split
· simp
@@ -510,13 +772,13 @@ theorem Iter.anyM_eq_anyM_mapM_pure {α β : Type} {m : Type → Type w'} [Itera
· simp
theorem Iter.anyM_mapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[Iterator α Id β] [Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{it : Iter (α := α) β} {f : β m β'} {p : β' m (ULift Bool)} :
(it.mapM f).anyM p = (it.mapM (pure (f := m))).anyM (fun x => do p ( f x)) := by
rw [mapM_eq_toIter_mapM_toIterM, IterM.anyM_mapM, mapM_eq_toIter_mapM_toIterM]
theorem Iter.anyM_filterM {α β : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[Iterator α Id β] [Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{it : Iter (α := α) β} {f : β m (ULift Bool)} {p : β m (ULift Bool)} :
(it.filterM f).anyM p = (it.mapM (pure (f := m))).anyM (fun x => do
if ( f x).down then
@@ -576,8 +838,8 @@ theorem Iter.anyM_filter {α β : Type w} {m : Type → Type w'}
· simp
theorem Iter.any_filterMapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
[LawfulMonad m] [LawfulIteratorLoop α Id m]
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (Option β')} {p : β' Bool} :
(it.filterMapM f).any p = (it.mapM (pure (f := m))).anyM (fun x => do
match f x with
@@ -586,15 +848,15 @@ theorem Iter.any_filterMapM {α β β' : Type w} {m : Type w → Type w'}
simp [IterM.any_eq_anyM, anyM_filterMapM]
theorem Iter.any_mapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
[LawfulMonad m] [LawfulIteratorLoop α Id m]
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m β'} {p : β' Bool} :
(it.mapM f).any p = (it.mapM pure).anyM (fun x => (.up <| p ·) <$> (f x)) := by
simp [IterM.any_eq_anyM, anyM_mapM]
theorem Iter.any_filterM {α β : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
[LawfulMonad m] [LawfulIteratorLoop α Id m]
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (ULift Bool)} {p : β Bool} :
(it.filterM f).any p = (it.mapM (pure (f := m))).anyM (fun x => do
if ( f x).down then
@@ -636,7 +898,7 @@ theorem Iter.any_map {α β β' : Type w}
· simp
theorem Iter.allM_filterMapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[Iterator α Id β] [Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{it : Iter (α := α) β} {f : β m (Option β')} {p : β' m (ULift Bool)} :
(it.filterMapM f).allM p = (it.mapM (pure (f := m))).allM (fun x => do
match f x with
@@ -650,29 +912,19 @@ This lemma expresses `Iter.allM` in terms of `IterM.allM`.
It requires all involved types to live in `Type 0`.
-/
theorem Iter.allM_eq_allM_mapM_pure {α β : Type} {m : Type Type w'} [Iterator α Id β]
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {p : β m Bool} :
[Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m] {it : Iter (α := α) β} {p : β m Bool} :
it.allM p = ULift.down <$> (it.mapM (α := α) (pure (f := m))).allM (fun x => ULift.up <$> p x) := by
rw [allM_eq_forIn, IterM.allM_eq_forIn, map_eq_pure_bind]
induction it using Iter.inductSteps with | step it ihy ihs =>
rw [forIn_eq_match_step, IterM.forIn_eq_match_step, bind_assoc, step_mapM]
cases it.step using PlausibleIterStep.casesOn
· simp only [bind_assoc, liftM_pure, pure_bind, map_eq_pure_bind, Shrink.inflate_deflate]
apply bind_congr; intro px
split
· simp [ihy _]
· simp
· simp [ihs _]
· simp
simp [allM_eq_not_anyM_not, anyM_eq_anyM_mapM_pure, IterM.allM_eq_not_anyM_not]
theorem Iter.allM_mapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[Iterator α Id β] [Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{it : Iter (α := α) β} {f : β m β'} {p : β' m (ULift Bool)} :
(it.mapM f).allM p = (it.mapM (pure (f := m))).allM (fun x => do p ( f x)) := by
rw [mapM_eq_toIter_mapM_toIterM, IterM.allM_mapM, mapM_eq_toIter_mapM_toIterM]
theorem Iter.allM_filterM {α β : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
[Iterator α Id β] [Finite α Id] [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{it : Iter (α := α) β} {f : β m (ULift Bool)} {p : β m (ULift Bool)} :
(it.filterM f).allM p = (it.mapM (pure (f := m))).allM (fun x => do
if ( f x).down then
@@ -732,8 +984,9 @@ theorem Iter.allM_filter {α β : Type w} {m : Type → Type w'}
· simp
theorem Iter.all_filterMapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
[LawfulMonad m] [LawfulIteratorLoop α Id m]
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (Option β')} {p : β' Bool} :
(it.filterMapM f).all p = (it.mapM (pure (f := m))).allM (fun x => do
match f x with
@@ -742,15 +995,15 @@ theorem Iter.all_filterMapM {α β β' : Type w} {m : Type w → Type w'}
simp [IterM.all_eq_allM, allM_filterMapM]
theorem Iter.all_mapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
[LawfulMonad m] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m β'} {p : β' Bool} :
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[LawfulIteratorLoop α Id m] {it : Iter (α := α) β} {f : β m β'} {p : β' Bool} :
(it.mapM f).all p = (it.mapM pure).allM (fun x => (.up <| p ·) <$> (f x)) := by
simp [IterM.all_eq_allM, allM_mapM]
theorem Iter.all_filterM {α β : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
[LawfulMonad m] [LawfulIteratorLoop α Id m]
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (ULift Bool)} {p : β Bool} :
(it.filterM f).all p = (it.mapM (pure (f := m))).allM (fun x => do
if ( f x).down then
@@ -791,4 +1044,4 @@ theorem Iter.all_map {α β β' : Type w}
· simp [ihs _]
· simp
end Std.Iterators
end Std

View File

@@ -10,48 +10,59 @@ import Init.Data.Iterators.Lemmas.Combinators.FilterMap
public import Init.Data.Iterators.Combinators.FlatMap
import all Init.Data.Iterators.Combinators.FlatMap
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.FlatMap
import Init.Control.Lawful.MonadAttach.Lemmas
namespace Std.Iterators
open Std.Internal
namespace Std
open Std.Internal Std.Iterators
namespace Iterators.Types
public theorem Flatten.IsPlausibleStep.outerYield_flatMapM_pure {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [Iterator α Id β]
[Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ it₁' : Iter (α := α) β} {it₂' b}
(h : it₁.IsPlausibleStep (.yield it₁' b)) :
(h : it₁.IsPlausibleStep (.yield it₁' b)) (h' : MonadAttach.CanReturn (f b) it₂') :
(it₁.flatMapAfterM f none).IsPlausibleStep (.skip (it₁'.flatMapAfterM f (some it₂'))) := by
apply outerYield_flatMapM
exact .yieldSome h (out' := b) (by simp [PostconditionT.lift, PostconditionT.bind])
apply outerYield_flatMapM (b := b)
· exact FilterMap.PlausibleStep.yieldSome h (by simp)
· exact h'
public theorem Flatten.IsPlausibleStep.outerSkip_flatMapM_pure {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ it₁' : Iter (α := α) β}
(h : it₁.IsPlausibleStep (.skip it₁')) :
(it₁.flatMapAfterM f none).IsPlausibleStep (.skip (it₁'.flatMapAfterM f none)) :=
outerSkip_flatMapM (.skip h)
public theorem Flatten.IsPlausibleStep.outerDone_flatMapM_pure {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : Iter (α := α) β}
(h : it₁.IsPlausibleStep .done) :
(it₁.flatMapAfterM f none).IsPlausibleStep .done :=
outerDone_flatMapM (.done h)
public theorem Flatten.IsPlausibleStep.innerYield_flatMapM_pure {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : Iter (α := α) β} {it₂ it₂' b}
(h : it₂.IsPlausibleStep (.yield it₂' b)) :
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.yield (it₁.flatMapAfterM f (some it₂')) b) :=
innerYield_flatMapM h
public theorem Flatten.IsPlausibleStep.innerSkip_flatMapM_pure {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : Iter (α := α) β} {it₂ it₂'}
(h : it₂.IsPlausibleStep (.skip it₂')) :
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfterM f (some it₂'))) :=
innerSkip_flatMapM h
public theorem Flatten.IsPlausibleStep.innerDone_flatMapM_pure {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : Iter (α := α) β} {it₂}
(h : it₂.IsPlausibleStep .done) :
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfterM f none)) :=
@@ -99,15 +110,19 @@ public theorem Flatten.IsPlausibleStep.innerDone_flatMap_pure {α : Type w} {β
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfter f none)) :=
innerDone_flatMap h
end Iterators.Types
public theorem Iter.step_flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [Iterator α Id β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : Iter (α := α) β} {it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfterM f it₂).step = (do
match it₂ with
| none =>
match it₁.step with
| .yield it₁' b h =>
return .deflate (.skip (it₁'.flatMapAfterM f (some ( f b))) (.outerYield_flatMapM_pure h))
let fx MonadAttach.attach (f b)
return .deflate (.skip (it₁'.flatMapAfterM f (some fx.val)) (.outerYield_flatMapM_pure h fx.property))
| .skip it₁' h => return .deflate (.skip (it₁'.flatMapAfterM f none) (.outerSkip_flatMapM_pure h))
| .done h => return .deflate (.done (.outerDone_flatMapM_pure h))
| some it₂ =>
@@ -118,18 +133,22 @@ public theorem Iter.step_flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type
return .deflate (.skip (it₁.flatMapAfterM f (some it₂')) (.innerSkip_flatMapM_pure h))
| .done h =>
return .deflate (.skip (it₁.flatMapAfterM f none) (.innerDone_flatMapM_pure h))) := by
simp only [flatMapAfterM, IterM.step_flatMapAfterM, Iter.step_mapM]
simp only [flatMapAfterM, IterM.step_flatMapAfterM, Iter.step_mapWithPostcondition,
PostconditionT.operation_pure]
split
· split <;> simp [*]
· rfl
public theorem Iter.step_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α Id β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : Iter (α := α) β} :
(it₁.flatMapM f).step = (do
match it₁.step with
| .yield it₁' b h =>
return .deflate (.skip (it₁'.flatMapAfterM f (some ( f b))) (.outerYield_flatMapM_pure h))
let fx MonadAttach.attach (f b)
return .deflate (.skip (it₁'.flatMapAfterM f (some fx.val)) (.outerYield_flatMapM_pure h fx.property))
| .skip it₁' h => return .deflate (.skip (it₁'.flatMapAfterM f none) (.outerSkip_flatMapM_pure h))
| .done h => return .deflate (.done (.outerDone_flatMapM_pure h))) := by
simp [flatMapM, step_flatMapAfterM]
@@ -167,10 +186,9 @@ public theorem Iter.step_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
| .done h => .done (.outerDone_flatMap_pure h)) := by
simp [flatMap, step_flatMapAfter]
public theorem Iter.toList_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
[IteratorCollect α Id m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α Id m] [LawfulIteratorCollect α₂ m m]
public theorem Iter.toList_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : Iter (α := α) β} {it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfterM f it₂).toList = do
@@ -179,17 +197,11 @@ public theorem Iter.toList_flatMapAfterM {α α₂ β γ : Type w} {m : Type w
| some it₂ => return ( it₂.toList) ++
( List.flatten <$> (it₁.mapM fun b => do ( f b).toList).toList) := by
simp only [flatMapAfterM, IterM.toList_flatMapAfterM]
split
· simp only [mapM, IterM.toList_mapM_mapM, monadLift_self]
congr <;> simp
· apply bind_congr; intro step
simp only [mapM, IterM.toList_mapM_mapM, monadLift_self, bind_pure_comp, Functor.map_map]
congr <;> simp
split <;> simp [IterM.toList_mapM_eq_toList_mapWithPostcondition]
public theorem Iter.toArray_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
[IteratorCollect α Id m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α Id m] [LawfulIteratorCollect α₂ m m]
public theorem Iter.toArray_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : Iter (α := α) β} {it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfterM f it₂).toArray = do
@@ -198,58 +210,47 @@ public theorem Iter.toArray_flatMapAfterM {α α₂ β γ : Type w} {m : Type w
| some it₂ => return ( it₂.toArray) ++
( Array.flatten <$> (it₁.mapM fun b => do ( f b).toArray).toArray) := by
simp only [flatMapAfterM, IterM.toArray_flatMapAfterM]
split
· simp only [mapM, IterM.toArray_mapM_mapM, monadLift_self]
congr <;> simp
· apply bind_congr; intro step
simp only [mapM, IterM.toArray_mapM_mapM, monadLift_self, bind_pure_comp, Functor.map_map]
congr <;> simp
split <;> simp [IterM.toArray_mapM_eq_toArray_mapWithPostcondition]
public theorem Iter.toList_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
[IteratorCollect α Id m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α Id m] [LawfulIteratorCollect α₂ m m]
public theorem Iter.toList_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : Iter (α := α) β} :
(it₁.flatMapM f).toList = List.flatten <$> (it₁.mapM fun b => do ( f b).toList).toList := by
simp [flatMapM, toList_flatMapAfterM]
public theorem Iter.toArray_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
[IteratorCollect α Id m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α Id m] [LawfulIteratorCollect α₂ m m]
public theorem Iter.toArray_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α Id β] [Iterator α₂ m γ] [Finite α Id] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : Iter (α := α) β} :
(it₁.flatMapM f).toArray = Array.flatten <$> (it₁.mapM fun b => do ( f b).toArray).toArray := by
simp [flatMapM, toArray_flatMapAfterM]
public theorem Iter.toList_flatMapAfter {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
[Finite α Id] [Finite α₂ Id] [IteratorCollect α Id Id] [IteratorCollect α₂ Id Id]
[LawfulIteratorCollect α Id Id] [LawfulIteratorCollect α₂ Id Id]
[Finite α Id] [Finite α₂ Id]
{f : β Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂ : Option (Iter (α := α₂) γ)} :
(it₁.flatMapAfter f it₂).toList = match it₂ with
| none => (it₁.map fun b => (f b).toList).toList.flatten
| some it₂ => it₂.toList ++
(it₁.map fun b => (f b).toList).toList.flatten := by
simp only [flatMapAfter, Iter.toList, toIterM_toIter, IterM.toList_flatMapAfter]
cases it₂ <;> simp [map, IterM.toList_map_eq_toList_mapM]
cases it₂ <;> simp [map, IterM.toList_map_eq_toList_mapM, - IterM.toList_map]
public theorem Iter.toArray_flatMapAfter {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
[Finite α Id] [Finite α₂ Id] [IteratorCollect α Id Id] [IteratorCollect α₂ Id Id]
[LawfulIteratorCollect α Id Id] [LawfulIteratorCollect α₂ Id Id]
[Finite α Id] [Finite α₂ Id]
{f : β Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂ : Option (Iter (α := α₂) γ)} :
(it₁.flatMapAfter f it₂).toArray = match it₂ with
| none => (it₁.map fun b => (f b).toArray).toArray.flatten
| some it₂ => it₂.toArray ++
(it₁.map fun b => (f b).toArray).toArray.flatten := by
simp only [flatMapAfter, Iter.toArray, toIterM_toIter, IterM.toArray_flatMapAfter]
cases it₂ <;> simp [map, IterM.toArray_map_eq_toArray_mapM]
cases it₂ <;> simp [map, IterM.toArray_map_eq_toArray_mapM, - IterM.toArray_map]
public theorem Iter.toList_flatMap {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
[Finite α Id] [Finite α₂ Id]
[Iterator α Id β] [Iterator α₂ Id γ] [Finite α Id] [Finite α₂ Id]
[IteratorCollect α Id Id] [IteratorCollect α₂ Id Id]
[LawfulIteratorCollect α Id Id] [LawfulIteratorCollect α₂ Id Id]
{f : β Iter (α := α₂) γ} {it₁ : Iter (α := α) β} :
(it₁.flatMap f).toList = (it₁.map fun b => (f b).toList).toList.flatten := by
simp [flatMap, toList_flatMapAfter]
@@ -257,10 +258,8 @@ public theorem Iter.toList_flatMap {α α₂ β γ : Type w} [Iterator α Id β]
public theorem Iter.toArray_flatMap {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
[Finite α Id] [Finite α₂ Id]
[Iterator α Id β] [Iterator α₂ Id γ] [Finite α Id] [Finite α₂ Id]
[IteratorCollect α Id Id] [IteratorCollect α₂ Id Id]
[LawfulIteratorCollect α Id Id] [LawfulIteratorCollect α₂ Id Id]
{f : β Iter (α := α₂) γ} {it₁ : Iter (α := α) β} :
(it₁.flatMap f).toArray = (it₁.map fun b => (f b).toArray).toArray.flatten := by
simp [flatMap, toArray_flatMapAfter]
end Std.Iterators
end Std

View File

@@ -13,20 +13,20 @@ public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public section
namespace Std.Iterators
namespace Std
open Std.Iterators Std.Iterators.Types
variable {α : Type w} {m : Type w Type w'} {β : Type w} {P : β Prop}
theorem IterM.step_attachWith [Iterator α m β] [Monad m] {it : IterM (α := α) m β} {hP} :
(it.attachWith P hP).step =
(fun s => .deflate Types.Attach.Monadic.modifyStep (it.attachWith P hP) s.inflate, s.inflate, rfl) <$> it.step :=
(fun s => .deflate Attach.Monadic.modifyStep (it.attachWith P hP) s.inflate, s.inflate, rfl) <$> it.step :=
rfl
@[simp]
theorem IterM.map_unattach_toList_attachWith [Iterator α m β] [Monad m]
{it : IterM (α := α) m β} {hP}
[Finite α m] [IteratorCollect α m m]
[LawfulMonad m] [LawfulIteratorCollect α m m] :
[Finite α m] [LawfulMonad m] :
List.unattach <$> (it.attachWith P hP).toList = it.toList := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [IterM.toList_eq_match_step, IterM.toList_eq_match_step, step_attachWith]
@@ -45,8 +45,7 @@ theorem IterM.map_unattach_toList_attachWith [Iterator α m β] [Monad m]
@[simp]
theorem IterM.map_unattach_toListRev_attachWith [Iterator α m β] [Monad m] [Monad n]
{it : IterM (α := α) m β} {hP}
[Finite α m] [IteratorCollect α m m]
[LawfulMonad m] [LawfulIteratorCollect α m m] :
[Finite α m] [LawfulMonad m] :
List.unattach <$> (it.attachWith P hP).toListRev = it.toListRev := by
rw [toListRev_eq, toListRev_eq, map_unattach_toList_attachWith (it := it) (hP := hP)]
simp [-map_unattach_toList_attachWith]
@@ -54,8 +53,8 @@ theorem IterM.map_unattach_toListRev_attachWith [Iterator α m β] [Monad m] [Mo
@[simp]
theorem IterM.map_unattach_toArray_attachWith [Iterator α m β] [Monad m] [Monad n]
{it : IterM (α := α) m β} {hP}
[Finite α m] [IteratorCollect α m m]
[LawfulMonad m] [LawfulIteratorCollect α m m] :
[Finite α m]
[LawfulMonad m] :
(·.map Subtype.val) <$> (it.attachWith P hP).toArray = it.toArray := by
rw [ toArray_toList, toArray_toList, map_unattach_toList_attachWith (it := it) (hP := hP)]
simp [-map_unattach_toList_attachWith, -IterM.toArray_toList]
@@ -65,9 +64,8 @@ theorem IterM.count_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
letI : IteratorCollect α m m := .defaultImplementation
rw [ up_length_toList_eq_count, up_length_toList_eq_count,
map_unattach_toList_attachWith (it := it) (P := P) (hP := hP)]
simp only [Functor.map_map, List.length_unattach]
end Std.Iterators
end Std

View File

@@ -11,8 +11,8 @@ import Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap
public import Init.Data.Iterators.Combinators.Monadic.FlatMap
import all Init.Data.Iterators.Combinators.Monadic.FlatMap
namespace Std.Iterators
open Std.Internal
namespace Std
open Std.Internal Std.Iterators
theorem IterM.step_flattenAfter {α α₂ β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
@@ -32,46 +32,48 @@ theorem IterM.step_flattenAfter {α α₂ β : Type w} {m : Type w → Type w'}
cases it₂
all_goals
· apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn <;> simp [IterM.flattenAfter, toIterM]
cases step.inflate using PlausibleIterStep.casesOn <;> simp [IterM.flattenAfter, IterM.mk]
namespace Iterators.Types
public theorem Flatten.IsPlausibleStep.outerYield_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ it₁' : IterM (α := α) m β} {it₂' b}
(h : it₁.IsPlausibleStep (.yield it₁' b)) :
(h : it₁.IsPlausibleStep (.yield it₁' b)) (h' : MonadAttach.CanReturn (f b) it₂') :
(it₁.flatMapAfterM f none).IsPlausibleStep (.skip (it₁'.flatMapAfterM f (some it₂'))) :=
.outerYield (.yieldSome h _, trivial, rfl)
.outerYield (.yieldSome h _, h', rfl)
public theorem Flatten.IsPlausibleStep.outerSkip_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ it₁' : IterM (α := α) m β}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α m β]
[Iterator α₂ m γ] {f : β m (IterM (α := α₂) m γ)} {it₁ it₁' : IterM (α := α) m β}
(h : it₁.IsPlausibleStep (.skip it₁')) :
(it₁.flatMapAfterM f none).IsPlausibleStep (.skip (it₁'.flatMapAfterM f none)) :=
.outerSkip (.skip h)
public theorem Flatten.IsPlausibleStep.outerDone_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α m β]
[Iterator α₂ m γ] {f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β}
(h : it₁.IsPlausibleStep .done) :
(it₁.flatMapAfterM f none).IsPlausibleStep .done :=
.outerDone (.done h)
public theorem Flatten.IsPlausibleStep.innerYield_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} {it₂ it₂' b}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α m β]
[Iterator α₂ m γ] {f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} {it₂ it₂' b}
(h : it₂.IsPlausibleStep (.yield it₂' b)) :
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.yield (it₁.flatMapAfterM f (some it₂')) b) :=
.innerYield h
public theorem Flatten.IsPlausibleStep.innerSkip_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} {it₂ it₂'}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α m β]
[Iterator α₂ m γ] {f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} {it₂ it₂'}
(h : it₂.IsPlausibleStep (.skip it₂')) :
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfterM f (some it₂'))) :=
.innerSkip h
public theorem Flatten.IsPlausibleStep.innerDone_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} {it₂}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m] [Iterator α m β]
[Iterator α₂ m γ] {f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} {it₂}
(h : it₂.IsPlausibleStep .done) :
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfterM f none)) :=
.innerDone h
@@ -118,15 +120,19 @@ public theorem Flatten.IsPlausibleStep.innerDone_flatMap {α : Type w} {β : Typ
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfter f none)) :=
.innerDone h
end Iterators.Types
public theorem IterM.step_flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] {f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β}
{it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfterM f it₂).step = (do
match it₂ with
| none =>
match ( it₁.step).inflate with
| .yield it₁' b h =>
return .deflate (.skip (it₁'.flatMapAfterM f (some ( f b))) (.outerYield_flatMapM h))
let fx MonadAttach.attach (f b)
return .deflate (.skip (it₁'.flatMapAfterM f (some fx.val)) (.outerYield_flatMapM h fx.property))
| .skip it₁' h => return .deflate (.skip (it₁'.flatMapAfterM f none) (.outerSkip_flatMapM h))
| .done h => return .deflate (.done (.outerDone_flatMapM h))
| some it₂ =>
@@ -138,17 +144,22 @@ public theorem IterM.step_flatMapAfterM {α : Type w} {β : Type w} {α₂ : Typ
split
· simp only [bind_assoc]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn <;> simp
cases step.inflate using PlausibleIterStep.casesOn
· simp only [bind_pure_comp, bind_map_left, Shrink.inflate_deflate]
· simp
· simp
· rfl
public theorem IterM.step_flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
{f : β m (IterM (α := α₂) m γ)} {it₁ : IterM (α := α) m β} :
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [LawfulMonad m]
[WeaklyLawfulMonadAttach m] [Iterator α m β] [Iterator α₂ m γ] {f : β m (IterM (α := α₂) m γ)}
{it₁ : IterM (α := α) m β} :
(it₁.flatMapM f).step = (do
match ( it₁.step).inflate with
| .yield it₁' b h =>
return .deflate (.skip (it₁'.flatMapAfterM f (some ( f b)))
(.outerYield_flatMapM h))
let fx MonadAttach.attach (f b)
return .deflate (.skip (it₁'.flatMapAfterM f (some fx.val))
(.outerYield_flatMapM h fx.property))
| .skip it₁' h => return .deflate (.skip (it₁'.flatMapAfterM f none) (.outerSkip_flatMapM h))
| .done h => return .deflate (.done (.outerDone_flatMapM h))) := by
simp [flatMapM, step_flatMapAfterM]
@@ -187,10 +198,9 @@ public theorem IterM.step_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
| .done h => return .deflate (.done (.outerDone_flatMap h))) := by
simp [flatMap, step_flatMapAfter]
theorem IterM.toList_flattenAfter {α α₂ β : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m]
theorem IterM.toList_flattenAfter {α α₂ β : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
{it₁ : IterM (α := α) m (IterM (α := α₂) m β)} {it₂ : Option (IterM (α := α₂) m β)} :
(it₁.flattenAfter it₂).toList = do
match it₂ with
@@ -203,7 +213,10 @@ theorem IterM.toList_flattenAfter {α α₂ β : Type w} {m : Type w → Type w'
simp only [bind_assoc, map_eq_pure_bind]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy₁ _]
· simp only [bind_pure_comp, pure_bind, Shrink.inflate_deflate,
bind_map_left, Functor.map_map, List.flatten_cons, ihy₁ _]
conv => lhs; rw [ WeaklyLawfulMonadAttach.map_attach (x := IterM.toList _)]
simp
· simp [ihs₁ _]
· simp
cases it₂
@@ -219,42 +232,31 @@ theorem IterM.toList_flattenAfter {α α₂ β : Type w} {m : Type w → Type w'
· simp [ihs₂ _]
· simp [hn]
theorem IterM.toArray_flattenAfter {α α₂ β : Type w} {m : Type w Type w'} [Monad m] [LawfulMonad m]
theorem IterM.toArray_flattenAfter {α α₂ β : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
{it₁ : IterM (α := α) m (IterM (α := α₂) m β)} {it₂ : Option (IterM (α := α₂) m β)} :
(it₁.flattenAfter it₂).toArray = do
match it₂ with
| none => Array.flatten <$> (it₁.mapM fun it₂ => it₂.toArray).toArray
| some it₂ => return ( it₂.toArray) ++ ( Array.flatten <$> (it₁.mapM fun it₂ => it₂.toArray).toArray) := by
induction it₁ using IterM.inductSteps generalizing it₂ with | step it₁ ihy₁ ihs₁ =>
have hn : (it₁.flattenAfter none).toArray =
Array.flatten <$> (it₁.mapM fun it₂ => it₂.toArray).toArray := by
rw [toArray_eq_match_step, toArray_eq_match_step, step_flattenAfter, step_mapM]
simp only [bind_assoc, map_eq_pure_bind]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy₁ _]
· simp [ihs₁ _]
· simp
cases it₂
· exact hn
· rename_i ih₂
induction ih₂ using IterM.inductSteps with | step it₂ ihy₂ ihs₂ =>
rw [toArray_eq_match_step, step_flattenAfter, bind_assoc]
simp only
rw [toArray_eq_match_step, bind_assoc]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy₂ _]
· simp [ihs₂ _]
· simp [hn]
simp only [ IterM.toArray_toList, toList_flattenAfter]
split
· simp only [Functor.map_map]
simp only [ Array.flatten_map_toArray_toArray, Functor.map_map]
rw [IterM.toArray_toList, IterM.toArray_toList, IterM.toArray_map, IterM.toArray_map_mapM]
apply congrArg (it₁.mapM · |>.toArray |> Functor.map Array.flatten); ext it₂
simp
· simp only [bind_pure_comp, Functor.map_map, map_bind, Array.flatten_toArray, bind_map_left,
List.append_toArray]
apply bind_congr; intro bs
simp only [ Functor.map_map, IterM.toList_map, IterM.toList_map_mapM]
apply congrArg (fun f => List.toArray <$> HAppend.hAppend bs <$> List.flatten <$> (mapM f it₁).toList)
simp
public theorem IterM.toList_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
public theorem IterM.toList_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfterM f it₂).toList = do
@@ -264,10 +266,9 @@ public theorem IterM.toList_flatMapAfterM {α α₂ β γ : Type w} {m : Type w
( List.flatten <$> (it₁.mapM fun b => do ( f b).toList).toList) := by
simp [flatMapAfterM, toList_flattenAfter]; rfl
public theorem IterM.toArray_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
public theorem IterM.toArray_flatMapAfterM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfterM f it₂).toArray = do
@@ -277,28 +278,25 @@ public theorem IterM.toArray_flatMapAfterM {α α₂ β γ : Type w} {m : Type w
( Array.flatten <$> (it₁.mapM fun b => do ( f b).toArray).toArray) := by
simp [flatMapAfterM, toArray_flattenAfter]; rfl
public theorem IterM.toList_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
public theorem IterM.toList_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : IterM (α := α) m β} :
(it₁.flatMapM f).toList = List.flatten <$> (it₁.mapM fun b => do ( f b).toList).toList := by
simp [flatMapM, toList_flatMapAfterM]
public theorem IterM.toArray_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
public theorem IterM.toArray_flatMapM {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
{f : β m (IterM (α := α₂) m γ)}
{it₁ : IterM (α := α) m β} :
(it₁.flatMapM f).toArray = Array.flatten <$> (it₁.mapM fun b => do ( f b).toArray).toArray := by
simp [flatMapM, toArray_flatMapAfterM]
public theorem IterM.toList_flatMapAfter {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
public theorem IterM.toList_flatMapAfter {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
{f : β IterM (α := α₂) m γ}
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfter f it₂).toList = do
@@ -308,10 +306,9 @@ public theorem IterM.toList_flatMapAfter {α α₂ β γ : Type w} {m : Type w
( List.flatten <$> (it₁.mapM fun b => (f b).toList).toList) := by
simp [flatMapAfter, toList_flattenAfter]; rfl
public theorem IterM.toArray_flatMapAfter {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
public theorem IterM.toArray_flatMapAfter {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
{f : β IterM (α := α₂) m γ}
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
(it₁.flatMapAfter f it₂).toArray = do
@@ -321,24 +318,22 @@ public theorem IterM.toArray_flatMapAfter {α α₂ β γ : Type w} {m : Type w
( Array.flatten <$> (it₁.mapM fun b => (f b).toArray).toArray) := by
simp [flatMapAfter, toArray_flattenAfter]; rfl
public theorem IterM.toList_flatMap {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
public theorem IterM.toList_flatMap {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
{f : β IterM (α := α₂) m γ}
{it₁ : IterM (α := α) m β} :
(it₁.flatMap f).toList = List.flatten <$> (it₁.mapM fun b => (f b).toList).toList := by
simp [flatMap, toList_flatMapAfter]
public theorem IterM.toArray_flatMap {α α₂ β γ : Type w} {m : Type w Type w'} [Monad m]
[LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
public theorem IterM.toArray_flatMap {α α₂ β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[Iterator α m β] [Iterator α₂ m γ] [Finite α m] [Finite α₂ m]
[IteratorCollect α m m] [IteratorCollect α₂ m m]
[LawfulIteratorCollect α m m] [LawfulIteratorCollect α₂ m m]
{f : β IterM (α := α₂) m γ}
{it₁ : IterM (α := α) m β} :
(it₁.flatMap f).toArray = Array.flatten <$> (it₁.mapM fun b => (f b).toArray).toArray := by
simp [flatMap, toArray_flatMapAfter]
end Std.Iterators
end Std

View File

@@ -11,7 +11,10 @@ public import Init.Data.Iterators.Lemmas.Consumers.Monadic
@[expose] public section
namespace Std.Iterators
namespace Std
open Std.Iterators Std.Iterators.Types
namespace Iterators.Types
theorem Take.isPlausibleStep_take_yield [Monad m] [Iterator α m β] {n : Nat}
{it : IterM (α := α) m β} (h : it.IsPlausibleStep (.yield it' out)) :
@@ -23,6 +26,8 @@ theorem Take.isPlausibleStep_take_skip [Monad m] [Iterator α m β] {n : Nat}
(it.take (n + 1)).IsPlausibleStep (.skip (it'.take (n + 1))) :=
(.skip h (by simp [IterM.take]))
end Iterators.Types
theorem IterM.step_take {α m β} [Monad m] [Iterator α m β] {n : Nat}
{it : IterM (α := α) m β} :
(it.take n).step = (match n with
@@ -42,7 +47,6 @@ theorem IterM.step_take {α m β} [Monad m] [Iterator α m β] {n : Nat}
theorem IterM.toList_take_zero {α m β} [Monad m] [LawfulMonad m] [Iterator α m β]
[Finite (Take α m) m]
[IteratorCollect (Take α m) m m] [LawfulIteratorCollect (Take α m) m m]
{it : IterM (α := α) m β} :
(it.take 0).toList = pure [] := by
rw [toList_eq_match_step]
@@ -62,7 +66,6 @@ theorem IterM.step_toTake {α m β} [Monad m] [Iterator α m β] [Finite α m]
@[simp]
theorem IterM.toList_toTake {α m β} [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toTake.toList = it.toList := by
induction it using IterM.inductSteps with | step it ihy ihs
@@ -74,4 +77,4 @@ theorem IterM.toList_toTake {α m β} [Monad m] [LawfulMonad m] [Iterator α m
· simp [ihs _]
· simp
end Std.Iterators
end Std

View File

@@ -12,7 +12,8 @@ public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
variable {α : Type u} {m : Type u Type u'} {n : Type max u v Type v'}
{β : Type u}
@@ -30,8 +31,8 @@ theorem IterM.step_uLift [Iterator α m β] [Monad n] {it : IterM (α := α) m
@[simp]
theorem IterM.toList_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
[MonadLiftT m (ULiftT n)] [Finite α m] [IteratorCollect α m m]
[LawfulMonad m] [LawfulMonad n] [LawfulIteratorCollect α m m]
[MonadLiftT m (ULiftT n)] [Finite α m]
[LawfulMonad m] [LawfulMonad n]
[LawfulMonadLiftT m (ULiftT n)] :
(it.uLift n).toList =
(fun l => l.down.map ULift.up) <$> (monadLift it.toList : ULiftT n _).run := by
@@ -46,8 +47,8 @@ theorem IterM.toList_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (
@[simp]
theorem IterM.toListRev_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
[MonadLiftT m (ULiftT n)] [Finite α m] [IteratorCollect α m m]
[LawfulMonad m] [LawfulMonad n] [LawfulIteratorCollect α m m]
[MonadLiftT m (ULiftT n)] [Finite α m]
[LawfulMonad m] [LawfulMonad n]
[LawfulMonadLiftT m (ULiftT n)] :
(it.uLift n).toListRev =
(fun l => l.down.map ULift.up) <$> (monadLift it.toListRev : ULiftT n _).run := by
@@ -56,8 +57,8 @@ theorem IterM.toListRev_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM
@[simp]
theorem IterM.toArray_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
[MonadLiftT m (ULiftT n)] [Finite α m] [IteratorCollect α m m]
[LawfulMonad m] [LawfulMonad n] [LawfulIteratorCollect α m m]
[MonadLiftT m (ULiftT n)] [Finite α m]
[LawfulMonad m] [LawfulMonad n]
[LawfulMonadLiftT m (ULiftT n)] :
(it.uLift n).toArray =
(fun l => l.down.map ULift.up) <$> (monadLift it.toArray : ULiftT n _).run := by
@@ -80,4 +81,4 @@ theorem IterM.count_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α
· simp [ihs _]
· simp
end Std.Iterators
end Std

View File

@@ -12,7 +12,8 @@ public import Init.Data.Iterators.Lemmas.Consumers
@[expose] public section
namespace Std.Iterators
namespace Std
open Std.Iterators Std.Iterators.Types
theorem Iter.take_eq_toIter_take_toIterM {α β} [Iterator α Id β] {n : Nat}
{it : Iter (α := α) β} :
@@ -62,8 +63,7 @@ theorem Iter.atIdxSlow?_take {α β}
@[simp]
theorem Iter.toList_take_of_finite {α β} [Iterator α Id β] {n : Nat}
[Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
[Finite α Id] {it : Iter (α := α) β} :
(it.take n).toList = it.toList.take n := by
induction it using Iter.inductSteps generalizing n with | step it ihy ihs
rw [Iter.toList_eq_match_step, Iter.toList_eq_match_step, Iter.step_take]
@@ -79,23 +79,19 @@ theorem Iter.toList_take_of_finite {α β} [Iterator α Id β] {n : Nat}
@[simp]
theorem Iter.toListRev_take_of_finite {α β} [Iterator α Id β] {n : Nat}
[Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
[Finite α Id] {it : Iter (α := α) β} :
(it.take n).toListRev = it.toListRev.drop (it.toList.length - n) := by
rw [toListRev_eq, toList_take_of_finite, List.reverse_take, toListRev_eq]
@[simp]
theorem Iter.toArray_take_of_finite {α β} [Iterator α Id β] {n : Nat}
[Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
[Finite α Id] {it : Iter (α := α) β} :
(it.take n).toArray = it.toArray.take n := by
rw [ toArray_toList, toArray_toList, List.take_toArray, toList_take_of_finite]
@[simp]
theorem Iter.toList_take_zero {α β} [Iterator α Id β]
[Finite (Take α Id) Id]
[IteratorCollect (Take α Id) Id Id] [LawfulIteratorCollect (Take α Id) Id Id]
{it : Iter (α := α) β} :
[Finite (Take α Id) Id] {it : Iter (α := α) β} :
(it.take 0).toList = [] := by
rw [toList_eq_match_step]
simp [step_take]
@@ -112,10 +108,8 @@ theorem Iter.step_toTake {α β} [Iterator α Id β] [Finite α Id]
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
@[simp]
theorem Iter.toList_toTake {α β} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
theorem Iter.toList_toTake {α β} [Iterator α Id β] [Finite α Id] {it : Iter (α := α) β} :
it.toTake.toList = it.toList := by
simp [toTake_eq_toIter_toTake_toIterM, toList_eq_toList_toIterM]
end Std.Iterators
end Std

View File

@@ -14,7 +14,8 @@ public import Init.Data.Iterators.Lemmas.Consumers.Loop
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
variable {α : Type u} {β : Type u}
@@ -36,8 +37,7 @@ theorem Iter.step_uLift [Iterator α Id β] {it : Iter (α := α) β} :
@[simp]
theorem Iter.toList_uLift [Iterator α Id β] {it : Iter (α := α) β}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
it.uLift.toList = it.toList.map ULift.up := by
simp only [monadLift, uLift_eq_toIter_uLift_toIterM, IterM.toList_toIter]
rw [IterM.toList_uLift]
@@ -45,15 +45,13 @@ theorem Iter.toList_uLift [Iterator α Id β] {it : Iter (α := α) β}
@[simp]
theorem Iter.toListRev_uLift [Iterator α Id β] {it : Iter (α := α) β}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
it.uLift.toListRev = it.toListRev.map ULift.up := by
rw [toListRev_eq, toListRev_eq, toList_uLift, List.map_reverse]
@[simp]
theorem Iter.toArray_uLift [Iterator α Id β] {it : Iter (α := α) β}
[Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] :
[Finite α Id] :
it.uLift.toArray = it.toArray.map ULift.up := by
rw [ toArray_toList, toArray_toList, toList_uLift]
simp [-toArray_toList]
@@ -66,4 +64,4 @@ theorem Iter.count_uLift [Iterator α Id β] {it : Iter (α := α) β}
rw [IterM.count_uLift]
simp [monadLift]
end Std.Iterators
end Std

View File

@@ -9,3 +9,4 @@ prelude
public import Init.Data.Iterators.Lemmas.Consumers.Monadic
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Loop
public import Init.Data.Iterators.Lemmas.Consumers.Access

View File

@@ -0,0 +1,26 @@
/-
Copyright (c) 2025 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.Iterators.Consumers.Access
namespace Std.Iter
open Std.Iterators
public theorem atIdxSlow?_eq_match [Iterator α Id β] [Productive α Id]
{n : Nat} {it : Iter (α := α) β} :
it.atIdxSlow? n =
(match it.step.val with
| .yield it' out =>
match n with
| 0 => some out
| n + 1 => it'.atIdxSlow? n
| .skip it' => it'.atIdxSlow? n
| .done => none) := by
fun_induction it.atIdxSlow? n <;> simp_all
end Std.Iter

View File

@@ -16,15 +16,16 @@ import all Init.Data.Iterators.Consumers.Monadic.Total
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
theorem Iter.toArray_eq_toArray_toIterM {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toArray_eq_toArray_toIterM {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toArray = it.toIterM.toArray.run :=
(rfl)
theorem Iter.toList_eq_toList_toIterM {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toList_eq_toList_toIterM {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toList = it.toIterM.toList.run :=
(rfl)
@@ -34,14 +35,14 @@ theorem Iter.toListRev_eq_toListRev_toIterM {α β} [Iterator α Id β] [Finite
(rfl)
@[simp]
theorem Iter.toArray_ensureTermination {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toArray_ensureTermination {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.ensureTermination.toArray = it.toArray :=
(rfl)
@[simp]
theorem Iter.toList_ensureTermination {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toList_ensureTermination {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.ensureTermination.toList = it.toList :=
(rfl)
@@ -51,7 +52,7 @@ theorem Iter.toListRev_ensureTermination_eq_toListRev {α β} [Iterator α Id β
(rfl)
@[simp]
theorem IterM.toList_toIter {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
theorem IterM.toList_toIter {α β} [Iterator α Id β] [Finite α Id]
{it : IterM (α := α) Id β} :
it.toIter.toList = it.toList.run :=
(rfl)
@@ -63,51 +64,50 @@ theorem IterM.toListRev_toIter {α β} [Iterator α Id β] [Finite α Id]
(rfl)
@[simp]
theorem Iter.toList_toArray {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toList_toArray {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toArray.toList = it.toList := by
simp [toArray_eq_toArray_toIterM, toList_eq_toList_toIterM, IterM.toList_toArray]
theorem Iter.toList_toArray_ensureTermination {α β} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
{it : Iter (α := α) β} :
it.ensureTermination.toArray.toList = it.toList := by
simp
@[simp]
theorem Iter.toArray_toList {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toArray_toList {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toList.toArray = it.toArray := by
simp [toArray_eq_toArray_toIterM, toList_eq_toList_toIterM, IterM.toArray_toList]
theorem Iter.toArray_toList_ensureTermination {α β} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
{it : Iter (α := α) β} :
it.ensureTermination.toList.toArray = it.toArray := by
simp
@[simp]
theorem Iter.reverse_toListRev [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
it.toListRev.reverse = it.toList := by
simp [toListRev_eq_toListRev_toIterM, toList_eq_toList_toIterM, IterM.reverse_toListRev]
theorem Iter.reverse_toListRev_ensureTermination [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
{it : Iter (α := α) β} :
it.ensureTermination.toListRev.reverse = it.toList := by
simp
theorem Iter.toListRev_eq {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toListRev_eq {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toListRev = it.toList.reverse := by
simp [Iter.toListRev_eq_toListRev_toIterM, Iter.toList_eq_toList_toIterM, IterM.toListRev_eq]
theorem Iter.toListRev_ensureTermination {α β} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
{it : Iter (α := α) β} :
it.ensureTermination.toListRev = it.toList.reverse := by
simp [toListRev_eq]
theorem Iter.toArray_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toArray_eq_match_step {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toArray = match it.step.val with
| .yield it' out => #[out] ++ it'.toArray
| .skip it' => it'.toArray
@@ -117,16 +117,16 @@ theorem Iter.toArray_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [I
generalize it.toIterM.step.run = step
cases step.inflate using PlausibleIterStep.casesOn <;> simp
theorem Iter.toArray_ensureTermination_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toArray_ensureTermination_eq_match_step {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.ensureTermination.toArray = match it.step.val with
| .yield it' out => #[out] ++ it'.toArray
| .skip it' => it'.toArray
| .done => #[] := by
rw [toArray_ensureTermination, toArray_eq_match_step]
theorem Iter.toList_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toList_eq_match_step {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toList = match it.step.val with
| .yield it' out => out :: it'.toList
| .skip it' => it'.toList
@@ -134,8 +134,8 @@ theorem Iter.toList_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [It
rw [ Iter.toList_toArray, Iter.toArray_eq_match_step]
split <;> simp [Iter.toList_toArray]
theorem Iter.toList_ensureTermination_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
theorem Iter.toList_ensureTermination_eq_match_step {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.ensureTermination.toList = match it.step.val with
| .yield it' out => out :: it'.toList
| .skip it' => it'.toList
@@ -159,7 +159,7 @@ theorem Iter.toListRev_ensureTermination_eq_match_step {α β} [Iterator α Id
rw [toListRev_ensureTermination_eq_toListRev, toListRev_eq_match_step]
theorem Iter.getElem?_toList_eq_atIdxSlow? {α β}
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} {k : Nat} :
it.toList[k]? = it.atIdxSlow? k := by
induction it using Iter.inductSteps generalizing k with | step it ihy ihs
@@ -171,15 +171,15 @@ theorem Iter.getElem?_toList_eq_atIdxSlow? {α β}
· simp
theorem Iter.toList_eq_of_atIdxSlow?_eq {α₁ α₂ β}
[Iterator α₁ Id β] [Finite α₁ Id] [IteratorCollect α₁ Id Id] [LawfulIteratorCollect α₁ Id Id]
[Iterator α₂ Id β] [Finite α₂ Id] [IteratorCollect α₂ Id Id] [LawfulIteratorCollect α₂ Id Id]
[Iterator α₁ Id β] [Finite α₁ Id]
[Iterator α₂ Id β] [Finite α₂ Id]
{it₁ : Iter (α := α₁) β} {it₂ : Iter (α := α₂) β}
(h : k, it₁.atIdxSlow? k = it₂.atIdxSlow? k) :
it₁.toList = it₂.toList := by
ext; simp [getElem?_toList_eq_atIdxSlow?, h]
theorem Iter.isPlausibleIndirectOutput_of_mem_toList
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} {b : β} :
b it.toList it.IsPlausibleIndirectOutput b := by
induction it using Iter.inductSteps with | step it ihy ihs
@@ -202,7 +202,7 @@ theorem Iter.isPlausibleIndirectOutput_of_mem_toList
simp
theorem Iter.isPlausibleIndirectOutput_of_mem_toListRev
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} {b : β} :
b it.toListRev it.IsPlausibleIndirectOutput b := by
intro h
@@ -210,7 +210,7 @@ theorem Iter.isPlausibleIndirectOutput_of_mem_toListRev
simpa [toListRev_eq] using h
theorem Iter.isPlausibleIndirectOutput_of_mem_toArray
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} {b : β} :
b it.toArray it.IsPlausibleIndirectOutput b := by
intro h
@@ -218,4 +218,4 @@ theorem Iter.isPlausibleIndirectOutput_of_mem_toArray
rw [ Array.mem_toList_iff] at h
simpa [toList_toArray] using h
end Std.Iterators
end Std

View File

@@ -15,7 +15,8 @@ import Init.Data.Array.Monadic
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
theorem Iter.forIn'_eq {α β : Type w} [Iterator α Id β] [Finite α Id]
{m : Type x Type x'} [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [hl : LawfulIteratorLoop α Id m]
@@ -149,8 +150,7 @@ private theorem Iter.forIn'_toList.aux {ρ : Type u} {α : Type v} {γ : Type x}
cases h; rfl
theorem Iter.isPlausibleStep_iff_step_eq {α β} [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id]
[LawfulIteratorCollect α Id Id] [LawfulDeterministicIterator α Id]
[Finite α Id] [LawfulDeterministicIterator α Id]
{it : Iter (α := α) β} {step} :
it.IsPlausibleStep step it.step.val = step := by
obtain step', hs' := LawfulDeterministicIterator.isPlausibleStep_eq_eq (it := it.toIterM)
@@ -169,8 +169,7 @@ theorem Iter.isPlausibleStep_iff_step_eq {α β} [Iterator α Id β]
simpa using h
theorem Iter.mem_toList_iff_isPlausibleIndirectOutput {α β} [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id]
[LawfulIteratorCollect α Id Id] [LawfulDeterministicIterator α Id]
[Finite α Id] [LawfulDeterministicIterator α Id]
{it : Iter (α := α) β} {out : β} :
out it.toList it.IsPlausibleIndirectOutput out := by
induction it using Iter.inductSteps with | step it ihy ihs
@@ -216,8 +215,7 @@ theorem Iter.mem_toList_iff_isPlausibleIndirectOutput {α β} [Iterator α Id β
simp [heq, IterStep.successor] at h₁
theorem Iter.mem_toArray_iff_isPlausibleIndirectOutput {α β} [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id]
[LawfulIteratorCollect α Id Id] [LawfulDeterministicIterator α Id]
[Finite α Id] [LawfulDeterministicIterator α Id]
{it : Iter (α := α) β} {out : β} :
out it.toArray it.IsPlausibleIndirectOutput out := by
rw [ Iter.toArray_toList, List.mem_toArray, mem_toList_iff_isPlausibleIndirectOutput]
@@ -225,7 +223,6 @@ theorem Iter.mem_toArray_iff_isPlausibleIndirectOutput {α β} [Iterator α Id
theorem Iter.forIn'_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulDeterministicIterator α Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : (out : β) _ γ m (ForInStep γ)} :
@@ -259,7 +256,6 @@ theorem Iter.forIn'_toList {α β : Type w} [Iterator α Id β]
theorem Iter.forIn'_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulDeterministicIterator α Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : (out : β) _ γ m (ForInStep γ)} :
@@ -270,7 +266,6 @@ theorem Iter.forIn'_toArray {α β : Type w} [Iterator α Id β]
theorem Iter.forIn'_eq_forIn'_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulDeterministicIterator α Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : (out : β) _ γ m (ForInStep γ)} :
@@ -282,7 +277,6 @@ theorem Iter.forIn'_eq_forIn'_toList {α β : Type w} [Iterator α Id β]
theorem Iter.forIn'_eq_forIn'_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulDeterministicIterator α Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : (out : β) _ γ m (ForInStep γ)} :
@@ -294,7 +288,6 @@ theorem Iter.forIn'_eq_forIn'_toArray {α β : Type w} [Iterator α Id β]
theorem Iter.forIn_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : β γ m (ForInStep γ)} :
ForIn.forIn it.toList init f = ForIn.forIn it init f := by
@@ -320,7 +313,6 @@ theorem Iter.forIn_toList {α β : Type w} [Iterator α Id β]
theorem Iter.forIn_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : β γ m (ForInStep γ)} :
ForIn.forIn it.toArray init f = ForIn.forIn it init f := by
@@ -361,15 +353,15 @@ theorem Iter.foldM_eq_match_step {α β : Type w} {γ : Type x} [Iterator α Id
theorem Iter.foldlM_toList {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
{m : Type x Type x'} [Monad m] [LawfulMonad m] [IteratorLoop α Id m]
[LawfulIteratorLoop α Id m] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulIteratorLoop α Id m]
{f : γ β m γ} {init : γ} {it : Iter (α := α) β} :
it.toList.foldlM (init := init) f = it.foldM (init := init) f := by
rw [Iter.foldM_eq_forIn, Iter.forIn_toList]
simp only [List.forIn_yield_eq_foldlM, id_map']
it.toList.foldlM (init := init) f = it.foldM (init := init) f:= by
rw [foldM_eq_forIn, Iter.forIn_toList]
simp
theorem Iter.foldlM_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
{m : Type x Type x'} [Monad m] [LawfulMonad m] [IteratorLoop α Id m]
[LawfulIteratorLoop α Id m] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulIteratorLoop α Id m]
{f : γ β m γ} {init : γ} {it : Iter (α := α) β} :
it.toArray.foldlM (init := init) f = it.foldM (init := init) f := by
rw [Iter.foldM_eq_forIn, Iter.forIn_toArray]
@@ -378,7 +370,6 @@ theorem Iter.foldlM_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [
theorem IterM.forIn_eq_foldM {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : β γ m (ForInStep γ)} :
forIn it init f = ForInStep.value <$>
@@ -443,14 +434,12 @@ theorem Iter.fold_hom {γ₁ : Type x₁} {γ₂ : Type x₂} [Iterator α Id β
theorem Iter.toList_eq_fold {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
it.toList = it.fold (init := []) (fun l out => l ++ [out]) := by
rw [Iter.toList_eq_toList_toIterM, IterM.toList_eq_fold, Iter.fold_eq_fold_toIterM]
theorem Iter.toArray_eq_fold {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
it.toArray = it.fold (init := #[]) (fun xs out => xs.push out) := by
simp only [ toArray_toList, toList_eq_fold]
@@ -460,7 +449,6 @@ theorem Iter.toArray_eq_fold {α β : Type w} [Iterator α Id β]
@[simp]
theorem Iter.foldl_toList {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{f : γ β γ} {init : γ} {it : Iter (α := α) β} :
it.toList.foldl (init := init) f = it.fold (init := init) f := by
rw [fold_eq_foldM, List.foldl_eq_foldlM, Iter.foldlM_toList]
@@ -468,7 +456,6 @@ theorem Iter.foldl_toList {α β : Type w} {γ : Type x} [Iterator α Id β] [Fi
@[simp]
theorem Iter.foldl_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{f : γ β γ} {init : γ} {it : Iter (α := α) β} :
it.toArray.foldl (init := init) f = it.fold (init := init) f := by
rw [fold_eq_foldM, Array.foldl_eq_foldlM, Iter.foldlM_toArray]
@@ -508,7 +495,6 @@ theorem Iter.count_eq_match_step {α β : Type w} [Iterator α Id β]
@[simp]
theorem Iter.size_toArray_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toArray.size = it.count := by
@@ -520,7 +506,6 @@ def Iter.size_toArray_eq_size := @size_toArray_eq_count
@[simp]
theorem Iter.length_toList_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toList.length = it.count := by
@@ -531,7 +516,6 @@ def Iter.length_toList_eq_size := @length_toList_eq_count
@[simp]
theorem Iter.length_toListRev_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toListRev.length = it.count := by
@@ -574,7 +558,6 @@ theorem Iter.anyM_eq_match_step {α β : Type w} {m : Type → Type w'} [Iterato
theorem Iter.anyM_toList {α β : Type w} {m : Type Type w'} [Iterator α Id β]
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {p : β m Bool} :
it.toList.anyM p = it.anyM p := by
induction it using Iter.inductSteps with | step it ihy ihs =>
@@ -586,7 +569,6 @@ theorem Iter.anyM_toList {α β : Type w} {m : Type → Type w'} [Iterator α Id
theorem Iter.anyM_toArray {α β : Type w} {m : Type Type w'} [Iterator α Id β]
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {p : β m Bool} :
it.toArray.anyM p = it.anyM p := by
simp only [ Iter.toArray_toList, List.anyM_toArray, anyM_toList]
@@ -633,7 +615,6 @@ theorem Iter.any_eq_forIn {α β : Type w} [Iterator α Id β]
theorem Iter.any_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {p : β Bool} :
it.toList.any p = it.any p := by
induction it using Iter.inductSteps with | step it ihy ihs =>
@@ -646,7 +627,6 @@ theorem Iter.any_toList {α β : Type w} [Iterator α Id β]
theorem Iter.any_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {p : β Bool} :
it.toArray.any p = it.any p := by
simp only [ Iter.toArray_toList, List.any_toArray, any_toList]
@@ -725,7 +705,6 @@ theorem Iter.all_eq_forIn {α β : Type w} [Iterator α Id β]
theorem Iter.all_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {p : β Bool} :
it.toList.all p = it.all p := by
induction it using Iter.inductSteps with | step it ihy ihs =>
@@ -738,7 +717,6 @@ theorem Iter.all_toList {α β : Type w} [Iterator α Id β]
theorem Iter.all_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {p : β Bool} :
it.toArray.all p = it.all p := by
simp only [ Iter.toArray_toList, List.all_toArray, all_toList]
@@ -792,8 +770,8 @@ theorem Iter.findSomeM?_eq_match_step {α β : Type w} {γ : Type x} {m : Type x
· simp
theorem Iter.findSomeM?_toList {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorCollect α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [IteratorLoop α Id m]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (Option γ)} :
it.toList.findSomeM? f = it.findSomeM? f := by
induction it using Iter.inductSteps with | step it ihy ihs
@@ -835,8 +813,8 @@ theorem Iter.findSome?_eq_match_step {α β : Type w} {γ : Type x}
· simp
theorem Iter.findSome?_toList {α β : Type w} {γ : Type x}
[Iterator α Id β] [IteratorLoop α Id Id] [IteratorCollect α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β Option γ} :
it.toList.findSome? f = it.findSome? f := by
simp [findSome?_eq_findSomeM?, List.findSome?_eq_findSomeM?, findSomeM?_toList]
@@ -846,7 +824,6 @@ theorem Iter.findSomeM?_pure {α β : Type w} {γ : Type x} {m : Type x → Type
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β Option γ} :
it.findSomeM? (pure <| f ·) = pure (f := m) (it.findSome? f) := by
letI : IteratorCollect α Id Id := .defaultImplementation
simp [ findSomeM?_toList, findSome?_toList, List.findSomeM?_pure]
theorem Iter.findM?_eq_findSomeM? {α β : Type w} {m : Type w Type w'} [Monad m]
@@ -873,15 +850,15 @@ theorem Iter.findM?_eq_match_step {α β : Type w} {m : Type w → Type w'} [Mon
· simp
theorem Iter.findM?_toList {α β : Type} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorCollect α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [IteratorLoop α Id m]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m Bool} :
it.toList.findM? f = it.findM? (.up <$> f ·) := by
simp [findM?_eq_findSomeM?, List.findM?_eq_findSomeM?, findSomeM?_toList]
theorem Iter.findM?_eq_findM?_toList {α β : Type} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorCollect α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [IteratorLoop α Id m]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (ULift Bool)} :
it.findM? f = it.toList.findM? (ULift.down <$> f ·) := by
simp [findM?_toList]
@@ -917,8 +894,8 @@ theorem Iter.find?_eq_match_step {α β : Type w}
· simp
theorem Iter.find?_toList {α β : Type w}
[Iterator α Id β] [IteratorLoop α Id Id] [IteratorCollect α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] [LawfulIteratorCollect α Id Id]
[Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β Bool} :
it.toList.find? f = it.find? f := by
simp [find?_eq_findSome?, List.find?_eq_findSome?_guard, findSome?_toList, Option.guard_def]
@@ -938,4 +915,4 @@ theorem Iter.findM?_pure {α β : Type w} {m : Type w → Type w'} [Monad m]
· simp [ihs _]
· simp
end Std.Iterators
end Std

View File

@@ -15,87 +15,68 @@ import all Init.WFExtrinsicFix
public section
namespace Std.Iterators
open Std.Internal
namespace Std
open Std.Iterators Std.Internal
variable {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
{lift : δ : Type w m δ n δ} {f : β n γ} {it : IterM (α := α) m β}
variable {α β : Type w} {m : Type w Type w'} {it : IterM (α := α) m β}
private theorem IterM.DefaultConsumers.toArrayMapped.go_eq [Monad n]
[Iterator α m β] [LawfulMonad n] [Finite α m] {acc : Array γ} :
letI : MonadLift m n := lift (δ := _)
go lift f it acc (m := m) = (do
private theorem IterM.toArray.go_eq [Monad m]
[Iterator α m β] [LawfulMonad m] [Finite α m] {acc : Array β} :
go it acc (m := m) = (do
match ( it.step).inflate.val with
| .yield it' out => go lift f it' (acc.push ( f out))
| .skip it' => go lift f it' acc
| .yield it' out => go it' (acc.push out)
| .skip it' => go it' acc
| .done => return acc) := by
letI : MonadLift m n := lift (δ := _)
rw [toArrayMapped.go, WellFounded.extrinsicFix₂_eq_apply]
rw [toArray.go, WellFounded.extrinsicFix₂_eq_apply]
· simp only
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· apply bind_congr; intro fx
simp [go]
· simp [go]
· simp
cases step.inflate using PlausibleIterStep.casesOn <;> simp [go]
· simp only [show (IterM.finitelyManySteps! = IterM.finitelyManySteps) by rfl]
apply InvImage.wf
exact WellFoundedRelation.wf
private theorem IterM.DefaultConsumers.toArrayMapped.go.aux₁ [Monad n] [LawfulMonad n]
[Iterator α m β] [Finite α m] {b : γ} {bs : Array γ} :
IterM.DefaultConsumers.toArrayMapped.go lift f it (#[b] ++ bs) (m := m) =
(#[b] ++ ·) <$> IterM.DefaultConsumers.toArrayMapped.go lift f it bs (m := m) := by
private theorem IterM.toArray.go.aux₁ [Monad m] [LawfulMonad m]
[Iterator α m β] [Finite α m] {b : β} {bs : Array β} :
IterM.toArray.go it (#[b] ++ bs) (m := m) =
(#[b] ++ ·) <$> IterM.toArray.go it bs (m := m) := by
induction it using IterM.inductSteps generalizing bs with | step it ihy ihs
rw [go_eq, map_eq_pure_bind, go_eq, bind_assoc]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn <;> simp (discharger := assumption) [ihy, ihs]
private theorem IterM.DefaultConsumers.toArrayMapped.go.aux₂ [Monad n] [LawfulMonad n]
[Iterator α m β] [Finite α m] {acc : Array γ} :
IterM.DefaultConsumers.toArrayMapped.go lift f it acc (m := m) =
(acc ++ ·) <$> IterM.DefaultConsumers.toArrayMapped lift f it (m := m) := by
private theorem IterM.toArray.go.aux₂ [Monad m] [LawfulMonad m]
[Iterator α m β] [Finite α m] {acc : Array β} :
IterM.toArray.go it acc (m := m) =
(acc ++ ·) <$> it.toArray := by
rw [ Array.toArray_toList (xs := acc)]
generalize acc.toList = acc
induction acc with
| nil => simp [toArrayMapped]
| nil => simp [toArray]
| cons x xs ih =>
rw [List.toArray_cons, IterM.DefaultConsumers.toArrayMapped.go.aux₁, ih]
rw [List.toArray_cons, IterM.toArray.go.aux₁, ih]
simp only [Functor.map_map, Array.append_assoc]
theorem IterM.DefaultConsumers.toArrayMapped_eq_match_step [Monad n] [LawfulMonad n]
theorem IterM.toArray_eq_match_step [Monad m] [LawfulMonad m]
[Iterator α m β] [Finite α m] :
IterM.DefaultConsumers.toArrayMapped lift f it (m := m) = letI : MonadLift m n := lift (δ := _); (do
match ( it.step).inflate.val with
| .yield it' out =>
return #[ f out] ++ ( IterM.DefaultConsumers.toArrayMapped lift f it' (m := m))
| .skip it' => IterM.DefaultConsumers.toArrayMapped lift f it' (m := m)
| .done => return #[]) := by
rw [IterM.DefaultConsumers.toArrayMapped, IterM.DefaultConsumers.toArrayMapped.go_eq]
apply bind_congr
intro step
cases step.inflate using PlausibleIterStep.casesOn <;>
simp [IterM.DefaultConsumers.toArrayMapped.go.aux₂]
@[simp]
theorem IterM.toArray_ensureTermination [Monad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] {it : IterM (α := α) m β} :
it.ensureTermination.toArray = it.toArray :=
(rfl)
theorem IterM.toArray_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m] :
it.toArray = (do
IterM.toArray it (m := m) = (do
match ( it.step).inflate.val with
| .yield it' out => return #[out] ++ ( it'.toArray)
| .skip it' => it'.toArray
| .done => return #[]) := by
simp only [IterM.toArray, LawfulIteratorCollect.toArrayMapped_eq]
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp [bind_pure_comp, pure_bind]
rw [IterM.toArray, IterM.toArray.go_eq]
apply bind_congr
intro step
cases step.inflate using PlausibleIterStep.casesOn <;>
simp [IterM.toArray.go.aux₂]
@[simp]
theorem IterM.toArray_ensureTermination [Monad m] [Iterator α m β] [Finite α m]
{it : IterM (α := α) m β} :
it.ensureTermination.toArray = it.toArray :=
(rfl)
theorem IterM.toArray_ensureTermination_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β]
[Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m] :
[Finite α m] :
it.ensureTermination.toArray = (do
match ( it.step).inflate.val with
| .yield it' out => return #[out] ++ ( it'.toArray)
@@ -105,34 +86,34 @@ theorem IterM.toArray_ensureTermination_eq_match_step [Monad m] [LawfulMonad m]
@[simp]
theorem IterM.toList_ensureTermination [Monad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] {it : IterM (α := α) m β} :
{it : IterM (α := α) m β} :
it.ensureTermination.toList = it.toList :=
(rfl)
@[simp]
theorem IterM.toList_toArray [Monad m] [Iterator α m β] [Finite α m] [IteratorCollect α m m]
theorem IterM.toList_toArray [Monad m] [Iterator α m β] [Finite α m]
{it : IterM (α := α) m β} :
Array.toList <$> it.toArray = it.toList := by
simp [IterM.toList]
theorem IterM.toList_toArray_ensureTermination [Monad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] {it : IterM (α := α) m β} :
{it : IterM (α := α) m β} :
Array.toList <$> it.ensureTermination.toArray = it.toList := by
simp
@[simp]
theorem IterM.toArray_toList [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] {it : IterM (α := α) m β} :
{it : IterM (α := α) m β} :
List.toArray <$> it.toList = it.toArray := by
simp [IterM.toList, -toList_toArray]
theorem IterM.toArray_toList_ensureTermination [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] {it : IterM (α := α) m β} :
{it : IterM (α := α) m β} :
List.toArray <$> it.ensureTermination.toList = it.toArray := by
rw [toList_ensureTermination, toArray_toList]
theorem IterM.toList_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m] {it : IterM (α := α) m β} :
{it : IterM (α := α) m β} :
it.toList = (do
match ( it.step).inflate.val with
| .yield it' out => return out :: ( it'.toList)
@@ -145,7 +126,7 @@ theorem IterM.toList_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β]
split <;> simp
theorem IterM.toList_ensureTermination_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β]
[Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m] {it : IterM (α := α) m β} :
[Finite α m] {it : IterM (α := α) m β} :
it.ensureTermination.toList = (do
match ( it.step).inflate.val with
| .yield it' out => return out :: ( it'.toList)
@@ -219,7 +200,6 @@ theorem IterM.toListRev_ensureTermination_eq_match_step [Monad m] [LawfulMonad m
@[simp]
theorem IterM.reverse_toListRev [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
List.reverse <$> it.toListRev = it.toList := by
apply Eq.symm
@@ -232,35 +212,19 @@ theorem IterM.reverse_toListRev [Monad m] [LawfulMonad m] [Iterator α m β] [Fi
@[simp]
theorem IterM.reverse_toListRev_ensureTermination [Monad m] [LawfulMonad m] [Iterator α m β]
[Finite α m] [IteratorCollect α m m] [LawfulIteratorCollect α m m]
[Finite α m]
{it : IterM (α := α) m β} :
List.reverse <$> it.ensureTermination.toListRev = it.toList := by
rw [toListRev_ensureTermination_eq_toListRev, reverse_toListRev]
theorem IterM.toListRev_eq [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toListRev = List.reverse <$> it.toList := by
simp [ IterM.reverse_toListRev]
theorem IterM.toListRev_ensureTermination [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.ensureTermination.toListRev = List.reverse <$> it.toList := by
simp [ IterM.reverse_toListRev]
theorem LawfulIteratorCollect.toArray_eq {α β : Type w} {m : Type w Type w'}
[Monad m] [Iterator α m β] [Finite α m] [IteratorCollect α m m]
[hl : LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toArray = (letI : IteratorCollect α m m := .defaultImplementation; it.toArray) := by
simp [IterM.toArray, toArrayMapped_eq, IteratorCollect.defaultImplementation]
theorem LawfulIteratorCollect.toList_eq {α β : Type w} {m : Type w Type w'}
[Monad m] [Iterator α m β] [Finite α m] [IteratorCollect α m m]
[hl : LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toList = (letI : IteratorCollect α m m := .defaultImplementation; it.toList) := by
simp [IterM.toList, toArray_eq, -IterM.toList_toArray]
end Std.Iterators
end Std

View File

@@ -12,7 +12,8 @@ import all Init.Data.Iterators.Consumers.Monadic.Loop
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
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]
@@ -217,6 +218,34 @@ theorem IterM.forIn_eq_match_step {α β : Type w} {m : Type w → Type w'} [Ite
simp only [forIn]
exact forIn'_eq_match_step
theorem IterM.forIn_toList {α β : Type w} [Monad m] [LawfulMonad m] [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{it : IterM (α := α) Id β} {f : β γ m (ForInStep γ)} {init : γ} :
ForIn.forIn it.toList.run init f = ForIn.forIn it init f := by
rw [List.forIn_eq_foldlM]
induction it using IterM.inductSteps generalizing init with | step it ihy ihs
rw [forIn_eq_match_step, IterM.toList_eq_match_step]
simp only [map_eq_pure_bind, Id.run_bind, liftM, monadLift, pure_bind]
cases it.step.run.inflate using PlausibleIterStep.casesOn
· rename_i it' out h
simp only [List.foldlM_cons, bind_pure_comp, map_bind, Id.run_map]
apply bind_congr
intro forInStep
cases forInStep
· induction it'.toList.run <;> simp [*]
· simp only [ForIn.forIn] at ihy
simp [ihy h]
· rename_i it' h
simp only [bind_pure_comp]
rw [ihs h]
· simp
theorem IterM.forIn_toArray {α β : Type w} [Monad m] [LawfulMonad m] [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{it : IterM (α := α) Id β} {f : β γ m (ForInStep γ)} {init : γ} :
ForIn.forIn it.toArray.run init f = ForIn.forIn it init f := by
simp [ toArray_toList, forIn_toList]
theorem IterM.forM_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] {n : Type w Type w''} [Monad m] [Monad n] [LawfulMonad n]
[IteratorLoop α m n] [LawfulIteratorLoop α m n]
@@ -325,7 +354,6 @@ theorem IterM.fold_hom {m : Type w → Type w'} [Iterator α m β] [Finite α m]
theorem IterM.toList_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toList = it.fold (init := []) (fun l out => l ++ [out]) := by
suffices h : l' : List β, (l' ++ ·) <$> it.toList =
@@ -348,13 +376,43 @@ theorem IterM.toList_eq_fold {α β : Type w} {m : Type w → Type w'} [Iterator
theorem IterM.toArray_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toArray = it.fold (init := #[]) (fun xs out => xs.push out) := by
simp only [ toArray_toList, toList_eq_fold]
rw [ fold_hom]
simp
theorem IterM.foldlM_toList {α β : Type w} [Monad m] [LawfulMonad m] [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{it : IterM (α := α) Id β} {f : γ β m γ} {init : γ} :
it.toList.run.foldlM f init = it.foldM f init := by
simp [foldM_eq_forIn, forIn_toList]
theorem IterM.foldlM_toArray {α β : Type w} [Monad m] [LawfulMonad m] [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
{it : IterM (α := α) Id β} {f : γ β m γ} {init : γ} :
it.toArray.run.foldlM f init = it.foldM f init := by
simp [ toArray_toList, foldlM_toList]
theorem IterM.foldl_toList {α β : Type w} [Monad m] [LawfulMonad m] [Iterator α m β]
[Finite α m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : γ β γ} {init : γ} :
(·.foldl f init) <$> it.toList = it.fold f init := by
induction it using IterM.inductSteps generalizing init with | step it ihy ihs
rw [toList_eq_match_step, fold_eq_match_step]
simp only [bind_pure_comp, map_bind]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy _]
· simp [ihs _]
· simp
theorem IterM.foldl_toArray {α β : Type w} [Monad m] [LawfulMonad m] [Iterator α m β]
[Finite α m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : γ β γ} {init : γ} :
(·.foldl f init) <$> it.toArray = it.fold f init := by
simp only [ toArray_toList, Functor.map_map, List.foldl_toArray, foldl_toList]
theorem IterM.drain_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β] [Finite α m]
[Monad m] [IteratorLoop α m m] {it : IterM (α := α) m β} :
it.drain = it.fold (init := PUnit.unit) (fun _ _ => .unit) :=
@@ -383,7 +441,6 @@ theorem IterM.drain_eq_match_step {α β : Type w} {m : Type w → Type w'} [Ite
theorem IterM.drain_eq_map_toList {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.drain = (fun _ => .unit) <$> it.toList := by
induction it using IterM.inductSteps with | step it ihy ihs
@@ -400,14 +457,12 @@ theorem IterM.drain_eq_map_toList {α β : Type w} {m : Type w → Type w'} [Ite
theorem IterM.drain_eq_map_toListRev {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.drain = (fun _ => .unit) <$> it.toListRev := by
simp [IterM.drain_eq_map_toList, IterM.toListRev_eq]
theorem IterM.drain_eq_map_toArray {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.drain = (fun _ => .unit) <$> it.toList := by
simp [IterM.drain_eq_map_toList]
@@ -451,7 +506,6 @@ theorem IterM.count_eq_match_step {α β : Type w} {m : Type w → Type w'} [Ite
@[simp]
theorem IterM.up_size_toArray_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.size) <$> it.toArray = it.count := by
@@ -462,7 +516,6 @@ theorem IterM.up_size_toArray_eq_count {α β : Type w} [Iterator α m β] [Fini
@[simp]
theorem IterM.up_length_toList_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toList = it.count := by
@@ -473,7 +526,6 @@ theorem IterM.up_length_toList_eq_count {α β : Type w} [Iterator α m β] [Fin
@[simp]
theorem IterM.up_length_toListRev_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toListRev = it.count := by
@@ -780,4 +832,4 @@ theorem IterM.findM?_pure {α β : Type w} {m : Type w → Type w'} [Monad m]
· simp [ihs _]
· simp
end Std.Iterators
end Std

View File

@@ -10,7 +10,8 @@ public import Init.Data.Iterators.Basic
public section
namespace Std.Iterators
namespace Std
open Std.Iterators
/--
Induction principle for finite monadic iterators: One can define a function `f` that maps every
@@ -46,4 +47,4 @@ def IterM.inductSkips {α m β} [Iterator α m β] [Productive α m]
step it (fun {it'} _ => inductSkips motive step it')
termination_by it.finitelyManySkips
end Std.Iterators
end Std

View File

@@ -19,34 +19,32 @@ This module provides lemmas about the interactions of `List.iter` with `Iter.ste
collectors.
-/
namespace Std.Iterators
open Std Std.Iterators
variable {β : Type w}
@[simp]
theorem _root_.List.step_iter_nil :
theorem List.step_iter_nil :
(([] : List β).iter).step = .done, rfl := by
simp [Iter.step, IterM.step, Iterator.step, List.iter, List.iterM, toIterM]
simp [Iter.step, IterM.step, Iterator.step, List.iter, List.iterM, IterM.mk]
@[simp]
theorem _root_.List.step_iter_cons {x : β} {xs : List β} :
theorem List.step_iter_cons {x : β} {xs : List β} :
((x :: xs).iter).step = .yield xs.iter x, rfl := by
simp [List.iter, List.iterM, toIterM, IterM.toIter, Iter.step, Iter.toIterM, IterM.step,
simp [List.iter, List.iterM, IterM.mk, IterM.toIter, Iter.step, Iter.toIterM, IterM.step,
Iterator.step]
@[simp]
theorem _root_.List.toArray_iter {l : List β} :
@[simp, grind =]
theorem List.toArray_iter {l : List β} :
l.iter.toArray = l.toArray := by
simp [List.iter, List.toArray_iterM, Iter.toArray_eq_toArray_toIterM]
@[simp]
theorem _root_.List.toList_iter {l : List β} :
@[simp, grind =]
theorem List.toList_iter {l : List β} :
l.iter.toList = l := by
simp [List.iter, List.toList_iterM]
@[simp]
theorem _root_.List.toListRev_iter {l : List β} :
@[simp, grind =]
theorem List.toListRev_iter {l : List β} :
l.iter.toListRev = l.reverse := by
simp [List.iter, Iter.toListRev_eq_toListRev_toIterM, List.toListRev_iterM]
end Std.Iterators

View File

@@ -18,54 +18,43 @@ This module provides lemmas about the interactions of `List.iterM` with `IterM.s
collectors.
-/
namespace Std.Iterators
open Std.Internal
open Std Std.Internal Std.Iterators Std.Iterators.Types
variable {m : Type w Type w'} {n : Type w Type w''} [Monad m] {β : Type w}
@[simp]
theorem _root_.List.step_iterM_nil :
theorem List.step_iterM_nil :
(([] : List β).iterM m).step = pure (.deflate .done, rfl) := by
simp only [IterM.step, Iterator.step]; rfl
@[simp]
theorem _root_.List.step_iterM_cons {x : β} {xs : List β} :
theorem List.step_iterM_cons {x : β} {xs : List β} :
((x :: xs).iterM m).step = pure (.deflate .yield (xs.iterM m) x, rfl) := by
simp only [List.iterM, IterM.step, Iterator.step]; rfl
theorem _root_.List.step_iterM {l : List β} :
theorem List.step_iterM {l : List β} :
(l.iterM m).step = match l with
| [] => pure (.deflate .done, rfl)
| x :: xs => pure (.deflate .yield (xs.iterM m) x, rfl) := by
cases l <;> simp [List.step_iterM_cons, List.step_iterM_nil]
theorem ListIterator.toArrayMapped_iterM [Monad n] [LawfulMonad n]
{β : Type w} {γ : Type w} {lift : δ : Type w m δ n δ}
[LawfulMonadLiftFunction lift] {f : β n γ} {l : List β} :
IteratorCollect.toArrayMapped lift f (l.iterM m) (m := m) = List.toArray <$> l.mapM f := by
rw [LawfulIteratorCollect.toArrayMapped_eq]
@[simp, grind =]
theorem List.toArray_iterM [LawfulMonad m] {β : Type w} {l : List β} :
(l.iterM m).toArray = pure l.toArray := by
induction l with
| nil =>
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp [List.step_iterM_nil, LawfulMonadLiftFunction.lift_pure]
rw [IterM.toArray_eq_match_step]
simp [List.step_iterM_nil]
| cons x xs ih =>
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp [List.step_iterM_cons, List.mapM_cons, pure_bind, ih, LawfulMonadLiftFunction.lift_pure]
rw [IterM.toArray_eq_match_step]
simp [List.step_iterM_cons, pure_bind, ih]
@[simp]
theorem _root_.List.toArray_iterM [LawfulMonad m] {l : List β} :
(l.iterM m).toArray = pure l.toArray := by
simp only [IterM.toArray, ListIterator.toArrayMapped_iterM]
rw [List.mapM_pure, map_pure, List.map_id']
@[simp]
theorem _root_.List.toList_iterM [LawfulMonad m] {l : List β} :
@[simp, grind =]
theorem List.toList_iterM [LawfulMonad m] {l : List β} :
(l.iterM m).toList = pure l := by
rw [ IterM.toList_toArray, List.toArray_iterM, map_pure, List.toList_toArray]
@[simp]
theorem _root_.List.toListRev_iterM [LawfulMonad m] {l : List β} :
@[simp, grind =]
theorem List.toListRev_iterM [LawfulMonad m] {l : List β} :
(l.iterM m).toListRev = pure l.reverse := by
simp [IterM.toListRev_eq, List.toList_iterM]
end Std.Iterators

View File

@@ -53,6 +53,11 @@ def PostconditionT.lift {α : Type w} {m : Type w → Type w'} [Functor m] (x :
PostconditionT m α :=
fun _ => True, (·, .intro) <$> x
@[always_inline, inline, expose]
def PostconditionT.attachLift {α : Type w} {m : Type w Type w'} [MonadAttach m]
(x : m α) : PostconditionT m α :=
MonadAttach.CanReturn x, MonadAttach.attach x
@[always_inline, inline, expose]
protected def PostconditionT.pure {m : Type w Type w'} [Pure m] {α : Type w}
(a : α) : PostconditionT m α :=
@@ -67,7 +72,7 @@ def PostconditionT.liftWithProperty {α : Type w} {m : Type w → Type w'} {P :
P, x
/--
Given a function `f : α → β`, returns a a function `PostconditionT m α → PostconditionT m β`,
Given a function `f : α → β`, returns a function `PostconditionT m α → PostconditionT m β`,
turning `PostconditionT m` into a functor.
The postcondition of the `x.map f` states that the return value is the image under `f` of some
@@ -80,7 +85,7 @@ protected def PostconditionT.map {m : Type w → Type w'} [Functor m] {α : Type
(fun a => f a.val, _, rfl) <$> x.operation
/--
Given a function `α → PostconditionT m β`, returns a a function
Given a function `α → PostconditionT m β`, returns a function
`PostconditionT m α → PostconditionT m β`, turning `PostconditionT m` into a monad.
-/
@[always_inline, inline, expose]
@@ -116,6 +121,11 @@ def PostconditionT.run {m : Type w → Type w'} [Monad m] {α : Type w} (x : Pos
m α :=
(fun a => a.val) <$> x.operation
theorem PostconditionT.run_eq_map {m : Type w Type w'} [Monad m] {α : Type w}
{x : PostconditionT m α} :
x.run = Subtype.val <$> x.operation :=
(rfl)
instance {m : Type w Type w'} [Functor m] : Functor (PostconditionT m) where
map := PostconditionT.map
@@ -238,6 +248,28 @@ theorem PostconditionT.operation_bind' {m : Type w → Type w'} [Monad m] {α :
(fun fa => fa.1, by exacta.1, a.2, fa.2) <$> (f a.1).operation) := by
rfl
theorem PostconditionT.operation_eq_map_mk_operation {m : Type w Type w'}
[Monad m] [LawfulMonad m] {x : PostconditionT m α} :
x.operation = (fun a => a.val, a.property) <$> x.operation := by
simp
theorem PostconditionT.operation_bind_eq_operation_bind_mk {m : Type w Type w'}
[Monad m] {x : PostconditionT m α} {f : Subtype x.Property m β} :
x.operation >>= f = x.operation >>= (fun a => f a.val, a.property) := by
rfl
@[simp]
theorem PostconditionT.run_bind {m : Type w Type w'} [Monad m] [LawfulMonad m]
{α : Type w} {β : Type w} {x : PostconditionT m α} {f : α PostconditionT m β} :
(x.bind f).run = x.run >>= (f · |>.run) := by
simp [run_eq_map]
@[simp]
theorem PostconditionT.run_bind' {m : Type w Type w'} [Monad m] [LawfulMonad m]
{α : Type w} {β : Type w} {x : PostconditionT m α} {f : α PostconditionT m β} :
(x >>= f).run = x.run >>= (f · |>.run) :=
run_bind
@[simp]
theorem PostconditionT.property_lift {m : Type w Type w'} [Functor m] {α : Type w}
{x : m α} : (lift x : PostconditionT m α).Property = (fun _ => True) := by
@@ -249,6 +281,18 @@ theorem PostconditionT.operation_lift {m : Type w → Type w'} [Functor m] {α :
(·, property_lift (m := m) True.intro) <$> x := by
rfl
@[simp]
theorem PostconditionT.run_attachLift {m : Type w Type w'} [Monad m] [MonadAttach m]
[WeaklyLawfulMonadAttach m] {α : Type w}
{x : m α} : (attachLift x).run = x := by
simp [attachLift, run_eq_map, WeaklyLawfulMonadAttach.map_attach]
@[simp]
theorem PostconditionT.operation_attachLift {m : Type w Type w'} [Monad m] [MonadAttach m]
{α : Type w} {x : m α} : (attachLift x : PostconditionT m α).operation =
MonadAttach.attach x := by
rfl
instance {m : Type w Type w'} {n : Type w Type w''} [MonadLift m n] :
MonadLift (PostconditionT m) (PostconditionT n) where
monadLift x := _, monadLift x.operation

View File

@@ -10,14 +10,14 @@ public import Init.Data.Iterators.Producers.Monadic.List
@[expose] public section
open Std Std.Iterators Std.Iterators.Types
/-!
# List iterator
This module provides an iterator for lists that is accessible via `List.iter`.
-/
namespace Std.Iterators
/--
Returns a finite iterator for the given list.
The iterator yields the elements of the list in order and then terminates.
@@ -30,8 +30,6 @@ The monadic version of this iterator is `List.iterM`.
* `Productive` instance: always
-/
@[always_inline, inline]
def _root_.List.iter {α : Type w} (l : List α) :
def List.iter {α : Type w} (l : List α) :
Iter (α := ListIterator α) α :=
((l.iterM Id).toIter : Iter α)
end Std.Iterators

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.Iterators.Consumers
public import Init.Data.Iterators.Internal.Termination
@[expose] public section
@@ -17,9 +16,9 @@ public import Init.Data.Iterators.Internal.Termination
This module provides an iterator for lists that is accessible via `List.iterM`.
-/
namespace Std.Iterators
namespace Std
variable {α : Type w} {m : Type w Type w'}
namespace Iterators.Types
/--
The underlying state of a list iterator. Its contents are internal and should
@@ -29,6 +28,12 @@ not be used by downstream users of the library.
structure ListIterator (α : Type w) where
list : List α
end Iterators.Types
open Std.Iterators Std.Iterators.Types
variable {α : Type w} {m : Type w Type w'}
/--
Returns a finite iterator for the given list.
The iterator yields the elements of the list in order and then terminates.
@@ -43,38 +48,35 @@ The non-monadic version of this iterator is `List.iter`.
@[always_inline, inline]
def _root_.List.iterM {α : Type w} (l : List α) (m : Type w Type w') [Pure m] :
IterM (α := ListIterator α) m α :=
toIterM { list := l } m α
.mk { list := l } m α
namespace Iterators.Types
@[always_inline, inline]
instance {α : Type w} [Pure m] : Iterator (ListIterator α) m α where
instance ListIterator.instIterator {α : Type w} [Pure m] : Iterator (ListIterator α) m α where
IsPlausibleStep it
| .yield it' out => it.internalState.list = out :: it'.internalState.list
| .skip _ => False
| .done => it.internalState.list = []
step it := pure (match it with
| [] => .deflate .done, rfl
| x :: xs => .deflate .yield (toIterM xs m α) x, rfl)
| x :: xs => .deflate .yield (.mk xs m α) x, rfl)
private def ListIterator.finitenessRelation [Pure m] :
private def ListIterator.instFinitenessRelation [Pure m] :
FinitenessRelation (ListIterator α) m where
rel := InvImage WellFoundedRelation.rel (ListIterator.list IterM.internalState)
Rel := InvImage WellFoundedRelation.rel (ListIterator.list IterM.internalState)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
simp_wf
obtain step, h, h' := h
cases step <;> simp_all [IterStep.successor, IterM.IsPlausibleStep, Iterator.IsPlausibleStep]
instance [Pure m] : Finite (ListIterator α) m :=
by exact Finite.of_finitenessRelation ListIterator.finitenessRelation
instance ListIterator.instFinite [Pure m] : Finite (ListIterator α) m :=
by exact Finite.of_finitenessRelation ListIterator.instFinitenessRelation
@[always_inline, inline]
instance {α : Type w} [Monad m] {n : Type w Type w''} [Monad n] :
IteratorCollect (ListIterator α) m n :=
.defaultImplementation
@[always_inline, inline]
instance {α : Type w} [Monad m] {n : Type x Type x'} [Monad n] :
instance ListIterator.instIteratorLoop {α : Type w} [Monad m] {n : Type x Type x'} [Monad n] :
IteratorLoop (ListIterator α) m n :=
.defaultImplementation
end Std.Iterators
end Std.Iterators.Types

View File

@@ -15,9 +15,7 @@ This module provides the typeclass `ToIterator`, which is implemented by types t
converted into iterators.
-/
open Std.Iterators
namespace Std.Iterators
namespace Std
/-- This typeclass provides an iterator for elements of type `γ`. -/
class ToIterator (γ : Type u) (m : Type w Type w') (α β : outParam (Type w)) where
@@ -60,4 +58,4 @@ theorem ToIterator.iter_eq {γ : Type u} {x : γ} {α β : Type v} {it} :
ToIterator.iter x = (it x).toIter :=
rfl
end Std.Iterators
end Std

View File

@@ -11,7 +11,7 @@ public import Init.Core
public section
/--
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that `a == b` implies
`hash a = hash b`.
This is automatic if the `BEq` instance is lawful.

View File

@@ -169,10 +169,10 @@ Examples:
| a::as, b::bs, eqv => eqv a b && isEqv as bs eqv
| _, _, _ => false
@[simp] theorem isEqv_nil_nil : isEqv ([] : List α) [] eqv = true := rfl
@[simp] theorem isEqv_nil_cons : isEqv ([] : List α) (a::as) eqv = false := rfl
@[simp] theorem isEqv_cons_nil : isEqv (a::as : List α) [] eqv = false := rfl
theorem isEqv_cons₂ : isEqv (a::as) (b::bs) eqv = (eqv a b && isEqv as bs eqv) := rfl
@[simp, grind =] theorem isEqv_nil_nil : isEqv ([] : List α) [] eqv = true := rfl
@[simp, grind =] theorem isEqv_nil_cons : isEqv ([] : List α) (a::as) eqv = false := rfl
@[simp, grind =] theorem isEqv_cons_nil : isEqv (a::as : List α) [] eqv = false := rfl
@[grind =] theorem isEqv_cons₂ : isEqv (a::as) (b::bs) eqv = (eqv a b && isEqv as bs eqv) := rfl
/-! ## Lexicographic ordering -/
@@ -717,6 +717,7 @@ Examples:
* `["red", "green", "blue"].leftpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].leftpad 1 "blank" = ["red", "green", "blue"]`
-/
@[simp, grind =]
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
@@ -730,6 +731,7 @@ Examples:
* `["red", "green", "blue"].rightpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].rightpad 1 "blank" = ["red", "green", "blue"]`
-/
@[simp, grind =]
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
/-! ### reduceOption -/

View File

@@ -50,7 +50,7 @@ Users that want to use `mapM` with `Applicative` should use `mapA` instead.
Applies the monadic action `f` to every element in the list, left-to-right, and returns the list of
results.
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
This implementation is tail recursive. `List.mapM'` is a non-tail-recursive variant that may be
more convenient to reason about. `List.forM` is the variant that discards the results and
`List.mapA` is the variant that works with `Applicative`.
-/
@@ -107,7 +107,7 @@ Applies the monadic action `f` to the corresponding elements of two lists, left-
at the end of the shorter list. `zipWithM f as bs` is equivalent to `mapM id (zipWith f as bs)`
for lawful `Monad` instances.
This implementation is tail recursive. `List.zipWithM'` is a a non-tail-recursive variant that may
This implementation is tail recursive. `List.zipWithM'` is a non-tail-recursive variant that may
be more convenient to reason about.
-/
@[inline, expose]

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