mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-26 23:04:07 +00:00
Compare commits
377 Commits
joachim/st
...
paul/test/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fb399cd56 | ||
|
|
bb338a93f0 | ||
|
|
e46b882a90 | ||
|
|
d4d2114166 | ||
|
|
9e18eea271 | ||
|
|
fa4cd6d78c | ||
|
|
e1b19198a9 | ||
|
|
1590c9c9d9 | ||
|
|
cd8280700d | ||
|
|
2e779f79de | ||
|
|
50bbf101be | ||
|
|
90ba5f3f40 | ||
|
|
cedc641fd5 | ||
|
|
afee8aa1c1 | ||
|
|
31e4eb62b7 | ||
|
|
4bcb3cea42 | ||
|
|
36bae38e6b | ||
|
|
ba0e755adc | ||
|
|
596827c0e9 | ||
|
|
3666544aad | ||
|
|
4ce04776b6 | ||
|
|
621fdea272 | ||
|
|
fb3aae7509 | ||
|
|
f11fffb27b | ||
|
|
7e1f9e24c4 | ||
|
|
42a0e92453 | ||
|
|
d4c74b3566 | ||
|
|
2e8afdf74d | ||
|
|
c7f941076e | ||
|
|
9185fd2a34 | ||
|
|
642863e8c5 | ||
|
|
62d2688579 | ||
|
|
e8870da205 | ||
|
|
a011c9c5dd | ||
|
|
a6a3df8af0 | ||
|
|
b44c7e161c | ||
|
|
0bcac0d46c | ||
|
|
1bf16f710e | ||
|
|
c3d753640a | ||
|
|
e94ed002b5 | ||
|
|
7564329f06 | ||
|
|
0336a8385b | ||
|
|
c6e530a4f1 | ||
|
|
97d427b32b | ||
|
|
bc1a22cc22 | ||
|
|
0e28043ec6 | ||
|
|
45862d5486 | ||
|
|
ba8c2ed4ee | ||
|
|
9e241a4087 | ||
|
|
e90f6f77db | ||
|
|
9deb9ab59d | ||
|
|
6de7100f69 | ||
|
|
6f409e0eea | ||
|
|
3de1cc54c5 | ||
|
|
a3755fe0a5 | ||
|
|
4c1e4a77b4 | ||
|
|
896da85304 | ||
|
|
11cd55b4f1 | ||
|
|
88823b27a6 | ||
|
|
c9facc8102 | ||
|
|
63d1b530ba | ||
|
|
3f09741fb9 | ||
|
|
9f9531fa13 | ||
|
|
dae0d6fa05 | ||
|
|
4a3401f69a | ||
|
|
4526cdda5f | ||
|
|
c4639150c1 | ||
|
|
37870c168b | ||
|
|
57003e5c79 | ||
|
|
b2f485e352 | ||
|
|
5e29d7660a | ||
|
|
567cf74f1b | ||
|
|
fa2ddf1c56 | ||
|
|
f9af240bc4 | ||
|
|
3bfeb0bc1f | ||
|
|
8447586fea | ||
|
|
470e3b7fd0 | ||
|
|
0a0323734b | ||
|
|
69b058dc82 | ||
|
|
2c48ae7dfb | ||
|
|
c81a8897a9 | ||
|
|
3bc63aefb7 | ||
|
|
fa40491c78 | ||
|
|
af438425d5 | ||
|
|
648e1b1877 | ||
|
|
f84aa23d6d | ||
|
|
6bec8adf16 | ||
|
|
16873fb123 | ||
|
|
34d8eeb3be | ||
|
|
f1cc85eb19 | ||
|
|
08e6f714ca | ||
|
|
b8f8dde0b3 | ||
|
|
b09e33f76b | ||
|
|
a95227c7d7 | ||
|
|
8258cfe2a1 | ||
|
|
94e8fd4845 | ||
|
|
9063adbd51 | ||
|
|
3e16f5332f | ||
|
|
974fdd85c4 | ||
|
|
e8a16dfcc8 | ||
|
|
ad43266357 | ||
|
|
9efb2bf35c | ||
|
|
9fbbe6554d | ||
|
|
db30cf3954 | ||
|
|
e9a1c9ef63 | ||
|
|
df8ff255cb | ||
|
|
fdd30d9250 | ||
|
|
36eaa68744 | ||
|
|
99b26ce49e | ||
|
|
aac353c6b9 | ||
|
|
9167b13afa | ||
|
|
ea9c7cf2ae | ||
|
|
c3726bdf05 | ||
|
|
30e23eae2b | ||
|
|
d8fb702d73 | ||
|
|
f63ddd67a2 | ||
|
|
5457a227ba | ||
|
|
de6ff061ed | ||
|
|
6a87c0e530 | ||
|
|
86da5ae26e | ||
|
|
1b8dd80ed1 | ||
|
|
07b2913969 | ||
|
|
8f9fb4c5b2 | ||
|
|
12adfbf0e3 | ||
|
|
f47dfe9e7f | ||
|
|
4af9cc0592 | ||
|
|
196cdb6039 | ||
|
|
3833984756 | ||
|
|
5433fe129d | ||
|
|
fb3238d47c | ||
|
|
960c01fcae | ||
|
|
21cf5881f5 | ||
|
|
2d87d50e34 | ||
|
|
4b63048825 | ||
|
|
2f7f63243f | ||
|
|
dc70d0cc43 | ||
|
|
b994cb4497 | ||
|
|
d0493e4c1e | ||
|
|
c7d3401417 | ||
|
|
8435dea274 | ||
|
|
3dfd125337 | ||
|
|
c24df9e8d6 | ||
|
|
c2918b2701 | ||
|
|
bd514319d6 | ||
|
|
4133dc06f4 | ||
|
|
38c6d9110d | ||
|
|
abed967ded | ||
|
|
48a1b07516 | ||
|
|
1cd6db1579 | ||
|
|
d68de2e018 | ||
|
|
e2353689f2 | ||
|
|
b81608d0d9 | ||
|
|
aa4539750a | ||
|
|
94c45c3f00 | ||
|
|
e56351da7a | ||
|
|
58e599f2f9 | ||
|
|
c91a2c63c2 | ||
|
|
d7cbdebf0b | ||
|
|
28a5e9f93c | ||
|
|
470498cc06 | ||
|
|
d57f71c1c0 | ||
|
|
eaf8cf15ff | ||
|
|
cae739c27c | ||
|
|
9280a0ba9e | ||
|
|
e42262e397 | ||
|
|
a96ae4bb12 | ||
|
|
14039942f3 | ||
|
|
5bb7f37645 | ||
|
|
15a719cb36 | ||
|
|
0f1eb1d0e5 | ||
|
|
e766839345 | ||
|
|
22bef1c45a | ||
|
|
b771d12072 | ||
|
|
214abb7eb2 | ||
|
|
76a734c907 | ||
|
|
f65fe51630 | ||
|
|
e6d021967e | ||
|
|
4c360d50fa | ||
|
|
821218aabd | ||
|
|
7b1fb7ac9e | ||
|
|
cd632b033d | ||
|
|
d92cdae8e9 | ||
|
|
7d5a96941e | ||
|
|
b4cf6b02b9 | ||
|
|
ea7c740ad4 | ||
|
|
aa8fa47321 | ||
|
|
7e6365567f | ||
|
|
1361d733a6 | ||
|
|
0ad15fe982 | ||
|
|
531dbf0e1b | ||
|
|
2e649e16f0 | ||
|
|
0e4794a1a9 | ||
|
|
975a81cdb8 | ||
|
|
f7de0c408f | ||
|
|
60cdda3c1e | ||
|
|
8484dbad5d | ||
|
|
b0ebfaa812 | ||
|
|
c3cc61cdb4 | ||
|
|
ff87bcb8e5 | ||
|
|
a6ed0d640d | ||
|
|
8154453bb5 | ||
|
|
11e4e44be0 | ||
|
|
c871f66cfa | ||
|
|
0f866236c7 | ||
|
|
f6c8b8d974 | ||
|
|
175661b6c3 | ||
|
|
fd88637948 | ||
|
|
7376772cbd | ||
|
|
c358b0c734 | ||
|
|
8207919728 | ||
|
|
06b7b022b3 | ||
|
|
460b3c3e43 | ||
|
|
b46688d683 | ||
|
|
82f60a7ff3 | ||
|
|
6bf2486e13 | ||
|
|
f1c903ca65 | ||
|
|
35d8925c50 | ||
|
|
9f404d8fbe | ||
|
|
81c93aeae8 | ||
|
|
cf36ac986d | ||
|
|
609d99e860 | ||
|
|
78c9a01bb2 | ||
|
|
a2cf78ac4a | ||
|
|
bc72487aed | ||
|
|
b40dabdecd | ||
|
|
19df2c41b3 | ||
|
|
ce8fdb1aa7 | ||
|
|
fab1897f28 | ||
|
|
3804a1df8d | ||
|
|
514a5fddc6 | ||
|
|
d8f0507d2a | ||
|
|
4eb5b5776d | ||
|
|
6642061623 | ||
|
|
4e8b5cfc46 | ||
|
|
c07ee77d33 | ||
|
|
b82f969e5b | ||
|
|
97c23abf8e | ||
|
|
ef9777ec0d | ||
|
|
b7360969ed | ||
|
|
9b1b932242 | ||
|
|
d4563a818f | ||
|
|
e8781f12c0 | ||
|
|
1ca4faae18 | ||
|
|
3a5887276c | ||
|
|
e086b9b5c6 | ||
|
|
16ae74e98e | ||
|
|
2a28cd98fc | ||
|
|
bba35e4532 | ||
|
|
17581a2628 | ||
|
|
05664b15a3 | ||
|
|
1590a72913 | ||
|
|
f77ce8c669 | ||
|
|
4e1a2487b7 | ||
|
|
b60556af4e | ||
|
|
2bca310bea | ||
|
|
5042c8cc37 | ||
|
|
1e99ff1dba | ||
|
|
48bb954e4e | ||
|
|
96160e553a | ||
|
|
18702bdd47 | ||
|
|
4eaaadf1c1 | ||
|
|
2234c91163 | ||
|
|
4f7ba5eb09 | ||
|
|
da70626e64 | ||
|
|
214acc921c | ||
|
|
f483c6c10f | ||
|
|
c0d5e8bc2c | ||
|
|
c02f570b76 | ||
|
|
19d16ff9b7 | ||
|
|
58420f9416 | ||
|
|
b3b33e85d3 | ||
|
|
723acce2a7 | ||
|
|
e765138bb4 | ||
|
|
501375f340 | ||
|
|
ce56e2139e | ||
|
|
c34e4cf0f7 | ||
|
|
f2c9fcc0b2 | ||
|
|
950a2b7896 | ||
|
|
88f17dee71 | ||
|
|
4d2647f9c7 | ||
|
|
a471f005d6 | ||
|
|
f6a25b13b9 | ||
|
|
a847b13b1a | ||
|
|
186a81627b | ||
|
|
0df74178d8 | ||
|
|
72f9b725aa | ||
|
|
dc53fac626 | ||
|
|
13c88f960f | ||
|
|
0d2a574f96 | ||
|
|
a7562bc578 | ||
|
|
c86b10d141 | ||
|
|
54a88e941f | ||
|
|
b87d2c0fb9 | ||
|
|
eb990538ae | ||
|
|
4c0765fc07 | ||
|
|
5e24120dba | ||
|
|
f317e28d84 | ||
|
|
bb8e6801f0 | ||
|
|
5440bf724d | ||
|
|
c88ec35c0d | ||
|
|
73ff198d11 | ||
|
|
cee149cc1f | ||
|
|
2236122411 | ||
|
|
c74d24aaaa | ||
|
|
34d619bf93 | ||
|
|
eb11ccb234 | ||
|
|
2db0a98b7c | ||
|
|
6cabf59099 | ||
|
|
89bbe804a5 | ||
|
|
4e656ea8e9 | ||
|
|
aa9f7ab14b | ||
|
|
5ef0207a85 | ||
|
|
a1b8ffe31b | ||
|
|
f21f8d96f9 | ||
|
|
1918d4f0dc | ||
|
|
08c87b2ad3 | ||
|
|
489f8acd77 | ||
|
|
3e61514ce4 | ||
|
|
f63c2363ee | ||
|
|
fe96911368 | ||
|
|
08f0d12ffb | ||
|
|
06d2390fb3 | ||
|
|
3ac9bbb3d8 | ||
|
|
118160bf07 | ||
|
|
c1bc886d98 | ||
|
|
0708024c46 | ||
|
|
2d9571563a | ||
|
|
e2617903f8 | ||
|
|
7ba21c4d1b | ||
|
|
b7f1cf9ba7 | ||
|
|
12c282b1e9 | ||
|
|
5f4d724c2d | ||
|
|
98616529fd | ||
|
|
8f80d2c2e0 | ||
|
|
fd0a65f312 | ||
|
|
bd5d750780 | ||
|
|
49d4752bfd | ||
|
|
95a7c769d8 | ||
|
|
7b8e51e025 | ||
|
|
949cf69246 | ||
|
|
e02f229305 | ||
|
|
9b49b6b68d | ||
|
|
eb20c07b4a | ||
|
|
3fdde57e7b | ||
|
|
c79d74d9a1 | ||
|
|
082c65f226 | ||
|
|
6a0b0c8273 | ||
|
|
62b900e8ef | ||
|
|
429e09cd82 | ||
|
|
c4d67c22e6 | ||
|
|
923d7e1ed6 | ||
|
|
5db865ea2f | ||
|
|
0f2ac0b099 | ||
|
|
b7ff463358 | ||
|
|
799c6b5ff8 | ||
|
|
2d0c62c767 | ||
|
|
fb6c96e54b | ||
|
|
c20378682e | ||
|
|
b7e6862163 | ||
|
|
983d64395a | ||
|
|
6db52f0aa9 | ||
|
|
e489c342d7 | ||
|
|
0bfbb71796 | ||
|
|
38c401cf3b | ||
|
|
a2ceebe200 | ||
|
|
292b74b0a4 | ||
|
|
d76752ffb8 | ||
|
|
d4463ce549 | ||
|
|
074dc60bea | ||
|
|
6d8a16f137 | ||
|
|
f0e594d5db | ||
|
|
32d22075dc | ||
|
|
902226642f | ||
|
|
67ba4da71f | ||
|
|
0eed450b86 | ||
|
|
834886bca2 | ||
|
|
5339c47555 |
@@ -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.
|
||||
|
||||
@@ -13,12 +13,54 @@ These comments explain the scripts' behavior, which repositories get special han
|
||||
## Arguments
|
||||
- `version`: The version to release (e.g., v4.24.0)
|
||||
|
||||
## Release Notes (Required for -rc1 releases)
|
||||
|
||||
For first release candidates (`-rc1`), you must create release notes BEFORE the reference-manual toolchain bump PR can be merged.
|
||||
|
||||
**Steps to create release notes:**
|
||||
|
||||
1. Generate the release notes:
|
||||
```bash
|
||||
cd /path/to/lean4
|
||||
python3 script/release_notes.py --since <previous_version> > /tmp/release-notes-<version>.md
|
||||
```
|
||||
Replace `<previous_version>` with the last stable release (e.g., `v4.27.0` when releasing `v4.28.0-rc1`).
|
||||
|
||||
2. Review `/tmp/release-notes-<version>.md` for common issues:
|
||||
- **Unterminated code blocks**: Look for code fences that aren't closed. Fetch original PR with `gh pr view <number>` to repair.
|
||||
- **Truncated descriptions**: Some may end mid-sentence. Complete them from the original PR.
|
||||
- **Markdown issues**: Other syntax problems that could cause parsing errors.
|
||||
|
||||
3. Create the release notes file in the reference-manual repository:
|
||||
- File path: `Manual/Releases/v<version>.lean` (e.g., `v4_28_0.lean`)
|
||||
- Use Verso format with proper imports and `#doc (Manual)` block
|
||||
- **Use `#` for headers, not `##`** (Verso uses level 1 for subsections)
|
||||
- **Use plain ` ``` ` not ` ```lean `** (the latter executes code)
|
||||
- **Wrap underscore identifiers in backticks**: `` `bv_decide` `` not `bv_decide`
|
||||
|
||||
4. Update `Manual/Releases.lean`:
|
||||
- Add import: `import Manual.Releases.«v4_28_0»`
|
||||
- Add include: `{include 0 Manual.Releases.«v4_28_0»}`
|
||||
|
||||
5. Build to verify: `lake build Manual.Releases.v4_28_0`
|
||||
|
||||
6. Create a **separate PR** for release notes (not bundled with toolchain bump):
|
||||
```bash
|
||||
git checkout -b v<version>-release-notes
|
||||
gh pr create --title "doc: add v<version> release notes"
|
||||
```
|
||||
|
||||
For subsequent RCs (`-rc2`, etc.) and stable releases, just update the version number in the existing release notes file title.
|
||||
|
||||
See `doc/dev/release_checklist.md` section "Writing the release notes" for full details.
|
||||
|
||||
## Process
|
||||
|
||||
1. Run `script/release_checklist.py {version}` to check the current status
|
||||
2. **CRITICAL: If preliminary lean4 checks fail, STOP immediately and alert the user**
|
||||
- Check for: release branch exists, CMake version correct, tag exists, release page exists, release notes exist
|
||||
- Check for: release branch exists, CMake version correct, tag exists, release page exists, release notes file exists
|
||||
- **IMPORTANT**: The release page is created AUTOMATICALLY by CI after pushing the tag - DO NOT create it manually
|
||||
- **IMPORTANT**: For -rc1 releases, release notes must be created before proceeding
|
||||
- Do NOT create any PRs or proceed with repository updates if these checks fail
|
||||
3. Create a todo list tracking all repositories that need updates
|
||||
4. **CRITICAL RULE: You can ONLY run `release_steps.py` for a repository if `release_checklist.py` explicitly says to do so**
|
||||
@@ -39,6 +81,7 @@ These comments explain the scripts' behavior, which repositories get special han
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **NEVER merge PRs autonomously** - always wait for the user to merge PRs themselves
|
||||
- The `release_steps.py` script is idempotent - it's safe to rerun
|
||||
- The `release_checklist.py` script is idempotent - it's safe to rerun
|
||||
- Some repositories depend on others (e.g., mathlib4 depends on batteries, aesop, etc.)
|
||||
@@ -60,6 +103,15 @@ Every time you run `release_checklist.py`, you MUST:
|
||||
This summary should be provided EVERY time you run the checklist, not just after creating new PRs.
|
||||
The user needs to see the complete picture of what's waiting for review.
|
||||
|
||||
## Nightly Infrastructure
|
||||
|
||||
The nightly build system uses branches and tags across two repositories:
|
||||
|
||||
- `leanprover/lean4` has **branches** `nightly` and `nightly-with-mathlib` tracking the latest nightly builds
|
||||
- `leanprover/lean4-nightly` has **dated tags** like `nightly-2026-01-23`
|
||||
|
||||
When a nightly succeeds with mathlib, all three should point to the same commit. Don't confuse these: branches are in the main lean4 repo, dated tags are in lean4-nightly.
|
||||
|
||||
## Error Handling
|
||||
|
||||
**CRITICAL**: If something goes wrong or a command fails:
|
||||
|
||||
2
.github/workflows/actionlint.yml
vendored
2
.github/workflows/actionlint.yml
vendored
@@ -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:
|
||||
|
||||
4
.github/workflows/build-template.yml
vendored
4
.github/workflows/build-template.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/check-prelude.yml
vendored
2
.github/workflows/check-prelude.yml
vendored
@@ -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 }}
|
||||
|
||||
2
.github/workflows/check-stage0.yml
vendored
2
.github/workflows/check-stage0.yml
vendored
@@ -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
|
||||
|
||||
57
.github/workflows/check-stdlib-flags.yml
vendored
Normal file
57
.github/workflows/check-stdlib-flags.yml
vendored
Normal 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');
|
||||
}
|
||||
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
@@ -50,9 +50,9 @@ 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'
|
||||
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
|
||||
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
|
||||
id: set-nightly
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
CMAKE_MAJOR=$(grep -E "^set\(LEAN_VERSION_MAJOR " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_MINOR=$(grep -E "^set\(LEAN_VERSION_MINOR " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_PATCH=$(grep -E "^set\(LEAN_VERSION_PATCH " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_IS_RELEASE=$(grep -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | sed -nE 's/^set\(LEAN_VERSION_IS_RELEASE ([0-9]+)\).*/\1/p')
|
||||
|
||||
# Expected values from tag parsing
|
||||
TAG_MAJOR="${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}"
|
||||
@@ -267,12 +267,17 @@ 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
|
||||
// `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'"
|
||||
// * `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.
|
||||
// * `bv_` sometimes times out calling into cadical even though we should be using
|
||||
// the standard compile flags for it.
|
||||
// * `grind_guide` always times out.
|
||||
// * `pkg/|lake/` tests sometimes time out (likely even hang), related to Lake CI
|
||||
// failures?
|
||||
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel|9366|run/bv_|grind_guide|pkg/|lake/'"
|
||||
},
|
||||
{
|
||||
"name": "macOS",
|
||||
@@ -432,7 +437,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 +458,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 +483,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
|
||||
|
||||
2
.github/workflows/copyright-header.yml
vendored
2
.github/workflows/copyright-header.yml
vendored
@@ -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: |
|
||||
|
||||
88
.github/workflows/pr-release.yml
vendored
88
.github/workflows/pr-release.yml
vendored
@@ -20,7 +20,9 @@ on:
|
||||
jobs:
|
||||
on-success:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
|
||||
# Run even if CI fails, as long as build artifacts are available
|
||||
# The "Verify release artifacts exist" step will fail if necessary artifacts are missing
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
|
||||
steps:
|
||||
- name: Retrieve information about the original workflow
|
||||
uses: potiuk/get-workflow-origin@v1_1 # https://github.com/marketplace/actions/get-workflow-origin
|
||||
@@ -62,42 +64,56 @@ jobs:
|
||||
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
|
||||
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}"
|
||||
- name: Delete existing release if present
|
||||
- name: Delete existing releases if present
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
# Try to delete any existing release for the current PR (just the version without the SHA suffix).
|
||||
# Delete any existing releases for this PR.
|
||||
# The short format release is always recreated with the latest commit.
|
||||
# The SHA-suffixed release should be unique per commit, but delete just in case.
|
||||
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
|
||||
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }} -y || true
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
# Verify artifacts were downloaded (equivalent to fail_on_unmatched_files in the old action).
|
||||
- name: Verify release artifacts exist
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
files=(artifacts/*/*)
|
||||
if [ ${#files[@]} -eq 0 ]; then
|
||||
echo "::error::No artifacts found matching artifacts/*/*"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found ${#files[@]} artifacts to upload:"
|
||||
printf '%s\n' "${files[@]}"
|
||||
# We use `gh release create` instead of `softprops/action-gh-release` because
|
||||
# the latter enumerates all releases to check for existing ones, which fails
|
||||
# when the repository has more than 10000 releases (GitHub API pagination limit).
|
||||
# Upstream fix: https://github.com/softprops/action-gh-release/pull/725
|
||||
- name: Release (short format)
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
|
||||
with:
|
||||
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# There are coredumps files here as well, but all in deeper subdirectories.
|
||||
files: artifacts/*/*
|
||||
fail_on_unmatched_files: true
|
||||
draft: false
|
||||
tag_name: pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
repository: ${{ github.repository_owner }}/lean4-pr-releases
|
||||
run: |
|
||||
# There are coredump files in deeper subdirectories; artifacts/*/* gets the release archives.
|
||||
gh release create \
|
||||
--repo ${{ github.repository_owner }}/lean4-pr-releases \
|
||||
--title "Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}" \
|
||||
--notes "" \
|
||||
pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} \
|
||||
artifacts/*/*
|
||||
env:
|
||||
# The token used here must have `workflow` privileges.
|
||||
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
|
||||
- name: Release (SHA-suffixed format)
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
|
||||
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.
|
||||
files: artifacts/*/*
|
||||
fail_on_unmatched_files: true
|
||||
draft: false
|
||||
tag_name: pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}
|
||||
repository: ${{ github.repository_owner }}/lean4-pr-releases
|
||||
run: |
|
||||
gh release create \
|
||||
--repo ${{ github.repository_owner }}/lean4-pr-releases \
|
||||
--title "Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})" \
|
||||
--notes "" \
|
||||
pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }} \
|
||||
artifacts/*/*
|
||||
env:
|
||||
# The token used here must have `workflow` privileges.
|
||||
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
|
||||
- name: Report release status (short format)
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
@@ -166,22 +182,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 +403,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 +463,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 +546,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 }}
|
||||
|
||||
2
.github/workflows/update-stage0.yml
vendored
2
.github/workflows/update-stage0.yml
vendored
@@ -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"
|
||||
|
||||
190
doc/dev/ffi.md
190
doc/dev/ffi.md
@@ -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.
|
||||
|
||||
@@ -69,6 +69,10 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- `repl`:
|
||||
There are two copies of `lean-toolchain`/`lakefile.lean`:
|
||||
in the root, and in `test/Mathlib/`. Edit both, and run `lake update` in both directories.
|
||||
- `lean-fro.org`:
|
||||
After updating the toolchains and running `lake update`, you must run `scripts/update.sh` to regenerate
|
||||
the site content. This script updates generated files that depend on the Lean version.
|
||||
The `release_steps.py` script handles this automatically.
|
||||
- An awkward situation that sometimes occurs (e.g. with Verso) is that the `master`/`main` branch has already been moved
|
||||
to a nightly toolchain that comes *after* the stable toolchain we are
|
||||
targeting. In this case it is necessary to create a branch `releases/v4.6.0` from the last commit which was on
|
||||
@@ -214,6 +218,21 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
|
||||
|
||||
# Writing the release notes
|
||||
|
||||
Release notes content is only written for the first release candidate (`-rc1`). For subsequent RCs and stable releases,
|
||||
just update the title in the existing release notes file (see "Release notes title format" below).
|
||||
|
||||
## Release notes title format
|
||||
|
||||
The title in the `#doc (Manual)` line must follow these formats:
|
||||
|
||||
- **For -rc1**: `"Lean 4.7.0-rc1 (2024-03-15)"` — Include the RC suffix and the release date
|
||||
- **For -rc2, -rc3, etc.**: `"Lean 4.7.0-rc2 (2024-03-20)"` — Update the RC number and date
|
||||
- **For stable release**: `"Lean 4.7.0 (2024-04-01)"` — Remove the RC suffix but keep the date
|
||||
|
||||
The date should be the actual date when the tag was pushed (or when CI completed and created the release page).
|
||||
|
||||
## Generating the release notes
|
||||
|
||||
Release notes are automatically generated from the commit history, using `script/release_notes.py`.
|
||||
|
||||
Run this as `script/release_notes.py --since v4.6.0`, where `v4.6.0` is the *previous* release version.
|
||||
@@ -228,4 +247,113 @@ Some judgement is required here: ignore commits which look minor,
|
||||
but manually add items to the release notes for significant PRs that were rebase-merged.
|
||||
|
||||
There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
|
||||
|
||||
## Reviewing and fixing the generated markdown
|
||||
|
||||
Before adding the release notes to the reference manual, carefully review the generated markdown for these common issues:
|
||||
|
||||
1. **Unterminated code blocks**: PR descriptions sometimes have unclosed code fences. Look for code blocks
|
||||
that don't have a closing ` ``` `. If found, fetch the original PR description with `gh pr view <number>`
|
||||
and repair the code block with the complete content.
|
||||
|
||||
2. **Truncated descriptions**: Some PR descriptions may end abruptly mid-sentence. Review these and complete
|
||||
the descriptions based on the original PR.
|
||||
|
||||
3. **Markdown syntax issues**: Check for other markdown problems that could cause parsing errors.
|
||||
|
||||
## Creating the release notes file
|
||||
|
||||
The release notes go in `Manual/Releases/v4_7_0.lean` in the reference-manual repository.
|
||||
|
||||
The file structure must follow the Verso format:
|
||||
|
||||
```lean
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: <Your Name>
|
||||
-/
|
||||
|
||||
import VersoManual
|
||||
import Manual.Meta
|
||||
import Manual.Meta.Markdown
|
||||
|
||||
open Manual
|
||||
open Verso.Genre
|
||||
open Verso.Genre.Manual
|
||||
open Verso.Genre.Manual.InlineLean
|
||||
|
||||
#doc (Manual) "Lean 4.7.0-rc1 (2024-03-15)" =>
|
||||
%%%
|
||||
tag := "release-v4.7.0"
|
||||
file := "v4.7.0"
|
||||
%%%
|
||||
|
||||
<release notes content here>
|
||||
```
|
||||
|
||||
**Important formatting rules for Verso:**
|
||||
- Use `#` for section headers inside the document, not `##` (Verso uses header level 1 for subsections)
|
||||
- Use plain ` ``` ` for code blocks, not ` ```lean ` (the latter will cause Lean to execute the code)
|
||||
- Identifiers with underscores like `bv_decide` should be wrapped in backticks: `` `bv_decide` ``
|
||||
(otherwise the underscore may be interpreted as markdown emphasis)
|
||||
|
||||
## Updating Manual/Releases.lean
|
||||
|
||||
After creating the release notes file, update `Manual/Releases.lean` to include it:
|
||||
|
||||
1. Add the import near the top with other version imports:
|
||||
```lean
|
||||
import Manual.Releases.«v4_7_0»
|
||||
```
|
||||
|
||||
2. Add the include statement after the other includes:
|
||||
```lean
|
||||
{include 0 Manual.Releases.«v4_7_0»}
|
||||
```
|
||||
|
||||
## Building and verifying
|
||||
|
||||
Build the release notes to check for errors:
|
||||
```bash
|
||||
lake build Manual.Releases.v4_7_0
|
||||
```
|
||||
|
||||
Common errors and fixes:
|
||||
- "Wrong header nesting - got ## but expected at most #": Change `##` to `#`
|
||||
- "Tactic 'X' failed" or similar: Code is being executed; change ` ```lean ` to ` ``` `
|
||||
- "'_'" errors: Underscore in identifier being parsed as emphasis; wrap in backticks
|
||||
|
||||
## Creating the PR
|
||||
|
||||
**Important: Timing with the reference-manual tag**
|
||||
|
||||
The reference-manual repository deploys documentation when a version tag is pushed. If you merge
|
||||
release notes AFTER the tag is created, the deployed documentation won't include them.
|
||||
|
||||
You have two options:
|
||||
|
||||
1. **Preferred**: Include the release notes in the same PR as the toolchain bump (or merge the
|
||||
release notes PR before creating the tag). This ensures the tag includes the release notes.
|
||||
|
||||
2. **If release notes are merged after the tag**: You must regenerate the tag to trigger a new deployment:
|
||||
```bash
|
||||
cd /path/to/reference-manual
|
||||
git fetch origin
|
||||
git tag -d v4.7.0-rc1 # Delete local tag
|
||||
git tag v4.7.0-rc1 origin/main # Create tag at current main (which has release notes)
|
||||
git push origin :refs/tags/v4.7.0-rc1 # Delete remote tag
|
||||
git push origin v4.7.0-rc1 # Push new tag (triggers Deploy workflow)
|
||||
```
|
||||
|
||||
If creating a separate PR for release notes:
|
||||
```bash
|
||||
git checkout -b v4.7.0-release-notes
|
||||
git add Manual/Releases/v4_7_0.lean Manual/Releases.lean
|
||||
git commit -m "doc: add v4.7.0 release notes"
|
||||
git push -u origin v4.7.0-release-notes
|
||||
gh pr create --title "doc: add v4.7.0 release notes" --body "This PR adds the release notes for Lean v4.7.0."
|
||||
```
|
||||
|
||||
See `./releases_drafts/README.md` for more information about pre-written release note entries.
|
||||
See `./releases_drafts/README.md` for more information.
|
||||
|
||||
106
doc/style.md
106
doc/style.md
@@ -810,7 +810,7 @@ Docstrings for constants should have the following structure:
|
||||
|
||||
The **short summary** should be 1–3 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 1–3 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 can’t necessarily be machine checked and
|
||||
don’t 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
|
||||
there’s 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
|
||||
|
||||
@@ -29,7 +29,7 @@ def main (args : List String) : IO Unit := do
|
||||
if !msgs.toList.isEmpty then -- skip this file if there are parse errors
|
||||
msgs.forM fun msg => msg.toString >>= IO.println
|
||||
throw <| .userError "parse errors in file"
|
||||
let `(header| $[module%$moduleTk?]? $imps:import*) := header
|
||||
let `(header| $[module%$moduleTk?]? $[prelude%$preludeTk?]? $imps:import*) := header
|
||||
| throw <| .userError s!"unexpected header syntax of {path}"
|
||||
if moduleTk?.isSome then
|
||||
continue
|
||||
@@ -38,11 +38,11 @@ def main (args : List String) : IO Unit := do
|
||||
let startPos := header.raw.getPos? |>.getD parserState.pos
|
||||
|
||||
let dummyEnv ← mkEmptyEnvironment
|
||||
let (initCmd, parserState', _) :=
|
||||
let (initCmd, parserState', msgs') :=
|
||||
Parser.parseCommand inputCtx { env := dummyEnv, options := {} } parserState msgs
|
||||
|
||||
-- insert section if any trailing command
|
||||
if !initCmd.isOfKind ``Parser.Command.eoi then
|
||||
-- insert section if any trailing command (or error, which could be from an unknown command)
|
||||
if !initCmd.isOfKind ``Parser.Command.eoi || msgs'.hasErrors then
|
||||
let insertPos? :=
|
||||
-- put below initial module docstring if any
|
||||
guard (initCmd.isOfKind ``Parser.Command.moduleDoc) *> initCmd.getTailPos? <|>
|
||||
@@ -57,19 +57,21 @@ def main (args : List String) : IO Unit := do
|
||||
sec := "\n\n" ++ sec
|
||||
if insertPos?.isNone then
|
||||
sec := sec ++ "\n\n"
|
||||
text := text.extract 0 insertPos ++ sec ++ text.extract insertPos text.rawEndPos
|
||||
let insertPos := text.pos! insertPos
|
||||
text := text.extract text.startPos insertPos ++ sec ++ text.extract insertPos text.endPos
|
||||
|
||||
-- prepend each import with `public `
|
||||
for imp in imps.reverse do
|
||||
let insertPos := imp.raw.getPos?.get!
|
||||
let prfx := if doMeta then "public meta " else "public "
|
||||
text := text.extract 0 insertPos ++ prfx ++ text.extract insertPos text.rawEndPos
|
||||
let insertPos := text.pos! insertPos
|
||||
text := text.extract text.startPos insertPos ++ prfx ++ text.extract insertPos text.endPos
|
||||
|
||||
-- insert `module` header
|
||||
let mut initText := text.extract 0 startPos
|
||||
if !initText.trim.isEmpty then
|
||||
let mut initText := text.extract text.startPos (text.pos! startPos)
|
||||
if !initText.trimAscii.isEmpty then
|
||||
-- If there is a header comment, preserve it and put `module` in the line after
|
||||
initText := initText.trimRight ++ "\n"
|
||||
text := initText ++ "module\n\n" ++ text.extract startPos text.rawEndPos
|
||||
initText := initText.trimAsciiEnd.toString ++ "\n"
|
||||
text := initText ++ "module\n\n" ++ text.extract (text.pos! startPos) text.endPos
|
||||
|
||||
IO.FS.writeFile path text
|
||||
|
||||
441
script/build_artifact.py
Executable file
441
script/build_artifact.py
Executable 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()
|
||||
@@ -3,9 +3,3 @@ name = "scripts"
|
||||
[[lean_exe]]
|
||||
name = "modulize"
|
||||
root = "Modulize"
|
||||
|
||||
[[lean_exe]]
|
||||
name = "shake"
|
||||
root = "Shake"
|
||||
# needed by `Lake.loadWorkspace`
|
||||
supportInterpreter = true
|
||||
|
||||
1290
script/lean-bisect
Executable file
1290
script/lean-bisect
Executable file
File diff suppressed because it is too large
Load Diff
307
script/lean-bisect-test.lean
Normal file
307
script/lean-bisect-test.lean
Normal 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!"
|
||||
@@ -185,6 +185,30 @@ def get_release_notes(tag_name):
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def check_release_notes_file_exists(toolchain, github_token):
|
||||
"""Check if the release notes file exists in the reference-manual repository.
|
||||
|
||||
For -rc1 releases, this checks that the release notes have been created.
|
||||
For subsequent RCs and stable releases, release notes should already exist.
|
||||
|
||||
Returns tuple (exists: bool, is_rc1: bool) where is_rc1 indicates if this is
|
||||
the first release candidate (when release notes need to be written).
|
||||
"""
|
||||
# Determine the release notes file path
|
||||
# e.g., v4.28.0-rc1 -> Manual/Releases/v4_28_0.lean
|
||||
base_version = strip_rc_suffix(toolchain.lstrip('v')) # "4.28.0"
|
||||
file_name = f"v{base_version.replace('.', '_')}.lean" # "v4_28_0.lean"
|
||||
file_path = f"Manual/Releases/{file_name}"
|
||||
|
||||
is_rc1 = toolchain.endswith("-rc1")
|
||||
|
||||
repo_url = "https://github.com/leanprover/reference-manual"
|
||||
|
||||
# Check if the file exists on main branch
|
||||
content = get_branch_content(repo_url, "main", file_path, github_token)
|
||||
|
||||
return (content is not None, is_rc1)
|
||||
|
||||
def get_branch_content(repo_url, branch, file_path, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/contents/{file_path}?ref={branch}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
@@ -501,6 +525,76 @@ def check_proofwidgets4_release(repo_url, target_toolchain, github_token):
|
||||
print(f" You will need to create and push a tag v0.0.{next_version}")
|
||||
return False
|
||||
|
||||
def check_reference_manual_release_title(repo_url, toolchain, pr_branch, github_token):
|
||||
"""Check if the reference-manual release notes title matches the release type.
|
||||
|
||||
For RC releases (e.g., v4.27.0-rc1), the title should contain the exact RC suffix.
|
||||
For final releases (e.g., v4.27.0), the title should NOT contain any "-rc".
|
||||
|
||||
Returns True if check passes or is not applicable, False if title needs updating.
|
||||
"""
|
||||
is_rc = is_release_candidate(toolchain)
|
||||
|
||||
# For RC releases, get the base version and RC suffix
|
||||
# e.g., "v4.27.0-rc1" -> version="4.27.0", rc_suffix="-rc1"
|
||||
if is_rc:
|
||||
parts = toolchain.lstrip('v').split('-', 1)
|
||||
version = parts[0]
|
||||
rc_suffix = '-' + parts[1] if len(parts) > 1 else ''
|
||||
else:
|
||||
version = toolchain.lstrip('v')
|
||||
rc_suffix = ''
|
||||
|
||||
# Construct the release notes file path (e.g., Manual/Releases/v4_27_0.lean for v4.27.0)
|
||||
file_name = f"v{version.replace('.', '_')}.lean" # "v4_27_0.lean"
|
||||
file_path = f"Manual/Releases/{file_name}"
|
||||
|
||||
# Try to get the file from the PR branch first, then fall back to main branch
|
||||
content = get_branch_content(repo_url, pr_branch, file_path, github_token)
|
||||
if content is None:
|
||||
# Try the default branch
|
||||
content = get_branch_content(repo_url, "main", file_path, github_token)
|
||||
|
||||
if content is None:
|
||||
print(f" ⚠️ Could not check release notes file: {file_path}")
|
||||
return True # Don't block on this
|
||||
|
||||
# Look for the #doc line with the title
|
||||
for line in content.splitlines():
|
||||
if line.strip().startswith('#doc') and 'Manual' in line:
|
||||
has_rc_in_title = '-rc' in line.lower()
|
||||
|
||||
if is_rc:
|
||||
# For RC releases, title should contain the exact RC suffix (e.g., "-rc1")
|
||||
# Use regex to match exact suffix followed by non-digit (to avoid -rc1 matching -rc10)
|
||||
# Pattern matches the RC suffix followed by a non-digit or end-of-string context
|
||||
# e.g., "-rc1" followed by space, quote, paren, or similar
|
||||
exact_match = re.search(rf'{re.escape(rc_suffix)}(?![0-9])', line, re.IGNORECASE)
|
||||
if exact_match:
|
||||
print(f" ✅ Release notes title correctly shows {rc_suffix}")
|
||||
return True
|
||||
elif has_rc_in_title:
|
||||
print(f" ❌ Release notes title shows wrong RC version (expected {rc_suffix})")
|
||||
print(f" Update {file_path} to use '{rc_suffix}' in the title")
|
||||
return False
|
||||
else:
|
||||
print(f" ❌ Release notes title missing RC suffix")
|
||||
print(f" Update {file_path} to include '{rc_suffix}' in the title")
|
||||
return False
|
||||
else:
|
||||
# For final releases, title should NOT contain -rc
|
||||
if has_rc_in_title:
|
||||
print(f" ❌ Release notes title still shows RC version")
|
||||
print(f" Update {file_path} to remove '-rcN' from the title")
|
||||
return False
|
||||
else:
|
||||
print(f" ✅ Release notes title is updated for final release")
|
||||
return True
|
||||
|
||||
# If we didn't find the #doc line, don't block
|
||||
print(f" ⚠️ Could not find release notes title in {file_path}")
|
||||
return True
|
||||
|
||||
def run_mathlib_verify_version_tags(toolchain, verbose=False):
|
||||
"""Run mathlib4's verify_version_tags.py script to validate the release tag.
|
||||
|
||||
@@ -644,6 +738,27 @@ def main():
|
||||
else:
|
||||
print(f" ✅ Release notes page title looks good ('{actual_title}').")
|
||||
|
||||
# Check if release notes file exists in reference-manual repository
|
||||
# For -rc1 releases, this is when release notes need to be written
|
||||
# For subsequent RCs and stable releases, they should already exist
|
||||
release_notes_exists, is_rc1 = check_release_notes_file_exists(toolchain, github_token)
|
||||
base_version = strip_rc_suffix(toolchain.lstrip('v'))
|
||||
release_notes_file = f"Manual/Releases/v{base_version.replace('.', '_')}.lean"
|
||||
|
||||
if not release_notes_exists:
|
||||
if is_rc1:
|
||||
print(f" ❌ Release notes file not found: {release_notes_file}")
|
||||
print(f" This is an -rc1 release, so release notes need to be written.")
|
||||
print(f" Run `script/release_notes.py --since <previous_version>` to generate them.")
|
||||
print(f" See doc/dev/release_checklist.md section 'Writing the release notes' for details.")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ❌ Release notes file not found: {release_notes_file}")
|
||||
print(f" Release notes should have been created for -rc1. Check the reference-manual repository.")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Release notes file exists: {release_notes_file}")
|
||||
|
||||
repo_status["lean4"] = lean4_success
|
||||
|
||||
# If the release page doesn't exist, skip repository checks and master branch checks
|
||||
@@ -709,6 +824,11 @@ def main():
|
||||
print(f" ⚠️ CI: {ci_message}")
|
||||
else:
|
||||
print(f" ❓ CI: {ci_message}")
|
||||
|
||||
# For reference-manual, check that the release notes title has been updated
|
||||
if name == "reference-manual":
|
||||
pr_branch = f"bump_to_{toolchain}"
|
||||
check_reference_manual_release_title(url, toolchain, pr_branch, github_token)
|
||||
else:
|
||||
print(f" ❌ PR with title '{pr_title}' does not exist")
|
||||
print(f" Run `script/release_steps.py {toolchain} {name}` to create it")
|
||||
|
||||
@@ -14,13 +14,6 @@ repositories:
|
||||
bump-branch: true
|
||||
dependencies: []
|
||||
|
||||
- name: verso
|
||||
url: https://github.com/leanprover/verso
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: lean4checker
|
||||
url: https://github.com/leanprover/lean4checker
|
||||
toolchain-tag: true
|
||||
@@ -42,6 +35,14 @@ repositories:
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: verso
|
||||
url: https://github.com/leanprover/verso
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies:
|
||||
- plausible
|
||||
|
||||
- name: import-graph
|
||||
url: https://github.com/leanprover-community/import-graph
|
||||
toolchain-tag: true
|
||||
@@ -50,12 +51,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 +128,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
|
||||
|
||||
@@ -23,6 +23,7 @@ What this script does:
|
||||
- Special merging strategies for repositories with nightly-testing branches
|
||||
- Safety checks for repositories using bump branches
|
||||
- Custom build and test procedures
|
||||
- lean-fro.org: runs scripts/update.sh to regenerate site content
|
||||
|
||||
6. Commits the changes with message "chore: bump toolchain to {version}"
|
||||
|
||||
@@ -412,20 +413,14 @@ def execute_release_steps(repo, version, config):
|
||||
run_command("lake update", cwd=repo_path, stream_output=True)
|
||||
print(blue("Running `lake update` in examples/hero..."))
|
||||
run_command("lake update", cwd=repo_path / "examples" / "hero", stream_output=True)
|
||||
|
||||
# Run scripts/update.sh to regenerate content
|
||||
print(blue("Running `scripts/update.sh` to regenerate content..."))
|
||||
run_command("scripts/update.sh", cwd=repo_path, stream_output=True)
|
||||
print(green("Content regenerated successfully"))
|
||||
elif repo_name == "cslib":
|
||||
print(blue("Updating lakefile.toml..."))
|
||||
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
|
||||
|
||||
print(blue("Updating docs/lakefile.toml..."))
|
||||
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path / "docs")
|
||||
|
||||
# Update lean-toolchain in docs
|
||||
print(blue("Updating docs/lean-toolchain..."))
|
||||
docs_toolchain = repo_path / "docs" / "lean-toolchain"
|
||||
with open(docs_toolchain, "w") as f:
|
||||
f.write(f"leanprover/lean4:{version}\n")
|
||||
print(green(f"Updated docs/lean-toolchain to leanprover/lean4:{version}"))
|
||||
|
||||
run_command("lake update", cwd=repo_path, stream_output=True)
|
||||
elif dependencies:
|
||||
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
|
||||
|
||||
@@ -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 29)
|
||||
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'")
|
||||
@@ -40,6 +40,10 @@ find_program(LLD_PATH lld)
|
||||
if(LLD_PATH)
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS_DEFAULT " -fuse-ld=lld")
|
||||
endif()
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
# Create space in install names so they can be patched later in Nix.
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS_DEFAULT " -headerpad_max_install_names")
|
||||
endif()
|
||||
|
||||
set(LEAN_EXTRA_LINKER_FLAGS ${LEAN_EXTRA_LINKER_FLAGS_DEFAULT} CACHE STRING "Additional flags used by the linker")
|
||||
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler. Unlike `CMAKE_CXX_FLAGS`, these will not be used to build e.g. cadical.")
|
||||
@@ -452,11 +456,14 @@ if(LLVM AND ${STAGE} GREATER 0)
|
||||
message(VERBOSE "leanshared linker flags: '${LEANSHARED_LINKER_FLAGS}' | lean extra cxx flags '${CMAKE_CXX_FLAGS}'")
|
||||
endif()
|
||||
|
||||
# get rid of unused parts of C++ stdlib
|
||||
# We always strip away unused declarations to reduce binary sizes as the time cost is small and the
|
||||
# potential benefit can be huge, especially when stripping `meta import`s.
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-dead_strip")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fdata-sections -ffunction-sections")
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS " -Wl,-dead_strip")
|
||||
elseif(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,--gc-sections")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fdata-sections -ffunction-sections")
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS " -Wl,--gc-sections")
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
@@ -631,6 +638,9 @@ if(${STAGE} GREATER 1)
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleanrt.a" "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a"
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a"
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/temp/libleancpp_1.a" "${CMAKE_BINARY_DIR}/lib/temp/libleancpp_1.a")
|
||||
add_dependencies(leanrt_initial-exec copy-leancpp)
|
||||
add_dependencies(leanrt copy-leancpp)
|
||||
add_dependencies(leancpp_1 copy-leancpp)
|
||||
add_dependencies(leancpp copy-leancpp)
|
||||
if(LLVM)
|
||||
add_custom_target(copy-lean-h-bc
|
||||
@@ -695,7 +705,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 +768,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)
|
||||
|
||||
@@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Prelude
|
||||
public import Init.Notation
|
||||
@@ -38,6 +37,7 @@ public import Init.Omega
|
||||
public import Init.MacroTrace
|
||||
public import Init.Grind
|
||||
public import Init.GrindInstances
|
||||
public import Init.Sym
|
||||
public import Init.While
|
||||
public import Init.Syntax
|
||||
public import Init.Internal
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
10
src/Init/Control/Lawful/MonadAttach.lean
Normal file
10
src/Init/Control/Lawful/MonadAttach.lean
Normal 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
|
||||
86
src/Init/Control/Lawful/MonadAttach/Instances.lean
Normal file
86
src/Init/Control/Lawful/MonadAttach/Instances.lean
Normal 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
|
||||
90
src/Init/Control/Lawful/MonadAttach/Lemmas.lean
Normal file
90
src/Init/Control/Lawful/MonadAttach/Lemmas.lean
Normal 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]
|
||||
@@ -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}
|
||||
|
||||
|
||||
126
src/Init/Control/MonadAttach.lean
Normal file
126
src/Init/Control/MonadAttach.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
/--
|
||||
@@ -1561,6 +1565,10 @@ instance {p q : Prop} [d : Decidable (p ↔ q)] : Decidable (p = q) :=
|
||||
| isTrue h => isTrue (propext h)
|
||||
| isFalse h => isFalse fun heq => h (heq ▸ Iff.rfl)
|
||||
|
||||
/-- Helper theorem for proving injectivity theorems -/
|
||||
theorem Lean.injEq_helper {P Q R : Prop} :
|
||||
(P → Q → R) → (P ∧ Q → R) := by intro h ⟨h₁,h₂⟩; exact h h₁ h₂
|
||||
|
||||
gen_injective_theorems% Array
|
||||
gen_injective_theorems% BitVec
|
||||
gen_injective_theorems% ByteArray
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -674,6 +674,39 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
|
||||
simp only [Option.map_map, Function.comp_def, Fin.cast_cast]
|
||||
simp [Array.size]
|
||||
|
||||
/-! ### find? and findFinIdx? -/
|
||||
|
||||
theorem find?_eq_map_findFinIdx?_getElem {xs : Array α} {p : α → Bool} :
|
||||
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
|
||||
cases xs
|
||||
simp [List.find?_eq_map_findFinIdx?_getElem]
|
||||
rfl
|
||||
|
||||
theorem find?_eq_bind_findIdx?_getElem? {xs : Array α} {p : α → Bool} :
|
||||
xs.find? p = (xs.findIdx? p).bind (xs[·]?) := by
|
||||
cases xs
|
||||
simp [List.find?_eq_bind_findIdx?_getElem?]
|
||||
|
||||
theorem find?_eq_getElem?_findIdx {xs : Array α} {p : α → Bool} :
|
||||
xs.find? p = xs[xs.findIdx p]? := by
|
||||
cases xs
|
||||
simp [List.find?_eq_getElem?_findIdx]
|
||||
|
||||
theorem findIdx?_eq_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx? p = (xs.find? p).bind (xs.idxOf? ·) := by
|
||||
cases xs
|
||||
simp [List.findIdx?_eq_bind_find?_idxOf?]
|
||||
|
||||
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α → Bool} :
|
||||
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf? ·) := by
|
||||
cases xs
|
||||
simp [List.findFinIdx?_eq_bind_find?_finIdxOf?]
|
||||
|
||||
theorem findIdx_eq_getD_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx p = ((xs.find? p).bind (xs.idxOf? ·)).getD xs.size := by
|
||||
cases xs
|
||||
simp [List.findIdx_eq_getD_bind_find?_idxOf?]
|
||||
|
||||
/-! ### idxOf
|
||||
|
||||
The verification API for `idxOf` is still incomplete.
|
||||
|
||||
@@ -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 ..
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -159,4 +159,17 @@ theorem setWidth_neg_of_le {x : BitVec v} (h : w ≤ v) : BitVec.setWidth w (-x)
|
||||
omega
|
||||
omega
|
||||
|
||||
@[induction_eliminator, elab_as_elim]
|
||||
theorem cons_induction {motive : (w : Nat) → BitVec w → Prop} (nil : motive 0 .nil)
|
||||
(cons : ∀ {w : Nat} (b : Bool) (bv : BitVec w), motive w bv → motive (w + 1) (.cons b bv)) :
|
||||
∀ {w : Nat} (x : BitVec w), motive w x := by
|
||||
intros w x
|
||||
induction w
|
||||
case zero =>
|
||||
simp only [BitVec.eq_nil x, nil]
|
||||
case succ wl ih =>
|
||||
rw [← cons_msb_setWidth x]
|
||||
apply cons
|
||||
apply ih
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -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,35 @@ 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]
|
||||
|
||||
theorem concat_extractLsb'_getLsb {x : BitVec (w + 1)} :
|
||||
BitVec.concat (x.extractLsb' 1 w) (x.getLsb 0) = x := by
|
||||
ext i hw
|
||||
by_cases h : i = 0
|
||||
· simp [h]
|
||||
· simp [h, hw, show (1 + (i - 1)) = i by omega, getElem_concat]
|
||||
|
||||
@[elab_as_elim]
|
||||
theorem concat_induction {motive : (w : Nat) → BitVec w → Prop} (nil : motive 0 .nil)
|
||||
(concat : ∀ {w : Nat} (bv : BitVec w) (b : Bool), motive w bv → motive (w + 1) (bv.concat b)) :
|
||||
∀ {w : Nat} (x : BitVec w), motive w x := by
|
||||
intros w x
|
||||
induction w
|
||||
case zero =>
|
||||
simp only [BitVec.eq_nil x, nil]
|
||||
case succ wl ih =>
|
||||
rw [← concat_extractLsb'_getLsb (x := x)]
|
||||
apply concat
|
||||
apply ih
|
||||
|
||||
/-! ### shiftConcat -/
|
||||
|
||||
@[grind =]
|
||||
@@ -5812,6 +5886,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 +6371,246 @@ 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]
|
||||
|
||||
|
||||
@[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_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]
|
||||
|
||||
@[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 cpopNatRec_zero_le (x : BitVec w) (n : Nat) :
|
||||
x.cpopNatRec n 0 ≤ w := by
|
||||
induction x
|
||||
· case nil => simp
|
||||
· case cons w b bv ih =>
|
||||
by_cases hle : n ≤ w
|
||||
· have := cpopNatRec_cons_of_le (b := b) (x := bv) (n := n) (acc := 0) hle
|
||||
omega
|
||||
· rw [cpopNatRec_cons_of_lt (by omega)]
|
||||
have : b.toNat ≤ 1 := by cases b <;> simp
|
||||
omega
|
||||
|
||||
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
|
||||
|
||||
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 x generalizing y
|
||||
· case nil =>
|
||||
simp
|
||||
· case cons w b bv ih =>
|
||||
simp [cons_append, ih]
|
||||
omega
|
||||
|
||||
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)]
|
||||
|
||||
theorem toNat_cpop_not {x : BitVec w} :
|
||||
(~~~x).cpop.toNat = w - x.cpop.toNat := by
|
||||
induction x
|
||||
· case nil =>
|
||||
simp
|
||||
· case cons b x ih =>
|
||||
have := toNat_cpop_le x
|
||||
cases b
|
||||
<;> (simp [ih]; omega)
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,3 +9,4 @@ prelude
|
||||
public import Init.Data.Char.Basic
|
||||
public import Init.Data.Char.Lemmas
|
||||
public import Init.Data.Char.Order
|
||||
public import Init.Data.Char.Ordinal
|
||||
|
||||
@@ -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
|
||||
|
||||
242
src/Init/Data/Char/Ordinal.lean
Normal file
242
src/Init/Data/Char/Ordinal.lean
Normal file
@@ -0,0 +1,242 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Fin.OverflowAware
|
||||
public import Init.Data.UInt.Basic
|
||||
public import Init.Data.Function
|
||||
import Init.Data.Char.Lemmas
|
||||
import Init.Data.Char.Order
|
||||
import Init.Grind
|
||||
|
||||
/-!
|
||||
# Bijection between `Char` and `Fin Char.numCodePoints`
|
||||
|
||||
In this file, we construct a bijection between `Char` and `Fin Char.numCodePoints` and show that
|
||||
it is compatible with various operations. Since `Fin` is simpler than `Char` due to being based
|
||||
on natural numbers instead of `UInt32` and not having a hole in the middle (surrogate code points),
|
||||
this is sometimes useful to simplify reasoning about `Char`.
|
||||
|
||||
We use these declarations in the construction of `Char` ranges, see the module
|
||||
`Init.Data.Range.Polymorphic.Char`.
|
||||
-/
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
public section
|
||||
|
||||
namespace Char
|
||||
|
||||
/-- The number of surrogate code points. -/
|
||||
abbrev numSurrogates : Nat :=
|
||||
-- 0xe000 - 0xd800
|
||||
2048
|
||||
|
||||
/-- The size of the {name}`Char` type. -/
|
||||
abbrev numCodePoints : Nat :=
|
||||
-- 0x110000 - numSurrogates
|
||||
1112064
|
||||
|
||||
/--
|
||||
Packs {name}`Char` bijectively into {lean}`Fin Char.numCodePoints` by shifting code points which are
|
||||
greater than the surrogate code points by the number of surrogate code points.
|
||||
|
||||
The inverse of this function is called {name (scope := "Init.Data.Char.Ordinal")}`Char.ofOrdinal`.
|
||||
-/
|
||||
def ordinal (c : Char) : Fin Char.numCodePoints :=
|
||||
if h : c.val < 0xd800 then
|
||||
⟨c.val.toNat, by grind [UInt32.lt_iff_toNat_lt]⟩
|
||||
else
|
||||
⟨c.val.toNat - Char.numSurrogates, by grind [UInt32.lt_iff_toNat_lt]⟩
|
||||
|
||||
/--
|
||||
Unpacks {lean}`Fin Char.numCodePoints` bijectively to {name}`Char` by shifting code points which are
|
||||
greater than the surrogate code points by the number of surrogate code points.
|
||||
|
||||
The inverse of this function is called {name}`Char.ordinal`.
|
||||
-/
|
||||
def ofOrdinal (f : Fin Char.numCodePoints) : Char :=
|
||||
if h : (f : Nat) < 0xd800 then
|
||||
⟨UInt32.ofNatLT f (by grind), by grind [UInt32.toNat_ofNatLT]⟩
|
||||
else
|
||||
⟨UInt32.ofNatLT (f + Char.numSurrogates) (by grind), by grind [UInt32.toNat_ofNatLT]⟩
|
||||
|
||||
/--
|
||||
Computes the next {name}`Char`, skipping over surrogate code points (which are not valid
|
||||
{name}`Char`s) as necessary.
|
||||
|
||||
This function is specified by its interaction with {name}`Char.ordinal`, see
|
||||
{name (scope := "Init.Data.Char.Ordinal")}`Char.succ?_eq`.
|
||||
-/
|
||||
def succ? (c : Char) : Option Char :=
|
||||
if h₀ : c.val < 0xd7ff then
|
||||
some ⟨c.val + 1, by grind [UInt32.lt_iff_toNat_lt, UInt32.toNat_add]⟩
|
||||
else if h₁ : c.val = 0xd7ff then
|
||||
some ⟨0xe000, by decide⟩
|
||||
else if h₂ : c.val < 0x10ffff then
|
||||
some ⟨c.val + 1, by
|
||||
simp only [UInt32.lt_iff_toNat_lt, UInt32.reduceToNat, Nat.not_lt, ← UInt32.toNat_inj,
|
||||
UInt32.isValidChar, Nat.isValidChar, UInt32.toNat_add, Nat.reducePow] at *
|
||||
grind⟩
|
||||
else none
|
||||
|
||||
/--
|
||||
Computes the {name}`m`-th next {name}`Char`, skipping over surrogate code points (which are not
|
||||
valid {name}`Char`s) as necessary.
|
||||
|
||||
This function is specified by its interaction with {name}`Char.ordinal`, see
|
||||
{name (scope := "Init.Data.Char.Ordinal")}`Char.succMany?_eq`.
|
||||
-/
|
||||
def succMany? (m : Nat) (c : Char) : Option Char :=
|
||||
c.ordinal.addNat? m |>.map Char.ofOrdinal
|
||||
|
||||
@[grind =]
|
||||
theorem coe_ordinal {c : Char} :
|
||||
(c.ordinal : Nat) =
|
||||
if c.val < 0xd800 then
|
||||
c.val.toNat
|
||||
else
|
||||
c.val.toNat - Char.numSurrogates := by
|
||||
grind [Char.ordinal]
|
||||
|
||||
@[simp]
|
||||
theorem ordinal_zero : '\x00'.ordinal = 0 := by
|
||||
ext
|
||||
simp [coe_ordinal]
|
||||
|
||||
@[grind =]
|
||||
theorem val_ofOrdinal {f : Fin Char.numCodePoints} :
|
||||
(Char.ofOrdinal f).val =
|
||||
if h : (f : Nat) < 0xd800 then
|
||||
UInt32.ofNatLT f (by grind)
|
||||
else
|
||||
UInt32.ofNatLT (f + Char.numSurrogates) (by grind) := by
|
||||
grind [Char.ofOrdinal]
|
||||
|
||||
@[simp]
|
||||
theorem ofOrdinal_ordinal {c : Char} : Char.ofOrdinal c.ordinal = c := by
|
||||
ext
|
||||
simp only [val_ofOrdinal, coe_ordinal, UInt32.ofNatLT_add]
|
||||
split
|
||||
· grind [UInt32.lt_iff_toNat_lt, UInt32.ofNatLT_toNat]
|
||||
· rw [dif_neg]
|
||||
· simp only [← UInt32.toNat_inj, UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
|
||||
grind [UInt32.toNat_lt, UInt32.lt_iff_toNat_lt]
|
||||
· grind [UInt32.lt_iff_toNat_lt]
|
||||
|
||||
@[simp]
|
||||
theorem ordinal_ofOrdinal {f : Fin Char.numCodePoints} : (Char.ofOrdinal f).ordinal = f := by
|
||||
ext
|
||||
simp [coe_ordinal, val_ofOrdinal]
|
||||
split
|
||||
· rw [if_pos, UInt32.toNat_ofNatLT]
|
||||
simpa [UInt32.lt_iff_toNat_lt]
|
||||
· rw [if_neg, UInt32.toNat_add, UInt32.toNat_ofNatLT, UInt32.toNat_ofNatLT, Nat.mod_eq_of_lt,
|
||||
Nat.add_sub_cancel]
|
||||
· grind
|
||||
· simp only [UInt32.lt_iff_toNat_lt, UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow,
|
||||
UInt32.reduceToNat, Nat.not_lt]
|
||||
grind
|
||||
|
||||
@[simp]
|
||||
theorem ordinal_comp_ofOrdinal : Char.ordinal ∘ Char.ofOrdinal = id := by
|
||||
ext; simp
|
||||
|
||||
@[simp]
|
||||
theorem ofOrdinal_comp_ordinal : Char.ofOrdinal ∘ Char.ordinal = id := by
|
||||
ext; simp
|
||||
|
||||
@[simp]
|
||||
theorem ordinal_inj {c d : Char} : c.ordinal = d.ordinal ↔ c = d :=
|
||||
⟨fun h => by simpa using congrArg Char.ofOrdinal h, (· ▸ rfl)⟩
|
||||
|
||||
theorem ordinal_injective : Function.Injective Char.ordinal :=
|
||||
fun _ _ => ordinal_inj.1
|
||||
|
||||
@[simp]
|
||||
theorem ofOrdinal_inj {f g : Fin Char.numCodePoints} :
|
||||
Char.ofOrdinal f = Char.ofOrdinal g ↔ f = g :=
|
||||
⟨fun h => by simpa using congrArg Char.ordinal h, (· ▸ rfl)⟩
|
||||
|
||||
theorem ofOrdinal_injective : Function.Injective Char.ofOrdinal :=
|
||||
fun _ _ => ofOrdinal_inj.1
|
||||
|
||||
theorem ordinal_le_of_le {c d : Char} (h : c ≤ d) : c.ordinal ≤ d.ordinal := by
|
||||
simp only [le_def, UInt32.le_iff_toNat_le] at h
|
||||
simp only [Fin.le_def, coe_ordinal, UInt32.lt_iff_toNat_lt, UInt32.reduceToNat]
|
||||
grind
|
||||
|
||||
theorem ofOrdinal_le_of_le {f g : Fin Char.numCodePoints} (h : f ≤ g) :
|
||||
Char.ofOrdinal f ≤ Char.ofOrdinal g := by
|
||||
simp only [Fin.le_def] at h
|
||||
simp only [le_def, val_ofOrdinal, UInt32.ofNatLT_add, UInt32.le_iff_toNat_le]
|
||||
split
|
||||
· simp only [UInt32.toNat_ofNatLT]
|
||||
split
|
||||
· simpa
|
||||
· simp only [UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
|
||||
grind
|
||||
· simp only [UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
|
||||
rw [dif_neg (by grind)]
|
||||
simp only [UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
|
||||
grind
|
||||
|
||||
theorem le_iff_ordinal_le {c d : Char} : c ≤ d ↔ c.ordinal ≤ d.ordinal :=
|
||||
⟨ordinal_le_of_le, fun h => by simpa using ofOrdinal_le_of_le h⟩
|
||||
|
||||
theorem le_iff_ofOrdinal_le {f g : Fin Char.numCodePoints} :
|
||||
f ≤ g ↔ Char.ofOrdinal f ≤ Char.ofOrdinal g :=
|
||||
⟨ofOrdinal_le_of_le, fun h => by simpa using ordinal_le_of_le h⟩
|
||||
|
||||
theorem lt_iff_ordinal_lt {c d : Char} : c < d ↔ c.ordinal < d.ordinal := by
|
||||
simp only [Std.lt_iff_le_and_not_ge, le_iff_ordinal_le]
|
||||
|
||||
theorem lt_iff_ofOrdinal_lt {f g : Fin Char.numCodePoints} :
|
||||
f < g ↔ Char.ofOrdinal f < Char.ofOrdinal g := by
|
||||
simp only [Std.lt_iff_le_and_not_ge, le_iff_ofOrdinal_le]
|
||||
|
||||
theorem succ?_eq {c : Char} : c.succ? = (c.ordinal.addNat? 1).map Char.ofOrdinal := by
|
||||
fun_cases Char.succ? with
|
||||
| case1 h =>
|
||||
rw [Fin.addNat?_eq_some]
|
||||
· simp only [coe_ordinal, Option.map_some, Option.some.injEq, Char.ext_iff, val_ofOrdinal,
|
||||
UInt32.ofNatLT_add, UInt32.reduceOfNatLT]
|
||||
split
|
||||
· simp only [UInt32.ofNatLT_toNat, dite_eq_ite, left_eq_ite_iff, Nat.not_lt,
|
||||
Nat.reduceLeDiff, UInt32.left_eq_add]
|
||||
grind [UInt32.lt_iff_toNat_lt]
|
||||
· grind
|
||||
· simp [coe_ordinal]
|
||||
grind [UInt32.lt_iff_toNat_lt]
|
||||
| case2 =>
|
||||
rw [Fin.addNat?_eq_some]
|
||||
· simp [coe_ordinal, *, Char.ext_iff, val_ofOrdinal, numSurrogates]
|
||||
· simp [coe_ordinal, *, numCodePoints]
|
||||
| case3 =>
|
||||
rw [Fin.addNat?_eq_some]
|
||||
· simp only [coe_ordinal, Option.map_some, Option.some.injEq, Char.ext_iff, val_ofOrdinal,
|
||||
UInt32.ofNatLT_add, UInt32.reduceOfNatLT]
|
||||
split
|
||||
· grind
|
||||
· rw [dif_neg]
|
||||
· simp only [← UInt32.toNat_inj, UInt32.toNat_add, UInt32.reduceToNat, Nat.reducePow,
|
||||
UInt32.toNat_ofNatLT, Nat.mod_add_mod]
|
||||
grind [UInt32.lt_iff_toNat_lt, UInt32.toNat_inj]
|
||||
· grind [UInt32.lt_iff_toNat_lt, UInt32.toNat_inj]
|
||||
· grind [UInt32.lt_iff_toNat_lt]
|
||||
| case4 =>
|
||||
rw [eq_comm]
|
||||
grind [UInt32.lt_iff_toNat_lt]
|
||||
|
||||
theorem map_ordinal_succ? {c : Char} : c.succ?.map ordinal = c.ordinal.addNat? 1 := by
|
||||
simp [succ?_eq]
|
||||
|
||||
theorem succMany?_eq {m : Nat} {c : Char} :
|
||||
c.succMany? m = (c.ordinal.addNat? m).map Char.ofOrdinal := by
|
||||
rfl
|
||||
|
||||
end Char
|
||||
@@ -11,3 +11,4 @@ public import Init.Data.Fin.Log2
|
||||
public import Init.Data.Fin.Iterate
|
||||
public import Init.Data.Fin.Fold
|
||||
public import Init.Data.Fin.Lemmas
|
||||
public import Init.Data.Fin.OverflowAware
|
||||
|
||||
51
src/Init/Data/Fin/OverflowAware.lean
Normal file
51
src/Init/Data/Fin/OverflowAware.lean
Normal file
@@ -0,0 +1,51 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Fin.Basic
|
||||
import Init.Data.Fin.Lemmas
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
public section
|
||||
|
||||
namespace Fin
|
||||
|
||||
/--
|
||||
Overflow-aware addition of a natural number to an element of {lean}`Fin n`.
|
||||
|
||||
Examples:
|
||||
* {lean}`(2 : Fin 3).addNat? 1 = (none : Option (Fin 3))`
|
||||
* {lean}`(2 : Fin 4).addNat? 1 = (some 3 : Option (Fin 4))`
|
||||
-/
|
||||
@[inline]
|
||||
protected def addNat? (i : Fin n) (m : Nat) : Option (Fin n) :=
|
||||
if h : i + m < n then some ⟨i + m, h⟩ else none
|
||||
|
||||
theorem addNat?_eq_some {i : Fin n} (h : i + m < n) : i.addNat? m = some ⟨i + m, h⟩ := by
|
||||
simp [Fin.addNat?, h]
|
||||
|
||||
theorem addNat?_eq_some_iff {i : Fin n} :
|
||||
i.addNat? m = some j ↔ i + m < n ∧ j = i + m := by
|
||||
simp only [Fin.addNat?]
|
||||
split <;> simp [Fin.ext_iff, eq_comm, *]
|
||||
|
||||
@[simp]
|
||||
theorem addNat?_eq_none_iff {i : Fin n} : i.addNat? m = none ↔ n ≤ i + m := by
|
||||
simp only [Fin.addNat?]
|
||||
split <;> simp_all [Nat.not_lt]
|
||||
|
||||
@[simp]
|
||||
theorem addNat?_zero {i : Fin n} : i.addNat? 0 = some i := by
|
||||
simp [addNat?_eq_some_iff]
|
||||
|
||||
@[grind =]
|
||||
theorem addNat?_eq_dif {i : Fin n} :
|
||||
i.addNat? m = if h : i + m < n then some ⟨i + m, h⟩ else none := by
|
||||
rfl
|
||||
|
||||
end Fin
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -1419,4 +1447,12 @@ instance : LawfulOrderLT Int where
|
||||
lt_iff := by
|
||||
simp [← Int.not_le, Decidable.imp_iff_not_or, Std.Total.total]
|
||||
|
||||
instance : LawfulOrderLeftLeaningMin Int where
|
||||
min_eq_left _ _ := Int.min_eq_left
|
||||
min_eq_right _ _ h := Int.min_eq_right (le_of_lt (not_le.1 h))
|
||||
|
||||
instance : LawfulOrderLeftLeaningMax Int where
|
||||
max_eq_left _ _ := Int.max_eq_left
|
||||
max_eq_right _ _ h := Int.max_eq_right (le_of_lt (not_le.1 h))
|
||||
|
||||
end Int
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
@@ -629,6 +630,43 @@ def Iter.Total.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id
|
||||
(it : Iter.Total (α := α) β) (f : β → Bool) : Option β :=
|
||||
it.it.find? f
|
||||
|
||||
/--
|
||||
Returns the first output of the iterator, or `none` if no such output is found.
|
||||
|
||||
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
|
||||
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
|
||||
|
||||
If the iterator is not productive, this function might run forever. The variant
|
||||
`it.ensureTermination.first?` always terminates after finitely many steps.
|
||||
|
||||
Examples:
|
||||
* `[7, 6].iter.first? = some 7`
|
||||
* `[].iter.first? = none`
|
||||
-/
|
||||
@[inline]
|
||||
def Iter.first? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
|
||||
(it : Iter (α := α) β) : Option β :=
|
||||
it.toIterM.first?.run
|
||||
|
||||
/--
|
||||
Returns the first output of the iterator, or `none` if no such output is found.
|
||||
|
||||
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
|
||||
Short-circuits upon encountering the first result. The elements in `it` are examined in order of
|
||||
iteration.
|
||||
|
||||
This variant terminates after finitely many steps and requires a proof that the iterator is
|
||||
productive. If such a proof is not available, consider using `Iter.first?`.
|
||||
|
||||
Examples:
|
||||
* `[7, 6].iter.first? = some 7`
|
||||
* `[].iter.first? = none`
|
||||
-/
|
||||
@[inline]
|
||||
def Iter.Total.first? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id] [Productive α Id]
|
||||
(it : Iter.Total (α := α) β) : Option β :=
|
||||
it.it.first?
|
||||
|
||||
/--
|
||||
Steps through the whole iterator, counting the number of outputs emitted.
|
||||
|
||||
@@ -677,4 +715,4 @@ def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorL
|
||||
(it : Iter.Partial (α := α) β) : Nat :=
|
||||
it.it.count
|
||||
|
||||
end Std.Iterators
|
||||
end Std
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,22 +170,23 @@ 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
|
||||
constructor; simp
|
||||
|
||||
theorem IteratorLoop.wellFounded_of_finite {m : Type w → Type w'}
|
||||
{α β : Type w} {γ : Type x} [Iterator α m β] [Finite α m] :
|
||||
WellFounded α m (γ := γ) fun _ _ _ => True := by
|
||||
{α β : Type w} {γ : Type x} [Iterator α m β] [Finite α m] {P : β → γ → ForInStep γ → Prop} :
|
||||
WellFounded α m (γ := γ) P := by
|
||||
apply Subrelation.wf
|
||||
(r := InvImage IterM.TerminationMeasures.Finite.Rel (fun p => p.1.finitelyManySteps))
|
||||
· intro p' p h
|
||||
@@ -189,6 +197,16 @@ theorem IteratorLoop.wellFounded_of_finite {m : Type w → Type w'}
|
||||
· apply InvImage.wf
|
||||
exact WellFoundedRelation.wf
|
||||
|
||||
theorem IteratorLoop.wellFounded_of_productive {α β : Type w} {m : Type w → Type w'}
|
||||
[Iterator α m β] [IteratorLoop α m m] [Productive α m] {P : β → γ → ForInStep γ → Prop}
|
||||
(hp : ∀ {b g s}, P b g s → s matches ForInStep.done ..) :
|
||||
WellFounded α m (γ := γ) P := by
|
||||
rw [WellFounded]
|
||||
unfold IteratorLoop.rel
|
||||
have {b g q} : ¬ P b g (ForInStep.yield q) := fun h => by simpa using hp h
|
||||
simp only [and_false, exists_false, false_or, this]
|
||||
exact Subrelation.wf And.left (InvImage.wf Prod.fst Productive.wf)
|
||||
|
||||
/--
|
||||
This `ForIn'`-style loop construct traverses a finite iterator using an `IteratorLoop` instance.
|
||||
-/
|
||||
@@ -212,13 +230,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 +245,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 +253,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 +266,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)
|
||||
|
||||
@@ -891,6 +912,44 @@ def IterM.Total.find? {α β : Type w} {m : Type w → Type w'} [Monad m] [Itera
|
||||
m (Option β) :=
|
||||
it.it.find? f
|
||||
|
||||
/--
|
||||
Returns the first output of the iterator, or `none` if no such output is found.
|
||||
|
||||
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
|
||||
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
|
||||
|
||||
If the iterator is not productive, this function might run forever. The variant
|
||||
`it.ensureTermination.first?` always terminates after finitely many steps.
|
||||
|
||||
Examples:
|
||||
* `([7, 6].iterM Id).first? = pure (some 7)`
|
||||
* `([].iterM Id).first? = pure none`
|
||||
-/
|
||||
@[inline]
|
||||
def IterM.first? {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β]
|
||||
[IteratorLoop α m m] (it : IterM (α := α) m β) : m (Option β) :=
|
||||
IteratorLoop.forIn (fun _ _ => flip Bind.bind) _ (fun b _ s => s = ForInStep.done (some b)) it
|
||||
none (fun b _ _ => pure ⟨ForInStep.done (some b), rfl⟩)
|
||||
|
||||
/--
|
||||
Returns the first output of the iterator, or `none` if no such output is found.
|
||||
|
||||
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
|
||||
Short-circuits upon encountering the first result. The elements in `it` are examined in order of
|
||||
iteration.
|
||||
|
||||
This variant terminates after finitely many steps and requires a proof that the iterator is
|
||||
productive. If such a proof is not available, consider using `IterM.first?`.
|
||||
|
||||
Examples:
|
||||
* `([7, 6].iterM Id).first? = pure (some 7)`
|
||||
* `([].iterM Id).first? = pure none`
|
||||
-/
|
||||
@[inline]
|
||||
def IterM.Total.first? {α β : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β]
|
||||
[IteratorLoop α m m] [Productive α m] (it : IterM.Total (α := α) m β) : m (Option β) :=
|
||||
it.it.first?
|
||||
|
||||
section Count
|
||||
|
||||
/--
|
||||
@@ -943,4 +1002,4 @@ def IterM.Partial.size {α : Type w} {m : Type w → Type w'} {β : Type w} [Ite
|
||||
|
||||
end Count
|
||||
|
||||
end Std.Iterators
|
||||
end Std
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,4 +7,3 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
|
||||
public import Init.Data.Iterators.Internal.Termination
|
||||
|
||||
@@ -111,6 +111,11 @@ instance {n : Type u → Type w} [Monad n] [LawfulMonad n] :
|
||||
liftBind_pure := by simp
|
||||
liftBind_bind := by simp
|
||||
|
||||
instance {m : Type u → Type v} [Monad m] [LawfulMonad m] :
|
||||
LawfulMonadLiftBindFunction (m := m) (n := m) (fun _ _ => flip Bind.bind) where
|
||||
liftBind_pure := by simp [flip]
|
||||
liftBind_bind := by simp [flip]
|
||||
|
||||
end LiftBind
|
||||
|
||||
end Std.Internal
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
26
src/Init/Data/Iterators/Lemmas/Consumers/Access.lean
Normal file
26
src/Init/Data/Iterators/Lemmas/Consumers/Access.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user