mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-18 19:04:07 +00:00
Compare commits
3 Commits
array_30
...
expr_eq_ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34fe8d3805 | ||
|
|
ea84c036a7 | ||
|
|
6b57dceb04 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -25,7 +25,7 @@ Please put an X between the brackets as you perform the following steps:
|
||||
|
||||
### Context
|
||||
|
||||
[Broader context that the issue occurred in. If there was any prior discussion on [the Lean Zulip](https://leanprover.zulipchat.com), link it here as well.]
|
||||
[Broader context that the issue occured in. If there was any prior discussion on [the Lean Zulip](https://leanprover.zulipchat.com), link it here as well.]
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -5,7 +5,6 @@
|
||||
* Include the link to your `RFC` or `bug` issue in the description.
|
||||
* If the issue does not already have approval from a developer, submit the PR as draft.
|
||||
* The PR title/description will become the commit message. Keep it up-to-date as the PR evolves.
|
||||
* A toolchain of the form `leanprover/lean4-pr-releases:pr-release-NNNN` for Linux and M-series Macs will be generated upon build. To generate binaries for Windows and Intel-based Macs as well, write a comment containing `release-ci` on its own line.
|
||||
* If you rebase your PR onto `nightly-with-mathlib` then CI will test Mathlib against your PR.
|
||||
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels yourself, by writing a comment containing one of these labels on its own line.
|
||||
* Remove this section, up to and including the `---` before submitting.
|
||||
|
||||
38
.github/workflows/ci.yml
vendored
38
.github/workflows/ci.yml
vendored
@@ -114,7 +114,7 @@ jobs:
|
||||
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
|
||||
check_level=1
|
||||
else
|
||||
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels')"
|
||||
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }}) --jq '.labels'"
|
||||
if echo "$labels" | grep -q "release-ci"; then
|
||||
check_level=2
|
||||
elif echo "$labels" | grep -q "merge-ci"; then
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
"check-level": 2,
|
||||
"CMAKE_PRESET": "debug",
|
||||
// exclude seriously slow tests
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress'"
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
|
||||
},
|
||||
// TODO: suddenly started failing in CI
|
||||
/*{
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
"os": "macos-14",
|
||||
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
|
||||
"release": true,
|
||||
"check-level": 0,
|
||||
"check-level": 1,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
|
||||
@@ -226,19 +226,21 @@ jobs:
|
||||
},
|
||||
{
|
||||
"name": "Linux aarch64",
|
||||
"os": "nscloud-ubuntu-22.04-arm64-4x8",
|
||||
"os": "ubuntu-latest",
|
||||
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-linux_aarch64",
|
||||
"release": true,
|
||||
"check-level": 2,
|
||||
"cross": true,
|
||||
"cross_target": "aarch64-unknown-linux-gnu",
|
||||
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*"
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*"
|
||||
},
|
||||
{
|
||||
"name": "Linux 32bit",
|
||||
"os": "ubuntu-latest",
|
||||
// Use 32bit on stage0 and stage1 to keep oleans compatible
|
||||
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/",
|
||||
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86",
|
||||
"cmultilib": true,
|
||||
"release": true,
|
||||
"check-level": 2,
|
||||
@@ -249,7 +251,7 @@ jobs:
|
||||
"name": "Web Assembly",
|
||||
"os": "ubuntu-latest",
|
||||
// Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
|
||||
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32 -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/",
|
||||
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32",
|
||||
"wasm": true,
|
||||
"cmultilib": true,
|
||||
"release": true,
|
||||
@@ -257,7 +259,7 @@ jobs:
|
||||
"cross": true,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
// Just a few selected tests because wasm is slow
|
||||
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_libuv\\.lean\""
|
||||
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean\""
|
||||
}
|
||||
];
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
|
||||
@@ -297,11 +299,11 @@ jobs:
|
||||
with:
|
||||
msystem: clang64
|
||||
# `:` means do not prefix with msystem
|
||||
pacboy: "make: python: cmake clang ccache gmp libuv git: zip: unzip: diffutils: binutils: tree: zstd tar:"
|
||||
pacboy: "make: python: cmake clang ccache gmp git: zip: unzip: diffutils: binutils: tree: zstd tar:"
|
||||
if: runner.os == 'Windows'
|
||||
- name: Install Brew Packages
|
||||
run: |
|
||||
brew install ccache tree zstd coreutils gmp libuv
|
||||
brew install ccache tree zstd coreutils gmp
|
||||
if: runner.os == 'macOS'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -316,7 +318,7 @@ jobs:
|
||||
git fetch --depth=1 origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD flake.nix flake.lock
|
||||
if: github.event_name == 'pull_request'
|
||||
# (needs to be after "Checkout" so files don't get overridden)
|
||||
# (needs to be after "Checkout" so files don't get overriden)
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v12
|
||||
with:
|
||||
@@ -325,19 +327,17 @@ jobs:
|
||||
if: matrix.wasm
|
||||
- name: Install 32bit c libs
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386
|
||||
sudo apt-get install -y gcc-multilib g++-multilib ccache
|
||||
if: matrix.cmultilib
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-build-v3
|
||||
save-always: true
|
||||
# open nix-shell once for initial setup
|
||||
- name: Setup
|
||||
run: |
|
||||
@@ -382,12 +382,6 @@ jobs:
|
||||
make -C build install
|
||||
- name: Check Binaries
|
||||
run: ${{ matrix.binary-check }} lean-*/bin/* || true
|
||||
- name: Count binary symbols
|
||||
run: |
|
||||
for f in lean-*/bin/*; do
|
||||
echo "$f: $(nm $f | grep " T " | wc -l) exported symbols"
|
||||
done
|
||||
if: matrix.name == 'Windows'
|
||||
- name: List Install Tree
|
||||
run: |
|
||||
# omit contents of Init/, ...
|
||||
|
||||
14
.github/workflows/labels-from-comments.yml
vendored
14
.github/workflows/labels-from-comments.yml
vendored
@@ -1,7 +1,6 @@
|
||||
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, `WIP`,
|
||||
# or `release-ci` labels by commenting on the PR or issue.
|
||||
# If any labels from the set {`awaiting-review`, `awaiting-author`, `WIP`} are added, other labels
|
||||
# from that set are removed automatically at the same time.
|
||||
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, or `WIP` labels,
|
||||
# by commenting on the PR or issue.
|
||||
# Other labels from this set are removed automatically at the same time.
|
||||
|
||||
name: Label PR based on Comment
|
||||
|
||||
@@ -11,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
update-label:
|
||||
if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP') || contains(github.event.comment.body, 'release-ci'))
|
||||
if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP'))
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -26,7 +25,6 @@ jobs:
|
||||
const awaitingReview = commentLines.includes('awaiting-review');
|
||||
const awaitingAuthor = commentLines.includes('awaiting-author');
|
||||
const wip = commentLines.includes('WIP');
|
||||
const releaseCI = commentLines.includes('release-ci');
|
||||
|
||||
if (awaitingReview || awaitingAuthor || wip) {
|
||||
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-review' }).catch(() => {});
|
||||
@@ -43,7 +41,3 @@ jobs:
|
||||
if (wip) {
|
||||
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['WIP'] });
|
||||
}
|
||||
|
||||
if (releaseCI) {
|
||||
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['release-ci'] });
|
||||
}
|
||||
|
||||
10
.github/workflows/nix-ci.yml
vendored
10
.github/workflows/nix-ci.yml
vendored
@@ -55,14 +55,13 @@ jobs:
|
||||
# the default is to use a virtual merge commit between the PR and master: just use the PR
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set Up Nix Cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: nix-store-cache
|
||||
key: ${{ matrix.name }}-nix-store-cache-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-nix-store-cache
|
||||
save-always: true
|
||||
- name: Further Set Up Nix Cache
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: |
|
||||
@@ -79,14 +78,13 @@ jobs:
|
||||
sudo mkdir -m0770 -p /nix/var/cache/ccache
|
||||
sudo chown -R $USER /nix/var/cache/ccache
|
||||
- name: Setup CCache Cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /nix/var/cache/ccache
|
||||
key: ${{ matrix.name }}-nix-ccache-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-nix-ccache
|
||||
save-always: true
|
||||
- name: Further Set Up CCache Cache
|
||||
run: |
|
||||
sudo chown -R root:nixbld /nix/var/cache
|
||||
@@ -105,7 +103,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
- name: Build manual
|
||||
run: |
|
||||
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,inked} -o push-doc
|
||||
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,test,inked} -o push-doc
|
||||
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
|
||||
# https://github.com/netlify/cli/issues/1809
|
||||
cp -r --dereference ./result ./dist
|
||||
@@ -148,3 +146,5 @@ jobs:
|
||||
- name: Fixup CCache Cache
|
||||
run: |
|
||||
sudo chown -R $USER /nix/var/cache
|
||||
- name: CCache stats
|
||||
run: CCACHE_DIR=/nix/var/cache/ccache nix run .#nixpkgs.ccache -- -s
|
||||
|
||||
21
.github/workflows/pr-release.yml
vendored
21
.github/workflows/pr-release.yml
vendored
@@ -134,7 +134,7 @@ jobs:
|
||||
MESSAGE=""
|
||||
|
||||
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
|
||||
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
else
|
||||
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
|
||||
git -C lean4.git log -10 origin/master
|
||||
|
||||
git -C lean4.git fetch origin nightly-with-mathlib
|
||||
git -C lean4.git fetch origin nightly-with-mathlib
|
||||
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
|
||||
@@ -163,11 +163,10 @@ jobs:
|
||||
# so keep in sync
|
||||
|
||||
# Use GitHub API to check if a comment already exists
|
||||
existing_comment="$(curl --retry 3 --location --silent \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
|
||||
existing_comment="$(curl -L -s -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \
|
||||
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-bot"))')"
|
||||
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-mathlib4-bot"))')"
|
||||
existing_comment_id="$(echo "$existing_comment" | jq -r .id)"
|
||||
existing_comment_body="$(echo "$existing_comment" | jq -r .body)"
|
||||
|
||||
@@ -177,14 +176,14 @@ jobs:
|
||||
echo "Posting message to the comments: $MESSAGE"
|
||||
|
||||
# Append new result to the existing comment or post a new comment
|
||||
# It's essential we use the MATHLIB4_COMMENT_BOT token here, so that Mathlib CI can subsequently edit the comment.
|
||||
# It's essential we use the MATHLIB4_BOT token here, so that Mathlib CI can subsequently edit the comment.
|
||||
if [ -z "$existing_comment_id" ]; then
|
||||
INTRO="Mathlib CI status ([docs](https://leanprover-community.github.io/contribute/tags_and_branches.html)):"
|
||||
# Post new comment with a bullet point
|
||||
echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
curl -L -s \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$(jq --null-input --arg intro "$INTRO" --arg val "$MESSAGE" '{"body":($intro + "\n" + $val)}')" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
@@ -193,7 +192,7 @@ jobs:
|
||||
echo "Appending to existing comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
curl -L -s \
|
||||
-X PATCH \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$(jq --null-input --arg existing "$existing_comment_body" --arg message "$MESSAGE" '{"body":($existing + "\n" + $message)}')" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/comments/$existing_comment_id"
|
||||
@@ -329,18 +328,16 @@ 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 "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}",' lakefile.lean
|
||||
sed -i 's,require "leanprover-community" / "batteries" @ ".\+",require "leanprover-community" / "batteries" @ "git#nightly-testing-'"${MOST_RECENT_NIGHTLY}"'",' lakefile.lean
|
||||
lake update batteries
|
||||
git add lakefile.lean lake-manifest.json
|
||||
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
else
|
||||
echo "Branch already exists, merging $BASE and bumping Batteries."
|
||||
echo "Branch already exists, pushing an empty commit."
|
||||
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Mathlib `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag 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
|
||||
lake update batteries
|
||||
get add lake-manifest.json
|
||||
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
fi
|
||||
|
||||
|
||||
6
.github/workflows/restart-on-label.yml
vendored
6
.github/workflows/restart-on-label.yml
vendored
@@ -14,9 +14,8 @@ jobs:
|
||||
# (unfortunately cannot search by PR number, only base branch,
|
||||
# and that is't even unique given PRs from forks, but the risk
|
||||
# of confusion is low and the danger is mild)
|
||||
echo "Trying to find a run with branch $head_ref and commit $head_sha"
|
||||
run_id="$(gh run list -e pull_request -b "$head_ref" -c "$head_sha" \
|
||||
--workflow 'CI' --limit 1 --json databaseId --jq '.[0].databaseId')"
|
||||
run_id=$(gh run list -e pull_request -b "$head_ref" --workflow 'CI' --limit 1 \
|
||||
--limit 1 --json databaseId --jq '.[0].databaseId')
|
||||
echo "Run id: ${run_id}"
|
||||
gh run view "$run_id"
|
||||
echo "Cancelling (just in case)"
|
||||
@@ -30,6 +29,5 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
head_ref: ${{ github.head_ref }}
|
||||
head_sha: ${{ github.event.pull_request.head.sha }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
|
||||
2
.github/workflows/update-stage0.yml
vendored
2
.github/workflows/update-stage0.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
# uses: DeterminateSystems/magic-nix-cache-action@v2
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Restore Build Cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: nix-store-cache
|
||||
key: Nix Linux-nix-store-cache-${{ github.sha }}
|
||||
|
||||
@@ -30,35 +30,6 @@ if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
|
||||
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
endif()
|
||||
|
||||
# Don't do anything with cadical on wasm
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
# On CI Linux, we source cadical from Nix instead; see flake.nix
|
||||
find_program(CADICAL cadical)
|
||||
if(NOT CADICAL)
|
||||
set(CADICAL_CXX c++)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
|
||||
endif()
|
||||
# missing stdio locking API on Windows
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
|
||||
endif()
|
||||
ExternalProject_add(cadical
|
||||
PREFIX cadical
|
||||
GIT_REPOSITORY https://github.com/arminbiere/cadical
|
||||
GIT_TAG rel-1.9.5
|
||||
CONFIGURE_COMMAND ""
|
||||
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
|
||||
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
|
||||
BUILD_IN_SOURCE ON
|
||||
INSTALL_COMMAND "")
|
||||
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
|
||||
set(EXTRA_DEPENDS "cadical")
|
||||
endif()
|
||||
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
|
||||
endif()
|
||||
|
||||
ExternalProject_add(stage0
|
||||
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
|
||||
SOURCE_SUBDIR src
|
||||
|
||||
@@ -43,5 +43,3 @@
|
||||
/src/Init/Guard.lean @digama0
|
||||
/src/Lean/Server/CodeActions/ @digama0
|
||||
/src/Std/ @TwoFX
|
||||
/src/Std/Tactic/BVDecide/ @hargoniX
|
||||
/src/Lean/Elab/Tactic/BVDecide/ @hargoniX
|
||||
|
||||
30
LICENSES
30
LICENSES
@@ -1341,33 +1341,3 @@ whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
==============================================================================
|
||||
CaDiCaL is under the MIT License:
|
||||
==============================================================================
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2021 Armin Biere, Johannes Kepler University Linz, Austria
|
||||
Copyright (c) 2020-2021 Mathias Fleury, Johannes Kepler University Linz, Austria
|
||||
Copyright (c) 2020-2021 Nils Froleyks, Johannes Kepler University Linz, Austria
|
||||
Copyright (c) 2022-2024 Katalin Fazekas, Vienna University of Technology, Austria
|
||||
Copyright (c) 2021-2024 Armin Biere, University of Freiburg, Germany
|
||||
Copyright (c) 2021-2024 Mathias Fleury, University of Freiburg, Germany
|
||||
Copyright (c) 2023-2024 Florian Pollitt, University of Freiburg, Germany
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
364
RELEASES.md
364
RELEASES.md
@@ -8,349 +8,9 @@ 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.12.0
|
||||
----------
|
||||
Development in progress.
|
||||
|
||||
v4.11.0
|
||||
----------
|
||||
|
||||
### Language features, tactics, and metaprograms
|
||||
|
||||
* The variable inclusion mechanism has been changed. Like before, when a definition mentions a variable, Lean will add it as an argument of the definition, but now in theorem bodies, variables are not included based on usage in order to ensure that changes to the proof cannot change the statement of the overall theorem. Instead, variables are only available to the proof if they have been mentioned in the theorem header or in an **`include` command** or are instance implicit and depend only on such variables. The **`omit` command** can be used to omit included variables.
|
||||
|
||||
See breaking changes below.
|
||||
|
||||
PRs: [#4883](https://github.com/leanprover/lean4/pull/4883), [1242ff](https://github.com/leanprover/lean4/commit/1242ffbfb5a79296041683682268e770fc3cf820), [#5000](https://github.com/leanprover/lean4/pull/5000), [#5036](https://github.com/leanprover/lean4/pull/5036), [#5138](https://github.com/leanprover/lean4/pull/5138), [0edf1b](https://github.com/leanprover/lean4/commit/0edf1bac392f7e2fe0266b28b51c498306363a84).
|
||||
|
||||
* **Recursive definitions**
|
||||
* Structural recursion can now be explicitly requested using
|
||||
```
|
||||
termination_by structural x
|
||||
```
|
||||
in analogy to the existing `termination_by x` syntax that causes well-founded recursion to be used.
|
||||
[#4542](https://github.com/leanprover/lean4/pull/4542)
|
||||
* [#4672](https://github.com/leanprover/lean4/pull/4672) fixes a bug that could lead to ill-typed terms.
|
||||
* The `termination_by?` syntax no longer forces the use of well-founded recursion, and when structural
|
||||
recursion is inferred, it will print the result using the `termination_by structural` syntax.
|
||||
* **Mutual structural recursion** is now supported. This feature supports both mutual recursion over a non-mutual
|
||||
data type, as well as recursion over mutual or nested data types:
|
||||
|
||||
```lean
|
||||
mutual
|
||||
def Even : Nat → Prop
|
||||
| 0 => True
|
||||
| n+1 => Odd n
|
||||
|
||||
def Odd : Nat → Prop
|
||||
| 0 => False
|
||||
| n+1 => Even n
|
||||
end
|
||||
|
||||
mutual
|
||||
inductive A
|
||||
| other : B → A
|
||||
| empty
|
||||
inductive B
|
||||
| other : A → B
|
||||
| empty
|
||||
end
|
||||
|
||||
mutual
|
||||
def A.size : A → Nat
|
||||
| .other b => b.size + 1
|
||||
| .empty => 0
|
||||
|
||||
def B.size : B → Nat
|
||||
| .other a => a.size + 1
|
||||
| .empty => 0
|
||||
end
|
||||
|
||||
inductive Tree where | node : List Tree → Tree
|
||||
|
||||
mutual
|
||||
def Tree.size : Tree → Nat
|
||||
| node ts => Tree.list_size ts
|
||||
|
||||
def Tree.list_size : List Tree → Nat
|
||||
| [] => 0
|
||||
| t::ts => Tree.size t + Tree.list_size ts
|
||||
end
|
||||
```
|
||||
|
||||
Functional induction principles are generated for these functions as well (`A.size.induct`, `A.size.mutual_induct`).
|
||||
|
||||
Nested structural recursion is still not supported.
|
||||
|
||||
PRs: [#4639](https://github.com/leanprover/lean4/pull/4639), [#4715](https://github.com/leanprover/lean4/pull/4715), [#4642](https://github.com/leanprover/lean4/pull/4642), [#4656](https://github.com/leanprover/lean4/pull/4656), [#4684](https://github.com/leanprover/lean4/pull/4684), [#4715](https://github.com/leanprover/lean4/pull/4715), [#4728](https://github.com/leanprover/lean4/pull/4728), [#4575](https://github.com/leanprover/lean4/pull/4575), [#4731](https://github.com/leanprover/lean4/pull/4731), [#4658](https://github.com/leanprover/lean4/pull/4658), [#4734](https://github.com/leanprover/lean4/pull/4734), [#4738](https://github.com/leanprover/lean4/pull/4738), [#4718](https://github.com/leanprover/lean4/pull/4718), [#4733](https://github.com/leanprover/lean4/pull/4733), [#4787](https://github.com/leanprover/lean4/pull/4787), [#4788](https://github.com/leanprover/lean4/pull/4788), [#4789](https://github.com/leanprover/lean4/pull/4789), [#4807](https://github.com/leanprover/lean4/pull/4807), [#4772](https://github.com/leanprover/lean4/pull/4772)
|
||||
* [#4809](https://github.com/leanprover/lean4/pull/4809) makes unnecessary `termination_by` clauses cause warnings, not errors.
|
||||
* [#4831](https://github.com/leanprover/lean4/pull/4831) improves handling of nested structural recursion through non-recursive types.
|
||||
* [#4839](https://github.com/leanprover/lean4/pull/4839) improves support for structural recursive over inductive predicates when there are reflexive arguments.
|
||||
* `simp` tactic
|
||||
* [#4784](https://github.com/leanprover/lean4/pull/4784) sets configuration `Simp.Config.implicitDefEqProofs` to `true` by default.
|
||||
|
||||
* `omega` tactic
|
||||
* [#4612](https://github.com/leanprover/lean4/pull/4612) normalizes the order that constraints appear in error messages.
|
||||
* [#4695](https://github.com/leanprover/lean4/pull/4695) prevents pushing casts into multiplications unless it produces a non-trivial linear combination.
|
||||
* [#4989](https://github.com/leanprover/lean4/pull/4989) fixes a regression.
|
||||
|
||||
* `decide` tactic
|
||||
* [#4711](https://github.com/leanprover/lean4/pull/4711) switches from using default transparency to *at least* default transparency when reducing the `Decidable` instance.
|
||||
* [#4674](https://github.com/leanprover/lean4/pull/4674) adds detailed feedback on `decide` tactic failure. It tells you which `Decidable` instances it unfolded, if it get stuck on `Eq.rec` it gives a hint about avoiding tactics when defining `Decidable` instances, and if it gets stuck on `Classical.choice` it gives hints about classical instances being in scope. During this process, it processes `Decidable.rec`s and matches to pin blame on a non-reducing instance.
|
||||
|
||||
* `@[ext]` attribute
|
||||
* [#4543](https://github.com/leanprover/lean4/pull/4543) and [#4762](https://github.com/leanprover/lean4/pull/4762) make `@[ext]` realize `ext_iff` theorems from user `ext` theorems. Fixes the attribute so that `@[local ext]` and `@[scoped ext]` are usable. The `@[ext (iff := false)]` option can be used to turn off `ext_iff` realization.
|
||||
* [#4694](https://github.com/leanprover/lean4/pull/4694) makes "go to definition" work for the generated lemmas. Also adjusts the core library to make use of `ext_iff` generation.
|
||||
* [#4710](https://github.com/leanprover/lean4/pull/4710) makes `ext_iff` theorem preserve inst implicit binder types, rather than making all binder types implicit.
|
||||
|
||||
* `#eval` command
|
||||
* [#4810](https://github.com/leanprover/lean4/pull/4810) introduces a safer `#eval` command that prevents evaluation of terms that contain `sorry`. The motivation is that failing tactics, in conjunction with operations such as array accesses, can lead to the Lean process crashing. Users can use the new `#eval!` command to use the previous unsafe behavior. ([#4829](https://github.com/leanprover/lean4/pull/4829) adjusts a test.)
|
||||
|
||||
* [#4447](https://github.com/leanprover/lean4/pull/4447) adds `#discr_tree_key` and `#discr_tree_simp_key` commands, for helping debug discrimination tree failures. The `#discr_tree_key t` command prints the discrimination tree keys for a term `t` (or, if it is a single identifier, the type of that constant). It uses the default configuration for generating keys. The `#discr_tree_simp_key` command is similar to `#discr_tree_key`, but treats the underlying type as one of a simp lemma, that is it transforms it into an equality and produces the key of the left-hand side.
|
||||
|
||||
For example,
|
||||
```
|
||||
#discr_tree_key (∀ {a n : Nat}, bar a (OfNat.ofNat n))
|
||||
-- bar _ (@OfNat.ofNat Nat _ _)
|
||||
|
||||
#discr_tree_simp_key Nat.add_assoc
|
||||
-- @HAdd.hAdd Nat Nat Nat _ (@HAdd.hAdd Nat Nat Nat _ _ _) _
|
||||
```
|
||||
|
||||
* [#4741](https://github.com/leanprover/lean4/pull/4741) changes option parsing to allow user-defined options from the command line. Initial options are now re-parsed and validated after importing. Command line option assignments prefixed with `weak.` are silently discarded if the option name without the prefix does not exist.
|
||||
|
||||
* **Deriving handlers**
|
||||
* [7253ef](https://github.com/leanprover/lean4/commit/7253ef8751f76bcbe0e6f46dcfa8069699a2bac7) and [a04f3c](https://github.com/leanprover/lean4/commit/a04f3cab5a9fe2870825af6544ca13c5bb766706) improve the construction of the `BEq` deriving handler.
|
||||
* [86af04](https://github.com/leanprover/lean4/commit/86af04cc08c0dbbe0e735ea13d16edea3465f850) makes `BEq` deriving handler work when there are dependently typed fields.
|
||||
* [#4826](https://github.com/leanprover/lean4/pull/4826) refactors the `DecidableEq` deriving handle to use `termination_by structural`.
|
||||
|
||||
* **Metaprogramming**
|
||||
* [#4593](https://github.com/leanprover/lean4/pull/4593) adds `unresolveNameGlobalAvoidingLocals`.
|
||||
* [#4618](https://github.com/leanprover/lean4/pull/4618) deletes deprecated functions from 2022.
|
||||
* [#4642](https://github.com/leanprover/lean4/pull/4642) adds `Meta.lambdaBoundedTelescope`.
|
||||
* [#4731](https://github.com/leanprover/lean4/pull/4731) adds `Meta.withErasedFVars`, to enter a context with some fvars erased from the local context.
|
||||
* [#4777](https://github.com/leanprover/lean4/pull/4777) adds assignment validation at `closeMainGoal`, preventing users from circumventing the occurs check for tactics such as `exact`.
|
||||
* [#4807](https://github.com/leanprover/lean4/pull/4807) introduces `Lean.Meta.PProdN` module for packing and projecting nested `PProd`s.
|
||||
* [#5170](https://github.com/leanprover/lean4/pull/5170) fixes `Syntax.unsetTrailing`. A consequence of this is that "go to definition" now works on the last module name in an `import` block (issue [#4958](https://github.com/leanprover/lean4/issues/4958)).
|
||||
|
||||
|
||||
### Language server, widgets, and IDE extensions
|
||||
|
||||
* [#4727](https://github.com/leanprover/lean4/pull/4727) makes it so that responses to info view requests come as soon as the relevant tactic has finished execution.
|
||||
* [#4580](https://github.com/leanprover/lean4/pull/4580) makes it so that whitespace changes do not invalidate imports, and so starting to type the first declaration after imports should no longer cause them to reload.
|
||||
* [#4780](https://github.com/leanprover/lean4/pull/4780) fixes an issue where hovering over unimported builtin names could result in a panic.
|
||||
|
||||
### Pretty printing
|
||||
|
||||
* [#4558](https://github.com/leanprover/lean4/pull/4558) fixes the `pp.instantiateMVars` setting and changes the default value to `true`.
|
||||
* [#4631](https://github.com/leanprover/lean4/pull/4631) makes sure syntax nodes always run their formatters. Fixes an issue where if `ppSpace` appears in a `macro` or `elab` command then it does not format with a space.
|
||||
* [#4665](https://github.com/leanprover/lean4/pull/4665) fixes a bug where pretty printed signatures (for example in `#check`) were overly hoverable due to `pp.tagAppFns` being set.
|
||||
* [#4724](https://github.com/leanprover/lean4/pull/4724) makes `match` pretty printer be sensitive to `pp.explicit`, which makes hovering over a `match` in the Infoview show the underlying term.
|
||||
* [#4764](https://github.com/leanprover/lean4/pull/4764) documents why anonymous constructor notation isn't pretty printed with flattening.
|
||||
* [#4786](https://github.com/leanprover/lean4/pull/4786) adjusts the parenthesizer so that only the parentheses are hoverable, implemented by having the parentheses "steal" the term info from the parenthesized expression.
|
||||
* [#4854](https://github.com/leanprover/lean4/pull/4854) allows arbitrarily long sequences of optional arguments to be omitted from the end of applications, versus the previous conservative behavior of omitting up to one optional argument.
|
||||
|
||||
### Library
|
||||
|
||||
* `Nat`
|
||||
* [#4597](https://github.com/leanprover/lean4/pull/4597) adds bitwise lemmas `Nat.and_le_(left|right)`.
|
||||
* [#4874](https://github.com/leanprover/lean4/pull/4874) adds simprocs for simplifying bit expressions.
|
||||
* `Int`
|
||||
* [#4903](https://github.com/leanprover/lean4/pull/4903) fixes performance of `HPow Int Nat Int` synthesis by rewriting it as a `NatPow Int` instance.
|
||||
* `UInt*` and `Fin`
|
||||
* [#4605](https://github.com/leanprover/lean4/pull/4605) adds lemmas.
|
||||
* [#4629](https://github.com/leanprover/lean4/pull/4629) adds `*.and_toNat`.
|
||||
* `Option`
|
||||
* [#4599](https://github.com/leanprover/lean4/pull/4599) adds `get` lemmas.
|
||||
* [#4600](https://github.com/leanprover/lean4/pull/4600) adds `Option.or`, a version of `Option.orElse` that is strict in the second argument.
|
||||
* `GetElem`
|
||||
* [#4603](https://github.com/leanprover/lean4/pull/4603) adds `getElem_congr` to help with rewriting indices.
|
||||
* `List` and `Array`
|
||||
* Upstreamed from Batteries: [#4586](https://github.com/leanprover/lean4/pull/4586) upstreams `List.attach` and `Array.attach`, [#4697](https://github.com/leanprover/lean4/pull/4697) upstreams `List.Subset` and `List.Sublist` and API, [#4706](https://github.com/leanprover/lean4/pull/4706) upstreams basic material on `List.Pairwise` and `List.Nodup`, [#4720](https://github.com/leanprover/lean4/pull/4720) upstreams more `List.erase` API, [#4836](https://github.com/leanprover/lean4/pull/4836) and [#4837](https://github.com/leanprover/lean4/pull/4837) upstream `List.IsPrefix`/`List.IsSuffix`/`List.IsInfix` and add `Decidable` instances, [#4855](https://github.com/leanprover/lean4/pull/4855) upstreams `List.tail`, `List.findIdx`, `List.indexOf`, `List.countP`, `List.count`, and `List.range'`, [#4856](https://github.com/leanprover/lean4/pull/4856) upstreams more List lemmas, [#4866](https://github.com/leanprover/lean4/pull/4866) upstreams `List.pairwise_iff_getElem`, [#4865](https://github.com/leanprover/lean4/pull/4865) upstreams `List.eraseIdx` lemmas.
|
||||
* [#4687](https://github.com/leanprover/lean4/pull/4687) adjusts `List.replicate` simp lemmas and simprocs.
|
||||
* [#4704](https://github.com/leanprover/lean4/pull/4704) adds characterizations of `List.Sublist`.
|
||||
* [#4707](https://github.com/leanprover/lean4/pull/4707) adds simp normal form tests for `List.Pairwise` and `List.Nodup`.
|
||||
* [#4708](https://github.com/leanprover/lean4/pull/4708) and [#4815](https://github.com/leanprover/lean4/pull/4815) reorganize lemmas on list getters.
|
||||
* [#4765](https://github.com/leanprover/lean4/pull/4765) adds simprocs for literal array accesses such as `#[1,2,3,4,5][2]`.
|
||||
* [#4790](https://github.com/leanprover/lean4/pull/4790) removes typeclass assumptions for `List.Nodup.eraseP`.
|
||||
* [#4801](https://github.com/leanprover/lean4/pull/4801) adds efficient `usize` functions for array types.
|
||||
* [#4820](https://github.com/leanprover/lean4/pull/4820) changes `List.filterMapM` to run left-to-right.
|
||||
* [#4835](https://github.com/leanprover/lean4/pull/4835) fills in and cleans up gaps in List API.
|
||||
* [#4843](https://github.com/leanprover/lean4/pull/4843), [#4868](https://github.com/leanprover/lean4/pull/4868), and [#4877](https://github.com/leanprover/lean4/pull/4877) correct `List.Subset` lemmas.
|
||||
* [#4863](https://github.com/leanprover/lean4/pull/4863) splits `Init.Data.List.Lemmas` into function-specific files.
|
||||
* [#4875](https://github.com/leanprover/lean4/pull/4875) fixes statement of `List.take_takeWhile`.
|
||||
* Lemmas: [#4602](https://github.com/leanprover/lean4/pull/4602), [#4627](https://github.com/leanprover/lean4/pull/4627), [#4678](https://github.com/leanprover/lean4/pull/4678) for `List.head` and `list.getLast`, [#4723](https://github.com/leanprover/lean4/pull/4723) for `List.erase`, [#4742](https://github.com/leanprover/lean4/pull/4742)
|
||||
* `ByteArray`
|
||||
* [#4582](https://github.com/leanprover/lean4/pull/4582) eliminates `partial` from `ByteArray.toList` and `ByteArray.findIdx?`.
|
||||
* `BitVec`
|
||||
* [#4568](https://github.com/leanprover/lean4/pull/4568) adds recurrence theorems for bitblasting multiplication.
|
||||
* [#4571](https://github.com/leanprover/lean4/pull/4571) adds `shiftLeftRec` lemmas.
|
||||
* [#4872](https://github.com/leanprover/lean4/pull/4872) adds `ushiftRightRec` and lemmas.
|
||||
* [#4873](https://github.com/leanprover/lean4/pull/4873) adds `getLsb_replicate`.
|
||||
* `Std.HashMap` added:
|
||||
* [#4583](https://github.com/leanprover/lean4/pull/4583) **adds `Std.HashMap`** as a verified replacement for `Lean.HashMap`. See the PR for naming differences, but [#4725](https://github.com/leanprover/lean4/pull/4725) renames `HashMap.remove` to `HashMap.erase`.
|
||||
* [#4682](https://github.com/leanprover/lean4/pull/4682) adds `Inhabited` instances.
|
||||
* [#4732](https://github.com/leanprover/lean4/pull/4732) improves `BEq` argument order in hash map lemmas.
|
||||
* [#4759](https://github.com/leanprover/lean4/pull/4759) makes lemmas resolve instances via unification.
|
||||
* [#4771](https://github.com/leanprover/lean4/pull/4771) documents that hash maps should be used linearly to avoid expensive copies.
|
||||
* [#4791](https://github.com/leanprover/lean4/pull/4791) removes `bif` from hash map lemmas, which is inconvenient to work with in practice.
|
||||
* [#4803](https://github.com/leanprover/lean4/pull/4803) adds more lemmas.
|
||||
* `SMap`
|
||||
* [#4690](https://github.com/leanprover/lean4/pull/4690) upstreams `SMap.foldM`.
|
||||
* `BEq`
|
||||
* [#4607](https://github.com/leanprover/lean4/pull/4607) adds `PartialEquivBEq`, `ReflBEq`, `EquivBEq`, and `LawfulHashable` classes.
|
||||
* `IO`
|
||||
* [#4660](https://github.com/leanprover/lean4/pull/4660) adds `IO.Process.Child.tryWait`.
|
||||
* [#4747](https://github.com/leanprover/lean4/pull/4747), [#4730](https://github.com/leanprover/lean4/pull/4730), and [#4756](https://github.com/leanprover/lean4/pull/4756) add `×'` syntax for `PProd`. Adds a delaborator for `PProd` and `MProd` values to pretty print as flattened angle bracket tuples.
|
||||
* **Other fixes or improvements**
|
||||
* [#4604](https://github.com/leanprover/lean4/pull/4604) adds lemmas for cond.
|
||||
* [#4619](https://github.com/leanprover/lean4/pull/4619) changes some definitions into theorems.
|
||||
* [#4616](https://github.com/leanprover/lean4/pull/4616) fixes some names with duplicated namespaces.
|
||||
* [#4620](https://github.com/leanprover/lean4/pull/4620) fixes simp lemmas flagged by the simpNF linter.
|
||||
* [#4666](https://github.com/leanprover/lean4/pull/4666) makes the `Antisymm` class be a `Prop`.
|
||||
* [#4621](https://github.com/leanprover/lean4/pull/4621) cleans up unused arguments flagged by linter.
|
||||
* [#4680](https://github.com/leanprover/lean4/pull/4680) adds imports for orphaned `Init` modules.
|
||||
* [#4679](https://github.com/leanprover/lean4/pull/4679) adds imports for orphaned `Std.Data` modules.
|
||||
* [#4688](https://github.com/leanprover/lean4/pull/4688) adds forward and backward directions of `not_exists`.
|
||||
* [#4689](https://github.com/leanprover/lean4/pull/4689) upstreams `eq_iff_true_of_subsingleton`.
|
||||
* [#4709](https://github.com/leanprover/lean4/pull/4709) fixes precedence handling for `Repr` instances for negative numbers for `Int` and `Float`.
|
||||
* [#4760](https://github.com/leanprover/lean4/pull/4760) renames `TC` ("transitive closure") to `Relation.TransGen`.
|
||||
* [#4842](https://github.com/leanprover/lean4/pull/4842) fixes `List` deprecations.
|
||||
* [#4852](https://github.com/leanprover/lean4/pull/4852) upstreams some Mathlib attributes applied to lemmas.
|
||||
* [93ac63](https://github.com/leanprover/lean4/commit/93ac635a89daa5a8e8ef33ec96b0bcbb5d7ec1ea) improves proof.
|
||||
* [#4862](https://github.com/leanprover/lean4/pull/4862) and [#4878](https://github.com/leanprover/lean4/pull/4878) generalize the universe for `PSigma.exists` and rename it to `Exists.of_psigma_prop`.
|
||||
* Typos: [#4737](https://github.com/leanprover/lean4/pull/4737), [7d2155](https://github.com/leanprover/lean4/commit/7d2155943c67c743409420b4546d47fadf73af1c)
|
||||
* Docs: [#4782](https://github.com/leanprover/lean4/pull/4782), [#4869](https://github.com/leanprover/lean4/pull/4869), [#4648](https://github.com/leanprover/lean4/pull/4648)
|
||||
|
||||
### Lean internals
|
||||
* **Elaboration**
|
||||
* [#4596](https://github.com/leanprover/lean4/pull/4596) enforces `isDefEqStuckEx` at `unstuckMVar` procedure, causing isDefEq to throw a stuck defeq exception if the metavariable was created in a previous level. This results in some better error messages, and it helps `rw` succeed in synthesizing instances (see issue [#2736](https://github.com/leanprover/lean4/issues/2736)).
|
||||
* [#4713](https://github.com/leanprover/lean4/pull/4713) fixes deprecation warnings when there are overloaded symbols.
|
||||
* `elab_as_elim` algorithm:
|
||||
* [#4722](https://github.com/leanprover/lean4/pull/4722) adds check that inferred motive is type-correct.
|
||||
* [#4800](https://github.com/leanprover/lean4/pull/4800) elaborates arguments for parameters appearing in the types of targets.
|
||||
* [#4817](https://github.com/leanprover/lean4/pull/4817) makes the algorithm correctly handle eliminators with explicit motive arguments.
|
||||
* [#4792](https://github.com/leanprover/lean4/pull/4792) adds term elaborator for `Lean.Parser.Term.namedPattern` (e.g. `n@(n' + 1)`) to report errors when used in non-pattern-matching contexts.
|
||||
* [#4818](https://github.com/leanprover/lean4/pull/4818) makes anonymous dot notation work when the expected type is a pi-type-valued type synonym.
|
||||
* **Typeclass inference**
|
||||
* [#4646](https://github.com/leanprover/lean4/pull/4646) improves `synthAppInstances`, the function responsible for synthesizing instances for the `rw` and `apply` tactics. Adds a synthesis loop to handle functions whose instances need to be synthesized in a complex order.
|
||||
* **Inductive types**
|
||||
* [#4684](https://github.com/leanprover/lean4/pull/4684) (backported as [98ee78](https://github.com/leanprover/lean4/commit/98ee789990f91ff5935627787b537911ef8773c4)) refactors `InductiveVal` to have a `numNested : Nat` field instead of `isNested : Bool`. This modifies the kernel.
|
||||
* **Definitions**
|
||||
* [#4776](https://github.com/leanprover/lean4/pull/4776) improves performance of `Replacement.apply`.
|
||||
* [#4712](https://github.com/leanprover/lean4/pull/4712) fixes `.eq_def` theorem generation with messy universes.
|
||||
* [#4841](https://github.com/leanprover/lean4/pull/4841) improves success of finding `T.below x` hypothesis when transforming `match` statements for `IndPredBelow`.
|
||||
* **Diagnostics and profiling**
|
||||
* [#4611](https://github.com/leanprover/lean4/pull/4611) makes kernel diagnostics appear when `diagnostics` is enabled even if it is the only section.
|
||||
* [#4753](https://github.com/leanprover/lean4/pull/4753) adds missing `profileitM` functions.
|
||||
* [#4754](https://github.com/leanprover/lean4/pull/4754) adds `Lean.Expr.numObjs` to compute the number of allocated sub-expressions in a given expression, primarily for diagnosing performance issues.
|
||||
* [#4769](https://github.com/leanprover/lean4/pull/4769) adds missing `withTraceNode`s to improve `trace.profiler` output.
|
||||
* [#4781](https://github.com/leanprover/lean4/pull/4781) and [#4882](https://github.com/leanprover/lean4/pull/4882) make the "use `set_option diagnostics true`" message be conditional on current setting of `diagnostics`.
|
||||
* **Performance**
|
||||
* [#4767](https://github.com/leanprover/lean4/pull/4767), [#4775](https://github.com/leanprover/lean4/pull/4775), and [#4887](https://github.com/leanprover/lean4/pull/4887) add `ShareCommon.shareCommon'` for sharing common terms. In an example with 16 million subterms, it is 20 times faster than the old `shareCommon` procedure.
|
||||
* [#4779](https://github.com/leanprover/lean4/pull/4779) ensures `Expr.replaceExpr` preserves DAG structure in `Expr`s.
|
||||
* [#4783](https://github.com/leanprover/lean4/pull/4783) documents performance issue in `Expr.replaceExpr`.
|
||||
* [#4794](https://github.com/leanprover/lean4/pull/4794), [#4797](https://github.com/leanprover/lean4/pull/4797), [#4798](https://github.com/leanprover/lean4/pull/4798) make `for_each` use precise cache.
|
||||
* [#4795](https://github.com/leanprover/lean4/pull/4795) makes `Expr.find?` and `Expr.findExt?` use the kernel implementations.
|
||||
* [#4799](https://github.com/leanprover/lean4/pull/4799) makes `Expr.replace` use the kernel implementation.
|
||||
* [#4871](https://github.com/leanprover/lean4/pull/4871) makes `Expr.foldConsts` use a precise cache.
|
||||
* [#4890](https://github.com/leanprover/lean4/pull/4890) makes `expr_eq_fn` use a precise cache.
|
||||
* **Utilities**
|
||||
* [#4453](https://github.com/leanprover/lean4/pull/4453) upstreams `ToExpr FilePath` and `compile_time_search_path%`.
|
||||
* **Module system**
|
||||
* [#4652](https://github.com/leanprover/lean4/pull/4652) fixes handling of `const2ModIdx` in `finalizeImport`, making it prefer the original module for a declaration when a declaration is re-declared.
|
||||
* **Kernel**
|
||||
* [#4637](https://github.com/leanprover/lean4/pull/4637) adds a check to prevent large `Nat` exponentiations from evaluating. Elaborator reduction is controlled by the option `exponentiation.threshold`.
|
||||
* [#4683](https://github.com/leanprover/lean4/pull/4683) updates comments in `kernel/declaration.h`, making sure they reflect the current Lean 4 types.
|
||||
* [#4796](https://github.com/leanprover/lean4/pull/4796) improves performance by using `replace` with a precise cache.
|
||||
* [#4700](https://github.com/leanprover/lean4/pull/4700) improves performance by fixing the implementation of move constructors and move assignment operators. Expression copying was taking 10% of total runtime in some workloads. See issue [#4698](https://github.com/leanprover/lean4/issues/4698).
|
||||
* [#4702](https://github.com/leanprover/lean4/pull/4702) improves performance in `replace_rec_fn::apply` by avoiding expression copies. These copies represented about 13% of time spent in `save_result` in some workloads. See the same issue.
|
||||
* **Other fixes or improvements**
|
||||
* [#4590](https://github.com/leanprover/lean4/pull/4590) fixes a typo in some constants and `trace.profiler.useHeartbeats`.
|
||||
* [#4617](https://github.com/leanprover/lean4/pull/4617) add 'since' dates to `deprecated` attributes.
|
||||
* [#4625](https://github.com/leanprover/lean4/pull/4625) improves the robustness of the constructor-as-variable test.
|
||||
* [#4740](https://github.com/leanprover/lean4/pull/4740) extends test with nice example reported on Zulip.
|
||||
* [#4766](https://github.com/leanprover/lean4/pull/4766) moves `Syntax.hasIdent` to be available earlier and shakes dependencies.
|
||||
* [#4881](https://github.com/leanprover/lean4/pull/4881) splits out `Lean.Language.Lean.Types`.
|
||||
* [#4893](https://github.com/leanprover/lean4/pull/4893) adds `LEAN_EXPORT` for `sharecommon` functions.
|
||||
* Typos: [#4635](https://github.com/leanprover/lean4/pull/4635), [#4719](https://github.com/leanprover/lean4/pull/4719), [af40e6](https://github.com/leanprover/lean4/commit/af40e618111581c82fc44de922368a02208b499f)
|
||||
* Docs: [#4748](https://github.com/leanprover/lean4/pull/4748) (`Command.Scope`)
|
||||
|
||||
### Compiler, runtime, and FFI
|
||||
* [#4661](https://github.com/leanprover/lean4/pull/4661) moves `Std` from `libleanshared` to much smaller `libInit_shared`. This fixes the Windows build.
|
||||
* [#4668](https://github.com/leanprover/lean4/pull/4668) fixes initialization, explicitly initializing `Std` in `lean_initialize`.
|
||||
* [#4746](https://github.com/leanprover/lean4/pull/4746) adjusts `shouldExport` to exclude more symbols to get below Windows symbol limit. Some exceptions are added by [#4884](https://github.com/leanprover/lean4/pull/4884) and [#4956](https://github.com/leanprover/lean4/pull/4956) to support Verso.
|
||||
* [#4778](https://github.com/leanprover/lean4/pull/4778) adds `lean_is_exclusive_obj` (`Lean.isExclusiveUnsafe`) and `lean_set_external_data`.
|
||||
* [#4515](https://github.com/leanprover/lean4/pull/4515) fixes calling programs with spaces on Windows.
|
||||
|
||||
### Lake
|
||||
|
||||
* [#4735](https://github.com/leanprover/lean4/pull/4735) improves a number of elements related to Git checkouts, cloud releases,
|
||||
and related error handling.
|
||||
|
||||
* On error, Lake now prints all top-level logs. Top-level logs are those produced by Lake outside of the job monitor (e.g., when cloning dependencies).
|
||||
* When fetching a remote for a dependency, Lake now forcibly fetches tags. This prevents potential errors caused by a repository recreating tags already fetched.
|
||||
* Git error handling is now more informative.
|
||||
* The builtin package facets `release`, `optRelease`, `extraDep` are now captions in the same manner as other facets.
|
||||
* `afterReleaseSync` and `afterReleaseAsync` now fetch `optRelease` rather than `release`.
|
||||
* Added support for optional jobs, whose failure does not cause the whole build to failure. Now `optRelease` is such a job.
|
||||
|
||||
* [#4608](https://github.com/leanprover/lean4/pull/4608) adds draft CI workflow when creating new projects.
|
||||
* [#4847](https://github.com/leanprover/lean4/pull/4847) adds CLI options to control log levels. The `--log-level=<lv>` controls the minimum log level Lake should output. For instance, `--log-level=error` will only print errors (not warnings or info). Also, adds an analogous `--fail-level` option to control the minimum log level for build failures. The existing `--iofail` and `--wfail` options are respectively equivalent to `--fail-level=info` and `--fail-level=warning`.
|
||||
|
||||
* Docs: [#4853](https://github.com/leanprover/lean4/pull/4853)
|
||||
|
||||
|
||||
### DevOps/CI
|
||||
|
||||
* **Workflows**
|
||||
* [#4531](https://github.com/leanprover/lean4/pull/4531) makes release trigger an update of `release.lean-lang.org`.
|
||||
* [#4598](https://github.com/leanprover/lean4/pull/4598) adjusts `pr-release` to the new `lakefile.lean` syntax.
|
||||
* [#4632](https://github.com/leanprover/lean4/pull/4632) makes `pr-release` use the correct tag name.
|
||||
* [#4638](https://github.com/leanprover/lean4/pull/4638) adds ability to manually trigger nightly release.
|
||||
* [#4640](https://github.com/leanprover/lean4/pull/4640) adds more debugging output for `restart-on-label` CI.
|
||||
* [#4663](https://github.com/leanprover/lean4/pull/4663) bumps up waiting for 10s to 30s for `restart-on-label`.
|
||||
* [#4664](https://github.com/leanprover/lean4/pull/4664) bumps versions for `actions/checkout` and `actions/upload-artifacts`.
|
||||
* [582d6e](https://github.com/leanprover/lean4/commit/582d6e7f7168e0dc0819099edaace27d913b893e) bumps version for `actions/download-artifact`.
|
||||
* [6d9718](https://github.com/leanprover/lean4/commit/6d971827e253a4dc08cda3cf6524d7f37819eb47) adds back dropped `check-stage3`.
|
||||
* [0768ad](https://github.com/leanprover/lean4/commit/0768ad4eb9020af0777587a25a692d181e857c14) adds Jira sync (for FRO).
|
||||
* [#4830](https://github.com/leanprover/lean4/pull/4830) adds support to report CI errors on FRO Zulip.
|
||||
* [#4838](https://github.com/leanprover/lean4/pull/4838) adds trigger for `nightly_bump_toolchain` on mathlib4 upon nightly release.
|
||||
* [abf420](https://github.com/leanprover/lean4/commit/abf4206e9c0fcadf17b6f7933434fd1580175015) fixes msys2.
|
||||
* [#4895](https://github.com/leanprover/lean4/pull/4895) deprecates Nix-based builds and removes interactive components. Users who prefer the flake build should maintain it externally.
|
||||
* [#4693](https://github.com/leanprover/lean4/pull/4693), [#4458](https://github.com/leanprover/lean4/pull/4458), and [#4876](https://github.com/leanprover/lean4/pull/4876) update the **release checklist**.
|
||||
* [#4669](https://github.com/leanprover/lean4/pull/4669) fixes the "max dynamic symbols" metric per static library.
|
||||
* [#4691](https://github.com/leanprover/lean4/pull/4691) improves compatibility of `tests/list_simp` for retesting simp normal forms with Mathlib.
|
||||
* [#4806](https://github.com/leanprover/lean4/pull/4806) updates the quickstart guide.
|
||||
* [c02aa9](https://github.com/leanprover/lean4/commit/c02aa98c6a08c3a9b05f68039c071085a4ef70d7) documents the **triage team** in the contribution guide.
|
||||
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* For `@[ext]`-generated `ext` and `ext_iff` lemmas, the `x` and `y` term arguments are now implicit. Furthermore these two lemmas are now protected ([#4543](https://github.com/leanprover/lean4/pull/4543)).
|
||||
|
||||
* Now `trace.profiler.useHearbeats` is `trace.profiler.useHeartbeats` ([#4590](https://github.com/leanprover/lean4/pull/4590)).
|
||||
|
||||
* A bugfix in the structural recursion code may in some cases break existing code, when a parameter of the type of the recursive argument is bound behind indices of that type. This can usually be fixed by reordering the parameters of the function ([#4672](https://github.com/leanprover/lean4/pull/4672)).
|
||||
|
||||
* Now `List.filterMapM` sequences monadic actions left-to-right ([#4820](https://github.com/leanprover/lean4/pull/4820)).
|
||||
|
||||
* The effect of the `variable` command on proofs of `theorem`s has been changed. Whether such section variables are accessible in the proof now depends only on the theorem signature and other top-level commands, not on the proof itself. This change ensures that
|
||||
* the statement of a theorem is independent of its proof. In other words, changes in the proof cannot change the theorem statement.
|
||||
* tactics such as `induction` cannot accidentally include a section variable.
|
||||
* the proof can be elaborated in parallel to subsequent declarations in a future version of Lean.
|
||||
|
||||
The effect of `variable`s on the theorem header as well as on other kinds of declarations is unchanged.
|
||||
|
||||
Specifically, section variables are included if they
|
||||
* are directly referenced by the theorem header,
|
||||
* are included via the new `include` command in the current section and not subsequently mentioned in an `omit` statement,
|
||||
* are directly referenced by any variable included by these rules, OR
|
||||
* are instance-implicit variables that reference only variables included by these rules.
|
||||
|
||||
For porting, a new option `deprecated.oldSectionVars` is included to locally switch back to the old behavior.
|
||||
|
||||
|
||||
Development in progress.
|
||||
|
||||
v4.10.0
|
||||
----------
|
||||
@@ -381,7 +41,7 @@ v4.10.0
|
||||
|
||||
* **Commands**
|
||||
* [#4370](https://github.com/leanprover/lean4/pull/4370) makes the `variable` command fully elaborate binders during validation, fixing an issue where some errors would be reported only at the next declaration.
|
||||
* [#4408](https://github.com/leanprover/lean4/pull/4408) fixes a discrepancy in universe parameter order between `theorem` and `def` declarations.
|
||||
* [#4408](https://github.com/leanprover/lean4/pull/4408) fixes a discrepency in universe parameter order between `theorem` and `def` declarations.
|
||||
* [#4493](https://github.com/leanprover/lean4/pull/4493) and
|
||||
[#4482](https://github.com/leanprover/lean4/pull/4482) fix a discrepancy in the elaborators for `theorem`, `def`, and `example`,
|
||||
making `Prop`-valued `example`s and other definition commands elaborate like `theorem`s.
|
||||
@@ -443,7 +103,7 @@ v4.10.0
|
||||
* [#4454](https://github.com/leanprover/lean4/pull/4454) adds public `Name.isInternalDetail` function for filtering declarations using naming conventions for internal names.
|
||||
|
||||
* **Other fixes or improvements**
|
||||
* [#4416](https://github.com/leanprover/lean4/pull/4416) sorts the output of `#print axioms` for determinism.
|
||||
* [#4416](https://github.com/leanprover/lean4/pull/4416) sorts the ouput of `#print axioms` for determinism.
|
||||
* [#4528](https://github.com/leanprover/lean4/pull/4528) fixes error message range for the cdot focusing tactic.
|
||||
|
||||
### Language server, widgets, and IDE extensions
|
||||
@@ -479,7 +139,7 @@ v4.10.0
|
||||
* [#4372](https://github.com/leanprover/lean4/pull/4372) fixes linearity in `HashMap.insert` and `HashMap.erase`, leading to a 40% speedup in a replace-heavy workload.
|
||||
* `Option`
|
||||
* [#4403](https://github.com/leanprover/lean4/pull/4403) generalizes type of `Option.forM` from `Unit` to `PUnit`.
|
||||
* [#4504](https://github.com/leanprover/lean4/pull/4504) remove simp attribute from `Option.elim` and instead adds it to individual reduction lemmas, making unfolding less aggressive.
|
||||
* [#4504](https://github.com/leanprover/lean4/pull/4504) remove simp attribute from `Option.elim` and instead adds it to individal reduction lemmas, making unfolding less aggressive.
|
||||
* `Nat`
|
||||
* [#4242](https://github.com/leanprover/lean4/pull/4242) adds missing theorems for `n + 1` and `n - 1` normal forms.
|
||||
* [#4486](https://github.com/leanprover/lean4/pull/4486) makes `Nat.min_assoc` be a simp lemma.
|
||||
@@ -863,7 +523,7 @@ v4.9.0
|
||||
fixing a pretty printing error in hovers and strengthening the unused variable linter.
|
||||
* [dfb496](https://github.com/leanprover/lean4/commit/dfb496a27123c3864571aec72f6278e2dad1cecf) fixes `declareBuiltin` to allow it to be called multiple times per declaration.
|
||||
* [#4569](https://github.com/leanprover/lean4/pull/4569) fixes an issue introduced in a merge conflict, where the interrupt exception was swallowed by some `tryCatchRuntimeEx` uses.
|
||||
* [#4584](https://github.com/leanprover/lean4/pull/4584) (backported as [b056a0](https://github.com/leanprover/lean4/commit/b056a0b395bb728512a3f3e83bf9a093059d4301)) adapts kernel interruption to the new cancellation system.
|
||||
* [b056a0](https://github.com/leanprover/lean4/commit/b056a0b395bb728512a3f3e83bf9a093059d4301) adapts kernel interruption to the new cancellation system.
|
||||
* Cleanup: [#4112](https://github.com/leanprover/lean4/pull/4112), [#4126](https://github.com/leanprover/lean4/pull/4126), [#4091](https://github.com/leanprover/lean4/pull/4091), [#4139](https://github.com/leanprover/lean4/pull/4139), [#4153](https://github.com/leanprover/lean4/pull/4153).
|
||||
* Tests: [030406](https://github.com/leanprover/lean4/commit/03040618b8f9b35b7b757858483e57340900cdc4), [#4133](https://github.com/leanprover/lean4/pull/4133).
|
||||
|
||||
@@ -940,7 +600,7 @@ While most changes could be considered to be a breaking change, this section mak
|
||||
In particular, tactics embedded in the type will no longer make use of the type of `value` in expressions such as `let x : type := value; body`.
|
||||
* Now functions defined by well-founded recursion are marked with `@[irreducible]` by default ([#4061](https://github.com/leanprover/lean4/pull/4061)).
|
||||
Existing proofs that hold by definitional equality (e.g. `rfl`) can be
|
||||
rewritten to explicitly unfold the function definition (using `simp`,
|
||||
rewritten to explictly unfold the function definition (using `simp`,
|
||||
`unfold`, `rw`), or the recursive function can be temporarily made
|
||||
semireducible (using `unseal f in` before the command), or the function
|
||||
definition itself can be marked as `@[semireducible]` to get the previous
|
||||
@@ -1559,7 +1219,7 @@ v4.7.0
|
||||
and `BitVec` as we begin making the APIs and simp normal forms for these types
|
||||
more complete and consistent.
|
||||
4. Laying the groundwork for the Std roadmap, as a library focused on
|
||||
essential datatypes not provided by the core language (e.g. `RBMap`)
|
||||
essential datatypes not provided by the core langauge (e.g. `RBMap`)
|
||||
and utilities such as basic IO.
|
||||
While we have achieved most of our initial aims in `v4.7.0-rc1`,
|
||||
some upstreaming will continue over the coming months.
|
||||
@@ -1570,7 +1230,7 @@ v4.7.0
|
||||
There is now kernel support for these functions.
|
||||
[#3376](https://github.com/leanprover/lean4/pull/3376).
|
||||
|
||||
* `omega`, our integer linear arithmetic tactic, is now available in the core language.
|
||||
* `omega`, our integer linear arithmetic tactic, is now availabe in the core langauge.
|
||||
* It is supplemented by a preprocessing tactic `bv_omega` which can solve goals about `BitVec`
|
||||
which naturally translate into linear arithmetic problems.
|
||||
[#3435](https://github.com/leanprover/lean4/pull/3435).
|
||||
@@ -1663,11 +1323,11 @@ v4.6.0
|
||||
/-
|
||||
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
|
||||
* The constructor `.done` instructs `simp` that the result does
|
||||
not need to be simplified further.
|
||||
not need to be simplied further.
|
||||
* The constructor `.visit` instructs `simp` to visit the resulting expression.
|
||||
* The constructor `.continue` instructs `simp` to try other simplification procedures.
|
||||
|
||||
All three constructors take a `Result`. The `.continue` constructor may also take `none`.
|
||||
All three constructors take a `Result`. The `.continue` contructor may also take `none`.
|
||||
`Result` has two fields `expr` (the new expression), and `proof?` (an optional proof).
|
||||
If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`.
|
||||
-/
|
||||
@@ -1879,7 +1539,7 @@ v4.5.0
|
||||
---------
|
||||
|
||||
* Modify the lexical syntax of string literals to have string gaps, which are escape sequences of the form `"\" newline whitespace*`.
|
||||
These have the interpretation of an empty string and allow a string to flow across multiple lines without introducing additional whitespace.
|
||||
These have the interpetation of an empty string and allow a string to flow across multiple lines without introducing additional whitespace.
|
||||
The following is equivalent to `"this is a string"`.
|
||||
```lean
|
||||
"this is \
|
||||
@@ -1902,7 +1562,7 @@ v4.5.0
|
||||
|
||||
If the well-founded relation you want to use is not the one that the
|
||||
`WellFoundedRelation` type class would infer for your termination argument,
|
||||
you can use `WellFounded.wrap` from the std library to explicitly give one:
|
||||
you can use `WellFounded.wrap` from the std libarary to explicitly give one:
|
||||
```diff
|
||||
-termination_by' ⟨r, hwf⟩
|
||||
+termination_by x => hwf.wrap x
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[0829/202002.254:ERROR:crashpad_client_win.cc(868)] not connected
|
||||
@@ -73,7 +73,7 @@ update the archived C source code of the stage 0 compiler in `stage0/src`.
|
||||
The github repository will automatically update stage0 on `master` once
|
||||
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
|
||||
|
||||
If you have write access to the lean4 repository, you can also manually
|
||||
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/leanprover/lean4/actions/workflows/update-stage0.yml>
|
||||
or using Github CLI with
|
||||
|
||||
@@ -5,7 +5,7 @@ Some notes on how to debug Lean, which may also be applicable to debugging Lean
|
||||
|
||||
## Tracing
|
||||
|
||||
In `CoreM` and derived monads, we use `trace[traceCls] "msg with {interpolations}"` to fill the structured trace viewable with `set_option trace.traceCls true`.
|
||||
In `CoreM` and derived monads, we use `trace![traceCls] "msg with {interpolations}"` to fill the structured trace viewable with `set_option trace.traceCls true`.
|
||||
New trace classes have to be registered using `registerTraceClass` first.
|
||||
|
||||
Notable trace classes:
|
||||
@@ -22,9 +22,7 @@ Notable trace classes:
|
||||
|
||||
In pure contexts or when execution is aborted before the messages are finally printed, one can instead use the term `dbg_trace "msg with {interpolations}"; val` (`;` can also be replaced by a newline), which will print the message to stderr before evaluating `val`. `dbgTraceVal val` can be used as a shorthand for `dbg_trace "{val}"; val`.
|
||||
Note that if the return value is not actually used, the trace code is silently dropped as well.
|
||||
|
||||
By default, such stderr output is buffered and shown as messages after a command has been elaborated, which is necessary to ensure deterministic ordering of messages under parallelism.
|
||||
If Lean aborts the process before it can finish the command or takes too long to do that, using `-DstderrAsMessages=false` avoids this buffering and shows `dbg_trace` output (but not `trace`s or other diagnostics) immediately.
|
||||
In the language server, stderr output is buffered and shown as messages after a command has been elaborated, unless the option `server.stderrAsMessages` is deactivated.
|
||||
|
||||
## Debuggers
|
||||
|
||||
|
||||
@@ -71,12 +71,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [Verso](https://github.com/leanprover/verso)
|
||||
- Dependencies: exist, but they're not part of the release workflow
|
||||
- The `SubVerso` dependency should be compatible with _every_ Lean release simultaneously, rather than following this 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
|
||||
@@ -153,31 +147,33 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
|
||||
- This step can take up to an hour.
|
||||
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/
|
||||
- Verify that the release is marked as a prerelease (this should have been done automatically by the CI release job).
|
||||
- In the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
|
||||
- Edit the release notes on Github to select the "Set as a pre-release box".
|
||||
- If release notes have been written already, copy the section of `RELEASES.md` for this version into the Github release notes
|
||||
and use the title "Changes since v4.6.0 (from RELEASES.md)".
|
||||
- Otherwise, in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
|
||||
This will add a list of all the commits since the last stable version.
|
||||
- Delete anything already mentioned in the hand-written release notes above.
|
||||
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
|
||||
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
|
||||
but for minor items don't put any work in expanding on commit messages.
|
||||
- (How we want to release notes to look is evolving: please update this section if it looks wrong!)
|
||||
- Next, we will move a curated list of downstream repos to the release candidate.
|
||||
- This assumes that for each repository either:
|
||||
* There is already a *reviewed* branch `bump/v4.7.0` containing the required adaptations.
|
||||
The preparation of this branch is beyond the scope of this document.
|
||||
* The repository does not need any changes to move to the new version.
|
||||
- This assumes that there is already a *reviewed* branch `bump/v4.7.0` on each repository
|
||||
containing the required adaptations (or no adaptations are required).
|
||||
The preparation of this branch is beyond the scope of this document.
|
||||
- For each of the target repositories:
|
||||
- If the repository does not need any changes (i.e. `bump/v4.7.0` does not exist) then create
|
||||
a new PR updating `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1` and running `lake update`.
|
||||
- Otherwise:
|
||||
- Checkout the `bump/v4.7.0` branch.
|
||||
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
|
||||
- `git merge origin/master`
|
||||
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
|
||||
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
|
||||
back to `master` or `main`, and run `lake update` for those dependencies.
|
||||
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
|
||||
- `git commit`
|
||||
- `git push`
|
||||
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
|
||||
or notify the maintainers that it is ready to go.
|
||||
- Once the PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
|
||||
- Checkout the `bump/v4.7.0` branch.
|
||||
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
|
||||
- `git merge origin/master`
|
||||
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
|
||||
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
|
||||
back to `master` or `main`, and run `lake update` for those dependencies.
|
||||
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
|
||||
- `git commit`
|
||||
- `git push`
|
||||
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
|
||||
or notify the maintainers that it is ready to go.
|
||||
- Once this PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
|
||||
- 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!
|
||||
@@ -198,7 +194,7 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
finalized release notes from the `releases/v4.6.0` branch.
|
||||
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
|
||||
```
|
||||
Release candidate, release notes will be copied from the branch `releases/v4.7.0` once completed.
|
||||
Release candidate, release notes will be copied from `branch releases/v4.7.0` once completed.
|
||||
```
|
||||
and inserts the following section before that section:
|
||||
```
|
||||
@@ -207,8 +203,6 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
Development in progress.
|
||||
```
|
||||
- Removes all the entries from the `./releases_drafts/` folder.
|
||||
- Titled "chore: begin development cycle for v4.8.0"
|
||||
|
||||
|
||||
## Time estimates:
|
||||
Slightly longer than the corresponding steps for a stable release.
|
||||
|
||||
@@ -18,7 +18,7 @@ def ctor (mvarId : MVarId) (idx : Nat) : MetaM (List MVarId) := do
|
||||
else if h : idx - 1 < ctors.length then
|
||||
mvarId.apply (.const ctors[idx - 1] us)
|
||||
else
|
||||
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} constructors"
|
||||
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors"
|
||||
|
||||
open Elab Tactic
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ We now define the constant folding optimization that traverses a term if replace
|
||||
/-!
|
||||
The correctness of the `Term.constFold` is proved using induction, case-analysis, and the term simplifier.
|
||||
We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to
|
||||
use hypotheses such as `a = b` as rewriting/simplifications rules.
|
||||
use hypotheses such as `a = b` as rewriting/simplications rules.
|
||||
We use the `split` to break the nested `match` expression in the `plus` case into two cases.
|
||||
The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`.
|
||||
The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in
|
||||
|
||||
@@ -225,7 +225,7 @@ We now define the constant folding optimization that traverses a term if replace
|
||||
/-!
|
||||
The correctness of the `constFold` is proved using induction, case-analysis, and the term simplifier.
|
||||
We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to
|
||||
use hypotheses such as `a = b` as rewriting/simplifications rules.
|
||||
use hypotheses such as `a = b` as rewriting/simplications rules.
|
||||
We use the `split` to break the nested `match` expression in the `plus` case into two cases.
|
||||
The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`.
|
||||
The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in
|
||||
|
||||
@@ -29,7 +29,7 @@ inductive HasType : Expr → Ty → Prop
|
||||
|
||||
/-!
|
||||
We can easily show that if `e` has type `t₁` and type `t₂`, then `t₁` and `t₂` must be equal
|
||||
by using the `cases` tactic. This tactic creates a new subgoal for every constructor,
|
||||
by using the the `cases` tactic. This tactic creates a new subgoal for every constructor,
|
||||
and automatically discharges unreachable cases. The tactic combinator `tac₁ <;> tac₂` applies
|
||||
`tac₂` to each subgoal produced by `tac₁`. Then, the tactic `rfl` is used to close all produced
|
||||
goals using reflexivity.
|
||||
@@ -82,7 +82,7 @@ theorem Expr.typeCheck_correct (h₁ : HasType e ty) (h₂ : e.typeCheck ≠ .un
|
||||
/-!
|
||||
Now, we prove that if `Expr.typeCheck e` returns `Maybe.unknown`, then forall `ty`, `HasType e ty` does not hold.
|
||||
The notation `e.typeCheck` is sugar for `Expr.typeCheck e`. Lean can infer this because we explicitly said that `e` has type `Expr`.
|
||||
The proof is by induction on `e` and case analysis. The tactic `rename_i` is used to rename "inaccessible" variables.
|
||||
The proof is by induction on `e` and case analysis. The tactic `rename_i` is used to to rename "inaccessible" variables.
|
||||
We say a variable is inaccessible if it is introduced by a tactic (e.g., `cases`) or has been shadowed by another variable introduced
|
||||
by the user. Note that the tactic `simp [typeCheck]` is applied to all goal generated by the `induction` tactic, and closes
|
||||
the cases corresponding to the constructors `Expr.nat` and `Expr.bool`.
|
||||
|
||||
@@ -4,18 +4,15 @@ open Lean Widget
|
||||
/-!
|
||||
# The user-widgets system
|
||||
|
||||
Proving and programming are inherently interactive tasks.
|
||||
Lots of mathematical objects and data structures are visual in nature.
|
||||
*User widgets* let you associate custom interactive UIs
|
||||
with sections of a Lean document.
|
||||
User widgets are rendered in the Lean infoview.
|
||||
Proving and programming are inherently interactive tasks. Lots of mathematical objects and data
|
||||
structures are visual in nature. *User widgets* let you associate custom interactive UIs with
|
||||
sections of a Lean document. User widgets are rendered in the Lean infoview.
|
||||
|
||||

|
||||
|
||||
## Trying it out
|
||||
|
||||
To try it out, type in the following code and place your cursor over the `#widget` command.
|
||||
You can also [view this manual entry in the online editor](https://live.lean-lang.org/#url=https%3A%2F%2Fraw.githubusercontent.com%2Fleanprover%2Flean4%2Fmaster%2Fdoc%2Fexamples%2Fwidgets.lean).
|
||||
To try it out, simply type in the following code and place your cursor over the `#widget` command.
|
||||
-/
|
||||
|
||||
@[widget_module]
|
||||
@@ -24,37 +21,38 @@ def helloWidget : Widget.Module where
|
||||
import * as React from 'react';
|
||||
export default function(props) {
|
||||
const name = props.name || 'world'
|
||||
return React.createElement('p', {}, 'Hello ' + name + '!')
|
||||
return React.createElement('p', {}, name + '!')
|
||||
}"
|
||||
|
||||
#widget helloWidget
|
||||
|
||||
/-!
|
||||
If you want to dive into a full sample right away, check out
|
||||
[`Rubiks`](https://github.com/leanprover-community/ProofWidgets4/blob/main/ProofWidgets/Demos/Rubiks.lean).
|
||||
This sample uses higher-level widget components from the ProofWidgets library.
|
||||
|
||||
[`RubiksCube`](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/).
|
||||
Below, we'll explain the system piece by piece.
|
||||
|
||||
⚠️ WARNING: All of the user widget APIs are **unstable** and subject to breaking changes.
|
||||
|
||||
## Widget modules and instances
|
||||
## Widget sources and instances
|
||||
|
||||
A [widget module](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/UserWidget.html#Lean.Widget.Module)
|
||||
is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
|
||||
that can execute in the Lean infoview.
|
||||
Most widget modules export a [React component](https://reactjs.org/docs/components-and-props.html)
|
||||
as the piece of user interface to be rendered.
|
||||
To access React, the module can use `import * as React from 'react'`.
|
||||
Our first example of a widget module is `helloWidget` above.
|
||||
Widget modules must be registered with the `@[widget_module]` attribute.
|
||||
A *widget source* is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
|
||||
which exports a [React component](https://reactjs.org/docs/components-and-props.html). To access
|
||||
React, the module must use `import * as React from 'react'`. Our first example of a widget source
|
||||
is of course the value of `helloWidget.javascript`.
|
||||
|
||||
A [widget instance](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/Types.html#Lean.Widget.WidgetInstance)
|
||||
is then the identifier of a widget module (e.g. `` `helloWidget ``)
|
||||
bundled with a value for its props.
|
||||
This value is passed as the argument to the React component.
|
||||
In our first invocation of `#widget`, we set it to `.null`.
|
||||
Try out what happens when you type in:
|
||||
We can register a widget source with the `@[widget]` attribute, giving it a friendlier name
|
||||
in the `name` field. This is bundled together in a `UserWidgetDefinition`.
|
||||
|
||||
A *widget instance* is then the identifier of a `UserWidgetDefinition` (so `` `helloWidget ``,
|
||||
not `"Hello"`) associated with a range of positions in the Lean source code. Widget instances
|
||||
are stored in the *infotree* in the same manner as other information about the source file
|
||||
such as the type of every expression. In our example, the `#widget` command stores a widget instance
|
||||
with the entire line as its range. We can think of a widget instance as an instruction for the
|
||||
infoview: "when the user places their cursor here, please render the following widget".
|
||||
|
||||
Every widget instance also contains a `props : Json` value. This value is passed as an argument
|
||||
to the React component. In our first invocation of `#widget`, we set it to `.null`. Try out what
|
||||
happens when you type in:
|
||||
-/
|
||||
|
||||
structure HelloWidgetProps where
|
||||
@@ -64,37 +62,21 @@ structure HelloWidgetProps where
|
||||
#widget helloWidget with { name? := "<your name here>" : HelloWidgetProps }
|
||||
|
||||
/-!
|
||||
Under the hood, widget instances are associated with a range of positions in the source file.
|
||||
Widget instances are stored in the *infotree*
|
||||
in the same manner as other information about the source file
|
||||
such as the type of every expression.
|
||||
In our example, the `#widget` command stores a widget instance
|
||||
with the entire line as its range.
|
||||
One can think of the infotree entry as an instruction for the infoview:
|
||||
"when the user places their cursor here, please render the following widget".
|
||||
-/
|
||||
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
|
||||
is the web-based infoview in VSCode.
|
||||
|
||||
/-!
|
||||
## Querying the Lean server
|
||||
|
||||
💡 NOTE: The RPC system presented below does not depend on JavaScript.
|
||||
However, the primary use case is the web-based infoview in VSCode.
|
||||
Besides enabling us to create cool client-side visualizations, user widgets come with the ability
|
||||
to communicate with the Lean server. Thanks to this, they have the same metaprogramming capabilities
|
||||
as custom elaborators or the tactic framework. To see this in action, let's implement a `#check`
|
||||
command as a web input form. This example assumes some familiarity with React.
|
||||
|
||||
Besides enabling us to create cool client-side visualizations,
|
||||
user widgets have the ability to communicate with the Lean server.
|
||||
Thanks to this, they have the same metaprogramming capabilities
|
||||
as custom elaborators or the tactic framework.
|
||||
To see this in action, let's implement a `#check` command as a web input form.
|
||||
This example assumes some familiarity with React.
|
||||
|
||||
The first thing we'll need is to create an *RPC method*.
|
||||
Meaning "Remote Procedure Call",this is a Lean function callable from widget code
|
||||
(possibly remotely over the internet).
|
||||
The first thing we'll need is to create an *RPC method*. Meaning "Remote Procedure Call", this
|
||||
is basically a Lean function callable from widget code (possibly remotely over the internet).
|
||||
Our method will take in the `name : Name` of a constant in the environment and return its type.
|
||||
By convention, we represent the input data as a `structure`.
|
||||
Since it will be sent over from JavaScript,
|
||||
we need `FromJson` and `ToJson` instance.
|
||||
We'll see why the position field is needed later.
|
||||
By convention, we represent the input data as a `structure`. Since it will be sent over from JavaScript,
|
||||
we need `FromJson` and `ToJson`. We'll see below why the position field is needed.
|
||||
-/
|
||||
|
||||
structure GetTypeParams where
|
||||
@@ -105,33 +87,25 @@ structure GetTypeParams where
|
||||
deriving FromJson, ToJson
|
||||
|
||||
/-!
|
||||
After its argument structure, we define the `getType` method.
|
||||
RPCs method execute in the `RequestM` monad and must return a `RequestTask α`
|
||||
where `α` is the "actual" return type.
|
||||
The `Task` is so that requests can be handled concurrently.
|
||||
As a first guess, we'd use `Expr` as `α`.
|
||||
However, expressions in general can be large objects
|
||||
which depend on an `Environment` and `LocalContext`.
|
||||
Thus we cannot directly serialize an `Expr` and send it to JavaScript.
|
||||
Instead, there are two options:
|
||||
After its arguments, we define the `getType` method. Every RPC method executes in the `RequestM`
|
||||
monad and must return a `RequestTask α` where `α` is its "actual" return type. The `Task` is so
|
||||
that requests can be handled concurrently. A first guess for `α` might be `Expr`. However,
|
||||
expressions in general can be large objects which depend on an `Environment` and `LocalContext`.
|
||||
Thus we cannot directly serialize an `Expr` and send it to the widget. Instead, there are two
|
||||
options:
|
||||
- One is to send a *reference* which points to an object residing on the server. From JavaScript's
|
||||
point of view, references are entirely opaque, but they can be sent back to other RPC methods for
|
||||
further processing.
|
||||
- Two is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
|
||||
This representation contains extra data which the infoview uses for interactivity. We take this
|
||||
strategy here.
|
||||
|
||||
- One is to send a *reference* which points to an object residing on the server.
|
||||
From JavaScript's point of view, references are entirely opaque,
|
||||
but they can be sent back to other RPC methods for further processing.
|
||||
- The other is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
|
||||
This representation contains extra data which the infoview uses for interactivity.
|
||||
We take this strategy here.
|
||||
|
||||
RPC methods execute in the context of a file,
|
||||
but not of any particular `Environment`,
|
||||
so they don't know about the available `def`initions and `theorem`s.
|
||||
Thus, we need to pass in a position at which we want to use the local `Environment`.
|
||||
This is why we store it in `GetTypeParams`.
|
||||
The `withWaitFindSnapAtPos` method launches a concurrent computation
|
||||
whose job is to find such an `Environment` for us,
|
||||
in the form of a `snap : Snapshot`.
|
||||
With this in hand, we can call `MetaM` procedures
|
||||
to find out the type of `name` and pretty-print it.
|
||||
RPC methods execute in the context of a file, but not any particular `Environment` so they don't
|
||||
know about the available `def`initions and `theorem`s. Thus, we need to pass in a position at which
|
||||
we want to use the local `Environment`. This is why we store it in `GetTypeParams`. The `withWaitFindSnapAtPos`
|
||||
method launches a concurrent computation whose job is to find such an `Environment` and a bit
|
||||
more information for us, in the form of a `snap : Snapshot`. With this in hand, we can call
|
||||
`MetaM` procedures to find out the type of `name` and pretty-print it.
|
||||
-/
|
||||
|
||||
open Server RequestM in
|
||||
@@ -147,22 +121,18 @@ def getType (params : GetTypeParams) : RequestM (RequestTask CodeWithInfos) :=
|
||||
/-!
|
||||
## Using infoview components
|
||||
|
||||
Now that we have all we need on the server side, let's write the widget module.
|
||||
By importing `@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
|
||||
For example, the `<InteractiveCode>` component displays expressions
|
||||
with `term : type` tooltips as seen in the goal view.
|
||||
We will use it to implement our custom `#check` display.
|
||||
Now that we have all we need on the server side, let's write the widget source. By importing
|
||||
`@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
|
||||
For example, the `<InteractiveCode>` component displays expressions with `term : type` tooltips
|
||||
as seen in the goal view. We will use it to implement our custom `#check` display.
|
||||
|
||||
⚠️ WARNING: Like the other widget APIs, the infoview JS API is **unstable** and subject to breaking changes.
|
||||
|
||||
The code below demonstrates useful parts of the API.
|
||||
To make RPC method calls, we invoke the `useRpcSession` hook.
|
||||
The `useAsync` helper packs the results of an RPC call into an `AsyncState` structure
|
||||
which indicates whether the call has resolved successfully,
|
||||
has returned an error, or is still in-flight.
|
||||
Based on this we either display an `InteractiveCode` component with the result,
|
||||
`mapRpcError` the error in order to turn it into a readable message,
|
||||
or show a `Loading..` message, respectively.
|
||||
The code below demonstrates useful parts of the API. To make RPC method calls, we use the `RpcContext`.
|
||||
The `useAsync` helper packs the results of a call into an `AsyncState` structure which indicates
|
||||
whether the call has resolved successfully, has returned an error, or is still in-flight. Based
|
||||
on this we either display an `InteractiveCode` with the type, `mapRpcError` the error in order
|
||||
to turn it into a readable message, or show a `Loading..` message, respectively.
|
||||
-/
|
||||
|
||||
@[widget_module]
|
||||
@@ -170,10 +140,10 @@ def checkWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
const e = React.createElement;
|
||||
import { useRpcSession, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
|
||||
import { RpcContext, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
|
||||
|
||||
export default function(props) {
|
||||
const rs = useRpcSession()
|
||||
const rs = React.useContext(RpcContext)
|
||||
const [name, setName] = React.useState('getType')
|
||||
|
||||
const st = useAsync(() =>
|
||||
@@ -189,7 +159,7 @@ export default function(props) {
|
||||
"
|
||||
|
||||
/-!
|
||||
We can now try out the widget.
|
||||
Finally we can try out the widget.
|
||||
-/
|
||||
|
||||
#widget checkWidget
|
||||
@@ -199,31 +169,30 @@ We can now try out the widget.
|
||||
|
||||
## Building widget sources
|
||||
|
||||
While typing JavaScript inline is fine for a simple example,
|
||||
for real developments we want to use packages from NPM, a proper build system, and JSX.
|
||||
Thus, most actual widget sources are built with Lake and NPM.
|
||||
They consist of multiple files and may import libraries which don't work as ESModules by default.
|
||||
On the other hand a widget module must be a single, self-contained ESModule in the form of a string.
|
||||
Readers familiar with web development may already have guessed that to obtain such a string, we need a *bundler*.
|
||||
Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
|
||||
and [`esbuild`](https://esbuild.github.io/).
|
||||
If we go with `rollup.js`, to make a widget work with the infoview we need to:
|
||||
While typing JavaScript inline is fine for a simple example, for real developments we want to use
|
||||
packages from NPM, a proper build system, and JSX. Thus, most actual widget sources are built with
|
||||
Lake and NPM. They consist of multiple files and may import libraries which don't work as ESModules
|
||||
by default. On the other hand a widget source must be a single, self-contained ESModule in the form
|
||||
of a string. Readers familiar with web development may already have guessed that to obtain such a
|
||||
string, we need a *bundler*. Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
|
||||
and [`esbuild`](https://esbuild.github.io/). If we go with `rollup.js`, to make a widget work with
|
||||
the infoview we need to:
|
||||
- Set [`output.format`](https://rollupjs.org/guide/en/#outputformat) to `'es'`.
|
||||
- [Externalize](https://rollupjs.org/guide/en/#external) `react`, `react-dom`, `@leanprover/infoview`.
|
||||
These libraries are already loaded by the infoview so they should not be bundled.
|
||||
|
||||
ProofWidgets provides a working `rollup.js` build configuration in
|
||||
[rollup.config.js](https://github.com/leanprover-community/ProofWidgets4/blob/main/widget/rollup.config.js).
|
||||
In the RubiksCube sample, we provide a working `rollup.js` build configuration in
|
||||
[rollup.config.js](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/widget/rollup.config.js).
|
||||
|
||||
## Inserting text
|
||||
|
||||
Besides making RPC calls, widgets can instruct the editor to carry out certain actions.
|
||||
We can insert text, copy text to the clipboard, or highlight a certain location in the document.
|
||||
To do this, use the `EditorContext` React context.
|
||||
This will return an `EditorConnection`
|
||||
whose `api` field contains a number of methods that interact with the editor.
|
||||
We can also instruct the editor to insert text, copy text to the clipboard, or
|
||||
reveal a certain location in the document.
|
||||
To do this, use the `React.useContext(EditorContext)` React context.
|
||||
This will return an `EditorConnection` whose `api` field contains a number of methods to
|
||||
interact with the text editor.
|
||||
|
||||
The full API can be viewed [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52).
|
||||
You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
|
||||
-/
|
||||
|
||||
@[widget_module]
|
||||
@@ -243,4 +212,6 @@ export default function(props) {
|
||||
}
|
||||
"
|
||||
|
||||
/-! Finally, we can try this out: -/
|
||||
|
||||
#widget insertTextWidget
|
||||
|
||||
@@ -396,7 +396,7 @@ Every expression in Lean has a natural computational interpretation, unless it i
|
||||
|
||||
* *β-reduction* : An expression ``(λ x, t) s`` β-reduces to ``t[s/x]``, that is, the result of replacing ``x`` by ``s`` in ``t``.
|
||||
* *ζ-reduction* : An expression ``let x := s in t`` ζ-reduces to ``t[s/x]``.
|
||||
* *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to ``t``.
|
||||
* *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to to ``t``.
|
||||
* *ι-reduction* : When a function defined by recursion on an inductive type is applied to an element given by an explicit constructor, the result ι-reduces to the specified function value, as described in [Inductive Types](inductive.md).
|
||||
|
||||
The reduction relation is transitive, which is to say, is ``s`` reduces to ``s'`` and ``t`` reduces to ``t'``, then ``s t`` reduces to ``s' t'``, ``λ x, s`` reduces to ``λ x, s'``, and so on. If ``s`` and ``t`` reduce to a common term, they are said to be *definitionally equal*. Definitional equality is defined to be the smallest equivalence relation that satisfies all these properties and also includes α-equivalence and the following two relations:
|
||||
|
||||
138
doc/flake.lock
generated
138
doc/flake.lock
generated
@@ -18,15 +18,12 @@
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"lastModified": 1656928814,
|
||||
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -38,12 +35,13 @@
|
||||
"lean": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-old": "nixpkgs-old"
|
||||
"lean4-mode": "lean4-mode",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 0,
|
||||
"narHash": "sha256-saRAtQ6VautVXKDw1XH35qwP0KEBKTKZbg/TRa4N9Vw=",
|
||||
"narHash": "sha256-YnYbmG0oou1Q/GE4JbMNb8/yqUVXBPIvcdQQJHBqtPk=",
|
||||
"path": "../.",
|
||||
"type": "path"
|
||||
},
|
||||
@@ -52,6 +50,22 @@
|
||||
"type": "path"
|
||||
}
|
||||
},
|
||||
"lean4-mode": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1659020985,
|
||||
"narHash": "sha256-+dRaXB7uvN/weSZiKcfSKWhcdJVNg9Vg8k0pJkDNjpc=",
|
||||
"owner": "leanprover",
|
||||
"repo": "lean4-mode",
|
||||
"rev": "37d5c99b7b29c80ab78321edd6773200deb0bca6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "leanprover",
|
||||
"repo": "lean4-mode",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"leanInk": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -69,6 +83,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1633514407,
|
||||
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mdBook": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -85,13 +115,65 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1657097207,
|
||||
"narHash": "sha256-SmeGmjWM3fEed3kQjqIAO8VpGmkC2sL1aPE7kKpK650=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nix",
|
||||
"rev": "f6316b49a0c37172bca87ede6ea8144d7d89832f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1710889954,
|
||||
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
|
||||
"lastModified": 1653988320,
|
||||
"narHash": "sha256-ZaqFFsSDipZ6KVqriwM34T739+KLYJvNmCWzErjAg7c=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
|
||||
"rev": "2fa57ed190fd6c7c746319444f34b5917666e5c1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-22.05-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1657208011,
|
||||
"narHash": "sha256-BlIFwopAykvdy1DYayEkj6ZZdkn+cVgPNX98QVLc0jM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2770cc0b1e8faa0e20eb2c6aea64c256a706d4f2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -101,23 +183,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-old": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1581379743,
|
||||
"narHash": "sha256-i1XCn9rKuLjvCdu2UeXKzGLF6IuQePQKFt4hEKRU5oc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-19.03",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"alectryon": "alectryon",
|
||||
@@ -129,21 +194,6 @@
|
||||
"leanInk": "leanInk",
|
||||
"mdBook": "mdBook"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
};
|
||||
|
||||
outputs = inputs@{ self, ... }: inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
with inputs.lean.packages.${system}.deprecated; with nixpkgs;
|
||||
with inputs.lean.packages.${system}; with nixpkgs;
|
||||
let
|
||||
doc-src = lib.sourceByRegex ../. ["doc.*" "tests(/lean(/beginEndAsMacro.lean)?)?"];
|
||||
in {
|
||||
@@ -44,6 +44,21 @@
|
||||
mdbook build -d $out
|
||||
'';
|
||||
};
|
||||
# We use a separate derivation instead of `checkPhase` so we can push it but not `doc` to the binary cache
|
||||
test = stdenv.mkDerivation {
|
||||
name ="lean-doc-test";
|
||||
src = doc-src;
|
||||
buildInputs = [ lean-mdbook stage1.Lean.lean-package strace ];
|
||||
patchPhase = ''
|
||||
cd doc
|
||||
patchShebangs test
|
||||
'';
|
||||
buildPhase = ''
|
||||
mdbook test
|
||||
touch $out
|
||||
'';
|
||||
dontInstall = true;
|
||||
};
|
||||
leanInk = (buildLeanPackage {
|
||||
name = "Main";
|
||||
src = inputs.leanInk;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@@ -8,7 +8,6 @@ Requirements
|
||||
- C++14 compatible compiler
|
||||
- [CMake](http://www.cmake.org)
|
||||
- [GMP (GNU multiprecision library)](http://gmplib.org/)
|
||||
- [LibUV](https://libuv.org/)
|
||||
|
||||
Platform-Specific Setup
|
||||
-----------------------
|
||||
@@ -28,9 +27,9 @@ Setting up a basic parallelized release build:
|
||||
git clone https://github.com/leanprover/lean4
|
||||
cd lean4
|
||||
cmake --preset release
|
||||
make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)
|
||||
make -C build/release -j$(nproc) # see below for macOS
|
||||
```
|
||||
You can replace `$(nproc || sysctl -n hw.logicalcpu)` with the desired parallelism amount.
|
||||
You can replace `$(nproc)`, which is not available on macOS and some alternative shells, with the desired parallelism amount.
|
||||
|
||||
The above commands will compile the Lean library and binaries into the
|
||||
`stage1` subfolder; see below for details.
|
||||
|
||||
@@ -25,7 +25,7 @@ MSYS2 has a package management system, [pacman][pacman], which is used in Arch L
|
||||
Here are the commands to install all dependencies needed to compile Lean on your machine.
|
||||
|
||||
```bash
|
||||
pacman -S make python mingw-w64-x86_64-cmake mingw-w64-x86_64-clang mingw-w64-x86_64-ccache mingw-w64-x86_64-libuv mingw-w64-x86_64-gmp git unzip diffutils binutils
|
||||
pacman -S make python mingw-w64-x86_64-cmake mingw-w64-x86_64-clang mingw-w64-x86_64-ccache git unzip diffutils binutils
|
||||
```
|
||||
|
||||
You should now be able to run these commands:
|
||||
@@ -64,7 +64,6 @@ they are installed in your MSYS setup:
|
||||
- libgcc_s_seh-1.dll
|
||||
- libstdc++-6.dll
|
||||
- libgmp-10.dll
|
||||
- libuv-1.dll
|
||||
- libwinpthread-1.dll
|
||||
|
||||
The following linux command will do that:
|
||||
|
||||
@@ -32,16 +32,15 @@ following to use `g++`.
|
||||
cmake -DCMAKE_CXX_COMPILER=g++ ...
|
||||
```
|
||||
|
||||
## Required Packages: CMake, GMP, libuv
|
||||
## Required Packages: CMake, GMP
|
||||
|
||||
```bash
|
||||
brew install cmake
|
||||
brew install gmp
|
||||
brew install libuv
|
||||
```
|
||||
|
||||
## Recommended Packages: CCache
|
||||
|
||||
```bash
|
||||
brew install ccache
|
||||
```
|
||||
```
|
||||
@@ -8,5 +8,5 @@ follow the [generic build instructions](index.md).
|
||||
## Basic packages
|
||||
|
||||
```bash
|
||||
sudo apt-get install git libgmp-dev libuv1-dev cmake ccache clang
|
||||
sudo apt-get install git libgmp-dev cmake ccache clang
|
||||
```
|
||||
|
||||
@@ -171,7 +171,7 @@ of data contained in the container resulting in a new container that has the sam
|
||||
|
||||
`u <*> pure y = pure (. y) <*> u`.
|
||||
|
||||
This law is a little more complicated, so don't sweat it too much. It states that the order that
|
||||
This law is is a little more complicated, so don't sweat it too much. It states that the order that
|
||||
you wrap things shouldn't matter. One the left, you apply any applicative `u` over a pure wrapped
|
||||
object. On the right, you first wrap a function applying the object as an argument. Note that `(·
|
||||
y)` is short hand for: `fun f => f y`. Then you apply this to the first applicative `u`. These
|
||||
|
||||
@@ -5,11 +5,11 @@ See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
|
||||
|
||||
1. Install [VS Code](https://code.visualstudio.com/).
|
||||
|
||||
1. Launch VS Code and install the `Lean 4` extension by clicking on the 'Extensions' sidebar entry and searching for 'Lean 4'.
|
||||
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
|
||||
|
||||

|
||||
|
||||
1. Open the Lean 4 setup guide by creating a new text file using 'File > New Text File' (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting 'Documentation… > Docs: Show Setup Guide'.
|
||||
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Docs: Show Setup Guide".
|
||||
|
||||

|
||||
|
||||
|
||||
120
flake.lock
generated
120
flake.lock
generated
@@ -1,5 +1,21 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
@@ -18,35 +34,72 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"lean4-mode": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1710889954,
|
||||
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
|
||||
"lastModified": 1709737301,
|
||||
"narHash": "sha256-uT9JN2kLNKJK9c/S/WxLjiHmwijq49EgLb+gJUSDpz0=",
|
||||
"owner": "leanprover",
|
||||
"repo": "lean4-mode",
|
||||
"rev": "f1f24c15134dee3754b82c9d9924866fe6bc6b9f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"owner": "leanprover",
|
||||
"repo": "lean4-mode",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-cadical": {
|
||||
"libgit2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1722221733,
|
||||
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
|
||||
"lastModified": 1697646580,
|
||||
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"libgit2": "libgit2",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1711102798,
|
||||
"narHash": "sha256-CXOIJr8byjolqG7eqCLa+Wfi7rah62VmLoqSXENaZnw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"repo": "nix",
|
||||
"rev": "a22328066416650471c3545b0b138669ea212ab4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1709083642,
|
||||
"narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"rev": "b550fe4b4776908ac2a861124307045f8e717c8e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -67,11 +120,44 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1710889954,
|
||||
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-cadical": "nixpkgs-cadical",
|
||||
"lean4-mode": "lean4-mode",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-old": "nixpkgs-old"
|
||||
}
|
||||
},
|
||||
|
||||
73
flake.nix
73
flake.nix
@@ -1,36 +1,48 @@
|
||||
{
|
||||
description = "Lean development flake. Not intended for end users.";
|
||||
description = "Lean interactive theorem prover";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
# old nixpkgs used for portable release with older glibc (2.27)
|
||||
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
|
||||
inputs.nixpkgs-old.flake = false;
|
||||
# for cadical 1.9.5; sync with CMakeLists.txt
|
||||
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.nix.url = "github:NixOS/nix";
|
||||
inputs.lean4-mode = {
|
||||
url = "github:leanprover/lean4-mode";
|
||||
flake = false;
|
||||
};
|
||||
# used *only* by `stage0-from-input` below
|
||||
#inputs.lean-stage0 = {
|
||||
# url = github:leanprover/lean4;
|
||||
# inputs.nixpkgs.follows = "nixpkgs";
|
||||
# inputs.flake-utils.follows = "flake-utils";
|
||||
# inputs.nix.follows = "nix";
|
||||
# inputs.lean4-mode.follows = "lean4-mode";
|
||||
#};
|
||||
|
||||
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
|
||||
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, nix, lean4-mode, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
# for `vscode-with-extensions`
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
# An old nixpkgs for creating releases with an old glibc
|
||||
pkgsDist-old = import nixpkgs-old { inherit system; };
|
||||
# An old nixpkgs for creating releases with an old glibc
|
||||
pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
|
||||
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
|
||||
cadical = if pkgs.stdenv.isLinux then
|
||||
# use statically-linked cadical on Linux to avoid glibc versioning troubles
|
||||
pkgsCadical.pkgsStatic.cadical.overrideAttrs { doCheck = false; }
|
||||
else pkgsCadical.cadical;
|
||||
|
||||
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; };
|
||||
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; inherit nix lean4-mode; };
|
||||
|
||||
devShellWithDist = pkgsDist: pkgs.mkShell.override {
|
||||
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
|
||||
} ({
|
||||
buildInputs = with pkgs; [
|
||||
cmake gmp libuv ccache cadical
|
||||
cmake gmp ccache
|
||||
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
|
||||
gdb
|
||||
# TODO: only add when proven to not affect the flakification
|
||||
#pkgs.python3
|
||||
tree # for CI
|
||||
];
|
||||
# https://github.com/NixOS/nixpkgs/issues/60919
|
||||
@@ -39,7 +51,6 @@
|
||||
CTEST_OUTPUT_ON_FAILURE = 1;
|
||||
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||
GMP = pkgsDist.gmp.override { withStatic = true; };
|
||||
LIBUV = pkgsDist.libuv.overrideAttrs (attrs: { configureFlags = ["--enable-static"]; });
|
||||
GLIBC = pkgsDist.glibc;
|
||||
GLIBC_DEV = pkgsDist.glibc.dev;
|
||||
GCC_LIB = pkgsDist.gcc.cc.lib;
|
||||
@@ -47,15 +58,41 @@
|
||||
GDB = pkgsDist.gdb;
|
||||
});
|
||||
in {
|
||||
packages = {
|
||||
# to be removed when Nix CI is not needed anymore
|
||||
inherit (lean-packages) cacheRoots test update-stage0-commit ciShell;
|
||||
deprecated = lean-packages;
|
||||
packages = lean-packages // rec {
|
||||
debug = lean-packages.override { debug = true; };
|
||||
stage0debug = lean-packages.override { stage0debug = true; };
|
||||
asan = lean-packages.override { extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address" "-DLEANC_EXTRA_FLAGS=-fsanitize=address" "-DSMALL_ALLOCATOR=OFF" "-DSYMBOLIC=OFF" ]; };
|
||||
asandebug = asan.override { debug = true; };
|
||||
tsan = lean-packages.override {
|
||||
extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=thread" "-DLEANC_EXTRA_FLAGS=-fsanitize=thread" "-DCOMPRESSED_OBJECT_HEADER=OFF" ];
|
||||
stage0 = (lean-packages.override {
|
||||
# Compressed headers currently trigger data race reports in tsan.
|
||||
# Turn them off for stage 0 as well so stage 1 can read its own stdlib.
|
||||
extraCMakeFlags = [ "-DCOMPRESSED_OBJECT_HEADER=OFF" ];
|
||||
}).stage1;
|
||||
};
|
||||
tsandebug = tsan.override { debug = true; };
|
||||
stage0-from-input = lean-packages.override {
|
||||
stage0 = pkgs.writeShellScriptBin "lean" ''
|
||||
exec ${inputs.lean-stage0.packages.${system}.lean}/bin/lean -Dinterpreter.prefer_native=false "$@"
|
||||
'';
|
||||
};
|
||||
inherit self;
|
||||
};
|
||||
defaultPackage = lean-packages.lean-all;
|
||||
|
||||
# The default development shell for working on lean itself
|
||||
devShells.default = devShellWithDist pkgs;
|
||||
devShells.oldGlibc = devShellWithDist pkgsDist-old;
|
||||
devShells.oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
|
||||
});
|
||||
|
||||
checks.lean = lean-packages.test;
|
||||
}) // rec {
|
||||
templates.pkg = {
|
||||
path = ./nix/templates/pkg;
|
||||
description = "A custom Lean package";
|
||||
};
|
||||
|
||||
defaultTemplate = templates.pkg;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
|
||||
stdenv, lib, cmake, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
|
||||
stdenv, lib, cmake, gmp, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
|
||||
... } @ args:
|
||||
with builtins;
|
||||
lib.warn "The Nix-based build is deprecated" rec {
|
||||
rec {
|
||||
inherit stdenv;
|
||||
sourceByRegex = p: rs: lib.sourceByRegex p (map (r: "(/src/)?${r}") rs);
|
||||
buildCMake = args: stdenv.mkDerivation ({
|
||||
nativeBuildInputs = [ cmake ];
|
||||
buildInputs = [ gmp libuv llvmPackages.llvm ];
|
||||
buildInputs = [ gmp llvmPackages.llvm ];
|
||||
# https://github.com/NixOS/nixpkgs/issues/60919
|
||||
hardeningDisable = [ "all" ];
|
||||
dontStrip = (args.debug or debug);
|
||||
@@ -17,7 +17,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
'';
|
||||
} // args // {
|
||||
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
|
||||
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
|
||||
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
|
||||
preConfigure = args.preConfigure or "" + ''
|
||||
# ignore absence of submodule
|
||||
sed -i 's!lake/Lake.lean!!' CMakeLists.txt
|
||||
@@ -26,7 +26,11 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
lean-bin-tools-unwrapped = buildCMake {
|
||||
name = "lean-bin-tools";
|
||||
outputs = [ "out" "leanc_src" ];
|
||||
realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "[a-z].*" ".*\.in" "Leanc\.lean" ];
|
||||
realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "cmake.*" "bin.*" "include.*" ".*\.in" "Leanc\.lean" ];
|
||||
preConfigure = ''
|
||||
touch empty.cpp
|
||||
sed -i 's/add_subdirectory.*//;s/set(LEAN_OBJS.*/set(LEAN_OBJS empty.cpp)/' CMakeLists.txt
|
||||
'';
|
||||
dontBuild = true;
|
||||
installPhase = ''
|
||||
mkdir $out $leanc_src
|
||||
@@ -41,10 +45,11 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
leancpp = buildCMake {
|
||||
name = "leancpp";
|
||||
src = src + "/src";
|
||||
buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "leanshell" "leanmain" ];
|
||||
buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "shell" ];
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
mv lib/ $out/
|
||||
mv shell/CMakeFiles/shell.dir/lean.cpp.o $out/lib
|
||||
mv runtime/libleanrt_initial-exec.a $out/lib
|
||||
'';
|
||||
};
|
||||
@@ -95,13 +100,12 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Std ]; };
|
||||
Lake = build {
|
||||
name = "Lake";
|
||||
sharedLibName = "Lake_shared";
|
||||
src = src + "/src/lake";
|
||||
deps = [ Init Lean ];
|
||||
};
|
||||
Lake-Main = build {
|
||||
name = "LakeMain";
|
||||
roots = [{ glob = "one"; mod = "LakeMain"; }];
|
||||
name = "Lake.Main";
|
||||
roots = [ "Lake.Main" ];
|
||||
executableName = "lake";
|
||||
deps = [ Lake ];
|
||||
linkFlags = lib.optional stdenv.isLinux "-rdynamic";
|
||||
@@ -118,15 +122,12 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
touch empty.c
|
||||
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
|
||||
'';
|
||||
leanshared_1 = runCommand "leanshared_1" { buildInputs = [ stdenv.cc ]; libName = "leanshared_1${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
|
||||
mkdir $out
|
||||
touch empty.c
|
||||
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
|
||||
'';
|
||||
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
|
||||
mkdir $out
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
|
||||
-Wl,--whole-archive ${leancpp}/lib/temp/libleanshell.a -lInit -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++ \
|
||||
${if stdenv.isDarwin
|
||||
then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Std.staticLib}/libStd.a -Wl,-force_load,${Lean.staticLib}/libLean.a -Wl,-force_load,${leancpp}/lib/lean/libleancpp.a ${leancpp}/lib/libleanrt_initial-exec.a -lc++"
|
||||
else "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} \
|
||||
-lm ${stdlibLinkFlags} \
|
||||
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
|
||||
-o $out/$libName
|
||||
@@ -134,18 +135,18 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
mods = foldl' (mods: pkg: mods // pkg.mods) {} stdlib;
|
||||
print-paths = Lean.makePrintPathsFor [] mods;
|
||||
leanc = writeShellScriptBin "leanc" ''
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared_1} -L${leanshared} -L${Lake.sharedLib} "$@"
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared} "$@"
|
||||
'';
|
||||
lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } ''
|
||||
mkdir -p $out/bin
|
||||
${leanc}/bin/leanc ${leancpp}/lib/temp/libleanmain.a ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* -o $out/bin/lean
|
||||
${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${libInit_shared}/* ${leanshared}/* -o $out/bin/lean
|
||||
'';
|
||||
# derivation following the directory layout of the "basic" setup, mostly useful for running tests
|
||||
lean-all = stdenv.mkDerivation {
|
||||
name = "lean-${desc}";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin $out/lib/lean
|
||||
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* ${Lake.sharedLib}/* $out/lib/lean/
|
||||
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared}/* $out/lib/lean/
|
||||
# put everything in a single final derivation so `IO.appDir` references work
|
||||
cp ${lean}/bin/lean ${leanc}/bin/leanc ${Lake-Main.executable}/bin/lake $out/bin
|
||||
# NOTE: `lndir` will not override existing `bin/leanc`
|
||||
@@ -159,7 +160,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
test = buildCMake {
|
||||
name = "lean-test-${desc}";
|
||||
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
|
||||
buildInputs = [ gmp libuv perl git cadical ];
|
||||
buildInputs = [ gmp perl git ];
|
||||
preConfigure = ''
|
||||
cd src
|
||||
'';
|
||||
@@ -170,7 +171,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
ln -sf ${lean-all}/* .
|
||||
'';
|
||||
buildPhase = ''
|
||||
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi' -j$NIX_BUILD_CORES
|
||||
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)' -j$NIX_BUILD_CORES
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
@@ -178,7 +179,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
'';
|
||||
};
|
||||
update-stage0 =
|
||||
let cTree = symlinkJoin { name = "cs"; paths = map (lib: lib.cTree) (stdlib ++ [Lake-Main]); }; in
|
||||
let cTree = symlinkJoin { name = "cs"; paths = map (lib: lib.cTree) stdlib; }; in
|
||||
writeShellScriptBin "update-stage0" ''
|
||||
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/lib/update-stage0"}
|
||||
'';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{ lean, lean-leanDeps ? lean, lean-final ? lean, leanc,
|
||||
stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, substituteAll, symlinkJoin, linkFarmFromDrvs,
|
||||
stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, lean-emacs, lean-vscode, nix, substituteAll, symlinkJoin, linkFarmFromDrvs,
|
||||
runCommand, darwin, mkShell, ... }:
|
||||
let lean-final' = lean-final; in
|
||||
lib.makeOverridable (
|
||||
@@ -30,7 +30,7 @@ lib.makeOverridable (
|
||||
pluginDeps ? [],
|
||||
# `overrideAttrs` for `buildMod`
|
||||
overrideBuildModAttrs ? null,
|
||||
debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name, sharedLibName ? libName,
|
||||
debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name,
|
||||
srcTarget ? "..#stage0", srcArgs ? "(\${args[*]})", lean-final ? lean-final' }@args:
|
||||
with builtins; let
|
||||
# "Init.Core" ~> "Init/Core"
|
||||
@@ -197,6 +197,19 @@ with builtins; let
|
||||
then map (m: m.module) header.imports
|
||||
else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" header.errors}";
|
||||
in mkMod mod (map (dep: if modDepsMap ? ${dep} then modCandidates.${dep} else externalModMap.${dep}) deps)) modDepsMap;
|
||||
makeEmacsWrapper = name: emacs: lean: writeShellScriptBin name ''
|
||||
${emacs} --eval "(progn (setq lean4-rootdir \"${lean}\"))" "$@"
|
||||
'';
|
||||
makeVSCodeWrapper = name: lean: writeShellScriptBin name ''
|
||||
PATH=${lean}/bin:$PATH ${lean-vscode}/bin/code "$@"
|
||||
'';
|
||||
printPaths = deps: writeShellScriptBin "print-paths" ''
|
||||
echo '${toJSON {
|
||||
oleanPath = [(depRoot "print-paths" deps)];
|
||||
srcPath = ["."] ++ map (dep: dep.src) allExternalDeps;
|
||||
loadDynlibPaths = map pathOfSharedLib (loadDynlibsOfDeps deps);
|
||||
}}'
|
||||
'';
|
||||
expandGlob = g:
|
||||
if typeOf g == "string" then [g]
|
||||
else if g.glob == "one" then [g.mod]
|
||||
@@ -233,7 +246,7 @@ in rec {
|
||||
cTree = symlinkJoin { name = "${name}-cTree"; paths = map (mod: mod.c) (attrValues mods); };
|
||||
oTree = symlinkJoin { name = "${name}-oTree"; paths = (attrValues objects); };
|
||||
iTree = symlinkJoin { name = "${name}-iTree"; paths = map (mod: mod.ilean) (attrValues mods); };
|
||||
sharedLib = mkSharedLib "lib${sharedLibName}" ''
|
||||
sharedLib = mkSharedLib "lib${libName}" ''
|
||||
${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \
|
||||
${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}'';
|
||||
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
|
||||
@@ -244,4 +257,48 @@ in rec {
|
||||
-o $out/bin/${executableName} \
|
||||
${lib.concatStringsSep " " allLinkFlags}
|
||||
'') {};
|
||||
|
||||
lean-package = writeShellScriptBin "lean" ''
|
||||
LEAN_PATH=${modRoot}:$LEAN_PATH LEAN_SRC_PATH=$LEAN_SRC_PATH:${src} exec ${lean-final}/bin/lean "$@"
|
||||
'';
|
||||
emacs-package = makeEmacsWrapper "emacs-package" lean-package;
|
||||
vscode-package = makeVSCodeWrapper "vscode-package" lean-package;
|
||||
|
||||
link-ilean = writeShellScriptBin "link-ilean" ''
|
||||
dest=''${1:-.}
|
||||
mkdir -p $dest/build/lib
|
||||
ln -sf ${iTree}/* $dest/build/lib
|
||||
'';
|
||||
|
||||
makePrintPathsFor = deps: mods: printPaths deps // mapAttrs (_: mod: makePrintPathsFor (deps ++ [mod]) mods) mods;
|
||||
print-paths = makePrintPathsFor [] (mods' // externalModMap);
|
||||
# `lean` wrapper that dynamically runs Nix for the actual `lean` executable so the same editor can be
|
||||
# used for multiple projects/after upgrading the `lean` input/for editing both stage 1 and the tests
|
||||
lean-bin-dev = substituteAll {
|
||||
name = "lean";
|
||||
dir = "bin";
|
||||
src = ./lean-dev.in;
|
||||
isExecutable = true;
|
||||
srcRoot = fullSrc; # use root flake.nix in case of Lean repo
|
||||
inherit bash nix srcTarget srcArgs;
|
||||
};
|
||||
lake-dev = substituteAll {
|
||||
name = "lake";
|
||||
dir = "bin";
|
||||
src = ./lake-dev.in;
|
||||
isExecutable = true;
|
||||
srcRoot = fullSrc; # use root flake.nix in case of Lean repo
|
||||
inherit bash nix srcTarget srcArgs;
|
||||
};
|
||||
lean-dev = symlinkJoin { name = "lean-dev"; paths = [ lean-bin-dev lake-dev ]; };
|
||||
emacs-dev = makeEmacsWrapper "emacs-dev" "${lean-emacs}/bin/emacs" lean-dev;
|
||||
emacs-path-dev = makeEmacsWrapper "emacs-path-dev" "emacs" lean-dev;
|
||||
vscode-dev = makeVSCodeWrapper "vscode-dev" lean-dev;
|
||||
|
||||
devShell = mkShell {
|
||||
buildInputs = [ nix ];
|
||||
shellHook = ''
|
||||
export LEAN_SRC_PATH="${srcPath}"
|
||||
'';
|
||||
};
|
||||
})
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{ src, pkgs, ... } @ args:
|
||||
{ src, pkgs, nix, ... } @ args:
|
||||
with pkgs;
|
||||
let
|
||||
nix-pinned = writeShellScriptBin "nix" ''
|
||||
${nix.packages.${system}.default}/bin/nix --experimental-features 'nix-command flakes' --extra-substituters https://lean4.cachix.org/ --option warn-dirty false "$@"
|
||||
'';
|
||||
# https://github.com/NixOS/nixpkgs/issues/130963
|
||||
llvmPackages = if stdenv.isDarwin then llvmPackages_11 else llvmPackages_15;
|
||||
cc = (ccacheWrapper.override rec {
|
||||
@@ -39,9 +42,40 @@ let
|
||||
inherit (lean) stdenv;
|
||||
lean = lean.stage1;
|
||||
inherit (lean.stage1) leanc;
|
||||
inherit lean-emacs lean-vscode;
|
||||
nix = nix-pinned;
|
||||
}));
|
||||
lean4-mode = emacsPackages.melpaBuild {
|
||||
pname = "lean4-mode";
|
||||
version = "1";
|
||||
commit = "1";
|
||||
src = args.lean4-mode;
|
||||
packageRequires = with pkgs.emacsPackages.melpaPackages; [ dash f flycheck magit-section lsp-mode s ];
|
||||
recipe = pkgs.writeText "recipe" ''
|
||||
(lean4-mode
|
||||
:repo "leanprover/lean4-mode"
|
||||
:fetcher github
|
||||
:files ("*.el" "data"))
|
||||
'';
|
||||
};
|
||||
lean-emacs = emacsWithPackages [ lean4-mode ];
|
||||
# updating might be nicer by building from source from a flake input, but this is good enough for now
|
||||
vscode-lean4 = vscode-utils.extensionFromVscodeMarketplace {
|
||||
name = "lean4";
|
||||
publisher = "leanprover";
|
||||
version = "0.0.63";
|
||||
sha256 = "sha256-kjEex7L0F2P4pMdXi4NIZ1y59ywJVubqDqsoYagZNkI=";
|
||||
};
|
||||
lean-vscode = vscode-with-extensions.override {
|
||||
vscodeExtensions = [ vscode-lean4 ];
|
||||
};
|
||||
in {
|
||||
inherit cc buildLeanPackage llvmPackages;
|
||||
inherit cc lean4-mode buildLeanPackage llvmPackages vscode-lean4;
|
||||
lean = lean.stage1;
|
||||
stage0print-paths = lean.stage1.Lean.print-paths;
|
||||
HEAD-as-stage0 = (lean.stage1.Lean.overrideArgs { srcTarget = "..#stage0-from-input.stage0"; srcArgs = "(--override-input lean-stage0 ..\?rev=$(git rev-parse HEAD) -- -Dinterpreter.prefer_native=false \"$@\")"; });
|
||||
HEAD-as-stage1 = (lean.stage1.Lean.overrideArgs { srcTarget = "..\?rev=$(git rev-parse HEAD)#stage0"; });
|
||||
nix = nix-pinned;
|
||||
nixpkgs = pkgs;
|
||||
ciShell = writeShellScriptBin "ciShell" ''
|
||||
set -o pipefail
|
||||
@@ -49,4 +83,5 @@ in {
|
||||
# prefix lines with cumulative and individual execution time
|
||||
"$@" |& ts -i "(%.S)]" | ts -s "[%M:%S"
|
||||
'';
|
||||
} // lean.stage1
|
||||
vscode = lean-vscode;
|
||||
} // lean.stage1.Lean // lean.stage1 // lean
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
* The `Lean` module has switched from `Lean.HashMap` and `Lean.HashSet` to `Std.HashMap` and `Std.HashSet`. `Lean.HashMap` and `Lean.HashSet` are now deprecated and will be removed in a future release. Users of `Lean` APIs that interact with hash maps, for example `Lean.Environment.const2ModIdx`, might encounter minor breakage due to the following breaking changes from `Lean.HashMap` to `Std.HashMap`:
|
||||
* query functions use the term `get` instead of `find`,
|
||||
* the notation `map[key]` no longer returns an optional value but expects a proof that the key is present in the map instead. The previous behavior is available via the `map[key]?` notation.
|
||||
@@ -1 +0,0 @@
|
||||
* #4963 [LibUV](https://libuv.org/) is now required to build Lean. This change only affects developers who compile Lean themselves instead of obtaining toolchains via `elan`. We have updated the official build instructions with information on how to obtain LibUV on our supported platforms.
|
||||
65
releases_drafts/mutualStructural.md
Normal file
65
releases_drafts/mutualStructural.md
Normal file
@@ -0,0 +1,65 @@
|
||||
* Structural recursion can now be explicitly requested using
|
||||
```
|
||||
termination_by structural x
|
||||
```
|
||||
in analogy to the existing `termination_by x` syntax that causes well-founded recursion to be used.
|
||||
(#4542)
|
||||
|
||||
* The `termination_by?` syntax no longer forces the use of well-founded recursion, and when structural
|
||||
recursion is inferred, will print the result using the `termination_by` syntax.
|
||||
|
||||
* Mutual structural recursion is supported now. This supports both mutual recursion over a non-mutual
|
||||
data type, as well as recursion over mutual or nested data types:
|
||||
|
||||
```lean
|
||||
mutual
|
||||
def Even : Nat → Prop
|
||||
| 0 => True
|
||||
| n+1 => Odd n
|
||||
|
||||
def Odd : Nat → Prop
|
||||
| 0 => False
|
||||
| n+1 => Even n
|
||||
end
|
||||
|
||||
mutual
|
||||
inductive A
|
||||
| other : B → A
|
||||
| empty
|
||||
inductive B
|
||||
| other : A → B
|
||||
| empty
|
||||
end
|
||||
|
||||
mutual
|
||||
def A.size : A → Nat
|
||||
| .other b => b.size + 1
|
||||
| .empty => 0
|
||||
|
||||
def B.size : B → Nat
|
||||
| .other a => a.size + 1
|
||||
| .empty => 0
|
||||
end
|
||||
|
||||
inductive Tree where | node : List Tree → Tree
|
||||
|
||||
mutual
|
||||
def Tree.size : Tree → Nat
|
||||
| node ts => Tree.list_size ts
|
||||
|
||||
def Tree.list_size : List Tree → Nat
|
||||
| [] => 0
|
||||
| t::ts => Tree.size t + Tree.list_size ts
|
||||
end
|
||||
```
|
||||
|
||||
Functional induction principles are generated for these functions as well (`A.size.induct`, `A.size.mutual_induct`).
|
||||
|
||||
Nested structural recursion is still not supported.
|
||||
|
||||
PRs #4639, #4715, #4642, #4656, #4684, #4715, #4728, #4575, #4731, #4658, #4734, #4738, #4718,
|
||||
#4733, #4787, #4788, #4789, #4807, #4772
|
||||
|
||||
* A bugfix in the structural recursion code may in some cases break existing code, when a parameter
|
||||
of the type of the recursive argument is bound behind indices of that type. This can usually be
|
||||
fixed by reordering the parameters of the function (PR #4672)
|
||||
@@ -17,8 +17,8 @@ for f in $(git ls-files src ':!:src/lake/*' ':!:src/Leanc.lean'); do
|
||||
done
|
||||
|
||||
# special handling for Lake files due to its nested directory
|
||||
# copy the README to ensure the `stage0/src/lake` directory is committed
|
||||
for f in $(git ls-files 'src/lake/Lake/*' src/lake/Lake.lean src/lake/LakeMain.lean src/lake/README.md ':!:src/lakefile.toml'); do
|
||||
# copy the README to ensure the `stage0/src/lake` directory is comitted
|
||||
for f in $(git ls-files 'src/lake/Lake/*' src/lake/Lake.lean src/lake/README.md ':!:src/lakefile.toml'); do
|
||||
if [[ $f == *.lean ]]; then
|
||||
f=${f#src/lake}
|
||||
f=${f%.lean}.c
|
||||
|
||||
@@ -38,7 +38,7 @@ $CP $GLIBC/lib/*crt* llvm/lib/
|
||||
$CP $GLIBC/lib/*crt* stage1/lib/
|
||||
# runtime
|
||||
(cd llvm; $CP --parents lib/clang/*/lib/*/{clang_rt.*.o,libclang_rt.builtins*} ../stage1)
|
||||
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a $LIBUV/lib/libuv.a stage1/lib/
|
||||
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a stage1/lib/
|
||||
# LLVM 15 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
|
||||
# but clang-15 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
|
||||
# https://github.com/llvm/llvm-project/issues/54955
|
||||
@@ -62,8 +62,8 @@ fi
|
||||
# use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers),
|
||||
# but do not change sysroot so users can still link against system libs
|
||||
echo -n " -DLEANC_INTERNAL_FLAGS='-nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
|
||||
# when not using the above flags, link GMP dynamically/as usual
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -luv -Wl,--no-as-needed'"
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -Wl,--no-as-needed'"
|
||||
# do not set `LEAN_CC` for tests
|
||||
echo -n " -DLEAN_TEST_VARS=''"
|
||||
|
||||
@@ -9,7 +9,6 @@ set -uxo pipefail
|
||||
# use full LLVM release for compiling C++ code, but subset for compiling C code and distribution
|
||||
|
||||
GMP=${GMP:-$(brew --prefix)}
|
||||
LIBUV=${LIBUV:-$(brew --prefix)}
|
||||
|
||||
[[ -d llvm ]] || (mkdir llvm; gtar xf $1 --strip-components 1 --directory llvm)
|
||||
[[ -d llvm-host ]] || if [[ "$#" -gt 1 ]]; then
|
||||
@@ -47,9 +46,8 @@ echo -n " -DLEAN_EXTRA_CXX_FLAGS='${EXTRA_FLAGS:-}'"
|
||||
if [[ -L llvm-host ]]; then
|
||||
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang"
|
||||
gcp $GMP/lib/libgmp.a stage1/lib/
|
||||
gcp $LIBUV/lib/libuv.a stage1/lib/
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -luv'"
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp'"
|
||||
else
|
||||
echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/15.0.1 ${EXTRA_FLAGS:-}'"
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"
|
||||
|
||||
@@ -31,15 +31,15 @@ cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
|
||||
# runtime
|
||||
(cd llvm; cp --parents lib/clang/*/lib/*/libclang_rt.builtins* ../stage1)
|
||||
# further dependencies
|
||||
cp /clang64/lib/lib{m,bcrypt,mingw32,moldname,mingwex,msvcrt,pthread,advapi32,shell32,user32,kernel32,ucrtbase}.* /clang64/lib/libgmp.a /clang64/lib/libuv.a llvm/lib/lib{c++,c++abi,unwind}.a stage1/lib/
|
||||
cp /clang64/lib/lib{m,bcrypt,mingw32,moldname,mingwex,msvcrt,pthread,advapi32,shell32,user32,kernel32,ucrtbase}.* /clang64/lib/libgmp.a llvm/lib/lib{c++,c++abi,unwind}.a stage1/lib/
|
||||
echo -n " -DLEAN_STANDALONE=ON"
|
||||
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang.exe -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER=$PWD/llvm/bin/clang++.exe -DCMAKE_CXX_COMPILER_WORKS=1 -DLEAN_CXX_STDLIB='-lc++ -lc++abi'"
|
||||
echo -n " -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_CXX_COMPILER=clang++"
|
||||
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter /clang64/include/'"
|
||||
echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang.exe"
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -static-libgcc -Wl,-Bstatic -lgmp -luv -lunwind -Wl,-Bdynamic -fuse-ld=lld'"
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -static-libgcc -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -fuse-ld=lld'"
|
||||
# when not using the above flags, link GMP dynamically/as usual
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -luv -lucrtbase'"
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -lucrtbase'"
|
||||
# do not set `LEAN_CC` for tests
|
||||
echo -n " -DAUTO_THREAD_FINALIZATION=OFF -DSTAGE0_AUTO_THREAD_FINALIZATION=OFF"
|
||||
echo -n " -DLEAN_TEST_VARS=''"
|
||||
|
||||
@@ -10,7 +10,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 12)
|
||||
set(LEAN_VERSION_MINOR 11)
|
||||
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'")
|
||||
@@ -243,15 +243,6 @@ if("${USE_GMP}" MATCHES "ON")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Emscripten")
|
||||
# LibUV
|
||||
find_package(LibUV 1.0.0 REQUIRED)
|
||||
include_directories(${LIBUV_INCLUDE_DIR})
|
||||
endif()
|
||||
if(NOT LEAN_STANDALONE)
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${LIBUV_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
# ccache
|
||||
if(CCACHE AND NOT CMAKE_CXX_COMPILER_LAUNCHER AND NOT CMAKE_C_COMPILER_LAUNCHER)
|
||||
find_program(CCACHE_PATH ccache)
|
||||
@@ -333,12 +324,7 @@ if(NOT LEAN_STANDALONE)
|
||||
endif()
|
||||
|
||||
# flags for user binaries = flags for toolchain binaries + Lake
|
||||
set(LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(LEANC_SHARED_LINKER_FLAGS " ${TOOLCHAIN_SHARED_LINKER_FLAGS} -Wl,--as-needed -lLake_shared -Wl,--no-as-needed")
|
||||
else()
|
||||
set(LEANC_SHARED_LINKER_FLAGS " ${TOOLCHAIN_SHARED_LINKER_FLAGS} -lLake_shared")
|
||||
endif()
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
|
||||
|
||||
if (LLVM)
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -L${LLVM_CONFIG_LIBDIR} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS} ${LLVM_CONFIG_SYSTEM_LIBS}")
|
||||
@@ -383,20 +369,15 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
|
||||
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
|
||||
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -install_name @rpath/libLake_shared.dylib")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
@@ -421,8 +402,8 @@ endif()
|
||||
# executable or `leanshared`, plugins would try to look them up at load time (even though they
|
||||
# are already loaded) and probably fail unless we set up LD_LIBRARY_PATH.
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
# import libraries created by the stdlib.make targets
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
|
||||
# import library created by the `leanshared` target
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared")
|
||||
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
|
||||
endif()
|
||||
@@ -479,22 +460,6 @@ if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE)
|
||||
string(APPEND LEANC_EXTRA_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(initialize)
|
||||
add_subdirectory(shell)
|
||||
# to be included in `leanshared` but not the smaller `leanshared_1` (as it would pull
|
||||
# in the world)
|
||||
add_library(leaninitialize STATIC $<TARGET_OBJECTS:initialize>)
|
||||
set_target_properties(leaninitialize PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
|
||||
OUTPUT_NAME leaninitialize)
|
||||
add_library(leanshell STATIC util/shell.cpp)
|
||||
set_target_properties(leanshell PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
|
||||
OUTPUT_NAME leanshell)
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--whole-archive -lleanmanifest -Wl,--no-whole-archive")
|
||||
endif()
|
||||
|
||||
if(${STAGE} GREATER 1)
|
||||
# reuse C++ parts, which don't change
|
||||
add_library(leanrt_initial-exec STATIC IMPORTED)
|
||||
@@ -503,17 +468,13 @@ if(${STAGE} GREATER 1)
|
||||
add_library(leanrt STATIC IMPORTED)
|
||||
set_target_properties(leanrt PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a")
|
||||
add_library(leancpp_1 STATIC IMPORTED)
|
||||
set_target_properties(leancpp_1 PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/temp/libleancpp_1.a")
|
||||
add_library(leancpp STATIC IMPORTED)
|
||||
set_target_properties(leancpp PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
|
||||
add_custom_target(copy-leancpp
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/runtime/libleanrt_initial-exec.a" "${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a"
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleanrt.a" "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a"
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a"
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/temp/libleancpp_1.a" "${CMAKE_BINARY_DIR}/lib/temp/libleancpp_1.a")
|
||||
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
|
||||
add_dependencies(leancpp copy-leancpp)
|
||||
if(LLVM)
|
||||
add_custom_target(copy-lean-h-bc
|
||||
@@ -533,23 +494,14 @@ else()
|
||||
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:constructions>)
|
||||
add_subdirectory(library/compiler)
|
||||
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:compiler>)
|
||||
add_subdirectory(initialize)
|
||||
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:initialize>)
|
||||
|
||||
# leancpp without `initialize` (see `leaninitialize` above)
|
||||
add_library(leancpp_1 STATIC ${LEAN_OBJS})
|
||||
set_target_properties(leancpp_1 PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
|
||||
OUTPUT_NAME leancpp_1)
|
||||
add_library(leancpp STATIC ${LEAN_OBJS} $<TARGET_OBJECTS:initialize>)
|
||||
add_library(leancpp STATIC ${LEAN_OBJS})
|
||||
set_target_properties(leancpp PROPERTIES
|
||||
OUTPUT_NAME leancpp)
|
||||
endif()
|
||||
|
||||
if((${STAGE} GREATER 0) AND CADICAL)
|
||||
add_custom_target(copy-cadical
|
||||
COMMAND cmake -E copy_if_different "${CADICAL}" "${CMAKE_BINARY_DIR}/bin/cadical${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
add_dependencies(leancpp copy-cadical)
|
||||
endif()
|
||||
|
||||
# MSYS2 bash usually handles Windows paths relatively well, but not when putting them in the PATH
|
||||
string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
|
||||
|
||||
@@ -557,12 +509,25 @@ string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
|
||||
# (also looks nicer in the build log)
|
||||
file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
# set up libInit_shared only on Windows; see also stdlib.make.in
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libInit.a.export ${CMAKE_BINARY_DIR}/lib/temp/libStd.a.export ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libStd.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLean.a.export -lleancpp -Wl,--no-whole-archive -lInit_shared -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
|
||||
else()
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
endif()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
# We do not use dynamic linking via leanshared for Emscripten to keep things
|
||||
# simple. (And we are not interested in `Lake` anyway.) To use dynamic
|
||||
# linking, we would probably have to set MAIN_MODULE=2 on `leanshared`,
|
||||
# SIDE_MODULE=2 on `lean`, and set CMAKE_SHARED_LIBRARY_SUFFIX to ".js".
|
||||
string(APPEND LEAN_EXE_LINKER_FLAGS " ${LIB}/temp/libleanshell.a ${TOOLCHAIN_STATIC_LINKER_FLAGS} ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
|
||||
string(APPEND LEAN_EXE_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
|
||||
endif()
|
||||
|
||||
# Build the compiler using the bootstrapped C sources for stage0, and use
|
||||
@@ -596,13 +561,8 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
)
|
||||
add_custom_target(leanshared ALL
|
||||
DEPENDS Init_shared leancpp
|
||||
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared_1${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_custom_target(lake_shared ALL
|
||||
DEPENDS leanshared
|
||||
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libLake_shared${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
else()
|
||||
add_custom_target(Init_shared ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
@@ -612,29 +572,19 @@ else()
|
||||
|
||||
add_custom_target(leanshared ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS Init_shared leancpp_1 leancpp leanshell leaninitialize
|
||||
DEPENDS Init_shared leancpp
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
|
||||
VERBATIM)
|
||||
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared")
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
add_custom_target(lake_lib ALL
|
||||
add_custom_target(lake ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS leanshared
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake
|
||||
VERBATIM)
|
||||
add_custom_target(lake_shared ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS lake_lib
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make libLake_shared
|
||||
VERBATIM)
|
||||
add_custom_target(lake ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS lake_shared
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make lake
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
if(PREV_STAGE)
|
||||
@@ -663,9 +613,7 @@ file(COPY ${LEAN_SOURCE_DIR}/bin/leanmake DESTINATION ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION bin)
|
||||
|
||||
if (${STAGE} GREATER 0 AND CADICAL)
|
||||
install(PROGRAMS "${CADICAL}" DESTINATION bin)
|
||||
endif()
|
||||
add_subdirectory(shell)
|
||||
|
||||
add_custom_target(clean-stdlib
|
||||
COMMAND rm -rf "${CMAKE_BINARY_DIR}/lib" || true)
|
||||
|
||||
@@ -37,26 +37,38 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
|
||||
f (ite P x y) = ite P (f x) (f y) :=
|
||||
apply_dite f P (fun _ => x) (fun _ => y)
|
||||
|
||||
@[simp] theorem dite_eq_left_iff {P : Prop} [Decidable P] {B : ¬ P → α} :
|
||||
dite P (fun _ => a) B = a ↔ ∀ h, B h = a := by
|
||||
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
|
||||
|
||||
@[simp] theorem dite_eq_right_iff {P : Prop} [Decidable P] {A : P → α} :
|
||||
(dite P A fun _ => b) = b ↔ ∀ h, A h = b := by
|
||||
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
|
||||
|
||||
@[simp] theorem ite_eq_left_iff {P : Prop} [Decidable P] : ite P a b = a ↔ ¬P → b = a :=
|
||||
dite_eq_left_iff
|
||||
|
||||
@[simp] theorem ite_eq_right_iff {P : Prop} [Decidable P] : ite P a b = b ↔ P → a = b :=
|
||||
dite_eq_right_iff
|
||||
|
||||
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
|
||||
@[simp] theorem dite_eq_ite [Decidable P] : (dite P (fun _ => a) fun _ => b) = ite P a b := rfl
|
||||
|
||||
@[deprecated "Use `ite_eq_right_iff`" (since := "2024-09-18")]
|
||||
-- We don't mark this as `simp` as it is already handled by `ite_eq_right_iff`.
|
||||
theorem ite_some_none_eq_none [Decidable P] :
|
||||
(if P then some x else none) = none ↔ ¬ P := by
|
||||
simp only [ite_eq_right_iff, reduceCtorEq]
|
||||
simp only [ite_eq_right_iff]
|
||||
rfl
|
||||
|
||||
@[deprecated "Use `Option.ite_none_right_eq_some`" (since := "2024-09-18")]
|
||||
theorem ite_some_none_eq_some [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
|
||||
|
||||
@[deprecated "Use `dite_eq_right_iff" (since := "2024-09-18")]
|
||||
-- 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
|
||||
|
||||
@[deprecated "Use `Option.dite_none_right_eq_some`" (since := "2024-09-18")]
|
||||
theorem dite_some_none_eq_some [Decidable P] {x : P → α} {y : α} :
|
||||
@[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 [h]
|
||||
|
||||
@@ -121,11 +121,11 @@ theorem propComplete (a : Prop) : a = True ∨ a = False :=
|
||||
| Or.inl ha => Or.inl (eq_true ha)
|
||||
| Or.inr hn => Or.inr (eq_false hn)
|
||||
|
||||
-- this supersedes byCases in Decidable
|
||||
-- this supercedes byCases in Decidable
|
||||
theorem byCases {p q : Prop} (hpq : p → q) (hnpq : ¬p → q) : q :=
|
||||
Decidable.byCases (dec := propDecidable _) hpq hnpq
|
||||
|
||||
-- this supersedes byContradiction in Decidable
|
||||
-- this supercedes byContradiction in Decidable
|
||||
theorem byContradiction {p : Prop} (h : ¬p → False) : p :=
|
||||
Decidable.byContradiction (dec := propDecidable _) h
|
||||
|
||||
@@ -134,30 +134,6 @@ The left-to-right direction, double negation elimination (DNE),
|
||||
is classically true but not constructively. -/
|
||||
@[simp] theorem not_not : ¬¬a ↔ a := Decidable.not_not
|
||||
|
||||
/-- Transfer decidability of `¬ p` to decidability of `p`. -/
|
||||
-- This can not be an instance as it would be tried everywhere.
|
||||
def decidable_of_decidable_not (p : Prop) [h : Decidable (¬ p)] : Decidable p :=
|
||||
match h with
|
||||
| isFalse h => isTrue (Classical.not_not.mp h)
|
||||
| isTrue h => isFalse h
|
||||
|
||||
attribute [local instance] decidable_of_decidable_not in
|
||||
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
|
||||
@[simp low] protected theorem dite_not [hn : Decidable (¬p)] (x : ¬p → α) (y : ¬¬p → α) :
|
||||
dite (¬p) x y = dite p (fun h => y (not_not_intro h)) x := by
|
||||
cases hn <;> rename_i g
|
||||
· simp [not_not.mp g]
|
||||
· simp [g]
|
||||
|
||||
attribute [local instance] decidable_of_decidable_not in
|
||||
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
|
||||
@[simp low] protected theorem ite_not (p : Prop) [Decidable (¬ p)] (x y : α) : ite (¬p) x y = ite p y x :=
|
||||
dite_not (fun _ => x) (fun _ => y)
|
||||
|
||||
attribute [local instance] decidable_of_decidable_not in
|
||||
@[simp low] protected theorem decide_not (p : Prop) [Decidable (¬ p)] : decide (¬p) = !decide p :=
|
||||
byCases (fun h : p => by simp_all) (fun h => by simp_all)
|
||||
|
||||
@[simp low] theorem not_forall {p : α → Prop} : (¬∀ x, p x) ↔ ∃ x, ¬p x := Decidable.not_forall
|
||||
|
||||
theorem not_forall_not {p : α → Prop} : (¬∀ x, ¬p x) ↔ ∃ x, p x := Decidable.not_forall_not
|
||||
@@ -184,7 +160,7 @@ theorem not_iff : ¬(a ↔ b) ↔ (¬a ↔ b) := Decidable.not_iff
|
||||
|
||||
@[simp] theorem not_imp : ¬(a → b) ↔ a ∧ ¬b := Decidable.not_imp_iff_and_not
|
||||
|
||||
@[simp] theorem imp_and_neg_imp_iff (p : Prop) {q : Prop} : (p → q) ∧ (¬p → q) ↔ q :=
|
||||
@[simp] theorem imp_and_neg_imp_iff (p q : Prop) : (p → q) ∧ (¬p → q) ↔ q :=
|
||||
Iff.intro (fun (a : _ ∧ _) => (Classical.em p).rec a.left a.right)
|
||||
(fun a => And.intro (fun _ => a) (fun _ => a))
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ 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
|
||||
|
||||
@@ -34,7 +34,7 @@ instance : Monad (ExceptCpsT ε m) where
|
||||
bind x f := fun _ k₁ k₂ => x _ (fun a => f a _ k₁ k₂) k₂
|
||||
|
||||
instance : LawfulMonad (ExceptCpsT σ m) := by
|
||||
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
|
||||
refine' { .. } <;> intros <;> rfl
|
||||
|
||||
instance : MonadExceptOf ε (ExceptCpsT ε m) where
|
||||
throw e := fun _ _ k => k e
|
||||
|
||||
@@ -33,10 +33,6 @@ attribute [simp] id_map
|
||||
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
|
||||
id_map x
|
||||
|
||||
@[simp] theorem Functor.map_map [Functor f] [LawfulFunctor f] (m : α → β) (g : β → γ) (x : f α) :
|
||||
g <$> m <$> x = (fun a => g (m a)) <$> x :=
|
||||
(comp_map _ _ _).symm
|
||||
|
||||
/--
|
||||
The `Applicative` typeclass only contains the operations of an applicative functor.
|
||||
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
|
||||
@@ -87,16 +83,12 @@ class LawfulMonad (m : Type u → Type v) [Monad m] extends LawfulApplicative m
|
||||
seq_assoc x g h := (by simp [← bind_pure_comp, ← bind_map, bind_assoc, pure_bind])
|
||||
|
||||
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
|
||||
attribute [simp] pure_bind bind_assoc bind_pure_comp
|
||||
attribute [simp] pure_bind bind_assoc
|
||||
|
||||
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
|
||||
show x >>= (fun a => pure (id a)) = x
|
||||
rw [bind_pure_comp, id_map]
|
||||
|
||||
/--
|
||||
Use `simp [← bind_pure_comp]` rather than `simp [map_eq_pure_bind]`,
|
||||
as `bind_pure_comp` is in the default simp set, so also using `map_eq_pure_bind` would cause a loop.
|
||||
-/
|
||||
theorem map_eq_pure_bind [Monad m] [LawfulMonad m] (f : α → β) (x : m α) : f <$> x = x >>= fun a => pure (f a) := by
|
||||
rw [← bind_pure_comp]
|
||||
|
||||
@@ -117,21 +109,10 @@ theorem seq_eq_bind {α β : Type u} [Monad m] [LawfulMonad m] (mf : m (α →
|
||||
|
||||
theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *> y = x >>= fun _ => y := by
|
||||
rw [seqRight_eq]
|
||||
simp only [map_eq_pure_bind, const, seq_eq_bind_map, bind_assoc, pure_bind, id_eq, bind_pure]
|
||||
simp [map_eq_pure_bind, seq_eq_bind_map, const]
|
||||
|
||||
theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y = x >>= fun a => y >>= fun _ => pure a := by
|
||||
rw [seqLeft_eq]
|
||||
simp only [map_eq_pure_bind, seq_eq_bind_map, bind_assoc, pure_bind, const_apply]
|
||||
|
||||
@[simp] theorem map_bind [Monad m] [LawfulMonad m] (f : β → γ) (x : m α) (g : α → m β) :
|
||||
f <$> (x >>= g) = x >>= fun a => f <$> g a := by
|
||||
rw [← bind_pure_comp, LawfulMonad.bind_assoc]
|
||||
simp [bind_pure_comp]
|
||||
|
||||
@[simp] theorem bind_map_left [Monad m] [LawfulMonad m] (f : α → β) (x : m α) (g : β → m γ) :
|
||||
((f <$> x) >>= fun b => g b) = (x >>= fun a => g (f a)) := by
|
||||
rw [← bind_pure_comp]
|
||||
simp only [bind_assoc, pure_bind]
|
||||
rw [seqLeft_eq]; simp [map_eq_pure_bind, seq_eq_bind_map]
|
||||
|
||||
/--
|
||||
An alternative constructor for `LawfulMonad` which has more
|
||||
@@ -172,7 +153,7 @@ namespace Id
|
||||
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
|
||||
|
||||
instance : LawfulMonad Id := by
|
||||
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
|
||||
refine' { .. } <;> intros <;> rfl
|
||||
|
||||
end Id
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ theorem ext {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
|
||||
@[simp] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl
|
||||
|
||||
@[simp] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α → ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by
|
||||
simp [ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont]
|
||||
simp[ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont, map_eq_pure_bind]
|
||||
|
||||
@[simp] theorem bind_throw [Monad m] [LawfulMonad m] (f : α → ExceptT ε m β) : (throw e >>= f) = throw e := by
|
||||
simp [throw, throwThe, MonadExceptOf.throw, bind, ExceptT.bind, ExceptT.bindCont, ExceptT.mk]
|
||||
@@ -43,7 +43,7 @@ theorem run_bind [Monad m] (x : ExceptT ε m α)
|
||||
|
||||
@[simp] theorem run_map [Monad m] [LawfulMonad m] (f : α → β) (x : ExceptT ε m α)
|
||||
: (f <$> x).run = Except.map f <$> x.run := by
|
||||
simp [Functor.map, ExceptT.map, ←bind_pure_comp]
|
||||
simp [Functor.map, ExceptT.map, map_eq_pure_bind]
|
||||
apply bind_congr
|
||||
intro a; cases a <;> simp [Except.map]
|
||||
|
||||
@@ -62,7 +62,7 @@ protected theorem seqLeft_eq {α β ε : Type u} {m : Type u → Type v} [Monad
|
||||
intro
|
||||
| Except.error _ => simp
|
||||
| Except.ok _ =>
|
||||
simp [←bind_pure_comp]; apply bind_congr; intro b;
|
||||
simp [map_eq_pure_bind]; apply bind_congr; intro b;
|
||||
cases b <;> simp [comp, Except.map, const]
|
||||
|
||||
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
|
||||
@@ -175,7 +175,7 @@ theorem ext {x y : StateT σ m α} (h : ∀ s, x.run s = y.run s) : x = y :=
|
||||
simp [bind, StateT.bind, run]
|
||||
|
||||
@[simp] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α → β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
|
||||
simp [Functor.map, StateT.map, run, ←bind_pure_comp]
|
||||
simp [Functor.map, StateT.map, run, map_eq_pure_bind]
|
||||
|
||||
@[simp] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
|
||||
|
||||
@@ -210,13 +210,13 @@ theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f :
|
||||
|
||||
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by
|
||||
apply ext; intro s
|
||||
simp [←bind_pure_comp, const]
|
||||
simp [map_eq_pure_bind, const]
|
||||
apply bind_congr; intro p; cases p
|
||||
simp [Prod.eta]
|
||||
|
||||
theorem seqLeft_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x <* y = const β <$> x <*> y := by
|
||||
apply ext; intro s
|
||||
simp [←bind_pure_comp]
|
||||
simp [map_eq_pure_bind]
|
||||
|
||||
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
|
||||
id_map := by intros; apply ext; intros; simp[Prod.eta]
|
||||
@@ -224,7 +224,7 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
|
||||
seqLeft_eq := seqLeft_eq
|
||||
seqRight_eq := seqRight_eq
|
||||
pure_seq := by intros; apply ext; intros; simp
|
||||
bind_pure_comp := by intros; apply ext; intros; simp
|
||||
bind_pure_comp := by intros; apply ext; intros; simp; apply LawfulMonad.bind_pure_comp
|
||||
bind_map := by intros; rfl
|
||||
pure_bind := by intros; apply ext; intros; simp
|
||||
bind_assoc := by intros; apply ext; intros; simp
|
||||
|
||||
@@ -35,7 +35,7 @@ instance : Monad (StateCpsT σ m) where
|
||||
bind x f := fun δ s k => x δ s fun a s => f a δ s k
|
||||
|
||||
instance : LawfulMonad (StateCpsT σ m) := by
|
||||
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
|
||||
refine' { .. } <;> intros <;> rfl
|
||||
|
||||
@[always_inline]
|
||||
instance : MonadStateOf σ (StateCpsT σ m) where
|
||||
|
||||
@@ -97,18 +97,11 @@ Users should prefer `unfold` for unfolding definitions. -/
|
||||
syntax (name := delta) "delta" (ppSpace colGt ident)+ : conv
|
||||
|
||||
/--
|
||||
* `unfold id` unfolds all occurrences of definition `id` in the target.
|
||||
* `unfold foo` unfolds all occurrences of `foo` in the target.
|
||||
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
|
||||
|
||||
Definitions can be either global or local definitions.
|
||||
|
||||
For non-recursive global definitions, this tactic is identical to `delta`.
|
||||
For recursive global definitions, it uses the "unfolding lemma" `id.eq_def`,
|
||||
which is generated for each recursive definition, to unfold according to the recursive definition given by the user.
|
||||
Only one level of unfolding is performed, in contrast to `simp only [id]`, which unfolds definition `id` recursively.
|
||||
|
||||
This is the `conv` version of the `unfold` tactic.
|
||||
-/
|
||||
Like the `unfold` tactic, this uses equational lemmas for the chosen definition
|
||||
to rewrite the target. For recursive definitions,
|
||||
only one layer of unfolding is performed. -/
|
||||
syntax (name := unfold) "unfold" (ppSpace colGt ident)+ : conv
|
||||
|
||||
/--
|
||||
|
||||
@@ -36,17 +36,6 @@ and `flip (·<·)` is the greater-than relation.
|
||||
|
||||
theorem Function.comp_def {α β δ} (f : β → δ) (g : α → β) : f ∘ g = fun x => f (g x) := rfl
|
||||
|
||||
@[simp] theorem Function.const_comp {f : α → β} {c : γ} :
|
||||
(Function.const β c ∘ f) = Function.const α c := by
|
||||
rfl
|
||||
@[simp] theorem Function.comp_const {f : β → γ} {b : β} :
|
||||
(f ∘ Function.const α b) = Function.const α (f b) := by
|
||||
rfl
|
||||
@[simp] theorem Function.true_comp {f : α → β} : ((fun _ => true) ∘ f) = fun _ => true := by
|
||||
rfl
|
||||
@[simp] theorem Function.false_comp {f : α → β} : ((fun _ => false) ∘ f) = fun _ => false := by
|
||||
rfl
|
||||
|
||||
attribute [simp] namedPattern
|
||||
|
||||
/--
|
||||
@@ -165,23 +154,9 @@ inductive PSum (α : Sort u) (β : Sort v) where
|
||||
|
||||
@[inherit_doc] infixr:30 " ⊕' " => PSum
|
||||
|
||||
/--
|
||||
`PSum α β` is inhabited if `α` is inhabited.
|
||||
This is not an instance to avoid non-canonical instances.
|
||||
-/
|
||||
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := ⟨PSum.inl default⟩
|
||||
instance {α β} [Inhabited α] : Inhabited (PSum α β) := ⟨PSum.inl default⟩
|
||||
|
||||
/--
|
||||
`PSum α β` is inhabited if `β` is inhabited.
|
||||
This is not an instance to avoid non-canonical instances.
|
||||
-/
|
||||
@[reducible] def PSum.inhabitedRight {α β} [Inhabited β] : Inhabited (PSum α β) := ⟨PSum.inr default⟩
|
||||
|
||||
instance PSum.nonemptyLeft [h : Nonempty α] : Nonempty (PSum α β) :=
|
||||
Nonempty.elim h (fun a => ⟨PSum.inl a⟩)
|
||||
|
||||
instance PSum.nonemptyRight [h : Nonempty β] : Nonempty (PSum α β) :=
|
||||
Nonempty.elim h (fun b => ⟨PSum.inr b⟩)
|
||||
instance {α β} [Inhabited β] : Inhabited (PSum α β) := ⟨PSum.inr default⟩
|
||||
|
||||
/--
|
||||
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
|
||||
@@ -814,16 +789,15 @@ theorem cast_heq {α β : Sort u} : (h : α = β) → (a : α) → HEq (cast h a
|
||||
|
||||
variable {a b c d : Prop}
|
||||
|
||||
theorem iff_iff_implies_and_implies {a b : Prop} : (a ↔ b) ↔ (a → b) ∧ (b → a) :=
|
||||
theorem iff_iff_implies_and_implies (a b : Prop) : (a ↔ b) ↔ (a → b) ∧ (b → a) :=
|
||||
Iff.intro (fun h => And.intro h.mp h.mpr) (fun h => Iff.intro h.left h.right)
|
||||
|
||||
@[refl] theorem Iff.refl (a : Prop) : a ↔ a :=
|
||||
theorem Iff.refl (a : Prop) : a ↔ a :=
|
||||
Iff.intro (fun h => h) (fun h => h)
|
||||
|
||||
protected theorem Iff.rfl {a : Prop} : a ↔ a :=
|
||||
Iff.refl a
|
||||
|
||||
-- And, also for backward compatibility, we try `Iff.rfl.` using `exact` (see #5366)
|
||||
macro_rules | `(tactic| rfl) => `(tactic| exact Iff.rfl)
|
||||
|
||||
theorem Iff.of_eq (h : a = b) : a ↔ b := h ▸ Iff.rfl
|
||||
@@ -838,9 +812,6 @@ instance : Trans Iff Iff Iff where
|
||||
theorem Eq.comm {a b : α} : a = b ↔ b = a := Iff.intro Eq.symm Eq.symm
|
||||
theorem eq_comm {a b : α} : a = b ↔ b = a := Eq.comm
|
||||
|
||||
theorem HEq.comm {a : α} {b : β} : HEq a b ↔ HEq b a := Iff.intro HEq.symm HEq.symm
|
||||
theorem heq_comm {a : α} {b : β} : HEq a b ↔ HEq b a := HEq.comm
|
||||
|
||||
@[symm] theorem Iff.symm (h : a ↔ b) : b ↔ a := Iff.intro h.mpr h.mp
|
||||
theorem Iff.comm: (a ↔ b) ↔ (b ↔ a) := Iff.intro Iff.symm Iff.symm
|
||||
theorem iff_comm : (a ↔ b) ↔ (b ↔ a) := Iff.comm
|
||||
@@ -914,7 +885,7 @@ theorem byContradiction [dec : Decidable p] (h : ¬p → False) : p :=
|
||||
theorem of_not_not [Decidable p] : ¬ ¬ p → p :=
|
||||
fun hnn => byContradiction (fun hn => absurd hn hnn)
|
||||
|
||||
theorem not_and_iff_or_not {p q : Prop} [d₁ : Decidable p] [d₂ : Decidable q] : ¬ (p ∧ q) ↔ ¬ p ∨ ¬ q :=
|
||||
theorem not_and_iff_or_not (p q : Prop) [d₁ : Decidable p] [d₂ : Decidable q] : ¬ (p ∧ q) ↔ ¬ p ∨ ¬ q :=
|
||||
Iff.intro
|
||||
(fun h => match d₁, d₂ with
|
||||
| isTrue h₁, isTrue h₂ => absurd (And.intro h₁ h₂) h
|
||||
@@ -1133,13 +1104,6 @@ inductive Relation.TransGen {α : Sort u} (r : α → α → Prop) : α → α
|
||||
/-- Deprecated synonym for `Relation.TransGen`. -/
|
||||
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
|
||||
|
||||
theorem Relation.TransGen.trans {α : Sort u} {r : α → α → Prop} {a b c} :
|
||||
TransGen r a b → TransGen r b c → TransGen r a c := by
|
||||
intro hab hbc
|
||||
induction hbc with
|
||||
| single h => exact TransGen.tail hab h
|
||||
| tail _ h ih => exact TransGen.tail ih h
|
||||
|
||||
/-! # Subtype -/
|
||||
|
||||
namespace Subtype
|
||||
@@ -1168,20 +1132,12 @@ end Subtype
|
||||
section
|
||||
variable {α : Type u} {β : Type v}
|
||||
|
||||
/-- This is not an instance to avoid non-canonical instances. -/
|
||||
@[reducible] def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
|
||||
instance Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
|
||||
default := Sum.inl default
|
||||
|
||||
/-- This is not an instance to avoid non-canonical instances. -/
|
||||
@[reducible] def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
|
||||
instance Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
|
||||
default := Sum.inr default
|
||||
|
||||
instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
|
||||
Nonempty.elim h (fun a => ⟨Sum.inl a⟩)
|
||||
|
||||
instance Sum.nonemptyRight [h : Nonempty β] : Nonempty (Sum α β) :=
|
||||
Nonempty.elim h (fun b => ⟨Sum.inr b⟩)
|
||||
|
||||
instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b =>
|
||||
match a, b with
|
||||
| Sum.inl a, Sum.inl b =>
|
||||
@@ -1197,21 +1153,6 @@ end
|
||||
|
||||
/-! # Product -/
|
||||
|
||||
instance [h1 : Nonempty α] [h2 : Nonempty β] : Nonempty (α × β) :=
|
||||
Nonempty.elim h1 fun x =>
|
||||
Nonempty.elim h2 fun y =>
|
||||
⟨(x, y)⟩
|
||||
|
||||
instance [h1 : Nonempty α] [h2 : Nonempty β] : Nonempty (MProd α β) :=
|
||||
Nonempty.elim h1 fun x =>
|
||||
Nonempty.elim h2 fun y =>
|
||||
⟨⟨x, y⟩⟩
|
||||
|
||||
instance [h1 : Nonempty α] [h2 : Nonempty β] : Nonempty (PProd α β) :=
|
||||
Nonempty.elim h1 fun x =>
|
||||
Nonempty.elim h2 fun y =>
|
||||
⟨⟨x, y⟩⟩
|
||||
|
||||
instance [Inhabited α] [Inhabited β] : Inhabited (α × β) where
|
||||
default := (default, default)
|
||||
|
||||
@@ -1392,7 +1333,7 @@ theorem Nat.succ.inj {m n : Nat} : m.succ = n.succ → m = n :=
|
||||
theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) :=
|
||||
Eq.propIntro Nat.succ.inj (congrArg Nat.succ)
|
||||
|
||||
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b ↔ a = b :=
|
||||
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] (a b : α) : a == b ↔ a = b :=
|
||||
⟨eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl⟩
|
||||
|
||||
/-! # Prop lemmas -/
|
||||
@@ -1457,7 +1398,7 @@ theorem false_of_true_eq_false (h : True = False) : False := false_of_true_iff_
|
||||
|
||||
theorem true_eq_false_of_false : False → (True = False) := False.elim
|
||||
|
||||
theorem iff_def : (a ↔ b) ↔ (a → b) ∧ (b → a) := iff_iff_implies_and_implies
|
||||
theorem iff_def : (a ↔ b) ↔ (a → b) ∧ (b → a) := iff_iff_implies_and_implies a b
|
||||
theorem iff_def' : (a ↔ b) ↔ (b → a) ∧ (a → b) := Iff.trans iff_def And.comm
|
||||
|
||||
theorem true_iff_false : (True ↔ False) ↔ False := iff_false_intro (·.mp True.intro)
|
||||
@@ -1485,7 +1426,7 @@ theorem imp_true_iff (α : Sort u) : (α → True) ↔ True := iff_true_intro (f
|
||||
|
||||
theorem false_imp_iff (a : Prop) : (False → a) ↔ True := iff_true_intro False.elim
|
||||
|
||||
theorem true_imp_iff {α : Prop} : (True → α) ↔ α := imp_iff_right True.intro
|
||||
theorem true_imp_iff (α : Prop) : (True → α) ↔ α := imp_iff_right True.intro
|
||||
|
||||
@[simp high] theorem imp_self : (a → a) ↔ True := iff_true_intro id
|
||||
|
||||
@@ -1605,7 +1546,7 @@ so you should consider the simpler versions if they apply:
|
||||
* `Quot.recOnSubsingleton`, when the target type is a `Subsingleton`
|
||||
* `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption
|
||||
-/
|
||||
@[elab_as_elim] protected abbrev rec
|
||||
protected abbrev rec
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
(h : (a b : α) → (p : r a b) → Eq.ndrec (f a) (sound p) = f b)
|
||||
(q : Quot r) : motive q :=
|
||||
@@ -1691,7 +1632,7 @@ protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s → Pro
|
||||
|
||||
/--
|
||||
The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`,
|
||||
then it lifts to a function on `Quotient s` such that `liftOn (mk a) f h = f a`.
|
||||
then it lifts to a function on `Quotient s` such that `lift (mk a) f h = f a`.
|
||||
-/
|
||||
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α → β) (c : (a b : α) → a ≈ b → f a = f b) : β :=
|
||||
Quot.liftOn q f c
|
||||
@@ -1896,8 +1837,7 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
|
||||
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
|
||||
exact congrArg extfunApp (Quot.sound h)
|
||||
|
||||
instance Pi.instSubsingleton {α : Sort u} {β : α → Sort v} [∀ a, Subsingleton (β a)] :
|
||||
Subsingleton (∀ a, β a) where
|
||||
instance {α : Sort u} {β : α → Sort v} [∀ a, Subsingleton (β a)] : Subsingleton (∀ a, β a) where
|
||||
allEq f g := funext fun a => Subsingleton.elim (f a) (g a)
|
||||
|
||||
/-! # Squash -/
|
||||
@@ -2060,7 +2000,7 @@ class IdempotentOp (op : α → α → α) : Prop where
|
||||
`LeftIdentify op o` indicates `o` is a left identity of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and
|
||||
is used primarily for inferring the identity using class resolution.
|
||||
is used primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class LeftIdentity (op : α → β → β) (o : outParam α) : Prop
|
||||
|
||||
@@ -2076,7 +2016,7 @@ class LawfulLeftIdentity (op : α → β → β) (o : outParam α) extends LeftI
|
||||
`RightIdentify op o` indicates `o` is a right identity `o` of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for inferring the identity using class resolution.
|
||||
primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class RightIdentity (op : α → β → α) (o : outParam β) : Prop
|
||||
|
||||
@@ -2092,7 +2032,7 @@ class LawfulRightIdentity (op : α → β → α) (o : outParam β) extends Righ
|
||||
`Identity op o` indicates `o` is a left and right identity of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for inferring the identity using class resolution.
|
||||
primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class Identity (op : α → α → α) (o : outParam α) extends LeftIdentity op o, RightIdentity op o : Prop
|
||||
|
||||
|
||||
@@ -33,10 +33,7 @@ import Init.Data.Prod
|
||||
import Init.Data.AC
|
||||
import Init.Data.Queue
|
||||
import Init.Data.Channel
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Sum
|
||||
import Init.Data.BEq
|
||||
import Init.Data.Subtype
|
||||
import Init.Data.ULift
|
||||
import Init.Data.PLift
|
||||
import Init.Data.Zero
|
||||
import Init.Data.NeZero
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Dany Fabian
|
||||
|
||||
prelude
|
||||
import Init.Classical
|
||||
import Init.ByCases
|
||||
import Init.Data.List
|
||||
|
||||
namespace Lean.Data.AC
|
||||
inductive Expr
|
||||
@@ -260,7 +260,7 @@ theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm
|
||||
simp [ContextInformation.isComm, Option.isSome] at h
|
||||
match h₂ : ctx.comm with
|
||||
| none =>
|
||||
simp [h₂] at h
|
||||
simp only [h₂] at h
|
||||
| some val =>
|
||||
simp [h₂] at h
|
||||
exact val.down
|
||||
|
||||
@@ -14,5 +14,3 @@ import Init.Data.Array.Attach
|
||||
import Init.Data.Array.BasicAux
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.TakeDrop
|
||||
import Init.Data.Array.Bootstrap
|
||||
import Init.Data.Array.GetLit
|
||||
|
||||
@@ -20,7 +20,7 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
with the same elements but in the type `{x // P x}`. -/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(xs : Array α) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Array {x // P x} :=
|
||||
⟨xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)⟩
|
||||
⟨xs.data.attachWith P fun x h => H x (Array.Mem.mk h)⟩
|
||||
|
||||
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
|
||||
with the same elements but in the type `{x // x ∈ xs}`. -/
|
||||
|
||||
@@ -13,75 +13,42 @@ import Init.Data.ToString.Basic
|
||||
import Init.GetElem
|
||||
universe u v w
|
||||
|
||||
/-! ### Array literal syntax -/
|
||||
|
||||
syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term
|
||||
|
||||
macro_rules
|
||||
| `(#[ $elems,* ]) => `(List.toArray [ $elems,* ])
|
||||
|
||||
namespace Array
|
||||
variable {α : Type u}
|
||||
|
||||
namespace Array
|
||||
@[extern "lean_mk_array"]
|
||||
def mkArray {α : Type u} (n : Nat) (v : α) : Array α := {
|
||||
data := List.replicate n v
|
||||
}
|
||||
|
||||
/-! ### Preliminary theorems -/
|
||||
/--
|
||||
`ofFn f` with `f : Fin n → α` returns the list whose ith element is `f i`.
|
||||
```
|
||||
ofFn f = #[f 0, f 1, ... , f(n - 1)]
|
||||
``` -/
|
||||
def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty n) where
|
||||
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
|
||||
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
|
||||
|
||||
@[simp] theorem size_set (a : Array α) (i : Fin a.size) (v : α) : (set a i v).size = a.size :=
|
||||
List.length_set ..
|
||||
/-- The array `#[0, 1, ..., n - 1]`. -/
|
||||
def range (n : Nat) : Array Nat :=
|
||||
n.fold (flip Array.push) (mkEmpty n)
|
||||
|
||||
@[simp] theorem size_push (a : Array α) (v : α) : (push a v).size = a.size + 1 :=
|
||||
List.length_concat ..
|
||||
@[simp] theorem size_mkArray (n : Nat) (v : α) : (mkArray n v).size = n :=
|
||||
List.length_replicate ..
|
||||
|
||||
theorem ext (a b : Array α)
|
||||
(h₁ : a.size = b.size)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.size) → (hi₂ : i < b.size) → a[i] = b[i])
|
||||
: a = b := by
|
||||
let rec extAux (a b : List α)
|
||||
(h₁ : a.length = b.length)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.length) → (hi₂ : i < b.length) → a.get ⟨i, hi₁⟩ = b.get ⟨i, hi₂⟩)
|
||||
: a = b := by
|
||||
induction a generalizing b with
|
||||
| nil =>
|
||||
cases b with
|
||||
| nil => rfl
|
||||
| cons b bs => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons a as ih =>
|
||||
cases b with
|
||||
| nil => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons b bs =>
|
||||
have hz₁ : 0 < (a::as).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have headEq : a = b := h₂ 0 hz₁ hz₂
|
||||
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁
|
||||
have h₂' : (i : Nat) → (hi₁ : i < as.length) → (hi₂ : i < bs.length) → as.get ⟨i, hi₁⟩ = bs.get ⟨i, hi₂⟩ := by
|
||||
intro i hi₁ hi₂
|
||||
have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have hi₂' : i+1 < (b::bs).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have : (a::as).get ⟨i+1, hi₁'⟩ = (b::bs).get ⟨i+1, hi₂'⟩ := h₂ (i+1) hi₁' hi₂'
|
||||
apply this
|
||||
have tailEq : as = bs := ih bs h₁' h₂'
|
||||
rw [headEq, tailEq]
|
||||
cases a; cases b
|
||||
apply congrArg
|
||||
apply extAux
|
||||
assumption
|
||||
assumption
|
||||
instance : EmptyCollection (Array α) := ⟨Array.empty⟩
|
||||
instance : Inhabited (Array α) where
|
||||
default := Array.empty
|
||||
|
||||
theorem ext' {as bs : Array α} (h : as.toList = bs.toList) : as = bs := by
|
||||
cases as; cases bs; simp at h; rw [h]
|
||||
@[simp] def isEmpty (a : Array α) : Bool :=
|
||||
a.size = 0
|
||||
|
||||
@[simp] theorem toArrayAux_eq (as : List α) (acc : Array α) : (as.toArrayAux acc).toList = acc.toList ++ as := by
|
||||
induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toList_toArray (as : List α) : as.toArray.toList = as := rfl
|
||||
|
||||
@[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size]
|
||||
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @toList_toArray
|
||||
|
||||
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
|
||||
|
||||
/-! ### Externs -/
|
||||
def singleton (v : α) : Array α :=
|
||||
mkArray 1 v
|
||||
|
||||
/-- Low-level version of `size` that directly queries the C array object cached size.
|
||||
While this is not provable, `usize` always returns the exact size of the array since
|
||||
@@ -97,6 +64,29 @@ def usize (a : @& Array α) : USize := a.size.toUSize
|
||||
def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
|
||||
a[i.toNat]
|
||||
|
||||
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
|
||||
def back [Inhabited α] (a : Array α) : α :=
|
||||
a.get! (a.size - 1)
|
||||
|
||||
def get? (a : Array α) (i : Nat) : Option α :=
|
||||
if h : i < a.size then some a[i] else none
|
||||
|
||||
def back? (a : Array α) : Option α :=
|
||||
a.get? (a.size - 1)
|
||||
|
||||
-- auxiliary declaration used in the equation compiler when pattern matching array literals.
|
||||
abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size = n) (h₂ : i < n) : α :=
|
||||
have := h₁.symm ▸ h₂
|
||||
a[i]
|
||||
|
||||
@[simp] theorem size_set (a : Array α) (i : Fin a.size) (v : α) : (set a i v).size = a.size :=
|
||||
List.length_set ..
|
||||
|
||||
@[simp] theorem size_push (a : Array α) (v : α) : (push a v).size = a.size + 1 :=
|
||||
List.length_concat ..
|
||||
|
||||
/-- Low-level version of `fset` which is as fast as a C array fset.
|
||||
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
|
||||
`fset` may be slightly slower than `uset`. -/
|
||||
@@ -104,19 +94,6 @@ def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
|
||||
def uset (a : Array α) (i : USize) (v : α) (h : i.toNat < a.size) : Array α :=
|
||||
a.set ⟨i.toNat, h⟩ v
|
||||
|
||||
@[extern "lean_array_pop"]
|
||||
def pop (a : Array α) : Array α where
|
||||
toList := a.toList.dropLast
|
||||
|
||||
@[simp] theorem size_pop (a : Array α) : a.pop.size = a.size - 1 := by
|
||||
match a with
|
||||
| ⟨[]⟩ => rfl
|
||||
| ⟨a::as⟩ => simp [pop, Nat.succ_sub_succ_eq_sub, size]
|
||||
|
||||
@[extern "lean_mk_array"]
|
||||
def mkArray {α : Type u} (n : Nat) (v : α) : Array α where
|
||||
toList := List.replicate n v
|
||||
|
||||
/--
|
||||
Swaps two entries in an array.
|
||||
|
||||
@@ -130,10 +107,6 @@ def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
|
||||
let a' := a.set i v₂
|
||||
a'.set (size_set a i v₂ ▸ j) v₁
|
||||
|
||||
@[simp] theorem size_swap (a : Array α) (i j : Fin a.size) : (a.swap i j).size = a.size := by
|
||||
show ((a.set i (a.get j)).set (size_set a i _ ▸ j) (a.get i)).size = a.size
|
||||
rw [size_set, size_set]
|
||||
|
||||
/--
|
||||
Swaps two entries in an array, or returns the array unchanged if either index is out of bounds.
|
||||
|
||||
@@ -147,64 +120,6 @@ def swap! (a : Array α) (i j : @& Nat) : Array α :=
|
||||
else a
|
||||
else a
|
||||
|
||||
/-! ### GetElem instance for `USize`, backed by `uget` -/
|
||||
|
||||
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
|
||||
/-! ### Definitions -/
|
||||
|
||||
instance : EmptyCollection (Array α) := ⟨Array.empty⟩
|
||||
instance : Inhabited (Array α) where
|
||||
default := Array.empty
|
||||
|
||||
@[simp] def isEmpty (a : Array α) : Bool :=
|
||||
a.size = 0
|
||||
|
||||
@[specialize]
|
||||
def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) :
|
||||
∀ (i : Nat) (_ : i ≤ a.size), Bool
|
||||
| 0, _ => true
|
||||
| i+1, h =>
|
||||
p a[i] (b[i]'(hsz ▸ h)) && isEqvAux a b hsz p i (Nat.le_trans (Nat.le_add_right i 1) h)
|
||||
|
||||
@[inline] def isEqv (a b : Array α) (p : α → α → Bool) : Bool :=
|
||||
if h : a.size = b.size then
|
||||
isEqvAux a b h p a.size (Nat.le_refl a.size)
|
||||
else
|
||||
false
|
||||
|
||||
instance [BEq α] : BEq (Array α) :=
|
||||
⟨fun a b => isEqv a b BEq.beq⟩
|
||||
|
||||
/--
|
||||
`ofFn f` with `f : Fin n → α` returns the list whose ith element is `f i`.
|
||||
```
|
||||
ofFn f = #[f 0, f 1, ... , f(n - 1)]
|
||||
``` -/
|
||||
def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty n) where
|
||||
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
go (i : Nat) (acc : Array α) : Array α :=
|
||||
if h : i < n then go (i+1) (acc.push (f ⟨i, h⟩)) else acc
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
/-- The array `#[0, 1, ..., n - 1]`. -/
|
||||
def range (n : Nat) : Array Nat :=
|
||||
n.fold (flip Array.push) (mkEmpty n)
|
||||
|
||||
def singleton (v : α) : Array α :=
|
||||
mkArray 1 v
|
||||
|
||||
def back [Inhabited α] (a : Array α) : α :=
|
||||
a.get! (a.size - 1)
|
||||
|
||||
def get? (a : Array α) (i : Nat) : Option α :=
|
||||
if h : i < a.size then some a[i] else none
|
||||
|
||||
def back? (a : Array α) : Option α :=
|
||||
a.get? (a.size - 1)
|
||||
|
||||
@[inline] def swapAt (a : Array α) (i : Fin a.size) (v : α) : α × Array α :=
|
||||
let e := a.get i
|
||||
let a := a.set i v
|
||||
@@ -218,6 +133,11 @@ def swapAt! (a : Array α) (i : Nat) (v : α) : α × Array α :=
|
||||
have : Inhabited α := ⟨v⟩
|
||||
panic! ("index " ++ toString i ++ " out of bounds")
|
||||
|
||||
@[extern "lean_array_pop"]
|
||||
def pop (a : Array α) : Array α := {
|
||||
data := a.data.dropLast
|
||||
}
|
||||
|
||||
def shrink (a : Array α) (n : Nat) : Array α :=
|
||||
let rec loop
|
||||
| 0, a => a
|
||||
@@ -386,12 +306,12 @@ unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
|
||||
def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m β) (as : Array α) : m (Array β) :=
|
||||
-- Note: we cannot use `foldlM` here for the reference implementation because this calls
|
||||
-- `bind` and `pure` too many times. (We are not assuming `m` is a `LawfulMonad`)
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
map (i : Nat) (r : Array β) : m (Array β) := do
|
||||
if hlt : i < as.size then
|
||||
map (i+1) (r.push (← f as[i]))
|
||||
else
|
||||
pure r
|
||||
let rec map (i : Nat) (r : Array β) : m (Array β) := do
|
||||
if hlt : i < as.size then
|
||||
map (i+1) (r.push (← f as[i]))
|
||||
else
|
||||
pure r
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
map 0 (mkEmpty as.size)
|
||||
|
||||
@@ -455,8 +375,7 @@ unsafe def anyMUnsafe {α : Type u} {m : Type → Type w} [Monad m] (p : α →
|
||||
@[implemented_by anyMUnsafe]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
|
||||
let any (stop : Nat) (h : stop ≤ as.size) :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) : m Bool := do
|
||||
let rec loop (j : Nat) : m Bool := do
|
||||
if hlt : j < stop then
|
||||
have : j < as.size := Nat.lt_of_lt_of_le hlt h
|
||||
if (← p as[j]) then
|
||||
@@ -465,6 +384,7 @@ def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as :
|
||||
loop (j+1)
|
||||
else
|
||||
pure false
|
||||
termination_by stop - j
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
loop start
|
||||
if h : stop ≤ as.size then
|
||||
@@ -546,28 +466,16 @@ def findRev? {α : Type} (as : Array α) (p : α → Bool) : Option α :=
|
||||
|
||||
@[inline]
|
||||
def findIdx? {α : Type u} (as : Array α) (p : α → Bool) : Option Nat :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) :=
|
||||
let rec loop (j : Nat) :=
|
||||
if h : j < as.size then
|
||||
if p as[j] then some j else loop (j + 1)
|
||||
else none
|
||||
termination_by as.size - j
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
loop 0
|
||||
|
||||
def getIdx? [BEq α] (a : Array α) (v : α) : Option Nat :=
|
||||
a.findIdx? fun a => a == v
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size) :=
|
||||
if h : i < a.size then
|
||||
let idx : Fin a.size := ⟨i, h⟩;
|
||||
if a.get idx == v then some idx
|
||||
else indexOfAux a v (i+1)
|
||||
else none
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) :=
|
||||
indexOfAux a v 0
|
||||
a.findIdx? fun a => a == v
|
||||
|
||||
@[inline]
|
||||
def any (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool :=
|
||||
@@ -583,11 +491,18 @@ def contains [BEq α] (as : Array α) (a : α) : Bool :=
|
||||
def elem [BEq α] (a : α) (as : Array α) : Bool :=
|
||||
as.contains a
|
||||
|
||||
@[inline] def getEvenElems (as : Array α) : Array α :=
|
||||
(·.2) <| as.foldl (init := (true, Array.empty)) fun (even, r) a =>
|
||||
if even then
|
||||
(false, r.push a)
|
||||
else
|
||||
(true, r)
|
||||
|
||||
/-- Convert a `Array α` into an `List α`. This is O(n) in the size of the array. -/
|
||||
-- This function is exported to C, where it is called by `Array.toList`
|
||||
-- This function is exported to C, where it is called by `Array.data`
|
||||
-- (the projection) to implement this functionality.
|
||||
@[export lean_array_to_list_impl]
|
||||
def toListImpl (as : Array α) : List α :=
|
||||
@[export lean_array_to_list]
|
||||
def toList (as : Array α) : List α :=
|
||||
as.foldr List.cons []
|
||||
|
||||
/-- Prepends an `Array α` onto the front of a list. Equivalent to `as.toList ++ l`. -/
|
||||
@@ -595,6 +510,17 @@ def toListImpl (as : Array α) : List α :=
|
||||
def toListAppend (as : Array α) (l : List α) : List α :=
|
||||
as.foldr List.cons l
|
||||
|
||||
instance {α : Type u} [Repr α] : Repr (Array α) where
|
||||
reprPrec a _ :=
|
||||
let _ : Std.ToFormat α := ⟨repr⟩
|
||||
if a.size == 0 then
|
||||
"#[]"
|
||||
else
|
||||
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList a) ("," ++ Std.Format.line)) "]"
|
||||
|
||||
instance [ToString α] : ToString (Array α) where
|
||||
toString a := "#" ++ toString a.toList
|
||||
|
||||
protected def append (as : Array α) (bs : Array α) : Array α :=
|
||||
bs.foldl (init := as) fun r v => r.push v
|
||||
|
||||
@@ -620,13 +546,44 @@ def concatMap (f : α → Array β) (as : Array α) : Array β :=
|
||||
def flatten (as : Array (Array α)) : Array α :=
|
||||
as.foldl (init := empty) fun r a => r ++ a
|
||||
|
||||
end Array
|
||||
|
||||
export Array (mkArray)
|
||||
|
||||
syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term
|
||||
|
||||
macro_rules
|
||||
| `(#[ $elems,* ]) => `(List.toArray [ $elems,* ])
|
||||
|
||||
namespace Array
|
||||
|
||||
-- TODO(Leo): cleanup
|
||||
@[specialize]
|
||||
def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) (i : Nat) : Bool :=
|
||||
if h : i < a.size then
|
||||
have : i < b.size := hsz ▸ h
|
||||
p a[i] b[i] && isEqvAux a b hsz p (i+1)
|
||||
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
|
||||
isEqvAux a b h p 0
|
||||
else
|
||||
false
|
||||
|
||||
instance [BEq α] : BEq (Array α) :=
|
||||
⟨fun a b => isEqv a b BEq.beq⟩
|
||||
|
||||
@[inline]
|
||||
def filter (p : α → Bool) (as : Array α) (start := 0) (stop := as.size) : Array α :=
|
||||
as.foldl (init := #[]) (start := start) (stop := stop) fun r a =>
|
||||
if p a then r.push a else r
|
||||
|
||||
@[inline]
|
||||
def filterM {α : Type} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m (Array α) :=
|
||||
def filterM [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m (Array α) :=
|
||||
as.foldlM (init := #[]) (start := start) (stop := stop) fun r a => do
|
||||
if (← p a) then return r.push a else return r
|
||||
|
||||
@@ -661,25 +618,93 @@ def partition (p : α → Bool) (as : Array α) : Array α × Array α := Id.run
|
||||
cs := cs.push a
|
||||
return (bs, cs)
|
||||
|
||||
theorem ext (a b : Array α)
|
||||
(h₁ : a.size = b.size)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.size) → (hi₂ : i < b.size) → a[i] = b[i])
|
||||
: a = b := by
|
||||
let rec extAux (a b : List α)
|
||||
(h₁ : a.length = b.length)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.length) → (hi₂ : i < b.length) → a.get ⟨i, hi₁⟩ = b.get ⟨i, hi₂⟩)
|
||||
: a = b := by
|
||||
induction a generalizing b with
|
||||
| nil =>
|
||||
cases b with
|
||||
| nil => rfl
|
||||
| cons b bs => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons a as ih =>
|
||||
cases b with
|
||||
| nil => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons b bs =>
|
||||
have hz₁ : 0 < (a::as).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have headEq : a = b := h₂ 0 hz₁ hz₂
|
||||
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁
|
||||
have h₂' : (i : Nat) → (hi₁ : i < as.length) → (hi₂ : i < bs.length) → as.get ⟨i, hi₁⟩ = bs.get ⟨i, hi₂⟩ := by
|
||||
intro i hi₁ hi₂
|
||||
have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have hi₂' : i+1 < (b::bs).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have : (a::as).get ⟨i+1, hi₁'⟩ = (b::bs).get ⟨i+1, hi₂'⟩ := h₂ (i+1) hi₁' hi₂'
|
||||
apply this
|
||||
have tailEq : as = bs := ih bs h₁' h₂'
|
||||
rw [headEq, tailEq]
|
||||
cases a; cases b
|
||||
apply congrArg
|
||||
apply extAux
|
||||
assumption
|
||||
assumption
|
||||
|
||||
theorem extLit {n : Nat}
|
||||
(a b : Array α)
|
||||
(hsz₁ : a.size = n) (hsz₂ : b.size = n)
|
||||
(h : (i : Nat) → (hi : i < n) → a.getLit i hsz₁ hi = b.getLit i hsz₂ hi) : a = b :=
|
||||
Array.ext a b (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ ▸ hi₁)
|
||||
|
||||
end Array
|
||||
|
||||
-- CLEANUP the following code
|
||||
namespace Array
|
||||
|
||||
def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size) :=
|
||||
if h : i < a.size then
|
||||
let idx : Fin a.size := ⟨i, h⟩;
|
||||
if a.get idx == v then some idx
|
||||
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
|
||||
|
||||
@[simp] theorem size_swap (a : Array α) (i j : Fin a.size) : (a.swap i j).size = a.size := by
|
||||
show ((a.set i (a.get j)).set (size_set a i _ ▸ j) (a.get i)).size = a.size
|
||||
rw [size_set, size_set]
|
||||
|
||||
@[simp] theorem size_pop (a : Array α) : a.pop.size = a.size - 1 := by
|
||||
match a with
|
||||
| ⟨[]⟩ => rfl
|
||||
| ⟨a::as⟩ => simp [pop, Nat.succ_sub_succ_eq_sub, size]
|
||||
|
||||
theorem reverse.termination {i j : Nat} (h : i < j) : j - 1 - (i + 1) < j - i := by
|
||||
rw [Nat.sub_sub, Nat.add_comm]
|
||||
exact Nat.lt_of_le_of_lt (Nat.pred_le _) (Nat.sub_succ_lt_self _ _ h)
|
||||
|
||||
def reverse (as : Array α) : Array α :=
|
||||
if h : as.size ≤ 1 then
|
||||
as
|
||||
else
|
||||
loop as 0 ⟨as.size - 1, Nat.pred_lt (mt (fun h : as.size = 0 => h ▸ by decide) h)⟩
|
||||
where
|
||||
termination {i j : Nat} (h : i < j) : j - 1 - (i + 1) < j - i := by
|
||||
rw [Nat.sub_sub, Nat.add_comm]
|
||||
exact Nat.lt_of_le_of_lt (Nat.pred_le _) (Nat.sub_succ_lt_self _ _ h)
|
||||
loop (as : Array α) (i : Nat) (j : Fin as.size) :=
|
||||
if h : i < j then
|
||||
have := termination h
|
||||
have := reverse.termination h
|
||||
let as := as.swap ⟨i, Nat.lt_trans h j.2⟩ j
|
||||
have : j-1 < as.size := by rw [size_swap]; exact Nat.lt_of_le_of_lt (Nat.pred_le _) j.2
|
||||
loop as (i+1) ⟨j-1, this⟩
|
||||
else
|
||||
as
|
||||
termination_by j - i
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
if h : as.size > 0 then
|
||||
if p (as.get ⟨as.size - 1, Nat.sub_lt h (by decide)⟩) then
|
||||
@@ -688,11 +713,11 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
as
|
||||
else
|
||||
as
|
||||
termination_by as.size
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
go (i : Nat) (r : Array α) : Array α :=
|
||||
let rec go (i : Nat) (r : Array α) : Array α :=
|
||||
if h : i < as.size then
|
||||
let a := as.get ⟨i, h⟩
|
||||
if p a then
|
||||
@@ -701,6 +726,7 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
r
|
||||
else
|
||||
r
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
go 0 #[]
|
||||
|
||||
@@ -708,7 +734,6 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
|
||||
This function takes worst case O(n) time because
|
||||
it has to backshift all elements at positions greater than `i`.-/
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
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
|
||||
@@ -719,7 +744,6 @@ def feraseIdx (a : Array α) (i : Fin a.size) : Array α :=
|
||||
termination_by a.size - i.val
|
||||
decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ i.isLt
|
||||
|
||||
-- This is required in `Lean.Data.PersistentHashMap`.
|
||||
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 =>
|
||||
@@ -743,14 +767,14 @@ def erase [BEq α] (as : Array α) (a : α) : Array α :=
|
||||
|
||||
/-- Insert element `a` at position `i`. -/
|
||||
@[inline] def insertAt (as : Array α) (i : Fin (as.size + 1)) (a : α) : Array α :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (as : Array α) (j : Fin as.size) :=
|
||||
let rec loop (as : Array α) (j : Fin as.size) :=
|
||||
if i.1 < j then
|
||||
let j' := ⟨j-1, Nat.lt_of_le_of_lt (Nat.pred_le _) j.2⟩
|
||||
let as := as.swap j' j
|
||||
loop as ⟨j', by rw [size_swap]; exact j'.2⟩
|
||||
else
|
||||
as
|
||||
termination_by j.1
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
let j := as.size
|
||||
let as := as.push a
|
||||
@@ -762,7 +786,37 @@ def insertAt! (as : Array α) (i : Nat) (a : α) : Array α :=
|
||||
insertAt as ⟨i, Nat.lt_succ_of_le h⟩ a
|
||||
else panic! "invalid index"
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : ∀ (i : Nat), i ≤ a.size → List α → List α
|
||||
| 0, _, acc => acc
|
||||
| (i+1), hi, acc => toListLitAux a n hsz i (Nat.le_of_succ_le hi) (a.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
|
||||
|
||||
def toArrayLit (a : Array α) (n : Nat) (hsz : a.size = n) : Array α :=
|
||||
List.toArray <| toListLitAux a n hsz n (hsz ▸ Nat.le_refl _) []
|
||||
|
||||
theorem ext' {as bs : Array α} (h : as.data = bs.data) : as = bs := by
|
||||
cases as; cases bs; simp at h; rw [h]
|
||||
|
||||
@[simp] theorem toArrayAux_eq (as : List α) (acc : Array α) : (as.toArrayAux acc).data = acc.data ++ as := by
|
||||
induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append]
|
||||
|
||||
theorem data_toArray (as : List α) : as.toArray.data = as := by
|
||||
simp [List.toArray, Array.mkEmpty]
|
||||
|
||||
theorem toArrayLit_eq (as : Array α) (n : Nat) (hsz : as.size = n) : as = toArrayLit as n hsz := by
|
||||
apply ext'
|
||||
simp [toArrayLit, data_toArray]
|
||||
have hle : n ≤ as.size := hsz ▸ Nat.le_refl _
|
||||
have hge : as.size ≤ n := hsz ▸ Nat.le_refl _
|
||||
have := go n hle
|
||||
rw [List.drop_eq_nil_of_le hge] at this
|
||||
rw [this]
|
||||
where
|
||||
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.data i ((id (α := as.data.length = n) h₁) ▸ h₂) :=
|
||||
rfl
|
||||
|
||||
go (i : Nat) (hi : i ≤ as.size) : toListLitAux as n hsz i hi (as.data.drop i) = as.data := by
|
||||
induction i <;> simp [getLit_eq, List.get_drop_eq_drop, toListLitAux, List.drop, *]
|
||||
|
||||
def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
@@ -774,6 +828,7 @@ def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : N
|
||||
false
|
||||
else
|
||||
true
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
/-- Return true iff `as` is a prefix of `bs`.
|
||||
@@ -784,8 +839,24 @@ def isPrefixOf [BEq α] (as bs : Array α) : Bool :=
|
||||
else
|
||||
false
|
||||
|
||||
@[semireducible, specialize] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def zipWithAux (f : α → β → γ) (as : Array α) (bs : Array β) (i : Nat) (cs : Array γ) : Array γ :=
|
||||
private def allDiffAuxAux [BEq α] (as : Array α) (a : α) : forall (i : Nat), i < as.size → Bool
|
||||
| 0, _ => true
|
||||
| i+1, h =>
|
||||
have : i < as.size := Nat.lt_trans (Nat.lt_succ_self _) h;
|
||||
a != as[i] && allDiffAuxAux as a i this
|
||||
|
||||
private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
|
||||
else
|
||||
true
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
allDiffAux as 0
|
||||
|
||||
@[specialize] def zipWithAux (f : α → β → γ) (as : Array α) (bs : Array β) (i : Nat) (cs : Array γ) : Array γ :=
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
if h : i < bs.size then
|
||||
@@ -795,6 +866,7 @@ def zipWithAux (f : α → β → γ) (as : Array α) (bs : Array β) (i : Nat)
|
||||
cs
|
||||
else
|
||||
cs
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
@[inline] def zipWith (as : Array α) (bs : Array β) (f : α → β → γ) : Array γ :=
|
||||
@@ -810,66 +882,4 @@ def split (as : Array α) (p : α → Bool) : Array α × Array α :=
|
||||
as.foldl (init := (#[], #[])) fun (as, bs) a =>
|
||||
if p a then (as.push a, bs) else (as, bs.push a)
|
||||
|
||||
/-! ## Auxiliary functions used in metaprogramming.
|
||||
|
||||
We do not intend to provide verification theorems for these functions.
|
||||
-/
|
||||
|
||||
/-! ### eraseReps -/
|
||||
|
||||
/--
|
||||
`O(|l|)`. Erase repeated adjacent elements. Keeps the first occurrence of each run.
|
||||
* `eraseReps #[1, 3, 2, 2, 2, 3, 5] = #[1, 3, 2, 3, 5]`
|
||||
-/
|
||||
def eraseReps {α} [BEq α] (as : Array α) : Array α :=
|
||||
if h : 0 < as.size then
|
||||
let ⟨last, r⟩ := as.foldl (init := (as[0], #[])) fun ⟨last, r⟩ a =>
|
||||
if a == last then ⟨last, r⟩ else ⟨a, r.push last⟩
|
||||
r.push last
|
||||
else
|
||||
#[]
|
||||
|
||||
/-! ### allDiff -/
|
||||
|
||||
private def allDiffAuxAux [BEq α] (as : Array α) (a : α) : forall (i : Nat), i < as.size → Bool
|
||||
| 0, _ => true
|
||||
| i+1, h =>
|
||||
have : i < as.size := Nat.lt_trans (Nat.lt_succ_self _) h;
|
||||
a != as[i] && allDiffAuxAux as a i this
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
|
||||
else
|
||||
true
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
allDiffAux as 0
|
||||
|
||||
/-! ### getEvenElems -/
|
||||
|
||||
@[inline] def getEvenElems (as : Array α) : Array α :=
|
||||
(·.2) <| as.foldl (init := (true, Array.empty)) fun (even, r) a =>
|
||||
if even then
|
||||
(false, r.push a)
|
||||
else
|
||||
(true, r)
|
||||
|
||||
/-! ### Repr and ToString -/
|
||||
|
||||
instance {α : Type u} [Repr α] : Repr (Array α) where
|
||||
reprPrec a _ :=
|
||||
let _ : Std.ToFormat α := ⟨repr⟩
|
||||
if a.size == 0 then
|
||||
"#[]"
|
||||
else
|
||||
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList a) ("," ++ Std.Format.line)) "]"
|
||||
|
||||
instance [ToString α] : ToString (Array α) where
|
||||
toString a := "#" ++ toString a.toList
|
||||
|
||||
end Array
|
||||
|
||||
export Array (mkArray)
|
||||
|
||||
@@ -34,11 +34,11 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
|
||||
|
||||
@[simp] theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by
|
||||
apply propext; apply Iff.intro
|
||||
· intro h; simpa [toArray] using h
|
||||
· intro h; simp [toArray] at h; have := of_toArrayAux_eq_toArrayAux h rfl; exact this.1
|
||||
· intro h; rw [h]
|
||||
|
||||
def Array.mapM' [Monad m] (f : α → m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
|
||||
go 0 ⟨mkEmpty as.size, rfl⟩ (by simp)
|
||||
go 0 ⟨mkEmpty as.size, rfl⟩ (by simp_arith)
|
||||
where
|
||||
go (i : Nat) (acc : { bs : Array β // bs.size = i }) (hle : i ≤ as.size) : m { bs : Array β // bs.size = as.size } := do
|
||||
if h : i = as.size then
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.List.TakeDrop
|
||||
|
||||
/-!
|
||||
## Bootstrapping theorems about arrays
|
||||
|
||||
This file contains some theorems about `Array` and `List` needed for `Init.Data.List.Impl`.
|
||||
-/
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem foldlM_eq_foldlM_toList.aux [Monad m]
|
||||
(f : β → α → m β) (arr : Array α) (i j) (H : arr.size ≤ i + j) (b) :
|
||||
foldlM.loop f arr arr.size (Nat.le_refl _) i j b = (arr.toList.drop j).foldlM f b := by
|
||||
unfold foldlM.loop
|
||||
split; split
|
||||
· cases Nat.not_le_of_gt ‹_› (Nat.zero_add _ ▸ H)
|
||||
· rename_i i; rw [Nat.succ_add] at H
|
||||
simp [foldlM_eq_foldlM_toList.aux f arr i (j+1) H]
|
||||
rw (config := {occs := .pos [2]}) [← List.get_drop_eq_drop _ _ ‹_›]
|
||||
rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
|
||||
theorem foldlM_eq_foldlM_toList [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
arr.foldlM f init = arr.toList.foldlM f init := by
|
||||
simp [foldlM, foldlM_eq_foldlM_toList.aux]
|
||||
|
||||
theorem foldl_eq_foldl_toList (f : β → α → β) (init : β) (arr : Array α) :
|
||||
arr.foldl f init = arr.toList.foldl f init :=
|
||||
List.foldl_eq_foldlM .. ▸ foldlM_eq_foldlM_toList ..
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_toList.aux [Monad m]
|
||||
(f : α → β → m β) (arr : Array α) (init : β) (i h) :
|
||||
(arr.toList.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f arr 0 i h init := by
|
||||
unfold foldrM.fold
|
||||
match i with
|
||||
| 0 => simp [List.foldlM, List.take]
|
||||
| i+1 => rw [← List.take_concat_get _ _ h]; simp [← (aux f arr · i)]; rfl
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_toList [Monad m] (f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.toList.reverse.foldlM (fun x y => f y x) init := by
|
||||
have : arr = #[] ∨ 0 < arr.size :=
|
||||
match arr with | ⟨[]⟩ => .inl rfl | ⟨a::l⟩ => .inr (Nat.zero_lt_succ _)
|
||||
match arr, this with | _, .inl rfl => rfl | arr, .inr h => ?_
|
||||
simp [foldrM, h, ← foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
|
||||
|
||||
theorem foldrM_eq_foldrM_toList [Monad m]
|
||||
(f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.toList.foldrM f init := by
|
||||
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
|
||||
|
||||
theorem foldr_eq_foldr_toList (f : α → β → β) (init : β) (arr : Array α) :
|
||||
arr.foldr f init = arr.toList.foldr f init :=
|
||||
List.foldr_eq_foldrM .. ▸ foldrM_eq_foldrM_toList ..
|
||||
|
||||
@[simp] theorem push_toList (arr : Array α) (a : α) : (arr.push a).toList = arr.toList ++ [a] := by
|
||||
simp [push, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toListAppend_eq (arr : Array α) (l) : arr.toListAppend l = arr.toList ++ l := by
|
||||
simp [toListAppend, foldr_eq_foldr_toList]
|
||||
|
||||
@[simp] theorem toListImpl_eq (arr : Array α) : arr.toListImpl = arr.toList := by
|
||||
simp [toListImpl, foldr_eq_foldr_toList]
|
||||
|
||||
@[simp] theorem pop_toList (arr : Array α) : arr.pop.toList = arr.toList.dropLast := rfl
|
||||
|
||||
@[simp] theorem append_eq_append (arr arr' : Array α) : arr.append arr' = arr ++ arr' := rfl
|
||||
|
||||
@[simp] theorem append_toList (arr arr' : Array α) :
|
||||
(arr ++ arr').toList = arr.toList ++ arr'.toList := by
|
||||
rw [← append_eq_append]; unfold Array.append
|
||||
rw [foldl_eq_foldl_toList]
|
||||
induction arr'.toList generalizing arr <;> simp [*]
|
||||
|
||||
@[simp] theorem appendList_eq_append
|
||||
(arr : Array α) (l : List α) : arr.appendList l = arr ++ l := rfl
|
||||
|
||||
@[simp] theorem appendList_toList (arr : Array α) (l : List α) :
|
||||
(arr ++ l).toList = arr.toList ++ l := by
|
||||
rw [← appendList_eq_append]; unfold Array.appendList
|
||||
induction l generalizing arr <;> simp [*]
|
||||
|
||||
@[deprecated foldlM_eq_foldlM_toList (since := "2024-09-09")]
|
||||
abbrev foldlM_eq_foldlM_data := @foldlM_eq_foldlM_toList
|
||||
|
||||
@[deprecated foldl_eq_foldl_toList (since := "2024-09-09")]
|
||||
abbrev foldl_eq_foldl_data := @foldl_eq_foldl_toList
|
||||
|
||||
@[deprecated foldrM_eq_reverse_foldlM_toList (since := "2024-09-09")]
|
||||
abbrev foldrM_eq_reverse_foldlM_data := @foldrM_eq_reverse_foldlM_toList
|
||||
|
||||
@[deprecated foldrM_eq_foldrM_toList (since := "2024-09-09")]
|
||||
abbrev foldrM_eq_foldrM_data := @foldrM_eq_foldrM_toList
|
||||
|
||||
@[deprecated foldr_eq_foldr_toList (since := "2024-09-09")]
|
||||
abbrev foldr_eq_foldr_data := @foldr_eq_foldr_toList
|
||||
|
||||
@[deprecated push_toList (since := "2024-09-09")]
|
||||
abbrev push_data := @push_toList
|
||||
|
||||
@[deprecated toListImpl_eq (since := "2024-09-09")]
|
||||
abbrev toList_eq := @toListImpl_eq
|
||||
|
||||
@[deprecated pop_toList (since := "2024-09-09")]
|
||||
abbrev pop_data := @pop_toList
|
||||
|
||||
@[deprecated append_toList (since := "2024-09-09")]
|
||||
abbrev append_data := @append_toList
|
||||
|
||||
@[deprecated appendList_toList (since := "2024-09-09")]
|
||||
abbrev appendList_data := @appendList_toList
|
||||
|
||||
end Array
|
||||
@@ -5,49 +5,43 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.BEq
|
||||
import Init.ByCases
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem rel_of_isEqvAux
|
||||
(r : α → α → Bool) (a b : Array α) (hsz : a.size = b.size) (i : Nat) (hi : i ≤ a.size)
|
||||
(heqv : Array.isEqvAux a b hsz r i hi)
|
||||
(j : Nat) (hj : j < i) : r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz ▸ hi))) := by
|
||||
induction i with
|
||||
| zero => contradiction
|
||||
| succ i ih =>
|
||||
simp only [Array.isEqvAux, Bool.and_eq_true, decide_eq_true_eq] at heqv
|
||||
by_cases hj' : j < i
|
||||
next =>
|
||||
exact ih _ heqv.right hj'
|
||||
next =>
|
||||
replace hj' : j = i := Nat.eq_of_le_of_lt_succ (Nat.not_lt.mp hj') hj
|
||||
subst hj'
|
||||
exact heqv.left
|
||||
theorem eq_of_isEqvAux [DecidableEq α] (a b : Array α) (hsz : a.size = b.size) (i : Nat) (hi : i ≤ a.size) (heqv : Array.isEqvAux a b hsz (fun x y => x = y) i) (j : Nat) (low : i ≤ j) (high : j < a.size) : a[j] = b[j]'(hsz ▸ high) := by
|
||||
by_cases h : i < a.size
|
||||
· unfold Array.isEqvAux at heqv
|
||||
simp [h] at heqv
|
||||
have hind := eq_of_isEqvAux a b hsz (i+1) (Nat.succ_le_of_lt h) heqv.2
|
||||
by_cases heq : i = j
|
||||
· subst heq; exact heqv.1
|
||||
· exact hind j (Nat.succ_le_of_lt (Nat.lt_of_le_of_ne low heq)) high
|
||||
· have heq : i = a.size := Nat.le_antisymm hi (Nat.ge_of_not_lt h)
|
||||
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 rel_of_isEqv (r : α → α → Bool) (a b : Array α) :
|
||||
Array.isEqv a b r → ∃ h : a.size = b.size, ∀ (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h ▸ h')) := by
|
||||
simp only [isEqv]
|
||||
split <;> rename_i h
|
||||
· exact fun h' => ⟨h, rel_of_isEqvAux r a b h a.size (Nat.le_refl ..) h'⟩
|
||||
· intro; contradiction
|
||||
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) (h : Array.isEqv a b (fun x y => x = y)) : a = b := by
|
||||
have ⟨h, h'⟩ := rel_of_isEqv (fun x y => x = y) a b h
|
||||
exact ext _ _ h (fun i lt _ => by simpa using h' i lt)
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) : Array.isEqv a b (fun x y => x = y) → a = b := by
|
||||
simp [Array.isEqv]
|
||||
split
|
||||
next hsz =>
|
||||
intro h
|
||||
have aux := eq_of_isEqvAux a b hsz 0 (Nat.zero_le ..) h
|
||||
exact ext a b hsz fun i h _ => aux i (Nat.zero_le ..) _
|
||||
next => intro; contradiction
|
||||
|
||||
theorem isEqvAux_self (r : α → α → Bool) (hr : ∀ a, r a a) (a : Array α) (i : Nat) (h : i ≤ a.size) :
|
||||
Array.isEqvAux a a rfl r i h = true := by
|
||||
induction i with
|
||||
| zero => simp [Array.isEqvAux]
|
||||
| succ i ih =>
|
||||
simp_all only [isEqvAux, Bool.and_self]
|
||||
theorem isEqvAux_self [DecidableEq α] (a : Array α) (i : Nat) : Array.isEqvAux a a rfl (fun x y => x = y) i = true := by
|
||||
unfold Array.isEqvAux
|
||||
split
|
||||
next h => simp [h, isEqvAux_self a (i+1)]
|
||||
next h => simp [h]
|
||||
termination_by a.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
theorem isEqv_self_beq [BEq α] [ReflBEq α] (a : Array α) : Array.isEqv a a (· == ·) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (· = ·) = true := by
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (fun x y => x = y) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
instance [DecidableEq α] : DecidableEq (Array α) :=
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### getLit -/
|
||||
|
||||
-- auxiliary declaration used in the equation compiler when pattern matching array literals.
|
||||
abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size = n) (h₂ : i < n) : α :=
|
||||
have := h₁.symm ▸ h₂
|
||||
a[i]
|
||||
|
||||
theorem extLit {n : Nat}
|
||||
(a b : Array α)
|
||||
(hsz₁ : a.size = n) (hsz₂ : b.size = n)
|
||||
(h : (i : Nat) → (hi : i < n) → a.getLit i hsz₁ hi = b.getLit i hsz₂ hi) : a = b :=
|
||||
Array.ext a b (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ ▸ hi₁)
|
||||
|
||||
def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : ∀ (i : Nat), i ≤ a.size → List α → List α
|
||||
| 0, _, acc => acc
|
||||
| (i+1), hi, acc => toListLitAux a n hsz i (Nat.le_of_succ_le hi) (a.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
|
||||
|
||||
def toArrayLit (a : Array α) (n : Nat) (hsz : a.size = n) : Array α :=
|
||||
List.toArray <| toListLitAux a n hsz n (hsz ▸ Nat.le_refl _) []
|
||||
|
||||
theorem toArrayLit_eq (as : Array α) (n : Nat) (hsz : as.size = n) : as = toArrayLit as n hsz := by
|
||||
apply ext'
|
||||
simp [toArrayLit, toList_toArray]
|
||||
have hle : n ≤ as.size := hsz ▸ Nat.le_refl _
|
||||
have hge : as.size ≤ n := hsz ▸ Nat.le_refl _
|
||||
have := go n hle
|
||||
rw [List.drop_eq_nil_of_le hge] at this
|
||||
rw [this]
|
||||
where
|
||||
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.toList i ((id (α := as.toList.length = n) h₁) ▸ h₂) :=
|
||||
rfl
|
||||
go (i : Nat) (hi : i ≤ as.size) : toListLitAux as n hsz i hi (as.toList.drop i) = as.toList := by
|
||||
induction i <;> simp [getLit_eq, List.get_drop_eq_drop, toListLitAux, List.drop, *]
|
||||
|
||||
end Array
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,11 @@ namespace Array
|
||||
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
|
||||
-- NB: This is defined as a structure rather than a plain def so that a lemma
|
||||
-- like `sizeOf_lt_of_mem` will not apply with no actual arrays around.
|
||||
structure Mem (as : Array α) (a : α) : Prop where
|
||||
val : a ∈ as.toList
|
||||
structure Mem (a : α) (as : Array α) : Prop where
|
||||
val : a ∈ as.data
|
||||
|
||||
instance : Membership α (Array α) where
|
||||
mem := Mem
|
||||
mem a as := Mem a as
|
||||
|
||||
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a < sizeOf as := by
|
||||
cases as with | _ as =>
|
||||
@@ -38,8 +38,8 @@ macro "array_get_dec" : tactic =>
|
||||
-- subsumed by simp
|
||||
-- | with_reducible apply sizeOf_get
|
||||
-- | with_reducible apply sizeOf_getElem
|
||||
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_get ..)); simp_arith
|
||||
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_getElem ..)); simp_arith
|
||||
| (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)
|
||||
@@ -52,7 +52,7 @@ macro "array_mem_dec" : tactic =>
|
||||
`(tactic| first
|
||||
| with_reducible apply Array.sizeOf_lt_of_mem; assumption; done
|
||||
| with_reducible
|
||||
apply Nat.lt_of_lt_of_le (Array.sizeOf_lt_of_mem ?h)
|
||||
apply Nat.lt_trans (Array.sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
simp_arith)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Ord
|
||||
|
||||
namespace Array
|
||||
-- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
|
||||
@@ -45,11 +44,4 @@ def qpartition (as : Array α) (lt : α → α → Bool) (lo hi : Nat) : Nat ×
|
||||
else as
|
||||
sort as low high
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
Sort an array using `compare` to compare elements.
|
||||
-/
|
||||
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
|
||||
xs.qsort fun x y => compare x y |>.isLT
|
||||
|
||||
end Array
|
||||
|
||||
@@ -59,22 +59,6 @@ def popFront (s : Subarray α) : Subarray α :=
|
||||
else
|
||||
s
|
||||
|
||||
/--
|
||||
The empty subarray.
|
||||
-/
|
||||
protected def empty : Subarray α where
|
||||
array := #[]
|
||||
start := 0
|
||||
stop := 0
|
||||
start_le_stop := Nat.le_refl 0
|
||||
stop_le_array_size := Nat.le_refl 0
|
||||
|
||||
instance : EmptyCollection (Subarray α) :=
|
||||
⟨Subarray.empty⟩
|
||||
|
||||
instance : Inhabited (Subarray α) :=
|
||||
⟨{}⟩
|
||||
|
||||
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
|
||||
let sz := USize.ofNat s.stop
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
|
||||
@@ -10,9 +10,8 @@ import Init.Data.List.Nat.TakeDrop
|
||||
namespace Array
|
||||
|
||||
theorem exists_of_uset (self : Array α) (i d h) :
|
||||
∃ l₁ l₂, self.toList = l₁ ++ self[i] :: l₂ ∧ List.length l₁ = i.toNat ∧
|
||||
(self.uset i d h).toList = l₁ ++ d :: l₂ := by
|
||||
simpa only [ugetElem_eq_getElem, getElem_eq_getElem_toList, uset, toList_set] using
|
||||
List.exists_of_set _
|
||||
∃ l₁ l₂, self.data = l₁ ++ self[i] :: l₂ ∧ List.length l₁ = i.toNat ∧
|
||||
(self.uset i d h).data = l₁ ++ d :: l₂ := by
|
||||
simpa [Array.getElem_eq_data_getElem] using List.exists_of_set _
|
||||
|
||||
end Array
|
||||
|
||||
@@ -56,5 +56,5 @@ theorem BEq.neq_of_beq_of_neq [BEq α] [PartialEquivBEq α] {a b c : α} :
|
||||
|
||||
instance (priority := low) [BEq α] [LawfulBEq α] : EquivBEq α where
|
||||
refl := LawfulBEq.rfl
|
||||
symm h := beq_iff_eq.2 <| Eq.symm <| beq_iff_eq.1 h
|
||||
trans hab hbc := beq_iff_eq.2 <| (beq_iff_eq.1 hab).trans <| beq_iff_eq.1 hbc
|
||||
symm h := (beq_iff_eq _ _).2 <| Eq.symm <| (beq_iff_eq _ _).1 h
|
||||
trans hab hbc := (beq_iff_eq _ _).2 <| ((beq_iff_eq _ _).1 hab).trans <| (beq_iff_eq _ _).1 hbc
|
||||
|
||||
@@ -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: Kim Morrison
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.BitVec.Basic
|
||||
|
||||
@@ -20,8 +20,6 @@ We define many of the bitvector operations from the
|
||||
of SMT-LIBv2.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/--
|
||||
A bitvector of the specified width.
|
||||
|
||||
@@ -36,14 +34,14 @@ structure BitVec (w : Nat) where
|
||||
O(1), because we use `Fin` as the internal representation of a bitvector. -/
|
||||
toFin : Fin (2^w)
|
||||
|
||||
/--
|
||||
Bitvectors have decidable equality. This should be used via the instance `DecidableEq (BitVec n)`.
|
||||
-/
|
||||
@[deprecated (since := "2024-04-12")]
|
||||
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
|
||||
-- need a name for this function to implement `canUnfoldAtMatcher` at `WHNF.lean`.
|
||||
def BitVec.decEq (x y : BitVec n) : Decidable (x = y) :=
|
||||
match x, y with
|
||||
def BitVec.decEq (a b : BitVec n) : Decidable (a = b) :=
|
||||
match a, b with
|
||||
| ⟨n⟩, ⟨m⟩ =>
|
||||
if h : n = m then
|
||||
isTrue (h ▸ rfl)
|
||||
@@ -64,14 +62,14 @@ protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2^n) : BitVec n where
|
||||
/-- The `BitVec` with value `i mod 2^n`. -/
|
||||
@[match_pattern]
|
||||
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
|
||||
toFin := Fin.ofNat' (2^n) i
|
||||
toFin := Fin.ofNat' i (Nat.two_pow_pos n)
|
||||
|
||||
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
|
||||
instance natCastInst : NatCast (BitVec w) := ⟨BitVec.ofNat w⟩
|
||||
|
||||
/-- Given a bitvector `x`, return the underlying `Nat`. This is O(1) because `BitVec` is a
|
||||
/-- Given a bitvector `a`, return the underlying `Nat`. This is O(1) because `BitVec` is a
|
||||
(zero-cost) wrapper around a `Nat`. -/
|
||||
protected def toNat (x : BitVec n) : Nat := x.toFin.val
|
||||
protected def toNat (a : BitVec n) : Nat := a.toFin.val
|
||||
|
||||
/-- Return the bound in terms of toNat. -/
|
||||
theorem isLt (x : BitVec w) : x.toNat < 2^w := x.toFin.isLt
|
||||
@@ -116,76 +114,25 @@ end zero_allOnes
|
||||
|
||||
section getXsb
|
||||
|
||||
/--
|
||||
Return the `i`-th least significant bit.
|
||||
|
||||
This will be renamed `getLsb` after the existing deprecated alias is removed.
|
||||
-/
|
||||
@[inline] def getLsb' (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
|
||||
|
||||
/-- Return the `i`-th least significant bit or `none` if `i ≥ w`. -/
|
||||
@[inline] def getLsb? (x : BitVec w) (i : Nat) : Option Bool :=
|
||||
if h : i < w then some (getLsb' x ⟨i, h⟩) else none
|
||||
|
||||
/--
|
||||
Return the `i`-th most significant bit.
|
||||
|
||||
This will be renamed `getMsb` after the existing deprecated alias is removed.
|
||||
-/
|
||||
@[inline] def getMsb' (x : BitVec w) (i : Fin w) : Bool := x.getLsb' ⟨w-1-i, by omega⟩
|
||||
|
||||
/-- Return the `i`-th most significant bit or `none` if `i ≥ w`. -/
|
||||
@[inline] def getMsb? (x : BitVec w) (i : Nat) : Option Bool :=
|
||||
if h : i < w then some (getMsb' x ⟨i, h⟩) else none
|
||||
|
||||
/-- Return the `i`-th least significant bit or `false` if `i ≥ w`. -/
|
||||
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
|
||||
x.toNat.testBit i
|
||||
|
||||
@[deprecated getLsbD (since := "2024-08-29"), inherit_doc getLsbD]
|
||||
def getLsb (x : BitVec w) (i : Nat) : Bool := x.getLsbD i
|
||||
@[inline] def getLsb (x : BitVec w) (i : Nat) : Bool := x.toNat.testBit i
|
||||
|
||||
/-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/
|
||||
@[inline] def getMsbD (x : BitVec w) (i : Nat) : Bool :=
|
||||
i < w && x.getLsbD (w-1-i)
|
||||
|
||||
@[deprecated getMsbD (since := "2024-08-29"), inherit_doc getMsbD]
|
||||
def getMsb (x : BitVec w) (i : Nat) : Bool := x.getMsbD i
|
||||
@[inline] def getMsb (x : BitVec w) (i : Nat) : Bool := i < w && getLsb x (w-1-i)
|
||||
|
||||
/-- Return most-significant bit in bitvector. -/
|
||||
@[inline] protected def msb (x : BitVec n) : Bool := getMsbD x 0
|
||||
@[inline] protected def msb (a : BitVec n) : Bool := getMsb a 0
|
||||
|
||||
end getXsb
|
||||
|
||||
section getElem
|
||||
|
||||
instance : GetElem (BitVec w) Nat Bool fun _ i => i < w where
|
||||
getElem xs i h := xs.getLsb' ⟨i, h⟩
|
||||
|
||||
/-- We prefer `x[i]` as the simp normal form for `getLsb'` -/
|
||||
@[simp] theorem getLsb'_eq_getElem (x : BitVec w) (i : Fin w) :
|
||||
x.getLsb' i = x[i] := rfl
|
||||
|
||||
/-- We prefer `x[i]?` as the simp normal form for `getLsb?` -/
|
||||
@[simp] theorem getLsb?_eq_getElem? (x : BitVec w) (i : Nat) :
|
||||
x.getLsb? i = x[i]? := rfl
|
||||
|
||||
theorem getElem_eq_testBit_toNat (x : BitVec w) (i : Nat) (h : i < w) :
|
||||
x[i] = x.toNat.testBit i := rfl
|
||||
|
||||
theorem getLsbD_eq_getElem {x : BitVec w} {i : Nat} (h : i < w) :
|
||||
x.getLsbD i = x[i] := rfl
|
||||
|
||||
end getElem
|
||||
|
||||
section Int
|
||||
|
||||
/-- Interpret the bitvector as an integer stored in two's complement form. -/
|
||||
protected def toInt (x : BitVec n) : Int :=
|
||||
if 2 * x.toNat < 2^n then
|
||||
x.toNat
|
||||
protected def toInt (a : BitVec n) : Int :=
|
||||
if 2 * a.toNat < 2^n then
|
||||
a.toNat
|
||||
else
|
||||
(x.toNat : Int) - (2^n : Nat)
|
||||
(a.toNat : Int) - (2^n : Nat)
|
||||
|
||||
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
|
||||
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
|
||||
@@ -266,11 +213,11 @@ instance : Neg (BitVec n) := ⟨.neg⟩
|
||||
/--
|
||||
Return the absolute value of a signed bitvector.
|
||||
-/
|
||||
protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
|
||||
protected def abs (s : BitVec n) : BitVec n := if s.msb then .neg s else s
|
||||
|
||||
/--
|
||||
Multiplication for bit vectors. This can be interpreted as either signed or unsigned
|
||||
multiplication modulo `2^n`.
|
||||
Multiplication for bit vectors. This can be interpreted as either signed or unsigned negation
|
||||
modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvmul`.
|
||||
-/
|
||||
@@ -313,12 +260,12 @@ sdiv 5#4 -2 = -2#4
|
||||
sdiv (-7#4) (-2) = 3#4
|
||||
```
|
||||
-/
|
||||
def sdiv (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
| false, false => udiv x y
|
||||
| false, true => .neg (udiv x (.neg y))
|
||||
| true, false => .neg (udiv (.neg x) y)
|
||||
| true, true => udiv (.neg x) (.neg y)
|
||||
def sdiv (s t : BitVec n) : BitVec n :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => udiv s t
|
||||
| false, true => .neg (udiv s (.neg t))
|
||||
| true, false => .neg (udiv (.neg s) t)
|
||||
| true, true => udiv (.neg s) (.neg t)
|
||||
|
||||
/--
|
||||
Signed division for bit vectors using SMTLIB rules for division by zero.
|
||||
@@ -327,40 +274,40 @@ Specifically, `smtSDiv x 0 = if x >= 0 then -1 else 1`
|
||||
|
||||
SMT-Lib name: `bvsdiv`.
|
||||
-/
|
||||
def smtSDiv (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
| false, false => smtUDiv x y
|
||||
| false, true => .neg (smtUDiv x (.neg y))
|
||||
| true, false => .neg (smtUDiv (.neg x) y)
|
||||
| true, true => smtUDiv (.neg x) (.neg y)
|
||||
def smtSDiv (s t : BitVec n) : BitVec n :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => smtUDiv s t
|
||||
| false, true => .neg (smtUDiv s (.neg t))
|
||||
| true, false => .neg (smtUDiv (.neg s) t)
|
||||
| true, true => smtUDiv (.neg s) (.neg t)
|
||||
|
||||
/--
|
||||
Remainder for signed division rounding to zero.
|
||||
|
||||
SMT_Lib name: `bvsrem`.
|
||||
-/
|
||||
def srem (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
| false, false => umod x y
|
||||
| false, true => umod x (.neg y)
|
||||
| true, false => .neg (umod (.neg x) y)
|
||||
| true, true => .neg (umod (.neg x) (.neg y))
|
||||
def srem (s t : BitVec n) : BitVec n :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => umod s t
|
||||
| false, true => umod s (.neg t)
|
||||
| true, false => .neg (umod (.neg s) t)
|
||||
| true, true => .neg (umod (.neg s) (.neg t))
|
||||
|
||||
/--
|
||||
Remainder for signed division rounded to negative infinity.
|
||||
|
||||
SMT_Lib name: `bvsmod`.
|
||||
-/
|
||||
def smod (x y : BitVec m) : BitVec m :=
|
||||
match x.msb, y.msb with
|
||||
| false, false => umod x y
|
||||
def smod (s t : BitVec m) : BitVec m :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => umod s t
|
||||
| false, true =>
|
||||
let u := umod x (.neg y)
|
||||
(if u = .zero m then u else .add u y)
|
||||
let u := umod s (.neg t)
|
||||
(if u = .zero m then u else .add u t)
|
||||
| true, false =>
|
||||
let u := umod (.neg x) y
|
||||
(if u = .zero m then u else .sub y u)
|
||||
| true, true => .neg (umod (.neg x) (.neg y))
|
||||
let u := umod (.neg s) t
|
||||
(if u = .zero m then u else .sub t u)
|
||||
| true, true => .neg (umod (.neg s) (.neg t))
|
||||
|
||||
end arithmetic
|
||||
|
||||
@@ -424,8 +371,8 @@ end relations
|
||||
|
||||
section cast
|
||||
|
||||
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
|
||||
@[inline] def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq ▸ x.isLt)
|
||||
/-- `cast eq i` embeds `i` into an equal `BitVec` type. -/
|
||||
@[inline] def cast (eq : n = m) (i : BitVec n) : BitVec m := .ofNatLt i.toNat (eq ▸ i.isLt)
|
||||
|
||||
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
|
||||
cast h (BitVec.ofNat n x) = BitVec.ofNat m x := by
|
||||
@@ -442,7 +389,7 @@ Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to
|
||||
new bitvector of size `len`. If `start + len > n`, then the vector will be zero-padded in the
|
||||
high bits.
|
||||
-/
|
||||
def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toNat >>> start)
|
||||
def extractLsb' (start len : Nat) (a : BitVec n) : BitVec len := .ofNat _ (a.toNat >>> start)
|
||||
|
||||
/--
|
||||
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
|
||||
@@ -450,59 +397,44 @@ yield a new bitvector of size `hi - lo + 1`.
|
||||
|
||||
SMT-Lib name: `extract`.
|
||||
-/
|
||||
def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x
|
||||
def extractLsb (hi lo : Nat) (a : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ a
|
||||
|
||||
/--
|
||||
A version of `setWidth` that requires a proof, but is a noop.
|
||||
A version of `zeroExtend` that requires a proof, but is a noop.
|
||||
-/
|
||||
def setWidth' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
|
||||
def zeroExtend' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
|
||||
x.toNat#'(by
|
||||
apply Nat.lt_of_lt_of_le x.isLt
|
||||
exact Nat.pow_le_pow_of_le_right (by trivial) le)
|
||||
|
||||
@[deprecated setWidth' (since := "2024-09-18"), inherit_doc setWidth'] abbrev zeroExtend' := @setWidth'
|
||||
|
||||
/--
|
||||
`shiftLeftZeroExtend x n` returns `zeroExtend (w+n) x <<< n` without
|
||||
needing to compute `x % 2^(2+n)`.
|
||||
-/
|
||||
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w + m) :=
|
||||
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w + m) := by
|
||||
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w+m) :=
|
||||
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w+m) := by
|
||||
simp [Nat.shiftLeft_eq, Nat.pow_add]
|
||||
apply Nat.mul_lt_mul_of_pos_right p
|
||||
exact (Nat.two_pow_pos m)
|
||||
(msbs.toNat <<< m)#'(shiftLeftLt msbs.isLt m)
|
||||
|
||||
/--
|
||||
Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
Zero extend vector `x` of length `w` by adding zeros in the high bits until it has length `v`.
|
||||
If `v < w` then it truncates the high bits instead.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
-/
|
||||
def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
|
||||
def zeroExtend (v : Nat) (x : BitVec w) : BitVec v :=
|
||||
if h : w ≤ v then
|
||||
setWidth' h x
|
||||
zeroExtend' h x
|
||||
else
|
||||
.ofNat v x.toNat
|
||||
|
||||
/--
|
||||
Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
Truncate the high bits of bitvector `x` of length `w`, resulting in a vector of length `v`.
|
||||
If `v > w` then it zero-extends the vector instead.
|
||||
-/
|
||||
abbrev zeroExtend := @setWidth
|
||||
|
||||
/--
|
||||
Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
-/
|
||||
abbrev truncate := @setWidth
|
||||
abbrev truncate := @zeroExtend
|
||||
|
||||
/--
|
||||
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant
|
||||
@@ -568,24 +500,24 @@ instance : Complement (BitVec w) := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Left shift for bit vectors. The low bits are filled with zeros. As a numeric operation, this is
|
||||
equivalent to `x * 2^s`, modulo `2^n`.
|
||||
equivalent to `a * 2^s`, modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvshl` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
protected def shiftLeft (x : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (x.toNat <<< s)
|
||||
protected def shiftLeft (a : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (a.toNat <<< s)
|
||||
instance : HShiftLeft (BitVec w) Nat (BitVec w) := ⟨.shiftLeft⟩
|
||||
|
||||
/--
|
||||
(Logical) right shift for bit vectors. The high bits are filled with zeros.
|
||||
As a numeric operation, this is equivalent to `x / 2^s`, rounding down.
|
||||
As a numeric operation, this is equivalent to `a / 2^s`, rounding down.
|
||||
|
||||
SMT-Lib name: `bvlshr` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
|
||||
(x.toNat >>> s)#'(by
|
||||
let ⟨x, lt⟩ := x
|
||||
def ushiftRight (a : BitVec n) (s : Nat) : BitVec n :=
|
||||
(a.toNat >>> s)#'(by
|
||||
let ⟨a, lt⟩ := a
|
||||
simp only [BitVec.toNat, Nat.shiftRight_eq_div_pow, Nat.div_lt_iff_lt_mul (Nat.two_pow_pos s)]
|
||||
rw [←Nat.mul_one x]
|
||||
rw [←Nat.mul_one a]
|
||||
exact Nat.mul_lt_mul_of_lt_of_le' lt (Nat.two_pow_pos s) (Nat.le_refl 1))
|
||||
|
||||
instance : HShiftRight (BitVec w) Nat (BitVec w) := ⟨.ushiftRight⟩
|
||||
@@ -593,24 +525,15 @@ instance : HShiftRight (BitVec w) Nat (BitVec w) := ⟨.ushiftRight⟩
|
||||
/--
|
||||
Arithmetic right shift for bit vectors. The high bits are filled with the
|
||||
most-significant bit.
|
||||
As a numeric operation, this is equivalent to `x.toInt >>> s`.
|
||||
As a numeric operation, this is equivalent to `a.toInt >>> s`.
|
||||
|
||||
SMT-Lib name: `bvashr` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
def sshiftRight (x : BitVec n) (s : Nat) : BitVec n := .ofInt n (x.toInt >>> s)
|
||||
def sshiftRight (a : BitVec n) (s : Nat) : BitVec n := .ofInt n (a.toInt >>> s)
|
||||
|
||||
instance {n} : HShiftLeft (BitVec m) (BitVec n) (BitVec m) := ⟨fun x y => x <<< y.toNat⟩
|
||||
instance {n} : HShiftRight (BitVec m) (BitVec n) (BitVec m) := ⟨fun x y => x >>> y.toNat⟩
|
||||
|
||||
/--
|
||||
Arithmetic right shift for bit vectors. The high bits are filled with the
|
||||
most-significant bit.
|
||||
As a numeric operation, this is equivalent to `a.toInt >>> s.toNat`.
|
||||
|
||||
SMT-Lib name: `bvashr`.
|
||||
-/
|
||||
def sshiftRight' (a : BitVec n) (s : BitVec m) : BitVec n := a.sshiftRight s.toNat
|
||||
|
||||
/-- Auxiliary function for `rotateLeft`, which does not take into account the case where
|
||||
the rotation amount is greater than the bitvector width. -/
|
||||
def rotateLeftAux (x : BitVec w) (n : Nat) : BitVec w :=
|
||||
@@ -653,16 +576,18 @@ input is on the left, so `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
|
||||
SMT-Lib name: `concat`.
|
||||
-/
|
||||
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
|
||||
shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs
|
||||
shiftLeftZeroExtend msbs m ||| zeroExtend' (Nat.le_add_left m n) lsbs
|
||||
|
||||
instance : HAppend (BitVec w) (BitVec v) (BitVec (w + v)) := ⟨.append⟩
|
||||
|
||||
-- TODO: write this using multiplication
|
||||
/-- `replicate i x` concatenates `i` copies of `x` into a new vector of length `w*i`. -/
|
||||
def replicate : (i : Nat) → BitVec w → BitVec (w*i)
|
||||
| 0, _ => 0#0
|
||||
| 0, _ => 0
|
||||
| n+1, x =>
|
||||
(x ++ replicate n x).cast (by rw [Nat.mul_succ]; omega)
|
||||
have hEq : w + w*n = w*(n + 1) := by
|
||||
rw [Nat.mul_add, Nat.add_comm, Nat.mul_one]
|
||||
hEq ▸ (x ++ replicate n x)
|
||||
|
||||
/-!
|
||||
### Cons and Concat
|
||||
@@ -676,13 +601,6 @@ result of appending a single bit to the front in the naive implementation).
|
||||
That is, the new bit is the least significant bit. -/
|
||||
def concat {n} (msbs : BitVec n) (lsb : Bool) : BitVec (n+1) := msbs ++ (ofBool lsb)
|
||||
|
||||
/--
|
||||
`x.shiftConcat b` shifts all bits of `x` to the left by `1` and sets the least significant bit to `b`.
|
||||
It is a non-dependent version of `concat` that does not change the total bitwidth.
|
||||
-/
|
||||
def shiftConcat (x : BitVec n) (b : Bool) : BitVec n :=
|
||||
(x.concat b).truncate n
|
||||
|
||||
/-- Prepend a single bit to the front of a bitvector, using big endian order (see `append`).
|
||||
That is, the new bit is the most significant bit. -/
|
||||
def cons {n} (msb : Bool) (lsbs : BitVec n) : BitVec (n+1) :=
|
||||
|
||||
@@ -28,8 +28,6 @@ https://github.com/mhk119/lean-smt/blob/bitvec/Smt/Data/Bitwise.lean.
|
||||
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
open Nat Bool
|
||||
|
||||
namespace Bool
|
||||
@@ -92,8 +90,8 @@ def carry (i : Nat) (x y : BitVec w) (c : Bool) : Bool :=
|
||||
cases c <;> simp [carry, mod_one]
|
||||
|
||||
theorem carry_succ (i : Nat) (x y : BitVec w) (c : Bool) :
|
||||
carry (i+1) x y c = atLeastTwo (x.getLsbD i) (y.getLsbD i) (carry i x y c) := by
|
||||
simp only [carry, mod_two_pow_succ, atLeastTwo, getLsbD]
|
||||
carry (i+1) x y c = atLeastTwo (x.getLsb i) (y.getLsb i) (carry i x y c) := by
|
||||
simp only [carry, mod_two_pow_succ, atLeastTwo, getLsb]
|
||||
simp only [Nat.pow_succ']
|
||||
have sum_bnd : x.toNat%2^i + (y.toNat%2^i + c.toNat) < 2*2^i := by
|
||||
simp only [← Nat.pow_succ']
|
||||
@@ -110,7 +108,7 @@ theorem carry_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) : carry i x y
|
||||
induction i with
|
||||
| zero => simp
|
||||
| succ i ih =>
|
||||
replace h := congrArg (·.getLsbD i) h
|
||||
replace h := congrArg (·.getLsb i) h
|
||||
simp_all [carry_succ]
|
||||
|
||||
/-- The final carry bit when computing `x + y + c` is `true` iff `x.toNat + y.toNat + c.toNat ≥ 2^w`. -/
|
||||
@@ -132,18 +130,18 @@ theorem toNat_add_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) :
|
||||
simp [not_eq_true, carry_of_and_eq_zero h]
|
||||
|
||||
/-- Carry function for bitwise addition. -/
|
||||
def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, x ^^ (y ^^ c))
|
||||
def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, Bool.xor x (Bool.xor y c))
|
||||
|
||||
/-- Bitwise addition implemented via a ripple carry adder. -/
|
||||
def adc (x y : BitVec w) : Bool → Bool × BitVec w :=
|
||||
iunfoldr fun (i : Fin w) c => adcb (x.getLsbD i) (y.getLsbD i) c
|
||||
iunfoldr fun (i : Fin w) c => adcb (x.getLsb i) (y.getLsb i) c
|
||||
|
||||
theorem getLsbD_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool) :
|
||||
getLsbD (x + y + setWidth w (ofBool c)) i =
|
||||
(getLsbD x i ^^ (getLsbD y i ^^ carry i x y c)) := by
|
||||
theorem getLsb_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool) :
|
||||
getLsb (x + y + zeroExtend w (ofBool c)) i =
|
||||
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y c)) := by
|
||||
let ⟨x, x_lt⟩ := x
|
||||
let ⟨y, y_lt⟩ := y
|
||||
simp only [getLsbD, toNat_add, toNat_setWidth, i_lt, toNat_ofFin, toNat_ofBool,
|
||||
simp only [getLsb, toNat_add, toNat_zeroExtend, i_lt, toNat_ofFin, toNat_ofBool,
|
||||
Nat.mod_add_mod, Nat.add_mod_mod]
|
||||
apply Eq.trans
|
||||
rw [← Nat.div_add_mod x (2^i), ← Nat.div_add_mod y (2^i)]
|
||||
@@ -159,23 +157,23 @@ theorem getLsbD_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool
|
||||
]
|
||||
simp [testBit_to_div_mod, carry, Nat.add_assoc]
|
||||
|
||||
theorem getLsbD_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
|
||||
getLsbD (x + y) i =
|
||||
(getLsbD x i ^^ (getLsbD y i ^^ carry i x y false)) := by
|
||||
simpa using getLsbD_add_add_bool i_lt x y false
|
||||
theorem getLsb_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
|
||||
getLsb (x + y) i =
|
||||
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y false)) := by
|
||||
simpa using getLsb_add_add_bool i_lt x y false
|
||||
|
||||
theorem adc_spec (x y : BitVec w) (c : Bool) :
|
||||
adc x y c = (carry w x y c, x + y + setWidth w (ofBool c)) := by
|
||||
adc x y c = (carry w x y c, x + y + zeroExtend w (ofBool c)) := by
|
||||
simp only [adc]
|
||||
apply iunfoldr_replace
|
||||
(fun i => carry i x y c)
|
||||
(x + y + setWidth w (ofBool c))
|
||||
(x + y + zeroExtend w (ofBool c))
|
||||
c
|
||||
case init =>
|
||||
simp [carry, Nat.mod_one]
|
||||
cases c <;> rfl
|
||||
case step =>
|
||||
simp [adcb, Prod.mk.injEq, carry_succ, getLsbD_add_add_bool]
|
||||
simp [adcb, Prod.mk.injEq, carry_succ, getLsb_add_add_bool]
|
||||
|
||||
theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := by
|
||||
simp [adc_spec]
|
||||
@@ -197,37 +195,37 @@ theorem add_eq_or_of_and_eq_zero {w : Nat} (x y : BitVec w)
|
||||
(h : x &&& y = 0#w) : x + y = x ||| y := by
|
||||
rw [add_eq_adc, adc, iunfoldr_replace (fun _ => false) (x ||| y)]
|
||||
· rfl
|
||||
· simp only [adcb, atLeastTwo, Bool.and_false, Bool.or_false, bne_false, getLsbD_or,
|
||||
· simp only [adcb, atLeastTwo, Bool.and_false, Bool.or_false, bne_false, getLsb_or,
|
||||
Prod.mk.injEq, and_eq_false_imp]
|
||||
intros i
|
||||
replace h : (x &&& y).getLsbD i = (0#w).getLsbD i := by rw [h]
|
||||
simp only [getLsbD_and, getLsbD_zero, and_eq_false_imp] at h
|
||||
replace h : (x &&& y).getLsb i = (0#w).getLsb i := by rw [h]
|
||||
simp only [getLsb_and, getLsb_zero, and_eq_false_imp] at h
|
||||
constructor
|
||||
· intros hx
|
||||
simp_all [hx]
|
||||
· by_cases hx : x.getLsbD i <;> simp_all [hx]
|
||||
· by_cases hx : x.getLsb i <;> simp_all [hx]
|
||||
|
||||
/-! ### Negation -/
|
||||
|
||||
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
|
||||
getLsbD (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) i.val = !(getLsbD x i.val) := by
|
||||
apply iunfoldr_getLsbD (fun _ => ()) i (by simp)
|
||||
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.getLsbD i)))) ()).snd + x = -1 := by
|
||||
((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.getLsbD i)))) ()).snd)]
|
||||
<;> simp [bit_not_testBit, negOne_eq_allOnes, getLsbD_allOnes]
|
||||
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.getLsbD i)))) ()).snd = ~~~ x := by
|
||||
((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.getLsbD i)))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
|
||||
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.getLsbD i)))) ()).snd) _ rfl]
|
||||
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 _]
|
||||
@@ -289,82 +287,73 @@ theorem sle_eq_carry (x y : BitVec w) :
|
||||
A recurrence that describes multiplication as repeated addition.
|
||||
Is useful for bitblasting multiplication.
|
||||
-/
|
||||
def mulRec (x y : BitVec w) (s : Nat) : BitVec w :=
|
||||
let cur := if y.getLsbD s then (x <<< s) else 0
|
||||
def mulRec (l r : BitVec w) (s : Nat) : BitVec w :=
|
||||
let cur := if r.getLsb s then (l <<< s) else 0
|
||||
match s with
|
||||
| 0 => cur
|
||||
| s + 1 => mulRec x y s + cur
|
||||
| s + 1 => mulRec l r s + cur
|
||||
|
||||
theorem mulRec_zero_eq (x y : BitVec w) :
|
||||
mulRec x y 0 = if y.getLsbD 0 then x else 0 := by
|
||||
theorem mulRec_zero_eq (l r : BitVec w) :
|
||||
mulRec l r 0 = if r.getLsb 0 then l else 0 := by
|
||||
simp [mulRec]
|
||||
|
||||
theorem mulRec_succ_eq (x y : BitVec w) (s : Nat) :
|
||||
mulRec x y (s + 1) = mulRec x y s + if y.getLsbD (s + 1) then (x <<< (s + 1)) else 0 := rfl
|
||||
theorem mulRec_succ_eq (l r : BitVec w) (s : Nat) :
|
||||
mulRec l r (s + 1) = mulRec l r s + if r.getLsb (s + 1) then (l <<< (s + 1)) else 0 := rfl
|
||||
|
||||
/--
|
||||
Recurrence lemma: truncating to `i+1` bits and then zero extending to `w`
|
||||
equals truncating upto `i` bits `[0..i-1]`, and then adding the `i`th bit of `x`.
|
||||
-/
|
||||
theorem setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow (x : BitVec w) (i : Nat) :
|
||||
setWidth w (x.setWidth (i + 1)) =
|
||||
setWidth w (x.setWidth i) + (x &&& twoPow w i) := by
|
||||
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow (x : BitVec w) (i : Nat) :
|
||||
zeroExtend w (x.truncate (i + 1)) =
|
||||
zeroExtend w (x.truncate i) + (x &&& twoPow w i) := by
|
||||
rw [add_eq_or_of_and_eq_zero]
|
||||
· ext k
|
||||
simp only [getLsbD_setWidth, Fin.is_lt, decide_True, Bool.true_and, getLsbD_or, getLsbD_and]
|
||||
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
|
||||
by_cases hik : i = k
|
||||
· subst hik
|
||||
simp
|
||||
· simp only [getLsbD_twoPow, hik, decide_False, Bool.and_false, Bool.or_false]
|
||||
· simp only [getLsb_twoPow, hik, decide_False, Bool.and_false, Bool.or_false]
|
||||
by_cases hik' : k < (i + 1)
|
||||
· have hik'' : k < i := by omega
|
||||
simp [hik', hik'']
|
||||
· have hik'' : ¬ (k < i) := by omega
|
||||
simp [hik', hik'']
|
||||
· ext k
|
||||
simp only [and_twoPow, getLsbD_and, getLsbD_setWidth, Fin.is_lt, decide_True, Bool.true_and,
|
||||
getLsbD_zero, and_eq_false_imp, and_eq_true, decide_eq_true_eq, and_imp]
|
||||
by_cases hi : x.getLsbD i <;> simp [hi] <;> omega
|
||||
|
||||
@[deprecated setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow (since := "2024-09-18"),
|
||||
inherit_doc setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow]
|
||||
abbrev zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow :=
|
||||
@setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow
|
||||
simp
|
||||
by_cases hi : x.getLsb i <;> simp [hi] <;> omega
|
||||
|
||||
/--
|
||||
Recurrence lemma: multiplying `x` with the first `s` bits of `y` is the
|
||||
same as truncating `y` to `s` bits, then zero extending to the original length,
|
||||
Recurrence lemma: multiplying `l` with the first `s` bits of `r` is the
|
||||
same as truncating `r` to `s` bits, then zero extending to the original length,
|
||||
and performing the multplication. -/
|
||||
theorem mulRec_eq_mul_signExtend_setWidth (x y : BitVec w) (s : Nat) :
|
||||
mulRec x y s = x * ((y.setWidth (s + 1)).setWidth w) := by
|
||||
theorem mulRec_eq_mul_signExtend_truncate (l r : BitVec w) (s : Nat) :
|
||||
mulRec l r s = l * ((r.truncate (s + 1)).zeroExtend w) := by
|
||||
induction s
|
||||
case zero =>
|
||||
simp only [mulRec_zero_eq, ofNat_eq_ofNat, Nat.reduceAdd]
|
||||
by_cases y.getLsbD 0
|
||||
case pos hy =>
|
||||
simp only [hy, ↓reduceIte, setWidth_one_eq_ofBool_getLsb_zero,
|
||||
ofBool_true, ofNat_eq_ofNat]
|
||||
rw [setWidth_ofNat_one_eq_ofNat_one_of_lt (by omega)]
|
||||
by_cases r.getLsb 0
|
||||
case pos hr =>
|
||||
simp only [hr, ↓reduceIte, truncate, zeroExtend_one_eq_ofBool_getLsb_zero,
|
||||
hr, ofBool_true, ofNat_eq_ofNat]
|
||||
rw [zeroExtend_ofNat_one_eq_ofNat_one_of_lt (by omega)]
|
||||
simp
|
||||
case neg hy =>
|
||||
simp [hy, setWidth_one_eq_ofBool_getLsb_zero]
|
||||
case neg hr =>
|
||||
simp [hr, zeroExtend_one_eq_ofBool_getLsb_zero]
|
||||
case succ s' hs =>
|
||||
rw [mulRec_succ_eq, hs]
|
||||
have heq :
|
||||
(if y.getLsbD (s' + 1) = true then x <<< (s' + 1) else 0) =
|
||||
(x * (y &&& (BitVec.twoPow w (s' + 1)))) := by
|
||||
(if r.getLsb (s' + 1) = true then l <<< (s' + 1) else 0) =
|
||||
(l * (r &&& (BitVec.twoPow w (s' + 1)))) := by
|
||||
simp only [ofNat_eq_ofNat, and_twoPow]
|
||||
by_cases hy : y.getLsbD (s' + 1) <;> simp [hy]
|
||||
rw [heq, ← BitVec.mul_add, ← setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow]
|
||||
by_cases hr : r.getLsb (s' + 1) <;> simp [hr]
|
||||
rw [heq, ← BitVec.mul_add, ← zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow]
|
||||
|
||||
@[deprecated mulRec_eq_mul_signExtend_setWidth (since := "2024-09-18"),
|
||||
inherit_doc mulRec_eq_mul_signExtend_setWidth]
|
||||
abbrev mulRec_eq_mul_signExtend_truncate := @mulRec_eq_mul_signExtend_setWidth
|
||||
|
||||
theorem getLsbD_mul (x y : BitVec w) (i : Nat) :
|
||||
(x * y).getLsbD i = (mulRec x y w).getLsbD i := by
|
||||
simp only [mulRec_eq_mul_signExtend_setWidth]
|
||||
rw [setWidth_setWidth_of_le]
|
||||
theorem getLsb_mul (x y : BitVec w) (i : Nat) :
|
||||
(x * y).getLsb i = (mulRec x y w).getLsb i := by
|
||||
simp only [mulRec_eq_mul_signExtend_truncate]
|
||||
rw [truncate, ← truncate_eq_zeroExtend, ← truncate_eq_zeroExtend,
|
||||
truncate_truncate_of_le]
|
||||
· simp
|
||||
· omega
|
||||
|
||||
@@ -410,22 +399,22 @@ theorem shiftLeft_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
|
||||
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
|
||||
-/
|
||||
theorem shiftLeftRec_eq {x : BitVec w₁} {y : BitVec w₂} {n : Nat} :
|
||||
shiftLeftRec x y n = x <<< (y.setWidth (n + 1)).setWidth w₂ := by
|
||||
shiftLeftRec x y n = x <<< (y.truncate (n + 1)).zeroExtend w₂ := by
|
||||
induction n generalizing x y
|
||||
case zero =>
|
||||
ext i
|
||||
simp only [shiftLeftRec_zero, twoPow_zero, Nat.reduceAdd, setWidth_one,
|
||||
and_one_eq_setWidth_ofBool_getLsbD]
|
||||
simp only [shiftLeftRec_zero, twoPow_zero, Nat.reduceAdd, truncate_one,
|
||||
and_one_eq_zeroExtend_ofBool_getLsb]
|
||||
case succ n ih =>
|
||||
simp only [shiftLeftRec_succ, and_twoPow]
|
||||
rw [ih]
|
||||
by_cases h : y.getLsbD (n + 1)
|
||||
by_cases h : y.getLsb (n + 1)
|
||||
· simp only [h, ↓reduceIte]
|
||||
rw [setWidth_setWidth_succ_eq_setWidth_setWidth_or_twoPow_of_getLsbD_true h,
|
||||
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h,
|
||||
shiftLeft_or_of_and_eq_zero]
|
||||
simp [and_twoPow]
|
||||
simp
|
||||
· simp only [h, false_eq_true, ↓reduceIte, shiftLeft_zero']
|
||||
rw [setWidth_setWidth_succ_eq_setWidth_setWidth_of_getLsbD_false (i := n + 1)]
|
||||
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false (i := n + 1)]
|
||||
simp [h]
|
||||
|
||||
/--
|
||||
@@ -438,446 +427,6 @@ theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
· simp [of_length_zero]
|
||||
· simp [shiftLeftRec_eq]
|
||||
|
||||
/-! # udiv/urem recurrence for bitblasting
|
||||
|
||||
In order to prove the correctness of the division algorithm on the integers,
|
||||
one shows that `n.div d = q` and `n.mod d = r` iff `n = d * q + r` and `0 ≤ r < d`.
|
||||
Mnemonic: `n` is the numerator, `d` is the denominator, `q` is the quotient, and `r` the remainder.
|
||||
|
||||
This *uniqueness of decomposition* is not true for bitvectors.
|
||||
For `n = 0, d = 3, w = 3`, we can write:
|
||||
- `0 = 0 * 3 + 0` (`q = 0`, `r = 0 < 3`.)
|
||||
- `0 = 2 * 3 + 2 = 6 + 2 ≃ 0 (mod 8)` (`q = 2`, `r = 2 < 3`).
|
||||
|
||||
Such examples can be created by choosing different `(q, r)` for a fixed `(d, n)`
|
||||
such that `(d * q + r)` overflows and wraps around to equal `n`.
|
||||
|
||||
This tells us that the division algorithm must have more restrictions than just the ones
|
||||
we have for integers. These restrictions are captured in `DivModState.Lawful`.
|
||||
The key idea is to state the relationship in terms of the toNat values of {n, d, q, r}.
|
||||
If the division equation `d.toNat * q.toNat + r.toNat = n.toNat` holds,
|
||||
then `n.udiv d = q` and `n.umod d = r`.
|
||||
|
||||
Following this, we implement the division algorithm by repeated shift-subtract.
|
||||
|
||||
References:
|
||||
- Fast 32-bit Division on the DSP56800E: Minimized nonrestoring division algorithm by David Baca
|
||||
- Bitwuzla sources for bitblasting.h
|
||||
-/
|
||||
|
||||
private theorem Nat.div_add_eq_left_of_lt {x y z : Nat} (hx : z ∣ x) (hy : y < z) (hz : 0 < z) :
|
||||
(x + y) / z = x / z := by
|
||||
refine Nat.div_eq_of_lt_le ?lo ?hi
|
||||
· apply Nat.le_trans
|
||||
· exact div_mul_le_self x z
|
||||
· omega
|
||||
· simp only [succ_eq_add_one, Nat.add_mul, Nat.one_mul]
|
||||
apply Nat.add_lt_add_of_le_of_lt
|
||||
· apply Nat.le_of_eq
|
||||
exact (Nat.div_eq_iff_eq_mul_left hz hx).mp rfl
|
||||
· exact hy
|
||||
|
||||
/-- If the division equation `d.toNat * q.toNat + r.toNat = n.toNat` holds,
|
||||
then `n.udiv d = q`. -/
|
||||
theorem udiv_eq_of_mul_add_toNat {d n q r : BitVec w} (hd : 0 < d)
|
||||
(hrd : r < d)
|
||||
(hdqnr : d.toNat * q.toNat + r.toNat = n.toNat) :
|
||||
n.udiv d = q := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [toNat_udiv]
|
||||
replace hdqnr : (d.toNat * q.toNat + r.toNat) / d.toNat = n.toNat / d.toNat := by
|
||||
simp [hdqnr]
|
||||
rw [Nat.div_add_eq_left_of_lt] at hdqnr
|
||||
· rw [← hdqnr]
|
||||
exact mul_div_right q.toNat hd
|
||||
· exact Nat.dvd_mul_right d.toNat q.toNat
|
||||
· exact hrd
|
||||
· exact hd
|
||||
|
||||
/-- If the division equation `d.toNat * q.toNat + r.toNat = n.toNat` holds,
|
||||
then `n.umod d = r`. -/
|
||||
theorem umod_eq_of_mul_add_toNat {d n q r : BitVec w} (hrd : r < d)
|
||||
(hdqnr : d.toNat * q.toNat + r.toNat = n.toNat) :
|
||||
n.umod d = r := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [toNat_umod]
|
||||
replace hdqnr : (d.toNat * q.toNat + r.toNat) % d.toNat = n.toNat % d.toNat := by
|
||||
simp [hdqnr]
|
||||
rw [Nat.add_mod, Nat.mul_mod_right] at hdqnr
|
||||
simp only [Nat.zero_add, mod_mod] at hdqnr
|
||||
replace hrd : r.toNat < d.toNat := by
|
||||
simpa [BitVec.lt_def] using hrd
|
||||
rw [Nat.mod_eq_of_lt hrd] at hdqnr
|
||||
simp [hdqnr]
|
||||
|
||||
/-! ### DivModState -/
|
||||
|
||||
/-- `DivModState` is a structure that maintains the state of recursive `divrem` calls. -/
|
||||
structure DivModState (w : Nat) : Type where
|
||||
/-- The number of bits in the numerator that are not yet processed -/
|
||||
wn : Nat
|
||||
/-- The number of bits in the remainder (and quotient) -/
|
||||
wr : Nat
|
||||
/-- The current quotient. -/
|
||||
q : BitVec w
|
||||
/-- The current remainder. -/
|
||||
r : BitVec w
|
||||
|
||||
|
||||
/-- `DivModArgs` contains the arguments to a `divrem` call which remain constant throughout
|
||||
execution. -/
|
||||
structure DivModArgs (w : Nat) where
|
||||
/-- the numerator (aka, dividend) -/
|
||||
n : BitVec w
|
||||
/-- the denumerator (aka, divisor)-/
|
||||
d : BitVec w
|
||||
|
||||
/-- A `DivModState` is lawful if the remainder width `wr` plus the numerator width `wn` equals `w`,
|
||||
and the bitvectors `r` and `n` have values in the bounds given by bitwidths `wr`, resp. `wn`.
|
||||
|
||||
This is a proof engineering choice: an alternative world could have been
|
||||
`r : BitVec wr` and `n : BitVec wn`, but this required much more dependent typing coercions.
|
||||
|
||||
Instead, we choose to declare all involved bitvectors as length `w`, and then prove that
|
||||
the values are within their respective bounds.
|
||||
|
||||
We start with `wn = w` and `wr = 0`, and then in each step, we decrement `wn` and increment `wr`.
|
||||
In this way, we grow a legal remainder in each loop iteration.
|
||||
-/
|
||||
structure DivModState.Lawful {w : Nat} (args : DivModArgs w) (qr : DivModState w) : Prop where
|
||||
/-- The sum of widths of the dividend and remainder is `w`. -/
|
||||
hwrn : qr.wr + qr.wn = w
|
||||
/-- The denominator is positive. -/
|
||||
hdPos : 0 < args.d
|
||||
/-- The remainder is strictly less than the denominator. -/
|
||||
hrLtDivisor : qr.r.toNat < args.d.toNat
|
||||
/-- The remainder is morally a `Bitvec wr`, and so has value less than `2^wr`. -/
|
||||
hrWidth : qr.r.toNat < 2^qr.wr
|
||||
/-- The quotient is morally a `Bitvec wr`, and so has value less than `2^wr`. -/
|
||||
hqWidth : qr.q.toNat < 2^qr.wr
|
||||
/-- The low `(w - wn)` bits of `n` obey the invariant for division. -/
|
||||
hdiv : args.n.toNat >>> qr.wn = args.d.toNat * qr.q.toNat + qr.r.toNat
|
||||
|
||||
/-- A lawful DivModState implies `w > 0`. -/
|
||||
def DivModState.Lawful.hw {args : DivModArgs w} {qr : DivModState w}
|
||||
{h : DivModState.Lawful args qr} : 0 < w := by
|
||||
have hd := h.hdPos
|
||||
rcases w with rfl | w
|
||||
· have hcontra : args.d = 0#0 := by apply Subsingleton.elim
|
||||
rw [hcontra] at hd
|
||||
simp at hd
|
||||
· omega
|
||||
|
||||
/-- An initial value with both `q, r = 0`. -/
|
||||
def DivModState.init (w : Nat) : DivModState w := {
|
||||
wn := w
|
||||
wr := 0
|
||||
q := 0#w
|
||||
r := 0#w
|
||||
}
|
||||
|
||||
/-- The initial state is lawful. -/
|
||||
def DivModState.lawful_init {w : Nat} (args : DivModArgs w) (hd : 0#w < args.d) :
|
||||
DivModState.Lawful args (DivModState.init w) := by
|
||||
simp only [BitVec.DivModState.init]
|
||||
exact {
|
||||
hwrn := by simp only; omega,
|
||||
hdPos := by assumption
|
||||
hrLtDivisor := by simp [BitVec.lt_def] at hd ⊢; assumption
|
||||
hrWidth := by simp [DivModState.init],
|
||||
hqWidth := by simp [DivModState.init],
|
||||
hdiv := by
|
||||
simp only [DivModState.init, toNat_ofNat, zero_mod, Nat.mul_zero, Nat.add_zero];
|
||||
rw [Nat.shiftRight_eq_div_pow]
|
||||
apply Nat.div_eq_of_lt args.n.isLt
|
||||
}
|
||||
|
||||
/--
|
||||
A lawful DivModState with a fully consumed dividend (`wn = 0`) witnesses that the
|
||||
quotient has been correctly computed.
|
||||
-/
|
||||
theorem DivModState.udiv_eq_of_lawful {n d : BitVec w} {qr : DivModState w}
|
||||
(h_lawful : DivModState.Lawful {n, d} qr)
|
||||
(h_final : qr.wn = 0) :
|
||||
n.udiv d = qr.q := by
|
||||
apply udiv_eq_of_mul_add_toNat h_lawful.hdPos h_lawful.hrLtDivisor
|
||||
have hdiv := h_lawful.hdiv
|
||||
simp only [h_final] at *
|
||||
omega
|
||||
|
||||
/--
|
||||
A lawful DivModState with a fully consumed dividend (`wn = 0`) witnesses that the
|
||||
remainder has been correctly computed.
|
||||
-/
|
||||
theorem DivModState.umod_eq_of_lawful {qr : DivModState w}
|
||||
(h : DivModState.Lawful {n, d} qr)
|
||||
(h_final : qr.wn = 0) :
|
||||
n.umod d = qr.r := by
|
||||
apply umod_eq_of_mul_add_toNat h.hrLtDivisor
|
||||
have hdiv := h.hdiv
|
||||
simp only [shiftRight_zero] at hdiv
|
||||
simp only [h_final] at *
|
||||
exact hdiv.symm
|
||||
|
||||
/-! ### DivModState.Poised -/
|
||||
|
||||
/--
|
||||
A `Poised` DivModState is a state which is `Lawful` and furthermore, has at least
|
||||
one numerator bit left to process `(0 < wn)`
|
||||
|
||||
The input to the shift subtractor is a legal input to `divrem`, and we also need to have an
|
||||
input bit to perform shift subtraction on, and thus we need `0 < wn`.
|
||||
-/
|
||||
structure DivModState.Poised {w : Nat} (args : DivModArgs w) (qr : DivModState w)
|
||||
extends DivModState.Lawful args qr : Type where
|
||||
/-- Only perform a round of shift-subtract if we have dividend bits. -/
|
||||
hwn_lt : 0 < qr.wn
|
||||
|
||||
/--
|
||||
In the shift subtract input, the dividend is at least one bit long (`wn > 0`), so
|
||||
the remainder has bits to be computed (`wr < w`).
|
||||
-/
|
||||
def DivModState.wr_lt_w {qr : DivModState w} (h : qr.Poised args) : qr.wr < w := by
|
||||
have hwrn := h.hwrn
|
||||
have hwn_lt := h.hwn_lt
|
||||
omega
|
||||
|
||||
/-! ### Division shift subtractor -/
|
||||
|
||||
/--
|
||||
One round of the division algorithm, that tries to perform a subtract shift.
|
||||
Note that this should only be called when `r.msb = false`, so we will not overflow.
|
||||
-/
|
||||
def divSubtractShift (args : DivModArgs w) (qr : DivModState w) : DivModState w :=
|
||||
let {n, d} := args
|
||||
let wn := qr.wn - 1
|
||||
let wr := qr.wr + 1
|
||||
let r' := shiftConcat qr.r (n.getLsbD wn)
|
||||
if r' < d then {
|
||||
q := qr.q.shiftConcat false, -- If `r' < d`, then we do not have a quotient bit.
|
||||
r := r'
|
||||
wn, wr
|
||||
} else {
|
||||
q := qr.q.shiftConcat true, -- Otherwise, `r' ≥ d`, and we have a quotient bit.
|
||||
r := r' - d -- we subtract to maintain the invariant that `r < d`.
|
||||
wn, wr
|
||||
}
|
||||
|
||||
/-- The value of shifting right by `wn - 1` equals shifting by `wn` and grabbing the lsb at `(wn - 1)`. -/
|
||||
theorem DivModState.toNat_shiftRight_sub_one_eq
|
||||
{args : DivModArgs w} {qr : DivModState w} (h : qr.Poised args) :
|
||||
args.n.toNat >>> (qr.wn - 1)
|
||||
= (args.n.toNat >>> qr.wn) * 2 + (args.n.getLsbD (qr.wn - 1)).toNat := by
|
||||
show BitVec.toNat (args.n >>> (qr.wn - 1)) = _
|
||||
have {..} := h -- break the structure down for `omega`
|
||||
rw [shiftRight_sub_one_eq_shiftConcat args.n h.hwn_lt]
|
||||
rw [toNat_shiftConcat_eq_of_lt (k := w - qr.wn)]
|
||||
· simp
|
||||
· omega
|
||||
· apply BitVec.toNat_ushiftRight_lt
|
||||
omega
|
||||
|
||||
/--
|
||||
This is used when proving the correctness of the divison algorithm,
|
||||
where we know that `r < d`.
|
||||
We then want to show that `((r.shiftConcat b) - d) < d` as the loop invariant.
|
||||
In arithmetic, this is the same as showing that
|
||||
`r * 2 + 1 - d < d`, which this theorem establishes.
|
||||
-/
|
||||
private theorem two_mul_add_sub_lt_of_lt_of_lt_two (h : a < x) (hy : y < 2) :
|
||||
2 * a + y - x < x := by omega
|
||||
|
||||
/-- We show that the output of `divSubtractShift` is lawful, which tells us that it
|
||||
obeys the division equation. -/
|
||||
theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
|
||||
DivModState.Lawful args (divSubtractShift args qr) := by
|
||||
rcases args with ⟨n, d⟩
|
||||
simp only [divSubtractShift, decide_eq_true_eq]
|
||||
-- We add these hypotheses for `omega` to find them later.
|
||||
have ⟨⟨hrwn, hd, hrd, hr, hn, hrnd⟩, hwn_lt⟩ := h
|
||||
have : d.toNat * (qr.q.toNat * 2) = d.toNat * qr.q.toNat * 2 := by rw [Nat.mul_assoc]
|
||||
by_cases rltd : shiftConcat qr.r (n.getLsbD (qr.wn - 1)) < d
|
||||
· simp only [rltd, ↓reduceIte]
|
||||
constructor <;> try bv_omega
|
||||
case pos.hrWidth => apply toNat_shiftConcat_lt_of_lt <;> omega
|
||||
case pos.hqWidth => apply toNat_shiftConcat_lt_of_lt <;> omega
|
||||
case pos.hdiv =>
|
||||
simp [qr.toNat_shiftRight_sub_one_eq h, h.hdiv, this,
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth,
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hqWidth]
|
||||
omega
|
||||
· simp only [rltd, ↓reduceIte]
|
||||
constructor <;> try bv_omega
|
||||
case neg.hrLtDivisor =>
|
||||
simp only [lt_def, Nat.not_lt] at rltd
|
||||
rw [BitVec.toNat_sub_of_le rltd,
|
||||
toNat_shiftConcat_eq_of_lt (hk := qr.wr_lt_w h) (hx := h.hrWidth),
|
||||
Nat.mul_comm]
|
||||
apply two_mul_add_sub_lt_of_lt_of_lt_two <;> bv_omega
|
||||
case neg.hrWidth =>
|
||||
simp only
|
||||
have hdr' : d ≤ (qr.r.shiftConcat (n.getLsbD (qr.wn - 1))) :=
|
||||
BitVec.not_lt_iff_le.mp rltd
|
||||
have hr' : ((qr.r.shiftConcat (n.getLsbD (qr.wn - 1)))).toNat < 2 ^ (qr.wr + 1) := by
|
||||
apply toNat_shiftConcat_lt_of_lt <;> bv_omega
|
||||
rw [BitVec.toNat_sub_of_le hdr']
|
||||
omega
|
||||
case neg.hqWidth =>
|
||||
apply toNat_shiftConcat_lt_of_lt <;> omega
|
||||
case neg.hdiv =>
|
||||
have rltd' := (BitVec.not_lt_iff_le.mp rltd)
|
||||
simp only [qr.toNat_shiftRight_sub_one_eq h,
|
||||
BitVec.toNat_sub_of_le rltd',
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth]
|
||||
simp only [BitVec.le_def,
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth] at rltd'
|
||||
simp only [toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hqWidth, h.hdiv, Nat.mul_add]
|
||||
bv_omega
|
||||
|
||||
/-! ### Core division algorithm circuit -/
|
||||
|
||||
/-- A recursive definition of division for bitblasting, in terms of a shift-subtraction circuit. -/
|
||||
def divRec {w : Nat} (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
DivModState w :=
|
||||
match m with
|
||||
| 0 => qr
|
||||
| m + 1 => divRec m args <| divSubtractShift args qr
|
||||
|
||||
@[simp]
|
||||
theorem divRec_zero (qr : DivModState w) :
|
||||
divRec 0 args qr = qr := rfl
|
||||
|
||||
@[simp]
|
||||
theorem divRec_succ (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
divRec (m + 1) args qr =
|
||||
divRec m args (divSubtractShift args qr) := rfl
|
||||
|
||||
/-- The output of `divRec` is a lawful state -/
|
||||
theorem lawful_divRec {args : DivModArgs w} {qr : DivModState w}
|
||||
(h : DivModState.Lawful args qr) :
|
||||
DivModState.Lawful args (divRec qr.wn args qr) := by
|
||||
generalize hm : qr.wn = m
|
||||
induction m generalizing qr
|
||||
case zero =>
|
||||
exact h
|
||||
case succ wn' ih =>
|
||||
simp only [divRec_succ]
|
||||
apply ih
|
||||
· apply lawful_divSubtractShift
|
||||
constructor
|
||||
· assumption
|
||||
· omega
|
||||
· simp only [divSubtractShift, hm]
|
||||
split <;> rfl
|
||||
|
||||
/-- The output of `divRec` has no more bits left to process (i.e., `wn = 0`) -/
|
||||
@[simp]
|
||||
theorem wn_divRec (args : DivModArgs w) (qr : DivModState w) :
|
||||
(divRec qr.wn args qr).wn = 0 := by
|
||||
generalize hm : qr.wn = m
|
||||
induction m generalizing qr
|
||||
case zero =>
|
||||
assumption
|
||||
case succ wn' ih =>
|
||||
apply ih
|
||||
simp only [divSubtractShift, hm]
|
||||
split <;> rfl
|
||||
|
||||
/-- The result of `udiv` agrees with the result of the division recurrence. -/
|
||||
theorem udiv_eq_divRec (hd : 0#w < d) :
|
||||
let out := divRec w {n, d} (DivModState.init w)
|
||||
n.udiv d = out.q := by
|
||||
have := DivModState.lawful_init {n, d} hd
|
||||
have := lawful_divRec this
|
||||
apply DivModState.udiv_eq_of_lawful this (wn_divRec ..)
|
||||
|
||||
/-- The result of `umod` agrees with the result of the division recurrence. -/
|
||||
theorem umod_eq_divRec (hd : 0#w < d) :
|
||||
let out := divRec w {n, d} (DivModState.init w)
|
||||
n.umod d = out.r := by
|
||||
have := DivModState.lawful_init {n, d} hd
|
||||
have := lawful_divRec this
|
||||
apply DivModState.umod_eq_of_lawful this (wn_divRec ..)
|
||||
|
||||
theorem divRec_succ' (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
divRec (m+1) args qr =
|
||||
let wn := qr.wn - 1
|
||||
let wr := qr.wr + 1
|
||||
let r' := shiftConcat qr.r (args.n.getLsbD wn)
|
||||
let input : DivModState _ :=
|
||||
if r' < args.d then {
|
||||
q := qr.q.shiftConcat false,
|
||||
r := r'
|
||||
wn, wr
|
||||
} else {
|
||||
q := qr.q.shiftConcat true,
|
||||
r := r' - args.d
|
||||
wn, wr
|
||||
}
|
||||
divRec m args input := by
|
||||
simp [divRec_succ, divSubtractShift]
|
||||
|
||||
/- ### Arithmetic shift right (sshiftRight) recurrence -/
|
||||
|
||||
/--
|
||||
`sshiftRightRec x y n` shifts `x` arithmetically/signed to the right by the first `n` bits of `y`.
|
||||
The theorem `sshiftRight_eq_sshiftRightRec` proves the equivalence of `(x.sshiftRight y)` and `sshiftRightRec`.
|
||||
Together with equations `sshiftRightRec_zero`, `sshiftRightRec_succ`,
|
||||
this allows us to unfold `sshiftRight` into a circuit for bitblasting.
|
||||
-/
|
||||
def sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
|
||||
let shiftAmt := (y &&& (twoPow w₂ n))
|
||||
match n with
|
||||
| 0 => x.sshiftRight' shiftAmt
|
||||
| n + 1 => (sshiftRightRec x y n).sshiftRight' shiftAmt
|
||||
|
||||
@[simp]
|
||||
theorem sshiftRightRec_zero_eq (x : BitVec w₁) (y : BitVec w₂) :
|
||||
sshiftRightRec x y 0 = x.sshiftRight' (y &&& 1#w₂) := by
|
||||
simp only [sshiftRightRec, twoPow_zero]
|
||||
|
||||
@[simp]
|
||||
theorem sshiftRightRec_succ_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
|
||||
sshiftRightRec x y (n + 1) = (sshiftRightRec x y n).sshiftRight' (y &&& twoPow w₂ (n + 1)) := by
|
||||
simp [sshiftRightRec]
|
||||
|
||||
/--
|
||||
If `y &&& z = 0`, `x.sshiftRight (y ||| z) = (x.sshiftRight y).sshiftRight z`.
|
||||
This follows as `y &&& z = 0` implies `y ||| z = y + z`,
|
||||
and thus `x.sshiftRight (y ||| z) = x.sshiftRight (y + z) = (x.sshiftRight y).sshiftRight z`.
|
||||
-/
|
||||
theorem sshiftRight'_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
|
||||
(h : y &&& z = 0#w₂) :
|
||||
x.sshiftRight' (y ||| z) = (x.sshiftRight' y).sshiftRight' z := by
|
||||
simp [sshiftRight', ← add_eq_or_of_and_eq_zero _ _ h,
|
||||
toNat_add_of_and_eq_zero h, sshiftRight_add]
|
||||
|
||||
theorem sshiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
|
||||
sshiftRightRec x y n = x.sshiftRight' ((y.setWidth (n + 1)).setWidth w₂) := by
|
||||
induction n generalizing x y
|
||||
case zero =>
|
||||
ext i
|
||||
simp [twoPow_zero, Nat.reduceAdd, and_one_eq_setWidth_ofBool_getLsbD, setWidth_one]
|
||||
case succ n ih =>
|
||||
simp only [sshiftRightRec_succ_eq, and_twoPow, ih]
|
||||
by_cases h : y.getLsbD (n + 1)
|
||||
· rw [setWidth_setWidth_succ_eq_setWidth_setWidth_or_twoPow_of_getLsbD_true h,
|
||||
sshiftRight'_or_of_and_eq_zero (by simp [and_twoPow]), h]
|
||||
simp
|
||||
· rw [setWidth_setWidth_succ_eq_setWidth_setWidth_of_getLsbD_false (i := n + 1)
|
||||
(by simp [h])]
|
||||
simp [h]
|
||||
|
||||
/--
|
||||
Show that `x.sshiftRight y` can be written in terms of `sshiftRightRec`.
|
||||
This can be unfolded in terms of `sshiftRightRec_zero_eq`, `sshiftRightRec_succ_eq` for bitblasting.
|
||||
-/
|
||||
theorem sshiftRight_eq_sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
(x.sshiftRight' y).getLsbD i = (sshiftRightRec x y (w₂ - 1)).getLsbD i := by
|
||||
rcases w₂ with rfl | w₂
|
||||
· simp [of_length_zero]
|
||||
· simp [sshiftRightRec_eq]
|
||||
|
||||
/- ### Logical shift right (ushiftRight) recurrence for bitblasting -/
|
||||
|
||||
/--
|
||||
@@ -916,20 +465,20 @@ theorem ushiftRight'_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
|
||||
simp [← add_eq_or_of_and_eq_zero _ _ h, toNat_add_of_and_eq_zero h, shiftRight_add]
|
||||
|
||||
theorem ushiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
|
||||
ushiftRightRec x y n = x >>> (y.setWidth (n + 1)).setWidth w₂ := by
|
||||
ushiftRightRec x y n = x >>> (y.truncate (n + 1)).zeroExtend w₂ := by
|
||||
induction n generalizing x y
|
||||
case zero =>
|
||||
ext i
|
||||
simp only [ushiftRightRec_zero, twoPow_zero, Nat.reduceAdd,
|
||||
and_one_eq_setWidth_ofBool_getLsbD, setWidth_one]
|
||||
and_one_eq_zeroExtend_ofBool_getLsb, truncate_one]
|
||||
case succ n ih =>
|
||||
simp only [ushiftRightRec_succ, and_twoPow]
|
||||
rw [ih]
|
||||
by_cases h : y.getLsbD (n + 1) <;> simp only [h, ↓reduceIte]
|
||||
· rw [setWidth_setWidth_succ_eq_setWidth_setWidth_or_twoPow_of_getLsbD_true h,
|
||||
by_cases h : y.getLsb (n + 1) <;> simp only [h, ↓reduceIte]
|
||||
· rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h,
|
||||
ushiftRight'_or_of_and_eq_zero]
|
||||
simp [and_twoPow]
|
||||
· simp [setWidth_setWidth_succ_eq_setWidth_setWidth_of_getLsbD_false, h]
|
||||
simp
|
||||
· simp [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false, h]
|
||||
|
||||
/--
|
||||
Show that `x >>> y` can be written in terms of `ushiftRightRec`.
|
||||
|
||||
@@ -8,8 +8,6 @@ import Init.Data.BitVec.Lemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Fin.Iterate
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
namespace BitVec
|
||||
|
||||
/--
|
||||
@@ -41,24 +39,24 @@ theorem iunfoldr.fst_eq
|
||||
private theorem iunfoldr.eq_test
|
||||
{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.getLsbD i.val)) :
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
|
||||
iunfoldr f a = (state w, BitVec.truncate w value) := by
|
||||
apply Fin.hIterate_eq (fun i => ((state i, BitVec.truncate i value) : α × BitVec i))
|
||||
case init =>
|
||||
simp only [init, eq_nil]
|
||||
case step =>
|
||||
intro i
|
||||
simp_all [setWidth_succ]
|
||||
simp_all [truncate_succ]
|
||||
|
||||
theorem iunfoldr_getLsbD' {f : Fin w → α → α × Bool} (state : Nat → α)
|
||||
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, getLsbD (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd)
|
||||
(∀ 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, getLsbD p.snd i.val = (f ⟨i.val, Nat.lt_of_lt_of_le i.isLt hj⟩ (state i.val)).snd)
|
||||
(∀ 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 =>
|
||||
@@ -73,7 +71,7 @@ theorem iunfoldr_getLsbD' {f : Fin w → α → α × Bool} (state : Nat → α)
|
||||
apply And.intro
|
||||
case left =>
|
||||
intro i
|
||||
simp only [getLsbD_cons]
|
||||
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)]
|
||||
@@ -90,10 +88,10 @@ theorem iunfoldr_getLsbD' {f : Fin w → α → α × Bool} (state : Nat → α)
|
||||
rw [← ind j, ← (ih hj2).2]
|
||||
|
||||
|
||||
theorem iunfoldr_getLsbD {f : Fin w → α → α × Bool} (state : Nat → α) (i : Fin w)
|
||||
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)) :
|
||||
getLsbD (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd := by
|
||||
exact (iunfoldr_getLsbD' state ind).1 i
|
||||
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`.
|
||||
@@ -101,14 +99,14 @@ Correctness theorem for `iunfoldr`.
|
||||
theorem iunfoldr_replace
|
||||
{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.getLsbD i.val)) :
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
|
||||
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.getLsbD i.val)) :
|
||||
(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]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,15 +4,18 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: F. G. Dorais
|
||||
-/
|
||||
prelude
|
||||
import Init.NotationExtra
|
||||
|
||||
|
||||
namespace Bool
|
||||
import Init.BinderPredicates
|
||||
|
||||
/-- Boolean exclusive or -/
|
||||
abbrev xor : Bool → Bool → Bool := bne
|
||||
|
||||
@[inherit_doc] infixl:33 " ^^ " => xor
|
||||
namespace Bool
|
||||
|
||||
/- Namespaced versions that can be used instead of prefixing `_root_` -/
|
||||
@[inherit_doc not] protected abbrev not := not
|
||||
@[inherit_doc or] protected abbrev or := or
|
||||
@[inherit_doc and] protected abbrev and := and
|
||||
@[inherit_doc xor] protected abbrev xor := xor
|
||||
|
||||
instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∀ x, p x) :=
|
||||
match inst true, inst false with
|
||||
@@ -52,16 +55,10 @@ theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
|
||||
theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
|
||||
theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
|
||||
|
||||
-- These lemmas assist with confluence.
|
||||
@[simp] theorem eq_false_imp_eq_true_iff :
|
||||
∀ (a b : Bool), ((a = false → b = true) ↔ (b = false → a = true)) = True := by decide
|
||||
@[simp] theorem eq_true_imp_eq_false_iff :
|
||||
∀ (a b : Bool), ((a = true → b = false) ↔ (b = true → a = false)) = True := by decide
|
||||
|
||||
/-! ### and -/
|
||||
|
||||
@[simp] theorem and_self_left : ∀ (a b : Bool), (a && (a && b)) = (a && b) := by decide
|
||||
@[simp] theorem and_self_right : ∀ (a b : Bool), ((a && b) && b) = (a && b) := by decide
|
||||
@[simp] theorem and_self_left : ∀(a b : Bool), (a && (a && b)) = (a && b) := by decide
|
||||
@[simp] theorem and_self_right : ∀(a b : Bool), ((a && b) && b) = (a && b) := by decide
|
||||
|
||||
@[simp] theorem not_and_self : ∀ (x : Bool), (!x && x) = false := by decide
|
||||
@[simp] theorem and_not_self : ∀ (x : Bool), (x && !x) = false := by decide
|
||||
@@ -73,8 +70,8 @@ Added for confluence with `not_and_self` `and_not_self` on term
|
||||
1. `(b = true ∨ !b = true)` via `Bool.and_eq_true`
|
||||
2. `false = true` via `Bool.and_not_self`
|
||||
-/
|
||||
@[simp] theorem eq_true_and_eq_false_self : ∀ (b : Bool), (b = true ∧ b = false) ↔ False := by decide
|
||||
@[simp] theorem eq_false_and_eq_true_self : ∀ (b : Bool), (b = false ∧ b = true) ↔ False := by decide
|
||||
@[simp] theorem eq_true_and_eq_false_self : ∀(b : Bool), (b = true ∧ b = false) ↔ False := by decide
|
||||
@[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⟩
|
||||
@@ -89,20 +86,15 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
|
||||
`Bool.coe_iff_coe` and `a → b` via `Bool.and_eq_true` and
|
||||
`and_iff_left_iff_imp`.
|
||||
-/
|
||||
@[simp] theorem and_iff_left_iff_imp : ∀ {a b : Bool}, ((a && b) = a) ↔ (a → b) := by decide
|
||||
@[simp] theorem and_iff_right_iff_imp : ∀ {a b : Bool}, ((a && b) = b) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_self_and : ∀ {a b : Bool}, (a = (a && b)) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_and_self : ∀ {a b : Bool}, (b = (a && b)) ↔ (b → a) := by decide
|
||||
|
||||
@[simp] theorem not_and_iff_left_iff_imp : ∀ {a b : Bool}, ((!a && b) = a) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem and_not_iff_right_iff_imp : ∀ {a b : Bool}, ((a && !b) = b) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem iff_not_self_and : ∀ {a b : Bool}, (a = (!a && b)) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem iff_and_not_self : ∀ {a b : Bool}, (b = (a && !b)) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem and_iff_left_iff_imp : ∀(a b : Bool), ((a && b) = a) ↔ (a → b) := by decide
|
||||
@[simp] theorem and_iff_right_iff_imp : ∀(a b : Bool), ((a && b) = b) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_self_and : ∀(a b : Bool), (a = (a && b)) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_and_self : ∀(a b : Bool), (b = (a && b)) ↔ (b → a) := by decide
|
||||
|
||||
/-! ### or -/
|
||||
|
||||
@[simp] theorem or_self_left : ∀ (a b : Bool), (a || (a || b)) = (a || b) := by decide
|
||||
@[simp] theorem or_self_right : ∀ (a b : Bool), ((a || b) || b) = (a || b) := by decide
|
||||
@[simp] theorem or_self_left : ∀(a b : Bool), (a || (a || b)) = (a || b) := by decide
|
||||
@[simp] theorem or_self_right : ∀(a b : Bool), ((a || b) || b) = (a || b) := by decide
|
||||
|
||||
@[simp] theorem not_or_self : ∀ (x : Bool), (!x || x) = true := by decide
|
||||
@[simp] theorem or_not_self : ∀ (x : Bool), (x || !x) = true := by decide
|
||||
@@ -123,15 +115,10 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
|
||||
`Bool.coe_iff_coe` and `a → b` via `Bool.or_eq_true` and
|
||||
`and_iff_left_iff_imp`.
|
||||
-/
|
||||
@[simp] theorem or_iff_left_iff_imp : ∀ {a b : Bool}, ((a || b) = a) ↔ (b → a) := by decide
|
||||
@[simp] theorem or_iff_right_iff_imp : ∀ {a b : Bool}, ((a || b) = b) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_self_or : ∀ {a b : Bool}, (a = (a || b)) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_or_self : ∀ {a b : Bool}, (b = (a || b)) ↔ (a → b) := by decide
|
||||
|
||||
@[simp] theorem not_or_iff_left_iff_imp : ∀ {a b : Bool}, ((!a || b) = a) ↔ a ∧ b := by decide
|
||||
@[simp] theorem or_not_iff_right_iff_imp : ∀ {a b : Bool}, ((a || !b) = b) ↔ a ∧ b := by decide
|
||||
@[simp] theorem iff_not_self_or : ∀ {a b : Bool}, (a = (!a || b)) ↔ a ∧ b := by decide
|
||||
@[simp] theorem iff_or_not_self : ∀ {a b : Bool}, (b = (a || !b)) ↔ a ∧ b := by decide
|
||||
@[simp] theorem or_iff_left_iff_imp : ∀(a b : Bool), ((a || b) = a) ↔ (b → a) := by decide
|
||||
@[simp] theorem or_iff_right_iff_imp : ∀(a b : Bool), ((a || b) = b) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_self_or : ∀(a b : Bool), (a = (a || b)) ↔ (b → a) := by decide
|
||||
@[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⟩
|
||||
@@ -147,8 +134,8 @@ theorem and_or_distrib_right : ∀ (x y z : Bool), ((x || y) && z) = (x && z ||
|
||||
theorem or_and_distrib_left : ∀ (x y z : Bool), (x || y && z) = ((x || y) && (x || z)) := by decide
|
||||
theorem or_and_distrib_right : ∀ (x y z : Bool), (x && y || z) = ((x || z) && (y || z)) := by decide
|
||||
|
||||
theorem and_xor_distrib_left : ∀ (x y z : Bool), (x && (y ^^ z)) = ((x && y) ^^ (x && z)) := by decide
|
||||
theorem and_xor_distrib_right : ∀ (x y z : Bool), ((x ^^ y) && z) = ((x && z) ^^ (y && z)) := by decide
|
||||
theorem and_xor_distrib_left : ∀ (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
|
||||
theorem and_xor_distrib_right : ∀ (x y z : Bool), (xor x y && z) = xor (x && z) (y && z) := by decide
|
||||
|
||||
/-- De Morgan's law for boolean and -/
|
||||
@[simp] theorem not_and : ∀ (x y : Bool), (!(x && y)) = (!x || !y) := by decide
|
||||
@@ -156,10 +143,10 @@ theorem and_xor_distrib_right : ∀ (x y z : Bool), ((x ^^ y) && z) = ((x && z)
|
||||
/-- De Morgan's law for boolean or -/
|
||||
@[simp] theorem not_or : ∀ (x y : Bool), (!(x || y)) = (!x && !y) := by decide
|
||||
|
||||
theorem and_eq_true_iff {x y : Bool} : (x && y) = true ↔ x = true ∧ y = true :=
|
||||
theorem and_eq_true_iff (x y : Bool) : (x && y) = true ↔ x = true ∧ y = true :=
|
||||
Iff.of_eq (and_eq_true x y)
|
||||
|
||||
theorem and_eq_false_iff : ∀ {x y : Bool}, (x && y) = false ↔ x = false ∨ y = false := by decide
|
||||
theorem and_eq_false_iff : ∀ (x y : Bool), (x && y) = false ↔ x = false ∨ y = false := by decide
|
||||
|
||||
/-
|
||||
New simp rule that replaces `Bool.and_eq_false_eq_eq_false_or_eq_false` in
|
||||
@@ -174,11 +161,11 @@ Consider the term: `¬((b && c) = true)`:
|
||||
1. Further reduces to `b = false ∨ c = false` via `Bool.and_eq_false_eq_eq_false_or_eq_false`.
|
||||
2. Further reduces to `b = true → c = false` via `not_and` and `Bool.not_eq_true`.
|
||||
-/
|
||||
@[simp] theorem and_eq_false_imp : ∀ {x y : Bool}, (x && y) = false ↔ (x = true → y = false) := by decide
|
||||
@[simp] theorem and_eq_false_imp : ∀ (x y : Bool), (x && y) = false ↔ (x = true → y = false) := by decide
|
||||
|
||||
theorem or_eq_true_iff : ∀ {x y : Bool}, (x || y) = true ↔ x = true ∨ y = true := by simp
|
||||
theorem or_eq_true_iff : ∀ (x y : Bool), (x || y) = true ↔ x = true ∨ y = true := by simp
|
||||
|
||||
@[simp] theorem or_eq_false_iff : ∀ {x y : Bool}, (x || y) = false ↔ x = false ∧ y = false := by decide
|
||||
@[simp] theorem or_eq_false_iff : ∀ (x y : Bool), (x || y) = false ↔ x = false ∧ y = false := by decide
|
||||
|
||||
/-! ### eq/beq/bne -/
|
||||
|
||||
@@ -215,11 +202,8 @@ instance : Std.LawfulIdentity (· != ·) false where
|
||||
@[simp] theorem not_beq_self : ∀ (x : Bool), ((!x) == x) = false := by decide
|
||||
@[simp] theorem beq_not_self : ∀ (x : Bool), (x == !x) = false := by decide
|
||||
|
||||
@[simp] theorem not_bne : ∀ (a b : Bool), ((!a) != b) = !(a != b) := by decide
|
||||
@[simp] theorem bne_not : ∀ (a b : Bool), (a != !b) = !(a != b) := by decide
|
||||
|
||||
theorem not_bne_self : ∀ (x : Bool), ((!x) != x) = true := by decide
|
||||
theorem bne_not_self : ∀ (x : Bool), (x != !x) = true := by decide
|
||||
@[simp] theorem not_bne_self : ∀ (x : Bool), ((!x) != x) = true := by decide
|
||||
@[simp] theorem bne_not_self : ∀ (x : Bool), (x != !x) = true := by decide
|
||||
|
||||
/-
|
||||
Added for equivalence with `Bool.not_beq_self` and needed for confluence
|
||||
@@ -233,13 +217,13 @@ due to `beq_iff_eq`.
|
||||
@[simp] theorem bne_self_left : ∀(a b : Bool), (a != (a != b)) = b := by decide
|
||||
@[simp] theorem bne_self_right : ∀(a b : Bool), ((a != b) != b) = a := by decide
|
||||
|
||||
theorem not_bne_not : ∀ (x y : Bool), ((!x) != (!y)) = (x != y) := by simp
|
||||
@[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
|
||||
@[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
|
||||
|
||||
theorem eq_not_of_ne : ∀ {x y : Bool}, x ≠ y → x = !y := by decide
|
||||
|
||||
@@ -251,53 +235,54 @@ theorem beq_eq_decide_eq [BEq α] [LawfulBEq α] [DecidableEq α] (a b : α) :
|
||||
· simp [ne_of_beq_false h]
|
||||
· simp [eq_of_beq h]
|
||||
|
||||
theorem eq_not : ∀ {a b : Bool}, (a = (!b)) ↔ (a ≠ b) := by decide
|
||||
theorem not_eq : ∀ {a b : Bool}, ((!a) = b) ↔ (a ≠ b) := by decide
|
||||
@[simp] theorem not_eq_not : ∀ {a b : Bool}, ¬a = !b ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem coe_iff_coe : ∀{a b : Bool}, (a ↔ b) ↔ a = b := by decide
|
||||
@[simp] theorem not_not_eq : ∀ {a b : Bool}, ¬(!a) = b ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem coe_true_iff_false : ∀{a b : Bool}, (a ↔ b = false) ↔ a = (!b) := by decide
|
||||
@[simp] theorem coe_false_iff_true : ∀{a b : Bool}, (a = false ↔ b) ↔ (!a) = b := by decide
|
||||
@[simp] theorem coe_false_iff_false : ∀{a b : Bool}, (a = false ↔ b = false) ↔ (!a) = (!b) := by decide
|
||||
@[simp] theorem coe_iff_coe : ∀(a b : Bool), (a ↔ b) ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem coe_true_iff_false : ∀(a b : Bool), (a ↔ b = false) ↔ a = (!b) := by decide
|
||||
@[simp] theorem coe_false_iff_true : ∀(a b : Bool), (a = false ↔ b) ↔ (!a) = b := by decide
|
||||
@[simp] theorem coe_false_iff_false : ∀(a b : Bool), (a = false ↔ b = false) ↔ (!a) = (!b) := by decide
|
||||
|
||||
/-! ### beq properties -/
|
||||
|
||||
theorem beq_comm {α} [BEq α] [LawfulBEq α] {a b : α} : (a == b) = (b == a) :=
|
||||
Bool.coe_iff_coe.mp (by simp [@eq_comm α])
|
||||
(Bool.coe_iff_coe (a == b) (b == a)).mp (by simp [@eq_comm α])
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
theorem false_xor : ∀ (x : Bool), (false ^^ x) = x := false_bne
|
||||
theorem false_xor : ∀ (x : Bool), xor false x = x := false_bne
|
||||
|
||||
theorem xor_false : ∀ (x : Bool), (x ^^ false) = x := bne_false
|
||||
theorem xor_false : ∀ (x : Bool), xor x false = x := bne_false
|
||||
|
||||
theorem true_xor : ∀ (x : Bool), (true ^^ x) = !x := true_bne
|
||||
theorem true_xor : ∀ (x : Bool), xor true x = !x := true_bne
|
||||
|
||||
theorem xor_true : ∀ (x : Bool), (x ^^ true) = !x := bne_true
|
||||
theorem xor_true : ∀ (x : Bool), xor x true = !x := bne_true
|
||||
|
||||
theorem not_xor_self : ∀ (x : Bool), (!x ^^ x) = true := not_bne_self
|
||||
theorem not_xor_self : ∀ (x : Bool), xor (!x) x = true := not_bne_self
|
||||
|
||||
theorem xor_not_self : ∀ (x : Bool), (x ^^ !x) = true := bne_not_self
|
||||
theorem xor_not_self : ∀ (x : Bool), xor x (!x) = true := bne_not_self
|
||||
|
||||
theorem not_xor : ∀ (x y : Bool), (!x ^^ y) = !(x ^^ y) := by decide
|
||||
theorem not_xor : ∀ (x y : Bool), xor (!x) y = !(xor x y) := by decide
|
||||
|
||||
theorem xor_not : ∀ (x y : Bool), (x ^^ !y) = !(x ^^ y) := by decide
|
||||
theorem xor_not : ∀ (x y : Bool), xor x (!y) = !(xor x y) := by decide
|
||||
|
||||
theorem not_xor_not : ∀ (x y : Bool), (!x ^^ !y) = (x ^^ y) := not_bne_not
|
||||
theorem not_xor_not : ∀ (x y : Bool), xor (!x) (!y) = (xor x y) := not_bne_not
|
||||
|
||||
theorem xor_self : ∀ (x : Bool), (x ^^ x) = false := by decide
|
||||
theorem xor_self : ∀ (x : Bool), xor x x = false := by decide
|
||||
|
||||
theorem xor_comm : ∀ (x y : Bool), (x ^^ y) = (y ^^ x) := by decide
|
||||
theorem xor_comm : ∀ (x y : Bool), xor x y = xor y x := by decide
|
||||
|
||||
theorem xor_left_comm : ∀ (x y z : Bool), (x ^^ (y ^^ z)) = (y ^^ (x ^^ z)) := by decide
|
||||
theorem xor_left_comm : ∀ (x y z : Bool), xor x (xor y z) = xor y (xor x z) := by decide
|
||||
|
||||
theorem xor_right_comm : ∀ (x y z : Bool), ((x ^^ y) ^^ z) = ((x ^^ z) ^^ y) := by decide
|
||||
theorem xor_right_comm : ∀ (x y z : Bool), xor (xor x y) z = xor (xor x z) y := by decide
|
||||
|
||||
theorem xor_assoc : ∀ (x y z : Bool), ((x ^^ y) ^^ z) = (x ^^ (y ^^ z)) := bne_assoc
|
||||
theorem xor_assoc : ∀ (x y z : Bool), xor (xor x y) z = xor x (xor y z) := bne_assoc
|
||||
|
||||
theorem xor_left_inj : ∀ {x y z : Bool}, (x ^^ y) = (x ^^ z) ↔ y = z := bne_left_inj
|
||||
theorem xor_left_inj : ∀ (x y z : Bool), xor x y = xor x z ↔ y = z := bne_left_inj
|
||||
|
||||
theorem xor_right_inj : ∀ {x y z : Bool}, (x ^^ z) = (y ^^ z) ↔ x = y := bne_right_inj
|
||||
theorem xor_right_inj : ∀ (x y z : Bool), xor x z = xor y z ↔ x = y := bne_right_inj
|
||||
|
||||
/-! ### le/lt -/
|
||||
|
||||
@@ -368,20 +353,22 @@ theorem and_or_inj_left_iff :
|
||||
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
|
||||
def toNat (b : Bool) : Nat := cond b 1 0
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_false : false.toNat = 0 := rfl
|
||||
@[simp] theorem toNat_false : false.toNat = 0 := rfl
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_true : true.toNat = 1 := rfl
|
||||
@[simp] theorem toNat_true : true.toNat = 1 := rfl
|
||||
|
||||
theorem toNat_le (c : Bool) : c.toNat ≤ 1 := by
|
||||
cases c <;> trivial
|
||||
|
||||
@[bv_toNat]
|
||||
@[deprecated toNat_le (since := "2024-02-23")]
|
||||
abbrev toNat_le_one := toNat_le
|
||||
|
||||
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
Nat.lt_succ_of_le (toNat_le _)
|
||||
|
||||
@[simp] theorem toNat_eq_zero {b : Bool} : b.toNat = 0 ↔ b = false := by
|
||||
@[simp] theorem toNat_eq_zero (b : Bool) : b.toNat = 0 ↔ b = false := by
|
||||
cases b <;> simp
|
||||
@[simp] theorem toNat_eq_one {b : Bool} : b.toNat = 1 ↔ b = true := by
|
||||
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 ↔ b = true := by
|
||||
cases b <;> simp
|
||||
|
||||
/-! ### ite -/
|
||||
@@ -406,13 +393,6 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
(ite p t f = false) = ite p (t = false) (f = false) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem ite_eq_false : (if b = false then p else q) ↔ if b then q else p := by
|
||||
cases b <;> simp
|
||||
|
||||
@[simp] theorem ite_eq_true_else_eq_false {q : Prop} :
|
||||
(if b = true then q else b = false) ↔ (b = true → q) := by
|
||||
cases b <;> simp
|
||||
|
||||
/-
|
||||
`not_ite_eq_true_eq_true` and related theorems below are added for
|
||||
non-confluence. A motivating example is
|
||||
@@ -427,57 +407,37 @@ lemmas.
|
||||
-/
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_true_eq_true {p : Prop} [h : Decidable p] {b c : Bool} :
|
||||
theorem not_ite_eq_true_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = true) (c = true)) ↔ (ite p (b = false) (c = false)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_false_eq_false {p : Prop} [h : Decidable p] {b c : Bool} :
|
||||
theorem not_ite_eq_false_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = false) (c = false)) ↔ (ite p (b = true) (c = true)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_true_eq_false {p : Prop} [h : Decidable p] {b c : Bool} :
|
||||
theorem not_ite_eq_true_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = true) (c = false)) ↔ (ite p (b = false) (c = true)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_false_eq_true {p : Prop} [h : Decidable p] {b c : Bool} :
|
||||
theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = false) (c = true)) ↔ (ite p (b = true) (c = false)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
/-
|
||||
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = true then True else b = true`.
|
||||
However the discrimination tree key is just `→`, so this is tried too often.
|
||||
Added for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = true then True else b = true`
|
||||
-/
|
||||
theorem eq_false_imp_eq_true : ∀ {b : Bool}, (b = false → b = true) ↔ (b = true) := by decide
|
||||
@[simp] theorem eq_false_imp_eq_true : ∀(b:Bool), (b = false → b = true) ↔ (b = true) := by decide
|
||||
|
||||
/-
|
||||
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = false then True else b = false`.
|
||||
However the discrimination tree key is just `→`, so this is tried too often.
|
||||
Added for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = false then True else b = false`
|
||||
-/
|
||||
theorem eq_true_imp_eq_false : ∀ {b : Bool}, (b = true → b = false) ↔ (b = false) := by decide
|
||||
@[simp] theorem eq_true_imp_eq_false : ∀(b:Bool), (b = true → b = false) ↔ (b = false) := by decide
|
||||
|
||||
/-! ### forall -/
|
||||
|
||||
theorem forall_bool' {p : Bool → Prop} (b : Bool) : (∀ x, p x) ↔ p b ∧ p !b :=
|
||||
⟨fun h ↦ ⟨h _, h _⟩, fun ⟨h₁, h₂⟩ x ↦ by cases b <;> cases x <;> assumption⟩
|
||||
|
||||
@[simp]
|
||||
theorem forall_bool {p : Bool → Prop} : (∀ b, p b) ↔ p false ∧ p true :=
|
||||
forall_bool' false
|
||||
|
||||
/-! ### exists -/
|
||||
|
||||
theorem exists_bool' {p : Bool → Prop} (b : Bool) : (∃ x, p x) ↔ p b ∨ p !b :=
|
||||
⟨fun ⟨x, hx⟩ ↦ by cases x <;> cases b <;> first | exact .inl ‹_› | exact .inr ‹_›,
|
||||
fun h ↦ by cases h <;> exact ⟨_, ‹_›⟩⟩
|
||||
|
||||
@[simp]
|
||||
theorem exists_bool {p : Bool → Prop} : (∃ b, p b) ↔ p false ∨ p true :=
|
||||
exists_bool' false
|
||||
|
||||
/-! ### cond -/
|
||||
|
||||
@@ -491,11 +451,6 @@ theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := cond_eq_ite
|
||||
|
||||
@[simp] theorem cond_self (c : Bool) (t : α) : cond c t t = t := by cases c <;> rfl
|
||||
|
||||
/-- If the return values are propositions, there is no harm in simplifying a `bif` to an `if`. -/
|
||||
@[simp] theorem cond_prop {b : Bool} {p q : Prop} :
|
||||
(bif b then p else q) ↔ if b then p else q := by
|
||||
cases b <;> simp
|
||||
|
||||
/-
|
||||
This is a simp rule in Mathlib, but results in non-confluence that is difficult
|
||||
to fix as decide distributes over propositions. As an example, observe that
|
||||
@@ -513,11 +468,11 @@ theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
|
||||
cond (decide p) t e = if p then t else e := by
|
||||
simp [cond_eq_ite]
|
||||
|
||||
@[simp] theorem cond_eq_ite_iff {a : Bool} {p : Prop} [h : Decidable p] {x y u v : α} :
|
||||
@[simp] theorem cond_eq_ite_iff (a : Bool) (p : Prop) [h : Decidable p] (x y u v : α) :
|
||||
(cond a x y = ite p u v) ↔ ite a x y = ite p u v := by
|
||||
simp [Bool.cond_eq_ite]
|
||||
|
||||
@[simp] theorem ite_eq_cond_iff {p : Prop} {a : Bool} [h : Decidable p] {x y u v : α} :
|
||||
@[simp] theorem ite_eq_cond_iff (p : Prop) [h : Decidable p] (a : Bool) (x y u v : α) :
|
||||
(ite p x y = cond a u v) ↔ ite p x y = ite a u v := by
|
||||
simp [Bool.cond_eq_ite]
|
||||
|
||||
@@ -536,10 +491,6 @@ protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := co
|
||||
@[simp] theorem cond_true_right : ∀(c t : Bool), cond c t true = (!c || t) := by decide
|
||||
@[simp] theorem cond_false_right : ∀(c t : Bool), cond c t false = ( c && t) := by decide
|
||||
|
||||
-- These restore confluence between the above lemmas and `cond_not`.
|
||||
@[simp] theorem cond_true_not_same : ∀ (c b : Bool), cond c (!c) b = (!c && b) := by decide
|
||||
@[simp] theorem cond_false_not_same : ∀ (c b : Bool), cond c b (!c) = (!c || b) := by decide
|
||||
|
||||
@[simp] theorem cond_true_same : ∀(c b : Bool), cond c c b = (c || b) := by decide
|
||||
@[simp] theorem cond_false_same : ∀(c b : Bool), cond c b c = (c && b) := by decide
|
||||
|
||||
@@ -553,7 +504,7 @@ theorem apply_cond (f : α → β) {b : Bool} {a a' : α} :
|
||||
f (bif b then a else a') = bif b then f a else f a' := by
|
||||
cases b <;> simp
|
||||
|
||||
/-! # decidability -/
|
||||
/-# decidability -/
|
||||
|
||||
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true
|
||||
|
||||
@@ -569,24 +520,9 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
|
||||
decide (p ↔ q) = (decide p == decide q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem and_eq_decide (p q : Prop) [dpq : Decidable (p ∧ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(p && q) = decide (p ∧ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem or_eq_decide (p q : Prop) [dpq : Decidable (p ∨ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(p || q) = decide (p ∨ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(decide p == decide q) = decide (p ↔ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
end Bool
|
||||
|
||||
export Bool (cond_eq_if xor and or not)
|
||||
export Bool (cond_eq_if)
|
||||
|
||||
/-! ### decide -/
|
||||
|
||||
@@ -595,19 +531,3 @@ export Bool (cond_eq_if xor and or not)
|
||||
|
||||
@[simp] theorem true_eq_decide_iff {p : Prop} [h : Decidable p] : true = decide p ↔ p := by
|
||||
cases h with | _ q => simp [q]
|
||||
|
||||
/-! ### coercions -/
|
||||
|
||||
/--
|
||||
This should not be turned on globally as an instance because it degrades performance in Mathlib,
|
||||
but may be used locally.
|
||||
-/
|
||||
def boolPredToPred : Coe (α → Bool) (α → Prop) where
|
||||
coe r := fun a => Eq (r a) true
|
||||
|
||||
/--
|
||||
This should not be turned on globally as an instance because it degrades performance in Mathlib,
|
||||
but may be used locally.
|
||||
-/
|
||||
def boolRelToRel : Coe (α → α → Bool) (α → α → Prop) where
|
||||
coe r := fun a b => Eq (r a b) true
|
||||
|
||||
@@ -191,137 +191,6 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 →
|
||||
def foldl {β : Type v} (f : β → UInt8 → β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : β :=
|
||||
Id.run <| as.foldlM f init start stop
|
||||
|
||||
/-- Iterator over the bytes (`UInt8`) of a `ByteArray`.
|
||||
|
||||
Typically created by `arr.iter`, where `arr` is a `ByteArray`.
|
||||
|
||||
An iterator is *valid* if the position `i` is *valid* for the array `arr`, meaning `0 ≤ i ≤ arr.size`
|
||||
|
||||
Most operations on iterators return arbitrary values if the iterator is not valid. The functions in
|
||||
the `ByteArray.Iterator` API should rule out the creation of invalid iterators, with two exceptions:
|
||||
|
||||
- `Iterator.next iter` is invalid if `iter` is already at the end of the array (`iter.atEnd` is
|
||||
`true`)
|
||||
- `Iterator.forward iter n`/`Iterator.nextn iter n` is invalid if `n` is strictly greater than the
|
||||
number of remaining bytes.
|
||||
-/
|
||||
structure Iterator where
|
||||
/-- The array the iterator is for. -/
|
||||
array : ByteArray
|
||||
/-- The current position.
|
||||
|
||||
This position is not necessarily valid for the array, for instance if one keeps calling
|
||||
`Iterator.next` when `Iterator.atEnd` is true. If the position is not valid, then the
|
||||
current byte is `(default : UInt8)`. -/
|
||||
idx : Nat
|
||||
deriving Inhabited
|
||||
|
||||
/-- Creates an iterator at the beginning of an array. -/
|
||||
def mkIterator (arr : ByteArray) : Iterator :=
|
||||
⟨arr, 0⟩
|
||||
|
||||
@[inherit_doc mkIterator]
|
||||
abbrev iter := mkIterator
|
||||
|
||||
/-- The size of an array iterator is the number of bytes remaining. -/
|
||||
instance : SizeOf Iterator where
|
||||
sizeOf i := i.array.size - i.idx
|
||||
|
||||
theorem Iterator.sizeOf_eq (i : Iterator) : sizeOf i = i.array.size - i.idx :=
|
||||
rfl
|
||||
|
||||
namespace Iterator
|
||||
|
||||
/-- Number of bytes remaining in the iterator. -/
|
||||
def remainingBytes : Iterator → Nat
|
||||
| ⟨arr, i⟩ => arr.size - i
|
||||
|
||||
@[inherit_doc Iterator.idx]
|
||||
def pos := Iterator.idx
|
||||
|
||||
/-- The byte at the current position.
|
||||
|
||||
On an invalid position, returns `(default : UInt8)`. -/
|
||||
@[inline]
|
||||
def curr : Iterator → UInt8
|
||||
| ⟨arr, i⟩ =>
|
||||
if h:i < arr.size then
|
||||
arr[i]'h
|
||||
else
|
||||
default
|
||||
|
||||
/-- Moves the iterator's position forward by one byte, unconditionally.
|
||||
|
||||
It is only valid to call this function if the iterator is not at the end of the array, *i.e.*
|
||||
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
|
||||
@[inline]
|
||||
def next : Iterator → Iterator
|
||||
| ⟨arr, i⟩ => ⟨arr, i + 1⟩
|
||||
|
||||
/-- Decreases the iterator's position.
|
||||
|
||||
If the position is zero, this function is the identity. -/
|
||||
@[inline]
|
||||
def prev : Iterator → Iterator
|
||||
| ⟨arr, i⟩ => ⟨arr, i - 1⟩
|
||||
|
||||
/-- True if the iterator is past the array's last byte. -/
|
||||
@[inline]
|
||||
def atEnd : Iterator → Bool
|
||||
| ⟨arr, i⟩ => i ≥ arr.size
|
||||
|
||||
/-- True if the iterator is not past the array's last byte. -/
|
||||
@[inline]
|
||||
def hasNext : Iterator → Bool
|
||||
| ⟨arr, i⟩ => i < arr.size
|
||||
|
||||
/-- The byte at the current position. --/
|
||||
@[inline]
|
||||
def curr' (it : Iterator) (h : it.hasNext) : UInt8 :=
|
||||
match it with
|
||||
| ⟨arr, i⟩ =>
|
||||
have : i < arr.size := by
|
||||
simp only [hasNext, decide_eq_true_eq] at h
|
||||
assumption
|
||||
arr[i]
|
||||
|
||||
/-- Moves the iterator's position forward by one byte. --/
|
||||
@[inline]
|
||||
def next' (it : Iterator) (_h : it.hasNext) : Iterator :=
|
||||
match it with
|
||||
| ⟨arr, i⟩ => ⟨arr, i + 1⟩
|
||||
|
||||
/-- True if the position is not zero. -/
|
||||
@[inline]
|
||||
def hasPrev : Iterator → Bool
|
||||
| ⟨_, i⟩ => i > 0
|
||||
|
||||
/-- Moves the iterator's position to the end of the array.
|
||||
|
||||
Note that `i.toEnd.atEnd` is always `true`. -/
|
||||
@[inline]
|
||||
def toEnd : Iterator → Iterator
|
||||
| ⟨arr, _⟩ => ⟨arr, arr.size⟩
|
||||
|
||||
/-- Moves the iterator's position several bytes forward.
|
||||
|
||||
The resulting iterator is only valid if the number of bytes to skip is less than or equal to
|
||||
the number of bytes left in the iterator. -/
|
||||
@[inline]
|
||||
def forward : Iterator → Nat → Iterator
|
||||
| ⟨arr, i⟩, f => ⟨arr, i + f⟩
|
||||
|
||||
@[inherit_doc forward, inline]
|
||||
def nextn : Iterator → Nat → Iterator := forward
|
||||
|
||||
/-- Moves the iterator's position several bytes back.
|
||||
|
||||
If asked to go back more bytes than available, stops at the beginning of the array. -/
|
||||
@[inline]
|
||||
def prevn : Iterator → Nat → Iterator
|
||||
| ⟨arr, i⟩, f => ⟨arr, i - f⟩
|
||||
|
||||
end Iterator
|
||||
end ByteArray
|
||||
|
||||
def List.toByteArray (bs : List UInt8) : ByteArray :=
|
||||
|
||||
@@ -63,27 +63,27 @@ instance : Inhabited Char where
|
||||
default := 'A'
|
||||
|
||||
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
|
||||
@[inline] def isWhitespace (c : Char) : Bool :=
|
||||
def isWhitespace (c : Char) : Bool :=
|
||||
c = ' ' || c = '\t' || c = '\r' || c = '\n'
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
|
||||
@[inline] def isUpper (c : Char) : Bool :=
|
||||
def isUpper (c : Char) : Bool :=
|
||||
c.val ≥ 65 && c.val ≤ 90
|
||||
|
||||
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
|
||||
@[inline] def isLower (c : Char) : Bool :=
|
||||
def isLower (c : Char) : Bool :=
|
||||
c.val ≥ 97 && c.val ≤ 122
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
|
||||
@[inline] def isAlpha (c : Char) : Bool :=
|
||||
def isAlpha (c : Char) : Bool :=
|
||||
c.isUpper || c.isLower
|
||||
|
||||
/-- Is the character in `0123456789`? -/
|
||||
@[inline] def isDigit (c : Char) : Bool :=
|
||||
def isDigit (c : Char) : Bool :=
|
||||
c.val ≥ 48 && c.val ≤ 57
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
|
||||
@[inline] def isAlphanum (c : Char) : Bool :=
|
||||
def isAlphanum (c : Char) : Bool :=
|
||||
c.isAlpha || c.isDigit
|
||||
|
||||
/-- Convert an upper case character to its lower case character.
|
||||
|
||||
@@ -14,7 +14,7 @@ instance coeToNat : CoeOut (Fin n) Nat :=
|
||||
⟨fun v => v.val⟩
|
||||
|
||||
/--
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is similar to `Empty.elim`.
|
||||
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 _)
|
||||
@@ -31,7 +31,7 @@ This differs from addition, which wraps around:
|
||||
(2 : Fin 3) + 1 = (0 : Fin 3)
|
||||
```
|
||||
-/
|
||||
def succ : Fin n → Fin (n + 1)
|
||||
def succ : Fin n → Fin n.succ
|
||||
| ⟨i, h⟩ => ⟨i+1, Nat.succ_lt_succ h⟩
|
||||
|
||||
variable {n : Nat}
|
||||
@@ -39,20 +39,16 @@ variable {n : Nat}
|
||||
/--
|
||||
Returns `a` modulo `n + 1` as a `Fin n.succ`.
|
||||
-/
|
||||
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
|
||||
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 `NeZero n` ensures that `Fin n` is nonempty.
|
||||
The assumption `n > 0` ensures that `Fin n` is nonempty.
|
||||
-/
|
||||
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
|
||||
⟨a % n, Nat.mod_lt _ (pos_of_neZero n)⟩
|
||||
|
||||
-- We intend to deprecate `Fin.ofNat` in favor of `Fin.ofNat'` (and later rename).
|
||||
-- This is waiting on https://github.com/leanprover/lean4/pull/5323
|
||||
-- attribute [deprecated Fin.ofNat' (since := "2024-09-16")] Fin.ofNat
|
||||
protected def ofNat' {n : Nat} (a : Nat) (h : n > 0) : Fin n :=
|
||||
⟨a % n, Nat.mod_lt _ h⟩
|
||||
|
||||
private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
|
||||
| 0, h => Nat.mod_lt _ h
|
||||
@@ -145,17 +141,14 @@ instance : ShiftLeft (Fin n) where
|
||||
instance : ShiftRight (Fin n) where
|
||||
shiftRight := Fin.shiftRight
|
||||
|
||||
instance instOfNat {n : Nat} [NeZero n] {i : Nat} : OfNat (Fin n) i where
|
||||
ofNat := Fin.ofNat' n i
|
||||
instance instOfNat : OfNat (Fin (no_index (n+1))) i where
|
||||
ofNat := Fin.ofNat i
|
||||
|
||||
instance instInhabited {n : Nat} [NeZero n] : Inhabited (Fin n) where
|
||||
instance : Inhabited (Fin (no_index (n+1))) where
|
||||
default := 0
|
||||
|
||||
@[simp] theorem zero_eta : (⟨0, Nat.zero_lt_succ _⟩ : Fin (n + 1)) = 0 := rfl
|
||||
|
||||
theorem ne_of_val_ne {i j : Fin n} (h : val i ≠ val j) : i ≠ j :=
|
||||
fun h' => absurd (val_eq_of_eq h') h
|
||||
|
||||
theorem val_ne_of_ne {i j : Fin n} (h : i ≠ j) : val i ≠ val j :=
|
||||
fun h' => absurd (eq_of_val_eq h') h
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
`hIterate` is a heterogeneous iterative operation that applies a
|
||||
`hIterate` is a heterogenous iterative operation that applies a
|
||||
index-dependent function `f` to a value `init : P start` a total of
|
||||
`stop - start` times to produce a value of type `P stop`.
|
||||
|
||||
@@ -35,7 +35,7 @@ Concretely, `hIterate start stop f init` is equal to
|
||||
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
|
||||
```
|
||||
|
||||
Because it is heterogeneous and must return a value of type `P stop`,
|
||||
Because it is heterogenous and must return a value of type `P stop`,
|
||||
`hIterate` requires proof that `start ≤ stop`.
|
||||
|
||||
One can prove properties of `hIterate` using the general theorem
|
||||
@@ -70,7 +70,7 @@ private theorem hIterateFrom_elim {P : Nat → Sort _}(Q : ∀(i : Nat), P i →
|
||||
|
||||
/-
|
||||
`hIterate_elim` provides a mechanism for showing that the result of
|
||||
`hIterate` satisfies a property `Q stop` by showing that the states
|
||||
`hIterate` satisifies a property `Q stop` by showing that the states
|
||||
at the intermediate indices `i : start ≤ i < stop` satisfy `Q i`.
|
||||
-/
|
||||
theorem hIterate_elim {P : Nat → Sort _} (Q : ∀(i : Nat), P i → Prop)
|
||||
|
||||
@@ -11,6 +11,9 @@ import Init.ByCases
|
||||
import Init.Conv
|
||||
import Init.Omega
|
||||
|
||||
-- Remove after the next stage0 update
|
||||
set_option allowUnsafeReducibility true
|
||||
|
||||
namespace Fin
|
||||
|
||||
/-- If you actually have an element of `Fin n`, then the `n` is always positive -/
|
||||
@@ -51,18 +54,11 @@ theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
|
||||
|
||||
theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
|
||||
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
|
||||
(Fin.ofNat' n a).val = a % n := rfl
|
||||
@[simp] theorem val_ofNat' (a : Nat) (is_pos : n > 0) :
|
||||
(Fin.ofNat' a is_pos).val = a % n := rfl
|
||||
|
||||
@[simp] theorem ofNat'_self {n : Nat} [NeZero n] : Fin.ofNat' n n = 0 := by
|
||||
ext
|
||||
simp
|
||||
congr
|
||||
|
||||
@[simp] theorem ofNat'_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat' n x) = x := by
|
||||
ext
|
||||
rw [val_ofNat', Nat.mod_eq_of_lt]
|
||||
exact x.2
|
||||
@[deprecated ofNat'_zero_val (since := "2024-02-22")]
|
||||
theorem ofNat'_zero_val : (Fin.ofNat' 0 h).val = 0 := Nat.zero_mod _
|
||||
|
||||
@[simp] theorem mod_val (a b : Fin n) : (a % b).val = a.val % b.val :=
|
||||
rfl
|
||||
@@ -73,9 +69,6 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
@[simp] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem val_eq_zero (a : Fin 1) : a.val = 0 :=
|
||||
Nat.eq_zero_of_le_zero <| Nat.le_of_lt_succ a.isLt
|
||||
|
||||
theorem ite_val {n : Nat} {c : Prop} [Decidable c] {x : c → Fin n} (y : ¬c → Fin n) :
|
||||
(if h : c then x h else y h).val = if h : c then (x h).val else (y h).val := by
|
||||
by_cases c <;> simp [*]
|
||||
@@ -128,7 +121,7 @@ theorem mk_le_of_le_val {b : Fin n} {a : Nat} (h : a ≤ b) :
|
||||
|
||||
@[simp] theorem mk_lt_mk {x y : Nat} {hx hy} : (⟨x, hx⟩ : Fin n) < ⟨y, hy⟩ ↔ x < y := .rfl
|
||||
|
||||
@[simp] theorem val_zero (n : Nat) [NeZero n] : ((0 : Fin n) : Nat) = 0 := rfl
|
||||
@[simp] theorem val_zero (n : Nat) : (0 : Fin (n + 1)).1 = 0 := rfl
|
||||
|
||||
@[simp] theorem mk_zero : (⟨0, Nat.succ_pos n⟩ : Fin (n + 1)) = 0 := rfl
|
||||
|
||||
@@ -148,12 +141,6 @@ theorem eq_zero_or_eq_succ {n : Nat} : ∀ i : Fin (n + 1), i = 0 ∨ ∃ j : Fi
|
||||
theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) : ∃ j : Fin n, i = j.succ :=
|
||||
(eq_zero_or_eq_succ i).resolve_left hi
|
||||
|
||||
protected theorem le_antisymm_iff {x y : Fin n} : x = y ↔ x ≤ y ∧ y ≤ x :=
|
||||
Fin.ext_iff.trans Nat.le_antisymm_iff
|
||||
|
||||
protected theorem le_antisymm {x y : Fin n} (h1 : x ≤ y) (h2 : y ≤ x) : x = y :=
|
||||
Fin.le_antisymm_iff.2 ⟨h1, h2⟩
|
||||
|
||||
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
|
||||
|
||||
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
|
||||
@@ -175,24 +162,8 @@ theorem rev_eq {n a : Nat} (i : Fin (n + 1)) (h : n = a + i) :
|
||||
@[simp] theorem rev_lt_rev {i j : Fin n} : rev i < rev j ↔ j < i := by
|
||||
rw [← Fin.not_le, ← Fin.not_le, rev_le_rev]
|
||||
|
||||
/-! ### last -/
|
||||
|
||||
@[simp] theorem val_last (n : Nat) : last n = n := rfl
|
||||
|
||||
@[simp] theorem last_zero : (Fin.last 0 : Fin 1) = 0 := by
|
||||
ext
|
||||
simp
|
||||
|
||||
@[simp] theorem zero_eq_last_iff {n : Nat} : (0 : Fin (n + 1)) = last n ↔ n = 0 := by
|
||||
constructor
|
||||
· intro h
|
||||
simp_all [Fin.ext_iff]
|
||||
· rintro rfl
|
||||
simp
|
||||
|
||||
@[simp] theorem last_eq_zero_iff {n : Nat} : Fin.last n = 0 ↔ n = 0 := by
|
||||
simp [eq_comm (a := Fin.last n)]
|
||||
|
||||
theorem le_last (i : Fin (n + 1)) : i ≤ last n := Nat.le_of_lt_succ i.is_lt
|
||||
|
||||
theorem last_pos : (0 : Fin (n + 2)) < last (n + 1) := Nat.succ_pos _
|
||||
@@ -226,28 +197,10 @@ instance subsingleton_one : Subsingleton (Fin 1) := subsingleton_iff_le_one.2 (b
|
||||
|
||||
theorem fin_one_eq_zero (a : Fin 1) : a = 0 := Subsingleton.elim a 0
|
||||
|
||||
@[simp] theorem zero_eq_one_iff {n : Nat} [NeZero n] : (0 : Fin n) = 1 ↔ n = 1 := by
|
||||
constructor
|
||||
· intro h
|
||||
simp [Fin.ext_iff] at h
|
||||
change 0 % n = 1 % n at h
|
||||
rw [eq_comm] at h
|
||||
simpa using h
|
||||
· rintro rfl
|
||||
simp
|
||||
|
||||
@[simp] theorem one_eq_zero_iff {n : Nat} [NeZero n] : (1 : Fin n) = 0 ↔ n = 1 := by
|
||||
rw [eq_comm]
|
||||
simp
|
||||
|
||||
theorem add_def (a b : Fin n) : a + b = Fin.mk ((a + b) % n) (Nat.mod_lt _ a.size_pos) := rfl
|
||||
|
||||
theorem val_add (a b : Fin n) : (a + b).val = (a.val + b.val) % n := rfl
|
||||
|
||||
@[simp] protected theorem zero_add {n : Nat} [NeZero n] (i : Fin n) : (0 : Fin n) + i = i := by
|
||||
ext
|
||||
simp [Fin.add_def, Nat.mod_eq_of_lt i.2]
|
||||
|
||||
theorem val_add_one_of_lt {n : Nat} {i : Fin n.succ} (h : i < last _) : (i + 1).1 = i + 1 := by
|
||||
match n with
|
||||
| 0 => cases h
|
||||
@@ -371,10 +324,6 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
|
||||
|
||||
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h ⟨i, hn⟩ = ⟨i, h ▸ hn⟩ := rfl
|
||||
|
||||
@[simp] theorem cast_refl (n : Nat) (h : n = n) : cast h = id := by
|
||||
ext
|
||||
simp
|
||||
|
||||
@[simp] theorem cast_trans {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
|
||||
cast h' (cast h i) = cast (Eq.trans h h') i := rfl
|
||||
|
||||
@@ -434,7 +383,7 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
|
||||
|
||||
@[simp] theorem succ_last (n : Nat) : (last n).succ = last n.succ := rfl
|
||||
|
||||
@[simp] theorem succ_eq_last_succ {n : Nat} {i : Fin n.succ} :
|
||||
@[simp] theorem succ_eq_last_succ {n : Nat} (i : Fin n.succ) :
|
||||
i.succ = last (n + 1) ↔ i = last n := by rw [← succ_last, succ_inj]
|
||||
|
||||
@[simp] theorem castSucc_castLT (i : Fin (n + 1)) (h : (i : Nat) < n) :
|
||||
@@ -458,10 +407,10 @@ theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
|
||||
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
|
||||
simpa [lt_def] using h
|
||||
|
||||
@[simp] theorem castSucc_eq_zero_iff {a : Fin (n + 1)} : castSucc a = 0 ↔ a = 0 := by simp [Fin.ext_iff]
|
||||
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 ↔ a = 0 := by simp [Fin.ext_iff]
|
||||
|
||||
theorem castSucc_ne_zero_iff {a : Fin (n + 1)} : castSucc a ≠ 0 ↔ a ≠ 0 :=
|
||||
not_congr <| castSucc_eq_zero_iff
|
||||
theorem castSucc_ne_zero_iff (a : Fin (n + 1)) : castSucc a ≠ 0 ↔ a ≠ 0 :=
|
||||
not_congr <| castSucc_eq_zero_iff a
|
||||
|
||||
theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
|
||||
castSucc (Fin.succ j) = Fin.succ (castSucc j) := by simp [Fin.ext_iff]
|
||||
@@ -483,10 +432,6 @@ theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = castSucc i.succ
|
||||
|
||||
@[simp] theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
|
||||
|
||||
@[simp] theorem addNat_zero (n : Nat) (i : Fin n) : addNat i 0 = i := by
|
||||
ext
|
||||
simp
|
||||
|
||||
@[simp] theorem addNat_one {i : Fin n} : addNat i 1 = i.succ := rfl
|
||||
|
||||
theorem le_coe_addNat (m : Nat) (i : Fin n) : m ≤ addNat i m :=
|
||||
@@ -516,7 +461,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
|
||||
theorem le_coe_natAdd (m : Nat) (i : Fin n) : m ≤ natAdd m i := Nat.le_add_right ..
|
||||
|
||||
@[simp] theorem natAdd_zero {n : Nat} : natAdd 0 = cast (Nat.zero_add n).symm := by ext; simp
|
||||
theorem natAdd_zero {n : Nat} : natAdd 0 = cast (Nat.zero_add n).symm := by ext; simp
|
||||
|
||||
/-- For rewriting in the reverse direction, see `Fin.cast_natAdd_right`. -/
|
||||
theorem natAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
|
||||
@@ -554,19 +499,9 @@ theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
|
||||
|
||||
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
|
||||
|
||||
@[simp] theorem addNat_last (n : Nat) :
|
||||
addNat (last n) m = cast (by omega) (last (n + m)) := by
|
||||
ext
|
||||
simp
|
||||
|
||||
theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSucc (natAdd n i) :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem natAdd_eq_addNat (n : Nat) (i : Fin n) : Fin.natAdd n i = i.addNat n := by
|
||||
ext
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
|
||||
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
|
||||
|
||||
@@ -590,7 +525,7 @@ theorem pred_succ (i : Fin n) {h : i.succ ≠ 0} : i.succ.pred h = i := by
|
||||
cases i
|
||||
rfl
|
||||
|
||||
theorem pred_eq_iff_eq_succ {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) {j : Fin n} :
|
||||
theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i ≠ 0) (j : Fin n) :
|
||||
i.pred hi = j ↔ i = j.succ :=
|
||||
⟨fun h => by simp only [← h, Fin.succ_pred], fun h => by simp only [h, Fin.pred_succ]⟩
|
||||
|
||||
@@ -632,15 +567,6 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
@[simp] theorem subNat_mk {i : Nat} (h₁ : i < n + m) (h₂ : m ≤ i) :
|
||||
subNat m ⟨i, h₁⟩ h₂ = ⟨i - m, Nat.sub_lt_right_of_lt_add h₂ h₁⟩ := rfl
|
||||
|
||||
@[simp] theorem subNat_zero (i : Fin n) (h : 0 ≤ (i : Nat)): Fin.subNat 0 i h = i := by
|
||||
ext
|
||||
simp
|
||||
|
||||
@[simp] theorem subNat_one_succ (i : Fin (n + 1)) (h : 1 ≤ ↑i) : (subNat 1 i h).succ = i := by
|
||||
ext
|
||||
simp
|
||||
omega
|
||||
|
||||
@[simp] theorem pred_castSucc_succ (i : Fin n) :
|
||||
pred (castSucc i.succ) (Fin.ne_of_gt (castSucc_pos i.succ_pos)) = castSucc i := rfl
|
||||
|
||||
@@ -651,7 +577,7 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
|
||||
|
||||
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n ≤ i) :
|
||||
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [← cast_addNat]
|
||||
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [← cast_addNat]; rfl
|
||||
|
||||
/-! ### recursion and induction principles -/
|
||||
|
||||
@@ -819,13 +745,13 @@ theorem addCases_right {m n : Nat} {motive : Fin (m + n) → Sort _} {left right
|
||||
|
||||
/-! ### add -/
|
||||
|
||||
theorem ofNat'_add [NeZero n] (x : Nat) (y : Fin n) :
|
||||
Fin.ofNat' n x + y = Fin.ofNat' n (x + y.val) := by
|
||||
@[simp] theorem ofNat'_add (x : Nat) (lt : 0 < n) (y : Fin n) :
|
||||
Fin.ofNat' x lt + y = Fin.ofNat' (x + y.val) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.add_def]
|
||||
|
||||
theorem add_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
|
||||
x + Fin.ofNat' n y = Fin.ofNat' n (x.val + y) := by
|
||||
@[simp] theorem add_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
|
||||
x + Fin.ofNat' y lt = Fin.ofNat' (x.val + y) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.add_def]
|
||||
|
||||
@@ -834,21 +760,16 @@ theorem add_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
|
||||
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = ((n - b) + a) % n := by
|
||||
cases a; cases b; rfl
|
||||
|
||||
theorem ofNat'_sub [NeZero n] (x : Nat) (y : Fin n) :
|
||||
Fin.ofNat' n x - y = Fin.ofNat' n ((n - y.val) + x) := by
|
||||
@[simp] theorem ofNat'_sub (x : Nat) (lt : 0 < n) (y : Fin n) :
|
||||
Fin.ofNat' x lt - y = Fin.ofNat' ((n - y.val) + x) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.sub_def]
|
||||
|
||||
theorem sub_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
|
||||
x - Fin.ofNat' n y = Fin.ofNat' n ((n - y % n) + x.val) := by
|
||||
@[simp] theorem sub_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
|
||||
x - Fin.ofNat' y lt = Fin.ofNat' ((n - y % n) + x.val) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.sub_def]
|
||||
|
||||
@[simp] protected theorem sub_self [NeZero n] {x : Fin n} : x - x = 0 := by
|
||||
ext
|
||||
rw [Fin.sub_def]
|
||||
simp
|
||||
|
||||
private theorem _root_.Nat.mod_eq_sub_of_lt_two_mul {x n} (h₁ : n ≤ x) (h₂ : x < 2 * n) :
|
||||
x % n = x - n := by
|
||||
rw [Nat.mod_eq, if_pos (by omega), Nat.mod_eq_of_lt (by omega)]
|
||||
|
||||
@@ -72,35 +72,21 @@ instance floatDecLt (a b : Float) : Decidable (a < b) := Float.decLt a b
|
||||
instance floatDecLe (a b : Float) : Decidable (a ≤ b) := Float.decLe a b
|
||||
|
||||
@[extern "lean_float_to_string"] opaque Float.toString : Float → String
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns 0.
|
||||
If larger than the maximum value for UInt8 (including Inf), returns maximum value of UInt8
|
||||
(i.e. UInt8.size - 1).
|
||||
-/
|
||||
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt8, returns 0. -/
|
||||
@[extern "lean_float_to_uint8"] opaque Float.toUInt8 : Float → UInt8
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns 0.
|
||||
If larger than the maximum value for UInt16 (including Inf), returns maximum value of UInt16
|
||||
(i.e. UInt16.size - 1).
|
||||
-/
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt16, returns 0. -/
|
||||
@[extern "lean_float_to_uint16"] opaque Float.toUInt16 : Float → UInt16
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns 0.
|
||||
If larger than the maximum value for UInt32 (including Inf), returns maximum value of UInt32
|
||||
(i.e. UInt32.size - 1).
|
||||
-/
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt32, returns 0. -/
|
||||
@[extern "lean_float_to_uint32"] opaque Float.toUInt32 : Float → UInt32
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns 0.
|
||||
If larger than the maximum value for UInt64 (including Inf), returns maximum value of UInt64
|
||||
(i.e. UInt64.size - 1).
|
||||
-/
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt64, returns 0. -/
|
||||
@[extern "lean_float_to_uint64"] opaque Float.toUInt64 : Float → UInt64
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns 0.
|
||||
If larger than the maximum value for USize (including Inf), returns maximum value of USize
|
||||
(i.e. USize.size - 1; Note that this value is platform dependent).
|
||||
-/
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for USize, returns 0. -/
|
||||
@[extern "lean_float_to_usize"] opaque Float.toUSize : Float → USize
|
||||
|
||||
@[extern "lean_float_isnan"] opaque Float.isNaN : Float → Bool
|
||||
|
||||
@@ -10,6 +10,5 @@ import Init.Data.Int.DivMod
|
||||
import Init.Data.Int.DivModLemmas
|
||||
import Init.Data.Int.Gcd
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.Order
|
||||
import Init.Data.Int.Pow
|
||||
|
||||
@@ -8,7 +8,7 @@ The integers, with addition, multiplication, and subtraction.
|
||||
prelude
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Nat.Div
|
||||
|
||||
import Init.Data.List.Basic
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
open Nat
|
||||
|
||||
@@ -322,8 +322,8 @@ protected def pow (m : Int) : Nat → Int
|
||||
| 0 => 1
|
||||
| succ n => Int.pow m n * m
|
||||
|
||||
instance : NatPow Int where
|
||||
pow := Int.pow
|
||||
instance : HPow Int Nat Int where
|
||||
hPow := Int.pow
|
||||
|
||||
instance : LawfulBEq Int where
|
||||
eq_of_beq h := by simp [BEq.beq] at h; assumption
|
||||
|
||||
@@ -16,99 +16,83 @@ There are three main conventions for integer division,
|
||||
referred here as the E, F, T rounding conventions.
|
||||
All three pairs satisfy the identity `x % y + (x / y) * y = x` unconditionally,
|
||||
and satisfy `x / 0 = 0` and `x % 0 = x`.
|
||||
|
||||
### Historical notes
|
||||
In early versions of Lean, the typeclasses provided by `/` and `%`
|
||||
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.
|
||||
|
||||
However we decided it was better to use `ediv` and `emod`,
|
||||
as they are consistent with the conventions used in SMTLib, and Mathlib,
|
||||
and often mathematical reasoning is easier with these conventions.
|
||||
|
||||
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
|
||||
In September 2024, we decided to do this rename (with deprecations in place),
|
||||
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
|
||||
ever need to use these functions and their associated lemmas.
|
||||
-/
|
||||
|
||||
/-! ### T-rounding division -/
|
||||
|
||||
/--
|
||||
`tdiv` uses the [*"T-rounding"*][t-rounding]
|
||||
`div` uses the [*"T-rounding"*][t-rounding]
|
||||
(**T**runcation-rounding) convention, meaning that it rounds toward
|
||||
zero. Also note that division by zero is defined to equal zero.
|
||||
|
||||
The relation between integer division and modulo is found in
|
||||
`Int.tmod_add_tdiv` which states that
|
||||
`tmod a b + b * (tdiv a b) = a`, unconditionally.
|
||||
`Int.mod_add_div` which states that
|
||||
`a % b + b * (a / b) = a`, unconditionally.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862 [theo
|
||||
mod_add_div]:
|
||||
https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int).tdiv (0 : Int) -- 0
|
||||
#eval (0 : Int).tdiv (7 : Int) -- 0
|
||||
#eval (7 : Int) / (0 : Int) -- 0
|
||||
#eval (0 : Int) / (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).tdiv (6 : Int) -- 2
|
||||
#eval (12 : Int).tdiv (-6 : Int) -- -2
|
||||
#eval (-12 : Int).tdiv (6 : Int) -- -2
|
||||
#eval (-12 : Int).tdiv (-6 : Int) -- 2
|
||||
#eval (12 : Int) / (6 : Int) -- 2
|
||||
#eval (12 : Int) / (-6 : Int) -- -2
|
||||
#eval (-12 : Int) / (6 : Int) -- -2
|
||||
#eval (-12 : Int) / (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int).tdiv (7 : Int) -- 1
|
||||
#eval (12 : Int).tdiv (-7 : Int) -- -1
|
||||
#eval (-12 : Int).tdiv (7 : Int) -- -1
|
||||
#eval (-12 : Int).tdiv (-7 : Int) -- 1
|
||||
#eval (12 : Int) / (7 : Int) -- 1
|
||||
#eval (12 : Int) / (-7 : Int) -- -1
|
||||
#eval (-12 : Int) / (7 : Int) -- -1
|
||||
#eval (-12 : Int) / (-7 : Int) -- 1
|
||||
```
|
||||
|
||||
Implemented by efficient native code.
|
||||
-/
|
||||
@[extern "lean_int_div"]
|
||||
def tdiv : (@& Int) → (@& Int) → Int
|
||||
def div : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m / n)
|
||||
| ofNat m, -[n +1] => -ofNat (m / succ n)
|
||||
| -[m +1], ofNat n => -ofNat (succ m / n)
|
||||
| -[m +1], -[n +1] => ofNat (succ m / succ n)
|
||||
|
||||
@[deprecated tdiv (since := "2024-09-11")] abbrev div := tdiv
|
||||
|
||||
/-- Integer modulo. This function uses the
|
||||
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
|
||||
to pair with `Int.tdiv`, meaning that `tmod a b + b * (tdiv a b) = a`
|
||||
unconditionally (see [`Int.tmod_add_tdiv`][theo tmod_add_tdiv]). In
|
||||
to pair with `Int.div`, meaning that `a % b + b * (a / b) = a`
|
||||
unconditionally (see [`Int.mod_add_div`][theo mod_add_div]). In
|
||||
particular, `a % 0 = a`.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
|
||||
[theo mod_add_div]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int).tmod (0 : Int) -- 7
|
||||
#eval (0 : Int).tmod (7 : Int) -- 0
|
||||
#eval (7 : Int) % (0 : Int) -- 7
|
||||
#eval (0 : Int) % (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).tmod (6 : Int) -- 0
|
||||
#eval (12 : Int).tmod (-6 : Int) -- 0
|
||||
#eval (-12 : Int).tmod (6 : Int) -- 0
|
||||
#eval (-12 : Int).tmod (-6 : Int) -- 0
|
||||
#eval (12 : Int) % (6 : Int) -- 0
|
||||
#eval (12 : Int) % (-6 : Int) -- 0
|
||||
#eval (-12 : Int) % (6 : Int) -- 0
|
||||
#eval (-12 : Int) % (-6 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).tmod (7 : Int) -- 5
|
||||
#eval (12 : Int).tmod (-7 : Int) -- 5
|
||||
#eval (-12 : Int).tmod (7 : Int) -- -5
|
||||
#eval (-12 : Int).tmod (-7 : Int) -- -5
|
||||
#eval (12 : Int) % (7 : Int) -- 5
|
||||
#eval (12 : Int) % (-7 : Int) -- 5
|
||||
#eval (-12 : Int) % (7 : Int) -- 2
|
||||
#eval (-12 : Int) % (-7 : Int) -- 2
|
||||
```
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
@[extern "lean_int_mod"]
|
||||
def tmod : (@& Int) → (@& Int) → Int
|
||||
def mod : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m % n)
|
||||
| ofNat m, -[n +1] => ofNat (m % succ n)
|
||||
| -[m +1], ofNat n => -ofNat (succ m % n)
|
||||
| -[m +1], -[n +1] => -ofNat (succ m % succ n)
|
||||
|
||||
@[deprecated tmod (since := "2024-09-11")] abbrev mod := tmod
|
||||
|
||||
/-! ### F-rounding division
|
||||
This pair satisfies `fdiv x y = floor (x / y)`.
|
||||
-/
|
||||
@@ -117,22 +101,6 @@ This pair satisfies `fdiv x y = floor (x / y)`.
|
||||
Integer division. This version of division uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int).fdiv (0 : Int) -- 0
|
||||
#eval (0 : Int).fdiv (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).fdiv (6 : Int) -- 2
|
||||
#eval (12 : Int).fdiv (-6 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (6 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int).fdiv (7 : Int) -- 1
|
||||
#eval (12 : Int).fdiv (-7 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (7 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (-7 : Int) -- 1
|
||||
```
|
||||
-/
|
||||
def fdiv : Int → Int → Int
|
||||
| 0, _ => 0
|
||||
@@ -146,23 +114,6 @@ def fdiv : Int → Int → Int
|
||||
Integer modulus. This version of `Int.mod` uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int).fmod (0 : Int) -- 7
|
||||
#eval (0 : Int).fmod (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).fmod (6 : Int) -- 0
|
||||
#eval (12 : Int).fmod (-6 : Int) -- 0
|
||||
#eval (-12 : Int).fmod (6 : Int) -- 0
|
||||
#eval (-12 : Int).fmod (-6 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).fmod (7 : Int) -- 5
|
||||
#eval (12 : Int).fmod (-7 : Int) -- -2
|
||||
#eval (-12 : Int).fmod (7 : Int) -- 2
|
||||
#eval (-12 : Int).fmod (-7 : Int) -- -5
|
||||
```
|
||||
-/
|
||||
def fmod : Int → Int → Int
|
||||
| 0, _ => 0
|
||||
@@ -179,26 +130,6 @@ This pair satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`.
|
||||
Integer division. This version of `Int.div` uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
|
||||
This is the function powering the `/` notation on integers.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int) / (0 : Int) -- 0
|
||||
#eval (0 : Int) / (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) / (6 : Int) -- 2
|
||||
#eval (12 : Int) / (-6 : Int) -- -2
|
||||
#eval (-12 : Int) / (6 : Int) -- -2
|
||||
#eval (-12 : Int) / (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int) / (7 : Int) -- 1
|
||||
#eval (12 : Int) / (-7 : Int) -- -1
|
||||
#eval (-12 : Int) / (7 : Int) -- -2
|
||||
#eval (-12 : Int) / (-7 : Int) -- 2
|
||||
```
|
||||
|
||||
Implemented by efficient native code.
|
||||
-/
|
||||
@[extern "lean_int_ediv"]
|
||||
def ediv : (@& Int) → (@& Int) → Int
|
||||
@@ -212,26 +143,6 @@ def ediv : (@& Int) → (@& Int) → Int
|
||||
Integer modulus. This version of `Int.mod` uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
|
||||
This is the function powering the `%` notation on integers.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int) % (0 : Int) -- 7
|
||||
#eval (0 : Int) % (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (6 : Int) -- 0
|
||||
#eval (12 : Int) % (-6 : Int) -- 0
|
||||
#eval (-12 : Int) % (6 : Int) -- 0
|
||||
#eval (-12 : Int) % (-6 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (7 : Int) -- 5
|
||||
#eval (12 : Int) % (-7 : Int) -- 5
|
||||
#eval (-12 : Int) % (7 : Int) -- 2
|
||||
#eval (-12 : Int) % (-7 : Int) -- 2
|
||||
```
|
||||
|
||||
Implemented by efficient native code.
|
||||
-/
|
||||
@[extern "lean_int_emod"]
|
||||
def emod : (@& Int) → (@& Int) → Int
|
||||
@@ -249,9 +160,7 @@ instance : Mod Int where
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_ediv (m n : Nat) : (↑(m / n) : Int) = ↑m / ↑n := rfl
|
||||
|
||||
theorem ofNat_tdiv (m n : Nat) : ↑(m / n) = tdiv ↑m ↑n := rfl
|
||||
|
||||
@[deprecated ofNat_tdiv (since := "2024-09-11")] abbrev ofNat_div := ofNat_tdiv
|
||||
theorem ofNat_div (m n : Nat) : ↑(m / n) = div ↑m ↑n := rfl
|
||||
|
||||
theorem ofNat_fdiv : ∀ m n : Nat, ↑(m / n) = fdiv ↑m ↑n
|
||||
| 0, _ => by simp [fdiv]
|
||||
|
||||
@@ -14,6 +14,9 @@ import Init.RCases
|
||||
# Lemmas about integer division needed to bootstrap `omega`.
|
||||
-/
|
||||
|
||||
-- Remove after the next stage0 update
|
||||
set_option allowUnsafeReducibility true
|
||||
|
||||
open Nat (succ)
|
||||
|
||||
namespace Int
|
||||
@@ -54,7 +57,7 @@ protected theorem dvd_mul_right (a b : Int) : a ∣ a * b := ⟨_, rfl⟩
|
||||
|
||||
protected theorem dvd_mul_left (a b : Int) : b ∣ a * b := ⟨_, Int.mul_comm ..⟩
|
||||
|
||||
@[simp] protected theorem neg_dvd {a b : Int} : -a ∣ b ↔ a ∣ b := by
|
||||
protected theorem neg_dvd {a b : Int} : -a ∣ b ↔ a ∣ b := by
|
||||
constructor <;> exact fun ⟨k, e⟩ =>
|
||||
⟨-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]⟩
|
||||
|
||||
@@ -137,12 +140,12 @@ theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 ≤ b) (H' : a * b = 1) : b
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => rfl
|
||||
|
||||
@[simp] protected theorem zero_tdiv : ∀ b : Int, tdiv 0 b = 0
|
||||
@[simp] protected theorem zero_div : ∀ b : Int, div 0 b = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => show -ofNat _ = _ by simp
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem tdiv_zero : ∀ a : Int, tdiv a 0 = 0
|
||||
@[simp] protected theorem div_zero : ∀ a : Int, div a 0 = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => rfl
|
||||
|
||||
@@ -156,17 +159,16 @@ unseal Nat.div in
|
||||
|
||||
/-! ### div equivalences -/
|
||||
|
||||
theorem tdiv_eq_ediv : ∀ {a b : Int}, 0 ≤ a → 0 ≤ b → a.tdiv b = a / b
|
||||
theorem div_eq_ediv : ∀ {a b : Int}, 0 ≤ a → 0 ≤ b → a.div b = a / b
|
||||
| 0, _, _, _ | _, 0, _, _ => by simp
|
||||
| succ _, succ _, _, _ => rfl
|
||||
|
||||
|
||||
theorem fdiv_eq_ediv : ∀ (a : Int) {b : Int}, 0 ≤ b → fdiv a b = a / b
|
||||
| 0, _, _ | -[_+1], 0, _ => by simp
|
||||
| succ _, ofNat _, _ | -[_+1], succ _, _ => rfl
|
||||
|
||||
theorem fdiv_eq_tdiv {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fdiv a b = tdiv a b :=
|
||||
tdiv_eq_ediv Ha Hb ▸ fdiv_eq_ediv _ Hb
|
||||
theorem fdiv_eq_div {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fdiv a b = div a b :=
|
||||
div_eq_ediv Ha Hb ▸ fdiv_eq_ediv _ Hb
|
||||
|
||||
/-! ### mod zero -/
|
||||
|
||||
@@ -176,9 +178,9 @@ theorem fdiv_eq_tdiv {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fdiv a b = tdiv
|
||||
| ofNat _ => congrArg ofNat <| Nat.mod_zero _
|
||||
| -[_+1] => congrArg negSucc <| Nat.mod_zero _
|
||||
|
||||
@[simp] theorem zero_tmod (b : Int) : tmod 0 b = 0 := by cases b <;> simp [tmod]
|
||||
@[simp] theorem zero_mod (b : Int) : mod 0 b = 0 := by cases b <;> simp [mod]
|
||||
|
||||
@[simp] theorem tmod_zero : ∀ a : Int, tmod a 0 = a
|
||||
@[simp] theorem mod_zero : ∀ a : Int, mod a 0 = a
|
||||
| ofNat _ => congrArg ofNat <| Nat.mod_zero _
|
||||
| -[_+1] => congrArg (fun n => -ofNat n) <| Nat.mod_zero _
|
||||
|
||||
@@ -194,7 +196,7 @@ theorem fdiv_eq_tdiv {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fdiv a b = tdiv
|
||||
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := rfl
|
||||
|
||||
|
||||
/-! ### mod definitions -/
|
||||
/-! ### mod definitiions -/
|
||||
|
||||
theorem emod_add_ediv : ∀ a b : Int, a % b + b * (a / b) = a
|
||||
| ofNat _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
|
||||
@@ -222,7 +224,7 @@ theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
|
||||
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
|
||||
rw [← Int.add_sub_cancel (a % b), emod_add_ediv]
|
||||
|
||||
theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
|
||||
theorem mod_add_div : ∀ a b : Int, mod a b + b * (a.div b) = a
|
||||
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
|
||||
| ofNat m, -[n+1] => by
|
||||
show (m % succ n + -↑(succ n) * -↑(m / succ n) : Int) = m
|
||||
@@ -239,17 +241,17 @@ theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
|
||||
rw [Int.neg_mul, ← Int.neg_add]
|
||||
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
|
||||
|
||||
theorem tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
|
||||
rw [Int.add_comm]; apply tmod_add_tdiv ..
|
||||
theorem div_add_mod (a b : Int) : b * a.div b + mod a b = a := by
|
||||
rw [Int.add_comm]; apply mod_add_div ..
|
||||
|
||||
theorem tmod_add_tdiv' (m k : Int) : tmod m k + m.tdiv k * k = m := by
|
||||
rw [Int.mul_comm]; apply tmod_add_tdiv
|
||||
theorem mod_add_div' (m k : Int) : mod m k + m.div k * k = m := by
|
||||
rw [Int.mul_comm]; apply mod_add_div
|
||||
|
||||
theorem tdiv_add_tmod' (m k : Int) : m.tdiv k * k + tmod m k = m := by
|
||||
rw [Int.mul_comm]; apply tdiv_add_tmod
|
||||
theorem div_add_mod' (m k : Int) : m.div k * k + mod m k = m := by
|
||||
rw [Int.mul_comm]; apply div_add_mod
|
||||
|
||||
theorem tmod_def (a b : Int) : tmod a b = a - b * a.tdiv b := by
|
||||
rw [← Int.add_sub_cancel (tmod a b), tmod_add_tdiv]
|
||||
theorem mod_def (a b : Int) : mod a b = a - b * a.div b := by
|
||||
rw [← Int.add_sub_cancel (mod a b), mod_add_div]
|
||||
|
||||
theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
|
||||
| 0, ofNat _ | 0, -[_+1] => congrArg ofNat <| by simp
|
||||
@@ -279,11 +281,11 @@ theorem fmod_def (a b : Int) : a.fmod b = a - b * a.fdiv b := by
|
||||
theorem fmod_eq_emod (a : Int) {b : Int} (hb : 0 ≤ b) : fmod a b = a % b := by
|
||||
simp [fmod_def, emod_def, fdiv_eq_ediv _ hb]
|
||||
|
||||
theorem tmod_eq_emod {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : tmod a b = a % b := by
|
||||
simp [emod_def, tmod_def, tdiv_eq_ediv ha hb]
|
||||
theorem mod_eq_emod {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : mod a b = a % b := by
|
||||
simp [emod_def, mod_def, div_eq_ediv ha hb]
|
||||
|
||||
theorem fmod_eq_tmod {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fmod a b = tmod a b :=
|
||||
tmod_eq_emod Ha Hb ▸ fmod_eq_emod _ Hb
|
||||
theorem fmod_eq_mod {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fmod a b = mod a b :=
|
||||
mod_eq_emod Ha Hb ▸ fmod_eq_emod _ Hb
|
||||
|
||||
/-! ### `/` ediv -/
|
||||
|
||||
@@ -298,7 +300,7 @@ theorem ediv_neg' {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
|
||||
|
||||
protected theorem div_def (a b : Int) : a / b = Int.ediv a b := rfl
|
||||
|
||||
theorem negSucc_ediv (m : Nat) {b : Int} (H : 0 < b) : -[m+1] / b = -(ediv m b + 1) :=
|
||||
theorem negSucc_ediv (m : Nat) {b : Int} (H : 0 < b) : -[m+1] / b = -(div m b + 1) :=
|
||||
match b, eq_succ_of_zero_lt H with
|
||||
| _, ⟨_, rfl⟩ => rfl
|
||||
|
||||
@@ -306,22 +308,6 @@ theorem ediv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a / b :=
|
||||
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_zero_le _
|
||||
|
||||
theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0) : 0 ≤ a / b := by
|
||||
match a, b with
|
||||
| ofNat a, b =>
|
||||
match Int.le_antisymm Ha (ofNat_zero_le a) with
|
||||
| h1 =>
|
||||
rw [h1, zero_ediv]
|
||||
exact Int.le_refl 0
|
||||
| a, ofNat b =>
|
||||
match Int.le_antisymm Hb (ofNat_zero_le b) with
|
||||
| h1 =>
|
||||
rw [h1, Int.ediv_zero]
|
||||
exact Int.le_refl 0
|
||||
| negSucc a, negSucc b =>
|
||||
rw [Int.div_def, ediv]
|
||||
exact le_add_one (ediv_nonneg (ofNat_zero_le a) (Int.le_trans (ofNat_zero_le b) (le.intro 1 rfl)))
|
||||
|
||||
theorem ediv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a / b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. ▸ Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
@@ -371,7 +357,6 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c ∣ a) : (a + b) / c = a / c +
|
||||
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a ≠ 0) : (a * b) / a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_ediv_cancel _ H
|
||||
|
||||
|
||||
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0 := by
|
||||
rw [Int.div_def]
|
||||
match b, h with
|
||||
@@ -469,12 +454,6 @@ theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
|
||||
@[simp] theorem add_mul_emod_self_left (a b c : Int) : (a + b * c) % b = a % b := by
|
||||
rw [Int.mul_comm, Int.add_mul_emod_self]
|
||||
|
||||
@[simp] theorem add_neg_mul_emod_self {a b c : Int} : (a + -(b * c)) % c = a % c := by
|
||||
rw [Int.neg_mul_eq_neg_mul, add_mul_emod_self]
|
||||
|
||||
@[simp] theorem add_neg_mul_emod_self_left {a b c : Int} : (a + -(b * c)) % b = a % b := by
|
||||
rw [Int.neg_mul_eq_mul_neg, add_mul_emod_self_left]
|
||||
|
||||
@[simp] theorem add_emod_self {a b : Int} : (a + b) % b = a % b := by
|
||||
have := add_mul_emod_self_left a b 1; rwa [Int.mul_one] at this
|
||||
|
||||
@@ -519,12 +498,9 @@ theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
|
||||
Int.mul_assoc, Int.mul_assoc, ← Int.mul_add n _ _, add_mul_emod_self_left,
|
||||
← Int.mul_assoc, add_mul_emod_self]
|
||||
|
||||
@[simp] theorem emod_self {a : Int} : a % a = 0 := by
|
||||
@[local simp] theorem emod_self {a : Int} : a % a = 0 := by
|
||||
have := mul_emod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp] theorem neg_emod_self (a : Int) : -a % a = 0 := by
|
||||
rw [neg_emod, Int.sub_self, zero_emod]
|
||||
|
||||
@[simp] theorem emod_emod_of_dvd (n : Int) {m k : Int}
|
||||
(h : m ∣ k) : (n % k) % m = n % m := by
|
||||
conv => rhs; rw [← emod_add_ediv n k]
|
||||
@@ -617,17 +593,9 @@ theorem dvd_emod_sub_self {x : Int} {m : Nat} : (m : Int) ∣ x % m - x := by
|
||||
theorem emod_eq_zero_of_dvd : ∀ {a b : Int}, a ∣ b → b % a = 0
|
||||
| _, _, ⟨_, rfl⟩ => mul_emod_right ..
|
||||
|
||||
theorem dvd_iff_emod_eq_zero {a b : Int} : a ∣ b ↔ b % a = 0 :=
|
||||
theorem dvd_iff_emod_eq_zero (a b : Int) : a ∣ b ↔ b % a = 0 :=
|
||||
⟨emod_eq_zero_of_dvd, dvd_of_emod_eq_zero⟩
|
||||
|
||||
@[simp] theorem neg_mul_emod_left (a b : Int) : -(a * b) % b = 0 := by
|
||||
rw [← dvd_iff_emod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_left a b
|
||||
|
||||
@[simp] theorem neg_mul_emod_right (a b : Int) : -(a * b) % a = 0 := by
|
||||
rw [← dvd_iff_emod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_right a b
|
||||
|
||||
instance decidableDvd : DecidableRel (α := Int) (· ∣ ·) := fun _ _ =>
|
||||
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
|
||||
|
||||
@@ -652,12 +620,6 @@ theorem neg_ediv_of_dvd : ∀ {a b : Int}, b ∣ a → (-a) / b = -(a / b)
|
||||
· simp [bz]
|
||||
· rw [Int.neg_mul_eq_mul_neg, Int.mul_ediv_cancel_left _ bz, Int.mul_ediv_cancel_left _ bz]
|
||||
|
||||
@[simp] theorem neg_mul_ediv_cancel (a b : Int) (h : b ≠ 0) : -(a * b) / b = -a := by
|
||||
rw [neg_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ h]
|
||||
|
||||
@[simp] theorem neg_mul_ediv_cancel_left (a b : Int) (h : a ≠ 0) : -(a * b) / a = -b := by
|
||||
rw [neg_ediv_of_dvd (Int.dvd_mul_right a b), mul_ediv_cancel_left _ h]
|
||||
|
||||
theorem sub_ediv_of_dvd (a : Int) {b c : Int}
|
||||
(hcb : c ∣ b) : (a - b) / c = a / c - b / c := by
|
||||
rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg, Int.add_ediv_of_dvd_right (Int.dvd_neg.2 hcb)]
|
||||
@@ -673,22 +635,13 @@ theorem sub_ediv_of_dvd (a : Int) {b c : Int}
|
||||
@[simp] protected theorem ediv_self {a : Int} (H : a ≠ 0) : a / a = 1 := by
|
||||
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp] protected theorem neg_ediv_self (a : Int) (h : a ≠ 0) : (-a) / a = -1 := by
|
||||
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
|
||||
|
||||
@[simp]
|
||||
theorem emod_sub_cancel (x y : Int): (x - y) % y = x % y := by
|
||||
theorem emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
|
||||
by_cases h : y = 0
|
||||
· simp [h]
|
||||
· simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]
|
||||
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
|
||||
|
||||
@[simp] theorem add_neg_emod_self (a b : Int) : (a + -b) % b = a % b := by
|
||||
rw [← Int.sub_eq_add_neg, emod_sub_cancel]
|
||||
|
||||
@[simp] theorem neg_add_emod_self (a b : Int) : (-a + b) % a = b % a := by
|
||||
rw [Int.add_comm, add_neg_emod_self]
|
||||
|
||||
/-- If `a % b = c` then `b` divides `a - c`. -/
|
||||
theorem dvd_sub_of_emod_eq {a b c : Int} (h : a % b = c) : b ∣ a - c := by
|
||||
have hx : (a % b) % b = c % b := by
|
||||
@@ -801,7 +754,7 @@ protected theorem lt_ediv_of_mul_lt {a b c : Int} (H1 : 0 ≤ b) (H2 : b ∣ c)
|
||||
a < c / b :=
|
||||
Int.lt_of_not_ge <| mt (Int.le_mul_of_ediv_le H1 H2) (Int.not_le_of_gt H3)
|
||||
|
||||
protected theorem lt_ediv_iff_mul_lt {a b : Int} {c : Int} (H : 0 < c) (H' : c ∣ b) :
|
||||
protected theorem lt_ediv_iff_mul_lt {a b : Int} (c : Int) (H : 0 < c) (H' : c ∣ b) :
|
||||
a < b / c ↔ a * c < b :=
|
||||
⟨Int.mul_lt_of_lt_ediv H, Int.lt_ediv_of_mul_lt (Int.le_of_lt H) H'⟩
|
||||
|
||||
@@ -813,191 +766,179 @@ theorem ediv_eq_ediv_of_mul_eq_mul {a b c d : Int}
|
||||
Int.ediv_eq_of_eq_mul_right H3 <| by
|
||||
rw [← Int.mul_ediv_assoc _ H2]; exact (Int.ediv_eq_of_eq_mul_left H4 H5.symm).symm
|
||||
|
||||
/-! ### tdiv -/
|
||||
/-! ### div -/
|
||||
|
||||
@[simp] protected theorem tdiv_one : ∀ a : Int, a.tdiv 1 = a
|
||||
@[simp] protected theorem div_one : ∀ a : Int, a.div 1 = a
|
||||
| (n:Nat) => congrArg ofNat (Nat.div_one _)
|
||||
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
|
||||
| -[n+1] => by simp [Int.div, neg_ofNat_succ]; rfl
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem tdiv_neg : ∀ a b : Int, a.tdiv (-b) = -(a.tdiv b)
|
||||
@[simp] protected theorem div_neg : ∀ a b : Int, a.div (-b) = -(a.div b)
|
||||
| ofNat m, 0 => show ofNat (m / 0) = -↑(m / 0) by rw [Nat.div_zero]; rfl
|
||||
| ofNat m, -[n+1] | -[m+1], succ n => (Int.neg_neg _).symm
|
||||
| ofNat m, succ n | -[m+1], 0 | -[m+1], -[n+1] => rfl
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem neg_tdiv : ∀ a b : Int, (-a).tdiv b = -(a.tdiv b)
|
||||
@[simp] protected theorem neg_div : ∀ a b : Int, (-a).div b = -(a.div b)
|
||||
| 0, n => by simp [Int.neg_zero]
|
||||
| succ m, (n:Nat) | -[m+1], 0 | -[m+1], -[n+1] => rfl
|
||||
| succ m, -[n+1] | -[m+1], succ n => (Int.neg_neg _).symm
|
||||
|
||||
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
|
||||
simp [Int.tdiv_neg, Int.neg_tdiv, Int.neg_neg]
|
||||
protected theorem neg_div_neg (a b : Int) : (-a).div (-b) = a.div b := by
|
||||
simp [Int.div_neg, Int.neg_div, Int.neg_neg]
|
||||
|
||||
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a.tdiv b :=
|
||||
protected theorem div_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a.div b :=
|
||||
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_zero_le _
|
||||
|
||||
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a.tdiv b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. ▸ Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
protected theorem div_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a.div b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.div_neg .. ▸ Int.div_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.tdiv b = 0 :=
|
||||
theorem div_eq_zero_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.div b = 0 :=
|
||||
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
|
||||
|
||||
@[simp] protected theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b ≠ 0) : (a * b).tdiv b = a :=
|
||||
have : ∀ {a b : Nat}, (b : Int) ≠ 0 → (tdiv (a * b) b : Int) = a := fun H => by
|
||||
rw [← ofNat_mul, ← ofNat_tdiv,
|
||||
@[simp] protected theorem mul_div_cancel (a : Int) {b : Int} (H : b ≠ 0) : (a * b).div b = a :=
|
||||
have : ∀ {a b : Nat}, (b : Int) ≠ 0 → (div (a * b) b : Int) = a := fun H => by
|
||||
rw [← ofNat_mul, ← ofNat_div,
|
||||
Nat.mul_div_cancel _ <| Nat.pos_of_ne_zero <| Int.ofNat_ne_zero.1 H]
|
||||
match a, b, a.eq_nat_or_neg, b.eq_nat_or_neg with
|
||||
| _, _, ⟨a, .inl rfl⟩, ⟨b, .inl rfl⟩ => this H
|
||||
| _, _, ⟨a, .inl rfl⟩, ⟨b, .inr rfl⟩ => by
|
||||
rw [Int.mul_neg, Int.neg_tdiv, Int.tdiv_neg, Int.neg_neg,
|
||||
rw [Int.mul_neg, Int.neg_div, Int.div_neg, Int.neg_neg,
|
||||
this (Int.neg_ne_zero.1 H)]
|
||||
| _, _, ⟨a, .inr rfl⟩, ⟨b, .inl rfl⟩ => by rw [Int.neg_mul, Int.neg_tdiv, this H]
|
||||
| _, _, ⟨a, .inr rfl⟩, ⟨b, .inl rfl⟩ => by rw [Int.neg_mul, Int.neg_div, this H]
|
||||
| _, _, ⟨a, .inr rfl⟩, ⟨b, .inr rfl⟩ => by
|
||||
rw [Int.neg_mul_neg, Int.tdiv_neg, this (Int.neg_ne_zero.1 H)]
|
||||
rw [Int.neg_mul_neg, Int.div_neg, this (Int.neg_ne_zero.1 H)]
|
||||
|
||||
@[simp] protected theorem mul_tdiv_cancel_left (b : Int) (H : a ≠ 0) : (a * b).tdiv a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_tdiv_cancel _ H
|
||||
@[simp] protected theorem mul_div_cancel_left (b : Int) (H : a ≠ 0) : (a * b).div a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_div_cancel _ H
|
||||
|
||||
@[simp] protected theorem tdiv_self {a : Int} (H : a ≠ 0) : a.tdiv a = 1 := by
|
||||
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
|
||||
@[simp] protected theorem div_self {a : Int} (H : a ≠ 0) : a.div a = 1 := by
|
||||
have := Int.mul_div_cancel 1 H; rwa [Int.one_mul] at this
|
||||
|
||||
theorem mul_tdiv_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : b * (a.tdiv b) = a := by
|
||||
have := tmod_add_tdiv a b; rwa [H, Int.zero_add] at this
|
||||
theorem mul_div_cancel_of_mod_eq_zero {a b : Int} (H : a.mod b = 0) : b * (a.div b) = a := by
|
||||
have := mod_add_div a b; rwa [H, Int.zero_add] at this
|
||||
|
||||
theorem tdiv_mul_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : a.tdiv b * b = a := by
|
||||
rw [Int.mul_comm, mul_tdiv_cancel_of_tmod_eq_zero H]
|
||||
theorem div_mul_cancel_of_mod_eq_zero {a b : Int} (H : a.mod b = 0) : a.div b * b = a := by
|
||||
rw [Int.mul_comm, mul_div_cancel_of_mod_eq_zero H]
|
||||
|
||||
theorem dvd_of_tmod_eq_zero {a b : Int} (H : tmod b a = 0) : a ∣ b :=
|
||||
⟨b.tdiv a, (mul_tdiv_cancel_of_tmod_eq_zero H).symm⟩
|
||||
theorem dvd_of_mod_eq_zero {a b : Int} (H : mod b a = 0) : a ∣ b :=
|
||||
⟨b.div a, (mul_div_cancel_of_mod_eq_zero H).symm⟩
|
||||
|
||||
protected theorem mul_tdiv_assoc (a : Int) : ∀ {b c : Int}, c ∣ b → (a * b).tdiv c = a * (b.tdiv c)
|
||||
protected theorem mul_div_assoc (a : Int) : ∀ {b c : Int}, c ∣ b → (a * b).div c = a * (b.div c)
|
||||
| _, c, ⟨d, rfl⟩ =>
|
||||
if cz : c = 0 then by simp [cz, Int.mul_zero] else by
|
||||
rw [Int.mul_left_comm, Int.mul_tdiv_cancel_left _ cz, Int.mul_tdiv_cancel_left _ cz]
|
||||
rw [Int.mul_left_comm, Int.mul_div_cancel_left _ cz, Int.mul_div_cancel_left _ cz]
|
||||
|
||||
protected theorem mul_tdiv_assoc' (b : Int) {a c : Int} (h : c ∣ a) :
|
||||
(a * b).tdiv c = a.tdiv c * b := by
|
||||
rw [Int.mul_comm, Int.mul_tdiv_assoc _ h, Int.mul_comm]
|
||||
protected theorem mul_div_assoc' (b : Int) {a c : Int} (h : c ∣ a) :
|
||||
(a * b).div c = a.div c * b := by
|
||||
rw [Int.mul_comm, Int.mul_div_assoc _ h, Int.mul_comm]
|
||||
|
||||
theorem tdiv_dvd_tdiv : ∀ {a b c : Int}, a ∣ b → b ∣ c → b.tdiv a ∣ c.tdiv a
|
||||
theorem div_dvd_div : ∀ {a b c : Int}, a ∣ b → b ∣ c → b.div a ∣ c.div a
|
||||
| a, _, _, ⟨b, rfl⟩, ⟨c, rfl⟩ => by
|
||||
by_cases az : a = 0
|
||||
· simp [az]
|
||||
· rw [Int.mul_tdiv_cancel_left _ az, Int.mul_assoc, Int.mul_tdiv_cancel_left _ az]
|
||||
· rw [Int.mul_div_cancel_left _ az, Int.mul_assoc, Int.mul_div_cancel_left _ az]
|
||||
apply Int.dvd_mul_right
|
||||
|
||||
@[simp] theorem natAbs_tdiv (a b : Int) : natAbs (a.tdiv b) = (natAbs a).div (natAbs b) :=
|
||||
@[simp] theorem natAbs_div (a b : Int) : natAbs (a.div b) = (natAbs a).div (natAbs b) :=
|
||||
match a, b, eq_nat_or_neg a, eq_nat_or_neg b with
|
||||
| _, _, ⟨_, .inl rfl⟩, ⟨_, .inl rfl⟩ => rfl
|
||||
| _, _, ⟨_, .inl rfl⟩, ⟨_, .inr rfl⟩ => by rw [Int.tdiv_neg, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inl rfl⟩ => by rw [Int.neg_tdiv, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inr rfl⟩ => by rw [Int.neg_tdiv_neg, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inl rfl⟩, ⟨_, .inr rfl⟩ => by rw [Int.div_neg, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inl rfl⟩ => by rw [Int.neg_div, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inr rfl⟩ => by rw [Int.neg_div_neg, natAbs_neg, natAbs_neg]; rfl
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
|
||||
protected theorem div_eq_of_eq_mul_right {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = b * c) : a.div b = c := by rw [H2, Int.mul_div_cancel_left _ H1]
|
||||
|
||||
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
|
||||
(H1 : a ≠ 0) (H2 : a * b = c) : b = c.tdiv a :=
|
||||
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
|
||||
protected theorem eq_div_of_mul_eq_right {a b c : Int}
|
||||
(H1 : a ≠ 0) (H2 : a * b = c) : b = c.div a :=
|
||||
(Int.div_eq_of_eq_mul_right H1 H2.symm).symm
|
||||
|
||||
/-! ### (t-)mod -/
|
||||
|
||||
theorem ofNat_tmod (m n : Nat) : (↑(m % n) : Int) = tmod m n := rfl
|
||||
theorem ofNat_mod (m n : Nat) : (↑(m % n) : Int) = mod m n := rfl
|
||||
|
||||
@[simp] theorem tmod_one (a : Int) : tmod a 1 = 0 := by
|
||||
simp [tmod_def, Int.tdiv_one, Int.one_mul, Int.sub_self]
|
||||
@[simp] theorem mod_one (a : Int) : mod a 1 = 0 := by
|
||||
simp [mod_def, Int.div_one, Int.one_mul, Int.sub_self]
|
||||
|
||||
theorem tmod_eq_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : tmod a b = a := by
|
||||
rw [tmod_eq_emod H1 (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
|
||||
theorem mod_eq_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : mod a b = a := by
|
||||
rw [mod_eq_emod H1 (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
|
||||
|
||||
theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
|
||||
theorem mod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : mod a b < b :=
|
||||
match a, b, eq_succ_of_zero_lt H with
|
||||
| ofNat _, _, ⟨n, rfl⟩ => ofNat_lt.2 <| Nat.mod_lt _ n.succ_pos
|
||||
| -[_+1], _, ⟨n, rfl⟩ => Int.lt_of_le_of_lt
|
||||
(Int.neg_nonpos_of_nonneg <| Int.ofNat_nonneg _) (ofNat_pos.2 n.succ_pos)
|
||||
|
||||
theorem tmod_nonneg : ∀ {a : Int} (b : Int), 0 ≤ a → 0 ≤ tmod a b
|
||||
theorem mod_nonneg : ∀ {a : Int} (b : Int), 0 ≤ a → 0 ≤ mod a b
|
||||
| ofNat _, -[_+1], _ | ofNat _, ofNat _, _ => ofNat_nonneg _
|
||||
|
||||
@[simp] theorem tmod_neg (a b : Int) : tmod a (-b) = tmod a b := by
|
||||
rw [tmod_def, tmod_def, Int.tdiv_neg, Int.neg_mul_neg]
|
||||
@[simp] theorem mod_neg (a b : Int) : mod a (-b) = mod a b := by
|
||||
rw [mod_def, mod_def, Int.div_neg, Int.neg_mul_neg]
|
||||
|
||||
@[simp] theorem mul_tmod_left (a b : Int) : (a * b).tmod b = 0 :=
|
||||
@[simp] theorem mul_mod_left (a b : Int) : (a * b).mod b = 0 :=
|
||||
if h : b = 0 then by simp [h, Int.mul_zero] else by
|
||||
rw [Int.tmod_def, Int.mul_tdiv_cancel _ h, Int.mul_comm, Int.sub_self]
|
||||
rw [Int.mod_def, Int.mul_div_cancel _ h, Int.mul_comm, Int.sub_self]
|
||||
|
||||
@[simp] theorem mul_tmod_right (a b : Int) : (a * b).tmod a = 0 := by
|
||||
rw [Int.mul_comm, mul_tmod_left]
|
||||
@[simp] theorem mul_mod_right (a b : Int) : (a * b).mod a = 0 := by
|
||||
rw [Int.mul_comm, mul_mod_left]
|
||||
|
||||
theorem tmod_eq_zero_of_dvd : ∀ {a b : Int}, a ∣ b → tmod b a = 0
|
||||
| _, _, ⟨_, rfl⟩ => mul_tmod_right ..
|
||||
theorem mod_eq_zero_of_dvd : ∀ {a b : Int}, a ∣ b → mod b a = 0
|
||||
| _, _, ⟨_, rfl⟩ => mul_mod_right ..
|
||||
|
||||
theorem dvd_iff_tmod_eq_zero {a b : Int} : a ∣ b ↔ tmod b a = 0 :=
|
||||
⟨tmod_eq_zero_of_dvd, dvd_of_tmod_eq_zero⟩
|
||||
theorem dvd_iff_mod_eq_zero (a b : Int) : a ∣ b ↔ mod b a = 0 :=
|
||||
⟨mod_eq_zero_of_dvd, dvd_of_mod_eq_zero⟩
|
||||
|
||||
@[simp] theorem neg_mul_tmod_right (a b : Int) : (-(a * b)).tmod a = 0 := by
|
||||
rw [← dvd_iff_tmod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_right a b
|
||||
protected theorem div_mul_cancel {a b : Int} (H : b ∣ a) : a.div b * b = a :=
|
||||
div_mul_cancel_of_mod_eq_zero (mod_eq_zero_of_dvd H)
|
||||
|
||||
@[simp] theorem neg_mul_tmod_left (a b : Int) : (-(a * b)).tmod b = 0 := by
|
||||
rw [← dvd_iff_tmod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_mul_left a b
|
||||
protected theorem mul_div_cancel' {a b : Int} (H : a ∣ b) : a * b.div a = b := by
|
||||
rw [Int.mul_comm, Int.div_mul_cancel H]
|
||||
|
||||
protected theorem tdiv_mul_cancel {a b : Int} (H : b ∣ a) : a.tdiv b * b = a :=
|
||||
tdiv_mul_cancel_of_tmod_eq_zero (tmod_eq_zero_of_dvd H)
|
||||
protected theorem eq_mul_of_div_eq_right {a b c : Int}
|
||||
(H1 : b ∣ a) (H2 : a.div b = c) : a = b * c := by rw [← H2, Int.mul_div_cancel' H1]
|
||||
|
||||
protected theorem mul_tdiv_cancel' {a b : Int} (H : a ∣ b) : a * b.tdiv a = b := by
|
||||
rw [Int.mul_comm, Int.tdiv_mul_cancel H]
|
||||
@[simp] theorem mod_self {a : Int} : a.mod a = 0 := by
|
||||
have := mul_mod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
protected theorem eq_mul_of_tdiv_eq_right {a b c : Int}
|
||||
(H1 : b ∣ a) (H2 : a.tdiv b = c) : a = b * c := by rw [← H2, Int.mul_tdiv_cancel' H1]
|
||||
|
||||
@[simp] theorem tmod_self {a : Int} : a.tmod a = 0 := by
|
||||
have := mul_tmod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp] theorem neg_tmod_self (a : Int) : (-a).tmod a = 0 := by
|
||||
rw [← dvd_iff_tmod_eq_zero, Int.dvd_neg]
|
||||
exact Int.dvd_refl a
|
||||
|
||||
theorem lt_tdiv_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.tdiv b + 1) * b := by
|
||||
theorem lt_div_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.div b + 1) * b := by
|
||||
rw [Int.add_mul, Int.one_mul, Int.mul_comm]
|
||||
exact Int.lt_add_of_sub_left_lt <| Int.tmod_def .. ▸ tmod_lt_of_pos _ H
|
||||
exact Int.lt_add_of_sub_left_lt <| Int.mod_def .. ▸ mod_lt_of_pos _ H
|
||||
|
||||
protected theorem tdiv_eq_iff_eq_mul_right {a b c : Int}
|
||||
(H : b ≠ 0) (H' : b ∣ a) : a.tdiv b = c ↔ a = b * c :=
|
||||
⟨Int.eq_mul_of_tdiv_eq_right H', Int.tdiv_eq_of_eq_mul_right H⟩
|
||||
protected theorem div_eq_iff_eq_mul_right {a b c : Int}
|
||||
(H : b ≠ 0) (H' : b ∣ a) : a.div b = c ↔ a = b * c :=
|
||||
⟨Int.eq_mul_of_div_eq_right H', Int.div_eq_of_eq_mul_right H⟩
|
||||
|
||||
protected theorem tdiv_eq_iff_eq_mul_left {a b c : Int}
|
||||
(H : b ≠ 0) (H' : b ∣ a) : a.tdiv b = c ↔ a = c * b := by
|
||||
rw [Int.mul_comm]; exact Int.tdiv_eq_iff_eq_mul_right H H'
|
||||
protected theorem div_eq_iff_eq_mul_left {a b c : Int}
|
||||
(H : b ≠ 0) (H' : b ∣ a) : a.div b = c ↔ a = c * b := by
|
||||
rw [Int.mul_comm]; exact Int.div_eq_iff_eq_mul_right H H'
|
||||
|
||||
protected theorem eq_mul_of_tdiv_eq_left {a b c : Int}
|
||||
(H1 : b ∣ a) (H2 : a.tdiv b = c) : a = c * b := by
|
||||
rw [Int.mul_comm, Int.eq_mul_of_tdiv_eq_right H1 H2]
|
||||
protected theorem eq_mul_of_div_eq_left {a b c : Int}
|
||||
(H1 : b ∣ a) (H2 : a.div b = c) : a = c * b := by
|
||||
rw [Int.mul_comm, Int.eq_mul_of_div_eq_right H1 H2]
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a.tdiv b = c :=
|
||||
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
protected theorem div_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a.div b = c :=
|
||||
Int.div_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
|
||||
protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d ∣ n) (H : n.tdiv d = 0) : n = 0 := by
|
||||
rw [← Int.mul_tdiv_cancel' h, H, Int.mul_zero]
|
||||
protected theorem eq_zero_of_div_eq_zero {d n : Int} (h : d ∣ n) (H : n.div d = 0) : n = 0 := by
|
||||
rw [← Int.mul_div_cancel' h, H, Int.mul_zero]
|
||||
|
||||
@[simp] protected theorem tdiv_left_inj {a b d : Int}
|
||||
(hda : d ∣ a) (hdb : d ∣ b) : a.tdiv d = b.tdiv d ↔ a = b := by
|
||||
refine ⟨fun h => ?_, congrArg (tdiv · d)⟩
|
||||
rw [← Int.mul_tdiv_cancel' hda, ← Int.mul_tdiv_cancel' hdb, h]
|
||||
@[simp] protected theorem div_left_inj {a b d : Int}
|
||||
(hda : d ∣ a) (hdb : d ∣ b) : a.div d = b.div d ↔ a = b := by
|
||||
refine ⟨fun h => ?_, congrArg (div · d)⟩
|
||||
rw [← Int.mul_div_cancel' hda, ← Int.mul_div_cancel' hdb, h]
|
||||
|
||||
theorem tdiv_sign : ∀ a b, a.tdiv (sign b) = a * sign b
|
||||
theorem div_sign : ∀ a b, a.div (sign b) = a * sign b
|
||||
| _, succ _ => by simp [sign, Int.mul_one]
|
||||
| _, 0 => by simp [sign, Int.mul_zero]
|
||||
| _, -[_+1] => by simp [sign, Int.mul_neg, Int.mul_one]
|
||||
|
||||
protected theorem sign_eq_tdiv_abs (a : Int) : sign a = a.tdiv (natAbs a) :=
|
||||
protected theorem sign_eq_div_abs (a : Int) : sign a = a.div (natAbs a) :=
|
||||
if az : a = 0 then by simp [az] else
|
||||
(Int.tdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
|
||||
(Int.div_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
|
||||
(sign_mul_natAbs _).symm).symm
|
||||
|
||||
/-! ### fdiv -/
|
||||
@@ -1050,7 +991,7 @@ theorem fmod_eq_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.fmod b = a :=
|
||||
rw [fmod_eq_emod _ (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
|
||||
|
||||
theorem fmod_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a.fmod b :=
|
||||
fmod_eq_tmod ha hb ▸ tmod_nonneg _ ha
|
||||
fmod_eq_mod ha hb ▸ mod_nonneg _ ha
|
||||
|
||||
theorem fmod_nonneg' (a : Int) {b : Int} (hb : 0 < b) : 0 ≤ a.fmod b :=
|
||||
fmod_eq_emod _ (Int.le_of_lt hb) ▸ emod_nonneg _ (Int.ne_of_lt hb).symm
|
||||
@@ -1070,10 +1011,10 @@ theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
|
||||
|
||||
/-! ### Theorems crossing div/mod versions -/
|
||||
|
||||
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.tdiv b = a / b := by
|
||||
theorem div_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.div b = a / b := by
|
||||
by_cases b0 : b = 0
|
||||
· simp [b0]
|
||||
· rw [Int.tdiv_eq_iff_eq_mul_left b0 h, ← Int.ediv_eq_iff_eq_mul_left b0 h]
|
||||
· rw [Int.div_eq_iff_eq_mul_left b0 h, ← Int.ediv_eq_iff_eq_mul_left b0 h]
|
||||
|
||||
theorem fdiv_eq_ediv_of_dvd : ∀ {a b : Int}, b ∣ a → a.fdiv b = a / b
|
||||
| _, b, ⟨c, rfl⟩ => by
|
||||
@@ -1150,7 +1091,8 @@ theorem bmod_mul_bmod : Int.bmod (Int.bmod x n * y) n = Int.bmod (x * y) n := by
|
||||
next p =>
|
||||
simp
|
||||
next p =>
|
||||
rw [Int.sub_mul, Int.sub_eq_add_neg, ← Int.mul_neg, bmod_add_mul_cancel, emod_mul_bmod_congr]
|
||||
rw [Int.sub_mul, Int.sub_eq_add_neg, ← Int.mul_neg]
|
||||
simp
|
||||
|
||||
@[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]
|
||||
@@ -1167,7 +1109,7 @@ theorem emod_bmod {x : Int} {m : Nat} : bmod (x % m) m = bmod x m := by
|
||||
|
||||
@[simp] theorem bmod_zero : Int.bmod 0 m = 0 := by
|
||||
dsimp [bmod]
|
||||
simp only [Int.zero_sub, ite_eq_left_iff, Int.neg_eq_zero]
|
||||
simp only [zero_emod, Int.zero_sub, ite_eq_left_iff, Int.neg_eq_zero]
|
||||
intro h
|
||||
rw [@Int.not_lt] at h
|
||||
match m with
|
||||
@@ -1285,65 +1227,3 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
|
||||
all_goals decide
|
||||
· exact ofNat_nonneg x
|
||||
· exact succ_ofNat_pos (x + 1)
|
||||
|
||||
/-! ### Deprecations -/
|
||||
|
||||
@[deprecated Int.zero_tdiv (since := "2024-09-11")] protected abbrev zero_div := @Int.zero_tdiv
|
||||
@[deprecated Int.tdiv_zero (since := "2024-09-11")] protected abbrev div_zero := @Int.tdiv_zero
|
||||
@[deprecated tdiv_eq_ediv (since := "2024-09-11")] abbrev div_eq_ediv := @tdiv_eq_ediv
|
||||
@[deprecated fdiv_eq_tdiv (since := "2024-09-11")] abbrev fdiv_eq_div := @fdiv_eq_tdiv
|
||||
@[deprecated zero_tmod (since := "2024-09-11")] abbrev zero_mod := @zero_tmod
|
||||
@[deprecated tmod_zero (since := "2024-09-11")] abbrev mod_zero := @tmod_zero
|
||||
@[deprecated tmod_add_tdiv (since := "2024-09-11")] abbrev mod_add_div := @tmod_add_tdiv
|
||||
@[deprecated tdiv_add_tmod (since := "2024-09-11")] abbrev div_add_mod := @tdiv_add_tmod
|
||||
@[deprecated tmod_add_tdiv' (since := "2024-09-11")] abbrev mod_add_div' := @tmod_add_tdiv'
|
||||
@[deprecated tdiv_add_tmod' (since := "2024-09-11")] abbrev div_add_mod' := @tdiv_add_tmod'
|
||||
@[deprecated tmod_def (since := "2024-09-11")] abbrev mod_def := @tmod_def
|
||||
@[deprecated tmod_eq_emod (since := "2024-09-11")] abbrev mod_eq_emod := @tmod_eq_emod
|
||||
@[deprecated fmod_eq_tmod (since := "2024-09-11")] abbrev fmod_eq_mod := @fmod_eq_tmod
|
||||
@[deprecated Int.tdiv_one (since := "2024-09-11")] protected abbrev div_one := @Int.tdiv_one
|
||||
@[deprecated Int.tdiv_neg (since := "2024-09-11")] protected abbrev div_neg := @Int.tdiv_neg
|
||||
@[deprecated Int.neg_tdiv (since := "2024-09-11")] protected abbrev neg_div := @Int.neg_tdiv
|
||||
@[deprecated Int.neg_tdiv_neg (since := "2024-09-11")] protected abbrev neg_div_neg := @Int.neg_tdiv_neg
|
||||
@[deprecated Int.tdiv_nonneg (since := "2024-09-11")] protected abbrev div_nonneg := @Int.tdiv_nonneg
|
||||
@[deprecated Int.tdiv_nonpos (since := "2024-09-11")] protected abbrev div_nonpos := @Int.tdiv_nonpos
|
||||
@[deprecated Int.tdiv_eq_zero_of_lt (since := "2024-09-11")] abbrev div_eq_zero_of_lt := @Int.tdiv_eq_zero_of_lt
|
||||
@[deprecated Int.mul_tdiv_cancel (since := "2024-09-11")] protected abbrev mul_div_cancel := @Int.mul_tdiv_cancel
|
||||
@[deprecated Int.mul_tdiv_cancel_left (since := "2024-09-11")] protected abbrev mul_div_cancel_left := @Int.mul_tdiv_cancel_left
|
||||
@[deprecated Int.tdiv_self (since := "2024-09-11")] protected abbrev div_self := @Int.tdiv_self
|
||||
@[deprecated Int.mul_tdiv_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev mul_div_cancel_of_mod_eq_zero := @Int.mul_tdiv_cancel_of_tmod_eq_zero
|
||||
@[deprecated Int.tdiv_mul_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev div_mul_cancel_of_mod_eq_zero := @Int.tdiv_mul_cancel_of_tmod_eq_zero
|
||||
@[deprecated Int.dvd_of_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_of_mod_eq_zero := @Int.dvd_of_tmod_eq_zero
|
||||
@[deprecated Int.mul_tdiv_assoc (since := "2024-09-11")] protected abbrev mul_div_assoc := @Int.mul_tdiv_assoc
|
||||
@[deprecated Int.mul_tdiv_assoc' (since := "2024-09-11")] protected abbrev mul_div_assoc' := @Int.mul_tdiv_assoc'
|
||||
@[deprecated Int.tdiv_dvd_tdiv (since := "2024-09-11")] abbrev div_dvd_div := @Int.tdiv_dvd_tdiv
|
||||
@[deprecated Int.natAbs_tdiv (since := "2024-09-11")] abbrev natAbs_div := @Int.natAbs_tdiv
|
||||
@[deprecated Int.tdiv_eq_of_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_right := @Int.tdiv_eq_of_eq_mul_right
|
||||
@[deprecated Int.eq_tdiv_of_mul_eq_right (since := "2024-09-11")] protected abbrev eq_div_of_mul_eq_right := @Int.eq_tdiv_of_mul_eq_right
|
||||
@[deprecated Int.ofNat_tmod (since := "2024-09-11")] abbrev ofNat_mod := @Int.ofNat_tmod
|
||||
@[deprecated Int.tmod_one (since := "2024-09-11")] abbrev mod_one := @Int.tmod_one
|
||||
@[deprecated Int.tmod_eq_of_lt (since := "2024-09-11")] abbrev mod_eq_of_lt := @Int.tmod_eq_of_lt
|
||||
@[deprecated Int.tmod_lt_of_pos (since := "2024-09-11")] abbrev mod_lt_of_pos := @Int.tmod_lt_of_pos
|
||||
@[deprecated Int.tmod_nonneg (since := "2024-09-11")] abbrev mod_nonneg := @Int.tmod_nonneg
|
||||
@[deprecated Int.tmod_neg (since := "2024-09-11")] abbrev mod_neg := @Int.tmod_neg
|
||||
@[deprecated Int.mul_tmod_left (since := "2024-09-11")] abbrev mul_mod_left := @Int.mul_tmod_left
|
||||
@[deprecated Int.mul_tmod_right (since := "2024-09-11")] abbrev mul_mod_right := @Int.mul_tmod_right
|
||||
@[deprecated Int.tmod_eq_zero_of_dvd (since := "2024-09-11")] abbrev mod_eq_zero_of_dvd := @Int.tmod_eq_zero_of_dvd
|
||||
@[deprecated Int.dvd_iff_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_iff_mod_eq_zero := @Int.dvd_iff_tmod_eq_zero
|
||||
@[deprecated Int.neg_mul_tmod_right (since := "2024-09-11")] abbrev neg_mul_mod_right := @Int.neg_mul_tmod_right
|
||||
@[deprecated Int.neg_mul_tmod_left (since := "2024-09-11")] abbrev neg_mul_mod_left := @Int.neg_mul_tmod_left
|
||||
@[deprecated Int.tdiv_mul_cancel (since := "2024-09-11")] protected abbrev div_mul_cancel := @Int.tdiv_mul_cancel
|
||||
@[deprecated Int.mul_tdiv_cancel' (since := "2024-09-11")] protected abbrev mul_div_cancel' := @Int.mul_tdiv_cancel'
|
||||
@[deprecated Int.eq_mul_of_tdiv_eq_right (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_right := @Int.eq_mul_of_tdiv_eq_right
|
||||
@[deprecated Int.tmod_self (since := "2024-09-11")] abbrev mod_self := @Int.tmod_self
|
||||
@[deprecated Int.neg_tmod_self (since := "2024-09-11")] abbrev neg_mod_self := @Int.neg_tmod_self
|
||||
@[deprecated Int.lt_tdiv_add_one_mul_self (since := "2024-09-11")] abbrev lt_div_add_one_mul_self := @Int.lt_tdiv_add_one_mul_self
|
||||
@[deprecated Int.tdiv_eq_iff_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_right := @Int.tdiv_eq_iff_eq_mul_right
|
||||
@[deprecated Int.tdiv_eq_iff_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_left := @Int.tdiv_eq_iff_eq_mul_left
|
||||
@[deprecated Int.eq_mul_of_tdiv_eq_left (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_left := @Int.eq_mul_of_tdiv_eq_left
|
||||
@[deprecated Int.tdiv_eq_of_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_left := @Int.tdiv_eq_of_eq_mul_left
|
||||
@[deprecated Int.eq_zero_of_tdiv_eq_zero (since := "2024-09-11")] protected abbrev eq_zero_of_div_eq_zero := @Int.eq_zero_of_tdiv_eq_zero
|
||||
@[deprecated Int.tdiv_left_inj (since := "2024-09-11")] protected abbrev div_left_inj := @Int.tdiv_left_inj
|
||||
@[deprecated Int.tdiv_sign (since := "2024-09-11")] abbrev div_sign := @Int.tdiv_sign
|
||||
@[deprecated Int.sign_eq_tdiv_abs (since := "2024-09-11")] protected abbrev sign_eq_div_abs := @Int.sign_eq_tdiv_abs
|
||||
@[deprecated Int.tdiv_eq_ediv_of_dvd (since := "2024-09-11")] abbrev div_eq_ediv_of_dvd := @Int.tdiv_eq_ediv_of_dvd
|
||||
|
||||
@@ -7,7 +7,6 @@ prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Conv
|
||||
import Init.NotationExtra
|
||||
import Init.PropLemmas
|
||||
|
||||
namespace Int
|
||||
|
||||
@@ -289,7 +288,7 @@ protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
|
||||
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
|
||||
simp [Int.sub_eq_add_neg, ← Int.add_assoc]
|
||||
|
||||
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
|
||||
protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
|
||||
|
||||
@[simp] protected theorem sub_add_cancel (a b : Int) : a - b + b = a :=
|
||||
Int.neg_add_cancel_right a b
|
||||
@@ -329,22 +328,22 @@ theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
|
||||
/- ## add/sub injectivity -/
|
||||
|
||||
@[simp]
|
||||
protected theorem add_right_inj {i j : Int} (k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
protected theorem add_right_inj (i j k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
apply Iff.intro
|
||||
· intro p
|
||||
rw [←Int.add_sub_cancel i k, ←Int.add_sub_cancel j k, p]
|
||||
· exact congrArg (· + k)
|
||||
|
||||
@[simp]
|
||||
protected theorem add_left_inj {i j : Int} (k : Int) : (k + i = k + j) ↔ i = j := by
|
||||
protected theorem add_left_inj (i j k : Int) : (k + i = k + j) ↔ i = j := by
|
||||
simp [Int.add_comm k]
|
||||
|
||||
@[simp]
|
||||
protected theorem sub_left_inj {i j : Int} (k : Int) : (k - i = k - j) ↔ i = j := by
|
||||
protected theorem sub_left_inj (i j k : Int) : (k - i = k - j) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg, Int.neg_inj]
|
||||
|
||||
@[simp]
|
||||
protected theorem sub_right_inj {i j : Int} (k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
protected theorem sub_right_inj (i j k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg]
|
||||
|
||||
/- ## Ring properties -/
|
||||
@@ -445,10 +444,10 @@ protected theorem neg_mul_eq_neg_mul (a b : Int) : -(a * b) = -a * b :=
|
||||
protected theorem neg_mul_eq_mul_neg (a b : Int) : -(a * b) = a * -b :=
|
||||
Int.neg_eq_of_add_eq_zero <| by rw [← Int.mul_add, Int.add_right_neg, Int.mul_zero]
|
||||
|
||||
@[simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
|
||||
@[local simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
|
||||
(Int.neg_mul_eq_neg_mul a b).symm
|
||||
|
||||
@[simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
|
||||
@[local simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
|
||||
(Int.neg_mul_eq_mul_neg a b).symm
|
||||
|
||||
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp
|
||||
@@ -487,9 +486,6 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 ∨ b = 0 := by
|
||||
protected theorem mul_ne_zero {a b : Int} (a0 : a ≠ 0) (b0 : b ≠ 0) : a * b ≠ 0 :=
|
||||
Or.rec a0 b0 ∘ Int.mul_eq_zero.mp
|
||||
|
||||
@[simp] protected theorem mul_ne_zero_iff {a b : Int} : a * b ≠ 0 ↔ a ≠ 0 ∧ b ≠ 0 := by
|
||||
rw [ne_eq, Int.mul_eq_zero, not_or, ne_eq]
|
||||
|
||||
protected theorem eq_of_mul_eq_mul_right {a b c : Int} (ha : a ≠ 0) (h : b * a = c * a) : b = c :=
|
||||
have : (b - c) * a = 0 := by rwa [Int.sub_mul, Int.sub_eq_zero]
|
||||
Int.sub_eq_zero.1 <| (Int.mul_eq_zero.mp this).resolve_right ha
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Order
|
||||
import Init.Omega
|
||||
|
||||
|
||||
/-!
|
||||
# Further lemmas about `Int` relying on `omega` automation.
|
||||
-/
|
||||
|
||||
namespace Int
|
||||
|
||||
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by
|
||||
symm
|
||||
simp only [Int.toNat]
|
||||
split <;> rename_i x a
|
||||
· simp only [Int.ofNat_eq_coe]
|
||||
split <;> rename_i y b h
|
||||
· simp at h
|
||||
omega
|
||||
· simp [Int.negSucc_eq] at h
|
||||
omega
|
||||
· simp only [Nat.zero_sub]
|
||||
split <;> rename_i y b h
|
||||
· simp [Int.negSucc_eq] at h
|
||||
omega
|
||||
· rfl
|
||||
|
||||
@[simp] theorem toNat_sub_max_self (a : Int) : (a - max a 0).toNat = 0 := by
|
||||
simp [toNat]
|
||||
split <;> simp_all <;> omega
|
||||
|
||||
@[simp] theorem toNat_sub_self_max (a : Int) : (a - max 0 a).toNat = 0 := by
|
||||
simp [toNat]
|
||||
split <;> simp_all <;> omega
|
||||
|
||||
end Int
|
||||
@@ -26,9 +26,9 @@ theorem nonneg_or_nonneg_neg : ∀ (a : Int), NonNeg a ∨ NonNeg (-a)
|
||||
| (_:Nat) => .inl ⟨_⟩
|
||||
| -[_+1] => .inr ⟨_⟩
|
||||
|
||||
theorem le_def {a b : Int} : a ≤ b ↔ NonNeg (b - a) := .rfl
|
||||
theorem le_def (a b : Int) : a ≤ b ↔ NonNeg (b - a) := .rfl
|
||||
|
||||
theorem lt_iff_add_one_le {a b : Int} : a < b ↔ a + 1 ≤ b := .rfl
|
||||
theorem lt_iff_add_one_le (a b : Int) : a < b ↔ a + 1 ≤ b := .rfl
|
||||
|
||||
theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a ≤ b := by
|
||||
simp [le_def, h]; constructor
|
||||
@@ -240,24 +240,9 @@ theorem le_natAbs {a : Int} : a ≤ natAbs a :=
|
||||
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
|
||||
Int.not_le.1 fun h => let ⟨_, h⟩ := eq_ofNat_of_zero_le h; nomatch h
|
||||
|
||||
theorem negSucc_le_zero (n : Nat) : -[n+1] ≤ 0 :=
|
||||
Int.le_of_lt (negSucc_lt_zero n)
|
||||
|
||||
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 ≤ -[n+1] ↔ False := by
|
||||
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
|
||||
|
||||
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
|
||||
rw [Int.max_eq_left (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
|
||||
rw [Int.max_eq_right (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
|
||||
rw [Int.max_eq_right (negSucc_le_zero _)]
|
||||
|
||||
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
|
||||
rw [Int.max_eq_left (negSucc_le_zero _)]
|
||||
|
||||
protected theorem add_le_add_left {a b : Int} (h : a ≤ b) (c : Int) : c + a ≤ c + b :=
|
||||
let ⟨n, hn⟩ := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
|
||||
|
||||
@@ -480,21 +465,13 @@ theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
|
||||
|
||||
@[simp] theorem toNat_one : (1 : Int).toNat = 1 := rfl
|
||||
|
||||
theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
|
||||
@[simp] theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
|
||||
rw [toNat_eq_max, Int.max_eq_left h]
|
||||
|
||||
@[simp] theorem toNat_ofNat (n : Nat) : toNat ↑n = n := rfl
|
||||
|
||||
@[simp] theorem toNat_negSucc (n : Nat) : (Int.negSucc n).toNat = 0 := by
|
||||
simp [toNat]
|
||||
|
||||
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
|
||||
|
||||
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
|
||||
match a with
|
||||
| Int.ofNat n => simp
|
||||
| Int.negSucc n => simp
|
||||
|
||||
theorem self_le_toNat (a : Int) : a ≤ toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
|
||||
|
||||
@[simp] theorem le_toNat {n : Nat} {z : Int} (h : 0 ≤ z) : n ≤ z.toNat ↔ (n : Int) ≤ z := by
|
||||
@@ -515,7 +492,7 @@ theorem toNat_add_nat {a : Int} (ha : 0 ≤ a) (n : Nat) : (a + n).toNat = a.toN
|
||||
| (n+1:Nat) => by simp [ofNat_add]
|
||||
| -[n+1] => rfl
|
||||
|
||||
theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
|
||||
@[simp] theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
|
||||
| 0 => rfl
|
||||
| (_+1:Nat) => Int.sub_zero _
|
||||
| -[_+1] => Int.zero_sub _
|
||||
@@ -531,7 +508,7 @@ theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
|
||||
|
||||
/-! ### toNat' -/
|
||||
|
||||
theorem mem_toNat' : ∀ {a : Int} {n : Nat}, toNat' a = some n ↔ a = n
|
||||
theorem mem_toNat' : ∀ (a : Int) (n : Nat), toNat' a = some n ↔ a = n
|
||||
| (m : Nat), n => by simp [toNat', Int.ofNat_inj]
|
||||
| -[m+1], n => by constructor <;> nofun
|
||||
|
||||
@@ -829,10 +806,10 @@ protected theorem lt_add_of_neg_lt_sub_right {a b c : Int} (h : -b < a - c) : c
|
||||
protected theorem neg_lt_sub_right_of_lt_add {a b c : Int} (h : c < a + b) : -b < a - c :=
|
||||
Int.lt_sub_left_of_add_lt (Int.sub_right_lt_of_lt_add h)
|
||||
|
||||
protected theorem add_lt_iff {a b c : Int} : a + b < c ↔ a < -b + c := by
|
||||
protected theorem add_lt_iff (a b c : Int) : a + b < c ↔ a < -b + c := by
|
||||
rw [← Int.add_lt_add_iff_left (-b), Int.add_comm (-b), Int.add_neg_cancel_right]
|
||||
|
||||
protected theorem sub_lt_iff {a b c : Int} : a - b < c ↔ a < c + b :=
|
||||
protected theorem sub_lt_iff (a b c : Int) : a - b < c ↔ a < c + b :=
|
||||
Iff.intro Int.lt_add_of_sub_right_lt Int.sub_right_lt_of_lt_add
|
||||
|
||||
protected theorem sub_lt_of_sub_lt {a b c : Int} (h : a - b < c) : a - c < b :=
|
||||
@@ -853,10 +830,12 @@ protected theorem lt_of_sub_lt_sub_left {a b c : Int} (h : c - a < c - b) : b <
|
||||
protected theorem lt_of_sub_lt_sub_right {a b c : Int} (h : a - c < b - c) : a < b :=
|
||||
Int.lt_of_add_lt_add_right h
|
||||
|
||||
@[simp] protected theorem sub_lt_sub_left_iff {a b c : Int} : c - a < c - b ↔ b < a :=
|
||||
@[simp] protected theorem sub_lt_sub_left_iff (a b c : Int) :
|
||||
c - a < c - b ↔ b < a :=
|
||||
⟨Int.lt_of_sub_lt_sub_left, (Int.sub_lt_sub_left · c)⟩
|
||||
|
||||
@[simp] protected theorem sub_lt_sub_right_iff {a b c : Int} : a - c < b - c ↔ a < b :=
|
||||
@[simp] protected theorem sub_lt_sub_right_iff (a b c : Int) :
|
||||
a - c < b - c ↔ a < b :=
|
||||
⟨Int.lt_of_sub_lt_sub_right, (Int.sub_lt_sub_right · c)⟩
|
||||
|
||||
protected theorem sub_lt_sub_of_le_of_lt {a b c d : Int}
|
||||
@@ -988,13 +967,13 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
|
||||
| 0, h => nomatch h
|
||||
| -[_+1], _ => negSucc_lt_zero _
|
||||
|
||||
theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 ↔ 0 < a :=
|
||||
theorem sign_eq_one_iff_pos (a : Int) : sign a = 1 ↔ 0 < a :=
|
||||
⟨pos_of_sign_eq_one, sign_eq_one_of_pos⟩
|
||||
|
||||
theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
theorem sign_eq_neg_one_iff_neg (a : Int) : sign a = -1 ↔ a < 0 :=
|
||||
⟨neg_of_sign_eq_neg_one, sign_eq_neg_one_of_neg⟩
|
||||
|
||||
@[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 ↔ a = 0 :=
|
||||
@[simp] theorem sign_eq_zero_iff_zero (a : Int) : sign a = 0 ↔ a = 0 :=
|
||||
⟨eq_zero_of_sign_eq_zero, fun h => by rw [h, sign_zero]⟩
|
||||
|
||||
@[simp] theorem sign_sign : sign (sign x) = sign x := by
|
||||
@@ -1027,7 +1006,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
|
||||
theorem eq_nat_or_neg (a : Int) : ∃ n : Nat, a = n ∨ a = -↑n := ⟨_, natAbs_eq a⟩
|
||||
|
||||
theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
|
||||
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [← natAbs_mul, h, natAbs.eq_def]
|
||||
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [← natAbs_mul, h, natAbs]
|
||||
|
||||
@[simp] theorem natAbs_mul_self' (a : Int) : (natAbs a * natAbs a : Int) = a * a := by
|
||||
rw [← Int.ofNat_mul, natAbs_mul_self]
|
||||
|
||||
@@ -5,7 +5,6 @@ Authors: Jeremy Avigad, Deniz Aydin, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
namespace Int
|
||||
|
||||
@@ -36,24 +35,10 @@ theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : ∀ {j}, i ≤
|
||||
theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
|
||||
pow_le_pow_of_le_right h (Nat.zero_le _)
|
||||
|
||||
@[norm_cast]
|
||||
theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
|
||||
match n with
|
||||
| 0 => rfl
|
||||
| n + 1 =>
|
||||
simp only [Nat.pow_succ, Int.pow_succ, natCast_mul, natCast_pow _ n]
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) :
|
||||
((2 ^ (w - 1) : Nat) - (2 ^ w : Nat) : Int) = - ((2 ^ (w - 1) : Nat) : Int) := by
|
||||
rw [← Nat.two_pow_pred_add_two_pow_pred h]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_pred_sub_two_pow' {w : Nat} (h : 0 < w) :
|
||||
(2 : Int) ^ (w - 1) - (2 : Int) ^ w = - (2 : Int) ^ (w - 1) := by
|
||||
norm_cast
|
||||
rw [← Nat.two_pow_pred_add_two_pow_pred h]
|
||||
simp [h]
|
||||
|
||||
end Int
|
||||
|
||||
@@ -21,5 +21,3 @@ import Init.Data.List.Pairwise
|
||||
import Init.Data.List.Sublist
|
||||
import Init.Data.List.TakeDrop
|
||||
import Init.Data.List.Zip
|
||||
import Init.Data.List.Perm
|
||||
import Init.Data.List.Sort
|
||||
|
||||
@@ -48,8 +48,6 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
|
||||
@[simp] theorem attach_nil : ([] : List α).attach = [] := rfl
|
||||
|
||||
@[simp] theorem attachWith_nil : ([] : List α).attachWith P H = [] := rfl
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_map (p : α → Prop) (f : α → β) (l : List α) (H) :
|
||||
@pmap _ _ p (fun a _ => f a) l H = map f l := by
|
||||
@@ -57,14 +55,11 @@ theorem pmap_eq_map (p : α → Prop) (f : α → β) (l : List α) (H) :
|
||||
· rfl
|
||||
· simp only [*, pmap, map]
|
||||
|
||||
theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a, q a → β} (l : List α) {H₁ H₂}
|
||||
theorem pmap_congr {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a, q a → β} (l : List α) {H₁ H₂}
|
||||
(h : ∀ a ∈ l, ∀ (h₁ h₂), f a h₁ = g a h₂) : pmap f l H₁ = pmap g l H₂ := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x l ih =>
|
||||
rw [pmap, pmap, h _ (mem_cons_self _ _), ih fun a ha => h a (mem_cons_of_mem _ ha)]
|
||||
|
||||
@[deprecated pmap_congr_left (since := "2024-09-06")] abbrev pmap_congr := @pmap_congr_left
|
||||
| cons x l ih => rw [pmap, pmap, h _ (mem_cons_self _ _), ih fun a ha => h a (mem_cons_of_mem _ ha)]
|
||||
|
||||
theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (l H) :
|
||||
map g (pmap f l H) = pmap (fun a h => g (f a h)) l H := by
|
||||
@@ -78,33 +73,9 @@ theorem pmap_map {p : β → Prop} (g : ∀ b, p b → γ) (f : α → β) (l H)
|
||||
· rfl
|
||||
· simp only [*, pmap, map]
|
||||
|
||||
theorem attach_congr {l₁ l₂ : List α} (h : l₁ = l₂) :
|
||||
l₁.attach = l₂.attach.map (fun x => ⟨x.1, h ▸ x.2⟩) := by
|
||||
subst h
|
||||
simp
|
||||
|
||||
theorem attachWith_congr {l₁ l₂ : List α} (w : l₁ = l₂) {P : α → Prop} {H : ∀ x ∈ l₁, P x} :
|
||||
l₁.attachWith P H = l₂.attachWith P fun x h => H _ (w ▸ h) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
@[simp] theorem attach_cons {x : α} {xs : List α} :
|
||||
(x :: xs).attach =
|
||||
⟨x, mem_cons_self x xs⟩ :: xs.attach.map fun ⟨y, h⟩ => ⟨y, mem_cons_of_mem x h⟩ := by
|
||||
simp only [attach, attachWith, pmap, map_pmap, cons.injEq, true_and]
|
||||
apply pmap_congr_left
|
||||
intros a _ m' _
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem attachWith_cons {x : α} {xs : List α} {p : α → Prop} (h : ∀ a ∈ x :: xs, p a) :
|
||||
(x :: xs).attachWith p h = ⟨x, h x (mem_cons_self x xs)⟩ ::
|
||||
xs.attachWith p (fun a ha ↦ h a (mem_cons_of_mem x ha)) :=
|
||||
rfl
|
||||
|
||||
theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l H) :
|
||||
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_congr_left l fun _ _ _ _ => rfl
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_congr l fun _ _ _ _ => rfl
|
||||
|
||||
theorem attach_map_coe (l : List α) (f : α → β) :
|
||||
(l.attach.map fun (i : {i // i ∈ l}) => f i) = l.map f := by
|
||||
@@ -115,20 +86,14 @@ theorem attach_map_val (l : List α) (f : α → β) : (l.attach.map fun i => f
|
||||
|
||||
@[simp]
|
||||
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
|
||||
(attach_map_coe _ _).trans (List.map_id _)
|
||||
(attach_map_coe _ _).trans l.map_id
|
||||
|
||||
theorem attachWith_map_coe {p : α → Prop} (f : α → β) (l : List α) (H : ∀ a ∈ l, p a) :
|
||||
((l.attachWith p H).map fun (i : { i // p i}) => f i) = l.map f := by
|
||||
rw [attachWith, map_pmap]; exact pmap_eq_map _ _ _ _
|
||||
|
||||
theorem attachWith_map_val {p : α → Prop} (f : α → β) (l : List α) (H : ∀ a ∈ l, p a) :
|
||||
((l.attachWith p H).map fun i => f i.val) = l.map f :=
|
||||
attachWith_map_coe _ _ _
|
||||
theorem countP_attach (l : List α) (p : α → Bool) : l.attach.countP (fun a : {x // x ∈ l} => p a) = l.countP p := by
|
||||
simp only [← Function.comp_apply (g := Subtype.val), ← countP_map, attach_map_subtype_val]
|
||||
|
||||
@[simp]
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} (l : List α) (H : ∀ a ∈ l, p a) :
|
||||
(l.attachWith p H).map Subtype.val = l :=
|
||||
(attachWith_map_coe _ _ _).trans (List.map_id _)
|
||||
theorem count_attach [DecidableEq α] (l : List α) (a : {x // x ∈ l}) : l.attach.count a = l.count ↑a :=
|
||||
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attach _ _
|
||||
|
||||
@[simp]
|
||||
theorem mem_attach (l : List α) : ∀ x, x ∈ l.attach
|
||||
@@ -142,11 +107,6 @@ theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H b} :
|
||||
b ∈ pmap f l H ↔ ∃ (a : _) (h : a ∈ l), f a (H a h) = b := by
|
||||
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
|
||||
|
||||
theorem mem_pmap_of_mem {p : α → Prop} {f : ∀ a, p a → β} {l H} {a} (h : a ∈ l) :
|
||||
f a (H a h) ∈ pmap f l H := by
|
||||
rw [mem_pmap]
|
||||
exact ⟨a, h, rfl⟩
|
||||
|
||||
@[simp]
|
||||
theorem length_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H} : length (pmap f l H) = length l := by
|
||||
induction l
|
||||
@@ -154,43 +114,30 @@ theorem length_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H} : length (pm
|
||||
· simp only [*, pmap, length]
|
||||
|
||||
@[simp]
|
||||
theorem length_attach {L : List α} : L.attach.length = L.length :=
|
||||
theorem length_attach (L : List α) : L.attach.length = L.length :=
|
||||
length_pmap
|
||||
|
||||
@[simp]
|
||||
theorem length_attachWith {p : α → Prop} {l H} : length (l.attachWith p H) = length l :=
|
||||
length_pmap
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_nil_iff {p : α → Prop} {f : ∀ a, p a → β} {l H} : pmap f l H = [] ↔ l = [] := by
|
||||
theorem pmap_eq_nil {p : α → Prop} {f : ∀ a, p a → β} {l H} : pmap f l H = [] ↔ l = [] := by
|
||||
rw [← length_eq_zero, length_pmap, length_eq_zero]
|
||||
|
||||
theorem pmap_ne_nil_iff {P : α → Prop} (f : (a : α) → P a → β) {xs : List α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) : xs.pmap f H ≠ [] ↔ xs ≠ [] := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem attach_eq_nil_iff {l : List α} : l.attach = [] ↔ l = [] :=
|
||||
pmap_eq_nil_iff
|
||||
theorem attach_eq_nil (l : List α) : l.attach = [] ↔ l = [] :=
|
||||
pmap_eq_nil
|
||||
|
||||
theorem attach_ne_nil_iff {l : List α} : l.attach ≠ [] ↔ l ≠ [] :=
|
||||
pmap_ne_nil_iff _ _
|
||||
theorem getLast_pmap (p : α → Prop) (f : ∀ a, p a → β) (l : List α)
|
||||
(hl₁ : ∀ a ∈ l, p a) (hl₂ : l ≠ []) :
|
||||
(l.pmap f hl₁).getLast (mt List.pmap_eq_nil.1 hl₂) =
|
||||
f (l.getLast hl₂) (hl₁ _ (List.getLast_mem hl₂)) := by
|
||||
induction l with
|
||||
| nil => apply (hl₂ rfl).elim
|
||||
| cons l_hd l_tl l_ih =>
|
||||
by_cases hl_tl : l_tl = []
|
||||
· simp [hl_tl]
|
||||
· simp only [pmap]
|
||||
rw [getLast_cons, l_ih _ hl_tl]
|
||||
simp only [getLast_cons hl_tl]
|
||||
|
||||
@[simp]
|
||||
theorem attachWith_eq_nil_iff {l : List α} {P : α → Prop} {H : ∀ a ∈ l, P a} :
|
||||
l.attachWith P H = [] ↔ l = [] :=
|
||||
pmap_eq_nil_iff
|
||||
|
||||
theorem attachWith_ne_nil_iff {l : List α} {P : α → Prop} {H : ∀ a ∈ l, P a} :
|
||||
l.attachWith P H ≠ [] ↔ l ≠ [] :=
|
||||
pmap_ne_nil_iff _ _
|
||||
|
||||
@[deprecated pmap_eq_nil_iff (since := "2024-09-06")] abbrev pmap_eq_nil := @pmap_eq_nil_iff
|
||||
@[deprecated pmap_ne_nil_iff (since := "2024-09-06")] abbrev pmap_ne_nil := @pmap_ne_nil_iff
|
||||
@[deprecated attach_eq_nil_iff (since := "2024-09-06")] abbrev attach_eq_nil := @attach_eq_nil_iff
|
||||
@[deprecated attach_ne_nil_iff (since := "2024-09-06")] abbrev attach_ne_nil := @attach_ne_nil_iff
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) (n : Nat) :
|
||||
(pmap f l h)[n]? = Option.pmap f l[n]? fun x H => h x (getElem?_mem H) := by
|
||||
induction l generalizing n with
|
||||
@@ -212,12 +159,11 @@ theorem get?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h :
|
||||
simp only [get?_eq_getElem?]
|
||||
simp [getElem?_pmap, h]
|
||||
|
||||
@[simp]
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) {n : Nat}
|
||||
(hn : n < (pmap f l h).length) :
|
||||
(pmap f l h)[n] =
|
||||
f (l[n]'(@length_pmap _ _ p f l h ▸ hn))
|
||||
(h _ (getElem_mem (@length_pmap _ _ p f l h ▸ hn))) := by
|
||||
(h _ (getElem_mem l n (@length_pmap _ _ p f l h ▸ hn))) := by
|
||||
induction l generalizing n with
|
||||
| nil =>
|
||||
simp only [length, pmap] at hn
|
||||
@@ -235,199 +181,7 @@ theorem get_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h :
|
||||
simp only [get_eq_getElem]
|
||||
simp [getElem_pmap]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_attachWith {xs : List α} {i : Nat} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||||
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (getElem?_mem a)) :=
|
||||
getElem?_pmap ..
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_attach {xs : List α} {i : Nat} :
|
||||
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => getElem?_mem a) :=
|
||||
getElem?_attachWith
|
||||
|
||||
@[simp]
|
||||
theorem getElem_attachWith {xs : List α} {P : α → Prop} {H : ∀ a ∈ xs, P a}
|
||||
{i : Nat} (h : i < (xs.attachWith P H).length) :
|
||||
(xs.attachWith P H)[i] = ⟨xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h))⟩ :=
|
||||
getElem_pmap ..
|
||||
|
||||
@[simp]
|
||||
theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
|
||||
xs.attach[i] = ⟨xs[i]'(by simpa using h), getElem_mem (by simpa using h)⟩ :=
|
||||
getElem_attachWith h
|
||||
|
||||
@[simp] theorem head?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).head? = xs.attach.head?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp at ih
|
||||
simp [head?_pmap, ih]
|
||||
|
||||
@[simp] theorem head_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (h : xs.pmap f H ≠ []) :
|
||||
(xs.pmap f H).head h = f (xs.head (by simpa using h)) (H _ (head_mem _)) := by
|
||||
induction xs with
|
||||
| nil => simp at h
|
||||
| cons x xs ih => simp [head_pmap, ih]
|
||||
|
||||
@[simp] theorem head?_attachWith {P : α → Prop} {xs : List α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.attachWith P H).head? = xs.head?.pbind (fun a h => some ⟨a, H _ (mem_of_mem_head? h)⟩) := by
|
||||
cases xs <;> simp_all
|
||||
|
||||
@[simp] theorem head_attachWith {P : α → Prop} {xs : List α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} (h : xs.attachWith P H ≠ []) :
|
||||
(xs.attachWith P H).head h = ⟨xs.head (by simpa using h), H _ (head_mem _)⟩ := by
|
||||
cases xs with
|
||||
| nil => simp at h
|
||||
| cons x xs => simp [head_attachWith, h]
|
||||
|
||||
@[simp] theorem head?_attach (xs : List α) :
|
||||
xs.attach.head? = xs.head?.pbind (fun a h => some ⟨a, mem_of_mem_head? h⟩) := by
|
||||
cases xs <;> simp_all
|
||||
|
||||
@[simp] theorem head_attach {xs : List α} (h) :
|
||||
xs.attach.head h = ⟨xs.head (by simpa using h), head_mem (by simpa using h)⟩ := by
|
||||
cases xs with
|
||||
| nil => simp at h
|
||||
| cons x xs => simp [head_attach, h]
|
||||
|
||||
@[simp] theorem tail_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).tail = xs.tail.pmap f (fun a h => H a (mem_of_mem_tail h)) := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[simp] theorem tail_attachWith {P : α → Prop} {xs : List α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).tail = xs.tail.attachWith P (fun a h => H a (mem_of_mem_tail h)) := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[simp] theorem tail_attach (xs : List α) :
|
||||
xs.attach.tail = xs.tail.attach.map (fun ⟨x, h⟩ => ⟨x, mem_of_mem_tail h⟩) := by
|
||||
cases xs <;> simp
|
||||
|
||||
theorem foldl_pmap (l : List α) {P : α → Prop} (f : (a : α) → P a → β)
|
||||
(H : ∀ (a : α), a ∈ l → P a) (g : γ → β → γ) (x : γ) :
|
||||
(l.pmap f H).foldl g x = l.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
|
||||
rw [pmap_eq_map_attach, foldl_map]
|
||||
|
||||
theorem foldr_pmap (l : List α) {P : α → Prop} (f : (a : α) → P a → β)
|
||||
(H : ∀ (a : α), a ∈ l → P a) (g : β → γ → γ) (x : γ) :
|
||||
(l.pmap f H).foldr g x = l.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
|
||||
rw [pmap_eq_map_attach, foldr_map]
|
||||
|
||||
/--
|
||||
If we fold over `l.attach` with a function that ignores the membership predicate,
|
||||
we get the same results as folding over `l` directly.
|
||||
|
||||
This is useful when we need to use `attach` to show termination.
|
||||
|
||||
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
|
||||
and even when rewriting we need to specify the function explicitly.
|
||||
-/
|
||||
theorem foldl_attach (l : List α) (f : β → α → β) (b : β) :
|
||||
l.attach.foldl (fun acc t => f acc t.1) b = l.foldl f b := by
|
||||
induction l generalizing b with
|
||||
| nil => simp
|
||||
| cons a l ih => rw [foldl_cons, attach_cons, foldl_cons, foldl_map, ih]
|
||||
|
||||
/--
|
||||
If we fold over `l.attach` with a function that ignores the membership predicate,
|
||||
we get the same results as folding over `l` directly.
|
||||
|
||||
This is useful when we need to use `attach` to show termination.
|
||||
|
||||
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
|
||||
and even when rewriting we need to specify the function explicitly.
|
||||
-/
|
||||
theorem foldr_attach (l : List α) (f : α → β → β) (b : β) :
|
||||
l.attach.foldr (fun t acc => f t.1 acc) b = l.foldr f b := by
|
||||
induction l generalizing b with
|
||||
| nil => simp
|
||||
| cons a l ih => rw [foldr_cons, attach_cons, foldr_cons, foldr_map, ih]
|
||||
|
||||
theorem attach_map {l : List α} (f : α → β) :
|
||||
(l.map f).attach = l.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem f h⟩) := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
theorem attachWith_map {l : List α} (f : α → β) {P : β → Prop} {H : ∀ (b : β), b ∈ l.map f → P b} :
|
||||
(l.map f).attachWith P H = (l.attachWith (P ∘ f) (fun a h => H _ (mem_map_of_mem f h))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
theorem map_attachWith {l : List α} {P : α → Prop} {H : ∀ (a : α), a ∈ l → P a}
|
||||
(f : { x // P x } → β) :
|
||||
(l.attachWith P H).map f =
|
||||
l.pmap (fun a (h : a ∈ l ∧ P a) => f ⟨a, H _ h.1⟩) (fun a h => ⟨h, H a h⟩) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [attachWith_cons, map_cons, ih, pmap, cons.injEq, true_and]
|
||||
apply pmap_congr_left
|
||||
simp
|
||||
|
||||
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
|
||||
theorem map_attach {l : List α} (f : { x // x ∈ l } → β) :
|
||||
l.attach.map f = l.pmap (fun a h => f ⟨a, h⟩) (fun _ => id) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [attach_cons, map_cons, map_map, Function.comp_apply, pmap, cons.injEq, true_and, ih]
|
||||
apply pmap_congr_left
|
||||
simp
|
||||
|
||||
theorem attach_filterMap {l : List α} {f : α → Option β} :
|
||||
(l.filterMap f).attach = l.attach.filterMap
|
||||
fun ⟨x, h⟩ => (f x).pbind (fun b m => some ⟨b, mem_filterMap.mpr ⟨x, h, m⟩⟩) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [filterMap_cons, attach_cons, ih, filterMap_map]
|
||||
split <;> rename_i h
|
||||
· simp only [Option.pbind_eq_none_iff, reduceCtorEq, Option.mem_def, exists_false,
|
||||
or_false] at h
|
||||
rw [attach_congr]
|
||||
rotate_left
|
||||
· simp only [h]
|
||||
rfl
|
||||
rw [ih]
|
||||
simp only [map_filterMap, Option.map_pbind, Option.map_some']
|
||||
rfl
|
||||
· simp only [Option.pbind_eq_some_iff] at h
|
||||
obtain ⟨a, h, w⟩ := h
|
||||
simp only [Option.some.injEq] at w
|
||||
subst w
|
||||
simp only [Option.mem_def] at h
|
||||
rw [attach_congr]
|
||||
rotate_left
|
||||
· simp only [h]
|
||||
rfl
|
||||
rw [attach_cons, map_cons, map_map, ih, map_filterMap]
|
||||
congr
|
||||
ext
|
||||
simp
|
||||
|
||||
theorem attach_filter {l : List α} (p : α → Bool) :
|
||||
(l.filter p).attach = l.attach.filterMap
|
||||
fun x => if w : p x.1 then some ⟨x.1, mem_filter.mpr ⟨x.2, w⟩⟩ else none := by
|
||||
rw [attach_congr (congrFun (filterMap_eq_filter _).symm _), attach_filterMap, map_filterMap]
|
||||
simp only [Option.guard]
|
||||
congr
|
||||
ext1
|
||||
split <;> simp
|
||||
|
||||
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
|
||||
-- Also missing are `filterMap_attach`, `filter_attach`, `filterMap_attachWith` and `filter_attachWith`.
|
||||
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} (g : ∀ a, p a → β) (f : ∀ b, q b → γ) (l H₁ H₂) :
|
||||
pmap f (pmap g l H₁) H₂ =
|
||||
pmap (α := { x // x ∈ l }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) l.attach
|
||||
(fun a _ => H₁ a a.2) := by
|
||||
simp [pmap_eq_map_attach, attach_map]
|
||||
|
||||
@[simp] theorem pmap_append {p : ι → Prop} (f : ∀ a : ι, p a → α) (l₁ l₂ : List ι)
|
||||
theorem pmap_append {p : ι → Prop} (f : ∀ a : ι, p a → α) (l₁ l₂ : List ι)
|
||||
(h : ∀ a ∈ l₁ ++ l₂, p a) :
|
||||
(l₁ ++ l₂).pmap f h =
|
||||
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
|
||||
@@ -443,109 +197,3 @@ theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ l₂ :
|
||||
((l₁ ++ l₂).pmap f fun a ha => (List.mem_append.1 ha).elim (h₁ a) (h₂ a)) =
|
||||
l₁.pmap f h₁ ++ l₂.pmap f h₂ :=
|
||||
pmap_append f l₁ l₂ _
|
||||
|
||||
@[simp] theorem attach_append (xs ys : List α) :
|
||||
(xs ++ ys).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_append_of_mem_left ys h⟩) ++
|
||||
ys.attach.map fun ⟨x, h⟩ => ⟨x, mem_append_of_mem_right xs h⟩ := by
|
||||
simp only [attach, attachWith, pmap, map_pmap, pmap_append]
|
||||
congr 1 <;>
|
||||
exact pmap_congr_left _ fun _ _ _ _ => rfl
|
||||
|
||||
@[simp] theorem attachWith_append {P : α → Prop} {xs ys : List α}
|
||||
{H : ∀ (a : α), a ∈ xs ++ ys → P a} :
|
||||
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_of_mem_left ys h)) ++
|
||||
ys.attachWith P (fun a h => H a (mem_append_of_mem_right xs h)) := by
|
||||
simp only [attachWith, attach_append, map_pmap, pmap_append]
|
||||
|
||||
@[simp] theorem pmap_reverse {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs.reverse → P a) :
|
||||
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
|
||||
induction xs <;> simp_all
|
||||
|
||||
theorem reverse_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
|
||||
rw [pmap_reverse]
|
||||
|
||||
@[simp] theorem attachWith_reverse {P : α → Prop} {xs : List α}
|
||||
{H : ∀ (a : α), a ∈ xs.reverse → P a} :
|
||||
xs.reverse.attachWith P H =
|
||||
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse :=
|
||||
pmap_reverse ..
|
||||
|
||||
theorem reverse_attachWith {P : α → Prop} {xs : List α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) :=
|
||||
reverse_pmap ..
|
||||
|
||||
@[simp] theorem attach_reverse (xs : List α) :
|
||||
xs.reverse.attach = xs.attach.reverse.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
simp only [attach, attachWith, reverse_pmap, map_pmap]
|
||||
apply pmap_congr_left
|
||||
intros
|
||||
rfl
|
||||
|
||||
theorem reverse_attach (xs : List α) :
|
||||
xs.attach.reverse = xs.reverse.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
simp only [attach, attachWith, reverse_pmap, map_pmap]
|
||||
apply pmap_congr_left
|
||||
intros
|
||||
rfl
|
||||
|
||||
@[simp] theorem getLast?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).getLast? = xs.attach.getLast?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
simp only [getLast?_eq_head?_reverse]
|
||||
rw [reverse_pmap, reverse_attach, head?_map, pmap_eq_map_attach, head?_map]
|
||||
simp only [Option.map_map]
|
||||
congr
|
||||
|
||||
@[simp] theorem getLast_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (h : xs.pmap f H ≠ []) :
|
||||
(xs.pmap f H).getLast h = f (xs.getLast (by simpa using h)) (H _ (getLast_mem _)) := by
|
||||
simp only [getLast_eq_head_reverse]
|
||||
simp only [reverse_pmap, head_pmap, head_reverse]
|
||||
|
||||
@[simp] theorem getLast?_attachWith {P : α → Prop} {xs : List α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).getLast? = xs.getLast?.pbind (fun a h => some ⟨a, H _ (mem_of_getLast?_eq_some h)⟩) := by
|
||||
rw [getLast?_eq_head?_reverse, reverse_attachWith, head?_attachWith]
|
||||
simp
|
||||
|
||||
@[simp] theorem getLast_attachWith {P : α → Prop} {xs : List α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} (h : xs.attachWith P H ≠ []) :
|
||||
(xs.attachWith P H).getLast h = ⟨xs.getLast (by simpa using h), H _ (getLast_mem _)⟩ := by
|
||||
simp only [getLast_eq_head_reverse, reverse_attachWith, head_attachWith, head_map]
|
||||
|
||||
@[simp]
|
||||
theorem getLast?_attach {xs : List α} :
|
||||
xs.attach.getLast? = xs.getLast?.pbind fun a h => some ⟨a, mem_of_getLast?_eq_some h⟩ := by
|
||||
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map, head?_attach]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem getLast_attach {xs : List α} (h : xs.attach ≠ []) :
|
||||
xs.attach.getLast h = ⟨xs.getLast (by simpa using h), getLast_mem (by simpa using h)⟩ := by
|
||||
simp only [getLast_eq_head_reverse, reverse_attach, head_map, head_attach]
|
||||
|
||||
@[simp]
|
||||
theorem countP_attach (l : List α) (p : α → Bool) :
|
||||
l.attach.countP (fun a : {x // x ∈ l} => p a) = l.countP p := by
|
||||
simp only [← Function.comp_apply (g := Subtype.val), ← countP_map, attach_map_subtype_val]
|
||||
|
||||
@[simp]
|
||||
theorem countP_attachWith {p : α → Prop} (l : List α) (H : ∀ a ∈ l, p a) (q : α → Bool) :
|
||||
(l.attachWith p H).countP (fun a : {x // p x} => q a) = l.countP q := by
|
||||
simp only [← Function.comp_apply (g := Subtype.val), ← countP_map, attachWith_map_subtype_val]
|
||||
|
||||
@[simp]
|
||||
theorem count_attach [DecidableEq α] (l : List α) (a : {x // x ∈ l}) :
|
||||
l.attach.count a = l.count ↑a :=
|
||||
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attach _ _
|
||||
|
||||
@[simp]
|
||||
theorem count_attachWith [DecidableEq α] {p : α → Prop} (l : List α) (H : ∀ a ∈ l, p a) (a : {x // p x}) :
|
||||
(l.attachWith p H).count a = l.count ↑a :=
|
||||
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attachWith _ _ _
|
||||
|
||||
end List
|
||||
|
||||
@@ -43,7 +43,7 @@ The operations are organized as follow:
|
||||
* Logic: `any`, `all`, `or`, and `and`.
|
||||
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
|
||||
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
|
||||
* Minima and maxima: `min?` and `max?`.
|
||||
* Minima and maxima: `minimum?` and `maximum?`.
|
||||
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`,
|
||||
`removeAll`
|
||||
(currently these functions are mostly only used in meta code,
|
||||
@@ -96,7 +96,7 @@ namespace List
|
||||
|
||||
/-! ### concat -/
|
||||
|
||||
theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
|
||||
@[simp high] theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
|
||||
induction as with
|
||||
| nil => rfl
|
||||
| cons _ xs ih => simp [concat, ih]
|
||||
@@ -278,9 +278,8 @@ def getLastD : (as : List α) → (fallback : α) → α
|
||||
| [], a₀ => a₀
|
||||
| a::as, _ => getLast (a::as) (fun h => List.noConfusion h)
|
||||
|
||||
-- These aren't `simp` lemmas since we always simplify `getLastD` in terms of `getLast?`.
|
||||
theorem getLastD_nil (a) : @getLastD α [] a = a := rfl
|
||||
theorem getLastD_cons (a b l) : @getLastD α (b::l) a = getLastD l b := by cases l <;> rfl
|
||||
@[simp] theorem getLastD_nil (a) : @getLastD α [] a = a := rfl
|
||||
@[simp] theorem getLastD_cons (a b l) : @getLastD α (b::l) a = getLastD l b := by cases l <;> rfl
|
||||
|
||||
/-! ## Head and tail -/
|
||||
|
||||
@@ -689,7 +688,7 @@ inductive Mem (a : α) : List α → Prop
|
||||
| tail (b : α) {as : List α} : Mem a as → Mem a (b::as)
|
||||
|
||||
instance : Membership α (List α) where
|
||||
mem l a := Mem a l
|
||||
mem := Mem
|
||||
|
||||
theorem mem_of_elem_eq_true [BEq α] [LawfulBEq α] {a : α} {as : List α} : elem a as = true → a ∈ as := by
|
||||
match as with
|
||||
@@ -963,26 +962,6 @@ def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists f
|
||||
|
||||
@[inherit_doc] infixl:50 " <:+: " => IsInfix
|
||||
|
||||
/-! ### splitAt -/
|
||||
|
||||
/--
|
||||
Split a list at an index.
|
||||
```
|
||||
splitAt 2 [a, b, c] = ([a, b], [c])
|
||||
```
|
||||
-/
|
||||
def splitAt (n : Nat) (l : List α) : List α × List α := go l n [] where
|
||||
/--
|
||||
Auxiliary for `splitAt`:
|
||||
`splitAt.go l xs n acc = (acc.reverse ++ take n xs, drop n xs)` if `n < xs.length`,
|
||||
and `(l, [])` otherwise.
|
||||
-/
|
||||
go : List α → Nat → List α → List α × List α
|
||||
| [], _, _ => (l, []) -- This branch ensures the pointer equality of the result with the input
|
||||
-- without any runtime branching cost.
|
||||
| x :: xs, n+1, acc => go xs n (x :: acc)
|
||||
| xs, _, acc => (acc.reverse, xs)
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
/--
|
||||
@@ -1244,36 +1223,6 @@ theorem lookup_cons [BEq α] {k : α} :
|
||||
((k,b)::es).lookup a = match a == k with | true => some b | false => es.lookup a :=
|
||||
rfl
|
||||
|
||||
/-! ## Permutations -/
|
||||
|
||||
/-! ### Perm -/
|
||||
|
||||
/--
|
||||
`Perm l₁ l₂` or `l₁ ~ l₂` asserts that `l₁` and `l₂` are permutations
|
||||
of each other. This is defined by induction using pairwise swaps.
|
||||
-/
|
||||
inductive Perm : List α → List α → Prop
|
||||
/-- `[] ~ []` -/
|
||||
| nil : Perm [] []
|
||||
/-- `l₁ ~ l₂ → x::l₁ ~ x::l₂` -/
|
||||
| cons (x : α) {l₁ l₂ : List α} : Perm l₁ l₂ → Perm (x :: l₁) (x :: l₂)
|
||||
/-- `x::y::l ~ y::x::l` -/
|
||||
| swap (x y : α) (l : List α) : Perm (y :: x :: l) (x :: y :: l)
|
||||
/-- `Perm` is transitive. -/
|
||||
| trans {l₁ l₂ l₃ : List α} : Perm l₁ l₂ → Perm l₂ l₃ → Perm l₁ l₃
|
||||
|
||||
@[inherit_doc] scoped infixl:50 " ~ " => Perm
|
||||
|
||||
/-! ### isPerm -/
|
||||
|
||||
/--
|
||||
`O(|l₁| * |l₂|)`. Computes whether `l₁` is a permutation of `l₂`. See `isPerm_iff` for a
|
||||
characterization in terms of `List.Perm`.
|
||||
-/
|
||||
def isPerm [BEq α] : List α → List α → Bool
|
||||
| [], l₂ => l₂.isEmpty
|
||||
| a :: l₁, l₂ => l₂.contains a && l₁.isPerm (l₂.erase a)
|
||||
|
||||
/-! ## Logical operations -/
|
||||
|
||||
/-! ### any -/
|
||||
@@ -1464,34 +1413,30 @@ def enum : List α → List (Nat × α) := enumFrom 0
|
||||
|
||||
/-! ## Minima and maxima -/
|
||||
|
||||
/-! ### min? -/
|
||||
/-! ### minimum? -/
|
||||
|
||||
/--
|
||||
Returns the smallest element of the list, if it is not empty.
|
||||
* `[].min? = none`
|
||||
* `[4].min? = some 4`
|
||||
* `[1, 4, 2, 10, 6].min? = some 1`
|
||||
* `[].minimum? = none`
|
||||
* `[4].minimum? = some 4`
|
||||
* `[1, 4, 2, 10, 6].minimum? = some 1`
|
||||
-/
|
||||
def min? [Min α] : List α → Option α
|
||||
def minimum? [Min α] : List α → Option α
|
||||
| [] => none
|
||||
| a::as => some <| as.foldl min a
|
||||
|
||||
@[inherit_doc min?, deprecated min? (since := "2024-09-29")] abbrev minimum? := @min?
|
||||
|
||||
/-! ### max? -/
|
||||
/-! ### maximum? -/
|
||||
|
||||
/--
|
||||
Returns the largest element of the list, if it is not empty.
|
||||
* `[].max? = none`
|
||||
* `[4].max? = some 4`
|
||||
* `[1, 4, 2, 10, 6].max? = some 10`
|
||||
* `[].maximum? = none`
|
||||
* `[4].maximum? = some 4`
|
||||
* `[1, 4, 2, 10, 6].maximum? = some 10`
|
||||
-/
|
||||
def max? [Max α] : List α → Option α
|
||||
def maximum? [Max α] : List α → Option α
|
||||
| [] => none
|
||||
| a::as => some <| as.foldl max a
|
||||
|
||||
@[inherit_doc max?, deprecated max? (since := "2024-09-29")] abbrev maximum? := @max?
|
||||
|
||||
/-! ## Other list operations
|
||||
|
||||
The functions are currently mostly used in meta code,
|
||||
@@ -1592,14 +1537,6 @@ such that adjacent elements are related by `R`.
|
||||
| [] => []
|
||||
| a::as => loop as a [] []
|
||||
where
|
||||
/--
|
||||
The arguments of `groupBy.loop l ag g gs` represent the following:
|
||||
|
||||
- `l : List α` are the elements which we still need to group.
|
||||
- `ag : α` is the previous element for which a comparison was performed.
|
||||
- `g : List α` is the group currently being assembled, in **reverse order**.
|
||||
- `gs : List (List α)` is all of the groups that have been completed, in **reverse order**.
|
||||
-/
|
||||
@[specialize] loop : List α → α → List α → List (List α) → List (List α)
|
||||
| a::as, ag, g, gs => match R ag a with
|
||||
| true => loop as a (ag::g) gs
|
||||
@@ -1615,178 +1552,4 @@ by filtering out all elements of `xs` which are also in `ys`.
|
||||
def removeAll [BEq α] (xs ys : List α) : List α :=
|
||||
xs.filter (fun x => !ys.elem x)
|
||||
|
||||
/-!
|
||||
# Runtime re-implementations using `@[csimp]`
|
||||
|
||||
More of these re-implementations are provided in `Init/Data/List/Impl.lean`.
|
||||
They can not be here, because the remaining ones required `Array` for their implementation.
|
||||
|
||||
This leaves a dangerous situation: if you import this file, but not `Init/Data/List/Impl.lean`,
|
||||
then at runtime you will get non tail-recursive versions.
|
||||
-/
|
||||
|
||||
/-! ### length -/
|
||||
|
||||
theorem length_add_eq_lengthTRAux (as : List α) (n : Nat) : as.length + n = as.lengthTRAux n := by
|
||||
induction as generalizing n with
|
||||
| nil => simp [length, lengthTRAux]
|
||||
| cons a as ih =>
|
||||
simp [length, lengthTRAux, ← ih, Nat.succ_add]
|
||||
rfl
|
||||
|
||||
@[csimp] theorem length_eq_lengthTR : @List.length = @List.lengthTR := by
|
||||
apply funext; intro α; apply funext; intro as
|
||||
simp [lengthTR, ← length_add_eq_lengthTRAux]
|
||||
|
||||
/-! ### map -/
|
||||
|
||||
/-- Tail-recursive version of `List.map`. -/
|
||||
@[inline] def mapTR (f : α → β) (as : List α) : List β :=
|
||||
loop as []
|
||||
where
|
||||
@[specialize] loop : List α → List β → List β
|
||||
| [], bs => bs.reverse
|
||||
| a::as, bs => loop as (f a :: bs)
|
||||
|
||||
theorem mapTR_loop_eq (f : α → β) (as : List α) (bs : List β) :
|
||||
mapTR.loop f as bs = bs.reverse ++ map f as := by
|
||||
induction as generalizing bs with
|
||||
| nil => simp [mapTR.loop, map]
|
||||
| cons a as ih =>
|
||||
simp only [mapTR.loop, map]
|
||||
rw [ih (f a :: bs), reverse_cons, append_assoc]
|
||||
rfl
|
||||
|
||||
@[csimp] theorem map_eq_mapTR : @map = @mapTR :=
|
||||
funext fun α => funext fun β => funext fun f => funext fun as => by
|
||||
simp [mapTR, mapTR_loop_eq]
|
||||
|
||||
/-! ### filter -/
|
||||
|
||||
/-- Tail-recursive version of `List.filter`. -/
|
||||
@[inline] def filterTR (p : α → Bool) (as : List α) : List α :=
|
||||
loop as []
|
||||
where
|
||||
@[specialize] loop : List α → List α → List α
|
||||
| [], rs => rs.reverse
|
||||
| a::as, rs => match p a with
|
||||
| true => loop as (a::rs)
|
||||
| false => loop as rs
|
||||
|
||||
theorem filterTR_loop_eq (p : α → Bool) (as bs : List α) :
|
||||
filterTR.loop p as bs = bs.reverse ++ filter p as := by
|
||||
induction as generalizing bs with
|
||||
| nil => simp [filterTR.loop, filter]
|
||||
| cons a as ih =>
|
||||
simp only [filterTR.loop, filter]
|
||||
split <;> simp_all
|
||||
|
||||
@[csimp] theorem filter_eq_filterTR : @filter = @filterTR := by
|
||||
apply funext; intro α; apply funext; intro p; apply funext; intro as
|
||||
simp [filterTR, filterTR_loop_eq]
|
||||
|
||||
/-! ### replicate -/
|
||||
|
||||
/-- Tail-recursive version of `List.replicate`. -/
|
||||
def replicateTR {α : Type u} (n : Nat) (a : α) : List α :=
|
||||
let rec loop : Nat → List α → List α
|
||||
| 0, as => as
|
||||
| n+1, as => loop n (a::as)
|
||||
loop n []
|
||||
|
||||
theorem replicateTR_loop_replicate_eq (a : α) (m n : Nat) :
|
||||
replicateTR.loop a n (replicate m a) = replicate (n + m) a := by
|
||||
induction n generalizing m with simp [replicateTR.loop]
|
||||
| succ n ih => simp [Nat.succ_add]; exact ih (m+1)
|
||||
|
||||
theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++ acc
|
||||
| 0 => rfl
|
||||
| n+1 => by rw [← replicateTR_loop_replicate_eq _ 1 n, replicate, replicate,
|
||||
replicateTR.loop, replicateTR_loop_eq n, replicateTR_loop_eq n, append_assoc]; rfl
|
||||
|
||||
@[csimp] theorem replicate_eq_replicateTR : @List.replicate = @List.replicateTR := by
|
||||
apply funext; intro α; apply funext; intro n; apply funext; intro a
|
||||
exact (replicateTR_loop_replicate_eq _ 0 n).symm
|
||||
|
||||
/-! ## Additional functions -/
|
||||
|
||||
/-! ### leftpad -/
|
||||
|
||||
/-- Optimized version of `leftpad`. -/
|
||||
@[inline] def leftpadTR (n : Nat) (a : α) (l : List α) : List α :=
|
||||
replicateTR.loop a (n - length l) l
|
||||
|
||||
@[csimp] theorem leftpad_eq_leftpadTR : @leftpad = @leftpadTR := by
|
||||
repeat (apply funext; intro)
|
||||
simp [leftpad, leftpadTR, replicateTR_loop_eq]
|
||||
|
||||
|
||||
/-! ## Zippers -/
|
||||
|
||||
/-! ### unzip -/
|
||||
|
||||
/-- Tail recursive version of `List.unzip`. -/
|
||||
def unzipTR (l : List (α × β)) : List α × List β :=
|
||||
l.foldr (fun (a, b) (al, bl) => (a::al, b::bl)) ([], [])
|
||||
|
||||
@[csimp] theorem unzip_eq_unzipTR : @unzip = @unzipTR := by
|
||||
apply funext; intro α; apply funext; intro β; apply funext; intro l
|
||||
simp [unzipTR]; induction l <;> simp [*]
|
||||
|
||||
/-! ## Ranges and enumeration -/
|
||||
|
||||
/-! ### range' -/
|
||||
|
||||
/-- Optimized version of `range'`. -/
|
||||
@[inline] def range'TR (s n : Nat) (step : Nat := 1) : List Nat := go n (s + step * n) [] where
|
||||
/-- Auxiliary for `range'TR`: `range'TR.go n e = [e-n, ..., e-1] ++ acc`. -/
|
||||
go : Nat → Nat → List Nat → List Nat
|
||||
| 0, _, acc => acc
|
||||
| n+1, e, acc => go n (e-step) ((e-step) :: acc)
|
||||
|
||||
@[csimp] theorem range'_eq_range'TR : @range' = @range'TR := by
|
||||
apply funext; intro s; apply funext; intro n; apply funext; intro step
|
||||
let rec go (s) : ∀ n m,
|
||||
range'TR.go step n (s + step * n) (range' (s + step * n) m step) = range' s (n + m) step
|
||||
| 0, m => by simp [range'TR.go]
|
||||
| n+1, m => by
|
||||
simp [range'TR.go]
|
||||
rw [Nat.mul_succ, ← Nat.add_assoc, Nat.add_sub_cancel, Nat.add_right_comm n]
|
||||
exact go s n (m + 1)
|
||||
exact (go s n 0).symm
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
/-- Tail-recursive version of `List.iota`. -/
|
||||
def iotaTR (n : Nat) : List Nat :=
|
||||
let rec go : Nat → List Nat → List Nat
|
||||
| 0, r => r.reverse
|
||||
| m@(n+1), r => go n (m::r)
|
||||
go n []
|
||||
|
||||
@[csimp]
|
||||
theorem iota_eq_iotaTR : @iota = @iotaTR :=
|
||||
have aux (n : Nat) (r : List Nat) : iotaTR.go n r = r.reverse ++ iota n := by
|
||||
induction n generalizing r with
|
||||
| zero => simp [iota, iotaTR.go]
|
||||
| succ n ih => simp [iota, iotaTR.go, ih, append_assoc]
|
||||
funext fun n => by simp [iotaTR, aux]
|
||||
|
||||
/-! ## Other list operations -/
|
||||
|
||||
/-! ### intersperse -/
|
||||
|
||||
/-- Tail recursive version of `List.intersperse`. -/
|
||||
def intersperseTR (sep : α) : List α → List α
|
||||
| [] => []
|
||||
| [x] => [x]
|
||||
| x::y::xs => x :: sep :: y :: xs.foldr (fun a r => sep :: a :: r) []
|
||||
|
||||
@[csimp] theorem intersperse_eq_intersperseTR : @intersperse = @intersperseTR := by
|
||||
apply funext; intro α; apply funext; intro sep; apply funext; intro l
|
||||
simp [intersperseTR]
|
||||
match l with
|
||||
| [] | [_] => rfl
|
||||
| x::y::xs => simp [intersperse]; induction xs generalizing y <;> simp [*]
|
||||
|
||||
end List
|
||||
|
||||
@@ -155,7 +155,7 @@ def mapMono (as : List α) (f : α → α) : List α :=
|
||||
|
||||
/-! ## Additional lemmas required for bootstrapping `Array`. -/
|
||||
|
||||
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h'} : (as ++ bs)[i] = as[i] := by
|
||||
theorem getElem_append_left (as bs : List α) (h : i < as.length) {h'} : (as ++ bs)[i] = as[i] := by
|
||||
induction as generalizing i with
|
||||
| nil => trivial
|
||||
| cons a as ih =>
|
||||
@@ -163,14 +163,12 @@ theorem getElem_append_left {as bs : List α} (h : i < as.length) {h'} : (as ++
|
||||
| zero => rfl
|
||||
| succ i => apply ih
|
||||
|
||||
theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length ≤ i) {h₂} :
|
||||
(as ++ bs)[i]'h₂ =
|
||||
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by
|
||||
theorem getElem_append_right (as bs : List α) (h : ¬ i < as.length) {h' h''} : (as ++ bs)[i]'h' = bs[i - as.length]'h'' := by
|
||||
induction as generalizing i with
|
||||
| nil => trivial
|
||||
| cons a as ih =>
|
||||
cases i with simp [get, Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h₁
|
||||
| succ i => apply ih; simp [h₁]
|
||||
cases i with simp [get, Nat.succ_sub_succ] <;> simp_arith [Nat.succ_sub_succ] at h
|
||||
| succ i => apply ih; simp_arith [h]
|
||||
|
||||
theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.length) : (as ++ [a] : List _).get i = a := by
|
||||
cases i; rename_i i h'
|
||||
@@ -179,8 +177,8 @@ theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.
|
||||
| zero => simp [List.get]
|
||||
| succ => simp_arith at h'
|
||||
| cons a as ih =>
|
||||
cases i with simp at h
|
||||
| succ i => apply ih; simp [h]
|
||||
cases i with simp_arith at h
|
||||
| succ i => apply ih; simp_arith [h]
|
||||
|
||||
theorem sizeOf_lt_of_mem [SizeOf α] {as : List α} (h : a ∈ as) : sizeOf a < sizeOf as := by
|
||||
induction h with
|
||||
@@ -194,7 +192,7 @@ macro "sizeOf_list_dec" : tactic =>
|
||||
`(tactic| first
|
||||
| with_reducible apply sizeOf_lt_of_mem; assumption; done
|
||||
| with_reducible
|
||||
apply Nat.lt_of_lt_of_le (sizeOf_lt_of_mem ?h)
|
||||
apply Nat.lt_trans (sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
simp_arith)
|
||||
|
||||
@@ -224,7 +222,7 @@ theorem append_cancel_right {as bs cs : List α} (h : as ++ bs = cs ++ bs) : as
|
||||
next => apply append_cancel_right
|
||||
next => intro h; simp [h]
|
||||
|
||||
theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
|
||||
@[simp] theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
|
||||
match as, i with
|
||||
| a::as, ⟨0, _⟩ => simp_arith [get]
|
||||
| a::as, ⟨i+1, h⟩ =>
|
||||
|
||||
@@ -40,9 +40,6 @@ protected theorem countP_go_eq_add (l) : countP.go p l n = n + countP.go p l 0 :
|
||||
theorem countP_cons (a : α) (l) : countP p (a :: l) = countP p l + if p a then 1 else 0 := by
|
||||
by_cases h : p a <;> simp [h]
|
||||
|
||||
theorem countP_singleton (a : α) : countP p [a] = if p a then 1 else 0 := by
|
||||
simp [countP_cons]
|
||||
|
||||
theorem length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a => ¬p a) l := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
@@ -50,11 +47,11 @@ theorem length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a
|
||||
if h : p x then
|
||||
rw [countP_cons_of_pos _ _ h, countP_cons_of_neg _ _ _, length, ih]
|
||||
· rw [Nat.add_assoc, Nat.add_comm _ 1, Nat.add_assoc]
|
||||
· simp [h]
|
||||
· simp only [h, not_true_eq_false, decide_False, not_false_eq_true]
|
||||
else
|
||||
rw [countP_cons_of_pos (fun a => ¬p a) _ _, countP_cons_of_neg _ _ h, length, ih]
|
||||
· rfl
|
||||
· simp [h]
|
||||
· simp only [h, not_false_eq_true, decide_True]
|
||||
|
||||
theorem countP_eq_length_filter (l) : countP p l = length (filter p l) := by
|
||||
induction l with
|
||||
@@ -64,10 +61,6 @@ theorem countP_eq_length_filter (l) : countP p l = length (filter p l) := by
|
||||
then rw [countP_cons_of_pos p l h, ih, filter_cons_of_pos h, length]
|
||||
else rw [countP_cons_of_neg p l h, ih, filter_cons_of_neg h]
|
||||
|
||||
theorem countP_eq_length_filter' : countP p = length ∘ filter p := by
|
||||
funext l
|
||||
apply countP_eq_length_filter
|
||||
|
||||
theorem countP_le_length : countP p l ≤ l.length := by
|
||||
simp only [countP_eq_length_filter]
|
||||
apply length_filter_le
|
||||
@@ -75,63 +68,29 @@ theorem countP_le_length : countP p l ≤ l.length := by
|
||||
@[simp] theorem countP_append (l₁ l₂) : countP p (l₁ ++ l₂) = countP p l₁ + countP p l₂ := by
|
||||
simp only [countP_eq_length_filter, filter_append, length_append]
|
||||
|
||||
@[simp] theorem countP_pos_iff {p} : 0 < countP p l ↔ ∃ a ∈ l, p a := by
|
||||
theorem countP_pos : 0 < countP p l ↔ ∃ a ∈ l, p a := by
|
||||
simp only [countP_eq_length_filter, length_pos_iff_exists_mem, mem_filter, exists_prop]
|
||||
|
||||
@[deprecated countP_pos_iff (since := "2024-09-09")] abbrev countP_pos := @countP_pos_iff
|
||||
theorem countP_eq_zero : countP p l = 0 ↔ ∀ a ∈ l, ¬p a := by
|
||||
simp only [countP_eq_length_filter, length_eq_zero, filter_eq_nil]
|
||||
|
||||
@[simp] theorem one_le_countP_iff {p} : 1 ≤ countP p l ↔ ∃ a ∈ l, p a :=
|
||||
countP_pos_iff
|
||||
|
||||
@[simp] theorem countP_eq_zero {p} : countP p l = 0 ↔ ∀ a ∈ l, ¬p a := by
|
||||
simp only [countP_eq_length_filter, length_eq_zero, filter_eq_nil_iff]
|
||||
|
||||
@[simp] theorem countP_eq_length {p} : countP p l = l.length ↔ ∀ a ∈ l, p a := by
|
||||
theorem countP_eq_length : countP p l = l.length ↔ ∀ a ∈ l, p a := by
|
||||
rw [countP_eq_length_filter, filter_length_eq_length]
|
||||
|
||||
theorem countP_replicate (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (replicate n a) = if p a then n else 0 := by
|
||||
simp only [countP_eq_length_filter, filter_replicate]
|
||||
split <;> simp
|
||||
|
||||
theorem boole_getElem_le_countP (p : α → Bool) (l : List α) (i : Nat) (h : i < l.length) :
|
||||
(if p l[i] then 1 else 0) ≤ l.countP p := by
|
||||
induction l generalizing i with
|
||||
| nil => simp at h
|
||||
| cons x l ih =>
|
||||
cases i with
|
||||
| zero => simp [countP_cons]
|
||||
| succ i =>
|
||||
simp only [length_cons, add_one_lt_add_one_iff] at h
|
||||
simp only [getElem_cons_succ, countP_cons]
|
||||
specialize ih _ h
|
||||
exact le_add_right_of_le ih
|
||||
|
||||
theorem Sublist.countP_le (s : l₁ <+ l₂) : countP p l₁ ≤ countP p l₂ := by
|
||||
simp only [countP_eq_length_filter]
|
||||
apply s.filter _ |>.length_le
|
||||
|
||||
theorem IsPrefix.countP_le (s : l₁ <+: l₂) : countP p l₁ ≤ countP p l₂ := s.sublist.countP_le _
|
||||
theorem IsSuffix.countP_le (s : l₁ <:+ l₂) : countP p l₁ ≤ countP p l₂ := s.sublist.countP_le _
|
||||
theorem IsInfix.countP_le (s : l₁ <:+: l₂) : countP p l₁ ≤ countP p l₂ := s.sublist.countP_le _
|
||||
|
||||
-- See `Init.Data.List.Nat.Count` for `Sublist.le_countP : countP p l₂ - (l₂.length - l₁.length) ≤ countP p l₁`.
|
||||
|
||||
theorem countP_tail_le (l) : countP p l.tail ≤ countP p l :=
|
||||
(tail_sublist l).countP_le _
|
||||
|
||||
-- See `Init.Data.List.Nat.Count` for `le_countP_tail : countP p l - 1 ≤ countP p l.tail`.
|
||||
|
||||
theorem countP_filter (l : List α) :
|
||||
countP p (filter q l) = countP (fun a => p a && q a) l := by
|
||||
countP p (filter q l) = countP (fun a => p a ∧ q a) l := by
|
||||
simp only [countP_eq_length_filter, filter_filter]
|
||||
|
||||
@[simp] theorem countP_true : (countP fun (_ : α) => true) = length := by
|
||||
funext l
|
||||
@[simp] theorem countP_true {l : List α} : (l.countP fun _ => true) = l.length := by
|
||||
rw [countP_eq_length]
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_false : (countP fun (_ : α) => false) = Function.const _ 0 := by
|
||||
funext l
|
||||
@[simp] theorem countP_false {l : List α} : (l.countP fun _ => false) = 0 := by
|
||||
rw [countP_eq_zero]
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_map (p : β → Bool) (f : α → β) :
|
||||
@@ -139,30 +98,6 @@ theorem countP_filter (l : List α) :
|
||||
| [] => rfl
|
||||
| a :: l => by rw [map_cons, countP_cons, countP_cons, countP_map p f l]; rfl
|
||||
|
||||
theorem length_filterMap_eq_countP (f : α → Option β) (l : List α) :
|
||||
(filterMap f l).length = countP (fun a => (f a).isSome) l := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x l ih =>
|
||||
simp only [filterMap_cons, countP_cons]
|
||||
split <;> simp [ih, *]
|
||||
|
||||
theorem countP_filterMap (p : β → Bool) (f : α → Option β) (l : List α) :
|
||||
countP p (filterMap f l) = countP (fun a => ((f a).map p).getD false) l := by
|
||||
simp only [countP_eq_length_filter, filter_filterMap, ← filterMap_eq_filter]
|
||||
simp only [length_filterMap_eq_countP]
|
||||
congr
|
||||
ext a
|
||||
simp (config := { contextual := true }) [Option.getD_eq_iff]
|
||||
|
||||
@[simp] theorem countP_join (l : List (List α)) :
|
||||
countP p l.join = Nat.sum (l.map (countP p)) := by
|
||||
simp only [countP_eq_length_filter, filter_join]
|
||||
simp [countP_eq_length_filter']
|
||||
|
||||
@[simp] theorem countP_reverse (l : List α) : countP p l.reverse = countP p l := by
|
||||
simp [countP_eq_length_filter, filter_reverse]
|
||||
|
||||
variable {p q}
|
||||
|
||||
theorem countP_mono_left (h : ∀ x ∈ l, p x → q x) : countP p l ≤ countP q l := by
|
||||
@@ -197,11 +132,6 @@ theorem count_cons (a b : α) (l : List α) :
|
||||
count a (b :: l) = count a l + if b == a then 1 else 0 := by
|
||||
simp [count, countP_cons]
|
||||
|
||||
theorem count_eq_countP (a : α) (l : List α) : count a l = countP (· == a) l := rfl
|
||||
theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
|
||||
funext l
|
||||
apply count_eq_countP
|
||||
|
||||
theorem count_tail : ∀ (l : List α) (a : α) (h : l ≠ []),
|
||||
l.tail.count a = l.count a - if l.head h == a then 1 else 0
|
||||
| head :: tail, a, _ => by simp [count_cons]
|
||||
@@ -210,17 +140,6 @@ theorem count_le_length (a : α) (l : List α) : count a l ≤ l.length := count
|
||||
|
||||
theorem Sublist.count_le (h : l₁ <+ l₂) (a : α) : count a l₁ ≤ count a l₂ := h.countP_le _
|
||||
|
||||
theorem IsPrefix.count_le (h : l₁ <+: l₂) (a : α) : count a l₁ ≤ count a l₂ := h.sublist.count_le _
|
||||
theorem IsSuffix.count_le (h : l₁ <:+ l₂) (a : α) : count a l₁ ≤ count a l₂ := h.sublist.count_le _
|
||||
theorem IsInfix.count_le (h : l₁ <:+: l₂) (a : α) : count a l₁ ≤ count a l₂ := h.sublist.count_le _
|
||||
|
||||
-- See `Init.Data.List.Nat.Count` for `Sublist.le_count : count a l₂ - (l₂.length - l₁.length) ≤ countP a l₁`.
|
||||
|
||||
theorem count_tail_le (a : α) (l) : count a l.tail ≤ count a l :=
|
||||
(tail_sublist l).count_le _
|
||||
|
||||
-- See `Init.Data.List.Nat.Count` for `le_count_tail : count a l - 1 ≤ count a l.tail`.
|
||||
|
||||
theorem count_le_count_cons (a b : α) (l : List α) : count a l ≤ count a (b :: l) :=
|
||||
(sublist_cons_self _ _).count_le _
|
||||
|
||||
@@ -230,17 +149,6 @@ theorem count_singleton (a b : α) : count a [b] = if b == a then 1 else 0 := by
|
||||
@[simp] theorem count_append (a : α) : ∀ l₁ l₂, count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=
|
||||
countP_append _
|
||||
|
||||
theorem count_join (a : α) (l : List (List α)) : count a l.join = Nat.sum (l.map (count a)) := by
|
||||
simp only [count_eq_countP, countP_join, count_eq_countP']
|
||||
|
||||
@[simp] theorem count_reverse (a : α) (l : List α) : count a l.reverse = count a l := by
|
||||
simp only [count_eq_countP, countP_eq_length_filter, filter_reverse, length_reverse]
|
||||
|
||||
theorem boole_getElem_le_count (a : α) (l : List α) (i : Nat) (h : i < l.length) :
|
||||
(if l[i] == a then 1 else 0) ≤ l.count a := by
|
||||
rw [count_eq_countP]
|
||||
apply boole_getElem_le_countP (· == a)
|
||||
|
||||
variable [LawfulBEq α]
|
||||
|
||||
@[simp] theorem count_cons_self (a : α) (l : List α) : count a (a :: l) = count a l + 1 := by
|
||||
@@ -256,19 +164,14 @@ theorem count_concat_self (a : α) (l : List α) :
|
||||
count a (concat l a) = (count a l) + 1 := by simp
|
||||
|
||||
@[simp]
|
||||
theorem count_pos_iff {a : α} {l : List α} : 0 < count a l ↔ a ∈ l := by
|
||||
simp only [count, countP_pos_iff, beq_iff_eq, exists_eq_right]
|
||||
|
||||
@[deprecated count_pos_iff (since := "2024-09-09")] abbrev count_pos_iff_mem := @count_pos_iff
|
||||
|
||||
@[simp] theorem one_le_count_iff {a : α} {l : List α} : 1 ≤ count a l ↔ a ∈ l :=
|
||||
count_pos_iff
|
||||
theorem count_pos_iff_mem {a : α} {l : List α} : 0 < count a l ↔ a ∈ l := by
|
||||
simp only [count, countP_pos, beq_iff_eq, exists_eq_right]
|
||||
|
||||
theorem count_eq_zero_of_not_mem {a : α} {l : List α} (h : a ∉ l) : count a l = 0 :=
|
||||
Decidable.byContradiction fun h' => h <| count_pos_iff.1 (Nat.pos_of_ne_zero h')
|
||||
Decidable.byContradiction fun h' => h <| count_pos_iff_mem.1 (Nat.pos_of_ne_zero h')
|
||||
|
||||
theorem not_mem_of_count_eq_zero {a : α} {l : List α} (h : count a l = 0) : a ∉ l :=
|
||||
fun h' => Nat.ne_of_lt (count_pos_iff.2 h') h.symm
|
||||
fun h' => Nat.ne_of_lt (count_pos_iff_mem.2 h') h.symm
|
||||
|
||||
theorem count_eq_zero {l : List α} : count a l = 0 ↔ a ∉ l :=
|
||||
⟨not_mem_of_count_eq_zero, count_eq_zero_of_not_mem⟩
|
||||
@@ -288,7 +191,7 @@ theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b ==
|
||||
· exact count_eq_zero.2 <| mt eq_of_mem_replicate (Ne.symm h)
|
||||
|
||||
theorem filter_beq (l : List α) (a : α) : l.filter (· == a) = replicate (count a l) a := by
|
||||
simp only [count, countP_eq_length_filter, eq_replicate_iff, mem_filter, beq_iff_eq]
|
||||
simp only [count, countP_eq_length_filter, eq_replicate, mem_filter, beq_iff_eq]
|
||||
exact ⟨trivial, fun _ h => h.2⟩
|
||||
|
||||
theorem filter_eq {α} [DecidableEq α] (l : List α) (a : α) : l.filter (· = a) = replicate (count a l) a :=
|
||||
@@ -313,29 +216,20 @@ theorem count_le_count_map [DecidableEq β] (l : List α) (f : α → β) (x :
|
||||
rw [count, count, countP_map]
|
||||
apply countP_mono_left; simp (config := { contextual := true })
|
||||
|
||||
theorem count_filterMap {α} [BEq β] (b : β) (f : α → Option β) (l : List α) :
|
||||
count b (filterMap f l) = countP (fun a => f a == some b) l := by
|
||||
rw [count_eq_countP, countP_filterMap]
|
||||
congr
|
||||
ext a
|
||||
obtain _ | b := f a
|
||||
· simp
|
||||
· simp
|
||||
|
||||
theorem count_erase (a b : α) :
|
||||
∀ l : List α, count a (l.erase b) = count a l - if b == a then 1 else 0
|
||||
| [] => by simp
|
||||
| c :: l => by
|
||||
rw [erase_cons]
|
||||
if hc : c = b then
|
||||
have hc_beq := beq_iff_eq.mpr hc
|
||||
have hc_beq := (beq_iff_eq _ _).mpr hc
|
||||
rw [if_pos hc_beq, hc, count_cons, Nat.add_sub_cancel]
|
||||
else
|
||||
have hc_beq := beq_false_of_ne hc
|
||||
simp only [hc_beq, if_false, count_cons, count_cons, count_erase a b l, reduceCtorEq]
|
||||
simp only [hc_beq, if_false, count_cons, count_cons, count_erase a b l]
|
||||
if ha : b = a then
|
||||
rw [ha, eq_comm] at hc
|
||||
rw [if_pos (beq_iff_eq.2 ha), if_neg (by simpa using Ne.symm hc), Nat.add_zero, Nat.add_zero]
|
||||
rw [if_pos ((beq_iff_eq _ _).2 ha), if_neg (by simpa using Ne.symm hc), Nat.add_zero, Nat.add_zero]
|
||||
else
|
||||
rw [if_neg (by simpa using ha), Nat.sub_zero, Nat.sub_zero]
|
||||
|
||||
|
||||
@@ -33,25 +33,6 @@ theorem eraseP_of_forall_not {l : List α} (h : ∀ a, a ∈ l → ¬p a) : l.er
|
||||
| nil => rfl
|
||||
| cons _ _ ih => simp [h _ (.head ..), ih (forall_mem_cons.1 h).2]
|
||||
|
||||
@[simp] theorem eraseP_eq_nil {xs : List α} {p : α → Bool} : xs.eraseP p = [] ↔ xs = [] ∨ ∃ x, p x ∧ xs = [x] := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [eraseP_cons, cond_eq_if]
|
||||
split <;> rename_i h
|
||||
· simp only [reduceCtorEq, cons.injEq, false_or]
|
||||
constructor
|
||||
· rintro rfl
|
||||
simpa
|
||||
· rintro ⟨_, _, rfl, rfl⟩
|
||||
rfl
|
||||
· simp only [reduceCtorEq, cons.injEq, false_or, false_iff, not_exists, not_and]
|
||||
rintro x h' rfl
|
||||
simp_all
|
||||
|
||||
theorem eraseP_ne_nil {xs : List α} {p : α → Bool} : xs.eraseP p ≠ [] ↔ xs ≠ [] ∧ ∀ x, p x → xs ≠ [x] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_eraseP : ∀ {l : List α} {a} (al : a ∈ l) (pa : p a),
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬p b) ∧ p a ∧ l = l₁ ++ a :: l₂ ∧ l.eraseP p = l₁ ++ l₂
|
||||
| b :: l, a, al, pa =>
|
||||
@@ -109,10 +90,6 @@ protected theorem Sublist.eraseP : l₁ <+ l₂ → l₁.eraseP p <+ l₂.eraseP
|
||||
theorem length_eraseP_le (l : List α) : (l.eraseP p).length ≤ l.length :=
|
||||
l.eraseP_sublist.length_le
|
||||
|
||||
theorem le_length_eraseP (l : List α) : l.length - 1 ≤ (l.eraseP p).length := by
|
||||
rw [length_eraseP]
|
||||
split <;> simp
|
||||
|
||||
theorem mem_of_mem_eraseP {l : List α} : a ∈ l.eraseP p → a ∈ l := (eraseP_subset _ ·)
|
||||
|
||||
@[simp] theorem mem_eraseP_of_neg {l : List α} (pa : ¬p a) : a ∈ l.eraseP p ↔ a ∈ l := by
|
||||
@@ -182,23 +159,6 @@ theorem eraseP_append (l₁ l₂ : List α) :
|
||||
rw [eraseP_append_right _]
|
||||
simp_all
|
||||
|
||||
theorem eraseP_replicate (n : Nat) (a : α) (p : α → Bool) :
|
||||
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate_succ, eraseP_cons]
|
||||
split <;> simp [*]
|
||||
|
||||
protected theorem IsPrefix.eraseP (h : l₁ <+: l₂) : l₁.eraseP p <+: l₂.eraseP p := by
|
||||
rw [IsPrefix] at h
|
||||
obtain ⟨t, rfl⟩ := h
|
||||
rw [eraseP_append]
|
||||
split
|
||||
· exact prefix_append (eraseP p l₁) t
|
||||
· rw [eraseP_of_forall_not (by simp_all)]
|
||||
exact prefix_append l₁ (eraseP p t)
|
||||
|
||||
theorem eraseP_eq_iff {p} {l : List α} :
|
||||
l.eraseP p = l' ↔
|
||||
((∀ a ∈ l, ¬ p a) ∧ l = l') ∨
|
||||
@@ -244,11 +204,8 @@ theorem eraseP_eq_iff {p} {l : List α} :
|
||||
(replicate n a).eraseP p = replicate n a := by
|
||||
rw [eraseP_of_forall_not (by simp_all)]
|
||||
|
||||
theorem Pairwise.eraseP (q) : Pairwise p l → Pairwise p (l.eraseP q) :=
|
||||
Pairwise.sublist <| eraseP_sublist _
|
||||
|
||||
theorem Nodup.eraseP (p) : Nodup l → Nodup (l.eraseP p) :=
|
||||
Pairwise.eraseP p
|
||||
Nodup.sublist <| eraseP_sublist _
|
||||
|
||||
theorem eraseP_comm {l : List α} (h : ∀ a ∈ l, ¬ p a ∨ ¬ q a) :
|
||||
(l.eraseP p).eraseP q = (l.eraseP q).eraseP p := by
|
||||
@@ -264,12 +221,6 @@ theorem eraseP_comm {l : List α} (h : ∀ a ∈ l, ¬ p a ∨ ¬ q a) :
|
||||
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
|
||||
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
|
||||
|
||||
theorem head_eraseP_mem (xs : List α) (p : α → Bool) (h) : (xs.eraseP p).head h ∈ xs :=
|
||||
(eraseP_sublist xs).head_mem h
|
||||
|
||||
theorem getLast_eraseP_mem (xs : List α) (p : α → Bool) (h) : (xs.eraseP p).getLast h ∈ xs :=
|
||||
(eraseP_sublist xs).getLast_mem h
|
||||
|
||||
/-! ### erase -/
|
||||
section erase
|
||||
variable [BEq α]
|
||||
@@ -298,16 +249,6 @@ theorem erase_eq_eraseP [LawfulBEq α] (a : α) : ∀ l : List α, l.erase a =
|
||||
| b :: l => by
|
||||
if h : a = b then simp [h] else simp [h, Ne.symm h, erase_eq_eraseP a l]
|
||||
|
||||
@[simp] theorem erase_eq_nil [LawfulBEq α] {xs : List α} {a : α} :
|
||||
xs.erase a = [] ↔ xs = [] ∨ xs = [a] := by
|
||||
rw [erase_eq_eraseP]
|
||||
simp
|
||||
|
||||
theorem erase_ne_nil [LawfulBEq α] {xs : List α} {a : α} :
|
||||
xs.erase a ≠ [] ↔ xs ≠ [] ∧ xs ≠ [a] := by
|
||||
rw [erase_eq_eraseP]
|
||||
simp
|
||||
|
||||
theorem exists_erase_eq [LawfulBEq α] {a : α} {l : List α} (h : a ∈ l) :
|
||||
∃ l₁ l₂, a ∉ l₁ ∧ l = l₁ ++ a :: l₂ ∧ l.erase a = l₁ ++ l₂ := by
|
||||
let ⟨_, l₁, l₂, h₁, e, h₂, h₃⟩ := exists_of_eraseP h (beq_self_eq_true _)
|
||||
@@ -330,16 +271,9 @@ theorem erase_subset (a : α) (l : List α) : l.erase a ⊆ l := (erase_sublist
|
||||
theorem Sublist.erase (a : α) {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₁.erase a <+ l₂.erase a := by
|
||||
simp only [erase_eq_eraseP']; exact h.eraseP
|
||||
|
||||
theorem IsPrefix.erase (a : α) {l₁ l₂ : List α} (h : l₁ <+: l₂) : l₁.erase a <+: l₂.erase a := by
|
||||
simp only [erase_eq_eraseP']; exact h.eraseP
|
||||
|
||||
theorem length_erase_le (a : α) (l : List α) : (l.erase a).length ≤ l.length :=
|
||||
(erase_sublist a l).length_le
|
||||
|
||||
theorem le_length_erase [LawfulBEq α] (a : α) (l : List α) : l.length - 1 ≤ (l.erase a).length := by
|
||||
rw [length_erase]
|
||||
split <;> simp
|
||||
|
||||
theorem mem_of_mem_erase {a b : α} {l : List α} (h : a ∈ l.erase b) : a ∈ l := erase_subset _ _ h
|
||||
|
||||
@[simp] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {l : List α} (ab : a ≠ b) :
|
||||
@@ -348,7 +282,7 @@ theorem mem_of_mem_erase {a b : α} {l : List α} (h : a ∈ l.erase b) : a ∈
|
||||
|
||||
@[simp] theorem erase_eq_self_iff [LawfulBEq α] {l : List α} : l.erase a = l ↔ a ∉ l := by
|
||||
rw [erase_eq_eraseP', eraseP_eq_self_iff]
|
||||
simp [forall_mem_ne']
|
||||
simp
|
||||
|
||||
theorem erase_filter [LawfulBEq α] (f : α → Bool) (l : List α) :
|
||||
(filter f l).erase a = filter f (l.erase a) := by
|
||||
@@ -381,11 +315,6 @@ theorem erase_append [LawfulBEq α] {a : α} {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).erase a = if a ∈ l₁ then l₁.erase a ++ l₂ else l₁ ++ l₂.erase a := by
|
||||
simp [erase_eq_eraseP, eraseP_append]
|
||||
|
||||
theorem erase_replicate [LawfulBEq α] (n : Nat) (a b : α) :
|
||||
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
|
||||
rw [erase_eq_eraseP]
|
||||
simp [eraseP_replicate]
|
||||
|
||||
theorem erase_comm [LawfulBEq α] (a b : α) (l : List α) :
|
||||
(l.erase a).erase b = (l.erase b).erase a := by
|
||||
if ab : a == b then rw [eq_of_beq ab] else ?_
|
||||
@@ -425,10 +354,7 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {l : List α} :
|
||||
rw [erase_of_not_mem]
|
||||
simp_all
|
||||
|
||||
theorem Pairwise.erase [LawfulBEq α] {l : List α} (a) : Pairwise p l → Pairwise p (l.erase a) :=
|
||||
Pairwise.sublist <| erase_sublist _ _
|
||||
|
||||
theorem Nodup.erase_eq_filter [LawfulBEq α] {l} (d : Nodup l) (a : α) : l.erase a = l.filter (· != a) := by
|
||||
theorem Nodup.erase_eq_filter [BEq α] [LawfulBEq α] {l} (d : Nodup l) (a : α) : l.erase a = l.filter (· != a) := by
|
||||
induction d with
|
||||
| nil => rfl
|
||||
| cons m _n ih =>
|
||||
@@ -441,41 +367,26 @@ theorem Nodup.erase_eq_filter [LawfulBEq α] {l} (d : Nodup l) (a : α) : l.eras
|
||||
simpa [@eq_comm α] using m
|
||||
· simp [beq_false_of_ne h, ih, h]
|
||||
|
||||
theorem Nodup.mem_erase_iff [LawfulBEq α] {a : α} (d : Nodup l) : a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l := by
|
||||
theorem Nodup.mem_erase_iff [BEq α] [LawfulBEq α] {a : α} (d : Nodup l) : a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l := by
|
||||
rw [Nodup.erase_eq_filter d, mem_filter, and_comm, bne_iff_ne]
|
||||
|
||||
theorem Nodup.not_mem_erase [LawfulBEq α] {a : α} (h : Nodup l) : a ∉ l.erase a := fun H => by
|
||||
theorem Nodup.not_mem_erase [BEq α] [LawfulBEq α] {a : α} (h : Nodup l) : a ∉ l.erase a := fun H => by
|
||||
simpa using ((Nodup.mem_erase_iff h).mp H).left
|
||||
|
||||
theorem Nodup.erase [LawfulBEq α] (a : α) : Nodup l → Nodup (l.erase a) :=
|
||||
Pairwise.erase a
|
||||
|
||||
theorem head_erase_mem (xs : List α) (a : α) (h) : (xs.erase a).head h ∈ xs :=
|
||||
(erase_sublist a xs).head_mem h
|
||||
|
||||
theorem getLast_erase_mem (xs : List α) (a : α) (h) : (xs.erase a).getLast h ∈ xs :=
|
||||
(erase_sublist a xs).getLast_mem h
|
||||
theorem Nodup.erase [BEq α] [LawfulBEq α] (a : α) : Nodup l → Nodup (l.erase a) :=
|
||||
Nodup.sublist <| erase_sublist _ _
|
||||
|
||||
end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem length_eraseIdx (l : List α) (i : Nat) :
|
||||
(l.eraseIdx i).length = if i < l.length then l.length - 1 else l.length := by
|
||||
induction l generalizing i with
|
||||
| nil => simp
|
||||
| cons x l ih =>
|
||||
cases i with
|
||||
| zero => simp
|
||||
| succ i =>
|
||||
simp only [eraseIdx, length_cons, ih, add_one_lt_add_one_iff, Nat.add_one_sub_one]
|
||||
split
|
||||
· cases l <;> simp_all
|
||||
· rfl
|
||||
|
||||
theorem length_eraseIdx_of_lt {l : List α} {i} (h : i < length l) :
|
||||
(l.eraseIdx i).length = length l - 1 := by
|
||||
simp [length_eraseIdx, h]
|
||||
theorem length_eraseIdx : ∀ {l i}, i < length l → length (@eraseIdx α l i) = length l - 1
|
||||
| [], _, _ => rfl
|
||||
| _::_, 0, _ => by simp [eraseIdx]
|
||||
| x::xs, i+1, h => by
|
||||
have : i < length xs := Nat.lt_of_succ_lt_succ h
|
||||
simp [eraseIdx, ← Nat.add_one]
|
||||
rw [length_eraseIdx this, Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) this)]
|
||||
|
||||
@[simp] theorem eraseIdx_zero (l : List α) : eraseIdx l 0 = tail l := by cases l <;> rfl
|
||||
|
||||
@@ -485,28 +396,11 @@ theorem eraseIdx_eq_take_drop_succ :
|
||||
| a::l, 0 => by simp
|
||||
| a::l, i + 1 => by simp [eraseIdx_eq_take_drop_succ l i]
|
||||
|
||||
-- See `Init.Data.List.Nat.Erase` for `getElem?_eraseIdx` and `getElem_eraseIdx`.
|
||||
|
||||
@[simp] theorem eraseIdx_eq_nil {l : List α} {i : Nat} : eraseIdx l i = [] ↔ l = [] ∨ (length l = 1 ∧ i = 0) := by
|
||||
match l, i with
|
||||
| [], _
|
||||
| a::l, 0
|
||||
| a::l, i + 1 => simp [Nat.succ_inj']
|
||||
|
||||
theorem eraseIdx_ne_nil {l : List α} {i : Nat} : eraseIdx l i ≠ [] ↔ 2 ≤ l.length ∨ (l.length = 1 ∧ i ≠ 0) := by
|
||||
match l with
|
||||
| []
|
||||
| [a]
|
||||
| a::b::l => simp [Nat.succ_inj']
|
||||
|
||||
theorem eraseIdx_sublist : ∀ (l : List α) (k : Nat), eraseIdx l k <+ l
|
||||
| [], _ => by simp
|
||||
| a::l, 0 => by simp
|
||||
| a::l, k + 1 => by simp [eraseIdx_sublist l k]
|
||||
|
||||
theorem mem_of_mem_eraseIdx {l : List α} {i : Nat} {a : α} (h : a ∈ l.eraseIdx i) : a ∈ l :=
|
||||
(eraseIdx_sublist _ _).mem h
|
||||
|
||||
theorem eraseIdx_subset (l : List α) (k : Nat) : eraseIdx l k ⊆ l := (eraseIdx_sublist l k).subset
|
||||
|
||||
@[simp]
|
||||
@@ -518,13 +412,6 @@ theorem eraseIdx_eq_self : ∀ {l : List α} {k : Nat}, eraseIdx l k = l ↔ len
|
||||
theorem eraseIdx_of_length_le {l : List α} {k : Nat} (h : length l ≤ k) : eraseIdx l k = l := by
|
||||
rw [eraseIdx_eq_self.2 h]
|
||||
|
||||
theorem length_eraseIdx_le (l : List α) (i : Nat) : length (l.eraseIdx i) ≤ length l :=
|
||||
(eraseIdx_sublist l i).length_le
|
||||
|
||||
theorem le_length_eraseIdx (l : List α) (i : Nat) : length l - 1 ≤ length (l.eraseIdx i) := by
|
||||
rw [length_eraseIdx]
|
||||
split <;> simp
|
||||
|
||||
theorem eraseIdx_append_of_lt_length {l : List α} {k : Nat} (hk : k < length l) (l' : List α) :
|
||||
eraseIdx (l ++ l') k = eraseIdx l k ++ l' := by
|
||||
induction l generalizing k with
|
||||
@@ -543,23 +430,6 @@ theorem eraseIdx_append_of_length_le {l : List α} {k : Nat} (hk : length l ≤
|
||||
| zero => simp_all
|
||||
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_sub_succ]
|
||||
|
||||
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} :
|
||||
(replicate n a).eraseIdx k = if k < n then replicate (n - 1) a else replicate n a := by
|
||||
split <;> rename_i h
|
||||
· rw [eq_replicate_iff, length_eraseIdx_of_lt (by simpa using h)]
|
||||
simp only [length_replicate, true_and]
|
||||
intro b m
|
||||
replace m := mem_of_mem_eraseIdx m
|
||||
simp only [mem_replicate] at m
|
||||
exact m.2
|
||||
· rw [eraseIdx_of_length_le (by simpa using h)]
|
||||
|
||||
theorem Pairwise.eraseIdx {l : List α} (k) : Pairwise p l → Pairwise p (l.eraseIdx k) :=
|
||||
Pairwise.sublist <| eraseIdx_sublist _ _
|
||||
|
||||
theorem Nodup.eraseIdx {l : List α} (k) : Nodup l → Nodup (l.eraseIdx k) :=
|
||||
Pairwise.eraseIdx k
|
||||
|
||||
protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
|
||||
eraseIdx l k <+: eraseIdx l' k := by
|
||||
rcases h with ⟨t, rfl⟩
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,17 +3,15 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Bootstrap
|
||||
import Init.Data.Array.Lemmas
|
||||
|
||||
/-!
|
||||
## Tail recursive implementations for `List` definitions.
|
||||
|
||||
Many of the proofs require theorems about `Array`,
|
||||
so these are in a separate file to minimize imports.
|
||||
|
||||
If you import `Init.Data.List.Basic` but do not import this file,
|
||||
then at runtime you will get non-tail recursive versions of the following definitions.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
@@ -31,18 +29,27 @@ The following operations are still missing `@[csimp]` replacements:
|
||||
The following operations are not recursive to begin with
|
||||
(or are defined in terms of recursive primitives):
|
||||
`isEmpty`, `isSuffixOf`, `isSuffixOf?`, `rotateLeft`, `rotateRight`, `insert`, `zip`, `enum`,
|
||||
`min?`, `max?`, and `removeAll`.
|
||||
|
||||
The following operations were already given `@[csimp]` replacements in `Init/Data/List/Basic.lean`:
|
||||
`length`, `map`, `filter`, `replicate`, `leftPad`, `unzip`, `range'`, `iota`, `intersperse`.
|
||||
`minimum?`, `maximum?`, and `removeAll`.
|
||||
|
||||
The following operations are given `@[csimp]` replacements below:
|
||||
`set`, `filterMap`, `foldr`, `append`, `bind`, `join`,
|
||||
`take`, `takeWhile`, `dropLast`, `replace`, `erase`, `eraseIdx`, `zipWith`,
|
||||
`enumFrom`, and `intercalate`.
|
||||
`length`, `set`, `map`, `filter`, `filterMap`, `foldr`, `append`, `bind`, `join`, `replicate`,
|
||||
`take`, `takeWhile`, `dropLast`, `replace`, `erase`, `eraseIdx`, `zipWith`, `unzip`, `iota`,
|
||||
`enumFrom`, `intersperse`, and `intercalate`.
|
||||
|
||||
-/
|
||||
|
||||
/-! ### length -/
|
||||
|
||||
theorem length_add_eq_lengthTRAux (as : List α) (n : Nat) : as.length + n = as.lengthTRAux n := by
|
||||
induction as generalizing n with
|
||||
| nil => simp [length, lengthTRAux]
|
||||
| cons a as ih =>
|
||||
simp [length, lengthTRAux, ← ih, Nat.succ_add]
|
||||
rfl
|
||||
|
||||
@[csimp] theorem length_eq_lengthTR : @List.length = @List.lengthTR := by
|
||||
apply funext; intro α; apply funext; intro as
|
||||
simp [lengthTR, ← length_add_eq_lengthTRAux]
|
||||
|
||||
/-! ### set -/
|
||||
|
||||
@@ -57,13 +64,60 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem set_eq_setTR : @set = @setTR := by
|
||||
funext α l n a; simp [setTR]
|
||||
let rec go (acc) : ∀ xs n, l = acc.toList ++ xs →
|
||||
setTR.go l a xs n acc = acc.toList ++ xs.set n a
|
||||
let rec go (acc) : ∀ xs n, l = acc.data ++ xs →
|
||||
setTR.go l a xs n acc = acc.data ++ xs.set n a
|
||||
| [], _ => fun h => by simp [setTR.go, set, h]
|
||||
| x::xs, 0 => by simp [setTR.go, set]
|
||||
| x::xs, n+1 => fun h => by simp only [setTR.go, set]; rw [go _ xs] <;> simp [h]
|
||||
exact (go #[] _ _ rfl).symm
|
||||
|
||||
/-! ### map -/
|
||||
|
||||
/-- Tail-recursive version of `List.map`. -/
|
||||
@[inline] def mapTR (f : α → β) (as : List α) : List β :=
|
||||
loop as []
|
||||
where
|
||||
@[specialize] loop : List α → List β → List β
|
||||
| [], bs => bs.reverse
|
||||
| a::as, bs => loop as (f a :: bs)
|
||||
|
||||
theorem mapTR_loop_eq (f : α → β) (as : List α) (bs : List β) :
|
||||
mapTR.loop f as bs = bs.reverse ++ map f as := by
|
||||
induction as generalizing bs with
|
||||
| nil => simp [mapTR.loop, map]
|
||||
| cons a as ih =>
|
||||
simp only [mapTR.loop, map]
|
||||
rw [ih (f a :: bs), reverse_cons, append_assoc]
|
||||
rfl
|
||||
|
||||
@[csimp] theorem map_eq_mapTR : @map = @mapTR :=
|
||||
funext fun α => funext fun β => funext fun f => funext fun as => by
|
||||
simp [mapTR, mapTR_loop_eq]
|
||||
|
||||
/-! ### filter -/
|
||||
|
||||
/-- Tail-recursive version of `List.filter`. -/
|
||||
@[inline] def filterTR (p : α → Bool) (as : List α) : List α :=
|
||||
loop as []
|
||||
where
|
||||
@[specialize] loop : List α → List α → List α
|
||||
| [], rs => rs.reverse
|
||||
| a::as, rs => match p a with
|
||||
| true => loop as (a::rs)
|
||||
| false => loop as rs
|
||||
|
||||
theorem filterTR_loop_eq (p : α → Bool) (as bs : List α) :
|
||||
filterTR.loop p as bs = bs.reverse ++ filter p as := by
|
||||
induction as generalizing bs with
|
||||
| nil => simp [filterTR.loop, filter]
|
||||
| cons a as ih =>
|
||||
simp only [filterTR.loop, filter]
|
||||
split <;> simp_all
|
||||
|
||||
@[csimp] theorem filter_eq_filterTR : @filter = @filterTR := by
|
||||
apply funext; intro α; apply funext; intro p; apply funext; intro as
|
||||
simp [filterTR, filterTR_loop_eq]
|
||||
|
||||
/-! ### filterMap -/
|
||||
|
||||
/-- Tail recursive version of `filterMap`. -/
|
||||
@@ -77,11 +131,10 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem filterMap_eq_filterMapTR : @List.filterMap = @filterMapTR := by
|
||||
funext α β f l
|
||||
let rec go : ∀ as acc, filterMapTR.go f as acc = acc.toList ++ as.filterMap f
|
||||
let rec go : ∀ as acc, filterMapTR.go f as acc = acc.data ++ as.filterMap f
|
||||
| [], acc => by simp [filterMapTR.go, filterMap]
|
||||
| a::as, acc => by
|
||||
simp only [filterMapTR.go, go as, Array.push_toList, append_assoc, singleton_append,
|
||||
filterMap]
|
||||
simp only [filterMapTR.go, go as, Array.push_data, append_assoc, singleton_append, filterMap]
|
||||
split <;> simp [*]
|
||||
exact (go l #[]).symm
|
||||
|
||||
@@ -91,7 +144,7 @@ The following operations are given `@[csimp]` replacements below:
|
||||
@[specialize] def foldrTR (f : α → β → β) (init : β) (l : List α) : β := l.toArray.foldr f init
|
||||
|
||||
@[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by
|
||||
funext α β f init l; simp [foldrTR, Array.foldr_eq_foldr_toList, -Array.size_toArray]
|
||||
funext α β f init l; simp [foldrTR, Array.foldr_eq_foldr_data, -Array.size_toArray]
|
||||
|
||||
/-! ### bind -/
|
||||
|
||||
@@ -104,7 +157,7 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem bind_eq_bindTR : @List.bind = @bindTR := by
|
||||
funext α β as f
|
||||
let rec go : ∀ as acc, bindTR.go f as acc = acc.toList ++ as.bind f
|
||||
let rec go : ∀ as acc, bindTR.go f as acc = acc.data ++ as.bind f
|
||||
| [], acc => by simp [bindTR.go, bind]
|
||||
| x::xs, acc => by simp [bindTR.go, bind, go xs]
|
||||
exact (go as #[]).symm
|
||||
@@ -117,6 +170,40 @@ The following operations are given `@[csimp]` replacements below:
|
||||
@[csimp] theorem join_eq_joinTR : @join = @joinTR := by
|
||||
funext α l; rw [← List.bind_id, List.bind_eq_bindTR]; rfl
|
||||
|
||||
/-! ### replicate -/
|
||||
|
||||
/-- Tail-recursive version of `List.replicate`. -/
|
||||
def replicateTR {α : Type u} (n : Nat) (a : α) : List α :=
|
||||
let rec loop : Nat → List α → List α
|
||||
| 0, as => as
|
||||
| n+1, as => loop n (a::as)
|
||||
loop n []
|
||||
|
||||
theorem replicateTR_loop_replicate_eq (a : α) (m n : Nat) :
|
||||
replicateTR.loop a n (replicate m a) = replicate (n + m) a := by
|
||||
induction n generalizing m with simp [replicateTR.loop]
|
||||
| succ n ih => simp [Nat.succ_add]; exact ih (m+1)
|
||||
|
||||
theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++ acc
|
||||
| 0 => rfl
|
||||
| n+1 => by rw [← replicateTR_loop_replicate_eq _ 1 n, replicate, replicate,
|
||||
replicateTR.loop, replicateTR_loop_eq n, replicateTR_loop_eq n, append_assoc]; rfl
|
||||
|
||||
@[csimp] theorem replicate_eq_replicateTR : @List.replicate = @List.replicateTR := by
|
||||
apply funext; intro α; apply funext; intro n; apply funext; intro a
|
||||
exact (replicateTR_loop_replicate_eq _ 0 n).symm
|
||||
|
||||
/-! ## Additional functions -/
|
||||
|
||||
/-! ### leftpad -/
|
||||
|
||||
/-- Optimized version of `leftpad`. -/
|
||||
@[inline] def leftpadTR (n : Nat) (a : α) (l : List α) : List α :=
|
||||
replicateTR.loop a (n - length l) l
|
||||
|
||||
@[csimp] theorem leftpad_eq_leftpadTR : @leftpad = @leftpadTR := by
|
||||
funext α n a l; simp [leftpad, leftpadTR, replicateTR_loop_eq]
|
||||
|
||||
/-! ## Sublists -/
|
||||
|
||||
/-! ### take -/
|
||||
@@ -132,7 +219,7 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem take_eq_takeTR : @take = @takeTR := by
|
||||
funext α n l; simp [takeTR]
|
||||
suffices ∀ xs acc, l = acc.toList ++ xs → takeTR.go l xs n acc = acc.toList ++ xs.take n from
|
||||
suffices ∀ xs acc, l = acc.data ++ xs → takeTR.go l xs n acc = acc.data ++ xs.take n from
|
||||
(this l #[] (by simp)).symm
|
||||
intro xs; induction xs generalizing n with intro acc
|
||||
| nil => cases n <;> simp [take, takeTR.go]
|
||||
@@ -153,13 +240,13 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem takeWhile_eq_takeWhileTR : @takeWhile = @takeWhileTR := by
|
||||
funext α p l; simp [takeWhileTR]
|
||||
suffices ∀ xs acc, l = acc.toList ++ xs →
|
||||
takeWhileTR.go p l xs acc = acc.toList ++ xs.takeWhile p from
|
||||
suffices ∀ xs acc, l = acc.data ++ xs →
|
||||
takeWhileTR.go p l xs acc = acc.data ++ xs.takeWhile p from
|
||||
(this l #[] (by simp)).symm
|
||||
intro xs; induction xs with intro acc
|
||||
| nil => simp [takeWhile, takeWhileTR.go]
|
||||
| cons x xs IH =>
|
||||
simp only [takeWhileTR.go, Array.toListImpl_eq, takeWhile]
|
||||
simp only [takeWhileTR.go, Array.toList_eq, takeWhile]
|
||||
split
|
||||
· intro h; rw [IH] <;> simp_all
|
||||
· simp [*]
|
||||
@@ -186,8 +273,8 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem replace_eq_replaceTR : @List.replace = @replaceTR := by
|
||||
funext α _ l b c; simp [replaceTR]
|
||||
suffices ∀ xs acc, l = acc.toList ++ xs →
|
||||
replaceTR.go l b c xs acc = acc.toList ++ xs.replace b c from
|
||||
suffices ∀ xs acc, l = acc.data ++ xs →
|
||||
replaceTR.go l b c xs acc = acc.data ++ xs.replace b c from
|
||||
(this l #[] (by simp)).symm
|
||||
intro xs; induction xs with intro acc
|
||||
| nil => simp [replace, replaceTR.go]
|
||||
@@ -209,7 +296,7 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem erase_eq_eraseTR : @List.erase = @eraseTR := by
|
||||
funext α _ l a; simp [eraseTR]
|
||||
suffices ∀ xs acc, l = acc.toList ++ xs → eraseTR.go l a xs acc = acc.toList ++ xs.erase a from
|
||||
suffices ∀ xs acc, l = acc.data ++ xs → eraseTR.go l a xs acc = acc.data ++ xs.erase a from
|
||||
(this l #[] (by simp)).symm
|
||||
intro xs; induction xs with intro acc h
|
||||
| nil => simp [List.erase, eraseTR.go, h]
|
||||
@@ -229,8 +316,8 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem eraseP_eq_erasePTR : @eraseP = @erasePTR := by
|
||||
funext α p l; simp [erasePTR]
|
||||
let rec go (acc) : ∀ xs, l = acc.toList ++ xs →
|
||||
erasePTR.go p l xs acc = acc.toList ++ xs.eraseP p
|
||||
let rec go (acc) : ∀ xs, l = acc.data ++ xs →
|
||||
erasePTR.go p l xs acc = acc.data ++ xs.eraseP p
|
||||
| [] => fun h => by simp [erasePTR.go, eraseP, h]
|
||||
| x::xs => by
|
||||
simp [erasePTR.go, eraseP]; cases p x <;> simp
|
||||
@@ -250,7 +337,7 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem eraseIdx_eq_eraseIdxTR : @eraseIdx = @eraseIdxTR := by
|
||||
funext α l n; simp [eraseIdxTR]
|
||||
suffices ∀ xs acc, l = acc.toList ++ xs → eraseIdxTR.go l xs n acc = acc.toList ++ xs.eraseIdx n from
|
||||
suffices ∀ xs acc, l = acc.data ++ xs → eraseIdxTR.go l xs n acc = acc.data ++ xs.eraseIdx n from
|
||||
(this l #[] (by simp)).symm
|
||||
intro xs; induction xs generalizing n with intro acc h
|
||||
| nil => simp [eraseIdx, eraseIdxTR.go, h]
|
||||
@@ -274,13 +361,59 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
@[csimp] theorem zipWith_eq_zipWithTR : @zipWith = @zipWithTR := by
|
||||
funext α β γ f as bs
|
||||
let rec go : ∀ as bs acc, zipWithTR.go f as bs acc = acc.toList ++ as.zipWith f bs
|
||||
let rec go : ∀ as bs acc, zipWithTR.go f as bs acc = acc.data ++ as.zipWith f bs
|
||||
| [], _, acc | _::_, [], acc => by simp [zipWithTR.go, zipWith]
|
||||
| a::as, b::bs, acc => by simp [zipWithTR.go, zipWith, go as bs]
|
||||
exact (go as bs #[]).symm
|
||||
|
||||
/-! ### unzip -/
|
||||
|
||||
/-- Tail recursive version of `List.unzip`. -/
|
||||
def unzipTR (l : List (α × β)) : List α × List β :=
|
||||
l.foldr (fun (a, b) (al, bl) => (a::al, b::bl)) ([], [])
|
||||
|
||||
@[csimp] theorem unzip_eq_unzipTR : @unzip = @unzipTR := by
|
||||
funext α β l; simp [unzipTR]; induction l <;> simp [*]
|
||||
|
||||
/-! ## Ranges and enumeration -/
|
||||
|
||||
/-! ### range' -/
|
||||
|
||||
/-- Optimized version of `range'`. -/
|
||||
@[inline] def range'TR (s n : Nat) (step : Nat := 1) : List Nat := go n (s + step * n) [] where
|
||||
/-- Auxiliary for `range'TR`: `range'TR.go n e = [e-n, ..., e-1] ++ acc`. -/
|
||||
go : Nat → Nat → List Nat → List Nat
|
||||
| 0, _, acc => acc
|
||||
| n+1, e, acc => go n (e-step) ((e-step) :: acc)
|
||||
|
||||
@[csimp] theorem range'_eq_range'TR : @range' = @range'TR := by
|
||||
funext s n step
|
||||
let rec go (s) : ∀ n m,
|
||||
range'TR.go step n (s + step * n) (range' (s + step * n) m step) = range' s (n + m) step
|
||||
| 0, m => by simp [range'TR.go]
|
||||
| n+1, m => by
|
||||
simp [range'TR.go]
|
||||
rw [Nat.mul_succ, ← Nat.add_assoc, Nat.add_sub_cancel, Nat.add_right_comm n]
|
||||
exact go s n (m + 1)
|
||||
exact (go s n 0).symm
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
/-- Tail-recursive version of `List.iota`. -/
|
||||
def iotaTR (n : Nat) : List Nat :=
|
||||
let rec go : Nat → List Nat → List Nat
|
||||
| 0, r => r.reverse
|
||||
| m@(n+1), r => go n (m::r)
|
||||
go n []
|
||||
|
||||
@[csimp]
|
||||
theorem iota_eq_iotaTR : @iota = @iotaTR :=
|
||||
have aux (n : Nat) (r : List Nat) : iotaTR.go n r = r.reverse ++ iota n := by
|
||||
induction n generalizing r with
|
||||
| zero => simp [iota, iotaTR.go]
|
||||
| succ n ih => simp [iota, iotaTR.go, ih, append_assoc]
|
||||
funext fun n => by simp [iotaTR, aux]
|
||||
|
||||
/-! ### enumFrom -/
|
||||
|
||||
/-- Tail recursive version of `List.enumFrom`. -/
|
||||
@@ -296,11 +429,25 @@ def enumFromTR (n : Nat) (l : List α) : List (Nat × α) :=
|
||||
| a::as, n => by
|
||||
rw [← show _ + as.length = n + (a::as).length from Nat.succ_add .., foldr, go as]
|
||||
simp [enumFrom, f]
|
||||
rw [Array.foldr_eq_foldr_toList]
|
||||
rw [Array.foldr_eq_foldr_data]
|
||||
simp [go]
|
||||
|
||||
/-! ## Other list operations -/
|
||||
|
||||
/-! ### intersperse -/
|
||||
|
||||
/-- Tail recursive version of `List.intersperse`. -/
|
||||
def intersperseTR (sep : α) : List α → List α
|
||||
| [] => []
|
||||
| [x] => [x]
|
||||
| x::y::xs => x :: sep :: y :: xs.foldr (fun a r => sep :: a :: r) []
|
||||
|
||||
@[csimp] theorem intersperse_eq_intersperseTR : @intersperse = @intersperseTR := by
|
||||
funext α sep l; simp [intersperseTR]
|
||||
match l with
|
||||
| [] | [_] => rfl
|
||||
| x::y::xs => simp [intersperse]; induction xs generalizing y <;> simp [*]
|
||||
|
||||
/-! ### intercalate -/
|
||||
|
||||
/-- Tail recursive version of `List.intercalate`. -/
|
||||
@@ -322,7 +469,7 @@ where
|
||||
| [_] => simp
|
||||
| x::y::xs =>
|
||||
let rec go {acc x} : ∀ xs,
|
||||
intercalateTR.go sep.toArray x xs acc = acc.toList ++ join (intersperse sep (x::xs))
|
||||
intercalateTR.go sep.toArray x xs acc = acc.data ++ join (intersperse sep (x::xs))
|
||||
| [] => by simp [intercalateTR.go]
|
||||
| _::_ => by simp [intercalateTR.go, go]
|
||||
simp [intersperse, go]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ prelude
|
||||
import Init.Data.List.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.min?` and `List.max?.
|
||||
# Lemmas about `List.minimum?` and `List.maximum?.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
@@ -16,24 +16,24 @@ open Nat
|
||||
|
||||
/-! ## Minima and maxima -/
|
||||
|
||||
/-! ### min? -/
|
||||
/-! ### minimum? -/
|
||||
|
||||
@[simp] theorem min?_nil [Min α] : ([] : List α).min? = none := rfl
|
||||
@[simp] theorem minimum?_nil [Min α] : ([] : List α).minimum? = none := rfl
|
||||
|
||||
-- We don't put `@[simp]` on `min?_cons`,
|
||||
-- We don't put `@[simp]` on `minimum?_cons`,
|
||||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||||
theorem min?_cons [Min α] {xs : List α} : (x :: xs).min? = foldl min x xs := rfl
|
||||
theorem minimum?_cons [Min α] {xs : List α} : (x :: xs).minimum? = foldl min x xs := rfl
|
||||
|
||||
@[simp] theorem min?_eq_none_iff {xs : List α} [Min α] : xs.min? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [min?]
|
||||
@[simp] theorem minimum?_eq_none_iff {xs : List α} [Min α] : xs.minimum? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [minimum?]
|
||||
|
||||
theorem min?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b) :
|
||||
{xs : List α} → xs.min? = some a → a ∈ xs := by
|
||||
theorem minimum?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b) :
|
||||
{xs : List α} → xs.minimum? = some a → a ∈ xs := by
|
||||
intro xs
|
||||
match xs with
|
||||
| nil => simp
|
||||
| x :: xs =>
|
||||
simp only [min?_cons, Option.some.injEq, List.mem_cons]
|
||||
simp only [minimum?_cons, Option.some.injEq, List.mem_cons]
|
||||
intro eq
|
||||
induction xs generalizing x with
|
||||
| nil =>
|
||||
@@ -49,12 +49,12 @@ theorem min?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b
|
||||
|
||||
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
|
||||
|
||||
theorem le_min?_iff [Min α] [LE α]
|
||||
theorem le_minimum?_iff [Min α] [LE α]
|
||||
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) :
|
||||
{xs : List α} → xs.min? = some a → ∀ {x}, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b
|
||||
{xs : List α} → xs.minimum? = some a → ∀ x, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [min?]
|
||||
rw [minimum?]
|
||||
intro eq y
|
||||
simp only [Option.some.injEq] at eq
|
||||
induction xs generalizing x with
|
||||
@@ -67,46 +67,46 @@ theorem le_min?_iff [Min α] [LE α]
|
||||
|
||||
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
|
||||
-- and `le_min_iff`.
|
||||
theorem min?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
theorem minimum?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
(le_refl : ∀ a : α, a ≤ a)
|
||||
(min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b)
|
||||
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) {xs : List α} :
|
||||
xs.min? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||||
refine ⟨fun h => ⟨min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _)⟩, ?_⟩
|
||||
xs.minimum? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||||
refine ⟨fun h => ⟨minimum?_mem min_eq_or h, (le_minimum?_iff le_min_iff h _).1 (le_refl _)⟩, ?_⟩
|
||||
intro ⟨h₁, h₂⟩
|
||||
cases xs with
|
||||
| nil => simp at h₁
|
||||
| cons x xs =>
|
||||
exact congrArg some <| anti.1
|
||||
((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl))
|
||||
((le_minimum?_iff le_min_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
|
||||
|
||||
theorem min?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
|
||||
(replicate n a).min? = if n = 0 then none else some a := by
|
||||
theorem minimum?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
|
||||
(replicate n a).minimum? = if n = 0 then none else some a := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons]
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, minimum?_cons]
|
||||
|
||||
@[simp] theorem min?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
|
||||
(replicate n a).min? = some a := by
|
||||
simp [min?_replicate, Nat.ne_of_gt h, w]
|
||||
@[simp] theorem minimum?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
|
||||
(replicate n a).minimum? = some a := by
|
||||
simp [minimum?_replicate, Nat.ne_of_gt h, w]
|
||||
|
||||
/-! ### max? -/
|
||||
/-! ### maximum? -/
|
||||
|
||||
@[simp] theorem max?_nil [Max α] : ([] : List α).max? = none := rfl
|
||||
@[simp] theorem maximum?_nil [Max α] : ([] : List α).maximum? = none := rfl
|
||||
|
||||
-- We don't put `@[simp]` on `max?_cons`,
|
||||
-- We don't put `@[simp]` on `maximum?_cons`,
|
||||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||||
theorem max?_cons [Max α] {xs : List α} : (x :: xs).max? = foldl max x xs := rfl
|
||||
theorem maximum?_cons [Max α] {xs : List α} : (x :: xs).maximum? = foldl max x xs := rfl
|
||||
|
||||
@[simp] theorem max?_eq_none_iff {xs : List α} [Max α] : xs.max? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [max?]
|
||||
@[simp] theorem maximum?_eq_none_iff {xs : List α} [Max α] : xs.maximum? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [maximum?]
|
||||
|
||||
theorem max?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a ∨ max a b = b) :
|
||||
{xs : List α} → xs.max? = some a → a ∈ xs
|
||||
theorem maximum?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a ∨ max a b = b) :
|
||||
{xs : List α} → xs.maximum? = some a → a ∈ xs
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [max?]; rintro ⟨⟩
|
||||
rw [maximum?]; rintro ⟨⟩
|
||||
induction xs generalizing x with simp at *
|
||||
| cons y xs ih =>
|
||||
rcases ih (max x y) with h | h <;> simp [h]
|
||||
@@ -114,57 +114,40 @@ theorem max?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a ∨ max a b = b
|
||||
|
||||
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
|
||||
|
||||
theorem max?_le_iff [Max α] [LE α]
|
||||
theorem maximum?_le_iff [Max α] [LE α]
|
||||
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) :
|
||||
{xs : List α} → xs.max? = some a → ∀ {x}, a ≤ x ↔ ∀ b ∈ xs, b ≤ x
|
||||
{xs : List α} → xs.maximum? = some a → ∀ x, a ≤ x ↔ ∀ b ∈ xs, b ≤ x
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [max?]; rintro ⟨⟩ y
|
||||
rw [maximum?]; rintro ⟨⟩ y
|
||||
induction xs generalizing x with
|
||||
| nil => simp
|
||||
| cons y xs ih => simp [ih, max_le_iff, and_assoc]
|
||||
|
||||
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `max_eq_or`,
|
||||
-- and `le_min_iff`.
|
||||
theorem max?_eq_some_iff [Max α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
theorem maximum?_eq_some_iff [Max α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
(le_refl : ∀ a : α, a ≤ a)
|
||||
(max_eq_or : ∀ a b : α, max a b = a ∨ max a b = b)
|
||||
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) {xs : List α} :
|
||||
xs.max? = some a ↔ a ∈ xs ∧ ∀ b ∈ xs, b ≤ a := by
|
||||
refine ⟨fun h => ⟨max?_mem max_eq_or h, (max?_le_iff max_le_iff h).1 (le_refl _)⟩, ?_⟩
|
||||
xs.maximum? = some a ↔ a ∈ xs ∧ ∀ b ∈ xs, b ≤ a := by
|
||||
refine ⟨fun h => ⟨maximum?_mem max_eq_or h, (maximum?_le_iff max_le_iff h _).1 (le_refl _)⟩, ?_⟩
|
||||
intro ⟨h₁, h₂⟩
|
||||
cases xs with
|
||||
| nil => simp at h₁
|
||||
| cons x xs =>
|
||||
exact congrArg some <| anti.1
|
||||
(h₂ _ (max?_mem max_eq_or (xs := x::xs) rfl))
|
||||
((max?_le_iff max_le_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (maximum?_mem max_eq_or (xs := x::xs) rfl))
|
||||
((maximum?_le_iff max_le_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
|
||||
|
||||
theorem max?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
|
||||
(replicate n a).max? = if n = 0 then none else some a := by
|
||||
theorem maximum?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
|
||||
(replicate n a).maximum? = if n = 0 then none else some a := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons]
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, maximum?_cons]
|
||||
|
||||
@[simp] theorem max?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
|
||||
(replicate n a).max? = some a := by
|
||||
simp [max?_replicate, Nat.ne_of_gt h, w]
|
||||
|
||||
@[deprecated min?_nil (since := "2024-09-29")] abbrev minimum?_nil := @min?_nil
|
||||
@[deprecated min?_cons (since := "2024-09-29")] abbrev minimum?_cons := @min?_cons
|
||||
@[deprecated min?_eq_none_iff (since := "2024-09-29")] abbrev mininmum?_eq_none_iff := @min?_eq_none_iff
|
||||
@[deprecated min?_mem (since := "2024-09-29")] abbrev minimum?_mem := @min?_mem
|
||||
@[deprecated le_min?_iff (since := "2024-09-29")] abbrev le_minimum?_iff := @le_min?_iff
|
||||
@[deprecated min?_eq_some_iff (since := "2024-09-29")] abbrev minimum?_eq_some_iff := @min?_eq_some_iff
|
||||
@[deprecated min?_replicate (since := "2024-09-29")] abbrev minimum?_replicate := @min?_replicate
|
||||
@[deprecated min?_replicate_of_pos (since := "2024-09-29")] abbrev minimum?_replicate_of_pos := @min?_replicate_of_pos
|
||||
@[deprecated max?_nil (since := "2024-09-29")] abbrev maximum?_nil := @max?_nil
|
||||
@[deprecated max?_cons (since := "2024-09-29")] abbrev maximum?_cons := @max?_cons
|
||||
@[deprecated max?_eq_none_iff (since := "2024-09-29")] abbrev maximum?_eq_none_iff := @max?_eq_none_iff
|
||||
@[deprecated max?_mem (since := "2024-09-29")] abbrev maximum?_mem := @max?_mem
|
||||
@[deprecated max?_le_iff (since := "2024-09-29")] abbrev maximum?_le_iff := @max?_le_iff
|
||||
@[deprecated max?_eq_some_iff (since := "2024-09-29")] abbrev maximum?_eq_some_iff := @max?_eq_some_iff
|
||||
@[deprecated max?_replicate (since := "2024-09-29")] abbrev maximum?_replicate := @max?_replicate
|
||||
@[deprecated max?_replicate_of_pos (since := "2024-09-29")] abbrev maximum?_replicate_of_pos := @max?_replicate_of_pos
|
||||
@[simp] theorem maximum?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
|
||||
(replicate n a).maximum? = some a := by
|
||||
simp [maximum?_replicate, Nat.ne_of_gt h, w]
|
||||
|
||||
end List
|
||||
|
||||
@@ -51,27 +51,6 @@ theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α)
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α → m β) {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).mapM f = (return (← l₁.mapM f) ++ (← l₂.mapM f)) := by induction l₁ <;> simp [*]
|
||||
|
||||
/-- Auxiliary lemma for `mapM_eq_reverse_foldlM_cons`. -/
|
||||
theorem foldlM_cons_eq_append [Monad m] [LawfulMonad m] (f : α → m β) (as : List α) (b : β) (bs : List β) :
|
||||
(as.foldlM (init := b :: bs) fun acc a => return ((← f a) :: acc)) =
|
||||
(· ++ b :: bs) <$> as.foldlM (init := []) fun acc a => return ((← f a) :: acc) := by
|
||||
induction as generalizing b bs with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp only [bind_pure_comp] at ih
|
||||
simp [ih, _root_.map_bind, Functor.map_map, Function.comp_def]
|
||||
|
||||
theorem mapM_eq_reverse_foldlM_cons [Monad m] [LawfulMonad m] (f : α → m β) (l : List α) :
|
||||
mapM f l = reverse <$> (l.foldlM (fun acc a => return ((← f a) :: acc)) []) := by
|
||||
rw [← mapM'_eq_mapM]
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp only [mapM'_cons, ih, bind_map_left, foldlM_cons, LawfulMonad.bind_assoc, pure_bind,
|
||||
foldlM_cons_eq_append, _root_.map_bind, Functor.map_map, Function.comp_def, reverse_append,
|
||||
reverse_cons, reverse_nil, nil_append, singleton_append]
|
||||
simp [bind_pure_comp]
|
||||
|
||||
/-! ### forM -/
|
||||
|
||||
-- We use `List.forM` as the simp normal form, rather that `ForM.forM`.
|
||||
@@ -87,16 +66,4 @@ theorem mapM_eq_reverse_foldlM_cons [Monad m] [LawfulMonad m] (f : α → m β)
|
||||
(l₁ ++ l₂).forM f = (do l₁.forM f; l₂.forM f) := by
|
||||
induction l₁ <;> simp [*]
|
||||
|
||||
/-! ### allM -/
|
||||
|
||||
theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α → m Bool) (as : List α) :
|
||||
allM p as = (! ·) <$> anyM ((! ·) <$> p ·) as := by
|
||||
induction as with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp only [allM, anyM, bind_map_left, _root_.map_bind]
|
||||
congr
|
||||
funext b
|
||||
split <;> simp_all
|
||||
|
||||
end List
|
||||
|
||||
@@ -7,8 +7,4 @@ prelude
|
||||
import Init.Data.List.Nat.Basic
|
||||
import Init.Data.List.Nat.Pairwise
|
||||
import Init.Data.List.Nat.Range
|
||||
import Init.Data.List.Nat.Sublist
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Nat.Count
|
||||
import Init.Data.List.Nat.Erase
|
||||
import Init.Data.List.Nat.Find
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user