mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-26 14:54:15 +00:00
Compare commits
295 Commits
kernel_let
...
grind_spli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c49e0db449 | ||
|
|
fa661f3414 | ||
|
|
cb405b5775 | ||
|
|
3a76a3640f | ||
|
|
f4ea880165 | ||
|
|
ed98c366cf | ||
|
|
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 | ||
|
|
94e5b66dfe | ||
|
|
8443600762 | ||
|
|
8b64425033 | ||
|
|
d96fd949ff | ||
|
|
d33aece210 | ||
|
|
9a7bab5f90 | ||
|
|
e2f87ed215 | ||
|
|
e42892cfb6 | ||
|
|
cc5c070328 | ||
|
|
f122454ef6 | ||
|
|
02f482129a | ||
|
|
0807f73171 | ||
|
|
27fa5b0bb5 | ||
|
|
8f9966ba74 | ||
|
|
eabd7309b7 | ||
|
|
795d13ddce | ||
|
|
2b23afdfab | ||
|
|
20b0bd0a20 | ||
|
|
979c2b4af0 | ||
|
|
b3cd5999e7 | ||
|
|
a4dcb25f69 | ||
|
|
85ce814689 | ||
|
|
9fc18b8ab4 | ||
|
|
852a3db447 | ||
|
|
d0d5d4ca39 | ||
|
|
b32f3e8930 | ||
|
|
34f5fba54d | ||
|
|
4c9601e60f | ||
|
|
42be7bb5c7 | ||
|
|
5f68c1662d | ||
|
|
2d14d51935 | ||
|
|
7cbeb14e46 | ||
|
|
cee2886154 | ||
|
|
35764213fc | ||
|
|
6b92cbdfa4 | ||
|
|
72bb7cf364 | ||
|
|
4881c3042e | ||
|
|
ec7add0b48 | ||
|
|
9b842b7554 | ||
|
|
fc718eac88 | ||
|
|
8b3c82cce2 | ||
|
|
0d1b7e6c88 | ||
|
|
d898c9ed17 | ||
|
|
c6abc3c036 | ||
|
|
d07862db2a | ||
|
|
8a79ef3633 | ||
|
|
b1c82f776b | ||
|
|
f278f31469 | ||
|
|
38b4062edb | ||
|
|
ae8dc414c3 | ||
|
|
7822ee4500 | ||
|
|
8f22c56420 | ||
|
|
0e122870be | ||
|
|
13c23877d4 | ||
|
|
7fba12f8f7 | ||
|
|
abb487a0c0 | ||
|
|
1091053824 | ||
|
|
545bd8a96c | ||
|
|
2f9618f76b | ||
|
|
fa36fcd448 | ||
|
|
9a3b4b2716 | ||
|
|
9fb5ab8450 | ||
|
|
a62c0bce77 | ||
|
|
3ce554abd7 | ||
|
|
257c347f9f | ||
|
|
ca1315e3ba | ||
|
|
197bc6cb66 | ||
|
|
02ca710872 | ||
|
|
3fbf080d72 |
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")
|
||||
|
||||
@@ -52,6 +52,7 @@ def sort_sections_order():
|
||||
return [
|
||||
"Language",
|
||||
"Library",
|
||||
"Tactics",
|
||||
"Compiler",
|
||||
"Pretty Printing",
|
||||
"Documentation",
|
||||
|
||||
@@ -112,3 +112,11 @@ repositories:
|
||||
branch: master
|
||||
dependencies:
|
||||
- mathlib4
|
||||
|
||||
- name: lean-fro.org
|
||||
url: https://github.com/leanprover/lean-fro.org
|
||||
toolchain-tag: false
|
||||
stable-branch: false
|
||||
branch: master
|
||||
dependencies:
|
||||
- verso
|
||||
|
||||
@@ -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
|
||||
@@ -377,6 +400,18 @@ def execute_release_steps(repo, version, config):
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(red("Tests failed, but continuing with PR creation..."))
|
||||
print(red(f"Test error: {e}"))
|
||||
elif repo_name == "lean-fro.org":
|
||||
# Update lean-toolchain in examples/hero
|
||||
print(blue("Updating examples/hero/lean-toolchain..."))
|
||||
docs_toolchain = repo_path / "examples" / "hero" / "lean-toolchain"
|
||||
with open(docs_toolchain, "w") as f:
|
||||
f.write(f"leanprover/lean4:{version}\n")
|
||||
print(green(f"Updated examples/hero/lean-toolchain to leanprover/lean4:{version}"))
|
||||
|
||||
print(blue("Running `lake update`..."))
|
||||
run_command("lake update", cwd=repo_path, stream_output=True)
|
||||
print(blue("Running `lake update` in examples/hero..."))
|
||||
run_command("lake update", cwd=repo_path / "examples" / "hero", stream_output=True)
|
||||
elif repo_name == "cslib":
|
||||
print(blue("Updating lakefile.toml..."))
|
||||
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
|
||||
|
||||
@@ -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
|
||||
@@ -1580,6 +1581,7 @@ instance {p q : Prop} [d : Decidable (p ↔ q)] : Decidable (p = q) :=
|
||||
|
||||
gen_injective_theorems% Array
|
||||
gen_injective_theorems% BitVec
|
||||
gen_injective_theorems% ByteArray
|
||||
gen_injective_theorems% Char
|
||||
gen_injective_theorems% DoResultBC
|
||||
gen_injective_theorems% DoResultPR
|
||||
@@ -1604,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
|
||||
@@ -176,9 +174,6 @@ theorem attach_map_val (xs : Array α) (f : α → β) :
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[deprecated attach_map_val (since := "2025-02-17")]
|
||||
abbrev attach_map_coe := @attach_map_val
|
||||
|
||||
-- The argument `xs : Array α` is explicit to allow rewriting from right to left.
|
||||
theorem attach_map_subtype_val (xs : Array α) : xs.attach.map Subtype.val = xs := by
|
||||
cases xs; simp
|
||||
@@ -187,9 +182,6 @@ theorem attachWith_map_val {p : α → Prop} {f : α → β} {xs : Array α} (H
|
||||
((xs.attachWith p H).map fun (i : { i // p i}) => f i) = xs.map f := by
|
||||
cases xs; simp
|
||||
|
||||
@[deprecated attachWith_map_val (since := "2025-02-17")]
|
||||
abbrev attachWith_map_coe := @attachWith_map_val
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} {xs : Array α} (H : ∀ a ∈ xs, p a) :
|
||||
(xs.attachWith p H).map Subtype.val = xs := by
|
||||
cases xs; simp
|
||||
@@ -294,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
|
||||
@@ -370,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
|
||||
@@ -401,9 +389,6 @@ theorem map_attach_eq_pmap {xs : Array α} {f : { x // x ∈ xs } → β} :
|
||||
cases xs
|
||||
ext <;> simp
|
||||
|
||||
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
|
||||
abbrev map_attach := @map_attach_eq_pmap
|
||||
|
||||
@[grind =]
|
||||
theorem attach_filterMap {xs : Array α} {f : α → Option β} :
|
||||
(xs.filterMap f).attach = xs.attach.filterMap
|
||||
@@ -439,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
|
||||
@@ -447,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)) ++
|
||||
@@ -462,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
|
||||
@@ -470,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
|
||||
|
||||
@@ -129,20 +129,11 @@ end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[deprecated Array.toArray_toList (since := "2025-02-17")]
|
||||
abbrev toArray_toList := @Array.toArray_toList
|
||||
|
||||
-- This does not need to be a simp lemma, as already after the `whnfR` the right hand side is `as`.
|
||||
theorem toList_toArray {as : List α} : as.toArray.toList = as := rfl
|
||||
|
||||
@[deprecated toList_toArray (since := "2025-02-17")]
|
||||
abbrev _root_.Array.toList_toArray := @List.toList_toArray
|
||||
|
||||
@[simp, grind =] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
|
||||
|
||||
@[deprecated size_toArray (since := "2025-02-17")]
|
||||
abbrev _root_.Array.size_toArray := @List.size_toArray
|
||||
|
||||
@[simp, grind =] theorem getElem_toArray {xs : List α} {i : Nat} (h : i < xs.toArray.size) :
|
||||
xs.toArray[i] = xs[i]'(by simpa using h) := rfl
|
||||
|
||||
@@ -412,10 +403,6 @@ that requires a proof the array is non-empty.
|
||||
def back? (xs : Array α) : Option α :=
|
||||
xs[xs.size - 1]?
|
||||
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), expose]
|
||||
def get? (xs : Array α) (i : Nat) : Option α :=
|
||||
if h : i < xs.size then some xs[i] else none
|
||||
|
||||
/--
|
||||
Swaps a new element with the element at the given index.
|
||||
|
||||
@@ -765,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
|
||||
@@ -926,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
|
||||
@@ -1265,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
|
||||
@@ -1283,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
|
||||
@@ -1320,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⟩
|
||||
@@ -1730,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
|
||||
@@ -1755,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
|
||||
@@ -1779,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
|
||||
@@ -1812,7 +1791,6 @@ Examples:
|
||||
* `#["apple", "pear", "orange"].eraseIdxIfInBounds 3 = #["apple", "pear", "orange"]`
|
||||
* `#["apple", "pear", "orange"].eraseIdxIfInBounds 5 = #["apple", "pear", "orange"]`
|
||||
-/
|
||||
@[grind]
|
||||
def eraseIdxIfInBounds (xs : Array α) (i : Nat) : Array α :=
|
||||
if h : i < xs.size then xs.eraseIdx i h else xs
|
||||
|
||||
@@ -1875,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
|
||||
@@ -1930,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]
|
||||
@@ -1959,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]
|
||||
@@ -2122,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)
|
||||
|
||||
@@ -24,29 +24,6 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
/--
|
||||
Use the indexing notation `a[i]` instead.
|
||||
|
||||
Access an element from an array without needing a runtime bounds checks,
|
||||
using a `Nat` index and a proof that it is in bounds.
|
||||
|
||||
This function does not use `get_elem_tactic` to automatically find the proof that
|
||||
the index is in bounds. This is because the tactic itself needs to look up values in
|
||||
arrays.
|
||||
-/
|
||||
@[deprecated "Use indexing notation `as[i]` instead" (since := "2025-02-17")]
|
||||
def get {α : Type u} (xs : @& Array α) (i : @& Nat) (h : LT.lt i xs.size) : α :=
|
||||
xs.toList.get ⟨i, h⟩
|
||||
|
||||
/--
|
||||
Use the indexing notation `a[i]!` instead.
|
||||
|
||||
Access an element from an array, or panic if the index is out of bounds.
|
||||
-/
|
||||
@[deprecated "Use indexing notation `as[i]!` instead" (since := "2025-02-17"), expose]
|
||||
def get! {α : Type u} [Inhabited α] (xs : @& Array α) (i : @& Nat) : α :=
|
||||
Array.getD xs i default
|
||||
|
||||
theorem foldlM_toList.aux [Monad m]
|
||||
{f : β → α → m β} {xs : Array α} {i j} (H : xs.size ≤ i + j) {b} :
|
||||
foldlM.loop f xs xs.size (Nat.le_refl _) i j b = (xs.toList.drop j).foldlM f b := by
|
||||
@@ -108,9 +85,6 @@ abbrev push_toList := @toList_push
|
||||
|
||||
@[simp, grind =] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
|
||||
|
||||
@[deprecated toList_pop (since := "2025-02-17")]
|
||||
abbrev pop_toList := @Array.toList_pop
|
||||
|
||||
@[simp] theorem append_eq_append {xs ys : Array α} : xs.append ys = xs ++ ys := rfl
|
||||
|
||||
@[simp, grind =] theorem toList_append {xs ys : Array α} :
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -324,6 +324,13 @@ abbrev erase_mkArray_ne := @erase_replicate_ne
|
||||
|
||||
end erase
|
||||
|
||||
/-! ### eraseIdxIfInBounds -/
|
||||
|
||||
@[grind =]
|
||||
theorem eraseIdxIfInBounds_eq {xs : Array α} {i : Nat} :
|
||||
xs.eraseIdxIfInBounds i = if h : i < xs.size then xs.eraseIdx i else xs := by
|
||||
simp [eraseIdxIfInBounds]
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
|
||||
@@ -278,9 +278,6 @@ theorem find?_flatten_eq_none_iff {xss : Array (Array α)} {p : α → Bool} :
|
||||
xss.flatten.find? p = none ↔ ∀ ys ∈ xss, ∀ x ∈ ys, !p x := by
|
||||
simp
|
||||
|
||||
@[deprecated find?_flatten_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_none := @find?_flatten_eq_none_iff
|
||||
|
||||
/--
|
||||
If `find? p` returns `some a` from `xs.flatten`, then `p a` holds, and
|
||||
some array in `xs` contains `a`, and no earlier element of that array satisfies `p`.
|
||||
@@ -306,9 +303,6 @@ theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a
|
||||
⟨zs.toList, bs.toList.map Array.toList, by simpa using h⟩,
|
||||
by simpa using h₁, by simpa using h₂⟩
|
||||
|
||||
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
|
||||
|
||||
@[simp, grind =] theorem find?_flatMap {xs : Array α} {f : α → Array β} {p : β → Bool} :
|
||||
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
|
||||
cases xs
|
||||
@@ -318,17 +312,11 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
|
||||
(xs.flatMap f).find? p = none ↔ ∀ x ∈ xs, ∀ y ∈ f x, !p y := by
|
||||
simp
|
||||
|
||||
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
|
||||
|
||||
@[grind =]
|
||||
theorem find?_replicate :
|
||||
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
|
||||
simp [← List.toArray_replicate, List.find?_replicate]
|
||||
|
||||
@[deprecated find?_replicate (since := "2025-03-18")]
|
||||
abbrev find?_mkArray := @find?_replicate
|
||||
|
||||
@[simp] theorem find?_replicate_of_size_pos (h : 0 < n) :
|
||||
find? p (replicate n a) = if p a then some a else none := by
|
||||
simp [find?_replicate, Nat.ne_of_gt h]
|
||||
@@ -346,34 +334,19 @@ abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[deprecated find?_replicate_of_neg (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_neg := @find?_replicate_of_neg
|
||||
|
||||
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
|
||||
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
simp [← List.toArray_replicate, Classical.or_iff_not_imp_left]
|
||||
|
||||
@[deprecated find?_replicate_eq_none_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
|
||||
|
||||
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff
|
||||
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
|
||||
|
||||
@[simp] theorem get_find?_replicate {n : Nat} {a : α} {p : α → Bool} (h) :
|
||||
((replicate n a).find? p).get h = a := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated get_find?_replicate (since := "2025-03-18")]
|
||||
abbrev get_find?_mkArray := @get_find?_replicate
|
||||
|
||||
@[grind =]
|
||||
theorem find?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) {p : β → Bool} :
|
||||
|
||||
@@ -80,9 +80,6 @@ theorem ne_empty_of_size_pos (h : 0 < xs.size) : xs ≠ #[] := by
|
||||
@[simp] theorem size_eq_zero_iff : xs.size = 0 ↔ xs = #[] :=
|
||||
⟨eq_empty_of_size_eq_zero, fun h => h ▸ rfl⟩
|
||||
|
||||
@[deprecated size_eq_zero_iff (since := "2025-02-24")]
|
||||
abbrev size_eq_zero := @size_eq_zero_iff
|
||||
|
||||
theorem eq_empty_iff_size_eq_zero : xs = #[] ↔ xs.size = 0 :=
|
||||
size_eq_zero_iff.symm
|
||||
|
||||
@@ -107,17 +104,10 @@ theorem exists_mem_of_size_eq_add_one {xs : Array α} (h : xs.size = n + 1) :
|
||||
theorem size_pos_iff {xs : Array α} : 0 < xs.size ↔ xs ≠ #[] :=
|
||||
Nat.pos_iff_ne_zero.trans (not_congr size_eq_zero_iff)
|
||||
|
||||
@[deprecated size_pos_iff (since := "2025-02-24")]
|
||||
abbrev size_pos := @size_pos_iff
|
||||
|
||||
theorem size_eq_one_iff {xs : Array α} : xs.size = 1 ↔ ∃ a, xs = #[a] := by
|
||||
cases xs
|
||||
simpa using List.length_eq_one_iff
|
||||
|
||||
@[deprecated size_eq_one_iff (since := "2025-02-24")]
|
||||
abbrev size_eq_one := @size_eq_one_iff
|
||||
|
||||
|
||||
/-! ## L[i] and L[i]? -/
|
||||
|
||||
theorem getElem?_eq_none_iff {xs : Array α} : xs[i]? = none ↔ xs.size ≤ i := by
|
||||
@@ -371,6 +361,7 @@ abbrev getElem?_mkArray := @getElem?_replicate
|
||||
|
||||
/-! ### mem -/
|
||||
|
||||
@[grind ←]
|
||||
theorem not_mem_empty (a : α) : ¬ a ∈ #[] := by simp
|
||||
|
||||
@[simp, grind =] theorem mem_push {xs : Array α} {x y : α} : x ∈ xs.push y ↔ x ∈ xs ∨ x = y := by
|
||||
@@ -542,18 +533,12 @@ theorem isEmpty_eq_false_iff_exists_mem {xs : Array α} :
|
||||
@[simp] theorem isEmpty_iff {xs : Array α} : xs.isEmpty ↔ xs = #[] := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[deprecated isEmpty_iff (since := "2025-02-17")]
|
||||
abbrev isEmpty_eq_true := @isEmpty_iff
|
||||
|
||||
@[grind →]
|
||||
theorem empty_of_isEmpty {xs : Array α} (h : xs.isEmpty) : xs = #[] := Array.isEmpty_iff.mp h
|
||||
|
||||
@[simp] theorem isEmpty_eq_false_iff {xs : Array α} : xs.isEmpty = false ↔ xs ≠ #[] := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[deprecated isEmpty_eq_false_iff (since := "2025-02-17")]
|
||||
abbrev isEmpty_eq_false := @isEmpty_eq_false_iff
|
||||
|
||||
theorem isEmpty_iff_size_eq_zero {xs : Array α} : xs.isEmpty ↔ xs.size = 0 := by
|
||||
rw [isEmpty_iff, size_eq_zero_iff]
|
||||
|
||||
@@ -2996,11 +2981,6 @@ theorem _root_.List.toArray_drop {l : List α} {k : Nat} :
|
||||
(l.drop k).toArray = l.toArray.extract k := by
|
||||
rw [List.drop_eq_extract, List.extract_toArray, List.size_toArray]
|
||||
|
||||
@[deprecated extract_size (since := "2025-02-27")]
|
||||
theorem take_size {xs : Array α} : xs.take xs.size = xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/-! ### shrink -/
|
||||
|
||||
@[simp] private theorem size_shrink_loop {xs : Array α} {n : Nat} : (shrink.loop n xs).size = xs.size - n := by
|
||||
@@ -3586,8 +3566,6 @@ theorem foldr_eq_foldl_reverse {xs : Array α} {f : α → β → β} {b} :
|
||||
subst w
|
||||
rw [foldr_eq_foldl_reverse, foldl_push_eq_append rfl, map_reverse]
|
||||
|
||||
@[deprecated foldr_push_eq_append (since := "2025-02-09")] abbrev foldr_flip_push_eq_append := @foldr_push_eq_append
|
||||
|
||||
theorem foldl_assoc {op : α → α → α} [ha : Std.Associative op] {xs : Array α} {a₁ a₂} :
|
||||
xs.foldl op (op a₁ a₂) = op a₁ (xs.foldl op a₂) := by
|
||||
rcases xs with ⟨l⟩
|
||||
@@ -4712,44 +4690,3 @@ namespace List
|
||||
simp_all
|
||||
|
||||
end List
|
||||
|
||||
/-! ### Deprecations -/
|
||||
namespace Array
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "`get?` is deprecated" (since := "2025-02-12"), simp]
|
||||
theorem get?_eq_getElem? (xs : Array α) (i : Nat) : xs.get? i = xs[i]? := rfl
|
||||
|
||||
@[deprecated getD_eq_getD_getElem? (since := "2025-02-12")] abbrev getD_eq_get? := @getD_eq_getD_getElem?
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem!_eq_getD (since := "2025-02-12")]
|
||||
theorem get!_eq_getD [Inhabited α] (xs : Array α) : xs.get! n = xs.getD n default := rfl
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead of `a.get! i`." (since := "2025-02-12")]
|
||||
theorem get!_eq_getD_getElem? [Inhabited α] (xs : Array α) (i : Nat) :
|
||||
xs.get! i = xs[i]?.getD default := by
|
||||
by_cases p : i < xs.size <;>
|
||||
simp [get!, getD_eq_getD_getElem?, p]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_getElem? := @get!_eq_getD_getElem?
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "`Array.get?` is deprecated, use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_eq_get?_toList (xs : Array α) (i : Nat) : xs.get? i = xs.toList.get? i := by
|
||||
simp [← getElem?_toList]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
|
||||
|
||||
/-! ### set -/
|
||||
|
||||
@[deprecated getElem?_set_self (since := "2025-02-27")] abbrev get?_set_eq := @getElem?_set_self
|
||||
@[deprecated getElem?_set_ne (since := "2025-02-27")] abbrev get?_set_ne := @getElem?_set_ne
|
||||
@[deprecated getElem?_set (since := "2025-02-27")] abbrev get?_set := @getElem?_set
|
||||
@[deprecated get_set (since := "2025-02-27")] abbrev get_set := @getElem_set
|
||||
@[deprecated get_set_ne (since := "2025-02-27")] abbrev get_set_ne := @getElem_set_ne
|
||||
|
||||
end Array
|
||||
|
||||
@@ -8,5 +8,3 @@ module
|
||||
prelude
|
||||
public import Init.Data.Array.Lex.Basic
|
||||
public import Init.Data.Array.Lex.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
@@ -9,8 +9,8 @@ prelude
|
||||
public import Init.Core
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Range.Polymorphic.Iterators
|
||||
import Init.Data.Range.Polymorphic.Nat
|
||||
public import Init.Data.Range.Polymorphic.Iterators
|
||||
public import Init.Data.Range.Polymorphic.Nat
|
||||
import Init.Data.Iterators.Consumers
|
||||
|
||||
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
|
||||
|
||||
@@ -7,6 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.GetElem
|
||||
public import Init.Data.Array.Basic
|
||||
import Init.Data.Array.GetLit
|
||||
public import Init.Data.Slice.Basic
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,10 +29,6 @@ set_option linter.missingDocs true
|
||||
|
||||
namespace BitVec
|
||||
|
||||
@[inline, deprecated BitVec.ofNatLT (since := "2025-02-13"), inherit_doc BitVec.ofNatLT]
|
||||
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2 ^ n) : BitVec n :=
|
||||
BitVec.ofNatLT i p
|
||||
|
||||
section Nat
|
||||
|
||||
/--
|
||||
@@ -874,4 +870,7 @@ def clzAuxRec {w : Nat} (x : BitVec w) (n : Nat) : BitVec w :=
|
||||
/-- Count the number of leading zeros. -/
|
||||
def clz (x : BitVec w) : BitVec w := clzAuxRec x (w - 1)
|
||||
|
||||
/-- Count the number of trailing zeros. -/
|
||||
def ctz (x : BitVec w) : BitVec w := (x.reverse).clz
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -74,10 +77,6 @@ theorem some_eq_getElem?_iff {l : BitVec w} : some a = l[n]? ↔ ∃ h : n < w,
|
||||
theorem getElem_of_getElem? {l : BitVec w} : l[n]? = some a → ∃ h : n < w, l[n] = a :=
|
||||
getElem?_eq_some_iff.mp
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated getElem?_eq_some_iff (since := "2025-02-17")]
|
||||
abbrev getElem?_eq_some := @getElem?_eq_some_iff
|
||||
|
||||
theorem getElem?_eq_none_iff {l : BitVec w} : l[n]? = none ↔ w ≤ n := by
|
||||
simp
|
||||
|
||||
@@ -350,25 +349,14 @@ theorem ofBool_eq_iff_eq : ∀ {b b' : Bool}, BitVec.ofBool b = BitVec.ofBool b'
|
||||
@[simp] theorem ofBool_xor_ofBool : ofBool b ^^^ ofBool b' = ofBool (b ^^ b') := by
|
||||
cases b <;> cases b' <;> rfl
|
||||
|
||||
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
|
||||
theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
|
||||
|
||||
@[simp, grind =] theorem getLsbD_ofNatLT {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
|
||||
getLsbD (x#'lt) i = x.testBit i := by
|
||||
simp [getLsbD, BitVec.ofNatLT]
|
||||
|
||||
@[deprecated getLsbD_ofNatLT (since := "2025-02-13")]
|
||||
theorem getLsbD_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
|
||||
getLsbD (x#'lt) i = x.testBit i := getLsbD_ofNatLT x lt i
|
||||
|
||||
@[simp, grind =] theorem getMsbD_ofNatLT {n x i : Nat} (h : x < 2^n) :
|
||||
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := by
|
||||
simp [getMsbD, getLsbD]
|
||||
|
||||
@[deprecated getMsbD_ofNatLT (since := "2025-02-13")]
|
||||
theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
|
||||
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := getMsbD_ofNatLT h
|
||||
|
||||
@[grind =]
|
||||
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
|
||||
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
|
||||
@@ -1100,6 +1088,10 @@ theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
|
||||
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.coe_castLE, val_toFin,
|
||||
Nat.mod_eq_of_lt (by apply BitVec.toNat_lt_twoPow_of_le p)]
|
||||
|
||||
theorem toNat_setWidth_of_le {w w' : Nat} {b : BitVec w} (h : w ≤ w') : (b.setWidth w').toNat = b.toNat := by
|
||||
rw [BitVec.toNat_setWidth, Nat.mod_eq_of_lt]
|
||||
exact BitVec.toNat_lt_twoPow_of_le h
|
||||
|
||||
/-! ## extractLsb -/
|
||||
|
||||
@[simp, grind =]
|
||||
@@ -1287,6 +1279,17 @@ theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
|
||||
ext i hi
|
||||
omega
|
||||
|
||||
theorem extractLsb'_setWidth_of_le {b : BitVec w} {start len w' : Nat} (h : start + len ≤ w') :
|
||||
(b.setWidth w').extractLsb' start len = b.extractLsb' start len := by
|
||||
ext i h_i
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem setWidth_extractLsb'_of_le {c : BitVec w} (h : len₁ ≤ len₂) :
|
||||
(c.extractLsb' start len₂).setWidth len₁ = c.extractLsb' start len₁ := by
|
||||
ext i hi
|
||||
simp [show i < len₂ by omega]
|
||||
|
||||
/-! ### allOnes -/
|
||||
|
||||
@[simp, grind =] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
|
||||
@@ -1530,6 +1533,12 @@ theorem extractLsb_and {x : BitVec w} {hi lo : Nat} :
|
||||
@[simp, grind =] theorem ofNat_and {x y : Nat} : BitVec.ofNat w (x &&& y) = BitVec.ofNat w x &&& BitVec.ofNat w y :=
|
||||
eq_of_toNat_eq (by simp [Nat.and_mod_two_pow])
|
||||
|
||||
theorem and_or_distrib_left {x y z : BitVec w} : x &&& (y ||| z) = (x &&& y) ||| (x &&& z) :=
|
||||
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_left])
|
||||
|
||||
theorem and_or_distrib_right {x y z : BitVec w} : (x ||| y) &&& z = (x &&& z) ||| (y &&& z) :=
|
||||
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_right])
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
@[simp, grind =] theorem toNat_xor (x y : BitVec v) :
|
||||
@@ -2180,6 +2189,10 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
|
||||
have := lt_of_getLsbD ha
|
||||
omega
|
||||
|
||||
theorem setWidth_ushiftRight_eq_extractLsb {b : BitVec w} : (b >>> w').setWidth w'' = b.extractLsb' w' w'' := by
|
||||
ext i hi
|
||||
simp
|
||||
|
||||
/-! ### ushiftRight reductions from BitVec to Nat -/
|
||||
|
||||
@[simp, grind =]
|
||||
@@ -2970,10 +2983,9 @@ theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
|
||||
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
|
||||
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
|
||||
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
|
||||
(x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
|
||||
x.extractLsb' start₁ (len₂ + len₁) := by
|
||||
ext i h
|
||||
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
|
||||
Nat.not_lt]
|
||||
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, ite_eq_left_iff, Nat.not_lt]
|
||||
intro hi
|
||||
congr 1
|
||||
omega
|
||||
@@ -3085,6 +3097,51 @@ theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||||
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
|
||||
simp [extractLsb'_append_eq_ite, show ¬ start < w by omega]
|
||||
|
||||
theorem extractLsb'_append_eq_left {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' w' w = a := by
|
||||
simp [BitVec.extractLsb'_append_eq_of_le]
|
||||
|
||||
theorem extractLsb'_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' 0 w' = b := by
|
||||
simp [BitVec.extractLsb'_append_eq_of_add_le]
|
||||
|
||||
theorem setWidth_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).setWidth w' = b := by
|
||||
ext i hi
|
||||
simp [getLsbD_append, hi]
|
||||
|
||||
theorem append_left_inj {s₁ s₂ : BitVec w} (t : BitVec w') : s₁ ++ t = s₂ ++ t ↔ s₁ = s₂ := by
|
||||
refine ⟨fun h => ?_, fun h => h ▸ rfl⟩
|
||||
ext i hi
|
||||
simpa [getElem_append, dif_neg] using congrArg (·[i + w']'(by omega)) h
|
||||
|
||||
theorem append_right_inj (s : BitVec w) {t₁ t₂ : BitVec w'} : s ++ t₁ = s ++ t₂ ↔ t₁ = t₂ := by
|
||||
refine ⟨fun h => ?_, fun h => h ▸ rfl⟩
|
||||
ext i hi
|
||||
simpa [getElem_append, hi] using congrArg (·[i]) h
|
||||
|
||||
theorem setWidth_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} :
|
||||
(b ++ b').setWidth w'' = (b.setWidth w'' <<< w') ||| b'.setWidth w'' := by
|
||||
ext i hi
|
||||
simp only [getElem_setWidth, getElem_or, getElem_shiftLeft]
|
||||
rw [getLsbD_append]
|
||||
split <;> simp_all
|
||||
|
||||
theorem setWidth_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} :
|
||||
(b ++ b' ++ b'').setWidth w''' = (b.setWidth w''' <<< (w' + w'')) ||| (b'.setWidth w''' <<< w'') ||| b''.setWidth w''' := by
|
||||
rw [BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
|
||||
BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
|
||||
BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
|
||||
|
||||
theorem setWidth_append_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} {b''' : BitVec w'''} :
|
||||
(b ++ b' ++ b'' ++ b''').setWidth w'''' = (b.setWidth w'''' <<< (w' + w'' + w''')) ||| (b'.setWidth w'''' <<< (w'' + w''')) |||
|
||||
(b''.setWidth w'''' <<< w''') ||| b'''.setWidth w'''' := by
|
||||
simp only [BitVec.setWidth_append_eq_shiftLeft_setWidth_or, BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
|
||||
|
||||
theorem and_setWidth_allOnes (w' w : Nat) (b : BitVec (w' + w)) :
|
||||
b &&& (BitVec.allOnes w).setWidth (w' + w) = 0#w' ++ b.setWidth w := by
|
||||
ext i hi
|
||||
simp only [getElem_and, getElem_setWidth, getLsbD_allOnes]
|
||||
rw [BitVec.getElem_append]
|
||||
split <;> simp_all
|
||||
|
||||
/-! ### rev -/
|
||||
|
||||
@[grind =]
|
||||
@@ -3696,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]
|
||||
@@ -4041,6 +4101,9 @@ instance instLawfulOrderLT : LawfulOrderLT (BitVec n) := by
|
||||
apply LawfulOrderLT.of_le
|
||||
simpa using fun _ _ => BitVec.lt_asymm
|
||||
|
||||
theorem length_pos_of_lt {b b' : BitVec w} (h : b < b') : 0 < w :=
|
||||
length_pos_of_ne (BitVec.ne_of_lt h)
|
||||
|
||||
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y → x % y < y := by
|
||||
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
|
||||
apply Nat.mod_lt
|
||||
@@ -4112,6 +4175,14 @@ theorem lt_of_msb_false_of_msb_true {x y : BitVec w} (hx : x.msb = false) (hy :
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem lt_add_one {b : BitVec w} (h : b ≠ allOnes w) : b < b + 1 := by
|
||||
simp only [ne_eq, ← toNat_inj, toNat_allOnes] at h
|
||||
simp only [BitVec.lt_def, ofNat_eq_ofNat, toNat_add, toNat_ofNat, Nat.add_mod_mod]
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
· exact Nat.lt_add_one _
|
||||
· have := b.toNat_lt_twoPow_of_le (Nat.le_refl _)
|
||||
omega
|
||||
|
||||
/-! ### udiv -/
|
||||
|
||||
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
|
||||
@@ -5257,7 +5328,7 @@ theorem replicate_succ' {x : BitVec w} :
|
||||
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
|
||||
simp [replicate_append_self]
|
||||
|
||||
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
|
||||
theorem setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [toNat_setWidth]
|
||||
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
|
||||
@@ -5266,6 +5337,14 @@ theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y)
|
||||
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
|
||||
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
|
||||
|
||||
theorem setWidth_setWidth_eq_self {a : BitVec w} {w' : Nat} (h : a < BitVec.twoPow w w') : (a.setWidth w').setWidth w = a := by
|
||||
by_cases hw : w' < w
|
||||
· simp only [toNat_eq, toNat_setWidth]
|
||||
rw [Nat.mod_mod_of_dvd' (Nat.pow_dvd_pow _ (Nat.le_of_lt hw)), Nat.mod_eq_of_lt]
|
||||
rwa [BitVec.lt_def, BitVec.toNat_twoPow_of_lt hw] at h
|
||||
· rw [BitVec.lt_def, BitVec.toNat_twoPow_of_le (by omega)] at h
|
||||
simp at h
|
||||
|
||||
/-! ### intMin -/
|
||||
|
||||
@[grind =]
|
||||
@@ -5779,6 +5858,25 @@ theorem msb_replicate {n w : Nat} {x : BitVec w} :
|
||||
simp only [BitVec.msb, getMsbD_replicate, Nat.zero_mod]
|
||||
cases n <;> cases w <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem reverse_eq_zero_iff {x : BitVec w} :
|
||||
x.reverse = 0#w ↔ x = 0#w := by
|
||||
constructor
|
||||
· intro hrev
|
||||
ext i hi
|
||||
rw [← getLsbD_eq_getElem, getLsbD_eq_getMsbD, ← getLsbD_reverse]
|
||||
simp [hrev]
|
||||
· intro hzero
|
||||
ext i hi
|
||||
rw [← getLsbD_eq_getElem, getLsbD_eq_getMsbD, getMsbD_reverse]
|
||||
simp [hi, hzero]
|
||||
|
||||
@[simp]
|
||||
theorem reverse_reverse_eq {x : BitVec w} :
|
||||
x.reverse.reverse = x := by
|
||||
ext k hk
|
||||
rw [getElem_reverse, getMsbD_reverse, getLsbD_eq_getElem]
|
||||
|
||||
/-! ### Inequalities (le / lt) -/
|
||||
|
||||
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
|
||||
@@ -5909,6 +6007,12 @@ theorem getElem_eq_true_of_lt_of_le {x : BitVec w} (hk' : k < w) (hlt: x.toNat <
|
||||
omega
|
||||
· simp [show w ≤ k + k' by omega] at hk'
|
||||
|
||||
theorem not_lt_iff {b : BitVec w} : ~~~b < b ↔ 0 < w ∧ b.msb = true := by
|
||||
refine ⟨fun h => ?_, fun ⟨hw, hb⟩ => ?_⟩
|
||||
· have := length_pos_of_lt h
|
||||
exact ⟨this, by rwa [← ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)] at h⟩
|
||||
· rwa [← ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)]
|
||||
|
||||
/-! ### Count leading zeros -/
|
||||
|
||||
theorem clzAuxRec_zero (x : BitVec w) :
|
||||
@@ -6182,16 +6286,70 @@ theorem toNat_lt_two_pow_sub_clz {x : BitVec w} :
|
||||
· simp [show w + 1 ≤ i by omega]
|
||||
· simp; omega
|
||||
|
||||
theorem clz_eq_reverse_ctz {x : BitVec w} :
|
||||
x.clz = (x.reverse).ctz := by
|
||||
simp [ctz]
|
||||
|
||||
/-! ### Deprecations -/
|
||||
/-! ### Count trailing zeros -/
|
||||
|
||||
set_option linter.missingDocs false
|
||||
|
||||
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
|
||||
abbrev toFin_uShiftRight := @toFin_ushiftRight
|
||||
theorem ctz_eq_reverse_clz {x : BitVec w} :
|
||||
x.ctz = (x.reverse).clz := by
|
||||
simp [ctz]
|
||||
|
||||
/-- The number of trailing zeroes is strictly less than the bitwidth iff the bitvector is nonzero. -/
|
||||
@[simp]
|
||||
theorem ctz_lt_iff_ne_zero {x : BitVec w} :
|
||||
ctz x < w ↔ x ≠ 0#w := by
|
||||
simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq]
|
||||
rw [show BitVec.ofNat w w = w by simp, ← reverse_eq_zero_iff (x := x)]
|
||||
apply clz_lt_iff_ne_zero (x := x.reverse)
|
||||
|
||||
/-- If a bitvec is different than zero the bits at indexes lower than `ctz x` are false. -/
|
||||
theorem getLsbD_false_of_lt_ctz {x : BitVec w} (hi : i < x.ctz.toNat) :
|
||||
x.getLsbD i = false := by
|
||||
rw [getLsbD_eq_getMsbD, ← getLsbD_reverse]
|
||||
have hiff := ctz_lt_iff_ne_zero (x := x)
|
||||
by_cases hzero : x = 0#w
|
||||
· simp [hzero, getLsbD_reverse]
|
||||
· simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq, hzero, not_false_eq_true,
|
||||
iff_true] at hiff
|
||||
simp only [ctz] at hi
|
||||
have hi' : i < w := by simp [BitVec.lt_def] at hiff; omega
|
||||
simp only [hi', decide_true, Bool.true_and]
|
||||
have : (x.reverse.clzAuxRec (w - 1)).toNat ≤ w := by
|
||||
rw [show ((x.reverse.clzAuxRec (w - 1)).toNat ≤ w) =
|
||||
((x.reverse.clzAuxRec (w - 1)).toNat ≤ (BitVec.ofNat w w).toNat) by simp, ← le_def]
|
||||
apply clzAuxRec_le (x := x.reverse) (n := w - 1)
|
||||
let j := (x.reverse.clzAuxRec (w - 1)).toNat - 1 - i
|
||||
rw [show w - 1 - i = w - (x.reverse.clzAuxRec (w - 1)).toNat + j by
|
||||
subst j
|
||||
rw [Nat.sub_sub (n := (x.reverse.clzAuxRec (w - 1)).toNat),
|
||||
← Nat.add_sub_assoc (by exact Nat.one_add_le_iff.mpr hi)]
|
||||
omega]
|
||||
have hfalse : ∀ (i : Nat), w - 1 < i → x.reverse.getLsbD i = false := by
|
||||
intros i hj
|
||||
simp [show w ≤ i by omega]
|
||||
exact getLsbD_false_of_clzAuxRec (x := x.reverse) (n := w - 1) hfalse (j := j)
|
||||
|
||||
/-- If a bitvec is different than zero, the bit at index `ctz x`, i.e., the first bit after the
|
||||
trailing zeros, is true. -/
|
||||
theorem getLsbD_true_ctz_of_ne_zero {x : BitVec w} (hx : x ≠ 0#w) :
|
||||
x.getLsbD (ctz x).toNat = true := by
|
||||
simp only [ctz_eq_reverse_clz, clz]
|
||||
rw [getLsbD_eq_getMsbD, ← getLsbD_reverse]
|
||||
have := ctz_lt_iff_ne_zero (x := x)
|
||||
simp only [ctz_eq_reverse_clz, clz, natCast_eq_ofNat, lt_def, toNat_ofNat, Nat.mod_two_pow_self,
|
||||
ne_eq] at this
|
||||
simp only [this, hx, not_false_eq_true, decide_true, Bool.true_and]
|
||||
have hnotrev : ¬x.reverse = 0#w := by simp [reverse_eq_zero_iff, hx]
|
||||
apply getLsbD_true_of_eq_clzAuxRec_of_ne_zero (x := x.reverse) (n := w - 1) hnotrev
|
||||
intro i hi
|
||||
simp [show w ≤ i by omega]
|
||||
|
||||
/-- A nonzero bitvector is lower-bounded by its trailing zeroes. -/
|
||||
theorem two_pow_ctz_le_toNat_of_ne_zero {x : BitVec w} (hx : x ≠ 0#w) :
|
||||
2 ^ (ctz x).toNat ≤ x.toNat := by
|
||||
have hclz := getLsbD_true_ctz_of_ne_zero (x := x) hx
|
||||
exact Nat.ge_two_pow_of_testBit hclz
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -7,6 +7,6 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.ByteArray.Basic
|
||||
public import Init.Data.ByteArray.Bootstrap
|
||||
public import Init.Data.ByteArray.Extra
|
||||
|
||||
public section
|
||||
public import Init.Data.ByteArray.Lemmas
|
||||
|
||||
@@ -11,16 +11,13 @@ public import Init.Data.UInt.Basic
|
||||
public import Init.Data.UInt.BasicAux
|
||||
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
|
||||
|
||||
structure ByteArray where
|
||||
data : Array UInt8
|
||||
|
||||
attribute [extern "lean_byte_array_mk"] ByteArray.mk
|
||||
attribute [extern "lean_byte_array_data"] ByteArray.data
|
||||
|
||||
namespace ByteArray
|
||||
|
||||
deriving instance BEq for ByteArray
|
||||
@@ -30,41 +27,49 @@ attribute [ext] ByteArray
|
||||
instance : DecidableEq ByteArray :=
|
||||
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
|
||||
|
||||
@[extern "lean_mk_empty_byte_array"]
|
||||
def emptyWithCapacity (c : @& Nat) : ByteArray :=
|
||||
{ data := #[] }
|
||||
|
||||
@[deprecated emptyWithCapacity (since := "2025-03-12")]
|
||||
abbrev mkEmpty := emptyWithCapacity
|
||||
|
||||
def empty : ByteArray := emptyWithCapacity 0
|
||||
|
||||
instance : Inhabited ByteArray where
|
||||
default := empty
|
||||
|
||||
instance : EmptyCollection ByteArray where
|
||||
emptyCollection := ByteArray.empty
|
||||
|
||||
@[extern "lean_byte_array_push"]
|
||||
def push : ByteArray → UInt8 → ByteArray
|
||||
| ⟨bs⟩, b => ⟨bs.push b⟩
|
||||
|
||||
@[extern "lean_byte_array_size"]
|
||||
def size : (@& ByteArray) → Nat
|
||||
| ⟨bs⟩ => bs.size
|
||||
/--
|
||||
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]
|
||||
@@ -75,43 +80,94 @@ 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)
|
||||
|
||||
protected def append (a : ByteArray) (b : ByteArray) : ByteArray :=
|
||||
@[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
|
||||
|
||||
instance : Append ByteArray := ⟨ByteArray.append⟩
|
||||
@[simp]
|
||||
theorem size_data {a : ByteArray} :
|
||||
a.data.size = a.size := rfl
|
||||
|
||||
@[csimp]
|
||||
theorem append_eq_fastAppend : @ByteArray.append = @ByteArray.fastAppend := by
|
||||
funext a b
|
||||
ext1
|
||||
apply Array.ext'
|
||||
simp [ByteArray.fastAppend, copySlice, ← size_data, - Array.append_assoc]
|
||||
|
||||
-- Needs to come after the `csimp` lemma
|
||||
instance : Append ByteArray where
|
||||
append := ByteArray.append
|
||||
|
||||
@[simp]
|
||||
theorem append_eq {a b : ByteArray} : a.append b = a ++ b := rfl
|
||||
|
||||
@[simp]
|
||||
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
|
||||
@@ -122,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
|
||||
@@ -143,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
|
||||
@@ -160,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
|
||||
@@ -178,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 β :=
|
||||
@@ -195,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) :=
|
||||
@@ -213,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`.
|
||||
|
||||
@@ -241,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 :=
|
||||
@@ -258,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⟩ =>
|
||||
@@ -276,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
|
||||
@@ -322,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⟩
|
||||
@@ -340,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⟩
|
||||
@@ -350,13 +466,4 @@ def prevn : Iterator → Nat → Iterator
|
||||
end Iterator
|
||||
end ByteArray
|
||||
|
||||
/--
|
||||
Converts a list of bytes into a `ByteArray`.
|
||||
-/
|
||||
def List.toByteArray (bs : List UInt8) : ByteArray :=
|
||||
let rec loop
|
||||
| [], r => r
|
||||
| b::bs, r => loop bs (r.push b)
|
||||
loop bs ByteArray.empty
|
||||
|
||||
instance : ToString ByteArray := ⟨fun bs => bs.toList.toString⟩
|
||||
|
||||
60
src/Init/Data/ByteArray/Bootstrap.lean
Normal file
60
src/Init/Data/ByteArray/Bootstrap.lean
Normal file
@@ -0,0 +1,60 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.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⟩⟩
|
||||
|
||||
@[simp]
|
||||
theorem toList_data_append' {a b : ByteArray} :
|
||||
(a.append b).data.toList = a.data.toList ++ b.data.toList := by
|
||||
have ⟨⟨a⟩⟩ := a
|
||||
have ⟨⟨b⟩⟩ := b
|
||||
rfl
|
||||
|
||||
theorem ext : {x y : ByteArray} → x.data = y.data → x = y
|
||||
| ⟨_⟩, ⟨_⟩, rfl => rfl
|
||||
|
||||
end ByteArray
|
||||
|
||||
@[simp]
|
||||
theorem List.toList_data_toByteArray {l : List UInt8} :
|
||||
l.toByteArray.data.toList = l := by
|
||||
rw [List.toByteArray]
|
||||
suffices ∀ a b, (List.toByteArray.loop a b).data.toList = b.data.toList ++ a by
|
||||
simpa using this l ByteArray.empty
|
||||
intro a b
|
||||
fun_induction List.toByteArray.loop a b with simp_all [toList_push]
|
||||
where
|
||||
toList_push {xs : Array UInt8} {x : UInt8} : (xs.push x).toList = xs.toList ++ [x] := by
|
||||
have ⟨xs⟩ := xs
|
||||
simp [Array.push, List.concat_eq_append]
|
||||
|
||||
theorem List.toByteArray_append' {l l' : List UInt8} :
|
||||
(l ++ l').toByteArray = l.toByteArray.append l'.toByteArray :=
|
||||
ByteArray.ext (ext (by simp))
|
||||
where
|
||||
ext : {x y : Array UInt8} → x.toList = y.toList → x = y
|
||||
| ⟨_⟩, ⟨_⟩, rfl => rfl
|
||||
@@ -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 |||
|
||||
|
||||
259
src/Init/Data/ByteArray/Lemmas.lean
Normal file
259
src/Init/Data/ByteArray/Lemmas.lean
Normal file
@@ -0,0 +1,259 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.ByteArray.Basic
|
||||
public import Init.Data.Array.Extract
|
||||
|
||||
public section
|
||||
|
||||
-- At present the preferred normal form for empty byte arrays is `ByteArray.empty`
|
||||
@[simp]
|
||||
theorem emptyc_eq_empty : (∅ : ByteArray) = ByteArray.empty := rfl
|
||||
|
||||
@[simp]
|
||||
theorem emptyWithCapacity_eq_empty : ByteArray.emptyWithCapacity 0 = ByteArray.empty := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.data_empty : ByteArray.empty.data = #[] := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.data_extract {a : ByteArray} {b e : Nat} :
|
||||
(a.extract b e).data = a.data.extract b e := by
|
||||
simp [extract, copySlice]
|
||||
by_cases b ≤ e
|
||||
· rw [(by omega : b + (e - b) = e)]
|
||||
· rw [Array.extract_eq_empty_of_le (by omega), Array.extract_eq_empty_of_le (by omega)]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_zero_size {b : ByteArray} : b.extract 0 b.size = b := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_same {b : ByteArray} {i : Nat} : b.extract i i = ByteArray.empty := by
|
||||
ext1
|
||||
simp [Nat.min_le_left]
|
||||
|
||||
theorem ByteArray.fastAppend_eq_copySlice {a b : ByteArray} :
|
||||
a.fastAppend b = b.copySlice 0 a a.size b.size false := rfl
|
||||
|
||||
@[simp]
|
||||
theorem List.toByteArray_append {l l' : List UInt8} :
|
||||
(l ++ l').toByteArray = l.toByteArray ++ l'.toByteArray := by
|
||||
simp [List.toByteArray_append']
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.toList_data_append {l l' : ByteArray} :
|
||||
(l ++ l').data.toList = l.data.toList ++ l'.data.toList := by
|
||||
simp [← append_eq]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.data_append {l l' : ByteArray} :
|
||||
(l ++ l').data = l.data ++ l'.data := by
|
||||
simp [← Array.toList_inj]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.size_empty : ByteArray.empty.size = 0 := by
|
||||
simp [← ByteArray.size_data]
|
||||
|
||||
@[simp]
|
||||
theorem List.data_toByteArray {l : List UInt8} :
|
||||
l.toByteArray.data = l.toArray := by
|
||||
rw [List.toByteArray]
|
||||
suffices ∀ a b, (List.toByteArray.loop a b).data = b.data ++ a.toArray by
|
||||
simpa using this l ByteArray.empty
|
||||
intro a b
|
||||
fun_induction List.toByteArray.loop a b with simp_all
|
||||
|
||||
@[simp]
|
||||
theorem List.size_toByteArray {l : List UInt8} :
|
||||
l.toByteArray.size = l.length := by
|
||||
simp [← ByteArray.size_data]
|
||||
|
||||
@[simp]
|
||||
theorem List.toByteArray_nil : List.toByteArray [] = ByteArray.empty := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.empty_append {b : ByteArray} : ByteArray.empty ++ b = b := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.append_empty {b : ByteArray} : b ++ ByteArray.empty = b := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp, grind =]
|
||||
theorem ByteArray.size_append {a b : ByteArray} : (a ++ b).size = a.size + b.size := by
|
||||
simp [← size_data]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.size_eq_zero_iff {a : ByteArray} : a.size = 0 ↔ a = ByteArray.empty := by
|
||||
refine ⟨fun h => ?_, fun h => h ▸ ByteArray.size_empty⟩
|
||||
ext1
|
||||
simp [← Array.size_eq_zero_iff, h]
|
||||
|
||||
theorem ByteArray.getElem_eq_getElem_data {a : ByteArray} {i : Nat} {h : i < a.size} :
|
||||
a[i] = a.data[i]'(by simpa [← size_data]) := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.getElem_append_left {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
|
||||
(hlt : i < a.size) : (a ++ b)[i] = a[i] := by
|
||||
simp only [getElem_eq_getElem_data, data_append]
|
||||
rw [Array.getElem_append_left (by simpa)]
|
||||
|
||||
theorem ByteArray.getElem_append_right {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
|
||||
(hle : a.size ≤ i) : (a ++ b)[i] = b[i - a.size]'(by simp_all; omega) := by
|
||||
simp only [getElem_eq_getElem_data, data_append]
|
||||
rw [Array.getElem_append_right (by simpa)]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem List.getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.toByteArray.size} :
|
||||
l.toByteArray[i]'h = l[i]'(by simp_all) := by
|
||||
simp [ByteArray.getElem_eq_getElem_data]
|
||||
|
||||
theorem List.getElem_eq_getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.length} :
|
||||
l[i]'h = l.toByteArray[i]'(by simp_all) := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.size_extract {a : ByteArray} {b e : Nat} :
|
||||
(a.extract b e).size = min e a.size - b := by
|
||||
simp [← size_data]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_eq_empty_iff {b : ByteArray} {i j : Nat} : b.extract i j = ByteArray.empty ↔ min j b.size ≤ i := by
|
||||
rw [← size_eq_zero_iff, size_extract]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_add_left {b : ByteArray} {i j : Nat} : b.extract (i + j) i = ByteArray.empty := by
|
||||
simp only [extract_eq_empty_iff]
|
||||
exact Nat.le_trans (Nat.min_le_left _ _) (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.append_eq_empty_iff {a b : ByteArray} :
|
||||
a ++ b = ByteArray.empty ↔ a = ByteArray.empty ∧ b = ByteArray.empty := by
|
||||
simp [← size_eq_zero_iff, size_append]
|
||||
|
||||
@[simp]
|
||||
theorem List.toByteArray_eq_empty {l : List UInt8} :
|
||||
l.toByteArray = ByteArray.empty ↔ l = [] := by
|
||||
simp [← ByteArray.size_eq_zero_iff]
|
||||
|
||||
theorem ByteArray.append_right_inj {ys₁ ys₂ : ByteArray} (xs : ByteArray) :
|
||||
xs ++ ys₁ = xs ++ ys₂ ↔ ys₁ = ys₂ := by
|
||||
simp [ByteArray.ext_iff, Array.append_right_inj]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_append_extract {a : ByteArray} {i j k : Nat} :
|
||||
a.extract i j ++ a.extract j k = a.extract (min i j) (max j k) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_eq_extract_append_extract {a : ByteArray} {i k : Nat} (j : Nat)
|
||||
(hi : i ≤ j) (hk : j ≤ k) :
|
||||
a.extract i k = a.extract i j ++ a.extract j k := by
|
||||
simp
|
||||
rw [Nat.min_eq_left hi, Nat.max_eq_right hk]
|
||||
|
||||
theorem ByteArray.append_inj_left {xs₁ xs₂ ys₁ ys₂ : ByteArray} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) : xs₁ = xs₂ := by
|
||||
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 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]
|
||||
|
||||
theorem ByteArray.extract_append_eq_left {a b : ByteArray} {i : Nat} (hi : i = a.size) :
|
||||
(a ++ b).extract 0 i = a := by
|
||||
subst hi
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append_size_left {a b : ByteArray} {i : Nat} :
|
||||
(a ++ b).extract i a.size = a.extract i a.size := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append_size_add {a b : ByteArray} {i j : Nat} :
|
||||
(a ++ b).extract (a.size + i) (a.size + j) = b.extract i j := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append {as bs : ByteArray} {i j : Nat} :
|
||||
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append_size_add' {a b : ByteArray} {i j k : Nat} (h : k = a.size) :
|
||||
(a ++ b).extract (k + i) (k + j) = b.extract i j := by
|
||||
cases h
|
||||
rw [extract_append_size_add]
|
||||
|
||||
theorem ByteArray.extract_extract {a : ByteArray} {i j k l : Nat} :
|
||||
(a.extract i j).extract k l = a.extract (i + k) (min (i + l) j) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.getElem_extract_aux {xs : ByteArray} {start stop : Nat} (h : i < (xs.extract start stop).size) :
|
||||
start + i < xs.size := by
|
||||
rw [size_extract] at h; apply Nat.add_lt_of_lt_sub'; apply Nat.lt_of_lt_of_le h
|
||||
apply Nat.sub_le_sub_right; apply Nat.min_le_right
|
||||
|
||||
theorem ByteArray.getElem_extract {i : Nat} {b : ByteArray} {start stop : Nat}
|
||||
(h) : (b.extract start stop)[i]'h = b[start + i]'(getElem_extract_aux h) := by
|
||||
simp [getElem_eq_getElem_data]
|
||||
|
||||
theorem ByteArray.extract_eq_extract_left {a : ByteArray} {i i' j : Nat} :
|
||||
a.extract i j = a.extract i' j ↔ min j a.size - i = min j a.size - i' := by
|
||||
simp [ByteArray.ext_iff, Array.extract_eq_extract_left]
|
||||
|
||||
theorem ByteArray.extract_add_one {a : ByteArray} {i : Nat} (ha : i + 1 ≤ a.size) :
|
||||
a.extract i (i + 1) = [a[i]].toByteArray := by
|
||||
ext
|
||||
· simp
|
||||
omega
|
||||
· rename_i j hj hj'
|
||||
obtain rfl : j = 0 := by simpa using hj'
|
||||
simp [ByteArray.getElem_eq_getElem_data]
|
||||
|
||||
theorem ByteArray.extract_add_two {a : ByteArray} {i : Nat} (ha : i + 2 ≤ a.size) :
|
||||
a.extract i (i + 2) = [a[i], a[i + 1]].toByteArray := by
|
||||
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
|
||||
extract_add_one (by omega), extract_add_one (by omega)]
|
||||
simp [← List.toByteArray_append]
|
||||
|
||||
theorem ByteArray.extract_add_three {a : ByteArray} {i : Nat} (ha : i + 3 ≤ a.size) :
|
||||
a.extract i (i + 3) = [a[i], a[i + 1], a[i + 2]].toByteArray := by
|
||||
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
|
||||
extract_add_one (by omega), extract_add_two (by omega)]
|
||||
simp [← List.toByteArray_append]
|
||||
|
||||
theorem ByteArray.extract_add_four {a : ByteArray} {i : Nat} (ha : i + 4 ≤ a.size) :
|
||||
a.extract i (i + 4) = [a[i], a[i + 1], a[i + 2], a[i + 3]].toByteArray := by
|
||||
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
|
||||
extract_add_one (by omega), extract_add_three (by omega)]
|
||||
simp [← List.toByteArray_append]
|
||||
|
||||
theorem ByteArray.append_assoc {a b c : ByteArray} : a ++ b ++ c = a ++ (b ++ c) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.toList_empty : ByteArray.empty.toList = [] := by
|
||||
simp [ByteArray.toList, ByteArray.toList.loop]
|
||||
|
||||
theorem ByteArray.copySlice_eq_append {src : ByteArray} {srcOff : Nat} {dest : ByteArray} {destOff len : Nat} {exact : Bool} :
|
||||
ByteArray.copySlice src srcOff dest destOff len exact =
|
||||
dest.extract 0 destOff ++ src.extract srcOff (srcOff +len) ++ dest.extract (destOff + min len (src.data.size - srcOff)) dest.data.size := by
|
||||
ext1
|
||||
simp [copySlice]
|
||||
@@ -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
|
||||
|
||||
@@ -3,15 +3,11 @@ Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Core
|
||||
public import Init.Grind.Tactics
|
||||
|
||||
public section
|
||||
|
||||
namespace Function
|
||||
|
||||
/--
|
||||
@@ -51,6 +47,7 @@ theorem curry_apply {α β γ} (f : α × β → γ) (x : α) (y : β) : curry f
|
||||
rfl
|
||||
|
||||
/-- A function `f : α → β` is called injective if `f x = f y` implies `x = y`. -/
|
||||
@[expose]
|
||||
def Injective (f : α → β) : Prop :=
|
||||
∀ ⦃a₁ a₂⦄, f a₁ = f a₂ → a₁ = a₂
|
||||
|
||||
@@ -59,6 +56,7 @@ theorem Injective.comp {α β γ} {g : β → γ} {f : α → β} (hg : Injectiv
|
||||
|
||||
/-- A function `f : α → β` is called surjective if every `b : β` is equal to `f a`
|
||||
for some `a : α`. -/
|
||||
@[expose]
|
||||
def Surjective (f : α → β) : Prop :=
|
||||
∀ b, Exists fun a => f a = b
|
||||
|
||||
@@ -69,20 +67,22 @@ theorem Surjective.comp {α β γ} {g : β → γ} {f : α → β} (hg : Surject
|
||||
Exists.intro a (show g (f a) = c from Eq.trans (congrArg g ha) hb)
|
||||
|
||||
/-- `LeftInverse g f` means that `g` is a left inverse to `f`. That is, `g ∘ f = id`. -/
|
||||
@[grind]
|
||||
@[expose, grind]
|
||||
def LeftInverse {α β} (g : β → α) (f : α → β) : Prop :=
|
||||
∀ x, g (f x) = x
|
||||
|
||||
/-- `HasLeftInverse f` means that `f` has an unspecified left inverse. -/
|
||||
@[expose]
|
||||
def HasLeftInverse {α β} (f : α → β) : Prop :=
|
||||
Exists fun finv : β → α => LeftInverse finv f
|
||||
|
||||
/-- `RightInverse g f` means that `g` is a right inverse to `f`. That is, `f ∘ g = id`. -/
|
||||
@[grind]
|
||||
@[expose, grind]
|
||||
def RightInverse {α β} (g : β → α) (f : α → β) : Prop :=
|
||||
LeftInverse f g
|
||||
|
||||
/-- `HasRightInverse f` means that `f` has an unspecified right inverse. -/
|
||||
@[expose]
|
||||
def HasRightInverse {α β} (f : α → β) : Prop :=
|
||||
Exists fun finv : β → α => RightInverse finv f
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -206,9 +206,6 @@ theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 ≤ a / b ↔ 0 ≤ a
|
||||
| Int.ofNat (b+1), _ =>
|
||||
rcases a with ⟨a⟩ <;> simp [Int.ediv, -natCast_ediv]
|
||||
|
||||
@[deprecated ediv_nonneg_iff_of_pos (since := "2025-02-28")]
|
||||
abbrev div_nonneg_iff_of_pos := @ediv_nonneg_iff_of_pos
|
||||
|
||||
/-! ### emod -/
|
||||
|
||||
theorem emod_nonneg : ∀ (a : Int) {b : Int}, b ≠ 0 → 0 ≤ a % b
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -1347,8 +1350,6 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
|
||||
| 0 => Int.mul_zero _
|
||||
| -[_+1] => Int.mul_neg_one _
|
||||
|
||||
@[deprecated mul_sign_self (since := "2025-02-24")] abbrev mul_sign := @mul_sign_self
|
||||
|
||||
@[simp] theorem sign_mul_self (i : Int) : sign i * i = natAbs i := by
|
||||
rw [Int.mul_comm, mul_sign_self]
|
||||
|
||||
|
||||
@@ -50,14 +50,9 @@ protected theorem pow_ne_zero {n : Int} {m : Nat} : n ≠ 0 → n ^ m ≠ 0 := b
|
||||
|
||||
instance {n : Int} {m : Nat} [NeZero n] : NeZero (n ^ m) := ⟨Int.pow_ne_zero (NeZero.ne _)⟩
|
||||
|
||||
@[deprecated Nat.pow_le_pow_left (since := "2025-02-17")]
|
||||
abbrev pow_le_pow_of_le_left := @Nat.pow_le_pow_left
|
||||
|
||||
@[deprecated Nat.pow_le_pow_right (since := "2025-02-17")]
|
||||
abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
|
||||
|
||||
-- This can't be removed until the next update-stage0
|
||||
@[deprecated Nat.pow_pos (since := "2025-02-17")]
|
||||
abbrev pos_pow_of_pos := @Nat.pow_pos
|
||||
abbrev _root_.Nat.pos_pow_of_pos := @Nat.pow_pos
|
||||
|
||||
@[simp, norm_cast]
|
||||
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
|
||||
|
||||
@@ -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
|
||||
@@ -19,6 +19,7 @@ universe v u v' u'
|
||||
section ULiftT
|
||||
|
||||
/-- `ULiftT.{v, u}` shrinks a monad on `Type max u v` to a monad on `Type u`. -/
|
||||
@[expose] -- for codegen
|
||||
def ULiftT (n : Type max u v → Type v') (α : Type u) := n (ULift.{v} α)
|
||||
|
||||
/-- Returns the underlying `n`-monadic representation of a `ULiftT n α` value. -/
|
||||
@@ -89,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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user