Compare commits

..

1 Commits

Author SHA1 Message Date
Leonardo de Moura
09ce61cfd1 feat: safe exponentiation
- Adds configuration option `exponentiation.threshold`
- An expression `b^n` where `b` and `n` are literals is not reduced by
  `whnf`, `simp`, and `isDefEq` if `n > exponentiation.threshold`.

Motivation: prevents system from becoming irresponsive.

TODO: improve support in the kernel. It is using a hard-coded limit
for now.
2024-07-02 20:39:33 -07:00
1870 changed files with 8265 additions and 56796 deletions

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: actionlint
uses: raven-actions/actionlint@v1
with:

View File

@@ -9,17 +9,6 @@ on:
merge_group:
schedule:
- cron: '0 7 * * *' # 8AM CET/11PM PT
# for manual re-release of a nightly
workflow_dispatch:
inputs:
action:
description: 'Action'
required: true
default: 'release nightly'
type: choice
options:
- release nightly
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
@@ -52,11 +41,11 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
- name: Set Nightly
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
id: set-nightly
run: |
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
@@ -176,7 +165,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 +193,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 +215,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 +240,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 +248,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)}`)
@@ -296,12 +287,12 @@ jobs:
uses: msys2/setup-msys2@v2
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:"
# `:p` means prefix with appropriate msystem prefix
pacboy: "make python cmake:p clang:p ccache:p gmp:p git zip unzip diffutils binutils tree zstd:p tar"
if: 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
@@ -325,19 +316,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 +371,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/, ...
@@ -403,7 +386,7 @@ jobs:
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
if: matrix.release
with:
name: build-${{ matrix.name }}
@@ -432,7 +415,7 @@ jobs:
if: matrix.test-speedcenter
- name: Check Stage 3
run: |
make -C build -j$NPROC check-stage3
make -C build -j$NPROC stage3
if: matrix.test-speedcenter
- name: Test Speedcenter Benchmarks
run: |
@@ -461,24 +444,12 @@ jobs:
# mark as merely cancelled not failed if builds are cancelled
if: ${{ !cancelled() }}
steps:
- if: ${{ contains(needs.*.result, 'failure') && github.repository == 'leanprover/lean4' && github.ref_name == 'master' }}
uses: zulip/github-actions-zulip/send-message@v1
with:
api-key: ${{ secrets.ZULIP_BOT_KEY }}
email: "github-actions-bot@lean-fro.zulipchat.com"
organization-url: "https://lean-fro.zulipchat.com"
to: "infrastructure"
topic: "Github actions"
type: "stream"
content: |
A build of `${{ github.ref_name }}`, triggered by event `${{ github.event_name }}`, [failed](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
- if: contains(needs.*.result, 'failure')
uses: actions/github-script@v7
with:
script: |
core.setFailed('Some jobs failed')
# This job creates releases from tags
# (whether they are "unofficial" releases for experiments, or official releases when the tag is "v" followed by a semver string.)
# We do not attempt to automatically construct a changelog here:
@@ -488,7 +459,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v3
with:
path: artifacts
- name: Release
@@ -513,12 +484,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
# needed for tagging
fetch-depth: 0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v3
with:
path: artifacts
- name: Prepare Nightly Release
@@ -551,8 +522,3 @@ jobs:
gh workflow -R leanprover/release-index run update-index.yml
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}
- name: Update toolchain on mathlib4's nightly-testing branch
run: |
gh workflow -R leanprover-community/mathlib4 run nightly_bump_toolchain.yml
env:
GITHUB_TOKEN: ${{ secrets.MATHLIB4_BOT }}

View File

@@ -1,34 +0,0 @@
name: Jira sync
on:
issues:
types: [closed]
jobs:
jira-sync:
runs-on: ubuntu-latest
steps:
- name: Move Jira issue to Done
env:
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }}
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
run: |
issue_number=${{ github.event.issue.number }}
jira_issue_key=$(curl -s -u "${JIRA_USERNAME}:${JIRA_API_TOKEN}" \
-X GET -H "Content-Type: application/json" \
"${JIRA_BASE_URL}/rest/api/2/search?jql=summary~\"${issue_number}\"" | \
jq -r '.issues[0].key')
if [ -z "$jira_issue_key" ]; then
exit
fi
curl -s -u "${JIRA_USERNAME}:${JIRA_API_TOKEN}" \
-X POST -H "Content-Type: application/json" \
--data "{\"transition\": {\"id\": \"41\"}}" \
"${JIRA_BASE_URL}/rest/api/2/issue/${jira_issue_key}/transitions"
echo "Moved Jira issue ${jira_issue_key} to Done"

View File

@@ -50,19 +50,18 @@ jobs:
NIX_BUILD_ARGS: --print-build-logs --fallback
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: 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

View File

@@ -163,8 +163,7 @@ 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_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-mathlib4-bot"))')"
@@ -235,7 +234,7 @@ jobs:
# Checkout the Batteries repository with all branches
- name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }}
@@ -292,7 +291,7 @@ jobs:
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
repository: leanprover-community/mathlib4
token: ${{ secrets.MATHLIB4_BOT }}
@@ -329,7 +328,7 @@ jobs:
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
git add lean-toolchain
sed -i 's,require "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "nightly-testing-'"${MOST_RECENT_NIGHTLY}"'",' lakefile.lean
sed -i "s/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \".\+\"/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
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 }}"

View File

@@ -14,22 +14,18 @@ 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)"
gh run cancel "$run_id" || echo "(failed)"
echo "Waiting for 30s"
sleep 30
gh run view "$run_id"
echo "Waiting for 10s"
sleep 10
echo "Rerunning"
gh run rerun "$run_id"
gh run view "$run_id"
shell: bash
env:
head_ref: ${{ github.head_ref }}
head_sha: ${{ github.event.pull_request.head.sha }}
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}

View File

@@ -23,7 +23,7 @@ jobs:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"
@@ -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 }}

View File

@@ -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

View File

@@ -42,6 +42,4 @@
/src/Lean/Elab/Tactic/Guard.lean @digama0
/src/Init/Guard.lean @digama0
/src/Lean/Server/CodeActions/ @digama0
/src/Std/ @TwoFX
/src/Std/Tactic/BVDecide/ @hargoniX
/src/Lean/Elab/Tactic/BVDecide/ @hargoniX

View File

@@ -63,20 +63,6 @@ Because the change will be squashed, there is no need to polish the commit messa
Reviews and Feedback:
----
The lean4 repo is managed by the Lean FRO's *triage team* that aims to provide initial feedback on new bug reports, PRs, and RFCs weekly.
This feedback generally consists of prioritizing the ticket using one of the following categories:
* label `P-high`: We will work on this issue
* label `P-medium`: We may work on this issue if we find the time
* label `P-low`: We are not planning to work on this issue
* *closed*: This issue is already fixed, it is not an issue, or is not sufficiently compatible with our roadmap for the project and we will not work on it nor accept external contributions on it
For *bug reports*, the listed priority reflects our commitment to fixing the issue.
It is generally indicative but not necessarily identical to the priority an external contribution addressing this bug would receive.
For *PRs* and *RFCs*, the priority reflects our commitment to reviewing them and getting them to an acceptable state.
Accepted RFCs are marked with the label `RFC accepted` and afterwards assigned a new "implementation" priority as with bug reports.
General guidelines for interacting with reviews and feedback:
**Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time.
**Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism.

View File

@@ -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.

View File

@@ -8,299 +8,16 @@ 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
v4.11.0
----------
Development in progress.
v4.11.0
----------
Release candidate, release notes will be copied from the branch `releases/v4.11.0` once completed.
v4.10.0
----------
### Language features, tactics, and metaprograms
* `split` tactic:
* [#4401](https://github.com/leanprover/lean4/pull/4401) improves the strategy `split` uses to generalize discriminants of matches and adds `trace.split.failure` trace class for diagnosing issues.
* `rw` tactic:
* [#4385](https://github.com/leanprover/lean4/pull/4385) prevents the tactic from claiming pre-existing goals are new subgoals.
* [dac1da](https://github.com/leanprover/lean4/commit/dac1dacc5b39911827af68247d575569d9c399b5) adds configuration for ordering new goals, like for `apply`.
* `simp` tactic:
* [#4430](https://github.com/leanprover/lean4/pull/4430) adds `dsimproc`s for `if` expressions (`ite` and `dite`).
* [#4434](https://github.com/leanprover/lean4/pull/4434) improves heuristics for unfolding. Equational lemmas now have priorities where more-specific equationals lemmas are tried first before a possible catch-all.
* [#4481](https://github.com/leanprover/lean4/pull/4481) fixes an issue where function-valued `OfNat` numeric literals would become denormalized.
* [#4467](https://github.com/leanprover/lean4/pull/4467) fixes an issue where dsimp theorems might not apply to literals.
* [#4484](https://github.com/leanprover/lean4/pull/4484) fixes the source position for the warning for deprecated simp arguments.
* [#4258](https://github.com/leanprover/lean4/pull/4258) adds docstrings for `dsimp` configuration.
* [#4567](https://github.com/leanprover/lean4/pull/4567) improves the accuracy of used simp lemmas reported by `simp?`.
* [fb9727](https://github.com/leanprover/lean4/commit/fb97275dcbb683efe6da87ed10a3f0cd064b88fd) adds (but does not implement) the simp configuration option `implicitDefEqProofs`, which will enable including `rfl`-theorems in proof terms.
* `omega` tactic:
* [#4360](https://github.com/leanprover/lean4/pull/4360) makes the tactic generate error messages lazily, improving its performance when used in tactic combinators.
* `bv_omega` tactic:
* [#4579](https://github.com/leanprover/lean4/pull/4579) works around changes to the definition of `Fin.sub` in this release.
* [#4490](https://github.com/leanprover/lean4/pull/4490) sets up groundwork for a tactic index in generated documentation, as there was in Lean 3. See PR description for details.
* **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 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.
* [8f023b](https://github.com/leanprover/lean4/commit/8f023b85c554186ae562774b8122322d856c674e), [3c4d6b](https://github.com/leanprover/lean4/commit/3c4d6ba8648eb04d90371eb3fdbd114d16949501) and [0783d0](https://github.com/leanprover/lean4/commit/0783d0fcbe31b626fbd3ed2f29d838e717f09101) change the `#reduce` command to be able to control what gets reduced.
For example, `#reduce (proofs := true) (types := false) e` reduces both proofs and types in the expression `e`.
By default, neither proofs or types are reduced.
* [#4489](https://github.com/leanprover/lean4/pull/4489) fixes an elaboration bug in `#check_tactic`.
* [#4505](https://github.com/leanprover/lean4/pull/4505) adds support for `open _root_.<namespace>`.
* **Options**
* [#4576](https://github.com/leanprover/lean4/pull/4576) adds the `debug.byAsSorry` option. Setting `set_option debug.byAsSorry true` causes all `by ...` terms to elaborate as `sorry`.
* [7b56eb](https://github.com/leanprover/lean4/commit/7b56eb20a03250472f4b145118ae885274d1f8f7) and [d8e719](https://github.com/leanprover/lean4/commit/d8e719f9ab7d049e423473dfc7a32867d32c856f) add the `debug.skipKernelTC` option. Setting `set_option debug.skipKernelTC true` turns off kernel typechecking. This is meant for temporarily working around kernel performance issues, and it compromises soundness since buggy tactics may produce invalid proofs, which will not be caught if this option is set to true.
* [#4301](https://github.com/leanprover/lean4/pull/4301)
adds a linter to flag situations where a local variable's name is one of
the argumentless constructors of its type. This can arise when a user either
doesn't open a namespace or doesn't add a dot or leading qualifier, as
in the following:
```lean
inductive Tree (α : Type) where
| leaf
| branch (left : Tree α) (val : α) (right : Tree α)
def depth : Tree α → Nat
| leaf => 0
```
With this linter, the `leaf` pattern is highlighted as a local
variable whose name overlaps with the constructor `Tree.leaf`.
The linter can be disabled with `set_option linter.constructorNameAsVariable false`.
Additionally, the error message that occurs when a name in a pattern that takes arguments isn't valid now suggests similar names that would be valid. This means that the following definition:
```lean
def length (list : List α) : Nat :=
match list with
| nil => 0
| cons x xs => length xs + 1
```
now results in the following warning:
```
warning: Local variable 'nil' resembles constructor 'List.nil' - write '.nil' (with a dot) or 'List.nil' to use the constructor.
note: this linter can be disabled with `set_option linter.constructorNameAsVariable false`
```
and error:
```
invalid pattern, constructor or constant marked with '[match_pattern]' expected
Suggestion: 'List.cons' is similar
```
* **Metaprogramming**
* [#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 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
* [#4443](https://github.com/leanprover/lean4/pull/4443) makes the watchdog be more resilient against badly behaving clients.
### Pretty printing
* [#4433](https://github.com/leanprover/lean4/pull/4433) restores fallback pretty printers when context is not available, and documents `addMessageContext`.
* [#4556](https://github.com/leanprover/lean4/pull/4556) introduces `pp.maxSteps` option and sets the default value of `pp.deepTerms` to `false`. Together, these keep excessively large or deep terms from overwhelming the Infoview.
### Library
* [#4560](https://github.com/leanprover/lean4/pull/4560) splits `GetElem` class into `GetElem` and `GetElem?`.
This enables removing `Decidable` instance arguments from `GetElem.getElem?` and `GetElem.getElem!`, improving their rewritability.
See the docstrings for these classes for more information.
* `Array`
* [#4389](https://github.com/leanprover/lean4/pull/4389) makes `Array.toArrayAux_eq` be a `simp` lemma.
* [#4399](https://github.com/leanprover/lean4/pull/4399) improves robustness of the proof for `Array.reverse_data`.
* `List`
* [#4469](https://github.com/leanprover/lean4/pull/4469) and [#4475](https://github.com/leanprover/lean4/pull/4475) improve the organization of the `List` API.
* [#4470](https://github.com/leanprover/lean4/pull/4470) improves the `List.set` and `List.concat` API.
* [#4472](https://github.com/leanprover/lean4/pull/4472) upstreams lemmas about `List.filter` from Batteries.
* [#4473](https://github.com/leanprover/lean4/pull/4473) adjusts `@[simp]` attributes.
* [#4488](https://github.com/leanprover/lean4/pull/4488) makes `List.getElem?_eq_getElem` be a simp lemma.
* [#4487](https://github.com/leanprover/lean4/pull/4487) adds missing `List.replicate` API.
* [#4521](https://github.com/leanprover/lean4/pull/4521) adds lemmas about `List.map`.
* [#4500](https://github.com/leanprover/lean4/pull/4500) changes `List.length_cons` to use `as.length + 1` instead of `as.length.succ`.
* [#4524](https://github.com/leanprover/lean4/pull/4524) fixes the statement of `List.filter_congr`.
* [#4525](https://github.com/leanprover/lean4/pull/4525) changes binder explicitness in `List.bind_map`.
* [#4550](https://github.com/leanprover/lean4/pull/4550) adds `maximum?_eq_some_iff'` and `minimum?_eq_some_iff?`.
* [#4400](https://github.com/leanprover/lean4/pull/4400) switches the normal forms for indexing `List` and `Array` to `xs[n]` and `xs[n]?`.
* `HashMap`
* [#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 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.
* [#4522](https://github.com/leanprover/lean4/pull/4522) moves `@[simp]` from `Nat.pred_le` to `Nat.sub_one_le`.
* [#4532](https://github.com/leanprover/lean4/pull/4532) changes various `Nat.succ n` to `n + 1`.
* `Int`
* [#3850](https://github.com/leanprover/lean4/pull/3850) adds complete div/mod simprocs for `Int`.
* `String`/`Char`
* [#4357](https://github.com/leanprover/lean4/pull/4357) make the byte size interface be `Nat`-valued with functions `Char.utf8Size` and `String.utf8ByteSize`.
* [#4438](https://github.com/leanprover/lean4/pull/4438) upstreams `Char.ext` from Batteries and adds some `Char` documentation to the manual.
* `Fin`
* [#4421](https://github.com/leanprover/lean4/pull/4421) adjusts `Fin.sub` to be more performant in definitional equality checks.
* `Prod`
* [#4526](https://github.com/leanprover/lean4/pull/4526) adds missing `Prod.map` lemmas.
* [#4533](https://github.com/leanprover/lean4/pull/4533) fixes binder explicitness in lemmas.
* `BitVec`
* [#4428](https://github.com/leanprover/lean4/pull/4428) adds missing `simproc` for `BitVec` equality.
* [#4417](https://github.com/leanprover/lean4/pull/4417) adds `BitVec.twoPow` and lemmas, toward bitblasting multiplication for LeanSAT.
* `Std` library
* [#4499](https://github.com/leanprover/lean4/pull/4499) introduces `Std`, a library situated between `Init` and `Lean`, providing functionality not in the prelude both to Lean's implementation and to external users.
* **Other fixes or improvements**
* [#3056](https://github.com/leanprover/lean4/pull/3056) standardizes on using `(· == a)` over `(a == ·)`.
* [#4502](https://github.com/leanprover/lean4/pull/4502) fixes errors reported by running the library through the the Batteries linters.
### Lean internals
* [#4391](https://github.com/leanprover/lean4/pull/4391) makes `getBitVecValue?` recognize `BitVec.ofNatLt`.
* [#4410](https://github.com/leanprover/lean4/pull/4410) adjusts `instantiateMVars` algorithm to zeta reduce `let` expressions while beta reducing instantiated metavariables.
* [#4420](https://github.com/leanprover/lean4/pull/4420) fixes occurs check for metavariable assignments to also take metavariable types into account.
* [#4425](https://github.com/leanprover/lean4/pull/4425) fixes `forEachModuleInDir` to iterate over each Lean file exactly once.
* [#3886](https://github.com/leanprover/lean4/pull/3886) adds support to build Lean core oleans using Lake.
* **Defeq and WHNF algorithms**
* [#4387](https://github.com/leanprover/lean4/pull/4387) improves performance of `isDefEq` by eta reducing lambda-abstracted terms during metavariable assignments, since these are beta reduced during metavariable instantiation anyway.
* [#4388](https://github.com/leanprover/lean4/pull/4388) removes redundant code in `isDefEqQuickOther`.
* **Typeclass inference**
* [#4530](https://github.com/leanprover/lean4/pull/4530) fixes handling of metavariables when caching results at `synthInstance?`.
* **Elaboration**
* [#4426](https://github.com/leanprover/lean4/pull/4426) makes feature where the "don't know how to synthesize implicit argument" error reports the name of the argument more reliable.
* [#4497](https://github.com/leanprover/lean4/pull/4497) fixes a name resolution bug for generalized field notation (dot notation).
* [#4536](https://github.com/leanprover/lean4/pull/4536) blocks the implicit lambda feature for `(e :)` notation.
* [#4562](https://github.com/leanprover/lean4/pull/4562) makes it be an error for there to be two functions with the same name in a `where`/`let rec` block.
* Recursion principles
* [#4549](https://github.com/leanprover/lean4/pull/4549) refactors `findRecArg`, extracting `withRecArgInfo`.
Errors are now reported in parameter order rather than the order they are tried (non-indices are tried first).
For every argument, it will say why it wasn't tried, even if the reason is obvious (e.g. a fixed prefix or is `Prop`-typed, etc.).
* Porting core C++ to Lean
* [#4474](https://github.com/leanprover/lean4/pull/4474) takes a step to refactor `constructions` toward a future port to Lean.
* [#4498](https://github.com/leanprover/lean4/pull/4498) ports `mk_definition_inferring_unsafe` to Lean.
* [#4516](https://github.com/leanprover/lean4/pull/4516) ports `recOn` construction to Lean.
* [#4517](https://github.com/leanprover/lean4/pull/4517), [#4653](https://github.com/leanprover/lean4/pull/4653), and [#4651](https://github.com/leanprover/lean4/pull/4651) port `below` and `brecOn` construction to Lean.
* Documentation
* [#4501](https://github.com/leanprover/lean4/pull/4501) adds a more-detailed docstring for `PersistentEnvExtension`.
* **Other fixes or improvements**
* [#4382](https://github.com/leanprover/lean4/pull/4382) removes `@[inline]` attribute from `NameMap.find?`, which caused respecialization at each call site.
* [5f9ded](https://github.com/leanprover/lean4/commit/5f9dedfe5ee9972acdebd669f228f487844a6156) improves output of `trace.Elab.snapshotTree`.
* [#4424](https://github.com/leanprover/lean4/pull/4424) removes "you might need to open '{dir}' in your editor" message that is now handled by Lake and the VS Code extension.
* [#4451](https://github.com/leanprover/lean4/pull/4451) improves the performance of `CollectMVars` and `FindMVar`.
* [#4479](https://github.com/leanprover/lean4/pull/4479) adds missing `DecidableEq` and `Repr` instances for intermediate structures used by the `BitVec` and `Fin` simprocs.
* [#4492](https://github.com/leanprover/lean4/pull/4492) adds tests for a previous `isDefEq` issue.
* [9096d6](https://github.com/leanprover/lean4/commit/9096d6fc7180fe533c504f662bcb61550e4a2492) removes `PersistentHashMap.size`.
* [#4508](https://github.com/leanprover/lean4/pull/4508) fixes `@[implemented_by]` for functions defined by well-founded recursion.
* [#4509](https://github.com/leanprover/lean4/pull/4509) adds additional tests for `apply?` tactic.
* [d6eab3](https://github.com/leanprover/lean4/commit/d6eab393f4df9d473b5736d636b178eb26d197e6) fixes a benchmark.
* [#4563](https://github.com/leanprover/lean4/pull/4563) adds a workaround for a bug in `IndPredBelow.mkBelowMatcher`.
* **Cleanup:** [#4380](https://github.com/leanprover/lean4/pull/4380), [#4431](https://github.com/leanprover/lean4/pull/4431), [#4494](https://github.com/leanprover/lean4/pull/4494), [e8f768](https://github.com/leanprover/lean4/commit/e8f768f9fd8cefc758533bc76e3a12b398ed4a39), [de2690](https://github.com/leanprover/lean4/commit/de269060d17a581ed87f40378dbec74032633b27), [d3a756](https://github.com/leanprover/lean4/commit/d3a7569c97123d022828106468d54e9224ed8207), [#4404](https://github.com/leanprover/lean4/pull/4404), [#4537](https://github.com/leanprover/lean4/pull/4537).
### Compiler, runtime, and FFI
* [d85d3d](https://github.com/leanprover/lean4/commit/d85d3d5f3a09ff95b2ee47c6f89ef50b7e339126) fixes criterion for tail-calls in ownership calculation.
* [#3963](https://github.com/leanprover/lean4/pull/3963) adds validation of UTF-8 at the C++-to-Lean boundary in the runtime.
* [#4512](https://github.com/leanprover/lean4/pull/4512) fixes missing unboxing in interpreter when loading initialized value.
* [#4477](https://github.com/leanprover/lean4/pull/4477) exposes the compiler flags for the bundled C compiler (clang).
### Lake
* [#4384](https://github.com/leanprover/lean4/pull/4384) deprecates `inputFile` and replaces it with `inputBinFile` and `inputTextFile`. Unlike `inputBinFile` (and `inputFile`), `inputTextFile` normalizes line endings, which helps ensure text file traces are platform-independent.
* [#4371](https://github.com/leanprover/lean4/pull/4371) simplifies dependency resolution code.
* [#4439](https://github.com/leanprover/lean4/pull/4439) touches up the Lake configuration DSL and makes other improvements:
string literals can now be used instead of identifiers for names,
avoids using French quotes in `lake new` and `lake init` templates,
changes the `exe` template to use `Main` for the main module,
improves the `math` template error if `lean-toolchain` fails to download,
and downgrades unknown configuration fields from an error to a warning to improve cross-version compatibility.
* [#4496](https://github.com/leanprover/lean4/pull/4496) tweaks `require` syntax and updates docs. Now `require` in TOML for a package name such as `doc-gen4` does not need French quotes.
* [#4485](https://github.com/leanprover/lean4/pull/4485) fixes a bug where package versions in indirect dependencies would take precedence over direct dependencies.
* [#4478](https://github.com/leanprover/lean4/pull/4478) fixes a bug where Lake incorrectly included the module dynamic library in a platform-independent trace.
* [#4529](https://github.com/leanprover/lean4/pull/4529) fixes some issues with bad import errors.
A bad import in an executable no longer prevents the executable's root
module from being built. This also fixes a problem where the location
of a transitive bad import would not been shown.
The root module of the executable now respects `nativeFacets`.
* [#4564](https://github.com/leanprover/lean4/pull/4564) fixes a bug where non-identifier script names could not be entered on the CLI without French quotes.
* [#4566](https://github.com/leanprover/lean4/pull/4566) addresses a few issues with precompiled libraries.
* Fixes a bug where Lake would always precompile the package of a module.
* If a module is precompiled, it now precompiles its imports. Previously, it would only do this if imported.
* [#4495](https://github.com/leanprover/lean4/pull/4495), [#4692](https://github.com/leanprover/lean4/pull/4692), [#4849](https://github.com/leanprover/lean4/pull/4849)
add a new type of `require` that fetches package metadata from a
registry API endpoint (e.g. Reservoir) and then clones a Git package
using the information provided. To require such a dependency, the new
syntax is:
```lean
require <scope> / <pkg-name> [@ git <rev>]
-- Examples:
require "leanprover" / "doc-gen4"
require "leanprover-community" / "proofwidgets" @ git "v0.0.39"
```
Or in TOML:
```toml
[[require]]
name = "<pkg-name>"
scope = "<scope>"
rev = "<rev>"
```
Unlike with Git dependencies, Lake can make use of the richer
information provided by the registry to determine the default branch of
the package. This means for repositories of packages like `doc-gen4`
which have a default branch that is not `master`, Lake will now use said
default branch (e.g., in `doc-gen4`'s case, `main`).
Lake also supports configuring the registry endpoint via an environment
variable: `RESERVIOR_API_URL`. Thus, any server providing a similar
interface to Reservoir can be used as the registry. Further
configuration options paralleling those of Cargo's [Alternative Registries](https://doc.rust-lang.org/cargo/reference/registries.html)
and [Source Replacement](https://doc.rust-lang.org/cargo/reference/source-replacement.html)
will come in the future.
### DevOps/CI
* [#4427](https://github.com/leanprover/lean4/pull/4427) uses Namespace runners for CI for `leanprover/lean4`.
* [#4440](https://github.com/leanprover/lean4/pull/4440) fixes speedcenter tests in CI.
* [#4441](https://github.com/leanprover/lean4/pull/4441) fixes that workflow change would break CI for unrebased PRs.
* [#4442](https://github.com/leanprover/lean4/pull/4442) fixes Wasm release-ci.
* [6d265b](https://github.com/leanprover/lean4/commit/6d265b42b117eef78089f479790587a399da7690) fixes for `github.event.pull_request.merge_commit_sha` sometimes not being available.
* [16cad2](https://github.com/leanprover/lean4/commit/16cad2b45c6a77efe4dce850dcdbaafaa7c91fc3) adds optimization for CI to not fetch complete history.
* [#4544](https://github.com/leanprover/lean4/pull/4544) causes releases to be marked as prerelease on GitHub.
* [#4446](https://github.com/leanprover/lean4/pull/4446) switches Lake to using `src/lake/lakefile.toml` to avoid needing to load a version of Lake to build Lake.
* Nix
* [5eb5fa](https://github.com/leanprover/lean4/commit/5eb5fa49cf9862e99a5bccff8d4ca1a062f81900) fixes `update-stage0-commit` for Nix.
* [#4476](https://github.com/leanprover/lean4/pull/4476) adds gdb to Nix shell.
* [e665a0](https://github.com/leanprover/lean4/commit/e665a0d716dc42ba79b339b95e01eb99fe932cb3) fixes `update-stage0` for Nix.
* [4808eb](https://github.com/leanprover/lean4/commit/4808eb7c4bfb98f212b865f06a97d46c44978a61) fixes `cacheRoots` for Nix.
* [#3811](https://github.com/leanprover/lean4/pull/3811) adds platform-dependent flag to lib target.
* [#4587](https://github.com/leanprover/lean4/pull/4587) adds linking of `-lStd` back into nix build flags on darwin.
### Breaking changes
* `Char.csize` is replaced by `Char.utf8Size` ([#4357](https://github.com/leanprover/lean4/pull/4357)).
* Library lemmas now are in terms of `(· == a)` over `(a == ·)` ([#3056](https://github.com/leanprover/lean4/pull/3056)).
* Now the normal forms for indexing into `List` and `Array` is `xs[n]` and `xs[n]?` rather than using functions like `List.get` ([#4400](https://github.com/leanprover/lean4/pull/4400)).
* Sometimes terms created via a sequence of unifications will be more eta reduced than before and proofs will require adaptation ([#4387](https://github.com/leanprover/lean4/pull/4387)).
* The `GetElem` class has been split into two; see the docstrings for `GetElem` and `GetElem?` for more information ([#4560](https://github.com/leanprover/lean4/pull/4560)).
Release candidate, release notes will be copied from branch `releases/v4.10.0` once completed.
v4.9.0
----------
----------
### Language features, tactics, and metaprograms
@@ -323,8 +40,6 @@ v4.9.0
* [#4395](https://github.com/leanprover/lean4/pull/4395) adds conservative fix for whitespace handling to avoid incremental reuse leading to goals in front of the text cursor being shown.
* [#4407](https://github.com/leanprover/lean4/pull/4407) fixes non-incremental commands in macros blocking further incremental reporting.
* [#4436](https://github.com/leanprover/lean4/pull/4436) fixes incremental reporting when there are nested tactics in terms.
* [#4459](https://github.com/leanprover/lean4/pull/4459) adds incrementality support for `next` and `if` tactics.
* [#4554](https://github.com/leanprover/lean4/pull/4554) disables incrementality for tactics in terms in tactics.
* **Functional induction**
* [#4135](https://github.com/leanprover/lean4/pull/4135) ensures that the names used for functional induction are reserved.
* [#4327](https://github.com/leanprover/lean4/pull/4327) adds support for structural recursion on reflexive types.
@@ -370,7 +85,7 @@ v4.9.0
When `index := false`, only the head function is taken into account, like in Lean 3.
This feature can help users diagnose tricky simp failures or issues in code from libraries
developed using Lean 3 and then ported to Lean 4.
In the following example, it will report that `foo` is a problematic theorem.
```lean
opaque f : Nat → Nat → Nat
@@ -390,7 +105,7 @@ v4.9.0
opaque f : Nat → Nat → Nat
@[simp] theorem foo : f x (no_index (x, y).2) = y := by sorry
example : f a b ≤ b := by
simp -- `foo` is still applied with `index := true`
```
@@ -408,8 +123,6 @@ v4.9.0
* [#4267](https://github.com/leanprover/lean4/pull/4267) cases signature elaboration errors to show even if there are parse errors in the body.
* [#4368](https://github.com/leanprover/lean4/pull/4368) improves error messages when numeric literals fail to synthesize an `OfNat` instance,
including special messages warning when the expected type of the numeral can be a proposition.
* [#4643](https://github.com/leanprover/lean4/pull/4643) fixes issue leading to nested error messages and info trees vanishing, where snapshot subtrees were not restored on reuse.
* [#4657](https://github.com/leanprover/lean4/pull/4657) calculates error suppression per snapshot, letting elaboration errors appear even when there are later parse errors ([RFC #3556](https://github.com/leanprover/lean4/issues/3556)).
* **Metaprogramming**
* [#4167](https://github.com/leanprover/lean4/pull/4167) adds `Lean.MVarId.revertAll` to revert all free variables.
* [#4169](https://github.com/leanprover/lean4/pull/4169) adds `Lean.MVarId.ensureNoMVar` to ensure the goal's target contains no expression metavariables.
@@ -526,8 +239,6 @@ v4.9.0
* [#4192](https://github.com/leanprover/lean4/pull/4192) fixes restoration of infotrees when auto-bound implicit feature is activated,
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.
* [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).
@@ -538,7 +249,6 @@ v4.9.0
* [#3915](https://github.com/leanprover/lean4/pull/3915) documents the runtime memory layout for inductive types.
### Lake
* [#4518](https://github.com/leanprover/lean4/pull/4518) makes trace reading more robust. Lake now rebuilds if trace files are invalid or unreadable and is backwards compatible with previous pure numeric traces.
* [#4057](https://github.com/leanprover/lean4/pull/4057) adds support for docstrings on `require` commands.
* [#4088](https://github.com/leanprover/lean4/pull/4088) improves hovers for `family_def` and `library_data` commands.
* [#4147](https://github.com/leanprover/lean4/pull/4147) adds default `README.md` to package templates
@@ -588,14 +298,12 @@ v4.9.0
* [#4333](https://github.com/leanprover/lean4/pull/4333) adjusts workflow to update Batteries in manifest when creating `lean-pr-testing-NNNN` Mathlib branches.
* [#4355](https://github.com/leanprover/lean4/pull/4355) simplifies `lean4checker` step of release checklist.
* [#4361](https://github.com/leanprover/lean4/pull/4361) adds installing elan to `pr-release` CI step.
* [#4628](https://github.com/leanprover/lean4/pull/4628) fixes the Windows build, which was missing an exported symbol.
### Breaking changes
While most changes could be considered to be a breaking change, this section makes special note of API changes.
* `Nat.zero_or` and `Nat.or_zero` have been swapped ([#4094](https://github.com/leanprover/lean4/pull/4094)).
* `IsLawfulSingleton` is now `LawfulSingleton` ([#4350](https://github.com/leanprover/lean4/pull/4350)).
* The `BitVec` literal notation is now `<num>#<term>` rather than `<term>#<term>`, and it is global rather than scoped. Use `BitVec.ofNat w x` rather than `x#w` when `x` is a not a numeric literal ([0d3051](https://github.com/leanprover/lean4/commit/0d30517dca094a07bcb462252f718e713b93ffba)).
* `BitVec.rotateLeft` and `BitVec.rotateRight` now take the shift modulo the bitwidth ([#4229](https://github.com/leanprover/lean4/pull/4229)).
* These are no longer simp lemmas:
`List.length_pos` ([#4172](https://github.com/leanprover/lean4/pull/4172)),

View File

@@ -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

View File

@@ -5,11 +5,7 @@ See below for the checklist for release candidates.
We'll use `v4.6.0` as the intended release version as a running example.
- One week before the planned release, ensure that
(1) someone has written the release notes and
(2) someone has written the first draft of the release blog post.
If there is any material in `./releases_drafts/` on the `releases/v4.6.0` branch, then the release notes are not done.
(See the section "Writing the release notes".)
- One week before the planned release, ensure that someone has written the first draft of the release blog post
- `git checkout releases/v4.6.0`
(This branch should already exist, from the release candidates.)
- `git pull`
@@ -17,6 +13,11 @@ We'll use `v4.6.0` as the intended release version as a running example.
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
- `set(LEAN_VERSION_IS_RELEASE 1)`
- (both of these should already be in place from the release candidates)
- In `RELEASES.md`, verify that the `v4.6.0` section has been completed during the release candidate cycle.
It should be in bullet point form, with a point for every significant PR,
and may have a paragraph describing each major new language feature.
It should have a "breaking changes" section calling out changes that are specifically likely
to cause problems for downstream users.
- `git tag v4.6.0`
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
- Now wait, while CI runs.
@@ -27,9 +28,8 @@ We'll use `v4.6.0` as the intended release version as a running example.
you may want to start on the release candidate checklist now.
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
- Edit the release notes on Github to select the "Set as the latest release".
- Follow the instructions in creating a release candidate for the "GitHub release notes" step,
now that we have a written `RELEASES.md` section.
Do a quick sanity check.
- Copy and paste the Github release notes from the previous releases candidate for this version
(e.g. `v4.6.0-rc1`), and quickly sanity check.
- Next, we will move a curated list of downstream repos to the latest stable release.
- For each of the repositories listed below:
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
@@ -92,10 +92,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
- Merge the tag into `stable`
- The `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Replace the `v4.6.0` section on `master` with the `v4.6.0` section on `releases/v4.6.0`
and commit this to `master`.
- Merge the release announcement PR for the Lean website - it will be deployed automatically
- Finally, make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
@@ -106,6 +102,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
## Optimistic(?) time estimates:
- Initial checks and push the tag: 30 minutes.
- Note that if `RELEASES.md` has discrepancies this could take longer!
- Waiting for the release: 60 minutes.
- Fixing release notes: 10 minutes.
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
@@ -132,52 +129,54 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
```
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.`
- We will rely on automatically generated release notes for release candidates,
and the written release notes will be used for stable versions only.
It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion.
- In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
- Our current goal is to have written release notes only about major language features or breaking changes,
and to rely on automatically generated release notes for bugfixes and minor changes.
- Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
- Commit your changes to `RELEASES.md`, and push.
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
- In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
- Commit your changes to `src/CMakeLists.txt`, and push.
- `git tag v4.7.0-rc1`
- `git push origin v4.7.0-rc1`
- Ping the FRO Zulip that release notes need to be written. The release notes do not block completing the rest of this checklist.
- Now wait, while CI runs.
- 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".
This will add a list of all the commits since the last stable version.
- Once the release appears at https://github.com/leanprover/lean4/releases/
- Edit the release notes on Github to select the "Set as a pre-release box".
- Copy the section of `RELEASES.md` for this version into the Github release notes.
- Use the title "Changes since v4.6.0 (from RELEASES.md)"
- Then 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!
It is essential for Mathlib CI that you then create the next `bump/v4.8.0` branch
for the next development cycle.
Set the `lean-toolchain` file on this branch to same `nightly` you used for this release.
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
@@ -188,21 +187,12 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
Please also make sure that whoever is handling social media knows the release is out.
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
- Replaces the "release notes will be copied" text in the `v4.6.0` section of `RELEASES.md` with the
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.
```
and inserts the following section before that section:
```
v4.8.0
----------
Development in progress.
```
- Removes all the entries from the `./releases_drafts/` folder.
- Titled "chore: begin development cycle for v4.8.0"
- In `RELEASES.md`, update the `v4.7.0` section to say:
"Release candidate, release notes will be copied from branch `releases/v4.7.0` once completed."
Make sure that whoever is preparing the release notes during this cycle knows that it is their job to do so!
- In `RELEASES.md`, update the `v4.8.0` section to say:
"Development in progress".
- In `RELEASES.md`, verify that the old section `v4.6.0` has the full releases notes from the `releases/v4.6.0` branch.
## Time estimates:
Slightly longer than the corresponding steps for a stable release.
@@ -227,30 +217,12 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
* This can either be done by the person managing this process directly,
or by soliciting assistance from authors of files, or generally helpful people on Zulip!
* Each repo has a `bump/v4.7.0` which accumulates reviewed changes adapting to new versions.
* Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we will create a PR to `bump/v4.7.0`.
* For Mathlib, there is a script in `scripts/create-adaptation-pr.sh` that automates this process.
* For Batteries and Aesop it is currently manual.
* For all of these repositories, the process is the same:
* Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we:
* Make sure `bump/v4.7.0` is up to date with `master` (by merging `master`, no PR necessary)
* Create from `bump/v4.7.0` a `bump/nightly-2024-02-15` branch.
* In that branch, `git merge nightly-testing` to bring across changes from `nightly-testing`.
* In that branch, `git merge --squash nightly-testing` to bring across changes from `nightly-testing`.
* Sanity check changes, commit, and make a PR to `bump/v4.7.0` from the `bump/nightly-2024-02-15` branch.
* Solicit review, merge the PR into `bump/v4.7.0`.
* Solicit review, merge the PR into `bump/v4,7,0`.
* It is always okay to merge in the following directions:
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
Please remember to push any merges you make to intermediate steps!
# Writing the release notes
We are currently trying a system where release notes are compiled all at once from someone looking through the commit history.
The exact steps are a work in progress.
Here is the general idea:
* The work is done right on the `releases/v4.6.0` branch sometime after it is created but before the stable release is made.
The release notes for `v4.6.0` will later be copied to `master` when we begin a new development cycle.
* There can be material for release notes entries in commit messages.
* There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
See `./releases_drafts/README.md` for more information.
* The release notes should be written from a downstream expert user's point of view.
This section will be updated when the next release notes are written (for `v4.10.0`).

View File

@@ -149,4 +149,4 @@ def fact : Expr ctx (Ty.fn Ty.int Ty.int) :=
(op (·*·) (delay fun _ => app fact (op (·-·) (var stop) (val 1))) (var stop)))
decreasing_by sorry
#eval! fact.interp Env.nil 10
#eval fact.interp Env.nil 10

View File

@@ -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.
![Rubik's cube](../images/widgets_rubiks.png)
## 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` instnace.
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

138
doc/flake.lock generated
View File

@@ -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",

View File

@@ -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: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -13,7 +13,7 @@ Recall that nonnegative numerals are considered to be a `Nat` if there are no ty
The operator `/` for `Int` implements integer division.
```lean
#eval -10 / 4 -- -3
#eval -10 / 4 -- -2
```
Similar to `Nat`, the internal representation of `Int` is optimized. Small integers are

View File

@@ -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.

View File

@@ -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:

View File

@@ -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
```
```

View File

@@ -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
```

View File

@@ -5,19 +5,14 @@ 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".
![installing the vscode-lean4 extension](images/code-ext.png)
![installing the vscode-lean4 extension](images/code-ext.png)
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`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Setup: Show Setup Guide".
![show setup guide](images/show-setup-guide.png)
![show setup guide](images/show-setup-guide.png)
1. Follow the Lean 4 setup guide. It will:
1. Follow the Lean 4 setup guide. It will walk you through learning resources for Lean 4, teach you how to set up Lean's dependencies on your platform, install Lean 4 for you at the click of a button and help you set up your first project.
- walk you through learning resources for Lean,
- teach you how to set up Lean's dependencies on your platform,
- install Lean 4 for you at the click of a button,
- help you set up your first project.
![setup guide](images/setup_guide.png)
![setup guide](images/setup_guide.png)

120
flake.lock generated
View File

@@ -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"
}
},

View File

@@ -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;
};
}

View File

@@ -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
'';
};
@@ -117,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
@@ -133,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} "$@"
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}/* $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`
@@ -158,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
'';
@@ -169,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

View File

@@ -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 (
@@ -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]
@@ -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}"
'';
};
})

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -1,17 +0,0 @@
**breaking change**
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.

View File

@@ -0,0 +1,45 @@
A new linter flags situations where a local variable's name is one of
the argumentless constructors of its type. This can arise when a user either
doesn't open a namespace or doesn't add a dot or leading qualifier, as
in the following:
````
inductive Tree (α : Type) where
| leaf
| branch (left : Tree α) (val : α) (right : Tree α)
def depth : Tree α → Nat
| leaf => 0
````
With this linter, the `leaf` pattern is highlighted as a local
variable whose name overlaps with the constructor `Tree.leaf`.
The linter can be disabled with `set_option linter.constructorNameAsVariable false`.
Additionally, the error message that occurs when a name in a pattern that takes arguments isn't valid now suggests similar names that would be valid. This means that the following definition:
```
def length (list : List α) : Nat :=
match list with
| nil => 0
| cons x xs => length xs + 1
```
now results in the following warning:
```
warning: Local variable 'nil' resembles constructor 'List.nil' - write '.nil' (with a dot) or 'List.nil' to use the constructor.
note: this linter can be disabled with `set_option linter.constructorNameAsVariable false`
```
and error:
```
invalid pattern, constructor or constant marked with '[match_pattern]' expected
Suggestion: 'List.cons' is similar
```
#4301

View File

@@ -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=''"

View File

@@ -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'"

View File

@@ -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=''"

View File

@@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 3.10)
cmake_policy(SET CMP0054 NEW)
cmake_policy(SET CMP0110 NEW)
if(NOT (${CMAKE_GENERATOR} MATCHES "Unix Makefiles"))
message(FATAL_ERROR "The only supported CMake generator at the moment is 'Unix Makefiles'")
endif()
@@ -10,7 +9,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 +242,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)
@@ -310,11 +300,11 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH)
string(APPEND LEANSHARED_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}")
endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lleanrt")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lnodefs.js -lleanrt")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
else()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -lStd -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
endif()
set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags")
@@ -323,7 +313,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LEAN_CXX_STDLIB "-lc++")
endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB} -lStd")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
# in local builds, link executables and not just dynlibs against C++ stdlib as well,
@@ -382,7 +372,6 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
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 CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
@@ -412,8 +401,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()
@@ -470,22 +459,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)
@@ -494,17 +467,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
@@ -524,23 +493,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")
@@ -548,12 +508,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}/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/libStd.a.export ${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
@@ -598,11 +571,11 @@ 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")
@@ -639,9 +612,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)

View File

@@ -57,7 +57,7 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
-- 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
@[simp] theorem ite_some_none_eq_some [Decidable P] :
@@ -67,8 +67,12 @@ theorem ite_some_none_eq_none [Decidable P] :
-- 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
simp only [dite_eq_right_iff]
rfl
@[simp] theorem dite_some_none_eq_some [Decidable P] {x : P α} {y : α} :
(if h : P then some (x h) else none) = some y h : P, x h = y := by
by_cases h : P <;> simp [h]
by_cases h : P <;> simp only [h, dite_cond_eq_true, dite_cond_eq_false, Option.some.injEq,
false_iff, not_exists]
case pos => exact fun h_eq Exists.intro h h_eq, fun h_exists => h_exists.2
case neg => exact fun h_false _ h_false

View File

@@ -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

View File

@@ -153,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

View File

@@ -188,13 +188,13 @@ theorem ext {x y : StateT σ m α} (h : ∀ s, x.run s = y.run s) : x = y :=
@[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
@[simp] theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ) :
(monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_monadMap [Monad m] [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ)
: (monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
show (f >>= fun g => g <$> x).run s = _

View File

@@ -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

View File

@@ -64,5 +64,5 @@ end StateRefT'
instance (ω σ : Type) (m : Type Type) : MonadControl m (StateRefT' ω σ m) :=
inferInstanceAs (MonadControl m (ReaderT _ _))
instance {m : Type Type} {ω σ : Type} [MonadFinally m] : MonadFinally (StateRefT' ω σ m) :=
instance {m : Type Type} {ω σ : Type} [MonadFinally m] [Monad m] : MonadFinally (StateRefT' ω σ m) :=
inferInstanceAs (MonadFinally (ReaderT _ _))

View File

@@ -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
/--
@@ -485,8 +474,6 @@ class LawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert
insert_emptyc_eq (x : α) : (insert x : β) = singleton x
export LawfulSingleton (insert_emptyc_eq)
attribute [simp] insert_emptyc_eq
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
class Sep (α : outParam <| Type u) (γ : Type v) where
/-- Computes `{ a ∈ c | p a }`. -/
@@ -714,7 +701,7 @@ theorem Ne.elim (h : a ≠ b) : a = b → False := h
theorem Ne.irrefl (h : a a) : False := h rfl
@[symm] theorem Ne.symm (h : a b) : b a := fun h₁ => h (h₁.symm)
theorem Ne.symm (h : a b) : b a := fun h₁ => h (h₁.symm)
theorem ne_comm {α} {a b : α} : a b b a := Ne.symm, Ne.symm
@@ -767,7 +754,7 @@ noncomputable def HEq.elim {α : Sort u} {a : α} {p : α → Sort v} {b : α} (
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
HEq.ndrecOn h₁ h₂
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
theorem HEq.symm (h : HEq a b) : HEq b a :=
h.rec (HEq.refl a)
theorem heq_of_eq (h : a = a') : HEq a a' :=
@@ -823,15 +810,15 @@ 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
@[symm] theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
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
@[symm] theorem And.symm : a b b a := fun ha, hb => hb, ha
theorem And.symm : a b b a := fun ha, hb => hb, ha
theorem And.comm : a b b a := Iff.intro And.symm And.symm
theorem and_comm : a b b a := And.comm
@[symm] theorem Or.symm : a b b a := .rec .inr .inl
theorem Or.symm : a b b a := .rec .inr .inl
theorem Or.comm : a b b a := Iff.intro Or.symm Or.symm
theorem or_comm : a b b a := Or.comm
@@ -1102,30 +1089,19 @@ def InvImage {α : Sort u} {β : Sort v} (r : β → β → Prop) (f : α → β
fun a₁ a₂ => r (f a₁) (f a₂)
/--
The transitive closure `TransGen r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `TransGen r a z` if and only if there exists a sequence
The transitive closure `r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `r a z` if and only if there exists a sequence
`a r b r ... r z` of length at least 1 connecting `a` to `z`.
-/
inductive Relation.TransGen {α : Sort u} (r : α α Prop) : α α Prop
/-- If `r a b` then `TransGen r a b`. This is the base case of the transitive closure. -/
| single {a b} : r a b TransGen r a b
inductive TC {α : Sort u} (r : α α Prop) : α α Prop where
/-- If `r a b` then `r a b`. This is the base case of the transitive closure. -/
| base : a b, r a b TC r a b
/-- The transitive closure is transitive. -/
| tail {a b c} : TransGen r a b r b c TransGen r a c
/-- 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
| trans : a b c, TC r a b TC r b c TC r a c
/-! # Subtype -/
namespace Subtype
theorem existsOfSubtype {α : Type u} {p : α Prop} : { x // p x } Exists (fun x => p x)
| a, h => a, h
@@ -1222,13 +1198,9 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
/-! # Dependent products -/
theorem Exists.of_psigma_prop {α : Sort u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
| x, hx => x, hx
@[deprecated Exists.of_psigma_prop (since := "2024-07-27")]
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x) :=
Exists.of_psigma_prop
protected theorem PSigma.eta {α : Sort u} {β : α Sort v} {a₁ a₂ : α} {b₁ : β a₁} {b₂ : β a₂}
(h₁ : a₁ = a₂) (h₂ : Eq.ndrec b₁ h₁ = b₂) : PSigma.mk a₁ b₁ = PSigma.mk a₂ b₂ := by
subst h₁
@@ -1390,9 +1362,6 @@ theorem iff_false_right (ha : ¬a) : (b ↔ a) ↔ ¬b := Iff.comm.trans (iff_fa
theorem of_iff_true (h : a True) : a := h.mpr trivial
theorem iff_true_intro (h : a) : a True := iff_of_true h trivial
theorem eq_iff_true_of_subsingleton [Subsingleton α] (x y : α) : x = y True :=
iff_true_intro (Subsingleton.elim ..)
theorem not_of_iff_false : (p False) ¬p := Iff.mp
theorem iff_false_intro (h : ¬a) : a False := iff_of_false h id
@@ -1564,13 +1533,13 @@ 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 :=
Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2)
@[inherit_doc Quot.rec, elab_as_elim] protected abbrev recOn
@[inherit_doc Quot.rec] protected abbrev recOn
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
@@ -1581,7 +1550,7 @@ so you should consider the simpler versions if they apply:
Dependent induction principle for a quotient, when the target type is a `Subsingleton`.
In this case the quotient's side condition is trivial so any function can be lifted.
-/
@[elab_as_elim] protected abbrev recOnSubsingleton
protected abbrev recOnSubsingleton
[h : (a : α) Subsingleton (motive (Quot.mk r a))]
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
@@ -1650,7 +1619,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
@@ -1898,7 +1867,7 @@ instance : Subsingleton (Squash α) where
/--
`Antisymm (·≤·)` says that `(·≤·)` is antisymmetric, that is, `a ≤ b → b ≤ a → a = b`.
-/
class Antisymm {α : Sort u} (r : α α Prop) : Prop where
class Antisymm {α : Sort u} (r : α α Prop) where
/-- An antisymmetric relation `(·≤·)` satisfies `a ≤ b → b ≤ a → a = b`. -/
antisymm {a b : α} : r a b r b a a = b

View File

@@ -35,7 +35,3 @@ 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

View File

@@ -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

View File

@@ -13,4 +13,3 @@ import Init.Data.Array.Mem
import Init.Data.Array.Attach
import Init.Data.Array.BasicAux
import Init.Data.Array.Lemmas
import Init.Data.Array.TakeDrop

View File

@@ -50,13 +50,6 @@ instance : Inhabited (Array α) where
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
the implementation only supports arrays of size less than `USize.size`.
-/
@[extern "lean_array_size", simp]
def usize (a : @& Array α) : USize := a.size.toUSize
/-- Low-level version of `fget` which is as fast as a C array read.
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
`fget` may be slightly slower than `uget`. -/
@@ -108,7 +101,7 @@ def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
a'.set (size_set a i v₂ j) v₁
/--
Swaps two entries in an array, or returns the array unchanged if either index is out of bounds.
Swaps two entries in an array, or panics if either index is out of bounds.
This will perform the update destructively provided that `a` has a reference
count of 1 when called.
@@ -181,7 +174,7 @@ def modifyOp (self : Array α) (idx : Nat) (f : αα) : Array α :=
This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < usizeSz` to true. -/
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Array α) (b : β) (f : α β m (ForInStep β)) : m β :=
let sz := as.usize
let sz := USize.ofNat as.size
let rec @[specialize] loop (i : USize) (b : β) : m β := do
if i < sz then
let a := as.uget i lcProof
@@ -287,7 +280,7 @@ def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
/-- See comment at `forInUnsafe` -/
@[inline]
unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α m β) (as : Array α) : m (Array β) :=
let sz := as.usize
let sz := USize.ofNat as.size
let rec @[specialize] map (i : USize) (r : Array NonScalar) : m (Array PNonScalar.{v}) := do
if i < sz then
let v := r.uget i lcProof

View File

@@ -6,8 +6,7 @@ Authors: Mario Carneiro
prelude
import Init.Data.Nat.MinMax
import Init.Data.Nat.Lemmas
import Init.Data.List.Monadic
import Init.Data.List.Nat.Range
import Init.Data.List.Lemmas
import Init.Data.Fin.Basic
import Init.Data.Array.Mem
import Init.TacticsExtra
@@ -52,7 +51,7 @@ theorem foldlM_eq_foldlM_data.aux [Monad m]
simp [foldlM_eq_foldlM_data.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
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
theorem foldlM_eq_foldlM_data [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
@@ -142,7 +141,7 @@ where
· rw [ List.get_drop_eq_drop _ i _]
simp only [aux (i + 1), map_eq_pure_bind, data_length, List.foldlM_cons, bind_assoc, pure_bind]
rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
termination_by arr.size - i
decreasing_by decreasing_trivial_pre_omega
@@ -335,19 +334,8 @@ theorem mem_data {a : α} {l : Array α} : a ∈ l.data ↔ a ∈ l := (mem_def
theorem not_mem_nil (a : α) : ¬ a #[] := nofun
theorem getElem_of_mem {a : α} {as : Array α} :
a as ( (n : Nat) (h : n < as.size), as[n]'h = a) := by
intro ha
rcases List.getElem_of_mem ha.val with i, hbound, hi
exists i
exists hbound
/-- # get lemmas -/
theorem lt_of_getElem {x : α} {a : Array α} {idx : Nat} {hidx : idx < a.size} (_ : a[idx] = x) :
idx < a.size :=
hidx
theorem getElem?_mem {l : Array α} {i : Fin l.size} : l[i] l := by
erw [Array.mem_def, getElem_eq_data_getElem]
apply List.get_mem
@@ -517,13 +505,6 @@ theorem size_eq_length_data (as : Array α) : as.size = as.data.length := rfl
simp only [mkEmpty_eq, size_push] at *
omega
@[simp] theorem data_range (n : Nat) : (range n).data = List.range n := by
induction n <;> simp_all [range, Nat.fold, flip, List.range_succ]
@[simp]
theorem getElem_range {n : Nat} {x : Nat} (h : x < (Array.range n).size) : (Array.range n)[x] = x := by
simp [getElem_eq_data_getElem]
set_option linter.deprecated false in
@[simp] theorem reverse_data (a : Array α) : a.reverse.data = a.data.reverse := by
let rec go (as : Array α) (i j hj)
@@ -726,27 +707,12 @@ theorem mapIdx_spec (as : Array α) (f : Fin as.size → α → β)
unfold modify modifyM Id.run
split <;> simp
theorem getElem_modify {as : Array α} {x i} (h : i < as.size) :
(as.modify x f)[i]'(by simp [h]) = if x = i then f as[i] else as[i] := by
simp only [modify, modifyM, get_eq_getElem, Id.run, Id.pure_eq]
split
· simp only [Id.bind_eq, get_set _ _ _ h]; split <;> simp [*]
· rw [if_neg (mt (by rintro rfl; exact h) _)]
theorem getElem_modify_self {as : Array α} {i : Nat} (h : i < as.size) (f : α α) :
(as.modify i f)[i]'(by simp [h]) = f as[i] := by
simp [getElem_modify h]
theorem getElem_modify_of_ne {as : Array α} {i : Nat} (hj : j < as.size)
(f : α α) (h : i j) :
(as.modify i f)[j]'(by rwa [size_modify]) = as[j] := by
simp [getElem_modify hj, h]
@[deprecated getElem_modify (since := "2024-08-08")]
theorem get_modify {arr : Array α} {x i} (h : i < arr.size) :
(arr.modify x f).get i, by simp [h] =
if x = i then f (arr.get i, h) else arr.get i, h := by
simp [getElem_modify h]
simp [modify, modifyM, Id.run]; split
· simp [get_set _ _ _ h]; split <;> simp [*]
· rw [if_neg (mt (by rintro rfl; exact h) _)]
/-! ### filter -/

View File

@@ -13,17 +13,17 @@ 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
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 =>
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
@[simp] theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)
@@ -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)

View File

@@ -1,17 +0,0 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop
namespace Array
theorem exists_of_uset (self : Array α) (i d h) :
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

View File

@@ -1,60 +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, Markus Himmel
-/
prelude
import Init.Data.Bool
set_option linter.missingDocs true
/-- `PartialEquivBEq α` says that the `BEq` implementation is a
partial equivalence relation, that is:
* it is symmetric: `a == b → b == a`
* it is transitive: `a == b → b == c → a == c`.
-/
class PartialEquivBEq (α) [BEq α] : Prop where
/-- Symmetry for `BEq`. If `a == b` then `b == a`. -/
symm : (a : α) == b b == a
/-- Transitivity for `BEq`. If `a == b` and `b == c` then `a == c`. -/
trans : (a : α) == b b == c a == c
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- Reflexivity for `BEq`. -/
refl : (a : α) == a
/-- `EquivBEq` says that the `BEq` implementation is an equivalence relation. -/
class EquivBEq (α) [BEq α] extends PartialEquivBEq α, ReflBEq α : Prop
@[simp]
theorem BEq.refl [BEq α] [ReflBEq α] {a : α} : a == a :=
ReflBEq.refl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.refl
theorem BEq.symm [BEq α] [PartialEquivBEq α] {a b : α} : a == b b == a :=
PartialEquivBEq.symm
theorem BEq.comm [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = (b == a) :=
Bool.eq_iff_iff.2 BEq.symm, BEq.symm
theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = false (b == a) = false :=
BEq.comm (α := α) id
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b b == c a == c :=
PartialEquivBEq.trans
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
(a == b) = false b == c (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))
theorem BEq.neq_of_beq_of_neq [BEq α] [PartialEquivBEq α] {a b c : α} :
a == b (b == c) = false (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₂ (BEq.trans (BEq.symm h₁) h₃)
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

View File

@@ -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)
@@ -69,9 +67,9 @@ protected def ofNat (n : Nat) (i : Nat) : BitVec n where
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
@@ -123,18 +121,18 @@ section getXsb
@[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 := getMsb x 0
@[inline] protected def msb (a : BitVec n) : Bool := getMsb a 0
end getXsb
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
@@ -215,7 +213,7 @@ 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 negation
@@ -262,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.
@@ -276,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
@@ -373,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
@@ -391,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
@@ -399,12 +397,12 @@ 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 `zeroExtend` that requires a proof, but is a noop.
-/
def zeroExtend' {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)
@@ -413,8 +411,8 @@ def zeroExtend' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
`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)
@@ -502,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
@@ -527,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 :=
@@ -594,9 +583,11 @@ 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

View File

@@ -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
@@ -100,37 +98,6 @@ theorem carry_succ (i : Nat) (x y : BitVec w) (c : Bool) :
exact mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ ..
cases x.toNat.testBit i <;> cases y.toNat.testBit i <;> (simp; omega)
/--
If `x &&& y = 0`, then the carry bit `(x + y + 0)` is always `false` for any index `i`.
Intuitively, this is because a carry is only produced when at least two of `x`, `y`, and the
previous carry are true. However, since `x &&& y = 0`, at most one of `x, y` can be true,
and thus we never have a previous carry, which means that the sum cannot produce a carry.
-/
theorem carry_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) : carry i x y false = false := by
induction i with
| zero => simp
| succ i ih =>
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`. -/
theorem carry_width {x y : BitVec w} :
carry w x y c = decide (x.toNat + y.toNat + c.toNat 2^w) := by
simp [carry]
/--
If `x &&& y = 0`, then addition does not overflow, and thus `(x + y).toNat = x.toNat + y.toNat`.
-/
theorem toNat_add_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) :
(x + y).toNat = x.toNat + y.toNat := by
rw [toNat_add]
apply Nat.mod_eq_of_lt
suffices ¬ decide (x.toNat + y.toNat + false.toNat 2^w) by
simp only [decide_eq_true_eq] at this
omega
rw [ carry_width]
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, Bool.xor x (Bool.xor y c))
@@ -192,21 +159,6 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
theorem allOnes_sub_eq_not (x : BitVec w) : allOnes w - x = ~~~x := by
rw [ add_not_self x, BitVec.add_comm, add_sub_cancel]
/-- Addition of bitvectors is the same as bitwise or, if bitwise and is zero. -/
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, getLsb_or,
Prod.mk.injEq, and_eq_false_imp]
intros i
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.getLsb i <;> simp_all [hx]
/-! ### Negation -/
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
@@ -283,274 +235,4 @@ theorem sle_eq_carry (x y : BitVec w) :
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
/-! ### mul recurrence for bitblasting -/
/--
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.getLsb s then (x <<< s) else 0
match s with
| 0 => cur
| s + 1 => mulRec x y s + cur
theorem mulRec_zero_eq (x y : BitVec w) :
mulRec x y 0 = if y.getLsb 0 then x 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.getLsb (s + 1) then (x <<< (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 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 [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp
· 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
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,
and performing the multplication. -/
theorem mulRec_eq_mul_signExtend_truncate (x y : BitVec w) (s : Nat) :
mulRec x y s = x * ((y.truncate (s + 1)).zeroExtend w) := by
induction s
case zero =>
simp only [mulRec_zero_eq, ofNat_eq_ofNat, Nat.reduceAdd]
by_cases y.getLsb 0
case pos hy =>
simp only [hy, reduceIte, truncate, zeroExtend_one_eq_ofBool_getLsb_zero,
ofBool_true, ofNat_eq_ofNat]
rw [zeroExtend_ofNat_one_eq_ofNat_one_of_lt (by omega)]
simp
case neg hy =>
simp [hy, zeroExtend_one_eq_ofBool_getLsb_zero]
case succ s' hs =>
rw [mulRec_succ_eq, hs]
have heq :
(if y.getLsb (s' + 1) = true then x <<< (s' + 1) else 0) =
(x * (y &&& (BitVec.twoPow w (s' + 1)))) := by
simp only [ofNat_eq_ofNat, and_twoPow]
by_cases hy : y.getLsb (s' + 1) <;> simp [hy]
rw [heq, BitVec.mul_add, zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow]
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
/-! ## shiftLeft recurrence for bitblasting -/
/--
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
The theorem `shiftLeft_eq_shiftLeftRec` proves the equivalence of `(x <<< y)` and `shiftLeftRec`.
Together with equations `shiftLeftRec_zero`, `shiftLeftRec_succ`,
this allows us to unfold `shiftLeft` into a circuit for bitblasting.
-/
def shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
match n with
| 0 => x <<< shiftAmt
| n + 1 => (shiftLeftRec x y n) <<< shiftAmt
@[simp]
theorem shiftLeftRec_zero {x : BitVec w₁} {y : BitVec w₂} :
shiftLeftRec x y 0 = x <<< (y &&& twoPow w₂ 0) := by
simp [shiftLeftRec]
@[simp]
theorem shiftLeftRec_succ {x : BitVec w₁} {y : BitVec w₂} :
shiftLeftRec x y (n + 1) = (shiftLeftRec x y n) <<< (y &&& twoPow w₂ (n + 1)) := by
simp [shiftLeftRec]
/--
If `y &&& z = 0`, `x <<< (y ||| z) = x <<< y <<< z`.
This follows as `y &&& z = 0` implies `y ||| z = y + z`,
and thus `x <<< (y ||| z) = x <<< (y + z) = x <<< y <<< z`.
-/
theorem shiftLeft_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
(h : y &&& z = 0#w₂) :
x <<< (y ||| z) = x <<< y <<< z := by
rw [ add_eq_or_of_and_eq_zero _ _ h,
shiftLeft_eq', toNat_add_of_and_eq_zero h]
simp [shiftLeft_add]
/--
`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.truncate (n + 1)).zeroExtend w₂ := by
induction n generalizing x y
case zero =>
ext i
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.getLsb (n + 1)
· simp only [h, reduceIte]
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h,
shiftLeft_or_of_and_eq_zero]
simp
· simp only [h, false_eq_true, reduceIte, shiftLeft_zero']
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false (i := n + 1)]
simp [h]
/--
Show that `x <<< y` can be written in terms of `shiftLeftRec`.
This can be unfolded in terms of `shiftLeftRec_zero`, `shiftLeftRec_succ` for bitblasting.
-/
theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
x <<< y = shiftLeftRec x y (w₂ - 1) := by
rcases w₂ with rfl | w₂
· simp [of_length_zero]
· simp [shiftLeftRec_eq]
/- ### 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.truncate (n + 1)).zeroExtend w₂) := by
induction n generalizing x y
case zero =>
ext i
simp [twoPow_zero, Nat.reduceAdd, and_one_eq_zeroExtend_ofBool_getLsb, truncate_one]
case succ n ih =>
simp only [sshiftRightRec_succ_eq, and_twoPow, ih]
by_cases h : y.getLsb (n + 1)
· rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h,
sshiftRight'_or_of_and_eq_zero (by simp), h]
simp
· rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_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).getLsb i = (sshiftRightRec x y (w₂ - 1)).getLsb i := by
rcases w₂ with rfl | w₂
· simp [of_length_zero]
· simp [sshiftRightRec_eq]
/- ### Logical shift right (ushiftRight) recurrence for bitblasting -/
/--
`ushiftRightRec x y n` shifts `x` logically to the right by the first `n` bits of `y`.
The theorem `shiftRight_eq_ushiftRightRec` proves the equivalence
of `(x >>> y)` and `ushiftRightRec`.
Together with equations `ushiftRightRec_zero`, `ushiftRightRec_succ`,
this allows us to unfold `ushiftRight` into a circuit for bitblasting.
-/
def ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
match n with
| 0 => x >>> shiftAmt
| n + 1 => (ushiftRightRec x y n) >>> shiftAmt
@[simp]
theorem ushiftRightRec_zero (x : BitVec w₁) (y : BitVec w₂) :
ushiftRightRec x y 0 = x >>> (y &&& twoPow w₂ 0) := by
simp [ushiftRightRec]
@[simp]
theorem ushiftRightRec_succ (x : BitVec w₁) (y : BitVec w₂) :
ushiftRightRec x y (n + 1) = (ushiftRightRec x y n) >>> (y &&& twoPow w₂ (n + 1)) := by
simp [ushiftRightRec]
/--
If `y &&& z = 0`, `x >>> (y ||| z) = x >>> y >>> z`.
This follows as `y &&& z = 0` implies `y ||| z = y + z`,
and thus `x >>> (y ||| z) = x >>> (y + z) = x >>> y >>> z`.
-/
theorem ushiftRight'_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
(h : y &&& z = 0#w₂) :
x >>> (y ||| z) = x >>> y >>> z := by
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.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_zeroExtend_ofBool_getLsb, truncate_one]
case succ n ih =>
simp only [ushiftRightRec_succ, and_twoPow]
rw [ih]
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
· simp [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false, h]
/--
Show that `x >>> y` can be written in terms of `ushiftRightRec`.
This can be unfolded in terms of `ushiftRightRec_zero`, `ushiftRightRec_succ` for bitblasting.
-/
theorem shiftRight_eq_ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
x >>> y = ushiftRightRec x y (w₂ - 1) := by
rcases w₂ with rfl | w₂
· simp [of_length_zero]
· simp [ushiftRightRec_eq]
end BitVec

View File

@@ -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
/--

View File

@@ -12,8 +12,6 @@ import Init.Data.Nat.Lemmas
import Init.Data.Nat.Mod
import Init.Data.Int.Bitwise.Lemmas
set_option linter.missingDocs true
namespace BitVec
/--
@@ -23,7 +21,7 @@ theorem ofFin_eq_ofNat : @BitVec.ofFin w (Fin.mk x lt) = BitVec.ofNat w x := by
simp only [BitVec.ofNat, Fin.ofNat', lt, Nat.mod_eq_of_lt]
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toNat_eq {n} : {x y : BitVec n}, x.toNat = y.toNat x = y
theorem eq_of_toNat_eq {n} : {i j : BitVec n}, i.toNat = j.toNat i = j
| _, _, _, _, rfl => rfl
@[simp] theorem val_toFin (x : BitVec w) : x.toFin.val = x.toNat := rfl
@@ -112,8 +110,8 @@ theorem eq_of_getMsb_eq {x y : BitVec w}
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
@[simp] theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false := by simp
theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false := by simp
@[simp] theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false := by simp [of_length_zero]
@[simp] theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false := by simp [of_length_zero]
@[simp] theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero]
theorem eq_of_toFin_eq : {x y : BitVec w}, x.toFin = y.toFin x = y
@@ -152,6 +150,9 @@ theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
getLsb (BitVec.ofNat n x) i = (i < n && x.testBit i) := by
simp [getLsb, BitVec.ofNat, Fin.val_ofNat']
@[simp, deprecated toNat_ofNat (since := "2024-02-22")]
theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
@[simp] theorem getLsb_zero : (0#w).getLsb i = false := by simp [getLsb]
@[simp] theorem getMsb_zero : (0#w).getMsb i = false := by simp [getMsb]
@@ -159,26 +160,9 @@ theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
@[simp] theorem toNat_mod_cancel (x : BitVec n) : x.toNat % (2^n) = x.toNat :=
Nat.mod_eq_of_lt x.isLt
@[simp] theorem sub_toNat_mod_cancel {x : BitVec w} (h : ¬ x = 0#w) :
(2 ^ w - x.toNat) % 2 ^ w = 2 ^ w - x.toNat := by
simp only [toNat_eq, toNat_ofNat, Nat.zero_mod] at h
rw [Nat.mod_eq_of_lt (by omega)]
@[simp] theorem sub_sub_toNat_cancel {x : BitVec w} :
2 ^ w - (2 ^ w - x.toNat) = x.toNat := by
simp [Nat.sub_sub_eq_min, Nat.min_eq_right]
omega
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_of_le_right (by trivial : 0 < 2) le)
@[simp]
theorem getLsb_ofBool (b : Bool) (i : Nat) : (BitVec.ofBool b).getLsb i = ((i = 0) && b) := by
rcases b with rfl | rfl
· simp [ofBool]
· simp only [ofBool, ofNat_eq_ofNat, cond_true, getLsb_ofNat, Bool.and_true]
by_cases hi : i = 0 <;> simp [hi] <;> omega
/-! ### msb -/
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsb]
@@ -235,12 +219,12 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
/-! ### toInt/ofInt -/
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem toInt_eq_toNat_cond (x : BitVec n) :
x.toInt =
if 2*x.toNat < 2^n then
(x.toNat : Int)
theorem toInt_eq_toNat_cond (i : BitVec n) :
i.toInt =
if 2*i.toNat < 2^n then
(i.toNat : Int)
else
(x.toNat : Int) - (2^n : Nat) :=
(i.toNat : Int) - (2^n : Nat) :=
rfl
theorem msb_eq_false_iff_two_mul_lt (x : BitVec w) : x.msb = false 2 * x.toNat < 2^w := by
@@ -267,13 +251,13 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
omega
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt x = y := by
theorem eq_of_toInt_eq {i j : BitVec n} : i.toInt = j.toInt i = j := by
intro eq
simp [toInt_eq_toNat_cond] at eq
apply eq_of_toNat_eq
revert eq
have _xlt := x.isLt
have _ylt := y.isLt
have _ilt := i.isLt
have _jlt := j.isLt
split <;> split <;> omega
theorem toInt_inj (x y : BitVec n) : x.toInt = y.toInt x = y :=
@@ -300,25 +284,12 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
@[simp] theorem ofInt_natCast (w n : Nat) :
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
@[simp] theorem ofInt_ofNat (w n : Nat) :
BitVec.ofInt w (no_index (OfNat.ofNat n)) = BitVec.ofNat w (OfNat.ofNat n) := rfl
theorem toInt_neg_iff {w : Nat} {x : BitVec w} :
BitVec.toInt x < 0 2 ^ w 2 * x.toNat := by
simp [toInt_eq_toNat_cond]; omega
theorem toInt_pos_iff {w : Nat} {x : BitVec w} :
0 BitVec.toInt x 2 * x.toNat < 2 ^ w := by
simp [toInt_eq_toNat_cond]; omega
/-! ### zeroExtend and truncate -/
theorem truncate_eq_zeroExtend {v : Nat} {x : BitVec w} :
truncate v x = zeroExtend v x := rfl
@[simp, bv_toNat] theorem toNat_zeroExtend' {m n : Nat} (p : m n) (x : BitVec m) :
(zeroExtend' p x).toNat = x.toNat := by
simp [zeroExtend']
unfold zeroExtend'
simp [p, x.isLt, Nat.mod_eq_of_lt]
@[bv_toNat] theorem toNat_zeroExtend (i : Nat) (x : BitVec n) :
BitVec.toNat (zeroExtend i x) = x.toNat % 2^i := by
@@ -348,7 +319,7 @@ theorem zeroExtend'_eq {x : BitVec w} (h : w ≤ v) : x.zeroExtend' h = x.zeroEx
apply eq_of_toNat_eq
simp [toNat_zeroExtend]
theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
@[simp] theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : BitVec.ofNat m x.toNat = truncate m x := by
apply eq_of_toNat_eq
@@ -402,7 +373,7 @@ theorem nat_eq_toNat (x : BitVec w) (y : Nat)
all_goals (first | apply getLsb_ge | apply Eq.symm; apply getLsb_ge)
<;> omega
theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
@[simp] theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
getLsb (truncate m x) i = (decide (i < m) && getLsb x i) :=
getLsb_zeroExtend m x i
@@ -421,10 +392,6 @@ theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k := b
(x.truncate l).truncate k = x.truncate k :=
zeroExtend_zeroExtend_of_le x h
/-- Truncating by the bitwidth has no effect. -/
-- This doesn't need to be a `@[simp]` lemma, as `zeroExtend_eq` applies.
theorem truncate_eq_self {x : BitVec w} : x.truncate w = x := zeroExtend_eq _
@[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k := by
apply eq_of_getLsb_eq
simp
@@ -437,28 +404,6 @@ theorem msb_zeroExtend (x : BitVec w) : (x.zeroExtend v).msb = (decide (0 < v) &
theorem msb_zeroExtend' (x : BitVec w) (h : w v) : (x.zeroExtend' h).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
rw [zeroExtend'_eq, msb_zeroExtend]
/-- zero extending a bitvector to width 1 equals the boolean of the lsb. -/
theorem zeroExtend_one_eq_ofBool_getLsb_zero (x : BitVec w) :
x.zeroExtend 1 = BitVec.ofBool (x.getLsb 0) := by
ext i
simp [getLsb_zeroExtend, Fin.fin_one_eq_zero i]
/-- Zero extending `1#v` to `1#w` equals `1#w` when `v > 0`. -/
theorem zeroExtend_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
(BitVec.ofNat v 1).zeroExtend w = BitVec.ofNat w 1 := by
ext i, hilt
simp only [getLsb_zeroExtend, hilt, decide_True, getLsb_ofNat, Bool.true_and,
Bool.and_iff_right_iff_imp, decide_eq_true_eq]
intros hi₁
have hv := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi₁
omega
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
theorem truncate_one {x : BitVec w} :
x.truncate 1 = ofBool (x.getLsb 0) := by
ext i
simp [show i = 0 by omega]
/-! ## extractLsb -/
@[simp]
@@ -478,18 +423,10 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
@[simp] theorem extractLsb_toNat (hi lo : Nat) (x : BitVec n) :
(extractLsb hi lo x).toNat = (x.toNat >>> lo) % 2^(hi-lo+1) := rfl
@[simp] theorem getLsb_extractLsb' (start len : Nat) (x : BitVec n) (i : Nat) :
(extractLsb' start len x).getLsb i = (i < len && x.getLsb (start+i)) := by
simp [getLsb, Nat.lt_succ]
@[simp] theorem getLsb_extract (hi lo : Nat) (x : BitVec n) (i : Nat) :
getLsb (extractLsb hi lo x) i = (i (hi-lo) && getLsb x (lo+i)) := by
simp [getLsb, Nat.lt_succ]
theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h : len > 0) :
x.extractLsb' start len = (x.extractLsb (len - 1 + start) start).cast (by omega) := by
apply eq_of_toNat_eq
simp [extractLsb, show len - 1 + 1 = len by omega]
unfold getLsb
simp [Nat.lt_succ]
/-! ### allOnes -/
@@ -530,13 +467,6 @@ theorem or_assoc (x y z : BitVec w) :
x ||| y ||| z = x ||| (y ||| z) := by
ext i
simp [Bool.or_assoc]
instance : Std.Associative (α := BitVec n) (· ||| ·) := BitVec.or_assoc
theorem or_comm (x y : BitVec w) :
x ||| y = y ||| x := by
ext i
simp [Bool.or_comm]
instance : Std.Commutative (fun (x y : BitVec w) => x ||| y) := BitVec.or_comm
/-! ### and -/
@@ -568,13 +498,6 @@ theorem and_assoc (x y z : BitVec w) :
x &&& y &&& z = x &&& (y &&& z) := by
ext i
simp [Bool.and_assoc]
instance : Std.Associative (α := BitVec n) (· &&& ·) := BitVec.and_assoc
theorem and_comm (x y : BitVec w) :
x &&& y = y &&& x := by
ext i
simp [Bool.and_comm]
instance : Std.Commutative (fun (x y : BitVec w) => x &&& y) := BitVec.and_comm
/-! ### xor -/
@@ -591,15 +514,6 @@ instance : Std.Commutative (fun (x y : BitVec w) => x &&& y) := ⟨BitVec.and_co
rw [ testBit_toNat, getLsb, getLsb]
simp
@[simp] theorem getMsb_xor {x y : BitVec w} :
(x ^^^ y).getMsb i = (xor (x.getMsb i) (y.getMsb i)) := by
simp only [getMsb]
by_cases h : i < w <;> simp [h]
@[simp] theorem msb_xor {x y : BitVec w} :
(x ^^^ y).msb = (xor x.msb y.msb) := by
simp [BitVec.msb]
@[simp] theorem truncate_xor {x y : BitVec w} :
(x ^^^ y).truncate k = x.truncate k ^^^ y.truncate k := by
ext
@@ -609,13 +523,6 @@ theorem xor_assoc (x y z : BitVec w) :
x ^^^ y ^^^ z = x ^^^ (y ^^^ z) := by
ext i
simp [Bool.xor_assoc]
instance : Std.Associative (fun (x y : BitVec w) => x ^^^ y) := BitVec.xor_assoc
theorem xor_comm (x y : BitVec w) :
x ^^^ y = y ^^^ x := by
ext i
simp [Bool.xor_comm]
instance : Std.Commutative (fun (x y : BitVec w) => x ^^^ y) := BitVec.xor_comm
/-! ### not -/
@@ -669,7 +576,7 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
ext
simp_all [lt_of_getLsb]
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x ^^^ cast h y = cast h (x ^^^ y) := by
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x &&& cast h y = cast h (x &&& y) := by
ext
simp_all [lt_of_getLsb]
@@ -682,15 +589,6 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
BitVec.toFin (x <<< n) = Fin.ofNat' (x.toNat <<< n) (Nat.two_pow_pos w) := rfl
@[simp]
theorem shiftLeft_zero_eq (x : BitVec w) : x <<< 0 = x := by
apply eq_of_toNat_eq
simp
@[simp]
theorem zero_shiftLeft (n : Nat) : 0#w <<< n = 0#w := by
simp [bv_toNat]
@[simp] theorem getLsb_shiftLeft (x : BitVec m) (n) :
getLsb (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsb x (i - n)) := by
rw [ testBit_toNat, getLsb]
@@ -699,27 +597,6 @@ theorem zero_shiftLeft (n : Nat) : 0#w <<< n = 0#w := by
cases h₁ : decide (i < m) <;> cases h₂ : decide (n i) <;> cases h₃ : decide (i < n)
all_goals { simp_all <;> omega }
theorem shiftLeft_xor_distrib (x y : BitVec w) (n : Nat) :
(x ^^^ y) <<< n = (x <<< n) ^^^ (y <<< n) := by
ext i
simp only [getLsb_shiftLeft, Fin.is_lt, decide_True, Bool.true_and, getLsb_xor]
by_cases h : i < n
<;> simp [h]
theorem shiftLeft_and_distrib (x y : BitVec w) (n : Nat) :
(x &&& y) <<< n = (x <<< n) &&& (y <<< n) := by
ext i
simp only [getLsb_shiftLeft, Fin.is_lt, decide_True, Bool.true_and, getLsb_and]
by_cases h : i < n
<;> simp [h]
theorem shiftLeft_or_distrib (x y : BitVec w) (n : Nat) :
(x ||| y) <<< n = (x <<< n) ||| (y <<< n) := by
ext i
simp only [getLsb_shiftLeft, Fin.is_lt, decide_True, Bool.true_and, getLsb_or]
by_cases h : i < n
<;> simp [h]
@[simp] theorem getMsb_shiftLeft (x : BitVec w) (i) :
(x <<< i).getMsb k = x.getMsb (k + i) := by
simp only [getMsb, getLsb_shiftLeft]
@@ -777,21 +654,6 @@ theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
(x <<< n) <<< m = x <<< (n + m) := by
rw [shiftLeft_add]
/-! ### shiftLeft reductions from BitVec to Nat -/
@[simp]
theorem shiftLeft_eq' {x : BitVec w₁} {y : BitVec w₂} : x <<< y = x <<< y.toNat := by rfl
theorem shiftLeft_zero' {x : BitVec w₁} : x <<< 0#w₂ = x := by simp
theorem shiftLeft_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {z : BitVec w₃} :
x <<< y <<< z = x <<< (y.toNat + z.toNat) := by
simp [shiftLeft_add]
theorem getLsb_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} :
(x <<< y).getLsb i = (decide (i < w₁) && !decide (i < y.toNat) && x.getLsb (i - y.toNat)) := by
simp [shiftLeft_eq', getLsb_shiftLeft]
/-! ### ushiftRight -/
@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
@@ -801,31 +663,6 @@ theorem getLsb_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} :
getLsb (x >>> i) j = getLsb x (i+j) := by
unfold getLsb ; simp
theorem ushiftRight_xor_distrib (x y : BitVec w) (n : Nat) :
(x ^^^ y) >>> n = (x >>> n) ^^^ (y >>> n) := by
ext
simp
theorem ushiftRight_and_distrib (x y : BitVec w) (n : Nat) :
(x &&& y) >>> n = (x >>> n) &&& (y >>> n) := by
ext
simp
theorem ushiftRight_or_distrib (x y : BitVec w) (n : Nat) :
(x ||| y) >>> n = (x >>> n) ||| (y >>> n) := by
ext
simp
@[simp]
theorem ushiftRight_zero_eq (x : BitVec w) : x >>> 0 = x := by
simp [bv_toNat]
/-! ### ushiftRight reductions from BitVec to Nat -/
@[simp]
theorem ushiftRight_eq' (x : BitVec w₁) (y : BitVec w₂) :
x >>> y = x >>> y.toNat := by rfl
/-! ### sshiftRight -/
theorem sshiftRight_eq {x : BitVec n} {i : Nat} :
@@ -869,7 +706,7 @@ theorem sshiftRight_eq_of_msb_true {x : BitVec w} {s : Nat} (h : x.msb = true) :
· rw [Nat.shiftRight_eq_div_pow]
apply Nat.lt_of_le_of_lt (Nat.div_le_self _ _) (by omega)
@[simp] theorem getLsb_sshiftRight (x : BitVec w) (s i : Nat) :
theorem getLsb_sshiftRight (x : BitVec w) (s i : Nat) :
getLsb (x.sshiftRight s) i =
(!decide (w i) && if s + i < w then x.getLsb (s + i) else x.msb) := by
rcases hmsb : x.msb with rfl | rfl
@@ -890,90 +727,6 @@ theorem sshiftRight_eq_of_msb_true {x : BitVec w} {s : Nat} (h : x.msb = true) :
Nat.not_lt, decide_eq_true_eq]
omega
theorem sshiftRight_xor_distrib (x y : BitVec w) (n : Nat) :
(x ^^^ y).sshiftRight n = (x.sshiftRight n) ^^^ (y.sshiftRight n) := by
ext i
simp only [getLsb_sshiftRight, getLsb_xor, msb_xor]
split
<;> by_cases w i
<;> simp [*]
theorem sshiftRight_and_distrib (x y : BitVec w) (n : Nat) :
(x &&& y).sshiftRight n = (x.sshiftRight n) &&& (y.sshiftRight n) := by
ext i
simp only [getLsb_sshiftRight, getLsb_and, msb_and]
split
<;> by_cases w i
<;> simp [*]
theorem sshiftRight_or_distrib (x y : BitVec w) (n : Nat) :
(x ||| y).sshiftRight n = (x.sshiftRight n) ||| (y.sshiftRight n) := by
ext i
simp only [getLsb_sshiftRight, getLsb_or, msb_or]
split
<;> by_cases w i
<;> simp [*]
/-- The msb after arithmetic shifting right equals the original msb. -/
theorem sshiftRight_msb_eq_msb {n : Nat} {x : BitVec w} :
(x.sshiftRight n).msb = x.msb := by
rw [msb_eq_getLsb_last, getLsb_sshiftRight, msb_eq_getLsb_last]
by_cases hw₀ : w = 0
· simp [hw₀]
· simp only [show ¬(w w - 1) by omega, decide_False, Bool.not_false, Bool.true_and,
ite_eq_right_iff]
intros h
simp [show n = 0 by omega]
@[simp] theorem sshiftRight_zero {x : BitVec w} : x.sshiftRight 0 = x := by
ext i
simp
theorem sshiftRight_add {x : BitVec w} {m n : Nat} :
x.sshiftRight (m + n) = (x.sshiftRight m).sshiftRight n := by
ext i
simp only [getLsb_sshiftRight, Nat.add_assoc]
by_cases h₁ : w (i : Nat)
· simp [h₁]
· simp only [h₁, decide_False, Bool.not_false, Bool.true_and]
by_cases h₂ : n + i < w
· simp [h₂]
· simp only [h₂, reduceIte]
by_cases h₃ : m + (n + i) < w
· simp [h₃]
omega
· simp [h₃, sshiftRight_msb_eq_msb]
/-! ### sshiftRight reductions from BitVec to Nat -/
@[simp]
theorem sshiftRight_eq' (x : BitVec w) : x.sshiftRight' y = x.sshiftRight y.toNat := rfl
/-! ### udiv -/
theorem udiv_eq {x y : BitVec n} : x.udiv y = BitVec.ofNat n (x.toNat / y.toNat) := by
have h : x.toNat / y.toNat < 2 ^ n := Nat.lt_of_le_of_lt (Nat.div_le_self ..) (by omega)
simp [udiv, bv_toNat, h, Nat.mod_eq_of_lt]
@[simp, bv_toNat]
theorem toNat_udiv {x y : BitVec n} : (x.udiv y).toNat = x.toNat / y.toNat := by
simp only [udiv_eq]
by_cases h : y = 0
· simp [h]
· rw [toNat_ofNat, Nat.mod_eq_of_lt]
exact Nat.lt_of_le_of_lt (Nat.div_le_self ..) (by omega)
/-! ### umod -/
theorem umod_eq {x y : BitVec n} :
x.umod y = BitVec.ofNat n (x.toNat % y.toNat) := by
have h : x.toNat % y.toNat < 2 ^ n := Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt
simp [umod, bv_toNat, Nat.mod_eq_of_lt h]
@[simp, bv_toNat]
theorem toNat_umod {x y : BitVec n} :
(x.umod y).toNat = x.toNat % y.toNat := rfl
/-! ### signExtend -/
/-- Equation theorem for `Int.sub` when both arguments are `Int.ofNat` -/
@@ -991,7 +744,7 @@ theorem signExtend_eq_not_zeroExtend_not_of_msb_false {x : BitVec w} {v : Nat} (
ext i
by_cases hv : i < v
· simp only [signExtend, getLsb, getLsb_zeroExtend, hv, decide_True, Bool.true_and, toNat_ofInt,
BitVec.toInt_eq_msb_cond, hmsb, reduceIte, reduceCtorEq]
BitVec.toInt_eq_msb_cond, hmsb, reduceIte]
rw [Int.ofNat_mod_ofNat, Int.toNat_ofNat, Nat.testBit_mod_two_pow]
simp [BitVec.testBit_toNat]
· simp only [getLsb_zeroExtend, hv, decide_False, Bool.false_and]
@@ -1027,18 +780,6 @@ theorem signExtend_eq_not_zeroExtend_not_of_msb_true {x : BitVec w} {v : Nat} (h
· rw [signExtend_eq_not_zeroExtend_not_of_msb_true hmsb]
by_cases (i < v) <;> by_cases (i < w) <;> simp_all <;> omega
/-- Sign extending to a width smaller than the starting width is a truncation. -/
theorem signExtend_eq_truncate_of_lt (x : BitVec w) {v : Nat} (hv : v w):
x.signExtend v = x.truncate v := by
ext i
simp only [getLsb_signExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_zeroExtend,
ite_eq_left_iff, Nat.not_lt]
omega
/-- Sign extending to the same bitwidth is a no op. -/
theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_truncate_of_lt _ (Nat.le_refl _), truncate_eq]
/-! ### append -/
theorem append_def (x : BitVec v) (y : BitVec w) :
@@ -1048,15 +789,15 @@ theorem append_def (x : BitVec v) (y : BitVec w) :
(x ++ y).toNat = x.toNat <<< n ||| y.toNat :=
rfl
@[simp] theorem getLsb_append {x : BitVec n} {y : BitVec m} :
getLsb (x ++ y) i = bif i < m then getLsb y i else getLsb x (i - m) := by
@[simp] theorem getLsb_append {v : BitVec n} {w : BitVec m} :
getLsb (v ++ w) i = bif i < m then getLsb w i else getLsb v (i - m) := by
simp only [append_def, getLsb_or, getLsb_shiftLeftZeroExtend, getLsb_zeroExtend']
by_cases h : i < m
· simp [h]
· simp [h]; simp_all
@[simp] theorem getMsb_append {x : BitVec n} {y : BitVec m} :
getMsb (x ++ y) i = bif n i then getMsb y (i - n) else getMsb x i := by
@[simp] theorem getMsb_append {v : BitVec n} {w : BitVec m} :
getMsb (v ++ w) i = bif n i then getMsb w (i - n) else getMsb v i := by
simp [append_def]
by_cases h : n i
· simp [h]
@@ -1377,19 +1118,6 @@ theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1 := by
have hx : x.toNat < 2^w := x.isLt
rw [Nat.sub_sub, Nat.add_comm 1 x.toNat, Nat.sub_sub, Nat.sub_add_cancel (by omega)]
@[simp]
theorem neg_neg {x : BitVec w} : - - x = x := by
by_cases h : x = 0#w
· simp [h]
· simp [bv_toNat, h]
theorem neg_ne_iff_ne_neg {x y : BitVec w} : -x y x -y := by
constructor
all_goals
intro h h'
subst h'
simp at h
/-! ### mul -/
theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) := by rfl
@@ -1416,18 +1144,6 @@ instance : Std.Associative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_asso
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
right_id := BitVec.mul_one
@[simp]
theorem BitVec.mul_zero {x : BitVec w} : x * 0#w = 0#w := by
apply eq_of_toNat_eq
simp [toNat_mul]
theorem BitVec.mul_add {x y z : BitVec w} :
x * (y + z) = x * y + x * z := by
apply eq_of_toNat_eq
simp only [toNat_mul, toNat_add, Nat.add_mod_mod, Nat.mod_add_mod]
rw [Nat.mul_mod, Nat.mod_mod (y.toNat + z.toNat),
Nat.mul_mod, Nat.mul_add]
@[simp, bv_toNat] theorem toInt_mul (x y : BitVec w) :
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
simp [toInt_eq_toNat_bmod]
@@ -1466,6 +1182,20 @@ protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x
simp
exact Nat.lt_of_le_of_ne
/-! ### intMax -/
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) : BitVec w := BitVec.ofNat w (2^w - 1)
theorem getLsb_intMax_eq (w : Nat) : (intMax w).getLsb i = decide (i < w) := by
simp [intMax, getLsb]
theorem toNat_intMax_eq : (intMax w).toNat = 2^w - 1 := by
have h : 2^w - 1 < 2^w := by
have pos : 2^w > 0 := Nat.pow_pos (by decide)
omega
simp [intMax, Nat.shiftLeft_eq, Nat.one_mul, natCast_eq_ofNat, toNat_ofNat, Nat.mod_eq_of_lt h]
/-! ### ofBoolList -/
@[simp] theorem getMsb_ofBoolListBE : (ofBoolListBE bs).getMsb i = bs.getD i false := by
@@ -1658,7 +1388,7 @@ theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
@[simp]
theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j)) := by
rcases w with rfl | w
· simp
· simp; omega
· simp only [twoPow, getLsb_shiftLeft, getLsb_ofNat]
by_cases hj : j < i
· simp only [hj, decide_True, Bool.not_true, Bool.and_false, Bool.false_and, Bool.false_eq,
@@ -1673,18 +1403,12 @@ theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j))
simp at hi
simp_all
@[simp]
theorem and_twoPow (x : BitVec w) (i : Nat) :
theorem and_twoPow_eq (x : BitVec w) (i : Nat) :
x &&& (twoPow w i) = if x.getLsb i then twoPow w i else 0#w := by
ext j
simp only [getLsb_and, getLsb_twoPow]
by_cases hj : i = j <;> by_cases hx : x.getLsb i <;> simp_all
@[simp]
theorem twoPow_and (x : BitVec w) (i : Nat) :
(twoPow w i) &&& x = if x.getLsb i then twoPow w i else 0#w := by
rw [BitVec.and_comm, and_twoPow]
@[simp]
theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
x * (twoPow w i) = x <<< i := by
@@ -1698,145 +1422,4 @@ theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
apply Nat.pow_dvd_pow 2 (by omega)
simp [Nat.mul_mod, hpow]
theorem twoPow_zero {w : Nat} : twoPow w 0 = 1#w := by
apply eq_of_toNat_eq
simp
@[simp]
theorem getLsb_one {w i : Nat} : (1#w).getLsb i = (decide (0 < w) && decide (0 = i)) := by
rw [ twoPow_zero, getLsb_twoPow]
/- ### zeroExtend, truncate, and bitwise operations -/
/--
When the `(i+1)`th bit of `x` is false,
keeping the lower `(i + 1)` bits of `x` equals keeping the lower `i` bits.
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false
{x : BitVec w} {i : Nat} (hx : x.getLsb i = false) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) := by
ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp [hx]
· by_cases hik' : k < i + 1 <;> simp [hik'] <;> omega
/--
When the `(i+1)`th bit of `x` is true,
keeping the lower `(i + 1)` bits of `x` equalsk eeping the lower `i` bits
and then performing bitwise-or with `twoPow i = (1 << i)`,
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true
{x : BitVec w} {i : Nat} (hx : x.getLsb i = true) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) ||| (twoPow w i) := by
ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp [hx]
· by_cases hik' : k < i + 1 <;> simp [hik, hik'] <;> omega
/-- Bitwise and of `(x : BitVec w)` with `1#w` equals zero extending `x.lsb` to `w`. -/
theorem and_one_eq_zeroExtend_ofBool_getLsb {x : BitVec w} :
(x &&& 1#w) = zeroExtend w (ofBool (x.getLsb 0)) := by
ext i
simp only [getLsb_and, getLsb_one, getLsb_zeroExtend, Fin.is_lt, decide_True, getLsb_ofBool,
Bool.true_and]
by_cases h : (0 = (i : Nat)) <;> simp [h] <;> omega
@[simp]
theorem replicate_zero_eq {x : BitVec w} : x.replicate 0 = 0#0 := by
simp [replicate]
@[simp]
theorem replicate_succ_eq {x : BitVec w} :
x.replicate (n + 1) =
(x ++ replicate n x).cast (by rw [Nat.mul_succ]; omega) := by
simp [replicate]
/--
If a number `w * n ≤ i < w * (n + 1)`, then `i - w * n` equals `i % w`.
This is true by subtracting `w * n` from the inequality, giving
`0 ≤ i - w * n < w`, which uniquely identifies `i % w`.
-/
private theorem Nat.sub_mul_eq_mod_of_lt_of_le (hlo : w * n i) (hhi : i < w * (n + 1)) :
i - w * n = i % w := by
rw [Nat.mod_def]
congr
symm
apply Nat.div_eq_of_lt_le
(by rw [Nat.mul_comm]; omega)
(by rw [Nat.mul_comm]; omega)
@[simp]
theorem getLsb_replicate {n w : Nat} (x : BitVec w) :
(x.replicate n).getLsb i =
(decide (i < w * n) && x.getLsb (i % w)) := by
induction n generalizing x
case zero => simp
case succ n ih =>
simp only [replicate_succ_eq, getLsb_cast, getLsb_append]
by_cases hi : i < w * (n + 1)
· simp only [hi, decide_True, Bool.true_and]
by_cases hi' : i < w * n
· simp [hi', ih]
· simp only [hi', decide_False, cond_false]
rw [Nat.sub_mul_eq_mod_of_lt_of_le] <;> omega
· rw [Nat.mul_succ] at hi
simp only [show ¬i < w * n by omega, decide_False, cond_false, hi, Bool.false_and]
apply BitVec.getLsb_ge (x := x) (i := i - w * n) (ge := by omega)
/-! ### intMin -/
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
abbrev intMin (w : Nat) := twoPow w (w - 1)
theorem getLsb_intMin (w : Nat) : (intMin w).getLsb i = decide (i + 1 = w) := by
simp only [getLsb_twoPow, boolToPropSimps]
omega
@[simp, bv_toNat]
theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
simp
@[simp]
theorem neg_intMin {w : Nat} : -intMin w = intMin w := by
by_cases h : 0 < w
· simp [bv_toNat, h]
· simp only [Nat.not_lt, Nat.le_zero_eq] at h
simp [bv_toNat, h]
/-! ### intMax -/
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
abbrev intMax (w : Nat) := (twoPow w (w - 1)) - 1
@[simp, bv_toNat]
theorem toNat_intMax : (intMax w).toNat = 2 ^ (w - 1) - 1 := by
simp only [intMax]
by_cases h : w = 0
· simp [h]
· have h' : 0 < w := by omega
rw [toNat_sub, toNat_twoPow, Nat.sub_add_comm (by simpa [h'] using Nat.one_le_two_pow),
Nat.add_sub_assoc (by simpa [h'] using Nat.one_le_two_pow),
Nat.two_pow_pred_mod_two_pow h', ofNat_eq_ofNat, toNat_ofNat, Nat.one_mod_two_pow h',
Nat.add_mod_left, Nat.mod_eq_of_lt]
have := Nat.two_pow_pred_lt_two_pow h'
have := Nat.two_pow_pos w
omega
@[simp]
theorem getLsb_intMax (w : Nat) : (intMax w).getLsb i = decide (i + 1 < w) := by
rw [ testBit_toNat, toNat_intMax, Nat.testBit_two_pow_sub_one, decide_eq_decide]
omega
@[simp] theorem intMax_add_one {w : Nat} : intMax w + 1#w = intMin w := by
simp only [toNat_eq, toNat_intMax, toNat_add, toNat_intMin, toNat_ofNat, Nat.add_mod_mod]
by_cases h : w = 0
· simp [h]
· rw [Nat.sub_add_cancel (Nat.two_pow_pos (w - 1)), Nat.two_pow_pred_mod_two_pow (by omega)]
end BitVec

View File

@@ -52,14 +52,8 @@ theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} [Decidable (b = true)] : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} [Decidable (b = false)] : decide (b = false) = !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
@[simp] theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
/-! ### and -/
@@ -97,11 +91,6 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
@[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
/-! ### or -/
@[simp] theorem or_self_left : (a b : Bool), (a || (a || b)) = (a || b) := by decide
@@ -131,11 +120,6 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
@[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
theorem or_comm : (x y : Bool), (x || y) = (y || x) := by decide
instance : Std.Commutative (· || ·) := or_comm
@@ -150,7 +134,7 @@ 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 && xor y z) = xor (x && y) (x && 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 -/
@@ -179,7 +163,7 @@ Consider the term: `¬((b && c) = true)`:
-/
@[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
@[simp] theorem or_eq_true_iff : (x y : Bool), (x || y) = true x = true y = true := by decide
@[simp] theorem or_eq_false_iff : (x y : Bool), (x || y) = false x = false y = false := by decide
@@ -203,9 +187,11 @@ in false_eq and true_eq.
@[simp] theorem true_beq : b, (true == b) = b := by decide
@[simp] theorem false_beq : b, (false == b) = !b := by decide
@[simp] theorem beq_true : b, (b == true) = b := by decide
instance : Std.LawfulIdentity (· == ·) true where
left_id := true_beq
right_id := beq_true
@[simp] theorem beq_false : b, (b == false) = !b := by decide
@[simp] theorem true_bne : (b : Bool), (true != b) = !b := by decide
@[simp] theorem false_bne : (b : Bool), (false != b) = b := by decide
@@ -218,11 +204,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
@@ -254,10 +237,8 @@ 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 not_not_eq : {a b : Bool}, ¬(!a) = b a = b := by decide
@[simp] theorem coe_iff_coe : (a b : Bool), (a b) a = b := by decide
@@ -372,7 +353,7 @@ theorem and_or_inj_left_iff :
/-! ## toNat -/
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
def toNat (b : Bool) : Nat := cond b 1 0
def toNat (b:Bool) : Nat := cond b 1 0
@[simp] theorem toNat_false : false.toNat = 0 := rfl
@@ -381,6 +362,9 @@ def toNat (b : Bool) : Nat := cond b 1 0
theorem toNat_le (c : Bool) : c.toNat 1 := by
cases c <;> trivial
@[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 _)
@@ -445,37 +429,17 @@ theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
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 -/
@@ -529,24 +493,10 @@ 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
theorem cond_pos {b : Bool} {a a' : α} (h : b = true) : (bif b then a else a') = a := by
rw [h, cond_true]
theorem cond_neg {b : Bool} {a a' : α} (h : b = false) : (bif b then a else a') = a' := by
rw [h, cond_false]
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
@@ -562,21 +512,6 @@ 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)
@@ -588,19 +523,3 @@ export Bool (cond_eq_if)
@[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

View File

@@ -37,10 +37,6 @@ def push : ByteArray → UInt8 → ByteArray
def size : (@& ByteArray) Nat
| bs => bs.size
@[extern "lean_sarray_size", simp]
def usize (a : @& ByteArray) : USize :=
a.size.toUSize
@[extern "lean_byte_array_uget"]
def uget : (a : @& ByteArray) (i : USize) i.toNat < a.size UInt8
| bs, i, h => bs[i]
@@ -123,7 +119,7 @@ def toList (bs : ByteArray) : List UInt8 :=
TODO: avoid code duplication in the future after we improve the compiler.
-/
@[inline] unsafe def forInUnsafe {β : Type v} {m : Type v Type w} [Monad m] (as : ByteArray) (b : β) (f : UInt8 β m (ForInStep β)) : m β :=
let sz := as.usize
let sz := USize.ofNat as.size
let rec @[specialize] loop (i : USize) (b : β) : m β := do
if i < sz then
let a := as.uget i lcProof
@@ -191,137 +187,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 :=

View File

@@ -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.

View File

@@ -31,9 +31,11 @@ theorem utf8Size_eq (c : Char) : c.utf8Size = 1 c.utf8Size = 2 c.utf8Siz
rw [Char.ofNat, dif_pos]
rfl
@[ext] protected theorem ext : {a b : Char} a.val = b.val a = b
@[ext] theorem Char.ext : {a b : Char} a.val = b.val a = b
| _,_, _,_, rfl => rfl
theorem Char.ext_iff {x y : Char} : x = y x.val = y.val := congrArg _, Char.ext
end Char
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size

View File

@@ -149,9 +149,6 @@ instance : Inhabited (Fin (no_index (n+1))) where
@[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

View File

@@ -1,15 +0,0 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.Nat.Bitwise
import Init.Data.Fin.Basic
namespace Fin
@[simp] theorem and_val (a b : Fin n) : (a &&& b).val = a.val &&& b.val :=
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt Nat.and_le_left a.isLt)
end Fin

View File

@@ -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 -/
@@ -34,7 +37,9 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (a, h : Fin n) = a := rfl
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
@[ext] theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem ext_iff {a b : Fin n} : a = b a.1 = b.1 := val_inj.symm
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -42,18 +47,21 @@ theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩
fun h i hi => h i, hi, fun h i, hi => h i hi
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
(a, ha : Fin n) = b, hb a = b := Fin.ext_iff
(a, ha : Fin n) = b, hb a = b := ext_iff
theorem val_mk {m n : Nat} (h : m < n) : (m, h : Fin n).val = m := rfl
theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
a = k, hk (a : Nat) = k := Fin.ext_iff
a = k, hk (a : Nat) = k := ext_iff
theorem mk_val (i : Fin n) : (i, i.isLt : Fin n) = i := Fin.eta ..
@[simp] theorem val_ofNat' (a : Nat) (is_pos : n > 0) :
(Fin.ofNat' a is_pos).val = a % n := rfl
@[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
@@ -135,15 +143,9 @@ 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
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@[simp] theorem rev_le_rev {i j : Fin n} : rev i rev j j i := by
@@ -169,12 +171,12 @@ 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 _
theorem eq_last_of_not_lt {i : Fin (n + 1)} (h : ¬(i : Nat) < n) : i = last n :=
Fin.ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
theorem val_lt_last {i : Fin (n + 1)} : i last n (i : Nat) < n :=
Decidable.not_imp_comm.1 eq_last_of_not_lt
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := Fin.ext <| by simp
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := ext <| by simp
@[simp] theorem rev_zero (n : Nat) : rev 0 = last n := by
rw [ rev_rev (last _), rev_last]
@@ -242,11 +244,11 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt one_pos
@[simp] theorem succ_lt_succ_iff {a b : Fin n} : a.succ < b.succ a < b := Nat.succ_lt_succ_iff
@[simp] theorem succ_inj {a b : Fin n} : a.succ = b.succ a = b := by
refine fun h => Fin.ext ?_, congrArg _
refine fun h => ext ?_, congrArg _
apply Nat.le_antisymm <;> exact succ_le_succ_iff.1 (h Nat.le_refl _)
theorem succ_ne_zero {n} : k : Fin n, Fin.succ k 0
| k, _, heq => Nat.succ_ne_zero k <| congrArg Fin.val heq
| k, _, heq => Nat.succ_ne_zero k <| ext_iff.1 heq
@[simp] theorem succ_zero_eq_one : Fin.succ (0 : Fin (n + 1)) = 1 := rfl
@@ -265,7 +267,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
rw [ succ_zero_eq_one, succ_lt_succ_iff]; exact succ_pos a
@[simp] theorem add_one_lt_iff {n : Nat} {k : Fin (n + 2)} : k + 1 < k k = last _ := by
simp only [lt_def, val_add, val_last, Fin.ext_iff]
simp only [lt_def, val_add, val_last, ext_iff]
let k, hk := k
match Nat.eq_or_lt_of_le (Nat.le_of_lt_succ hk) with
| .inl h => cases h; simp [Nat.succ_pos]
@@ -283,7 +285,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
split <;> simp [*, (Nat.succ_ne_zero _).symm, Nat.ne_of_gt (Nat.lt_succ_self _)]
@[simp] theorem last_le_iff {n : Nat} {k : Fin (n + 1)} : last n k k = last n := by
rw [Fin.ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
rw [ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
@[simp] theorem lt_add_one_iff {n : Nat} {k : Fin (n + 1)} : k < k + 1 k < last n := by
rw [ Decidable.not_iff_not]; simp
@@ -304,10 +306,10 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n m) :
castLE h i, hn = i, Nat.lt_of_lt_of_le hn h := rfl
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [Fin.ext_iff]
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [ext_iff]
@[simp] theorem castLE_succ {m n : Nat} (h : m + 1 n + 1) (i : Fin m) :
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [Fin.ext_iff]
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [ext_iff]
@[simp] theorem castLE_castLE {k m n} (km : k m) (mn : m n) (i : Fin k) :
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
@@ -320,7 +322,7 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h i, hn = i, h hn := rfl
@@ -346,7 +348,7 @@ theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m :=
/-- For rewriting in the reverse direction, see `Fin.cast_castAdd_left`. -/
theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := Fin.ext rfl
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := ext rfl
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
@@ -395,7 +397,7 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
Fin.castSucc a < Fin.castSucc b a < b := .rfl
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [Fin.ext_iff]
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [ext_iff]
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
@@ -407,7 +409,7 @@ 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 [ext_iff]
theorem castSucc_ne_zero_iff (a : Fin (n + 1)) : castSucc a 0 a 0 :=
not_congr <| castSucc_eq_zero_iff a
@@ -419,7 +421,7 @@ theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
cases n
· exact a.elim0
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
· simp [ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
@@ -452,7 +454,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
cast h (addNat i m') = addNat i m :=
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@@ -472,7 +474,7 @@ theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
cast h (natAdd m' i) = natAdd m i :=
Fin.ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
@@ -482,27 +484,27 @@ theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
Fin.ext <| (Nat.add_assoc ..).symm
ext <| (Nat.add_assoc ..).symm
@[simp]
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
Fin.ext <| Nat.zero_add _
ext <| Nat.zero_add _
@[simp]
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := Fin.ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := ext <| Nat.add_comm ..
@[simp]
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := Fin.ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := ext <| Nat.add_comm ..
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSucc (natAdd n i) :=
rfl
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := ext <| by
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
@@ -532,7 +534,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i ≠ 0) (j : Fin
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
Fin.pred i + 1, Nat.add_lt_add_right h 1 (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
i, h := by
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
simp only [ext_iff, coe_pred, Nat.add_sub_cancel]
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
Fin.pred i + 1, h₁ h₂ = i, Nat.lt_of_succ_lt_succ h₁ := pred_mk_succ i _
@@ -552,14 +554,14 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
{a b : Fin (n + 1)} {ha : a 0} {hb : b 0}, a.pred ha = b.pred hb a = b
| 0, _, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
| i + 1, _, 0, _, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
| i + 1, hi, j + 1, hj, ha, hb => by simp [Fin.ext_iff, Nat.succ.injEq]
| i + 1, hi, j + 1, hj, ha, hb => by simp [ext_iff, Nat.succ.injEq]
@[simp] theorem pred_one {n : Nat} :
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
rw [ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
exact Nat.add_lt_add_right h 1
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@@ -571,10 +573,10 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (castSucc i.succ) (Fin.ne_of_gt (castSucc_pos i.succ_pos)) = castSucc i := rfl
@[simp] theorem addNat_subNat {i : Fin (n + m)} (h : m i) : addNat (subNat m i h) m = i :=
Fin.ext <| Nat.sub_add_cancel h
ext <| Nat.sub_add_cancel h
@[simp] theorem subNat_addNat (i : Fin n) (m : Nat) (h : m addNat i m := le_coe_addNat m i) :
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
subNat m (addNat i m) h = i := 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]; rfl
@@ -784,9 +786,6 @@ theorem coe_sub_iff_le {a b : Fin n} : (↑(a - b) : Nat) = a - b ↔ b ≤ a :=
rw [Nat.mod_eq_of_lt]
all_goals omega
theorem sub_val_of_le {a b : Fin n} : b a (a - b).val = a.val - b.val :=
coe_sub_iff_le.2
theorem coe_sub_iff_lt {a b : Fin n} : ((a - b) : Nat) = n + a - b a < b := by
rw [sub_def, lt_def]
dsimp only
@@ -808,10 +807,10 @@ theorem coe_mul {n : Nat} : ∀ a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
match n with
| 0 => exact Subsingleton.elim (α := Fin 1) ..
| n+1 => simp [Fin.ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
| n+1 => simp [ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
Fin.ext <| by rw [mul_def, mul_def, Nat.mul_comm]
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
instance : Std.Commutative (α := Fin n) (· * ·) := Fin.mul_comm
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
@@ -827,9 +826,9 @@ instance : Std.LawfulIdentity (α := Fin (n + 1)) (· * ·) 1 where
left_id := Fin.one_mul
right_id := Fin.mul_one
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [Fin.ext_iff, mul_def]
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [ext_iff, mul_def]
protected theorem zero_mul (k : Fin (n + 1)) : (0 : Fin (n + 1)) * k = 0 := by
simp [Fin.ext_iff, mul_def]
simp [ext_iff, mul_def]
end Fin

View File

@@ -101,13 +101,13 @@ Returns an undefined value if `x` is not finite.
instance : ToString Float where
toString := Float.toString
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
instance : Repr Float where
reprPrec n prec := if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
reprPrec n _ := Float.toString n
instance : ReprAtom Float :=
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
@[extern "sin"] opaque Float.sin : Float Float
@[extern "cos"] opaque Float.cos : Float Float
@[extern "tan"] opaque Float.tan : Float Float

View File

@@ -37,10 +37,6 @@ def push : FloatArray → Float → FloatArray
def size : (@& FloatArray) Nat
| ds => ds.size
@[extern "lean_sarray_size", simp]
def usize (a : @& FloatArray) : USize :=
a.size.toUSize
@[extern "lean_float_array_uget"]
def uget : (a : @& FloatArray) (i : USize) i.toNat < a.size Float
| ds, i, h => ds[i]
@@ -94,7 +90,7 @@ partial def toList (ds : FloatArray) : List Float :=
-/
-- TODO: avoid code duplication in the future after we improve the compiler.
@[inline] unsafe def forInUnsafe {β : Type v} {m : Type v Type w} [Monad m] (as : FloatArray) (b : β) (f : Float β m (ForInStep β)) : m β :=
let sz := as.usize
let sz := USize.ofNat as.size
let rec @[specialize] loop (i : USize) (b : β) : m β := do
if i < sz then
let a := as.uget i lcProof

View File

@@ -62,16 +62,3 @@ instance (P : Prop) : Hashable P where
/-- An opaque (low-level) hash operation used to implement hashing for pointers. -/
@[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
mixHash u 11
/-- `LawfulHashable α` says that the `BEq α` and `Hashable α` instances on `α` are compatible, i.e.,
that `a == b` implies `hash a = hash b`. This is automatic if the `BEq` instance is lawful.
-/
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
/-- If `a == b`, then `hash a = hash b`. -/
hash_eq (a b : α) : a == b hash a = hash b
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b hash a = hash b :=
LawfulHashable.hash_eq a b
instance (priority := low) [BEq α] [Hashable α] [LawfulBEq α] : LawfulHashable α where
hash_eq _ _ h := eq_of_beq h rfl

View File

@@ -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

View File

@@ -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

View File

@@ -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]
@@ -354,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
@@ -452,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
@@ -502,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]
@@ -603,14 +596,6 @@ theorem emod_eq_zero_of_dvd : ∀ {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
@@ -635,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)]
@@ -656,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
@@ -921,14 +891,6 @@ theorem mod_eq_zero_of_dvd : ∀ {a b : Int}, a b → mod b a = 0
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_mod_right (a b : Int) : (-(a * b)).mod a = 0 := by
rw [ dvd_iff_mod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_right a b
@[simp] theorem neg_mul_mod_left (a b : Int) : (-(a * b)).mod b = 0 := by
rw [ dvd_iff_mod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_left 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)
@@ -941,10 +903,6 @@ protected theorem eq_mul_of_div_eq_right {a b c : Int}
@[simp] theorem mod_self {a : Int} : a.mod a = 0 := by
have := mul_mod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem neg_mod_self (a : Int) : (-a).mod a = 0 := by
rw [ dvd_iff_mod_eq_zero, Int.dvd_neg]
exact Int.dvd_refl a
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.mod_def .. mod_lt_of_pos _ H
@@ -1133,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]

View File

@@ -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
@@ -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

View File

@@ -1,40 +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.toNat - b = (a - b).toNat := by
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

View File

@@ -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]
@@ -485,16 +470,8 @@ theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
@[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
@@ -1029,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]

View File

@@ -4,22 +4,11 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.List.Attach
import Init.Data.List.Basic
import Init.Data.List.BasicAux
import Init.Data.List.Control
import Init.Data.List.Count
import Init.Data.List.Erase
import Init.Data.List.Find
import Init.Data.List.Impl
import Init.Data.List.Lemmas
import Init.Data.List.MinMax
import Init.Data.List.Monadic
import Init.Data.List.Nat
import Init.Data.List.Notation
import Init.Data.List.Pairwise
import Init.Data.List.Sublist
import Init.Data.List.Attach
import Init.Data.List.Impl
import Init.Data.List.TakeDrop
import Init.Data.List.Zip
import Init.Data.List.Perm
import Init.Data.List.Sort
import Init.Data.List.Notation

View File

@@ -4,8 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.List.Count
import Init.Data.Subtype
import Init.Data.List.Lemmas
namespace List
@@ -45,228 +44,3 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
| nil, hL' => rfl
| cons _ L', hL' => congrArg _ <| go L' fun _ hx => hL' (.tail _ hx)
exact go L h'
@[simp] theorem attach_nil : ([] : List α).attach = [] := rfl
@[simp]
theorem pmap_eq_map (p : α Prop) (f : α β) (l : List α) (H) :
@pmap _ _ p (fun a _ => f a) l H = map f l := by
induction l
· rfl
· simp only [*, pmap, map]
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)]
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
induction l
· rfl
· simp only [*, pmap, map]
theorem pmap_map {p : β Prop} (g : b, p b γ) (f : α β) (l H) :
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun a h => H _ (mem_map_of_mem _ h) := by
induction l
· rfl
· simp only [*, pmap, map]
@[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
intros a _ m' _
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 l fun _ _ _ _ => rfl
theorem attach_map_coe (l : List α) (f : α β) :
(l.attach.map fun (i : {i // i l}) => f i) = l.map f := by
rw [attach, attachWith, map_pmap]; exact pmap_eq_map _ _ _ _
theorem attach_map_val (l : List α) (f : α β) : (l.attach.map fun i => f i.val) = l.map f :=
attach_map_coe _ _
@[simp]
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
(attach_map_coe _ _).trans (List.map_id _)
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 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
| a, h => by
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
rcases this with _, _, m, rfl
exact m
@[simp]
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]
@[simp]
theorem length_pmap {p : α Prop} {f : a, p a β} {l H} : length (pmap f l H) = length l := by
induction l
· rfl
· simp only [*, pmap, length]
@[simp]
theorem length_attach (L : List α) : L.attach.length = L.length :=
length_pmap
@[simp]
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 {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 (l : List α) : l.attach = [] l = [] :=
pmap_eq_nil
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
| nil => simp
| cons hd tl hl =>
rcases n with n
· simp only [Option.pmap]
split <;> simp_all
· simp only [hl, pmap, Option.pmap, getElem?_cons_succ]
split <;> rename_i h₁ _ <;> split <;> rename_i h₂ _
· simp_all
· simp at h₂
simp_all
· simp_all
· simp_all
theorem get?_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) (n : Nat) :
get? (pmap f l h) n = Option.pmap f (get? l n) fun x H => h x (get?_mem H) := by
simp only [get?_eq_getElem?]
simp [getElem?_pmap, h]
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 l n (@length_pmap _ _ p f l h hn))) := by
induction l generalizing n with
| nil =>
simp only [length, pmap] at hn
exact absurd hn (Nat.not_lt_of_le n.zero_le)
| cons hd tl hl =>
cases n
· simp
· simp [hl]
theorem get_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) {n : Nat}
(hn : n < (pmap f l h).length) :
get (pmap f l h) n, hn =
f (get l n, @length_pmap _ _ p f l h hn)
(h _ (get_mem l n (@length_pmap _ _ p f l h hn))) := by
simp only [get_eq_getElem]
simp [getElem_pmap]
@[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 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)) ++
l₂.pmap f fun a ha => h a (mem_append_right l₁ ha) := by
induction l₁ with
| nil => rfl
| cons _ _ ih =>
dsimp only [pmap, cons_append]
rw [ih]
theorem pmap_append' {p : α Prop} (f : a : α, p a β) (l₁ l₂ : List α)
(h₁ : a l₁, p a) (h₂ : a l₂, p a) :
((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 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 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 _ fun _ _ _ _ => rfl
@[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
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
intros
rfl
theorem getLast?_attach {xs : List α} :
xs.attach.getLast? = match h : xs.getLast? with | none => none | some a => some a, mem_of_getLast?_eq_some h := by
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map]
split <;> rename_i h
· simp only [getLast?_eq_none_iff] at h
subst h
simp
· obtain ys, rfl := getLast?_eq_some_iff.mp h
simp
@[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_iff_getLast_eq_some, getLast?_pmap, Option.map_eq_some', Subtype.exists]
refine xs.getLast (by simpa using h), by simp, ?_
simp only [getLast?_attach, and_true]
split <;> rename_i h'
· simp only [getLast?_eq_none_iff] at h'
subst h'
simp at h
· symm
simpa [getLast_eq_iff_getLast_eq_some]
end List

View File

@@ -22,37 +22,29 @@ along with `@[csimp]` lemmas,
In `Init.Data.List.Lemmas` we develop the full API for these functions.
Recall that `length`, `get`, `set`, `foldl`, and `concat` have already been defined in `Init.Prelude`.
Recall that `length`, `get`, `set`, `fold`, and `concat` have already been defined in `Init.Prelude`.
The operations are organized as follow:
* Equality: `beq`, `isEqv`.
* Lexicographic ordering: `lt`, `le`, and instances.
* Head and tail operators: `head`, `head?`, `headD?`, `tail`, `tail?`, `tailD`.
* Basic operations:
`map`, `filter`, `filterMap`, `foldr`, `append`, `join`, `pure`, `bind`, `replicate`, and
`reverse`.
* Additional functions defined in terms of these: `leftpad`, `rightPad`, and `reduceOption`.
`map`, `filter`, `filterMap`, `foldr`, `append`, `join`, `pure`, `bind`, `replicate`, and `reverse`.
* List membership: `isEmpty`, `elem`, `contains`, `mem` (and the `∈` notation),
and decidability for predicates quantifying over membership in a `List`.
* Sublists: `take`, `drop`, `takeWhile`, `dropWhile`, `partition`, `dropLast`,
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`,
`rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseP`, `eraseIdx`.
* Finding elements: `find?`, `findSome?`, `findIdx`, `indexOf`, `findIdx?`, `indexOf?`,
`countP`, `count`, and `lookup`.
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
* Logic: `any`, `all`, `or`, and `and`.
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
* Minima and maxima: `minimum?` and `maximum?`.
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`,
`removeAll`
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`, `removeAll`
(currently these functions are mostly only used in meta code,
and do not have API suitable for verification).
Further operations are defined in `Init.Data.List.BasicAux`
(because they use `Array` in their implementations), namely:
Further operations are defined in `Init.Data.List.BasicAux` (because they use `Array` in their implementations), namely:
* Variant getters: `get!`, `get?`, `getD`, `getLast`, `getLast!`, `getLast?`, and `getLastD`.
* Head and tail: `head!`, `tail!`.
* Head and tail: `head`, `head!`, `head?`, `headD`, `tail!`, `tail?`, and `tailD`.
* Other operations on sublists: `partitionMap`, `rotateLeft`, and `rotateRight`.
-/
@@ -96,7 +88,7 @@ namespace List
/-! ### concat -/
theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
@[simp] 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 +270,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 -/
@@ -324,16 +315,6 @@ def headD : (as : List α) → (fallback : α) → α
@[simp 1100] theorem headD_nil : @headD α [] d = d := rfl
@[simp 1100] theorem headD_cons : @headD α (a::l) d = a := rfl
/-! ### tail -/
/-- Get the tail of a nonempty list, or return `[]` for `[]`. -/
def tail : List α List α
| [] => []
| _::as => as
@[simp] theorem tail_nil : @tail α [] = [] := rfl
@[simp] theorem tail_cons : @tail α (a::as) = as := rfl
/-! ### tail? -/
/--
@@ -596,28 +577,6 @@ theorem replicate_succ (a : α) (n) : replicate (n+1) a = a :: replicate n a :=
| zero => simp
| succ n ih => simp only [ih, replicate_succ, length_cons, Nat.succ_eq_add_one]
/-! ## Additional functions -/
/-! ### leftpad and rightpad -/
/--
Pads `l : List α` on the left with repeated occurrences of `a : α` until it is of length `n`.
If `l` is initially larger than `n`, just return `l`.
-/
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
/--
Pads `l : List α` on the right with repeated occurrences of `a : α` until it is of length `n`.
If `l` is initially larger than `n`, just return `l`.
-/
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
/-! ### reduceOption -/
/-- Drop `none`s from a list, and replace each remaining `some a` with `a`. -/
@[inline] def reduceOption {α} : List (Option α) List α :=
List.filterMap id
/-! ## List membership
* `L.contains a : Bool` determines, using a `[BEq α]` instance, whether `L` contains an element `· == a`.
@@ -689,7 +648,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
@@ -760,7 +719,7 @@ def take : Nat → List α → List α
@[simp] theorem take_nil : ([] : List α).take i = [] := by cases i <;> rfl
@[simp] theorem take_zero (l : List α) : l.take 0 = [] := rfl
@[simp] theorem take_succ_cons : (a::as).take (i+1) = a :: as.take i := rfl
@[simp] theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
/-! ### drop -/
@@ -858,8 +817,6 @@ def dropLast {α} : List α → List α
@[simp] theorem dropLast_cons₂ :
(x::y::zs).dropLast = x :: (y::zs).dropLast := rfl
-- Later this can be proved by `simp` via `[List.length_dropLast, List.length_cons, Nat.add_sub_cancel]`,
-- but we need this while bootstrapping `Array`.
@[simp] theorem length_dropLast_cons (a : α) (as : List α) : (a :: as).dropLast.length = as.length := by
match as with
| [] => rfl
@@ -867,49 +824,7 @@ def dropLast {α} : List α → List α
have ih := length_dropLast_cons b bs
simp [dropLast, ih]
/-! ### Subset -/
/--
`l₁ ⊆ l₂` means that every element of `l₁` is also an element of `l₂`, ignoring multiplicity.
-/
protected def Subset (l₁ l₂ : List α) := a : α, a l₁ a l₂
instance : HasSubset (List α) := List.Subset
instance [DecidableEq α] : DecidableRel (Subset : List α List α Prop) :=
fun _ _ => decidableBAll _ _
/-! ### Sublist and isSublist -/
/-- `l₁ <+ l₂`, or `Sublist l₁ l₂`, says that `l₁` is a (non-contiguous) subsequence of `l₂`. -/
inductive Sublist {α} : List α List α Prop
/-- the base case: `[]` is a sublist of `[]` -/
| slnil : Sublist [] []
/-- If `l₁` is a subsequence of `l₂`, then it is also a subsequence of `a :: l₂`. -/
| cons a : Sublist l₁ l₂ Sublist l₁ (a :: l₂)
/-- If `l₁` is a subsequence of `l₂`, then `a :: l₁` is a subsequence of `a :: l₂`. -/
| cons₂ a : Sublist l₁ l₂ Sublist (a :: l₁) (a :: l₂)
@[inherit_doc] scoped infixl:50 " <+ " => Sublist
/-- True if the first list is a potentially non-contiguous sub-sequence of the second list. -/
def isSublist [BEq α] : List α List α Bool
| [], _ => true
| _, [] => false
| l₁@(hd₁::tl₁), hd₂::tl₂ =>
if hd₁ == hd₂
then tl₁.isSublist tl₂
else l₁.isSublist tl₂
/-! ### IsPrefix / isPrefixOf / isPrefixOf? -/
/--
`IsPrefix l₁ l₂`, or `l₁ <+: l₂`, means that `l₁` is a prefix of `l₂`,
that is, `l₂` has the form `l₁ ++ t` for some `t`.
-/
def IsPrefix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => l₁ ++ t = l₂
@[inherit_doc] infixl:50 " <+: " => IsPrefix
/-! ### isPrefixOf -/
/-- `isPrefixOf l₁ l₂` returns `true` Iff `l₁` is a prefix of `l₂`.
That is, there exists a `t` such that `l₂ == l₁ ++ t`. -/
@@ -924,6 +839,8 @@ def isPrefixOf [BEq α] : List α → List α → Bool
theorem isPrefixOf_cons₂ [BEq α] {a : α} :
isPrefixOf (a::as) (b::bs) = (a == b && isPrefixOf as bs) := rfl
/-! ### isPrefixOf? -/
/-- `isPrefixOf? l₁ l₂` returns `some t` when `l₂ == l₁ ++ t`. -/
def isPrefixOf? [BEq α] : List α List α Option (List α)
| [], l₂ => some l₂
@@ -931,7 +848,7 @@ def isPrefixOf? [BEq α] : List α → List α → Option (List α)
| (x₁ :: l₁), (x₂ :: l₂) =>
if x₁ == x₂ then isPrefixOf? l₁ l₂ else none
/-! ### IsSuffix / isSuffixOf / isSuffixOf? -/
/-! ### isSuffixOf -/
/-- `isSuffixOf l₁ l₂` returns `true` Iff `l₁` is a suffix of `l₂`.
That is, there exists a `t` such that `l₂ == t ++ l₁`. -/
@@ -941,48 +858,12 @@ def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
@[simp] theorem isSuffixOf_nil_left [BEq α] : isSuffixOf ([] : List α) l = true := by
simp [isSuffixOf]
/-! ### isSuffixOf? -/
/-- `isSuffixOf? l₁ l₂` returns `some t` when `l₂ == t ++ l₁`.-/
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
/--
`IsSuffix l₁ l₂`, or `l₁ <:+ l₂`, means that `l₁` is a suffix of `l₂`,
that is, `l₂` has the form `t ++ l₁` for some `t`.
-/
def IsSuffix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => t ++ l₁ = l₂
@[inherit_doc] infixl:50 " <:+ " => IsSuffix
/-! ### IsInfix -/
/--
`IsInfix l₁ l₂`, or `l₁ <:+: l₂`, means that `l₁` is a contiguous
substring of `l₂`, that is, `l₂` has the form `s ++ l₁ ++ t` for some `s, t`.
-/
def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists fun t => s ++ l₁ ++ t = l₂
@[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 -/
/--
@@ -1025,55 +906,6 @@ def rotateRight (xs : List α) (n : Nat := 1) : List α :=
@[simp] theorem rotateRight_nil : ([] : List α).rotateRight n = [] := rfl
/-! ## Pairwise, Nodup -/
section Pairwise
variable (R : α α Prop)
/--
`Pairwise R l` means that all the elements with earlier indexes are
`R`-related to all the elements with later indexes.
```
Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3
```
For example if `R = (·≠·)` then it asserts `l` has no duplicates,
and if `R = (·<·)` then it asserts that `l` is (strictly) sorted.
-/
inductive Pairwise : List α Prop
/-- All elements of the empty list are vacuously pairwise related. -/
| nil : Pairwise []
/-- `a :: l` is `Pairwise R` if `a` `R`-relates to every element of `l`,
and `l` is `Pairwise R`. -/
| cons : {a : α} {l : List α}, ( a', a' l R a a') Pairwise l Pairwise (a :: l)
attribute [simp] Pairwise.nil
variable {R}
@[simp] theorem pairwise_cons : Pairwise R (a::l) ( a', a' l R a a') Pairwise R l :=
fun | .cons h₁ h₂ => h₁, h₂, fun h₁, h₂ => h₂.cons h₁
instance instDecidablePairwise [DecidableRel R] :
(l : List α) Decidable (Pairwise R l)
| [] => isTrue .nil
| hd :: tl =>
match instDecidablePairwise tl with
| isTrue ht =>
match decidableBAll (R hd) tl with
| isFalse hf => isFalse fun hf' => hf (pairwise_cons.1 hf').1
| isTrue ht' => isTrue <| pairwise_cons.mpr (And.intro ht' ht)
| isFalse hf => isFalse fun | .cons _ ih => hf ih
end Pairwise
/-- `Nodup l` means that `l` has no duplicates, that is, any element appears at most
once in the List. It is defined as `Pairwise (≠)`. -/
def Nodup : List α Prop := Pairwise (· ·)
instance nodupDecidable [DecidableEq α] : l : List α, Decidable (Nodup l) :=
instDecidablePairwise
/-! ## Manipulating elements -/
/-! ### replace -/
@@ -1119,11 +951,6 @@ theorem erase_cons [BEq α] (a b : α) (l : List α) :
(b :: l).erase a = if b == a then l else b :: l.erase a := by
simp only [List.erase]; split <;> simp_all
/-- `eraseP p l` removes the first element of `l` satisfying the predicate `p`. -/
def eraseP (p : α Bool) : List α List α
| [] => []
| a :: l => bif p a then l else a :: eraseP p l
/-! ### eraseIdx -/
/--
@@ -1141,8 +968,6 @@ def eraseIdx : List α → Nat → List α
@[simp] theorem eraseIdx_cons_zero : (a::as).eraseIdx 0 = as := rfl
@[simp] theorem eraseIdx_cons_succ : (a::as).eraseIdx (i+1) = a :: as.eraseIdx i := rfl
/-! Finding elements -/
/-! ### find? -/
/--
@@ -1180,50 +1005,6 @@ theorem findSome?_cons {f : α → Option β} :
(a::as).findSome? f = match f a with | some b => some b | none => as.findSome? f :=
rfl
/-! ### findIdx -/
/-- Returns the index of the first element satisfying `p`, or the length of the list otherwise. -/
@[inline] def findIdx (p : α Bool) (l : List α) : Nat := go l 0 where
/-- Auxiliary for `findIdx`: `findIdx.go p l n = findIdx p l + n` -/
@[specialize] go : List α Nat Nat
| [], n => n
| a :: l, n => bif p a then n else go l (n + 1)
@[simp] theorem findIdx_nil {α : Type _} (p : α Bool) : [].findIdx p = 0 := rfl
/-! ### indexOf -/
/-- Returns the index of the first element equal to `a`, or the length of the list otherwise. -/
def indexOf [BEq α] (a : α) : List α Nat := findIdx (· == a)
@[simp] theorem indexOf_nil [BEq α] : ([] : List α).indexOf x = 0 := rfl
/-! ### findIdx? -/
/-- Return the index of the first occurrence of an element satisfying `p`. -/
def findIdx? (p : α Bool) : List α (start : Nat := 0) Option Nat
| [], _ => none
| a :: l, i => if p a then some i else findIdx? p l (i + 1)
/-! ### indexOf? -/
/-- Return the index of the first occurrence of `a` in the list. -/
@[inline] def indexOf? [BEq α] (a : α) : List α Option Nat := findIdx? (· == a)
/-! ### countP -/
/-- `countP p l` is the number of elements of `l` that satisfy `p`. -/
@[inline] def countP (p : α Bool) (l : List α) : Nat := go l 0 where
/-- Auxiliary for `countP`: `countP.go p l acc = countP p l + acc`. -/
@[specialize] go : List α Nat Nat
| [], acc => acc
| x :: xs, acc => bif p x then go xs (acc + 1) else go xs acc
/-! ### count -/
/-- `count a l` is the number of occurrences of `a` in `l`. -/
@[inline] def count [BEq α] (a : α) : List α Nat := countP (· == a)
/-! ### lookup -/
/--
@@ -1244,36 +1025,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 -/
@@ -1395,14 +1146,6 @@ def unzip : List (α × β) → List α × List β
/-! ## Ranges and enumeration -/
/-- Sum of a list of natural numbers. -/
-- This is not in the `List` namespace as later `List.sum` will be defined polymorphically.
protected def _root_.Nat.sum (l : List Nat) : Nat := l.foldr (·+·) 0
@[simp] theorem _root_.Nat.sum_nil : Nat.sum ([] : List Nat) = 0 := rfl
@[simp] theorem _root_.Nat.sum_cons (a : Nat) (l : List Nat) :
Nat.sum (a::l) = a + Nat.sum l := rfl
/-! ### range -/
/--
@@ -1418,14 +1161,6 @@ where
@[simp] theorem range_zero : range 0 = [] := rfl
/-! ### range' -/
/-- `range' start len step` is the list of numbers `[start, start+step, ..., start+(len-1)*step]`.
It is intended mainly for proving properties of `range` and `iota`. -/
def range' : (start len : Nat) (step : Nat := 1) List Nat
| _, 0, _ => []
| s, n+1, step => s :: range' (s+step) n step
/-! ### iota -/
/--

View File

@@ -192,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)
@@ -222,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 =>

View File

@@ -127,12 +127,12 @@ results `y` for which `f x` returns `some y`.
@[inline]
def filterMapM {m : Type u Type v} [Monad m] {α β : Type u} (f : α m (Option β)) (as : List α) : m (List β) :=
let rec @[specialize] loop
| [], bs => pure bs.reverse
| [], bs => pure bs
| a :: as, bs => do
match ( f a) with
| none => loop as bs
| some b => loop as (b::bs)
loop as []
loop as.reverse []
/--
Folds a monadic function over a list from left to right:
@@ -227,8 +227,6 @@ def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
instance : ForIn m (List α) α where
forIn := List.forIn
@[simp] theorem forIn_eq_forIn [Monad m] : @List.forIn α β m _ = forIn := rfl
@[simp] theorem forIn_nil [Monad m] (f : α β m (ForInStep β)) (b : β) : forIn [] b f = pure b :=
rfl

View File

@@ -1,250 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Sublist
/-!
# Lemmas about `List.countP` and `List.count`.
-/
namespace List
open Nat
/-! ### countP -/
section countP
variable (p q : α Bool)
@[simp] theorem countP_nil : countP p [] = 0 := rfl
protected theorem countP_go_eq_add (l) : countP.go p l n = n + countP.go p l 0 := by
induction l generalizing n with
| nil => rfl
| cons head tail ih =>
unfold countP.go
rw [ih (n := n + 1), ih (n := n), ih (n := 1)]
if h : p head then simp [h, Nat.add_assoc] else simp [h]
@[simp] theorem countP_cons_of_pos (l) (pa : p a) : countP p (a :: l) = countP p l + 1 := by
have : countP.go p (a :: l) 0 = countP.go p l 1 := show cond .. = _ by rw [pa]; rfl
unfold countP
rw [this, Nat.add_comm, List.countP_go_eq_add]
@[simp] theorem countP_cons_of_neg (l) (pa : ¬p a) : countP p (a :: l) = countP p l := by
simp [countP, countP.go, pa]
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 length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a => ¬p a) l := by
induction l with
| nil => rfl
| cons x h ih =>
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]
else
rw [countP_cons_of_pos (fun a => ¬p a) _ _, countP_cons_of_neg _ _ h, length, ih]
· rfl
· simp [h]
theorem countP_eq_length_filter (l) : countP p l = length (filter p l) := by
induction l with
| nil => rfl
| cons x l ih =>
if h : p x
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_le_length : countP p l l.length := by
simp only [countP_eq_length_filter]
apply length_filter_le
@[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]
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]
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]
theorem countP_eq_length : countP p l = l.length a l, p a := by
rw [countP_eq_length_filter, filter_length_eq_length]
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 _
theorem countP_filter (l : List α) :
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 {l : List α} : (l.countP fun _ => true) = l.length := by
rw [countP_eq_length]
simp
@[simp] theorem countP_false {l : List α} : (l.countP fun _ => false) = 0 := by
rw [countP_eq_zero]
simp
@[simp] theorem countP_map (p : β Bool) (f : α β) :
l, countP p (map f l) = countP (p f) l
| [] => rfl
| a :: l => by rw [map_cons, countP_cons, countP_cons, countP_map p f l]; rfl
variable {p q}
theorem countP_mono_left (h : x l, p x q x) : countP p l countP q l := by
induction l with
| nil => apply Nat.le_refl
| cons a l ihl =>
rw [forall_mem_cons] at h
have ha, hl := h
simp [countP_cons]
cases h : p a
· simp only [Bool.false_eq_true, reduceIte, Nat.add_zero]
apply Nat.le_trans ?_ (Nat.le_add_right _ _)
apply ihl hl
· simp only [reduceIte, ha h, succ_le_succ_iff]
apply ihl hl
theorem countP_congr (h : x l, p x q x) : countP p l = countP q l :=
Nat.le_antisymm
(countP_mono_left fun x hx => (h x hx).1)
(countP_mono_left fun x hx => (h x hx).2)
end countP
/-! ### count -/
section count
variable [BEq α]
@[simp] theorem count_nil (a : α) : count a [] = 0 := rfl
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_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]
theorem count_le_length (a : α) (l : List α) : count a l l.length := countP_le_length _
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 _
theorem count_le_count_cons (a b : α) (l : List α) : count a l count a (b :: l) :=
(sublist_cons_self _ _).count_le _
theorem count_singleton (a b : α) : count a [b] = if b == a then 1 else 0 := by
simp [count_cons]
@[simp] theorem count_append (a : α) : l₁ l₂, count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=
countP_append _
variable [LawfulBEq α]
@[simp] theorem count_cons_self (a : α) (l : List α) : count a (a :: l) = count a l + 1 := by
simp [count_cons]
@[simp] theorem count_cons_of_ne (h : a b) (l : List α) : count a (b :: l) = count a l := by
simp only [count_cons, cond_eq_if, beq_iff_eq]
split <;> simp_all
theorem count_singleton_self (a : α) : count a [a] = 1 := by simp
theorem count_concat_self (a : α) (l : List α) :
count a (concat l a) = (count a l) + 1 := by simp
@[simp]
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_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_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
theorem count_eq_length {l : List α} : count a l = l.length b l, a = b := by
rw [count, countP_eq_length]
refine fun h b hb => Eq.symm ?_, fun h b hb => ?_
· simpa using h b hb
· rw [h b hb, beq_self_eq_true]
@[simp] theorem count_replicate_self (a : α) (n : Nat) : count a (replicate n a) = n :=
(count_eq_length.2 <| fun _ h => (eq_of_mem_replicate h).symm).trans (length_replicate ..)
theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b == a then n else 0 := by
split <;> (rename_i h; simp only [beq_iff_eq] at h)
· exact b = a count_replicate_self ..
· 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, 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 :=
filter_beq l a
theorem le_count_iff_replicate_sublist {l : List α} : n count a l replicate n a <+ l := by
refine fun h => ?_, fun h => ?_
· exact ((replicate_sublist_replicate a).2 h).trans <| filter_beq l a filter_sublist _
· simpa only [count_replicate_self] using h.count_le a
theorem replicate_count_eq_of_count_eq_length {l : List α} (h : count a l = length l) :
replicate (count a l) a = l :=
(le_count_iff_replicate_sublist.mp (Nat.le_refl _)).eq_of_length <|
(length_replicate (count a l) a).trans h
@[simp] theorem count_filter {l : List α} (h : p a) : count a (filter p l) = count a l := by
rw [count, countP_filter]; congr; funext b
simp; rintro rfl; exact h
theorem count_le_count_map [DecidableEq β] (l : List α) (f : α β) (x : α) :
count x l count (f x) (map f l) := by
rw [count, count, countP_map]
apply countP_mono_left; simp (config := { contextual := true })
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
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]
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]
else
rw [if_neg (by simpa using ha), Nat.sub_zero, Nat.sub_zero]
@[simp] theorem count_erase_self (a : α) (l : List α) :
count a (List.erase l a) = count a l - 1 := by rw [count_erase, if_pos (by simp)]
@[simp] theorem count_erase_of_ne (ab : a b) (l : List α) : count a (l.erase b) = count a l := by
rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
end count

View File

@@ -1,549 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
Yury Kudryashov
-/
prelude
import Init.Data.List.Pairwise
/-!
# Lemmas about `List.eraseP` and `List.erase`.
-/
namespace List
open Nat
/-! ### eraseP -/
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
theorem eraseP_cons (a : α) (l : List α) :
(a :: l).eraseP p = bif p a then l else a :: l.eraseP p := rfl
@[simp] theorem eraseP_cons_of_pos {l : List α} {p} (h : p a) : (a :: l).eraseP p = l := by
simp [eraseP_cons, h]
@[simp] theorem eraseP_cons_of_neg {l : List α} {p} (h : ¬p a) :
(a :: l).eraseP p = a :: l.eraseP p := by simp [eraseP_cons, h]
theorem eraseP_of_forall_not {l : List α} (h : a, a l ¬p a) : l.eraseP p = l := by
induction l with
| 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 =>
if pb : p b then
b, [], l, forall_mem_nil _, pb, by simp [pb]
else
match al with
| .head .. => nomatch pb pa
| .tail _ al =>
let c, l₁, l₂, h₁, h₂, h₃, h₄ := exists_of_eraseP al pa
c, b::l₁, l₂, (forall_mem_cons ..).2 pb, h₁,
h₂, by rw [h₃, cons_append], by simp [pb, h₄]
theorem exists_or_eq_self_of_eraseP (p) (l : List α) :
l.eraseP p = l
a l₁ l₂, ( b l₁, ¬p b) p a l = l₁ ++ a :: l₂ l.eraseP p = l₁ ++ l₂ :=
if h : a l, p a then
let _, ha, pa := h
.inr (exists_of_eraseP ha pa)
else
.inl (eraseP_of_forall_not (h ·, ·, ·))
@[simp] theorem length_eraseP_of_mem (al : a l) (pa : p a) :
length (l.eraseP p) = length l - 1 := by
let _, l₁, l₂, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [length_append, e₁]; rfl
theorem length_eraseP {l : List α} : (l.eraseP p).length = if l.any p then l.length - 1 else l.length := by
split <;> rename_i h
· simp only [any_eq_true] at h
obtain x, m, h := h
simp [length_eraseP_of_mem m h]
· simp only [any_eq_true] at h
rw [eraseP_of_forall_not]
simp_all
theorem eraseP_sublist (l : List α) : l.eraseP p <+ l := by
match exists_or_eq_self_of_eraseP p l with
| .inl h => rw [h]; apply Sublist.refl
| .inr c, l₁, l₂, _, _, h₃, h₄ => rw [h₄, h₃]; simp
theorem eraseP_subset (l : List α) : l.eraseP p l := (eraseP_sublist l).subset
protected theorem Sublist.eraseP : l₁ <+ l₂ l₁.eraseP p <+ l₂.eraseP p
| .slnil => Sublist.refl _
| .cons a s => by
by_cases h : p a
· simpa [h] using s.eraseP.trans (eraseP_sublist _)
· simpa [h] using s.eraseP.cons _
| .cons₂ a s => by
by_cases h : p a
· simpa [h] using s
· simpa [h] using s.eraseP
theorem length_eraseP_le (l : List α) : (l.eraseP p).length l.length :=
l.eraseP_sublist.length_le
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
refine mem_of_mem_eraseP, fun al => ?_
match exists_or_eq_self_of_eraseP p l with
| .inl h => rw [h]; assumption
| .inr c, l₁, l₂, h₁, h₂, h₃, h₄ =>
rw [h₄]; rw [h₃] at al
have : a c := fun h => (h pa).elim h₂
simp [this] at al; simp [al]
@[simp] theorem eraseP_eq_self_iff {p} {l : List α} : l.eraseP p = l a l, ¬ p a := by
rw [ Sublist.length_eq (eraseP_sublist l), length_eraseP]
split <;> rename_i h
· simp only [any_eq_true, length_eq_zero] at h
constructor
· intro; simp_all [Nat.sub_one_eq_self]
· intro; obtain x, m, h := h; simp_all
· simp_all
theorem eraseP_map (f : β α) : (l : List β), (map f l).eraseP p = map f (l.eraseP (p f))
| [] => rfl
| b::l => by by_cases h : p (f b) <;> simp [h, eraseP_map f l, eraseP_cons_of_pos]
theorem eraseP_filterMap (f : α Option β) : (l : List α),
(filterMap f l).eraseP p = filterMap f (l.eraseP (fun x => match f x with | some y => p y | none => false))
| [] => rfl
| a::l => by
rw [filterMap_cons, eraseP_cons]
split <;> rename_i h
· simp [h, eraseP_filterMap]
· rename_i b
rw [h, eraseP_cons]
by_cases w : p b
· simp [w]
· simp only [w, cond_false]
rw [filterMap_cons_some h, eraseP_filterMap]
theorem eraseP_filter (f : α Bool) (l : List α) :
(filter f l).eraseP p = filter f (l.eraseP (fun x => p x && f x)) := by
rw [ filterMap_eq_filter, eraseP_filterMap]
congr
ext x
simp only [Option.guard]
split <;> split at * <;> simp_all
theorem eraseP_append_left {a : α} (pa : p a) :
{l₁ : List α} l₂, a l₁ (l₁++l₂).eraseP p = l₁.eraseP p ++ l₂
| x :: xs, l₂, h => by
by_cases h' : p x <;> simp [h']
rw [eraseP_append_left pa l₂ ((mem_cons.1 h).resolve_left (mt _ h'))]
intro | rfl => exact pa
theorem eraseP_append_right :
{l₁ : List α} l₂, ( b l₁, ¬p b) eraseP p (l₁++l₂) = l₁ ++ l₂.eraseP p
| [], l₂, _ => rfl
| x :: xs, l₂, h => by
simp [(forall_mem_cons.1 h).1, eraseP_append_right _ (forall_mem_cons.1 h).2]
theorem eraseP_append (l₁ l₂ : List α) :
(l₁ ++ l₂).eraseP p = if l₁.any p then l₁.eraseP p ++ l₂ else l₁ ++ l₂.eraseP p := by
split <;> rename_i h
· simp only [any_eq_true] at h
obtain x, m, h := h
rw [eraseP_append_left h _ m]
· simp only [any_eq_true] at h
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')
a l₁ l₂, ( b l₁, ¬ p b) p a l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ := by
cases exists_or_eq_self_of_eraseP p l with
| inl h =>
constructor
· intro h'
left
exact eraseP_eq_self_iff.1 h, by simp_all
· rintro (-, rfl | a, l₁, l₂, h₁, h₂, rfl, rfl)
· assumption
· rw [eraseP_append_right _ h₁, eraseP_cons_of_pos h₂]
| inr h =>
obtain a, l₁, l₂, h₁, h₂, w₁, w₂ := h
rw [w₂]
subst w₁
constructor
· rintro rfl
right
refine a, l₁, l₂, ?_
simp_all
· rintro (h | h)
· simp_all
· obtain a', l₁', l₂', h₁', h₂', h, rfl := h
have p : l₁ = l₁' := by
have q : l₁ = takeWhile (fun x => !p x) (l₁ ++ a :: l₂) := by
rw [takeWhile_append_of_pos (by simp_all),
takeWhile_cons_of_neg (by simp [h₂]), append_nil]
have q' : l₁' = takeWhile (fun x => !p x) (l₁' ++ a' :: l₂') := by
rw [takeWhile_append_of_pos (by simpa using h₁'),
takeWhile_cons_of_neg (by simp [h₂']), append_nil]
simp [h] at q
rw [q', q]
subst p
simp_all
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
(replicate n a).eraseP p = replicate (n - 1) a := by
cases n <;> simp [replicate_succ, h]
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
(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
theorem eraseP_comm {l : List α} (h : a l, ¬ p a ¬ q a) :
(l.eraseP p).eraseP q = (l.eraseP q).eraseP p := by
induction l with
| nil => rfl
| cons a l ih =>
simp only [eraseP_cons]
by_cases h₁ : p a
· by_cases h₂ : q a
· simp_all
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
· by_cases h₂ : 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 α]
@[simp] theorem erase_cons_head [LawfulBEq α] (a : α) (l : List α) : (a :: l).erase a = l := by
simp [erase_cons]
@[simp] theorem erase_cons_tail {a b : α} {l : List α} (h : ¬(b == a)) :
(b :: l).erase a = b :: l.erase a := by simp only [erase_cons, if_neg h]
theorem erase_of_not_mem [LawfulBEq α] {a : α} : {l : List α}, a l l.erase a = l
| [], _ => rfl
| b :: l, h => by
rw [mem_cons, not_or] at h
simp only [erase_cons, if_neg, erase_of_not_mem h.2, beq_iff_eq, Ne.symm h.1, not_false_eq_true]
theorem erase_eq_eraseP' (a : α) (l : List α) : l.erase a = l.eraseP (· == a) := by
induction l
· simp
· next b t ih =>
rw [erase_cons, eraseP_cons, ih]
if h : b == a then simp [h] else simp [h]
theorem erase_eq_eraseP [LawfulBEq α] (a : α) : l : List α, l.erase a = l.eraseP (a == ·)
| [] => rfl
| 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 _)
rw [erase_eq_eraseP]; exact l₁, l₂, fun h => h₁ _ h (beq_self_eq_true _), eq_of_beq e h₂, h₃
@[simp] theorem length_erase_of_mem [LawfulBEq α] {a : α} {l : List α} (h : a l) :
length (l.erase a) = length l - 1 := by
rw [erase_eq_eraseP]; exact length_eraseP_of_mem h (beq_self_eq_true a)
theorem length_erase [LawfulBEq α] (a : α) (l : List α) :
length (l.erase a) = if a l then length l - 1 else length l := by
rw [erase_eq_eraseP, length_eraseP]
split <;> split <;> simp_all
theorem erase_sublist (a : α) (l : List α) : l.erase a <+ l :=
erase_eq_eraseP' a l eraseP_sublist ..
theorem erase_subset (a : α) (l : List α) : l.erase a l := (erase_sublist a l).subset
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 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) :
a l.erase b a l :=
erase_eq_eraseP b l mem_eraseP_of_neg (mt eq_of_beq ab.symm)
@[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']
theorem erase_filter [LawfulBEq α] (f : α Bool) (l : List α) :
(filter f l).erase a = filter f (l.erase a) := by
induction l with
| nil => rfl
| cons x xs ih =>
by_cases h : a = x
· rw [erase_cons]
simp only [h, beq_self_eq_true, reduceIte]
rw [filter_cons]
split
· rw [erase_cons_head]
· rw [erase_of_not_mem]
simp_all [mem_filter]
· rw [erase_cons_tail (by simpa using Ne.symm h), filter_cons, filter_cons]
split
· rw [erase_cons_tail (by simpa using Ne.symm h), ih]
· rw [ih]
theorem erase_append_left [LawfulBEq α] {l₁ : List α} (l₂) (h : a l₁) :
(l₁ ++ l₂).erase a = l₁.erase a ++ l₂ := by
simp [erase_eq_eraseP]; exact eraseP_append_left (beq_self_eq_true a) l₂ h
theorem erase_append_right [LawfulBEq α] {a : α} {l₁ : List α} (l₂ : List α) (h : a l₁) :
(l₁ ++ l₂).erase a = (l₁ ++ l₂.erase a) := by
rw [erase_eq_eraseP, erase_eq_eraseP, eraseP_append_right]
intros b h' h''; rw [eq_of_beq h''] at h; exact h h'
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 ?_
if ha : a l then ?_ else
simp only [erase_of_not_mem ha, erase_of_not_mem (mt mem_of_mem_erase ha)]
if hb : b l then ?_ else
simp only [erase_of_not_mem hb, erase_of_not_mem (mt mem_of_mem_erase hb)]
match l, l.erase a, exists_erase_eq ha with
| _, _, l₁, l₂, ha', rfl, rfl =>
if h₁ : b l₁ then
rw [erase_append_left _ h₁, erase_append_left _ h₁,
erase_append_right _ (mt mem_of_mem_erase ha'), erase_cons_head]
else
rw [erase_append_right _ h₁, erase_append_right _ h₁, erase_append_right _ ha',
erase_cons_tail ab, erase_cons_head]
theorem erase_eq_iff [LawfulBEq α] {a : α} {l : List α} :
l.erase a = l'
(a l l = l')
l₁ l₂, a l₁ l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ := by
rw [erase_eq_eraseP', eraseP_eq_iff]
simp only [beq_iff_eq, forall_mem_ne', exists_and_left]
constructor
· rintro (h, rfl | a', l', h, rfl, x, rfl, rfl)
· left; simp_all
· right; refine l', h, x, by simp
· rintro (h, rfl | l₁, h, x, rfl, rfl)
· left; simp_all
· right; refine a, l₁, h, by simp
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
(replicate n a).erase a = replicate (n - 1) a := by
cases n <;> simp [replicate_succ]
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(replicate n a).erase b = replicate n a := by
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
induction d with
| nil => rfl
| cons m _n ih =>
rename_i b l
by_cases h : b = a
· subst h
rw [erase_cons_head, filter_cons_of_neg (by simp)]
apply Eq.symm
rw [filter_eq_self]
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
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
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
end erase
/-! ### eraseIdx -/
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
theorem eraseIdx_eq_take_drop_succ :
(l : List α) (i : Nat), l.eraseIdx i = l.take i ++ l.drop (i + 1)
| nil, _ => by simp
| a::l, 0 => by simp
| a::l, i + 1 => by simp [eraseIdx_eq_take_drop_succ l i]
@[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]
theorem eraseIdx_eq_self : {l : List α} {k : Nat}, eraseIdx l k = l length l k
| [], _ => by simp
| a::l, 0 => by simp [(cons_ne_self _ _).symm]
| a::l, k + 1 => by simp [eraseIdx_eq_self]
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 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
| nil => simp_all
| cons x l ih =>
cases k with
| zero => rfl
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_lt_succ_iff]
theorem eraseIdx_append_of_length_le {l : List α} {k : Nat} (hk : length l k) (l' : List α) :
eraseIdx (l ++ l') k = l ++ eraseIdx l' (k - length l) := by
induction l generalizing k with
| nil => simp_all
| cons x l ih =>
cases k with
| 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, length_eraseIdx (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
if hkl : k < length l then
simp [eraseIdx_append_of_lt_length hkl]
else
rw [Nat.not_lt] at hkl
simp [eraseIdx_append_of_length_le hkl, eraseIdx_of_length_le hkl]
-- See also `mem_eraseIdx_iff_getElem` and `mem_eraseIdx_iff_getElem?` in
-- `Init/Data/List/Nat/Basic.lean`.
end List

View File

@@ -1,779 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
Kim Morrison, Jannis Limperg
-/
prelude
import Init.Data.List.Lemmas
import Init.Data.List.Sublist
import Init.Data.List.Range
/-!
# Lemmas about `List.findSome?`, `List.find?`, `List.findIdx`, `List.findIdx?`, and `List.indexOf`.
-/
namespace List
open Nat
/-! ### findSome? -/
@[simp] theorem findSome?_cons_of_isSome (l) (h : (f a).isSome) : findSome? f (a :: l) = f a := by
simp only [findSome?]
split <;> simp_all
@[simp] theorem findSome?_cons_of_isNone (l) (h : (f a).isNone) : findSome? f (a :: l) = findSome? f l := by
simp only [findSome?]
split <;> simp_all
theorem exists_of_findSome?_eq_some {l : List α} {f : α Option β} (w : l.findSome? f = some b) :
a, a l f a = b := by
induction l with
| nil => simp_all
| cons h l ih =>
simp_all only [findSome?_cons, mem_cons, exists_eq_or_imp]
split at w <;> simp_all
@[simp] theorem findSome?_eq_none : findSome? p l = none x l, p x = none := by
induction l <;> simp [findSome?_cons]; split <;> simp [*]
@[simp] theorem findSome?_isSome_iff (f : α Option β) (l : List α) :
(l.findSome? f).isSome x, x l (f x).isSome := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findSome?_cons]
split <;> simp_all
@[simp] theorem findSome?_guard (l : List α) : findSome? (Option.guard fun x => p x) l = find? p l := by
induction l with
| nil => simp
| cons x xs ih =>
simp [guard, findSome?, find?]
split <;> rename_i h
· simp only [Option.guard_eq_some] at h
obtain rfl, h := h
simp [h]
· simp only [Option.guard_eq_none] at h
simp [ih, h]
@[simp] theorem filterMap_head? (f : α Option β) (l : List α) : (l.filterMap f).head? = l.findSome? f := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [filterMap_cons, findSome?_cons]
split <;> simp [*]
@[simp] theorem filterMap_head (f : α Option β) (l : List α) (h) :
(l.filterMap f).head h = (l.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [head_eq_iff_head?_eq_some]
@[simp] theorem filterMap_getLast? (f : α Option β) (l : List α) : (l.filterMap f).getLast? = l.reverse.findSome? f := by
rw [getLast?_eq_head?_reverse]
simp [ filterMap_reverse]
@[simp] theorem filterMap_getLast (f : α Option β) (l : List α) (h) :
(l.filterMap f).getLast h = (l.reverse.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [getLast_eq_iff_getLast_eq_some]
@[simp] theorem map_findSome? (f : α Option β) (g : β γ) (l : List α) :
(l.findSome? f).map g = l.findSome? (Option.map g f) := by
induction l <;> simp [findSome?_cons]; split <;> simp [*]
theorem findSome?_map (f : β γ) (l : List β) : findSome? p (l.map f) = l.findSome? (p f) := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, findSome?]
split <;> simp_all
theorem findSome?_append {l₁ l₂ : List α} : (l₁ ++ l₂).findSome? f = (l₁.findSome? f).or (l₂.findSome? f) := by
induction l₁ with
| nil => simp
| cons x xs ih =>
simp only [cons_append, findSome?]
split <;> simp_all
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
cases n with
| zero => simp
| succ n =>
simp only [replicate_succ, findSome?_cons]
split <;> simp_all
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
simp [findSome?_replicate, Nat.ne_of_gt h]
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate]
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) : findSome? f (replicate n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_replicate, h]
theorem Sublist.findSome?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
(l₁.findSome? f).isSome (l₂.findSome? f).isSome := by
induction h with
| slnil => simp
| cons a h ih
| cons₂ a h ih =>
simp only [findSome?]
split
· simp_all
· exact ih
theorem Sublist.findSome?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
l₂.findSome? f = none l₁.findSome? f = none := by
simp only [List.findSome?_eq_none, Bool.not_eq_true]
exact fun w x m => w x (Sublist.mem m h)
theorem IsPrefix.findSome?_eq_some {l₁ l₂ : List α} {f : α Option β} (h : l₁ <+: l₂) :
List.findSome? f l₁ = some b List.findSome? f l₂ = some b := by
rw [IsPrefix] at h
obtain t, rfl := h
simp (config := {contextual := true}) [findSome?_append]
theorem IsPrefix.findSome?_eq_none {l₁ l₂ : List α} {f : α Option β} (h : l₁ <+: l₂) :
List.findSome? f l₂ = none List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
theorem IsSuffix.findSome?_eq_none {l₁ l₂ : List α} {f : α Option β} (h : l₁ <:+ l₂) :
List.findSome? f l₂ = none List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
theorem IsInfix.findSome?_eq_none {l₁ l₂ : List α} {f : α Option β} (h : l₁ <:+: l₂) :
List.findSome? f l₂ = none List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
/-! ### find? -/
@[simp] theorem find?_singleton (a : α) (p : α Bool) : [a].find? p = if p a then some a else none := by
simp only [find?]
split <;> simp_all
@[simp] theorem find?_cons_of_pos (l) (h : p a) : find? p (a :: l) = some a := by
simp [find?, h]
@[simp] theorem find?_cons_of_neg (l) (h : ¬p a) : find? p (a :: l) = find? p l := by
simp [find?, h]
@[simp] theorem find?_eq_none : find? p l = none x l, ¬ p x := by
induction l <;> simp [find?_cons]; split <;> simp [*]
theorem find?_eq_some : xs.find? p = some b p b as bs, xs = as ++ b :: bs a as, !p a := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, exists_and_right]
split <;> rename_i h
· simp only [Option.some.injEq]
constructor
· rintro rfl
exact h, [], xs, rfl, by simp
· rintro -, as, bs, h₁, h₂
cases as with
| nil => simp_all
| cons a as =>
specialize h₂ a (mem_cons_self _ _)
simp only [cons_append] at h₁
obtain rfl, - := h₁
simp_all
· simp only [ih, Bool.not_eq_true', exists_and_right, and_congr_right_iff]
intro pb
constructor
· rintro as, bs, rfl, h₁
refine x :: as, bs, rfl, ?_
intro a m
simp at m
obtain (rfl|m) := m
· exact h
· exact h₁ a m
· rintro as, bs, h₁, h₂
cases as with
| nil => simp_all
| cons a as =>
refine as, bs, ?_, fun a m => h₂ a (mem_cons_of_mem _ m)
cases h₁
simp
@[simp]
theorem find?_cons_eq_some : (a :: xs).find? p = some b (p a a = b) (!p a xs.find? p = some b) := by
rw [find?_cons]
split <;> simp_all
@[simp] theorem find?_isSome (xs : List α) (p : α Bool) : (xs.find? p).isSome x, x xs p x := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, mem_cons, exists_eq_or_imp]
split <;> simp_all
theorem find?_some : {l}, find? p l = some a p a
| b :: l, H => by
by_cases h : p b <;> simp [find?, h] at H
· exact H h
· exact find?_some H
theorem mem_of_find?_eq_some : {l}, find? p l = some a a l
| b :: l, H => by
by_cases h : p b <;> simp [find?, h] at H
· exact H .head _
· exact .tail _ (mem_of_find?_eq_some H)
@[simp] theorem get_find?_mem (xs : List α) (p : α Bool) (h) : (xs.find? p).get h xs := by
induction xs with
| nil => simp at h
| cons x xs ih =>
simp only [find?_cons]
by_cases h : p x
· simp [h]
· simp only [h]
right
apply ih
@[simp] theorem find?_filter (xs : List α) (p : α Bool) (q : α Bool) :
(xs.filter p).find? q = xs.find? (fun a => p a q a) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [filter_cons]
split <;>
· simp only [find?_cons]
split <;> simp_all
@[simp] theorem filter_head? (p : α Bool) (l : List α) : (l.filter p).head? = l.find? p := by
rw [ filterMap_eq_filter, filterMap_head?, findSome?_guard]
@[simp] theorem filter_head (p : α Bool) (l : List α) (h) :
(l.filter p).head h = (l.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [head_eq_iff_head?_eq_some]
@[simp] theorem filter_getLast? (p : α Bool) (l : List α) : (l.filter p).getLast? = l.reverse.find? p := by
rw [getLast?_eq_head?_reverse]
simp [ filter_reverse]
@[simp] theorem filter_getLast (p : α Bool) (l : List α) (h) :
(l.filter p).getLast h = (l.reverse.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [getLast_eq_iff_getLast_eq_some]
@[simp] theorem find?_filterMap (xs : List α) (f : α Option β) (p : β Bool) :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [filterMap_cons]
split <;>
· simp only [find?_cons]
split <;> simp_all
@[simp] theorem find?_map (f : β α) (l : List β) : find? p (l.map f) = (l.find? (p f)).map f := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, find?]
by_cases h : p (f x) <;> simp [h, ih]
@[simp] theorem find?_append {l₁ l₂ : List α} : (l₁ ++ l₂).find? p = (l₁.find? p).or (l₂.find? p) := by
induction l₁ with
| nil => simp
| cons x xs ih =>
simp only [cons_append, find?]
by_cases h : p x <;> simp [h, ih]
@[simp] theorem find?_join (xs : List (List α)) (p : α Bool) :
xs.join.find? p = xs.findSome? (·.find? p) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [join_cons, find?_append, findSome?_cons, ih]
split <;> simp [*]
theorem find?_join_eq_none (xs : List (List α)) (p : α Bool) :
xs.join.find? p = none ys xs, x ys, !p x := by
simp
/--
If `find? p` returns `some a` from `xs.join`, then `p a` holds, and
some list in `xs` contains `a`, and no earlier element of that list satisfies `p`.
Moreover, no earlier list in `xs` has an element satisfying `p`.
-/
theorem find?_join_eq_some (xs : List (List α)) (p : α Bool) (a : α) :
xs.join.find? p = some a
p a as ys zs bs, xs = as ++ (ys ++ a :: zs) :: bs
( a as, x a, !p x) ( x ys, !p x) := by
rw [find?_eq_some]
constructor
· rintro h, ys, zs, h₁, h₂
refine h, ?_
rw [join_eq_append] at h₁
obtain (as, bs, rfl, rfl, h₁ | as, bs, c, cs, ds, rfl, rfl, h₁) := h₁
· replace h₁ := h₁.symm
rw [join_eq_cons] at h₁
obtain bs, cs, ds, rfl, h₁, rfl := h₁
refine as ++ bs, [], cs, ds, by simp, ?_
simp
rintro a (ma | mb) x m
· simpa using h₂ x (by simpa using a, ma, m)
· specialize h₁ _ mb
simp_all
· simp [h₁]
refine as, bs, ?_
refine ?_, ?_, ?_
· simp_all
· intro l ml a m
simpa using h₂ a (by simpa using .inl l, ml, m)
· intro x m
simpa using h₂ x (by simpa using .inr m)
· rintro h, as, ys, zs, bs, rfl, h₁, h₂
refine h, as.join ++ ys, zs ++ bs.join, by simp, ?_
intro a m
simp at m
obtain l, ml, m | m := m
· exact h₁ l ml a m
· exact h₂ a m
@[simp] theorem find?_bind (xs : List α) (f : α List β) (p : β Bool) :
(xs.bind f).find? p = xs.findSome? (fun x => (f x).find? p) := by
simp [bind_def, findSome?_map]; rfl
theorem find?_bind_eq_none (xs : List α) (f : α List β) (p : β Bool) :
(xs.bind f).find? p = none x xs, y f x, !p y := by
simp
theorem find?_replicate : find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
cases n
· simp
· by_cases p a <;> simp_all [replicate_succ]
@[simp] theorem find?_replicate_of_length_pos (h : 0 < n) : find? p (replicate n a) = if p a then some a else none := by
simp [find?_replicate, Nat.ne_of_gt h]
@[simp] theorem find?_replicate_of_pos (h : p a) : find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
theorem find?_replicate_eq_none (n : Nat) (a : α) (p : α Bool) :
(replicate n a).find? p = none n = 0 !p a := by
simp [Classical.or_iff_not_imp_left]
@[simp] theorem find?_replicate_eq_some (n : Nat) (a b : α) (p : α Bool) :
(replicate n a).find? p = some b n 0 p a a = b := by
cases n <;> simp
@[simp] theorem get_find?_replicate (n : Nat) (a : α) (p : α Bool) (h) : ((replicate n a).find? p).get h = a := by
cases n with
| zero => simp at h
| succ n => simp
theorem Sublist.find?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.find? p).isSome (l₂.find? p).isSome := by
induction h with
| slnil => simp
| cons a h ih
| cons₂ a h ih =>
simp only [find?]
split
· simp
· simpa using ih
theorem Sublist.find?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₂.find? p = none l₁.find? p = none := by
simp only [List.find?_eq_none, Bool.not_eq_true]
exact fun w x m => w x (Sublist.mem m h)
theorem IsPrefix.find?_eq_some {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.find? p l₁ = some b List.find? p l₂ = some b := by
rw [IsPrefix] at h
obtain t, rfl := h
simp (config := {contextual := true}) [find?_append]
theorem IsPrefix.find?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.find? p l₂ = none List.find? p l₁ = none :=
h.sublist.find?_eq_none
theorem IsSuffix.find?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+ l₂) :
List.find? p l₂ = none List.find? p l₁ = none :=
h.sublist.find?_eq_none
theorem IsInfix.find?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+: l₂) :
List.find? p l₂ = none List.find? p l₁ = none :=
h.sublist.find?_eq_none
theorem find?_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) (p : β Bool) :
(xs.pmap f H).find? p = (xs.attach.find? (fun a, m => p (f a (H a m)))).map fun a, m => f a (H a m) := by
simp only [pmap_eq_map_attach, find?_map]
rfl
/-! ### findIdx -/
theorem findIdx_cons (p : α Bool) (b : α) (l : List α) :
(b :: l).findIdx p = bif p b then 0 else (l.findIdx p) + 1 := by
cases H : p b with
| true => simp [H, findIdx, findIdx.go]
| false => simp [H, findIdx, findIdx.go, findIdx_go_succ]
where
findIdx_go_succ (p : α Bool) (l : List α) (n : Nat) :
List.findIdx.go p l (n + 1) = (findIdx.go p l n) + 1 := by
cases l with
| nil => unfold findIdx.go; exact Nat.succ_eq_add_one n
| cons head tail =>
unfold findIdx.go
cases p head <;> simp only [cond_false, cond_true]
exact findIdx_go_succ p tail (n + 1)
theorem findIdx_of_getElem?_eq_some {xs : List α} (w : xs[xs.findIdx p]? = some y) : p y := by
induction xs with
| nil => simp_all
| cons x xs ih => by_cases h : p x <;> simp_all [findIdx_cons]
theorem findIdx_getElem {xs : List α} {w : xs.findIdx p < xs.length} :
p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
@[deprecated findIdx_of_getElem?_eq_some (since := "2024-08-12")]
theorem findIdx_of_get?_eq_some {xs : List α} (w : xs.get? (xs.findIdx p) = some y) : p y :=
findIdx_of_getElem?_eq_some (by simpa using w)
@[deprecated findIdx_getElem (since := "2024-08-12")]
theorem findIdx_get {xs : List α} {w : xs.findIdx p < xs.length} :
p (xs.get xs.findIdx p, w) :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
theorem findIdx_lt_length_of_exists {xs : List α} (h : x xs, p x) :
xs.findIdx p < xs.length := by
induction xs with
| nil => simp_all
| cons x xs ih =>
by_cases p x
· simp_all only [forall_exists_index, and_imp, mem_cons, exists_eq_or_imp, true_or,
findIdx_cons, cond_true, length_cons]
apply Nat.succ_pos
· simp_all [findIdx_cons, Nat.succ_lt_succ_iff]
obtain x', m', h' := h
exact ih x' m' h'
theorem findIdx_getElem?_eq_getElem_of_exists {xs : List α} (h : x xs, p x) :
xs[xs.findIdx p]? = some (xs[xs.findIdx p]'(xs.findIdx_lt_length_of_exists h)) :=
getElem?_eq_getElem (findIdx_lt_length_of_exists h)
@[deprecated findIdx_getElem?_eq_getElem_of_exists (since := "2024-08-12")]
theorem findIdx_get?_eq_get_of_exists {xs : List α} (h : x xs, p x) :
xs.get? (xs.findIdx p) = some (xs.get xs.findIdx p, xs.findIdx_lt_length_of_exists h) :=
get?_eq_get (findIdx_lt_length_of_exists h)
@[simp]
theorem findIdx_eq_length {p : α Bool} {xs : List α} :
xs.findIdx p = xs.length x xs, p x = false := by
induction xs with
| nil => simp_all
| cons x xs ih =>
rw [findIdx_cons, length_cons]
simp only [cond_eq_if]
split <;> simp_all [Nat.succ.injEq]
theorem findIdx_eq_length_of_false {p : α Bool} {xs : List α} (h : x xs, p x = false) :
xs.findIdx p = xs.length := by
rw [findIdx_eq_length]
exact h
theorem findIdx_le_length (p : α Bool) {xs : List α} : xs.findIdx p xs.length := by
by_cases e : x xs, p x
· exact Nat.le_of_lt (findIdx_lt_length_of_exists e)
· simp at e
exact Nat.le_of_eq (findIdx_eq_length.mpr e)
@[simp]
theorem findIdx_lt_length {p : α Bool} {xs : List α} :
xs.findIdx p < xs.length x xs, p x := by
rw [ Decidable.not_iff_not, Nat.not_lt]
have := @Nat.le_antisymm_iff (xs.findIdx p) xs.length
simp only [findIdx_le_length, true_and] at this
rw [ this, findIdx_eq_length, not_exists]
simp only [Bool.not_eq_true, not_and]
/-- `p` does not hold for elements with indices less than `xs.findIdx p`. -/
theorem not_of_lt_findIdx {p : α Bool} {xs : List α} {i : Nat} (h : i < xs.findIdx p) :
¬p (xs[i]'(Nat.le_trans h (findIdx_le_length p))) := by
revert i
induction xs with
| nil => intro i h; rw [findIdx_nil] at h; simp at h
| cons x xs ih =>
intro i h
have ho := h
rw [findIdx_cons] at h
have npx : ¬p x := by intro y; rw [y, cond_true] at h; simp at h
simp [npx, cond_false] at h
cases i.eq_zero_or_pos with
| inl e => simpa only [e, Fin.zero_eta, get_cons_zero]
| inr e =>
have ipm := Nat.succ_pred_eq_of_pos e
have ilt := Nat.le_trans ho (findIdx_le_length p)
simp (config := { singlePass := true }) only [ ipm, getElem_cons_succ]
rw [ ipm, Nat.succ_lt_succ_iff] at h
simpa using ih h
/-- If `¬ p xs[j]` for all `j < i`, then `i ≤ xs.findIdx p`. -/
theorem le_findIdx_of_not {p : α Bool} {xs : List α} {i : Nat} (h : i < xs.length)
(h2 : j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji h))) : i xs.findIdx p := by
apply Decidable.byContradiction
intro f
simp only [Nat.not_le] at f
exact absurd (@findIdx_getElem _ p xs (Nat.lt_trans f h)) (h2 (xs.findIdx p) f)
/-- If `¬ p xs[j]` for all `j ≤ i`, then `i < xs.findIdx p`. -/
theorem lt_findIdx_of_not {p : α Bool} {xs : List α} {i : Nat} (h : i < xs.length)
(h2 : j (hji : j i), ¬p (xs.get j, Nat.lt_of_le_of_lt hji h)) : i < xs.findIdx p := by
apply Decidable.byContradiction
intro f
simp only [Nat.not_lt] at f
exact absurd (@findIdx_getElem _ p xs (Nat.lt_of_le_of_lt f h)) (h2 (xs.findIdx p) f)
/-- `xs.findIdx p = i` iff `p xs[i]` and `¬ p xs [j]` for all `j < i`. -/
theorem findIdx_eq {p : α Bool} {xs : List α} {i : Nat} (h : i < xs.length) :
xs.findIdx p = i p xs[i] j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji h)) := by
refine fun f f (@findIdx_getElem _ p xs (f h)), fun _ hji not_of_lt_findIdx (f hji),
fun h1, h2 ?_
apply Nat.le_antisymm _ (le_findIdx_of_not h h2)
apply Decidable.byContradiction
intro h3
simp at h3
exact not_of_lt_findIdx h3 h1
theorem findIdx_append (p : α Bool) (l₁ l₂ : List α) :
(l₁ ++ l₂).findIdx p =
if l₁.findIdx p < l₁.length then l₁.findIdx p else l₂.findIdx p + l₁.length := by
simp
induction l₁ with
| nil => simp
| cons x xs ih =>
simp only [findIdx_cons, length_cons, cons_append]
by_cases h : p x
· simp [h]
· simp only [h, ih, cond_eq_if, Bool.false_eq_true, reduceIte, mem_cons, exists_eq_or_imp,
false_or]
split <;> simp [Nat.add_assoc]
theorem IsPrefix.findIdx_le {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
l₁.findIdx p l₂.findIdx p := by
rw [IsPrefix] at h
obtain t, rfl := h
simp only [findIdx_append, findIdx_lt_length]
split
· exact Nat.le_refl ..
· simp_all [findIdx_eq_length_of_false]
theorem IsPrefix.findIdx_eq_of_findIdx_lt_length {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂)
(lt : l₁.findIdx p < l₁.length) : l₂.findIdx p = l₁.findIdx p := by
rw [IsPrefix] at h
obtain t, rfl := h
simp only [findIdx_append, findIdx_lt_length]
split
· rfl
· simp_all
/-! ### findIdx? -/
@[simp] theorem findIdx?_nil : ([] : List α).findIdx? p i = none := rfl
@[simp] theorem findIdx?_cons :
(x :: xs).findIdx? p i = if p x then some i else findIdx? p xs (i + 1) := rfl
@[simp] theorem findIdx?_succ :
(xs : List α).findIdx? p (i+1) = (xs.findIdx? p i).map fun i => i + 1 := by
induction xs generalizing i with simp
| cons _ _ _ => split <;> simp_all
@[simp]
theorem findIdx?_eq_none_iff {xs : List α} {p : α Bool} :
xs.findIdx? p = none x, x xs p x = false := by
induction xs with
| nil => simp_all
| cons x xs ih =>
simp only [findIdx?_cons]
split <;> simp_all [cond_eq_if]
theorem findIdx?_isSome {xs : List α} {p : α Bool} :
(xs.findIdx? p).isSome = xs.any p := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons]
split <;> simp_all
theorem findIdx?_isNone {xs : List α} {p : α Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons]
split <;> simp_all
theorem findIdx?_eq_some_iff_findIdx_eq {xs : List α} {p : α Bool} {i : Nat} :
xs.findIdx? p = some i i < xs.length xs.findIdx p = i := by
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp only [findIdx?_cons, findIdx_cons]
split
· simp_all [cond_eq_if]
rintro rfl
exact zero_lt_succ xs.length
· simp_all [cond_eq_if, and_assoc]
constructor
· rintro a, lt, rfl, rfl
simp_all [Nat.succ_lt_succ_iff]
· rintro h, rfl
exact _, by simp_all [Nat.succ_lt_succ_iff], rfl, rfl
theorem findIdx?_eq_some_of_exists {xs : List α} {p : α Bool} (h : x, x xs p x) :
xs.findIdx? p = some (xs.findIdx p) := by
rw [findIdx?_eq_some_iff_findIdx_eq]
exact findIdx_lt_length_of_exists h, rfl
theorem findIdx?_eq_none_iff_findIdx_eq {xs : List α} {p : α Bool} :
xs.findIdx? p = none xs.findIdx p = xs.length := by
simp
theorem findIdx?_eq_some_iff_getElem (xs : List α) (p : α Bool) :
xs.findIdx? p = some i
h : i < xs.length, p xs[i] j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji h)) := by
induction xs generalizing i with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons, Nat.zero_add, findIdx?_succ]
split
· simp only [Option.some.injEq, Bool.not_eq_true, length_cons]
cases i with
| zero => simp_all
| succ i =>
simp only [Bool.not_eq_true, zero_ne_add_one, getElem_cons_succ, false_iff, not_exists,
not_and, Classical.not_forall, Bool.not_eq_false]
intros
refine 0, zero_lt_succ i, _
· simp only [Option.map_eq_some', ih, Bool.not_eq_true, length_cons]
constructor
· rintro a, h, h₁, h₂, rfl
refine Nat.succ_lt_succ_iff.mpr h, by simpa, fun j hj => ?_
cases j with
| zero => simp_all
| succ j =>
apply h₂
simp_all [Nat.succ_lt_succ_iff]
· rintro h, h₁, h₂
cases i with
| zero => simp_all
| succ i =>
refine i, Nat.succ_lt_succ_iff.mp h, by simpa, fun j hj => ?_, rfl
simpa using h₂ (j + 1) (Nat.succ_lt_succ_iff.mpr hj)
theorem findIdx?_of_eq_some {xs : List α} {p : α Bool} (w : xs.findIdx? p = some i) :
match xs[i]? with | some a => p a | none => false := by
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp_all only [findIdx?_cons, Nat.zero_add, findIdx?_succ]
split at w <;> cases i <;> simp_all [succ_inj']
theorem findIdx?_of_eq_none {xs : List α} {p : α Bool} (w : xs.findIdx? p = none) :
i : Nat, match xs[i]? with | some a => ¬ p a | none => true := by
intro i
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp_all only [Bool.not_eq_true, findIdx?_cons, Nat.zero_add, findIdx?_succ]
cases i with
| zero =>
split at w <;> simp_all
| succ i =>
simp only [getElem?_cons_succ]
apply ih
split at w <;> simp_all
@[simp] theorem findIdx?_map (f : β α) (l : List β) : findIdx? p (l.map f) = l.findIdx? (p f) := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, findIdx?]
split <;> simp_all
@[simp] theorem findIdx?_append :
(xs ++ ys : List α).findIdx? p =
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.length) := by
induction xs with simp
| cons _ _ _ => split <;> simp_all [Option.map_or', Option.map_map]; rfl
theorem findIdx?_join {l : List (List α)} {p : α Bool} :
l.join.findIdx? p =
(l.findIdx? (·.any p)).map
fun i => Nat.sum ((l.take i).map List.length) +
(l[i]?.map fun xs => xs.findIdx p).getD 0 := by
induction l with
| nil => simp
| cons xs l ih =>
simp only [join, findIdx?_append, map_take, map_cons, findIdx?, any_eq_true, Nat.zero_add,
findIdx?_succ]
split
· simp only [Option.map_some', take_zero, sum_nil, length_cons, zero_lt_succ,
getElem?_eq_getElem, getElem_cons_zero, Option.getD_some, Nat.zero_add]
rw [Option.or_of_isSome (by simpa [findIdx?_isSome])]
rw [findIdx?_eq_some_of_exists _]
· simp_all only [map_take, not_exists, not_and, Bool.not_eq_true, Option.map_map]
rw [Option.or_of_isNone (by simpa [findIdx?_isNone])]
congr 1
ext i
simp [Nat.add_comm, Nat.add_assoc]
@[simp] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by
cases n with
| zero => simp
| succ n =>
simp only [replicate, findIdx?_cons, Nat.zero_add, findIdx?_succ, zero_lt_succ, true_and]
split <;> simp_all
theorem findIdx?_eq_enum_findSome? {xs : List α} {p : α Bool} :
xs.findIdx? p = xs.enum.findSome? fun i, a => if p a then some i else none := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons, Nat.zero_add, findIdx?_succ, enum]
split
· simp_all
· simp_all only [enumFrom_cons, ite_false, Option.isNone_none, findSome?_cons_of_isNone, reduceCtorEq]
simp [Function.comp_def, map_fst_add_enum_eq_enumFrom, findSome?_map]
theorem Sublist.findIdx?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
(l₁.findIdx? p).isSome (l₂.findIdx? p).isSome := by
simp only [List.findIdx?_isSome, any_eq_true]
rintro w, m, q
exact w, h.mem m, q
theorem Sublist.findIdx?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
l₂.findIdx? p = none l₁.findIdx? p = none := by
simp only [findIdx?_eq_none_iff]
exact fun w x m => w x (h.mem m)
theorem IsPrefix.findIdx?_eq_some {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.findIdx? p l₁ = some i List.findIdx? p l₂ = some i := by
rw [IsPrefix] at h
obtain t, rfl := h
intro h
simp [findIdx?_append, h]
theorem IsPrefix.findIdx?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.findIdx? p l₂ = none List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
theorem IsSuffix.findIdx?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+ l₂) :
List.findIdx? p l₂ = none List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
theorem IsInfix.findIdx?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+: l₂) :
List.findIdx? p l₂ = none List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
/-! ### indexOf -/
theorem indexOf_cons [BEq α] :
(x :: xs : List α).indexOf y = bif x == y then 0 else xs.indexOf y + 1 := by
dsimp [indexOf]
simp [findIdx_cons]
end List

View File

@@ -193,17 +193,6 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
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 -/
@@ -306,24 +295,6 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
· rw [IH] <;> simp_all
· simp
/-- Tail-recursive version of `eraseP`. -/
@[inline] def erasePTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
@[specialize] go : List α Array α List α
| [], _ => l
| a :: l, acc => bif p a then acc.toListAppend l else go l (acc.push a)
@[csimp] theorem eraseP_eq_erasePTR : @eraseP = @erasePTR := by
funext α p l; simp [erasePTR]
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
· intro h; rw [go _ xs]; {simp}; simp [h]
exact (go #[] _ rfl).symm
/-! ### eraseIdx -/
/-- Tail recursive version of `List.eraseIdx`. -/
@@ -377,26 +348,6 @@ def unzipTR (l : List (α × β)) : List α × List β :=
/-! ## 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`. -/

File diff suppressed because it is too large Load Diff

View File

@@ -1,153 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Lemmas
/-!
# Lemmas about `List.minimum?` and `List.maximum?.
-/
namespace List
open Nat
/-! ## Minima and maxima -/
/-! ### minimum? -/
@[simp] theorem minimum?_nil [Min α] : ([] : List α).minimum? = none := rfl
-- We don't put `@[simp]` on `minimum?_cons`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem minimum?_cons [Min α] {xs : List α} : (x :: xs).minimum? = foldl min x xs := rfl
@[simp] theorem minimum?_eq_none_iff {xs : List α} [Min α] : xs.minimum? = none xs = [] := by
cases xs <;> simp [minimum?]
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 [minimum?_cons, Option.some.injEq, List.mem_cons]
intro eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons y xs ind =>
simp at eq
have p := ind _ eq
cases p with
| inl p =>
cases min_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem le_minimum?_iff [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c) :
{xs : List α} xs.minimum? = some a x, x a b, b xs x b
| nil => by simp
| cons x xs => by
rw [minimum?]
intro eq y
simp only [Option.some.injEq] at eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons z xs ih =>
simp at eq
simp [ih _ eq, le_min_iff, and_assoc]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
-- and `le_min_iff`.
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.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_minimum?_iff le_min_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
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, minimum?_cons]
@[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]
/-! ### maximum? -/
@[simp] theorem maximum?_nil [Max α] : ([] : List α).maximum? = none := rfl
-- We don't put `@[simp]` on `maximum?_cons`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem maximum?_cons [Max α] {xs : List α} : (x :: xs).maximum? = foldl max x xs := rfl
@[simp] theorem maximum?_eq_none_iff {xs : List α} [Max α] : xs.maximum? = none xs = [] := by
cases xs <;> simp [maximum?]
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 [maximum?]; rintro
induction xs generalizing x with simp at *
| cons y xs ih =>
rcases ih (max x y) with h | h <;> simp [h]
simp [ or_assoc, min_eq_or x y]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem maximum?_le_iff [Max α] [LE α]
(max_le_iff : a b c : α, max b c a b a c a) :
{xs : List α} xs.maximum? = some a x, a x b xs, b x
| nil => by simp
| cons x xs => by
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 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.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₂ _ (maximum?_mem max_eq_or (xs := x::xs) rfl))
((maximum?_le_iff max_le_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
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, maximum?_cons]
@[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

View File

@@ -1,69 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.TakeDrop
/-!
# Lemmas about `List.mapM` and `List.forM`.
-/
namespace List
open Nat
/-! ## Monadic operations -/
-- We may want to replace these `simp` attributes with explicit equational lemmas,
-- as we already have for all the non-monadic functions.
attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
-- Previously `mapM.loop`, `filterMapM.loop`, `forIn.loop`, `forIn'.loop`
-- had attribute `@[simp]`.
-- We don't currently provide simp lemmas,
-- as this is an internal implementation and they don't seem to be needed.
/-! ### mapM -/
/-- Alternate (non-tail-recursive) form of mapM for proofs. -/
def mapM' [Monad m] (f : α m β) : List α m (List β)
| [] => pure []
| a :: l => return ( f a) :: ( l.mapM' f)
@[simp] theorem mapM'_nil [Monad m] {f : α m β} : mapM' f [] = pure [] := rfl
@[simp] theorem mapM'_cons [Monad m] {f : α m β} :
mapM' f (a :: l) = return (( f a) :: ( l.mapM' f)) :=
rfl
theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α m β) (l : List α) :
mapM' f l = mapM f l := by simp [go, mapM] where
go : l acc, mapM.loop f l acc = return acc.reverse ++ ( mapM' f l)
| [], acc => by simp [mapM.loop, mapM']
| a::l, acc => by simp [go l, mapM.loop, mapM']
@[simp] theorem mapM_nil [Monad m] (f : α m β) : [].mapM f = pure [] := rfl
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α m β) :
(a :: l).mapM f = (return ( f a) :: ( l.mapM f)) := by simp [ mapM'_eq_mapM, mapM']
@[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 [*]
/-! ### forM -/
-- We use `List.forM` as the simp normal form, rather that `ForM.forM`.
-- As such we need to replace `List.forM_nil` and `List.forM_cons`:
@[simp] theorem forM_nil' [Monad m] : ([] : List α).forM f = (pure .unit : m PUnit) := rfl
@[simp] theorem forM_cons' [Monad m] :
(a::as).forM f = (f a >>= fun _ => as.forM f : m PUnit) :=
List.forM_cons _ _ _
@[simp] theorem forM_append [Monad m] [LawfulMonad m] (l₁ l₂ : List α) (f : α m PUnit) :
(l₁ ++ l₂).forM f = (do l₁.forM f; l₂.forM f) := by
induction l₁ <;> simp [*]
end List

View File

@@ -1,11 +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.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

View File

@@ -1,133 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Count
import Init.Data.List.MinMax
import Init.Data.Nat.Lemmas
/-!
# Miscellaneous `List` lemmas, that require more `Nat` lemmas than are available in `Init.Data.List.Lemmas`.
In particular, `omega` is available here.
-/
open Nat
namespace List
/-! ### filter -/
theorem length_filter_lt_length_iff_exists (l) :
length (filter p l) < length l x l, ¬p x := by
simpa [length_eq_countP_add_countP p l, countP_eq_length_filter] using
countP_pos (fun x => ¬p x) (l := l)
/-! ### reverse -/
theorem getElem_eq_getElem_reverse {l : List α} {i} (h : i < l.length) :
l[i] = l.reverse[l.length - 1 - i]'(by simpa using Nat.sub_one_sub_lt_of_lt h) := by
rw [getElem_reverse]
congr
omega
/-! ### leftpad -/
/-- The length of the List returned by `List.leftpad n a l` is equal
to the larger of `n` and `l.length` -/
@[simp]
theorem leftpad_length (n : Nat) (a : α) (l : List α) :
(leftpad n a l).length = max n l.length := by
simp only [leftpad, length_append, length_replicate, Nat.sub_add_eq_max]
/-! ### eraseIdx -/
theorem mem_eraseIdx_iff_getElem {x : α} :
{l} {k}, x eraseIdx l k i h, i k l[i]'h = x
| [], _ => by
simp only [eraseIdx, not_mem_nil, false_iff]
rintro i, h, -
exact Nat.not_lt_zero _ h
| a::l, 0 => by simp [mem_iff_getElem, Nat.succ_lt_succ_iff]
| a::l, k+1 => by
rw [ Nat.or_exists_add_one]
simp [mem_eraseIdx_iff_getElem, @eq_comm _ a, succ_inj', Nat.succ_lt_succ_iff]
theorem mem_eraseIdx_iff_getElem? {x : α} {l} {k} : x eraseIdx l k i k, l[i]? = some x := by
simp only [mem_eraseIdx_iff_getElem, getElem_eq_iff, exists_and_left]
refine exists_congr fun i => and_congr_right' ?_
constructor
· rintro _, h; exact h
· rintro h;
obtain h', - := getElem?_eq_some.1 h
exact h', h
/-! ### minimum? -/
-- A specialization of `minimum?_eq_some_iff` to Nat.
theorem minimum?_eq_some_iff' {xs : List Nat} :
xs.minimum? = some a (a xs b xs, a b) :=
minimum?_eq_some_iff
(le_refl := Nat.le_refl)
(min_eq_or := fun _ _ => by omega)
(le_min_iff := fun _ _ _ => by omega)
-- This could be generalized,
-- but will first require further work on order typeclasses in the core repository.
theorem minimum?_cons' {a : Nat} {l : List Nat} :
(a :: l).minimum? = some (match l.minimum? with
| none => a
| some m => min a m) := by
rw [minimum?_eq_some_iff']
split <;> rename_i h m
· simp_all
· rw [minimum?_eq_some_iff'] at m
obtain m, le := m
rw [Nat.min_def]
constructor
· split
· exact mem_cons_self a l
· exact mem_cons_of_mem a m
· intro b m
cases List.mem_cons.1 m with
| inl => split <;> omega
| inr h =>
specialize le b h
split <;> omega
/-! ### maximum? -/
-- A specialization of `maximum?_eq_some_iff` to Nat.
theorem maximum?_eq_some_iff' {xs : List Nat} :
xs.maximum? = some a (a xs b xs, b a) :=
maximum?_eq_some_iff
(le_refl := Nat.le_refl)
(max_eq_or := fun _ _ => by omega)
(max_le_iff := fun _ _ _ => by omega)
-- This could be generalized,
-- but will first require further work on order typeclasses in the core repository.
theorem maximum?_cons' {a : Nat} {l : List Nat} :
(a :: l).maximum? = some (match l.maximum? with
| none => a
| some m => max a m) := by
rw [maximum?_eq_some_iff']
split <;> rename_i h m
· simp_all
· rw [maximum?_eq_some_iff'] at m
obtain m, le := m
rw [Nat.max_def]
constructor
· split
· exact mem_cons_of_mem a m
· exact mem_cons_self a l
· intro b m
cases List.mem_cons.1 m with
| inl => split <;> omega
| inr h =>
specialize le b h
split <;> omega
end List

View File

@@ -1,73 +0,0 @@
/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, James Gallicchio
-/
prelude
import Init.Data.Fin.Lemmas
import Init.Data.List.Nat.TakeDrop
import Init.Data.List.Pairwise
/-!
# Lemmas about `List.Pairwise`
-/
namespace List
/-- Given a list `is` of monotonically increasing indices into `l`, getting each index
produces a sublist of `l`. -/
theorem map_getElem_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (· < ·)) :
is.map (l[·]) <+ l := by
suffices n l', l' = l.drop n ( i is, n i) map (l[·]) is <+ l'
from this 0 l (by simp) (by simp)
rintro n l' rfl his
induction is generalizing n with
| nil => simp
| cons hd tl IH =>
simp only [Fin.getElem_fin, map_cons]
have := IH h.of_cons (hd+1) (pairwise_cons.mp h).1
specialize his hd (.head _)
have := (drop_eq_getElem_cons ..).symm this.cons₂ (get l hd)
have := Sublist.append (nil_sublist (take hd l |>.drop n)) this
rwa [nil_append, (drop_append_of_le_length ?_), take_append_drop] at this
simp [Nat.min_eq_left (Nat.le_of_lt hd.isLt), his]
@[deprecated map_getElem_sublist (since := "2024-07-30")]
theorem map_get_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (·.val < ·.val)) :
is.map (get l) <+ l := by
simpa using map_getElem_sublist h
/-- Given a sublist `l' <+ l`, there exists an increasing list of indices `is` such that
`l' = is.map fun i => l[i]`. -/
theorem sublist_eq_map_getElem {l l' : List α} (h : l' <+ l) : is : List (Fin l.length),
l' = is.map (l[·]) is.Pairwise (· < ·) := by
induction h with
| slnil => exact [], by simp
| cons _ _ IH =>
let is, IH := IH
refine is.map (·.succ), ?_
simpa [Function.comp_def, pairwise_map]
| cons₂ _ _ IH =>
rcases IH with is,IH
refine 0, by simp [Nat.zero_lt_succ] :: is.map (·.succ), ?_
simp [Function.comp_def, pairwise_map, IH, get_eq_getElem]
@[deprecated sublist_eq_map_getElem (since := "2024-07-30")]
theorem sublist_eq_map_get (h : l' <+ l) : is : List (Fin l.length),
l' = map (get l) is is.Pairwise (· < ·) := by
simpa using sublist_eq_map_getElem h
theorem pairwise_iff_getElem : Pairwise R l
(i j : Nat) (_hi : i < l.length) (_hj : j < l.length) (_hij : i < j), R l[i] l[j] := by
rw [pairwise_iff_forall_sublist]
constructor <;> intro h
· intros i j hi hj h'
apply h
simpa [h'] using map_getElem_sublist (is := [i, hi, j, hj])
· intros a b h'
have is, h', hij := sublist_eq_map_getElem h'
rcases is with | a', | b', <;> simp at h'
rcases h' with rfl, rfl
apply h; simpa using hij
end List

View File

@@ -1,638 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Nat.TakeDrop
import Init.Data.List.Range
import Init.Data.List.Pairwise
import Init.Data.List.Find
/-!
# Lemmas about `List.range` and `List.enum`
-/
namespace List
open Nat
/-! ## Ranges and enumeration -/
/-! ### range' -/
theorem range'_succ (s n step) : range' s (n + 1) step = s :: range' (s + step) n step := by
simp [range', Nat.add_succ, Nat.mul_succ]
@[simp] theorem length_range' (s step) : n : Nat, length (range' s n step) = n
| 0 => rfl
| _ + 1 => congrArg succ (length_range' _ _ _)
@[simp] theorem range'_eq_nil : range' s n step = [] n = 0 := by
rw [ length_eq_zero, length_range']
theorem range'_ne_nil (s n : Nat) : range' s n [] n 0 := by
cases n <;> simp
@[simp] theorem range'_zero : range' s 0 = [] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
@[simp] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by
constructor
· intro h
have h' := congrArg List.length h
simp at h'
subst h'
cases n with
| zero => simp
| succ n =>
simp only [range'_succ] at h
simp_all
· rintro rfl, rfl | rfl <;> simp
theorem mem_range' : {n}, m range' s n step i < n, m = s + step * i
| 0 => by simp [range', Nat.not_lt_zero]
| n + 1 => by
have h (i) : i n i = 0 j, i = succ j j < n := by
cases i <;> simp [Nat.succ_le, Nat.succ_inj']
simp [range', mem_range', Nat.lt_succ, h]; simp only [ exists_and_right, and_assoc]
rw [exists_comm]; simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
@[simp] theorem mem_range'_1 : m range' s n s m m < s + n := by
simp [mem_range']; exact
fun i, h, e => e Nat.le_add_right .., Nat.add_lt_add_left h _,
fun h₁, h₂ => m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm
theorem head?_range' (n : Nat) : (range' s n).head? = if n = 0 then none else some s := by
induction n <;> simp_all [range'_succ, head?_append]
@[simp] theorem head_range' (n : Nat) (h) : (range' s n).head h = s := by
repeat simp_all [head?_range', head_eq_iff_head?_eq_some]
theorem getLast?_range' (n : Nat) : (range' s n).getLast? = if n = 0 then none else some (s + n - 1) := by
induction n generalizing s with
| zero => simp
| succ n ih =>
rw [range'_succ, getLast?_cons, ih]
by_cases h : n = 0
· rw [if_pos h]
simp [h]
· rw [if_neg h]
simp
omega
@[simp] theorem getLast_range' (n : Nat) (h) : (range' s n).getLast h = s + n - 1 := by
cases n with
| zero => simp at h
| succ n => simp [getLast?_range', getLast_eq_iff_getLast_eq_some]
theorem pairwise_lt_range' s n (step := 1) (pos : 0 < step := by simp) :
Pairwise (· < ·) (range' s n step) :=
match s, n, step, pos with
| _, 0, _, _ => Pairwise.nil
| s, n + 1, step, pos => by
simp only [range'_succ, pairwise_cons]
constructor
· intros n m
rw [mem_range'] at m
omega
· exact pairwise_lt_range' (s + step) n step pos
theorem pairwise_le_range' s n (step := 1) :
Pairwise (· ·) (range' s n step) :=
match s, n, step with
| _, 0, _ => Pairwise.nil
| s, n + 1, step => by
simp only [range'_succ, pairwise_cons]
constructor
· intros n m
rw [mem_range'] at m
omega
· exact pairwise_le_range' (s + step) n step
theorem nodup_range' (s n : Nat) (step := 1) (h : 0 < step := by simp) : Nodup (range' s n step) :=
(pairwise_lt_range' s n step h).imp Nat.ne_of_lt
@[simp]
theorem map_add_range' (a) : s n step, map (a + ·) (range' s n step) = range' (a + s) n step
| _, 0, _ => rfl
| s, n + 1, step => by simp [range', map_add_range' _ (s + step) n step, Nat.add_assoc]
theorem map_sub_range' (a s n : Nat) (h : a s) :
map (· - a) (range' s n step) = range' (s - a) n step := by
conv => lhs; rw [ Nat.add_sub_cancel' h]
rw [ map_add_range', map_map, (?_ : __ = _), map_id]
funext x; apply Nat.add_sub_cancel_left
theorem range'_append : s m n step : Nat,
range' s m step ++ range' (s + step * m) n step = range' s (n + m) step
| s, 0, n, step => rfl
| s, m + 1, n, step => by
simpa [range', Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
using range'_append (s + step) m n step
@[simp] theorem range'_append_1 (s m n : Nat) :
range' s m ++ range' (s + m) n = range' s (n + m) := by simpa using range'_append s m n 1
theorem range'_sublist_right {s m n : Nat} : range' s m step <+ range' s n step m n :=
fun h => by simpa only [length_range'] using h.length_le,
fun h => by rw [ Nat.sub_add_cancel h, range'_append]; apply sublist_append_left
theorem range'_subset_right {s m n : Nat} (step0 : 0 < step) :
range' s m step range' s n step m n := by
refine fun h => Nat.le_of_not_lt fun hn => ?_, fun h => (range'_sublist_right.2 h).subset
have i, h', e := mem_range'.1 <| h <| mem_range'.2 _, hn, rfl
exact Nat.ne_of_gt h' (Nat.eq_of_mul_eq_mul_left step0 (Nat.add_left_cancel e))
theorem range'_subset_right_1 {s m n : Nat} : range' s m range' s n m n :=
range'_subset_right (by decide)
theorem getElem?_range' (s step) :
{m n : Nat}, m < n (range' s n step)[m]? = some (s + step * m)
| 0, n + 1, _ => by simp [range'_succ]
| m + 1, n + 1, h => by
simp only [range'_succ, getElem?_cons_succ]
exact (getElem?_range' (s + step) step (Nat.lt_of_add_lt_add_right h)).trans <| by
simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
@[simp] theorem getElem_range' {n m step} (i) (H : i < (range' n m step).length) :
(range' n m step)[i] = n + step * i :=
(getElem?_eq_some.1 <| getElem?_range' n step (by simpa using H)).2
theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ [s + step * n] := by
rw [Nat.add_comm n 1]; exact (range'_append s n 1 step).symm
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ [s + n] := by
simp [range'_concat]
theorem range'_eq_cons_iff : range' s n = a :: xs s = a 0 < n xs = range' (a + 1) (n - 1) := by
induction n generalizing s with
| zero => simp
| succ n ih =>
simp only [range'_succ]
simp only [cons.injEq, and_congr_right_iff]
rintro rfl
simp [eq_comm]
@[simp] theorem range'_eq_singleton {s n a : Nat} : range' s n = [a] s = a n = 1 := by
rw [range'_eq_cons_iff]
simp only [nil_eq, range'_eq_nil, and_congr_right_iff]
rintro rfl
omega
theorem range'_eq_append_iff : range' s n = xs ++ ys k, k n xs = range' s k ys = range' (s + k) (n - k) := by
induction n generalizing s xs ys with
| zero => simp
| succ n ih =>
simp only [range'_succ]
rw [cons_eq_append]
constructor
· rintro (rfl, rfl | a, rfl, h)
· exact 0, by simp [range'_succ]
· simp only [ih] at h
obtain k, h, rfl, rfl := h
refine k + 1, ?_
simp_all [range'_succ]
omega
· rintro k, h, rfl, rfl
cases k with
| zero => simp [range'_succ]
| succ k =>
simp only [range'_succ, reduceCtorEq, false_and, cons.injEq, true_and, ih, range'_inj, exists_eq_left', or_true, and_true, false_or]
refine k, ?_
simp_all
omega
@[simp] theorem find?_range'_eq_some (s n : Nat) (i : Nat) (p : Nat Bool) :
(range' s n).find? p = some i p i i range' s n j, s j j < i !p j := by
rw [find?_eq_some]
simp only [Bool.not_eq_true', exists_and_right, mem_range'_1, and_congr_right_iff]
simp only [range'_eq_append_iff, eq_comm (a := i :: _), range'_eq_cons_iff]
intro h
constructor
· rintro as, x, k, h₁, rfl, rfl, h₂, rfl, h₃
constructor
· omega
· simpa using h₃
· rintro h₁, h₂, h₃
refine range' s (i - s), range' (i + 1) (n - (i - s) - 1), i - s, ?_ , ?_
· simp; omega
· simp only [mem_range'_1, and_imp]
intro a a₁ a₂
exact h₃ a a₁ (by omega)
@[simp] theorem find?_range'_eq_none (s n : Nat) (p : Nat Bool) :
(range' s n).find? p = none i, s i i < s + n !p i := by
rw [find?_eq_none]
simp
/-! ### range -/
theorem range_loop_range' : s n : Nat, range.loop s (range' s n) = range' 0 (n + s)
| 0, n => rfl
| s + 1, n => by rw [ Nat.add_assoc, Nat.add_right_comm n s 1]; exact range_loop_range' s (n + 1)
theorem range_eq_range' (n : Nat) : range n = range' 0 n :=
(range_loop_range' n 0).trans <| by rw [Nat.zero_add]
theorem range_succ_eq_map (n : Nat) : range (n + 1) = 0 :: map succ (range n) := by
rw [range_eq_range', range_eq_range', range', Nat.add_comm, map_add_range']
congr; exact funext (Nat.add_comm 1)
theorem reverse_range' : s n : Nat, reverse (range' s n) = map (s + n - 1 - ·) (range n)
| s, 0 => rfl
| s, n + 1 => by
rw [range'_1_concat, reverse_append, range_succ_eq_map,
show s + (n + 1) - 1 = s + n from rfl, map, map_map]
simp [reverse_range', Nat.sub_right_comm, Nat.sub_sub]
theorem range'_eq_map_range (s n : Nat) : range' s n = map (s + ·) (range n) := by
rw [range_eq_range', map_add_range']; rfl
@[simp] theorem length_range (n : Nat) : length (range n) = n := by
simp only [range_eq_range', length_range']
@[simp] theorem range_eq_nil {n : Nat} : range n = [] n = 0 := by
rw [ length_eq_zero, length_range]
theorem range_ne_nil (n : Nat) : range n [] n 0 := by
cases n <;> simp
@[simp]
theorem range_sublist {m n : Nat} : range m <+ range n m n := by
simp only [range_eq_range', range'_sublist_right]
@[simp]
theorem range_subset {m n : Nat} : range m range n m n := by
simp only [range_eq_range', range'_subset_right, lt_succ_self]
@[simp]
theorem mem_range {m n : Nat} : m range n m < n := by
simp only [range_eq_range', mem_range'_1, Nat.zero_le, true_and, Nat.zero_add]
theorem not_mem_range_self {n : Nat} : n range n := by simp
theorem self_mem_range_succ (n : Nat) : n range (n + 1) := by simp
theorem pairwise_lt_range (n : Nat) : Pairwise (· < ·) (range n) := by
simp (config := {decide := true}) only [range_eq_range', pairwise_lt_range']
theorem pairwise_le_range (n : Nat) : Pairwise (· ·) (range n) :=
Pairwise.imp Nat.le_of_lt (pairwise_lt_range _)
theorem getElem?_range {m n : Nat} (h : m < n) : (range n)[m]? = some m := by
simp [range_eq_range', getElem?_range' _ _ h]
@[simp] theorem getElem_range {n : Nat} (m) (h : m < (range n).length) : (range n)[m] = m := by
simp [range_eq_range']
theorem range_succ (n : Nat) : range (succ n) = range n ++ [n] := by
simp only [range_eq_range', range'_1_concat, Nat.zero_add]
theorem range_add (a b : Nat) : range (a + b) = range a ++ (range b).map (a + ·) := by
rw [ range'_eq_map_range]
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 0 a b).symm
theorem head?_range (n : Nat) : (range n).head? = if n = 0 then none else some 0 := by
induction n with
| zero => simp
| succ n ih =>
simp only [range_succ, head?_append, ih]
split <;> simp_all
@[simp] theorem head_range (n : Nat) (h) : (range n).head h = 0 := by
cases n with
| zero => simp at h
| succ n => simp [head?_range, head_eq_iff_head?_eq_some]
theorem getLast?_range (n : Nat) : (range n).getLast? = if n = 0 then none else some (n - 1) := by
induction n with
| zero => simp
| succ n ih =>
simp only [range_succ, getLast?_append, ih]
split <;> simp_all
@[simp] theorem getLast_range (n : Nat) (h) : (range n).getLast h = n - 1 := by
cases n with
| zero => simp at h
| succ n => simp [getLast?_range, getLast_eq_iff_getLast_eq_some]
theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
apply List.ext_getElem
· simp
· simp (config := { contextual := true }) [ getElem_take, Nat.lt_min]
theorem nodup_range (n : Nat) : Nodup (range n) := by
simp (config := {decide := true}) only [range_eq_range', nodup_range']
@[simp] theorem find?_range_eq_some (n : Nat) (i : Nat) (p : Nat Bool) :
(range n).find? p = some i p i i range n j, j < i !p j := by
simp [range_eq_range']
@[simp] theorem find?_range_eq_none (n : Nat) (p : Nat Bool) :
(range n).find? p = none i, i < n !p i := by
simp [range_eq_range']
/-! ### iota -/
theorem iota_eq_reverse_range' : n : Nat, iota n = reverse (range' 1 n)
| 0 => rfl
| n + 1 => by simp [iota, range'_concat, iota_eq_reverse_range' n, reverse_append, Nat.add_comm]
@[simp] theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
@[simp] theorem iota_eq_nil (n : Nat) : iota n = [] n = 0 := by
cases n <;> simp
theorem iota_ne_nil (n : Nat) : iota n [] n 0 := by
cases n <;> simp
@[simp]
theorem mem_iota {m n : Nat} : m iota n 0 < m m n := by
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
omega
@[simp] theorem iota_inj : iota n = iota n' n = n' := by
constructor
· intro h
have h' := congrArg List.length h
simp at h'
exact h'
· rintro rfl
simp
theorem iota_eq_cons_iff : iota n = a :: xs n = a 0 < n xs = iota (n - 1) := by
simp [iota_eq_reverse_range']
simp [range'_eq_append_iff, reverse_eq_iff]
constructor
· rintro k, h, rfl, h'
rw [eq_comm, range'_eq_singleton] at h'
simp only [reverse_inj, range'_inj, or_true, and_true]
omega
· rintro rfl, h, rfl
refine n - 1, by simp, rfl, ?_
rw [eq_comm, range'_eq_singleton]
omega
theorem iota_eq_append_iff : iota n = xs ++ ys k, k n xs = (range' (k + 1) (n - k)).reverse ys = iota k := by
simp only [iota_eq_reverse_range']
rw [reverse_eq_append]
rw [range'_eq_append_iff]
simp only [reverse_eq_iff]
constructor
· rintro k, h, rfl, rfl
simp; omega
· rintro k, h, rfl, rfl
exact k, by simp; omega
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range' 1 n
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
(pairwise_gt_iota n).imp Nat.ne_of_gt
@[simp] theorem head?_iota (n : Nat) : (iota n).head? = if n = 0 then none else some n := by
cases n <;> simp
@[simp] theorem head_iota (n : Nat) (h) : (iota n).head h = n := by
cases n with
| zero => simp at h
| succ n => simp
@[simp] theorem reverse_iota : reverse (iota n) = range' 1 n := by
induction n with
| zero => simp
| succ n ih =>
rw [iota_succ, reverse_cons, ih, range'_1_concat, Nat.add_comm]
@[simp] theorem getLast?_iota (n : Nat) : (iota n).getLast? = if n = 0 then none else some 1 := by
rw [getLast?_eq_head?_reverse]
simp [head?_range']
@[simp] theorem getLast_iota (n : Nat) (h) : (iota n).getLast h = 1 := by
rw [getLast_eq_head_reverse]
simp
@[simp] theorem find?_iota_eq_none (n : Nat) (p : Nat Bool) :
(iota n).find? p = none i, 0 < i i n !p i := by
rw [find?_eq_none]
simp
@[simp] theorem find?_iota_eq_some (n : Nat) (i : Nat) (p : Nat Bool) :
(iota n).find? p = some i p i i iota n j, i < j j n !p j := by
rw [find?_eq_some]
simp only [iota_eq_reverse_range', reverse_eq_append, reverse_cons, append_assoc,
singleton_append, Bool.not_eq_true', exists_and_right, mem_reverse, mem_range'_1,
and_congr_right_iff]
intro h
constructor
· rintro as, xs, h, h'
constructor
· replace h : i range' 1 n := by
rw [h]
exact mem_append_cons_self
simpa using h
· rw [range'_eq_append_iff] at h
simp [reverse_eq_iff] at h
obtain k, h₁, rfl, h₂ := h
rw [eq_comm, range'_eq_cons_iff, reverse_eq_iff] at h₂
obtain rfl, -, rfl := h₂
intro j j₁ j₂
apply h'
simp; omega
· rintro i₁, i₂, h
refine (range' (i+1) (n-i)).reverse, (range' 1 (i-1)).reverse, ?_, ?_
· simp [ range'_succ]
rw [range'_eq_append_iff]
refine i-1, ?_
constructor
· omega
· simp
omega
· simp
intros a a₁ a₂
apply h
· omega
· omega
/-! ### enumFrom -/
@[simp]
theorem enumFrom_singleton (x : α) (n : Nat) : enumFrom n [x] = [(n, x)] :=
rfl
@[simp] theorem head?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).head? = l.head?.map fun a => (n, a) := by
simp [head?_eq_getElem?]
@[simp] theorem getLast?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).getLast? = l.getLast?.map fun a => (n + l.length - 1, a) := by
simp [getLast?_eq_getElem?]
cases l <;> simp; omega
theorem mk_add_mem_enumFrom_iff_getElem? {n i : Nat} {x : α} {l : List α} :
(n + i, x) enumFrom n l l[i]? = some x := by
simp [mem_iff_get?]
theorem mk_mem_enumFrom_iff_le_and_getElem?_sub {n i : Nat} {x : α} {l : List α} :
(i, x) enumFrom n l n i l[i - n]? = x := by
if h : n i then
rcases Nat.exists_eq_add_of_le h with i, rfl
simp [mk_add_mem_enumFrom_iff_getElem?, Nat.add_sub_cancel_left]
else
have : k, n + k i := by rintro k rfl; simp at h
simp [h, mem_iff_get?, this]
theorem le_fst_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
n x.1 :=
(mk_mem_enumFrom_iff_le_and_getElem?_sub.1 h).1
theorem fst_lt_add_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.1 < n + length l := by
rcases mem_iff_get.1 h with i, rfl
simpa using i.isLt
theorem map_enumFrom (f : α β) (n : Nat) (l : List α) :
map (Prod.map id f) (enumFrom n l) = enumFrom n (map f l) := by
induction l generalizing n <;> simp_all
@[simp]
theorem enumFrom_map_fst (n) :
(l : List α), map Prod.fst (enumFrom n l) = range' n l.length
| [] => rfl
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
@[simp]
theorem enumFrom_map_snd : (n) (l : List α), map Prod.snd (enumFrom n l) = l
| _, [] => rfl
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
theorem snd_mem_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) : x.2 l :=
enumFrom_map_snd n l mem_map_of_mem _ h
theorem snd_eq_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.2 = l[x.1 - n]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) := by
induction l generalizing n with
| nil => cases h
| cons hd tl ih =>
cases h with
| head h => simp
| tail h m =>
specialize ih m
have : x.1 - n = x.1 - (n + 1) + 1 := by
have := le_fst_of_mem_enumFrom m
omega
simp [this, ih]
theorem mem_enumFrom {x : α} {i j : Nat} {xs : List α} (h : (i, x) xs.enumFrom j) :
j i i < j + xs.length
x = xs[i - j]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) :=
le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_eq_of_mem_enumFrom h
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
rw [enumFrom_cons, Nat.add_comm, map_fst_add_enumFrom_eq_enumFrom]
theorem enumFrom_map (n : Nat) (l : List α) (f : α β) :
enumFrom n (l.map f) = (enumFrom n l).map (Prod.map id f) := by
induction l with
| nil => rfl
| cons hd tl IH =>
rw [map_cons, enumFrom_cons', enumFrom_cons', map_cons, map_map, IH, map_map]
rfl
theorem enumFrom_append (xs ys : List α) (n : Nat) :
enumFrom n (xs ++ ys) = enumFrom n xs ++ enumFrom (n + xs.length) ys := by
induction xs generalizing ys n with
| nil => simp
| cons x xs IH =>
rw [cons_append, enumFrom_cons, IH, cons_append, enumFrom_cons, length, Nat.add_right_comm,
Nat.add_assoc]
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
@[simp]
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
(l.enumFrom n).unzip = (range' n l.length, l) := by
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
/-! ### enum -/
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
theorem enum_cons' (x : α) (xs : List α) :
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
enumFrom_cons' _ _ _
@[simp]
theorem enum_eq_nil {l : List α} : List.enum l = [] l = [] := enumFrom_eq_nil
@[simp] theorem enum_singleton (x : α) : enum [x] = [(0, x)] := rfl
@[simp] theorem enum_length : (enum l).length = l.length :=
enumFrom_length
@[simp]
theorem getElem?_enum (l : List α) (n : Nat) : (enum l)[n]? = l[n]?.map fun a => (n, a) := by
rw [enum, getElem?_enumFrom, Nat.zero_add]
@[simp]
theorem getElem_enum (l : List α) (i : Nat) (h : i < l.enum.length) :
l.enum[i] = (i, l[i]'(by simpa [enum_length] using h)) := by
simp [enum]
@[simp] theorem head?_enum (l : List α) :
l.enum.head? = l.head?.map fun a => (0, a) := by
simp [head?_eq_getElem?]
@[simp] theorem getLast?_enum (l : List α) :
l.enum.getLast? = l.getLast?.map fun a => (l.length - 1, a) := by
simp [getLast?_eq_getElem?]
theorem mk_mem_enum_iff_getElem? {i : Nat} {x : α} {l : List α} : (i, x) enum l l[i]? = x := by
simp [enum, mk_mem_enumFrom_iff_le_and_getElem?_sub]
theorem mem_enum_iff_getElem? {x : Nat × α} {l : List α} : x enum l l[x.1]? = some x.2 :=
mk_mem_enum_iff_getElem?
theorem fst_lt_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.1 < length l := by
simpa using fst_lt_add_of_mem_enumFrom h
theorem snd_mem_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.2 l :=
snd_mem_of_mem_enumFrom h
theorem snd_eq_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) :
x.2 = l[x.1]'(fst_lt_of_mem_enum h) :=
snd_eq_of_mem_enumFrom h
theorem mem_enum {x : α} {i : Nat} {xs : List α} (h : (i, x) xs.enum) :
i < xs.length x = xs[i]'(fst_lt_of_mem_enum h) :=
by simpa using mem_enumFrom h
theorem map_enum (f : α β) (l : List α) : map (Prod.map id f) (enum l) = enum (map f l) :=
map_enumFrom f 0 l
@[simp] theorem enum_map_fst (l : List α) : map Prod.fst (enum l) = range l.length := by
simp only [enum, enumFrom_map_fst, range_eq_range']
@[simp]
theorem enum_map_snd (l : List α) : map Prod.snd (enum l) = l :=
enumFrom_map_snd _ _
theorem enum_map (l : List α) (f : α β) : (l.map f).enum = l.enum.map (Prod.map id f) :=
enumFrom_map _ _ _
theorem enum_append (xs ys : List α) : enum (xs ++ ys) = enum xs ++ enumFrom xs.length ys := by
simp [enum, enumFrom_append]
theorem enum_eq_zip_range (l : List α) : l.enum = (range l.length).zip l :=
zip_of_prod (enum_map_fst _) (enum_map_snd _)
@[simp]
theorem unzip_enum_eq_prod (l : List α) : l.enum.unzip = (range l.length, l) := by
simp only [enum_eq_zip_range, unzip_zip, length_range]
end List

View File

@@ -1,174 +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.List.Sublist
import Init.Data.List.Nat.Basic
import Init.Data.List.Nat.TakeDrop
import Init.Data.Nat.Lemmas
/-!
# Further lemmas about `List.IsSuffix` / `List.IsPrefix` / `List.IsInfix`.
These are in a separate file from most of the lemmas about `List.IsSuffix`
as they required importing more lemmas about natural numbers, and use `omega`.
-/
namespace List
theorem IsSuffix.getElem {x y : List α} (h : x <:+ y) {n} (hn : n < x.length) :
x[n] = y[y.length - x.length + n]'(by have := h.length_le; omega) := by
rw [getElem_eq_getElem_reverse, h.reverse.getElem, getElem_reverse]
congr
have := h.length_le
omega
theorem isSuffix_iff : l₁ <:+ l₂
l₁.length l₂.length i (h : i < l₁.length), l₂[i + l₂.length - l₁.length]? = some l₁[i] := by
suffices l₁.length l₂.length l₁ <:+ l₂
l₁.length l₂.length i (h : i < l₁.length), l₂[i + l₂.length - l₁.length]? = some l₁[i] by
constructor
· intro h
exact this.mp h.length_le, h
· intro h
exact (this.mpr h).2
simp only [and_congr_right_iff]
intro le
rw [ reverse_prefix, isPrefix_iff]
simp only [length_reverse]
constructor
· intro w i h
specialize w (l₁.length - 1 - i) (by omega)
rw [getElem?_reverse (by omega)] at w
have p : l₂.length - 1 - (l₁.length - 1 - i) = i + l₂.length - l₁.length := by omega
rw [p] at w
rw [w, getElem_reverse]
congr
omega
· intro w i h
rw [getElem?_reverse]
specialize w (l₁.length - 1 - i) (by omega)
have p : l₁.length - 1 - i + l₂.length - l₁.length = l₂.length - 1 - i := by omega
rw [p] at w
rw [w, getElem_reverse]
exact Nat.lt_of_lt_of_le h le
theorem isInfix_iff : l₁ <:+: l₂
k, l₁.length + k l₂.length i (h : i < l₁.length), l₂[i + k]? = some l₁[i] := by
constructor
· intro h
obtain t, p, s := infix_iff_suffix_prefix.mp h
refine t.length - l₁.length, by have := p.length_le; have := s.length_le; omega, ?_
rw [isSuffix_iff] at p
obtain p', p := p
rw [isPrefix_iff] at s
intro i h
rw [s _ (by omega)]
specialize p i (by omega)
rw [Nat.add_sub_assoc (by omega)] at p
rw [ getElem?_eq_getElem, p]
· rintro k, le, w
refine l₂.take k, l₂.drop (k + l₁.length), ?_
ext1 i
rw [getElem?_append]
split
· rw [getElem?_append]
split
· rw [getElem?_take]; simp_all; omega
· simp_all
have p : i = (i - k) + k := by omega
rw [p, w _ (by omega), getElem?_eq_getElem]
· congr 2
omega
· omega
· rw [getElem?_drop]
congr
simp_all
omega
theorem suffix_iff_eq_append : l₁ <:+ l₂ take (length l₂ - length l₁) l₂ ++ l₁ = l₂ :=
by rintro r, rfl; simp only [length_append, Nat.add_sub_cancel_right, take_left], fun e =>
_, e
theorem prefix_take_iff {x y : List α} {n : Nat} : x <+: y.take n x <+: y x.length n := by
constructor
· intro h
constructor
· exact List.IsPrefix.trans h <| List.take_prefix n y
· replace h := h.length_le
rw [length_take, Nat.le_min] at h
exact h.left
· intro hp, hl
have hl' := hp.length_le
rw [List.prefix_iff_eq_take] at *
rw [hp, List.take_take]
simp [Nat.min_eq_left, hl, hl']
theorem suffix_iff_eq_drop : l₁ <:+ l₂ l₁ = drop (length l₂ - length l₁) l₂ :=
fun h => append_cancel_left <| (suffix_iff_eq_append.1 h).trans (take_append_drop _ _).symm,
fun e => e.symm drop_suffix _ _
theorem prefix_take_le_iff {L : List α} (hm : m < L.length) :
L.take m <+: L.take n m n := by
simp only [prefix_iff_eq_take, length_take]
induction m generalizing L n with
| zero => simp [Nat.min_eq_left, eq_self_iff_true, Nat.zero_le, take]
| succ m IH =>
cases L with
| nil => simp_all
| cons l ls =>
cases n with
| zero =>
simp
| succ n =>
simp only [length_cons, Nat.succ_eq_add_one, Nat.add_lt_add_iff_right] at hm
simp [ @IH n ls hm, Nat.min_eq_left, Nat.le_of_lt hm]
@[simp] theorem append_left_sublist_self (xs ys : List α) : xs ++ ys <+ ys xs = [] := by
constructor
· intro h
replace h := h.length_le
simp only [length_append] at h
have : xs.length = 0 := by omega
simp_all
· rintro rfl
simp
@[simp] theorem append_right_sublist_self (xs ys : List α) : xs ++ ys <+ xs ys = [] := by
constructor
· intro h
replace h := h.length_le
simp only [length_append] at h
have : ys.length = 0 := by omega
simp_all
· rintro rfl
simp
theorem append_sublist_of_sublist_left (xs ys zs : List α) (h : zs <+ xs) :
xs ++ ys <+ zs ys = [] xs = zs := by
constructor
· intro h'
have hl := h.length_le
have hl' := h'.length_le
simp only [length_append] at hl'
have : ys.length = 0 := by omega
simp_all only [Nat.add_zero, length_eq_zero, true_and, append_nil]
exact Sublist.eq_of_length_le h' hl
· rintro rfl, rfl
simp
theorem append_sublist_of_sublist_right (xs ys zs : List α) (h : zs <+ ys) :
xs ++ ys <+ zs xs = [] ys = zs := by
constructor
· intro h'
have hl := h.length_le
have hl' := h'.length_le
simp only [length_append] at hl'
have : xs.length = 0 := by omega
simp_all only [Nat.zero_add, length_eq_zero, true_and, append_nil]
exact Sublist.eq_of_length_le h' hl
· rintro rfl, rfl
simp
end List

View File

@@ -1,544 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Zip
import Init.Data.List.Sublist
import Init.Data.Nat.Lemmas
/-!
# Further lemmas about `List.take`, `List.drop`, `List.zip` and `List.zipWith`.
These are in a separate file from most of the list lemmas
as they required importing more lemmas about natural numbers, and use `omega`.
-/
namespace List
open Nat
/-! ### take -/
@[simp] theorem length_take : (i : Nat) (l : List α), length (take i l) = min i (length l)
| 0, l => by simp [Nat.zero_min]
| succ n, [] => by simp [Nat.min_zero]
| succ n, _ :: l => by simp [Nat.succ_min_succ, length_take]
theorem length_take_le (n) (l : List α) : length (take n l) n := by simp [Nat.min_le_left]
theorem length_take_le' (n) (l : List α) : length (take n l) l.length :=
by simp [Nat.min_le_right]
theorem length_take_of_le (h : n length l) : length (take n l) = n := by simp [Nat.min_eq_left h]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
theorem getElem_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
L[i] = (L.take j)[i]'(length_take .. Nat.lt_min.mpr hj, hi) :=
getElem_of_eq (take_append_drop j L).symm _ getElem_append ..
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
theorem getElem_take' (L : List α) {j i : Nat} {h : i < (L.take j).length} :
(L.take j)[i] =
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
rw [length_take, Nat.lt_min] at h; rw [getElem_take L _ h.1]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
@[deprecated getElem_take (since := "2024-06-12")]
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
get L i, hi = get (L.take j) i, length_take .. Nat.lt_min.mpr hj, hi := by
simp [getElem_take _ hi hj]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
@[deprecated getElem_take (since := "2024-06-12")]
theorem get_take' (L : List α) {j i} :
get (L.take j) i =
get L i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _) := by
simp [getElem_take']
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n m) :
(l.take n)[m]? = none :=
getElem?_eq_none <| Nat.le_trans (length_take_le _ _) h
@[deprecated getElem?_take_eq_none (since := "2024-06-12")]
theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n m) :
(l.take n).get? m = none := by
simp [getElem?_take_eq_none h]
theorem getElem?_take {l : List α} {n m : Nat} :
(l.take n)[m]? = if m < n then l[m]? else none := by
split
· next h => exact getElem?_take_of_lt h
· next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
@[deprecated getElem?_take (since := "2024-06-12")]
theorem get?_take_eq_if {l : List α} {n m : Nat} :
(l.take n).get? m = if m < n then l.get? m else none := by
simp [getElem?_take]
theorem head?_take {l : List α} {n : Nat} :
(l.take n).head? = if n = 0 then none else l.head? := by
simp [head?_eq_getElem?, getElem?_take]
split
· rw [if_neg (by omega)]
· rw [if_pos (by omega)]
theorem head_take {l : List α} {n : Nat} (h : l.take n []) :
(l.take n).head h = l.head (by simp_all) := by
apply Option.some_inj.1
rw [ head?_eq_head, head?_eq_head, head?_take, if_neg]
simp_all
theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none else l[n - 1]?.or l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_take, length_take]
split
· rw [if_neg (by omega)]
rw [Nat.min_def]
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [ getLast?_eq_getElem?, getElem?_eq_none (by omega)]
simp
· rw [if_pos]
omega
theorem getLast_take {l : List α} (h : l.take n []) :
(l.take n).getLast h = l[n - 1]?.getD (l.getLast (by simp_all)) := by
rw [getLast_eq_getElem, getElem_take']
simp [length_take, Nat.min_def]
simp at h
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [getElem?_eq_none (by omega), getLast_eq_getElem]
simp
theorem take_take : (n m) (l : List α), take n (take m l) = take (min n m) l
| n, 0, l => by rw [Nat.min_zero, take_zero, take_nil]
| 0, m, l => by rw [Nat.zero_min, take_zero, take_zero]
| succ n, succ m, nil => by simp only [take_nil]
| succ n, succ m, a :: l => by
simp only [take, succ_min_succ, take_take n m l]
theorem take_set_of_lt (a : α) {n m : Nat} (l : List α) (h : m < n) :
(l.set n a).take m = l.take m :=
List.ext_getElem? fun i => by
rw [getElem?_take, getElem?_take]
split
· next h' => rw [getElem?_set_ne (by omega)]
· rfl
@[simp] theorem take_replicate (a : α) : n m : Nat, take n (replicate m a) = replicate (min n m) a
| n, 0 => by simp [Nat.min_zero]
| 0, m => by simp [Nat.zero_min]
| succ n, succ m => by simp [replicate_succ, succ_min_succ, take_replicate]
@[simp] theorem drop_replicate (a : α) : n m : Nat, drop n (replicate m a) = replicate (m - n) a
| n, 0 => by simp
| 0, m => by simp
| succ n, succ m => by simp [replicate_succ, succ_sub_succ, drop_replicate]
/-- Taking the first `n` elements in `l₁ ++ l₂` is the same as appending the first `n` elements
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
theorem take_append_eq_append_take {l₁ l₂ : List α} {n : Nat} :
take n (l₁ ++ l₂) = take n l₁ ++ take (n - l₁.length) l₂ := by
induction l₁ generalizing n
· simp
· cases n
· simp [*]
· simp only [cons_append, take_succ_cons, length_cons, succ_eq_add_one, cons.injEq,
append_cancel_left_eq, true_and, *]
congr 1
omega
theorem take_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n l₁.length) :
(l₁ ++ l₂).take n = l₁.take n := by
simp [take_append_eq_append_take, Nat.sub_eq_zero_of_le h]
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
`i` elements of `l₂` to `l₁`. -/
theorem take_append {l₁ l₂ : List α} (i : Nat) :
take (l₁.length + i) (l₁ ++ l₂) = l₁ ++ take i l₂ := by
rw [take_append_eq_append_take, take_of_length_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
@[simp]
theorem take_eq_take :
{l : List α} {m n : Nat}, l.take m = l.take n min m l.length = min n l.length
| [], m, n => by simp [Nat.min_zero]
| _ :: xs, 0, 0 => by simp
| x :: xs, m + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, 0, n + 1 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, m + 1, n + 1 => by simp [succ_min_succ, take_eq_take]
theorem take_add (l : List α) (m n : Nat) : l.take (m + n) = l.take m ++ (l.drop m).take n := by
suffices take (m + n) (take m l ++ drop m l) = take m l ++ take n (drop m l) by
rw [take_append_drop] at this
assumption
rw [take_append_eq_append_take, take_of_length_le, append_right_inj]
· simp only [take_eq_take, length_take, length_drop]
omega
apply Nat.le_trans (m := m)
· apply length_take_le
· apply Nat.le_add_right
theorem dropLast_take {n : Nat} {l : List α} (h : n < l.length) :
(l.take n).dropLast = l.take (n - 1) := by
simp only [dropLast_eq_take, length_take, Nat.le_of_lt h, Nat.min_eq_left, take_take, sub_le]
theorem map_eq_append_split {f : α β} {l : List α} {s₁ s₂ : List β}
(h : map f l = s₁ ++ s₂) : l₁ l₂, l = l₁ ++ l₂ map f l₁ = s₁ map f l₂ = s₂ := by
have := h
rw [ take_append_drop (length s₁) l] at this
rw [map_append] at this
refine _, _, rfl, append_inj this ?_
rw [length_map, length_take, Nat.min_eq_left]
rw [ length_map l f, h, length_append]
apply Nat.le_add_right
theorem take_prefix_take_left (l : List α) {m n : Nat} (h : m n) : take m l <+: take n l := by
rw [isPrefix_iff]
intro i w
rw [getElem?_take_of_lt, getElem_take', getElem?_eq_getElem]
simp only [length_take] at w
exact Nat.lt_of_lt_of_le (Nat.lt_of_lt_of_le w (Nat.min_le_left _ _)) h
theorem take_sublist_take_left (l : List α) {m n : Nat} (h : m n) : take m l <+ take n l :=
(take_prefix_take_left l h).sublist
theorem take_subset_take_left (l : List α) {m n : Nat} (h : m n) : take m l take n l :=
(take_sublist_take_left l h).subset
/-! ### drop -/
theorem lt_length_drop (L : List α) {i j : Nat} (h : i + j < L.length) : j < (L.drop i).length := by
have A : i < L.length := Nat.lt_of_le_of_lt (Nat.le.intro rfl) h
rw [(take_append_drop i L).symm] at h
simpa only [Nat.le_of_lt A, Nat.min_eq_left, Nat.add_lt_add_iff_left, length_take,
length_append] using h
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
theorem getElem_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
L[i + j] = (L.drop i)[j]'(lt_length_drop L h) := by
have : i L.length := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_lt h)
rw [getElem_of_eq (take_append_drop i L).symm h, getElem_append_right'] <;>
simp [Nat.min_eq_left this, Nat.add_sub_cancel_left, Nat.le_add_right]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
@[deprecated getElem_drop (since := "2024-06-12")]
theorem get_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
get L i + j, h = get (L.drop i) j, lt_length_drop L h := by
simp [getElem_drop]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
theorem getElem_drop' (L : List α) {i : Nat} {j : Nat} {h : j < (L.drop i).length} :
(L.drop i)[j] = L[i + j]'(by
rw [Nat.add_comm]
exact Nat.add_lt_of_lt_sub (length_drop i L h)) := by
rw [getElem_drop]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
@[deprecated getElem_drop' (since := "2024-06-12")]
theorem get_drop' (L : List α) {i j} :
get (L.drop i) j = get L i + j, by
rw [Nat.add_comm]
exact Nat.add_lt_of_lt_sub (length_drop i L j.2) := by
simp [getElem_drop']
@[simp]
theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? := by
ext
simp only [getElem?_eq_some, getElem_drop', Option.mem_def]
constructor <;> intro h, ha
· exact _, ha
· refine ?_, ha
rw [length_drop]
rw [Nat.add_comm] at h
apply Nat.lt_sub_of_add_lt h
@[deprecated getElem?_drop (since := "2024-06-12")]
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
simp
theorem head?_drop (l : List α) (n : Nat) :
(l.drop n).head? = l[n]? := by
rw [head?_eq_getElem?, getElem?_drop, Nat.add_zero]
theorem head_drop {l : List α} {n : Nat} (h : l.drop n []) :
(l.drop n).head h = l[n]'(by simp_all) := by
have w : n < l.length := length_lt_of_drop_ne_nil h
simpa [getElem?_eq_getElem, h, w, head_eq_iff_head?_eq_some] using head?_drop l n
theorem getLast?_drop {l : List α} : (l.drop n).getLast? = if l.length n then none else l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_drop]
rw [length_drop]
split
· rw [getElem?_eq_none (by omega)]
· rw [getLast?_eq_getElem?]
congr
omega
theorem getLast_drop {l : List α} (h : l.drop n []) :
(l.drop n).getLast h = l.getLast (ne_nil_of_length_pos (by simp at h; omega)) := by
simp only [ne_eq, drop_eq_nil_iff_le] at h
apply Option.some_inj.1
simp only [ getLast?_eq_getLast, getLast?_drop, ite_eq_right_iff]
omega
theorem drop_length_cons {l : List α} (h : l []) (a : α) :
(a :: l).drop l.length = [l.getLast h] := by
induction l generalizing a with
| nil =>
cases h rfl
| cons y l ih =>
simp only [drop, length]
by_cases h₁ : l = []
· simp [h₁]
rw [getLast_cons h₁]
exact ih h₁ y
/-- Dropping the elements up to `n` in `l₁ ++ l₂` is the same as dropping the elements up to `n`
in `l₁`, dropping the elements up to `n - l₁.length` in `l₂`, and appending them. -/
theorem drop_append_eq_append_drop {l₁ l₂ : List α} {n : Nat} :
drop n (l₁ ++ l₂) = drop n l₁ ++ drop (n - l₁.length) l₂ := by
induction l₁ generalizing n
· simp
· cases n
· simp [*]
· simp only [cons_append, drop_succ_cons, length_cons, succ_eq_add_one, append_cancel_left_eq, *]
congr 1
omega
theorem drop_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n l₁.length) :
(l₁ ++ l₂).drop n = l₁.drop n ++ l₂ := by
simp [drop_append_eq_append_drop, Nat.sub_eq_zero_of_le h]
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
up to `i` in `l₂`. -/
@[simp]
theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
split <;> rename_i h
· ext1 m
by_cases h' : m < n
· rw [getElem?_append_left (by simp [length_take]; omega), getElem?_set_ne (by omega),
getElem?_take_of_lt h']
· by_cases h'' : m = n
· subst h''
rw [getElem?_set_eq _, getElem?_append_right, length_take,
Nat.min_eq_left (by omega), Nat.sub_self, getElem?_cons_zero]
rw [length_take]
exact Nat.min_le_left m l.length
· have h''' : n < m := by omega
rw [getElem?_set_ne (by omega), getElem?_append_right, length_take,
Nat.min_eq_left (by omega)]
· obtain k, rfl := Nat.exists_eq_add_of_lt h'''
have p : n + k + 1 - n = k + 1 := by omega
rw [p]
rw [getElem?_cons_succ, getElem?_drop]
congr 1
omega
· rw [length_take]
exact Nat.le_trans (Nat.min_le_left _ _) (by omega)
· rw [set_eq_of_length_le]
omega
theorem exists_of_set {n : Nat} {a' : α} {l : List α} (h : n < l.length) :
l₁ l₂, l = l₁ ++ l[n] :: l₂ l₁.length = n l.set n a' = l₁ ++ a' :: l₂ := by
refine l.take n, l.drop (n + 1), by simp, length_take_of_le (Nat.le_of_lt h), ?_
simp [set_eq_take_append_cons_drop, h]
theorem drop_set_of_lt (a : α) {n m : Nat} (l : List α)
(hnm : n < m) : drop m (l.set n a) = l.drop m :=
ext_getElem? fun k => by simpa only [getElem?_drop] using getElem?_set_ne (by omega)
theorem drop_take : (m n : Nat) (l : List α), drop n (take m l) = take (m - n) (drop n l)
| 0, _, _ => by simp
| _, 0, _ => by simp
| _, _, [] => by simp
| m+1, n+1, h :: t => by
simp [take_succ_cons, drop_succ_cons, drop_take m n t]
congr 1
omega
theorem take_reverse {α} {xs : List α} {n : Nat} :
xs.reverse.take n = (xs.drop (xs.length - n)).reverse := by
by_cases h : n xs.length
· induction xs generalizing n <;>
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
next xs_hd xs_tl xs_ih =>
cases Nat.lt_or_eq_of_le h with
| inl h' =>
have h' := Nat.le_of_succ_le_succ h'
rw [take_append_of_le_length, xs_ih h']
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
· rwa [succ_eq_add_one, Nat.sub_add_comm]
· rwa [length_reverse]
| inr h' =>
subst h'
rw [length, Nat.sub_self, drop]
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
rw [this, take_length, reverse_cons]
rw [length_append, length_reverse]
rfl
· have w : xs.length - n = 0 := by omega
rw [take_of_length_le, w, drop_zero]
simp
omega
theorem drop_reverse {α} {xs : List α} {n : Nat} :
xs.reverse.drop n = (xs.take (xs.length - n)).reverse := by
by_cases h : n xs.length
· conv =>
rhs
rw [ reverse_reverse xs]
rw [ reverse_reverse xs] at h
generalize xs.reverse = xs' at h
rw [take_reverse]
· simp only [length_reverse, reverse_reverse] at *
congr
omega
· have w : xs.length - n = 0 := by omega
rw [drop_of_length_le, w, take_zero, reverse_nil]
simp
omega
theorem reverse_take {l : List α} {n : Nat} :
(l.take n).reverse = l.reverse.drop (l.length - n) := by
by_cases h : n l.length
· rw [drop_reverse]
congr
omega
· have w : l.length - n = 0 := by omega
rw [w, drop_zero, take_of_length_le]
omega
theorem reverse_drop {l : List α} {n : Nat} :
(l.drop n).reverse = l.reverse.take (l.length - n) := by
by_cases h : n l.length
· rw [take_reverse]
congr
omega
· have w : l.length - n = 0 := by omega
rw [w, take_zero, drop_of_length_le, reverse_nil]
omega
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_replicate (n) (a : α) : rotateLeft (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (n + 1) % m + min ((n + 1) % m) m = m by
simpa [rotateLeft]
intro h
rw [Nat.min_eq_left (Nat.le_of_lt (Nat.mod_lt _ (by omega)))]
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
omega
/-! ### rotateRight -/
@[simp] theorem rotateRight_replicate (n) (a : α) : rotateRight (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (m - (n + 1) % m) + min (m - (n + 1) % m) m = m by
simpa [rotateRight]
intro h
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
rw [Nat.min_eq_left (by omega)]
omega
/-! ### zipWith -/
@[simp] theorem length_zipWith (f : α β γ) (l₁ l₂) :
length (zipWith f l₁ l₂) = min (length l₁) (length l₂) := by
induction l₁ generalizing l₂ <;> cases l₂ <;>
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
theorem lt_length_left_of_zipWith {f : α β γ} {i : Nat} {l : List α} {l' : List β}
(h : i < (zipWith f l l').length) : i < l.length := by rw [length_zipWith] at h; omega
theorem lt_length_right_of_zipWith {f : α β γ} {i : Nat} {l : List α} {l' : List β}
(h : i < (zipWith f l l').length) : i < l'.length := by rw [length_zipWith] at h; omega
@[simp]
theorem getElem_zipWith {f : α β γ} {l : List α} {l' : List β}
{i : Nat} {h : i < (zipWith f l l').length} :
(zipWith f l l')[i] =
f (l[i]'(lt_length_left_of_zipWith h))
(l'[i]'(lt_length_right_of_zipWith h)) := by
rw [ Option.some_inj, getElem?_eq_getElem, getElem?_zipWith_eq_some]
exact
l[i]'(lt_length_left_of_zipWith h), l'[i]'(lt_length_right_of_zipWith h),
by rw [getElem?_eq_getElem], by rw [getElem?_eq_getElem]; exact rfl, rfl
theorem zipWith_eq_zipWith_take_min : (l₁ : List α) (l₂ : List β),
zipWith f l₁ l₂ = zipWith f (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zipWith_eq_zipWith_take_min l₁ l₂]
theorem reverse_zipWith (h : l.length = l'.length) :
(zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
induction l generalizing l' with
| nil => simp
| cons hd tl hl =>
cases l' with
| nil => simp
| cons hd' tl' =>
simp only [Nat.add_right_cancel_iff, length] at h
have : tl.reverse.length = tl'.reverse.length := by simp [h]
simp [hl h, zipWith_append _ _ _ _ _ this]
@[deprecated reverse_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_reverse := @reverse_zipWith
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
rw [zipWith_eq_zipWith_take_min]
simp
/-! ### zip -/
@[simp] theorem length_zip (l₁ : List α) (l₂ : List β) :
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
simp [zip]
theorem lt_length_left_of_zip {i : Nat} {l : List α} {l' : List β} (h : i < (zip l l').length) :
i < l.length :=
lt_length_left_of_zipWith h
theorem lt_length_right_of_zip {i : Nat} {l : List α} {l' : List β} (h : i < (zip l l').length) :
i < l'.length :=
lt_length_right_of_zipWith h
@[simp]
theorem getElem_zip {l : List α} {l' : List β} {i : Nat} {h : i < (zip l l').length} :
(zip l l')[i] =
(l[i]'(lt_length_left_of_zip h), l'[i]'(lt_length_right_of_zip h)) :=
getElem_zipWith (h := h)
theorem zip_eq_zip_take_min : (l₁ : List α) (l₂ : List β),
zip l₁ l₂ = zip (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zip_eq_zip_take_min l₁ l₂]
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
rw [zip_eq_zip_take_min]
simp
end List

View File

@@ -1,307 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Sublist
import Init.Data.List.Attach
/-!
# Lemmas about `List.Pairwise` and `List.Nodup`.
-/
namespace List
open Nat
/-! ## Pairwise and Nodup -/
/-! ### Pairwise -/
theorem Pairwise.sublist : l₁ <+ l₂ l₂.Pairwise R l₁.Pairwise R
| .slnil, h => h
| .cons _ s, .cons _ h₂ => h₂.sublist s
| .cons₂ _ s, .cons h₁ h₂ => (h₂.sublist s).cons fun _ h => h₁ _ (s.subset h)
theorem Pairwise.imp {α R S} (H : {a b}, R a b S a b) :
{l : List α}, l.Pairwise R l.Pairwise S
| _, .nil => .nil
| _, .cons h₁ h₂ => .cons (H h₁ ·) (h₂.imp H)
theorem rel_of_pairwise_cons (p : (a :: l).Pairwise R) : {a'}, a' l R a a' :=
(pairwise_cons.1 p).1 _
theorem Pairwise.of_cons (p : (a :: l).Pairwise R) : Pairwise R l :=
(pairwise_cons.1 p).2
theorem Pairwise.tail : {l : List α} (_p : Pairwise R l), Pairwise R l.tail
| [], h => h
| _ :: _, h => h.of_cons
theorem Pairwise.imp_of_mem {S : α α Prop}
(H : {a b}, a l b l R a b S a b) (p : Pairwise R l) : Pairwise S l := by
induction p with
| nil => constructor
| @cons a l r _ ih =>
constructor
· exact fun x h => H (mem_cons_self ..) (mem_cons_of_mem _ h) <| r x h
· exact ih fun m m' => H (mem_cons_of_mem _ m) (mem_cons_of_mem _ m')
theorem Pairwise.and (hR : Pairwise R l) (hS : Pairwise S l) :
l.Pairwise fun a b => R a b S a b := by
induction hR with
| nil => simp only [Pairwise.nil]
| cons R1 _ IH =>
simp only [Pairwise.nil, pairwise_cons] at hS
exact fun b bl => R1 b bl, hS.1 b bl, IH hS.2
theorem pairwise_and_iff : l.Pairwise (fun a b => R a b S a b) Pairwise R l Pairwise S l :=
fun h => h.imp fun h => h.1, h.imp fun h => h.2, fun hR, hS => hR.and hS
theorem Pairwise.imp₂ (H : a b, R a b S a b T a b)
(hR : Pairwise R l) (hS : l.Pairwise S) : l.Pairwise T :=
(hR.and hS).imp fun h₁, h₂ => H _ _ h₁ h₂
theorem Pairwise.iff_of_mem {S : α α Prop} {l : List α}
(H : {a b}, a l b l (R a b S a b)) : Pairwise R l Pairwise S l :=
Pairwise.imp_of_mem fun m m' => (H m m').1, Pairwise.imp_of_mem fun m m' => (H m m').2
theorem Pairwise.iff {S : α α Prop} (H : a b, R a b S a b) {l : List α} :
Pairwise R l Pairwise S l :=
Pairwise.iff_of_mem fun _ _ => H ..
theorem pairwise_of_forall {l : List α} (H : x y, R x y) : Pairwise R l := by
induction l <;> simp [*]
theorem Pairwise.and_mem {l : List α} :
Pairwise R l Pairwise (fun x y => x l y l R x y) l :=
Pairwise.iff_of_mem <| by simp (config := { contextual := true })
theorem Pairwise.imp_mem {l : List α} :
Pairwise R l Pairwise (fun x y => x l y l R x y) l :=
Pairwise.iff_of_mem <| by simp (config := { contextual := true })
theorem Pairwise.forall_of_forall_of_flip (h₁ : x l, R x x) (h₂ : Pairwise R l)
(h₃ : l.Pairwise (flip R)) : x, x l y, y l R x y := by
induction l with
| nil => exact forall_mem_nil _
| cons a l ih =>
rw [pairwise_cons] at h₂ h₃
simp only [mem_cons]
rintro x (rfl | hx) y (rfl | hy)
· exact h₁ _ (l.mem_cons_self _)
· exact h₂.1 _ hy
· exact h₃.1 _ hx
· exact ih (fun x hx => h₁ _ <| mem_cons_of_mem _ hx) h₂.2 h₃.2 hx hy
theorem pairwise_singleton (R) (a : α) : Pairwise R [a] := by simp
theorem pairwise_pair {a b : α} : Pairwise R [a, b] R a b := by simp
theorem pairwise_map {l : List α} :
(l.map f).Pairwise R l.Pairwise fun a b => R (f a) (f b) := by
induction l
· simp
· simp only [map, pairwise_cons, forall_mem_map, *]
theorem Pairwise.of_map {S : β β Prop} (f : α β) (H : a b : α, S (f a) (f b) R a b)
(p : Pairwise S (map f l)) : Pairwise R l :=
(pairwise_map.1 p).imp (H _ _)
theorem Pairwise.map {S : β β Prop} (f : α β) (H : a b : α, R a b S (f a) (f b))
(p : Pairwise R l) : Pairwise S (map f l) :=
pairwise_map.2 <| p.imp (H _ _)
theorem pairwise_filterMap (f : β Option α) {l : List β} :
Pairwise R (filterMap f l) Pairwise (fun a a' : β => b f a, b' f a', R b b') l := by
let _S (a a' : β) := b f a, b' f a', R b b'
simp only [Option.mem_def]
induction l with
| nil => simp only [filterMap, Pairwise.nil]
| cons a l IH => ?_
match e : f a with
| none =>
rw [filterMap_cons_none e, pairwise_cons]
simp only [e, false_implies, implies_true, true_and, IH, reduceCtorEq]
| some b =>
rw [filterMap_cons_some e]
simpa [IH, e] using fun _ =>
fun h a ha b hab => h _ _ ha hab, fun h a b ha hab => h _ ha _ hab
theorem Pairwise.filterMap {S : β β Prop} (f : α Option β)
(H : a a' : α, R a a' b f a, b' f a', S b b') {l : List α} (p : Pairwise R l) :
Pairwise S (filterMap f l) :=
(pairwise_filterMap _).2 <| p.imp (H _ _)
@[deprecated Pairwise.filterMap (since := "2024-07-29")] abbrev Pairwise.filter_map := @Pairwise.filterMap
theorem pairwise_filter (p : α Prop) [DecidablePred p] {l : List α} :
Pairwise R (filter p l) Pairwise (fun x y => p x p y R x y) l := by
rw [ filterMap_eq_filter, pairwise_filterMap]
simp
theorem Pairwise.filter (p : α Bool) : Pairwise R l Pairwise R (filter p l) :=
Pairwise.sublist (filter_sublist _)
theorem pairwise_append {l₁ l₂ : List α} :
(l₁ ++ l₂).Pairwise R l₁.Pairwise R l₂.Pairwise R a l₁, b l₂, R a b := by
induction l₁ <;> simp [*, or_imp, forall_and, and_assoc, and_left_comm]
theorem pairwise_append_comm {R : α α Prop} (s : {x y}, R x y R y x) {l₁ l₂ : List α} :
Pairwise R (l₁ ++ l₂) Pairwise R (l₂ ++ l₁) := by
have (l₁ l₂ : List α) (H : x : α, x l₁ y : α, y l₂ R x y)
(x : α) (xm : x l₂) (y : α) (ym : y l₁) : R x y := s (H y ym x xm)
simp only [pairwise_append, and_left_comm]; rw [Iff.intro (this l₁ l₂) (this l₂ l₁)]
theorem pairwise_middle {R : α α Prop} (s : {x y}, R x y R y x) {a : α} {l₁ l₂ : List α} :
Pairwise R (l₁ ++ a :: l₂) Pairwise R (a :: (l₁ ++ l₂)) := by
show Pairwise R (l₁ ++ ([a] ++ l₂)) Pairwise R ([a] ++ l₁ ++ l₂)
rw [ append_assoc, pairwise_append, @pairwise_append _ _ ([a] ++ l₁), pairwise_append_comm s]
simp only [mem_append, or_comm]
theorem pairwise_join {L : List (List α)} :
Pairwise R (join L)
( l L, Pairwise R l) Pairwise (fun l₁ l₂ => x l₁, y l₂, R x y) L := by
induction L with
| nil => simp
| cons l L IH =>
simp only [join, pairwise_append, IH, mem_join, exists_imp, and_imp, forall_mem_cons,
pairwise_cons, and_assoc, and_congr_right_iff]
rw [and_comm, and_congr_left_iff]
intros; exact fun h a b c d e => h c d e a b, fun h c d e a b => h a b c d e
theorem pairwise_bind {R : β β Prop} {l : List α} {f : α List β} :
List.Pairwise R (l.bind f)
( a l, Pairwise R (f a)) Pairwise (fun a₁ a₂ => x f a₁, y f a₂, R x y) l := by
simp [List.bind, pairwise_join, pairwise_map]
theorem pairwise_reverse {l : List α} :
l.reverse.Pairwise R l.Pairwise (fun a b => R b a) := by
induction l <;> simp [*, pairwise_append, and_comm]
@[simp] theorem pairwise_replicate {n : Nat} {a : α} :
(replicate n a).Pairwise R n 1 R a a := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, pairwise_cons, mem_replicate, ne_eq, and_imp,
forall_eq_apply_imp_iff, ih]
constructor
· rintro h, h' | h'
· by_cases w : n = 0
· left
subst w
simp
· right
exact h w
· right
exact h'
· rintro (h | h)
· obtain rfl := eq_zero_of_le_zero (le_of_lt_succ h)
simp
· exact fun _ => h, Or.inr h
theorem Pairwise.drop {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.drop n) :=
h.sublist (drop_sublist _ _)
theorem Pairwise.take {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.take n) :=
h.sublist (take_sublist _ _)
theorem pairwise_iff_forall_sublist : l.Pairwise R ( {a b}, [a,b] <+ l R a b) := by
induction l with
| nil => simp
| cons hd tl IH =>
rw [List.pairwise_cons]
constructor <;> intro h
· intro
| a, b, .cons _ hab => exact IH.mp h.2 hab
| _, b, .cons₂ _ hab => refine h.1 _ (hab.subset ?_); simp
· constructor
· intro x hx
apply h
rw [List.cons_sublist_cons, List.singleton_sublist]
exact hx
· apply IH.mpr
intro a b hab
apply h; exact hab.cons _
theorem Pairwise.rel_of_mem_take_of_mem_drop
{l : List α} (h : l.Pairwise R) (hx : x l.take n) (hy : y l.drop n) : R x y := by
apply pairwise_iff_forall_sublist.mp h
rw [ take_append_drop n l, sublist_append_iff]
refine [x], [y], rfl, by simpa, by simpa
theorem Pairwise.rel_of_mem_append
{l₁ l₂ : List α} (h : (l₁ ++ l₂).Pairwise R) (hx : x l₁) (hy : y l₂) : R x y := by
apply pairwise_iff_forall_sublist.mp h
rw [sublist_append_iff]
exact [x], [y], rfl, by simpa, by simpa
theorem pairwise_of_forall_mem_list {l : List α} {r : α α Prop} (h : a l, b l, r a b) :
l.Pairwise r := by
rw [pairwise_iff_forall_sublist]
intro a b hab
apply h <;> (apply hab.subset; simp)
theorem pairwise_pmap {p : β Prop} {f : b, p b α} {l : List β} (h : x l, p x) :
Pairwise R (l.pmap f h)
Pairwise (fun b₁ b₂ => (h₁ : p b₁) (h₂ : p b₂), R (f b₁ h₁) (f b₂ h₂)) l := by
induction l with
| nil => simp
| cons a l ihl =>
obtain _, hl : p a b, b l p b := by simpa using h
simp only [ihl hl, pairwise_cons, exists₂_imp, pmap, and_congr_left_iff, mem_pmap]
refine fun _ => fun H b hb _ hpb => H _ _ hb rfl, ?_
rintro H _ b hb rfl
exact H b hb _ _
theorem Pairwise.pmap {l : List α} (hl : Pairwise R l) {p : α Prop} {f : a, p a β}
(h : x l, p x) {S : β β Prop}
(hS : x (hx : p x) y (hy : p y), R x y S (f x hx) (f y hy)) :
Pairwise S (l.pmap f h) := by
refine (pairwise_pmap h).2 (Pairwise.imp_of_mem ?_ hl)
intros; apply hS; assumption
/-! ### Nodup -/
@[simp]
theorem nodup_nil : @Nodup α [] :=
Pairwise.nil
@[simp]
theorem nodup_cons {a : α} {l : List α} : Nodup (a :: l) a l Nodup l := by
simp only [Nodup, pairwise_cons, forall_mem_ne]
theorem Nodup.sublist : l₁ <+ l₂ Nodup l₂ Nodup l₁ :=
Pairwise.sublist
theorem Sublist.nodup : l₁ <+ l₂ Nodup l₂ Nodup l₁ :=
Nodup.sublist
theorem getElem?_inj {xs : List α}
(h₀ : i < xs.length) (h₁ : Nodup xs) (h₂ : xs[i]? = xs[j]?) : i = j := by
induction xs generalizing i j with
| nil => cases h₀
| cons x xs ih =>
match i, j with
| 0, 0 => rfl
| i+1, j+1 =>
cases h₁ with
| cons ha h₁ =>
simp only [getElem?_cons_succ] at h₂
exact congrArg (· + 1) (ih (Nat.lt_of_succ_lt_succ h₀) h₁ h₂)
| i+1, 0 => ?_
| 0, j+1 => ?_
all_goals
simp only [get?_eq_getElem?, getElem?_cons_zero, getElem?_cons_succ] at h₂
cases h₁; rename_i h' h
have := h x ?_ rfl; cases this
rw [mem_iff_get?]
simp only [get?_eq_getElem?]
exact _, h₂; exact _ , h₂.symm
@[simp] theorem nodup_replicate {n : Nat} {a : α} :
(replicate n a).Nodup n 1 := by simp [Nodup]
end List

View File

@@ -1,463 +0,0 @@
/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
-/
prelude
import Init.Data.List.Pairwise
import Init.Data.List.Erase
/-!
# List Permutations
This file introduces the `List.Perm` relation, which is true if two lists are permutations of one
another.
## Notation
The notation `~` is used for permutation equivalence.
-/
open Nat
namespace List
open Perm (swap)
@[simp, refl] protected theorem Perm.refl : l : List α, l ~ l
| [] => .nil
| x :: xs => (Perm.refl xs).cons x
protected theorem Perm.rfl {l : List α} : l ~ l := .refl _
theorem Perm.of_eq (h : l₁ = l₂) : l₁ ~ l₂ := h .rfl
protected theorem Perm.symm {l₁ l₂ : List α} (h : l₁ ~ l₂) : l₂ ~ l₁ := by
induction h with
| nil => exact nil
| cons _ _ ih => exact cons _ ih
| swap => exact swap ..
| trans _ _ ih₁ ih₂ => exact trans ih₂ ih₁
theorem perm_comm {l₁ l₂ : List α} : l₁ ~ l₂ l₂ ~ l₁ := Perm.symm, Perm.symm
theorem Perm.swap' (x y : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : y :: x :: l₁ ~ x :: y :: l₂ :=
(swap ..).trans <| p.cons _ |>.cons _
/--
Similar to `Perm.recOn`, but the `swap` case is generalized to `Perm.swap'`,
where the tail of the lists are not necessarily the same.
-/
@[elab_as_elim] theorem Perm.recOnSwap'
{motive : (l₁ : List α) (l₂ : List α) l₁ ~ l₂ Prop} {l₁ l₂ : List α} (p : l₁ ~ l₂)
(nil : motive [] [] .nil)
(cons : x {l₁ l₂}, (h : l₁ ~ l₂) motive l₁ l₂ h motive (x :: l₁) (x :: l₂) (.cons x h))
(swap' : x y {l₁ l₂}, (h : l₁ ~ l₂) motive l₁ l₂ h
motive (y :: x :: l₁) (x :: y :: l₂) (.swap' _ _ h))
(trans : {l₁ l₂ l₃}, (h₁ : l₁ ~ l₂) (h₂ : l₂ ~ l₃) motive l₁ l₂ h₁ motive l₂ l₃ h₂
motive l₁ l₃ (.trans h₁ h₂)) : motive l₁ l₂ p :=
have motive_refl l : motive l l (.refl l) :=
List.recOn l nil fun x xs ih => cons x (.refl xs) ih
Perm.recOn p nil cons (fun x y l => swap' x y (.refl l) (motive_refl l)) trans
theorem Perm.eqv (α) : Equivalence (@Perm α) := .refl, .symm, .trans
instance isSetoid (α) : Setoid (List α) := .mk Perm (Perm.eqv α)
theorem Perm.mem_iff {a : α} {l₁ l₂ : List α} (p : l₁ ~ l₂) : a l₁ a l₂ := by
induction p with
| nil => rfl
| cons _ _ ih => simp only [mem_cons, ih]
| swap => simp only [mem_cons, or_left_comm]
| trans _ _ ih₁ ih₂ => simp only [ih₁, ih₂]
theorem Perm.subset {l₁ l₂ : List α} (p : l₁ ~ l₂) : l₁ l₂ := fun _ => p.mem_iff.mp
theorem Perm.append_right {l₁ l₂ : List α} (t₁ : List α) (p : l₁ ~ l₂) : l₁ ++ t₁ ~ l₂ ++ t₁ := by
induction p with
| nil => rfl
| cons _ _ ih => exact cons _ ih
| swap => exact swap ..
| trans _ _ ih₁ ih₂ => exact trans ih₁ ih₂
theorem Perm.append_left {t₁ t₂ : List α} : l : List α, t₁ ~ t₂ l ++ t₁ ~ l ++ t₂
| [], p => p
| x :: xs, p => (p.append_left xs).cons x
theorem Perm.append {l₁ l₂ t₁ t₂ : List α} (p₁ : l₁ ~ l₂) (p₂ : t₁ ~ t₂) : l₁ ++ t₁ ~ l₂ ++ t₂ :=
(p₁.append_right t₁).trans (p₂.append_left l₂)
theorem Perm.append_cons (a : α) {h₁ h₂ t₁ t₂ : List α} (p₁ : h₁ ~ h₂) (p₂ : t₁ ~ t₂) :
h₁ ++ a :: t₁ ~ h₂ ++ a :: t₂ := p₁.append (p₂.cons a)
@[simp] theorem perm_middle {a : α} : {l₁ l₂ : List α}, l₁ ++ a :: l₂ ~ a :: (l₁ ++ l₂)
| [], _ => .refl _
| b :: _, _ => (Perm.cons _ perm_middle).trans (swap a b _)
@[simp] theorem perm_append_singleton (a : α) (l : List α) : l ++ [a] ~ a :: l :=
perm_middle.trans <| by rw [append_nil]
theorem perm_append_comm : {l₁ l₂ : List α}, l₁ ++ l₂ ~ l₂ ++ l₁
| [], l₂ => by simp
| a :: t, l₂ => (perm_append_comm.cons _).trans perm_middle.symm
theorem perm_append_comm_assoc (l₁ l₂ l₃ : List α) :
Perm (l₁ ++ (l₂ ++ l₃)) (l₂ ++ (l₁ ++ l₃)) := by
simpa only [List.append_assoc] using perm_append_comm.append_right _
theorem concat_perm (l : List α) (a : α) : concat l a ~ a :: l := by simp
theorem Perm.length_eq {l₁ l₂ : List α} (p : l₁ ~ l₂) : length l₁ = length l₂ := by
induction p with
| nil => rfl
| cons _ _ ih => simp only [length_cons, ih]
| swap => rfl
| trans _ _ ih₁ ih₂ => simp only [ih₁, ih₂]
theorem Perm.eq_nil {l : List α} (p : l ~ []) : l = [] := eq_nil_of_length_eq_zero p.length_eq
theorem Perm.nil_eq {l : List α} (p : [] ~ l) : [] = l := p.symm.eq_nil.symm
@[simp] theorem perm_nil {l₁ : List α} : l₁ ~ [] l₁ = [] :=
fun p => p.eq_nil, fun e => e .rfl
@[simp] theorem nil_perm {l₁ : List α} : [] ~ l₁ l₁ = [] := perm_comm.trans perm_nil
theorem not_perm_nil_cons (x : α) (l : List α) : ¬[] ~ x :: l := (nomatch ·.symm.eq_nil)
theorem not_perm_cons_nil {l : List α} {a : α} : ¬(Perm (a::l) []) :=
fun h => by simpa using h.length_eq
theorem Perm.isEmpty_eq {l l' : List α} (h : Perm l l') : l.isEmpty = l'.isEmpty := by
cases l <;> cases l' <;> simp_all
@[simp] theorem reverse_perm : l : List α, reverse l ~ l
| [] => .nil
| a :: l => reverse_cons .. (perm_append_singleton _ _).trans ((reverse_perm l).cons a)
theorem perm_cons_append_cons {l l₁ l₂ : List α} (a : α) (p : l ~ l₁ ++ l₂) :
a :: l ~ l₁ ++ a :: l₂ := (p.cons a).trans perm_middle.symm
@[simp] theorem perm_replicate {n : Nat} {a : α} {l : List α} :
l ~ replicate n a l = replicate n a := by
refine fun p => eq_replicate.2 ?_, fun h => h .rfl
exact p.length_eq.trans <| length_replicate .., fun _b m => eq_of_mem_replicate <| p.subset m
@[simp] theorem replicate_perm {n : Nat} {a : α} {l : List α} :
replicate n a ~ l replicate n a = l := (perm_comm.trans perm_replicate).trans eq_comm
@[simp] theorem perm_singleton {a : α} {l : List α} : l ~ [a] l = [a] := perm_replicate (n := 1)
@[simp] theorem singleton_perm {a : α} {l : List α} : [a] ~ l [a] = l := replicate_perm (n := 1)
theorem Perm.eq_singleton (h : l ~ [a]) : l = [a] := perm_singleton.mp h
theorem Perm.singleton_eq (h : [a] ~ l) : [a] = l := singleton_perm.mp h
theorem singleton_perm_singleton {a b : α} : [a] ~ [b] a = b := by simp
theorem perm_cons_erase [DecidableEq α] {a : α} {l : List α} (h : a l) : l ~ a :: l.erase a :=
let _l₁, _l₂, _, e₁, e₂ := exists_erase_eq h
e₂ e₁ perm_middle
theorem Perm.filterMap (f : α Option β) {l₁ l₂ : List α} (p : l₁ ~ l₂) :
filterMap f l₁ ~ filterMap f l₂ := by
induction p with
| nil => simp
| cons x _p IH => cases h : f x <;> simp [h, filterMap_cons, IH, Perm.cons]
| swap x y l₂ => cases hx : f x <;> cases hy : f y <;> simp [hx, hy, filterMap_cons, swap]
| trans _p₁ _p₂ IH₁ IH₂ => exact IH₁.trans IH₂
theorem Perm.map (f : α β) {l₁ l₂ : List α} (p : l₁ ~ l₂) : map f l₁ ~ map f l₂ :=
filterMap_eq_map f p.filterMap _
theorem Perm.pmap {p : α Prop} (f : a, p a β) {l₁ l₂ : List α} (p : l₁ ~ l₂) {H₁ H₂} :
pmap f l₁ H₁ ~ pmap f l₂ H₂ := by
induction p with
| nil => simp
| cons x _p IH => simp [IH, Perm.cons]
| swap x y => simp [swap]
| trans _p₁ p₂ IH₁ IH₂ => exact IH₁.trans (IH₂ (H₁ := fun a m => H₂ a (p₂.subset m)))
theorem Perm.filter (p : α Bool) {l₁ l₂ : List α} (s : l₁ ~ l₂) :
filter p l₁ ~ filter p l₂ := by rw [ filterMap_eq_filter]; apply s.filterMap
theorem filter_append_perm (p : α Bool) (l : List α) :
filter p l ++ filter (fun x => !p x) l ~ l := by
induction l with
| nil => rfl
| cons x l ih =>
by_cases h : p x <;> simp [h]
· exact ih.cons x
· exact Perm.trans (perm_append_comm.trans (perm_append_comm.cons _)) (ih.cons x)
theorem exists_perm_sublist {l₁ l₂ l₂' : List α} (s : l₁ <+ l₂) (p : l₂ ~ l₂') :
l₁', l₁' ~ l₁ l₁' <+ l₂' := by
induction p generalizing l₁ with
| nil => exact [], sublist_nil.mp s .rfl, nil_sublist _
| cons x _ IH =>
match s with
| .cons _ s => let l₁', p', s' := IH s; exact l₁', p', s'.cons _
| .cons₂ _ s => let l₁', p', s' := IH s; exact x :: l₁', p'.cons x, s'.cons₂ _
| swap x y l' =>
match s with
| .cons _ (.cons _ s) => exact _, .rfl, (s.cons _).cons _
| .cons _ (.cons₂ _ s) => exact x :: _, .rfl, (s.cons _).cons₂ _
| .cons₂ _ (.cons _ s) => exact y :: _, .rfl, (s.cons₂ _).cons _
| .cons₂ _ (.cons₂ _ s) => exact x :: y :: _, .swap .., (s.cons₂ _).cons₂ _
| trans _ _ IH₁ IH₂ =>
let m₁, pm, sm := IH₁ s
let r₁, pr, sr := IH₂ sm
exact r₁, pr.trans pm, sr
theorem Perm.sizeOf_eq_sizeOf [SizeOf α] {l₁ l₂ : List α} (h : l₁ ~ l₂) :
sizeOf l₁ = sizeOf l₂ := by
induction h with
| nil => rfl
| cons _ _ h_sz₁₂ => simp [h_sz₁₂]
| swap => simp [Nat.add_left_comm]
| trans _ _ h_sz₁₂ h_sz₂₃ => simp [h_sz₁₂, h_sz₂₃]
theorem Sublist.exists_perm_append {l₁ l₂ : List α} : l₁ <+ l₂ l, l₂ ~ l₁ ++ l
| Sublist.slnil => nil, .rfl
| Sublist.cons a s =>
let l, p := Sublist.exists_perm_append s
a :: l, (p.cons a).trans perm_middle.symm
| Sublist.cons₂ a s =>
let l, p := Sublist.exists_perm_append s
l, p.cons a
theorem Perm.countP_eq (p : α Bool) {l₁ l₂ : List α} (s : l₁ ~ l₂) :
countP p l₁ = countP p l₂ := by
simp only [countP_eq_length_filter]
exact (s.filter _).length_eq
theorem Perm.countP_congr {l₁ l₂ : List α} (s : l₁ ~ l₂) {p p' : α Bool}
(hp : x l₁, p x = p' x) : l₁.countP p = l₂.countP p' := by
rw [ s.countP_eq p']
clear s
induction l₁ with
| nil => rfl
| cons y s hs =>
simp only [mem_cons, forall_eq_or_imp] at hp
simp only [countP_cons, hs hp.2, hp.1]
theorem countP_eq_countP_filter_add (l : List α) (p q : α Bool) :
l.countP p = (l.filter q).countP p + (l.filter fun a => !q a).countP p :=
countP_append .. Perm.countP_eq _ (filter_append_perm _ _).symm
theorem Perm.count_eq [DecidableEq α] {l₁ l₂ : List α} (p : l₁ ~ l₂) (a) :
count a l₁ = count a l₂ := p.countP_eq _
theorem Perm.foldl_eq' {f : β α β} {l₁ l₂ : List α} (p : l₁ ~ l₂)
(comm : x l₁, y l₁, (z), f (f z x) y = f (f z y) x)
(init) : foldl f init l₁ = foldl f init l₂ := by
induction p using recOnSwap' generalizing init with
| nil => simp
| cons x _p IH =>
simp only [foldl]
apply IH; intros; apply comm <;> exact .tail _ _
| swap' x y _p IH =>
simp only [foldl]
rw [comm x (.tail _ <| .head _) y (.head _)]
apply IH; intros; apply comm <;> exact .tail _ (.tail _ _)
| trans p₁ _p₂ IH₁ IH₂ =>
refine (IH₁ comm init).trans (IH₂ ?_ _)
intros; apply comm <;> apply p₁.symm.subset <;> assumption
theorem Perm.rec_heq {β : List α Sort _} {f : a l, β l β (a :: l)} {b : β []} {l l' : List α}
(hl : l ~ l') (f_congr : {a l l' b b'}, l ~ l' HEq b b' HEq (f a l b) (f a l' b'))
(f_swap : {a a' l b}, HEq (f a (a' :: l) (f a' l b)) (f a' (a :: l) (f a l b))) :
HEq (@List.rec α β b f l) (@List.rec α β b f l') := by
induction hl with
| nil => rfl
| cons a h ih => exact f_congr h ih
| swap a a' l => exact f_swap
| trans _h₁ _h₂ ih₁ ih₂ => exact ih₁.trans ih₂
/-- Lemma used to destruct perms element by element. -/
theorem perm_inv_core {a : α} {l₁ l₂ r₁ r₂ : List α} :
l₁ ++ a :: r₁ ~ l₂ ++ a :: r₂ l₁ ++ r₁ ~ l₂ ++ r₂ := by
-- Necessary generalization for `induction`
suffices s₁ s₂ (_ : s₁ ~ s₂) {l₁ l₂ r₁ r₂},
l₁ ++ a :: r₁ = s₁ l₂ ++ a :: r₂ = s₂ l₁ ++ r₁ ~ l₂ ++ r₂ from (this _ _ · rfl rfl)
intro s₁ s₂ p
induction p using Perm.recOnSwap' with intro l₁ l₂ r₁ r₂ e₁ e₂
| nil =>
simp at e₁
| cons x p IH =>
cases l₁ <;> cases l₂ <;>
dsimp at e₁ e₂ <;> injections <;> subst_vars
· exact p
· exact p.trans perm_middle
· exact perm_middle.symm.trans p
· exact (IH rfl rfl).cons _
| swap' x y p IH =>
obtain _ | y, _ | z, l₁ := l₁
<;> obtain _ | u, _ | v, l₂ := l₂
<;> dsimp at e₁ e₂ <;> injections <;> subst_vars
<;> try exact p.cons _
· exact (p.trans perm_middle).cons u
· exact ((p.trans perm_middle).cons _).trans (swap _ _ _)
· exact (perm_middle.symm.trans p).cons y
· exact (swap _ _ _).trans ((perm_middle.symm.trans p).cons u)
· exact (IH rfl rfl).swap' _ _
| trans p₁ p₂ IH₁ IH₂ =>
subst e₁ e₂
obtain l₂, r₂, rfl := append_of_mem (a := a) (p₁.subset (by simp))
exact (IH₁ rfl rfl).trans (IH₂ rfl rfl)
theorem Perm.cons_inv {a : α} {l₁ l₂ : List α} : a :: l₁ ~ a :: l₂ l₁ ~ l₂ :=
perm_inv_core (l₁ := []) (l₂ := [])
@[simp] theorem perm_cons (a : α) {l₁ l₂ : List α} : a :: l₁ ~ a :: l₂ l₁ ~ l₂ :=
.cons_inv, .cons a
theorem perm_append_left_iff {l₁ l₂ : List α} : l, l ++ l₁ ~ l ++ l₂ l₁ ~ l₂
| [] => .rfl
| a :: l => (perm_cons a).trans (perm_append_left_iff l)
theorem perm_append_right_iff {l₁ l₂ : List α} (l) : l₁ ++ l ~ l₂ ++ l l₁ ~ l₂ := by
refine fun p => ?_, .append_right _
exact (perm_append_left_iff _).1 <| perm_append_comm.trans <| p.trans perm_append_comm
section DecidableEq
variable [DecidableEq α]
theorem Perm.erase (a : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : l₁.erase a ~ l₂.erase a :=
if h₁ : a l₁ then
have h₂ : a l₂ := p.subset h₁
.cons_inv <| (perm_cons_erase h₁).symm.trans <| p.trans (perm_cons_erase h₂)
else by
have h₂ : a l₂ := mt p.mem_iff.2 h₁
rw [erase_of_not_mem h₁, erase_of_not_mem h₂]; exact p
theorem cons_perm_iff_perm_erase {a : α} {l₁ l₂ : List α} :
a :: l₁ ~ l₂ a l₂ l₁ ~ l₂.erase a := by
refine fun h => ?_, fun m, h => (h.cons a).trans (perm_cons_erase m).symm
have : a l₂ := h.subset (mem_cons_self a l₁)
exact this, (h.trans <| perm_cons_erase this).cons_inv
theorem perm_iff_count {l₁ l₂ : List α} : l₁ ~ l₂ a, count a l₁ = count a l₂ := by
refine Perm.count_eq, fun H => ?_
induction l₁ generalizing l₂ with
| nil =>
match l₂ with
| nil => rfl
| cons b l₂ =>
specialize H b
simp at H
| cons a l₁ IH =>
have : a l₂ := count_pos_iff_mem.mp (by rw [ H]; simp)
refine ((IH fun b => ?_).cons a).trans (perm_cons_erase this).symm
specialize H b
rw [(perm_cons_erase this).count_eq] at H
by_cases h : b = a <;> simpa [h, count_cons, Nat.succ_inj'] using H
theorem isPerm_iff : {l₁ l₂ : List α}, l₁.isPerm l₂ l₁ ~ l₂
| [], [] => by simp [isPerm, isEmpty]
| [], _ :: _ => by simp [isPerm, isEmpty, Perm.nil_eq]
| a :: l₁, l₂ => by simp [isPerm, isPerm_iff, cons_perm_iff_perm_erase]
instance decidablePerm (l₁ l₂ : List α) : Decidable (l₁ ~ l₂) := decidable_of_iff _ isPerm_iff
protected theorem Perm.insert (a : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) :
l₁.insert a ~ l₂.insert a := by
if h : a l₁ then
simp [h, p.subset h, p]
else
have := p.cons a
simpa [h, mt p.mem_iff.2 h] using this
theorem perm_insert_swap (x y : α) (l : List α) :
List.insert x (List.insert y l) ~ List.insert y (List.insert x l) := by
by_cases xl : x l <;> by_cases yl : y l <;> simp [xl, yl]
if xy : x = y then simp [xy] else
simp [List.insert, xl, yl, xy, Ne.symm xy]
constructor
end DecidableEq
theorem Perm.pairwise_iff {R : α α Prop} (S : {x y}, R x y R y x) :
{l₁ l₂ : List α} (_p : l₁ ~ l₂), Pairwise R l₁ Pairwise R l₂ :=
suffices {l₁ l₂}, l₁ ~ l₂ Pairwise R l₁ Pairwise R l₂
from fun p => this p, this p.symm
fun {l₁ l₂} p d => by
induction d generalizing l₂ with
| nil => rw [ p.nil_eq]; constructor
| cons h _ IH =>
have : _ l₂ := p.subset (mem_cons_self _ _)
obtain s₂, t₂, rfl := append_of_mem this
have p' := (p.trans perm_middle).cons_inv
refine (pairwise_middle S).2 (pairwise_cons.2 fun b m => ?_, IH p')
exact h _ (p'.symm.subset m)
theorem Pairwise.perm {R : α α Prop} {l l' : List α} (hR : l.Pairwise R) (hl : l ~ l')
(hsymm : {x y}, R x y R y x) : l'.Pairwise R := (hl.pairwise_iff hsymm).mp hR
theorem Perm.pairwise {R : α α Prop} {l l' : List α} (hl : l ~ l') (hR : l.Pairwise R)
(hsymm : {x y}, R x y R y x) : l'.Pairwise R := hR.perm hl hsymm
/--
If two lists are sorted by an antisymmetric relation, and permutations of each other,
they must be equal.
-/
theorem Perm.eq_of_sorted : {l₁ l₂ : List α}
(_ : a b, a l₁ b l₂ le a b le b a a = b)
(_ : l₁.Pairwise le) (_ : l₂.Pairwise le) (_ : l₁ ~ l₂), l₁ = l₂
| [], [], _, _, _, _ => rfl
| [], b :: l₂, _, _, _, h => by simp_all
| a :: l₁, [], _, _, _, h => by simp_all
| a :: l₁, b :: l₂, w, h₁, h₂, h => by
have am : a b :: l₂ := h.subset (mem_cons_self _ _)
have bm : b a :: l₁ := h.symm.subset (mem_cons_self _ _)
have ab : a = b := by
simp only [mem_cons] at am
rcases am with rfl | am
· rfl
· simp only [mem_cons] at bm
rcases bm with rfl | bm
· rfl
· exact w _ _ (mem_cons_self _ _) (mem_cons_self _ _)
(rel_of_pairwise_cons h₁ bm) (rel_of_pairwise_cons h₂ am)
subst ab
simp only [perm_cons] at h
have := Perm.eq_of_sorted
(fun x y hx hy => w x y (mem_cons_of_mem a hx) (mem_cons_of_mem a hy))
h₁.tail h₂.tail h
simp_all
theorem Nodup.perm {l l' : List α} (hR : l.Nodup) (hl : l ~ l') : l'.Nodup :=
Pairwise.perm hR hl (by intro x y h h'; simp_all)
theorem Perm.nodup {l l' : List α} (hl : l ~ l') (hR : l.Nodup) : l'.Nodup := hR.perm hl
theorem Perm.nodup_iff {l₁ l₂ : List α} : l₁ ~ l₂ (Nodup l₁ Nodup l₂) :=
Perm.pairwise_iff <| @Ne.symm α
theorem Perm.join {l₁ l₂ : List (List α)} (h : l₁ ~ l₂) : l₁.join ~ l₂.join := by
induction h with
| nil => rfl
| cons _ _ ih => simp only [join_cons, perm_append_left_iff, ih]
| swap => simp only [join_cons, append_assoc, perm_append_right_iff]; exact perm_append_comm ..
| trans _ _ ih₁ ih₂ => exact trans ih₁ ih₂
theorem Perm.bind_right {l₁ l₂ : List α} (f : α List β) (p : l₁ ~ l₂) : l₁.bind f ~ l₂.bind f :=
(p.map _).join
theorem Perm.eraseP (f : α Bool) {l₁ l₂ : List α}
(H : Pairwise (fun a b => f a f b False) l₁) (p : l₁ ~ l₂) : eraseP f l₁ ~ eraseP f l₂ := by
induction p with
| nil => simp
| cons a p IH =>
if h : f a then simp [h, p]
else simp [h]; exact IH (pairwise_cons.1 H).2
| swap a b l =>
by_cases h₁ : f a <;> by_cases h₂ : f b <;> simp [h₁, h₂]
· cases (pairwise_cons.1 H).1 _ (mem_cons.2 (Or.inl rfl)) h₂ h₁
· apply swap
| trans p₁ _ IH₁ IH₂ =>
refine (IH₁ H).trans (IH₂ ((p₁.pairwise_iff ?_).1 H))
exact fun h h₁ h₂ => h h₂ h₁
end List

View File

@@ -1,57 +0,0 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Pairwise
/-!
# Lemmas about `List.range` and `List.enum`
Most of the results are deferred to `Data.Init.List.Nat.Range`, where more results about
natural arithmetic are available.
-/
namespace List
open Nat
/-! ## Ranges and enumeration -/
/-! ### enumFrom -/
@[simp]
theorem enumFrom_eq_nil {n : Nat} {l : List α} : List.enumFrom n l = [] l = [] := by
cases l <;> simp
@[simp] theorem enumFrom_length : {n} {l : List α}, (enumFrom n l).length = l.length
| _, [] => rfl
| _, _ :: _ => congrArg Nat.succ enumFrom_length
@[simp]
theorem getElem?_enumFrom :
n (l : List α) m, (enumFrom n l)[m]? = l[m]?.map fun a => (n + m, a)
| n, [], m => rfl
| n, a :: l, 0 => by simp
| n, a :: l, m + 1 => by
simp only [enumFrom_cons, getElem?_cons_succ]
exact (getElem?_enumFrom (n + 1) l m).trans <| by rw [Nat.add_right_comm]; rfl
@[simp]
theorem getElem_enumFrom (l : List α) (n) (i : Nat) (h : i < (l.enumFrom n).length) :
(l.enumFrom n)[i] = (n + i, l[i]'(by simpa [enumFrom_length] using h)) := by
simp only [enumFrom_length] at h
rw [getElem_eq_getElem?]
simp only [getElem?_enumFrom, getElem?_eq_getElem h]
simp
theorem map_fst_add_enumFrom_eq_enumFrom (l : List α) (n k : Nat) :
map (Prod.map (· + n) id) (enumFrom k l) = enumFrom (n + k) l :=
ext_getElem? fun i by simp [(· ·), Nat.add_comm, Nat.add_left_comm]; rfl
theorem map_fst_add_enum_eq_enumFrom (l : List α) (n : Nat) :
map (Prod.map (· + n) id) (enum l) = enumFrom n l :=
map_fst_add_enumFrom_eq_enumFrom l _ _
end List

View File

@@ -1,9 +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.List.Sort.Basic
import Init.Data.List.Sort.Impl
import Init.Data.List.Sort.Lemmas

View File

@@ -1,81 +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.List.Impl
/-!
# Definition of `merge` and `mergeSort`.
These definitions are intended for verification purposes,
and are replaced at runtime by efficient versions in `Init.Data.List.Sort.Impl`.
-/
namespace List
/--
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
This version is not tail-recursive,
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
-/
def merge (le : α α Bool) : List α List α List α
| [], ys => ys
| xs, [] => xs
| x :: xs, y :: ys =>
if le x y then
x :: merge le xs (y :: ys)
else
y :: merge le (x :: xs) ys
@[simp] theorem nil_merge (ys : List α) : merge le [] ys = ys := by simp [merge]
@[simp] theorem merge_right (xs : List α) : merge le xs [] = xs := by
induction xs with
| nil => simp [merge]
| cons x xs ih => simp [merge, ih]
/--
Split a list in two equal parts. If the length is odd, the first part will be one element longer.
-/
def splitInTwo (l : { l : List α // l.length = n }) :
{ l : List α // l.length = (n+1)/2 } × { l : List α // l.length = n/2 } :=
let r := splitAt ((n+1)/2) l.1
(r.1, by simp [r, splitAt_eq, l.2]; omega, r.2, by simp [r, splitAt_eq, l.2]; omega)
/--
Simplified implementation of stable merge sort.
This function is designed for reasoning about the algorithm, and is not efficient.
(It particular it uses the non tail-recursive `merge` function,
and so can not be run on large lists, but also makes unnecessary traversals of lists.)
It is replaced at runtime in the compiler by `mergeSortTR₂` using a `@[csimp]` lemma.
Because we want the sort to be stable,
it is essential that we split the list in two contiguous sublists.
-/
def mergeSort (le : α α Bool) : List α List α
| [] => []
| [a] => [a]
| a :: b :: xs =>
let lr := splitInTwo a :: b :: xs, rfl
have := by simpa using lr.2.2
have := by simpa using lr.1.2
merge le (mergeSort le lr.1) (mergeSort le lr.2)
termination_by l => l.length
/--
Given an ordering relation `le : αα → Bool`,
construct the reverse lexicographic ordering on `Nat × α`.
which first compares the second components using `le`,
but if these are equivalent (in the sense `le a.2 b.2 && le b.2 a.2`)
then compares the first components using `≤`.
This function is only used in stating the stability properties of `mergeSort`.
-/
def enumLE (le : α α Bool) (a b : Nat × α) : Bool :=
if le a.2 b.2 then if le b.2 a.2 then a.1 b.1 else true else false
end List

View File

@@ -1,237 +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.List.Sort.Lemmas
/-!
# Replacing `merge` and `mergeSort` at runtime with tail-recursive and faster versions.
We replace `merge` with `mergeTR` using a `@[csimp]` lemma.
We replace `mergeSort` in two steps:
* first with `mergeSortTR`, which while not tail-recursive itself (it can't be),
uses `mergeTR` internally.
* second with `mergeSortTR₂`, which achieves an ~20% speed-up over `mergeSortTR`
by avoiding some unnecessary list reversals.
There is no public API in this file; it solely exists to implement the `@[csimp]` lemmas
affecting runtime behaviour.
## Future work
The current runtime implementation could be further improved in a number of ways, e.g.:
* only walking the list once during splitting,
* using insertion sort for small chunks rather than splitting all the way down to singletons,
* identifying already sorted or reverse sorted chunks and skipping them.
Because the theory developed for `mergeSort` is independent of the runtime implementation,
as long as such improvements are carefully validated by benchmarking,
they can be done without changing the theory, as long as a `@[csimp]` lemma is provided.
-/
open List
namespace List.MergeSort.Internal
/--
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
-/
def mergeTR (le : α α Bool) (l₁ l₂ : List α) : List α :=
go l₁ l₂ []
where go : List α List α List α List α
| [], l₂, acc => reverseAux acc l₂
| l₁, [], acc => reverseAux acc l₁
| x :: xs, y :: ys, acc =>
if le x y then
go xs (y :: ys) (x :: acc)
else
go (x :: xs) ys (y :: acc)
theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge le l₁ l₂ := by
induction l₁ generalizing l₂ acc with
| nil => simp [mergeTR.go, merge, reverseAux_eq]
| cons x l₁ ih₁ =>
induction l₂ generalizing acc with
| nil => simp [mergeTR.go, merge, reverseAux_eq]
| cons y l₂ ih₂ =>
simp [mergeTR.go, merge]
split <;> simp [ih₁, ih₂]
@[csimp] theorem merge_eq_mergeTR : @merge = @mergeTR := by
funext
simp [mergeTR, mergeTR_go_eq]
/--
Variant of `splitAt`, that does not reverse the first list, i.e
`splitRevAt n l = ((l.take n).reverse, l.drop n)`.
This exists solely as an optimization for `mergeSortTR` and `mergeSortTR₂`,
and should not be used elsewhere.
-/
def splitRevAt (n : Nat) (l : List α) : List α × List α := go l n [] where
/-- Auxiliary for `splitAtRev`: `splitAtRev.go xs n acc = ((take n xs).reverse ++ acc, drop n xs)`. -/
go : List α Nat List α List α × List α
| x :: xs, n+1, acc => go xs n (x :: acc)
| xs, _, acc => (acc, xs)
theorem splitRevAt_go (xs : List α) (n : Nat) (acc : List α) :
splitRevAt.go xs n acc = ((take n xs).reverse ++ acc, drop n xs) := by
induction xs generalizing n acc with
| nil => simp [splitRevAt.go]
| cons x xs ih =>
cases n with
| zero => simp [splitRevAt.go]
| succ n =>
rw [splitRevAt.go, ih n (x :: acc), take_succ_cons, reverse_cons, drop_succ_cons,
append_assoc, singleton_append]
theorem splitRevAt_eq (n : Nat) (l : List α) : splitRevAt n l = ((l.take n).reverse, l.drop n) := by
rw [splitRevAt, splitRevAt_go, append_nil]
/--
An intermediate speed-up for `mergeSort`.
This version uses the tail-recurive `mergeTR` function as a subroutine.
This is not the final version we use at runtime, as `mergeSortTR₂` is faster.
This definition is useful as an intermediate step in proving the `@[csimp]` lemma for `mergeSortTR₂`.
-/
def mergeSortTR (le : α α Bool) (l : List α) : List α :=
run l, rfl
where run : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitInTwo xs
mergeTR le (run l) (run r)
/--
Split a list in two equal parts, reversing the first part.
If the length is odd, the first part will be one element longer.
-/
def splitRevInTwo (l : { l : List α // l.length = n }) :
{ l : List α // l.length = (n+1)/2 } × { l : List α // l.length = n/2 } :=
let r := splitRevAt ((n+1)/2) l.1
(r.1, by simp [r, splitRevAt_eq, l.2]; omega, r.2, by simp [r, splitRevAt_eq, l.2]; omega)
/--
Split a list in two equal parts, reversing the first part.
If the length is odd, the second part will be one element longer.
-/
def splitRevInTwo' (l : { l : List α // l.length = n }) :
{ l : List α // l.length = n/2 } × { l : List α // l.length = (n+1)/2 } :=
let r := splitRevAt (n/2) l.1
(r.1, by simp [r, splitRevAt_eq, l.2]; omega, r.2, by simp [r, splitRevAt_eq, l.2]; omega)
/--
Faster version of `mergeSortTR`, which avoids unnecessary list reversals.
-/
-- Per the benchmark in `tests/bench/mergeSort/`
-- (which averages over 4 use cases: already sorted lists, reverse sorted lists, almost sorted lists, and random lists),
-- for lists of length 10^6, `mergeSortTR₂` is about 20% faster than `mergeSortTR`.
def mergeSortTR₂ (le : α α Bool) (l : List α) : List α :=
run l, rfl
where
run : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitRevInTwo xs
mergeTR le (run' l) (run r)
run' : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitRevInTwo' xs
mergeTR le (run' r) (run l)
theorem splitRevInTwo'_fst (l : { l : List α // l.length = n }) :
(splitRevInTwo' l).1 = (splitInTwo l.1.reverse, by simpa using l.2).2.1, by have := l.2; simp; omega := by
simp only [splitRevInTwo', splitRevAt_eq, reverse_take, splitInTwo_snd]
congr
have := l.2
omega
theorem splitRevInTwo'_snd (l : { l : List α // l.length = n }) :
(splitRevInTwo' l).2 = (splitInTwo l.1.reverse, by simpa using l.2).1.1.reverse, by have := l.2; simp; omega := by
simp only [splitRevInTwo', splitRevAt_eq, reverse_take, splitInTwo_fst, reverse_reverse]
congr 2
have := l.2
simp
omega
theorem splitRevInTwo_fst (l : { l : List α // l.length = n }) :
(splitRevInTwo l).1 = (splitInTwo l).1.1.reverse, by have := l.2; simp; omega := by
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_fst]
theorem splitRevInTwo_snd (l : { l : List α // l.length = n }) :
(splitRevInTwo l).2 = (splitInTwo l).2.1, by have := l.2; simp; omega := by
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_snd]
theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort le l.1
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR.run, mergeSort]
| n+2, a :: b :: l, h => by
cases h
simp only [mergeSortTR.run, mergeSortTR.run, mergeSort]
rw [merge_eq_mergeTR]
rw [mergeSortTR_run_eq_mergeSort, mergeSortTR_run_eq_mergeSort]
-- We don't make this a `@[csimp]` lemma because `mergeSort_eq_mergeSortTR₂` is faster.
theorem mergeSort_eq_mergeSortTR : @mergeSort = @mergeSortTR := by
funext
rw [mergeSortTR, mergeSortTR_run_eq_mergeSort]
-- This mutual block is unfortunately quite slow to elaborate.
set_option maxHeartbeats 400000 in
mutual
theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort le l.1
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR₂.run, mergeSort]
| n+2, a :: b :: l, h => by
cases h
simp only [mergeSortTR₂.run, mergeSort]
rw [splitRevInTwo_fst, splitRevInTwo_snd]
rw [mergeSortTR₂_run_eq_mergeSort, mergeSortTR₂_run'_eq_mergeSort]
rw [merge_eq_mergeTR]
rw [reverse_reverse]
termination_by n => n
theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort le l'
| 0, [], _, w
| 1, [a], _, w => by simp_all [mergeSortTR₂.run', mergeSort]
| n+2, a :: b :: l, h, w => by
cases h
simp only [mergeSortTR₂.run', mergeSort]
rw [splitRevInTwo'_fst, splitRevInTwo'_snd]
rw [mergeSortTR₂_run_eq_mergeSort, mergeSortTR₂_run'_eq_mergeSort _ rfl]
rw [ merge_eq_mergeTR]
have w' := congrArg length w
simp at w'
cases l' with
| nil => simp at w'
| cons x l' =>
cases l' with
| nil => simp at w'
| cons y l' =>
rw [mergeSort]
congr 2
· dsimp at w
simp only [w]
simp only [splitInTwo_fst, splitInTwo_snd, reverse_take, take_reverse]
congr 1
rw [w, length_reverse]
simp
· dsimp at w
simp only [w]
simp only [reverse_cons, append_assoc, singleton_append, splitInTwo_snd, length_cons]
congr 1
simp at w'
omega
termination_by n => n
end
@[csimp] theorem mergeSort_eq_mergeSortTR₂ : @mergeSort = @mergeSortTR₂ := by
funext
rw [mergeSortTR₂, mergeSortTR₂_run_eq_mergeSort]
end List.MergeSort.Internal

View File

@@ -1,406 +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.List.Perm
import Init.Data.List.Sort.Basic
import Init.Data.Bool
/-!
# Basic properties of `mergeSort`.
* `mergeSort_sorted`: `mergeSort` produces a sorted list.
* `mergeSort_perm`: `mergeSort` is a permutation of the input list.
* `mergeSort_of_sorted`: `mergeSort` does not change a sorted list.
* `mergeSort_cons`: proves `mergeSort le (x :: xs) = l₁ ++ x :: l₂` for some `l₁, l₂`
so that `mergeSort le xs = l₁ ++ l₂`, and no `a ∈ l₁` satisfies `le a x`.
* `mergeSort_stable`: if `c` is a sorted sublist of `l`, then `c` is still a sublist of `mergeSort le l`.
-/
namespace List
-- We enable this instance locally so we can write `Sorted le` instead of `Sorted (le · ·)` everywhere.
attribute [local instance] boolRelToRel
variable {le : α α Bool}
/-! ### splitInTwo -/
@[simp] theorem splitInTwo_fst (l : { l : List α // l.length = n }) : (splitInTwo l).1 = l.1.take ((n+1)/2), by simp [splitInTwo, splitAt_eq, l.2]; omega := by
simp [splitInTwo, splitAt_eq]
@[simp] theorem splitInTwo_snd (l : { l : List α // l.length = n }) : (splitInTwo l).2 = l.1.drop ((n+1)/2), by simp [splitInTwo, splitAt_eq, l.2]; omega := by
simp [splitInTwo, splitAt_eq]
theorem splitInTwo_fst_append_splitInTwo_snd (l : { l : List α // l.length = n }) : (splitInTwo l).1.1 ++ (splitInTwo l).2.1 = l.1 := by
simp
theorem splitInTwo_cons_cons_enumFrom_fst (i : Nat) (l : List α) :
(splitInTwo (i, a) :: (i+1, b) :: l.enumFrom (i+2), rfl).1.1 =
(splitInTwo a :: b :: l, rfl).1.1.enumFrom i := by
simp only [length_cons, splitInTwo_fst, enumFrom_length]
ext1 j
rw [getElem?_take, getElem?_enumFrom, getElem?_take]
split
· rw [getElem?_cons, getElem?_cons, getElem?_cons, getElem?_cons]
split
· simp; omega
· split
· simp; omega
· simp only [getElem?_enumFrom]
congr
ext <;> simp; omega
· simp
theorem splitInTwo_cons_cons_enumFrom_snd (i : Nat) (l : List α) :
(splitInTwo (i, a) :: (i+1, b) :: l.enumFrom (i+2), rfl).2.1 =
(splitInTwo a :: b :: l, rfl).2.1.enumFrom (i+(l.length+3)/2) := by
simp only [length_cons, splitInTwo_snd, enumFrom_length]
ext1 j
rw [getElem?_drop, getElem?_enumFrom, getElem?_drop]
rw [getElem?_cons, getElem?_cons, getElem?_cons, getElem?_cons]
split
· simp; omega
· split
· simp; omega
· simp only [getElem?_enumFrom]
congr
ext <;> simp; omega
theorem splitInTwo_fst_sorted (l : { l : List α // l.length = n }) (h : Pairwise le l.1) : Pairwise le (splitInTwo l).1.1 := by
rw [splitInTwo_fst]
exact h.take
theorem splitInTwo_snd_sorted (l : { l : List α // l.length = n }) (h : Pairwise le l.1) : Pairwise le (splitInTwo l).2.1 := by
rw [splitInTwo_snd]
exact h.drop
theorem splitInTwo_fst_le_splitInTwo_snd {l : { l : List α // l.length = n }} (h : Pairwise le l.1) :
a b, a (splitInTwo l).1.1 b (splitInTwo l).2.1 le a b := by
rw [splitInTwo_fst, splitInTwo_snd]
intro a b ma mb
exact h.rel_of_mem_take_of_mem_drop ma mb
/-! ### enumLE -/
theorem enumLE_trans (trans : a b c, le a b le b c le a c)
(a b c : Nat × α) : enumLE le a b enumLE le b c enumLE le a c := by
simp only [enumLE]
split <;> split <;> split <;> rename_i ab₂ ba₂ bc₂
· simp_all
intro ab₁
intro h
refine trans _ _ _ ab₂ bc₂, ?_
rcases h with (cd₂ | bc₁)
· exact Or.inl (Decidable.byContradiction
(fun ca₂ => by simp_all [trans _ _ _ (by simpa using ca₂) ab₂]))
· exact Or.inr (Nat.le_trans ab₁ bc₁)
· simp_all
· simp_all
intro h
refine trans _ _ _ ab₂ bc₂, ?_
left
rcases h with (cb₂ | _)
· exact (Decidable.byContradiction
(fun ca₂ => by simp_all [trans _ _ _ (by simpa using ca₂) ab₂]))
· exact (Decidable.byContradiction
(fun ca₂ => by simp_all [trans _ _ _ bc₂ (by simpa using ca₂)]))
· simp_all
· simp_all
· simp_all
· simp_all
· simp_all
theorem enumLE_total (total : a b, !le a b le b a)
(a b : Nat × α) : !enumLE le a b enumLE le b a := by
simp only [enumLE]
split <;> split
· simpa using Nat.le_of_lt
· simp
· simp
· simp_all [total a.2 b.2]
/-! ### merge -/
theorem merge_stable : (xs ys) (_ : x y, x xs y ys x.1 y.1),
(merge (enumLE le) xs ys).map (·.2) = merge le (xs.map (·.2)) (ys.map (·.2))
| [], ys, _ => by simp [merge]
| xs, [], _ => by simp [merge]
| (i, x) :: xs, (j, y) :: ys, h => by
simp only [merge, enumLE, map_cons]
split <;> rename_i w
· rw [if_pos (by simp [h _ _ (mem_cons_self ..) (mem_cons_self ..)])]
simp only [map_cons, cons.injEq, true_and]
rw [merge_stable, map_cons]
exact fun x' y' mx my => h x' y' (mem_cons_of_mem (i, x) mx) my
· simp only [reduceIte, map_cons, cons.injEq, true_and, reduceCtorEq]
rw [merge_stable, map_cons]
exact fun x' y' mx my => h x' y' mx (mem_cons_of_mem (j, y) my)
/--
The elements of `merge le xs ys` are exactly the elements of `xs` and `ys`.
-/
-- We subsequently prove that `mergeSort_perm : merge le xs ys ~ xs ++ ys`.
theorem mem_merge {a : α} {xs ys : List α} : a merge le xs ys a xs a ys := by
induction xs generalizing ys with
| nil => simp [merge]
| cons x xs ih =>
induction ys with
| nil => simp [merge]
| cons y ys ih =>
simp only [merge]
split <;> rename_i h
· simp_all [or_assoc]
· simp only [mem_cons, or_assoc, Bool.not_eq_true, ih, or_assoc]
apply or_congr_left
simp only [or_comm (a := a = y), or_assoc]
/--
If the ordering relation `le` is transitive and total (i.e. `le a b le b a` for all `a, b`)
then the `merge` of two sorted lists is sorted.
-/
theorem merge_sorted
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(l₁ l₂ : List α) (h₁ : l₁.Pairwise le) (h₂ : l₂.Pairwise le) : (merge le l₁ l₂).Pairwise le := by
induction l₁ generalizing l₂ with
| nil => simpa only [merge]
| cons x l₁ ih₁ =>
induction l₂ with
| nil => simpa only [merge]
| cons y l₂ ih₂ =>
simp only [merge]
split <;> rename_i h
· apply Pairwise.cons
· intro z m
rw [mem_merge, mem_cons] at m
rcases m with (m|rfl|m)
· exact rel_of_pairwise_cons h₁ m
· exact h
· exact trans _ _ _ h (rel_of_pairwise_cons h₂ m)
· exact ih₁ _ h₁.tail h₂
· apply Pairwise.cons
· intro z m
rw [mem_merge, mem_cons] at m
rcases m with (rfl|m|m)
· exact total _ _ (by simpa using h)
· exact trans _ _ _ (total _ _ (by simpa using h)) (rel_of_pairwise_cons h₁ m)
· exact rel_of_pairwise_cons h₂ m
· exact ih₂ h₂.tail
theorem merge_of_le : {xs ys : List α} (_ : a b, a xs b ys le a b),
merge le xs ys = xs ++ ys
| [], ys, _
| xs, [], _ => by simp [merge]
| x :: xs, y :: ys, h => by
simp only [merge, cons_append]
rw [if_pos, merge_of_le]
· intro a b ma mb
exact h a b (mem_cons_of_mem _ ma) mb
· exact h x y (mem_cons_self _ _) (mem_cons_self _ _)
variable (le) in
theorem merge_perm_append : {xs ys : List α}, merge le xs ys ~ xs ++ ys
| [], ys => by simp [merge]
| xs, [] => by simp [merge]
| x :: xs, y :: ys => by
simp only [merge]
split
· exact merge_perm_append.cons x
· exact (merge_perm_append.cons y).trans
((Perm.swap x y _).trans (perm_middle.symm.cons x))
/-! ### mergeSort -/
@[simp] theorem mergeSort_nil : [].mergeSort r = [] := by rw [List.mergeSort]
@[simp] theorem mergeSort_singleton (a : α) : [a].mergeSort r = [a] := by rw [List.mergeSort]
variable (le) in
theorem mergeSort_perm : (l : List α), mergeSort le l ~ l
| [] => by simp [mergeSort]
| [a] => by simp [mergeSort]
| a :: b :: xs => by
simp only [mergeSort]
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
exact (merge_perm_append le).trans
(((mergeSort_perm _).append (mergeSort_perm _)).trans
(Perm.of_eq (splitInTwo_fst_append_splitInTwo_snd _)))
termination_by l => l.length
@[simp] theorem mergeSort_length (l : List α) : (mergeSort le l).length = l.length :=
(mergeSort_perm le l).length_eq
@[simp] theorem mem_mergeSort {a : α} {l : List α} : a mergeSort le l a l :=
(mergeSort_perm le l).mem_iff
/--
The result of `mergeSort` is sorted,
as long as the comparison function is transitive (`le a b → le b c → le a c`)
and total in the sense that `le a b le b a`.
The comparison function need not be irreflexive, i.e. `le a b` and `le b a` is allowed even when `a ≠ b`.
-/
theorem mergeSort_sorted
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a) :
(l : List α) (mergeSort le l).Pairwise le
| [] => by simp [mergeSort]
| [a] => by simp [mergeSort]
| a :: b :: xs => by
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
rw [mergeSort]
apply merge_sorted @trans @total
apply mergeSort_sorted trans total
apply mergeSort_sorted trans total
termination_by l => l.length
/--
If the input list is already sorted, then `mergeSort` does not change the list.
-/
theorem mergeSort_of_sorted : {l : List α} (_ : Pairwise le l), mergeSort le l = l
| [], _ => by simp [mergeSort]
| [a], _ => by simp [mergeSort]
| a :: b :: xs, h => by
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
rw [mergeSort]
rw [mergeSort_of_sorted (splitInTwo_fst_sorted a :: b :: xs, rfl h)]
rw [mergeSort_of_sorted (splitInTwo_snd_sorted a :: b :: xs, rfl h)]
rw [merge_of_le (splitInTwo_fst_le_splitInTwo_snd h)]
rw [splitInTwo_fst_append_splitInTwo_snd]
termination_by l => l.length
/--
This merge sort algorithm is stable,
in the sense that breaking ties in the ordering function using the position in the list
has no effect on the output.
That is, elements which are equal with respect to the ordering function will remain
in the same order in the output list as they were in the input list.
See also:
* `mergeSort_stable`: if `c <+ l` and `c.Pairwise le`, then `c <+ mergeSort le l`.
* `mergeSort_stable_pair`: if `[a, b] <+ l` and `le a b`, then `[a, b] <+ mergeSort le l`)
-/
theorem mergeSort_enum {l : List α} :
(mergeSort (enumLE le) (l.enum)).map (·.2) = mergeSort le l :=
go 0 l
where go : (i : Nat) (l : List α),
(mergeSort (enumLE le) (l.enumFrom i)).map (·.2) = mergeSort le l
| _, []
| _, [a] => by simp [mergeSort]
| _, a :: b :: xs => by
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
simp only [mergeSort, enumFrom]
rw [splitInTwo_cons_cons_enumFrom_fst]
rw [splitInTwo_cons_cons_enumFrom_snd]
rw [merge_stable]
· rw [go, go]
· simp only [mem_mergeSort, Prod.forall]
intros j x k y mx my
have := mem_enumFrom mx
have := mem_enumFrom my
simp_all
omega
termination_by _ l => l.length
theorem mergeSort_cons {le : α α Bool}
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(a : α) (l : List α) :
l₁ l₂, mergeSort le (a :: l) = l₁ ++ a :: l₂ mergeSort le l = l₁ ++ l₂
b, b l₁ !le a b := by
rw [ mergeSort_enum]
rw [enum_cons]
have nd : Nodup ((a :: l).enum.map (·.1)) := by rw [enum_map_fst]; exact nodup_range _
have m₁ : (0, a) mergeSort (enumLE le) ((a :: l).enum) :=
mem_mergeSort.mpr (mem_cons_self _ _)
obtain l₁, l₂, h := append_of_mem m₁
have s := mergeSort_sorted (enumLE_trans trans) (enumLE_total total) ((a :: l).enum)
rw [h] at s
have p := mergeSort_perm (enumLE le) ((a :: l).enum)
rw [h] at p
refine l₁.map (·.2), l₂.map (·.2), ?_, ?_, ?_
· simpa using congrArg (·.map (·.2)) h
· rw [ mergeSort_enum.go 1, map_append]
congr 1
have q : mergeSort (enumLE le) (enumFrom 1 l) ~ l₁ ++ l₂ :=
(mergeSort_perm (enumLE le) (enumFrom 1 l)).trans
(p.symm.trans perm_middle).cons_inv
apply Perm.eq_of_sorted (le := enumLE le)
· rintro i, a j, b ha hb
simp only [mem_mergeSort] at ha
simp only [ q.mem_iff, mem_mergeSort] at hb
simp only [enumLE]
simp only [Bool.if_false_right, Bool.and_eq_true, Prod.mk.injEq, and_imp]
intro ab h ba h'
simp only [Bool.decide_eq_true] at ba
replace h : i j := by simpa [ab, ba] using h
replace h' : j i := by simpa [ab, ba] using h'
cases Nat.le_antisymm h h'
constructor
· rfl
· have := mem_enumFrom ha
have := mem_enumFrom hb
simp_all
· exact mergeSort_sorted (enumLE_trans trans) (enumLE_total total) ..
· exact s.sublist ((sublist_cons_self (0, a) l₂).append_left l₁)
· exact q
· intro b m
simp only [mem_map, Prod.exists, exists_eq_right] at m
obtain j, m := m
replace p := p.map (·.1)
have nd' := nd.perm p.symm
rw [map_append] at nd'
have j0 := nd'.rel_of_mem_append
(mem_map_of_mem (·.1) m) (mem_map_of_mem _ (mem_cons_self _ _))
simp only [ne_eq] at j0
have r := s.rel_of_mem_append m (mem_cons_self _ _)
simp_all [enumLE]
/--
Another statement of stability of merge sort.
If `c` is a sorted sublist of `l`,
then `c` is still a sublist of `mergeSort le l`.
-/
theorem mergeSort_stable
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a) :
{c : List α} (_ : c.Pairwise le) (_ : c <+ l),
c <+ mergeSort le l
| _, _, .slnil => nil_sublist _
| c, hc, @Sublist.cons _ _ l a h => by
obtain l₁, l₂, h₁, h₂, - := mergeSort_cons trans total a l
rw [h₁]
have h' := mergeSort_stable trans total hc h
rw [h₂] at h'
exact h'.middle a
| _, _, @Sublist.cons₂ _ l₁ l₂ a h => by
rename_i hc
obtain l₃, l₄, h₁, h₂, h₃ := mergeSort_cons trans total a l₂
rw [h₁]
have h' := mergeSort_stable trans total hc.tail h
rw [h₂] at h'
simp only [Bool.not_eq_true', tail_cons] at h₃ h'
exact
sublist_append_of_sublist_right (Sublist.cons₂ a
((fun w => Sublist.of_sublist_append_right w h') fun b m₁ m₃ =>
(Bool.eq_not_self true).mp ((rel_of_pairwise_cons hc m₁).symm.trans (h₃ b m₃))))
/--
Another statement of stability of merge sort.
If a pair `[a, b]` is a sublist of `l` and `le a b`,
then `[a, b]` is still a sublist of `mergeSort le l`.
-/
theorem mergeSort_stable_pair
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(hab : le a b) (h : [a, b] <+ l) : [a, b] <+ mergeSort le l :=
mergeSort_stable trans total (pairwise_pair.mpr hab) h

Some files were not shown because too many files have changed in this diff Show More