mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-21 20:34:07 +00:00
Compare commits
221 Commits
multiple_t
...
extCore_pu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
703b963b09 | ||
|
|
2f93363752 | ||
|
|
4329eae8d4 | ||
|
|
114f7e42f1 | ||
|
|
419982bd42 | ||
|
|
8431088c93 | ||
|
|
803ec8ff9d | ||
|
|
c4747752fe | ||
|
|
ed4d453346 | ||
|
|
45df6fcd37 | ||
|
|
4077bf2c05 | ||
|
|
54a3fbf88f | ||
|
|
746206c5e6 | ||
|
|
88141a0a49 | ||
|
|
b17afe0f06 | ||
|
|
7632cefa87 | ||
|
|
7a47bfa208 | ||
|
|
ae6335f115 | ||
|
|
f58999a7a6 | ||
|
|
888b59bf95 | ||
|
|
1dae353575 | ||
|
|
a4b788c332 | ||
|
|
5865c41a76 | ||
|
|
4b0e8d88ce | ||
|
|
9d427fdfcf | ||
|
|
fe1e7d56f4 | ||
|
|
fbe98d76b2 | ||
|
|
9a5e425990 | ||
|
|
14ff08db6f | ||
|
|
316859e871 | ||
|
|
47dbcd4b93 | ||
|
|
4f7d3bb692 | ||
|
|
0dc862e3ed | ||
|
|
d9ee24bf36 | ||
|
|
0639d49a4c | ||
|
|
3a26eb7281 | ||
|
|
830be29422 | ||
|
|
2a8c03109a | ||
|
|
07f8ab533c | ||
|
|
a73ebe8a77 | ||
|
|
3931a72573 | ||
|
|
bf809b5298 | ||
|
|
4b6f07060d | ||
|
|
09092549d0 | ||
|
|
1b4360c32a | ||
|
|
705dac4f77 | ||
|
|
3bab621364 | ||
|
|
526ab9caff | ||
|
|
71ddf227d2 | ||
|
|
dca8d6d188 | ||
|
|
6f1e932542 | ||
|
|
c32a57e580 | ||
|
|
aa86d95c08 | ||
|
|
f9e140838e | ||
|
|
98a6fa1ac7 | ||
|
|
11be7e8f4a | ||
|
|
a89463bf9e | ||
|
|
7600d41c90 | ||
|
|
80b8e44072 | ||
|
|
1d989523d4 | ||
|
|
3b061a0996 | ||
|
|
1b1c802362 | ||
|
|
50c19f704b | ||
|
|
bbc194b733 | ||
|
|
4e7a2b2371 | ||
|
|
215bc30296 | ||
|
|
b00d1f933f | ||
|
|
5ba0f8b885 | ||
|
|
43da17aa7f | ||
|
|
0195fdf9aa | ||
|
|
5a751d4688 | ||
|
|
486d93c5fd | ||
|
|
8cebe691a2 | ||
|
|
8655f7706f | ||
|
|
5c92ffc64d | ||
|
|
ca7e7c4279 | ||
|
|
13c38f64a5 | ||
|
|
b59959ddab | ||
|
|
8f9c27cc06 | ||
|
|
715c53d92e | ||
|
|
7a9d769444 | ||
|
|
15636a347f | ||
|
|
1ecdf8ddfa | ||
|
|
54c6efea95 | ||
|
|
b13f7e25ec | ||
|
|
6964a15b5d | ||
|
|
ad701b577b | ||
|
|
1f7374a5d6 | ||
|
|
aa3d409eb6 | ||
|
|
7771b8079c | ||
|
|
43d4c8fe9f | ||
|
|
4898f28c12 | ||
|
|
16400e2aa3 | ||
|
|
d228cd3edd | ||
|
|
232a0495b0 | ||
|
|
30f41fe542 | ||
|
|
fbfb0757ca | ||
|
|
ffb6142ee7 | ||
|
|
7b3c22cebb | ||
|
|
1ac81c6a7a | ||
|
|
662dc10447 | ||
|
|
7688919765 | ||
|
|
5d3df7b5f4 | ||
|
|
643da1ea1b | ||
|
|
3d75c2ce2b | ||
|
|
b979fa012b | ||
|
|
288b7d2023 | ||
|
|
5c707d936c | ||
|
|
5a2e46b021 | ||
|
|
24c86fc05d | ||
|
|
d17160518c | ||
|
|
89686fcd02 | ||
|
|
0b2193c771 | ||
|
|
2c6576b269 | ||
|
|
2cca32ccc3 | ||
|
|
784a063092 | ||
|
|
ba52e9393c | ||
|
|
1efefc25a5 | ||
|
|
c920326f0b | ||
|
|
63354ce594 | ||
|
|
3095c9d4df | ||
|
|
689b3aa8d7 | ||
|
|
d9058225a9 | ||
|
|
29c2b86ef4 | ||
|
|
ee8f0cca33 | ||
|
|
5bfbe2a875 | ||
|
|
9dc1faf327 | ||
|
|
663d4d2c79 | ||
|
|
81ea922025 | ||
|
|
d88e417cda | ||
|
|
dfd3d18530 | ||
|
|
7d55c033e1 | ||
|
|
5d8498888b | ||
|
|
5ede2bfcf2 | ||
|
|
c039e29a3f | ||
|
|
356d1f64bf | ||
|
|
9f2ce635ae | ||
|
|
76403367ba | ||
|
|
c016bb9434 | ||
|
|
239c348239 | ||
|
|
b82303e9b3 | ||
|
|
6f3fef9373 | ||
|
|
4338a8be32 | ||
|
|
19f6c168ef | ||
|
|
eba8bf3347 | ||
|
|
8c69b1eaec | ||
|
|
fd3f51012f | ||
|
|
8b2fea1ec7 | ||
|
|
9b1109c55d | ||
|
|
55b35c6e38 | ||
|
|
3ce5097c3c | ||
|
|
b6bfc9733c | ||
|
|
8637bd296e | ||
|
|
6881177e38 | ||
|
|
409daac2cb | ||
|
|
62fa92ec4a | ||
|
|
0504e32bb7 | ||
|
|
fbfc7694a0 | ||
|
|
69b8b0098c | ||
|
|
69c8f13bf2 | ||
|
|
39beb25f16 | ||
|
|
6d5efd79b9 | ||
|
|
b37d2ce2b9 | ||
|
|
18832eb600 | ||
|
|
05300f7b51 | ||
|
|
0bf7741a3e | ||
|
|
f80d6e7d38 | ||
|
|
5b8d4d7210 | ||
|
|
db8c77a8fa | ||
|
|
7ee3079afb | ||
|
|
c3d9d0d931 | ||
|
|
e98d7dd603 | ||
|
|
6102f00322 | ||
|
|
646f2fabbf | ||
|
|
f4a0259344 | ||
|
|
3f816156cc | ||
|
|
c92ec361cd | ||
|
|
49cff79712 | ||
|
|
2677ca8fb4 | ||
|
|
78b09d5dcc | ||
|
|
a164ae5073 | ||
|
|
2c54386555 | ||
|
|
62fd973b28 | ||
|
|
71e09ca883 | ||
|
|
e6dd41255b | ||
|
|
cfc46ac17f | ||
|
|
7c0868d562 | ||
|
|
28fb4bb1b2 | ||
|
|
2231d9b488 | ||
|
|
e72bf59385 | ||
|
|
343328b7df | ||
|
|
5b9befcdbf | ||
|
|
188ef680da | ||
|
|
5fd8c1b94d | ||
|
|
5ef7b45afa | ||
|
|
9f41f3324a | ||
|
|
055060990c | ||
|
|
4c44f4ef7c | ||
|
|
d6cd738ab4 | ||
|
|
68409ef6fd | ||
|
|
ca1101dddd | ||
|
|
ce7a4f50be | ||
|
|
eb9dd9a9e3 | ||
|
|
b6198434f2 | ||
|
|
1374445081 | ||
|
|
9df345e322 | ||
|
|
3b2705d0df | ||
|
|
44a2b085c4 | ||
|
|
7f18c734eb | ||
|
|
ac6ae51bce | ||
|
|
fd4a8c5407 | ||
|
|
2e5bbf4596 | ||
|
|
00b74e02cd | ||
|
|
90db9ef006 | ||
|
|
3ddda9ae4d | ||
|
|
ac0b82933f | ||
|
|
d8219a37ef | ||
|
|
7ea7acc687 | ||
|
|
161a1c06a2 | ||
|
|
781e3c6add | ||
|
|
b73b8a7edf |
57
.claude/commands/release.md
Normal file
57
.claude/commands/release.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Release Management Command
|
||||
|
||||
Execute the release process for a given version by running the release checklist and following its instructions.
|
||||
|
||||
## Before Starting
|
||||
|
||||
**IMPORTANT**: Before beginning the release process, read the in-file documentation:
|
||||
- Read `script/release_checklist.py` for what the checklist script does
|
||||
- Read `script/release_steps.py` for what the release steps script does
|
||||
|
||||
These comments explain the scripts' behavior, which repositories get special handling, and how errors are handled.
|
||||
|
||||
## Arguments
|
||||
- `version`: The version to release (e.g., v4.24.0)
|
||||
|
||||
## Process
|
||||
|
||||
1. Run `script/release_checklist.py {version}` to check the current status
|
||||
2. Create a todo list tracking all repositories that need updates
|
||||
3. For each repository that needs updating:
|
||||
- Run `script/release_steps.py {version} {repo_name}` to create the PR
|
||||
- Mark it complete when the PR is created
|
||||
4. After creating PRs, notify the user which PRs need review and merging
|
||||
5. Continuously rerun `script/release_checklist.py {version}` to check progress
|
||||
6. As PRs are merged, dependent repositories will become ready - create PRs for those as well
|
||||
7. Continue until all repositories are updated and the release is complete
|
||||
|
||||
## Important Notes
|
||||
|
||||
- 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.)
|
||||
- Wait for user to merge PRs before dependent repos can be updated
|
||||
- Alert user if anything unusual or scary happens
|
||||
- Use appropriate timeouts for long-running builds (verso can take 10+ minutes)
|
||||
- ProofWidgets4 uses semantic versioning (v0.0.X) - it's okay to create and push the next sequential tag yourself when needed for a release
|
||||
|
||||
## PR Status Reporting
|
||||
|
||||
Every time you run `release_checklist.py`, you MUST:
|
||||
1. Parse the output to identify ALL open PRs mentioned (lines with "✅ PR with title ... exists")
|
||||
2. Provide a summary to the user listing ALL open PRs that need review
|
||||
3. Group them by status:
|
||||
- PRs for repositories that are blocked by dependencies (show these but note they're blocked)
|
||||
- PRs for repositories that are ready to merge (highlight these)
|
||||
4. Format the summary clearly with PR numbers and URLs
|
||||
|
||||
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.
|
||||
|
||||
## Error Handling
|
||||
|
||||
**CRITICAL**: If something goes wrong or a command fails:
|
||||
- **DO NOT** try to manually reproduce the failing steps yourself
|
||||
- **DO NOT** try to fix things by running git commands or other manual operations
|
||||
- Both scripts are idempotent and designed to handle partial completion gracefully
|
||||
- If a script continues to fail after retrying, report the error to the user and wait for instructions
|
||||
2
.github/workflows/awaiting-manual.yml
vendored
2
.github/workflows/awaiting-manual.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Check awaiting-manual label
|
||||
id: check-awaiting-manual-label
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { labels, number: prNumber } = context.payload.pull_request;
|
||||
|
||||
2
.github/workflows/awaiting-mathlib.yml
vendored
2
.github/workflows/awaiting-mathlib.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Check awaiting-mathlib label
|
||||
id: check-awaiting-mathlib-label
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { labels, number: prNumber } = context.payload.pull_request;
|
||||
|
||||
4
.github/workflows/build-template.yml
vendored
4
.github/workflows/build-template.yml
vendored
@@ -116,10 +116,10 @@ jobs:
|
||||
build/stage1/**/*.ir
|
||||
build/stage1/**/*.c
|
||||
build/stage1/**/*.c.o*' || '' }}
|
||||
key: ${{ matrix.name }}-build-v3-${{ github.sha }}
|
||||
key: ${{ matrix.name }}-build-v4-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-build-v3
|
||||
${{ matrix.name }}-build-v4
|
||||
# open nix-shell once for initial setup
|
||||
- name: Setup
|
||||
run: |
|
||||
|
||||
2
.github/workflows/check-stage0.yml
vendored
2
.github/workflows/check-stage0.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
- if: github.event_name == 'pull_request'
|
||||
name: Set label
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo, number: issue_number } = context.issue;
|
||||
|
||||
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -137,7 +137,7 @@ jobs:
|
||||
|
||||
- name: Configure build matrix
|
||||
id: set-matrix
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const level = ${{ steps.set-level.outputs.check-level }};
|
||||
@@ -187,9 +187,10 @@ jobs:
|
||||
"name": "Linux Lake",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
|
||||
"check-level": 0,
|
||||
"test": true,
|
||||
"check-rebootstrap": level >= 1,
|
||||
"check-stage3": level >= 2,
|
||||
// only check-level >= 1 opts into tests implicitly. TODO: Clean up this logic.
|
||||
"test": true,
|
||||
// NOTE: `test-speedcenter` currently seems to be broken on `ubuntu-latest`
|
||||
"test-speedcenter": large && level >= 2,
|
||||
// made explicit until it can be assumed to have propagated to PRs
|
||||
@@ -213,8 +214,9 @@ jobs:
|
||||
},*/
|
||||
{
|
||||
"name": "macOS",
|
||||
"os": "macos-13",
|
||||
"os": "macos-15-intel",
|
||||
"release": true,
|
||||
"test": false, // Tier 2 platform
|
||||
"check-level": 2,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-apple-darwin.tar.zst",
|
||||
@@ -226,7 +228,7 @@ jobs:
|
||||
{
|
||||
"name": "macOS aarch64",
|
||||
// standard GH runner only comes with 7GB so use large runner if possible when running tests
|
||||
"os": large && !isPr ? "nscloud-macos-sonoma-arm64-6x14" : "macos-14",
|
||||
"os": large && !isPr ? "nscloud-macos-sequoia-arm64-6x14" : "macos-15",
|
||||
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
|
||||
"release": true,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
@@ -350,7 +352,7 @@ jobs:
|
||||
content: |
|
||||
A build of `${{ github.ref_name }}`, triggered by event `${{ github.event_name }}`, [failed](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
|
||||
- if: contains(needs.*.result, 'failure')
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Some jobs failed')
|
||||
@@ -367,7 +369,7 @@ jobs:
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
|
||||
with:
|
||||
files: artifacts/*/*
|
||||
fail_on_unmatched_files: true
|
||||
@@ -411,7 +413,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@72f2c25fcb47643c292f7107632f7a47c1df5cd8
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
|
||||
with:
|
||||
body_path: diff.md
|
||||
prerelease: true
|
||||
|
||||
2
.github/workflows/grove.yml
vendored
2
.github/workflows/grove.yml
vendored
@@ -110,7 +110,7 @@ jobs:
|
||||
# material.
|
||||
- id: deploy-alias
|
||||
if: ${{ steps.should-run.outputs.should-run == 'true' }}
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
name: Compute Alias
|
||||
with:
|
||||
result-encoding: string
|
||||
|
||||
2
.github/workflows/labels-from-comments.yml
vendored
2
.github/workflows/labels-from-comments.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Add label based on comment
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
||||
2
.github/workflows/pr-body.yml
vendored
2
.github/workflows/pr-body.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
- name: Check PR body
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { title, body, labels, draft } = context.payload.pull_request;
|
||||
|
||||
12
.github/workflows/pr-release.yml
vendored
12
.github/workflows/pr-release.yml
vendored
@@ -71,7 +71,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
- name: Release (short format)
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
|
||||
with:
|
||||
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# There are coredumps files here as well, but all in deeper subdirectories.
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
|
||||
- name: Release (SHA-suffixed format)
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
|
||||
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.
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
|
||||
- name: Report release status (short format)
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.repos.createCommitStatus({
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
|
||||
- name: Report release status (SHA-suffixed format)
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.repos.createCommitStatus({
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
|
||||
- name: Add label
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.addLabels({
|
||||
@@ -368,7 +368,7 @@ jobs:
|
||||
|
||||
- name: Report mathlib base
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' }}
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const description =
|
||||
|
||||
2
.github/workflows/pr-title.yml
vendored
2
.github/workflows/pr-title.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR title
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const msg = context.payload.pull_request? context.payload.pull_request.title : context.payload.merge_group.head_commit.message;
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
days-before-stale: -1
|
||||
days-before-pr-stale: 30
|
||||
|
||||
4
.github/workflows/update-stage0.yml
vendored
4
.github/workflows/update-stage0.yml
vendored
@@ -69,10 +69,10 @@ jobs:
|
||||
build/stage1/**/*.ir
|
||||
build/stage1/**/*.c
|
||||
build/stage1/**/*.c.o*
|
||||
key: Linux Lake-build-v3-${{ github.sha }}
|
||||
key: Linux Lake-build-v4-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
Linux Lake-build-v3
|
||||
Linux Lake-build-v4
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
# sync options with `Linux Lake` to ensure cache reuse
|
||||
run: |
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
/.github/ @kim-em
|
||||
/RELEASES.md @kim-em
|
||||
/src/kernel/ @leodemoura
|
||||
/src/library/compiler/ @zwarich
|
||||
/src/library/compiler/ @hargoniX
|
||||
/src/lake/ @tydeu
|
||||
/src/Lean/Compiler/ @leodemoura @zwarich
|
||||
/src/Lean/Compiler/ @leodemoura @hargoniX
|
||||
/src/Lean/Data/Lsp/ @mhuisi
|
||||
/src/Lean/Elab/Deriving/ @kim-em
|
||||
/src/Lean/Elab/Tactic/ @kim-em
|
||||
|
||||
@@ -59,7 +59,7 @@ All these tests are included by [src/shell/CMakeLists.txt](https://github.com/le
|
||||
open Foo in
|
||||
theorem tst2 (h : a ≤ b) : a + 2 ≤ b + 2 :=
|
||||
Bla.
|
||||
--^ textDocument/completion
|
||||
--^ completion
|
||||
```
|
||||
In this example, the test driver [`test_single.sh`](https://github.com/leanprover/lean4/tree/master/tests/lean/interactive/test_single.sh) will simulate an
|
||||
auto-completion request at `Bla.`. The expected output is stored in
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
} ({
|
||||
buildInputs = with pkgs; [
|
||||
cmake gmp libuv ccache pkg-config
|
||||
llvmPackages.bintools # wrapped lld
|
||||
llvmPackages.llvm # llvm-symbolizer for asan/lsan
|
||||
gdb
|
||||
tree # for CI
|
||||
|
||||
@@ -8,9 +8,14 @@
|
||||
},
|
||||
{
|
||||
"path": "tests"
|
||||
},
|
||||
{
|
||||
"path": "script"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
// Open terminal at root, not current workspace folder
|
||||
"terminal.integrated.cwd": "${workspaceFolder:.}",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"cmake.buildDirectory": "${workspaceFolder}/build/release",
|
||||
|
||||
75
script/Modulize.lean
Normal file
75
script/Modulize.lean
Normal file
@@ -0,0 +1,75 @@
|
||||
import Lake.CLI.Main
|
||||
|
||||
/-!
|
||||
|
||||
Usage: `lean --run script/Modulize.lean [--meta] file1.lean file2.lean ...`
|
||||
|
||||
A simple script that inserts `module` and `public section` into un-modulized files and
|
||||
bumps their imports to `public`.
|
||||
|
||||
When `--meta` is passed, `public meta section` and `public meta import` is used instead.
|
||||
-/
|
||||
|
||||
open Lean Parser.Module
|
||||
|
||||
def main (args : List String) : IO Unit := do
|
||||
let mut args := args
|
||||
let mut doMeta := false
|
||||
while !args.isEmpty && args[0]!.startsWith "-" do
|
||||
match args[0]! with
|
||||
| "--meta" => doMeta := true
|
||||
| arg => throw <| .userError s!"unknown flag '{arg}'"
|
||||
args := args.tail
|
||||
|
||||
for path in args do
|
||||
-- Parse the input file
|
||||
let mut text ← IO.FS.readFile path
|
||||
let inputCtx := Parser.mkInputContext text path
|
||||
let (header, parserState, msgs) ← Parser.parseHeader inputCtx
|
||||
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
|
||||
| throw <| .userError s!"unexpected header syntax of {path}"
|
||||
if moduleTk?.isSome then
|
||||
continue
|
||||
|
||||
-- initial whitespace if empty header
|
||||
let startPos := header.raw.getPos? |>.getD parserState.pos
|
||||
|
||||
let dummyEnv ← mkEmptyEnvironment
|
||||
let (initCmd, parserState', _) :=
|
||||
Parser.parseCommand inputCtx { env := dummyEnv, options := {} } parserState msgs
|
||||
|
||||
-- insert section if any trailing command
|
||||
if !initCmd.isOfKind ``Parser.Command.eoi then
|
||||
let insertPos? :=
|
||||
-- put below initial module docstring if any
|
||||
guard (initCmd.isOfKind ``Parser.Command.moduleDoc) *> initCmd.getTailPos? <|>
|
||||
-- else below header
|
||||
header.raw.getTailPos?
|
||||
let insertPos := insertPos?.getD startPos -- empty header
|
||||
let mut sec := if doMeta then
|
||||
"public meta section"
|
||||
else
|
||||
"@[expose] public section"
|
||||
if !imps.isEmpty then
|
||||
sec := "\n\n" ++ sec
|
||||
if insertPos?.isNone then
|
||||
sec := sec ++ "\n\n"
|
||||
text := text.extract 0 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.endPos
|
||||
|
||||
-- insert `module` header
|
||||
let mut initText := text.extract 0 startPos
|
||||
if !initText.trim.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.endPos
|
||||
|
||||
IO.FS.writeFile path text
|
||||
584
script/Shake.lean
Normal file
584
script/Shake.lean
Normal file
@@ -0,0 +1,584 @@
|
||||
/-
|
||||
Copyright (c) 2023 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro, Sebastian Ullrich
|
||||
-/
|
||||
import Lake.CLI.Main
|
||||
import Lean.ExtraModUses
|
||||
|
||||
/-! # `lake exe shake` command
|
||||
|
||||
This command will check the current project (or a specified target module) and all dependencies for
|
||||
unused imports. This works by looking at generated `.olean` files to deduce required imports and
|
||||
ensuring that every import is used to contribute some constant or other elaboration dependency
|
||||
recorded by `recordExtraModUse`. Because recompilation is not needed this is quite fast (about 8
|
||||
seconds to check `Mathlib` and all dependencies).
|
||||
-/
|
||||
|
||||
/-- help string for the command line interface -/
|
||||
def help : String := "Lean project tree shaking tool
|
||||
Usage: lake exe shake [OPTIONS] <MODULE>..
|
||||
|
||||
Arguments:
|
||||
<MODULE>
|
||||
A module path like `Mathlib`. All files transitively reachable from the
|
||||
provided module(s) will be checked.
|
||||
|
||||
Options:
|
||||
--force
|
||||
Skips the `lake build --no-build` sanity check
|
||||
|
||||
--fix
|
||||
Apply the suggested fixes directly. Make sure you have a clean checkout
|
||||
before running this, so you can review the changes.
|
||||
"
|
||||
|
||||
open Lean
|
||||
|
||||
/-- We use `Nat` as a bitset for doing efficient set operations.
|
||||
The bit indexes will usually be a module index. -/
|
||||
structure Bitset where
|
||||
toNat : Nat
|
||||
deriving Inhabited, DecidableEq, Repr
|
||||
|
||||
namespace Bitset
|
||||
|
||||
instance : EmptyCollection Bitset where
|
||||
emptyCollection := { toNat := 0 }
|
||||
|
||||
instance : Insert Nat Bitset where
|
||||
insert i s := { toNat := s.toNat ||| (1 <<< i) }
|
||||
|
||||
instance : Singleton Nat Bitset where
|
||||
singleton i := insert i ∅
|
||||
|
||||
instance : Inter Bitset where
|
||||
inter a b := { toNat := a.toNat &&& b.toNat }
|
||||
|
||||
instance : Union Bitset where
|
||||
union a b := { toNat := a.toNat ||| b.toNat }
|
||||
|
||||
instance : XorOp Bitset where
|
||||
xor a b := { toNat := a.toNat ^^^ b.toNat }
|
||||
|
||||
def has (s : Bitset) (i : Nat) : Bool := s ∩ {i} ≠ ∅
|
||||
|
||||
end Bitset
|
||||
|
||||
/-- The kind of a module dependency, corresponding to the homonymous `ExtraModUse` fields. -/
|
||||
structure NeedsKind where
|
||||
isExported : Bool
|
||||
isMeta : Bool
|
||||
deriving Inhabited, BEq, Repr, Hashable
|
||||
|
||||
namespace NeedsKind
|
||||
|
||||
@[match_pattern] abbrev priv : NeedsKind := { isExported := false, isMeta := false }
|
||||
@[match_pattern] abbrev pub : NeedsKind := { isExported := true, isMeta := false }
|
||||
@[match_pattern] abbrev metaPriv : NeedsKind := { isExported := false, isMeta := true }
|
||||
@[match_pattern] abbrev metaPub : NeedsKind := { isExported := true, isMeta := true }
|
||||
|
||||
def all : Array NeedsKind := #[pub, priv, metaPub, metaPriv]
|
||||
|
||||
def ofImport : Lean.Import → NeedsKind
|
||||
| { isExported := true, isMeta := true, .. } => .metaPub
|
||||
| { isExported := true, isMeta := false, .. } => .pub
|
||||
| { isExported := false, isMeta := true, .. } => .metaPriv
|
||||
| { isExported := false, isMeta := false, .. } => .priv
|
||||
|
||||
end NeedsKind
|
||||
|
||||
/-- Logically, a map `NeedsKind → Bitset`. -/
|
||||
structure Needs where
|
||||
pub : Bitset
|
||||
priv : Bitset
|
||||
metaPub : Bitset
|
||||
metaPriv : Bitset
|
||||
deriving Inhabited, Repr
|
||||
|
||||
def Needs.empty : Needs := default
|
||||
|
||||
def Needs.get (needs : Needs) (k : NeedsKind) : Bitset :=
|
||||
match k with
|
||||
| .pub => needs.pub
|
||||
| .priv => needs.priv
|
||||
| .metaPub => needs.metaPub
|
||||
| .metaPriv => needs.metaPriv
|
||||
|
||||
def Needs.has (needs : Needs) (k : NeedsKind) (i : ModuleIdx) : Bool :=
|
||||
needs.get k |>.has i
|
||||
|
||||
def Needs.set (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
|
||||
match k with
|
||||
| .pub => { needs with pub := s }
|
||||
| .priv => { needs with priv := s }
|
||||
| .metaPub => { needs with metaPub := s }
|
||||
| .metaPriv => { needs with metaPriv := s }
|
||||
|
||||
def Needs.modify (needs : Needs) (k : NeedsKind) (f : Bitset → Bitset) : Needs :=
|
||||
needs.set k (f (needs.get k))
|
||||
|
||||
def Needs.union (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
|
||||
needs.modify k (· ∪ s)
|
||||
|
||||
def Needs.sub (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
|
||||
needs.modify k (fun s' => s' ^^^ (s' ∩ s))
|
||||
|
||||
/-- The main state of the checker, containing information on all loaded modules. -/
|
||||
structure State where
|
||||
env : Environment
|
||||
/--
|
||||
`transDeps[i]` is the (non-reflexive) transitive closure of `mods[i].imports`. More specifically,
|
||||
* `j ∈ transDeps[i].pub` if `i -(public import)->+ j`
|
||||
* `j ∈ transDeps[i].priv` if `i -(import ...)-> _ -(public import)->* j`
|
||||
* `j ∈ transDeps[i].priv` if `i -(import all)->+ -(public import ...)-> _ -(public import)->* j`
|
||||
* `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import ...)->* j`
|
||||
* `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import ...)->* j`
|
||||
* `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ -(public meta import ...)-> _ -(public (meta)? import ...)->* j`
|
||||
-/
|
||||
transDeps : Array Needs := #[]
|
||||
/--
|
||||
`transDepsOrig` is the initial value of `transDeps` before changes potentially resulting from
|
||||
changes to upstream headers.
|
||||
-/
|
||||
transDepsOrig : Array Needs := #[]
|
||||
|
||||
def State.mods (s : State) := s.env.header.moduleData
|
||||
def State.modNames (s : State) := s.env.header.moduleNames
|
||||
|
||||
/--
|
||||
Given module `j`'s transitive dependencies, computes the union of `transImps` and the transitive
|
||||
dependencies resulting from importing the module via `imp` according to the rules of
|
||||
`State.transDeps`.
|
||||
-/
|
||||
def addTransitiveImps (transImps : Needs) (imp : Import) (j : Nat) (impTransImps : Needs) : Needs := Id.run do
|
||||
let mut transImps := transImps
|
||||
|
||||
-- `j ∈ transDeps[i].pub` if `i -(public import)->+ j`
|
||||
if imp.isExported && !imp.isMeta then
|
||||
transImps := transImps.union .pub {j} |>.union .pub (impTransImps.get .pub)
|
||||
|
||||
if !imp.isExported && !imp.isMeta then
|
||||
-- `j ∈ transDeps[i].priv` if `i -(import ...)-> _ -(public import)->* j`
|
||||
transImps := transImps.union .priv {j} |>.union .priv (impTransImps.get .pub)
|
||||
if imp.importAll then
|
||||
-- `j ∈ transDeps[i].priv` if `i -(import all)->+ -(public import ...)-> _ -(public import)->* j`
|
||||
transImps := transImps.union .priv (impTransImps.get .pub)
|
||||
|
||||
-- `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import ...)->* j`
|
||||
if imp.isExported then
|
||||
transImps := transImps.union .metaPub (impTransImps.get .metaPub)
|
||||
if imp.isMeta then
|
||||
transImps := transImps.union .metaPub {j} |>.union .metaPub (impTransImps.get .pub ∪ impTransImps.get .metaPub)
|
||||
|
||||
if !imp.isExported then
|
||||
if imp.isMeta then
|
||||
-- `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import ...)->* j`
|
||||
transImps := transImps.union .metaPriv {j} |>.union .metaPriv (impTransImps.get .pub ∪ impTransImps.get .metaPub)
|
||||
if imp.importAll then
|
||||
-- `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ -(public meta import ...)-> _ -(public (meta)? import ...)->* j`
|
||||
transImps := transImps.union .metaPriv (impTransImps.get .metaPub)
|
||||
|
||||
transImps
|
||||
|
||||
/-- Calculates the needs for a given module `mod` from constants and recorded extra uses. -/
|
||||
def calcNeeds (env : Environment) (i : ModuleIdx) : Needs := Id.run do
|
||||
let mut needs := default
|
||||
for ci in env.header.moduleData[i]!.constants do
|
||||
let pubCI? := env.setExporting true |>.find? ci.name
|
||||
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
|
||||
needs := visitExpr k ci.type needs
|
||||
if let some e := ci.value? (allowOpaque := true) then
|
||||
-- type and value has identical visibility under `meta`
|
||||
let k := if k.isMeta then k else
|
||||
if pubCI?.any (·.hasValue (allowOpaque := true)) then .pub else .priv
|
||||
needs := visitExpr k e needs
|
||||
|
||||
for use in getExtraModUses env i do
|
||||
let j := env.getModuleIdx? use.module |>.get!
|
||||
needs := needs.union { use with } {j}
|
||||
|
||||
return needs
|
||||
where
|
||||
/-- Accumulate the results from expression `e` into `deps`. -/
|
||||
visitExpr (k : NeedsKind) e deps :=
|
||||
Lean.Expr.foldConsts e deps fun c deps => match env.getModuleIdxFor? c with
|
||||
| some j =>
|
||||
let k := { k with isMeta := k.isMeta && !isMeta env c }
|
||||
if j != i then deps.union k {j} else deps
|
||||
| _ => deps
|
||||
|
||||
/--
|
||||
Calculates the same as `calcNeeds` but tracing each module to a use-def declaration pair or
|
||||
`none` if merely a recorded extra use.
|
||||
-/
|
||||
def getExplanations (env : Environment) (i : ModuleIdx) :
|
||||
Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name)) := Id.run do
|
||||
let mut deps := default
|
||||
for ci in env.header.moduleData[i]!.constants do
|
||||
let pubCI? := env.setExporting true |>.find? ci.name
|
||||
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
|
||||
deps := visitExpr k ci.name ci.type deps
|
||||
if let some e := ci.value? (allowOpaque := true) then
|
||||
let k := if k.isMeta then k else
|
||||
if pubCI?.any (·.hasValue (allowOpaque := true)) then .pub else .priv
|
||||
deps := visitExpr k ci.name e deps
|
||||
|
||||
for use in getExtraModUses env i do
|
||||
let j := env.getModuleIdx? use.module |>.get!
|
||||
if !deps.contains (j, { use with }) then
|
||||
deps := deps.insert (j, { use with }) none
|
||||
|
||||
return deps
|
||||
where
|
||||
/-- Accumulate the results from expression `e` into `deps`. -/
|
||||
visitExpr (k : NeedsKind) name e deps :=
|
||||
Lean.Expr.foldConsts e deps fun c deps => match env.getModuleIdxFor? c with
|
||||
| some i =>
|
||||
let k := { k with isMeta := k.isMeta && !isMeta env c }
|
||||
if
|
||||
if let some (some (name', _)) := deps[(i, k)]? then
|
||||
decide (name.toString.length < name'.toString.length)
|
||||
else true
|
||||
then
|
||||
deps.insert (i, k) (name, c)
|
||||
else
|
||||
deps
|
||||
| _ => deps
|
||||
|
||||
partial def initStateFromEnv (env : Environment) : State := Id.run do
|
||||
let mut s := { env }
|
||||
for i in 0...env.header.moduleData.size do
|
||||
let mod := env.header.moduleData[i]!
|
||||
let mut imps := #[]
|
||||
let mut transImps := Needs.empty
|
||||
for imp in mod.imports do
|
||||
let j := env.getModuleIdx? imp.module |>.get!
|
||||
imps := imps.push j
|
||||
transImps := addTransitiveImps transImps imp j s.transDeps[j]!
|
||||
s := { s with transDeps := s.transDeps.push transImps }
|
||||
s := { s with transDepsOrig := s.transDeps }
|
||||
return s
|
||||
|
||||
/-- The list of edits that will be applied in `--fix`. `edits[i] = (removed, added)` where:
|
||||
|
||||
* If `j ∈ removed` then we want to delete module named `j` from the imports of `i`
|
||||
* If `j ∈ added` then we want to add module index `j` to the imports of `i`.
|
||||
-/
|
||||
abbrev Edits := Std.HashMap Name (Array Import × Array Import)
|
||||
|
||||
/-- Register that we want to remove `tgt` from the imports of `src`. -/
|
||||
def Edits.remove (ed : Edits) (src : Name) (tgt : Import) : Edits :=
|
||||
match ed.get? src with
|
||||
| none => ed.insert src (#[tgt], #[])
|
||||
| some (a, b) => ed.insert src (a.push tgt, b)
|
||||
|
||||
/-- Register that we want to add `tgt` to the imports of `src`. -/
|
||||
def Edits.add (ed : Edits) (src : Name) (tgt : Import) : Edits :=
|
||||
match ed.get? src with
|
||||
| none => ed.insert src (#[], #[tgt])
|
||||
| some (a, b) => ed.insert src (a, b.push tgt)
|
||||
|
||||
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
|
||||
|
||||
Returns `(path, inputCtx, imports, endPos)` where `imports` is the `Lean.Parser.Module.import` list
|
||||
and `endPos` is the position of the end of the header.
|
||||
-/
|
||||
def parseHeaderFromString (text path : String) :
|
||||
IO (System.FilePath × Parser.InputContext ×
|
||||
TSyntaxArray ``Parser.Module.import × String.Pos) := do
|
||||
let inputCtx := Parser.mkInputContext text path
|
||||
let (header, parserState, msgs) ← Parser.parseHeader inputCtx
|
||||
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"
|
||||
-- the insertion point for `add` is the first newline after the imports
|
||||
let insertion := header.raw.getTailPos?.getD parserState.pos
|
||||
let insertion := text.findAux (· == '\n') text.endPos insertion + ⟨1⟩
|
||||
pure (path, inputCtx, .mk header.raw[2].getArgs, insertion)
|
||||
|
||||
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
|
||||
|
||||
Returns `(path, inputCtx, imports, endPos)` where `imports` is the `Lean.Parser.Module.import` list
|
||||
and `endPos` is the position of the end of the header.
|
||||
-/
|
||||
def parseHeader (srcSearchPath : SearchPath) (mod : Name) :
|
||||
IO (System.FilePath × Parser.InputContext ×
|
||||
TSyntaxArray ``Parser.Module.import × String.Pos) := do
|
||||
-- Parse the input file
|
||||
let some path ← srcSearchPath.findModuleWithExt "lean" mod
|
||||
| throw <| .userError s!"error: failed to find source file for {mod}"
|
||||
let text ← IO.FS.readFile path
|
||||
parseHeaderFromString text path.toString
|
||||
|
||||
def decodeImport : TSyntax ``Parser.Module.import → Import
|
||||
| `(Parser.Module.import| $[public%$pubTk?]? $[meta%$metaTk?]? import $[all%$allTk?]? $id) =>
|
||||
{ module := id.getId, isExported := pubTk?.isSome, isMeta := metaTk?.isSome, importAll := allTk?.isSome }
|
||||
| stx => panic! s!"unexpected syntax {stx}"
|
||||
|
||||
/-- Analyze and report issues from module `i`. Arguments:
|
||||
|
||||
* `srcSearchPath`: Used to find the path for error reporting purposes
|
||||
* `i`: the module index
|
||||
* `needs`: the module's calculated needs
|
||||
* `pinned`: dependencies that should be preserved even if unused
|
||||
* `edits`: accumulates the list of edits to apply if `--fix` is true
|
||||
* `addOnly`: if true, only add missing imports, do not remove unused ones
|
||||
-/
|
||||
def visitModule (srcSearchPath : SearchPath)
|
||||
(i : Nat) (needs : Needs) (preserve : Needs) (edits : Edits)
|
||||
(addOnly := false) (githubStyle := false) (explain := false) : StateT State IO Edits := do
|
||||
let s ← get
|
||||
-- Do transitive reduction of `needs` in `deps`.
|
||||
let mut deps := needs
|
||||
for j in [0:s.mods.size] do
|
||||
let transDeps := s.transDeps[j]!
|
||||
for k in NeedsKind.all do
|
||||
if s.transDepsOrig[i]!.has k j && preserve.has k j then
|
||||
deps := deps.union k {j}
|
||||
if deps.has k j then
|
||||
let transDeps := addTransitiveImps .empty { k with module := .anonymous } j transDeps
|
||||
for k' in NeedsKind.all do
|
||||
deps := deps.sub k' (transDeps.sub k' {j} |>.get k')
|
||||
|
||||
-- Any import which is not in `transDeps` was unused.
|
||||
-- Also accumulate `newDeps` which is the transitive closure of the remaining imports
|
||||
let mut toRemove : Array Import := #[]
|
||||
let mut newDeps := Needs.empty
|
||||
for imp in s.mods[i]!.imports do
|
||||
let j := s.env.getModuleIdx? imp.module |>.get!
|
||||
if
|
||||
-- skip folder-nested imports
|
||||
s.modNames[i]!.isPrefixOf imp.module ||
|
||||
imp.importAll then
|
||||
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
|
||||
else
|
||||
let k := NeedsKind.ofImport imp
|
||||
if !addOnly && !deps.has k j && !deps.has { k with isExported := false } j then
|
||||
toRemove := toRemove.push imp
|
||||
else
|
||||
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
|
||||
|
||||
-- If `newDeps` does not cover `deps`, then we have to add back some imports until it does.
|
||||
-- To minimize new imports we pick only new imports which are not transitively implied by
|
||||
-- another new import
|
||||
let mut toAdd : Array Import := #[]
|
||||
for j in [0:s.mods.size] do
|
||||
for k in NeedsKind.all do
|
||||
if deps.has k j && !newDeps.has k j && !newDeps.has { k with isExported := true } j then
|
||||
let imp := { k with module := s.modNames[j]! }
|
||||
toAdd := toAdd.push imp
|
||||
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
|
||||
|
||||
-- mark and report the removals
|
||||
let mut edits := toRemove.foldl (init := edits) fun edits imp =>
|
||||
edits.remove s.modNames[i]! imp
|
||||
|
||||
if !toAdd.isEmpty || !toRemove.isEmpty || explain then
|
||||
if let some path ← srcSearchPath.findModuleWithExt "lean" s.modNames[i]! then
|
||||
println! "{path}:"
|
||||
else
|
||||
println! "{s.modNames[i]!}:"
|
||||
|
||||
if !toRemove.isEmpty then
|
||||
println! " remove {toRemove}"
|
||||
|
||||
if githubStyle then
|
||||
try
|
||||
let (path, inputCtx, imports, endHeader) ← parseHeader srcSearchPath s.modNames[i]!
|
||||
for stx in imports do
|
||||
if toRemove.any fun imp => imp == decodeImport stx then
|
||||
let pos := inputCtx.fileMap.toPosition stx.raw.getPos?.get!
|
||||
println! "{path}:{pos.line}:{pos.column+1}: warning: unused import \
|
||||
(use `lake exe shake --fix` to fix this, or `lake exe shake --update` to ignore)"
|
||||
if !toAdd.isEmpty then
|
||||
-- we put the insert message on the beginning of the last import line
|
||||
let pos := inputCtx.fileMap.toPosition endHeader
|
||||
println! "{path}:{pos.line-1}:1: warning: \
|
||||
add {toAdd} instead"
|
||||
catch _ => pure ()
|
||||
|
||||
-- mark and report the additions
|
||||
edits := toAdd.foldl (init := edits) fun edits imp =>
|
||||
edits.add s.modNames[i]! imp
|
||||
|
||||
if !toAdd.isEmpty then
|
||||
println! " add {toAdd}"
|
||||
|
||||
-- recalculate transitive dependencies of downstream modules
|
||||
let mut newTransDepsI := Needs.empty
|
||||
for imp in s.mods[i]!.imports do
|
||||
if !toRemove.contains imp then
|
||||
let j := s.env.getModuleIdx? imp.module |>.get!
|
||||
newTransDepsI := addTransitiveImps newTransDepsI imp j s.transDeps[j]!
|
||||
for imp in toAdd do
|
||||
let j := s.env.getModuleIdx? imp.module |>.get!
|
||||
newTransDepsI := addTransitiveImps newTransDepsI imp j s.transDeps[j]!
|
||||
|
||||
set { s with transDeps := s.transDeps.set! i newTransDepsI }
|
||||
|
||||
if explain then
|
||||
let explanation := getExplanations s.env i
|
||||
let sanitize n := if n.hasMacroScopes then (sanitizeName n).run' { options := {} } else n
|
||||
let run (imp : Import) := do
|
||||
let j := s.env.getModuleIdx? imp.module |>.get!
|
||||
if let some exp? := explanation[(j, NeedsKind.ofImport imp)]? then
|
||||
println! " note: `{imp}` required"
|
||||
if let some (n, c) := exp? then
|
||||
println! " because `{sanitize n}` refers to `{sanitize c}`"
|
||||
else
|
||||
println! " because of additional compile-time dependencies"
|
||||
for j in s.mods[i]!.imports do
|
||||
if !toRemove.contains j then
|
||||
run j
|
||||
for i in toAdd do run i
|
||||
|
||||
return edits
|
||||
|
||||
/-- Convert a list of module names to a bitset of module indexes -/
|
||||
def toBitset (s : State) (ns : List Name) : Bitset :=
|
||||
ns.foldl (init := ∅) fun c name =>
|
||||
match s.env.getModuleIdxFor? name with
|
||||
| some i => c ∪ {i}
|
||||
| none => c
|
||||
|
||||
/-- The parsed CLI arguments. See `help` for more information -/
|
||||
structure Args where
|
||||
/-- `--help`: shows the help -/
|
||||
help : Bool := false
|
||||
/-- `--force`: skips the `lake build --no-build` sanity check -/
|
||||
force : Bool := false
|
||||
/-- `--gh-style`: output messages that can be parsed by `gh-problem-matcher-wrap` -/
|
||||
githubStyle : Bool := false
|
||||
/-- `--explain`: give constants explaining why each module is needed -/
|
||||
explain : Bool := false
|
||||
/-- `--fix`: apply the fixes directly -/
|
||||
fix : Bool := false
|
||||
/-- `<MODULE>..`: the list of root modules to check -/
|
||||
mods : Array Name := #[]
|
||||
|
||||
local instance : Ord Import where
|
||||
compare a b :=
|
||||
if a.isExported && !b.isExported then
|
||||
Ordering.lt
|
||||
else if !a.isExported && b.isExported then
|
||||
Ordering.gt
|
||||
else
|
||||
a.module.cmp b.module
|
||||
|
||||
/-- The main entry point. See `help` for more information on arguments. -/
|
||||
def main (args : List String) : IO UInt32 := do
|
||||
initSearchPath (← findSysroot)
|
||||
-- Parse the arguments
|
||||
let rec parseArgs (args : Args) : List String → Args
|
||||
| [] => args
|
||||
| "--help" :: rest => parseArgs { args with help := true } rest
|
||||
| "--force" :: rest => parseArgs { args with force := true } rest
|
||||
| "--fix" :: rest => parseArgs { args with fix := true } rest
|
||||
| "--explain" :: rest => parseArgs { args with explain := true } rest
|
||||
| "--gh-style" :: rest => parseArgs { args with githubStyle := true } rest
|
||||
| "--" :: rest => { args with mods := args.mods ++ rest.map (·.toName) }
|
||||
| other :: rest => parseArgs { args with mods := args.mods.push other.toName } rest
|
||||
let args := parseArgs {} args
|
||||
|
||||
-- Bail if `--help` is passed
|
||||
if args.help then
|
||||
IO.println help
|
||||
IO.Process.exit 0
|
||||
|
||||
if !args.force then
|
||||
if (← IO.Process.output { cmd := "lake", args := #["build", "--no-build"] }).exitCode != 0 then
|
||||
IO.println "There are out of date oleans. Run `lake build` or `lake exe cache get` first"
|
||||
IO.Process.exit 1
|
||||
|
||||
-- Determine default module(s) to run shake on
|
||||
let defaultTargetModules : Array Name ← try
|
||||
let (elanInstall?, leanInstall?, lakeInstall?) ← Lake.findInstall?
|
||||
let config ← Lake.MonadError.runEIO <| Lake.mkLoadConfig { elanInstall?, leanInstall?, lakeInstall? }
|
||||
let some workspace ← Lake.loadWorkspace config |>.toBaseIO
|
||||
| throw <| IO.userError "failed to load Lake workspace"
|
||||
let defaultTargetModules := workspace.root.defaultTargets.flatMap fun target =>
|
||||
if let some lib := workspace.root.findLeanLib? target then
|
||||
lib.roots
|
||||
else if let some exe := workspace.root.findLeanExe? target then
|
||||
#[exe.config.root]
|
||||
else
|
||||
#[]
|
||||
pure defaultTargetModules
|
||||
catch _ =>
|
||||
pure #[]
|
||||
|
||||
let srcSearchPath ← getSrcSearchPath
|
||||
-- the list of root modules
|
||||
let mods := if args.mods.isEmpty then defaultTargetModules else args.mods
|
||||
-- Only submodules of `pkg` will be edited or have info reported on them
|
||||
let pkg := mods[0]!.components.head!
|
||||
|
||||
-- Load all the modules
|
||||
let imps := mods.map ({ module := · })
|
||||
let (_, s) ← importModulesCore imps (isExported := true) |>.run
|
||||
let s := s.markAllExported
|
||||
let env ← finalizeImport s (isModule := true) imps {} (leakEnv := false) (loadExts := false)
|
||||
|
||||
StateT.run' (s := initStateFromEnv env) do
|
||||
|
||||
let s ← get
|
||||
-- Parse the config file
|
||||
|
||||
-- Run the calculation of the `needs` array in parallel
|
||||
let needs := s.mods.mapIdx fun i _ =>
|
||||
Task.spawn fun _ => calcNeeds s.env i
|
||||
|
||||
if args.fix then
|
||||
println! "The following changes will be made automatically:"
|
||||
|
||||
-- Check all selected modules
|
||||
let mut edits : Edits := ∅
|
||||
let mut revNeeds : Needs := default
|
||||
for i in [0:s.mods.size], t in needs do
|
||||
edits ← visitModule (addOnly := !pkg.isPrefixOf s.modNames[i]!) srcSearchPath i t.get revNeeds edits args.githubStyle args.explain
|
||||
if isExtraRevModUse s.env i then
|
||||
revNeeds := revNeeds.union .priv {i}
|
||||
|
||||
if !args.fix then
|
||||
-- return error if any issues were found
|
||||
return if edits.isEmpty then 0 else 1
|
||||
|
||||
-- Apply the edits to existing files
|
||||
let count ← edits.foldM (init := 0) fun count mod (remove, add) => do
|
||||
let add : Array Import := add.qsortOrd
|
||||
|
||||
-- Parse the input file
|
||||
let (path, inputCtx, imports, insertion) ←
|
||||
try parseHeader srcSearchPath mod
|
||||
catch e => println! e.toString; return count
|
||||
let text := inputCtx.fileMap.source
|
||||
|
||||
-- Calculate the edit result
|
||||
let mut pos : String.Pos := 0
|
||||
let mut out : String := ""
|
||||
let mut seen : Std.HashSet Import := {}
|
||||
for stx in imports do
|
||||
let mod := decodeImport stx
|
||||
if remove.contains mod || seen.contains mod then
|
||||
out := out ++ text.extract pos stx.raw.getPos?.get!
|
||||
-- We use the end position of the syntax, but include whitespace up to the first newline
|
||||
pos := text.findAux (· == '\n') text.endPos stx.raw.getTailPos?.get! + ⟨1⟩
|
||||
seen := seen.insert mod
|
||||
out := out ++ text.extract pos insertion
|
||||
for mod in add do
|
||||
if !seen.contains mod then
|
||||
seen := seen.insert mod
|
||||
out := out ++ s!"{mod}\n"
|
||||
out := out ++ text.extract insertion text.endPos
|
||||
|
||||
IO.FS.writeFile path out
|
||||
return count + 1
|
||||
|
||||
-- Since we throw an error upon encountering issues, we can be sure that everything worked
|
||||
-- if we reach this point of the script.
|
||||
if count > 0 then
|
||||
println! "Successfully applied {count} suggestions."
|
||||
else
|
||||
println! "No edits required."
|
||||
return 0
|
||||
9
script/lakefile.toml
Normal file
9
script/lakefile.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
name = "scripts"
|
||||
|
||||
[[lean_exe]]
|
||||
name = "modulize"
|
||||
root = "Modulize"
|
||||
|
||||
[[lean_exe]]
|
||||
name = "shake"
|
||||
root = "Shake"
|
||||
1
script/lean-toolchain
Normal file
1
script/lean-toolchain
Normal file
@@ -0,0 +1 @@
|
||||
lean4
|
||||
@@ -1,5 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Release Checklist for Lean4 and Downstream Repositories
|
||||
|
||||
This script validates the status of a Lean4 release across all dependent repositories.
|
||||
It checks whether repositories are ready for release and identifies missing steps.
|
||||
|
||||
IMPORTANT: Keep this documentation up-to-date when modifying the script's behavior!
|
||||
|
||||
What this script does:
|
||||
1. Validates preliminary Lean4 release infrastructure:
|
||||
- Checks that the release branch (releases/vX.Y.0) exists
|
||||
- Verifies CMake version settings are correct
|
||||
- Confirms the release tag exists
|
||||
- Validates the release page exists on GitHub
|
||||
- Checks the release notes page on lean-lang.org
|
||||
|
||||
2. For each downstream repository (batteries, mathlib4, etc.):
|
||||
- Checks if dependencies are ready (e.g., mathlib4 depends on batteries)
|
||||
- Verifies the main branch is on the target toolchain (or newer)
|
||||
- Checks if a PR exists to bump the toolchain (if not yet updated)
|
||||
- Validates tags exist for the release version
|
||||
- Ensures tags are merged into stable branches (for non-RC releases)
|
||||
- Verifies bump branches exist and are configured correctly
|
||||
- Special handling for ProofWidgets4 release tags
|
||||
|
||||
3. Optionally automates missing steps (when not in --dry-run mode):
|
||||
- Creates missing release tags using push_repo_release_tag.py
|
||||
- Merges tags into stable branches using merge_remote.py
|
||||
|
||||
Usage:
|
||||
./release_checklist.py v4.24.0 # Check release status
|
||||
./release_checklist.py v4.24.0 --verbose # Show detailed debug info
|
||||
./release_checklist.py v4.24.0 --dry-run # Check only, don't execute fixes
|
||||
|
||||
For automated release management with Claude Code:
|
||||
/release v4.24.0 # Run full release process with Claude
|
||||
|
||||
The script reads repository configurations from release_repos.yml and reports:
|
||||
- ✅ for completed requirements
|
||||
- ❌ for missing requirements (with instructions to fix)
|
||||
- 🟡 for repositories waiting on dependencies
|
||||
- ⮕ for automated actions being taken
|
||||
|
||||
This script is idempotent and safe to rerun multiple times.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import yaml
|
||||
import requests
|
||||
@@ -286,6 +332,68 @@ def check_bump_branch_toolchain(url, bump_branch, github_token):
|
||||
print(f" ✅ Bump branch correctly uses toolchain: {content}")
|
||||
return True
|
||||
|
||||
def get_pr_ci_status(repo_url, pr_number, github_token):
|
||||
"""Get the CI status for a pull request."""
|
||||
api_base = repo_url.replace("https://github.com/", "https://api.github.com/repos/")
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
|
||||
# Get PR details to find the head SHA
|
||||
pr_response = requests.get(f"{api_base}/pulls/{pr_number}", headers=headers)
|
||||
if pr_response.status_code != 200:
|
||||
return "unknown", "Could not fetch PR details"
|
||||
|
||||
pr_data = pr_response.json()
|
||||
head_sha = pr_data['head']['sha']
|
||||
|
||||
# Get check runs for the commit
|
||||
check_runs_response = requests.get(
|
||||
f"{api_base}/commits/{head_sha}/check-runs",
|
||||
headers=headers
|
||||
)
|
||||
|
||||
if check_runs_response.status_code != 200:
|
||||
return "unknown", "Could not fetch check runs"
|
||||
|
||||
check_runs_data = check_runs_response.json()
|
||||
check_runs = check_runs_data.get('check_runs', [])
|
||||
|
||||
if not check_runs:
|
||||
# No check runs, check for status checks (legacy)
|
||||
status_response = requests.get(
|
||||
f"{api_base}/commits/{head_sha}/status",
|
||||
headers=headers
|
||||
)
|
||||
if status_response.status_code == 200:
|
||||
status_data = status_response.json()
|
||||
state = status_data.get('state', 'unknown')
|
||||
if state == 'success':
|
||||
return "success", "All status checks passed"
|
||||
elif state == 'failure':
|
||||
return "failure", "Some status checks failed"
|
||||
elif state == 'pending':
|
||||
return "pending", "Status checks in progress"
|
||||
return "unknown", "No CI checks found"
|
||||
|
||||
# Analyze check runs
|
||||
conclusions = [run['conclusion'] for run in check_runs if run.get('status') == 'completed']
|
||||
in_progress = [run for run in check_runs if run.get('status') in ['queued', 'in_progress']]
|
||||
|
||||
if in_progress:
|
||||
return "pending", f"{len(in_progress)} check(s) in progress"
|
||||
|
||||
if not conclusions:
|
||||
return "pending", "Checks queued"
|
||||
|
||||
if all(c == 'success' for c in conclusions):
|
||||
return "success", f"All {len(conclusions)} checks passed"
|
||||
|
||||
failed = sum(1 for c in conclusions if c in ['failure', 'timed_out', 'action_required'])
|
||||
if failed > 0:
|
||||
return "failure", f"{failed} check(s) failed"
|
||||
|
||||
# Some checks are cancelled, skipped, or neutral
|
||||
return "warning", f"Some checks did not complete normally"
|
||||
|
||||
def pr_exists_with_title(repo_url, title, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + "/pulls"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
@@ -471,6 +579,19 @@ def main():
|
||||
if pr_info:
|
||||
pr_number, pr_url = pr_info
|
||||
print(f" ✅ PR with title '{pr_title}' exists: #{pr_number} ({pr_url})")
|
||||
|
||||
# Check CI status
|
||||
ci_status, ci_message = get_pr_ci_status(url, pr_number, github_token)
|
||||
if ci_status == "success":
|
||||
print(f" ✅ CI: {ci_message}")
|
||||
elif ci_status == "failure":
|
||||
print(f" ❌ CI: {ci_message}")
|
||||
elif ci_status == "pending":
|
||||
print(f" 🔄 CI: {ci_message}")
|
||||
elif ci_status == "warning":
|
||||
print(f" ⚠️ CI: {ci_message}")
|
||||
else:
|
||||
print(f" ❓ CI: {ci_message}")
|
||||
else:
|
||||
print(f" ❌ PR with title '{pr_title}' does not exist")
|
||||
print(f" Run `script/release_steps.py {toolchain} {name}` to create it")
|
||||
|
||||
@@ -1,30 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Execute release steps for Lean4 repositories.
|
||||
Execute Release Steps for Lean4 Downstream Repositories
|
||||
|
||||
This script helps automate the release process for Lean4 and its dependent repositories
|
||||
by actually executing the step-by-step instructions for updating toolchains, creating tags,
|
||||
and managing branches.
|
||||
This script automates the process of updating a downstream repository to a new Lean4 release.
|
||||
It handles creating branches, updating toolchains, merging changes, building, testing, and
|
||||
creating pull requests.
|
||||
|
||||
IMPORTANT: Keep this documentation up-to-date when modifying the script's behavior!
|
||||
|
||||
What this script does:
|
||||
1. Sets up the downstream_releases/ directory for cloning repositories
|
||||
|
||||
2. Clones or updates the target repository
|
||||
|
||||
3. Creates a branch named bump_to_{version} for the changes
|
||||
|
||||
4. Updates the lean-toolchain file to the target version
|
||||
|
||||
5. Handles repository-specific variations:
|
||||
- Different dependency update mechanisms
|
||||
- Special merging strategies for repositories with nightly-testing branches
|
||||
- Safety checks for repositories using bump branches
|
||||
- Custom build and test procedures
|
||||
|
||||
6. Commits the changes with message "chore: bump toolchain to {version}"
|
||||
|
||||
7. Builds the project (with a clean .lake cache)
|
||||
|
||||
8. Runs tests if available
|
||||
|
||||
9. Pushes the branch to GitHub
|
||||
|
||||
10. Creates a pull request (or reports if one already exists)
|
||||
|
||||
Usage:
|
||||
python3 release_steps.py <version> <repo>
|
||||
./release_steps.py v4.24.0 batteries # Update batteries to v4.24.0
|
||||
./release_steps.py v4.24.0-rc1 mathlib4 # Update mathlib4 to v4.24.0-rc1
|
||||
|
||||
Arguments:
|
||||
version: The version to set in the lean-toolchain file (e.g., v4.6.0)
|
||||
repo: The repository name as specified in release_repos.yml
|
||||
The script reads repository configurations from release_repos.yml.
|
||||
Each repository has specific handling for merging, dependencies, and testing.
|
||||
|
||||
Example:
|
||||
python3 release_steps.py v4.6.0 mathlib4
|
||||
python3 release_steps.py v4.6.0 batteries
|
||||
This script is idempotent - it's safe to rerun if it fails partway through.
|
||||
Existing branches, commits, and PRs will be reused rather than duplicated.
|
||||
|
||||
The script reads repository configurations from release_repos.yml in the same directory.
|
||||
Each repository may have specific requirements for:
|
||||
- Branch management
|
||||
- Toolchain updates
|
||||
- Dependency updates
|
||||
- Tagging conventions
|
||||
- Stable branch handling
|
||||
Error handling:
|
||||
- If build or tests fail, the script continues to create the PR anyway
|
||||
- Manual conflicts must be resolved by the user
|
||||
- Network issues during push/PR creation are reported with manual instructions
|
||||
"""
|
||||
|
||||
import argparse
|
||||
|
||||
@@ -34,7 +34,14 @@ if (NOT LEAN_PLATFORM_TARGET)
|
||||
OUTPUT_VARIABLE LEAN_PLATFORM_TARGET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
|
||||
set(LEAN_EXTRA_LINKER_FLAGS "" CACHE STRING "Additional flags used by the linker")
|
||||
set(LEAN_EXTRA_LINKER_FLAGS_DEFAULT "")
|
||||
# Use lld by default, if available
|
||||
find_program(LLD_PATH lld)
|
||||
if(LLD_PATH)
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS_DEFAULT " -fuse-ld=lld")
|
||||
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")
|
||||
set(LEAN_TEST_VARS "LEAN_CC=${CMAKE_C_COMPILER}" CACHE STRING "Additional environment variables used when running tests")
|
||||
|
||||
@@ -82,6 +89,7 @@ option(USE_MIMALLOC "use mimalloc" ON)
|
||||
# development-specific options
|
||||
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
|
||||
option(USE_LAKE "Use Lake instead of lean.mk for building core libs from language server" ON)
|
||||
option(USE_LAKE_CACHE "Use the Lake artifact cache for stage 1 builds (requires USE_LAKE)" OFF)
|
||||
|
||||
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
|
||||
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
|
||||
@@ -797,6 +805,9 @@ install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
|
||||
|
||||
# symlink source into expected installation location for go-to-definition, if file system allows it
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
|
||||
# get rid of all files in `src/lean` that may have been loaded from the cache
|
||||
# (at the time of writing this, this is the case for some lake test .c files)
|
||||
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/src/lean)
|
||||
if(${STAGE} EQUAL 0)
|
||||
file(CREATE_LINK ${CMAKE_SOURCE_DIR}/../../src ${CMAKE_BINARY_DIR}/src/lean RESULT _IGNORE_RES SYMBOLIC)
|
||||
else()
|
||||
@@ -823,10 +834,13 @@ if(LEAN_INSTALL_PREFIX)
|
||||
set(CMAKE_INSTALL_PREFIX "${LEAN_INSTALL_PREFIX}/lean-${LEAN_VERSION_STRING}${LEAN_INSTALL_SUFFIX}")
|
||||
endif()
|
||||
|
||||
if (STAGE GREATER 1)
|
||||
|
||||
if (USE_LAKE_CACHE AND STAGE EQUAL 1)
|
||||
set(LAKE_ARTIFACT_CACHE_TOML "true")
|
||||
else()
|
||||
# The build of stage2+ may depend on local changes made to src/ that are not reflected by the
|
||||
# commit hash in stage1/bin/lean, so we make sure to disable the global cache
|
||||
string(APPEND LEAN_EXTRA_LAKEFILE_TOML "\n\nenableArtifactCache = false")
|
||||
set(LAKE_ARTIFACT_CACHE_TOML "false")
|
||||
endif()
|
||||
|
||||
# Escape for `make`. Yes, twice.
|
||||
@@ -844,15 +858,13 @@ endfunction()
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_CC "${LEANC_CC}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_FLAGS "${LEANC_INTERNAL_FLAGS}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_LINKER_FLAGS "${LEANC_INTERNAL_LINKER_FLAGS}")
|
||||
set(LEANC_OPTS_TOML "${LEANC_OPTS} ${LEANC_EXTRA_CC_FLAGS} ${LEANC_INTERNAL_FLAGS}")
|
||||
set(LINK_OPTS_TOML "${LEANC_INTERNAL_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/lib/lean ${LEAN_EXTRA_LINKER_FLAGS}")
|
||||
|
||||
toml_escape("${LEAN_EXTRA_MAKE_OPTS}" LEAN_EXTRA_OPTS_TOML)
|
||||
toml_escape("${LEANC_OPTS_TOML}" LEANC_OPTS_TOML)
|
||||
toml_escape("${LINK_OPTS_TOML}" LINK_OPTS_TOML)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(LAKE_LIB_PREFIX "lib")
|
||||
if(${CMAKE_BUILD_TYPE} MATCHES "Debug|Release|RelWithDebInfo|MinSizeRel")
|
||||
set(CMAKE_BUILD_TYPE_TOML "${CMAKE_BUILD_TYPE}")
|
||||
else()
|
||||
set(CMAKE_BUILD_TYPE_TOML "Release")
|
||||
endif()
|
||||
|
||||
if(USE_LAKE)
|
||||
|
||||
@@ -8,7 +8,7 @@ module
|
||||
prelude
|
||||
public import Init.PropLemmas
|
||||
|
||||
public section
|
||||
@[expose] public section
|
||||
|
||||
universe u v
|
||||
|
||||
|
||||
@@ -16,5 +16,3 @@ public import Init.Control.Option
|
||||
public import Init.Control.Lawful
|
||||
public import Init.Control.StateCps
|
||||
public import Init.Control.ExceptCps
|
||||
|
||||
public section
|
||||
|
||||
@@ -10,5 +10,3 @@ 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 section
|
||||
|
||||
@@ -252,6 +252,7 @@ instance : LawfulMonad Id := by
|
||||
@[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] 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
|
||||
|
||||
@@ -9,6 +9,8 @@ prelude
|
||||
public import Init.Control.Lawful.Basic
|
||||
public import Init.Control.Except
|
||||
import all Init.Control.Except
|
||||
public import Init.Control.Option
|
||||
import all Init.Control.Option
|
||||
public import Init.Control.State
|
||||
import all Init.Control.State
|
||||
public import Init.Control.StateRef
|
||||
@@ -110,6 +112,121 @@ instance : LawfulMonad (Except ε) := LawfulMonad.mk'
|
||||
instance : LawfulApplicative (Except ε) := inferInstance
|
||||
instance : LawfulFunctor (Except ε) := inferInstance
|
||||
|
||||
/-! # OptionT -/
|
||||
|
||||
namespace OptionT
|
||||
|
||||
@[ext] theorem ext {x y : OptionT m α} (h : x.run = y.run) : x = y := by
|
||||
simp [run] at h
|
||||
assumption
|
||||
|
||||
@[simp, grind =] theorem run_mk {m : Type u → Type v} (x : m (Option α)) :
|
||||
OptionT.run (OptionT.mk x) = x := by rfl
|
||||
|
||||
@[simp, grind =] theorem run_pure [Monad m] (x : α) : run (pure x : OptionT m α) = pure (some x) := by
|
||||
simp [run, pure, OptionT.pure, OptionT.mk]
|
||||
|
||||
@[simp, grind =] theorem run_lift [Monad.{u, v} m] (x : m α) : run (OptionT.lift x : OptionT m α) = (return some (← x) : m (Option α)) := by
|
||||
simp [run, OptionT.lift, OptionT.mk]
|
||||
|
||||
@[simp, grind =] theorem run_throw [Monad m] : run (throw e : OptionT m β) = pure none := by
|
||||
simp [run, throw, throwThe, MonadExceptOf.throw, OptionT.fail, OptionT.mk]
|
||||
|
||||
@[simp, grind =] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α → OptionT m β) : run (OptionT.lift x >>= f : OptionT m β) = x >>= fun a => run (f a) := by
|
||||
simp [OptionT.run, OptionT.lift, bind, OptionT.bind, OptionT.mk]
|
||||
|
||||
@[simp, grind =] theorem bind_throw [Monad m] [LawfulMonad m] (f : α → OptionT m β) : (throw e >>= f) = throw e := by
|
||||
simp [throw, throwThe, MonadExceptOf.throw, bind, OptionT.bind, OptionT.mk, OptionT.fail]
|
||||
|
||||
@[simp, grind =] theorem run_bind (f : α → OptionT m β) [Monad m] :
|
||||
(x >>= f).run = Option.elimM x.run (pure none) (fun x => (f x).run) := by
|
||||
change x.run >>= _ = _
|
||||
simp [Option.elimM]
|
||||
exact bind_congr fun |some _ => rfl | none => rfl
|
||||
|
||||
@[simp, grind =] theorem lift_pure [Monad m] [LawfulMonad m] {α : Type u} (a : α) : OptionT.lift (pure a : m α) = pure a := by
|
||||
simp only [OptionT.lift, OptionT.mk, bind_pure_comp, map_pure, pure, OptionT.pure]
|
||||
|
||||
@[simp, grind =] theorem run_map [Monad m] [LawfulMonad m] (f : α → β) (x : OptionT m α)
|
||||
: (f <$> x).run = Option.map f <$> x.run := by
|
||||
simp [Functor.map, Option.map, ←bind_pure_comp]
|
||||
apply bind_congr
|
||||
intro a; cases a <;> simp [OptionT.pure, OptionT.mk]
|
||||
|
||||
protected theorem seq_eq {α β : Type u} [Monad m] (mf : OptionT m (α → β)) (x : OptionT m α) : mf <*> x = mf >>= fun f => f <$> x :=
|
||||
rfl
|
||||
|
||||
protected theorem bind_pure_comp [Monad m] (f : α → β) (x : OptionT m α) : x >>= pure ∘ f = f <$> x := by
|
||||
intros; rfl
|
||||
|
||||
protected theorem seqLeft_eq {α β : Type u} {m : Type u → Type v} [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) : x <* y = const β <$> x <*> y := by
|
||||
change (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
|
||||
rw [← OptionT.bind_pure_comp]
|
||||
apply ext
|
||||
simp [Option.elimM, Option.elim]
|
||||
apply bind_congr
|
||||
intro
|
||||
| none => simp
|
||||
| some _ =>
|
||||
simp [←bind_pure_comp]; apply bind_congr; intro b;
|
||||
cases b <;> simp [const]
|
||||
|
||||
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) : x *> y = const α id <$> x <*> y := by
|
||||
change (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
|
||||
rw [← OptionT.bind_pure_comp]
|
||||
apply ext
|
||||
simp [Option.elimM, Option.elim]
|
||||
apply bind_congr
|
||||
intro a; cases a <;> simp
|
||||
|
||||
instance [Monad m] [LawfulMonad m] : LawfulMonad (OptionT m) where
|
||||
id_map := by intros; apply ext; simp
|
||||
map_const := by intros; rfl
|
||||
seqLeft_eq := OptionT.seqLeft_eq
|
||||
seqRight_eq := OptionT.seqRight_eq
|
||||
pure_seq := by intros; apply ext; simp [OptionT.seq_eq, Option.elimM, Option.elim]
|
||||
bind_pure_comp := OptionT.bind_pure_comp
|
||||
bind_map := by intros; rfl
|
||||
pure_bind := by intros; apply ext; simp [Option.elimM, Option.elim]
|
||||
bind_assoc := by intros; apply ext; simp [Option.elimM, Option.elim]; apply bind_congr; intro a; cases a <;> simp
|
||||
|
||||
@[simp] theorem run_seq [Monad m] [LawfulMonad m] (f : OptionT m (α → β)) (x : OptionT m α) :
|
||||
(f <*> x).run = Option.elimM f.run (pure none) (fun f => Option.map f <$> x.run) := by
|
||||
simp [seq_eq_bind, Option.elimM, Option.elim]
|
||||
|
||||
@[simp] theorem run_seqLeft [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) :
|
||||
(x <* y).run = Option.elimM x.run (pure none)
|
||||
(fun x => Option.map (Function.const β x) <$> y.run) := by
|
||||
simp [seqLeft_eq, seq_eq_bind, Option.elimM, OptionT.run_bind]
|
||||
|
||||
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) :
|
||||
(x *> y).run = Option.elimM x.run (pure none) (Function.const α y.run) := by
|
||||
simp only [seqRight_eq, run_seq, Option.elimM, run_map, Option.elim, bind_map_left]
|
||||
refine bind_congr (fun | some _ => by simp | none => by simp)
|
||||
|
||||
@[simp, grind =] theorem run_failure [Monad m] : (failure : OptionT m α).run = pure none := by rfl
|
||||
|
||||
@[simp] theorem map_failure [Monad m] [LawfulMonad m] {α β : Type _} (f : α → β) :
|
||||
f <$> (failure : OptionT m α) = (failure : OptionT m β) := by
|
||||
simp [OptionT.mk, Functor.map, Alternative.failure, OptionT.fail, OptionT.bind]
|
||||
|
||||
@[simp] theorem run_orElse [Monad m] (x : OptionT m α) (y : OptionT m α) :
|
||||
(x <|> y).run = Option.elimM x.run y.run (fun x => pure (some x)) :=
|
||||
bind_congr fun | some _ => by rfl | none => by rfl
|
||||
|
||||
end OptionT
|
||||
|
||||
/-! # Option -/
|
||||
|
||||
instance : LawfulMonad Option := LawfulMonad.mk'
|
||||
(id_map := fun x => by cases x <;> rfl)
|
||||
(pure_bind := fun _ _ => by rfl)
|
||||
(bind_assoc := fun a _ _ => by cases a <;> rfl)
|
||||
(bind_pure_comp := bind_pure_comp)
|
||||
|
||||
instance : LawfulApplicative Option := inferInstance
|
||||
instance : LawfulFunctor Option := inferInstance
|
||||
|
||||
/-! # ReaderT -/
|
||||
|
||||
namespace ReaderT
|
||||
|
||||
@@ -9,5 +9,3 @@ prelude
|
||||
public import Init.Control.Lawful.MonadLift.Basic
|
||||
public import Init.Control.Lawful.MonadLift.Lemmas
|
||||
public import Init.Control.Lawful.MonadLift.Instances
|
||||
|
||||
public section
|
||||
|
||||
@@ -64,10 +64,6 @@ namespace OptionT
|
||||
|
||||
variable [Monad m] [LawfulMonad m]
|
||||
|
||||
@[simp]
|
||||
theorem lift_pure {α : Type u} (a : α) : OptionT.lift (pure a : m α) = pure a := by
|
||||
simp only [OptionT.lift, OptionT.mk, bind_pure_comp, map_pure, pure, OptionT.pure]
|
||||
|
||||
@[simp]
|
||||
theorem lift_bind {α β : Type u} (ma : m α) (f : α → m β) :
|
||||
OptionT.lift (ma >>= f) = OptionT.lift ma >>= (fun a => OptionT.lift (f a)) := by
|
||||
|
||||
@@ -39,13 +39,14 @@ variable {m : Type u → Type v} [Monad m] {α β : Type u}
|
||||
Converts an action that returns an `Option` into one that might fail, with `none` indicating
|
||||
failure.
|
||||
-/
|
||||
@[always_inline, inline, expose]
|
||||
protected def mk (x : m (Option α)) : OptionT m α :=
|
||||
x
|
||||
|
||||
/--
|
||||
Sequences two potentially-failing actions. The second action is run only if the first succeeds.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[always_inline, inline, expose]
|
||||
protected def bind (x : OptionT m α) (f : α → OptionT m β) : OptionT m β := OptionT.mk do
|
||||
match (← x) with
|
||||
| some a => f a
|
||||
@@ -54,7 +55,7 @@ protected def bind (x : OptionT m α) (f : α → OptionT m β) : OptionT m β :
|
||||
/--
|
||||
Succeeds with the provided value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[always_inline, inline, expose]
|
||||
protected def pure (a : α) : OptionT m α := OptionT.mk do
|
||||
pure (some a)
|
||||
|
||||
|
||||
@@ -144,8 +144,9 @@ Computed values are cached, so the value is not recomputed.
|
||||
x.fn ()
|
||||
|
||||
-- Ensure `Thunk.fn` is still computable even if it shouldn't be accessed directly.
|
||||
@[inline] private def Thunk.fnImpl (x : Thunk α) : Unit → α := fun _ => x.get
|
||||
@[csimp] private theorem Thunk.fn_eq_fnImpl : @Thunk.fn = @Thunk.fnImpl := rfl
|
||||
/-- Implementation detail. -/
|
||||
@[inline] def Thunk.fnImpl (x : Thunk α) : Unit → α := fun _ => x.get
|
||||
@[csimp] theorem Thunk.fn_eq_fnImpl : @Thunk.fn = @Thunk.fnImpl := rfl
|
||||
|
||||
/--
|
||||
Constructs a new thunk that forces `x` and then applies `x` to the result. Upon forcing, the result
|
||||
@@ -1605,7 +1606,7 @@ gen_injective_theorems% PSigma
|
||||
gen_injective_theorems% PSum
|
||||
gen_injective_theorems% Sigma
|
||||
gen_injective_theorems% String
|
||||
gen_injective_theorems% String.Pos
|
||||
gen_injective_theorems% String.Pos.Raw
|
||||
gen_injective_theorems% Substring
|
||||
gen_injective_theorems% Subtype
|
||||
gen_injective_theorems% Sum
|
||||
|
||||
@@ -30,6 +30,7 @@ public import Init.Data.Random
|
||||
public import Init.Data.ToString
|
||||
public import Init.Data.Range
|
||||
public import Init.Data.Hashable
|
||||
public import Init.Data.LawfulHashable
|
||||
public import Init.Data.OfScientific
|
||||
public import Init.Data.Format
|
||||
public import Init.Data.Stream
|
||||
@@ -52,5 +53,3 @@ public import Init.Data.Slice
|
||||
public import Init.Data.Order
|
||||
public import Init.Data.Rat
|
||||
public import Init.Data.Dyadic
|
||||
|
||||
public section
|
||||
|
||||
@@ -30,5 +30,3 @@ public import Init.Data.Array.Erase
|
||||
public import Init.Data.Array.Zip
|
||||
public import Init.Data.Array.InsertIdx
|
||||
public import Init.Data.Array.Extract
|
||||
|
||||
public section
|
||||
|
||||
@@ -84,10 +84,10 @@ well-founded recursion mechanism to prove that the function terminates.
|
||||
simp [pmap]
|
||||
|
||||
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
|
||||
@[inline] private def pmapImpl {P : α → Prop} (f : ∀ a, P a → β) (xs : Array α) (H : ∀ a ∈ xs, P a) :
|
||||
@[inline] def pmapImpl {P : α → Prop} (f : ∀ a, P a → β) (xs : Array α) (H : ∀ a ∈ xs, P a) :
|
||||
Array β := (xs.attachWith _ H).map fun ⟨x, h'⟩ => f x h'
|
||||
|
||||
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
|
||||
@[csimp] theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
|
||||
funext α β p f xs H
|
||||
cases xs
|
||||
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith_eq_pmap]
|
||||
@@ -95,16 +95,16 @@ well-founded recursion mechanism to prove that the function terminates.
|
||||
intro a m h₁ h₂
|
||||
congr
|
||||
|
||||
@[simp, grind =] theorem pmap_empty {P : α → Prop} (f : ∀ a, P a → β) : pmap f #[] (by simp) = #[] := rfl
|
||||
@[simp] theorem pmap_empty {P : α → Prop} (f : ∀ a, P a → β) : pmap f #[] (by simp) = #[] := rfl
|
||||
|
||||
@[simp, grind =] theorem pmap_push {P : α → Prop} (f : ∀ a, P a → β) (a : α) (xs : Array α) (h : ∀ b ∈ xs.push a, P b) :
|
||||
@[simp] theorem pmap_push {P : α → Prop} (f : ∀ a, P a → β) (a : α) (xs : Array α) (h : ∀ b ∈ xs.push a, P b) :
|
||||
pmap f (xs.push a) h =
|
||||
(pmap f xs (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp, grind =] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
|
||||
@[simp] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
|
||||
|
||||
@[simp, grind =] theorem attachWith_empty {P : α → Prop} (H : ∀ x ∈ #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
|
||||
@[simp] theorem attachWith_empty {P : α → Prop} (H : ∀ x ∈ #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
|
||||
|
||||
@[simp] theorem _root_.List.attachWith_mem_toArray {l : List α} :
|
||||
l.attachWith (fun x => x ∈ l.toArray) (fun x h => by simpa using h) =
|
||||
@@ -125,13 +125,11 @@ theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a,
|
||||
simp only [List.pmap_toArray, mk.injEq]
|
||||
rw [List.pmap_congr_left _ h]
|
||||
|
||||
@[grind =]
|
||||
theorem map_pmap {p : α → Prop} {g : β → γ} {f : ∀ a, p a → β} {xs : Array α} (H) :
|
||||
map g (pmap f xs H) = pmap (fun a h => g (f a h)) xs H := by
|
||||
cases xs
|
||||
simp [List.map_pmap]
|
||||
|
||||
@[grind =]
|
||||
theorem pmap_map {p : β → Prop} {g : ∀ b, p b → γ} {f : α → β} {xs : Array α} (H) :
|
||||
pmap g (map f xs) H = pmap (fun a h => g (f a) h) xs fun _ h => H _ (mem_map_of_mem h) := by
|
||||
cases xs
|
||||
@@ -147,14 +145,14 @@ theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H :
|
||||
subst w
|
||||
simp
|
||||
|
||||
@[simp, grind =] theorem attach_push {a : α} {xs : Array α} :
|
||||
@[simp] theorem attach_push {a : α} {xs : Array α} :
|
||||
(xs.push a).attach =
|
||||
(xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_push_of_mem a h⟩)).push ⟨a, by simp⟩ := by
|
||||
cases xs
|
||||
rw [attach_congr (List.push_toArray _ _)]
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp, grind =] theorem attachWith_push {a : α} {xs : Array α} {P : α → Prop} {H : ∀ x ∈ xs.push a, P x} :
|
||||
@[simp] theorem attachWith_push {a : α} {xs : Array α} {P : α → Prop} {H : ∀ x ∈ xs.push a, P x} :
|
||||
(xs.push a).attachWith P H =
|
||||
(xs.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push ⟨a, H a (by simp)⟩ := by
|
||||
cases xs
|
||||
@@ -288,25 +286,23 @@ theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
|
||||
xs.attach[i] = ⟨xs[i]'(by simpa using h), getElem_mem (by simpa using h)⟩ :=
|
||||
getElem_attachWith h
|
||||
|
||||
@[simp, grind =] theorem pmap_attach {xs : Array α} {p : {x // x ∈ xs} → Prop} {f : ∀ a, p a → β} (H) :
|
||||
@[simp] theorem pmap_attach {xs : Array α} {p : {x // x ∈ xs} → Prop} {f : ∀ a, p a → β} (H) :
|
||||
pmap f xs.attach H =
|
||||
xs.pmap (P := fun a => ∃ h : a ∈ xs, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨h, H ⟨a, h⟩ (by simp)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp, grind =] theorem pmap_attachWith {xs : Array α} {p : {x // q x} → Prop} {f : ∀ a, p a → β} (H₁ H₂) :
|
||||
@[simp] theorem pmap_attachWith {xs : Array α} {p : {x // q x} → Prop} {f : ∀ a, p a → β} (H₁ H₂) :
|
||||
pmap f (xs.attachWith q H₁) H₂ =
|
||||
xs.pmap (P := fun a => ∃ h : q a, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨H₁ _ h, H₂ ⟨a, H₁ _ h⟩ (by simpa)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
@[grind =]
|
||||
theorem foldl_pmap {xs : Array α} {P : α → Prop} {f : (a : α) → P a → β}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (g : γ → β → γ) (x : γ) :
|
||||
(xs.pmap f H).foldl g x = xs.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
|
||||
rw [pmap_eq_map_attach, foldl_map]
|
||||
|
||||
@[grind =]
|
||||
theorem foldr_pmap {xs : Array α} {P : α → Prop} {f : (a : α) → P a → β}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (g : β → γ → γ) (x : γ) :
|
||||
(xs.pmap f H).foldr g x = xs.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
|
||||
@@ -364,20 +360,18 @@ theorem foldr_attach {xs : Array α} {f : α → β → β} {b : β} :
|
||||
ext
|
||||
simpa using fun a => List.mem_of_getElem? a
|
||||
|
||||
@[grind =]
|
||||
theorem attach_map {xs : Array α} {f : α → β} :
|
||||
(xs.map f).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem h⟩) := by
|
||||
cases xs
|
||||
ext <;> simp
|
||||
|
||||
@[grind =]
|
||||
theorem attachWith_map {xs : Array α} {f : α → β} {P : β → Prop} (H : ∀ (b : β), b ∈ xs.map f → P b) :
|
||||
(xs.map f).attachWith P H = (xs.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem h))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
cases xs
|
||||
simp [List.attachWith_map]
|
||||
|
||||
@[simp, grind =] theorem map_attachWith {xs : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ xs → P a}
|
||||
@[simp] theorem map_attachWith {xs : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ xs → P a}
|
||||
{f : { x // P x } → β} :
|
||||
(xs.attachWith P H).map f = xs.attach.map fun ⟨x, h⟩ => f ⟨x, H _ h⟩ := by
|
||||
cases xs <;> simp_all
|
||||
@@ -430,7 +424,6 @@ theorem filter_attachWith {q : α → Prop} {xs : Array α} {p : {x // q x} →
|
||||
cases xs
|
||||
simp [Function.comp_def, List.filter_map]
|
||||
|
||||
@[grind =]
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f : ∀ b, q b → γ} {xs} (H₁ H₂) :
|
||||
pmap f (pmap g xs H₁) H₂ =
|
||||
pmap (α := { x // x ∈ xs }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) xs.attach
|
||||
@@ -438,7 +431,7 @@ theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f
|
||||
cases xs
|
||||
simp [List.pmap_pmap, List.pmap_map]
|
||||
|
||||
@[simp, grind =] theorem pmap_append {p : ι → Prop} {f : ∀ a : ι, p a → α} {xs ys : Array ι}
|
||||
@[simp] theorem pmap_append {p : ι → Prop} {f : ∀ a : ι, p a → α} {xs ys : Array ι}
|
||||
(h : ∀ a ∈ xs ++ ys, p a) :
|
||||
(xs ++ ys).pmap f h =
|
||||
(xs.pmap f fun a ha => h a (mem_append_left ys ha)) ++
|
||||
@@ -453,7 +446,7 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
|
||||
xs.pmap f h₁ ++ ys.pmap f h₂ :=
|
||||
pmap_append _
|
||||
|
||||
@[simp, grind =] theorem attach_append {xs ys : Array α} :
|
||||
@[simp] theorem attach_append {xs ys : Array α} :
|
||||
(xs ++ ys).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_append_left ys h⟩) ++
|
||||
ys.attach.map fun ⟨x, h⟩ => ⟨x, mem_append_right xs h⟩ := by
|
||||
cases xs
|
||||
@@ -461,62 +454,59 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
|
||||
rw [attach_congr (List.append_toArray _ _)]
|
||||
simp [List.attach_append, Function.comp_def]
|
||||
|
||||
@[simp, grind =] theorem attachWith_append {P : α → Prop} {xs ys : Array α}
|
||||
@[simp] theorem attachWith_append {P : α → Prop} {xs ys : Array α}
|
||||
{H : ∀ (a : α), a ∈ xs ++ ys → P a} :
|
||||
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_left ys h)) ++
|
||||
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp, grind =] theorem pmap_reverse {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
@[simp] theorem pmap_reverse {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs.reverse → P a) :
|
||||
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
|
||||
induction xs <;> simp_all
|
||||
|
||||
@[grind =]
|
||||
theorem reverse_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
|
||||
rw [pmap_reverse]
|
||||
|
||||
@[simp, grind =] theorem attachWith_reverse {P : α → Prop} {xs : Array α}
|
||||
@[simp] theorem attachWith_reverse {P : α → Prop} {xs : Array α}
|
||||
{H : ∀ (a : α), a ∈ xs.reverse → P a} :
|
||||
xs.reverse.attachWith P H =
|
||||
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[grind =]
|
||||
theorem reverse_attachWith {P : α → Prop} {xs : Array α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp, grind =] theorem attach_reverse {xs : Array α} :
|
||||
@[simp] theorem attach_reverse {xs : Array α} :
|
||||
xs.reverse.attach = xs.attach.reverse.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases xs
|
||||
rw [attach_congr List.reverse_toArray]
|
||||
simp
|
||||
|
||||
@[grind =]
|
||||
theorem reverse_attach {xs : Array α} :
|
||||
xs.attach.reverse = xs.reverse.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp, grind =] theorem back?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
@[simp] theorem back?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).back? = xs.attach.back?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp, grind =] theorem back?_attachWith {P : α → Prop} {xs : Array α}
|
||||
@[simp] theorem back?_attachWith {P : α → Prop} {xs : Array α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some ⟨a, H _ (mem_of_back? h)⟩) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp, grind =]
|
||||
@[simp]
|
||||
theorem back?_attach {xs : Array α} :
|
||||
xs.attach.back? = xs.back?.pbind fun a h => some ⟨a, mem_of_back? h⟩ := by
|
||||
cases xs
|
||||
|
||||
@@ -752,8 +752,7 @@ of results.
|
||||
def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m β) (as : Array α) : m (Array β) :=
|
||||
-- Note: we cannot use `foldlM` here for the reference implementation because this calls
|
||||
-- `bind` and `pure` too many times. (We are not assuming `m` is a `LawfulMonad`)
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
map (i : Nat) (bs : Array β) : m (Array β) := do
|
||||
let rec map (i : Nat) (bs : Array β) : m (Array β) := do
|
||||
if hlt : i < as.size then
|
||||
map (i+1) (bs.push (← f as[i]))
|
||||
else
|
||||
@@ -913,8 +912,7 @@ entire array is checked.
|
||||
@[implemented_by anyMUnsafe, expose]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
|
||||
let any (stop : Nat) (h : stop ≤ as.size) :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) : m Bool := do
|
||||
let rec loop (j : Nat) : m Bool := do
|
||||
if hlt : j < stop then
|
||||
have : j < as.size := Nat.lt_of_lt_of_le hlt h
|
||||
if (← p as[j]) then
|
||||
@@ -1252,8 +1250,7 @@ Examples:
|
||||
-/
|
||||
@[inline, expose]
|
||||
def findIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option Nat :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) :=
|
||||
let rec loop (j : Nat) :=
|
||||
if h : j < as.size then
|
||||
if p as[j] then some j else loop (j + 1)
|
||||
else none
|
||||
@@ -1270,8 +1267,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def findFinIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option (Fin as.size) :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) :=
|
||||
let rec loop (j : Nat) :=
|
||||
if h : j < as.size then
|
||||
if p as[j] then some ⟨j, h⟩ else loop (j + 1)
|
||||
else none
|
||||
@@ -1307,7 +1303,6 @@ Examples:
|
||||
@[inline, expose]
|
||||
def findIdx (p : α → Bool) (as : Array α) : Nat := (as.findIdx? p).getD as.size
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def idxOfAux [BEq α] (xs : Array α) (v : α) (i : Nat) : Option (Fin xs.size) :=
|
||||
if h : i < xs.size then
|
||||
if xs[i] == v then some ⟨i, h⟩
|
||||
@@ -1717,7 +1712,6 @@ Examples:
|
||||
* `#[3, 2, 3, 4].popWhile (· > 2) = #[3, 2]`
|
||||
* `(#[] : Array Nat).popWhile (· > 2) = #[]`
|
||||
-/
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
if h : as.size > 0 then
|
||||
if p (as[as.size - 1]'(Nat.sub_lt h (by decide))) then
|
||||
@@ -1742,8 +1736,7 @@ Examples:
|
||||
* `#[0, 1, 2, 3, 2, 1].takeWhile (· < 0) = #[]`
|
||||
-/
|
||||
def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
go (i : Nat) (acc : Array α) : Array α :=
|
||||
let rec go (i : Nat) (acc : Array α) : Array α :=
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
if p a then
|
||||
@@ -1766,7 +1759,6 @@ Examples:
|
||||
* `#["apple", "pear", "orange"].eraseIdx 1 = #["apple", "orange"]`
|
||||
* `#["apple", "pear", "orange"].eraseIdx 2 = #["apple", "pear"]`
|
||||
-/
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def eraseIdx (xs : Array α) (i : Nat) (h : i < xs.size := by get_elem_tactic) : Array α :=
|
||||
if h' : i + 1 < xs.size then
|
||||
let xs' := xs.swap (i + 1) i
|
||||
@@ -1861,8 +1853,7 @@ Examples:
|
||||
* `#["tues", "thur", "sat"].insertIdx 3 "wed" = #["tues", "thur", "sat", "wed"]`
|
||||
-/
|
||||
@[inline] def insertIdx (as : Array α) (i : Nat) (a : α) (_ : i ≤ as.size := by get_elem_tactic) : Array α :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (as : Array α) (j : Fin as.size) :=
|
||||
let rec loop (as : Array α) (j : Fin as.size) :=
|
||||
if i < j then
|
||||
let j' : Fin as.size := ⟨j-1, Nat.lt_of_le_of_lt (Nat.pred_le _) j.2⟩
|
||||
let as := as.swap j' j
|
||||
@@ -1916,7 +1907,6 @@ def insertIdxIfInBounds (as : Array α) (i : Nat) (a : α) : Array α :=
|
||||
else
|
||||
as
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
@@ -1945,7 +1935,7 @@ def isPrefixOf [BEq α] (as bs : Array α) : Bool :=
|
||||
else
|
||||
false
|
||||
|
||||
@[semireducible, specialize] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
@[specialize]
|
||||
def zipWithMAux {m : Type v → Type w} [Monad m] (as : Array α) (bs : Array β) (f : α → β → m γ) (i : Nat) (cs : Array γ) : m (Array γ) := do
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
@@ -2108,7 +2098,6 @@ private def allDiffAuxAux [BEq α] (as : Array α) (a : α) : forall (i : Nat),
|
||||
have : i < as.size := Nat.lt_trans (Nat.lt_succ_self _) h;
|
||||
a != as[i] && allDiffAuxAux as a i this
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
|
||||
|
||||
@@ -63,7 +63,7 @@ theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + coun
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.length_eq_countP_add_countP (p := p)]
|
||||
|
||||
@[grind _=_]
|
||||
@[grind =]
|
||||
theorem countP_eq_size_filter {xs : Array α} : countP p xs = (filter p xs).size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.countP_eq_length_filter]
|
||||
|
||||
@@ -8,5 +8,3 @@ module
|
||||
prelude
|
||||
public import Init.Data.Array.Lex.Basic
|
||||
public import Init.Data.Array.Lex.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
@@ -31,7 +31,7 @@ Specifically, `Array.lex as bs lt` is true if
|
||||
def lex [BEq α] (as bs : Array α) (lt : α → α → Bool := by exact (· < ·)) : Bool := Id.run do
|
||||
for h : i in 0...(min as.size bs.size) do
|
||||
-- TODO: `get_elem_tactic` should be able to find this itself.
|
||||
have : i < min as.size bs.size := Std.PRange.lt_upper_of_mem h
|
||||
have : i < min as.size bs.size := Std.Rco.lt_upper_of_mem h
|
||||
if lt as[i] bs[i] then
|
||||
return true
|
||||
else if as[i] != bs[i] then
|
||||
|
||||
@@ -42,8 +42,7 @@ protected theorem not_le_iff_gt [LT α] {xs ys : Array α} :
|
||||
Classical.not_not
|
||||
|
||||
@[simp] theorem lex_empty [BEq α] {lt : α → α → Bool} {xs : Array α} : xs.lex #[] lt = false := by
|
||||
rw [lex, Std.PRange.forIn'_eq_match]
|
||||
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
|
||||
simp [lex, Std.Rco.forIn'_eq_if]
|
||||
|
||||
private theorem cons_lex_cons.forIn'_congr_aux [Monad m] {as bs : ρ} {_ : Membership α ρ}
|
||||
[ForIn' m ρ α inferInstance] (w : as = bs)
|
||||
@@ -64,13 +63,13 @@ private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs
|
||||
(#[a] ++ xs).lex (#[b] ++ ys) lt =
|
||||
(lt a b || a == b && xs.lex ys lt) := by
|
||||
simp only [lex, size_append, List.size_toArray, List.length_cons, List.length_nil, Nat.zero_add,
|
||||
Nat.add_min_add_left, Nat.add_lt_add_iff_left, Std.PRange.forIn'_eq_forIn'_toList]
|
||||
Nat.add_min_add_left, Nat.add_lt_add_iff_left, Std.Rco.forIn'_eq_forIn'_toList]
|
||||
conv =>
|
||||
lhs; congr; congr
|
||||
rw [cons_lex_cons.forIn'_congr_aux Std.PRange.toList_eq_match rfl (fun _ _ _ => rfl)]
|
||||
simp only [Std.PRange.SupportsUpperBound.IsSatisfied, bind_pure_comp, map_pure]
|
||||
rw [cons_lex_cons.forIn'_congr_aux Std.Rco.toList_eq_if rfl (fun _ _ _ => rfl)]
|
||||
simp only [bind_pure_comp, map_pure]
|
||||
rw [cons_lex_cons.forIn'_congr_aux (if_pos (by omega)) rfl (fun _ _ _ => rfl)]
|
||||
simp only [Std.PRange.toList_Rox_eq_toList_Rcx_of_isSome_succ? (lo := 0) (h := rfl),
|
||||
simp only [Std.toList_Roo_eq_toList_Rco_of_isSome_succ? (lo := 0) (h := rfl),
|
||||
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_Rco_succ_succ,
|
||||
Option.get_some, List.forIn'_cons, List.size_toArray, List.length_cons, List.length_nil,
|
||||
Nat.lt_add_one, getElem_append_left, List.getElem_toArray, List.getElem_cons_zero]
|
||||
@@ -83,16 +82,10 @@ private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs
|
||||
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by
|
||||
induction l₁ generalizing l₂ with
|
||||
| nil =>
|
||||
cases l₂
|
||||
· rw [lex, Std.PRange.forIn'_eq_match]
|
||||
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
|
||||
· rw [lex, Std.PRange.forIn'_eq_match]
|
||||
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
|
||||
cases l₂ <;> simp [lex, Std.Rco.forIn'_eq_if]
|
||||
| cons x l₁ ih =>
|
||||
cases l₂ with
|
||||
| nil =>
|
||||
rw [lex, Std.PRange.forIn'_eq_match]
|
||||
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
|
||||
| nil => simp [lex, Std.Rco.forIn'_eq_if]
|
||||
| cons y l₂ =>
|
||||
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
|
||||
|
||||
|
||||
@@ -7,5 +7,3 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Array.QSort.Basic
|
||||
|
||||
public section
|
||||
|
||||
@@ -16,5 +16,3 @@ public import Init.Data.UInt
|
||||
public import Init.Data.Repr
|
||||
public import Init.Data.ToString.Basic
|
||||
public import Init.Data.String.Extra
|
||||
|
||||
public section
|
||||
|
||||
@@ -13,5 +13,3 @@ public import Init.Data.BitVec.Bitblast
|
||||
public import Init.Data.BitVec.Decidable
|
||||
public import Init.Data.BitVec.Lemmas
|
||||
public import Init.Data.BitVec.Folds
|
||||
|
||||
public section
|
||||
|
||||
@@ -17,6 +17,7 @@ import all Init.Data.BitVec.Basic
|
||||
public import Init.Data.BitVec.Decidable
|
||||
public import Init.Data.BitVec.Lemmas
|
||||
public import Init.Data.BitVec.Folds
|
||||
import Init.BinderPredicates
|
||||
|
||||
@[expose] public section
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ prelude
|
||||
public import Init.Data.BitVec.Basic
|
||||
import all Init.Data.BitVec.Basic
|
||||
import Init.Data.Int.Bitwise.Lemmas
|
||||
import Init.Ext
|
||||
|
||||
public section
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.BitVec.Bootstrap
|
||||
import Init.Ext
|
||||
|
||||
public section
|
||||
|
||||
@@ -49,11 +50,11 @@ instance instDecidableForallBitVecSucc (P : BitVec (n+1) → Prop) [DecidablePre
|
||||
|
||||
instance instDecidableExistsBitVecZero (P : BitVec 0 → Prop) [Decidable (P 0#0)] :
|
||||
Decidable (∃ v, P v) :=
|
||||
decidable_of_iff (¬ ∀ v, ¬ P v) Classical.not_forall_not
|
||||
decidable_of_iff (¬ ∀ v, ¬ P v) (by exact Classical.not_forall_not)
|
||||
|
||||
instance instDecidableExistsBitVecSucc (P : BitVec (n+1) → Prop) [DecidablePred P]
|
||||
[Decidable (∀ (x : Bool) (v : BitVec n), ¬ P (v.cons x))] : Decidable (∃ v, P v) :=
|
||||
decidable_of_iff (¬ ∀ v, ¬ P v) Classical.not_forall_not
|
||||
decidable_of_iff (¬ ∀ v, ¬ P v) (by exact Classical.not_forall_not)
|
||||
|
||||
/--
|
||||
For small numerals this isn't necessary (as typeclass search can use the above two instances),
|
||||
|
||||
@@ -22,6 +22,9 @@ public import Init.Data.Int.Pow
|
||||
public import Init.Data.Int.LemmasAux
|
||||
public import Init.Data.BitVec.Bootstrap
|
||||
public import Init.Data.Order.Factories
|
||||
public import Init.Data.List.BasicAux
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.BEq
|
||||
|
||||
public section
|
||||
|
||||
@@ -3750,6 +3753,9 @@ theorem neg_add {x y : BitVec w} : - (x + y) = - x - y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg, toInt_add, Int.neg_add, Int.add_neg_eq_sub]
|
||||
|
||||
theorem sub_sub (a b c : BitVec n) : a - b - c = a - (b + c) := by
|
||||
simp [BitVec.sub_eq_add_neg, BitVec.add_assoc, BitVec.neg_add]
|
||||
|
||||
theorem add_neg_eq_sub {x y : BitVec w} : x + - y = (x - y) := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg, Int.sub_eq_add_neg]
|
||||
|
||||
@@ -10,5 +10,3 @@ public import Init.Data.ByteArray.Basic
|
||||
public import Init.Data.ByteArray.Bootstrap
|
||||
public import Init.Data.ByteArray.Extra
|
||||
public import Init.Data.ByteArray.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
@@ -13,6 +13,8 @@ import all Init.Data.UInt.BasicAux
|
||||
public import Init.Data.Option.Basic
|
||||
public import Init.Data.Array.Extract
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
@[expose] public section
|
||||
universe u
|
||||
|
||||
@@ -34,18 +36,40 @@ instance : Inhabited ByteArray where
|
||||
instance : EmptyCollection ByteArray where
|
||||
emptyCollection := ByteArray.empty
|
||||
|
||||
/--
|
||||
Retrieves the size of the array as a platform-specific fixed-width integer.
|
||||
|
||||
Because {name}`USize` is big enough to address all memory on every platform that Lean supports,
|
||||
there are in practice no {name}`ByteArray`s that have more elements that {name}`USize` can count.
|
||||
-/
|
||||
@[extern "lean_sarray_size", simp]
|
||||
def usize (a : @& ByteArray) : USize :=
|
||||
a.size.toUSize
|
||||
|
||||
/--
|
||||
Retrieves the byte at the indicated index. Callers must prove that the index is in bounds. The index
|
||||
is represented by a platform-specific fixed-width integer (either 32 or 64 bits).
|
||||
|
||||
Because {name}`USize` is big enough to address all memory on every platform that Lean supports, there are
|
||||
in practice no {name}`ByteArray`s for which {name}`uget` cannot retrieve all elements.
|
||||
-/
|
||||
@[extern "lean_byte_array_uget"]
|
||||
def uget : (a : @& ByteArray) → (i : USize) → (h : i.toNat < a.size := by get_elem_tactic) → UInt8
|
||||
| ⟨bs⟩, i, h => bs[i]
|
||||
|
||||
/--
|
||||
Retrieves the byte at the indicated index. Panics if the index is out of bounds.
|
||||
-/
|
||||
@[extern "lean_byte_array_get"]
|
||||
def get! : (@& ByteArray) → (@& Nat) → UInt8
|
||||
| ⟨bs⟩, i => bs[i]!
|
||||
|
||||
/--
|
||||
Retrieves the byte at the indicated index. Callers must prove that the index is in bounds.
|
||||
|
||||
Use {name}`uget` for a more efficient alternative or {name}`get!` for a variant that panics if the
|
||||
index is out of bounds.
|
||||
-/
|
||||
@[extern "lean_byte_array_fget"]
|
||||
def get : (a : @& ByteArray) → (i : @& Nat) → (h : i < a.size := by get_elem_tactic) → UInt8
|
||||
| ⟨bs⟩, i, _ => bs[i]
|
||||
@@ -56,37 +80,65 @@ instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
|
||||
instance : GetElem ByteArray USize UInt8 fun xs i => i.toFin < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
|
||||
/--
|
||||
Replaces the byte at the given index.
|
||||
|
||||
The array is modified in-place if there are no other references to it.
|
||||
|
||||
If the index is out of bounds, the array is returned unmodified.
|
||||
-/
|
||||
@[extern "lean_byte_array_set"]
|
||||
def set! : ByteArray → (@& Nat) → UInt8 → ByteArray
|
||||
| ⟨bs⟩, i, b => ⟨bs.set! i b⟩
|
||||
|
||||
/--
|
||||
Replaces the byte at the given index.
|
||||
|
||||
No bounds check is performed, but the function requires a proof that the index is in bounds. This
|
||||
proof can usually be omitted, and will be synthesized automatically.
|
||||
|
||||
The array is modified in-place if there are no other references to it.
|
||||
-/
|
||||
@[extern "lean_byte_array_fset"]
|
||||
def set : (a : ByteArray) → (i : @& Nat) → UInt8 → (h : i < a.size := by get_elem_tactic) → ByteArray
|
||||
| ⟨bs⟩, i, b, h => ⟨bs.set i b h⟩
|
||||
|
||||
@[extern "lean_byte_array_uset"]
|
||||
@[extern "lean_byte_array_uset", inherit_doc ByteArray.set]
|
||||
def uset : (a : ByteArray) → (i : USize) → UInt8 → (h : i.toNat < a.size := by get_elem_tactic) → ByteArray
|
||||
| ⟨bs⟩, i, v, h => ⟨bs.uset i v h⟩
|
||||
|
||||
/--
|
||||
Computes a hash for a {name}`ByteArray`.
|
||||
-/
|
||||
@[extern "lean_byte_array_hash"]
|
||||
protected opaque hash (a : @& ByteArray) : UInt64
|
||||
|
||||
instance : Hashable ByteArray where
|
||||
hash := ByteArray.hash
|
||||
|
||||
/--
|
||||
Returns {name}`true` when {name}`s` contains zero bytes.
|
||||
-/
|
||||
def isEmpty (s : ByteArray) : Bool :=
|
||||
s.size == 0
|
||||
|
||||
/--
|
||||
Copy the slice at `[srcOff, srcOff + len)` in `src` to `[destOff, destOff + len)` in `dest`, growing `dest` if necessary.
|
||||
If `exact` is `false`, the capacity will be doubled when grown. -/
|
||||
Copies the slice at `[srcOff, srcOff + len)` in {name}`src` to `[destOff, destOff + len)` in
|
||||
{name}`dest`, growing {name}`dest` if necessary. If {name}`exact` is {name}`false`, the capacity
|
||||
will be doubled when grown.
|
||||
-/
|
||||
@[extern "lean_byte_array_copy_slice"]
|
||||
def copySlice (src : @& ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff len : Nat) (exact : Bool := true) : ByteArray :=
|
||||
⟨dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + min len (src.data.size - srcOff)) dest.data.size⟩
|
||||
|
||||
/--
|
||||
Copies the bytes with indices {name}`b` (inclusive) to {name}`e` (exclusive) to a new
|
||||
{name}`ByteArray`.
|
||||
-/
|
||||
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
|
||||
a.copySlice b empty 0 (e - b)
|
||||
|
||||
@[inline]
|
||||
protected def fastAppend (a : ByteArray) (b : ByteArray) : ByteArray :=
|
||||
-- we assume that `append`s may be repeated, so use asymptotic growing; use `copySlice` directly to customize
|
||||
b.copySlice 0 a a.size b.size false
|
||||
@@ -113,6 +165,9 @@ theorem append_eq {a b : ByteArray} : a.append b = a ++ b := rfl
|
||||
theorem fastAppend_eq {a b : ByteArray} : a.fastAppend b = a ++ b := by
|
||||
simp [← append_eq_fastAppend]
|
||||
|
||||
/--
|
||||
Converts a packed array of bytes to a linked list.
|
||||
-/
|
||||
def toList (bs : ByteArray) : List UInt8 :=
|
||||
let rec loop (i : Nat) (r : List UInt8) :=
|
||||
if i < bs.size then
|
||||
@@ -123,16 +178,12 @@ def toList (bs : ByteArray) : List UInt8 :=
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
loop 0 []
|
||||
|
||||
@[inline] def findIdx? (a : ByteArray) (p : UInt8 → Bool) (start := 0) : Option Nat :=
|
||||
let rec @[specialize] loop (i : Nat) :=
|
||||
if h : i < a.size then
|
||||
if p a[i] then some i else loop (i+1)
|
||||
else
|
||||
none
|
||||
termination_by a.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
loop start
|
||||
/--
|
||||
Finds the index of the first byte in {name}`a` for which {name}`p` returns {name}`true`. If no byte
|
||||
in {name}`a` satisfies {name}`p`, then the result is {name}`none`.
|
||||
|
||||
The index is returned along with a proof that it is a valid index in the array.
|
||||
-/
|
||||
@[inline] def findFinIdx? (a : ByteArray) (p : UInt8 → Bool) (start := 0) : Option (Fin a.size) :=
|
||||
let rec @[specialize] loop (i : Nat) :=
|
||||
if h : i < a.size then
|
||||
@@ -144,11 +195,29 @@ def toList (bs : ByteArray) : List UInt8 :=
|
||||
loop start
|
||||
|
||||
/--
|
||||
We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime.
|
||||
This is similar to the `Array` version.
|
||||
Finds the index of the first byte in {name}`a` for which {name}`p` returns {name}`true`. If no byte
|
||||
in {name}`a` satisfies {name}`p`, then the result is {name}`none`.
|
||||
|
||||
TODO: avoid code duplication in the future after we improve the compiler.
|
||||
The variant {name}`findFinIdx?` additionally returns a proof that the found index is in bounds.
|
||||
-/
|
||||
@[inline] def findIdx? (a : ByteArray) (p : UInt8 → Bool) (start := 0) : Option Nat :=
|
||||
let rec @[specialize] loop (i : Nat) :=
|
||||
if h : i < a.size then
|
||||
if p a[i] then some i else loop (i+1)
|
||||
else
|
||||
none
|
||||
termination_by a.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
loop start
|
||||
|
||||
/--
|
||||
An efficient implementation of {name}`ForIn.forIn` for {name}`ByteArray` that uses {name}`USize`
|
||||
rather than {name}`Nat` for indices.
|
||||
|
||||
We claim this unsafe implementation is correct because an array cannot have more than
|
||||
{name}`USize.size` elements in our runtime. This is similar to the {name}`Array` version.
|
||||
-/
|
||||
-- TODO: avoid code duplication in the future after we improve the compiler.
|
||||
@[inline] unsafe def forInUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteArray) (b : β) (f : UInt8 → β → m (ForInStep β)) : m β :=
|
||||
let sz := as.usize
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
@@ -161,7 +230,11 @@ def toList (bs : ByteArray) : List UInt8 :=
|
||||
pure b
|
||||
loop 0 b
|
||||
|
||||
/-- Reference implementation for `forIn` -/
|
||||
/--
|
||||
The reference implementation of {name}`ForIn.forIn` for {name}`ByteArray`.
|
||||
|
||||
In compiled code, this is replaced by the more efficient {name}`ByteArray.forInUnsafe`.
|
||||
-/
|
||||
@[implemented_by ByteArray.forInUnsafe]
|
||||
protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteArray) (b : β) (f : UInt8 → β → m (ForInStep β)) : m β :=
|
||||
let rec loop (i : Nat) (h : i ≤ as.size) (b : β) : m β := do
|
||||
@@ -179,7 +252,13 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteAr
|
||||
instance : ForIn m ByteArray UInt8 where
|
||||
forIn := ByteArray.forIn
|
||||
|
||||
/-- See comment at `forInUnsafe` -/
|
||||
/--
|
||||
An efficient implementation of a monadic left fold on for {name}`ByteArray` that uses {name}`USize`
|
||||
rather than {name}`Nat` for indices.
|
||||
|
||||
We claim this unsafe implementation is correct because an array cannot have more than
|
||||
{name}`USize.size` elements in our runtime. This is similar to the {name}`Array` version.
|
||||
-/
|
||||
-- TODO: avoid code duplication.
|
||||
@[inline]
|
||||
unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 → m β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : m β :=
|
||||
@@ -196,7 +275,14 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
|
||||
else
|
||||
pure init
|
||||
|
||||
/-- Reference implementation for `foldlM` -/
|
||||
/--
|
||||
A monadic left fold on {name}`ByteArray` that iterates over an array from low to high indices,
|
||||
computing a running value.
|
||||
|
||||
Each element of the array is combined with the value from the prior elements using a monadic
|
||||
function {name}`f`. The initial value {name}`init` is the starting value before any elements have
|
||||
been processed.
|
||||
-/
|
||||
@[implemented_by foldlMUnsafe]
|
||||
def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 → m β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : m β :=
|
||||
let fold (stop : Nat) (h : stop ≤ as.size) :=
|
||||
@@ -214,11 +300,23 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 →
|
||||
else
|
||||
fold as.size (Nat.le_refl _)
|
||||
|
||||
/--
|
||||
A left fold on {name}`ByteArray` that iterates over an array from low to high indices, computing a
|
||||
running value.
|
||||
|
||||
Each element of the array is combined with the value from the prior elements using a function
|
||||
{name}`f`. The initial value {name}`init` is the starting value before any elements have been
|
||||
processed.
|
||||
|
||||
{name}`ByteArray.foldlM` is a monadic variant of this function.
|
||||
-/
|
||||
@[inline]
|
||||
def foldl {β : Type v} (f : β → UInt8 → β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : β :=
|
||||
Id.run <| as.foldlM (pure <| f · ·) init start stop
|
||||
|
||||
/-- Iterator over the bytes (`UInt8`) of a `ByteArray`.
|
||||
set_option doc.verso false -- Awaiting intra-module forward reference support
|
||||
/--
|
||||
Iterator over the bytes (`UInt8`) of a `ByteArray`.
|
||||
|
||||
Typically created by `arr.iter`, where `arr` is a `ByteArray`.
|
||||
|
||||
@@ -242,6 +340,7 @@ structure Iterator where
|
||||
current byte is `(default : UInt8)`. -/
|
||||
idx : Nat
|
||||
deriving Inhabited
|
||||
set_option doc.verso true
|
||||
|
||||
/-- Creates an iterator at the beginning of an array. -/
|
||||
def mkIterator (arr : ByteArray) : Iterator :=
|
||||
@@ -259,16 +358,25 @@ theorem Iterator.sizeOf_eq (i : Iterator) : sizeOf i = i.array.size - i.idx :=
|
||||
|
||||
namespace Iterator
|
||||
|
||||
/-- Number of bytes remaining in the iterator. -/
|
||||
/--
|
||||
The number of bytes remaining in the iterator.
|
||||
-/
|
||||
def remainingBytes : Iterator → Nat
|
||||
| ⟨arr, i⟩ => arr.size - i
|
||||
|
||||
@[inherit_doc Iterator.idx]
|
||||
def pos := Iterator.idx
|
||||
|
||||
/-- The byte at the current position.
|
||||
/-- True if the iterator is past the array's last byte. -/
|
||||
@[inline]
|
||||
def atEnd : Iterator → Bool
|
||||
| ⟨arr, i⟩ => i ≥ arr.size
|
||||
|
||||
On an invalid position, returns `(default : UInt8)`. -/
|
||||
/--
|
||||
The byte at the current position.
|
||||
|
||||
On an invalid position, returns {lean}`(default : UInt8)`.
|
||||
-/
|
||||
@[inline]
|
||||
def curr : Iterator → UInt8
|
||||
| ⟨arr, i⟩ =>
|
||||
@@ -277,27 +385,28 @@ def curr : Iterator → UInt8
|
||||
else
|
||||
default
|
||||
|
||||
/-- Moves the iterator's position forward by one byte, unconditionally.
|
||||
/--
|
||||
Moves the iterator's position forward by one byte, unconditionally.
|
||||
|
||||
It is only valid to call this function if the iterator is not at the end of the array, *i.e.*
|
||||
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
|
||||
{name}`Iterator.atEnd` is {name}`false`; otherwise, the resulting iterator will be invalid.
|
||||
-/
|
||||
@[inline]
|
||||
def next : Iterator → Iterator
|
||||
| ⟨arr, i⟩ => ⟨arr, i + 1⟩
|
||||
|
||||
/-- Decreases the iterator's position.
|
||||
/--
|
||||
Decreases the iterator's position.
|
||||
|
||||
If the position is zero, this function is the identity. -/
|
||||
If the position is zero, this function is the identity.
|
||||
-/
|
||||
@[inline]
|
||||
def prev : Iterator → Iterator
|
||||
| ⟨arr, i⟩ => ⟨arr, i - 1⟩
|
||||
|
||||
/-- True if the iterator is past the array's last byte. -/
|
||||
@[inline]
|
||||
def atEnd : Iterator → Bool
|
||||
| ⟨arr, i⟩ => i ≥ arr.size
|
||||
|
||||
/-- True if the iterator is not past the array's last byte. -/
|
||||
/--
|
||||
True if the iterator is valid; that is, it is not past the array's last byte.
|
||||
-/
|
||||
@[inline]
|
||||
def hasNext : Iterator → Bool
|
||||
| ⟨arr, i⟩ => i < arr.size
|
||||
@@ -323,17 +432,21 @@ def next' (it : Iterator) (_h : it.hasNext) : Iterator :=
|
||||
def hasPrev : Iterator → Bool
|
||||
| ⟨_, i⟩ => i > 0
|
||||
|
||||
/-- Moves the iterator's position to the end of the array.
|
||||
/--
|
||||
Moves the iterator's position to the end of the array.
|
||||
|
||||
Note that `i.toEnd.atEnd` is always `true`. -/
|
||||
Given {given}`i : ByteArray.Iterator`, note that {lean}`i.toEnd.atEnd` is always {name}`true`.
|
||||
-/
|
||||
@[inline]
|
||||
def toEnd : Iterator → Iterator
|
||||
| ⟨arr, _⟩ => ⟨arr, arr.size⟩
|
||||
|
||||
/-- Moves the iterator's position several bytes forward.
|
||||
/--
|
||||
Moves the iterator's position several bytes forward.
|
||||
|
||||
The resulting iterator is only valid if the number of bytes to skip is less than or equal to
|
||||
the number of bytes left in the iterator. -/
|
||||
the number of bytes left in the iterator.
|
||||
-/
|
||||
@[inline]
|
||||
def forward : Iterator → Nat → Iterator
|
||||
| ⟨arr, i⟩, f => ⟨arr, i + f⟩
|
||||
@@ -341,9 +454,11 @@ def forward : Iterator → Nat → Iterator
|
||||
@[inherit_doc forward, inline]
|
||||
def nextn : Iterator → Nat → Iterator := forward
|
||||
|
||||
/-- Moves the iterator's position several bytes back.
|
||||
/--
|
||||
Moves the iterator's position several bytes back.
|
||||
|
||||
If asked to go back more bytes than available, stops at the beginning of the array. -/
|
||||
If asked to go back more bytes than available, stops at the beginning of the array.
|
||||
-/
|
||||
@[inline]
|
||||
def prevn : Iterator → Nat → Iterator
|
||||
| ⟨arr, i⟩, f => ⟨arr, i - f⟩
|
||||
|
||||
@@ -10,12 +10,19 @@ public import Init.Prelude
|
||||
public import Init.Data.List.Basic
|
||||
|
||||
public section
|
||||
set_option doc.verso true
|
||||
|
||||
namespace ByteArray
|
||||
|
||||
@[simp]
|
||||
theorem data_push {a : ByteArray} {b : UInt8} : (a.push b).data = a.data.push b := rfl
|
||||
|
||||
/--
|
||||
Appends two byte arrays.
|
||||
|
||||
In compiled code, calls to {name}`ByteArray.append` are replaced with the much more efficient
|
||||
{name (scope:="Init.Data.ByteArray.Basic")}`ByteArray.fastAppend`.
|
||||
-/
|
||||
@[expose]
|
||||
protected def append (a b : ByteArray) : ByteArray :=
|
||||
⟨⟨a.data.toList ++ b.data.toList⟩⟩
|
||||
|
||||
@@ -9,7 +9,13 @@ prelude
|
||||
public import Init.Data.ByteArray.Basic
|
||||
import Init.Data.String.Basic
|
||||
|
||||
/-- Interpret a `ByteArray` of size 8 as a little-endian `UInt64`. -/
|
||||
set_option doc.verso true
|
||||
|
||||
/--
|
||||
Interprets a {name}`ByteArray` of size 8 as a little-endian {name}`UInt64`.
|
||||
|
||||
Panics if the array's size is not 8.
|
||||
-/
|
||||
public def ByteArray.toUInt64LE! (bs : ByteArray) : UInt64 :=
|
||||
assert! bs.size == 8
|
||||
(bs.get! 7).toUInt64 <<< 0x38 |||
|
||||
@@ -21,7 +27,11 @@ public def ByteArray.toUInt64LE! (bs : ByteArray) : UInt64 :=
|
||||
(bs.get! 1).toUInt64 <<< 0x8 |||
|
||||
(bs.get! 0).toUInt64
|
||||
|
||||
/-- Interpret a `ByteArray` of size 8 as a big-endian `UInt64`. -/
|
||||
/--
|
||||
Interprets a {name}`ByteArray` of size 8 as a big-endian {name}`UInt64`.
|
||||
|
||||
Panics if the array's size is not 8.
|
||||
-/
|
||||
public def ByteArray.toUInt64BE! (bs : ByteArray) : UInt64 :=
|
||||
assert! bs.size == 8
|
||||
(bs.get! 0).toUInt64 <<< 0x38 |||
|
||||
|
||||
@@ -167,9 +167,9 @@ theorem ByteArray.append_inj_left {xs₁ xs₂ ys₁ ys₂ : ByteArray} (h : xs
|
||||
simp only [ByteArray.ext_iff, ← ByteArray.size_data, ByteArray.data_append] at *
|
||||
exact Array.append_inj_left h hl
|
||||
|
||||
theorem ByteArray.extract_append_eq_right {a b : ByteArray} {i : Nat} (hi : i = a.size) :
|
||||
(a ++ b).extract i (a ++ b).size = b := by
|
||||
subst hi
|
||||
theorem ByteArray.extract_append_eq_right {a b : ByteArray} {i j : Nat} (hi : i = a.size) (hj : j = a.size + b.size) :
|
||||
(a ++ b).extract i j = b := by
|
||||
subst hi hj
|
||||
ext1
|
||||
simp [← size_data]
|
||||
|
||||
|
||||
@@ -9,5 +9,3 @@ prelude
|
||||
public import Init.Data.Char.Basic
|
||||
public import Init.Data.Char.Lemmas
|
||||
public import Init.Data.Char.Order
|
||||
|
||||
public section
|
||||
|
||||
@@ -9,6 +9,7 @@ prelude
|
||||
public import Init.Data.Rat.Lemmas
|
||||
import Init.Data.Int.Bitwise.Lemmas
|
||||
import Init.Data.Int.DivMod.Lemmas
|
||||
import Init.Hints
|
||||
|
||||
/-!
|
||||
# The dyadic rationals
|
||||
|
||||
@@ -11,5 +11,3 @@ 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 section
|
||||
|
||||
@@ -140,7 +140,7 @@ Modulus of bounded numbers, usually invoked via the `%` operator.
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
protected def mod : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a % b, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a % b, by exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Division of bounded numbers, usually invoked via the `/` operator.
|
||||
@@ -154,7 +154,7 @@ Examples:
|
||||
* `(5 : Fin 10) / (7 : Fin 10) = (0 : Fin 10)`
|
||||
-/
|
||||
protected def div : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a / b, Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h⟩
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a / b, by exact Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h⟩
|
||||
|
||||
/--
|
||||
Modulus of bounded numbers with respect to a `Nat`.
|
||||
@@ -162,7 +162,7 @@ Modulus of bounded numbers with respect to a `Nat`.
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
def modn : Fin n → Nat → Fin n
|
||||
| ⟨a, h⟩, m => ⟨a % m, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
| ⟨a, h⟩, m => ⟨a % m, by exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Bitwise and.
|
||||
|
||||
@@ -23,7 +23,7 @@ Example:
|
||||
-/
|
||||
@[inline] def foldl (n) (f : α → Fin n → α) (init : α) : α := loop init 0 where
|
||||
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
|
||||
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
|
||||
@[specialize] loop (x : α) (i : Nat) : α :=
|
||||
if h : i < n then loop (f x ⟨i, h⟩) (i+1) else x
|
||||
termination_by n - i
|
||||
|
||||
@@ -34,7 +34,7 @@ and nesting to the right.
|
||||
Example:
|
||||
* `Fin.foldr 3 (·.val + ·) (0 : Nat) = (0 : Fin 3).val + ((1 : Fin 3).val + ((2 : Fin 3).val + 0))`
|
||||
-/
|
||||
@[inline] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop n (Nat.le_refl n) init where
|
||||
@[inline, expose] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop n (Nat.le_refl n) init where
|
||||
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
|
||||
@[specialize] loop : (i : _) → i ≤ n → α → α
|
||||
| 0, _, x => x
|
||||
@@ -65,7 +65,7 @@ Fin.foldlM n f x₀ = do
|
||||
pure xₙ
|
||||
```
|
||||
-/
|
||||
@[semireducible, specialize] loop (x : α) (i : Nat) : m α := do
|
||||
@[specialize] loop (x : α) (i : Nat) : m α := do
|
||||
if h : i < n then f x ⟨i, h⟩ >>= (loop · (i+1)) else pure x
|
||||
termination_by n - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
@@ -96,7 +96,7 @@ Fin.foldrM n f xₙ = do
|
||||
pure x₀
|
||||
```
|
||||
-/
|
||||
@[semireducible, specialize] loop : {i // i ≤ n} → α → m α
|
||||
@[specialize] loop : {i // i ≤ n} → α → m α
|
||||
| ⟨0, _⟩, x => pure x
|
||||
| ⟨i+1, h⟩, x => f ⟨i, h⟩ x >>= loop ⟨i, Nat.le_of_lt h⟩
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Nat.Lemmas
|
||||
public import Init.Data.Int.DivMod.Lemmas
|
||||
public import Init.Ext
|
||||
public import Init.ByCases
|
||||
public import Init.Conv
|
||||
@@ -15,7 +14,7 @@ public import Init.Omega
|
||||
public import Init.Data.Order.Factories
|
||||
import Init.Data.Order.Lemmas
|
||||
|
||||
public section
|
||||
@[expose] public section
|
||||
|
||||
open Std
|
||||
|
||||
@@ -327,7 +326,9 @@ theorem subsingleton_iff_le_one : Subsingleton (Fin n) ↔ n ≤ 1 := by
|
||||
(match n with | 0 | 1 | n+2 => ?_) <;> try simp
|
||||
· exact ⟨nofun⟩
|
||||
· exact ⟨fun ⟨0, _⟩ ⟨0, _⟩ => rfl⟩
|
||||
· exact fun h => by have := zero_lt_one (n := n); simp_all [h.elim 0 1]
|
||||
· have : ¬ n + 2 ≤ 1 := by simp [Nat.not_le]
|
||||
simp only [this, iff_false]
|
||||
exact fun h => by have := zero_lt_one (n := n); simp_all [h.elim 0 1]
|
||||
|
||||
instance subsingleton_zero : Subsingleton (Fin 0) := subsingleton_iff_le_one.2 (by decide)
|
||||
|
||||
@@ -914,16 +915,21 @@ theorem exists_fin_succ {P : Fin (n + 1) → Prop} : (∃ i, P i) ↔ P 0 ∨
|
||||
⟨fun ⟨i, h⟩ => Fin.cases Or.inl (fun i hi => Or.inr ⟨i, hi⟩) i h, fun h =>
|
||||
(h.elim fun h => ⟨0, h⟩) fun ⟨i, hi⟩ => ⟨i.succ, hi⟩⟩
|
||||
|
||||
theorem forall_fin_one {p : Fin 1 → Prop} : (∀ i, p i) ↔ p 0 :=
|
||||
@[simp] theorem forall_fin_zero {p : Fin 0 → Prop} : (∀ i, p i) ↔ True :=
|
||||
⟨fun _ => trivial, fun _ ⟨_, h⟩ => False.elim <| Nat.not_lt_zero _ h⟩
|
||||
|
||||
@[simp] theorem exists_fin_zero {p : Fin 0 → Prop} : (∃ i, p i) ↔ False := by simp
|
||||
|
||||
@[simp] theorem forall_fin_one {p : Fin 1 → Prop} : (∀ i, p i) ↔ p 0 :=
|
||||
⟨fun h => h _, fun h i => Subsingleton.elim i 0 ▸ h⟩
|
||||
|
||||
theorem exists_fin_one {p : Fin 1 → Prop} : (∃ i, p i) ↔ p 0 :=
|
||||
@[simp] theorem exists_fin_one {p : Fin 1 → Prop} : (∃ i, p i) ↔ p 0 :=
|
||||
⟨fun ⟨i, h⟩ => Subsingleton.elim i 0 ▸ h, fun h => ⟨_, h⟩⟩
|
||||
|
||||
theorem forall_fin_two {p : Fin 2 → Prop} : (∀ i, p i) ↔ p 0 ∧ p 1 :=
|
||||
@[simp] theorem forall_fin_two {p : Fin 2 → Prop} : (∀ i, p i) ↔ p 0 ∧ p 1 :=
|
||||
forall_fin_succ.trans <| and_congr_right fun _ => forall_fin_one
|
||||
|
||||
theorem exists_fin_two {p : Fin 2 → Prop} : (∃ i, p i) ↔ p 0 ∨ p 1 :=
|
||||
@[simp] theorem exists_fin_two {p : Fin 2 → Prop} : (∃ i, p i) ↔ p 0 ∨ p 1 :=
|
||||
exists_fin_succ.trans <| or_congr_right exists_fin_one
|
||||
|
||||
theorem fin_two_eq_of_eq_zero_iff : ∀ {a b : Fin 2}, (a = 0 ↔ b = 0) → a = b := by
|
||||
|
||||
@@ -7,5 +7,3 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.FloatArray.Basic
|
||||
|
||||
public section
|
||||
|
||||
@@ -10,5 +10,3 @@ public import Init.Data.Format.Basic
|
||||
public import Init.Data.Format.Macro
|
||||
public import Init.Data.Format.Instances
|
||||
public import Init.Data.Format.Syntax
|
||||
|
||||
public section
|
||||
|
||||
@@ -51,5 +51,5 @@ Converts a string to a pretty-printer document, replacing newlines in the string
|
||||
def String.toFormat (s : String) : Std.Format :=
|
||||
Std.Format.joinSep (s.splitOn "\n") Std.Format.line
|
||||
|
||||
instance : ToFormat String.Pos where
|
||||
instance : ToFormat String.Pos.Raw where
|
||||
format p := format p.byteIdx
|
||||
|
||||
@@ -16,7 +16,7 @@ universe u
|
||||
instance : Hashable Nat where
|
||||
hash n := UInt64.ofNat n
|
||||
|
||||
instance : Hashable String.Pos where
|
||||
instance : Hashable String.Pos.Raw where
|
||||
hash p := UInt64.ofNat p.byteIdx
|
||||
|
||||
instance [Hashable α] [Hashable β] : Hashable (α × β) where
|
||||
@@ -76,22 +76,3 @@ instance (P : Prop) : Hashable P where
|
||||
/-- An opaque (low-level) hash operation used to implement hashing for pointers. -/
|
||||
@[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
|
||||
mixHash u 11
|
||||
|
||||
/--
|
||||
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
|
||||
`hash a = hash b`.
|
||||
|
||||
This is automatic if the `BEq` instance is lawful.
|
||||
-/
|
||||
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
|
||||
/-- If `a == b`, then `hash a = hash b`. -/
|
||||
hash_eq (a b : α) : a == b → hash a = hash b
|
||||
|
||||
/--
|
||||
A lawful hash function respects its Boolean equality test.
|
||||
-/
|
||||
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b → hash a = hash b :=
|
||||
LawfulHashable.hash_eq a b
|
||||
|
||||
instance (priority := low) [BEq α] [Hashable α] [LawfulBEq α] : LawfulHashable α where
|
||||
hash_eq _ _ h := eq_of_beq h ▸ rfl
|
||||
|
||||
@@ -18,5 +18,3 @@ public import Init.Data.Int.Pow
|
||||
public import Init.Data.Int.Cooper
|
||||
public import Init.Data.Int.Linear
|
||||
public import Init.Data.Int.OfNat
|
||||
|
||||
public section
|
||||
|
||||
@@ -8,5 +8,3 @@ module
|
||||
prelude
|
||||
public import Init.Data.Int.Bitwise.Basic
|
||||
public import Init.Data.Int.Bitwise.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
@@ -9,6 +9,7 @@ prelude
|
||||
public import Init.Data.Ord.Basic
|
||||
import all Init.Data.Ord.Basic
|
||||
public import Init.Data.Int.Order
|
||||
import Init.Omega
|
||||
|
||||
public section
|
||||
|
||||
|
||||
@@ -9,5 +9,3 @@ prelude
|
||||
public import Init.Data.Int.DivMod.Basic
|
||||
public import Init.Data.Int.DivMod.Bootstrap
|
||||
public import Init.Data.Int.DivMod.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
@@ -13,6 +13,7 @@ public import Init.Data.Int.Order
|
||||
public import Init.Data.Int.Lemmas
|
||||
public import Init.Data.Nat.Dvd
|
||||
public import Init.RCases
|
||||
import Init.TacticsExtra
|
||||
|
||||
public section
|
||||
|
||||
@@ -122,8 +123,8 @@ theorem eq_one_of_mul_eq_one_right {a b : Int} (H : 0 ≤ a) (H' : a * b = 1) :
|
||||
theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 ≤ b) (H' : a * b = 1) : b = 1 :=
|
||||
eq_one_of_mul_eq_one_right (b := a) H <| by rw [Int.mul_comm, H']
|
||||
|
||||
instance decidableDvd : DecidableRel (α := Int) (· ∣ ·) := fun _ _ =>
|
||||
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
|
||||
instance decidableDvd : DecidableRel (α := Int) (· ∣ ·) := fun a b =>
|
||||
decidable_of_decidable_of_iff (p := b % a = 0) (by exact (dvd_iff_emod_eq_zero ..).symm)
|
||||
|
||||
protected theorem mul_dvd_mul_iff_left {a b c : Int} (h : a ≠ 0) : (a * b) ∣ (a * c) ↔ b ∣ c :=
|
||||
⟨by rintro ⟨d, h'⟩; exact ⟨d, by rw [Int.mul_assoc] at h'; exact (mul_eq_mul_left_iff h).mp h'⟩,
|
||||
|
||||
@@ -8,7 +8,6 @@ module
|
||||
prelude
|
||||
public import Init.Data.Int.Order
|
||||
public import Init.Data.Int.Pow
|
||||
public import Init.Data.Int.DivMod.Lemmas
|
||||
public import Init.Omega
|
||||
|
||||
public section
|
||||
|
||||
@@ -88,6 +88,20 @@ theorem finVal {n : Nat} {a : Fin n} {a' : Int}
|
||||
(h₁ : Lean.Grind.ToInt.toInt a = a') : NatCast.natCast (a.val) = a' := by
|
||||
rw [← h₁, Lean.Grind.ToInt.toInt, Lean.Grind.instToIntFinCoOfNatIntCast]
|
||||
|
||||
theorem eq_eq {a b : Nat} {a' b' : Int}
|
||||
(h₁ : NatCast.natCast a = a') (h₂ : NatCast.natCast b = b') : (a = b) = (a' = b') := by
|
||||
simp [← h₁, ←h₂]; constructor
|
||||
next => intro; subst a; rfl
|
||||
next => simp [Int.natCast_inj]
|
||||
|
||||
theorem lt_eq {a b : Nat} {a' b' : Int}
|
||||
(h₁ : NatCast.natCast a = a') (h₂ : NatCast.natCast b = b') : (a < b) = (a' < b') := by
|
||||
simp only [← h₁, ← h₂, Int.ofNat_lt]
|
||||
|
||||
theorem le_eq {a b : Nat} {a' b' : Int}
|
||||
(h₁ : NatCast.natCast a = a') (h₂ : NatCast.natCast b = b') : (a ≤ b) = (a' ≤ b') := by
|
||||
simp only [← h₁, ← h₂, Int.ofNat_le]
|
||||
|
||||
end Nat.ToInt
|
||||
|
||||
namespace Int.Nonneg
|
||||
|
||||
@@ -605,6 +605,9 @@ theorem natAbs_of_nonneg {a : Int} (H : 0 ≤ a) : (natAbs a : Int) = a :=
|
||||
match a, eq_ofNat_of_zero_le H with
|
||||
| _, ⟨_, rfl⟩ => rfl
|
||||
|
||||
theorem ofNat_natAbs_of_nonneg {a : Int} (h : 0 ≤ a) : (natAbs a : Int) = a :=
|
||||
natAbs_of_nonneg h
|
||||
|
||||
theorem ofNat_natAbs_of_nonpos {a : Int} (H : a ≤ 0) : (natAbs a : Int) = -a := by
|
||||
rw [← natAbs_neg, natAbs_of_nonneg (Int.neg_nonneg_of_nonpos H)]
|
||||
|
||||
|
||||
@@ -12,18 +12,80 @@ public import Init.Ext
|
||||
public import Init.NotationExtra
|
||||
public import Init.TacticsExtra
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
public section
|
||||
|
||||
/-!
|
||||
### Definition of iterators
|
||||
# Definition of iterators
|
||||
|
||||
This module defines iterators and what it means for an iterator to be finite and productive.
|
||||
-/
|
||||
|
||||
namespace Std
|
||||
|
||||
private opaque Internal.idOpaque {α} : { f : α → α // f = id } := ⟨id, rfl⟩
|
||||
|
||||
/--
|
||||
Currently, {lean}`Shrink α` is just a wrapper around {lean}`α`.
|
||||
|
||||
In the future, {name}`Shrink` should allow shrinking {lean}`α` into a potentially smaller universe,
|
||||
given a proof that {name}`α` is actually small, just like Mathlib's {lit}`Shrink`, except that
|
||||
the latter's conversion functions are noncomputable. Until then, {lean}`Shrink α` is always in the
|
||||
same universe as {name}`α`.
|
||||
|
||||
This no-op type exists so that fewer breaking changes will be needed when the
|
||||
real {lit}`Shrink` type is available and the iterators will be made more flexible with regard to
|
||||
universes.
|
||||
|
||||
The conversion functions {name (scope := "Init.Data.Iterators.Basic")}`Shrink.deflate` and
|
||||
{name (scope := "Init.Data.Iterators.Basic")}`Shrink.inflate` form an equivalence between
|
||||
{name}`α` and {lean}`Shrink α`, but this equivalence is intentionally not definitional.
|
||||
-/
|
||||
public def Shrink (α : Type u) : Type u := Internal.idOpaque.1 α
|
||||
|
||||
/-- Converts elements of {name}`α` into elements of {lean}`Shrink α`. -/
|
||||
@[always_inline]
|
||||
public def Shrink.deflate {α} (x : α) : Shrink α :=
|
||||
cast (by simp [Shrink, Internal.idOpaque.property]) x
|
||||
|
||||
/-- Converts elements of {lean}`Shrink α` into elements of {name}`α`. -/
|
||||
@[always_inline]
|
||||
public def Shrink.inflate {α} (x : Shrink α) : α :=
|
||||
cast (by simp [Shrink, Internal.idOpaque.property]) x
|
||||
|
||||
@[simp, grind =]
|
||||
public theorem Shrink.deflate_inflate {α} {x : Shrink α} :
|
||||
Shrink.deflate x.inflate = x := by
|
||||
simp [deflate, inflate]
|
||||
|
||||
@[simp, grind =]
|
||||
public theorem Shrink.inflate_deflate {α} {x : α} :
|
||||
(Shrink.deflate x).inflate = x := by
|
||||
simp [deflate, inflate]
|
||||
|
||||
public theorem Shrink.inflate_inj {α} {x y : Shrink α} :
|
||||
x.inflate = y.inflate ↔ x = y := by
|
||||
apply Iff.intro
|
||||
· intro h
|
||||
simpa using congrArg Shrink.deflate h
|
||||
· rintro rfl
|
||||
rfl
|
||||
|
||||
public theorem Shrink.deflate_inj {α} {x y : α} :
|
||||
Shrink.deflate x = Shrink.deflate y ↔ x = y := by
|
||||
apply Iff.intro
|
||||
· intro h
|
||||
simpa using congrArg Shrink.inflate h
|
||||
· 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
|
||||
|
||||
/--
|
||||
An iterator that sequentially emits values of type `β` in the monad `m`. It may be finite
|
||||
or infinite.
|
||||
@@ -284,7 +346,7 @@ step object is bundled with a proof that it is a "plausible" step for the given
|
||||
-/
|
||||
class Iterator (α : Type w) (m : Type w → Type w') (β : outParam (Type w)) where
|
||||
IsPlausibleStep : IterM (α := α) m β → IterStep (IterM (α := α) m β) β → Prop
|
||||
step : (it : IterM (α := α) m β) → m (PlausibleIterStep <| IsPlausibleStep it)
|
||||
step : (it : IterM (α := α) m β) → m (Shrink <| PlausibleIterStep <| IsPlausibleStep it)
|
||||
|
||||
section Monadic
|
||||
|
||||
@@ -358,7 +420,7 @@ the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
|
||||
-/
|
||||
@[always_inline, inline, expose]
|
||||
def IterM.step {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β]
|
||||
(it : IterM (α := α) m β) : m it.Step :=
|
||||
(it : IterM (α := α) m β) : m (Shrink it.Step) :=
|
||||
Iterator.step it
|
||||
|
||||
end Monadic
|
||||
@@ -582,7 +644,7 @@ the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
|
||||
-/
|
||||
@[always_inline, inline, expose]
|
||||
def Iter.step {α β : Type w} [Iterator α Id β] (it : Iter (α := α) β) : it.Step :=
|
||||
it.toIterM.step.run.toPure
|
||||
it.toIterM.step.run.inflate.toPure
|
||||
|
||||
end Pure
|
||||
|
||||
|
||||
@@ -8,6 +8,5 @@ module
|
||||
prelude
|
||||
public import Init.Data.Iterators.Combinators.Monadic
|
||||
public import Init.Data.Iterators.Combinators.FilterMap
|
||||
public import Init.Data.Iterators.Combinators.FlatMap
|
||||
public import Init.Data.Iterators.Combinators.ULift
|
||||
|
||||
public section
|
||||
|
||||
53
src/Init/Data/Iterators/Combinators/FlatMap.lean
Normal file
53
src/Init/Data/Iterators/Combinators/FlatMap.lean
Normal file
@@ -0,0 +1,53 @@
|
||||
/-
|
||||
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.Combinators.FilterMap
|
||||
public import Init.Data.Iterators.Combinators.Monadic.FlatMap
|
||||
|
||||
set_option doc.verso true
|
||||
|
||||
/-!
|
||||
# {lit}`flatMap` combinator
|
||||
|
||||
This file provides the {lit}`flatMap` iterator combinator and variants of it.
|
||||
|
||||
If {lit}`it` is any iterator, {lit}`it.flatMap f` maps each output of {lit}`it` to an iterator using
|
||||
{lit}`f`.
|
||||
|
||||
{lit}`it.flatMap f` first emits all outputs of the first obtained iterator, then of the second,
|
||||
and so on. In other words, {lit}`it` flattens the iterator of iterators obtained by mapping with
|
||||
{lit}`f`.
|
||||
-/
|
||||
|
||||
namespace Std.Iterators
|
||||
|
||||
@[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 γ]
|
||||
(f : β → m (IterM (α := α₂) m γ)) (it₁ : Iter (α := α) β) (it₂ : Option (IterM (α := α₂) m γ)) :=
|
||||
((it₁.mapM 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 γ]
|
||||
(f : β → m (IterM (α := α₂) m γ)) (it : Iter (α := α) β) :=
|
||||
(it.flatMapAfterM f none : IterM m γ)
|
||||
|
||||
@[always_inline, inherit_doc IterM.flatMapAfter]
|
||||
public def Iter.flatMapAfter {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
(f : β → Iter (α := α₂) γ) (it₁ : Iter (α := α) β) (it₂ : Option (Iter (α := α₂) γ)) :=
|
||||
((it₁.toIterM.flatMapAfter (fun b => (f b).toIterM) (Iter.toIterM <$> it₂)).toIter : Iter γ)
|
||||
|
||||
@[always_inline, expose, inherit_doc IterM.flatMap]
|
||||
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
|
||||
@@ -7,6 +7,5 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Iterators.Combinators.Monadic.FilterMap
|
||||
public import Init.Data.Iterators.Combinators.Monadic.FlatMap
|
||||
public import Init.Data.Iterators.Combinators.Monadic.ULift
|
||||
|
||||
public section
|
||||
|
||||
@@ -43,7 +43,8 @@ instance Attach.instIterator {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] {P : β → Prop} :
|
||||
Iterator (Attach α m P) m { out : β // P out } where
|
||||
IsPlausibleStep it step := ∃ step', Monadic.modifyStep it step' = step
|
||||
step it := (fun step => ⟨Monadic.modifyStep it step, step, rfl⟩) <$> it.internalState.inner.step
|
||||
step it := (fun step => .deflate ⟨Monadic.modifyStep it step.inflate, step.inflate, rfl⟩) <$>
|
||||
it.internalState.inner.step
|
||||
|
||||
def Attach.instFinitenessRelation {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [Finite α m] {P : β → Prop} :
|
||||
|
||||
@@ -149,13 +149,13 @@ instance FilterMap.instIterator {α β γ : Type w} {m : Type w → Type w'} {n
|
||||
step it :=
|
||||
letI : MonadLift m n := ⟨lift (α := _)⟩
|
||||
do
|
||||
match ← it.internalState.inner.step with
|
||||
match (← it.internalState.inner.step).inflate with
|
||||
| .yield it' out h => do
|
||||
match ← (f out).operation with
|
||||
| ⟨none, h'⟩ => pure <| .skip (it'.filterMapWithPostcondition f) (by exact .yieldNone h h')
|
||||
| ⟨some out', h'⟩ => pure <| .yield (it'.filterMapWithPostcondition f) out' (by exact .yieldSome h h')
|
||||
| .skip it' h => pure <| .skip (it'.filterMapWithPostcondition f) (by exact .skip h)
|
||||
| .done h => pure <| .done (.done h)
|
||||
| ⟨none, h'⟩ => pure <| .deflate <| .skip (it'.filterMapWithPostcondition f) (by exact .yieldNone h h')
|
||||
| ⟨some out', h'⟩ => pure <| .deflate <| .yield (it'.filterMapWithPostcondition f) out' (by exact .yieldSome h h')
|
||||
| .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 α}
|
||||
@@ -463,7 +463,7 @@ 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.filterMapWithPostcondition (fun b => some <$> PostconditionT.lift (f b)) : IterM n γ)
|
||||
(it.mapWithPostcondition (fun b => PostconditionT.lift (f b)) : IterM n γ)
|
||||
|
||||
/--
|
||||
If `it` is an iterator, then `it.filterM f` is another iterator that applies a monadic
|
||||
|
||||
385
src/Init/Data/Iterators/Combinators/Monadic/FlatMap.lean
Normal file
385
src/Init/Data/Iterators/Combinators/Monadic/FlatMap.lean
Normal file
@@ -0,0 +1,385 @@
|
||||
/-
|
||||
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.Combinators.Monadic.FilterMap
|
||||
public import Init.Data.Option.Lemmas
|
||||
|
||||
/-!
|
||||
# Monadic `flatMap` combinator
|
||||
|
||||
This file provides the `flatMap` iterator combinator and variants of it.
|
||||
|
||||
If `it` is any iterator, `it.flatMap f` maps each output of `it` to an iterator using
|
||||
`f`.
|
||||
|
||||
`it.flatMap f` first emits all outputs of the first obtained iterator, then of the second,
|
||||
and so on. In other words, `it` flattens the iterator of iterators obtained by mapping with
|
||||
`f`.
|
||||
-/
|
||||
|
||||
namespace Std.Iterators
|
||||
|
||||
/-- Internal implementation detail of the `flatMap` combinator -/
|
||||
@[ext, unbox]
|
||||
public structure Flatten (α α₂ β : Type w) (m) where
|
||||
it₁ : IterM (α := α) m (IterM (α := α₂) m β)
|
||||
it₂ : Option (IterM (α := α₂) m β)
|
||||
|
||||
/--
|
||||
Internal iterator combinator that is used to implement all `flatMap` variants
|
||||
-/
|
||||
@[always_inline]
|
||||
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 β)
|
||||
|
||||
/--
|
||||
Let `it₁` and `it₂` be iterators and `f` a monadic function mapping `it₁`'s outputs to iterators
|
||||
of the same type as `it₂`. Then `it₁.flatMapAfterM f it₂` first goes over `it₂` and then over
|
||||
`it₁.flatMap f it₂`, emitting all their values.
|
||||
|
||||
The main purpose of this combinator is to represent the intermediate state of a `flatMap` iterator
|
||||
that is currently iterating over one of the inner iterators.
|
||||
|
||||
**Marble diagram (without monadic effects):**
|
||||
|
||||
```text
|
||||
it₁ --b c --d -⊥
|
||||
it₂ a1-a2⊥
|
||||
f b b1-b2⊥
|
||||
f c c1-c2⊥
|
||||
f d ⊥
|
||||
it.flatMapAfterM f it₂ a1-a2----b1-b2--c1-c2----⊥
|
||||
```
|
||||
|
||||
**Termination properties:**
|
||||
|
||||
* `Finite` instance: only if `it₁`, `it₂` and the inner iterators are finite
|
||||
* `Productive` instance: only if `it₁` is finite and `it₂` and the inner iterators are productive
|
||||
|
||||
For certain functions `f`, the resulting iterator will be finite (or productive) even though
|
||||
no `Finite` (or `Productive`) instance is provided out of the box. For example, if the outer
|
||||
iterator is productive and the inner
|
||||
iterators are productive *and provably never empty*, then the resulting iterator is also productive.
|
||||
|
||||
**Performance:**
|
||||
|
||||
This combinator incurs an additional O(1) cost with each output of `it₁`, `it₂` or an internal
|
||||
iterator.
|
||||
|
||||
For each value emitted by the outer iterator `it₁`, this combinator calls `f`.
|
||||
-/
|
||||
@[always_inline]
|
||||
public def IterM.flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
(f : β → m (IterM (α := α₂) m γ)) (it₁ : IterM (α := α) m β) (it₂ : Option (IterM (α := α₂) m γ)) :=
|
||||
((it₁.mapM f).flattenAfter it₂ : IterM m γ)
|
||||
|
||||
/--
|
||||
Let `it` be an iterator and `f` a monadic function mapping `it`'s outputs to iterators.
|
||||
Then `it.flatMapM f` is an iterator that goes over `it` and for each output, it applies `f` and
|
||||
iterates over the resulting iterator. `it.flatMapM f` emits all values obtained from the inner
|
||||
iterators -- first, all of the first inner iterator, then all of the second one, and so on.
|
||||
|
||||
**Marble diagram (without monadic effects):**
|
||||
|
||||
```text
|
||||
it ---a --b c --d -⊥
|
||||
f a a1-a2⊥
|
||||
f b b1-b2⊥
|
||||
f c c1-c2⊥
|
||||
f d ⊥
|
||||
it.flatMapM ----a1-a2----b1-b2--c1-c2----⊥
|
||||
```
|
||||
|
||||
**Termination properties:**
|
||||
|
||||
* `Finite` instance: only if `it` and the inner iterators are finite
|
||||
* `Productive` instance: only if `it` is finite and the inner iterators are productive
|
||||
|
||||
For certain functions `f`, the resulting iterator will be finite (or productive) even though
|
||||
no `Finite` (or `Productive`) instance is provided out of the box. For example, if the outer
|
||||
iterator is productive and the inner
|
||||
iterators are productive *and provably never empty*, then the resulting iterator is also productive.
|
||||
|
||||
**Performance:**
|
||||
|
||||
This combinator incurs an additional O(1) cost with each output of `it` or an internal iterator.
|
||||
|
||||
For each value emitted by the outer iterator `it`, this combinator calls `f`.
|
||||
-/
|
||||
@[always_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 γ]
|
||||
(f : β → m (IterM (α := α₂) m γ)) (it : IterM (α := α) m β) :=
|
||||
(it.flatMapAfterM f none : IterM m γ)
|
||||
|
||||
/--
|
||||
Let `it₁` and `it₂` be iterators and `f` a function mapping `it₁`'s outputs to iterators
|
||||
of the same type as `it₂`. Then `it₁.flatMapAfter f it₂` first goes over `it₂` and then over
|
||||
`it₁.flatMap f it₂`, emitting all their values.
|
||||
|
||||
The main purpose of this combinator is to represent the intermediate state of a `flatMap` iterator
|
||||
that is currently iterating over one of the inner iterators.
|
||||
|
||||
**Marble diagram:**
|
||||
|
||||
```text
|
||||
it₁ --b c --d -⊥
|
||||
it₂ a1-a2⊥
|
||||
f b b1-b2⊥
|
||||
f c c1-c2⊥
|
||||
f d ⊥
|
||||
it.flatMapAfter f it₂ a1-a2----b1-b2--c1-c2----⊥
|
||||
```
|
||||
|
||||
**Termination properties:**
|
||||
|
||||
* `Finite` instance: only if `it₁`, `it₂` and the inner iterators are finite
|
||||
* `Productive` instance: only if `it₁` is finite and `it₂` and the inner iterators are productive
|
||||
|
||||
For certain functions `f`, the resulting iterator will be finite (or productive) even though
|
||||
no `Finite` (or `Productive`) instance is provided out of the box. For example, if the outer
|
||||
iterator is productive and the inner
|
||||
iterators are productive *and provably never empty*, then the resulting iterator is also productive.
|
||||
|
||||
**Performance:**
|
||||
|
||||
This combinator incurs an additional O(1) cost with each output of `it₁`, `it₂` or an internal
|
||||
iterator.
|
||||
|
||||
For each value emitted by the outer iterator `it₁`, this combinator calls `f`.
|
||||
-/
|
||||
@[always_inline]
|
||||
public def IterM.flatMapAfter {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
(f : β → IterM (α := α₂) m γ) (it₁ : IterM (α := α) m β) (it₂ : Option (IterM (α := α₂) m γ)) :=
|
||||
((it₁.map f).flattenAfter it₂ : IterM m γ)
|
||||
|
||||
/--
|
||||
Let `it` be an iterator and `f` a function mapping `it`'s outputs to iterators.
|
||||
Then `it.flatMap f` is an iterator that goes over `it` and for each output, it applies `f` and
|
||||
iterates over the resulting iterator. `it.flatMap f` emits all values obtained from the inner
|
||||
iterators -- first, all of the first inner iterator, then all of the second one, and so on.
|
||||
|
||||
**Marble diagram:**
|
||||
|
||||
```text
|
||||
it ---a --b c --d -⊥
|
||||
f a a1-a2⊥
|
||||
f b b1-b2⊥
|
||||
f c c1-c2⊥
|
||||
f d ⊥
|
||||
it.flatMap ----a1-a2----b1-b2--c1-c2----⊥
|
||||
```
|
||||
|
||||
**Termination properties:**
|
||||
|
||||
* `Finite` instance: only if `it` and the inner iterators are finite
|
||||
* `Productive` instance: only if `it` is finite and the inner iterators are productive
|
||||
|
||||
For certain functions `f`, the resulting iterator will be finite (or productive) even though
|
||||
no `Finite` (or `Productive`) instance is provided out of the box. For example, if the outer
|
||||
iterator is productive and the inner
|
||||
iterators are productive *and provably never empty*, then the resulting iterator is also productive.
|
||||
|
||||
**Performance:**
|
||||
|
||||
This combinator incurs an additional O(1) cost with each output of `it` or an internal iterator.
|
||||
|
||||
For each value emitted by the outer iterator `it`, this combinator calls `f`.
|
||||
-/
|
||||
@[always_inline, expose]
|
||||
public def IterM.flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
(f : β → IterM (α := α₂) m γ) (it : IterM (α := α) m β) :=
|
||||
(it.flatMapAfter f none : IterM m γ)
|
||||
|
||||
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 β))
|
||||
| outerSkip : ∀ {it₁ it₁'}, it₁.IsPlausibleStep (.skip it₁') →
|
||||
IsPlausibleStep (toIterM ⟨it₁, none⟩ m β) (.skip (toIterM ⟨it₁', none⟩ m β))
|
||||
| outerDone : ∀ {it₁}, it₁.IsPlausibleStep .done →
|
||||
IsPlausibleStep (toIterM ⟨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)
|
||||
| innerSkip : ∀ {it₁ it₂ it₂'}, it₂.IsPlausibleStep (.skip it₂') →
|
||||
IsPlausibleStep (toIterM ⟨it₁, some it₂⟩ m β) (.skip (toIterM ⟨it₁, some it₂'⟩ m β))
|
||||
| innerDone : ∀ {it₁ it₂}, it₂.IsPlausibleStep .done →
|
||||
IsPlausibleStep (toIterM ⟨it₁, some it₂⟩ m β) (.skip (toIterM ⟨it₁, none⟩ m β))
|
||||
|
||||
public instance Flatten.instIterator [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] :
|
||||
Iterator (Flatten α α₂ β m) m β where
|
||||
IsPlausibleStep := IsPlausibleStep
|
||||
step it :=
|
||||
match it with
|
||||
| ⟨it₁, none⟩ => do
|
||||
match (← it₁.step).inflate with
|
||||
| .yield it₁' it₂' h =>
|
||||
pure <| .deflate <| .skip ⟨it₁', some it₂'⟩ (.outerYield h)
|
||||
| .skip it₁' h =>
|
||||
pure <| .deflate <| .skip ⟨it₁', none⟩ (.outerSkip h)
|
||||
| .done h =>
|
||||
pure <| .deflate <| .done (.outerDone h)
|
||||
| ⟨it₁, some it₂⟩ => do
|
||||
match (← it₂.step).inflate with
|
||||
| .yield it₂' c h =>
|
||||
pure <| .deflate <| .yield ⟨it₁, some it₂'⟩ c (.innerYield h)
|
||||
| .skip it₂' h =>
|
||||
pure <| .deflate <| .skip ⟨it₁, some it₂'⟩ (.innerSkip h)
|
||||
| .done h =>
|
||||
pure <| .deflate <| .skip ⟨it₁, none⟩ (.innerDone h)
|
||||
|
||||
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] :
|
||||
IterM (α := Flatten α α₂ β m) m β → IterM (α := Flatten α α₂ β m) m β → Prop :=
|
||||
InvImage
|
||||
(Prod.Lex
|
||||
(InvImage IterM.TerminationMeasures.Finite.Rel IterM.finitelyManySteps)
|
||||
(Option.lt (InvImage IterM.TerminationMeasures.Finite.Rel IterM.finitelyManySteps)))
|
||||
(fun it => (it.internalState.it₁, it.internalState.it₂))
|
||||
|
||||
theorem Flatten.rel_of_left [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Finite α₂ m] {it it'}
|
||||
(h : it'.internalState.it₁.finitelyManySteps.Rel it.internalState.it₁.finitelyManySteps) :
|
||||
Rel α β m it' it :=
|
||||
Prod.Lex.left _ _ h
|
||||
|
||||
theorem Flatten.rel_of_right₁ [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Finite α₂ m] {it₁ it₂ it₂'}
|
||||
(h : (InvImage IterM.TerminationMeasures.Finite.Rel IterM.finitelyManySteps) it₂' it₂) :
|
||||
Rel α β m ⟨it₁, some it₂'⟩ ⟨it₁, some it₂⟩ := by
|
||||
refine Prod.Lex.right _ h
|
||||
|
||||
theorem Flatten.rel_of_right₂ [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Finite α₂ m] {it₁ it₂} :
|
||||
Rel α β m ⟨it₁, none⟩ ⟨it₁, some it₂⟩ :=
|
||||
Prod.Lex.right _ True.intro
|
||||
|
||||
instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Finite α₂ m] :
|
||||
FinitenessRelation (Flatten α α₂ β m) m where
|
||||
rel := Rel α β m
|
||||
wf := by
|
||||
apply InvImage.wf
|
||||
refine ⟨fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b⟩
|
||||
· exact InvImage.wf _ WellFoundedRelation.wf
|
||||
· exact Option.wellFounded_lt <| InvImage.wf _ WellFoundedRelation.wf
|
||||
subrelation {it it'} h := by
|
||||
obtain ⟨step, h, h'⟩ := h
|
||||
cases h' <;> cases h
|
||||
case outerYield =>
|
||||
apply Flatten.rel_of_left
|
||||
exact IterM.TerminationMeasures.Finite.rel_of_yield ‹_›
|
||||
case outerSkip =>
|
||||
apply Flatten.rel_of_left
|
||||
exact IterM.TerminationMeasures.Finite.rel_of_skip ‹_›
|
||||
case innerYield =>
|
||||
apply Flatten.rel_of_right₁
|
||||
exact IterM.TerminationMeasures.Finite.rel_of_yield ‹_›
|
||||
case innerSkip =>
|
||||
apply Flatten.rel_of_right₁
|
||||
exact IterM.TerminationMeasures.Finite.rel_of_skip ‹_›
|
||||
case innerDone =>
|
||||
apply Flatten.rel_of_right₂
|
||||
|
||||
@[no_expose]
|
||||
public instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Finite α₂ m] : Finite (Flatten α α₂ β m) m :=
|
||||
.of_finitenessRelation instFinitenessRelationFlattenOfIterMOfFinite
|
||||
|
||||
end Finite
|
||||
|
||||
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]
|
||||
[Productive α₂ m] :
|
||||
IterM (α := Flatten α α₂ β m) m β → IterM (α := Flatten α α₂ β m) m β → Prop :=
|
||||
InvImage
|
||||
(Prod.Lex
|
||||
(InvImage IterM.TerminationMeasures.Finite.Rel IterM.finitelyManySteps)
|
||||
(Option.lt (InvImage IterM.TerminationMeasures.Productive.Rel IterM.finitelyManySkips)))
|
||||
(fun it => (it.internalState.it₁, it.internalState.it₂))
|
||||
|
||||
theorem Flatten.productiveRel_of_left [Monad m] [Iterator α m (IterM (α := α₂) m β)]
|
||||
[Iterator α₂ m β] [Finite α m] [Productive α₂ m] {it it'}
|
||||
(h : it'.internalState.it₁.finitelyManySteps.Rel it.internalState.it₁.finitelyManySteps) :
|
||||
ProductiveRel α β m it' it :=
|
||||
Prod.Lex.left _ _ h
|
||||
|
||||
theorem Flatten.productiveRel_of_right₁ [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Productive α₂ m] {it₁ it₂ it₂'}
|
||||
(h : (InvImage IterM.TerminationMeasures.Productive.Rel IterM.finitelyManySkips) it₂' it₂) :
|
||||
ProductiveRel α β m ⟨it₁, some it₂'⟩ ⟨it₁, some it₂⟩ := by
|
||||
refine Prod.Lex.right _ h
|
||||
|
||||
theorem Flatten.productiveRel_of_right₂ [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Productive α₂ m] {it₁ it₂} :
|
||||
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] :
|
||||
ProductivenessRelation (Flatten α α₂ β m) m where
|
||||
rel := ProductiveRel α β m
|
||||
wf := by
|
||||
apply InvImage.wf
|
||||
refine ⟨fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b⟩
|
||||
· exact InvImage.wf _ WellFoundedRelation.wf
|
||||
· exact Option.wellFounded_lt <| InvImage.wf _ WellFoundedRelation.wf
|
||||
subrelation {it it'} h := by
|
||||
cases h
|
||||
case outerYield =>
|
||||
apply Flatten.productiveRel_of_left
|
||||
exact IterM.TerminationMeasures.Finite.rel_of_yield ‹_›
|
||||
case outerSkip =>
|
||||
apply Flatten.productiveRel_of_left
|
||||
exact IterM.TerminationMeasures.Finite.rel_of_skip ‹_›
|
||||
case innerSkip =>
|
||||
apply Flatten.productiveRel_of_right₁
|
||||
exact IterM.TerminationMeasures.Productive.rel_of_skip ‹_›
|
||||
case innerDone =>
|
||||
apply Flatten.productiveRel_of_right₂
|
||||
|
||||
@[no_expose]
|
||||
public instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
|
||||
[Finite α m] [Productive α₂ m] : Productive (Flatten α α₂ β m) m :=
|
||||
.of_productivenessRelation instProductivenessRelationFlattenOfFiniteIterMOfProductive
|
||||
|
||||
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.instIteratorCollectPartial [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
|
||||
[Iterator α₂ m β] : IteratorCollectPartial (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
|
||||
|
||||
public instance Flatten.instIteratorLoopPartial [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
|
||||
[Iterator α₂ m β] : IteratorLoopPartial (Flatten α α₂ β m) m n :=
|
||||
.defaultImplementation
|
||||
|
||||
end Std.Iterators
|
||||
@@ -90,9 +90,9 @@ instance Types.ULiftIterator.instIterator [Iterator α m β] [Monad n] :
|
||||
step = ULiftIterator.Monadic.modifyStep step'
|
||||
step it := do
|
||||
let step := (← (lift it.internalState.inner.step).run).down
|
||||
return ⟨Monadic.modifyStep step.val, ?hp⟩
|
||||
return .deflate ⟨Monadic.modifyStep step.inflate.val, ?hp⟩
|
||||
where finally
|
||||
case hp => exact ⟨step.val, step.property, rfl⟩
|
||||
case hp => exact ⟨step.inflate.val, step.inflate.property, rfl⟩
|
||||
|
||||
def Types.ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m] [Monad n] :
|
||||
FinitenessRelation (ULiftIterator α m n β lift) n where
|
||||
|
||||
@@ -13,5 +13,3 @@ public import Init.Data.Iterators.Consumers.Loop
|
||||
public import Init.Data.Iterators.Consumers.Partial
|
||||
|
||||
public import Init.Data.Iterators.Consumers.Stream
|
||||
|
||||
public section
|
||||
|
||||
@@ -139,6 +139,70 @@ def Iter.Partial.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id
|
||||
(init : γ) (it : Iter.Partial (α := α) β) : γ :=
|
||||
ForIn.forIn (m := Id) it init (fun x acc => ForInStep.yield (f acc x))
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`true` if the monadic predicate {name}`p` returns {lean}`true` for
|
||||
any element emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
-/
|
||||
@[specialize]
|
||||
def Iter.anyM {α β : Type w} {m : Type → Type w'} [Monad m]
|
||||
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
|
||||
(p : β → m Bool) (it : Iter (α := α) β) : m Bool :=
|
||||
ForIn.forIn it false (fun x _ => do
|
||||
if ← p x then
|
||||
return .done true
|
||||
else
|
||||
return .yield false)
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
|
||||
any element emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
-/
|
||||
@[inline]
|
||||
def Iter.any {α β : Type w}
|
||||
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
|
||||
(p : β → Bool) (it : Iter (α := α) β) : Bool :=
|
||||
(it.anyM (fun x => pure (f := Id) (p x))).run
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`true` if the monadic predicate {name}`p` returns {lean}`true` for
|
||||
all elements emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
-/
|
||||
@[specialize]
|
||||
def Iter.allM {α β : Type w} {m : Type → Type w'} [Monad m]
|
||||
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
|
||||
(p : β → m Bool) (it : Iter (α := α) β) : m Bool :=
|
||||
ForIn.forIn it true (fun x _ => do
|
||||
if ← p x then
|
||||
return .yield true
|
||||
else
|
||||
return .done false)
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
|
||||
all elements emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
-/
|
||||
@[inline]
|
||||
def Iter.all {α β : Type w}
|
||||
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
|
||||
(p : β → Bool) (it : Iter (α := α) β) : Bool :=
|
||||
(it.allM (fun x => pure (f := Id) (p x))).run
|
||||
|
||||
@[always_inline, inline, expose, inherit_doc IterM.size]
|
||||
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSize α Id]
|
||||
(it : Iter (α := α) β) : Nat :=
|
||||
|
||||
@@ -10,5 +10,3 @@ public import Init.Data.Iterators.Consumers.Monadic.Access
|
||||
public import Init.Data.Iterators.Consumers.Monadic.Collect
|
||||
public import Init.Data.Iterators.Consumers.Monadic.Loop
|
||||
public import Init.Data.Iterators.Consumers.Monadic.Partial
|
||||
|
||||
public section
|
||||
|
||||
@@ -91,7 +91,7 @@ def IterM.DefaultConsumers.toArrayMapped {α β : Type w} {m : Type w → Type w
|
||||
where
|
||||
@[specialize]
|
||||
go [Monad n] [Finite α m] (it : IterM (α := α) m β) a := letI : MonadLift m n := ⟨lift (α := _)⟩; do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' b _ => go it' (a.push (← f b))
|
||||
| .skip it' _ => go it' a
|
||||
| .done _ => return a
|
||||
@@ -150,7 +150,7 @@ partial def IterM.DefaultConsumers.toArrayMappedPartial {α β : Type w} {m : Ty
|
||||
where
|
||||
@[specialize]
|
||||
go [Monad n] (it : IterM (α := α) m β) a := letI : MonadLift m n := ⟨lift⟩; do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' b _ => go it' (a.push (← f b))
|
||||
| .skip it' _ => go it' a
|
||||
| .done _ => return a
|
||||
@@ -209,7 +209,7 @@ def IterM.toListRev {α : Type w} {m : Type w → Type w'} [Monad m] {β : Type
|
||||
go it []
|
||||
where
|
||||
go [Finite α m] it bs := do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' b _ => go it' (b :: bs)
|
||||
| .skip it' _ => go it' bs
|
||||
| .done _ => return bs
|
||||
@@ -229,7 +229,7 @@ partial def IterM.Partial.toListRev {α : Type w} {m : Type w → Type w'} [Mona
|
||||
where
|
||||
@[specialize]
|
||||
go it bs := do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' b _ => go it' (b :: bs)
|
||||
| .skip it' _ => go it' bs
|
||||
| .done _ => return bs
|
||||
|
||||
@@ -142,7 +142,8 @@ def IterM.DefaultConsumers.forIn' {m : Type w → Type w'} {α : Type w} {β : T
|
||||
(P : β → Prop) (hP : ∀ b, it.IsPlausibleIndirectOutput b → P b)
|
||||
(f : (b : β) → P b → (c : γ) → n (Subtype (plausible_forInStep b c))) : n γ :=
|
||||
haveI : WellFounded _ := wf
|
||||
(lift _ _ · it.step) fun
|
||||
(lift _ _ · it.step) fun s =>
|
||||
match s.inflate with
|
||||
| .yield it' out h => do
|
||||
match ← f out (hP _ <| .direct ⟨_, h⟩) init with
|
||||
| ⟨.yield c, _⟩ =>
|
||||
@@ -220,7 +221,8 @@ partial def IterM.DefaultConsumers.forInPartial {m : Type w → Type w'} {α : T
|
||||
(lift : ∀ γ δ, (γ → n δ) → m γ → n δ) (γ : Type x)
|
||||
(it : IterM (α := α) m β) (init : γ)
|
||||
(f : (b : β) → it.IsPlausibleIndirectOutput b → (c : γ) → n (ForInStep γ)) : n γ :=
|
||||
(lift _ _ · it.step) fun
|
||||
(lift _ _ · it.step) fun s =>
|
||||
match s.inflate with
|
||||
| .yield it' out h => do
|
||||
match ← f out (.direct ⟨_, h⟩) init with
|
||||
| .yield c =>
|
||||
@@ -416,6 +418,169 @@ def IterM.Partial.drain {α : Type w} {m : Type w → Type w'} [Monad m] {β : T
|
||||
m PUnit :=
|
||||
it.fold (γ := PUnit) (fun _ _ => .unit) .unit
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
|
||||
any element emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This function requires a {name}`Finite` instance proving that the iterator will finish after a
|
||||
finite number of steps. If the iterator is not finite or such an instance is not available,
|
||||
consider using {lit}`it.allowNontermination.anyM` instead of {lean}`it.anyM`. However, it is not
|
||||
possible to formally verify the behavior of the partial variant.
|
||||
-/
|
||||
@[specialize]
|
||||
def IterM.anyM {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
|
||||
(p : β → m (ULift Bool)) (it : IterM (α := α) m β) : m (ULift Bool) :=
|
||||
ForIn.forIn it (ULift.up false) (fun x _ => do
|
||||
if (← p x).down then
|
||||
return .done (.up true)
|
||||
else
|
||||
return .yield (.up false))
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
|
||||
any element emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This is a partial, potentially nonterminating, function. It is not possible to formally verify
|
||||
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.anyM`
|
||||
instead.
|
||||
-/
|
||||
@[specialize]
|
||||
def IterM.Partial.anyM {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoopPartial α m m]
|
||||
(p : β → m (ULift Bool)) (it : IterM.Partial (α := α) m β) : m (ULift Bool) :=
|
||||
ForIn.forIn it (ULift.up false) (fun x _ => do
|
||||
if (← p x).down then
|
||||
return .done (.up true)
|
||||
else
|
||||
return .yield (.up false))
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
|
||||
any element emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This function requires a {name}`Finite` instance proving that the iterator will finish after a
|
||||
finite number of steps. If the iterator is not finite or such an instance is not available,
|
||||
consider using {lit}`it.allowNontermination.any` instead of {lean}`it.any`. However, it is not
|
||||
possible to formally verify the behavior of the partial variant.
|
||||
-/
|
||||
@[inline]
|
||||
def IterM.any {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
|
||||
(p : β → Bool) (it : IterM (α := α) m β) : m (ULift Bool) := do
|
||||
it.anyM (fun x => pure (.up (p x)))
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
|
||||
any element emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This is a partial, potentially nonterminating, function. It is not possible to formally verify
|
||||
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.any`
|
||||
instead.
|
||||
-/
|
||||
@[inline]
|
||||
def IterM.Partial.any {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoopPartial α m m]
|
||||
(p : β → Bool) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do
|
||||
it.anyM (fun x => pure (.up (p x)))
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
|
||||
all elements emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This function requires a {name}`Finite` instance proving that the iterator will finish after a
|
||||
finite number of steps. If the iterator is not finite or such an instance is not available,
|
||||
consider using {lit}`it.allowNontermination.allM` instead of {lean}`it.allM`. However, it is not
|
||||
possible to formally verify the behavior of the partial variant.
|
||||
-/
|
||||
@[specialize]
|
||||
def IterM.allM {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
|
||||
(p : β → m (ULift Bool)) (it : IterM (α := α) m β) : m (ULift Bool) := do
|
||||
ForIn.forIn it (ULift.up true) (fun x _ => do
|
||||
if (← p x).down then
|
||||
return .yield (.up true)
|
||||
else
|
||||
return .done (.up false))
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the monadic predicate {name}`p` returns {lean}`ULift.up true` for
|
||||
all elements emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This is a partial, potentially nonterminating, function. It is not possible to formally verify
|
||||
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.allM`
|
||||
instead.
|
||||
-/
|
||||
@[specialize]
|
||||
def IterM.Partial.allM {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoopPartial α m m]
|
||||
(p : β → m (ULift Bool)) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do
|
||||
ForIn.forIn it (ULift.up true) (fun x _ => do
|
||||
if (← p x).down then
|
||||
return .yield (.up true)
|
||||
else
|
||||
return .done (.up false))
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
|
||||
all elements emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This function requires a {name}`Finite` instance proving that the iterator will finish after a
|
||||
finite number of steps. If the iterator is not finite or such an instance is not available,
|
||||
consider using {lit}`it.allowNontermination.all` instead of {lean}`it.all`. However, it is not
|
||||
possible to formally verify the behavior of the partial variant.
|
||||
-/
|
||||
@[inline]
|
||||
def IterM.all {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
|
||||
(p : β → Bool) (it : IterM (α := α) m β) : m (ULift Bool) := do
|
||||
it.allM (fun x => pure (.up (p x)))
|
||||
|
||||
set_option doc.verso true in
|
||||
/--
|
||||
Returns {lean}`ULift.up true` if the pure predicate {name}`p` returns {lean}`true` for
|
||||
all elements emitted by the iterator {name}`it`.
|
||||
|
||||
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
|
||||
examined in order of iteration.
|
||||
|
||||
This is a partial, potentially nonterminating, function. It is not possible to formally verify
|
||||
its behavior. If the iterator has a {name}`Finite` instance, consider using {name}`IterM.all`
|
||||
instead.
|
||||
-/
|
||||
@[inline]
|
||||
def IterM.Partial.all {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [IteratorLoopPartial α m m]
|
||||
(p : β → Bool) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do
|
||||
it.allM (fun x => pure (.up (p x)))
|
||||
|
||||
section Size
|
||||
|
||||
/--
|
||||
|
||||
@@ -8,5 +8,3 @@ module
|
||||
prelude
|
||||
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
|
||||
public import Init.Data.Iterators.Internal.Termination
|
||||
|
||||
public section
|
||||
|
||||
@@ -8,5 +8,3 @@ module
|
||||
prelude
|
||||
public import Init.Data.Iterators.Lemmas.Consumers
|
||||
public import Init.Data.Iterators.Lemmas.Combinators
|
||||
|
||||
public section
|
||||
|
||||
@@ -9,6 +9,5 @@ prelude
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.Attach
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.Monadic
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.FilterMap
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.FlatMap
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.ULift
|
||||
|
||||
public section
|
||||
|
||||
@@ -62,18 +62,18 @@ theorem Iter.step_filterMapWithPostcondition {f : β → PostconditionT n (Optio
|
||||
| .yield it' out h => do
|
||||
match ← (f out).operation with
|
||||
| ⟨none, h'⟩ =>
|
||||
pure <| .skip (it'.filterMapWithPostcondition f) (.yieldNone (out := out) h h')
|
||||
pure <| .deflate <| .skip (it'.filterMapWithPostcondition f) (.yieldNone (out := out) h h')
|
||||
| ⟨some out', h'⟩ =>
|
||||
pure <| .yield (it'.filterMapWithPostcondition f) out' (.yieldSome (out := out) h h')
|
||||
pure <| .deflate <| .yield (it'.filterMapWithPostcondition f) out' (.yieldSome (out := out) h h')
|
||||
| .skip it' h =>
|
||||
pure <| .skip (it'.filterMapWithPostcondition f) (.skip h)
|
||||
pure <| .deflate <| .skip (it'.filterMapWithPostcondition f) (.skip h)
|
||||
| .done h =>
|
||||
pure <| .done (.done h)) := by
|
||||
pure <| .deflate <| .done (.done h)) := by
|
||||
simp only [filterMapWithPostcondition_eq_toIter_filterMapWithPostcondition_toIterM, IterM.step_filterMapWithPostcondition,
|
||||
step]
|
||||
simp only [liftM, monadLift, pure_bind]
|
||||
generalize it.toIterM.step = step
|
||||
match step with
|
||||
match step.inflate with
|
||||
| .yield it' out h =>
|
||||
apply bind_congr
|
||||
intro step
|
||||
@@ -88,17 +88,17 @@ theorem Iter.step_filterWithPostcondition {f : β → PostconditionT n (ULift Bo
|
||||
| .yield it' out h => do
|
||||
match ← (f out).operation with
|
||||
| ⟨.up false, h'⟩ =>
|
||||
pure <| .skip (it'.filterWithPostcondition f) (.yieldNone (out := out) h ⟨⟨_, h'⟩, rfl⟩)
|
||||
pure <| .deflate <| .skip (it'.filterWithPostcondition f) (.yieldNone (out := out) h ⟨⟨_, h'⟩, rfl⟩)
|
||||
| ⟨.up true, h'⟩ =>
|
||||
pure <| .yield (it'.filterWithPostcondition f) out (.yieldSome (out := out) h ⟨⟨_, h'⟩, rfl⟩)
|
||||
pure <| .deflate <| .yield (it'.filterWithPostcondition f) out (.yieldSome (out := out) h ⟨⟨_, h'⟩, rfl⟩)
|
||||
| .skip it' h =>
|
||||
pure <| .skip (it'.filterWithPostcondition f) (.skip h)
|
||||
pure <| .deflate <| .skip (it'.filterWithPostcondition f) (.skip h)
|
||||
| .done h =>
|
||||
pure <| .done (.done h)) := by
|
||||
pure <| .deflate <| .done (.done h)) := by
|
||||
simp only [filterWithPostcondition_eq_toIter_filterMapWithPostcondition_toIterM, IterM.step_filterWithPostcondition, step]
|
||||
simp only [liftM, monadLift, pure_bind]
|
||||
generalize it.toIterM.step = step
|
||||
match step with
|
||||
match step.inflate with
|
||||
| .yield it' out h =>
|
||||
apply bind_congr
|
||||
intro step
|
||||
@@ -112,15 +112,15 @@ theorem Iter.step_mapWithPostcondition {f : β → PostconditionT n γ}
|
||||
match it.step with
|
||||
| .yield it' out h => do
|
||||
let out' ← (f out).operation
|
||||
pure <| .yield (it'.mapWithPostcondition f) out'.1 (.yieldSome h ⟨out', rfl⟩)
|
||||
pure <| .deflate <| .yield (it'.mapWithPostcondition f) out'.1 (.yieldSome h ⟨out', rfl⟩)
|
||||
| .skip it' h =>
|
||||
pure <| .skip (it'.mapWithPostcondition f) (.skip h)
|
||||
pure <| .deflate <| .skip (it'.mapWithPostcondition f) (.skip h)
|
||||
| .done h =>
|
||||
pure <| .done (.done h)) := by
|
||||
pure <| .deflate <| .done (.done h)) := by
|
||||
simp only [mapWithPostcondition_eq_toIter_mapWithPostcondition_toIterM, IterM.step_mapWithPostcondition, step]
|
||||
simp only [liftM, monadLift, pure_bind]
|
||||
generalize it.toIterM.step = step
|
||||
match step with
|
||||
match step.inflate with
|
||||
| .yield it' out h =>
|
||||
simp only [bind_pure_comp]
|
||||
rfl
|
||||
@@ -134,17 +134,17 @@ theorem Iter.step_filterMapM {β' : Type w} {f : β → n (Option β')}
|
||||
| .yield it' out h => do
|
||||
match ← f out with
|
||||
| none =>
|
||||
pure <| .skip (it'.filterMapM f) (.yieldNone (out := out) h .intro)
|
||||
pure <| .deflate <| .skip (it'.filterMapM f) (.yieldNone (out := out) h .intro)
|
||||
| some out' =>
|
||||
pure <| .yield (it'.filterMapM f) out' (.yieldSome (out := out) h .intro)
|
||||
pure <| .deflate <| .yield (it'.filterMapM f) out' (.yieldSome (out := out) h .intro)
|
||||
| .skip it' h =>
|
||||
pure <| .skip (it'.filterMapM f) (.skip h)
|
||||
pure <| .deflate <| .skip (it'.filterMapM f) (.skip h)
|
||||
| .done h =>
|
||||
pure <| .done (.done h)) := by
|
||||
pure <| .deflate <| .done (.done h)) := by
|
||||
simp only [filterMapM_eq_toIter_filterMapM_toIterM, IterM.step_filterMapM, step]
|
||||
simp only [liftM, monadLift, pure_bind]
|
||||
generalize it.toIterM.step = step
|
||||
match step with
|
||||
match step.inflate with
|
||||
| .yield it' out h =>
|
||||
apply bind_congr
|
||||
intro step
|
||||
@@ -159,17 +159,17 @@ theorem Iter.step_filterM {f : β → n (ULift Bool)}
|
||||
| .yield it' out h => do
|
||||
match ← f out with
|
||||
| .up false =>
|
||||
pure <| .skip (it'.filterM f) (.yieldNone (out := out) h ⟨⟨.up false, .intro⟩, rfl⟩)
|
||||
pure <| .deflate <| .skip (it'.filterM f) (.yieldNone (out := out) h ⟨⟨.up false, .intro⟩, rfl⟩)
|
||||
| .up true =>
|
||||
pure <| .yield (it'.filterM f) out (.yieldSome (out := out) h ⟨⟨.up true, .intro⟩, rfl⟩)
|
||||
pure <| .deflate <| .yield (it'.filterM f) out (.yieldSome (out := out) h ⟨⟨.up true, .intro⟩, rfl⟩)
|
||||
| .skip it' h =>
|
||||
pure <| .skip (it'.filterM f) (.skip h)
|
||||
pure <| .deflate <| .skip (it'.filterM f) (.skip h)
|
||||
| .done h =>
|
||||
pure <| .done (.done h)) := by
|
||||
pure <| .deflate <| .done (.done h)) := by
|
||||
simp only [filterM_eq_toIter_filterM_toIterM, IterM.step_filterM, step]
|
||||
simp only [liftM, monadLift, pure_bind]
|
||||
generalize it.toIterM.step = step
|
||||
match step with
|
||||
match step.inflate with
|
||||
| .yield it' out h =>
|
||||
simp [PostconditionT.lift]
|
||||
apply bind_congr
|
||||
@@ -179,24 +179,22 @@ theorem Iter.step_filterM {f : β → n (ULift Bool)}
|
||||
| .done h => rfl
|
||||
|
||||
theorem Iter.step_mapM {f : β → n γ}
|
||||
[Monad n] [LawfulMonad n] [MonadLiftT m n] :
|
||||
[Monad n] [LawfulMonad n] :
|
||||
(it.mapM f).step = (do
|
||||
match it.step with
|
||||
| .yield it' out h => do
|
||||
let out' ← f out
|
||||
pure <| .yield (it'.mapM f) out' (.yieldSome h ⟨⟨out', True.intro⟩, rfl⟩)
|
||||
pure <| .deflate <| .yield (it'.mapM f) out' (.yieldSome h ⟨⟨out', True.intro⟩, rfl⟩)
|
||||
| .skip it' h =>
|
||||
pure <| .skip (it'.mapM f) (.skip h)
|
||||
pure <| .deflate <| .skip (it'.mapM f) (.skip h)
|
||||
| .done h =>
|
||||
pure <| .done (.done h)) := by
|
||||
pure <| .deflate <| .done (.done h)) := by
|
||||
simp only [mapM_eq_toIter_mapM_toIterM, IterM.step_mapM, step]
|
||||
simp only [liftM, monadLift, pure_bind]
|
||||
generalize it.toIterM.step = step
|
||||
match step with
|
||||
match step.inflate with
|
||||
| .yield it' out h =>
|
||||
simp only [bind_pure_comp]
|
||||
simp only [Functor.map,
|
||||
]
|
||||
rfl
|
||||
| .skip it' h => rfl
|
||||
| .done h => rfl
|
||||
@@ -212,14 +210,32 @@ theorem Iter.step_filterMap {f : β → Option γ} :
|
||||
simp only [filterMap_eq_toIter_filterMap_toIterM, toIterM_toIter, IterM.step_filterMap, step]
|
||||
simp only [monadLift, Id.run_bind]
|
||||
generalize it.toIterM.step.run = step
|
||||
cases step using PlausibleIterStep.casesOn
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· simp only [IterM.Step.toPure_yield, toIter_toIterM, toIterM_toIter]
|
||||
split <;> split <;> (try exfalso; simp_all; done)
|
||||
· rfl
|
||||
· simp
|
||||
· rename_i h₁ _ h₂
|
||||
rw [h₁] at h₂
|
||||
cases h₂
|
||||
rfl
|
||||
simp
|
||||
· simp
|
||||
· simp
|
||||
|
||||
/--
|
||||
a weaker version of `step_filterMap` that does not use dependent `match`
|
||||
-/
|
||||
theorem Iter.val_step_filterMap {f : β → Option γ} :
|
||||
(it.filterMap f).step.val = match it.step.val with
|
||||
| .yield it' out =>
|
||||
match f out with
|
||||
| none => .skip (it'.filterMap f)
|
||||
| some out' => .yield (it'.filterMap f) out'
|
||||
| .skip it' => .skip (it'.filterMap f)
|
||||
| .done => .done := by
|
||||
simp [step_filterMap]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp only
|
||||
split <;> simp_all
|
||||
· simp
|
||||
· simp
|
||||
|
||||
@@ -233,7 +249,7 @@ theorem Iter.step_map {f : β → γ} :
|
||||
.done (.done h) := by
|
||||
simp only [map_eq_toIter_map_toIterM, step, toIterM_toIter, IterM.step_map, Id.run_bind]
|
||||
generalize it.toIterM.step.run = step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
|
||||
def Iter.step_filter {f : β → Bool} :
|
||||
(it.filter f).step = match it.step with
|
||||
@@ -248,7 +264,26 @@ def Iter.step_filter {f : β → Bool} :
|
||||
.done (.done h) := by
|
||||
simp only [filter_eq_toIter_filter_toIterM, step, toIterM_toIter, IterM.step_filter, Id.run_bind]
|
||||
generalize it.toIterM.step.run = step
|
||||
cases step using PlausibleIterStep.casesOn
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· simp only
|
||||
split <;> simp [*]
|
||||
· simp
|
||||
· simp
|
||||
|
||||
def Iter.val_step_filter {f : β → Bool} :
|
||||
(it.filter f).step.val = match it.step.val with
|
||||
| .yield it' out =>
|
||||
if f out = true then
|
||||
.yield (it'.filter f) out
|
||||
else
|
||||
.skip (it'.filter f)
|
||||
| .skip it' =>
|
||||
.skip (it'.filter f)
|
||||
| .done =>
|
||||
.done := by
|
||||
simp only [filter_eq_toIter_filter_toIterM, step, toIterM_toIter, IterM.step_filter, Id.run_bind]
|
||||
generalize it.toIterM.step.run = step
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· simp only
|
||||
split <;> simp [*]
|
||||
· simp
|
||||
@@ -317,4 +352,432 @@ theorem Iter.toArray_filter
|
||||
(it.filter f).toArray = it.toArray.filter f := by
|
||||
simp [filter_eq_toIter_filter_toIterM, IterM.toArray_filter, Iter.toArray_eq_toArray_toIterM]
|
||||
|
||||
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 (α := α) β} :
|
||||
(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]
|
||||
|
||||
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 (α := α) β} :
|
||||
(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]
|
||||
|
||||
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 (α := α) β} :
|
||||
(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 [*]
|
||||
|
||||
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 (α := α) β} :
|
||||
(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
|
||||
|
||||
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 (α := α) β} :
|
||||
(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
|
||||
|
||||
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 (α := α) β} :
|
||||
(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]
|
||||
|
||||
theorem Iter.fold_filterMap {α β γ : Type w} {δ : Type x}
|
||||
[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
|
||||
|
||||
theorem Iter.fold_map {α β γ : Type w} {δ : Type x}
|
||||
[Iterator α Id β] [Finite α Id]
|
||||
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{f : β → γ} {g : δ → γ → δ} {init : δ} {it : Iter (α := α) β} :
|
||||
(it.map f).fold (init := init) g =
|
||||
it.fold (init := init) (fun d b => g d (f b)) := by
|
||||
simp [fold_eq_foldM, foldM_map]
|
||||
|
||||
end Fold
|
||||
|
||||
theorem Iter.anyM_filterMapM {α β β' : Type w} {m : Type w → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad 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
|
||||
| some fx => p fx
|
||||
| none => return .up false) := by
|
||||
simp only [filterMapM_eq_toIter_filterMapM_toIterM, IterM.anyM_filterMapM]
|
||||
rfl
|
||||
|
||||
-- There is hope to generalize the following theorem as soon there is a `Shrink` type.
|
||||
/--
|
||||
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]
|
||||
{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]
|
||||
apply bind_congr; intro px
|
||||
split
|
||||
· simp
|
||||
· simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.anyM_mapM {α β β' : Type w} {m : Type w → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad 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]
|
||||
{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
|
||||
p x
|
||||
else
|
||||
return .up false) := by
|
||||
rw [filterM_eq_toIter_filterM_toIterM, IterM.anyM_filterM, mapM_eq_toIter_mapM_toIterM]
|
||||
|
||||
theorem Iter.anyM_filterMap {α β β' : Type w} {m : Type → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
|
||||
[LawfulMonad m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {f : β → Option β'} {p : β' → m Bool} :
|
||||
(it.filterMap f).anyM p = it.anyM (fun x => do
|
||||
match f x with
|
||||
| some fx => p fx
|
||||
| none => return false) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [anyM_eq_match_step, anyM_eq_match_step, val_step_filterMap]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· rename_i out _
|
||||
simp only
|
||||
cases f out
|
||||
· simp [ihy ‹_›]
|
||||
· apply bind_congr; intro px
|
||||
split <;> simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.anyM_map {α β β' : Type w} {m : Type → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
|
||||
[LawfulMonad m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {f : β → β'} {p : β' → m Bool} :
|
||||
(it.map f).anyM p = it.anyM (fun x => p (f x)) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [anyM_eq_match_step, anyM_eq_match_step, step_map]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.anyM_filter {α β : Type w} {m : Type → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m][IteratorLoop α Id m]
|
||||
[LawfulMonad m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {f : β → Bool} {p : β → m Bool} :
|
||||
(it.filter f).anyM p = it.anyM (fun x => do
|
||||
if f x then
|
||||
p x
|
||||
else
|
||||
return false) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [anyM_eq_match_step, anyM_eq_match_step, val_step_filter]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· rename_i out _
|
||||
simp only
|
||||
cases f out <;> simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· 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]
|
||||
{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
|
||||
| some fx => return .up (p fx)
|
||||
| none => return .up false) := by
|
||||
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]
|
||||
{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]
|
||||
{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
|
||||
return .up (p x)
|
||||
else
|
||||
return .up false) := by
|
||||
simp [IterM.any_eq_anyM, anyM_filterM]
|
||||
|
||||
theorem Iter.any_filterMap {α β β' : Type w}
|
||||
[Iterator α Id β] [Finite α Id][IteratorLoop α Id Id]
|
||||
[LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {f : β → Option β'} {p : β' → Bool} :
|
||||
(it.filterMap f).any p = it.any (fun x =>
|
||||
match f x with
|
||||
| some fx => (p fx)
|
||||
| none => false) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [any_eq_match_step, any_eq_match_step, val_step_filterMap]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· rename_i out _
|
||||
simp only
|
||||
cases f out
|
||||
· simp [*, ihy ‹_›]
|
||||
· simp only
|
||||
split <;> simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.any_map {α β β' : Type w}
|
||||
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
|
||||
[LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {f : β → β'} {p : β' → Bool} :
|
||||
(it.map f).any p = it.any (fun x => p (f x)) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [any_eq_match_step, any_eq_match_step, step_map]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.allM_filterMapM {α β β' : Type w} {m : Type w → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad 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
|
||||
| some fx => p fx
|
||||
| none => return .up true) := by
|
||||
simp only [filterMapM_eq_toIter_filterMapM_toIterM, IterM.allM_filterMapM]
|
||||
rfl
|
||||
|
||||
/--
|
||||
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} :
|
||||
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
|
||||
|
||||
theorem Iter.allM_mapM {α β β' : Type w} {m : Type w → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad 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]
|
||||
{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
|
||||
p x
|
||||
else
|
||||
return .up true) := by
|
||||
rw [filterM_eq_toIter_filterM_toIterM, IterM.allM_filterM, mapM_eq_toIter_mapM_toIterM]
|
||||
|
||||
theorem Iter.allM_filterMap {α β β' : Type w} {m : Type → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
|
||||
[LawfulMonad m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {f : β → Option β'} {p : β' → m Bool} :
|
||||
(it.filterMap f).allM p = it.allM (fun x => do
|
||||
match f x with
|
||||
| some fx => p fx
|
||||
| none => return true) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [allM_eq_match_step, allM_eq_match_step, val_step_filterMap]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· rename_i out _
|
||||
simp only
|
||||
cases f out
|
||||
· simp [ihy ‹_›]
|
||||
· apply bind_congr; intro px
|
||||
split <;> simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.allM_map {α β β' : Type w} {m : Type → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m] [IteratorLoop α Id m]
|
||||
[LawfulMonad m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {f : β → β'} {p : β' → m Bool} :
|
||||
(it.map f).allM p = it.allM (fun x => p (f x)) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [allM_eq_match_step, allM_eq_match_step, step_map]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.allM_filter {α β : Type w} {m : Type → Type w'}
|
||||
[Iterator α Id β] [Finite α Id] [Monad m][IteratorLoop α Id m]
|
||||
[LawfulMonad m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {f : β → Bool} {p : β → m Bool} :
|
||||
(it.filter f).allM p = it.allM (fun x => do
|
||||
if f x then
|
||||
p x
|
||||
else
|
||||
return true) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [allM_eq_match_step, allM_eq_match_step, val_step_filter]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· rename_i out _
|
||||
simp only
|
||||
cases f out <;> simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· 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]
|
||||
{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
|
||||
| some fx => return .up (p fx)
|
||||
| none => return .up true) := by
|
||||
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} :
|
||||
(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]
|
||||
{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
|
||||
return .up (p x)
|
||||
else
|
||||
return .up true) := by
|
||||
simp [IterM.all_eq_allM, allM_filterM]
|
||||
|
||||
theorem Iter.all_filterMap {α β β' : Type w}
|
||||
[Iterator α Id β] [Finite α Id][IteratorLoop α Id Id]
|
||||
[LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {f : β → Option β'} {p : β' → Bool} :
|
||||
(it.filterMap f).all p = it.all (fun x =>
|
||||
match f x with
|
||||
| some fx => (p fx)
|
||||
| none => true) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [all_eq_match_step, all_eq_match_step, val_step_filterMap]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· rename_i out _
|
||||
simp only
|
||||
cases f out
|
||||
· simp [*, ihy ‹_›]
|
||||
· simp only
|
||||
split <;> simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.all_map {α β β' : Type w}
|
||||
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
|
||||
[LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {f : β → β'} {p : β' → Bool} :
|
||||
(it.map f).all p = it.all (fun x => p (f x)) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs
|
||||
rw [all_eq_match_step, all_eq_match_step, step_map]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp [ihy ‹_›]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
end Std.Iterators
|
||||
|
||||
266
src/Init/Data/Iterators/Lemmas/Combinators/FlatMap.lean
Normal file
266
src/Init/Data/Iterators/Lemmas/Combinators/FlatMap.lean
Normal file
@@ -0,0 +1,266 @@
|
||||
/-
|
||||
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
|
||||
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
|
||||
|
||||
namespace Std.Iterators
|
||||
open Std.Internal
|
||||
|
||||
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 γ]
|
||||
{f : β → m (IterM (α := α₂) m γ)} {it₁ it₁' : Iter (α := α) β} {it₂' b}
|
||||
(h : it₁.IsPlausibleStep (.yield it₁' b)) :
|
||||
(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])
|
||||
|
||||
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 γ]
|
||||
{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 γ]
|
||||
{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 γ]
|
||||
{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 γ]
|
||||
{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 γ]
|
||||
{f : β → m (IterM (α := α₂) m γ)} {it₁ : Iter (α := α) β} {it₂}
|
||||
(h : it₂.IsPlausibleStep .done) :
|
||||
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfterM f none)) :=
|
||||
innerDone_flatMapM h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.outerYield_flatMap_pure {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ it₁' : Iter (α := α) β} {b}
|
||||
(h : it₁.IsPlausibleStep (.yield it₁' b)) :
|
||||
(it₁.flatMapAfter f none).IsPlausibleStep (.skip (it₁'.flatMapAfter f (some (f b)))) :=
|
||||
outerYield_flatMap h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.outerSkip_flatMap_pure {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ it₁' : Iter (α := α) β}
|
||||
(h : it₁.IsPlausibleStep (.skip it₁')) :
|
||||
(it₁.flatMapAfter f none).IsPlausibleStep (.skip (it₁'.flatMapAfter f none)) :=
|
||||
outerSkip_flatMap h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.outerDone_flatMap_pure {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ : Iter (α := α) β}
|
||||
(h : it₁.IsPlausibleStep .done) :
|
||||
(it₁.flatMapAfter f none).IsPlausibleStep .done :=
|
||||
outerDone_flatMap h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.innerYield_flatMap_pure {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂ it₂' b}
|
||||
(h : it₂.IsPlausibleStep (.yield it₂' b)) :
|
||||
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.yield (it₁.flatMapAfter f (some it₂')) b) :=
|
||||
innerYield_flatMap h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.innerSkip_flatMap_pure {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂ it₂'}
|
||||
(h : it₂.IsPlausibleStep (.skip it₂')) :
|
||||
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfter f (some it₂'))) :=
|
||||
innerSkip_flatMap h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.innerDone_flatMap_pure {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂}
|
||||
(h : it₂.IsPlausibleStep .done) :
|
||||
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfter f none)) :=
|
||||
innerDone_flatMap h
|
||||
|
||||
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 γ]
|
||||
{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))
|
||||
| .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₂ =>
|
||||
match (← it₂.step).inflate with
|
||||
| .yield it₂' out h =>
|
||||
return .deflate (.yield (it₁.flatMapAfterM f (some it₂')) out (.innerYield_flatMapM_pure h))
|
||||
| .skip it₂' h =>
|
||||
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]
|
||||
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 γ]
|
||||
{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))
|
||||
| .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]
|
||||
|
||||
public theorem Iter.step_flatMapAfter {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂ : Option (Iter (α := α₂) γ)} :
|
||||
(it₁.flatMapAfter f it₂).step = (match it₂ with
|
||||
| none =>
|
||||
match it₁.step with
|
||||
| .yield it₁' b h =>
|
||||
.skip (it₁'.flatMapAfter f (some (f b))) (.outerYield_flatMap_pure h)
|
||||
| .skip it₁' h => .skip (it₁'.flatMapAfter f none) (.outerSkip_flatMap_pure h)
|
||||
| .done h => .done (.outerDone_flatMap_pure h)
|
||||
| some it₂ =>
|
||||
match it₂.step with
|
||||
| .yield it₂' out h => .yield (it₁.flatMapAfter f (some it₂')) out (.innerYield_flatMap_pure h)
|
||||
| .skip it₂' h => .skip (it₁.flatMapAfter f (some it₂')) (.innerSkip_flatMap_pure h)
|
||||
| .done h => .skip (it₁.flatMapAfter f none) (.innerDone_flatMap_pure h)) := by
|
||||
simp only [flatMapAfter, step, toIterM_toIter, IterM.step_flatMapAfter]
|
||||
cases it₂
|
||||
· simp only [Option.map_eq_map, Option.map_none, Id.run_bind, Option.map_some]
|
||||
cases it₁.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
· rename_i it₂
|
||||
simp only [Option.map_eq_map, Option.map_some, Id.run_bind, Option.map_none]
|
||||
cases it₂.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
|
||||
public theorem Iter.step_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
|
||||
{f : β → Iter (α := α₂) γ} {it₁ : Iter (α := α) β} :
|
||||
(it₁.flatMap f).step = (match it₁.step with
|
||||
| .yield it₁' b h =>
|
||||
.skip (it₁'.flatMapAfter f (some (f b))) (.outerYield_flatMap_pure h)
|
||||
| .skip it₁' h => .skip (it₁'.flatMapAfter f none) (.outerSkip_flatMap_pure h)
|
||||
| .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]
|
||||
{f : β → m (IterM (α := α₂) m γ)}
|
||||
{it₁ : Iter (α := α) β} {it₂ : Option (IterM (α := α₂) m γ)} :
|
||||
(it₁.flatMapAfterM f it₂).toList = do
|
||||
match it₂ with
|
||||
| none => List.flatten <$> (it₁.mapM fun b => do (← f b).toList).toList
|
||||
| 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
|
||||
|
||||
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]
|
||||
{f : β → m (IterM (α := α₂) m γ)}
|
||||
{it₁ : Iter (α := α) β} {it₂ : Option (IterM (α := α₂) m γ)} :
|
||||
(it₁.flatMapAfterM f it₂).toArray = do
|
||||
match it₂ with
|
||||
| none => Array.flatten <$> (it₁.mapM fun b => do (← f b).toArray).toArray
|
||||
| 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
|
||||
|
||||
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]
|
||||
{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]
|
||||
{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]
|
||||
{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]
|
||||
|
||||
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]
|
||||
{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]
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
@@ -8,6 +8,5 @@ module
|
||||
prelude
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Attach
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.FlatMap
|
||||
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.ULift
|
||||
|
||||
public section
|
||||
|
||||
@@ -18,7 +18,7 @@ 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 => ⟨Types.Attach.Monadic.modifyStep (it.attachWith P hP) s, s, rfl⟩) <$> it.step :=
|
||||
(fun s => .deflate ⟨Types.Attach.Monadic.modifyStep (it.attachWith P hP) s.inflate, s.inflate, rfl⟩) <$> it.step :=
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
@@ -32,7 +32,7 @@ theorem IterM.map_unattach_toList_attachWith [Iterator α m β] [Monad m]
|
||||
simp only [bind_pure_comp, bind_map_left, map_bind]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· rename_i it' out hp
|
||||
simp only [IterM.attachWith] at ihy
|
||||
simp [Types.Attach.Monadic.modifyStep,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
344
src/Init/Data/Iterators/Lemmas/Combinators/Monadic/FlatMap.lean
Normal file
344
src/Init/Data/Iterators/Lemmas/Combinators/Monadic/FlatMap.lean
Normal file
@@ -0,0 +1,344 @@
|
||||
/-
|
||||
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
|
||||
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
|
||||
|
||||
theorem IterM.step_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 β)} :
|
||||
(it₁.flattenAfter it₂).step = (do
|
||||
match it₂ with
|
||||
| none =>
|
||||
match (← it₁.step).inflate with
|
||||
| .yield it₁' it₂' h => return .deflate (.skip (it₁'.flattenAfter (some it₂')) (.outerYield h))
|
||||
| .skip it₁' h => return .deflate (.skip (it₁'.flattenAfter none) (.outerSkip h))
|
||||
| .done h => return .deflate (.done (.outerDone h))
|
||||
| some it₂ =>
|
||||
match (← it₂.step).inflate with
|
||||
| .yield it₂' out h => return .deflate (.yield (it₁.flattenAfter (some it₂')) out (.innerYield h))
|
||||
| .skip it₂' h => return .deflate (.skip (it₁.flattenAfter (some it₂')) (.innerSkip h))
|
||||
| .done h => return .deflate (.skip (it₁.flattenAfter none) (.innerDone h))) := by
|
||||
cases it₂
|
||||
all_goals
|
||||
· apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp [IterM.flattenAfter, toIterM]
|
||||
|
||||
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 γ]
|
||||
{f : β → m (IterM (α := α₂) m γ)} {it₁ it₁' : IterM (α := α) m β} {it₂' b}
|
||||
(h : it₁.IsPlausibleStep (.yield it₁' b)) :
|
||||
(it₁.flatMapAfterM f none).IsPlausibleStep (.skip (it₁'.flatMapAfterM f (some it₂'))) :=
|
||||
.outerYield (.yieldSome h ⟨⟨_, trivial⟩, 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 β}
|
||||
(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 β}
|
||||
(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}
|
||||
(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₂'}
|
||||
(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₂}
|
||||
(h : it₂.IsPlausibleStep .done) :
|
||||
(it₁.flatMapAfterM f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfterM f none)) :=
|
||||
.innerDone h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.outerYield_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ it₁' : IterM (α := α) m β} {b}
|
||||
(h : it₁.IsPlausibleStep (.yield it₁' b)) :
|
||||
(it₁.flatMapAfter f none).IsPlausibleStep (.skip (it₁'.flatMapAfter f (some (f b)))) :=
|
||||
.outerYield (.yieldSome h ⟨⟨_, rfl⟩, rfl⟩)
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.outerSkip_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ it₁' : IterM (α := α) m β}
|
||||
(h : it₁.IsPlausibleStep (.skip it₁')) :
|
||||
(it₁.flatMapAfter f none).IsPlausibleStep (.skip (it₁'.flatMapAfter f none)) :=
|
||||
.outerSkip (.skip h)
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.outerDone_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ : IterM (α := α) m β}
|
||||
(h : it₁.IsPlausibleStep .done) :
|
||||
(it₁.flatMapAfter f none).IsPlausibleStep .done :=
|
||||
.outerDone (.done h)
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.innerYield_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ : IterM (α := α) m β} {it₂ it₂' b}
|
||||
(h : it₂.IsPlausibleStep (.yield it₂' b)) :
|
||||
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.yield (it₁.flatMapAfter f (some it₂')) b) :=
|
||||
.innerYield h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.innerSkip_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ : IterM (α := α) m β} {it₂ it₂'}
|
||||
(h : it₂.IsPlausibleStep (.skip it₂')) :
|
||||
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfter f (some it₂'))) :=
|
||||
.innerSkip h
|
||||
|
||||
public theorem Flatten.IsPlausibleStep.innerDone_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ : IterM (α := α) m β} {it₂}
|
||||
(h : it₂.IsPlausibleStep .done) :
|
||||
(it₁.flatMapAfter f (some it₂)).IsPlausibleStep (.skip (it₁.flatMapAfter f none)) :=
|
||||
.innerDone h
|
||||
|
||||
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 γ)} :
|
||||
(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))
|
||||
| .skip it₁' h => return .deflate (.skip (it₁'.flatMapAfterM f none) (.outerSkip_flatMapM h))
|
||||
| .done h => return .deflate (.done (.outerDone_flatMapM h))
|
||||
| some it₂ =>
|
||||
match (← it₂.step).inflate with
|
||||
| .yield it₂' out h => return .deflate (.yield (it₁.flatMapAfterM f (some it₂')) out (.innerYield_flatMapM h))
|
||||
| .skip it₂' h => return .deflate (.skip (it₁.flatMapAfterM f (some it₂')) (.innerSkip_flatMapM h))
|
||||
| .done h => return .deflate (.skip (it₁.flatMapAfterM f none) (.innerDone_flatMapM h))) := by
|
||||
simp only [flatMapAfterM, step_flattenAfter, IterM.step_mapM]
|
||||
split
|
||||
· simp only [bind_assoc]
|
||||
apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> 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 β} :
|
||||
(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))
|
||||
| .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]
|
||||
|
||||
public theorem IterM.step_flatMapAfter {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
|
||||
(it₁.flatMapAfter f it₂).step = (do
|
||||
match it₂ with
|
||||
| none =>
|
||||
match (← it₁.step).inflate with
|
||||
| .yield it₁' b h =>
|
||||
return .deflate (.skip (it₁'.flatMapAfter f (some (f b))) (.outerYield_flatMap h))
|
||||
| .skip it₁' h => return .deflate (.skip (it₁'.flatMapAfter f none) (.outerSkip_flatMap h))
|
||||
| .done h => return .deflate (.done (.outerDone_flatMap h))
|
||||
| some it₂ =>
|
||||
match (← it₂.step).inflate with
|
||||
| .yield it₂' out h => return .deflate (.yield (it₁.flatMapAfter f (some it₂')) out (.innerYield_flatMap h))
|
||||
| .skip it₂' h => return .deflate (.skip (it₁.flatMapAfter f (some it₂')) (.innerSkip_flatMap h))
|
||||
| .done h => return .deflate (.skip (it₁.flatMapAfter f none) (.innerDone_flatMap h))) := by
|
||||
simp only [flatMapAfter, step_flattenAfter, IterM.step_map]
|
||||
split
|
||||
· simp only [bind_assoc]
|
||||
apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
· rfl
|
||||
|
||||
public theorem IterM.step_flatMap {α : Type w} {β : Type w} {α₂ : Type w}
|
||||
{γ : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad m] [Iterator α m β] [Iterator α₂ m γ]
|
||||
{f : β → IterM (α := α₂) m γ} {it₁ : IterM (α := α) m β} :
|
||||
(it₁.flatMap f).step = (do
|
||||
match (← it₁.step).inflate with
|
||||
| .yield it₁' b h =>
|
||||
return .deflate (.skip (it₁'.flatMapAfter f (some (f b))) (.outerYield_flatMap h))
|
||||
| .skip it₁' h => return .deflate (.skip (it₁'.flatMapAfter f none) (.outerSkip_flatMap h))
|
||||
| .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]
|
||||
[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
|
||||
| none => List.flatten <$> (it₁.mapM fun it₂ => it₂.toList).toList
|
||||
| some it₂ => return (← it₂.toList) ++ (← List.flatten <$> (it₁.mapM fun it₂ => it₂.toList).toList) := by
|
||||
induction it₁ using IterM.inductSteps generalizing it₂ with | step it₁ ihy₁ ihs₁ =>
|
||||
have hn : (it₁.flattenAfter none).toList =
|
||||
List.flatten <$> (it₁.mapM fun it₂ => it₂.toList).toList := by
|
||||
rw [toList_eq_match_step, toList_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 [toList_eq_match_step, step_flattenAfter, bind_assoc]
|
||||
simp only
|
||||
rw [toList_eq_match_step, bind_assoc]
|
||||
apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· simp [ihy₂ ‹_›]
|
||||
· simp [ihs₂ ‹_›]
|
||||
· simp [hn]
|
||||
|
||||
theorem IterM.toArray_flattenAfter {α α₂ β : Type w} {m : Type w → Type w'} [Monad m] [LawfulMonad 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]
|
||||
|
||||
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]
|
||||
{f : β → m (IterM (α := α₂) m γ)}
|
||||
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
|
||||
(it₁.flatMapAfterM f it₂).toList = do
|
||||
match it₂ with
|
||||
| none => List.flatten <$> (it₁.mapM fun b => do (← f b).toList).toList
|
||||
| some it₂ => return (← it₂.toList) ++
|
||||
(← 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]
|
||||
{f : β → m (IterM (α := α₂) m γ)}
|
||||
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
|
||||
(it₁.flatMapAfterM f it₂).toArray = do
|
||||
match it₂ with
|
||||
| none => Array.flatten <$> (it₁.mapM fun b => do (← f b).toArray).toArray
|
||||
| some it₂ => return (← it₂.toArray) ++
|
||||
(← 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]
|
||||
{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]
|
||||
{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]
|
||||
{f : β → IterM (α := α₂) m γ}
|
||||
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
|
||||
(it₁.flatMapAfter f it₂).toList = do
|
||||
match it₂ with
|
||||
| none => List.flatten <$> (it₁.mapM fun b => (f b).toList).toList
|
||||
| some it₂ => return (← it₂.toList) ++
|
||||
(← 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]
|
||||
{f : β → IterM (α := α₂) m γ}
|
||||
{it₁ : IterM (α := α) m β} {it₂ : Option (IterM (α := α₂) m γ)} :
|
||||
(it₁.flatMapAfter f it₂).toArray = do
|
||||
match it₂ with
|
||||
| none => Array.flatten <$> (it₁.mapM fun b => (f b).toArray).toArray
|
||||
| some it₂ => return (← it₂.toArray) ++
|
||||
(← 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]
|
||||
[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]
|
||||
[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
|
||||
@@ -21,7 +21,7 @@ theorem IterM.step_uLift [Iterator α m β] [Monad n] {it : IterM (α := α) m
|
||||
[MonadLiftT m (ULiftT n)] :
|
||||
(it.uLift n).step = (do
|
||||
let step := (← (monadLift it.step : ULiftT n _).run).down
|
||||
return ⟨Types.ULiftIterator.Monadic.modifyStep step.val, step.val, step.property, rfl⟩) :=
|
||||
return .deflate ⟨Types.ULiftIterator.Monadic.modifyStep step.inflate.val, step.inflate.val, step.inflate.property, rfl⟩) :=
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
@@ -37,7 +37,7 @@ theorem IterM.toList_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (
|
||||
apply bind_congr
|
||||
intro step
|
||||
simp [Types.ULiftIterator.Monadic.modifyStep]
|
||||
cases step.down using PlausibleIterStep.casesOn
|
||||
cases step.down.inflate using PlausibleIterStep.casesOn
|
||||
· simp only [uLift] at ihy
|
||||
simp [ihy ‹_›]
|
||||
· exact ihs ‹_›
|
||||
|
||||
@@ -9,5 +9,3 @@ 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 section
|
||||
|
||||
@@ -77,7 +77,7 @@ theorem Iter.toArray_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [I
|
||||
simp only [Iter.toArray_eq_toArray_toIterM, Iter.step]
|
||||
rw [IterM.toArray_eq_match_step, Id.run_bind]
|
||||
generalize it.toIterM.step.run = step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
|
||||
theorem Iter.toList_eq_match_step {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
|
||||
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
|
||||
@@ -95,7 +95,7 @@ theorem Iter.toListRev_eq_match_step {α β} [Iterator α Id β] [Finite α Id]
|
||||
| .done => [] := by
|
||||
rw [Iter.toListRev_eq_toListRev_toIterM, IterM.toListRev_eq_match_step, Iter.step, Id.run_bind]
|
||||
generalize it.toIterM.step.run = step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
|
||||
theorem Iter.getElem?_toList_eq_atIdxSlow? {α β}
|
||||
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
|
||||
|
||||
@@ -44,20 +44,32 @@ theorem Iter.forIn_eq {α β : Type w} [Iterator α Id β] [Finite α Id]
|
||||
f out acc) := by
|
||||
simp [ForIn.forIn, forIn'_eq, -forIn'_eq_forIn]
|
||||
|
||||
@[congr] theorem Iter.forIn'_congr {α β : Type w}
|
||||
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
|
||||
@[congr] theorem Iter.forIn'_congr {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
|
||||
{ita itb : Iter (α := α) β} (w : ita = itb)
|
||||
{b b' : γ} (hb : b = b')
|
||||
{f : (a' : β) → _ → γ → Id (ForInStep γ)}
|
||||
{g : (a' : β) → _ → γ → Id (ForInStep γ)}
|
||||
{f : (a' : β) → _ → γ → m (ForInStep γ)}
|
||||
{g : (a' : β) → _ → γ → m (ForInStep γ)}
|
||||
(h : ∀ a m b, f a (by simpa [w] using m) b = g a m b) :
|
||||
letI : ForIn' Id (Iter (α := α) β) β _ := Iter.instForIn'
|
||||
letI : ForIn' m (Iter (α := α) β) β _ := Iter.instForIn'
|
||||
forIn' ita b f = forIn' itb b' g := by
|
||||
subst_eqs
|
||||
simp only [← funext_iff] at h
|
||||
rw [← h]
|
||||
rfl
|
||||
|
||||
@[congr] theorem Iter.forIn_congr {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id m]
|
||||
{ita itb : Iter (α := α) β} (w : ita = itb)
|
||||
{b b' : γ} (hb : b = b')
|
||||
{f : (a' : β) → γ → m (ForInStep γ)}
|
||||
{g : (a' : β) → γ → m (ForInStep γ)}
|
||||
(h : ∀ a b, f a b = g a b) :
|
||||
forIn ita b f = forIn itb b' g := by
|
||||
subst_eqs
|
||||
simp only [← funext_iff] at h
|
||||
rw [← h]
|
||||
|
||||
theorem Iter.forIn'_eq_forIn'_toIterM {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] {m : Type w → Type w''} [Monad m] [LawfulMonad m]
|
||||
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
@@ -100,7 +112,7 @@ theorem Iter.forIn'_eq_match_step {α β : Type w} [Iterator α Id β]
|
||||
simp only [forIn'_eq]
|
||||
rw [IterM.DefaultConsumers.forIn'_eq_match_step]
|
||||
simp only [bind_map_left, Iter.step]
|
||||
cases it.toIterM.step.run using PlausibleIterStep.casesOn
|
||||
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn
|
||||
· simp only [IterM.Step.toPure_yield, PlausibleIterStep.yield, toIter_toIterM, toIterM_toIter]
|
||||
apply bind_congr
|
||||
intro forInStep
|
||||
@@ -485,4 +497,236 @@ theorem Iter.length_toListRev_eq_size {α β : Type w} [Iterator α Id β] [Fini
|
||||
it.toListRev.length = it.size := by
|
||||
rw [toListRev_eq, List.length_reverse, length_toList_eq_size]
|
||||
|
||||
theorem Iter.anyM_eq_forIn {α β : Type w} {m : Type → Type w'} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {p : β → m Bool} :
|
||||
it.anyM p = (ForIn.forIn it false (fun x _ => do
|
||||
if ← p x then
|
||||
return .done true
|
||||
else
|
||||
return .yield false)) := by
|
||||
rfl
|
||||
|
||||
theorem Iter.anyM_eq_match_step {α β : Type w} {m : Type → Type w'} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {p : β → m Bool} :
|
||||
it.anyM p = (do
|
||||
match it.step.val with
|
||||
| .yield it' x =>
|
||||
if (← p x) then
|
||||
return true
|
||||
else
|
||||
it'.anyM p
|
||||
| .skip it' => it'.anyM p
|
||||
| .done => return false) := by
|
||||
rw [anyM_eq_forIn, forIn_eq_match_step]
|
||||
simp only [bind_assoc]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· apply bind_congr; intro px
|
||||
split
|
||||
· simp
|
||||
· simp [anyM_eq_forIn]
|
||||
· simp [anyM_eq_forIn]
|
||||
· simp
|
||||
|
||||
theorem Iter.anyM_toList {α β : Type w} {m : Type → Type w'} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → m Bool} :
|
||||
it.toList.anyM p = it.anyM p := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs =>
|
||||
rw [it.toList_eq_match_step, anyM_eq_match_step]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp only [List.anyM_cons, ihy ‹_›]
|
||||
· simp only [ihs ‹_›]
|
||||
· simp only [List.anyM_nil]
|
||||
|
||||
theorem Iter.anyM_toArray {α β : Type w} {m : Type → Type w'} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → m Bool} :
|
||||
it.toArray.anyM p = it.anyM p := by
|
||||
simp only [← Iter.toArray_toList, List.anyM_toArray, anyM_toList]
|
||||
|
||||
theorem Iter.any_eq_anyM {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.any p = (it.anyM (fun x => pure (f := Id) (p x))).run := by
|
||||
rfl
|
||||
|
||||
theorem Iter.anyM_pure {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.anyM (fun x => pure (f := Id) (p x)) = pure (it.any (fun x => p x)) := by
|
||||
simp [any_eq_anyM]
|
||||
|
||||
theorem Iter.any_eq_match_step {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.any p = (match it.step.val with
|
||||
| .yield it' x =>
|
||||
if p x then
|
||||
true
|
||||
else
|
||||
it'.any p
|
||||
| .skip it' => it'.any p
|
||||
| .done => false) := by
|
||||
rw [any_eq_anyM, anyM_eq_match_step]
|
||||
split
|
||||
· simp only [pure_bind, Bool.if_true_left, Bool.decide_eq_true, any_eq_anyM]
|
||||
split <;> simp [*]
|
||||
· simp [any_eq_anyM]
|
||||
· simp
|
||||
|
||||
theorem Iter.any_eq_forIn {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.any p = (ForIn.forIn (m := Id) it false (fun x _ => do
|
||||
if p x then
|
||||
return .done true
|
||||
else
|
||||
return .yield false)).run := by
|
||||
simp [any_eq_anyM, anyM_eq_forIn]
|
||||
|
||||
theorem Iter.any_toList {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.toList.any p = it.any p := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs =>
|
||||
rw [it.toList_eq_match_step, any_eq_match_step]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp only [List.any_cons, ihy ‹_›]
|
||||
split <;> simp [*]
|
||||
· simp only [ihs ‹_›]
|
||||
· simp only [List.any_nil]
|
||||
|
||||
theorem Iter.any_toArray {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.toArray.any p = it.any p := by
|
||||
simp only [← Iter.toArray_toList, List.any_toArray, any_toList]
|
||||
|
||||
theorem Iter.allM_eq_forIn {α β : Type w} {m : Type → Type w'} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {p : β → m Bool} :
|
||||
it.allM p = (ForIn.forIn it true (fun x _ => do
|
||||
if ← p x then
|
||||
return .yield true
|
||||
else
|
||||
return .done false)) := by
|
||||
rfl
|
||||
|
||||
theorem Iter.allM_eq_match_step {α β : Type w} {m : Type → Type w'} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {p : β → m Bool} :
|
||||
it.allM p = (do
|
||||
match it.step.val with
|
||||
| .yield it' x =>
|
||||
if (← p x) then
|
||||
it'.allM p
|
||||
else
|
||||
return false
|
||||
| .skip it' => it'.allM p
|
||||
| .done => return true) := by
|
||||
rw [allM_eq_forIn, forIn_eq_match_step]
|
||||
simp only [bind_assoc]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· apply bind_congr; intro px
|
||||
split
|
||||
· simp [allM_eq_forIn]
|
||||
· simp
|
||||
· simp [allM_eq_forIn]
|
||||
· simp
|
||||
|
||||
theorem Iter.all_eq_allM {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.all p = (it.allM (fun x => pure (f := Id) (p x))).run := by
|
||||
rfl
|
||||
|
||||
theorem Iter.allM_pure {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.allM (fun x => pure (f := Id) (p x)) = pure (it.all (fun x => p x)) := by
|
||||
simp [all_eq_allM]
|
||||
|
||||
theorem Iter.all_eq_match_step {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.all p = (match it.step.val with
|
||||
| .yield it' x =>
|
||||
if p x then
|
||||
it'.all p
|
||||
else
|
||||
false
|
||||
| .skip it' => it'.all p
|
||||
| .done => true) := by
|
||||
rw [all_eq_allM, allM_eq_match_step]
|
||||
split
|
||||
· simp only [pure_bind, all_eq_allM, Bool.if_false_right, Bool.decide_eq_true]
|
||||
split <;> simp [*]
|
||||
· simp [all_eq_allM]
|
||||
· simp
|
||||
|
||||
theorem Iter.all_eq_forIn {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.all p = (ForIn.forIn (m := Id) it true (fun x _ => do
|
||||
if p x then
|
||||
return .yield true
|
||||
else
|
||||
return .done false)).run := by
|
||||
simp [all_eq_allM, allM_eq_forIn]
|
||||
|
||||
theorem Iter.all_toList {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.toList.all p = it.all p := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs =>
|
||||
rw [it.toList_eq_match_step, all_eq_match_step]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp only [List.all_cons, ihy ‹_›]
|
||||
split <;> simp [*]
|
||||
· simp only [ihs ‹_›]
|
||||
· simp only [List.all_nil]
|
||||
|
||||
theorem Iter.all_toArray {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.toArray.all p = it.all p := by
|
||||
simp only [← Iter.toArray_toList, List.all_toArray, all_toList]
|
||||
|
||||
theorem Iter.allM_eq_not_anyM_not {α β : Type w} {m : Type → Type w'} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
|
||||
{it : Iter (α := α) β} {p : β → m Bool} :
|
||||
it.allM p = (! ·) <$> it.anyM ((! ·) <$> p ·) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs =>
|
||||
rw [allM_eq_match_step, anyM_eq_match_step, map_eq_pure_bind]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp only [map_eq_pure_bind, bind_assoc, pure_bind]
|
||||
apply bind_congr; intro px
|
||||
split
|
||||
· simp [*, ihy ‹_›]
|
||||
· simp [*]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem Iter.all_eq_not_any_not {α β : Type w} [Iterator α Id β]
|
||||
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
|
||||
{it : Iter (α := α) β} {p : β → Bool} :
|
||||
it.all p = ! it.any (! p ·) := by
|
||||
induction it using Iter.inductSteps with | step it ihy ihs =>
|
||||
rw [all_eq_match_step, any_eq_match_step]
|
||||
cases it.step using PlausibleIterStep.casesOn
|
||||
· simp only
|
||||
split
|
||||
· simp [*, ihy ‹_›]
|
||||
· simp [*]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
end Std.Iterators
|
||||
|
||||
@@ -8,5 +8,3 @@ module
|
||||
prelude
|
||||
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
|
||||
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
|
||||
|
||||
public section
|
||||
|
||||
@@ -46,7 +46,7 @@ theorem IterM.DefaultConsumers.toArrayMapped.go.aux₂ [Monad n] [LawfulMonad n]
|
||||
theorem IterM.DefaultConsumers.toArrayMapped_eq_match_step [Monad n] [LawfulMonad n]
|
||||
[Iterator α m β] [Finite α m] :
|
||||
IterM.DefaultConsumers.toArrayMapped lift f it (m := m) = letI : MonadLift m n := ⟨lift (δ := _)⟩; (do
|
||||
match (← it.step).val with
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' out =>
|
||||
return #[← f out] ++ (← IterM.DefaultConsumers.toArrayMapped lift f it' (m := m))
|
||||
| .skip it' => IterM.DefaultConsumers.toArrayMapped lift f it' (m := m)
|
||||
@@ -54,12 +54,13 @@ theorem IterM.DefaultConsumers.toArrayMapped_eq_match_step [Monad n] [LawfulMona
|
||||
rw [IterM.DefaultConsumers.toArrayMapped, IterM.DefaultConsumers.toArrayMapped.go]
|
||||
apply bind_congr
|
||||
intro step
|
||||
split <;> simp [IterM.DefaultConsumers.toArrayMapped.go.aux₂]
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;>
|
||||
simp [IterM.DefaultConsumers.toArrayMapped.go.aux₂]
|
||||
|
||||
theorem IterM.toArray_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
|
||||
[IteratorCollect α m m] [LawfulIteratorCollect α m m] :
|
||||
it.toArray = (do
|
||||
match (← it.step).val with
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' out => return #[out] ++ (← it'.toArray)
|
||||
| .skip it' => it'.toArray
|
||||
| .done => return #[]) := by
|
||||
@@ -82,7 +83,7 @@ theorem IterM.toArray_toList [Monad m] [LawfulMonad m] [Iterator α m β] [Finit
|
||||
theorem IterM.toList_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
|
||||
[IteratorCollect α m m] [LawfulIteratorCollect α m m] {it : IterM (α := α) m β} :
|
||||
it.toList = (do
|
||||
match (← it.step).val with
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' out => return out :: (← it'.toList)
|
||||
| .skip it' => it'.toList
|
||||
| .done => return []) := by
|
||||
@@ -114,7 +115,7 @@ theorem IterM.toListRev.go.aux₂ [Monad m] [LawfulMonad m] [Iterator α m β] [
|
||||
theorem IterM.toListRev_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
|
||||
{it : IterM (α := α) m β} :
|
||||
it.toListRev = (do
|
||||
match (← it.step).val with
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' out => return (← it'.toListRev) ++ [out]
|
||||
| .skip it' => it'.toListRev
|
||||
| .done => return []) := by
|
||||
@@ -122,7 +123,7 @@ theorem IterM.toListRev_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m
|
||||
rw [toListRev.go]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp [IterM.toListRev.go.aux₂]
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp [IterM.toListRev.go.aux₂]
|
||||
|
||||
theorem IterM.reverse_toListRev [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
|
||||
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
|
||||
@@ -134,7 +135,7 @@ theorem IterM.reverse_toListRev [Monad m] [LawfulMonad m] [Iterator α m β] [Fi
|
||||
rw [toListRev_eq_match_step, toList_eq_match_step, map_eq_pure_bind, bind_assoc]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp (discharger := assumption) [ihy, ihs]
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp (discharger := assumption) [ihy, ihs]
|
||||
|
||||
theorem IterM.toListRev_eq [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
|
||||
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
|
||||
|
||||
@@ -23,7 +23,8 @@ theorem IterM.DefaultConsumers.forIn'_eq_match_step {α β : Type w} {m : Type w
|
||||
{it : IterM (α := α) m β} {init : γ}
|
||||
{P hP} {f : (b : β) → P b → (c : γ) → n (Subtype (plausible_forInStep b c))} :
|
||||
IterM.DefaultConsumers.forIn' lift γ plausible_forInStep wf it init P hP f =
|
||||
(lift _ _ · it.step) (fun
|
||||
(lift _ _ · it.step) (fun s =>
|
||||
match s.inflate with
|
||||
| .yield it' out h => do
|
||||
match ← f out (hP _ <| .direct ⟨_, h⟩) init with
|
||||
| ⟨.yield c, _⟩ =>
|
||||
@@ -36,7 +37,7 @@ theorem IterM.DefaultConsumers.forIn'_eq_match_step {α β : Type w} {m : Type w
|
||||
| .done _ => return init) := by
|
||||
rw [forIn']
|
||||
congr; ext step
|
||||
cases step using PlausibleIterStep.casesOn <;> rfl
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> rfl
|
||||
|
||||
theorem IterM.forIn'_eq {α β : Type w} {m : Type w → Type w'} [Iterator α m β] [Finite α m]
|
||||
{n : Type w → Type w''} [Monad m] [Monad n] [LawfulMonad n] [IteratorLoop α m n]
|
||||
@@ -60,20 +61,34 @@ theorem IterM.forIn_eq {α β : Type w} {m : Type w → Type w'} [Iterator α m
|
||||
IteratorLoop.wellFounded_of_finite it init _ (fun _ => id) (fun out _ acc => (⟨·, .intro⟩) <$> f out acc) := by
|
||||
simp only [ForIn.forIn, forIn'_eq]
|
||||
|
||||
@[congr] theorem IterM.forIn'_congr {α β : Type w} {m : Type w → Type w'} [Monad m]
|
||||
[Iterator α m β] [Finite α m] [IteratorLoop α m m]
|
||||
@[congr] theorem IterM.forIn'_congr {α β : Type w} {m : Type w → Type w'}
|
||||
{n : Type w → Type w''} [Monad n] [Monad m]
|
||||
[Iterator α m β] [Finite α m] [IteratorLoop α m n] [MonadLiftT m n]
|
||||
{ita itb : IterM (α := α) m β} (w : ita = itb)
|
||||
{b b' : γ} (hb : b = b')
|
||||
{f : (a' : β) → _ → γ → m (ForInStep γ)}
|
||||
{g : (a' : β) → _ → γ → m (ForInStep γ)}
|
||||
{f : (a' : β) → _ → γ → n (ForInStep γ)}
|
||||
{g : (a' : β) → _ → γ → n (ForInStep γ)}
|
||||
(h : ∀ a m b, f a (by simpa [w] using m) b = g a m b) :
|
||||
letI : ForIn' m (IterM (α := α) m β) β _ := IterM.instForIn'
|
||||
letI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn'
|
||||
forIn' ita b f = forIn' itb b' g := by
|
||||
subst_eqs
|
||||
simp only [← funext_iff] at h
|
||||
rw [← h]
|
||||
rfl
|
||||
|
||||
@[congr] theorem IterM.forIn_congr {α β : Type w} {m : Type w → Type w'}
|
||||
{n : Type w → Type w''} [Monad n] [Monad m]
|
||||
[Iterator α m β] [Finite α m] [IteratorLoop α m n] [MonadLiftT m n]
|
||||
{ita itb : IterM (α := α) m β} (w : ita = itb)
|
||||
{b b' : γ} (hb : b = b')
|
||||
{f : (a' : β) → γ → n (ForInStep γ)}
|
||||
{g : (a' : β) → γ → n (ForInStep γ)}
|
||||
(h : ∀ a b, f a b = g a b) :
|
||||
forIn ita b f = forIn itb b' g := by
|
||||
subst_eqs
|
||||
simp only [← funext_iff] at h
|
||||
rw [← h]
|
||||
|
||||
theorem IterM.forIn'_eq_match_step {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] {n : Type w → Type w''} [Monad m] [Monad n] [LawfulMonad n]
|
||||
[IteratorLoop α m n] [LawfulIteratorLoop α m n]
|
||||
@@ -81,7 +96,7 @@ theorem IterM.forIn'_eq_match_step {α β : Type w} {m : Type w → Type w'} [It
|
||||
{f : (out : β) → _ → γ → n (ForInStep γ)} :
|
||||
letI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn'
|
||||
ForIn'.forIn' it init f = (do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' out h =>
|
||||
match ← f out (.direct ⟨_, h⟩) init with
|
||||
| .yield c =>
|
||||
@@ -95,7 +110,7 @@ theorem IterM.forIn'_eq_match_step {α β : Type w} {m : Type w → Type w'} [It
|
||||
rw [IterM.forIn'_eq, DefaultConsumers.forIn'_eq_match_step]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· simp only [map_eq_pure_bind, bind_assoc]
|
||||
apply bind_congr
|
||||
intro forInStep
|
||||
@@ -115,7 +130,7 @@ theorem IterM.forIn_eq_match_step {α β : Type w} {m : Type w → Type w'} [Ite
|
||||
[MonadLiftT m n] [LawfulMonadLiftT m n] {γ : Type w} {it : IterM (α := α) m β} {init : γ}
|
||||
{f : β → γ → n (ForInStep γ)} :
|
||||
ForIn.forIn it init f = (do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' out _ =>
|
||||
match ← f out init with
|
||||
| .yield c => ForIn.forIn it' c f
|
||||
@@ -139,7 +154,7 @@ theorem IterM.forM_eq_match_step {α β : Type w} {m : Type w → Type w'} [Iter
|
||||
[MonadLiftT m n] [LawfulMonadLiftT m n] {it : IterM (α := α) m β}
|
||||
{f : β → n PUnit} :
|
||||
ForM.forM it f = (do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' out _ =>
|
||||
f out
|
||||
ForM.forM it' f
|
||||
@@ -148,7 +163,7 @@ theorem IterM.forM_eq_match_step {α β : Type w} {m : Type w → Type w'} [Iter
|
||||
rw [forM_eq_forIn, forIn_eq_match_step]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp [forM_eq_forIn]
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp [forM_eq_forIn]
|
||||
|
||||
theorem IterM.foldM_eq_forIn {α β γ : Type w} {m : Type w → Type w'} [Iterator α m β] [Finite α m]
|
||||
{n : Type w → Type w''} [Monad n] [IteratorLoop α m n] [MonadLiftT m n] {f : γ → β → n γ}
|
||||
@@ -169,14 +184,14 @@ theorem IterM.foldM_eq_match_step {α β γ : Type w} {m : Type w → Type w'} [
|
||||
[LawfulIteratorLoop α m n] [MonadLiftT m n] [LawfulMonadLiftT m n]
|
||||
{f : γ → β → n γ} {init : γ} {it : IterM (α := α) m β} :
|
||||
it.foldM (init := init) f = (do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' out _ => it'.foldM (init := ← f init out) f
|
||||
| .skip it' _ => it'.foldM (init := init) f
|
||||
| .done _ => return init) := by
|
||||
rw [IterM.foldM_eq_forIn, IterM.forIn_eq_match_step]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp [foldM_eq_forIn]
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp [foldM_eq_forIn]
|
||||
|
||||
theorem IterM.fold_eq_forIn {α β γ : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m]
|
||||
@@ -204,7 +219,7 @@ theorem IterM.fold_eq_match_step {α β γ : Type w} {m : Type w → Type w'} [I
|
||||
[Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{f : γ → β → γ} {init : γ} {it : IterM (α := α) m β} :
|
||||
it.fold (init := init) f = (do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' out _ => it'.fold (init := f init out) f
|
||||
| .skip it' _ => it'.fold (init := init) f
|
||||
| .done _ => return init) := by
|
||||
@@ -212,7 +227,7 @@ theorem IterM.fold_eq_match_step {α β γ : Type w} {m : Type w → Type w'} [I
|
||||
simp only [fold_eq_foldM]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn <;> simp
|
||||
cases step.inflate using PlausibleIterStep.casesOn <;> simp
|
||||
|
||||
-- The argument `f : γ₁ → γ₂` is intentionally explicit, as it is sometimes not found by unification.
|
||||
theorem IterM.fold_hom {m : Type w → Type w'} [Iterator α m β] [Finite α m]
|
||||
@@ -246,7 +261,7 @@ theorem IterM.toList_eq_fold {α β : Type w} {m : Type w → Type w'} [Iterator
|
||||
simp only [map_eq_pure_bind, bind_assoc]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· rename_i it' out h
|
||||
specialize ihy h (l' ++ [out])
|
||||
simpa using ihy
|
||||
@@ -282,7 +297,7 @@ theorem IterM.drain_eq_match_step {α β : Type w} {m : Type w → Type w'} [Ite
|
||||
[Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} :
|
||||
it.drain = (do
|
||||
match ← it.step with
|
||||
match (← it.step).inflate with
|
||||
| .yield it' _ _ => it'.drain
|
||||
| .skip it' _ => it'.drain
|
||||
| .done _ => return .unit) := by
|
||||
@@ -299,7 +314,7 @@ theorem IterM.drain_eq_map_toList {α β : Type w} {m : Type w → Type w'} [Ite
|
||||
simp only [map_eq_pure_bind, bind_assoc]
|
||||
apply bind_congr
|
||||
intro step
|
||||
cases step using PlausibleIterStep.casesOn
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· rename_i it' out h
|
||||
simp [ihy h]
|
||||
· rename_i it' h
|
||||
@@ -320,4 +335,183 @@ theorem IterM.drain_eq_map_toArray {α β : Type w} {m : Type w → Type w'} [It
|
||||
it.drain = (fun _ => .unit) <$> it.toList := by
|
||||
simp [IterM.drain_eq_map_toList]
|
||||
|
||||
theorem IterM.anyM_eq_forIn {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → m (ULift Bool)} :
|
||||
it.anyM p = (ForIn.forIn it (.up false) (fun x _ => do
|
||||
if (← p x).down then
|
||||
return .done (.up true)
|
||||
else
|
||||
return .yield (.up false))) := by
|
||||
rfl
|
||||
|
||||
theorem IterM.anyM_eq_match_step {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → m (ULift Bool)} :
|
||||
it.anyM p = (do
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' x =>
|
||||
if (← p x).down then
|
||||
return .up true
|
||||
else
|
||||
it'.anyM p
|
||||
| .skip it' => it'.anyM p
|
||||
| .done => return .up false) := by
|
||||
rw [anyM_eq_forIn, forIn_eq_match_step]
|
||||
simp only [monadLift_self, bind_assoc]
|
||||
apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· apply bind_congr; intro px
|
||||
split
|
||||
· simp
|
||||
· simp [anyM_eq_forIn]
|
||||
· simp [anyM_eq_forIn]
|
||||
· simp
|
||||
|
||||
theorem IterM.any_eq_anyM {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → Bool} :
|
||||
it.any p = it.anyM (fun x => pure (.up (p x))) := by
|
||||
rfl
|
||||
|
||||
theorem IterM.anyM_pure {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → ULift Bool} :
|
||||
it.anyM (fun x => pure (p x)) = it.any (fun x => (p x).down) := by
|
||||
simp [any_eq_anyM]
|
||||
|
||||
theorem IterM.any_eq_match_step {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → Bool} :
|
||||
it.any p = (do
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' x =>
|
||||
if p x then
|
||||
return .up true
|
||||
else
|
||||
it'.any p
|
||||
| .skip it' => it'.any p
|
||||
| .done => return .up false) := by
|
||||
rw [any_eq_anyM, anyM_eq_match_step]
|
||||
apply bind_congr; intro step
|
||||
split
|
||||
· simp [any_eq_anyM]
|
||||
· simp [any_eq_anyM]
|
||||
· simp
|
||||
|
||||
theorem IterM.any_eq_forIn {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → Bool} :
|
||||
it.any p = (ForIn.forIn it (.up false) (fun x _ => do
|
||||
if p x then
|
||||
return .done (.up true)
|
||||
else
|
||||
return .yield (.up false))) := by
|
||||
simp [any_eq_anyM, anyM_eq_forIn]
|
||||
|
||||
theorem IterM.allM_eq_forIn {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → m (ULift Bool)} :
|
||||
it.allM p = (ForIn.forIn it (.up true) (fun x _ => do
|
||||
if (← p x).down then
|
||||
return .yield (.up true)
|
||||
else
|
||||
return .done (.up false))) := by
|
||||
rfl
|
||||
|
||||
theorem IterM.allM_eq_match_step {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → m (ULift Bool)} :
|
||||
it.allM p = (do
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' x =>
|
||||
if (← p x).down then
|
||||
it'.allM p
|
||||
else
|
||||
return .up false
|
||||
| .skip it' => it'.allM p
|
||||
| .done => return .up true) := by
|
||||
rw [allM_eq_forIn, forIn_eq_match_step]
|
||||
simp only [monadLift_self, bind_assoc]
|
||||
apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· apply bind_congr; intro px
|
||||
split
|
||||
· simp [allM_eq_forIn]
|
||||
· simp
|
||||
· simp [allM_eq_forIn]
|
||||
· simp
|
||||
|
||||
theorem IterM.all_eq_allM {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → Bool} :
|
||||
it.all p = it.allM (fun x => pure (.up (p x))) := by
|
||||
rfl
|
||||
|
||||
theorem IterM.allM_pure {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → ULift Bool} :
|
||||
it.allM (fun x => pure (p x)) = it.all (fun x => (p x).down) := by
|
||||
simp [all_eq_allM]
|
||||
|
||||
theorem IterM.all_eq_match_step {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → Bool} :
|
||||
it.all p = (do
|
||||
match (← it.step).inflate.val with
|
||||
| .yield it' x =>
|
||||
if p x then
|
||||
it'.all p
|
||||
else
|
||||
return .up false
|
||||
| .skip it' => it'.all p
|
||||
| .done => return .up true) := by
|
||||
rw [all_eq_allM, allM_eq_match_step]
|
||||
apply bind_congr; intro step
|
||||
split
|
||||
· simp [all_eq_allM]
|
||||
· simp [all_eq_allM]
|
||||
· simp
|
||||
|
||||
theorem IterM.all_eq_forIn {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → Bool} :
|
||||
it.all p = (ForIn.forIn it (.up true) (fun x _ => do
|
||||
if p x then
|
||||
return .yield (.up true)
|
||||
else
|
||||
return .done (.up false))) := by
|
||||
simp [all_eq_allM, allM_eq_forIn]
|
||||
|
||||
theorem IterM.allM_eq_not_anyM_not {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → m (ULift Bool)} :
|
||||
it.allM p = (fun x => .up !x.down) <$> it.anyM ((fun x => .up !x.down) <$> p ·) := by
|
||||
induction it using IterM.inductSteps with | step it ihy ihs =>
|
||||
rw [allM_eq_match_step, anyM_eq_match_step, map_eq_pure_bind, bind_assoc]
|
||||
apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· simp only [map_eq_pure_bind, bind_assoc, pure_bind]
|
||||
apply bind_congr; intro px
|
||||
split
|
||||
· simp [*, ihy ‹_›]
|
||||
· simp [*]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
theorem IterM.all_eq_not_any_not {α β : Type w} {m : Type w → Type w'} [Iterator α m β]
|
||||
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
|
||||
{it : IterM (α := α) m β} {p : β → Bool} :
|
||||
it.all p = (fun x => .up !x.down) <$> it.any (! p ·) := by
|
||||
induction it using IterM.inductSteps with | step it ihy ihs =>
|
||||
rw [all_eq_match_step, any_eq_match_step, map_eq_pure_bind, bind_assoc]
|
||||
apply bind_congr; intro step
|
||||
cases step.inflate using PlausibleIterStep.casesOn
|
||||
· simp only
|
||||
split
|
||||
· simp [*, ihy ‹_›]
|
||||
· simp [*]
|
||||
· simp [ihs ‹_›]
|
||||
· simp
|
||||
|
||||
end Std.Iterators
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user