mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-21 20:34:07 +00:00
Compare commits
200 Commits
docs/add_d
...
stack_over
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ac0662886 | ||
|
|
fe7b96d8a0 | ||
|
|
ec87283465 | ||
|
|
d7c6920550 | ||
|
|
227e861719 | ||
|
|
e9c302c17e | ||
|
|
5814a45d44 | ||
|
|
dcf74b0d89 | ||
|
|
a257767417 | ||
|
|
b8e67d87a8 | ||
|
|
2a5ca00ad6 | ||
|
|
ec27b3760d | ||
|
|
e5b7dc819b | ||
|
|
93c06c0552 | ||
|
|
bb7e6e4769 | ||
|
|
883a3e752d | ||
|
|
03040618b8 | ||
|
|
dfde4ee3aa | ||
|
|
07c407ab82 | ||
|
|
00dceb9a9d | ||
|
|
35d9307df3 | ||
|
|
9e4c414f48 | ||
|
|
6d22793ddf | ||
|
|
e0c1afd12d | ||
|
|
b1bedbe0d2 | ||
|
|
1ea92baa21 | ||
|
|
07be352ea7 | ||
|
|
3c11cca3cb | ||
|
|
3bd2a7419d | ||
|
|
26a1b934c2 | ||
|
|
93d7afb00a | ||
|
|
e362b50fa9 | ||
|
|
2df35360ee | ||
|
|
2db602c209 | ||
|
|
00cf5771f3 | ||
|
|
51abb0d4c7 | ||
|
|
e733149134 | ||
|
|
ac08be695e | ||
|
|
1d17c7df2b | ||
|
|
092ca8530a | ||
|
|
92fac419e7 | ||
|
|
e6160d7d4a | ||
|
|
74adb0961c | ||
|
|
4591747381 | ||
|
|
bc23383194 | ||
|
|
b470eb522b | ||
|
|
e13613d633 | ||
|
|
5f1c4df07d | ||
|
|
5f727699b0 | ||
|
|
e1b7984836 | ||
|
|
d9ea092585 | ||
|
|
359f60003a | ||
|
|
806e41151b | ||
|
|
527493c2a1 | ||
|
|
a12e8221da | ||
|
|
bcfad6e381 | ||
|
|
283587987a | ||
|
|
99e652ab1c | ||
|
|
c833afff11 | ||
|
|
c3714bdc6d | ||
|
|
cc2ccf71d5 | ||
|
|
f8d2ebd47a | ||
|
|
660eb9975a | ||
|
|
5c3f6363cc | ||
|
|
6e731b4370 | ||
|
|
18a69914da | ||
|
|
83c139f750 | ||
|
|
edbd7ce00d | ||
|
|
02925447bd | ||
|
|
a969d2702f | ||
|
|
27c79cb614 | ||
|
|
e2983e44ef | ||
|
|
01573067f9 | ||
|
|
f0b2621047 | ||
|
|
4b88965363 | ||
|
|
15cfe60640 | ||
|
|
7294646eb9 | ||
|
|
47a34316fc | ||
|
|
5a5a77dd44 | ||
|
|
5e30638725 | ||
|
|
dc442ec137 | ||
|
|
9d14c0456b | ||
|
|
bb1a373420 | ||
|
|
f817d5a706 | ||
|
|
adc4c6a7cf | ||
|
|
b8b6b219c3 | ||
|
|
63067d0d34 | ||
|
|
3be22538d2 | ||
|
|
99e8270d2d | ||
|
|
8fa36c7730 | ||
|
|
a359586a96 | ||
|
|
e3592e40cf | ||
|
|
7b0d4610b0 | ||
|
|
917a31f694 | ||
|
|
34a788110f | ||
|
|
ce350f3481 | ||
|
|
1630d9b803 | ||
|
|
605cecdde3 | ||
|
|
a9db0d2e53 | ||
|
|
158979380e | ||
|
|
f9f278266e | ||
|
|
861a92a06d | ||
|
|
f4ae6fc8aa | ||
|
|
f2a54ec0eb | ||
|
|
22a581f38d | ||
|
|
706a4cfd73 | ||
|
|
4fe0259354 | ||
|
|
41697dcf6c | ||
|
|
3990a9b3be | ||
|
|
05b68687c0 | ||
|
|
94360a72b3 | ||
|
|
fb135b8cfe | ||
|
|
4f664fb3b5 | ||
|
|
7a076d0bd4 | ||
|
|
f40c51f346 | ||
|
|
b2de43ed88 | ||
|
|
d95e741824 | ||
|
|
70a23945bf | ||
|
|
7c34b736fc | ||
|
|
7a65bde3e3 | ||
|
|
22ce2fea9b | ||
|
|
ea23ab6fef | ||
|
|
e437bfece9 | ||
|
|
5e313e98d0 | ||
|
|
46f42cc9ba | ||
|
|
7400a40116 | ||
|
|
41d310ab39 | ||
|
|
6ad28ca446 | ||
|
|
263c93aac8 | ||
|
|
e4daca8d6b | ||
|
|
3dd398a8a4 | ||
|
|
ac0f699775 | ||
|
|
69202d9b73 | ||
|
|
62cdb51ed5 | ||
|
|
5eb274d486 | ||
|
|
aeacb7b69e | ||
|
|
291bb84c97 | ||
|
|
3c36020d13 | ||
|
|
f23be4a964 | ||
|
|
c5ff671b8a | ||
|
|
9dcf07203e | ||
|
|
d6474135ba | ||
|
|
d1a42aae2a | ||
|
|
b6d77be6a5 | ||
|
|
0c9f9ab37a | ||
|
|
df1e6ba7fe | ||
|
|
faa4d16dc1 | ||
|
|
11ff00439e | ||
|
|
319940da77 | ||
|
|
11a9d2ee4b | ||
|
|
504336822f | ||
|
|
4f50544242 | ||
|
|
627a0f308b | ||
|
|
89558a007b | ||
|
|
036b5381f0 | ||
|
|
88ee503f02 | ||
|
|
2397a870f2 | ||
|
|
d3e004932c | ||
|
|
cefba8abd2 | ||
|
|
c6fbeaa721 | ||
|
|
85e7000666 | ||
|
|
75e68e7565 | ||
|
|
c51e4f57bd | ||
|
|
ac4b5089a3 | ||
|
|
784972462a | ||
|
|
535427ada4 | ||
|
|
c0fbcc76c4 | ||
|
|
ea910794fa | ||
|
|
a8df7d9d5c | ||
|
|
23aacdeac0 | ||
|
|
62bb0f662b | ||
|
|
822890ad27 | ||
|
|
3b0c101792 | ||
|
|
2dcd42f395 | ||
|
|
6712913bfe | ||
|
|
1c20b53419 | ||
|
|
40df539ef1 | ||
|
|
c4bfe25d18 | ||
|
|
3d24c68347 | ||
|
|
b0a305f19f | ||
|
|
eef928b98d | ||
|
|
9eeecb6d32 | ||
|
|
62747bd293 | ||
|
|
32b9bc47b7 | ||
|
|
864221d433 | ||
|
|
2e1ef2211c | ||
|
|
fb82428f2d | ||
|
|
c24b419ee4 | ||
|
|
ddbdfb954b | ||
|
|
e59fad2955 | ||
|
|
ecba8529cc | ||
|
|
723c340a8b | ||
|
|
2e3d523332 | ||
|
|
cd02ad76f1 | ||
|
|
2ba0a4549b | ||
|
|
3ed2d9b3ad | ||
|
|
36f1398aaa | ||
|
|
37938ecde1 | ||
|
|
68e3982eed | ||
|
|
36db040722 |
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -9,9 +9,15 @@ assignees: ''
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* [ ] Put an X between the brackets on this line if you have done all of the following:
|
||||
* Check that your issue is not already [filed](https://github.com/leanprover/lean4/issues).
|
||||
* Reduce the issue to a minimal, self-contained, reproducible test case. Avoid dependencies to mathlib4 or std4.
|
||||
Please put an X between the brackets as you perform the following steps:
|
||||
|
||||
* [ ] Check that your issue is not already filed:
|
||||
https://github.com/leanprover/lean4/issues
|
||||
* [ ] Reduce the issue to a minimal, self-contained, reproducible test case.
|
||||
Avoid dependencies to Mathlib or Batteries.
|
||||
* [ ] Test your test case against the latest nightly release, for example on
|
||||
https://live.lean-lang.org/#project=lean-nightly
|
||||
(You can also use the settings there to switch to “Lean nightly”)
|
||||
|
||||
### Description
|
||||
|
||||
@@ -33,8 +39,8 @@ assignees: ''
|
||||
|
||||
### Versions
|
||||
|
||||
[Output of `#eval Lean.versionString` or of `lean --version` in the folder that the issue occured in]
|
||||
[OS version]
|
||||
[Output of `#eval Lean.versionString`]
|
||||
[OS version, if not using live.lean-lang.org.]
|
||||
|
||||
### Additional Information
|
||||
|
||||
|
||||
57
.github/workflows/check-stage0.yml
vendored
Normal file
57
.github/workflows/check-stage0.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: Check for stage0 changes
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
check-stage0-on-queue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
filter: blob:none
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Find base commit
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "BASE=$(git merge-base origin/${{ github.base_ref }} HEAD)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Identify stage0 changes
|
||||
run: |
|
||||
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0 |
|
||||
grep -v -x -F $'stage0/src/stdlib_flags.h\nstage0/src/lean.mk.in' \
|
||||
> "$RUNNER_TEMP/stage0" || true
|
||||
if test -s "$RUNNER_TEMP/stage0"
|
||||
then
|
||||
echo "CHANGES=yes" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "CHANGES=no" >> "$GITHUB_ENV"
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- if: github.event_name == 'pull_request'
|
||||
name: Set label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo, number: issue_number } = context.issue;
|
||||
if (process.env.CHANGES == 'yes') {
|
||||
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['changes-stage0'] }).catch(() => {});
|
||||
} else {
|
||||
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'changes-stage0' }).catch(() => {});
|
||||
}
|
||||
|
||||
- if: env.CHANGES == 'yes'
|
||||
name: Report changes
|
||||
run: |
|
||||
echo "Found changes to stage0/, please do not merge using the merge queue." | tee "$GITHUB_STEP_SUMMARY"
|
||||
# shellcheck disable=SC2129
|
||||
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
||||
cat "$RUNNER_TEMP/stage0" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- if: github.event_name == 'merge_group' && env.CHANGES == 'yes'
|
||||
name: Fail when on the merge queue
|
||||
run: exit 1
|
||||
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -54,7 +54,10 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const quick = ${{ steps.set-quick.outputs.quick }};
|
||||
console.log(`quick: ${quick}`)
|
||||
console.log(`quick: ${quick}`);
|
||||
// use large runners outside PRs where available (original repo)
|
||||
// disabled for now as this mostly just speeds up the test suite which is not a bottleneck
|
||||
// let large = ${{ github.event_name != 'pull_request' && github.repository == 'leanprover/lean4' }} ? "-large" : "";
|
||||
let matrix = [
|
||||
{
|
||||
// portable release build: use channel with older glibc (2.27)
|
||||
@@ -110,7 +113,7 @@ jobs:
|
||||
},*/
|
||||
{
|
||||
"name": "macOS",
|
||||
"os": "macos-latest",
|
||||
"os": "macos-13",
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
@@ -121,7 +124,7 @@ jobs:
|
||||
},
|
||||
{
|
||||
"name": "macOS aarch64",
|
||||
"os": "macos-latest",
|
||||
"os": "macos-13",
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"cross": true,
|
||||
@@ -277,18 +280,18 @@ jobs:
|
||||
uses: cachix/install-nix-action@v18
|
||||
with:
|
||||
install_url: https://releases.nixos.org/nix/nix-2.12.0/install
|
||||
if: matrix.os == 'ubuntu-latest' && !matrix.cmultilib
|
||||
if: runner.os == 'Linux' && !matrix.cmultilib
|
||||
- name: Install MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: clang64
|
||||
# `:p` means prefix with appropriate msystem prefix
|
||||
pacboy: "make python cmake:p clang:p ccache:p gmp:p git zip unzip diffutils binutils tree zstd:p tar"
|
||||
if: matrix.os == 'windows-2022'
|
||||
if: runner.os == 'Windows'
|
||||
- name: Install Brew Packages
|
||||
run: |
|
||||
brew install ccache tree zstd coreutils gmp
|
||||
if: matrix.os == 'macos-latest'
|
||||
if: runner.os == 'macOS'
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v12
|
||||
with:
|
||||
@@ -312,13 +315,13 @@ jobs:
|
||||
run: |
|
||||
# open nix-shell once for initial setup
|
||||
true
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
if: runner.os == 'Linux'
|
||||
- name: Set up core dumps
|
||||
run: |
|
||||
mkdir -p $PWD/coredumps
|
||||
# store in current directory, for easy uploading together with binary
|
||||
echo $PWD/coredumps/%e.%p.%t | sudo tee /proc/sys/kernel/core_pattern
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
if: runner.os == 'Linux'
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
@@ -423,7 +426,7 @@ jobs:
|
||||
- name: CCache stats
|
||||
run: ccache -s
|
||||
- name: Show stacktrace for coredumps
|
||||
if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
if: ${{ failure() && runner.os == 'Linux' }}
|
||||
run: |
|
||||
for c in coredumps/*; do
|
||||
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
|
||||
@@ -433,7 +436,7 @@ jobs:
|
||||
# shared libs
|
||||
#- name: Upload coredumps
|
||||
# uses: actions/upload-artifact@v3
|
||||
# if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
# if: ${{ failure() && runner.os == 'Linux' }}
|
||||
# with:
|
||||
# name: coredumps-${{ matrix.name }}
|
||||
# path: |
|
||||
|
||||
2
.github/workflows/copyright-header.yml
vendored
2
.github/workflows/copyright-header.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
|
||||
- name: Verify .lean files start with a copyright header.
|
||||
run: |
|
||||
FILES=$(find . -type d \( -path "./tests" -o -path "./doc" -o -path "./src/lake/examples" -o -path "./src/lake/tests" -o -path "./build" -o -path "./nix" \) -prune -o -type f -name "*.lean" -exec perl -ne 'BEGIN { $/ = undef; } print "$ARGV\n" if !m{\A/-\nCopyright}; exit;' {} \;)
|
||||
FILES=$(find ./src -type d \( -path "./src/lake/examples" -o -path "./src/lake/tests" \) -prune -o -type f -name "*.lean" -exec perl -ne 'BEGIN { $/ = undef; } print "$ARGV\n" if !m{\A/-\nCopyright}; exit;' {} \;)
|
||||
if [ -n "$FILES" ]; then
|
||||
echo "Found .lean files which do not have a copyright header:"
|
||||
echo "$FILES"
|
||||
|
||||
34
.github/workflows/pr-release.yml
vendored
34
.github/workflows/pr-release.yml
vendored
@@ -126,11 +126,11 @@ jobs:
|
||||
if [ "$NIGHTLY_SHA" = "$MERGE_BASE_SHA" ]; then
|
||||
echo "The merge base of this PR coincides with the nightly release"
|
||||
|
||||
STD_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover/std4.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
|
||||
BATTERIES_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/batteries.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
|
||||
MATHLIB_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/mathlib4.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
|
||||
|
||||
if [[ -n "$STD_REMOTE_TAGS" ]]; then
|
||||
echo "... and Std has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
if [[ -n "$BATTERIES_REMOTE_TAGS" ]]; then
|
||||
echo "... and Batteries has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE=""
|
||||
|
||||
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
|
||||
@@ -140,8 +140,8 @@ jobs:
|
||||
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
|
||||
fi
|
||||
else
|
||||
echo "... but Std does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Std CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Std CI should run now."
|
||||
echo "... but Batteries does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Batteries CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Batteries CI should run now."
|
||||
fi
|
||||
|
||||
else
|
||||
@@ -150,8 +150,8 @@ jobs:
|
||||
git -C lean4.git log -10 origin/master
|
||||
|
||||
git -C lean4.git fetch origin nightly-with-mathlib
|
||||
NIGHTLY_WITH_MATHLIB_SHA="$(git -C lean4.git rev-parse "nightly-with-mathlib")"
|
||||
MESSAGE="- ❗ Std/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_WITH_MATHLIB_SHA\`."
|
||||
NIGHTLY_WITH_MATHLIB_SHA="$(git -C lean4.git rev-parse "origin/nightly-with-mathlib")"
|
||||
MESSAGE="- ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_WITH_MATHLIB_SHA\`."
|
||||
fi
|
||||
|
||||
if [[ -n "$MESSAGE" ]]; then
|
||||
@@ -223,27 +223,27 @@ jobs:
|
||||
description: description,
|
||||
});
|
||||
|
||||
# We next automatically create a Std branch using this toolchain.
|
||||
# Std doesn't itself have a mechanism to report results of CI from this branch back to Lean
|
||||
# Instead this is taken care of by Mathlib CI, which will fail if Std fails.
|
||||
# We next automatically create a Batteries branch using this toolchain.
|
||||
# Batteries doesn't itself have a mechanism to report results of CI from this branch back to Lean
|
||||
# Instead this is taken care of by Mathlib CI, which will fail if Batteries fails.
|
||||
- name: Cleanup workspace
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
sudo rm -rf ./*
|
||||
|
||||
# Checkout the Std repository with all branches
|
||||
- name: Checkout Std repository
|
||||
# Checkout the Batteries repository with all branches
|
||||
- name: Checkout Batteries repository
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: leanprover/std4
|
||||
repository: leanprover-community/batteries
|
||||
token: ${{ secrets.MATHLIB4_BOT }}
|
||||
ref: nightly-testing
|
||||
fetch-depth: 0 # This ensures we check out all tags and branches.
|
||||
|
||||
- name: Check if tag exists
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
id: check_std_tag
|
||||
id: check_batteries_tag
|
||||
run: |
|
||||
git config user.name "leanprover-community-mathlib4-bot"
|
||||
git config user.email "leanprover-community-mathlib4-bot@users.noreply.github.com"
|
||||
@@ -251,7 +251,7 @@ jobs:
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Std. Falling back to 'nightly-testing'."
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
@@ -268,7 +268,7 @@ jobs:
|
||||
else
|
||||
echo "Branch already exists, pushing an empty commit."
|
||||
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Std `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
|
||||
# The Batteries `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
|
||||
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
|
||||
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
@@ -321,7 +321,7 @@ jobs:
|
||||
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
|
||||
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
|
||||
git add lean-toolchain
|
||||
sed -i "s/require std from git \"https:\/\/github.com\/leanprover\/std4\" @ \".\+\"/require std from git \"https:\/\/github.com\/leanprover\/std4\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
|
||||
sed -i "s/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \".\+\"/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
|
||||
git add lakefile.lean
|
||||
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
else
|
||||
|
||||
26
CODEOWNERS
26
CODEOWNERS
@@ -6,7 +6,6 @@
|
||||
|
||||
/.github/ @Kha @semorrison
|
||||
/RELEASES.md @semorrison
|
||||
/src/Init/IO.lean @joehendrix
|
||||
/src/kernel/ @leodemoura
|
||||
/src/lake/ @tydeu
|
||||
/src/Lean/Compiler/ @leodemoura
|
||||
@@ -20,4 +19,27 @@
|
||||
/src/Lean/PrettyPrinter/Delaborator/ @kmill
|
||||
/src/Lean/Server/ @mhuisi
|
||||
/src/Lean/Widget/ @Vtec234
|
||||
/src/runtime/io.cpp @joehendrix
|
||||
/src/Init/Data/ @semorrison
|
||||
/src/Init/Data/Array/Lemmas.lean @digama0
|
||||
/src/Init/Data/List/Lemmas.lean @digama0
|
||||
/src/Init/Data/List/BasicAux.lean @digama0
|
||||
/src/Init/Data/Array/Subarray.lean @david-christiansen
|
||||
/src/Lean/Elab/Tactic/RCases.lean @digama0
|
||||
/src/Init/RCases.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Ext.lean @digama0
|
||||
/src/Init/Ext.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Simpa.lean @digama0
|
||||
/src/Lean/Elab/Tactic/NormCast.lean @digama0
|
||||
/src/Lean/Meta/Tactic/NormCast.lean @digama0
|
||||
/src/Lean/Meta/Tactic/TryThis.lean @digama0
|
||||
/src/Lean/Elab/Tactic/SimpTrace.lean @digama0
|
||||
/src/Lean/Elab/Tactic/NoMatch.lean @digama0
|
||||
/src/Lean/Elab/Tactic/ShowTerm.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Repeat.lean @digama0
|
||||
/src/Lean/Meta/Tactic/Repeat.lean @digama0
|
||||
/src/Lean/Meta/CoeAttr.lean @digama0
|
||||
/src/Lean/Elab/GuardMsgs.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Guard.lean @digama0
|
||||
/src/Init/Guard.lean @digama0
|
||||
/src/Lean/Server/CodeActions/ @digama0
|
||||
|
||||
|
||||
@@ -22,4 +22,4 @@ Please read our [Contribution Guidelines](CONTRIBUTING.md) first.
|
||||
|
||||
# Building from Source
|
||||
|
||||
See [Building Lean](https://lean-lang.org/lean4/doc/make/index.html).
|
||||
See [Building Lean](https://lean-lang.org/lean4/doc/make/index.html) (documentation source: [doc/make/index.md](doc/make/index.md)).
|
||||
|
||||
48
RELEASES.md
48
RELEASES.md
@@ -8,7 +8,10 @@ This file contains work-in-progress notes for the upcoming release, as well as p
|
||||
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
|
||||
of each version.
|
||||
|
||||
v4.8.0 (development in progress)
|
||||
v4.9.0 (development in progress)
|
||||
---------
|
||||
|
||||
v4.8.0
|
||||
---------
|
||||
|
||||
* **Executables configured with `supportInterpreter := true` on Windows should now be run via `lake exe` to function properly.**
|
||||
@@ -21,7 +24,7 @@ v4.8.0 (development in progress)
|
||||
|
||||
* Importing two different files containing proofs of the same theorem is no longer considered an error. This feature is particularly useful for theorems that are automatically generated on demand (e.g., equational theorems).
|
||||
|
||||
* Funcitonal induction principles.
|
||||
* Functional induction principles.
|
||||
|
||||
Derived from the definition of a (possibly mutually) recursive function, a **functional induction principle** is created that is tailored to proofs about that function.
|
||||
|
||||
@@ -57,6 +60,15 @@ v4.8.0 (development in progress)
|
||||
```
|
||||
is recognized without having to say `termination_by arr.size - i`.
|
||||
|
||||
* Shorter instances names. There is a new algorithm for generating names for anonymous instances.
|
||||
Across Std and Mathlib, the median ratio between lengths of new names and of old names is about 72%.
|
||||
With the old algorithm, the longest name was 1660 characters, and now the longest name is 202 characters.
|
||||
The new algorithm's 95th percentile name length is 67 characters, versus 278 for the old algorithm.
|
||||
While the new algorithm produces names that are 1.2% less unique,
|
||||
it avoids cross-project collisions by adding a module-based suffix
|
||||
when it does not refer to declarations from the same "project" (modules that share the same root).
|
||||
PR [#3089](https://github.com/leanprover/lean4/pull/3089).
|
||||
|
||||
* Attribute `@[pp_using_anonymous_constructor]` to make structures pretty print like `⟨x, y, z⟩`
|
||||
rather than `{a := x, b := y, c := z}`.
|
||||
This attribute is applied to `Sigma`, `PSigma`, `PProd`, `Subtype`, `And`, and `Fin`.
|
||||
@@ -70,18 +82,32 @@ v4.8.0 (development in progress)
|
||||
Field notation can be disabled on a function-by-function basis using the `@[pp_nodot]` attribute.
|
||||
|
||||
* Added options `pp.mvars` (default: true) and `pp.mvars.withType` (default: false).
|
||||
When `pp.mvars` is false, metavariables pretty print as `?_`,
|
||||
and when `pp.mvars.withType` is true, metavariables pretty print with a type ascription.
|
||||
These can be set when using `#guard_msgs` to make tests not rely on the unique ids assigned to anonymous metavariables.
|
||||
[#3798](https://github.com/leanprover/lean4/pull/3798).
|
||||
When `pp.mvars` is false, expression metavariables pretty print as `?_` and universe metavariables pretty print as `_`.
|
||||
When `pp.mvars.withType` is true, expression metavariables pretty print with a type ascription.
|
||||
These can be set when using `#guard_msgs` to make tests not depend on the particular names of metavariables.
|
||||
[#3798](https://github.com/leanprover/lean4/pull/3798) and
|
||||
[#3978](https://github.com/leanprover/lean4/pull/3978).
|
||||
|
||||
* Hovers for terms in `match` expressions in the Infoview now reliably show the correct term.
|
||||
|
||||
* Added `@[induction_eliminator]` and `@[cases_eliminator]` attributes to be able to define custom eliminators
|
||||
for the `induction` and `cases` tactics, replacing the `@[eliminator]` attribute.
|
||||
Gives custom eliminators for `Nat` so that `induction` and `cases` put goal states into terms of `0` and `n + 1`
|
||||
rather than `Nat.zero` and `Nat.succ n`.
|
||||
Added option `tactic.customEliminators` to control whether to use custom eliminators.
|
||||
[#3629](https://github.com/leanprover/lean4/pull/3629) and
|
||||
[#3655](https://github.com/leanprover/lean4/pull/3655).
|
||||
Added a hack for `rcases`/`rintro`/`obtain` to use the custom eliminator for `Nat`.
|
||||
[#3629](https://github.com/leanprover/lean4/pull/3629),
|
||||
[#3655](https://github.com/leanprover/lean4/pull/3655), and
|
||||
[#3747](https://github.com/leanprover/lean4/pull/3747).
|
||||
|
||||
* The `#guard_msgs` command now has options to change whitespace normalization and sensitivity to message ordering.
|
||||
For example, `#guard_msgs (whitespace := lax) in cmd` collapses whitespace before checking messages,
|
||||
and `#guard_msgs (ordering := sorted) in cmd` sorts the messages in lexicographic order before checking.
|
||||
PR [#3883](https://github.com/leanprover/lean4/pull/3883).
|
||||
|
||||
* The `#guard_msgs` command now supports showing a diff between the expected and actual outputs. This feature is currently
|
||||
disabled by default, but can be enabled with `set_option guard_msgs.diff true`. Depending on user feedback, this option
|
||||
may default to `true` in a future version of Lean.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
@@ -112,6 +138,12 @@ fact.def :
|
||||
|
||||
* The coercion from `String` to `Name` was removed. Previously, it was `Name.mkSimple`, which does not separate strings at dots, but experience showed that this is not always the desired coercion. For the previous behavior, manually insert a call to `Name.mkSimple`.
|
||||
|
||||
* The `Subarray` fields `as`, `h₁` and `h₂` have been renamed to `array`, `start_le_stop`, and `stop_le_array_size`, respectively. This more closely follows standard Lean conventions. Deprecated aliases for the field projections were added; these will be removed in a future release.
|
||||
|
||||
* The change to the instance name algorithm (described above) can break projects that made use of the auto-generated names.
|
||||
|
||||
* `Option.toMonad` has been renamed to `Option.getM` and the unneeded `[Monad m]` instance argument has been removed.
|
||||
|
||||
v4.7.0
|
||||
---------
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
open Std
|
||||
open Batteries
|
||||
open Lean
|
||||
|
||||
inductive BoolExpr where
|
||||
|
||||
@@ -75,14 +75,28 @@ The github repository will automatically update stage0 on `master` once
|
||||
|
||||
If you have write access to the lean4 repository, you can also also manually
|
||||
trigger that process, for example to be able to use new features in the compiler itself.
|
||||
You can do that on <https://github.com/nomeata/lean4/actions/workflows/update-stage0.yml>
|
||||
You can do that on <https://github.com/leanprover/lean4/actions/workflows/update-stage0.yml>
|
||||
or using Github CLI with
|
||||
```
|
||||
gh workflow run update-stage0.yml
|
||||
```
|
||||
|
||||
Leaving stage0 updates to the CI automation is preferable, but should you need to do it locally, you can use `make update-stage0-commit` in `build/release` to update `stage0` from `stage1` or `make -C stageN update-stage0-commit` to update from another stage.
|
||||
This command will automatically stage the updated files and introduce a commit, so make sure to commit your work before that. Then coordinate with the admins to not squash your PR so that stage 0 updates are preserved as separate commits.
|
||||
Leaving stage0 updates to the CI automation is preferable, but should you need
|
||||
to do it locally, you can use `make update-stage0-commit` in `build/release` to
|
||||
update `stage0` from `stage1` or `make -C stageN update-stage0-commit` to
|
||||
update from another stage. This command will automatically stage the updated files
|
||||
and introduce a commit,so make sure to commit your work before that.
|
||||
|
||||
If you rebased the branch (either onto a newer version of `master`, or fixing
|
||||
up some commits prior to the stage0 update, recreate the stage0 update commits.
|
||||
The script `script/rebase-stage0.sh` can be used for that.
|
||||
|
||||
The CI should prevent PRs with changes to stage0 (besides `stdlib_flags.h`)
|
||||
from entering `master` through the (squashing!) merge queue, and label such PRs
|
||||
with the `changes-stage0` label. Such PRs should have a cleaned up history,
|
||||
with separate stage0 update commits; then coordinate with the admins to merge
|
||||
your PR using rebase merge, bypassing the merge queue.
|
||||
|
||||
|
||||
## Further Bootstrapping Complications
|
||||
|
||||
|
||||
@@ -53,10 +53,59 @@ In the case of `@[extern]` all *irrelevant* types are removed first; see next se
|
||||
Its runtime value is either a pointer to an opaque bignum object or, if the lowest bit of the "pointer" is 1 (`lean_is_scalar`), an encoded unboxed natural number (`lean_box`/`lean_unbox`).
|
||||
* A universe `Sort u`, type constructor `... → Sort u`, or proposition `p : Prop` is *irrelevant* and is either statically erased (see above) or represented as a `lean_object *` with the runtime value `lean_box(0)`
|
||||
* Any other type is represented by `lean_object *`.
|
||||
Its runtime value is a pointer to an object of a subtype of `lean_object` (see respective declarations in `lean.h`) or the unboxed value `lean_box(cidx)` for the `cidx`th constructor of an inductive type if this constructor does not have any relevant parameters.
|
||||
Its runtime value is a pointer to an object of a subtype of `lean_object` (see the "Inductive types" section below) or the unboxed value `lean_box(cidx)` for the `cidx`th constructor of an inductive type if this constructor does not have any relevant parameters.
|
||||
|
||||
Example: the runtime value of `u : Unit` is always `lean_box(0)`.
|
||||
|
||||
#### Inductive types
|
||||
|
||||
For inductive types which are in the fallback `lean_object *` case above and not trivial constructors, the type is stored as a `lean_ctor_object`, and `lean_is_ctor` will return true. A `lean_ctor_object` stores the constructor index in the header, and the fields are stored in the `m_objs` portion of the object.
|
||||
|
||||
The memory order of the fields is derived from the types and order of the fields in the declaration. They are ordered as follows:
|
||||
|
||||
* Non-scalar fields stored as `lean_object *`
|
||||
* Fields of type `USize`
|
||||
* Other scalar fields, in decreasing order by size
|
||||
|
||||
Within each group the fields are ordered in declaration order. **Warning**: Trivial wrapper types still count toward a field being treated as non-scalar for this purpose.
|
||||
|
||||
* To access fields of the first kind, use `lean_ctor_get(val, i)` to get the `i`th non-scalar field.
|
||||
* To access `USize` fields, use `lean_ctor_get_usize(val, n+i)` to get the `i`th usize field and `n` is the total number of fields of the first kind.
|
||||
* To access other scalar fields, use `lean_ctor_get_uintN(val, off)` or `lean_ctor_get_usize(val, off)` as appropriate. Here `off` is the byte offset of the field in the structure, starting at `n*sizeof(void*)` where `n` is the number of fields of the first two kinds.
|
||||
|
||||
For example, a structure such as
|
||||
```lean
|
||||
structure S where
|
||||
ptr_1 : Array Nat
|
||||
usize_1 : USize
|
||||
sc64_1 : UInt64
|
||||
ptr_2 : { x : UInt64 // x > 0 } -- wrappers don't count as scalars
|
||||
sc64_2 : Float -- `Float` is 64 bit
|
||||
sc8_1 : Bool
|
||||
sc16_1 : UInt16
|
||||
sc8_2 : UInt8
|
||||
sc64_3 : UInt64
|
||||
usize_2 : USize
|
||||
ptr_3 : Char -- trivial wrapper around `UInt32`
|
||||
sc32_1 : UInt32
|
||||
sc16_2 : UInt16
|
||||
```
|
||||
would get re-sorted into the following memory order:
|
||||
|
||||
* `S.ptr_1` - `lean_ctor_get(val, 0)`
|
||||
* `S.ptr_2` - `lean_ctor_get(val, 1)`
|
||||
* `S.ptr_3` - `lean_ctor_get(val, 2)`
|
||||
* `S.usize_1` - `lean_ctor_get_usize(val, 3)`
|
||||
* `S.usize_2` - `lean_ctor_get_usize(val, 4)`
|
||||
* `S.sc64_1` - `lean_ctor_get_uint64(val, sizeof(void*)*5)`
|
||||
* `S.sc64_2` - `lean_ctor_get_float(val, sizeof(void*)*5 + 8)`
|
||||
* `S.sc64_3` - `lean_ctor_get_uint64(val, sizeof(void*)*5 + 16)`
|
||||
* `S.sc32_1` - `lean_ctor_get_uint32(val, sizeof(void*)*5 + 24)`
|
||||
* `S.sc16_1` - `lean_ctor_get_uint16(val, sizeof(void*)*5 + 28)`
|
||||
* `S.sc16_2` - `lean_ctor_get_uint16(val, sizeof(void*)*5 + 30)`
|
||||
* `S.sc8_1` - `lean_ctor_get_uint8(val, sizeof(void*)*5 + 32)`
|
||||
* `S.sc8_2` - `lean_ctor_get_uint8(val, sizeof(void*)*5 + 33)`
|
||||
|
||||
### Borrowing
|
||||
|
||||
By default, all `lean_object *` parameters of an `@[extern]` function are considered *owned*, i.e. the external code is passed a "virtual RC token" and is responsible for passing this token along to another consuming function (exactly once) or freeing it via `lean_dec`.
|
||||
|
||||
@@ -21,7 +21,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- Reconcile discrepancies in the `v4.6.0` section,
|
||||
usually via copy and paste and a commit to `releases/v4.6.0`.
|
||||
- `git tag v4.6.0`
|
||||
- `git push origin v4.6.0`
|
||||
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
|
||||
- Now wait, while CI runs.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`,
|
||||
looking for the `v4.6.0` tag.
|
||||
@@ -34,48 +34,76 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
(e.g. `v4.6.0-rc1`), and quickly sanity check.
|
||||
- Next, we will move a curated list of downstream repos to the latest stable release.
|
||||
- For each of the repositories listed below:
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`.
|
||||
The PR title should be "chore: bump toolchain to v4.6.0".
|
||||
Since the `v4.6.0` release should be functionally identical to the last release candidate,
|
||||
which the repository should already be on, this PR is a no-op besides changing the toolchain.
|
||||
- Once this is merged, create the tag `v4.6.0` from `master`/`main` and push it.
|
||||
- Merge the tag `v4.6.0` into the stable branch.
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
|
||||
- Update the toolchain file
|
||||
- In the Lakefile, if there are dependencies on specific version tags of dependencies that you've already pushed as part of this process, update them to the new tag.
|
||||
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so it will work and be saved in the manifest
|
||||
- Run `lake update`
|
||||
- The PR title should be "chore: bump toolchain to v4.6.0".
|
||||
- Merge the PR once CI completes.
|
||||
- Create the tag `v4.6.0` from `master`/`main` and push it.
|
||||
- Merge the tag `v4.6.0` into the `stable` branch and push it.
|
||||
- We do this for the repositories:
|
||||
- [lean4checker](https://github.com/leanprover/lean4checker)
|
||||
- `lean4checker` uses a different version tagging scheme: use `toolchain/v4.6.0` rather than `v4.6.0`.
|
||||
- [Std](https://github.com/leanprover-community/repl)
|
||||
- No dependencies
|
||||
- Note: `lean4checker` uses a different version tagging scheme: use `toolchain/v4.6.0` rather than `v4.6.0`.
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [Batteries](https://github.com/leanprover-community/batteries)
|
||||
- No dependencies
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [ProofWidgets4](https://github.com/leanprover-community/ProofWidgets4)
|
||||
- `ProofWidgets` uses a sequential version tagging scheme, e.g. `v0.0.29`,
|
||||
which does not refer to the toolchain being used.
|
||||
- Make a new release in this sequence after merging the toolchain bump PR.
|
||||
- `ProofWidgets` does not maintain a `stable` branch.
|
||||
- Dependencies: `Batteries`
|
||||
- Note on versions and branches:
|
||||
- `ProofWidgets` uses a sequential version tagging scheme, e.g. `v0.0.29`,
|
||||
which does not refer to the toolchain being used.
|
||||
- Make a new release in this sequence after merging the toolchain bump PR.
|
||||
- `ProofWidgets` does not maintain a `stable` branch.
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag, following the version convention of the repository
|
||||
- [Aesop](https://github.com/leanprover-community/aesop)
|
||||
- Dependencies: `Batteries`
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [doc-gen4](https://github.com/leanprover/doc-gen4)
|
||||
- Dependencies: exist, but they're not part of the release workflow
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [import-graph](https://github.com/leanprover-community/import-graph)
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [Mathlib](https://github.com/leanprover-community/mathlib4)
|
||||
- In addition to updating the `lean-toolchain` and `lakefile.lean`,
|
||||
in `.github/workflows/build.yml.in` in the `lean4checker` section update the line
|
||||
`git checkout toolchain/v4.6.0` to the appropriate tag,
|
||||
and then run `.github/workflows/mk_build_yml.sh`.
|
||||
- Dependencies: `Aesop`, `ProofWidgets4`, `lean4checker`, `Batteries`, `doc-gen4`, `import-graph`
|
||||
- Toolchain bump PR notes:
|
||||
- In addition to updating the `lean-toolchain` and `lakefile.lean`,
|
||||
in `.github/workflows/build.yml.in` in the `lean4checker` section update the line
|
||||
`git checkout toolchain/v4.6.0` to the appropriate tag,
|
||||
and then run `.github/workflows/mk_build_yml.sh`. Coordinate with
|
||||
a Mathlib maintainer to get this merged.
|
||||
- Push the PR branch to the main Mathlib repository rather than a fork, or CI may not work reliably
|
||||
- Create and push the tag
|
||||
- Create a new branch from the tag, push it, and open a pull request against `stable`.
|
||||
Coordinate with a Mathlib maintainer to get this merged.
|
||||
- [REPL](https://github.com/leanprover-community/repl)
|
||||
- Dependencies: `Mathlib` (for test code)
|
||||
- Note that there are two copies of `lean-toolchain`/`lakefile.lean`:
|
||||
in the root, and in `test/Mathlib/`.
|
||||
- Note that there are dependencies between these packages:
|
||||
you should update the lakefile so that you are using the `v4.6.0` tag of upstream repositories
|
||||
(or the sequential tag for `ProofWidgets4`), and run `lake update` before committing.
|
||||
- This means that this process is sequential; each repository must have its bump PR merged,
|
||||
and the new tag pushed, before you can make the PR for the downstream repositories.
|
||||
- `lean4checker` has no dependencies
|
||||
- `Std` has no dependencies
|
||||
- `Aesop` depends on `Std`
|
||||
- `ProofWidgets4` depends on `Std`
|
||||
- `Mathlib` depends on `Aesop`, `ProofWidgets4`, and `lean4checker` (and transitively on `Std`)
|
||||
- `REPL` depends on `Mathlib` (this dependency is only for testing).
|
||||
in the root, and in `test/Mathlib/`. Edit both, and run `lake update` in both directories.
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- Merge the release announcement PR for the Lean website - it will be deployed automatically
|
||||
- Finally, make an announcement!
|
||||
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
|
||||
Please see previous announcements for suggested language.
|
||||
You will want a few bullet points for main topics from the release notes.
|
||||
Link to the blog post from the Zulip announcement.
|
||||
Please also make sure that whoever is handling social media knows the release is out.
|
||||
- Make sure that whoever is handling social media knows the release is out.
|
||||
|
||||
## Optimistic(?) time estimates:
|
||||
- Initial checks and push the tag: 30 minutes.
|
||||
@@ -95,8 +123,8 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
|
||||
- Decide which nightly release you want to turn into a release candidate.
|
||||
We will use `nightly-2024-02-29` in this example.
|
||||
- It is essential that Std and Mathlib already have reviewed branches compatible with this nightly.
|
||||
- Check that both Std and Mathlib's `bump/v4.7.0` branch contain `nightly-2024-02-29`
|
||||
- It is essential that Batteries and Mathlib already have reviewed branches compatible with this nightly.
|
||||
- Check that both Batteries and Mathlib's `bump/v4.7.0` branch contain `nightly-2024-02-29`
|
||||
in their `lean-toolchain`.
|
||||
- The steps required to reach that state are beyond the scope of this checklist, but see below!
|
||||
- Create the release branch from this nightly tag:
|
||||
@@ -154,7 +182,7 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
- We do this for the same list of repositories as for stable releases, see above.
|
||||
As above, there are dependencies between these, and so the process above is iterative.
|
||||
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
|
||||
- For Std/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
|
||||
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
|
||||
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
|
||||
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
|
||||
- Make an announcement!
|
||||
@@ -176,7 +204,7 @@ In particular, updating the downstream repositories is significantly more work
|
||||
# Preparing `bump/v4.7.0` branches
|
||||
|
||||
While not part of the release process per se,
|
||||
this is a brief summary of the work that goes into updating Std/Aesop/Mathlib to new versions.
|
||||
this is a brief summary of the work that goes into updating Batteries/Aesop/Mathlib to new versions.
|
||||
|
||||
Please read https://leanprover-community.github.io/contribute/tags_and_branches.html
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@ def ack : Nat → Nat → Nat
|
||||
| 0, y => y+1
|
||||
| x+1, 0 => ack x 1
|
||||
| x+1, y+1 => ack x (ack (x+1) y)
|
||||
termination_by ack x y => (x, y)
|
||||
termination_by x y => (x, y)
|
||||
|
||||
def sum (a : Array Int) : Int :=
|
||||
let rec go (i : Nat) :=
|
||||
if i < a.size then
|
||||
if _ : i < a.size then
|
||||
a[i] + go (i+1)
|
||||
else
|
||||
0
|
||||
termination_by a.size - i
|
||||
go 0
|
||||
termination_by go i => a.size - i
|
||||
|
||||
set_option pp.proofs true
|
||||
#print sum.go
|
||||
|
||||
@@ -4,43 +4,42 @@ open Lean Meta
|
||||
|
||||
def ctor (mvarId : MVarId) (idx : Nat) : MetaM (List MVarId) := do
|
||||
/- Set `MetaM` context using `mvarId` -/
|
||||
withMVarContext mvarId do
|
||||
mvarId.withContext do
|
||||
/- Fail if the metavariable is already assigned. -/
|
||||
checkNotAssigned mvarId `ctor
|
||||
mvarId.checkNotAssigned `ctor
|
||||
/- Retrieve the target type, instantiateMVars, and use `whnf`. -/
|
||||
let target ← getMVarType' mvarId
|
||||
let target ← mvarId.getType'
|
||||
let .const declName us := target.getAppFn
|
||||
| throwTacticEx `ctor mvarId "target is not an inductive datatype"
|
||||
let .inductInfo { ctors, .. } ← getConstInfo declName
|
||||
| throwTacticEx `ctor mvarId "target is not an inductive datatype"
|
||||
if idx = 0 then
|
||||
throwTacticEx `ctor mvarId "invalid index, it must be > 0"
|
||||
throwTacticEx `ctor mvarId "invalid index, it must be > 0"
|
||||
else if h : idx - 1 < ctors.length then
|
||||
apply mvarId (.const ctors[idx - 1] us)
|
||||
mvarId.apply (.const ctors[idx - 1] us)
|
||||
else
|
||||
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors"
|
||||
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors"
|
||||
|
||||
open Elab Tactic
|
||||
|
||||
elab "ctor" idx:num : tactic =>
|
||||
elab "ctor" idx:num : tactic =>
|
||||
liftMetaTactic (ctor · idx.getNat)
|
||||
|
||||
example (p : Prop) : p := by
|
||||
example (p : Prop) : p := by
|
||||
ctor 1 -- Error
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 0 -- Error
|
||||
exact h
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 3 -- Error
|
||||
exact h
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 2
|
||||
exact h
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 1
|
||||
exact h -- Error
|
||||
|
||||
exact h -- Error
|
||||
|
||||
@@ -5,15 +5,15 @@ open Lean Meta
|
||||
def ex1 (declName : Name) : MetaM Unit := do
|
||||
let info ← getConstInfo declName
|
||||
IO.println s!"{declName} : {← ppExpr info.type}"
|
||||
if let some val := info.value? then
|
||||
if let some val := info.value? then
|
||||
IO.println s!"{declName} : {← ppExpr val}"
|
||||
|
||||
|
||||
#eval ex1 ``Nat
|
||||
|
||||
def ex2 (declName : Name) : MetaM Unit := do
|
||||
let info ← getConstInfo declName
|
||||
trace[Meta.debug] "{declName} : {info.type}"
|
||||
if let some val := info.value? then
|
||||
if let some val := info.value? then
|
||||
trace[Meta.debug] "{declName} : {val}"
|
||||
|
||||
#eval ex2 ``Add.add
|
||||
@@ -30,9 +30,9 @@ def ex3 (declName : Name) : MetaM Unit := do
|
||||
trace[Meta.debug] "{x} : {← inferType x}"
|
||||
|
||||
def myMin [LT α] [DecidableRel (α := α) (·<·)] (a b : α) : α :=
|
||||
if a < b then
|
||||
if a < b then
|
||||
a
|
||||
else
|
||||
else
|
||||
b
|
||||
|
||||
set_option trace.Meta.debug true in
|
||||
@@ -40,7 +40,7 @@ set_option trace.Meta.debug true in
|
||||
|
||||
def ex4 : MetaM Unit := do
|
||||
let nat := mkConst ``Nat
|
||||
withLocalDeclD `a nat fun a =>
|
||||
withLocalDeclD `a nat fun a =>
|
||||
withLocalDeclD `b nat fun b => do
|
||||
let e ← mkAppM ``HAdd.hAdd #[a, b]
|
||||
trace[Meta.debug] "{e} : {← inferType e}"
|
||||
@@ -66,15 +66,17 @@ open Elab Term
|
||||
|
||||
def ex5 : TermElabM Unit := do
|
||||
let nat := Lean.mkConst ``Nat
|
||||
withLocalDeclD `a nat fun a => do
|
||||
withLocalDeclD `a nat fun a => do
|
||||
withLocalDeclD `b nat fun b => do
|
||||
let ab ← mkAppM ``HAdd.hAdd #[a, b]
|
||||
let stx ← `(fun x => if x < 10 then $(← exprToSyntax ab) + x else x + $(← exprToSyntax a))
|
||||
let abStx ← exprToSyntax ab
|
||||
let aStx ← exprToSyntax a
|
||||
let stx ← `(fun x => if x < 10 then $abStx + x else x + $aStx)
|
||||
let e ← elabTerm stx none
|
||||
trace[Meta.debug] "{e} : {← inferType e}"
|
||||
let e := mkApp e (mkNatLit 5)
|
||||
let e ← whnf e
|
||||
trace[Meta.debug] "{e}"
|
||||
|
||||
|
||||
set_option trace.Meta.debug true in
|
||||
#eval ex5
|
||||
|
||||
@@ -4,16 +4,16 @@ def ack : Nat → Nat → Nat
|
||||
| 0, y => y+1
|
||||
| x+1, 0 => ack x 1
|
||||
| x+1, y+1 => ack x (ack (x+1) y)
|
||||
termination_by ack x y => (x, y)
|
||||
termination_by x y => (x, y)
|
||||
|
||||
def sum (a : Array Int) : Int :=
|
||||
let rec go (i : Nat) :=
|
||||
if i < a.size then
|
||||
if _ : i < a.size then
|
||||
a[i] + go (i+1)
|
||||
else
|
||||
0
|
||||
termination_by a.size - i
|
||||
go 0
|
||||
termination_by go i => a.size - i
|
||||
|
||||
set_option pp.proofs true
|
||||
#print sum.go
|
||||
|
||||
@@ -15,7 +15,7 @@ data type containing several important pieces of information. First and foremost
|
||||
current player, and it has a random generator.
|
||||
-/
|
||||
|
||||
open Std (HashMap)
|
||||
open Batteries (HashMap)
|
||||
abbrev TileIndex := Nat × Nat -- a 2D index
|
||||
|
||||
inductive TileState where
|
||||
|
||||
@@ -180,7 +180,7 @@ rec {
|
||||
update-stage0 =
|
||||
let cTree = symlinkJoin { name = "cs"; paths = [ Init.cTree Lean.cTree ]; }; in
|
||||
writeShellScriptBin "update-stage0" ''
|
||||
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/update-stage0"}
|
||||
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/lib/update-stage0"}
|
||||
'';
|
||||
update-stage0-commit = writeShellScriptBin "update-stage0-commit" ''
|
||||
set -euo pipefail
|
||||
|
||||
28
script/collideProfiles.lean
Normal file
28
script/collideProfiles.lean
Normal file
@@ -0,0 +1,28 @@
|
||||
import Lean.Util.Profiler
|
||||
|
||||
/-!
|
||||
|
||||
Usage:
|
||||
```sh
|
||||
lean --run ./script/collideProfiles.lean **/*.lean.json ... > merged.json
|
||||
```
|
||||
|
||||
Merges multiple `trace.profiler.output` profiles into a single one while deduplicating samples with
|
||||
the same stack. This is useful for building cumulative profiles of medium-to-large projects because
|
||||
Firefox Profiler cannot handle hundreds of tracks and the deduplication will also ensure that the
|
||||
profile is small enough for uploading.
|
||||
|
||||
As ordering of samples is not meaningful after this transformation, only "Call Tree" and "Flame
|
||||
Graph" are useful for such profiles.
|
||||
-/
|
||||
|
||||
open Lean
|
||||
|
||||
def main (args : List String) : IO Unit := do
|
||||
let profiles ← args.toArray.mapM fun path => do
|
||||
let json ← IO.FS.readFile ⟨path⟩
|
||||
let profile ← IO.ofExcept $ Json.parse json
|
||||
IO.ofExcept <| fromJson? profile
|
||||
-- NOTE: `collide` should not be interpreted
|
||||
let profile := Firefox.Profile.collide profiles
|
||||
IO.println <| Json.compress <| toJson profile
|
||||
39
script/issues_summary.sh
Normal file
39
script/issues_summary.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
# https://chat.openai.com/share/7469c7c3-aceb-4d80-aee5-62982e1f1538
|
||||
|
||||
# Output CSV Header
|
||||
echo '"Issue URL","Title","Days Since Creation","Days Since Last Update","Total Reactions","Assignee","Labels"'
|
||||
|
||||
# Get the current date in YYYY-MM-DD format
|
||||
today=$(date +%Y-%m-%d)
|
||||
|
||||
# Fetch only open issues (excluding PRs and closed issues) from the repository 'leanprover/lean4'
|
||||
issues=$(gh api repos/leanprover/lean4/issues --paginate --jq '.[] | select(.pull_request == null and .state == "open") | {url: .html_url, title: .title, created_at: (.created_at | split("T")[0]), updated_at: (.updated_at | split("T")[0]), number: .number, assignee: (.assignee.login // ""), labels: [.labels[].name] | join(",")}')
|
||||
|
||||
# Process each JSON object
|
||||
echo "$issues" | while IFS= read -r issue; do
|
||||
# Extract fields from JSON
|
||||
url=$(echo "$issue" | jq -r '.url')
|
||||
title=$(echo "$issue" | jq -r '.title')
|
||||
created_at=$(echo "$issue" | jq -r '.created_at')
|
||||
updated_at=$(echo "$issue" | jq -r '.updated_at')
|
||||
issue_number=$(echo "$issue" | jq -r '.number')
|
||||
assignee=$(echo "$issue" | jq -r '.assignee')
|
||||
labels=$(echo "$issue" | jq -r '.labels')
|
||||
|
||||
# Calculate days since creation and update using macOS compatible date calculation
|
||||
days_since_created=$(( ($(date -jf "%Y-%m-%d" "$today" +%s) - $(date -jf "%Y-%m-%d" "$created_at" +%s)) / 86400 ))
|
||||
days_since_updated=$(( ($(date -jf "%Y-%m-%d" "$today" +%s) - $(date -jf "%Y-%m-%d" "$updated_at" +%s)) / 86400 ))
|
||||
|
||||
# Fetch the total number of reactions for each issue
|
||||
reaction_data=$(gh api repos/leanprover/lean4/issues/$issue_number/reactions --paginate --jq 'length' 2>&1)
|
||||
if [[ $reaction_data == *"Not Found"* ]]; then
|
||||
total_reactions="Error fetching reactions"
|
||||
else
|
||||
total_reactions=$reaction_data
|
||||
fi
|
||||
|
||||
# Format output as CSV by escaping quotes and delimiting with commas
|
||||
echo "\"$url\",\"${title//\"/\"\"}\",\"$days_since_created\",\"$days_since_updated\",\"$total_reactions\",\"$assignee\",\"$labels\""
|
||||
done
|
||||
2
script/lib/README.md
Normal file
2
script/lib/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
This directory contains various scripts that are *not* meant to be called
|
||||
directly, but through other scripts or makefiles.
|
||||
19
script/lib/rebase-editor.sh
Executable file
19
script/lib/rebase-editor.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
# Script internal to `./script/rebase-stage0.sh`
|
||||
|
||||
# Determine OS type for sed in-place editing
|
||||
SED_CMD=("sed" "-i")
|
||||
if [[ "$OSTYPE" == "darwin"* ]]
|
||||
then
|
||||
# macOS requires an empty string argument with -i for in-place editing
|
||||
SED_CMD=("sed" "-i" "")
|
||||
fi
|
||||
|
||||
if [ "$STAGE0_WITH_NIX" = true ]
|
||||
then
|
||||
"${SED_CMD[@]}" '/chore: update stage0/ s,.*,x nix run .#update-stage0-commit,' "$1"
|
||||
else
|
||||
"${SED_CMD[@]}" '/chore: update stage0/ s,.*,x make -j32 -C build/release update-stage0 \&\& git commit -m "chore: update stage0",' "$1"
|
||||
fi
|
||||
24
script/rebase-stage0.sh
Executable file
24
script/rebase-stage0.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script rebases onto the given branch/commit, and updates
|
||||
# all `chore: update stage0` commits along the way.
|
||||
|
||||
# Whether to use nix or make to update stage0
|
||||
if [ "$1" = "-nix" ]
|
||||
then
|
||||
export STAGE0_WITH_NIX=true
|
||||
shift
|
||||
fi
|
||||
|
||||
# Check if an argument is provided
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "Usage: $0 [-nix] <options to git rebase -i>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
|
||||
# Run git rebase in interactive mode, but automatically edit the todo list
|
||||
# using the defined GIT_SEQUENCE_EDITOR command
|
||||
GIT_SEQUENCE_EDITOR="$REPO_ROOT/script/lib/rebase-editor.sh" git rebase -i "$@"
|
||||
|
||||
@@ -9,7 +9,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 8)
|
||||
set(LEAN_VERSION_MINOR 9)
|
||||
set(LEAN_VERSION_PATCH 0)
|
||||
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
@@ -315,6 +315,12 @@ endif()
|
||||
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
|
||||
|
||||
# in local builds, link executables and not just dynlibs against C++ stdlib as well,
|
||||
# which is required for e.g. asan
|
||||
if(NOT LEAN_STANDALONE)
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
|
||||
endif()
|
||||
|
||||
# flags for user binaries = flags for toolchain binaries + Lake
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
|
||||
|
||||
@@ -585,7 +591,7 @@ endif()
|
||||
|
||||
if(PREV_STAGE)
|
||||
add_custom_target(update-stage0
|
||||
COMMAND bash -c 'CSRCS=${CMAKE_BINARY_DIR}/lib/temp script/update-stage0'
|
||||
COMMAND bash -c 'CSRCS=${CMAKE_BINARY_DIR}/lib/temp script/lib/update-stage0'
|
||||
DEPENDS make_stdlib
|
||||
WORKING_DIRECTORY "${LEAN_SOURCE_DIR}/..")
|
||||
|
||||
|
||||
@@ -63,3 +63,16 @@ theorem ite_some_none_eq_none [Decidable P] :
|
||||
@[simp] theorem ite_some_none_eq_some [Decidable P] :
|
||||
(if P then some x else none) = some y ↔ P ∧ x = y := by
|
||||
split <;> simp_all
|
||||
|
||||
-- This is not marked as `simp` as it is already handled by `dite_eq_right_iff`.
|
||||
theorem dite_some_none_eq_none [Decidable P] {x : P → α} :
|
||||
(if h : P then some (x h) else none) = none ↔ ¬P := by
|
||||
simp only [dite_eq_right_iff]
|
||||
rfl
|
||||
|
||||
@[simp] theorem dite_some_none_eq_some [Decidable P] {x : P → α} {y : α} :
|
||||
(if h : P then some (x h) else none) = some y ↔ ∃ h : P, x h = y := by
|
||||
by_cases h : P <;> simp only [h, dite_cond_eq_true, dite_cond_eq_false, Option.some.injEq,
|
||||
false_iff, not_exists]
|
||||
case pos => exact ⟨fun h_eq ↦ Exists.intro h h_eq, fun h_exists => h_exists.2⟩
|
||||
case neg => exact fun h_false _ ↦ h_false
|
||||
|
||||
@@ -15,6 +15,13 @@ namespace Classical
|
||||
noncomputable def indefiniteDescription {α : Sort u} (p : α → Prop) (h : ∃ x, p x) : {x // p x} :=
|
||||
choice <| let ⟨x, px⟩ := h; ⟨⟨x, px⟩⟩
|
||||
|
||||
/--
|
||||
Given that there exists an element satisfying `p`, returns one such element.
|
||||
|
||||
This is a straightforward consequence of, and equivalent to, `Classical.choice`.
|
||||
|
||||
See also `choose_spec`, which asserts that the returned value has property `p`.
|
||||
-/
|
||||
noncomputable def choose {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : α :=
|
||||
(indefiniteDescription p h).val
|
||||
|
||||
|
||||
@@ -20,8 +20,29 @@ def Functor.discard {f : Type u → Type v} {α : Type u} [Functor f] (x : f α)
|
||||
|
||||
export Functor (discard)
|
||||
|
||||
/--
|
||||
An `Alternative` functor is an `Applicative` functor that can "fail" or be "empty"
|
||||
and a binary operation `<|>` that “collects values” or finds the “left-most success”.
|
||||
|
||||
Important instances include
|
||||
* `Option`, where `failure := none` and `<|>` returns the left-most `some`.
|
||||
* Parser combinators typically provide an `Applicative` instance for error-handling and
|
||||
backtracking.
|
||||
|
||||
Error recovery and state can interact subtly. For example, the implementation of `Alternative` for `OptionT (StateT σ Id)` keeps modifications made to the state while recovering from failure, while `StateT σ (OptionT Id)` discards them.
|
||||
-/
|
||||
-- NB: List instance is in mathlib. Once upstreamed, add
|
||||
-- * `List`, where `failure` is the empty list and `<|>` concatenates.
|
||||
class Alternative (f : Type u → Type v) extends Applicative f : Type (max (u+1) v) where
|
||||
/--
|
||||
Produces an empty collection or recoverable failure. The `<|>` operator collects values or recovers
|
||||
from failures. See `Alternative` for more details.
|
||||
-/
|
||||
failure : {α : Type u} → f α
|
||||
/--
|
||||
Depending on the `Alternative` instance, collects values or recovers from `failure`s by
|
||||
returning the leftmost success. Can be written using the `<|>` operator syntax.
|
||||
-/
|
||||
orElse : {α : Type u} → f α → (Unit → f α) → f α
|
||||
|
||||
instance (f : Type u → Type v) (α : Type u) [Alternative f] : OrElse (f α) := ⟨Alternative.orElse⟩
|
||||
@@ -30,9 +51,15 @@ variable {f : Type u → Type v} [Alternative f] {α : Type u}
|
||||
|
||||
export Alternative (failure)
|
||||
|
||||
/--
|
||||
If the proposition `p` is true, does nothing, else fails (using `failure`).
|
||||
-/
|
||||
@[always_inline, inline] def guard {f : Type → Type v} [Alternative f] (p : Prop) [Decidable p] : f Unit :=
|
||||
if p then pure () else failure
|
||||
|
||||
/--
|
||||
Returns `some x` if `f` succeeds with value `x`, else returns `none`.
|
||||
-/
|
||||
@[always_inline, inline] def optional (x : f α) : f (Option α) :=
|
||||
some <$> x <|> pure none
|
||||
|
||||
|
||||
@@ -12,6 +12,15 @@ open Function
|
||||
@[simp] theorem monadLift_self [Monad m] (x : m α) : monadLift x = x :=
|
||||
rfl
|
||||
|
||||
/--
|
||||
The `Functor` typeclass only contains the operations of a functor.
|
||||
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
|
||||
including the preservation of the identity and composition laws:
|
||||
```
|
||||
id <$> x = x
|
||||
(h ∘ g) <$> x = h <$> g <$> x
|
||||
```
|
||||
-/
|
||||
class LawfulFunctor (f : Type u → Type v) [Functor f] : Prop where
|
||||
map_const : (Functor.mapConst : α → f β → f α) = Functor.map ∘ const β
|
||||
id_map (x : f α) : id <$> x = x
|
||||
@@ -24,6 +33,16 @@ attribute [simp] id_map
|
||||
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
|
||||
id_map x
|
||||
|
||||
/--
|
||||
The `Applicative` typeclass only contains the operations of an applicative functor.
|
||||
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
|
||||
```
|
||||
pure id <*> v = v
|
||||
pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
|
||||
pure f <*> pure x = pure (f x)
|
||||
u <*> pure y = pure (· y) <*> u
|
||||
```
|
||||
-/
|
||||
class LawfulApplicative (f : Type u → Type v) [Applicative f] extends LawfulFunctor f : Prop where
|
||||
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
|
||||
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
|
||||
@@ -42,6 +61,18 @@ attribute [simp] map_pure seq_pure
|
||||
@[simp] theorem pure_id_seq [Applicative f] [LawfulApplicative f] (x : f α) : pure id <*> x = x := by
|
||||
simp [pure_seq]
|
||||
|
||||
/--
|
||||
The `Monad` typeclass only contains the operations of a monad.
|
||||
`LawfulMonad` further asserts that these operations satisfy the laws of a monad,
|
||||
including associativity and identity laws for `bind`:
|
||||
```
|
||||
pure x >>= f = f x
|
||||
x >>= pure = x
|
||||
x >>= f >>= g = x >>= (fun x => f x >>= g)
|
||||
```
|
||||
|
||||
`LawfulMonad.mk'` is an alternative constructor containing useful defaults for many fields.
|
||||
-/
|
||||
class LawfulMonad (m : Type u → Type v) [Monad m] extends LawfulApplicative m : Prop where
|
||||
bind_pure_comp (f : α → β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
|
||||
bind_map {α β : Type u} (f : m (α → β)) (x : m α) : f >>= (. <$> x) = f <*> x
|
||||
|
||||
@@ -235,13 +235,13 @@ end StateT
|
||||
|
||||
instance : LawfulMonad (EStateM ε σ) := .mk'
|
||||
(id_map := fun x => funext <| fun s => by
|
||||
dsimp only [EStateM.instMonadEStateM, EStateM.map]
|
||||
dsimp only [EStateM.instMonad, EStateM.map]
|
||||
match x s with
|
||||
| .ok _ _ => rfl
|
||||
| .error _ _ => rfl)
|
||||
(pure_bind := fun _ _ => rfl)
|
||||
(bind_assoc := fun x _ _ => funext <| fun s => by
|
||||
dsimp only [EStateM.instMonadEStateM, EStateM.bind]
|
||||
dsimp only [EStateM.instMonad, EStateM.bind]
|
||||
match x s with
|
||||
| .ok _ _ => rfl
|
||||
| .error _ _ => rfl)
|
||||
|
||||
@@ -10,7 +10,7 @@ import Init.Control.Except
|
||||
|
||||
universe u v
|
||||
|
||||
instance : ToBool (Option α) := ⟨Option.toBool⟩
|
||||
instance : ToBool (Option α) := ⟨Option.isSome⟩
|
||||
|
||||
def OptionT (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Option α)
|
||||
|
||||
@@ -1114,9 +1114,6 @@ theorem eta (a : {x // p x}) (h : p (val a)) : mk (val a) h = a := by
|
||||
cases a
|
||||
exact rfl
|
||||
|
||||
instance {α : Type u} {p : α → Prop} {a : α} (h : p a) : Inhabited {x // p x} where
|
||||
default := ⟨a, h⟩
|
||||
|
||||
instance {α : Type u} {p : α → Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
|
||||
fun ⟨a, h₁⟩ ⟨b, h₂⟩ =>
|
||||
if h : a = b then isTrue (by subst h; exact rfl)
|
||||
@@ -2040,4 +2037,8 @@ class LawfulCommIdentity (op : α → α → α) (o : outParam α) [hc : Commuta
|
||||
left_id a := Eq.trans (hc.comm o a) (right_id a)
|
||||
right_id a := Eq.trans (hc.comm a o) (left_id a)
|
||||
|
||||
instance : Commutative Or := ⟨fun _ _ => propext or_comm⟩
|
||||
instance : Commutative And := ⟨fun _ _ => propext and_comm⟩
|
||||
instance : Commutative Iff := ⟨fun _ _ => propext iff_comm⟩
|
||||
|
||||
end Std
|
||||
|
||||
@@ -14,6 +14,7 @@ import Init.Data.String
|
||||
import Init.Data.List
|
||||
import Init.Data.Int
|
||||
import Init.Data.Array
|
||||
import Init.Data.Array.Subarray.Split
|
||||
import Init.Data.ByteArray
|
||||
import Init.Data.FloatArray
|
||||
import Init.Data.Fin
|
||||
|
||||
@@ -31,6 +31,7 @@ def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty n) where
|
||||
go (i : Nat) (acc : Array α) : Array α :=
|
||||
if h : i < n then go (i+1) (acc.push (f ⟨i, h⟩)) else acc
|
||||
termination_by n - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
/-- The array `#[0, 1, ..., n - 1]`. -/
|
||||
def range (n : Nat) : Array Nat :=
|
||||
@@ -43,7 +44,7 @@ instance : EmptyCollection (Array α) := ⟨Array.empty⟩
|
||||
instance : Inhabited (Array α) where
|
||||
default := Array.empty
|
||||
|
||||
def isEmpty (a : Array α) : Bool :=
|
||||
@[simp] def isEmpty (a : Array α) : Bool :=
|
||||
a.size = 0
|
||||
|
||||
def singleton (v : α) : Array α :=
|
||||
@@ -52,7 +53,7 @@ def singleton (v : α) : Array α :=
|
||||
/-- Low-level version of `fget` which is as fast as a C array read.
|
||||
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
|
||||
`fget` may be slightly slower than `uget`. -/
|
||||
@[extern "lean_array_uget"]
|
||||
@[extern "lean_array_uget", simp]
|
||||
def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
|
||||
a[i.toNat]
|
||||
|
||||
@@ -306,6 +307,7 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
|
||||
else
|
||||
pure r
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
map 0 (mkEmpty as.size)
|
||||
|
||||
@[inline]
|
||||
@@ -378,6 +380,7 @@ def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as :
|
||||
else
|
||||
pure false
|
||||
termination_by stop - j
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
loop start
|
||||
if h : stop ≤ as.size then
|
||||
any stop h
|
||||
@@ -463,6 +466,7 @@ def findIdx? {α : Type u} (as : Array α) (p : α → Bool) : Option Nat :=
|
||||
if p as[j] then some j else loop (j + 1)
|
||||
else none
|
||||
termination_by as.size - j
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
loop 0
|
||||
|
||||
def getIdx? [BEq α] (a : Array α) (v : α) : Option Nat :=
|
||||
@@ -557,6 +561,7 @@ def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) (
|
||||
else
|
||||
true
|
||||
termination_by a.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
@[inline] def isEqv (a b : Array α) (p : α → α → Bool) : Bool :=
|
||||
if h : a.size = b.size then
|
||||
@@ -661,6 +666,7 @@ def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size)
|
||||
else indexOfAux a v (i+1)
|
||||
else none
|
||||
termination_by a.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) :=
|
||||
indexOfAux a v 0
|
||||
@@ -703,6 +709,7 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
else
|
||||
as
|
||||
termination_by as.size
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
let rec go (i : Nat) (r : Array α) : Array α :=
|
||||
@@ -715,6 +722,7 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
else
|
||||
r
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
go 0 #[]
|
||||
|
||||
/-- Remove the element at a given index from an array without bounds checks, using a `Fin` index.
|
||||
@@ -725,16 +733,15 @@ def feraseIdx (a : Array α) (i : Fin a.size) : Array α :=
|
||||
if h : i.val + 1 < a.size then
|
||||
let a' := a.swap ⟨i.val + 1, h⟩ i
|
||||
let i' : Fin a'.size := ⟨i.val + 1, by simp [a', h]⟩
|
||||
have : a'.size - i' < a.size - i := by
|
||||
simp [a', Nat.sub_succ_lt_self _ _ i.isLt]
|
||||
a'.feraseIdx i'
|
||||
else
|
||||
a.pop
|
||||
termination_by a.size - i.val
|
||||
decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ i.isLt
|
||||
|
||||
theorem size_feraseIdx (a : Array α) (i : Fin a.size) : (a.feraseIdx i).size = a.size - 1 := by
|
||||
induction a, i using Array.feraseIdx.induct with
|
||||
| @case1 a i h a' _ _ ih =>
|
||||
| @case1 a i h a' _ ih =>
|
||||
unfold feraseIdx
|
||||
simp [h, a', ih]
|
||||
| case2 a i h =>
|
||||
@@ -763,6 +770,7 @@ def erase [BEq α] (as : Array α) (a : α) : Array α :=
|
||||
else
|
||||
as
|
||||
termination_by j.1
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
let j := as.size
|
||||
let as := as.push a
|
||||
loop as ⟨j, size_push .. ▸ j.lt_succ_self⟩
|
||||
@@ -816,6 +824,7 @@ def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : N
|
||||
else
|
||||
true
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
/-- Return true iff `as` is a prefix of `bs`.
|
||||
That is, `bs = as ++ t` for some `t : List α`.-/
|
||||
@@ -837,6 +846,7 @@ private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
else
|
||||
true
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
allDiffAux as 0
|
||||
@@ -852,6 +862,7 @@ def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
else
|
||||
cs
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
@[inline] def zipWith (as : Array α) (bs : Array β) (f : α → β → γ) : Array γ :=
|
||||
zipWithAux f as bs 0 #[]
|
||||
|
||||
@@ -48,6 +48,7 @@ where
|
||||
let b ← f as[i]
|
||||
go (i+1) ⟨acc.val.push b, by simp [acc.property]⟩ hlt
|
||||
termination_by as.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
@[inline] private unsafe def mapMonoMImp [Monad m] (as : Array α) (f : α → m α) : m (Array α) :=
|
||||
go 0 as
|
||||
|
||||
@@ -21,6 +21,8 @@ theorem eq_of_isEqvAux [DecidableEq α] (a b : Array α) (hsz : a.size = b.size)
|
||||
subst heq
|
||||
exact absurd (Nat.lt_of_lt_of_le high low) (Nat.lt_irrefl j)
|
||||
termination_by a.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) : Array.isEqv a b (fun x y => x = y) → a = b := by
|
||||
simp [Array.isEqv]
|
||||
@@ -37,6 +39,7 @@ theorem isEqvAux_self [DecidableEq α] (a : Array α) (i : Nat) : Array.isEqvAux
|
||||
case inl h => simp [h, isEqvAux_self a (i+1)]
|
||||
case inr h => simp [h]
|
||||
termination_by a.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (fun x y => x = y) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.MinMax
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.Array.Mem
|
||||
@@ -20,6 +21,13 @@ namespace Array
|
||||
|
||||
attribute [simp] data_toArray uset
|
||||
|
||||
@[simp] theorem singleton_def (v : α) : singleton v = #[v] := rfl
|
||||
|
||||
@[simp] theorem toArray_data : (a : Array α) → a.data.toArray = a
|
||||
| ⟨l⟩ => ext' (data_toArray l)
|
||||
|
||||
@[simp] theorem data_length {l : Array α} : l.data.length = l.size := rfl
|
||||
|
||||
@[simp] theorem mkEmpty_eq (α n) : @mkEmpty α n = #[] := rfl
|
||||
|
||||
@[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size]
|
||||
@@ -130,6 +138,7 @@ where
|
||||
simp [aux (i+1), map_eq_pure_bind]; rfl
|
||||
· rw [List.drop_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
termination_by arr.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
@[simp] theorem map_data (f : α → β) (arr : Array α) : (arr.map f).data = arr.data.map f := by
|
||||
rw [map, mapM_eq_foldlM]
|
||||
@@ -139,7 +148,8 @@ where
|
||||
simp [H]
|
||||
|
||||
@[simp] theorem size_map (f : α → β) (arr : Array α) : (arr.map f).size = arr.size := by
|
||||
simp [size]
|
||||
simp only [← data_length]
|
||||
simp
|
||||
|
||||
@[simp] theorem pop_data (arr : Array α) : arr.pop.data = arr.data.dropLast := rfl
|
||||
|
||||
@@ -187,7 +197,8 @@ theorem anyM_stop_le_start [Monad m] (p : α → m Bool) (as : Array α) (start
|
||||
theorem mem_def (a : α) (as : Array α) : a ∈ as ↔ a ∈ as.data :=
|
||||
⟨fun | .mk h => h, Array.Mem.mk⟩
|
||||
|
||||
/-- # get -/
|
||||
/-! # get -/
|
||||
|
||||
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
|
||||
|
||||
theorem getElem?_lt
|
||||
@@ -217,7 +228,7 @@ theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default
|
||||
@[simp] theorem get!_eq_getElem? [Inhabited α] (a : Array α) (i : Nat) : a.get! i = (a.get? i).getD default := by
|
||||
by_cases p : i < a.size <;> simp [getD_get?, get!_eq_getD, p]
|
||||
|
||||
/-- # set -/
|
||||
/-! # set -/
|
||||
|
||||
@[simp] theorem getElem_set_eq (a : Array α) (i : Fin a.size) (v : α) {j : Nat}
|
||||
(eq : i.val = j) (p : j < (a.set i v).size) :
|
||||
@@ -240,7 +251,7 @@ theorem getElem_set (a : Array α) (i : Fin a.size) (v : α) (j : Nat)
|
||||
(ne : i.val ≠ j) : (a.set i v)[j]? = a[j]? := by
|
||||
by_cases h : j < a.size <;> simp [getElem?_lt, getElem?_ge, Nat.ge_of_not_lt, ne, h]
|
||||
|
||||
/- # setD -/
|
||||
/-! # setD -/
|
||||
|
||||
@[simp] theorem set!_is_setD : @set! = @setD := rfl
|
||||
|
||||
@@ -266,4 +277,788 @@ theorem getElem?_setD_eq (a : Array α) {i : Nat} (p : i < a.size) (v : α) : (a
|
||||
by_cases h : i < a.size <;>
|
||||
simp [setD, Nat.not_lt_of_le, h, getD_get?]
|
||||
|
||||
/-! # ofFn -/
|
||||
|
||||
@[simp] theorem size_ofFn_go {n} (f : Fin n → α) (i acc) :
|
||||
(ofFn.go f i acc).size = acc.size + (n - i) := by
|
||||
if hin : i < n then
|
||||
unfold ofFn.go
|
||||
have : 1 + (n - (i + 1)) = n - i :=
|
||||
Nat.sub_sub .. ▸ Nat.add_sub_cancel' (Nat.le_sub_of_add_le (Nat.add_comm .. ▸ hin))
|
||||
rw [dif_pos hin, size_ofFn_go f (i+1), size_push, Nat.add_assoc, this]
|
||||
else
|
||||
have : n - i = 0 := Nat.sub_eq_zero_of_le (Nat.le_of_not_lt hin)
|
||||
unfold ofFn.go
|
||||
simp [hin, this]
|
||||
termination_by n - i
|
||||
|
||||
@[simp] theorem size_ofFn (f : Fin n → α) : (ofFn f).size = n := by simp [ofFn]
|
||||
|
||||
theorem getElem_ofFn_go (f : Fin n → α) (i) {acc k}
|
||||
(hki : k < n) (hin : i ≤ n) (hi : i = acc.size)
|
||||
(hacc : ∀ j, ∀ hj : j < acc.size, acc[j] = f ⟨j, Nat.lt_of_lt_of_le hj (hi ▸ hin)⟩) :
|
||||
haveI : acc.size + (n - acc.size) = n := Nat.add_sub_cancel' (hi ▸ hin)
|
||||
(ofFn.go f i acc)[k]'(by simp [*]) = f ⟨k, hki⟩ := by
|
||||
unfold ofFn.go
|
||||
if hin : i < n then
|
||||
have : 1 + (n - (i + 1)) = n - i :=
|
||||
Nat.sub_sub .. ▸ Nat.add_sub_cancel' (Nat.le_sub_of_add_le (Nat.add_comm .. ▸ hin))
|
||||
simp only [dif_pos hin]
|
||||
rw [getElem_ofFn_go f (i+1) _ hin (by simp [*]) (fun j hj => ?hacc)]
|
||||
cases (Nat.lt_or_eq_of_le <| Nat.le_of_lt_succ (by simpa using hj)) with
|
||||
| inl hj => simp [get_push, hj, hacc j hj]
|
||||
| inr hj => simp [get_push, *]
|
||||
else
|
||||
simp [hin, hacc k (Nat.lt_of_lt_of_le hki (Nat.le_of_not_lt (hi ▸ hin)))]
|
||||
termination_by n - i
|
||||
|
||||
@[simp] theorem getElem_ofFn (f : Fin n → α) (i : Nat) (h) :
|
||||
(ofFn f)[i] = f ⟨i, size_ofFn f ▸ h⟩ :=
|
||||
getElem_ofFn_go _ _ _ (by simp) (by simp) nofun
|
||||
|
||||
/-- # mkArray -/
|
||||
|
||||
@[simp] theorem mkArray_data (n : Nat) (v : α) : (mkArray n v).data = List.replicate n v := rfl
|
||||
|
||||
@[simp] theorem getElem_mkArray (n : Nat) (v : α) (h : i < (mkArray n v).size) :
|
||||
(mkArray n v)[i] = v := by simp [Array.getElem_eq_data_get]
|
||||
|
||||
/-- # mem -/
|
||||
|
||||
theorem mem_data {a : α} {l : Array α} : a ∈ l.data ↔ a ∈ l := (mem_def _ _).symm
|
||||
|
||||
theorem not_mem_nil (a : α) : ¬ a ∈ #[] := nofun
|
||||
|
||||
/-- # get lemmas -/
|
||||
|
||||
theorem getElem?_mem {l : Array α} {i : Fin l.size} : l[i] ∈ l := by
|
||||
erw [Array.mem_def, getElem_eq_data_get]
|
||||
apply List.get_mem
|
||||
|
||||
theorem getElem_fin_eq_data_get (a : Array α) (i : Fin _) : a[i] = a.data.get i := rfl
|
||||
|
||||
@[simp] theorem ugetElem_eq_getElem (a : Array α) {i : USize} (h : i.toNat < a.size) :
|
||||
a[i] = a[i.toNat] := rfl
|
||||
|
||||
theorem getElem?_eq_getElem (a : Array α) (i : Nat) (h : i < a.size) : a[i]? = a[i] :=
|
||||
getElem?_pos ..
|
||||
|
||||
theorem get?_len_le (a : Array α) (i : Nat) (h : a.size ≤ i) : a[i]? = none := by
|
||||
simp [getElem?_neg, h]
|
||||
|
||||
theorem getElem_mem_data (a : Array α) (h : i < a.size) : a[i] ∈ a.data := by
|
||||
simp only [getElem_eq_data_get, List.get_mem]
|
||||
|
||||
theorem getElem?_eq_data_get? (a : Array α) (i : Nat) : a[i]? = a.data.get? i := by
|
||||
by_cases i < a.size <;> simp_all [getElem?_pos, getElem?_neg, List.get?_eq_get, eq_comm]; rfl
|
||||
|
||||
theorem get?_eq_data_get? (a : Array α) (i : Nat) : a.get? i = a.data.get? i :=
|
||||
getElem?_eq_data_get? ..
|
||||
|
||||
theorem get!_eq_get? [Inhabited α] (a : Array α) : a.get! n = (a.get? n).getD default := by
|
||||
simp [get!_eq_getD]
|
||||
|
||||
@[simp] theorem back_eq_back? [Inhabited α] (a : Array α) : a.back = a.back?.getD default := by
|
||||
simp [back, back?]
|
||||
|
||||
@[simp] theorem back?_push (a : Array α) : (a.push x).back? = some x := by
|
||||
simp [back?, getElem?_eq_data_get?]
|
||||
|
||||
theorem back_push [Inhabited α] (a : Array α) : (a.push x).back = x := by simp
|
||||
|
||||
theorem get?_push_lt (a : Array α) (x : α) (i : Nat) (h : i < a.size) :
|
||||
(a.push x)[i]? = some a[i] := by
|
||||
rw [getElem?_pos, get_push_lt]
|
||||
|
||||
theorem get?_push_eq (a : Array α) (x : α) : (a.push x)[a.size]? = some x := by
|
||||
rw [getElem?_pos, get_push_eq]
|
||||
|
||||
theorem get?_push {a : Array α} : (a.push x)[i]? = if i = a.size then some x else a[i]? := by
|
||||
match Nat.lt_trichotomy i a.size with
|
||||
| Or.inl g =>
|
||||
have h1 : i < a.size + 1 := by omega
|
||||
have h2 : i ≠ a.size := by omega
|
||||
simp [getElem?, size_push, g, h1, h2, get_push_lt]
|
||||
| Or.inr (Or.inl heq) =>
|
||||
simp [heq, getElem?_pos, get_push_eq]
|
||||
| Or.inr (Or.inr g) =>
|
||||
simp only [getElem?, size_push]
|
||||
have h1 : ¬ (i < a.size) := by omega
|
||||
have h2 : ¬ (i < a.size + 1) := by omega
|
||||
have h3 : i ≠ a.size := by omega
|
||||
simp [h1, h2, h3]
|
||||
|
||||
@[simp] theorem get?_size {a : Array α} : a[a.size]? = none := by
|
||||
simp only [getElem?, Nat.lt_irrefl, dite_false]
|
||||
|
||||
@[simp] theorem data_set (a : Array α) (i v) : (a.set i v).data = a.data.set i.1 v := rfl
|
||||
|
||||
theorem get_set_eq (a : Array α) (i : Fin a.size) (v : α) :
|
||||
(a.set i v)[i.1] = v := by
|
||||
simp only [set, getElem_eq_data_get, List.get_set_eq]
|
||||
|
||||
theorem get?_set_eq (a : Array α) (i : Fin a.size) (v : α) :
|
||||
(a.set i v)[i.1]? = v := by simp [getElem?_pos, i.2]
|
||||
|
||||
@[simp] theorem get?_set_ne (a : Array α) (i : Fin a.size) {j : Nat} (v : α)
|
||||
(h : i.1 ≠ j) : (a.set i v)[j]? = a[j]? := by
|
||||
by_cases j < a.size <;> simp [getElem?_pos, getElem?_neg, *]
|
||||
|
||||
theorem get?_set (a : Array α) (i : Fin a.size) (j : Nat) (v : α) :
|
||||
(a.set i v)[j]? = if i.1 = j then some v else a[j]? := by
|
||||
if h : i.1 = j then subst j; simp [*] else simp [*]
|
||||
|
||||
theorem get_set (a : Array α) (i : Fin a.size) (j : Nat) (hj : j < a.size) (v : α) :
|
||||
(a.set i v)[j]'(by simp [*]) = if i = j then v else a[j] := by
|
||||
if h : i.1 = j then subst j; simp [*] else simp [*]
|
||||
|
||||
@[simp] theorem get_set_ne (a : Array α) (i : Fin a.size) {j : Nat} (v : α) (hj : j < a.size)
|
||||
(h : i.1 ≠ j) : (a.set i v)[j]'(by simp [*]) = a[j] := by
|
||||
simp only [set, getElem_eq_data_get, List.get_set_ne _ h]
|
||||
|
||||
theorem getElem_setD (a : Array α) (i : Nat) (v : α) (h : i < (setD a i v).size) :
|
||||
(setD a i v)[i] = v := by
|
||||
simp at h
|
||||
simp only [setD, h, dite_true, get_set, ite_true]
|
||||
|
||||
theorem set_set (a : Array α) (i : Fin a.size) (v v' : α) :
|
||||
(a.set i v).set ⟨i, by simp [i.2]⟩ v' = a.set i v' := by simp [set, List.set_set]
|
||||
|
||||
private theorem fin_cast_val (e : n = n') (i : Fin n) : e ▸ i = ⟨i.1, e ▸ i.2⟩ := by cases e; rfl
|
||||
|
||||
theorem swap_def (a : Array α) (i j : Fin a.size) :
|
||||
a.swap i j = (a.set i (a.get j)).set ⟨j.1, by simp [j.2]⟩ (a.get i) := by
|
||||
simp [swap, fin_cast_val]
|
||||
|
||||
theorem data_swap (a : Array α) (i j : Fin a.size) :
|
||||
(a.swap i j).data = (a.data.set i (a.get j)).set j (a.get i) := by simp [swap_def]
|
||||
|
||||
theorem get?_swap (a : Array α) (i j : Fin a.size) (k : Nat) : (a.swap i j)[k]? =
|
||||
if j = k then some a[i.1] else if i = k then some a[j.1] else a[k]? := by
|
||||
simp [swap_def, get?_set, ← getElem_fin_eq_data_get]
|
||||
|
||||
@[simp] theorem swapAt_def (a : Array α) (i : Fin a.size) (v : α) :
|
||||
a.swapAt i v = (a[i.1], a.set i v) := rfl
|
||||
|
||||
-- @[simp] -- FIXME: gives a weird linter error
|
||||
theorem swapAt!_def (a : Array α) (i : Nat) (v : α) (h : i < a.size) :
|
||||
a.swapAt! i v = (a[i], a.set ⟨i, h⟩ v) := by simp [swapAt!, h]
|
||||
|
||||
@[simp] theorem data_pop (a : Array α) : a.pop.data = a.data.dropLast := by simp [pop]
|
||||
|
||||
@[simp] theorem pop_empty : (#[] : Array α).pop = #[] := rfl
|
||||
|
||||
@[simp] theorem pop_push (a : Array α) : (a.push x).pop = a := by simp [pop]
|
||||
|
||||
@[simp] theorem getElem_pop (a : Array α) (i : Nat) (hi : i < a.pop.size) :
|
||||
a.pop[i] = a[i]'(Nat.lt_of_lt_of_le (a.size_pop ▸ hi) (Nat.sub_le _ _)) :=
|
||||
List.get_dropLast ..
|
||||
|
||||
theorem eq_empty_of_size_eq_zero {as : Array α} (h : as.size = 0) : as = #[] := by
|
||||
apply ext
|
||||
· simp [h]
|
||||
· intros; contradiction
|
||||
|
||||
theorem eq_push_pop_back_of_size_ne_zero [Inhabited α] {as : Array α} (h : as.size ≠ 0) :
|
||||
as = as.pop.push as.back := by
|
||||
apply ext
|
||||
· simp [Nat.sub_add_cancel (Nat.zero_lt_of_ne_zero h)]
|
||||
· intros i h h'
|
||||
if hlt : i < as.pop.size then
|
||||
rw [get_push_lt (h:=hlt), getElem_pop]
|
||||
else
|
||||
have heq : i = as.pop.size :=
|
||||
Nat.le_antisymm (size_pop .. ▸ Nat.le_pred_of_lt h) (Nat.le_of_not_gt hlt)
|
||||
cases heq; rw [get_push_eq, back, ←size_pop, get!_eq_getD, getD, dif_pos h]; rfl
|
||||
|
||||
theorem eq_push_of_size_ne_zero {as : Array α} (h : as.size ≠ 0) :
|
||||
∃ (bs : Array α) (c : α), as = bs.push c :=
|
||||
let _ : Inhabited α := ⟨as[0]⟩
|
||||
⟨as.pop, as.back, eq_push_pop_back_of_size_ne_zero h⟩
|
||||
|
||||
theorem size_eq_length_data (as : Array α) : as.size = as.data.length := rfl
|
||||
|
||||
@[simp] theorem size_swap! (a : Array α) (i j) :
|
||||
(a.swap! i j).size = a.size := by unfold swap!; split <;> (try split) <;> simp [size_swap]
|
||||
|
||||
@[simp] theorem size_reverse (a : Array α) : a.reverse.size = a.size := by
|
||||
let rec go (as : Array α) (i j) : (reverse.loop as i j).size = as.size := by
|
||||
rw [reverse.loop]
|
||||
if h : i < j then
|
||||
have := reverse.termination h
|
||||
simp [(go · (i+1) ⟨j-1, ·⟩), h]
|
||||
else simp [h]
|
||||
termination_by j - i
|
||||
simp only [reverse]; split <;> simp [go]
|
||||
|
||||
@[simp] theorem size_range {n : Nat} : (range n).size = n := by
|
||||
unfold range
|
||||
induction n with
|
||||
| zero => simp [Nat.fold]
|
||||
| succ k ih =>
|
||||
rw [Nat.fold, flip]
|
||||
simp only [mkEmpty_eq, size_push] at *
|
||||
omega
|
||||
|
||||
@[simp] theorem reverse_data (a : Array α) : a.reverse.data = a.data.reverse := by
|
||||
let rec go (as : Array α) (i j hj)
|
||||
(h : i + j + 1 = a.size) (h₂ : as.size = a.size)
|
||||
(H : ∀ k, as.data.get? k = if i ≤ k ∧ k ≤ j then a.data.get? k else a.data.reverse.get? k)
|
||||
(k) : (reverse.loop as i ⟨j, hj⟩).data.get? k = a.data.reverse.get? k := by
|
||||
rw [reverse.loop]; dsimp; split <;> rename_i h₁
|
||||
· have := reverse.termination h₁
|
||||
match j with | j+1 => ?_
|
||||
simp at *
|
||||
rw [(go · (i+1) j)]
|
||||
· rwa [Nat.add_right_comm i]
|
||||
· simp [size_swap, h₂]
|
||||
· intro k
|
||||
rw [← getElem?_eq_data_get?, get?_swap]
|
||||
simp [getElem?_eq_data_get?, getElem_eq_data_get, ← List.get?_eq_get, H, Nat.le_of_lt h₁]
|
||||
split <;> rename_i h₂
|
||||
· simp [← h₂, Nat.not_le.2 (Nat.lt_succ_self _)]
|
||||
exact (List.get?_reverse' _ _ (Eq.trans (by simp_arith) h)).symm
|
||||
split <;> rename_i h₃
|
||||
· simp [← h₃, Nat.not_le.2 (Nat.lt_succ_self _)]
|
||||
exact (List.get?_reverse' _ _ (Eq.trans (by simp_arith) h)).symm
|
||||
simp only [Nat.succ_le, Nat.lt_iff_le_and_ne.trans (and_iff_left h₃),
|
||||
Nat.lt_succ.symm.trans (Nat.lt_iff_le_and_ne.trans (and_iff_left (Ne.symm h₂)))]
|
||||
· rw [H]; split <;> rename_i h₂
|
||||
· cases Nat.le_antisymm (Nat.not_lt.1 h₁) (Nat.le_trans h₂.1 h₂.2)
|
||||
cases Nat.le_antisymm h₂.1 h₂.2
|
||||
exact (List.get?_reverse' _ _ h).symm
|
||||
· rfl
|
||||
termination_by j - i
|
||||
simp only [reverse]; split
|
||||
· match a with | ⟨[]⟩ | ⟨[_]⟩ => rfl
|
||||
· have := Nat.sub_add_cancel (Nat.le_of_not_le ‹_›)
|
||||
refine List.ext <| go _ _ _ _ (by simp [this]) rfl fun k => ?_
|
||||
split; {rfl}; rename_i h
|
||||
simp [← show k < _ + 1 ↔ _ from Nat.lt_succ (n := a.size - 1), this] at h
|
||||
rw [List.get?_eq_none.2 ‹_›, List.get?_eq_none.2 (a.data.length_reverse ▸ ‹_›)]
|
||||
|
||||
/-! ### foldl / foldr -/
|
||||
|
||||
-- This proof is the pure version of `Array.SatisfiesM_foldlM`,
|
||||
-- reproduced to avoid a dependency on `SatisfiesM`.
|
||||
theorem foldl_induction
|
||||
{as : Array α} (motive : Nat → β → Prop) {init : β} (h0 : motive 0 init) {f : β → α → β}
|
||||
(hf : ∀ i : Fin as.size, ∀ b, motive i.1 b → motive (i.1 + 1) (f b as[i])) :
|
||||
motive as.size (as.foldl f init) := by
|
||||
let rec go {i j b} (h₁ : j ≤ as.size) (h₂ : as.size ≤ i + j) (H : motive j b) :
|
||||
(motive as.size) (foldlM.loop (m := Id) f as as.size (Nat.le_refl _) i j b) := by
|
||||
unfold foldlM.loop; split
|
||||
· next hj =>
|
||||
split
|
||||
· cases Nat.not_le_of_gt (by simp [hj]) h₂
|
||||
· exact go hj (by rwa [Nat.succ_add] at h₂) (hf ⟨j, hj⟩ b H)
|
||||
· next hj => exact Nat.le_antisymm h₁ (Nat.ge_of_not_lt hj) ▸ H
|
||||
simpa [foldl, foldlM] using go (Nat.zero_le _) (Nat.le_refl _) h0
|
||||
|
||||
-- This proof is the pure version of `Array.SatisfiesM_foldrM`,
|
||||
-- reproduced to avoid a dependency on `SatisfiesM`.
|
||||
theorem foldr_induction
|
||||
{as : Array α} (motive : Nat → β → Prop) {init : β} (h0 : motive as.size init) {f : α → β → β}
|
||||
(hf : ∀ i : Fin as.size, ∀ b, motive (i.1 + 1) b → motive i.1 (f as[i] b)) :
|
||||
motive 0 (as.foldr f init) := by
|
||||
let rec go {i b} (hi : i ≤ as.size) (H : motive i b) :
|
||||
(motive 0) (foldrM.fold (m := Id) f as 0 i hi b) := by
|
||||
unfold foldrM.fold; simp; split
|
||||
· next hi => exact (hi ▸ H)
|
||||
· next hi =>
|
||||
split; {simp at hi}
|
||||
· next i hi' =>
|
||||
exact go _ (hf ⟨i, hi'⟩ b H)
|
||||
simp [foldr, foldrM]; split; {exact go _ h0}
|
||||
· next h => exact (Nat.eq_zero_of_not_pos h ▸ h0)
|
||||
|
||||
/-! ### map -/
|
||||
|
||||
@[simp] theorem mem_map {f : α → β} {l : Array α} : b ∈ l.map f ↔ ∃ a, a ∈ l ∧ f a = b := by
|
||||
simp only [mem_def, map_data, List.mem_map]
|
||||
|
||||
theorem mapM_eq_mapM_data [Monad m] [LawfulMonad m] (f : α → m β) (arr : Array α) :
|
||||
arr.mapM f = return mk (← arr.data.mapM f) := by
|
||||
rw [mapM_eq_foldlM, foldlM_eq_foldlM_data, ← List.foldrM_reverse]
|
||||
conv => rhs; rw [← List.reverse_reverse arr.data]
|
||||
induction arr.data.reverse with
|
||||
| nil => simp; rfl
|
||||
| cons a l ih => simp [ih]; simp [map_eq_pure_bind, push]
|
||||
|
||||
theorem mapM_map_eq_foldl (as : Array α) (f : α → β) (i) :
|
||||
mapM.map (m := Id) f as i b = as.foldl (start := i) (fun r a => r.push (f a)) b := by
|
||||
unfold mapM.map
|
||||
split <;> rename_i h
|
||||
· simp only [Id.bind_eq]
|
||||
dsimp [foldl, Id.run, foldlM]
|
||||
rw [mapM_map_eq_foldl, dif_pos (by omega), foldlM.loop, dif_pos h]
|
||||
-- Calling `split` here gives a bad goal.
|
||||
have : size as - i = Nat.succ (size as - i - 1) := by omega
|
||||
rw [this]
|
||||
simp [foldl, foldlM, Id.run, Nat.sub_add_eq]
|
||||
· dsimp [foldl, Id.run, foldlM]
|
||||
rw [dif_pos (by omega), foldlM.loop, dif_neg h]
|
||||
rfl
|
||||
termination_by as.size - i
|
||||
|
||||
theorem map_eq_foldl (as : Array α) (f : α → β) :
|
||||
as.map f = as.foldl (fun r a => r.push (f a)) #[] :=
|
||||
mapM_map_eq_foldl _ _ _
|
||||
|
||||
theorem map_induction (as : Array α) (f : α → β) (motive : Nat → Prop) (h0 : motive 0)
|
||||
(p : Fin as.size → β → Prop) (hs : ∀ i, motive i.1 → p i (f as[i]) ∧ motive (i+1)) :
|
||||
motive as.size ∧
|
||||
∃ eq : (as.map f).size = as.size, ∀ i h, p ⟨i, h⟩ ((as.map f)[i]) := by
|
||||
have t := foldl_induction (as := as) (β := Array β)
|
||||
(motive := fun i arr => motive i ∧ arr.size = i ∧ ∀ i h2, p i arr[i.1])
|
||||
(init := #[]) (f := fun r a => r.push (f a)) ?_ ?_
|
||||
obtain ⟨m, eq, w⟩ := t
|
||||
· refine ⟨m, by simpa [map_eq_foldl] using eq, ?_⟩
|
||||
intro i h
|
||||
simp [eq] at w
|
||||
specialize w ⟨i, h⟩ h
|
||||
simpa [map_eq_foldl] using w
|
||||
· exact ⟨h0, rfl, nofun⟩
|
||||
· intro i b ⟨m, ⟨eq, w⟩⟩
|
||||
refine ⟨?_, ?_, ?_⟩
|
||||
· exact (hs _ m).2
|
||||
· simp_all
|
||||
· intro j h
|
||||
simp at h ⊢
|
||||
by_cases h' : j < size b
|
||||
· rw [get_push]
|
||||
simp_all
|
||||
· rw [get_push, dif_neg h']
|
||||
simp only [show j = i by omega]
|
||||
exact (hs _ m).1
|
||||
|
||||
theorem map_spec (as : Array α) (f : α → β) (p : Fin as.size → β → Prop)
|
||||
(hs : ∀ i, p i (f as[i])) :
|
||||
∃ eq : (as.map f).size = as.size, ∀ i h, p ⟨i, h⟩ ((as.map f)[i]) := by
|
||||
simpa using map_induction as f (fun _ => True) trivial p (by simp_all)
|
||||
|
||||
@[simp] theorem getElem_map (f : α → β) (as : Array α) (i : Nat) (h) :
|
||||
((as.map f)[i]) = f (as[i]'(size_map .. ▸ h)) := by
|
||||
have := map_spec as f (fun i b => b = f (as[i]))
|
||||
simp only [implies_true, true_implies] at this
|
||||
obtain ⟨eq, w⟩ := this
|
||||
apply w
|
||||
simp_all
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
-- This could also be prove from `SatisfiesM_mapIdxM`.
|
||||
theorem mapIdx_induction (as : Array α) (f : Fin as.size → α → β)
|
||||
(motive : Nat → Prop) (h0 : motive 0)
|
||||
(p : Fin as.size → β → Prop)
|
||||
(hs : ∀ i, motive i.1 → p i (f i as[i]) ∧ motive (i + 1)) :
|
||||
motive as.size ∧ ∃ eq : (Array.mapIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapIdx as f)[i]) := by
|
||||
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : ∀ i h h', p ⟨i, h⟩ bs[i]) (hm : motive j) :
|
||||
let arr : Array β := Array.mapIdxM.map (m := Id) as f i j h bs
|
||||
motive as.size ∧ ∃ eq : arr.size = as.size, ∀ i h, p ⟨i, h⟩ arr[i] := by
|
||||
induction i generalizing j bs with simp [mapIdxM.map]
|
||||
| zero =>
|
||||
have := (Nat.zero_add _).symm.trans h
|
||||
exact ⟨this ▸ hm, h₁ ▸ this, fun _ _ => h₂ ..⟩
|
||||
| succ i ih =>
|
||||
apply @ih (bs.push (f ⟨j, by omega⟩ as[j])) (j + 1) (by omega) (by simp; omega)
|
||||
· intro i i_lt h'
|
||||
rw [get_push]
|
||||
split
|
||||
· apply h₂
|
||||
· simp only [size_push] at h'
|
||||
obtain rfl : i = j := by omega
|
||||
apply (hs ⟨i, by omega⟩ hm).1
|
||||
· exact (hs ⟨j, by omega⟩ hm).2
|
||||
simp [mapIdx, mapIdxM]; exact go rfl nofun h0
|
||||
|
||||
theorem mapIdx_spec (as : Array α) (f : Fin as.size → α → β)
|
||||
(p : Fin as.size → β → Prop) (hs : ∀ i, p i (f i as[i])) :
|
||||
∃ eq : (Array.mapIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapIdx as f)[i]) :=
|
||||
(mapIdx_induction _ _ (fun _ => True) trivial p fun _ _ => ⟨hs .., trivial⟩).2
|
||||
|
||||
@[simp] theorem size_mapIdx (a : Array α) (f : Fin a.size → α → β) : (a.mapIdx f).size = a.size :=
|
||||
(mapIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
|
||||
|
||||
@[simp] theorem size_zipWithIndex (as : Array α) : as.zipWithIndex.size = as.size :=
|
||||
Array.size_mapIdx _ _
|
||||
|
||||
@[simp] theorem getElem_mapIdx (a : Array α) (f : Fin a.size → α → β) (i : Nat)
|
||||
(h : i < (mapIdx a f).size) :
|
||||
haveI : i < a.size := by simp_all
|
||||
(a.mapIdx f)[i] = f ⟨i, this⟩ a[i] :=
|
||||
(mapIdx_spec _ _ (fun i b => b = f i a[i]) fun _ => rfl).2 i _
|
||||
|
||||
/-! ### modify -/
|
||||
|
||||
@[simp] theorem size_modify (a : Array α) (i : Nat) (f : α → α) : (a.modify i f).size = a.size := by
|
||||
unfold modify modifyM Id.run
|
||||
split <;> simp
|
||||
|
||||
theorem get_modify {arr : Array α} {x i} (h : i < arr.size) :
|
||||
(arr.modify x f).get ⟨i, by simp [h]⟩ =
|
||||
if x = i then f (arr.get ⟨i, h⟩) else arr.get ⟨i, h⟩ := by
|
||||
simp [modify, modifyM, Id.run]; split
|
||||
· simp [get_set _ _ _ h]; split <;> simp [*]
|
||||
· rw [if_neg (mt (by rintro rfl; exact h) ‹_›)]
|
||||
|
||||
/-! ### filter -/
|
||||
|
||||
@[simp] theorem filter_data (p : α → Bool) (l : Array α) :
|
||||
(l.filter p).data = l.data.filter p := by
|
||||
dsimp only [filter]
|
||||
rw [foldl_eq_foldl_data]
|
||||
generalize l.data = l
|
||||
suffices ∀ a, (List.foldl (fun r a => if p a = true then push r a else r) a l).data =
|
||||
a.data ++ List.filter p l by
|
||||
simpa using this #[]
|
||||
induction l with simp
|
||||
| cons => split <;> simp [*]
|
||||
|
||||
@[simp] theorem filter_filter (q) (l : Array α) :
|
||||
filter p (filter q l) = filter (fun a => p a ∧ q a) l := by
|
||||
apply ext'
|
||||
simp only [filter_data, List.filter_filter]
|
||||
|
||||
@[simp] theorem mem_filter : x ∈ filter p as ↔ x ∈ as ∧ p x := by
|
||||
simp only [mem_def, filter_data, List.mem_filter]
|
||||
|
||||
theorem mem_of_mem_filter {a : α} {l} (h : a ∈ filter p l) : a ∈ l :=
|
||||
(mem_filter.mp h).1
|
||||
|
||||
/-! ### filterMap -/
|
||||
|
||||
@[simp] theorem filterMap_data (f : α → Option β) (l : Array α) :
|
||||
(l.filterMap f).data = l.data.filterMap f := by
|
||||
dsimp only [filterMap, filterMapM]
|
||||
rw [foldlM_eq_foldlM_data]
|
||||
generalize l.data = l
|
||||
have this : ∀ a : Array β, (Id.run (List.foldlM (m := Id) ?_ a l)).data =
|
||||
a.data ++ List.filterMap f l := ?_
|
||||
exact this #[]
|
||||
induction l
|
||||
· simp_all [Id.run]
|
||||
· simp_all [Id.run]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem mem_filterMap (f : α → Option β) (l : Array α) {b : β} :
|
||||
b ∈ filterMap f l ↔ ∃ a, a ∈ l ∧ f a = some b := by
|
||||
simp only [mem_def, filterMap_data, List.mem_filterMap]
|
||||
|
||||
/-! ### empty -/
|
||||
|
||||
theorem size_empty : (#[] : Array α).size = 0 := rfl
|
||||
|
||||
theorem empty_data : (#[] : Array α).data = [] := rfl
|
||||
|
||||
/-! ### append -/
|
||||
|
||||
theorem push_eq_append_singleton (as : Array α) (x) : as.push x = as ++ #[x] := rfl
|
||||
|
||||
@[simp] theorem mem_append {a : α} {s t : Array α} : a ∈ s ++ t ↔ a ∈ s ∨ a ∈ t := by
|
||||
simp only [mem_def, append_data, List.mem_append]
|
||||
|
||||
theorem size_append (as bs : Array α) : (as ++ bs).size = as.size + bs.size := by
|
||||
simp only [size, append_data, List.length_append]
|
||||
|
||||
theorem get_append_left {as bs : Array α} {h : i < (as ++ bs).size} (hlt : i < as.size) :
|
||||
(as ++ bs)[i] = as[i] := by
|
||||
simp only [getElem_eq_data_get]
|
||||
have h' : i < (as.data ++ bs.data).length := by rwa [← data_length, append_data] at h
|
||||
conv => rhs; rw [← List.get_append_left (bs:=bs.data) (h':=h')]
|
||||
apply List.get_of_eq; rw [append_data]
|
||||
|
||||
theorem get_append_right {as bs : Array α} {h : i < (as ++ bs).size} (hle : as.size ≤ i)
|
||||
(hlt : i - as.size < bs.size := Nat.sub_lt_left_of_lt_add hle (size_append .. ▸ h)) :
|
||||
(as ++ bs)[i] = bs[i - as.size] := by
|
||||
simp only [getElem_eq_data_get]
|
||||
have h' : i < (as.data ++ bs.data).length := by rwa [← data_length, append_data] at h
|
||||
conv => rhs; rw [← List.get_append_right (h':=h') (h:=Nat.not_lt_of_ge hle)]
|
||||
apply List.get_of_eq; rw [append_data]
|
||||
|
||||
@[simp] theorem append_nil (as : Array α) : as ++ #[] = as := by
|
||||
apply ext'; simp only [append_data, empty_data, List.append_nil]
|
||||
|
||||
@[simp] theorem nil_append (as : Array α) : #[] ++ as = as := by
|
||||
apply ext'; simp only [append_data, empty_data, List.nil_append]
|
||||
|
||||
theorem append_assoc (as bs cs : Array α) : as ++ bs ++ cs = as ++ (bs ++ cs) := by
|
||||
apply ext'; simp only [append_data, List.append_assoc]
|
||||
|
||||
/-! ### extract -/
|
||||
|
||||
theorem extract_loop_zero (as bs : Array α) (start : Nat) : extract.loop as 0 start bs = bs := by
|
||||
rw [extract.loop]; split <;> rfl
|
||||
|
||||
theorem extract_loop_succ (as bs : Array α) (size start : Nat) (h : start < as.size) :
|
||||
extract.loop as (size+1) start bs = extract.loop as size (start+1) (bs.push as[start]) := by
|
||||
rw [extract.loop, dif_pos h]; rfl
|
||||
|
||||
theorem extract_loop_of_ge (as bs : Array α) (size start : Nat) (h : start ≥ as.size) :
|
||||
extract.loop as size start bs = bs := by
|
||||
rw [extract.loop, dif_neg (Nat.not_lt_of_ge h)]
|
||||
|
||||
theorem extract_loop_eq_aux (as bs : Array α) (size start : Nat) :
|
||||
extract.loop as size start bs = bs ++ extract.loop as size start #[] := by
|
||||
induction size using Nat.recAux generalizing start bs with
|
||||
| zero => rw [extract_loop_zero, extract_loop_zero, append_nil]
|
||||
| succ size ih =>
|
||||
if h : start < as.size then
|
||||
rw [extract_loop_succ (h:=h), ih (bs.push _), push_eq_append_singleton]
|
||||
rw [extract_loop_succ (h:=h), ih (#[].push _), push_eq_append_singleton, nil_append]
|
||||
rw [append_assoc]
|
||||
else
|
||||
rw [extract_loop_of_ge (h:=Nat.le_of_not_lt h)]
|
||||
rw [extract_loop_of_ge (h:=Nat.le_of_not_lt h)]
|
||||
rw [append_nil]
|
||||
|
||||
theorem extract_loop_eq (as bs : Array α) (size start : Nat) (h : start + size ≤ as.size) :
|
||||
extract.loop as size start bs = bs ++ as.extract start (start + size) := by
|
||||
simp [extract]; rw [extract_loop_eq_aux, Nat.min_eq_left h, Nat.add_sub_cancel_left]
|
||||
|
||||
theorem size_extract_loop (as bs : Array α) (size start : Nat) :
|
||||
(extract.loop as size start bs).size = bs.size + min size (as.size - start) := by
|
||||
induction size using Nat.recAux generalizing start bs with
|
||||
| zero => rw [extract_loop_zero, Nat.zero_min, Nat.add_zero]
|
||||
| succ size ih =>
|
||||
if h : start < as.size then
|
||||
rw [extract_loop_succ (h:=h), ih, size_push, Nat.add_assoc, ←Nat.add_min_add_left,
|
||||
Nat.sub_succ, Nat.one_add, Nat.one_add, Nat.succ_pred_eq_of_pos (Nat.sub_pos_of_lt h)]
|
||||
else
|
||||
have h := Nat.le_of_not_gt h
|
||||
rw [extract_loop_of_ge (h:=h), Nat.sub_eq_zero_of_le h, Nat.min_zero, Nat.add_zero]
|
||||
|
||||
@[simp] theorem size_extract (as : Array α) (start stop : Nat) :
|
||||
(as.extract start stop).size = min stop as.size - start := by
|
||||
simp [extract]; rw [size_extract_loop, size_empty, Nat.zero_add, Nat.sub_min_sub_right,
|
||||
Nat.min_assoc, Nat.min_self]
|
||||
|
||||
theorem get_extract_loop_lt_aux (as bs : Array α) (size start : Nat) (hlt : i < bs.size) :
|
||||
i < (extract.loop as size start bs).size := by
|
||||
rw [size_extract_loop]
|
||||
apply Nat.lt_of_lt_of_le hlt
|
||||
exact Nat.le_add_right ..
|
||||
|
||||
theorem get_extract_loop_lt (as bs : Array α) (size start : Nat) (hlt : i < bs.size)
|
||||
(h := get_extract_loop_lt_aux as bs size start hlt) :
|
||||
(extract.loop as size start bs)[i] = bs[i] := by
|
||||
apply Eq.trans _ (get_append_left (bs:=extract.loop as size start #[]) hlt)
|
||||
· rw [size_append]; exact Nat.lt_of_lt_of_le hlt (Nat.le_add_right ..)
|
||||
· congr; rw [extract_loop_eq_aux]
|
||||
|
||||
theorem get_extract_loop_ge_aux (as bs : Array α) (size start : Nat) (hge : i ≥ bs.size)
|
||||
(h : i < (extract.loop as size start bs).size) : start + i - bs.size < as.size := by
|
||||
have h : i < bs.size + (as.size - start) := by
|
||||
apply Nat.lt_of_lt_of_le h
|
||||
rw [size_extract_loop]
|
||||
apply Nat.add_le_add_left
|
||||
exact Nat.min_le_right ..
|
||||
rw [Nat.add_sub_assoc hge]
|
||||
apply Nat.add_lt_of_lt_sub'
|
||||
exact Nat.sub_lt_left_of_lt_add hge h
|
||||
|
||||
theorem get_extract_loop_ge (as bs : Array α) (size start : Nat) (hge : i ≥ bs.size)
|
||||
(h : i < (extract.loop as size start bs).size)
|
||||
(h' := get_extract_loop_ge_aux as bs size start hge h) :
|
||||
(extract.loop as size start bs)[i] = as[start + i - bs.size] := by
|
||||
induction size using Nat.recAux generalizing start bs with
|
||||
| zero =>
|
||||
rw [size_extract_loop, Nat.zero_min, Nat.add_zero] at h
|
||||
omega
|
||||
| succ size ih =>
|
||||
have : start < as.size := by
|
||||
apply Nat.lt_of_le_of_lt (Nat.le_add_right start (i - bs.size))
|
||||
rwa [← Nat.add_sub_assoc hge]
|
||||
have : i < (extract.loop as size (start+1) (bs.push as[start])).size := by
|
||||
rwa [← extract_loop_succ]
|
||||
have heq : (extract.loop as (size+1) start bs)[i] =
|
||||
(extract.loop as size (start+1) (bs.push as[start]))[i] := by
|
||||
congr 1; rw [extract_loop_succ]
|
||||
rw [heq]
|
||||
if hi : bs.size = i then
|
||||
cases hi
|
||||
have h₁ : bs.size < (bs.push as[start]).size := by rw [size_push]; exact Nat.lt_succ_self ..
|
||||
have h₂ : bs.size < (extract.loop as size (start+1) (bs.push as[start])).size := by
|
||||
rw [size_extract_loop]; apply Nat.lt_of_lt_of_le h₁; exact Nat.le_add_right ..
|
||||
have h : (extract.loop as size (start + 1) (push bs as[start]))[bs.size] = as[start] := by
|
||||
rw [get_extract_loop_lt as (bs.push as[start]) size (start+1) h₁ h₂, get_push_eq]
|
||||
rw [h]; congr; rw [Nat.add_sub_cancel]
|
||||
else
|
||||
have hge : bs.size + 1 ≤ i := Nat.lt_of_le_of_ne hge hi
|
||||
rw [ih (bs.push as[start]) (start+1) ((size_push ..).symm ▸ hge)]
|
||||
congr 1; rw [size_push, Nat.add_right_comm, Nat.add_sub_add_right]
|
||||
|
||||
theorem get_extract_aux {as : Array α} {start stop : Nat} (h : i < (as.extract start stop).size) :
|
||||
start + i < as.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
|
||||
|
||||
@[simp] theorem get_extract {as : Array α} {start stop : Nat}
|
||||
(h : i < (as.extract start stop).size) :
|
||||
(as.extract start stop)[i] = as[start + i]'(get_extract_aux h) :=
|
||||
show (extract.loop as (min stop as.size - start) start #[])[i]
|
||||
= as[start + i]'(get_extract_aux h) by rw [get_extract_loop_ge]; rfl; exact Nat.zero_le _
|
||||
|
||||
@[simp] theorem extract_all (as : Array α) : as.extract 0 as.size = as := by
|
||||
apply ext
|
||||
· rw [size_extract, Nat.min_self, Nat.sub_zero]
|
||||
· intros; rw [get_extract]; congr; rw [Nat.zero_add]
|
||||
|
||||
theorem extract_empty_of_stop_le_start (as : Array α) {start stop : Nat} (h : stop ≤ start) :
|
||||
as.extract start stop = #[] := by
|
||||
simp [extract]; rw [←Nat.sub_min_sub_right, Nat.sub_eq_zero_of_le h, Nat.zero_min,
|
||||
extract_loop_zero]
|
||||
|
||||
theorem extract_empty_of_size_le_start (as : Array α) {start stop : Nat} (h : as.size ≤ start) :
|
||||
as.extract start stop = #[] := by
|
||||
simp [extract]; rw [←Nat.sub_min_sub_right, Nat.sub_eq_zero_of_le h, Nat.min_zero,
|
||||
extract_loop_zero]
|
||||
|
||||
@[simp] theorem extract_empty (start stop : Nat) : (#[] : Array α).extract start stop = #[] :=
|
||||
extract_empty_of_size_le_start _ (Nat.zero_le _)
|
||||
|
||||
/-! ### any -/
|
||||
|
||||
-- Auxiliary for `any_iff_exists`.
|
||||
theorem anyM_loop_iff_exists (p : α → Bool) (as : Array α) (start stop) (h : stop ≤ as.size) :
|
||||
anyM.loop (m := Id) p as stop h start = true ↔
|
||||
∃ i : Fin as.size, start ≤ ↑i ∧ ↑i < stop ∧ p as[i] = true := by
|
||||
unfold anyM.loop
|
||||
split <;> rename_i h₁
|
||||
· dsimp
|
||||
split <;> rename_i h₂
|
||||
· simp only [true_iff]
|
||||
refine ⟨⟨start, by omega⟩, by dsimp; omega, by dsimp; omega, h₂⟩
|
||||
· rw [anyM_loop_iff_exists]
|
||||
constructor
|
||||
· rintro ⟨i, ge, lt, h⟩
|
||||
have : start ≠ i := by rintro rfl; omega
|
||||
exact ⟨i, by omega, lt, h⟩
|
||||
· rintro ⟨i, ge, lt, h⟩
|
||||
have : start ≠ i := by rintro rfl; erw [h] at h₂; simp_all
|
||||
exact ⟨i, by omega, lt, h⟩
|
||||
· simp
|
||||
omega
|
||||
termination_by stop - start
|
||||
|
||||
-- This could also be proved from `SatisfiesM_anyM_iff_exists` in `Batteries.Data.Array.Init.Monadic`
|
||||
theorem any_iff_exists (p : α → Bool) (as : Array α) (start stop) :
|
||||
any as p start stop ↔ ∃ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop ∧ p as[i] := by
|
||||
dsimp [any, anyM, Id.run]
|
||||
split
|
||||
· rw [anyM_loop_iff_exists]; rfl
|
||||
· rw [anyM_loop_iff_exists]
|
||||
constructor
|
||||
· rintro ⟨i, ge, _, h⟩
|
||||
exact ⟨i, by omega, by omega, h⟩
|
||||
· rintro ⟨i, ge, _, h⟩
|
||||
exact ⟨i, by omega, by omega, h⟩
|
||||
|
||||
theorem any_eq_true (p : α → Bool) (as : Array α) :
|
||||
any as p ↔ ∃ i : Fin as.size, p as[i] := by simp [any_iff_exists, Fin.isLt]
|
||||
|
||||
theorem any_def {p : α → Bool} (as : Array α) : as.any p = as.data.any p := by
|
||||
rw [Bool.eq_iff_iff, any_eq_true, List.any_eq_true]; simp only [List.mem_iff_get]
|
||||
exact ⟨fun ⟨i, h⟩ => ⟨_, ⟨i, rfl⟩, h⟩, fun ⟨_, ⟨i, rfl⟩, h⟩ => ⟨i, h⟩⟩
|
||||
|
||||
/-! ### all -/
|
||||
|
||||
theorem all_eq_not_any_not (p : α → Bool) (as : Array α) (start stop) :
|
||||
all as p start stop = !(any as (!p ·) start stop) := by
|
||||
dsimp [all, allM]
|
||||
rfl
|
||||
|
||||
theorem all_iff_forall (p : α → Bool) (as : Array α) (start stop) :
|
||||
all as p start stop ↔ ∀ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop → p as[i] := by
|
||||
rw [all_eq_not_any_not]
|
||||
suffices ¬(any as (!p ·) start stop = true) ↔
|
||||
∀ i : Fin as.size, start ≤ i.1 ∧ i.1 < stop → p as[i] by
|
||||
simp_all
|
||||
rw [any_iff_exists]
|
||||
simp
|
||||
|
||||
theorem all_eq_true (p : α → Bool) (as : Array α) : all as p ↔ ∀ i : Fin as.size, p as[i] := by
|
||||
simp [all_iff_forall, Fin.isLt]
|
||||
|
||||
theorem all_def {p : α → Bool} (as : Array α) : as.all p = as.data.all p := by
|
||||
rw [Bool.eq_iff_iff, all_eq_true, List.all_eq_true]; simp only [List.mem_iff_get]
|
||||
constructor
|
||||
· rintro w x ⟨r, rfl⟩
|
||||
rw [← getElem_eq_data_get]
|
||||
apply w
|
||||
· intro w i
|
||||
exact w as[i] ⟨i, (getElem_eq_data_get as i.2).symm⟩
|
||||
|
||||
theorem all_eq_true_iff_forall_mem {l : Array α} : l.all p ↔ ∀ x, x ∈ l → p x := by
|
||||
simp only [all_def, List.all_eq_true, mem_def]
|
||||
|
||||
/-! ### contains -/
|
||||
|
||||
theorem contains_def [DecidableEq α] {a : α} {as : Array α} : as.contains a ↔ a ∈ as := by
|
||||
rw [mem_def, contains, any_def, List.any_eq_true]; simp [and_comm]
|
||||
|
||||
instance [DecidableEq α] (a : α) (as : Array α) : Decidable (a ∈ as) :=
|
||||
decidable_of_iff _ contains_def
|
||||
|
||||
/-! ### swap -/
|
||||
|
||||
open Fin
|
||||
|
||||
@[simp] theorem get_swap_right (a : Array α) {i j : Fin a.size} : (a.swap i j)[j.val] = a[i] :=
|
||||
by simp only [swap, fin_cast_val, get_eq_getElem, getElem_set_eq, getElem_fin]
|
||||
|
||||
@[simp] theorem get_swap_left (a : Array α) {i j : Fin a.size} : (a.swap i j)[i.val] = a[j] :=
|
||||
if he : ((Array.size_set _ _ _).symm ▸ j).val = i.val then by
|
||||
simp only [←he, fin_cast_val, get_swap_right, getElem_fin]
|
||||
else by
|
||||
apply Eq.trans
|
||||
· apply Array.get_set_ne
|
||||
· simp only [size_set, Fin.isLt]
|
||||
· assumption
|
||||
· simp [get_set_ne]
|
||||
|
||||
@[simp] theorem get_swap_of_ne (a : Array α) {i j : Fin a.size} (hp : p < a.size)
|
||||
(hi : p ≠ i) (hj : p ≠ j) : (a.swap i j)[p]'(a.size_swap .. |>.symm ▸ hp) = a[p] := by
|
||||
apply Eq.trans
|
||||
· have : ((a.size_set i (a.get j)).symm ▸ j).val = j.val := by simp only [fin_cast_val]
|
||||
apply Array.get_set_ne
|
||||
· simp only [this]
|
||||
apply Ne.symm
|
||||
· assumption
|
||||
· apply Array.get_set_ne
|
||||
· apply Ne.symm
|
||||
· assumption
|
||||
|
||||
theorem get_swap (a : Array α) (i j : Fin a.size) (k : Nat) (hk: k < a.size) :
|
||||
(a.swap i j)[k]'(by simp_all) = if k = i then a[j] else if k = j then a[i] else a[k] := by
|
||||
split
|
||||
· simp_all only [get_swap_left]
|
||||
· split <;> simp_all
|
||||
|
||||
theorem get_swap' (a : Array α) (i j : Fin a.size) (k : Nat) (hk' : k < (a.swap i j).size) :
|
||||
(a.swap i j)[k] = if k = i then a[j] else if k = j then a[i] else a[k]'(by simp_all) := by
|
||||
apply get_swap
|
||||
|
||||
@[simp] theorem swap_swap (a : Array α) {i j : Fin a.size} :
|
||||
(a.swap i j).swap ⟨i.1, (a.size_swap ..).symm ▸i.2⟩ ⟨j.1, (a.size_swap ..).symm ▸j.2⟩ = a := by
|
||||
apply ext
|
||||
· simp only [size_swap]
|
||||
· intros
|
||||
simp only [get_swap']
|
||||
split
|
||||
· simp_all
|
||||
· split <;> simp_all
|
||||
|
||||
theorem swap_comm (a : Array α) {i j : Fin a.size} : a.swap i j = a.swap j i := by
|
||||
apply ext
|
||||
· simp only [size_swap]
|
||||
· intros
|
||||
simp only [get_swap']
|
||||
split
|
||||
· split <;> simp_all
|
||||
· split <;> simp_all
|
||||
|
||||
|
||||
end Array
|
||||
|
||||
@@ -27,13 +27,20 @@ theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a <
|
||||
cases as with | _ as =>
|
||||
exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)
|
||||
|
||||
@[simp] theorem sizeOf_getElem [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) :
|
||||
sizeOf (as[i]'h) < sizeOf as := sizeOf_get _ _
|
||||
|
||||
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that
|
||||
`sizeOf arr[i] < sizeOf arr`, which is useful for well founded recursions
|
||||
over a nested inductive like `inductive T | mk : Array T → T`. -/
|
||||
macro "array_get_dec" : tactic =>
|
||||
`(tactic| first
|
||||
| apply sizeOf_get
|
||||
| apply Nat.lt_trans (sizeOf_get ..); simp_arith)
|
||||
-- subsumed by simp
|
||||
-- | with_reducible apply sizeOf_get
|
||||
-- | with_reducible apply sizeOf_getElem
|
||||
| (with_reducible apply Nat.lt_trans (sizeOf_get ..)); simp_arith
|
||||
| (with_reducible apply Nat.lt_trans (sizeOf_getElem ..)); simp_arith
|
||||
)
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec)
|
||||
|
||||
@@ -43,9 +50,10 @@ provided that `a ∈ arr` which is useful for well founded recursions over a nes
|
||||
-- NB: This is analogue to tactic `sizeOf_list_dec`
|
||||
macro "array_mem_dec" : tactic =>
|
||||
`(tactic| first
|
||||
| apply Array.sizeOf_lt_of_mem; assumption; done
|
||||
| apply Nat.lt_trans (Array.sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
| with_reducible apply Array.sizeOf_lt_of_mem; assumption; done
|
||||
| with_reducible
|
||||
apply Nat.lt_trans (Array.sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
simp_arith)
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_mem_dec)
|
||||
|
||||
@@ -27,6 +27,7 @@ def qpartition (as : Array α) (lt : α → α → Bool) (lo hi : Nat) : Nat ×
|
||||
let as := as.swap! i hi
|
||||
(i, as)
|
||||
termination_by hi - j
|
||||
decreasing_by all_goals simp_wf; decreasing_trivial_pre_omega
|
||||
loop as lo lo
|
||||
|
||||
@[inline] partial def qsort (as : Array α) (lt : α → α → Bool) (low := 0) (high := as.size - 1) : Array α :=
|
||||
|
||||
@@ -9,25 +9,40 @@ import Init.Data.Array.Basic
|
||||
universe u v w
|
||||
|
||||
structure Subarray (α : Type u) where
|
||||
as : Array α
|
||||
array : Array α
|
||||
start : Nat
|
||||
stop : Nat
|
||||
h₁ : start ≤ stop
|
||||
h₂ : stop ≤ as.size
|
||||
start_le_stop : start ≤ stop
|
||||
stop_le_array_size : stop ≤ array.size
|
||||
|
||||
@[deprecated Subarray.array]
|
||||
abbrev Subarray.as (s : Subarray α) : Array α := s.array
|
||||
|
||||
@[deprecated Subarray.start_le_stop]
|
||||
theorem Subarray.h₁ (s : Subarray α) : s.start ≤ s.stop := s.start_le_stop
|
||||
|
||||
@[deprecated Subarray.stop_le_array_size]
|
||||
theorem Subarray.h₂ (s : Subarray α) : s.stop ≤ s.array.size := s.stop_le_array_size
|
||||
|
||||
namespace Subarray
|
||||
|
||||
def size (s : Subarray α) : Nat :=
|
||||
s.stop - s.start
|
||||
|
||||
theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
|
||||
let {array, start, stop, start_le_stop, stop_le_array_size} := s
|
||||
simp [size]
|
||||
apply Nat.le_trans (Nat.sub_le stop start)
|
||||
assumption
|
||||
|
||||
def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
have : s.start + i.val < s.as.size := by
|
||||
apply Nat.lt_of_lt_of_le _ s.h₂
|
||||
have : s.start + i.val < s.array.size := by
|
||||
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size
|
||||
have := i.isLt
|
||||
simp [size] at this
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub this
|
||||
s.as[s.start + i.val]
|
||||
s.array[s.start + i.val]
|
||||
|
||||
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
@@ -42,7 +57,7 @@ abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
|
||||
|
||||
def popFront (s : Subarray α) : Subarray α :=
|
||||
if h : s.start < s.stop then
|
||||
{ s with start := s.start + 1, h₁ := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
|
||||
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
|
||||
else
|
||||
s
|
||||
|
||||
@@ -50,7 +65,7 @@ def popFront (s : Subarray α) : Subarray α :=
|
||||
let sz := USize.ofNat s.stop
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
if i < sz then
|
||||
let a := s.as.uget i lcProof
|
||||
let a := s.array.uget i lcProof
|
||||
match (← f a b) with
|
||||
| ForInStep.done b => pure b
|
||||
| ForInStep.yield b => loop (i+1) b
|
||||
@@ -68,27 +83,27 @@ instance : ForIn m (Subarray α) α where
|
||||
|
||||
@[inline]
|
||||
def foldlM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.as.foldlM f (init := init) (start := as.start) (stop := as.stop)
|
||||
as.array.foldlM f (init := init) (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.as.foldrM f (init := init) (start := as.stop) (stop := as.start)
|
||||
as.array.foldrM f (init := init) (start := as.stop) (stop := as.start)
|
||||
|
||||
@[inline]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.as.anyM p (start := as.start) (stop := as.stop)
|
||||
as.array.anyM p (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.as.allM p (start := as.start) (stop := as.stop)
|
||||
as.array.allM p (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def forM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.as.forM f (start := as.start) (stop := as.stop)
|
||||
as.array.forM f (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def forRevM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.as.forRevM f (start := as.stop) (stop := as.start)
|
||||
as.array.forRevM f (start := as.stop) (stop := as.start)
|
||||
|
||||
@[inline]
|
||||
def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Subarray α) : β :=
|
||||
@@ -135,15 +150,25 @@ variable {α : Type u}
|
||||
|
||||
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
|
||||
if h₂ : stop ≤ as.size then
|
||||
if h₁ : start ≤ stop then
|
||||
{ as := as, start := start, stop := stop, h₁ := h₁, h₂ := h₂ }
|
||||
else
|
||||
{ as := as, start := stop, stop := stop, h₁ := Nat.le_refl _, h₂ := h₂ }
|
||||
if h₁ : start ≤ stop then
|
||||
{ array := as, start := start, stop := stop,
|
||||
start_le_stop := h₁, stop_le_array_size := h₂ }
|
||||
else
|
||||
{ array := as, start := stop, stop := stop,
|
||||
start_le_stop := Nat.le_refl _, stop_le_array_size := h₂ }
|
||||
else
|
||||
if h₁ : start ≤ as.size then
|
||||
{ as := as, start := start, stop := as.size, h₁ := h₁, h₂ := Nat.le_refl _ }
|
||||
else
|
||||
{ as := as, start := as.size, stop := as.size, h₁ := Nat.le_refl _, h₂ := Nat.le_refl _ }
|
||||
if h₁ : start ≤ as.size then
|
||||
{ array := as,
|
||||
start := start,
|
||||
stop := as.size,
|
||||
start_le_stop := h₁,
|
||||
stop_le_array_size := Nat.le_refl _ }
|
||||
else
|
||||
{ array := as,
|
||||
start := as.size,
|
||||
stop := as.size,
|
||||
start_le_stop := Nat.le_refl _,
|
||||
stop_le_array_size := Nat.le_refl _ }
|
||||
|
||||
@[coe]
|
||||
def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
|
||||
71
src/Init/Data/Array/Subarray/Split.lean
Normal file
71
src/Init/Data/Array/Subarray/Split.lean
Normal file
@@ -0,0 +1,71 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: David Thrane Christiansen
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Array.Subarray
|
||||
import Init.Omega
|
||||
|
||||
/-
|
||||
This module contains splitting operations on subarrays that crucially rely on `omega` for proof
|
||||
automation. Placing them in another module breaks an import cycle, because `omega` itself uses the
|
||||
array library.
|
||||
-/
|
||||
|
||||
namespace Subarray
|
||||
/--
|
||||
Splits a subarray into two parts.
|
||||
-/
|
||||
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
|
||||
let ⟨i', isLt⟩ := i
|
||||
have := s.start_le_stop
|
||||
have := s.stop_le_array_size
|
||||
have : i' ≤ s.stop - s.start := Nat.lt_succ.mp isLt
|
||||
have : s.start + i' ≤ s.stop := by omega
|
||||
have : s.start + i' ≤ s.array.size := by omega
|
||||
have : s.start + i' ≤ s.stop := by
|
||||
simp only [size] at isLt
|
||||
omega
|
||||
let pre := {s with
|
||||
stop := s.start + i',
|
||||
start_le_stop := by omega,
|
||||
stop_le_array_size := by assumption
|
||||
}
|
||||
let post := {s with
|
||||
start := s.start + i'
|
||||
start_le_stop := by assumption
|
||||
}
|
||||
(pre, post)
|
||||
|
||||
/--
|
||||
Removes the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
|
||||
subarray is empty.
|
||||
-/
|
||||
def drop (arr : Subarray α) (i : Nat) : Subarray α where
|
||||
array := arr.array
|
||||
start := min (arr.start + i) arr.stop
|
||||
stop := arr.stop
|
||||
start_le_stop := by
|
||||
rw [Nat.min_def]
|
||||
split <;> simp only [Nat.le_refl, *]
|
||||
stop_le_array_size := arr.stop_le_array_size
|
||||
|
||||
/--
|
||||
Keeps only the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
|
||||
subarray is empty.
|
||||
-/
|
||||
def take (arr : Subarray α) (i : Nat) : Subarray α where
|
||||
array := arr.array
|
||||
start := arr.start
|
||||
stop := min (arr.start + i) arr.stop
|
||||
start_le_stop := by
|
||||
have := arr.start_le_stop
|
||||
rw [Nat.min_def]
|
||||
split <;> omega
|
||||
stop_le_array_size := by
|
||||
have := arr.stop_le_array_size
|
||||
rw [Nat.min_def]
|
||||
split <;> omega
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer
|
||||
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer, Harun Khan, Abdalrhman M Mohamed
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Fin.Basic
|
||||
@@ -34,7 +34,7 @@ structure BitVec (w : Nat) where
|
||||
O(1), because we use `Fin` as the internal representation of a bitvector. -/
|
||||
toFin : Fin (2^w)
|
||||
|
||||
@[deprecated] abbrev Std.BitVec := _root_.BitVec
|
||||
@[deprecated] protected abbrev Std.BitVec := _root_.BitVec
|
||||
|
||||
-- We manually derive the `DecidableEq` instances for `BitVec` because
|
||||
-- we want to have builtin support for bit-vector literals, and we
|
||||
|
||||
@@ -159,4 +159,29 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
|
||||
theorem allOnes_sub_eq_not (x : BitVec w) : allOnes w - x = ~~~x := by
|
||||
rw [← add_not_self x, BitVec.add_comm, add_sub_cancel]
|
||||
|
||||
/-! ### Negation -/
|
||||
|
||||
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
|
||||
getLsb (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd) i.val = !(getLsb x i.val) := by
|
||||
apply iunfoldr_getLsb (fun _ => ()) i (by simp)
|
||||
|
||||
theorem bit_not_add_self (x : BitVec w) :
|
||||
((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd + x = -1 := by
|
||||
simp only [add_eq_adc]
|
||||
apply iunfoldr_replace_snd (fun _ => false) (-1) false rfl
|
||||
intro i; simp only [ BitVec.not, adcb, testBit_toNat]
|
||||
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x.getLsb i)))) ()).snd)]
|
||||
<;> simp [bit_not_testBit, negOne_eq_allOnes, getLsb_allOnes]
|
||||
|
||||
theorem bit_not_eq_not (x : BitVec w) :
|
||||
((iunfoldr (fun i c => (c, !(x.getLsb i)))) ()).snd = ~~~ x := by
|
||||
simp [←allOnes_sub_eq_not, BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), ←negOne_eq_allOnes]
|
||||
|
||||
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
|
||||
simp only [← add_eq_adc]
|
||||
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd) _ rfl]
|
||||
· rw [BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), sub_toAdd, BitVec.add_comm _ (-x)]
|
||||
simp [← sub_toAdd, BitVec.sub_add_cancel]
|
||||
· simp [bit_not_testBit x _]
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix
|
||||
Authors: Joe Hendrix, Harun Khan
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.BitVec.Lemmas
|
||||
@@ -48,6 +48,51 @@ private theorem iunfoldr.eq_test
|
||||
intro i
|
||||
simp_all [truncate_succ]
|
||||
|
||||
theorem iunfoldr_getLsb' {f : Fin w → α → α × Bool} (state : Nat → α)
|
||||
(ind : ∀(i : Fin w), (f i (state i.val)).fst = state (i.val+1)) :
|
||||
(∀ i : Fin w, getLsb (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd)
|
||||
∧ (iunfoldr f (state 0)).fst = state w := by
|
||||
unfold iunfoldr
|
||||
simp
|
||||
apply Fin.hIterate_elim
|
||||
(fun j (p : α × BitVec j) => (hj : j ≤ w) →
|
||||
(∀ i : Fin j, getLsb p.snd i.val = (f ⟨i.val, Nat.lt_of_lt_of_le i.isLt hj⟩ (state i.val)).snd)
|
||||
∧ p.fst = state j)
|
||||
case hj => simp
|
||||
case init =>
|
||||
intro
|
||||
apply And.intro
|
||||
· intro i
|
||||
have := Fin.size_pos i
|
||||
contradiction
|
||||
· rfl
|
||||
case step =>
|
||||
intro j ⟨s, v⟩ ih hj
|
||||
apply And.intro
|
||||
case left =>
|
||||
intro i
|
||||
simp only [getLsb_cons]
|
||||
have hj2 : j.val ≤ w := by simp
|
||||
cases (Nat.lt_or_eq_of_le (Nat.lt_succ.mp i.isLt)) with
|
||||
| inl h3 => simp [if_neg, (Nat.ne_of_lt h3)]
|
||||
exact (ih hj2).1 ⟨i.val, h3⟩
|
||||
| inr h3 => simp [h3, if_pos]
|
||||
cases (Nat.eq_zero_or_pos j.val) with
|
||||
| inl hj3 => congr
|
||||
rw [← (ih hj2).2]
|
||||
| inr hj3 => congr
|
||||
exact (ih hj2).2
|
||||
case right =>
|
||||
simp
|
||||
have hj2 : j.val ≤ w := by simp
|
||||
rw [← ind j, ← (ih hj2).2]
|
||||
|
||||
|
||||
theorem iunfoldr_getLsb {f : Fin w → α → α × Bool} (state : Nat → α) (i : Fin w)
|
||||
(ind : ∀(i : Fin w), (f i (state i.val)).fst = state (i.val+1)) :
|
||||
getLsb (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd := by
|
||||
exact (iunfoldr_getLsb' state ind).1 i
|
||||
|
||||
/--
|
||||
Correctness theorem for `iunfoldr`.
|
||||
-/
|
||||
@@ -58,4 +103,11 @@ theorem iunfoldr_replace
|
||||
iunfoldr f a = (state w, value) := by
|
||||
simp [iunfoldr.eq_test state value a init step]
|
||||
|
||||
theorem iunfoldr_replace_snd
|
||||
{f : Fin w → α → α × Bool} (state : Nat → α) (value : BitVec w) (a : α)
|
||||
(init : state 0 = a)
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
|
||||
(iunfoldr f a).snd = value := by
|
||||
simp [iunfoldr.eq_test state value a init step]
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix
|
||||
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed,
|
||||
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Bool
|
||||
@@ -103,7 +104,13 @@ theorem eq_of_getMsb_eq {x y : BitVec w}
|
||||
have q := pred ⟨w - 1 - i, q_lt⟩
|
||||
simpa [q_lt, Nat.sub_sub_self, r] using q
|
||||
|
||||
@[simp] theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
|
||||
-- This cannot be a `@[simp]` lemma, as it would be tried at every term.
|
||||
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
|
||||
|
||||
@[simp] theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
|
||||
@[simp] theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false := by simp [of_length_zero]
|
||||
@[simp] theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false := by simp [of_length_zero]
|
||||
@[simp] theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero]
|
||||
|
||||
theorem eq_of_toFin_eq : ∀ {x y : BitVec w}, x.toFin = y.toFin → x = y
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
@@ -336,7 +343,7 @@ theorem nat_eq_toNat (x : BitVec w) (y : Nat)
|
||||
@[simp] theorem getMsb_zeroExtend_add {x : BitVec w} (h : k ≤ i) :
|
||||
(x.zeroExtend (w + k)).getMsb i = x.getMsb (i - k) := by
|
||||
by_cases h : w = 0
|
||||
· subst h; simp
|
||||
· subst h; simp [of_length_zero]
|
||||
simp only [getMsb, getLsb_zeroExtend]
|
||||
by_cases h₁ : i < w + k <;> by_cases h₂ : i - k < w <;> by_cases h₃ : w + k - 1 - i < w + k
|
||||
<;> simp [h₁, h₂, h₃]
|
||||
@@ -817,24 +824,42 @@ Definition of bitvector addition as a nat.
|
||||
.ofFin x + y = .ofFin (x + y.toFin) := rfl
|
||||
@[simp] theorem add_ofFin (x : BitVec n) (y : Fin (2^n)) :
|
||||
x + .ofFin y = .ofFin (x.toFin + y) := rfl
|
||||
@[simp] theorem ofNat_add_ofNat {n} (x y : Nat) : x#n + y#n = (x + y)#n := by
|
||||
|
||||
theorem ofNat_add {n} (x y : Nat) : (x + y)#n = x#n + y#n := by
|
||||
apply eq_of_toNat_eq ; simp [BitVec.ofNat]
|
||||
|
||||
theorem ofNat_add_ofNat {n} (x y : Nat) : x#n + y#n = (x + y)#n :=
|
||||
(ofNat_add x y).symm
|
||||
|
||||
protected theorem add_assoc (x y z : BitVec n) : x + y + z = x + (y + z) := by
|
||||
apply eq_of_toNat_eq ; simp [Nat.add_assoc]
|
||||
instance : Std.Associative (α := BitVec n) (· + ·) := ⟨BitVec.add_assoc⟩
|
||||
|
||||
protected theorem add_comm (x y : BitVec n) : x + y = y + x := by
|
||||
simp [add_def, Nat.add_comm]
|
||||
instance : Std.Commutative (α := BitVec n) (· + ·) := ⟨BitVec.add_comm⟩
|
||||
|
||||
@[simp] protected theorem add_zero (x : BitVec n) : x + 0#n = x := by simp [add_def]
|
||||
|
||||
@[simp] protected theorem zero_add (x : BitVec n) : 0#n + x = x := by simp [add_def]
|
||||
instance : Std.LawfulIdentity (α := BitVec n) (· + ·) 0#n where
|
||||
left_id := BitVec.zero_add
|
||||
right_id := BitVec.add_zero
|
||||
|
||||
theorem truncate_add (x y : BitVec w) (h : i ≤ w) :
|
||||
(x + y).truncate i = x.truncate i + y.truncate i := by
|
||||
have dvd : 2^i ∣ 2^w := Nat.pow_dvd_pow _ h
|
||||
simp [bv_toNat, h, Nat.mod_mod_of_dvd _ dvd]
|
||||
|
||||
@[simp, bv_toNat] theorem toInt_add (x y : BitVec w) :
|
||||
(x + y).toInt = (x.toInt + y.toInt).bmod (2^w) := by
|
||||
simp [toInt_eq_toNat_bmod]
|
||||
|
||||
theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
|
||||
BitVec.ofInt n x + BitVec.ofInt n y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp
|
||||
|
||||
/-! ### sub/neg -/
|
||||
|
||||
theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n (x.toNat + (2^n - y.toNat)) := by rfl
|
||||
@@ -872,10 +897,19 @@ theorem sub_toAdd {n} (x y : BitVec n) : x - y = x + - y := by
|
||||
|
||||
theorem add_sub_cancel (x y : BitVec w) : x + y - y = x := by
|
||||
apply eq_of_toNat_eq
|
||||
have y_toNat_le := Nat.le_of_lt y.toNat_lt
|
||||
have y_toNat_le := Nat.le_of_lt y.isLt
|
||||
rw [toNat_sub, toNat_add, Nat.mod_add_mod, Nat.add_assoc, ← Nat.add_sub_assoc y_toNat_le,
|
||||
Nat.add_sub_cancel_left, Nat.add_mod_right, toNat_mod_cancel]
|
||||
|
||||
theorem sub_add_cancel (x y : BitVec w) : x - y + y = x := by
|
||||
rw [sub_toAdd, BitVec.add_assoc, BitVec.add_comm _ y,
|
||||
← BitVec.add_assoc, ← sub_toAdd, add_sub_cancel]
|
||||
|
||||
theorem eq_sub_iff_add_eq {x y z : BitVec w} : x = z - y ↔ x + y = z := by
|
||||
apply Iff.intro <;> intro h
|
||||
· simp [h, sub_add_cancel]
|
||||
· simp [←h, add_sub_cancel]
|
||||
|
||||
theorem negOne_eq_allOnes : -1#w = allOnes w := by
|
||||
apply eq_of_toNat_eq
|
||||
if g : w = 0 then
|
||||
@@ -885,6 +919,13 @@ theorem negOne_eq_allOnes : -1#w = allOnes w := by
|
||||
have r : (2^w - 1) < 2^w := by omega
|
||||
simp [Nat.mod_eq_of_lt q, Nat.mod_eq_of_lt r]
|
||||
|
||||
theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1 := by
|
||||
apply eq_of_toNat_eq
|
||||
simp only [toNat_neg, ofNat_eq_ofNat, toNat_add, toNat_not, toNat_ofNat, Nat.add_mod_mod]
|
||||
congr
|
||||
have hx : x.toNat < 2^w := x.isLt
|
||||
rw [Nat.sub_sub, Nat.add_comm 1 x.toNat, ← Nat.sub_sub, Nat.sub_add_cancel (by omega)]
|
||||
|
||||
/-! ### mul -/
|
||||
|
||||
theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) := by rfl
|
||||
@@ -911,6 +952,15 @@ instance : Std.Associative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_asso
|
||||
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
|
||||
right_id := BitVec.mul_one
|
||||
|
||||
@[simp, bv_toNat] theorem toInt_mul (x y : BitVec w) :
|
||||
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
|
||||
simp [toInt_eq_toNat_bmod]
|
||||
|
||||
theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
|
||||
BitVec.ofInt n x * BitVec.ofInt n y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp
|
||||
|
||||
/-! ### le and lt -/
|
||||
|
||||
@[bv_toNat] theorem le_def (x y : BitVec n) :
|
||||
|
||||
@@ -74,6 +74,7 @@ Added for confluence with `not_and_self` `and_not_self` on term
|
||||
@[simp] theorem eq_false_and_eq_true_self : ∀(b : Bool), (b = false ∧ b = true) ↔ False := by decide
|
||||
|
||||
theorem and_comm : ∀ (x y : Bool), (x && y) = (y && x) := by decide
|
||||
instance : Std.Commutative (· && ·) := ⟨and_comm⟩
|
||||
|
||||
theorem and_left_comm : ∀ (x y z : Bool), (x && (y && z)) = (y && (x && z)) := by decide
|
||||
theorem and_right_comm : ∀ (x y z : Bool), ((x && y) && z) = ((x && z) && y) := by decide
|
||||
@@ -120,6 +121,7 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
|
||||
@[simp] theorem iff_or_self : ∀(a b : Bool), (b = (a || b)) ↔ (a → b) := by decide
|
||||
|
||||
theorem or_comm : ∀ (x y : Bool), (x || y) = (y || x) := by decide
|
||||
instance : Std.Commutative (· || ·) := ⟨or_comm⟩
|
||||
|
||||
theorem or_left_comm : ∀ (x y z : Bool), (x || (y || z)) = (y || (x || z)) := by decide
|
||||
theorem or_right_comm : ∀ (x y z : Bool), ((x || y) || z) = ((x || z) || y) := by decide
|
||||
@@ -186,12 +188,18 @@ in false_eq and true_eq.
|
||||
@[simp] theorem true_beq : ∀b, (true == b) = b := by decide
|
||||
@[simp] theorem false_beq : ∀b, (false == b) = !b := by decide
|
||||
@[simp] theorem beq_true : ∀b, (b == true) = b := by decide
|
||||
instance : Std.LawfulIdentity (· == ·) true where
|
||||
left_id := true_beq
|
||||
right_id := beq_true
|
||||
@[simp] theorem beq_false : ∀b, (b == false) = !b := by decide
|
||||
|
||||
@[simp] theorem true_bne : ∀(b : Bool), (true != b) = !b := by decide
|
||||
@[simp] theorem false_bne : ∀(b : Bool), (false != b) = b := by decide
|
||||
@[simp] theorem bne_true : ∀(b : Bool), (b != true) = !b := by decide
|
||||
@[simp] theorem bne_false : ∀(b : Bool), (b != false) = b := by decide
|
||||
instance : Std.LawfulIdentity (· != ·) false where
|
||||
left_id := false_bne
|
||||
right_id := bne_false
|
||||
|
||||
@[simp] theorem not_beq_self : ∀ (x : Bool), ((!x) == x) = false := by decide
|
||||
@[simp] theorem beq_not_self : ∀ (x : Bool), (x == !x) = false := by decide
|
||||
@@ -214,6 +222,7 @@ due to `beq_iff_eq`.
|
||||
@[simp] theorem not_bne_not : ∀ (x y : Bool), ((!x) != (!y)) = (x != y) := by decide
|
||||
|
||||
@[simp] theorem bne_assoc : ∀ (x y z : Bool), ((x != y) != z) = (x != (y != z)) := by decide
|
||||
instance : Std.Associative (· != ·) := ⟨bne_assoc⟩
|
||||
|
||||
@[simp] theorem bne_left_inj : ∀ (x y z : Bool), (x != y) = (x != z) ↔ y = z := by decide
|
||||
@[simp] theorem bne_right_inj : ∀ (x y z : Bool), (x != z) = (y != z) ↔ x = y := by decide
|
||||
|
||||
@@ -13,17 +13,40 @@ namespace Fin
|
||||
instance coeToNat : CoeOut (Fin n) Nat :=
|
||||
⟨fun v => v.val⟩
|
||||
|
||||
/--
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is simlar to `Empty.elim`.
|
||||
-/
|
||||
def elim0.{u} {α : Sort u} : Fin 0 → α
|
||||
| ⟨_, h⟩ => absurd h (not_lt_zero _)
|
||||
|
||||
/--
|
||||
Returns the successor of the argument.
|
||||
|
||||
The bound in the result type is increased:
|
||||
```
|
||||
(2 : Fin 3).succ = (3 : Fin 4)
|
||||
```
|
||||
This differs from addition, which wraps around:
|
||||
```
|
||||
(2 : Fin 3) + 1 = (0 : Fin 3)
|
||||
```
|
||||
-/
|
||||
def succ : Fin n → Fin n.succ
|
||||
| ⟨i, h⟩ => ⟨i+1, Nat.succ_lt_succ h⟩
|
||||
|
||||
variable {n : Nat}
|
||||
|
||||
/--
|
||||
Returns `a` modulo `n + 1` as a `Fin n.succ`.
|
||||
-/
|
||||
protected def ofNat {n : Nat} (a : Nat) : Fin n.succ :=
|
||||
⟨a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)⟩
|
||||
|
||||
/--
|
||||
Returns `a` modulo `n` as a `Fin n`.
|
||||
|
||||
The assumption `n > 0` ensures that `Fin n` is nonempty.
|
||||
-/
|
||||
protected def ofNat' {n : Nat} (a : Nat) (h : n > 0) : Fin n :=
|
||||
⟨a % n, Nat.mod_lt _ h⟩
|
||||
|
||||
@@ -33,12 +56,15 @@ private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
|
||||
have : n > 0 := Nat.lt_trans (Nat.zero_lt_succ _) h;
|
||||
Nat.mod_lt _ this
|
||||
|
||||
/-- Addition modulo `n` -/
|
||||
protected def add : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + b) % n, mlt h⟩
|
||||
|
||||
/-- Multiplication modulo `n` -/
|
||||
protected def mul : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a * b) % n, mlt h⟩
|
||||
|
||||
/-- Subtraction modulo `n` -/
|
||||
protected def sub : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + (n - b)) % n, mlt h⟩
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import Init.Data.Nat.Linear
|
||||
loop (x : α) (i : Nat) : α :=
|
||||
if h : i < n then loop (f x ⟨i, h⟩) (i+1) else x
|
||||
termination_by n - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
|
||||
@[inline] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop ⟨n, Nat.le_refl n⟩ init where
|
||||
|
||||
@@ -23,6 +23,7 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
|
||||
have p : i = n := (or_iff_left g).mp (Nat.eq_or_lt_of_le ubnd)
|
||||
_root_.cast (congrArg P p) a
|
||||
termination_by n - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
`hIterate` is a heterogenous iterative operation that applies a
|
||||
|
||||
@@ -602,6 +602,7 @@ A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
|
||||
@Fin.succRecOn (n + 1) i.succ motive zero succ = succ n i (Fin.succRecOn i zero succ) := by
|
||||
cases i; rfl
|
||||
|
||||
|
||||
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
|
||||
This function has two arguments: `zero` handles the base case on `motive 0`,
|
||||
and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
@@ -610,8 +611,12 @@ and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
@[elab_as_elim] def induction {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) :
|
||||
∀ i : Fin (n + 1), motive i
|
||||
| ⟨0, hi⟩ => by rwa [Fin.mk_zero]
|
||||
| ⟨i+1, hi⟩ => succ ⟨i, Nat.lt_of_succ_lt_succ hi⟩ (induction zero succ ⟨i, Nat.lt_of_succ_lt hi⟩)
|
||||
| ⟨i, hi⟩ => go i hi
|
||||
where
|
||||
-- Use a curried function so that this is structurally recursive
|
||||
go : ∀ (i : Nat) (hi : i < n + 1), motive ⟨i, hi⟩
|
||||
| 0, hi => by rwa [Fin.mk_zero]
|
||||
| i+1, hi => succ ⟨i, Nat.lt_of_succ_lt_succ hi⟩ (go i (Nat.lt_of_succ_lt hi))
|
||||
|
||||
@[simp] theorem induction_zero {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(hs : ∀ i : Fin n, motive (castSucc i) → motive i.succ) :
|
||||
@@ -793,15 +798,20 @@ protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
|
||||
|
||||
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
|
||||
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
|
||||
instance : Std.Commutative (α := Fin n) (· * ·) := ⟨Fin.mul_comm⟩
|
||||
|
||||
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
|
||||
apply eq_of_val_eq
|
||||
simp only [val_mul]
|
||||
rw [← Nat.mod_eq_of_lt a.isLt, ← Nat.mod_eq_of_lt b.isLt, ← Nat.mod_eq_of_lt c.isLt]
|
||||
simp only [← Nat.mul_mod, Nat.mul_assoc]
|
||||
instance : Std.Associative (α := Fin n) (· * ·) := ⟨Fin.mul_assoc⟩
|
||||
|
||||
protected theorem one_mul (k : Fin (n + 1)) : (1 : Fin (n + 1)) * k = k := by
|
||||
rw [Fin.mul_comm, Fin.mul_one]
|
||||
instance : Std.LawfulIdentity (α := Fin (n + 1)) (· * ·) 1 where
|
||||
left_id := Fin.one_mul
|
||||
right_id := Fin.mul_one
|
||||
|
||||
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [ext_iff, mul_def]
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ protected def neg (n : @& Int) : Int :=
|
||||
```
|
||||
-/
|
||||
@[default_instance mid]
|
||||
instance : Neg Int where
|
||||
instance instNegInt : Neg Int where
|
||||
neg := Int.neg
|
||||
|
||||
/-- Subtraction of two natural numbers. -/
|
||||
@@ -173,13 +173,13 @@ inductive NonNeg : Int → Prop where
|
||||
/-- Definition of `a ≤ b`, encoded as `b - a ≥ 0`. -/
|
||||
protected def le (a b : Int) : Prop := NonNeg (b - a)
|
||||
|
||||
instance : LE Int where
|
||||
instance instLEInt : LE Int where
|
||||
le := Int.le
|
||||
|
||||
/-- Definition of `a < b`, encoded as `a + 1 ≤ b`. -/
|
||||
protected def lt (a b : Int) : Prop := (a + 1) ≤ b
|
||||
|
||||
instance : LT Int where
|
||||
instance instLTInt : LT Int where
|
||||
lt := Int.lt
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
|
||||
@@ -178,7 +178,7 @@ theorem fdiv_eq_div {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fdiv a b = div a
|
||||
|
||||
@[simp] theorem mod_zero : ∀ a : Int, mod a 0 = a
|
||||
| ofNat _ => congrArg ofNat <| Nat.mod_zero _
|
||||
| -[_+1] => rfl
|
||||
| -[_+1] => congrArg (fun n => -ofNat n) <| Nat.mod_zero _
|
||||
|
||||
@[simp] theorem zero_fmod (b : Int) : fmod 0 b = 0 := by cases b <;> rfl
|
||||
|
||||
@@ -225,7 +225,9 @@ theorem mod_add_div : ∀ a b : Int, mod a b + b * (a.div b) = a
|
||||
| ofNat m, -[n+1] => by
|
||||
show (m % succ n + -↑(succ n) * -↑(m / succ n) : Int) = m
|
||||
rw [Int.neg_mul_neg]; exact congrArg ofNat (Nat.mod_add_div ..)
|
||||
| -[_+1], 0 => rfl
|
||||
| -[m+1], 0 => by
|
||||
show -(↑((succ m) % 0) : Int) + 0 * -↑(succ m / 0) = -↑(succ m)
|
||||
rw [Nat.mod_zero, Int.zero_mul, Int.add_zero]
|
||||
| -[m+1], ofNat n => by
|
||||
show -(↑((succ m) % n) : Int) + ↑n * -↑(succ m / n) = -↑(succ m)
|
||||
rw [Int.mul_neg, ← Int.neg_add]
|
||||
@@ -1054,19 +1056,39 @@ theorem emod_add_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n + y) n = Int.bmo
|
||||
simp [Int.emod_def, Int.sub_eq_add_neg]
|
||||
rw [←Int.mul_neg, Int.add_right_comm, Int.bmod_add_mul_cancel]
|
||||
|
||||
@[simp]
|
||||
theorem emod_mul_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n * y) n = Int.bmod (x * y) n := by
|
||||
simp [Int.emod_def, Int.sub_eq_add_neg]
|
||||
rw [←Int.mul_neg, Int.add_mul, Int.mul_assoc, Int.bmod_add_mul_cancel]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_add_bmod_congr : Int.bmod (Int.bmod x n + y) n = Int.bmod (x + y) n := by
|
||||
rw [bmod_def x n]
|
||||
split
|
||||
case inl p =>
|
||||
simp only [emod_add_bmod_congr]
|
||||
case inr p =>
|
||||
rw [Int.sub_eq_add_neg, Int.add_right_comm, ←Int.sub_eq_add_neg]
|
||||
simp
|
||||
|
||||
@[simp] theorem add_bmod_bmod : Int.bmod (x + Int.bmod y n) n = Int.bmod (x + y) n := by
|
||||
rw [Int.add_comm x, Int.bmod_add_bmod_congr, Int.add_comm y]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_mul_bmod : Int.bmod (Int.bmod x n * y) n = Int.bmod (x * y) n := by
|
||||
rw [bmod_def x n]
|
||||
split
|
||||
case inl p =>
|
||||
simp
|
||||
case inr p =>
|
||||
rw [Int.sub_eq_add_neg, Int.add_right_comm, ←Int.sub_eq_add_neg]
|
||||
rw [Int.sub_mul, Int.sub_eq_add_neg, ← Int.mul_neg]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem add_bmod_bmod : Int.bmod (x + Int.bmod y n) n = Int.bmod (x + y) n := by
|
||||
rw [Int.add_comm x, Int.bmod_add_bmod_congr, Int.add_comm y]
|
||||
@[simp] theorem mul_bmod_bmod : Int.bmod (x * Int.bmod y n) n = Int.bmod (x * y) n := by
|
||||
rw [Int.mul_comm x, bmod_mul_bmod, Int.mul_comm x]
|
||||
|
||||
theorem add_bmod (a b : Int) (n : Nat) : (a + b).bmod n = (a.bmod n + b.bmod n).bmod n := by
|
||||
simp
|
||||
|
||||
theorem emod_bmod {x : Int} {m : Nat} : bmod (x % m) m = bmod x m := by
|
||||
simp [bmod]
|
||||
|
||||
@@ -137,12 +137,16 @@ protected theorem add_comm : ∀ a b : Int, a + b = b + a
|
||||
| ofNat _, -[_+1] => rfl
|
||||
| -[_+1], ofNat _ => rfl
|
||||
| -[_+1], -[_+1] => by simp [Nat.add_comm]
|
||||
instance : Std.Commutative (α := Int) (· + ·) := ⟨Int.add_comm⟩
|
||||
|
||||
@[simp] protected theorem add_zero : ∀ a : Int, a + 0 = a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. ▸ a.add_zero
|
||||
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
|
||||
left_id := Int.zero_add
|
||||
right_id := Int.add_zero
|
||||
|
||||
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
|
||||
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
|
||||
@@ -196,6 +200,7 @@ where
|
||||
simp
|
||||
rw [Int.add_comm, subNatNat_add_negSucc]
|
||||
simp [Nat.add_comm, Nat.add_left_comm, Nat.add_assoc]
|
||||
instance : Std.Associative (α := Int) (· + ·) := ⟨Int.add_assoc⟩
|
||||
|
||||
protected theorem add_left_comm (a b c : Int) : a + (b + c) = b + (a + c) := by
|
||||
rw [← Int.add_assoc, Int.add_comm a, Int.add_assoc]
|
||||
@@ -351,6 +356,7 @@ protected theorem sub_right_inj (i j k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
|
||||
protected theorem mul_comm (a b : Int) : a * b = b * a := by
|
||||
cases a <;> cases b <;> simp [Nat.mul_comm]
|
||||
instance : Std.Commutative (α := Int) (· * ·) := ⟨Int.mul_comm⟩
|
||||
|
||||
theorem ofNat_mul_negOfNat (m n : Nat) : (m : Nat) * negOfNat n = negOfNat (m * n) := by
|
||||
cases n <;> rfl
|
||||
@@ -369,6 +375,7 @@ attribute [local simp] ofNat_mul_negOfNat negOfNat_mul_ofNat
|
||||
|
||||
protected theorem mul_assoc (a b c : Int) : a * b * c = a * (b * c) := by
|
||||
cases a <;> cases b <;> cases c <;> simp [Nat.mul_assoc]
|
||||
instance : Std.Associative (α := Int) (· * ·) := ⟨Int.mul_assoc⟩
|
||||
|
||||
protected theorem mul_left_comm (a b c : Int) : a * (b * c) = b * (a * c) := by
|
||||
rw [← Int.mul_assoc, ← Int.mul_assoc, Int.mul_comm a]
|
||||
@@ -458,6 +465,9 @@ protected theorem sub_mul (a b c : Int) : (a - b) * c = a * c - b * c := by
|
||||
| -[n+1] => show -[1 * n +1] = -[n+1] by rw [Nat.one_mul]
|
||||
|
||||
@[simp] protected theorem mul_one (a : Int) : a * 1 = a := by rw [Int.mul_comm, Int.one_mul]
|
||||
instance : Std.LawfulIdentity (α := Int) (· * ·) 1 where
|
||||
left_id := Int.one_mul
|
||||
right_id := Int.mul_one
|
||||
|
||||
protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ protected theorem min_comm (a b : Int) : min a b = min b a := by
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₁ h₂
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) min := ⟨Int.min_comm⟩
|
||||
|
||||
protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def]; split <;> simp [*]
|
||||
|
||||
@@ -206,6 +207,7 @@ protected theorem max_comm (a b : Int) : max a b = max b a := by
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) max := ⟨Int.max_comm⟩
|
||||
|
||||
protected theorem le_max_left (a b : Int) : a ≤ max a b := by rw [Int.max_def]; split <;> simp [*]
|
||||
|
||||
|
||||
@@ -9,3 +9,4 @@ import Init.Data.List.BasicAux
|
||||
import Init.Data.List.Control
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.Impl
|
||||
import Init.Data.List.TakeDrop
|
||||
|
||||
@@ -127,6 +127,9 @@ instance : Append (List α) := ⟨List.append⟩
|
||||
| nil => rfl
|
||||
| cons a as ih =>
|
||||
simp_all [HAppend.hAppend, Append.append, List.append]
|
||||
instance : Std.LawfulIdentity (α := List α) (· ++ ·) [] where
|
||||
left_id := nil_append
|
||||
right_id := append_nil
|
||||
|
||||
@[simp] theorem cons_append (a : α) (as bs : List α) : (a::as) ++ bs = a::(as ++ bs) := rfl
|
||||
|
||||
@@ -136,6 +139,7 @@ theorem append_assoc (as bs cs : List α) : (as ++ bs) ++ cs = as ++ (bs ++ cs)
|
||||
induction as with
|
||||
| nil => rfl
|
||||
| cons a as ih => simp [ih]
|
||||
instance : Std.Associative (α := List α) (· ++ ·) := ⟨append_assoc⟩
|
||||
|
||||
theorem append_cons (as : List α) (b : α) (bs : List α) : as ++ b :: bs = as ++ [b] ++ bs := by
|
||||
induction as with
|
||||
|
||||
@@ -5,6 +5,7 @@ Author: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.Ext
|
||||
|
||||
universe u
|
||||
|
||||
@@ -12,63 +13,157 @@ namespace List
|
||||
/-! The following functions can't be defined at `Init.Data.List.Basic`, because they depend on `Init.Util`,
|
||||
and `Init.Util` depends on `Init.Data.List.Basic`. -/
|
||||
|
||||
def get! [Inhabited α] : List α → Nat → α
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function panics when executed, and returns
|
||||
`default`. See `get?` and `getD` for safer alternatives.
|
||||
-/
|
||||
def get! [Inhabited α] : (as : List α) → (i : Nat) → α
|
||||
| a::_, 0 => a
|
||||
| _::as, n+1 => get! as n
|
||||
| _, _ => panic! "invalid index"
|
||||
|
||||
def get? : List α → Nat → Option α
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
|
||||
Also see `get`, `getD` and `get!`.
|
||||
-/
|
||||
def get? : (as : List α) → (i : Nat) → Option α
|
||||
| a::_, 0 => some a
|
||||
| _::as, n+1 => get? as n
|
||||
| _, _ => none
|
||||
|
||||
def getD (as : List α) (idx : Nat) (a₀ : α) : α :=
|
||||
(as.get? idx).getD a₀
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
|
||||
See also `get?` and `get!`.
|
||||
-/
|
||||
def getD (as : List α) (i : Nat) (fallback : α) : α :=
|
||||
(as.get? i).getD fallback
|
||||
|
||||
@[ext] theorem ext : ∀ {l₁ l₂ : List α}, (∀ n, l₁.get? n = l₂.get? n) → l₁ = l₂
|
||||
| [], [], _ => rfl
|
||||
| a :: l₁, [], h => nomatch h 0
|
||||
| [], a' :: l₂, h => nomatch h 0
|
||||
| a :: l₁, a' :: l₂, h => by
|
||||
have h0 : some a = some a' := h 0
|
||||
injection h0 with aa; simp only [aa, ext fun n => h (n+1)]
|
||||
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `head` and `headD` for safer alternatives.
|
||||
-/
|
||||
def head! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
| a::_ => a
|
||||
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
|
||||
If the list is empty, this function returns `none`.
|
||||
Also see `headD` and `head!`.
|
||||
-/
|
||||
def head? : List α → Option α
|
||||
| [] => none
|
||||
| a::_ => some a
|
||||
|
||||
def headD : List α → α → α
|
||||
| [], a₀ => a₀
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
|
||||
If the list is empty, this function returns `fallback`.
|
||||
Also see `head?` and `head!`.
|
||||
-/
|
||||
def headD : (as : List α) → (fallback : α) → α
|
||||
| [], fallback => fallback
|
||||
| a::_, _ => a
|
||||
|
||||
/--
|
||||
Returns the first element of a non-empty list.
|
||||
-/
|
||||
def head : (as : List α) → as ≠ [] → α
|
||||
| a::_, _ => a
|
||||
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns the empty list.
|
||||
See `tail` and `tailD` for safer alternatives.
|
||||
-/
|
||||
def tail! : List α → List α
|
||||
| [] => panic! "empty list"
|
||||
| _::as => as
|
||||
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
|
||||
If the list is empty, this function returns `none`.
|
||||
Also see `tailD` and `tail!`.
|
||||
-/
|
||||
def tail? : List α → Option (List α)
|
||||
| [] => none
|
||||
| _::as => some as
|
||||
|
||||
def tailD : List α → List α → List α
|
||||
| [], as₀ => as₀
|
||||
| _::as, _ => as
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
|
||||
If the list is empty, this function returns `fallback`.
|
||||
Also see `head?` and `head!`.
|
||||
-/
|
||||
def tailD (list fallback : List α) : List α :=
|
||||
match list with
|
||||
| [] => fallback
|
||||
| _ :: tl => tl
|
||||
|
||||
/--
|
||||
Returns the last element of a non-empty list.
|
||||
-/
|
||||
def getLast : ∀ (as : List α), as ≠ [] → α
|
||||
| [], h => absurd rfl h
|
||||
| [a], _ => a
|
||||
| _::b::as, _ => getLast (b::as) (fun h => List.noConfusion h)
|
||||
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `getLast` and `getLastD` for safer alternatives.
|
||||
-/
|
||||
def getLast! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
| a::as => getLast (a::as) (fun h => List.noConfusion h)
|
||||
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
|
||||
If the list is empty, this function returns `none`.
|
||||
Also see `getLastD` and `getLast!`.
|
||||
-/
|
||||
def getLast? : List α → Option α
|
||||
| [] => none
|
||||
| a::as => some (getLast (a::as) (fun h => List.noConfusion h))
|
||||
|
||||
def getLastD : List α → α → α
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
|
||||
If the list is empty, this function returns `fallback`.
|
||||
Also see `getLast?` and `getLast!`.
|
||||
-/
|
||||
def getLastD : (as : List α) → (fallback : α) → α
|
||||
| [], a₀ => a₀
|
||||
| a::as, _ => getLast (a::as) (fun h => List.noConfusion h)
|
||||
|
||||
/--
|
||||
`O(n)`. Rotates the elements of `xs` to the left such that the element at
|
||||
`xs[i]` rotates to `xs[(i - n) % l.length]`.
|
||||
* `rotateLeft [1, 2, 3, 4, 5] 3 = [4, 5, 1, 2, 3]`
|
||||
* `rotateLeft [1, 2, 3, 4, 5] 5 = [1, 2, 3, 4, 5]`
|
||||
* `rotateLeft [1, 2, 3, 4, 5] = [2, 3, 4, 5, 1]`
|
||||
-/
|
||||
def rotateLeft (xs : List α) (n : Nat := 1) : List α :=
|
||||
let len := xs.length
|
||||
if len ≤ 1 then
|
||||
@@ -79,6 +174,13 @@ def rotateLeft (xs : List α) (n : Nat := 1) : List α :=
|
||||
let e := xs.drop n
|
||||
e ++ b
|
||||
|
||||
/--
|
||||
`O(n)`. Rotates the elements of `xs` to the right such that the element at
|
||||
`xs[i]` rotates to `xs[(i + n) % l.length]`.
|
||||
* `rotateRight [1, 2, 3, 4, 5] 3 = [3, 4, 5, 1, 2]`
|
||||
* `rotateRight [1, 2, 3, 4, 5] 5 = [1, 2, 3, 4, 5]`
|
||||
* `rotateRight [1, 2, 3, 4, 5] = [5, 1, 2, 3, 4]`
|
||||
-/
|
||||
def rotateRight (xs : List α) (n : Nat := 1) : List α :=
|
||||
let len := xs.length
|
||||
if len ≤ 1 then
|
||||
@@ -124,9 +226,10 @@ theorem sizeOf_lt_of_mem [SizeOf α] {as : List α} (h : a ∈ as) : sizeOf a <
|
||||
over a nested inductive like `inductive T | mk : List T → T`. -/
|
||||
macro "sizeOf_list_dec" : tactic =>
|
||||
`(tactic| first
|
||||
| apply sizeOf_lt_of_mem; assumption; done
|
||||
| apply Nat.lt_trans (sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
| with_reducible apply sizeOf_lt_of_mem; assumption; done
|
||||
| with_reducible
|
||||
apply Nat.lt_trans (sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
simp_arith)
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| sizeOf_list_dec)
|
||||
@@ -209,6 +312,15 @@ def mapMono (as : List α) (f : α → α) : List α :=
|
||||
Monadic generalization of `List.partition`.
|
||||
|
||||
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic`.
|
||||
```
|
||||
def posOrNeg (x : Int) : Except String Bool :=
|
||||
if x > 0 then pure true
|
||||
else if x < 0 then pure false
|
||||
else throw "Zero is not positive or negative"
|
||||
|
||||
partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
|
||||
partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
|
||||
```
|
||||
-/
|
||||
@[inline] def partitionM [Monad m] (p : α → m Bool) (l : List α) : m (List α × List α) :=
|
||||
go l #[] #[]
|
||||
|
||||
@@ -40,6 +40,13 @@ Finally, we rarely use `mapM` with something that is not a `Monad`.
|
||||
Users that want to use `mapM` with `Applicative` should use `mapA` instead.
|
||||
-/
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
See `List.forM` for the variant that discards the results.
|
||||
See `List.mapA` for the variant that works with `Applicative`.
|
||||
-/
|
||||
@[inline]
|
||||
def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m β) (as : List α) : m (List β) :=
|
||||
let rec @[specialize] loop
|
||||
@@ -47,17 +54,42 @@ def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α
|
||||
| a :: as, bs => do loop as ((← f a)::bs)
|
||||
loop as []
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `mapM` can be more efficient.
|
||||
|
||||
See `List.forA` for the variant that discards the results.
|
||||
See `List.mapM` for the variant that works with `Monad`.
|
||||
|
||||
**Warning**: this function is not tail-recursive, meaning that it may fail with a stack overflow on long lists.
|
||||
-/
|
||||
@[specialize]
|
||||
def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m (List β)
|
||||
| [] => pure []
|
||||
| a::as => List.cons <$> f a <*> mapA f as
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right.
|
||||
|
||||
See `List.mapM` for the variant that collects results.
|
||||
See `List.forA` for the variant that works with `Applicative`.
|
||||
-/
|
||||
@[specialize]
|
||||
protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
match as with
|
||||
| [] => pure ⟨⟩
|
||||
| a :: as => do f a; List.forM as f
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `forM` can be more efficient.
|
||||
|
||||
See `List.mapA` for the variant that collects results.
|
||||
See `List.forM` for the variant that works with `Monad`.
|
||||
-/
|
||||
@[specialize]
|
||||
def forA {m : Type u → Type v} [Applicative m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
match as with
|
||||
@@ -71,15 +103,27 @@ def filterAuxM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) :
|
||||
let b ← f h
|
||||
filterAuxM f t (cond b (h :: acc) acc)
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, left-to-right, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
-/
|
||||
@[inline]
|
||||
def filterM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) (as : List α) : m (List α) := do
|
||||
let as ← filterAuxM f as []
|
||||
def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) := do
|
||||
let as ← filterAuxM p as []
|
||||
pure as.reverse
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, right-to-left, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
-/
|
||||
@[inline]
|
||||
def filterRevM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) (as : List α) : m (List α) :=
|
||||
filterAuxM f as.reverse []
|
||||
def filterRevM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) :=
|
||||
filterAuxM p as.reverse []
|
||||
|
||||
/--
|
||||
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns those
|
||||
results `y` for which `f x` returns `some y`.
|
||||
-/
|
||||
@[inline]
|
||||
def filterMapM {m : Type u → Type v} [Monad m] {α β : Type u} (f : α → m (Option β)) (as : List α) : m (List β) :=
|
||||
let rec @[specialize] loop
|
||||
@@ -90,6 +134,16 @@ def filterMapM {m : Type u → Type v} [Monad m] {α β : Type u} (f : α → m
|
||||
| some b => loop as (b::bs)
|
||||
loop as.reverse []
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from left to right:
|
||||
```
|
||||
foldlM f x₀ [a, b, c] = do
|
||||
let x₁ ← f x₀ a
|
||||
let x₂ ← f x₁ b
|
||||
let x₃ ← f x₂ c
|
||||
pure x₃
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
protected def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s → α → m s) → (init : s) → List α → m s
|
||||
| _, s, [] => pure s
|
||||
@@ -97,10 +151,26 @@ protected def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w
|
||||
let s' ← f s a
|
||||
List.foldlM f s' as
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from right to left:
|
||||
```
|
||||
foldrM f x₀ [a, b, c] = do
|
||||
let x₁ ← f c x₀
|
||||
let x₂ ← f b x₁
|
||||
let x₃ ← f a x₂
|
||||
pure x₃
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldrM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} (f : α → s → m s) (init : s) (l : List α) : m s :=
|
||||
l.reverse.foldlM (fun s a => f a s) init
|
||||
|
||||
/--
|
||||
Maps `f` over the list and collects the results with `<|>`.
|
||||
```
|
||||
firstM f [a, b, c] = f a <|> f b <|> f c <|> failure
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
def firstM {m : Type u → Type v} [Alternative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m β
|
||||
| [] => failure
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
360
src/Init/Data/List/TakeDrop.lean
Normal file
360
src/Init/Data/List/TakeDrop.lean
Normal file
@@ -0,0 +1,360 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.take`, `List.drop`, `List.zip` and `List.zipWith`.
|
||||
|
||||
These are in a separate file from most of the list lemmas
|
||||
as they required importing more lemmas about natural numbers.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### take -/
|
||||
|
||||
abbrev take_succ_cons := @take_cons_succ
|
||||
|
||||
@[simp] theorem length_take : ∀ (i : Nat) (l : List α), length (take i l) = min i (length l)
|
||||
| 0, l => by simp [Nat.zero_min]
|
||||
| succ n, [] => by simp [Nat.min_zero]
|
||||
| succ n, _ :: l => by simp [Nat.succ_min_succ, length_take]
|
||||
|
||||
theorem length_take_le (n) (l : List α) : length (take n l) ≤ n := by simp [Nat.min_le_left]
|
||||
|
||||
theorem length_take_le' (n) (l : List α) : length (take n l) ≤ l.length :=
|
||||
by simp [Nat.min_le_right]
|
||||
|
||||
theorem length_take_of_le (h : n ≤ length l) : length (take n l) = n := by simp [Nat.min_eq_left h]
|
||||
|
||||
theorem take_all_of_le {n} {l : List α} (h : length l ≤ n) : take n l = l :=
|
||||
take_length_le h
|
||||
|
||||
@[simp]
|
||||
theorem take_left : ∀ l₁ l₂ : List α, take (length l₁) (l₁ ++ l₂) = l₁
|
||||
| [], _ => rfl
|
||||
| a :: l₁, l₂ => congrArg (cons a) (take_left l₁ l₂)
|
||||
|
||||
theorem take_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : take n (l₁ ++ l₂) = l₁ := by
|
||||
rw [← h]; apply take_left
|
||||
|
||||
theorem take_take : ∀ (n m) (l : List α), take n (take m l) = take (min n m) l
|
||||
| n, 0, l => by rw [Nat.min_zero, take_zero, take_nil]
|
||||
| 0, m, l => by rw [Nat.zero_min, take_zero, take_zero]
|
||||
| succ n, succ m, nil => by simp only [take_nil]
|
||||
| succ n, succ m, a :: l => by
|
||||
simp only [take, succ_min_succ, take_take n m l]
|
||||
|
||||
theorem take_replicate (a : α) : ∀ n m : Nat, take n (replicate m a) = replicate (min n m) a
|
||||
| n, 0 => by simp [Nat.min_zero]
|
||||
| 0, m => by simp [Nat.zero_min]
|
||||
| succ n, succ m => by simp [succ_min_succ, take_replicate]
|
||||
|
||||
theorem map_take (f : α → β) :
|
||||
∀ (L : List α) (i : Nat), (L.take i).map f = (L.map f).take i
|
||||
| [], i => by simp
|
||||
| _, 0 => by simp
|
||||
| h :: t, n + 1 => by dsimp; rw [map_take f t n]
|
||||
|
||||
/-- Taking the first `n` elements in `l₁ ++ l₂` is the same as appending the first `n` elements
|
||||
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
|
||||
theorem take_append_eq_append_take {l₁ l₂ : List α} {n : Nat} :
|
||||
take n (l₁ ++ l₂) = take n l₁ ++ take (n - l₁.length) l₂ := by
|
||||
induction l₁ generalizing n
|
||||
· simp
|
||||
· cases n
|
||||
· simp [*]
|
||||
· simp only [cons_append, take_cons_succ, length_cons, succ_eq_add_one, cons.injEq,
|
||||
append_cancel_left_eq, true_and, *]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem take_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n ≤ l₁.length) :
|
||||
(l₁ ++ l₂).take n = l₁.take n := by
|
||||
simp [take_append_eq_append_take, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
|
||||
`i` elements of `l₂` to `l₁`. -/
|
||||
theorem take_append {l₁ l₂ : List α} (i : Nat) :
|
||||
take (l₁.length + i) (l₁ ++ l₂) = l₁ ++ take i l₂ := by
|
||||
rw [take_append_eq_append_take, take_all_of_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the big list to the small list. -/
|
||||
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
|
||||
get L ⟨i, hi⟩ = get (L.take j) ⟨i, length_take .. ▸ Nat.lt_min.mpr ⟨hj, hi⟩⟩ :=
|
||||
get_of_eq (take_append_drop j L).symm _ ▸ get_append ..
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
theorem get_take' (L : List α) {j i} :
|
||||
get (L.take j) i =
|
||||
get L ⟨i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _)⟩ := by
|
||||
let ⟨i, hi⟩ := i; rw [length_take, Nat.lt_min] at hi; rw [get_take L _ hi.1]
|
||||
|
||||
theorem get?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n).get? m = l.get? m := by
|
||||
induction n generalizing l m with
|
||||
| zero =>
|
||||
exact absurd h (Nat.not_lt_of_le m.zero_le)
|
||||
| succ _ hn =>
|
||||
cases l with
|
||||
| nil => simp only [take_nil]
|
||||
| cons hd tl =>
|
||||
cases m
|
||||
· simp only [get?, take]
|
||||
· simpa only using hn (Nat.lt_of_succ_lt_succ h)
|
||||
|
||||
theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n).get? m = none :=
|
||||
get?_eq_none.mpr <| Nat.le_trans (length_take_le _ _) h
|
||||
|
||||
theorem get?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n).get? m = if m < n then l.get? m else none := by
|
||||
split
|
||||
· next h => exact get?_take h
|
||||
· next h => exact get?_take_eq_none (Nat.le_of_not_lt h)
|
||||
|
||||
@[simp]
|
||||
theorem nth_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1)).get? n = l.get? n :=
|
||||
get?_take (Nat.lt_succ_self n)
|
||||
|
||||
theorem take_succ {l : List α} {n : Nat} : l.take (n + 1) = l.take n ++ (l.get? n).toList := by
|
||||
induction l generalizing n with
|
||||
| nil =>
|
||||
simp only [Option.toList, get?, take_nil, append_nil]
|
||||
| cons hd tl hl =>
|
||||
cases n
|
||||
· simp only [Option.toList, get?, eq_self_iff_true, take, nil_append]
|
||||
· simp only [hl, cons_append, get?, eq_self_iff_true, take]
|
||||
|
||||
@[simp]
|
||||
theorem take_eq_nil_iff {l : List α} {k : Nat} : l.take k = [] ↔ l = [] ∨ k = 0 := by
|
||||
cases l <;> cases k <;> simp [Nat.succ_ne_zero]
|
||||
|
||||
@[simp]
|
||||
theorem take_eq_take :
|
||||
∀ {l : List α} {m n : Nat}, l.take m = l.take n ↔ min m l.length = min n l.length
|
||||
| [], m, n => by simp [Nat.min_zero]
|
||||
| _ :: xs, 0, 0 => by simp
|
||||
| x :: xs, m + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
|
||||
| x :: xs, 0, n + 1 => by simp [Nat.zero_min, succ_min_succ]
|
||||
| x :: xs, m + 1, n + 1 => by simp [succ_min_succ, take_eq_take]; omega
|
||||
|
||||
theorem take_add (l : List α) (m n : Nat) : l.take (m + n) = l.take m ++ (l.drop m).take n := by
|
||||
suffices take (m + n) (take m l ++ drop m l) = take m l ++ take n (drop m l) by
|
||||
rw [take_append_drop] at this
|
||||
assumption
|
||||
rw [take_append_eq_append_take, take_all_of_le, append_right_inj]
|
||||
· simp only [take_eq_take, length_take, length_drop]
|
||||
omega
|
||||
apply Nat.le_trans (m := m)
|
||||
· apply length_take_le
|
||||
· apply Nat.le_add_right
|
||||
|
||||
theorem take_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.take i = []
|
||||
| _, _, rfl => take_nil
|
||||
|
||||
theorem ne_nil_of_take_ne_nil {as : List α} {i : Nat} (h: as.take i ≠ []) : as ≠ [] :=
|
||||
mt take_eq_nil_of_eq_nil h
|
||||
|
||||
theorem dropLast_eq_take (l : List α) : l.dropLast = l.take l.length.pred := by
|
||||
cases l with
|
||||
| nil => simp [dropLast]
|
||||
| cons x l =>
|
||||
induction l generalizing x with
|
||||
| nil => simp [dropLast]
|
||||
| cons hd tl hl => simp [dropLast, hl]
|
||||
|
||||
theorem dropLast_take {n : Nat} {l : List α} (h : n < l.length) :
|
||||
(l.take n).dropLast = l.take n.pred := by
|
||||
simp only [dropLast_eq_take, length_take, Nat.le_of_lt h, take_take, pred_le, Nat.min_eq_left]
|
||||
|
||||
theorem map_eq_append_split {f : α → β} {l : List α} {s₁ s₂ : List β}
|
||||
(h : map f l = s₁ ++ s₂) : ∃ l₁ l₂, l = l₁ ++ l₂ ∧ map f l₁ = s₁ ∧ map f l₂ = s₂ := by
|
||||
have := h
|
||||
rw [← take_append_drop (length s₁) l] at this ⊢
|
||||
rw [map_append] at this
|
||||
refine ⟨_, _, rfl, append_inj this ?_⟩
|
||||
rw [length_map, length_take, Nat.min_eq_left]
|
||||
rw [← length_map l f, h, length_append]
|
||||
apply Nat.le_add_right
|
||||
|
||||
/-! ### drop -/
|
||||
|
||||
@[simp]
|
||||
theorem drop_eq_nil_iff_le {l : List α} {k : Nat} : l.drop k = [] ↔ l.length ≤ k := by
|
||||
refine' ⟨fun h => _, drop_eq_nil_of_le⟩
|
||||
induction k generalizing l with
|
||||
| zero =>
|
||||
simp only [drop] at h
|
||||
simp [h]
|
||||
| succ k hk =>
|
||||
cases l
|
||||
· simp
|
||||
· simp only [drop] at h
|
||||
simpa [Nat.succ_le_succ_iff] using hk h
|
||||
|
||||
theorem drop_length_cons {l : List α} (h : l ≠ []) (a : α) :
|
||||
(a :: l).drop l.length = [l.getLast h] := by
|
||||
induction l generalizing a with
|
||||
| nil =>
|
||||
cases h rfl
|
||||
| cons y l ih =>
|
||||
simp only [drop, length]
|
||||
by_cases h₁ : l = []
|
||||
· simp [h₁]
|
||||
rw [getLast_cons' _ h₁]
|
||||
exact ih h₁ y
|
||||
|
||||
/-- Dropping the elements up to `n` in `l₁ ++ l₂` is the same as dropping the elements up to `n`
|
||||
in `l₁`, dropping the elements up to `n - l₁.length` in `l₂`, and appending them. -/
|
||||
theorem drop_append_eq_append_drop {l₁ l₂ : List α} {n : Nat} :
|
||||
drop n (l₁ ++ l₂) = drop n l₁ ++ drop (n - l₁.length) l₂ := by
|
||||
induction l₁ generalizing n
|
||||
· simp
|
||||
· cases n
|
||||
· simp [*]
|
||||
· simp only [cons_append, drop_succ_cons, length_cons, succ_eq_add_one, append_cancel_left_eq, *]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem drop_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n ≤ l₁.length) :
|
||||
(l₁ ++ l₂).drop n = l₁.drop n ++ l₂ := by
|
||||
simp [drop_append_eq_append_drop, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
|
||||
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
|
||||
up to `i` in `l₂`. -/
|
||||
@[simp]
|
||||
theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
|
||||
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
|
||||
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
|
||||
|
||||
theorem drop_sizeOf_le [SizeOf α] (l : List α) (n : Nat) : sizeOf (l.drop n) ≤ sizeOf l := by
|
||||
induction l generalizing n with
|
||||
| nil => rw [drop_nil]; apply Nat.le_refl
|
||||
| cons _ _ lih =>
|
||||
induction n with
|
||||
| zero => apply Nat.le_refl
|
||||
| succ n =>
|
||||
exact Trans.trans (lih _) (Nat.le_add_left _ _)
|
||||
|
||||
theorem lt_length_drop (L : List α) {i j : Nat} (h : i + j < L.length) : j < (L.drop i).length := by
|
||||
have A : i < L.length := Nat.lt_of_le_of_lt (Nat.le.intro rfl) h
|
||||
rw [(take_append_drop i L).symm] at h
|
||||
simpa only [Nat.le_of_lt A, Nat.min_eq_left, Nat.add_lt_add_iff_left, length_take,
|
||||
length_append] using h
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
|
||||
theorem get_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
|
||||
get L ⟨i + j, h⟩ = get (L.drop i) ⟨j, lt_length_drop L h⟩ := by
|
||||
have : i ≤ L.length := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_lt h)
|
||||
rw [get_of_eq (take_append_drop i L).symm ⟨i + j, h⟩, get_append_right'] <;>
|
||||
simp [Nat.min_eq_left this, Nat.add_sub_cancel_left, Nat.le_add_right]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
|
||||
theorem get_drop' (L : List α) {i j} :
|
||||
get (L.drop i) j = get L ⟨i + j, by
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub (length_drop i L ▸ j.2)⟩ := by
|
||||
rw [get_drop]
|
||||
|
||||
@[simp]
|
||||
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
|
||||
ext
|
||||
simp only [get?_eq_some, get_drop', Option.mem_def]
|
||||
constructor <;> intro ⟨h, ha⟩
|
||||
· exact ⟨_, ha⟩
|
||||
· refine ⟨?_, ha⟩
|
||||
rw [length_drop]
|
||||
rw [Nat.add_comm] at h
|
||||
apply Nat.lt_sub_of_add_lt h
|
||||
|
||||
@[simp] theorem drop_drop (n : Nat) : ∀ (m) (l : List α), drop n (drop m l) = drop (n + m) l
|
||||
| m, [] => by simp
|
||||
| 0, l => by simp
|
||||
| m + 1, a :: l =>
|
||||
calc
|
||||
drop n (drop (m + 1) (a :: l)) = drop n (drop m l) := rfl
|
||||
_ = drop (n + m) l := drop_drop n m l
|
||||
_ = drop (n + (m + 1)) (a :: l) := rfl
|
||||
|
||||
theorem take_drop : ∀ (m n : Nat) (l : List α), take n (drop m l) = drop m (take (m + n) l)
|
||||
| 0, _, _ => by simp
|
||||
| _, _, [] => by simp
|
||||
| _+1, _, _ :: _ => by simpa [Nat.succ_add, take_succ_cons, drop_succ_cons] using take_drop ..
|
||||
|
||||
theorem drop_take : ∀ (m n : Nat) (l : List α), drop n (take m l) = take (m - n) (drop n l)
|
||||
| 0, _, _ => by simp
|
||||
| _, 0, _ => by simp
|
||||
| _, _, [] => by simp
|
||||
| m+1, n+1, h :: t => by
|
||||
simp [take_succ_cons, drop_succ_cons, drop_take m n t]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem map_drop (f : α → β) :
|
||||
∀ (L : List α) (i : Nat), (L.drop i).map f = (L.map f).drop i
|
||||
| [], i => by simp
|
||||
| L, 0 => by simp
|
||||
| h :: t, n + 1 => by
|
||||
dsimp
|
||||
rw [map_drop f t]
|
||||
|
||||
theorem reverse_take {α} {xs : List α} (n : Nat) (h : n ≤ xs.length) :
|
||||
xs.reverse.take n = (xs.drop (xs.length - n)).reverse := by
|
||||
induction xs generalizing n <;>
|
||||
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
|
||||
next xs_hd xs_tl xs_ih =>
|
||||
cases Nat.lt_or_eq_of_le h with
|
||||
| inl h' =>
|
||||
have h' := Nat.le_of_succ_le_succ h'
|
||||
rw [take_append_of_le_length, xs_ih _ h']
|
||||
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
|
||||
· rwa [succ_eq_add_one, Nat.sub_add_comm]
|
||||
· rwa [length_reverse]
|
||||
| inr h' =>
|
||||
subst h'
|
||||
rw [length, Nat.sub_self, drop]
|
||||
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
|
||||
rw [this, take_length, reverse_cons]
|
||||
rw [length_append, length_reverse]
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem get_cons_drop : ∀ (l : List α) i, get l i :: drop (i + 1) l = drop i l
|
||||
| _::_, ⟨0, _⟩ => rfl
|
||||
| _::_, ⟨i+1, _⟩ => get_cons_drop _ ⟨i, _⟩
|
||||
|
||||
theorem drop_eq_get_cons {n} {l : List α} (h) : drop n l = get l ⟨n, h⟩ :: drop (n + 1) l :=
|
||||
(get_cons_drop _ ⟨n, h⟩).symm
|
||||
|
||||
theorem drop_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.drop i = []
|
||||
| _, _, rfl => drop_nil
|
||||
|
||||
theorem ne_nil_of_drop_ne_nil {as : List α} {i : Nat} (h: as.drop i ≠ []) : as ≠ [] :=
|
||||
mt drop_eq_nil_of_eq_nil h
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
@[simp] theorem length_zipWith (f : α → β → γ) (l₁ l₂) :
|
||||
length (zipWith f l₁ l₂) = min (length l₁) (length l₂) := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;>
|
||||
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
@[simp] theorem length_zip (l₁ : List α) (l₂ : List β) :
|
||||
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
|
||||
simp [zip]
|
||||
|
||||
end List
|
||||
@@ -137,6 +137,9 @@ instance : LawfulBEq Nat where
|
||||
@[simp] protected theorem zero_add : ∀ (n : Nat), 0 + n = n
|
||||
| 0 => rfl
|
||||
| n+1 => congrArg succ (Nat.zero_add n)
|
||||
instance : Std.LawfulIdentity (α := Nat) (· + ·) 0 where
|
||||
left_id := Nat.zero_add
|
||||
right_id := Nat.add_zero
|
||||
|
||||
theorem succ_add : ∀ (n m : Nat), (succ n) + m = succ (n + m)
|
||||
| _, 0 => rfl
|
||||
@@ -160,10 +163,12 @@ protected theorem add_comm : ∀ (n m : Nat), n + m = m + n
|
||||
have : succ (n + m) = succ (m + n) := by apply congrArg; apply Nat.add_comm
|
||||
rw [succ_add m n]
|
||||
apply this
|
||||
instance : Std.Commutative (α := Nat) (· + ·) := ⟨Nat.add_comm⟩
|
||||
|
||||
protected theorem add_assoc : ∀ (n m k : Nat), (n + m) + k = n + (m + k)
|
||||
| _, _, 0 => rfl
|
||||
| n, m, succ k => congrArg succ (Nat.add_assoc n m k)
|
||||
instance : Std.Associative (α := Nat) (· + ·) := ⟨Nat.add_assoc⟩
|
||||
|
||||
protected theorem add_left_comm (n m k : Nat) : n + (m + k) = m + (n + k) := by
|
||||
rw [← Nat.add_assoc, Nat.add_comm n m, Nat.add_assoc]
|
||||
@@ -207,12 +212,16 @@ theorem succ_mul (n m : Nat) : (succ n) * m = (n * m) + m := by
|
||||
protected theorem mul_comm : ∀ (n m : Nat), n * m = m * n
|
||||
| n, 0 => (Nat.zero_mul n).symm ▸ (Nat.mul_zero n).symm ▸ rfl
|
||||
| n, succ m => (mul_succ n m).symm ▸ (succ_mul m n).symm ▸ (Nat.mul_comm n m).symm ▸ rfl
|
||||
instance : Std.Commutative (α := Nat) (· * ·) := ⟨Nat.mul_comm⟩
|
||||
|
||||
@[simp] protected theorem mul_one : ∀ (n : Nat), n * 1 = n :=
|
||||
Nat.zero_add
|
||||
|
||||
@[simp] protected theorem one_mul (n : Nat) : 1 * n = n :=
|
||||
Nat.mul_comm n 1 ▸ Nat.mul_one n
|
||||
instance : Std.LawfulIdentity (α := Nat) (· * ·) 1 where
|
||||
left_id := Nat.one_mul
|
||||
right_id := Nat.mul_one
|
||||
|
||||
protected theorem left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := by
|
||||
induction n with
|
||||
@@ -231,6 +240,7 @@ protected theorem add_mul (n m k : Nat) : (n + m) * k = n * k + m * k :=
|
||||
protected theorem mul_assoc : ∀ (n m k : Nat), (n * m) * k = n * (m * k)
|
||||
| n, m, 0 => rfl
|
||||
| n, m, succ k => by simp [mul_succ, Nat.mul_assoc n m k, Nat.left_distrib]
|
||||
instance : Std.Associative (α := Nat) (· * ·) := ⟨Nat.mul_assoc⟩
|
||||
|
||||
protected theorem mul_left_comm (n m k : Nat) : n * (m * k) = m * (n * k) := by
|
||||
rw [← Nat.mul_assoc, Nat.mul_comm n m, Nat.mul_assoc]
|
||||
|
||||
@@ -402,12 +402,12 @@ theorem and_pow_two_identity {x : Nat} (lt : x < 2^n) : x &&& 2^n-1 = x := by
|
||||
|
||||
/-! ### lor -/
|
||||
|
||||
@[simp] theorem or_zero (x : Nat) : 0 ||| x = x := by
|
||||
@[simp] theorem zero_or (x : Nat) : 0 ||| x = x := by
|
||||
simp only [HOr.hOr, OrOp.or, lor]
|
||||
unfold bitwise
|
||||
simp [@eq_comm _ 0]
|
||||
|
||||
@[simp] theorem zero_or (x : Nat) : x ||| 0 = x := by
|
||||
@[simp] theorem or_zero (x : Nat) : x ||| 0 = x := by
|
||||
simp only [HOr.hOr, OrOp.or, lor]
|
||||
unfold bitwise
|
||||
simp [@eq_comm _ 0]
|
||||
|
||||
@@ -28,7 +28,7 @@ protected def div (x y : @& Nat) : Nat :=
|
||||
0
|
||||
decreasing_by apply div_rec_lemma; assumption
|
||||
|
||||
instance : Div Nat := ⟨Nat.div⟩
|
||||
instance instDiv : Div Nat := ⟨Nat.div⟩
|
||||
|
||||
theorem div_eq (x y : Nat) : x / y = if 0 < y ∧ y ≤ x then (x - y) / y + 1 else 0 := by
|
||||
show Nat.div x y = _
|
||||
@@ -82,22 +82,28 @@ decreasing_by apply div_rec_lemma; assumption
|
||||
|
||||
@[extern "lean_nat_mod"]
|
||||
protected def mod : @& Nat → @& Nat → Nat
|
||||
/- This case is not needed mathematically as the case below is equal to it; however, it makes
|
||||
`0 % n = 0` true definitionally rather than just propositionally.
|
||||
This property is desirable for `Fin n`, as it means `(ofNat 0 : Fin n).val = 0` by definition.
|
||||
Primarily, this is valuable because mathlib in Lean3 assumed this was true definitionally, and so
|
||||
keeping this definitional equality makes mathlib easier to port to mathlib4. -/
|
||||
/- These four cases are not needed mathematically, they are just special cases of the
|
||||
general case. However, it makes `0 % n = 0` etc. true definitionally rather than just
|
||||
propositionally.
|
||||
This property is desirable for `Fin n` literals, as it means `(ofNat 0 : Fin n).val = 0` by
|
||||
definition. This was true in lean3 and it simplified things for mathlib if it remains true. -/
|
||||
| 0, _ => 0
|
||||
| x@(_ + 1), y => Nat.modCore x y
|
||||
| 1, 0 => 0
|
||||
| 1, 1 => 0
|
||||
| 1, (_+2) => 1
|
||||
| x@(_ + 2), y => Nat.modCore x y
|
||||
|
||||
instance : Mod Nat := ⟨Nat.mod⟩
|
||||
instance instMod : Mod Nat := ⟨Nat.mod⟩
|
||||
|
||||
protected theorem modCore_eq_mod (x y : Nat) : Nat.modCore x y = x % y := by
|
||||
cases x with
|
||||
| zero =>
|
||||
match x, y with
|
||||
| 0, y =>
|
||||
rw [Nat.modCore]
|
||||
exact if_neg fun ⟨hlt, hle⟩ => Nat.lt_irrefl _ (Nat.lt_of_lt_of_le hlt hle)
|
||||
| succ x => rfl
|
||||
| 1, 0 => rw [Nat.modCore]; rfl
|
||||
| 1, 1 => rw [Nat.modCore, Nat.modCore]; rfl
|
||||
| 1, (_+2) => rw [Nat.modCore]; rfl
|
||||
| (_ + 2), _ => rfl
|
||||
|
||||
theorem mod_eq (x y : Nat) : x % y = if 0 < y ∧ y ≤ x then (x - y) % y else x := by
|
||||
rw [←Nat.modCore_eq_mod, ←Nat.modCore_eq_mod, Nat.modCore]
|
||||
|
||||
@@ -10,6 +10,24 @@ import Init.RCases
|
||||
|
||||
namespace Nat
|
||||
|
||||
/--
|
||||
Computes the greatest common divisor of two natural numbers.
|
||||
|
||||
This reference implementation via the Euclidean algorithm
|
||||
is overridden in both the kernel and the compiler to efficiently
|
||||
evaluate using the "bignum" representation (see `Nat`).
|
||||
The definition provided here is the logical model
|
||||
(and it is soundness-critical that they coincide).
|
||||
|
||||
The GCD of two natural numbers is the largest natural number
|
||||
that divides both arguments.
|
||||
In particular, the GCD of a number and `0` is the number itself:
|
||||
```
|
||||
example : Nat.gcd 10 15 = 5 := rfl
|
||||
example : Nat.gcd 0 5 = 5 := rfl
|
||||
example : Nat.gcd 7 0 = 7 := rfl
|
||||
```
|
||||
-/
|
||||
@[extern "lean_nat_gcd"]
|
||||
def gcd (m n : @& Nat) : Nat :=
|
||||
if m = 0 then
|
||||
@@ -36,9 +54,13 @@ theorem gcd_succ (x y : Nat) : gcd (succ x) y = gcd (y % succ x) (succ x) :=
|
||||
-- `simp [gcd_succ]` produces an invalid term unless `gcd_succ` is proved with `id rfl` instead
|
||||
rw [gcd_succ]
|
||||
exact gcd_zero_left _
|
||||
instance : Std.LawfulIdentity gcd 0 where
|
||||
left_id := gcd_zero_left
|
||||
right_id := gcd_zero_right
|
||||
|
||||
@[simp] theorem gcd_self (n : Nat) : gcd n n = n := by
|
||||
cases n <;> simp [gcd_succ]
|
||||
instance : Std.IdempotentOp gcd := ⟨gcd_self⟩
|
||||
|
||||
theorem gcd_rec (m n : Nat) : gcd m n = gcd (n % m) m :=
|
||||
match m with
|
||||
@@ -79,6 +101,7 @@ theorem gcd_comm (m n : Nat) : gcd m n = gcd n m :=
|
||||
Nat.dvd_antisymm
|
||||
(dvd_gcd (gcd_dvd_right m n) (gcd_dvd_left m n))
|
||||
(dvd_gcd (gcd_dvd_right n m) (gcd_dvd_left n m))
|
||||
instance : Std.Commutative gcd := ⟨gcd_comm⟩
|
||||
|
||||
theorem gcd_eq_left_iff_dvd : m ∣ n ↔ gcd m n = m :=
|
||||
⟨fun h => by rw [gcd_rec, mod_eq_zero_of_dvd h, gcd_zero_left],
|
||||
|
||||
@@ -14,6 +14,7 @@ def lcm (m n : Nat) : Nat := m * n / gcd m n
|
||||
|
||||
theorem lcm_comm (m n : Nat) : lcm m n = lcm n m := by
|
||||
rw [lcm, lcm, Nat.mul_comm n m, gcd_comm n m]
|
||||
instance : Std.Commutative lcm := ⟨lcm_comm⟩
|
||||
|
||||
@[simp] theorem lcm_zero_left (m : Nat) : lcm 0 m = 0 := by simp [lcm]
|
||||
|
||||
@@ -22,11 +23,15 @@ theorem lcm_comm (m n : Nat) : lcm m n = lcm n m := by
|
||||
@[simp] theorem lcm_one_left (m : Nat) : lcm 1 m = m := by simp [lcm]
|
||||
|
||||
@[simp] theorem lcm_one_right (m : Nat) : lcm m 1 = m := by simp [lcm]
|
||||
instance : Std.LawfulIdentity lcm 1 where
|
||||
left_id := lcm_one_left
|
||||
right_id := lcm_one_right
|
||||
|
||||
@[simp] theorem lcm_self (m : Nat) : lcm m m = m := by
|
||||
match eq_zero_or_pos m with
|
||||
| .inl h => rw [h, lcm_zero_left]
|
||||
| .inr h => simp [lcm, Nat.mul_div_cancel _ h]
|
||||
instance : Std.IdempotentOp lcm := ⟨lcm_self⟩
|
||||
|
||||
theorem dvd_lcm_left (m n : Nat) : m ∣ lcm m n :=
|
||||
⟨n / gcd m n, by rw [← Nat.mul_div_assoc m (Nat.gcd_dvd_right m n)]; rfl⟩
|
||||
@@ -54,6 +59,7 @@ Nat.dvd_antisymm
|
||||
(Nat.dvd_trans (dvd_lcm_left m n) (dvd_lcm_left (lcm m n) k))
|
||||
(lcm_dvd (Nat.dvd_trans (dvd_lcm_right m n) (dvd_lcm_left (lcm m n) k))
|
||||
(dvd_lcm_right (lcm m n) k)))
|
||||
instance : Std.Associative lcm := ⟨lcm_assoc⟩
|
||||
|
||||
theorem lcm_ne_zero (hm : m ≠ 0) (hn : n ≠ 0) : lcm m n ≠ 0 := by
|
||||
intro h
|
||||
|
||||
@@ -200,6 +200,7 @@ theorem succ_min_succ (x y) : min (succ x) (succ y) = succ (min x y) := by
|
||||
| inr h => rw [Nat.min_eq_right h, Nat.min_eq_right (Nat.succ_le_succ h)]
|
||||
|
||||
@[simp] protected theorem min_self (a : Nat) : min a a = a := Nat.min_eq_left (Nat.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Nat) min := ⟨Nat.min_self⟩
|
||||
|
||||
@[simp] protected theorem zero_min (a) : min 0 a = 0 := Nat.min_eq_left (Nat.zero_le _)
|
||||
|
||||
@@ -210,6 +211,7 @@ protected theorem min_assoc : ∀ (a b c : Nat), min (min a b) c = min a (min b
|
||||
| _, 0, _ => by rw [Nat.zero_min, Nat.min_zero, Nat.zero_min]
|
||||
| _, _, 0 => by rw [Nat.min_zero, Nat.min_zero, Nat.min_zero]
|
||||
| _+1, _+1, _+1 => by simp only [Nat.succ_min_succ]; exact congrArg succ <| Nat.min_assoc ..
|
||||
instance : Std.Associative (α := Nat) min := ⟨Nat.min_assoc⟩
|
||||
|
||||
protected theorem sub_sub_eq_min : ∀ (a b : Nat), a - (a - b) = min a b
|
||||
| 0, _ => by rw [Nat.zero_sub, Nat.zero_min]
|
||||
@@ -249,16 +251,21 @@ protected theorem max_lt {a b c : Nat} : max a b < c ↔ a < c ∧ b < c := by
|
||||
rw [← Nat.succ_le, ← Nat.succ_max_succ a b]; exact Nat.max_le
|
||||
|
||||
@[simp] protected theorem max_self (a : Nat) : max a a = a := Nat.max_eq_right (Nat.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Nat) max := ⟨Nat.max_self⟩
|
||||
|
||||
@[simp] protected theorem zero_max (a) : max 0 a = a := Nat.max_eq_right (Nat.zero_le _)
|
||||
|
||||
@[simp] protected theorem max_zero (a) : max a 0 = a := Nat.max_eq_left (Nat.zero_le _)
|
||||
instance : Std.LawfulIdentity (α := Nat) max 0 where
|
||||
left_id := Nat.zero_max
|
||||
right_id := Nat.max_zero
|
||||
|
||||
protected theorem max_assoc : ∀ (a b c : Nat), max (max a b) c = max a (max b c)
|
||||
| 0, _, _ => by rw [Nat.zero_max, Nat.zero_max]
|
||||
| _, 0, _ => by rw [Nat.zero_max, Nat.max_zero]
|
||||
| _, _, 0 => by rw [Nat.max_zero, Nat.max_zero]
|
||||
| _+1, _+1, _+1 => by simp only [Nat.succ_max_succ]; exact congrArg succ <| Nat.max_assoc ..
|
||||
instance : Std.Associative (α := Nat) max := ⟨Nat.max_assoc⟩
|
||||
|
||||
protected theorem sub_add_eq_max (a b : Nat) : a - b + b = max a b := by
|
||||
match Nat.le_total a b with
|
||||
|
||||
@@ -17,6 +17,7 @@ protected theorem min_comm (a b : Nat) : min a b = min b a := by
|
||||
| .inl h => simp [Nat.min_def, h, Nat.le_of_lt, Nat.not_le_of_lt]
|
||||
| .inr (.inl h) => simp [Nat.min_def, h]
|
||||
| .inr (.inr h) => simp [Nat.min_def, h, Nat.le_of_lt, Nat.not_le_of_lt]
|
||||
instance : Std.Commutative (α := Nat) min := ⟨Nat.min_comm⟩
|
||||
|
||||
protected theorem min_le_right (a b : Nat) : min a b ≤ b := by
|
||||
by_cases (a <= b) <;> simp [Nat.min_def, *]
|
||||
@@ -47,6 +48,7 @@ protected theorem max_comm (a b : Nat) : max a b = max b a := by
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Nat.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Nat.le_total ..
|
||||
instance : Std.Commutative (α := Nat) max := ⟨Nat.max_comm⟩
|
||||
|
||||
protected theorem le_max_left ( a b : Nat) : a ≤ max a b := by
|
||||
by_cases (a <= b) <;> simp [Nat.max_def, *]
|
||||
|
||||
@@ -13,29 +13,36 @@ namespace Option
|
||||
deriving instance DecidableEq for Option
|
||||
deriving instance BEq for Option
|
||||
|
||||
def toMonad [Monad m] [Alternative m] : Option α → m α
|
||||
/-- Lifts an optional value to any `Alternative`, sending `none` to `failure`. -/
|
||||
def getM [Alternative m] : Option α → m α
|
||||
| none => failure
|
||||
| some a => pure a
|
||||
|
||||
@[inline] def toBool : Option α → Bool
|
||||
| some _ => true
|
||||
| none => false
|
||||
@[deprecated getM] def toMonad [Monad m] [Alternative m] : Option α → m α :=
|
||||
getM
|
||||
|
||||
/-- Returns `true` on `some x` and `false` on `none`. -/
|
||||
@[inline] def isSome : Option α → Bool
|
||||
| some _ => true
|
||||
| none => false
|
||||
|
||||
@[deprecated isSome, inline] def toBool : Option α → Bool := isSome
|
||||
|
||||
/-- Returns `true` on `none` and `false` on `some x`. -/
|
||||
@[inline] def isNone : Option α → Bool
|
||||
| some _ => false
|
||||
| none => true
|
||||
|
||||
/--
|
||||
`x?.isEqSome y` is equivalent to `x? == some y`, but avoids an allocation.
|
||||
-/
|
||||
@[inline] def isEqSome [BEq α] : Option α → α → Bool
|
||||
| some a, b => a == b
|
||||
| none, _ => false
|
||||
|
||||
@[inline] protected def bind : Option α → (α → Option β) → Option β
|
||||
| none, _ => none
|
||||
| some a, b => b a
|
||||
| some a, f => f a
|
||||
|
||||
/-- Runs `f` on `o`'s value, if any, and returns its result, or else returns `none`. -/
|
||||
@[inline] protected def bindM [Monad m] (f : α → m (Option β)) (o : Option α) : m (Option β) := do
|
||||
@@ -44,6 +51,10 @@ def toMonad [Monad m] [Alternative m] : Option α → m α
|
||||
else
|
||||
return none
|
||||
|
||||
/--
|
||||
Runs a monadic function `f` on an optional value.
|
||||
If the optional value is `none` the function is not called.
|
||||
-/
|
||||
@[inline] protected def mapM [Monad m] (f : α → m β) (o : Option α) : m (Option β) := do
|
||||
if let some a := o then
|
||||
return some (← f a)
|
||||
@@ -53,18 +64,24 @@ def toMonad [Monad m] [Alternative m] : Option α → m α
|
||||
theorem map_id : (Option.map id : Option α → Option α) = id :=
|
||||
funext (fun o => match o with | none => rfl | some _ => rfl)
|
||||
|
||||
/-- Keeps an optional value only if it satisfies the predicate `p`. -/
|
||||
@[always_inline, inline] protected def filter (p : α → Bool) : Option α → Option α
|
||||
| some a => if p a then some a else none
|
||||
| none => none
|
||||
|
||||
/-- Checks that an optional value satisfies a predicate `p` or is `none`. -/
|
||||
@[always_inline, inline] protected def all (p : α → Bool) : Option α → Bool
|
||||
| some a => p a
|
||||
| none => true
|
||||
|
||||
/-- Checks that an optional value is not `none` and the value satisfies a predicate `p`. -/
|
||||
@[always_inline, inline] protected def any (p : α → Bool) : Option α → Bool
|
||||
| some a => p a
|
||||
| none => false
|
||||
|
||||
/--
|
||||
Implementation of `OrElse`'s `<|>` syntax for `Option`.
|
||||
-/
|
||||
@[always_inline, macro_inline] protected def orElse : Option α → (Unit → Option α) → Option α
|
||||
| some a, _ => some a
|
||||
| none, b => b ()
|
||||
|
||||
@@ -114,7 +114,18 @@ by `cmp₂` to break the tie.
|
||||
@[inline] def compareLex (cmp₁ cmp₂ : α → β → Ordering) (a : α) (b : β) : Ordering :=
|
||||
(cmp₁ a b).then (cmp₂ a b)
|
||||
|
||||
/--
|
||||
`Ord α` provides a computable total order on `α`, in terms of the
|
||||
`compare : α → α → Ordering` function.
|
||||
|
||||
Typically instances will be transitive, reflexive, and antisymmetric,
|
||||
but this is not enforced by the typeclass.
|
||||
|
||||
There is a derive handler, so appending `deriving Ord` to an inductive type or structure
|
||||
will attempt to create an `Ord` instance.
|
||||
-/
|
||||
class Ord (α : Type u) where
|
||||
/-- Compare two elements in `α` using the comparator contained in an `[Ord α]` instance. -/
|
||||
compare : α → α → Ordering
|
||||
|
||||
export Ord (compare)
|
||||
@@ -171,15 +182,13 @@ instance [Ord α] : Ord (Option α) where
|
||||
|
||||
/-- The lexicographic order on pairs. -/
|
||||
def lexOrd [Ord α] [Ord β] : Ord (α × β) where
|
||||
compare p1 p2 := match compare p1.1 p2.1 with
|
||||
| .eq => compare p1.2 p2.2
|
||||
| o => o
|
||||
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
|
||||
|
||||
def ltOfOrd [Ord α] : LT α where
|
||||
lt a b := compare a b == Ordering.lt
|
||||
lt a b := compare a b = Ordering.lt
|
||||
|
||||
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) :=
|
||||
inferInstanceAs (DecidableRel (fun a b => compare a b == Ordering.lt))
|
||||
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
|
||||
|
||||
def leOfOrd [Ord α] : LE α where
|
||||
le a b := (compare a b).isLE
|
||||
|
||||
@@ -13,11 +13,24 @@ open Sum Subtype Nat
|
||||
|
||||
open Std
|
||||
|
||||
/--
|
||||
A typeclass that specifies the standard way of turning values of some type into `Format`.
|
||||
|
||||
When rendered this `Format` should be as close as possible to something that can be parsed as the
|
||||
input value.
|
||||
-/
|
||||
class Repr (α : Type u) where
|
||||
/--
|
||||
Turn a value of type `α` into `Format` at a given precedence. The precedence value can be used
|
||||
to avoid parentheses if they are not necessary.
|
||||
-/
|
||||
reprPrec : α → Nat → Format
|
||||
|
||||
export Repr (reprPrec)
|
||||
|
||||
/--
|
||||
Turn `a` into `Format` using its `Repr` instance. The precedence level is initially set to 0.
|
||||
-/
|
||||
abbrev repr [Repr α] (a : α) : Format :=
|
||||
reprPrec a 0
|
||||
|
||||
@@ -103,6 +116,11 @@ instance {p : α → Prop} [Repr α] : Repr (Subtype p) where
|
||||
|
||||
namespace Nat
|
||||
|
||||
/-
|
||||
We have pure functions for calculating the decimal representation of a `Nat` (`toDigits`), but also
|
||||
a fast variant that handles small numbers (`USize`) via C code (`lean_string_of_usize`).
|
||||
-/
|
||||
|
||||
def digitChar (n : Nat) : Char :=
|
||||
if n = 0 then '0' else
|
||||
if n = 1 then '1' else
|
||||
@@ -133,6 +151,20 @@ def toDigitsCore (base : Nat) : Nat → Nat → List Char → List Char
|
||||
def toDigits (base : Nat) (n : Nat) : List Char :=
|
||||
toDigitsCore base (n+1) n []
|
||||
|
||||
@[extern "lean_string_of_usize"]
|
||||
protected def _root_.USize.repr (n : @& USize) : String :=
|
||||
(toDigits 10 n.toNat).asString
|
||||
|
||||
/-- We statically allocate and memoize reprs for small natural numbers. -/
|
||||
private def reprArray : Array String := Id.run do
|
||||
List.range 128 |>.map (·.toUSize.repr) |> Array.mk
|
||||
|
||||
private def reprFast (n : Nat) : String :=
|
||||
if h : n < 128 then Nat.reprArray.get ⟨n, h⟩ else
|
||||
if h : n < USize.size then (USize.ofNatCore n h).repr
|
||||
else (toDigits 10 n).asString
|
||||
|
||||
@[implemented_by reprFast]
|
||||
protected def repr (n : Nat) : String :=
|
||||
(toDigits 10 n).asString
|
||||
|
||||
@@ -162,6 +194,32 @@ def toSuperDigits (n : Nat) : List Char :=
|
||||
def toSuperscriptString (n : Nat) : String :=
|
||||
(toSuperDigits n).asString
|
||||
|
||||
def subDigitChar (n : Nat) : Char :=
|
||||
if n = 0 then '₀' else
|
||||
if n = 1 then '₁' else
|
||||
if n = 2 then '₂' else
|
||||
if n = 3 then '₃' else
|
||||
if n = 4 then '₄' else
|
||||
if n = 5 then '₅' else
|
||||
if n = 6 then '₆' else
|
||||
if n = 7 then '₇' else
|
||||
if n = 8 then '₈' else
|
||||
if n = 9 then '₉' else
|
||||
'*'
|
||||
|
||||
partial def toSubDigitsAux : Nat → List Char → List Char
|
||||
| n, ds =>
|
||||
let d := subDigitChar <| n % 10;
|
||||
let n' := n / 10;
|
||||
if n' = 0 then d::ds
|
||||
else toSubDigitsAux n' (d::ds)
|
||||
|
||||
def toSubDigits (n : Nat) : List Char :=
|
||||
toSubDigitsAux n []
|
||||
|
||||
def toSubscriptString (n : Nat) : String :=
|
||||
(toSubDigits n).asString
|
||||
|
||||
end Nat
|
||||
|
||||
instance : Repr Nat where
|
||||
|
||||
@@ -94,7 +94,8 @@ instance : Stream (Subarray α) α where
|
||||
next? s :=
|
||||
if h : s.start < s.stop then
|
||||
have : s.start + 1 ≤ s.stop := Nat.succ_le_of_lt h
|
||||
some (s.as.get ⟨s.start, Nat.lt_of_lt_of_le h s.h₂⟩, { s with start := s.start + 1, h₁ := this })
|
||||
some (s.array.get ⟨s.start, Nat.lt_of_lt_of_le h s.stop_le_array_size⟩,
|
||||
{ s with start := s.start + 1, start_le_stop := this })
|
||||
else
|
||||
none
|
||||
|
||||
|
||||
@@ -24,34 +24,80 @@ instance : LT String :=
|
||||
instance decLt (s₁ s₂ : @& String) : Decidable (s₁ < s₂) :=
|
||||
List.hasDecidableLt s₁.data s₂.data
|
||||
|
||||
/--
|
||||
Returns the length of a string in Unicode code points.
|
||||
|
||||
Examples:
|
||||
* `"".length = 0`
|
||||
* `"abc".length = 3`
|
||||
* `"L∃∀N".length = 4`
|
||||
-/
|
||||
@[extern "lean_string_length"]
|
||||
def length : (@& String) → Nat
|
||||
| ⟨s⟩ => s.length
|
||||
|
||||
/-- The internal implementation uses dynamic arrays and will perform destructive updates
|
||||
if the String is not shared. -/
|
||||
/--
|
||||
Pushes a character onto the end of a string.
|
||||
|
||||
The internal implementation uses dynamic arrays and will perform destructive updates
|
||||
if the string is not shared.
|
||||
|
||||
Example: `"abc".push 'd' = "abcd"`
|
||||
-/
|
||||
@[extern "lean_string_push"]
|
||||
def push : String → Char → String
|
||||
| ⟨s⟩, c => ⟨s ++ [c]⟩
|
||||
|
||||
/-- The internal implementation uses dynamic arrays and will perform destructive updates
|
||||
if the String is not shared. -/
|
||||
/--
|
||||
Appends two strings.
|
||||
|
||||
The internal implementation uses dynamic arrays and will perform destructive updates
|
||||
if the string is not shared.
|
||||
|
||||
Example: `"abc".append "def" = "abcdef"`
|
||||
-/
|
||||
@[extern "lean_string_append"]
|
||||
def append : String → (@& String) → String
|
||||
| ⟨a⟩, ⟨b⟩ => ⟨a ++ b⟩
|
||||
|
||||
/-- O(n) in the runtime, where n is the length of the String -/
|
||||
/--
|
||||
Converts a string to a list of characters.
|
||||
|
||||
Even though the logical model of strings is as a structure that wraps a list of characters,
|
||||
this operation takes time and space linear in the length of the string, because the compiler
|
||||
uses an optimized representation as dynamic arrays.
|
||||
|
||||
Example: `"abc".toList = ['a', 'b', 'c']`
|
||||
-/
|
||||
def toList (s : String) : List Char :=
|
||||
s.data
|
||||
|
||||
/-- Returns true if `p` is a valid UTF-8 position in the string `s`, meaning that `p ≤ s.endPos`
|
||||
and `p` lies on a UTF-8 character boundary. This has an O(1) implementation in the runtime. -/
|
||||
@[extern "lean_string_is_valid_pos"]
|
||||
def Pos.isValid (s : @&String) (p : @& Pos) : Bool :=
|
||||
go s.data 0
|
||||
where
|
||||
go : List Char → Pos → Bool
|
||||
| [], i => i = p
|
||||
| c::cs, i => if i = p then true else go cs (i + c)
|
||||
|
||||
def utf8GetAux : List Char → Pos → Pos → Char
|
||||
| [], _, _ => default
|
||||
| c::cs, i, p => if i = p then c else utf8GetAux cs (i + c) p
|
||||
|
||||
/--
|
||||
Return character at position `p`. If `p` is not a valid position
|
||||
returns `(default : Char)`.
|
||||
See `utf8GetAux` for the reference implementation.
|
||||
Returns the character at position `p` of a string. If `p` is not a valid position,
|
||||
returns `(default : Char)`.
|
||||
|
||||
See `utf8GetAux` for the reference implementation.
|
||||
|
||||
Examples:
|
||||
* `"abc".get ⟨1⟩ = 'b'`
|
||||
* `"abc".get ⟨3⟩ = (default : Char) = 'A'`
|
||||
|
||||
Positions can also be invalid if a byte index points into the middle of a multi-byte UTF-8
|
||||
character. For example,`"L∃∀N".get ⟨2⟩ = (default : Char) = 'A'`.
|
||||
-/
|
||||
@[extern "lean_string_utf8_get"]
|
||||
def get (s : @& String) (p : @& Pos) : Char :=
|
||||
@@ -62,12 +108,30 @@ def utf8GetAux? : List Char → Pos → Pos → Option Char
|
||||
| [], _, _ => none
|
||||
| c::cs, i, p => if i = p then c else utf8GetAux? cs (i + c) p
|
||||
|
||||
/--
|
||||
Returns the character at position `p`. If `p` is not a valid position, returns `none`.
|
||||
|
||||
Examples:
|
||||
* `"abc".get? ⟨1⟩ = some 'b'`
|
||||
* `"abc".get? ⟨3⟩ = none`
|
||||
|
||||
Positions can also be invalid if a byte index points into the middle of a multi-byte UTF-8
|
||||
character. For example, `"L∃∀N".get? ⟨2⟩ = none`
|
||||
-/
|
||||
@[extern "lean_string_utf8_get_opt"]
|
||||
def get? : (@& String) → (@& Pos) → Option Char
|
||||
| ⟨s⟩, p => utf8GetAux? s 0 p
|
||||
|
||||
/--
|
||||
Similar to `get`, but produces a panic error message if `p` is not a valid `String.Pos`.
|
||||
Returns the character at position `p` of a string. If `p` is not a valid position,
|
||||
returns `(default : Char)` and produces a panic error message.
|
||||
|
||||
Examples:
|
||||
* `"abc".get! ⟨1⟩ = 'b'`
|
||||
* `"abc".get! ⟨3⟩` panics
|
||||
|
||||
Positions can also be invalid if a byte index points into the middle of a multi-byte UTF-8 character. For example,
|
||||
`"L∃∀N".get! ⟨2⟩` panics.
|
||||
-/
|
||||
@[extern "lean_string_utf8_get_bang"]
|
||||
def get! (s : @& String) (p : @& Pos) : Char :=
|
||||
@@ -79,13 +143,48 @@ def utf8SetAux (c' : Char) : List Char → Pos → Pos → List Char
|
||||
| c::cs, i, p =>
|
||||
if i = p then (c'::cs) else c::(utf8SetAux c' cs (i + c) p)
|
||||
|
||||
/--
|
||||
Replaces the character at a specified position in a string with a new character. If the position
|
||||
is invalid, the string is returned unchanged.
|
||||
|
||||
If both the replacement character and the replaced character are ASCII characters and the string
|
||||
is not shared, destructive updates are used.
|
||||
|
||||
Examples:
|
||||
* `"abc".set ⟨1⟩ 'B' = "aBc"`
|
||||
* `"abc".set ⟨3⟩ 'D' = "abc"`
|
||||
* `"L∃∀N".set ⟨4⟩ 'X' = "L∃XN"`
|
||||
|
||||
Because `'∃'` is a multi-byte character, the byte index `2` in `L∃∀N` is an invalid position,
|
||||
so `"L∃∀N".set ⟨2⟩ 'X' = "L∃∀N"`.
|
||||
-/
|
||||
@[extern "lean_string_utf8_set"]
|
||||
def set : String → (@& Pos) → Char → String
|
||||
| ⟨s⟩, i, c => ⟨utf8SetAux c s 0 i⟩
|
||||
|
||||
/--
|
||||
Replaces the character at position `p` in the string `s` with the result of applying `f` to that character.
|
||||
If `p` is an invalid position, the string is returned unchanged.
|
||||
|
||||
Examples:
|
||||
* `abc.modify ⟨1⟩ Char.toUpper = "aBc"`
|
||||
* `abc.modify ⟨3⟩ Char.toUpper = "abc"`
|
||||
-/
|
||||
def modify (s : String) (i : Pos) (f : Char → Char) : String :=
|
||||
s.set i <| f <| s.get i
|
||||
|
||||
/--
|
||||
Returns the next position in a string after position `p`. If `p` is not a valid position or `p = s.endPos`,
|
||||
the result is unspecified.
|
||||
|
||||
Examples:
|
||||
* `"abc".next ⟨1⟩ = String.Pos.mk 2`
|
||||
* `"L∃∀N".next ⟨1⟩ = String.Pos.mk 4`, since `'∃'` is a multi-byte UTF-8 character
|
||||
|
||||
Cases where the result is unspecified:
|
||||
* `"abc".next ⟨3⟩`, since `3 = s.endPos`
|
||||
* `"L∃∀N".next ⟨2⟩`, since `2` points into the middle of a multi-byte UTF-8 character
|
||||
-/
|
||||
@[extern "lean_string_utf8_next"]
|
||||
def next (s : @& String) (p : @& Pos) : Pos :=
|
||||
let c := get s p
|
||||
|
||||
@@ -17,32 +17,132 @@ def toNat! (s : String) : Nat :=
|
||||
else
|
||||
panic! "Nat expected"
|
||||
|
||||
/--
|
||||
Convert a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`.
|
||||
The result is unspecified if `a` is not properly UTF-8 encoded.
|
||||
-/
|
||||
@[extern "lean_string_from_utf8_unchecked"]
|
||||
opaque fromUTF8Unchecked (a : @& ByteArray) : String
|
||||
def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
|
||||
let c ← a[i]?
|
||||
if c &&& 0x80 == 0 then
|
||||
some ⟨c.toUInt32, .inl (Nat.lt_trans c.1.2 (by decide))⟩
|
||||
else if c &&& 0xe0 == 0xc0 then
|
||||
let c1 ← a[i+1]?
|
||||
guard (c1 &&& 0xc0 == 0x80)
|
||||
let r := ((c &&& 0x1f).toUInt32 <<< 6) ||| (c1 &&& 0x3f).toUInt32
|
||||
guard (0x80 ≤ r)
|
||||
-- TODO: Prove h from the definition of r once we have the necessary lemmas
|
||||
if h : r < 0xd800 then some ⟨r, .inl h⟩ else none
|
||||
else if c &&& 0xf0 == 0xe0 then
|
||||
let c1 ← a[i+1]?
|
||||
let c2 ← a[i+2]?
|
||||
guard (c1 &&& 0xc0 == 0x80 && c2 &&& 0xc0 == 0x80)
|
||||
let r :=
|
||||
((c &&& 0x0f).toUInt32 <<< 12) |||
|
||||
((c1 &&& 0x3f).toUInt32 <<< 6) |||
|
||||
(c2 &&& 0x3f).toUInt32
|
||||
guard (0x800 ≤ r)
|
||||
-- TODO: Prove `r < 0x110000` from the definition of r once we have the necessary lemmas
|
||||
if h : r < 0xd800 ∨ 0xdfff < r ∧ r < 0x110000 then some ⟨r, h⟩ else none
|
||||
else if c &&& 0xf8 == 0xf0 then
|
||||
let c1 ← a[i+1]?
|
||||
let c2 ← a[i+2]?
|
||||
let c3 ← a[i+3]?
|
||||
guard (c1 &&& 0xc0 == 0x80 && c2 &&& 0xc0 == 0x80 && c3 &&& 0xc0 == 0x80)
|
||||
let r :=
|
||||
((c &&& 0x07).toUInt32 <<< 18) |||
|
||||
((c1 &&& 0x3f).toUInt32 <<< 12) |||
|
||||
((c2 &&& 0x3f).toUInt32 <<< 6) |||
|
||||
(c3 &&& 0x3f).toUInt32
|
||||
if h : 0x10000 ≤ r ∧ r < 0x110000 then
|
||||
some ⟨r, .inr ⟨Nat.lt_of_lt_of_le (by decide) h.1, h.2⟩⟩
|
||||
else none
|
||||
else
|
||||
none
|
||||
|
||||
/-- Convert the given `String` to a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded byte array. -/
|
||||
/-- Returns true if the given byte array consists of valid UTF-8. -/
|
||||
@[extern "lean_string_validate_utf8"]
|
||||
def validateUTF8 (a : @& ByteArray) : Bool :=
|
||||
(loop 0).isSome
|
||||
where
|
||||
loop (i : Nat) : Option Unit := do
|
||||
if i < a.size then
|
||||
let c ← utf8DecodeChar? a i
|
||||
loop (i + csize c)
|
||||
else pure ()
|
||||
termination_by a.size - i
|
||||
decreasing_by exact Nat.sub_lt_sub_left ‹_› (Nat.lt_add_of_pos_right (one_le_csize c))
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`. -/
|
||||
@[extern "lean_string_from_utf8"]
|
||||
def fromUTF8 (a : @& ByteArray) (h : validateUTF8 a) : String :=
|
||||
loop 0 ""
|
||||
where
|
||||
loop (i : Nat) (acc : String) : String :=
|
||||
if i < a.size then
|
||||
let c := (utf8DecodeChar? a i).getD default
|
||||
loop (i + csize c) (acc.push c)
|
||||
else acc
|
||||
termination_by a.size - i
|
||||
decreasing_by exact Nat.sub_lt_sub_left ‹_› (Nat.lt_add_of_pos_right (one_le_csize c))
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
|
||||
or returns `none` if `a` is not properly UTF-8 encoded. -/
|
||||
@[inline] def fromUTF8? (a : ByteArray) : Option String :=
|
||||
if h : validateUTF8 a then fromUTF8 a h else none
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
|
||||
or panics if `a` is not properly UTF-8 encoded. -/
|
||||
@[inline] def fromUTF8! (a : ByteArray) : String :=
|
||||
if h : validateUTF8 a then fromUTF8 a h else panic! "invalid UTF-8 string"
|
||||
|
||||
def utf8EncodeChar (c : Char) : List UInt8 :=
|
||||
let v := c.val
|
||||
if v ≤ 0x7f then
|
||||
[v.toUInt8]
|
||||
else if v ≤ 0x7ff then
|
||||
[(v >>> 6).toUInt8 &&& 0x1f ||| 0xc0,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
else if v ≤ 0xffff then
|
||||
[(v >>> 12).toUInt8 &&& 0x0f ||| 0xe0,
|
||||
(v >>> 6).toUInt8 &&& 0x3f ||| 0x80,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
else
|
||||
[(v >>> 18).toUInt8 &&& 0x07 ||| 0xf0,
|
||||
(v >>> 12).toUInt8 &&& 0x3f ||| 0x80,
|
||||
(v >>> 6).toUInt8 &&& 0x3f ||| 0x80,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
|
||||
@[simp] theorem length_utf8EncodeChar (c : Char) : (utf8EncodeChar c).length = csize c := by
|
||||
simp [csize, utf8EncodeChar, Char.utf8Size]
|
||||
cases Decidable.em (c.val ≤ 0x7f) <;> simp [*]
|
||||
cases Decidable.em (c.val ≤ 0x7ff) <;> simp [*]
|
||||
cases Decidable.em (c.val ≤ 0xffff) <;> simp [*]
|
||||
|
||||
/-- Converts the given `String` to a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded byte array. -/
|
||||
@[extern "lean_string_to_utf8"]
|
||||
opaque toUTF8 (a : @& String) : ByteArray
|
||||
def toUTF8 (a : @& String) : ByteArray :=
|
||||
⟨⟨a.data.bind utf8EncodeChar⟩⟩
|
||||
|
||||
@[simp] theorem size_toUTF8 (s : String) : s.toUTF8.size = s.utf8ByteSize := by
|
||||
simp [toUTF8, ByteArray.size, Array.size, utf8ByteSize, List.bind]
|
||||
induction s.data <;> simp [List.map, List.join, utf8ByteSize.go, Nat.add_comm, *]
|
||||
|
||||
/-- Accesses a byte in the UTF-8 encoding of the `String`. O(1) -/
|
||||
@[extern "lean_string_get_byte_fast"]
|
||||
opaque getUtf8Byte (s : @& String) (n : Nat) (h : n < s.utf8ByteSize) : UInt8
|
||||
def getUtf8Byte (s : @& String) (n : Nat) (h : n < s.utf8ByteSize) : UInt8 :=
|
||||
(toUTF8 s).get ⟨n, size_toUTF8 _ ▸ h⟩
|
||||
|
||||
theorem Iterator.sizeOf_next_lt_of_hasNext (i : String.Iterator) (h : i.hasNext) : sizeOf i.next < sizeOf i := by
|
||||
cases i; rename_i s pos; simp [Iterator.next, Iterator.sizeOf_eq]; simp [Iterator.hasNext] at h
|
||||
exact Nat.sub_lt_sub_left h (String.lt_next s pos)
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply String.Iterator.sizeOf_next_lt_of_hasNext; assumption)
|
||||
macro_rules
|
||||
| `(tactic| decreasing_trivial) =>
|
||||
`(tactic| with_reducible apply String.Iterator.sizeOf_next_lt_of_hasNext; assumption)
|
||||
|
||||
theorem Iterator.sizeOf_next_lt_of_atEnd (i : String.Iterator) (h : ¬ i.atEnd = true) : sizeOf i.next < sizeOf i :=
|
||||
have h : i.hasNext := decide_eq_true <| Nat.gt_of_not_le <| mt decide_eq_true h
|
||||
sizeOf_next_lt_of_hasNext i h
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply String.Iterator.sizeOf_next_lt_of_atEnd; assumption)
|
||||
macro_rules
|
||||
| `(tactic| decreasing_trivial) =>
|
||||
`(tactic| with_reducible apply String.Iterator.sizeOf_next_lt_of_atEnd; assumption)
|
||||
|
||||
namespace Iterator
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ def UInt16.shiftLeft (a b : UInt16) : UInt16 := ⟨a.val <<< (modn b 16).val⟩
|
||||
@[extern "lean_uint16_to_uint8"]
|
||||
def UInt16.toUInt8 (a : UInt16) : UInt8 := a.toNat.toUInt8
|
||||
@[extern "lean_uint8_to_uint16"]
|
||||
def UInt8.toUInt16 (a : UInt8) : UInt16 := a.toNat.toUInt16
|
||||
def UInt8.toUInt16 (a : UInt8) : UInt16 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
@[extern "lean_uint16_shift_right"]
|
||||
def UInt16.shiftRight (a b : UInt16) : UInt16 := ⟨a.val >>> (modn b 16).val⟩
|
||||
def UInt16.lt (a b : UInt16) : Prop := a.val < b.val
|
||||
@@ -186,9 +186,9 @@ def UInt32.toUInt8 (a : UInt32) : UInt8 := a.toNat.toUInt8
|
||||
@[extern "lean_uint32_to_uint16"]
|
||||
def UInt32.toUInt16 (a : UInt32) : UInt16 := a.toNat.toUInt16
|
||||
@[extern "lean_uint8_to_uint32"]
|
||||
def UInt8.toUInt32 (a : UInt8) : UInt32 := a.toNat.toUInt32
|
||||
def UInt8.toUInt32 (a : UInt8) : UInt32 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
@[extern "lean_uint16_to_uint32"]
|
||||
def UInt16.toUInt32 (a : UInt16) : UInt32 := a.toNat.toUInt32
|
||||
def UInt16.toUInt32 (a : UInt16) : UInt32 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
|
||||
instance UInt32.instOfNat : OfNat UInt32 n := ⟨UInt32.ofNat n⟩
|
||||
instance : Add UInt32 := ⟨UInt32.add⟩
|
||||
@@ -244,11 +244,11 @@ def UInt64.toUInt16 (a : UInt64) : UInt16 := a.toNat.toUInt16
|
||||
@[extern "lean_uint64_to_uint32"]
|
||||
def UInt64.toUInt32 (a : UInt64) : UInt32 := a.toNat.toUInt32
|
||||
@[extern "lean_uint8_to_uint64"]
|
||||
def UInt8.toUInt64 (a : UInt8) : UInt64 := a.toNat.toUInt64
|
||||
def UInt8.toUInt64 (a : UInt8) : UInt64 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
@[extern "lean_uint16_to_uint64"]
|
||||
def UInt16.toUInt64 (a : UInt16) : UInt64 := a.toNat.toUInt64
|
||||
def UInt16.toUInt64 (a : UInt16) : UInt64 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
@[extern "lean_uint32_to_uint64"]
|
||||
def UInt32.toUInt64 (a : UInt32) : UInt64 := a.toNat.toUInt64
|
||||
def UInt32.toUInt64 (a : UInt32) : UInt64 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
|
||||
instance UInt64.instOfNat : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
|
||||
instance : Add UInt64 := ⟨UInt64.add⟩
|
||||
@@ -321,7 +321,7 @@ def USize.shiftLeft (a b : USize) : USize := ⟨a.val <<< (modn b System.Platfor
|
||||
@[extern "lean_usize_shift_right"]
|
||||
def USize.shiftRight (a b : USize) : USize := ⟨a.val >>> (modn b System.Platform.numBits).val⟩
|
||||
@[extern "lean_uint32_to_usize"]
|
||||
def UInt32.toUSize (a : UInt32) : USize := a.toNat.toUSize
|
||||
def UInt32.toUSize (a : UInt32) : USize := USize.ofNat32 a.val a.1.2
|
||||
@[extern "lean_usize_to_uint32"]
|
||||
def USize.toUInt32 (a : USize) : UInt32 := a.toNat.toUInt32
|
||||
|
||||
|
||||
@@ -1057,6 +1057,7 @@ where
|
||||
else
|
||||
Syntax.mkCApp (Name.mkStr2 "Array" ("mkArray" ++ toString xs.size)) args
|
||||
termination_by xs.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
instance [Quote α `term] : Quote (Array α) `term where
|
||||
quote := quoteArray
|
||||
|
||||
@@ -68,38 +68,106 @@ namespace Simp
|
||||
|
||||
def defaultMaxSteps := 100000
|
||||
|
||||
/--
|
||||
The configuration for `simp`.
|
||||
Passed to `simp` using, for example, the `simp (config := {contextual := true})` syntax.
|
||||
|
||||
See also `Lean.Meta.Simp.neutralConfig`.
|
||||
-/
|
||||
structure Config where
|
||||
/--
|
||||
The maximum number of subexpressions to visit when performing simplification.
|
||||
The default is 100000.
|
||||
-/
|
||||
maxSteps : Nat := defaultMaxSteps
|
||||
/--
|
||||
When simp discharges side conditions for conditional lemmas, it can recursively apply simplification.
|
||||
The `maxDischargeDepth` (default: 2) is the maximum recursion depth when recursively applying simplification to side conditions.
|
||||
-/
|
||||
maxDischargeDepth : Nat := 2
|
||||
/--
|
||||
When `contextual` is true (default: `false`) and simplification encounters an implication `p → q`
|
||||
it includes `p` as an additional simp lemma when simplifying `q`.
|
||||
-/
|
||||
contextual : Bool := false
|
||||
/--
|
||||
When true (default: `true`) then the simplifier caches the result of simplifying each subexpression, if possible.
|
||||
-/
|
||||
memoize : Bool := true
|
||||
/--
|
||||
When `singlePass` is `true` (default: `false`), the simplifier runs through a single round of simplification,
|
||||
which consists of running pre-methods, recursing using congruence lemmas, and then running post-methods.
|
||||
Otherwise, when it is `false`, it iteratively applies this simplification procedure.
|
||||
-/
|
||||
singlePass : Bool := false
|
||||
/-- `let x := v; e[x]` reduces to `e[v]`. -/
|
||||
/--
|
||||
When `true` (default: `true`), performs zeta reduction of let expressions.
|
||||
That is, `let x := v; e[x]` reduces to `e[v]`.
|
||||
See also `zetaDelta`.
|
||||
-/
|
||||
zeta : Bool := true
|
||||
/--
|
||||
When `true` (default: `true`), performs beta reduction of applications of `fun` expressions.
|
||||
That is, `(fun x => e[x]) v` reduces to `e[v]`.
|
||||
-/
|
||||
beta : Bool := true
|
||||
/--
|
||||
TODO (currently unimplemented). When `true` (default: `true`), performs eta reduction for `fun` expressions.
|
||||
That is, `(fun x => f x)` reduces to `f`.
|
||||
-/
|
||||
eta : Bool := true
|
||||
/--
|
||||
Configures how to determine definitional equality between two structure instances.
|
||||
See documentation for `Lean.Meta.EtaStructMode`.
|
||||
-/
|
||||
etaStruct : EtaStructMode := .all
|
||||
/--
|
||||
When `true` (default: `true`), reduces `match` expressions applied to constructors.
|
||||
-/
|
||||
iota : Bool := true
|
||||
/--
|
||||
When `true` (default: `true`), reduces projections of structure constructors.
|
||||
-/
|
||||
proj : Bool := true
|
||||
/--
|
||||
When `true` (default: `false`), rewrites a proposition `p` to `True` or `False` by inferring
|
||||
a `Decidable p` instance and reducing it.
|
||||
-/
|
||||
decide : Bool := false
|
||||
/-- When `true` (default: `false`), simplifies simple arithmetic expressions. -/
|
||||
arith : Bool := false
|
||||
/--
|
||||
When `true` (default: `false`), unfolds definitions.
|
||||
This can be enabled using the `simp!` syntax.
|
||||
-/
|
||||
autoUnfold : Bool := false
|
||||
/--
|
||||
If `dsimp := true`, then switches to `dsimp` on dependent arguments where there is no congruence theorem that allows
|
||||
`simp` to visit them. If `dsimp := false`, then argument is not visited.
|
||||
When `true` (default: `true`) then switches to `dsimp` on dependent arguments
|
||||
if there is no congruence theorem that would allow `simp` to visit them.
|
||||
When `dsimp` is `false`, then the argument is not visited.
|
||||
-/
|
||||
dsimp : Bool := true
|
||||
/-- If `failIfUnchanged := true`, then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will fail if they do not make progress. -/
|
||||
/--
|
||||
If `failIfUnchanged` is `true` (default: `true`), then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will fail if they do not make progress.
|
||||
-/
|
||||
failIfUnchanged : Bool := true
|
||||
/-- If `ground := true`, then ground terms are reduced. A term is ground when
|
||||
it does not contain free or meta variables. Reduction is interrupted at a function application `f ...`
|
||||
if `f` is marked to not be unfolded. -/
|
||||
/--
|
||||
If `ground` is `true` (default: `false`), then ground terms are reduced.
|
||||
A term is ground when it does not contain free or meta variables.
|
||||
Reduction is interrupted at a function application `f ...` if `f` is marked to not be unfolded.
|
||||
Ground term reduction applies `@[seval]` lemmas.
|
||||
-/
|
||||
ground : Bool := false
|
||||
/-- If `unfoldPartialApp := true`, then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will unfold even partial applications of `f` when we request `f` to be unfolded. -/
|
||||
/--
|
||||
If `unfoldPartialApp` is `true` (default: `false`), then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will unfold even partial applications of `f` when we request `f` to be unfolded.
|
||||
-/
|
||||
unfoldPartialApp : Bool := false
|
||||
/-- Given a local context containing entry `x : t := e`, free variable `x` reduces to `e`. -/
|
||||
/--
|
||||
When `true` (default: `false`), local definitions are unfolded.
|
||||
That is, given a local context containing entry `x : t := e`, the free variable `x` reduces to `e`.
|
||||
-/
|
||||
zetaDelta : Bool := false
|
||||
deriving Inhabited, BEq
|
||||
|
||||
@@ -107,6 +175,9 @@ structure Config where
|
||||
structure ConfigCtx extends Config where
|
||||
contextual := true
|
||||
|
||||
/--
|
||||
A neutral configuration for `simp`, turning off all reductions and other built-in simplifications.
|
||||
-/
|
||||
def neutralConfig : Simp.Config := {
|
||||
zeta := false
|
||||
beta := false
|
||||
|
||||
@@ -296,7 +296,7 @@ macro_rules | `($x - $y) => `(binop% HSub.hSub $x $y)
|
||||
macro_rules | `($x * $y) => `(binop% HMul.hMul $x $y)
|
||||
macro_rules | `($x / $y) => `(binop% HDiv.hDiv $x $y)
|
||||
macro_rules | `($x % $y) => `(binop% HMod.hMod $x $y)
|
||||
-- exponentiation should be considered a right action (#2220)
|
||||
-- exponentiation should be considered a right action (#2854)
|
||||
macro_rules | `($x ^ $y) => `(rightact% HPow.hPow $x $y)
|
||||
macro_rules | `($x ++ $y) => `(binop% HAppend.hAppend $x $y)
|
||||
macro_rules | `(- $x) => `(unop% Neg.neg $x)
|
||||
@@ -492,9 +492,12 @@ The attribute `@[deprecated]` on a declaration indicates that the declaration
|
||||
is discouraged for use in new code, and/or should be migrated away from in
|
||||
existing code. It may be removed in a future version of the library.
|
||||
|
||||
`@[deprecated myBetterDef]` means that `myBetterDef` is the suggested replacement.
|
||||
* `@[deprecated myBetterDef]` means that `myBetterDef` is the suggested replacement.
|
||||
* `@[deprecated myBetterDef "use myBetterDef instead"]` allows customizing the deprecation message.
|
||||
* `@[deprecated (since := "2024-04-21")]` records when the deprecation was first applied.
|
||||
-/
|
||||
syntax (name := deprecated) "deprecated" (ppSpace ident)? : attr
|
||||
syntax (name := deprecated) "deprecated" (ppSpace ident)? (ppSpace str)?
|
||||
(" (" &"since" " := " str ")")? : attr
|
||||
|
||||
/--
|
||||
The `@[coe]` attribute on a function (which should also appear in a
|
||||
@@ -552,15 +555,52 @@ except that it doesn't print an empty diagnostic.
|
||||
-/
|
||||
syntax (name := runMeta) "run_meta " doSeq : command
|
||||
|
||||
/-- Element that can be part of a `#guard_msgs` specification. -/
|
||||
syntax guardMsgsSpecElt := &"drop"? (&"info" <|> &"warning" <|> &"error" <|> &"all")
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsFilterSeverity := &"info" <|> &"warning" <|> &"error" <|> &"all"
|
||||
|
||||
/-- Specification for `#guard_msgs` command. -/
|
||||
/--
|
||||
A message filter specification for `#guard_msgs`.
|
||||
- `info`, `warning`, `error`: capture messages with the given severity level.
|
||||
- `all`: capture all messages (the default).
|
||||
- `drop info`, `drop warning`, `drop error`: drop messages with the given severity level.
|
||||
- `drop all`: drop every message.
|
||||
These filters are processed in left-to-right order.
|
||||
-/
|
||||
syntax guardMsgsFilter := &"drop"? guardMsgsFilterSeverity
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsWhitespaceArg := &"exact" <|> &"normalized" <|> &"lax"
|
||||
|
||||
/--
|
||||
Whitespace handling for `#guard_msgs`:
|
||||
- `whitespace := exact` requires an exact whitespace match.
|
||||
- `whitespace := normalized` converts all newline characters to a space before matching
|
||||
(the default). This allows breaking long lines.
|
||||
- `whitespace := lax` collapses whitespace to a single space before matching.
|
||||
In all cases, leading and trailing whitespace is trimmed before matching.
|
||||
-/
|
||||
syntax guardMsgsWhitespace := &"whitespace" " := " guardMsgsWhitespaceArg
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsOrderingArg := &"exact" <|> &"sorted"
|
||||
|
||||
/--
|
||||
Message ordering for `#guard_msgs`:
|
||||
- `ordering := exact` uses the exact ordering of the messages (the default).
|
||||
- `ordering := sorted` sorts the messages in lexicographic order.
|
||||
This helps with testing commands that are non-deterministic in their ordering.
|
||||
-/
|
||||
syntax guardMsgsOrdering := &"ordering" " := " guardMsgsOrderingArg
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsSpecElt := guardMsgsFilter <|> guardMsgsWhitespace <|> guardMsgsOrdering
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsSpec := "(" guardMsgsSpecElt,* ")"
|
||||
|
||||
/--
|
||||
`#guard_msgs` captures the messages generated by another command and checks that they
|
||||
match the contents of the docstring attached to the `#guard_msgs` command.
|
||||
`/-- ... -/ #guard_msgs in cmd` captures the messages generated by the command `cmd`
|
||||
and checks that they match the contents of the docstring.
|
||||
|
||||
Basic example:
|
||||
```lean
|
||||
@@ -570,10 +610,10 @@ error: unknown identifier 'x'
|
||||
#guard_msgs in
|
||||
example : α := x
|
||||
```
|
||||
This checks that there is such an error and then consumes the message entirely.
|
||||
This checks that there is such an error and then consumes the message.
|
||||
|
||||
By default, the command intercepts all messages, but there is a way to specify which types
|
||||
of messages to consider. For example, we can select only warnings:
|
||||
By default, the command captures all messages, but the filter condition can be adjusted.
|
||||
For example, we can select only warnings:
|
||||
```lean
|
||||
/--
|
||||
warning: declaration uses 'sorry'
|
||||
@@ -586,29 +626,37 @@ or only errors
|
||||
#guard_msgs(error) in
|
||||
example : α := sorry
|
||||
```
|
||||
In this last example, since the message is not intercepted there is a warning on `sorry`.
|
||||
In the previous example, since warnings are not captured there is a warning on `sorry`.
|
||||
We can drop the warning completely with
|
||||
```lean
|
||||
#guard_msgs(error, drop warning) in
|
||||
example : α := sorry
|
||||
```
|
||||
|
||||
Syntax description:
|
||||
In general, `#guard_msgs` accepts a comma-separated list of configuration clauses in parentheses:
|
||||
```
|
||||
#guard_msgs (drop? info|warning|error|all,*)? in cmd
|
||||
#guard_msgs (configElt,*) in cmd
|
||||
```
|
||||
By default, the configuration list is `(all, whitespace := normalized, ordering := exact)`.
|
||||
|
||||
If there is no specification, `#guard_msgs` intercepts all messages.
|
||||
Otherwise, if there is one, the specification is considered in left-to-right order, and the first
|
||||
that applies chooses the outcome of the message:
|
||||
- `info`, `warning`, `error`: intercept a message with the given severity level.
|
||||
- `all`: intercept any message (so `#guard_msgs in cmd` and `#guard_msgs (all) in cmd`
|
||||
are equivalent).
|
||||
- `drop info`, `drop warning`, `drop error`: intercept a message with the given severity
|
||||
level and then drop it. These messages are not checked.
|
||||
- `drop all`: intercept a message and drop it.
|
||||
Message filters (processed in left-to-right order):
|
||||
- `info`, `warning`, `error`: capture messages with the given severity level.
|
||||
- `all`: capture all messages (the default).
|
||||
- `drop info`, `drop warning`, `drop error`: drop messages with the given severity level.
|
||||
- `drop all`: drop every message.
|
||||
|
||||
For example, `#guard_msgs (error, drop all) in cmd` means to check warnings and then drop
|
||||
Whitespace handling (after trimming leading and trailing whitespace):
|
||||
- `whitespace := exact` requires an exact whitespace match.
|
||||
- `whitespace := normalized` converts all newline characters to a space before matching
|
||||
(the default). This allows breaking long lines.
|
||||
- `whitespace := lax` collapses whitespace to a single space before matching.
|
||||
|
||||
Message ordering:
|
||||
- `ordering := exact` uses the exact ordering of the messages (the default).
|
||||
- `ordering := sorted` sorts the messages in lexicographic order.
|
||||
This helps with testing commands that are non-deterministic in their ordering.
|
||||
|
||||
For example, `#guard_msgs (error, drop all) in cmd` means to check warnings and drop
|
||||
everything else.
|
||||
-/
|
||||
syntax (name := guardMsgsCmd)
|
||||
@@ -639,4 +687,27 @@ syntax (name := checkSimp) "#check_simp " term "~>" term : command
|
||||
-/
|
||||
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
|
||||
|
||||
/--
|
||||
The `seal foo` command ensures that the definition of `foo` is sealed, meaning it is marked as `[irreducible]`.
|
||||
This command is particularly useful in contexts where you want to prevent the reduction of `foo` in proofs.
|
||||
|
||||
In terms of functionality, `seal foo` is equivalent to `attribute [local irreducible] foo`.
|
||||
This attribute specifies that `foo` should be treated as irreducible only within the local scope,
|
||||
which helps in maintaining the desired abstraction level without affecting global settings.
|
||||
-/
|
||||
syntax "seal " (ppSpace ident)+ : command
|
||||
|
||||
/--
|
||||
The `unseal foo` command ensures that the definition of `foo` is unsealed, meaning it is marked as `[semireducible]`, the
|
||||
default reducibility setting. This command is useful when you need to allow some level of reduction of `foo` in proofs.
|
||||
|
||||
Functionally, `unseal foo` is equivalent to `attribute [local semireducible] foo`.
|
||||
Applying this attribute makes `foo` semireducible only within the local scope.
|
||||
-/
|
||||
syntax "unseal " (ppSpace ident)+ : command
|
||||
|
||||
macro_rules
|
||||
| `(seal $fs:ident*) => `(attribute [local irreducible] $fs:ident*)
|
||||
| `(unseal $fs:ident*) => `(attribute [local semireducible] $fs:ident*)
|
||||
|
||||
end Parser
|
||||
|
||||
@@ -68,7 +68,7 @@ abbrev map (f : Int → Int) (xs : Coeffs) : Coeffs := List.map f xs
|
||||
/-- Shim for `.enum.find?`. -/
|
||||
abbrev findIdx? (f : Int → Bool) (xs : Coeffs) : Option Nat :=
|
||||
-- List.findIdx? f xs
|
||||
-- We could avoid `Std.Data.List.Basic` by using the less efficient:
|
||||
-- We could avoid `Batteries.Data.List.Basic` by using the less efficient:
|
||||
xs.enum.find? (f ·.2) |>.map (·.1)
|
||||
/-- Shim for `IntList.bmod`. -/
|
||||
abbrev bmod (x : Coeffs) (m : Nat) : Coeffs := IntList.bmod x m
|
||||
|
||||
@@ -477,6 +477,8 @@ and `Prod.snd p` respectively. You can also write `p.fst` and `p.snd`.
|
||||
For more information: [Constructors with Arguments](https://lean-lang.org/theorem_proving_in_lean4/inductive_types.html?highlight=Prod#constructors-with-arguments)
|
||||
-/
|
||||
structure Prod (α : Type u) (β : Type v) where
|
||||
/-- Constructs a pair from two terms. -/
|
||||
mk ::
|
||||
/-- The first projection out of a pair. if `p : α × β` then `p.1 : α`. -/
|
||||
fst : α
|
||||
/-- The second projection out of a pair. if `p : α × β` then `p.2 : β`. -/
|
||||
@@ -1096,7 +1098,7 @@ class OfNat (α : Type u) (_ : Nat) where
|
||||
ofNat : α
|
||||
|
||||
@[default_instance 100] /- low prio -/
|
||||
instance (n : Nat) : OfNat Nat n where
|
||||
instance instOfNatNat (n : Nat) : OfNat Nat n where
|
||||
ofNat := n
|
||||
|
||||
/-- `LE α` is the typeclass which supports the notation `x ≤ y` where `x y : α`.-/
|
||||
@@ -1430,31 +1432,31 @@ class ShiftRight (α : Type u) where
|
||||
shiftRight : α → α → α
|
||||
|
||||
@[default_instance]
|
||||
instance [Add α] : HAdd α α α where
|
||||
instance instHAdd [Add α] : HAdd α α α where
|
||||
hAdd a b := Add.add a b
|
||||
|
||||
@[default_instance]
|
||||
instance [Sub α] : HSub α α α where
|
||||
instance instHSub [Sub α] : HSub α α α where
|
||||
hSub a b := Sub.sub a b
|
||||
|
||||
@[default_instance]
|
||||
instance [Mul α] : HMul α α α where
|
||||
instance instHMul [Mul α] : HMul α α α where
|
||||
hMul a b := Mul.mul a b
|
||||
|
||||
@[default_instance]
|
||||
instance [Div α] : HDiv α α α where
|
||||
instance instHDiv [Div α] : HDiv α α α where
|
||||
hDiv a b := Div.div a b
|
||||
|
||||
@[default_instance]
|
||||
instance [Mod α] : HMod α α α where
|
||||
instance instHMod [Mod α] : HMod α α α where
|
||||
hMod a b := Mod.mod a b
|
||||
|
||||
@[default_instance]
|
||||
instance [Pow α β] : HPow α β α where
|
||||
instance instHPow [Pow α β] : HPow α β α where
|
||||
hPow a b := Pow.pow a b
|
||||
|
||||
@[default_instance]
|
||||
instance [NatPow α] : Pow α Nat where
|
||||
instance instPowNat [NatPow α] : Pow α Nat where
|
||||
pow a n := NatPow.pow a n
|
||||
|
||||
@[default_instance]
|
||||
@@ -1521,7 +1523,7 @@ protected def Nat.add : (@& Nat) → (@& Nat) → Nat
|
||||
| a, Nat.zero => a
|
||||
| a, Nat.succ b => Nat.succ (Nat.add a b)
|
||||
|
||||
instance : Add Nat where
|
||||
instance instAddNat : Add Nat where
|
||||
add := Nat.add
|
||||
|
||||
/- We mark the following definitions as pattern to make sure they can be used in recursive equations,
|
||||
@@ -1541,7 +1543,7 @@ protected def Nat.mul : (@& Nat) → (@& Nat) → Nat
|
||||
| _, 0 => 0
|
||||
| a, Nat.succ b => Nat.add (Nat.mul a b) a
|
||||
|
||||
instance : Mul Nat where
|
||||
instance instMulNat : Mul Nat where
|
||||
mul := Nat.mul
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
@@ -1557,7 +1559,7 @@ protected def Nat.pow (m : @& Nat) : (@& Nat) → Nat
|
||||
| 0 => 1
|
||||
| succ n => Nat.mul (Nat.pow m n) m
|
||||
|
||||
instance : NatPow Nat := ⟨Nat.pow⟩
|
||||
instance instNatPowNat : NatPow Nat := ⟨Nat.pow⟩
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/--
|
||||
@@ -1634,14 +1636,14 @@ protected inductive Nat.le (n : Nat) : Nat → Prop
|
||||
/-- If `n ≤ m`, then `n ≤ m + 1`. -/
|
||||
| step {m} : Nat.le n m → Nat.le n (succ m)
|
||||
|
||||
instance : LE Nat where
|
||||
instance instLENat : LE Nat where
|
||||
le := Nat.le
|
||||
|
||||
/-- The strict less than relation on natural numbers is defined as `n < m := n + 1 ≤ m`. -/
|
||||
protected def Nat.lt (n m : Nat) : Prop :=
|
||||
Nat.le (succ n) m
|
||||
|
||||
instance : LT Nat where
|
||||
instance instLTNat : LT Nat where
|
||||
lt := Nat.lt
|
||||
|
||||
theorem Nat.not_succ_le_zero : ∀ (n : Nat), LE.le (succ n) 0 → False
|
||||
@@ -1793,7 +1795,7 @@ protected def Nat.sub : (@& Nat) → (@& Nat) → Nat
|
||||
| a, 0 => a
|
||||
| a, succ b => pred (Nat.sub a b)
|
||||
|
||||
instance : Sub Nat where
|
||||
instance instSubNat : Sub Nat where
|
||||
sub := Nat.sub
|
||||
|
||||
/--
|
||||
@@ -1820,6 +1822,8 @@ It is the "canonical type with `n` elements".
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure Fin (n : Nat) where
|
||||
/-- Creates a `Fin n` from `i : Nat` and a proof that `i < n`. -/
|
||||
mk ::
|
||||
/-- If `i : Fin n`, then `i.val : ℕ` is the described number. It can also be
|
||||
written as `i.1` or just `i` when the target type is known. -/
|
||||
val : Nat
|
||||
@@ -3357,7 +3361,7 @@ protected def seqRight (x : EStateM ε σ α) (y : Unit → EStateM ε σ β) :
|
||||
| Result.error e s => Result.error e s
|
||||
|
||||
@[always_inline]
|
||||
instance : Monad (EStateM ε σ) where
|
||||
instance instMonad : Monad (EStateM ε σ) where
|
||||
bind := EStateM.bind
|
||||
pure := EStateM.pure
|
||||
map := EStateM.map
|
||||
@@ -4331,8 +4335,13 @@ def addMacroScope (mainModule : Name) (n : Name) (scp : MacroScope) : Name :=
|
||||
Name.mkNum (Name.mkStr (Name.appendCore (Name.mkStr n "_@") mainModule) "_hyg") scp
|
||||
|
||||
/--
|
||||
Append two names that may have macro scopes. The macro scopes in `b` are always erased.
|
||||
If `a` has macro scopes, then they are propagated to the result of `append a b`.
|
||||
Appends two names `a` and `b`, propagating macro scopes from `a` or `b`, if any, to the result.
|
||||
Panics if both `a` and `b` have macro scopes.
|
||||
|
||||
This function is used for the `Append Name` instance.
|
||||
|
||||
See also `Lean.Name.appendCore`, which appends names without any consideration for macro scopes.
|
||||
Also consider `Lean.Name.eraseMacroScopes` to erase macro scopes before appending, if appropriate.
|
||||
-/
|
||||
def Name.append (a b : Name) : Name :=
|
||||
match a.hasMacroScopes, b.hasMacroScopes with
|
||||
@@ -4363,7 +4372,7 @@ def defaultMaxRecDepth := 512
|
||||
|
||||
/-- The message to display on stack overflow. -/
|
||||
def maxRecDepthErrorMessage : String :=
|
||||
"maximum recursion depth has been reached (use `set_option maxRecDepth <num>` to increase limit)"
|
||||
"maximum recursion depth has been reached\nuse `set_option maxRecDepth <num>` to increase limit\nuse `set_option diagnostics true` to get diagnostic information"
|
||||
|
||||
namespace Syntax
|
||||
|
||||
|
||||
@@ -103,18 +103,26 @@ end SimprocHelperLemmas
|
||||
|
||||
@[simp] theorem and_true (p : Prop) : (p ∧ True) = p := propext ⟨(·.1), (⟨·, trivial⟩)⟩
|
||||
@[simp] theorem true_and (p : Prop) : (True ∧ p) = p := propext ⟨(·.2), (⟨trivial, ·⟩)⟩
|
||||
instance : Std.LawfulIdentity And True where
|
||||
left_id := true_and
|
||||
right_id := and_true
|
||||
@[simp] theorem and_false (p : Prop) : (p ∧ False) = False := eq_false (·.2)
|
||||
@[simp] theorem false_and (p : Prop) : (False ∧ p) = False := eq_false (·.1)
|
||||
@[simp] theorem and_self (p : Prop) : (p ∧ p) = p := propext ⟨(·.left), fun h => ⟨h, h⟩⟩
|
||||
instance : Std.IdempotentOp And := ⟨and_self⟩
|
||||
@[simp] theorem and_not_self : ¬(a ∧ ¬a) | ⟨ha, hn⟩ => absurd ha hn
|
||||
@[simp] theorem not_and_self : ¬(¬a ∧ a) := and_not_self ∘ And.symm
|
||||
@[simp] theorem and_imp : (a ∧ b → c) ↔ (a → b → c) := ⟨fun h ha hb => h ⟨ha, hb⟩, fun h ⟨ha, hb⟩ => h ha hb⟩
|
||||
@[simp] theorem not_and : ¬(a ∧ b) ↔ (a → ¬b) := and_imp
|
||||
@[simp] theorem or_self (p : Prop) : (p ∨ p) = p := propext ⟨fun | .inl h | .inr h => h, .inl⟩
|
||||
instance : Std.IdempotentOp Or := ⟨or_self⟩
|
||||
@[simp] theorem or_true (p : Prop) : (p ∨ True) = True := eq_true (.inr trivial)
|
||||
@[simp] theorem true_or (p : Prop) : (True ∨ p) = True := eq_true (.inl trivial)
|
||||
@[simp] theorem or_false (p : Prop) : (p ∨ False) = p := propext ⟨fun (.inl h) => h, .inl⟩
|
||||
@[simp] theorem false_or (p : Prop) : (False ∨ p) = p := propext ⟨fun (.inr h) => h, .inr⟩
|
||||
instance : Std.LawfulIdentity Or False where
|
||||
left_id := false_or
|
||||
right_id := or_false
|
||||
@[simp] theorem iff_self (p : Prop) : (p ↔ p) = True := eq_true .rfl
|
||||
@[simp] theorem iff_true (p : Prop) : (p ↔ True) = p := propext ⟨(·.2 trivial), fun h => ⟨fun _ => trivial, fun _ => h⟩⟩
|
||||
@[simp] theorem true_iff (p : Prop) : (True ↔ p) = p := propext ⟨(·.1 trivial), fun h => ⟨fun _ => h, fun _ => trivial⟩⟩
|
||||
@@ -140,6 +148,7 @@ theorem and_congr_left (h : c → (a ↔ b)) : a ∧ c ↔ b ∧ c :=
|
||||
theorem and_assoc : (a ∧ b) ∧ c ↔ a ∧ (b ∧ c) :=
|
||||
Iff.intro (fun ⟨⟨ha, hb⟩, hc⟩ => ⟨ha, hb, hc⟩)
|
||||
(fun ⟨ha, hb, hc⟩ => ⟨⟨ha, hb⟩, hc⟩)
|
||||
instance : Std.Associative And := ⟨fun _ _ _ => propext and_assoc⟩
|
||||
|
||||
@[simp] theorem and_self_left : a ∧ (a ∧ b) ↔ a ∧ b := by rw [←propext and_assoc, and_self]
|
||||
@[simp] theorem and_self_right : (a ∧ b) ∧ b ↔ a ∧ b := by rw [ propext and_assoc, and_self]
|
||||
@@ -167,6 +176,7 @@ theorem Or.imp_right (f : b → c) : a ∨ b → a ∨ c := .imp id f
|
||||
theorem or_assoc : (a ∨ b) ∨ c ↔ a ∨ (b ∨ c) :=
|
||||
Iff.intro (.rec (.imp_right .inl) (.inr ∘ .inr))
|
||||
(.rec (.inl ∘ .inl) (.imp_left .inr))
|
||||
instance : Std.Associative Or := ⟨fun _ _ _ => propext or_assoc⟩
|
||||
|
||||
@[simp] theorem or_self_left : a ∨ (a ∨ b) ↔ a ∨ b := by rw [←propext or_assoc, or_self]
|
||||
@[simp] theorem or_self_right : (a ∨ b) ∨ b ↔ a ∨ b := by rw [ propext or_assoc, or_self]
|
||||
@@ -187,8 +197,12 @@ theorem or_iff_left_of_imp (hb : b → a) : (a ∨ b) ↔ a := Iff.intro (Or.r
|
||||
@[simp] theorem Bool.or_false (b : Bool) : (b || false) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.or_true (b : Bool) : (b || true) = true := by cases b <;> rfl
|
||||
@[simp] theorem Bool.false_or (b : Bool) : (false || b) = b := by cases b <;> rfl
|
||||
instance : Std.LawfulIdentity (· || ·) false where
|
||||
left_id := Bool.false_or
|
||||
right_id := Bool.or_false
|
||||
@[simp] theorem Bool.true_or (b : Bool) : (true || b) = true := by cases b <;> rfl
|
||||
@[simp] theorem Bool.or_self (b : Bool) : (b || b) = b := by cases b <;> rfl
|
||||
instance : Std.IdempotentOp (· || ·) := ⟨Bool.or_self⟩
|
||||
@[simp] theorem Bool.or_eq_true (a b : Bool) : ((a || b) = true) = (a = true ∨ b = true) := by
|
||||
cases a <;> cases b <;> decide
|
||||
|
||||
@@ -196,14 +210,20 @@ theorem or_iff_left_of_imp (hb : b → a) : (a ∨ b) ↔ a := Iff.intro (Or.r
|
||||
@[simp] theorem Bool.and_true (b : Bool) : (b && true) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.false_and (b : Bool) : (false && b) = false := by cases b <;> rfl
|
||||
@[simp] theorem Bool.true_and (b : Bool) : (true && b) = b := by cases b <;> rfl
|
||||
instance : Std.LawfulIdentity (· && ·) true where
|
||||
left_id := Bool.true_and
|
||||
right_id := Bool.and_true
|
||||
@[simp] theorem Bool.and_self (b : Bool) : (b && b) = b := by cases b <;> rfl
|
||||
instance : Std.IdempotentOp (· && ·) := ⟨Bool.and_self⟩
|
||||
@[simp] theorem Bool.and_eq_true (a b : Bool) : ((a && b) = true) = (a = true ∧ b = true) := by
|
||||
cases a <;> cases b <;> decide
|
||||
|
||||
theorem Bool.and_assoc (a b c : Bool) : (a && b && c) = (a && (b && c)) := by
|
||||
cases a <;> cases b <;> cases c <;> decide
|
||||
instance : Std.Associative (· && ·) := ⟨Bool.and_assoc⟩
|
||||
theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
|
||||
cases a <;> cases b <;> cases c <;> decide
|
||||
instance : Std.Associative (· || ·) := ⟨Bool.or_assoc⟩
|
||||
|
||||
@[simp] theorem Bool.not_not (b : Bool) : (!!b) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.not_true : (!true) = false := by decide
|
||||
|
||||
@@ -311,6 +311,8 @@ Note that EOF does not actually close a stream, so further reads may block and r
|
||||
-/
|
||||
getLine : IO String
|
||||
putStr : String → IO Unit
|
||||
/-- Returns true if a stream refers to a Windows console or Unix terminal. -/
|
||||
isTty : BaseIO Bool
|
||||
deriving Inhabited
|
||||
|
||||
open FS
|
||||
@@ -360,6 +362,9 @@ Will succeed even if no lock has been acquired.
|
||||
-/
|
||||
@[extern "lean_io_prim_handle_unlock"] opaque unlock (h : @& Handle) : IO Unit
|
||||
|
||||
/-- Returns true if a handle refers to a Windows console or Unix terminal. -/
|
||||
@[extern "lean_io_prim_handle_is_tty"] opaque isTty (h : @& Handle) : BaseIO Bool
|
||||
|
||||
@[extern "lean_io_prim_handle_flush"] opaque flush (h : @& Handle) : IO Unit
|
||||
/-- Rewinds the read/write cursor to the beginning of the handle. -/
|
||||
@[extern "lean_io_prim_handle_rewind"] opaque rewind (h : @& Handle) : IO Unit
|
||||
@@ -620,7 +625,13 @@ partial def FS.removeDirAll (p : FilePath) : IO Unit := do
|
||||
|
||||
namespace Process
|
||||
|
||||
/-- Returns the process ID of the current process. -/
|
||||
/-- Returns the current working directory of the calling process. -/
|
||||
@[extern "lean_io_process_get_current_dir"] opaque getCurrentDir : IO FilePath
|
||||
|
||||
/-- Sets the current working directory of the calling process. -/
|
||||
@[extern "lean_io_process_set_current_dir"] opaque setCurrentDir (path : @& FilePath) : IO Unit
|
||||
|
||||
/-- Returns the process ID of the calling process. -/
|
||||
@[extern "lean_io_process_get_pid"] opaque getPID : BaseIO UInt32
|
||||
|
||||
inductive Stdio where
|
||||
@@ -743,36 +754,41 @@ namespace FS
|
||||
namespace Stream
|
||||
|
||||
@[export lean_stream_of_handle]
|
||||
def ofHandle (h : Handle) : Stream := {
|
||||
flush := Handle.flush h,
|
||||
read := Handle.read h,
|
||||
write := Handle.write h,
|
||||
getLine := Handle.getLine h,
|
||||
putStr := Handle.putStr h,
|
||||
}
|
||||
def ofHandle (h : Handle) : Stream where
|
||||
flush := Handle.flush h
|
||||
read := Handle.read h
|
||||
write := Handle.write h
|
||||
getLine := Handle.getLine h
|
||||
putStr := Handle.putStr h
|
||||
isTty := Handle.isTty h
|
||||
|
||||
structure Buffer where
|
||||
data : ByteArray := ByteArray.empty
|
||||
pos : Nat := 0
|
||||
|
||||
def ofBuffer (r : Ref Buffer) : Stream := {
|
||||
flush := pure (),
|
||||
def ofBuffer (r : Ref Buffer) : Stream where
|
||||
flush := pure ()
|
||||
read := fun n => r.modifyGet fun b =>
|
||||
let data := b.data.extract b.pos (b.pos + n.toNat)
|
||||
(data, { b with pos := b.pos + data.size }),
|
||||
(data, { b with pos := b.pos + data.size })
|
||||
write := fun data => r.modify fun b =>
|
||||
-- set `exact` to `false` so that repeatedly writing to the stream does not impose quadratic run time
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size },
|
||||
getLine := r.modifyGet fun b =>
|
||||
let pos := match b.data.findIdx? (start := b.pos) fun u => u == 0 || u = '\n'.toNat.toUInt8 with
|
||||
-- include '\n', but not '\0'
|
||||
| some pos => if b.data.get! pos == 0 then pos else pos + 1
|
||||
| none => b.data.size
|
||||
(String.fromUTF8Unchecked <| b.data.extract b.pos pos, { b with pos := pos }),
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size }
|
||||
getLine := do
|
||||
let buf ← r.modifyGet fun b =>
|
||||
let pos := match b.data.findIdx? (start := b.pos) fun u => u == 0 || u = '\n'.toNat.toUInt8 with
|
||||
-- include '\n', but not '\0'
|
||||
| some pos => if b.data.get! pos == 0 then pos else pos + 1
|
||||
| none => b.data.size
|
||||
(b.data.extract b.pos pos, { b with pos := pos })
|
||||
match String.fromUTF8? buf with
|
||||
| some str => pure str
|
||||
| none => throw (.userError "invalid UTF-8")
|
||||
putStr := fun s => r.modify fun b =>
|
||||
let data := s.toUTF8
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size },
|
||||
}
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size }
|
||||
isTty := pure false
|
||||
|
||||
end Stream
|
||||
|
||||
/-- Run action with `stdin` emptied and `stdout+stderr` captured into a `String`. -/
|
||||
@@ -785,7 +801,7 @@ def withIsolatedStreams [Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (x : m
|
||||
(if isolateStderr then withStderr (Stream.ofBuffer bOut) else id) <|
|
||||
x
|
||||
let bOut ← liftM (m := BaseIO) bOut.get
|
||||
let out := String.fromUTF8Unchecked bOut.data
|
||||
let out := String.fromUTF8! bOut.data
|
||||
pure (out, r)
|
||||
|
||||
end FS
|
||||
@@ -802,7 +818,7 @@ class Eval (α : Type u) where
|
||||
-- We take `Unit → α` instead of `α` because ‵α` may contain effectful debugging primitives (e.g., `dbg_trace`)
|
||||
eval : (Unit → α) → (hideUnit : Bool := true) → IO Unit
|
||||
|
||||
instance [ToString α] : Eval α where
|
||||
instance instEval [ToString α] : Eval α where
|
||||
eval a _ := IO.println (toString (a ()))
|
||||
|
||||
instance [Repr α] : Eval α where
|
||||
|
||||
@@ -50,7 +50,7 @@ def decodeUri (uri : String) : String := Id.run do
|
||||
((decoded.push c).push h1, i + 2)
|
||||
else
|
||||
(decoded.push c, i + 1)
|
||||
return String.fromUTF8Unchecked decoded
|
||||
return String.fromUTF8! decoded
|
||||
where hexDigitToUInt8? (c : UInt8) : Option UInt8 :=
|
||||
if zero ≤ c ∧ c ≤ nine then some (c - zero)
|
||||
else if lettera ≤ c ∧ c ≤ letterf then some (c - lettera + 10)
|
||||
|
||||
@@ -368,7 +368,7 @@ for new reflexive relations.
|
||||
Remark: `rfl` is an extensible tactic. We later add `macro_rules` to try different
|
||||
reflexivity theorems (e.g., `Iff.rfl`).
|
||||
-/
|
||||
macro "rfl" : tactic => `(tactic| fail "The rfl tactic failed. Possible reasons:
|
||||
macro "rfl" : tactic => `(tactic| case' _ => fail "The rfl tactic failed. Possible reasons:
|
||||
- The goal is not a reflexive relation (neither `=` nor a relation with a @[refl] lemma).
|
||||
- The arguments of the relation are not equal.
|
||||
Try using the reflexivitiy lemma for your relation explicitly, e.g. `exact Eq.rfl`.")
|
||||
@@ -835,7 +835,7 @@ syntax (name := renameI) "rename_i" (ppSpace colGt binderIdent)+ : tactic
|
||||
/--
|
||||
`repeat tac` repeatedly applies `tac` to the main goal until it fails.
|
||||
That is, if `tac` produces multiple subgoals, only subgoals up to the first failure will be visited.
|
||||
The `Std` library provides `repeat'` which repeats separately in each subgoal.
|
||||
The `Batteries` library provides `repeat'` which repeats separately in each subgoal.
|
||||
-/
|
||||
syntax "repeat " tacticSeq : tactic
|
||||
macro_rules
|
||||
@@ -1125,11 +1125,14 @@ normalizes `h` with `norm_cast` and tries to use that to close the goal. -/
|
||||
macro "assumption_mod_cast" : tactic => `(tactic| norm_cast0 at * <;> assumption)
|
||||
|
||||
/--
|
||||
The `norm_cast` family of tactics is used to normalize casts inside expressions.
|
||||
It is basically a `simp` tactic with a specific set of lemmas to move casts
|
||||
The `norm_cast` family of tactics is used to normalize certain coercions (*casts*) in expressions.
|
||||
- `norm_cast` normalizes casts in the target.
|
||||
- `norm_cast at h` normalizes casts in hypothesis `h`.
|
||||
|
||||
The tactic is basically a version of `simp` with a specific set of lemmas to move casts
|
||||
upwards in the expression.
|
||||
Therefore even in situations where non-terminal `simp` calls are discouraged (because of fragility),
|
||||
`norm_cast` is considered safe.
|
||||
`norm_cast` is considered to be safe.
|
||||
It also has special handling of numerals.
|
||||
|
||||
For instance, given an assumption
|
||||
@@ -1137,22 +1140,22 @@ For instance, given an assumption
|
||||
a b : ℤ
|
||||
h : ↑a + ↑b < (10 : ℚ)
|
||||
```
|
||||
|
||||
writing `norm_cast at h` will turn `h` into
|
||||
```lean
|
||||
h : a + b < 10
|
||||
```
|
||||
|
||||
There are also variants of `exact`, `apply`, `rw`, and `assumption` that
|
||||
work modulo `norm_cast` - in other words, they apply `norm_cast` to make
|
||||
them more flexible. They are called `exact_mod_cast`, `apply_mod_cast`,
|
||||
`rw_mod_cast`, and `assumption_mod_cast`, respectively.
|
||||
Writing `exact_mod_cast h` and `apply_mod_cast h` will normalize casts
|
||||
in the goal and `h` before using `exact h` or `apply h`.
|
||||
Writing `assumption_mod_cast` will normalize casts in the goal and, for
|
||||
every hypothesis `h` in the context, it will try to normalize casts in `h` and use
|
||||
`exact h`.
|
||||
`rw_mod_cast` acts like the `rw` tactic but it applies `norm_cast` between steps.
|
||||
There are also variants of basic tactics that use `norm_cast` to normalize expressions during
|
||||
their operation, to make them more flexible about the expressions they accept
|
||||
(we say that it is a tactic *modulo* the effects of `norm_cast`):
|
||||
- `exact_mod_cast` for `exact` and `apply_mod_cast` for `apply`.
|
||||
Writing `exact_mod_cast h` and `apply_mod_cast h` will normalize casts
|
||||
in the goal and `h` before using `exact h` or `apply h`.
|
||||
- `rw_mod_cast` for `rw`. It applies `norm_cast` between rewrites.
|
||||
- `assumption_mod_cast` for `assumption`.
|
||||
This is effectively `norm_cast at *; assumption`, but more efficient.
|
||||
It normalizes casts in the goal and, for every hypothesis `h` in the context,
|
||||
it will try to normalize casts in `h` and use `exact h`.
|
||||
|
||||
See also `push_cast`, which moves casts inwards rather than lifting them outwards.
|
||||
-/
|
||||
@@ -1160,22 +1163,37 @@ macro "norm_cast" loc:(location)? : tactic =>
|
||||
`(tactic| norm_cast0 $[$loc]? <;> try trivial)
|
||||
|
||||
/--
|
||||
`push_cast` rewrites the goal to move casts inward, toward the leaf nodes.
|
||||
`push_cast` rewrites the goal to move certain coercions (*casts*) inward, toward the leaf nodes.
|
||||
This uses `norm_cast` lemmas in the forward direction.
|
||||
For example, `↑(a + b)` will be written to `↑a + ↑b`.
|
||||
It is equivalent to `simp only with push_cast`.
|
||||
It can also be used at hypotheses with `push_cast at h`
|
||||
and with extra simp lemmas with `push_cast [int.add_zero]`.
|
||||
- `push_cast` moves casts inward in the goal.
|
||||
- `push_cast at h` moves casts inward in the hypothesis `h`.
|
||||
It can be used with extra simp lemmas with, for example, `push_cast [Int.add_zero]`.
|
||||
|
||||
Example:
|
||||
```lean
|
||||
example (a b : ℕ) (h1 : ((a + b : ℕ) : ℤ) = 10) (h2 : ((a + b + 0 : ℕ) : ℤ) = 10) :
|
||||
((a + b : ℕ) : ℤ) = 10 :=
|
||||
begin
|
||||
push_cast,
|
||||
push_cast at h1,
|
||||
push_cast [int.add_zero] at h2,
|
||||
end
|
||||
example (a b : Nat)
|
||||
(h1 : ((a + b : Nat) : Int) = 10)
|
||||
(h2 : ((a + b + 0 : Nat) : Int) = 10) :
|
||||
((a + b : Nat) : Int) = 10 := by
|
||||
/-
|
||||
h1 : ↑(a + b) = 10
|
||||
h2 : ↑(a + b + 0) = 10
|
||||
⊢ ↑(a + b) = 10
|
||||
-/
|
||||
push_cast
|
||||
/- Now
|
||||
⊢ ↑a + ↑b = 10
|
||||
-/
|
||||
push_cast at h1
|
||||
push_cast [Int.add_zero] at h2
|
||||
/- Now
|
||||
h1 h2 : ↑a + ↑b = 10
|
||||
-/
|
||||
exact h1
|
||||
```
|
||||
|
||||
See also `norm_cast`.
|
||||
-/
|
||||
syntax (name := pushCast) "push_cast" (config)? (discharger)? (&" only")?
|
||||
(" [" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
|
||||
@@ -1248,7 +1266,7 @@ Optional arguments passed via a configuration argument as `solve_by_elim (config
|
||||
but it is often useful to change to `.reducible`,
|
||||
so semireducible definitions will not be unfolded when trying to apply a lemma.
|
||||
|
||||
See also the doc-comment for `Std.Tactic.BacktrackConfig` for the options
|
||||
See also the doc-comment for `Lean.Meta.Tactic.Backtrack.BacktrackConfig` for the options
|
||||
`proc`, `suspend`, and `discharge` which allow further customization of `solve_by_elim`.
|
||||
Both `apply_assumption` and `apply_rules` are implemented via these hooks.
|
||||
-/
|
||||
@@ -1524,7 +1542,7 @@ macro "get_elem_tactic" : tactic =>
|
||||
|
||||
/--
|
||||
Searches environment for definitions or theorems that can be substituted in
|
||||
for `exact?% to solve the goal.
|
||||
for `exact?%` to solve the goal.
|
||||
-/
|
||||
syntax (name := Lean.Parser.Syntax.exact?) "exact?%" : term
|
||||
|
||||
|
||||
@@ -9,7 +9,18 @@ import Init.Data.Nat.Basic
|
||||
|
||||
universe u v
|
||||
|
||||
/--
|
||||
`Acc` is the accessibility predicate. Given some relation `r` (e.g. `<`) and a value `x`,
|
||||
`Acc r x` means that `x` is accessible through `r`:
|
||||
|
||||
`x` is accessible if there exists no infinite sequence `... < y₂ < y₁ < y₀ < x`.
|
||||
-/
|
||||
inductive Acc {α : Sort u} (r : α → α → Prop) : α → Prop where
|
||||
/--
|
||||
A value is accessible if for all `y` such that `r y x`, `y` is also accessible.
|
||||
Note that if there exists no `y` such that `r y x`, then `x` is accessible. Such an `x` is called a
|
||||
_base case_.
|
||||
-/
|
||||
| intro (x : α) (h : (y : α) → r y x → Acc r y) : Acc r x
|
||||
|
||||
noncomputable abbrev Acc.ndrec.{u1, u2} {α : Sort u2} {r : α → α → Prop} {C : α → Sort u1}
|
||||
@@ -31,6 +42,14 @@ def inv {x y : α} (h₁ : Acc r x) (h₂ : r y x) : Acc r y :=
|
||||
|
||||
end Acc
|
||||
|
||||
/--
|
||||
A relation `r` is `WellFounded` if all elements of `α` are accessible within `r`.
|
||||
If a relation is `WellFounded`, it does not allow for an infinite descent along the relation.
|
||||
|
||||
If the arguments of the recursive calls in a function definition decrease according to
|
||||
a well founded relation, then the function terminates.
|
||||
Well-founded relations are sometimes called _Artinian_ or said to satisfy the “descending chain condition”.
|
||||
-/
|
||||
inductive WellFounded {α : Sort u} (r : α → α → Prop) : Prop where
|
||||
| intro (h : ∀ a, Acc r a) : WellFounded r
|
||||
|
||||
|
||||
@@ -25,9 +25,16 @@ syntax "decreasing_trivial" : tactic
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| (simp (config := { arith := true, failIfUnchanged := false })) <;> done)
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| omega)
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| assumption)
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply Nat.sub_succ_lt_self; assumption) -- a - (i+1) < a - i if i < a
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply Nat.pred_lt'; assumption) -- i-1 < i if j < i
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply Nat.pred_lt; assumption) -- i-1 < i if i ≠ 0
|
||||
|
||||
/--
|
||||
Variant of `decreasing_trivial` that does not use `omega`, intended to be used in core modules
|
||||
before `omega` is available.
|
||||
-/
|
||||
syntax "decreasing_trivial_pre_omega" : tactic
|
||||
macro_rules | `(tactic| decreasing_trivial_pre_omega) => `(tactic| apply Nat.sub_succ_lt_self; assumption) -- a - (i+1) < a - i if i < a
|
||||
macro_rules | `(tactic| decreasing_trivial_pre_omega) => `(tactic| apply Nat.pred_lt'; assumption) -- i-1 < i if j < i
|
||||
macro_rules | `(tactic| decreasing_trivial_pre_omega) => `(tactic| apply Nat.pred_lt; assumption) -- i-1 < i if i ≠ 0
|
||||
|
||||
|
||||
/-- Constructs a proof of decreasing along a well founded relation, by applying
|
||||
lexicographic order lemmas and using `ts` to solve the base case. If it fails,
|
||||
|
||||
@@ -183,7 +183,6 @@ structure ParametricAttribute (α : Type) where
|
||||
deriving Inhabited
|
||||
|
||||
structure ParametricAttributeImpl (α : Type) extends AttributeImplCore where
|
||||
/-- This is used as the target for go-to-definition queries for simple attributes -/
|
||||
getParam : Name → Syntax → AttrM α
|
||||
afterSet : Name → α → AttrM Unit := fun _ _ _ => pure ()
|
||||
afterImport : Array (Array (Name × α)) → ImportM Unit := fun _ => pure ()
|
||||
|
||||
27
src/Lean/BuiltinDocAttr.lean
Normal file
27
src/Lean/BuiltinDocAttr.lean
Normal file
@@ -0,0 +1,27 @@
|
||||
/-
|
||||
Copyright (c) 2024 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Lean.Compiler.InitAttr
|
||||
import Lean.DocString
|
||||
|
||||
namespace Lean
|
||||
|
||||
def declareBuiltinDocStringAndRanges (declName : Name) : AttrM Unit := do
|
||||
if let some doc ← findDocString? (← getEnv) declName (includeBuiltin := false) then
|
||||
declareBuiltin (declName ++ `docString) (mkAppN (mkConst ``addBuiltinDocString) #[toExpr declName, toExpr doc])
|
||||
if let some declRanges ← findDeclarationRanges? declName then
|
||||
declareBuiltin (declName ++ `declRange) (mkAppN (mkConst ``addBuiltinDeclarationRanges) #[toExpr declName, toExpr declRanges])
|
||||
|
||||
builtin_initialize
|
||||
registerBuiltinAttribute {
|
||||
name := `builtin_doc
|
||||
descr := "make the docs and location of this declaration available as a builtin"
|
||||
add := fun decl stx _ => do
|
||||
Attribute.Builtin.ensureNoArgs stx
|
||||
declareBuiltinDocStringAndRanges decl
|
||||
}
|
||||
|
||||
end Lean
|
||||
@@ -66,12 +66,13 @@ builtin_initialize externAttr : ParametricAttribute ExternAttrData ←
|
||||
descr := "builtin and foreign functions"
|
||||
getParam := fun _ stx => syntaxToExternAttrData stx
|
||||
afterSet := fun declName _ => do
|
||||
let mut env ← getEnv
|
||||
if env.isProjectionFn declName || env.isConstructor declName then do
|
||||
env ← ofExcept <| addExtern env declName
|
||||
let env ← getEnv
|
||||
if env.isProjectionFn declName || env.isConstructor declName then
|
||||
if let some (.thmInfo ..) := env.find? declName then
|
||||
-- We should not mark theorems as extern
|
||||
return ()
|
||||
let env ← ofExcept <| addExtern env declName
|
||||
setEnv env
|
||||
else
|
||||
pure ()
|
||||
}
|
||||
|
||||
@[export lean_get_extern_attr_data]
|
||||
|
||||
@@ -9,9 +9,10 @@ import Lean.Compiler.IR.CompilerM
|
||||
import Lean.Compiler.IR.LiveVars
|
||||
|
||||
namespace Lean.IR.ExplicitRC
|
||||
/-! Insert explicit RC instructions. So, it assumes the input code does not contain `inc` nor `dec` instructions.
|
||||
This transformation is applied before lower level optimizations
|
||||
that introduce the instructions `release` and `set`
|
||||
/-!
|
||||
Insert explicit RC instructions. So, it assumes the input code does not contain `inc` nor `dec` instructions.
|
||||
This transformation is applied before lower level optimizations
|
||||
that introduce the instructions `release` and `set`
|
||||
-/
|
||||
|
||||
structure VarInfo where
|
||||
|
||||
@@ -9,152 +9,238 @@ import Lean.Compiler.IR.LiveVars
|
||||
import Lean.Compiler.IR.Format
|
||||
|
||||
namespace Lean.IR.ResetReuse
|
||||
/-! Remark: the insertResetReuse transformation is applied before we have
|
||||
inserted `inc/dec` instructions, and performed lower level optimizations
|
||||
that introduce the instructions `release` and `set`. -/
|
||||
|
||||
/-! Remark: the functions `S`, `D` and `R` defined here implement the
|
||||
corresponding functions in the paper "Counting Immutable Beans"
|
||||
|
||||
Here are the main differences:
|
||||
- We use the State monad to manage the generation of fresh variable names.
|
||||
- Support for join points, and `uset` and `sset` instructions for unboxed data.
|
||||
- `D` uses the auxiliary function `Dmain`.
|
||||
- `Dmain` returns a pair `(b, found)` to avoid quadratic behavior when checking
|
||||
the last occurrence of the variable `x`.
|
||||
- Because we have join points in the actual implementation, a variable may be live even if it
|
||||
does not occur in a function body. See example at `livevars.lean`.
|
||||
/-!
|
||||
Remark: the insertResetReuse transformation is applied before we have
|
||||
inserted `inc/dec` instructions, and performed lower level optimizations
|
||||
that introduce the instructions `release` and `set`.
|
||||
-/
|
||||
|
||||
private def mayReuse (c₁ c₂ : CtorInfo) : Bool :=
|
||||
/-!
|
||||
Remark: the functions `S`, `D` and `R` defined here implement the
|
||||
corresponding functions in the paper "Counting Immutable Beans"
|
||||
|
||||
Here are the main differences:
|
||||
- We use the State monad to manage the generation of fresh variable names.
|
||||
- Support for join points, and `uset` and `sset` instructions for unboxed data.
|
||||
- `D` uses the auxiliary function `Dmain`.
|
||||
- `Dmain` returns a pair `(b, found)` to avoid quadratic behavior when checking
|
||||
the last occurrence of the variable `x`.
|
||||
- Because we have join points in the actual implementation, a variable may be live even if it
|
||||
does not occur in a function body. See example at `livevars.lean`.
|
||||
-/
|
||||
|
||||
private def mayReuse (c₁ c₂ : CtorInfo) (relaxedReuse : Bool) : Bool :=
|
||||
c₁.size == c₂.size && c₁.usize == c₂.usize && c₁.ssize == c₂.ssize &&
|
||||
/- The following condition is a heuristic.
|
||||
We don't want to reuse cells from different types even when they are compatible
|
||||
If `relaxedReuse := false`, then we don't want to reuse cells from
|
||||
different constructors even when they are compatible
|
||||
because it produces counterintuitive behavior. -/
|
||||
c₁.name.getPrefix == c₂.name.getPrefix
|
||||
(relaxedReuse || c₁.name.getPrefix == c₂.name.getPrefix)
|
||||
|
||||
private partial def S (w : VarId) (c : CtorInfo) : FnBody → FnBody
|
||||
| FnBody.vdecl x t v@(Expr.ctor c' ys) b =>
|
||||
if mayReuse c c' then
|
||||
/--
|
||||
Replace `ctor` applications with `reuse` applications if compatible.
|
||||
`w` contains the "memory cell" being reused.
|
||||
-/
|
||||
private partial def S (w : VarId) (c : CtorInfo) (relaxedReuse : Bool) (b : FnBody) : FnBody :=
|
||||
go b
|
||||
where
|
||||
go : FnBody → FnBody
|
||||
| .vdecl x t v@(.ctor c' ys) b =>
|
||||
if mayReuse c c' relaxedReuse then
|
||||
let updtCidx := c.cidx != c'.cidx
|
||||
FnBody.vdecl x t (Expr.reuse w c' updtCidx ys) b
|
||||
.vdecl x t (.reuse w c' updtCidx ys) b
|
||||
else
|
||||
FnBody.vdecl x t v (S w c b)
|
||||
| FnBody.jdecl j ys v b =>
|
||||
let v' := S w c v
|
||||
if v == v' then FnBody.jdecl j ys v (S w c b)
|
||||
else FnBody.jdecl j ys v' b
|
||||
| FnBody.case tid x xType alts => FnBody.case tid x xType <| alts.map fun alt => alt.modifyBody (S w c)
|
||||
.vdecl x t v (go b)
|
||||
| .jdecl j ys v b =>
|
||||
let v' := go v
|
||||
if v == v' then
|
||||
.jdecl j ys v (go b)
|
||||
else
|
||||
.jdecl j ys v' b
|
||||
| .case tid x xType alts =>
|
||||
.case tid x xType <| alts.map fun alt => alt.modifyBody go
|
||||
| b =>
|
||||
if b.isTerminal then b
|
||||
else let
|
||||
(instr, b) := b.split
|
||||
instr.setBody (S w c b)
|
||||
if b.isTerminal then
|
||||
b
|
||||
else
|
||||
let (instr, b) := b.split
|
||||
instr.setBody (go b)
|
||||
|
||||
structure Context where
|
||||
lctx : LocalContext := {}
|
||||
/--
|
||||
Contains all variables in `cases` statements in the current path
|
||||
and variables that are already in `reset` statements when we
|
||||
invoke `R`.
|
||||
|
||||
We use this information to prevent double-reset in code such as
|
||||
```
|
||||
case x_i : obj of
|
||||
Prod.mk →
|
||||
case x_i : obj of
|
||||
Prod.mk →
|
||||
...
|
||||
```
|
||||
|
||||
A variable can already be in a `reset` statement when we
|
||||
invoke `R` because we execute it with and without `relaxedReuse`.
|
||||
-/
|
||||
alreadyFound : PHashSet VarId := {}
|
||||
/--
|
||||
If `relaxedReuse := true`, then allow memory cells from different
|
||||
constructors to be reused. For example, we can reuse a `PSigma.mk`
|
||||
to allocate a `Prod.mk`. To avoid counterintuitive behavior,
|
||||
we first try `relaxedReuse := false`, and then `relaxedReuse := true`.
|
||||
-/
|
||||
relaxedReuse : Bool := false
|
||||
|
||||
/-- We use `Context` to track join points in scope. -/
|
||||
abbrev M := ReaderT LocalContext (StateT Index Id)
|
||||
abbrev M := ReaderT Context (StateT Index Id)
|
||||
|
||||
private def mkFresh : M VarId := do
|
||||
let idx ← getModify (fun n => n + 1)
|
||||
pure { idx := idx }
|
||||
let idx ← getModify fun n => n + 1
|
||||
return { idx := idx }
|
||||
|
||||
/--
|
||||
Helper function for applying `S`. We only introduce a `reset` if we managed
|
||||
to replace a `ctor` withe `reuse` in `b`.
|
||||
-/
|
||||
private def tryS (x : VarId) (c : CtorInfo) (b : FnBody) : M FnBody := do
|
||||
let w ← mkFresh
|
||||
let b' := S w c b
|
||||
if b == b' then pure b
|
||||
else pure $ FnBody.vdecl w IRType.object (Expr.reset c.size x) b'
|
||||
let b' := S w c (← read).relaxedReuse b
|
||||
if b == b' then
|
||||
return b
|
||||
else
|
||||
return .vdecl w IRType.object (.reset c.size x) b'
|
||||
|
||||
private def Dfinalize (x : VarId) (c : CtorInfo) : FnBody × Bool → M FnBody
|
||||
| (b, true) => pure b
|
||||
| (b, true) => return b
|
||||
| (b, false) => tryS x c b
|
||||
|
||||
private def argsContainsVar (ys : Array Arg) (x : VarId) : Bool :=
|
||||
ys.any fun arg => match arg with
|
||||
| Arg.var y => x == y
|
||||
| _ => false
|
||||
| .var y => x == y
|
||||
| _ => false
|
||||
|
||||
private def isCtorUsing (b : FnBody) (x : VarId) : Bool :=
|
||||
match b with
|
||||
| (FnBody.vdecl _ _ (Expr.ctor _ ys) _) => argsContainsVar ys x
|
||||
| .vdecl _ _ (.ctor _ ys) _ => argsContainsVar ys x
|
||||
| _ => false
|
||||
|
||||
/-- Given `Dmain b`, the resulting pair `(new_b, flag)` contains the new body `new_b`,
|
||||
and `flag == true` if `x` is live in `b`.
|
||||
/--
|
||||
Given `Dmain b`, the resulting pair `(new_b, flag)` contains the new body `new_b`,
|
||||
and `flag == true` if `x` is live in `b`.
|
||||
|
||||
Note that, in the function `D` defined in the paper, for each `let x := e; F`,
|
||||
`D` checks whether `x` is live in `F` or not. This is great for clarity but it
|
||||
is expensive: `O(n^2)` where `n` is the size of the function body. -/
|
||||
private partial def Dmain (x : VarId) (c : CtorInfo) : FnBody → M (FnBody × Bool)
|
||||
| e@(FnBody.case tid y yType alts) => do
|
||||
let ctx ← read
|
||||
if e.hasLiveVar ctx x then do
|
||||
Note that, in the function `D` defined in the paper, for each `let x := e; F`,
|
||||
`D` checks whether `x` is live in `F` or not. This is great for clarity but it
|
||||
is expensive: `O(n^2)` where `n` is the size of the function body. -/
|
||||
private partial def Dmain (x : VarId) (c : CtorInfo) (e : FnBody) : M (FnBody × Bool) := do
|
||||
match e with
|
||||
| .case tid y yType alts =>
|
||||
if e.hasLiveVar (← read).lctx x then
|
||||
/- If `x` is live in `e`, we recursively process each branch. -/
|
||||
let alts ← alts.mapM fun alt => alt.mmodifyBody fun b => Dmain x c b >>= Dfinalize x c
|
||||
pure (FnBody.case tid y yType alts, true)
|
||||
else pure (e, false)
|
||||
| FnBody.jdecl j ys v b => do
|
||||
let (b, found) ← withReader (fun ctx => ctx.addJP j ys v) (Dmain x c b)
|
||||
return (.case tid y yType alts, true)
|
||||
else
|
||||
return (e, false)
|
||||
| .jdecl j ys v b =>
|
||||
let (b, found) ← withReader (fun ctx => { ctx with lctx := ctx.lctx.addJP j ys v }) (Dmain x c b)
|
||||
let (v, _ /- found' -/) ← Dmain x c v
|
||||
/- If `found' == true`, then `Dmain b` must also have returned `(b, true)` since
|
||||
we assume the IR does not have dead join points. So, if `x` is live in `j` (i.e., `v`),
|
||||
then it must also live in `b` since `j` is reachable from `b` with a `jmp`.
|
||||
On the other hand, `x` may be live in `b` but dead in `j` (i.e., `v`). -/
|
||||
pure (FnBody.jdecl j ys v b, found)
|
||||
| e => do
|
||||
let ctx ← read
|
||||
return (.jdecl j ys v b, found)
|
||||
| e =>
|
||||
if e.isTerminal then
|
||||
pure (e, e.hasLiveVar ctx x)
|
||||
return (e, e.hasLiveVar (← read).lctx x)
|
||||
else do
|
||||
let (instr, b) := e.split
|
||||
if isCtorUsing instr x then
|
||||
/- If the scrutinee `x` (the one that is providing memory) is being
|
||||
stored in a constructor, then reuse will probably not be able to reuse memory at runtime.
|
||||
It may work only if the new cell is consumed, but we ignore this case. -/
|
||||
pure (e, true)
|
||||
return (e, true)
|
||||
else
|
||||
let (b, found) ← Dmain x c b
|
||||
/- Remark: it is fine to use `hasFreeVar` instead of `hasLiveVar`
|
||||
since `instr` is not a `FnBody.jmp` (it is not a terminal) nor it is a `FnBody.jdecl`. -/
|
||||
since `instr` is not a `FnBody.jmp` (it is not a terminal) nor
|
||||
it is a `FnBody.jdecl`. -/
|
||||
if found || !instr.hasFreeVar x then
|
||||
pure (instr.setBody b, found)
|
||||
return (instr.setBody b, found)
|
||||
else
|
||||
let b ← tryS x c b
|
||||
pure (instr.setBody b, true)
|
||||
return (instr.setBody b, true)
|
||||
|
||||
private def D (x : VarId) (c : CtorInfo) (b : FnBody) : M FnBody :=
|
||||
Dmain x c b >>= Dfinalize x c
|
||||
|
||||
partial def R : FnBody → M FnBody
|
||||
| FnBody.case tid x xType alts => do
|
||||
partial def R (e : FnBody) : M FnBody := do
|
||||
match e with
|
||||
| .case tid x xType alts =>
|
||||
let alreadyFound := (← read).alreadyFound.contains x
|
||||
withReader (fun ctx => { ctx with alreadyFound := ctx.alreadyFound.insert x }) do
|
||||
let alts ← alts.mapM fun alt => do
|
||||
let alt ← alt.mmodifyBody R
|
||||
match alt with
|
||||
| Alt.ctor c b =>
|
||||
if c.isScalar then pure alt
|
||||
else Alt.ctor c <$> D x c b
|
||||
| _ => pure alt
|
||||
pure $ FnBody.case tid x xType alts
|
||||
| FnBody.jdecl j ys v b => do
|
||||
| .ctor c b =>
|
||||
if c.isScalar || alreadyFound then
|
||||
-- If `alreadyFound`, then we don't try to reuse memory cell to avoid
|
||||
-- double reset.
|
||||
return alt
|
||||
else
|
||||
.ctor c <$> D x c b
|
||||
| _ => return alt
|
||||
return .case tid x xType alts
|
||||
| .jdecl j ys v b =>
|
||||
let v ← R v
|
||||
let b ← withReader (fun ctx => ctx.addJP j ys v) (R b)
|
||||
pure $ FnBody.jdecl j ys v b
|
||||
| e => do
|
||||
if e.isTerminal then pure e
|
||||
else do
|
||||
let b ← withReader (fun ctx => { ctx with lctx := ctx.lctx.addJP j ys v }) (R b)
|
||||
return .jdecl j ys v b
|
||||
| e =>
|
||||
if e.isTerminal then
|
||||
return e
|
||||
else
|
||||
let (instr, b) := e.split
|
||||
let b ← R b
|
||||
pure (instr.setBody b)
|
||||
return instr.setBody b
|
||||
|
||||
abbrev N := StateT (PHashSet VarId) Id
|
||||
|
||||
partial def collectResets (e : FnBody) : N Unit := do
|
||||
match e with
|
||||
| .case _ _ _ alts => alts.forM fun alt => collectResets alt.body
|
||||
| .jdecl _ _ v b => collectResets v; collectResets b
|
||||
| .vdecl _ _ (.reset _ x) b => modify fun s => s.insert x; collectResets b
|
||||
| e => unless e.isTerminal do
|
||||
let (_, b) := e.split
|
||||
collectResets b
|
||||
|
||||
end ResetReuse
|
||||
|
||||
open ResetReuse
|
||||
|
||||
def Decl.insertResetReuse (d : Decl) : Decl :=
|
||||
|
||||
def Decl.insertResetReuseCore (d : Decl) (relaxedReuse : Bool) : Decl :=
|
||||
match d with
|
||||
| .fdecl (body := b) ..=>
|
||||
| .fdecl (body := b) .. =>
|
||||
let nextIndex := d.maxIndex + 1
|
||||
let bNew := (R b {}).run' nextIndex
|
||||
-- First time we execute `insertResetReuseCore`, `relaxedReuse := false`.
|
||||
let alreadyFound : PHashSet VarId := if relaxedReuse then (collectResets b *> get).run' {} else {}
|
||||
let bNew := R b { relaxedReuse, alreadyFound } |>.run' nextIndex
|
||||
d.updateBody! bNew
|
||||
| other => other
|
||||
|
||||
def Decl.insertResetReuse (d : Decl) : Decl :=
|
||||
/-
|
||||
We execute the reset/reuse algorithm twice. The first time, we only reuse memory cells
|
||||
between identical constructor memory cells. That is, we do not reuse a `PSigma.mk` memory cell
|
||||
when allocating a `Prod.mk` memory cell, even though they have the same layout. Recall
|
||||
that the reset/reuse placement algorithm is a heuristic, and the first pass prevents reuses
|
||||
that are unlikely to be useful at runtime. Then, we run the procedure again,
|
||||
relaxing this restriction. If there are still opportunities for reuse, we will take advantage of them.
|
||||
|
||||
The second pass addresses issue #4089.
|
||||
-/
|
||||
d.insertResetReuseCore (relaxedReuse := false)
|
||||
|>.insertResetReuseCore (relaxedReuse := true)
|
||||
|
||||
end Lean.IR
|
||||
|
||||
@@ -12,7 +12,8 @@ Run the code generation pipeline for all declarations in `declNames`
|
||||
that fulfill the requirements of `shouldGenerateCode`.
|
||||
-/
|
||||
def compile (declNames : Array Name) : CoreM Unit := do profileitM Exception "compiler new" (← getOptions) do
|
||||
discard <| LCNF.compile declNames
|
||||
withTraceNode `Compiler (fun _ => return m!"compiling: {declNames}") do
|
||||
discard <| LCNF.compile declNames
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Compiler
|
||||
|
||||
@@ -13,13 +13,25 @@ import Lean.Elab.InfoTree.Types
|
||||
import Lean.MonadEnv
|
||||
|
||||
namespace Lean
|
||||
namespace Core
|
||||
register_builtin_option diagnostics : Bool := {
|
||||
defValue := false
|
||||
group := "diagnostics"
|
||||
descr := "collect diagnostic information"
|
||||
}
|
||||
|
||||
register_builtin_option diagnostics.threshold : Nat := {
|
||||
defValue := 20
|
||||
group := "diagnostics"
|
||||
descr := "only diagnostic counters above this threshold are reported by the definitional equality"
|
||||
}
|
||||
|
||||
register_builtin_option maxHeartbeats : Nat := {
|
||||
defValue := 200000
|
||||
descr := "maximum amount of heartbeats per command. A heartbeat is number of (small) memory allocations (in thousands), 0 means no limit"
|
||||
}
|
||||
|
||||
namespace Core
|
||||
|
||||
builtin_initialize registerTraceClass `Kernel
|
||||
|
||||
def getMaxHeartbeats (opts : Options) : Nat :=
|
||||
@@ -72,6 +84,11 @@ structure Context where
|
||||
Recall that runtime exceptions are `maxRecDepth` or `maxHeartbeats`.
|
||||
-/
|
||||
catchRuntimeEx : Bool := false
|
||||
/--
|
||||
If `diag := true`, different parts of the system collect diagnostics.
|
||||
Use the `set_option diag true` to set it to true.
|
||||
-/
|
||||
diag : Bool := false
|
||||
deriving Nonempty
|
||||
|
||||
/-- CoreM is a monad for manipulating the Lean environment.
|
||||
@@ -104,7 +121,22 @@ instance : MonadOptions CoreM where
|
||||
getOptions := return (← read).options
|
||||
|
||||
instance : MonadWithOptions CoreM where
|
||||
withOptions f x := withReader (fun ctx => { ctx with options := f ctx.options }) x
|
||||
withOptions f x := do
|
||||
let options := f (← read).options
|
||||
let diag := diagnostics.get options
|
||||
if Kernel.isDiagnosticsEnabled (← getEnv) != diag then
|
||||
modifyEnv fun env => Kernel.enableDiag env diag
|
||||
withReader
|
||||
(fun ctx =>
|
||||
{ ctx with
|
||||
options
|
||||
diag
|
||||
maxRecDepth := maxRecDepth.get options })
|
||||
x
|
||||
|
||||
-- Helper function for ensuring fields that depend on `options` have the correct value.
|
||||
@[inline] private def withConsistentCtx (x : CoreM α) : CoreM α := do
|
||||
withOptions id x
|
||||
|
||||
instance : AddMessageContext CoreM where
|
||||
addMessageContext := addMessageContextPartial
|
||||
@@ -177,6 +209,13 @@ instance : MonadTrace CoreM where
|
||||
def restore (b : State) : CoreM Unit :=
|
||||
modify fun s => { s with env := b.env, messages := b.messages, infoState := b.infoState }
|
||||
|
||||
/--
|
||||
Restores full state including sources for unique identifiers. Only intended for incremental reuse
|
||||
between elaboration runs, not for backtracking within a single run.
|
||||
-/
|
||||
def restoreFull (b : State) : CoreM Unit :=
|
||||
set b
|
||||
|
||||
private def mkFreshNameImp (n : Name) : CoreM Name := do
|
||||
let fresh ← modifyGet fun s => (s.nextMacroScope, { s with nextMacroScope := s.nextMacroScope + 1 })
|
||||
return addMacroScope (← getEnv).mainModule n fresh
|
||||
@@ -185,7 +224,7 @@ def mkFreshUserName (n : Name) : CoreM Name :=
|
||||
mkFreshNameImp n
|
||||
|
||||
@[inline] def CoreM.run (x : CoreM α) (ctx : Context) (s : State) : EIO Exception (α × State) :=
|
||||
(x ctx).run s
|
||||
((withConsistentCtx x) ctx).run s
|
||||
|
||||
@[inline] def CoreM.run' (x : CoreM α) (ctx : Context) (s : State) : EIO Exception α :=
|
||||
Prod.fst <$> x.run ctx s
|
||||
@@ -199,7 +238,7 @@ def mkFreshUserName (n : Name) : CoreM Name :=
|
||||
instance [MetaEval α] : MetaEval (CoreM α) where
|
||||
eval env opts x _ := do
|
||||
let x : CoreM α := do try x finally printTraces
|
||||
let (a, s) ← x.toIO { maxRecDepth := maxRecDepth.get opts, options := opts, fileName := "<CoreM>", fileMap := default } { env := env }
|
||||
let (a, s) ← (withConsistentCtx x).toIO { fileName := "<CoreM>", fileMap := default, options := opts } { env := env }
|
||||
MetaEval.eval s.env opts a (hideUnit := true)
|
||||
|
||||
-- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]`
|
||||
@@ -212,7 +251,7 @@ protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m
|
||||
throw <| Exception.error .missing "elaboration interrupted"
|
||||
|
||||
def throwMaxHeartbeat (moduleName : Name) (optionName : Name) (max : Nat) : CoreM Unit := do
|
||||
let msg := s!"(deterministic) timeout at '{moduleName}', maximum number of heartbeats ({max/1000}) has been reached (use 'set_option {optionName} <num>' to set the limit)"
|
||||
let msg := s!"(deterministic) timeout at `{moduleName}`, maximum number of heartbeats ({max/1000}) has been reached\nuse `set_option {optionName} <num>` to set the limit\nuse `set_option {diagnostics.name} true` to get diagnostic information"
|
||||
throw <| Exception.error (← getRef) (MessageData.ofFormat (Std.Format.text msg))
|
||||
|
||||
def checkMaxHeartbeatsCore (moduleName : String) (optionName : Name) (max : Nat) : CoreM Unit := do
|
||||
@@ -245,6 +284,13 @@ def resetMessageLog : CoreM Unit :=
|
||||
def getMessageLog : CoreM MessageLog :=
|
||||
return (← get).messages
|
||||
|
||||
/--
|
||||
Returns the current log and then resets its messages but does NOT reset `MessageLog.hadErrors`. Used
|
||||
for incremental reporting during elaboration of a single command.
|
||||
-/
|
||||
def getAndEmptyMessageLog : CoreM MessageLog :=
|
||||
modifyGet fun log => ({ log with msgs := {} }, log)
|
||||
|
||||
instance : MonadLog CoreM where
|
||||
getRef := getRef
|
||||
getFileMap := return (← read).fileMap
|
||||
@@ -330,10 +376,13 @@ opaque compileDeclsNew (declNames : List Name) : CoreM Unit
|
||||
|
||||
def compileDecl (decl : Declaration) : CoreM Unit := do
|
||||
let opts ← getOptions
|
||||
let decls := Compiler.getDeclNamesForCodeGen decl
|
||||
if compiler.enableNew.get opts then
|
||||
compileDeclsNew (Compiler.getDeclNamesForCodeGen decl)
|
||||
match (← getEnv).compileDecl opts decl with
|
||||
| Except.ok env => setEnv env
|
||||
compileDeclsNew decls
|
||||
let res ← withTraceNode `compiler (fun _ => return m!"compiling old: {decls}") do
|
||||
return (← getEnv).compileDecl opts decl
|
||||
match res with
|
||||
| Except.ok env => setEnv env
|
||||
| Except.error (KernelException.other msg) =>
|
||||
checkUnsupported decl -- Generate nicer error message for unsupported recursors and axioms
|
||||
throwError msg
|
||||
@@ -355,9 +404,16 @@ def addAndCompile (decl : Declaration) : CoreM Unit := do
|
||||
addDecl decl;
|
||||
compileDecl decl
|
||||
|
||||
def getDiag (opts : Options) : Bool :=
|
||||
diagnostics.get opts
|
||||
|
||||
/-- Return `true` if diagnostic information collection is enabled. -/
|
||||
def isDiagnosticsEnabled : CoreM Bool :=
|
||||
return (← read).diag
|
||||
|
||||
def ImportM.runCoreM (x : CoreM α) : ImportM α := do
|
||||
let ctx ← read
|
||||
let (a, _) ← x.toIO { options := ctx.opts, fileName := "<ImportM>", fileMap := default } { env := ctx.env }
|
||||
let (a, _) ← (withOptions (fun _ => ctx.opts) x).toIO { fileName := "<ImportM>", fileMap := default } { env := ctx.env }
|
||||
return a
|
||||
|
||||
/-- Return `true` if the exception was generated by one our resource limits. -/
|
||||
|
||||
@@ -92,6 +92,7 @@ def moveEntries [Hashable α] (i : Nat) (source : Array (AssocList α β)) (targ
|
||||
moveEntries (i+1) source target
|
||||
else target
|
||||
termination_by source.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def expand [Hashable α] (size : Nat) (buckets : HashMapBucket α β) : HashMapImp α β :=
|
||||
let bucketsNew : HashMapBucket α β := ⟨
|
||||
@@ -122,7 +123,7 @@ def expand [Hashable α] (size : Nat) (buckets : HashMapBucket α β) : HashMapI
|
||||
let ⟨i, h⟩ := mkIdx (hash a) buckets.property
|
||||
let bkt := buckets.val[i]
|
||||
if let some b := bkt.find? a then
|
||||
(m, some b)
|
||||
(⟨size, buckets⟩, some b)
|
||||
else
|
||||
let size' := size + 1
|
||||
let buckets' := buckets.update i (AssocList.cons a b bkt) h
|
||||
@@ -137,8 +138,10 @@ def erase [BEq α] [Hashable α] (m : HashMapImp α β) (a : α) : HashMapImp α
|
||||
| ⟨ size, buckets ⟩ =>
|
||||
let ⟨i, h⟩ := mkIdx (hash a) buckets.property
|
||||
let bkt := buckets.val[i]
|
||||
if bkt.contains a then ⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else m
|
||||
if bkt.contains a then
|
||||
⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else
|
||||
⟨size, buckets⟩
|
||||
|
||||
inductive WellFormed [BEq α] [Hashable α] : HashMapImp α β → Prop where
|
||||
| mkWff : ∀ n, WellFormed (mkHashMapImp n)
|
||||
|
||||
@@ -84,6 +84,7 @@ def moveEntries [Hashable α] (i : Nat) (source : Array (List α)) (target : Has
|
||||
else
|
||||
target
|
||||
termination_by source.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def expand [Hashable α] (size : Nat) (buckets : HashSetBucket α) : HashSetImp α :=
|
||||
let bucketsNew : HashSetBucket α := ⟨
|
||||
@@ -112,8 +113,10 @@ def erase [BEq α] [Hashable α] (m : HashSetImp α) (a : α) : HashSetImp α :=
|
||||
| ⟨ size, buckets ⟩ =>
|
||||
let ⟨i, h⟩ := mkIdx (hash a) buckets.property
|
||||
let bkt := buckets.val[i]
|
||||
if bkt.contains a then ⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else m
|
||||
if bkt.contains a then
|
||||
⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else
|
||||
⟨size, buckets⟩
|
||||
|
||||
inductive WellFormed [BEq α] [Hashable α] : HashSetImp α → Prop where
|
||||
| mkWff : ∀ n, WellFormed (mkHashSetImp n)
|
||||
|
||||
@@ -18,7 +18,7 @@ open IO
|
||||
/-- Consumes `nBytes` bytes from the stream, interprets the bytes as a utf-8 string and the string as a valid JSON object. -/
|
||||
def readJson (h : FS.Stream) (nBytes : Nat) : IO Json := do
|
||||
let bytes ← h.read (USize.ofNat nBytes)
|
||||
let s := String.fromUTF8Unchecked bytes
|
||||
let some s := String.fromUTF8? bytes | throw (IO.userError "invalid UTF-8")
|
||||
ofExcept (Json.parse s)
|
||||
|
||||
def writeJson (h : FS.Stream) (j : Json) : IO Unit := do
|
||||
|
||||
@@ -324,7 +324,7 @@ inductive SemanticTokenType where
|
||||
| decorator
|
||||
-- Extensions
|
||||
| leanSorryLike
|
||||
deriving ToJson, FromJson
|
||||
deriving ToJson, FromJson, BEq, Hashable
|
||||
|
||||
-- must be in the same order as the constructors
|
||||
def SemanticTokenType.names : Array String :=
|
||||
|
||||
@@ -43,11 +43,19 @@ def fail (msg : String) : Parsec α := fun it =>
|
||||
error it msg
|
||||
|
||||
@[inline]
|
||||
def orElse (p : Parsec α) (q : Unit → Parsec α) : Parsec α := fun it =>
|
||||
def tryCatch (p : Parsec α)
|
||||
(csuccess : α → Parsec β)
|
||||
(cerror : Unit → Parsec β)
|
||||
: Parsec β := fun it =>
|
||||
match p it with
|
||||
| success rem a => success rem a
|
||||
| error rem err =>
|
||||
if it = rem then q () it else error rem err
|
||||
| .success rem a => csuccess a rem
|
||||
| .error rem err =>
|
||||
-- We assume that it.s never changes as the `Parsec` monad only modifies `it.pos`.
|
||||
if it.pos = rem.pos then cerror () rem else .error rem err
|
||||
|
||||
@[inline]
|
||||
def orElse (p : Parsec α) (q : Unit → Parsec α) : Parsec α :=
|
||||
tryCatch p pure q
|
||||
|
||||
@[inline]
|
||||
def attempt (p : Parsec α) : Parsec α := λ it =>
|
||||
@@ -74,8 +82,7 @@ def eof : Parsec Unit := fun it =>
|
||||
|
||||
@[specialize]
|
||||
partial def manyCore (p : Parsec α) (acc : Array α) : Parsec $ Array α :=
|
||||
(do manyCore p (acc.push $ ←p))
|
||||
<|> pure acc
|
||||
tryCatch p (manyCore p <| acc.push ·) (fun _ => pure acc)
|
||||
|
||||
@[inline]
|
||||
def many (p : Parsec α) : Parsec $ Array α := manyCore p #[]
|
||||
@@ -85,8 +92,7 @@ def many1 (p : Parsec α) : Parsec $ Array α := do manyCore p #[←p]
|
||||
|
||||
@[specialize]
|
||||
partial def manyCharsCore (p : Parsec Char) (acc : String) : Parsec String :=
|
||||
(do manyCharsCore p (acc.push $ ←p))
|
||||
<|> pure acc
|
||||
tryCatch p (manyCharsCore p <| acc.push ·) (fun _ => pure acc)
|
||||
|
||||
@[inline]
|
||||
def manyChars (p : Parsec Char) : Parsec String := manyCharsCore p ""
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Lean.Data.Format
|
||||
import Lean.Data.Json
|
||||
import Lean.ToExpr
|
||||
|
||||
namespace Lean
|
||||
@@ -12,7 +13,7 @@ namespace Lean
|
||||
structure Position where
|
||||
line : Nat
|
||||
column : Nat
|
||||
deriving Inhabited, DecidableEq, Repr
|
||||
deriving Inhabited, DecidableEq, Repr, ToJson, FromJson
|
||||
|
||||
namespace Position
|
||||
protected def lt : Position → Position → Bool
|
||||
|
||||
@@ -100,7 +100,7 @@ def fromArray (l : Array α) (cmp : α → α → Ordering) : RBTree α cmp :=
|
||||
RBMap.any t (fun a _ => p a)
|
||||
|
||||
def subset (t₁ t₂ : RBTree α cmp) : Bool :=
|
||||
t₁.all fun a => (t₂.find? a).toBool
|
||||
t₁.all fun a => (t₂.find? a).isSome
|
||||
|
||||
def seteq (t₁ t₂ : RBTree α cmp) : Bool :=
|
||||
subset t₁ t₂ && subset t₂ t₁
|
||||
|
||||
@@ -49,13 +49,26 @@ def ReducibilityHints.getHeightEx (h : ReducibilityHints) : UInt32 :=
|
||||
|
||||
namespace ReducibilityHints
|
||||
|
||||
-- Recall that if `lt h₁ h₂`, we want to reduce declaration associated with `h₁`.
|
||||
def lt : ReducibilityHints → ReducibilityHints → Bool
|
||||
| .abbrev, .abbrev => false
|
||||
| .abbrev, _ => true
|
||||
| .regular d₁, .regular d₂ => d₁ < d₂
|
||||
| .regular d₁, .regular d₂ => d₁ > d₂
|
||||
| .regular _, .opaque => true
|
||||
| _, _ => false
|
||||
|
||||
protected def compare : ReducibilityHints → ReducibilityHints → Ordering
|
||||
| .abbrev, .abbrev => .eq
|
||||
| .abbrev, _ => .lt
|
||||
| .regular _, .abbrev => .gt
|
||||
| .regular d₁, .regular d₂ => Ord.compare d₂ d₁
|
||||
| .regular _, .opaque => .lt
|
||||
| .opaque, .opaque => .eq
|
||||
| .opaque, _ => .gt
|
||||
|
||||
instance : Ord ReducibilityHints where
|
||||
compare := ReducibilityHints.compare
|
||||
|
||||
def isAbbrev : ReducibilityHints → Bool
|
||||
| .abbrev => true
|
||||
| _ => false
|
||||
@@ -122,6 +135,11 @@ structure TheoremVal extends ConstantVal where
|
||||
all : List Name := [name]
|
||||
deriving Inhabited, BEq
|
||||
|
||||
@[export lean_mk_theorem_val]
|
||||
def mkTheoremValEx (name : Name) (levelParams : List Name) (type : Expr) (value : Expr) (all : List Name) : TheoremVal := {
|
||||
name, levelParams, type, value, all
|
||||
}
|
||||
|
||||
/-- Value for an opaque constant declaration `opaque x : t := e` -/
|
||||
structure OpaqueVal extends ConstantVal where
|
||||
value : Expr
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user