mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 18:34:06 +00:00
Compare commits
156 Commits
emptyWithC
...
array_repl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66b47a843b | ||
|
|
cff19d7edf | ||
|
|
600b5c5f9c | ||
|
|
414ba28cef | ||
|
|
d24dfa1031 | ||
|
|
f241cc832b | ||
|
|
e663eb1b7a | ||
|
|
06d6dbff5d | ||
|
|
66e0a5440b | ||
|
|
7f362c8e8a | ||
|
|
cde237daea | ||
|
|
b97a7ef4cb | ||
|
|
eb0c015e7c | ||
|
|
b768e44ba7 | ||
|
|
385c6db4ce | ||
|
|
aef6c6d518 | ||
|
|
d57cbdfb95 | ||
|
|
7240d910d3 | ||
|
|
6931e91bf0 | ||
|
|
501bd64a89 | ||
|
|
2b11c8d9a4 | ||
|
|
770af38c14 | ||
|
|
7b787c81f3 | ||
|
|
bd01461b5f | ||
|
|
1afd678100 | ||
|
|
677d26a581 | ||
|
|
f673facdbe | ||
|
|
9fc991da33 | ||
|
|
3d0f41e323 | ||
|
|
7e1ee70b7c | ||
|
|
131b458236 | ||
|
|
74ffa1e413 | ||
|
|
42bbc4b6e2 | ||
|
|
7c62881a95 | ||
|
|
c66cb00c0f | ||
|
|
c066b5cf1c | ||
|
|
3221ca1704 | ||
|
|
c279c088c8 | ||
|
|
086d45f27c | ||
|
|
637d8b2a2d | ||
|
|
d8cbf1cefc | ||
|
|
edbb84d23b | ||
|
|
756fd66745 | ||
|
|
99f296a2e7 | ||
|
|
d2c35fd39d | ||
|
|
cbfb9e482f | ||
|
|
1fb4a32c8d | ||
|
|
f42a28f718 | ||
|
|
160ca476a1 | ||
|
|
17f67df257 | ||
|
|
10f0adc9f9 | ||
|
|
a67de7ebda | ||
|
|
08af091a1c | ||
|
|
22b327f077 | ||
|
|
497ac70c38 | ||
|
|
a5348f4bdc | ||
|
|
d7d1754e69 | ||
|
|
720f6fca94 | ||
|
|
a634b96f6d | ||
|
|
9821bd9707 | ||
|
|
0f781136e7 | ||
|
|
41a2e9af19 | ||
|
|
bf241f9e86 | ||
|
|
a97813e11f | ||
|
|
1b0168d7b3 | ||
|
|
dc57365e95 | ||
|
|
174145929f | ||
|
|
75300d30d3 | ||
|
|
2946ba04d5 | ||
|
|
3857603dbb | ||
|
|
389537cf0e | ||
|
|
134d11f1a3 | ||
|
|
404a931219 | ||
|
|
e288e9266b | ||
|
|
53fcae031e | ||
|
|
d66abc0fc0 | ||
|
|
6a202f5acb | ||
|
|
4e83f23955 | ||
|
|
5d91ed01b7 | ||
|
|
ce138e1cec | ||
|
|
0e598c96c9 | ||
|
|
dad9b18d49 | ||
|
|
a638e2e207 | ||
|
|
a0acbd77ea | ||
|
|
a26084c433 | ||
|
|
798da80459 | ||
|
|
5513f6a468 | ||
|
|
70fb253739 | ||
|
|
4b406b6d5f | ||
|
|
1a3614616d | ||
|
|
c53b0c99de | ||
|
|
5a5e83c26c | ||
|
|
5e0648fe98 | ||
|
|
49819dad16 | ||
|
|
594587541c | ||
|
|
6df6011641 | ||
|
|
e77b528ef5 | ||
|
|
6153474c00 | ||
|
|
654c3781c4 | ||
|
|
d32a7b250a | ||
|
|
53abb99a81 | ||
|
|
e7cde1180b | ||
|
|
318c782ea7 | ||
|
|
0da54f517a | ||
|
|
1284d43ad7 | ||
|
|
71b2b67a12 | ||
|
|
84a4e37f1b | ||
|
|
6f16a535f8 | ||
|
|
6cbb8876d6 | ||
|
|
ae81567fbe | ||
|
|
b7354aacaa | ||
|
|
1dc3626ff7 | ||
|
|
a788e6aa67 | ||
|
|
0f06393149 | ||
|
|
141e52685c | ||
|
|
10b7c4e46e | ||
|
|
41c58002f1 | ||
|
|
d5f01f2db1 | ||
|
|
c8aae00847 | ||
|
|
1bbd2c183b | ||
|
|
b55a5b0826 | ||
|
|
eeca0ce96b | ||
|
|
2cb89823f3 | ||
|
|
297be24c0d | ||
|
|
e59f487bf0 | ||
|
|
e1d15946f7 | ||
|
|
5c333d88c0 | ||
|
|
07ee2eea21 | ||
|
|
af82d75e86 | ||
|
|
25179352b4 | ||
|
|
06c57826ae | ||
|
|
044e3b1b56 | ||
|
|
96f9ee2a41 | ||
|
|
0f3d426591 | ||
|
|
a014ae1001 | ||
|
|
137f559520 | ||
|
|
3d6d51d2c6 | ||
|
|
3786ad6d0c | ||
|
|
b16769f5a0 | ||
|
|
4262ea14d6 | ||
|
|
816da7120e | ||
|
|
38ed354cdb | ||
|
|
56ac94b591 | ||
|
|
b78352ec9d | ||
|
|
1feae7abe1 | ||
|
|
a2cb435aa1 | ||
|
|
c3f61ba3a2 | ||
|
|
8850f9e9aa | ||
|
|
2363d2fa87 | ||
|
|
d1d2f215ad | ||
|
|
eb58f46ce7 | ||
|
|
1a2345b47f | ||
|
|
c1d145e9d7 | ||
|
|
3a308324f6 | ||
|
|
bc2561f538 | ||
|
|
ed89c2611e |
4
.github/workflows/awaiting-mathlib.yml
vendored
4
.github/workflows/awaiting-mathlib.yml
vendored
@@ -3,7 +3,7 @@ name: Check awaiting-mathlib label
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [opened, labeled]
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
check-awaiting-mathlib:
|
||||
@@ -17,4 +17,4 @@ jobs:
|
||||
const { labels } = context.payload.pull_request;
|
||||
if (labels.some(label => label.name == "awaiting-mathlib") && !labels.some(label => label.name == "builds-mathlib")) {
|
||||
core.setFailed('PR is marked "awaiting-mathlib" but "builds-mathlib" label has not been applied yet by the bot');
|
||||
}
|
||||
}
|
||||
|
||||
246
.github/workflows/build-template.yml
vendored
Normal file
246
.github/workflows/build-template.yml
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
name: build-template
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
check-level:
|
||||
type: string
|
||||
required: true
|
||||
config:
|
||||
type: string
|
||||
required: true
|
||||
nightly:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_VERSION_MAJOR:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_VERSION_MINOR:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_VERSION_PATCH:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_SPECIAL_VERSION_DESC:
|
||||
type: string
|
||||
required: true
|
||||
RELEASE_TAG:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
|
||||
strategy:
|
||||
matrix:
|
||||
include: ${{fromJson(inputs.config)}}
|
||||
# complete all jobs
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
# must be inside workspace
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_COMPRESS: true
|
||||
# current cache limit
|
||||
CCACHE_MAXSIZE: 200M
|
||||
# squelch error message about missing nixpkgs channel
|
||||
NIX_BUILD_SHELL: bash
|
||||
LSAN_OPTIONS: max_leaks=10
|
||||
# somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
|
||||
CXX: c++
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
if: runner.os == 'Linux' && !matrix.cmultilib
|
||||
- name: Install MSYS2
|
||||
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:"
|
||||
if: runner.os == 'Windows'
|
||||
- name: Install Brew Packages
|
||||
run: |
|
||||
brew install ccache tree zstd coreutils gmp libuv
|
||||
if: runner.os == 'macOS'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
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: Open Nix shell once
|
||||
run: true
|
||||
if: runner.os == 'Linux'
|
||||
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
|
||||
# master (as the workflow files themselves are always taken from the merge)
|
||||
# (needs to be after "Install *" to use the right shell)
|
||||
- name: CI Merge Checkout
|
||||
run: |
|
||||
git fetch --depth=1 origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD flake.nix flake.lock
|
||||
if: github.event_name == 'pull_request'
|
||||
# (needs to be after "Checkout" so files don't get overridden)
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
with:
|
||||
version: 3.1.44
|
||||
actions-cache-folder: emsdk
|
||||
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 pkgconf:i386
|
||||
if: matrix.cmultilib
|
||||
- name: Cache
|
||||
if: matrix.name != 'Linux Lake'
|
||||
uses: actions/cache@v4
|
||||
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
|
||||
- name: Cache
|
||||
if: matrix.name == 'Linux Lake'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.ccache
|
||||
build/stage1/**/*.trace
|
||||
build/stage1/**/*.olean
|
||||
build/stage1/**/*.ilean
|
||||
build/stage1/**/*.c
|
||||
build/stage1/**/*.c.o*
|
||||
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: |
|
||||
ccache --zero-stats
|
||||
if: runner.os == 'Linux'
|
||||
- name: Set up NPROC
|
||||
run: |
|
||||
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
[ -d build ] || mkdir build
|
||||
cd build
|
||||
# arguments passed to `cmake`
|
||||
# this also enables githash embedding into stage 1 library
|
||||
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
|
||||
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
|
||||
if [[ -n '${{ matrix.cross_target }}' ]]; then
|
||||
# used by `prepare-llvm`
|
||||
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
|
||||
OPTIONS+=(-DLEAN_PLATFORM_TARGET=${{ matrix.cross_target }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
|
||||
wget -q ${{ matrix.llvm-url }}
|
||||
PREPARE="$(${{ matrix.prepare-llvm }})"
|
||||
eval "OPTIONS+=($PREPARE)"
|
||||
fi
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ inputs.nightly }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ inputs.nightly }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ inputs.RELEASE_TAG }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ inputs.LEAN_VERSION_MAJOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ inputs.LEAN_VERSION_MINOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ inputs.LEAN_VERSION_PATCH }})
|
||||
OPTIONS+=(-DLEAN_VERSION_IS_RELEASE=1)
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ inputs.LEAN_SPECIAL_VERSION_DESC }})
|
||||
fi
|
||||
# contortion to support empty OPTIONS with old macOS bash
|
||||
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
|
||||
time make -j$NPROC
|
||||
- name: Install
|
||||
run: |
|
||||
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/, ...
|
||||
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
|
||||
- name: Pack
|
||||
run: |
|
||||
dir=$(echo lean-*-*)
|
||||
mkdir pack
|
||||
# high-compression tar.zst + zip for release, fast tar.zst otherwise
|
||||
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ inputs.nightly }}' || -n '${{ inputs.RELEASE_TAG }}' ]]; then
|
||||
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -19 -o pack/$dir.tar.zst
|
||||
zip -rq pack/$dir.zip $dir
|
||||
else
|
||||
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
|
||||
fi
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.release
|
||||
with:
|
||||
name: build-${{ matrix.name }}
|
||||
path: pack/*
|
||||
- name: Lean stats
|
||||
run: |
|
||||
build/stage1/bin/lean --stats src/Lean.lean
|
||||
if: ${{ !matrix.cross }}
|
||||
- name: Test
|
||||
id: test
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
|
||||
if: (matrix.wasm || !matrix.cross) && inputs.check-level >= 1
|
||||
- name: Test Summary
|
||||
uses: test-summary/action@v2
|
||||
with:
|
||||
paths: build/stage1/test-results.xml
|
||||
# prefix `if` above with `always` so it's run even if tests failed
|
||||
if: always() && steps.test.conclusion != 'skipped'
|
||||
- name: Check Test Binary
|
||||
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
|
||||
if: (!matrix.cross) && steps.test.conclusion != 'skipped'
|
||||
- name: Build Stage 2
|
||||
run: |
|
||||
make -C build -j$NPROC stage2
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check Stage 3
|
||||
run: |
|
||||
make -C build -j$NPROC check-stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Test Speedcenter Benchmarks
|
||||
run: |
|
||||
# Necessary for some timing metrics but does not work on Namespace runners
|
||||
# and we just want to test that the benchmarks run at all here
|
||||
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
|
||||
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
|
||||
cd tests/bench
|
||||
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check rebootstrap
|
||||
run: |
|
||||
# clean rebuild in case of Makefile changes
|
||||
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
|
||||
if: matrix.name == 'Linux' && inputs.check-level >= 1
|
||||
- name: CCache stats
|
||||
run: ccache -s
|
||||
- name: Show stacktrace for coredumps
|
||||
if: failure() && runner.os == 'Linux'
|
||||
run: |
|
||||
for c in $(find . -name core); do
|
||||
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
|
||||
echo bt | $GDB/bin/gdb -q $progbin $c || true
|
||||
done
|
||||
4
.github/workflows/check-stage0.yml
vendored
4
.github/workflows/check-stage0.yml
vendored
@@ -20,9 +20,7 @@ jobs:
|
||||
|
||||
- name: Identify stage0 changes
|
||||
run: |
|
||||
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0 |
|
||||
grep -v -x -F $'stage0/src/stdlib_flags.h\nstage0/src/lean.mk.in' \
|
||||
> "$RUNNER_TEMP/stage0" || true
|
||||
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0/stdlib > "$RUNNER_TEMP/stage0" || true
|
||||
if test -s "$RUNNER_TEMP/stage0"
|
||||
then
|
||||
echo "CHANGES=yes" >> "$GITHUB_ENV"
|
||||
|
||||
254
.github/workflows/ci.yml
vendored
254
.github/workflows/ci.yml
vendored
@@ -36,7 +36,9 @@ jobs:
|
||||
# 2: PRs with `release-ci` label, releases (incl. nightlies)
|
||||
check-level: ${{ steps.set-level.outputs.check-level }}
|
||||
# The build matrix, dynamically generated here
|
||||
matrix: ${{ steps.set-matrix.outputs.result }}
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
# secondary build jobs that should not block the CI success/merge queue
|
||||
matrix-secondary: ${{ steps.set-matrix.outputs.matrix-secondary }}
|
||||
# Should we make a nightly release? If so, this output contains the lean version string, else it is empty
|
||||
nightly: ${{ steps.set-nightly.outputs.nightly }}
|
||||
# Should this be the CI for a tagged release?
|
||||
@@ -135,6 +137,7 @@ jobs:
|
||||
console.log(`level: ${level}`);
|
||||
// use large runners where available (original repo)
|
||||
let large = ${{ github.repository == 'leanprover/lean4' }};
|
||||
const isPr = "${{ github.event_name }}" == "pull_request";
|
||||
let matrix = [
|
||||
{
|
||||
"name": "Linux LLVM",
|
||||
@@ -163,6 +166,14 @@ jobs:
|
||||
// foreign code may be linked against more recent glibc
|
||||
"CTEST_OPTIONS": "-E 'foreign'"
|
||||
},
|
||||
{
|
||||
"name": "Linux Lake",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
|
||||
// just a secondary PR build job for now
|
||||
"check-level": isPr ? 0 : 3,
|
||||
"secondary": true,
|
||||
"CMAKE_OPTIONS": "-DUSE_LAKE=ON"
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
|
||||
@@ -204,13 +215,18 @@ jobs:
|
||||
"os": "macos-14",
|
||||
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
|
||||
"release": true,
|
||||
// special cased below
|
||||
// "check-level": 0,
|
||||
"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*",
|
||||
"binary-check": "otool -L",
|
||||
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
|
||||
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
|
||||
// Special handling for MacOS aarch64, we want:
|
||||
// 1. To run it in PRs so Mac devs get PR toolchains (so secondary is sufficient)
|
||||
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
|
||||
// little value in the merge queue
|
||||
// 3. To run it in release (obviously)
|
||||
"check-level": isPr ? 0 : 2,
|
||||
"secondary": isPr,
|
||||
},
|
||||
{
|
||||
"name": "Windows",
|
||||
@@ -262,208 +278,40 @@ jobs:
|
||||
// }
|
||||
];
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
|
||||
const isPr = "${{ github.event_name }}" == "pull_request";
|
||||
const filter = (job) => {
|
||||
if (job["name"] === "macOS aarch64") {
|
||||
// Special handling for MacOS aarch64, we want:
|
||||
// 1. To run it in PRs so Mac devs get PR toolchains
|
||||
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
|
||||
// little value in the merge queue
|
||||
// 3. To run it in release (obviously)
|
||||
return isPr || level >= 2;
|
||||
} else {
|
||||
return level >= job["check-level"];
|
||||
}
|
||||
};
|
||||
return matrix.filter(filter);
|
||||
matrix = matrix.filter((job) => level >= job["check-level"]);
|
||||
core.setOutput('matrix', matrix.filter((job) => !job["secondary"]));
|
||||
core.setOutput('matrix-secondary', matrix.filter((job) => job["secondary"]));
|
||||
|
||||
build:
|
||||
needs: [configure]
|
||||
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
|
||||
strategy:
|
||||
matrix:
|
||||
include: ${{fromJson(needs.configure.outputs.matrix)}}
|
||||
# complete all jobs
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
# must be inside workspace
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_COMPRESS: true
|
||||
# current cache limit
|
||||
CCACHE_MAXSIZE: 200M
|
||||
# squelch error message about missing nixpkgs channel
|
||||
NIX_BUILD_SHELL: bash
|
||||
LSAN_OPTIONS: max_leaks=10
|
||||
# somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
|
||||
CXX: c++
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
if: runner.os == 'Linux' && !matrix.cmultilib
|
||||
- name: Install MSYS2
|
||||
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:"
|
||||
if: runner.os == 'Windows'
|
||||
- name: Install Brew Packages
|
||||
run: |
|
||||
brew install ccache tree zstd coreutils gmp libuv
|
||||
if: runner.os == 'macOS'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
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 }}
|
||||
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
|
||||
# master (as the workflow files themselves are always taken from the merge)
|
||||
# (needs to be after "Install *" to use the right shell)
|
||||
- name: CI Merge Checkout
|
||||
run: |
|
||||
git fetch --depth=1 origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD flake.nix flake.lock
|
||||
if: github.event_name == 'pull_request'
|
||||
# (needs to be after "Checkout" so files don't get overridden)
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
with:
|
||||
version: 3.1.44
|
||||
actions-cache-folder: emsdk
|
||||
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 pkgconf:i386
|
||||
if: matrix.cmultilib
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
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: |
|
||||
ccache --zero-stats
|
||||
if: runner.os == 'Linux'
|
||||
- name: Set up NPROC
|
||||
run: |
|
||||
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
# arguments passed to `cmake`
|
||||
# this also enables githash embedding into stage 1 library
|
||||
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
|
||||
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
|
||||
if [[ -n '${{ matrix.cross_target }}' ]]; then
|
||||
# used by `prepare-llvm`
|
||||
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
|
||||
OPTIONS+=(-DLEAN_PLATFORM_TARGET=${{ matrix.cross_target }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
|
||||
wget -q ${{ matrix.llvm-url }}
|
||||
PREPARE="$(${{ matrix.prepare-llvm }})"
|
||||
eval "OPTIONS+=($PREPARE)"
|
||||
fi
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.nightly }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.nightly }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ needs.configure.outputs.LEAN_VERSION_MAJOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ needs.configure.outputs.LEAN_VERSION_MINOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ needs.configure.outputs.LEAN_VERSION_PATCH }})
|
||||
OPTIONS+=(-DLEAN_VERSION_IS_RELEASE=1)
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }})
|
||||
fi
|
||||
# contortion to support empty OPTIONS with old macOS bash
|
||||
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
|
||||
time make -j$NPROC
|
||||
- name: Install
|
||||
run: |
|
||||
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/, ...
|
||||
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
|
||||
- name: Pack
|
||||
run: |
|
||||
dir=$(echo lean-*-*)
|
||||
mkdir pack
|
||||
# high-compression tar.zst + zip for release, fast tar.zst otherwise
|
||||
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ needs.configure.outputs.nightly }}' || -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
|
||||
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -19 -o pack/$dir.tar.zst
|
||||
zip -rq pack/$dir.zip $dir
|
||||
else
|
||||
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
|
||||
fi
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.release
|
||||
with:
|
||||
name: build-${{ matrix.name }}
|
||||
path: pack/*
|
||||
- name: Lean stats
|
||||
run: |
|
||||
build/stage1/bin/lean --stats src/Lean.lean
|
||||
if: ${{ !matrix.cross }}
|
||||
- name: Test
|
||||
id: test
|
||||
run: |
|
||||
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
|
||||
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.check-level >= 1
|
||||
- name: Test Summary
|
||||
uses: test-summary/action@v2
|
||||
with:
|
||||
paths: build/stage1/test-results.xml
|
||||
# prefix `if` above with `always` so it's run even if tests failed
|
||||
if: always() && steps.test.conclusion != 'skipped'
|
||||
- name: Check Test Binary
|
||||
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
|
||||
if: (!matrix.cross) && steps.test.conclusion != 'skipped'
|
||||
- name: Build Stage 2
|
||||
run: |
|
||||
make -C build -j$NPROC stage2
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check Stage 3
|
||||
run: |
|
||||
make -C build -j$NPROC check-stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Test Speedcenter Benchmarks
|
||||
run: |
|
||||
# Necessary for some timing metrics but does not work on Namespace runners
|
||||
# and we just want to test that the benchmarks run at all here
|
||||
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
|
||||
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
|
||||
cd tests/bench
|
||||
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check rebootstrap
|
||||
run: |
|
||||
# clean rebuild in case of Makefile changes
|
||||
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
|
||||
if: matrix.name == 'Linux' && needs.configure.outputs.check-level >= 1
|
||||
- name: CCache stats
|
||||
run: ccache -s
|
||||
needs: [configure]
|
||||
uses: ./.github/workflows/build-template.yml
|
||||
with:
|
||||
config: ${{needs.configure.outputs.matrix}}
|
||||
check-level: ${{ needs.configure.outputs.check-level }}
|
||||
nightly: ${{ needs.configure.outputs.nightly }}
|
||||
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
|
||||
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
|
||||
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }}
|
||||
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }}
|
||||
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }}
|
||||
secrets: inherit
|
||||
|
||||
# build jobs that should not be considered by `all-done` below
|
||||
build-secondary:
|
||||
needs: [configure]
|
||||
if: needs.configure.outputs.matrix-secondary != '[]'
|
||||
uses: ./.github/workflows/build-template.yml
|
||||
with:
|
||||
config: ${{needs.configure.outputs.matrix-secondary}}
|
||||
check-level: ${{ needs.configure.outputs.check-level }}
|
||||
nightly: ${{ needs.configure.outputs.nightly }}
|
||||
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
|
||||
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
|
||||
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }}
|
||||
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }}
|
||||
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }}
|
||||
secrets: inherit
|
||||
|
||||
# This job collects results from all the matrix jobs
|
||||
# This can be made the "required" job, instead of listing each
|
||||
|
||||
@@ -15,10 +15,7 @@ foreach(var ${vars})
|
||||
# must forward options that generate incompatible .olean format
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
if("${var}" MATCHES "LLVM*")
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
if("${var}" MATCHES "PKG_CONFIG*")
|
||||
if("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE")
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
|
||||
@@ -68,8 +65,8 @@ ExternalProject_add(stage0
|
||||
SOURCE_SUBDIR src
|
||||
BINARY_DIR stage0
|
||||
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
|
||||
# (however, `CHECK_OLEAN_VERSION=ON` in CI will override this as we need to
|
||||
# embed the githash into the stage 1 library built by stage 0)
|
||||
# (however, CI will override this as we need to embed the githash into the stage 1 library built
|
||||
# by stage 0)
|
||||
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
|
||||
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
|
||||
INSTALL_COMMAND "" # skip install
|
||||
@@ -83,6 +80,7 @@ ExternalProject_add(stage1
|
||||
BUILD_ALWAYS ON
|
||||
INSTALL_COMMAND ""
|
||||
DEPENDS stage0
|
||||
STEP_TARGETS configure
|
||||
)
|
||||
ExternalProject_add(stage2
|
||||
SOURCE_DIR "${LEAN_SOURCE_DIR}"
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
GLIBC_DEV = pkgsDist.glibc.dev;
|
||||
GCC_LIB = pkgsDist.gcc.cc.lib;
|
||||
ZLIB = pkgsDist.zlib;
|
||||
# for CI coredumps
|
||||
GDB = pkgsDist.gdb;
|
||||
});
|
||||
in {
|
||||
|
||||
@@ -170,7 +170,12 @@ def main():
|
||||
section_title = format_section_title(label) if label != "Uncategorised" else "Uncategorised"
|
||||
print(f"## {section_title}\n")
|
||||
for _, entry in sorted(entries, key=lambda x: x[0]):
|
||||
print(f"* {entry}\n")
|
||||
# Split entry into lines and indent all lines after the first
|
||||
lines = entry.splitlines()
|
||||
print(f"* {lines[0]}")
|
||||
for line in lines[1:]:
|
||||
print(f" {line}")
|
||||
print() # Empty line after each entry
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -455,20 +455,20 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=$ORIGIN/../lib:$ORIGIN/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
|
||||
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
|
||||
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -install_name @rpath/libLake_shared.dylib")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -install_name @rpath/libLake_shared.dylib")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -Wl,--no-whole-archive")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
@@ -515,7 +515,16 @@ if(USE_GITHASH)
|
||||
message(STATUS "git commit sha1: ${GIT_SHA1}")
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA1 "")
|
||||
if(USE_LAKE AND ${STAGE} EQUAL 0)
|
||||
# we need to embed *some* hash for Lake to invalidate stage 1 on stage 0 changes
|
||||
execute_process(
|
||||
COMMAND git ls-tree HEAD "${CMAKE_CURRENT_SOURCE_DIR}/../../stage0" --object-only
|
||||
OUTPUT_VARIABLE GIT_SHA1
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
message(STATUS "stage0 sha1: ${GIT_SHA1}")
|
||||
else()
|
||||
set(GIT_SHA1 "")
|
||||
endif()
|
||||
endif()
|
||||
configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h")
|
||||
|
||||
@@ -542,6 +551,9 @@ include_directories(${LEAN_SOURCE_DIR})
|
||||
include_directories(${CMAKE_BINARY_DIR}) # version.h etc., "private" headers
|
||||
include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" headers
|
||||
|
||||
# Lean code only needs this one include
|
||||
string(APPEND LEANC_OPTS " -I${CMAKE_BINARY_DIR}/include")
|
||||
|
||||
# Use CMake profile C++ flags for building Lean libraries, but do not embed in `leanc`
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
|
||||
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
|
||||
@@ -754,7 +766,12 @@ add_custom_target(clean-olean
|
||||
DEPENDS clean-stdlib)
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
|
||||
PATTERN temp EXCLUDE)
|
||||
PATTERN temp
|
||||
PATTERN "*.export"
|
||||
PATTERN "*.hash"
|
||||
PATTERN "*.trace"
|
||||
PATTERN "*.rsp"
|
||||
EXCLUDE)
|
||||
|
||||
# symlink source into expected installation location for go-to-definition, if file system allows it
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
|
||||
@@ -785,10 +802,32 @@ if(LEAN_INSTALL_PREFIX)
|
||||
endif()
|
||||
|
||||
# Escape for `make`. Yes, twice.
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
string(REPLACE "$" "\\\$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
|
||||
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
|
||||
|
||||
# hacky
|
||||
function(toml_escape IN OUTVAR)
|
||||
if(IN)
|
||||
string(STRIP "${IN}" OUT)
|
||||
string(REPLACE " " "\", \"" OUT "${OUT}")
|
||||
set(${OUTVAR} "\"${OUT}\"" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_CC "${LEANC_CC}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_FLAGS "${LEANC_INTERNAL_FLAGS}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_LINKER_FLAGS "${LEANC_INTERNAL_LINKER_FLAGS}")
|
||||
set(LEANC_OPTS_TOML "${LEANC_OPTS} ${LEANC_EXTRA_CC_FLAGS} ${LEANC_INTERNAL_FLAGS}")
|
||||
set(LINK_OPTS_TOML "${LEANC_INTERNAL_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/lib/lean ${LEAN_EXTRA_LINKER_FLAGS}")
|
||||
|
||||
toml_escape("${LEAN_EXTRA_MAKE_OPTS}" LEAN_EXTRA_OPTS_TOML)
|
||||
toml_escape("${LEANC_OPTS_TOML}" LEANC_OPTS_TOML)
|
||||
toml_escape("${LINK_OPTS_TOML}" LINK_OPTS_TOML)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(LAKE_LIB_PREFIX "lib")
|
||||
endif()
|
||||
|
||||
if(USE_LAKE AND STAGE EQUAL 1)
|
||||
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/lakefile.toml)
|
||||
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/lakefile.toml)
|
||||
|
||||
@@ -40,3 +40,4 @@ import Init.Syntax
|
||||
import Init.Internal
|
||||
import Init.Try
|
||||
import Init.BinderNameHint
|
||||
import Init.Task
|
||||
|
||||
@@ -175,7 +175,10 @@ theorem or_iff_not_imp_right : a ∨ b ↔ (¬b → a) := Decidable.or_iff_not_i
|
||||
|
||||
theorem not_imp_iff_and_not : ¬(a → b) ↔ a ∧ ¬b := Decidable.not_imp_iff_and_not
|
||||
|
||||
theorem not_and_iff_or_not_not : ¬(a ∧ b) ↔ ¬a ∨ ¬b := Decidable.not_and_iff_or_not_not
|
||||
theorem not_and_iff_not_or_not : ¬(a ∧ b) ↔ ¬a ∨ ¬b := Decidable.not_and_iff_not_or_not
|
||||
|
||||
@[deprecated not_and_iff_not_or_not (since := "2025-03-18")]
|
||||
abbrev not_and_iff_or_not_not := @not_and_iff_not_or_not
|
||||
|
||||
theorem not_iff : ¬(a ↔ b) ↔ (¬a ↔ b) := Decidable.not_iff
|
||||
|
||||
|
||||
@@ -51,14 +51,28 @@ def ForInStep.value (x : ForInStep α) : α :=
|
||||
@[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl
|
||||
@[simp] theorem ForInStep.value_yield (b : β) : (ForInStep.yield b).value = b := rfl
|
||||
|
||||
/--
|
||||
Maps a function over a functor, with parameters swapped so that the function comes last.
|
||||
|
||||
This function is `Functor.map` with the parameters reversed, typically used via the `<&>` operator.
|
||||
-/
|
||||
@[reducible]
|
||||
def Functor.mapRev {f : Type u → Type v} [Functor f] {α β : Type u} : f α → (α → β) → f β :=
|
||||
fun a f => f <$> a
|
||||
|
||||
@[inherit_doc Functor.mapRev]
|
||||
infixr:100 " <&> " => Functor.mapRev
|
||||
|
||||
recommended_spelling "mapRev" for "<&>" in [Functor.mapRev, «term_<&>_»]
|
||||
|
||||
/--
|
||||
Discards the value in a functor, retaining the functor's structure.
|
||||
|
||||
Discarding values is especially useful when using `Applicative` functors or `Monad`s to implement
|
||||
effects, and some operation should be carried out only for its effects. In `do`-notation, statements
|
||||
whose values are discarded must return `Unit`, and `discard` can be used to explicitly discard their
|
||||
values.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def Functor.discard {f : Type u → Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
|
||||
Functor.mapConst PUnit.unit x
|
||||
@@ -121,6 +135,13 @@ instance : ToBool Bool where
|
||||
| true => t
|
||||
| false => f
|
||||
|
||||
/--
|
||||
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns it and ignores
|
||||
`y`; otherwise, runs `y` and returns its result.
|
||||
|
||||
This a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
|
||||
operator.
|
||||
-/
|
||||
@[macro_inline] def orM {m : Type u → Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
|
||||
let b ← x
|
||||
match toBool b with
|
||||
@@ -131,6 +152,13 @@ infixr:30 " <||> " => orM
|
||||
|
||||
recommended_spelling "orM" for "<||>" in [orM, «term_<||>_»]
|
||||
|
||||
/--
|
||||
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns `y`; otherwise,
|
||||
returns the original result of `x`.
|
||||
|
||||
This a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
|
||||
operator.
|
||||
-/
|
||||
@[macro_inline] def andM {m : Type u → Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
|
||||
let b ← x
|
||||
match toBool b with
|
||||
@@ -141,6 +169,9 @@ infixr:35 " <&&> " => andM
|
||||
|
||||
recommended_spelling "andM" for "<&&>" in [andM, «term_<&&>_»]
|
||||
|
||||
/--
|
||||
Runs a monadic action and returns the negation of its result.
|
||||
-/
|
||||
@[macro_inline] def notM {m : Type → Type v} [Applicative m] (x : m Bool) : m Bool :=
|
||||
not <$> x
|
||||
|
||||
@@ -256,21 +287,61 @@ Using `control` means that `runInBase` can be used multiple times.
|
||||
-/
|
||||
|
||||
|
||||
/-- MonadControl is a way of stating that the monad `m` can be 'run inside' the monad `n`.
|
||||
|
||||
This is the same as [`MonadBaseControl`](https://hackage.haskell.org/package/monad-control-1.0.3.1/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl) in Haskell.
|
||||
To learn about `MonadControl`, see the comment above this docstring.
|
||||
/--
|
||||
A way to lift a computation from one monad to another while providing the lifted computation with a
|
||||
means of interpreting computations from the outer monad. This provides a means of lifting
|
||||
higher-order operations automatically.
|
||||
|
||||
Clients should typically use `control` or `controlAt`, which request an instance of `MonadControlT`:
|
||||
the reflexive, transitive closure of `MonadControl`. New instances should be defined for
|
||||
`MonadControl` itself.
|
||||
-/
|
||||
-- This is the same as
|
||||
-- [`MonadBaseControl`](https://hackage.haskell.org/package/monad-control-1.0.3.1/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl)
|
||||
-- in Haskell.
|
||||
class MonadControl (m : semiOutParam (Type u → Type v)) (n : Type u → Type w) where
|
||||
/--
|
||||
A type that can be used to reconstruct both a returned value and any state used by the outer
|
||||
monad.
|
||||
-/
|
||||
stM : Type u → Type u
|
||||
/--
|
||||
Lifts an action from the inner monad `m` to the outer monad `n`. The inner monad has access to a
|
||||
reverse lifting operator that can run an `n` action, returning a value and state together.
|
||||
-/
|
||||
liftWith : {α : Type u} → (({β : Type u} → n β → m (stM β)) → m α) → n α
|
||||
/--
|
||||
Lifts a monadic action that returns a state and a value in the inner monad to an action in the
|
||||
outer monad. The extra state information is used to restore the results of effects from the
|
||||
reverse lift passed to `liftWith`'s parameter.
|
||||
-/
|
||||
restoreM : {α : Type u} → m (stM α) → n α
|
||||
|
||||
/-- Transitive closure of MonadControl. -/
|
||||
/--
|
||||
A way to lift a computation from one monad to another while providing the lifted computation with a
|
||||
means of interpreting computations from the outer monad. This provides a means of lifting
|
||||
higher-order operations automatically.
|
||||
|
||||
Clients should typically use `control` or `controlAt`, which request an instance of `MonadControlT`:
|
||||
the reflexive, transitive closure of `MonadControl`. New instances should be defined for
|
||||
`MonadControl` itself.
|
||||
-/
|
||||
class MonadControlT (m : Type u → Type v) (n : Type u → Type w) where
|
||||
/--
|
||||
A type that can be used to reconstruct both a returned value and any state used by the outer
|
||||
monad.
|
||||
-/
|
||||
stM : Type u → Type u
|
||||
/--
|
||||
Lifts an action from the inner monad `m` to the outer monad `n`. The inner monad has access to a
|
||||
reverse lifting operator that can run an `n` action, returning a value and state together.
|
||||
-/
|
||||
liftWith : {α : Type u} → (({β : Type u} → n β → m (stM β)) → m α) → n α
|
||||
/--
|
||||
Lifts a monadic action that returns a state and a value in the inner monad to an action in the
|
||||
outer monad. The extra state information is used to restore the results of effects from the
|
||||
reverse lift passed to `liftWith`'s parameter.
|
||||
-/
|
||||
restoreM {α : Type u} : stM α → n α
|
||||
|
||||
export MonadControlT (stM liftWith restoreM)
|
||||
@@ -286,11 +357,28 @@ instance (m : Type u → Type v) [Pure m] : MonadControlT m m where
|
||||
liftWith f := f fun x => x
|
||||
restoreM x := pure x
|
||||
|
||||
/--
|
||||
Lifts an operation from an inner monad to an outer monad, providing it with a reverse lifting
|
||||
operator that allows outer monad computations to be run in the inner monad. The lifted operation is
|
||||
required to return extra information that is required in order to reconstruct the reverse lift's
|
||||
effects in the outer monad; this extra information is determined by `stM`.
|
||||
|
||||
This function takes the inner monad as an explicit parameter. Use `control` to infer the monad.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def controlAt (m : Type u → Type v) {n : Type u → Type w} [MonadControlT m n] [Bind n] {α : Type u}
|
||||
(f : ({β : Type u} → n β → m (stM m n β)) → m (stM m n α)) : n α :=
|
||||
liftWith f >>= restoreM
|
||||
|
||||
/--
|
||||
Lifts an operation from an inner monad to an outer monad, providing it with a reverse lifting
|
||||
operator that allows outer monad computations to be run in the inner monad. The lifted operation is
|
||||
required to return extra information that is required in order to reconstruct the reverse lift's
|
||||
effects in the outer monad; this extra information is determined by `stM`.
|
||||
|
||||
This function takes the inner monad as an implicit parameter. Use `controlAt` to specify it
|
||||
explicitly.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def control {m : Type u → Type v} {n : Type u → Type w} [MonadControlT m n] [Bind n] {α : Type u}
|
||||
(f : ({β : Type u} → n β → m (stM m n β)) → m (stM m n α)) : n α :=
|
||||
|
||||
@@ -13,10 +13,20 @@ import Init.Coe
|
||||
namespace Except
|
||||
variable {ε : Type u}
|
||||
|
||||
/--
|
||||
A successful computation in the `Except ε` monad: `a` is returned, and no exception is thrown.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure (a : α) : Except ε α :=
|
||||
Except.ok a
|
||||
|
||||
/--
|
||||
Transforms a successful result with a function, doing nothing when an exception is thrown.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).map toString = pure 2`
|
||||
* `(throw "Error" : Except String Nat).map toString = throw "Error"`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map (f : α → β) : Except ε α → Except ε β
|
||||
| Except.error err => Except.error err
|
||||
@@ -27,36 +37,78 @@ protected def map (f : α → β) : Except ε α → Except ε β
|
||||
intro e
|
||||
simp [Except.map]; cases e <;> rfl
|
||||
|
||||
/--
|
||||
Transforms exceptions with a function, doing nothing on successful results.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).mapError (·.length) = pure 2`
|
||||
* `(throw "Error" : Except String Nat).mapError (·.length) = throw 5`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def mapError (f : ε → ε') : Except ε α → Except ε' α
|
||||
| Except.error err => Except.error <| f err
|
||||
| Except.ok v => Except.ok v
|
||||
|
||||
/--
|
||||
Sequences two operations that may throw exceptions, allowing the second to depend on the value
|
||||
returned by the first.
|
||||
|
||||
If the first operation throws an exception, then it is the result of the computation. If the first
|
||||
succeeds but the second throws an exception, then that exception is the result. If both succeed,
|
||||
then the result is the result of the second computation.
|
||||
|
||||
This is the implementation of the `>>=` operator for `Except ε`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind (ma : Except ε α) (f : α → Except ε β) : Except ε β :=
|
||||
match ma with
|
||||
| Except.error err => Except.error err
|
||||
| Except.ok v => f v
|
||||
|
||||
/-- Returns true if the value is `Except.ok`, false otherwise. -/
|
||||
/-- Returns `true` if the value is `Except.ok`, `false` otherwise. -/
|
||||
@[always_inline, inline]
|
||||
protected def toBool : Except ε α → Bool
|
||||
| Except.ok _ => true
|
||||
| Except.error _ => false
|
||||
|
||||
@[inherit_doc Except.toBool]
|
||||
abbrev isOk : Except ε α → Bool := Except.toBool
|
||||
|
||||
/--
|
||||
Returns `none` if an exception was thrown, or `some` around the value on success.
|
||||
|
||||
Examples:
|
||||
* `(pure 10 : Except String Nat).toOption = some 10`
|
||||
* `(throw "Failure" : Except String Nat).toOption = none`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def toOption : Except ε α → Option α
|
||||
| Except.ok a => some a
|
||||
| Except.error _ => none
|
||||
|
||||
/--
|
||||
Handles exceptions thrown in the `Except ε` monad.
|
||||
|
||||
If `ma` is successful, its result is returned. If it throws an exception, then `handle` is invoked
|
||||
on the exception's value.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).tryCatch (pure ·.length) = pure 2`
|
||||
* `(throw "Error" : Except String Nat).tryCatch (pure ·.length) = pure 5`
|
||||
* `(throw "Error" : Except String Nat).tryCatch (fun x => throw ("E: " ++ x)) = throw "E: Error"`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def tryCatch (ma : Except ε α) (handle : ε → Except ε α) : Except ε α :=
|
||||
match ma with
|
||||
| Except.ok a => Except.ok a
|
||||
| Except.error e => handle e
|
||||
|
||||
/--
|
||||
Recovers from exceptions thrown in the `Except ε` monad. Typically used via the `<|>` operator.
|
||||
|
||||
`Except.tryCatch` is a related operator that allows the recovery procedure to depend on _which_
|
||||
exception was thrown.
|
||||
-/
|
||||
def orElseLazy (x : Except ε α) (y : Unit → Except ε α) : Except ε α :=
|
||||
match x with
|
||||
| Except.ok a => Except.ok a
|
||||
@@ -70,12 +122,26 @@ instance : Monad (Except ε) where
|
||||
|
||||
end Except
|
||||
|
||||
/--
|
||||
Adds exceptions of type `ε` to a monad `m`.
|
||||
-/
|
||||
def ExceptT (ε : Type u) (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Except ε α)
|
||||
|
||||
/--
|
||||
Use a monadic action that may return an exception's value as an action in the transformed monad that
|
||||
may throw the corresponding exception.
|
||||
|
||||
This is the inverse of `ExceptT.run`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def ExceptT.mk {ε : Type u} {m : Type u → Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
|
||||
|
||||
/--
|
||||
Use a monadic action that may throw an exception as an action that may return an exception's value.
|
||||
|
||||
This is the inverse of `ExceptT.mk`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def ExceptT.run {ε : Type u} {m : Type u → Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
|
||||
|
||||
@@ -83,25 +149,41 @@ namespace ExceptT
|
||||
|
||||
variable {ε : Type u} {m : Type u → Type v} [Monad m]
|
||||
|
||||
/--
|
||||
Returns the value `a` without throwing exceptions or having any other effect.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| pure (Except.ok a)
|
||||
|
||||
/--
|
||||
Handles exceptions thrown by an action that can have no effects _other_ than throwing exceptions.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bindCont {α β : Type u} (f : α → ExceptT ε m β) : Except ε α → m (Except ε β)
|
||||
| Except.ok a => f a
|
||||
| Except.error e => pure (Except.error e)
|
||||
|
||||
/--
|
||||
Sequences two actions that may throw exceptions. Typically used via `do`-notation or the `>>=`
|
||||
operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α → ExceptT ε m β) : ExceptT ε m β :=
|
||||
ExceptT.mk <| ma >>= ExceptT.bindCont f
|
||||
|
||||
/--
|
||||
Transforms a successful computation's value using `f`. Typically used via the `<$>` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map {α β : Type u} (f : α → β) (x : ExceptT ε m α) : ExceptT ε m β :=
|
||||
ExceptT.mk <| x >>= fun a => match a with
|
||||
| (Except.ok a) => pure <| Except.ok (f a)
|
||||
| (Except.error e) => pure <| Except.error e
|
||||
|
||||
/--
|
||||
Runs a computation from an underlying monad in the transformed monad with exceptions.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| Except.ok <$> t
|
||||
@@ -110,6 +192,9 @@ protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
|
||||
instance : MonadLift (Except ε) (ExceptT ε m) := ⟨fun e => ExceptT.mk <| pure e⟩
|
||||
instance : MonadLift m (ExceptT ε m) := ⟨ExceptT.lift⟩
|
||||
|
||||
/--
|
||||
Handles exceptions produced in the `ExceptT ε` transformer.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε → ExceptT ε m α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| ma >>= fun res => match res with
|
||||
@@ -124,6 +209,11 @@ instance : Monad (ExceptT ε m) where
|
||||
bind := ExceptT.bind
|
||||
map := ExceptT.map
|
||||
|
||||
/--
|
||||
Transforms exceptions using the function `f`.
|
||||
|
||||
This is the `ExceptT` version of `Except.mapError`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def adapt {ε' α : Type u} (f : ε → ε') : ExceptT ε m α → ExceptT ε' m α := fun x =>
|
||||
ExceptT.mk <| Except.mapError f <$> x
|
||||
@@ -150,8 +240,12 @@ instance (ε) : MonadExceptOf ε (Except ε) where
|
||||
namespace MonadExcept
|
||||
variable {ε : Type u} {m : Type v → Type w}
|
||||
|
||||
/-- Alternative orelse operator that allows to select which exception should be used.
|
||||
The default is to use the first exception since the standard `orelse` uses the second. -/
|
||||
/--
|
||||
An alternative unconditional error recovery operator that allows callers to specify which exception
|
||||
to throw in cases where both operations throw exceptions.
|
||||
|
||||
By default, the first is thrown, because the `<|>` operator throws the second.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α :=
|
||||
tryCatch t₁ fun e₁ => tryCatch t₂ fun e₂ => throw (if useFirstEx then e₁ else e₂)
|
||||
@@ -171,13 +265,24 @@ instance (ε : Type u) (m : Type u → Type v) [Monad m] : MonadControl m (Excep
|
||||
liftWith f := liftM <| f fun x => x.run
|
||||
restoreM x := x
|
||||
|
||||
/--
|
||||
Monads that provide the ability to ensure an action happens, regardless of exceptions or other
|
||||
failures.
|
||||
|
||||
`MonadFinally.tryFinally'` is used to desugar `try ... finally ...` syntax.
|
||||
-/
|
||||
class MonadFinally (m : Type u → Type v) where
|
||||
/-- `tryFinally' x f` runs `x` and then the "finally" computation `f`.
|
||||
When `x` succeeds with `a : α`, `f (some a)` is returned. If `x` fails
|
||||
for `m`'s definition of failure, `f none` is returned. Hence `tryFinally'`
|
||||
can be thought of as performing the same role as a `finally` block in
|
||||
an imperative programming language. -/
|
||||
tryFinally' {α β} : m α → (Option α → m β) → m (α × β)
|
||||
/--
|
||||
Runs an action, ensuring that some other action always happens afterward.
|
||||
|
||||
More specifically, `tryFinally' x f` runs `x` and then the “finally” computation `f`. If `x`
|
||||
succeeds with some value `a : α`, `f (some a)` is returned. If `x` fails for `m`'s definition of
|
||||
failure, `f none` is returned.
|
||||
|
||||
`tryFinally'` can be thought of as performing the same role as a `finally` block in an imperative
|
||||
programming language.
|
||||
-/
|
||||
tryFinally' {α β} : (x : m α) → (f : Option α → m β) → m (α × β)
|
||||
|
||||
export MonadFinally (tryFinally')
|
||||
|
||||
|
||||
@@ -10,19 +10,37 @@ import Init.Control.Lawful.Basic
|
||||
The Exception monad transformer using CPS style.
|
||||
-/
|
||||
|
||||
/--
|
||||
Adds exceptions of type `ε` to a monad `m`.
|
||||
|
||||
Instead of using `Except ε` to model exceptions, this implementation uses continuation passing
|
||||
style. This has different performance characteristics from `ExceptT ε`.
|
||||
-/
|
||||
def ExceptCpsT (ε : Type u) (m : Type u → Type v) (α : Type u) := (β : Type u) → (α → m β) → (ε → m β) → m β
|
||||
|
||||
namespace ExceptCpsT
|
||||
|
||||
/--
|
||||
Use a monadic action that may throw an exception as an action that may return an exception's value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
|
||||
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
|
||||
|
||||
set_option linter.unusedVariables false in -- `s` unused
|
||||
/--
|
||||
Use a monadic action that may throw an exception by providing explicit success and failure
|
||||
continuations.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α → m β) (error : ε → m β) : m β :=
|
||||
x _ ok error
|
||||
|
||||
/--
|
||||
Returns the value of a computation, forgetting whether it was an exception or a success.
|
||||
|
||||
This corresponds to early return.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
|
||||
x α pure pure
|
||||
@@ -40,6 +58,9 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
|
||||
throw e := fun _ _ k => k e
|
||||
tryCatch x handle := fun _ k₁ k₂ => x _ k₁ (fun e => handle e _ k₁ k₂)
|
||||
|
||||
/--
|
||||
Run an action from the transformed monad in the exception monad.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
|
||||
fun _ k _ => x >>= k
|
||||
|
||||
@@ -13,17 +13,26 @@ open Function
|
||||
rfl
|
||||
|
||||
/--
|
||||
The `Functor` typeclass only contains the operations of a functor.
|
||||
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
|
||||
including the preservation of the identity and composition laws:
|
||||
```
|
||||
id <$> x = x
|
||||
(h ∘ g) <$> x = h <$> g <$> x
|
||||
```
|
||||
A functor satisfies the functor laws.
|
||||
|
||||
The `Functor` class contains the operations of a functor, but does not require that instances
|
||||
prove they satisfy the laws of a functor. A `LawfulFunctor` instance includes proofs that the laws
|
||||
are satisfied. Because `Functor` instances may provide optimized implementations of `mapConst`,
|
||||
`LawfulFunctor` instances must also prove that the optimized implementation is equivalent to the
|
||||
standard implementation.
|
||||
-/
|
||||
class LawfulFunctor (f : Type u → Type v) [Functor f] : Prop where
|
||||
/--
|
||||
The `mapConst` implementation is equivalent to the default implementation.
|
||||
-/
|
||||
map_const : (Functor.mapConst : α → f β → f α) = Functor.map ∘ const β
|
||||
/--
|
||||
The `map` implementation preserves identity.
|
||||
-/
|
||||
id_map (x : f α) : id <$> x = x
|
||||
/--
|
||||
The `map` implementation preserves function composition.
|
||||
-/
|
||||
comp_map (g : α → β) (h : β → γ) (x : f α) : (h ∘ g) <$> x = h <$> g <$> x
|
||||
|
||||
export LawfulFunctor (map_const id_map comp_map)
|
||||
@@ -38,21 +47,48 @@ attribute [simp] id_map
|
||||
(comp_map _ _ _).symm
|
||||
|
||||
/--
|
||||
The `Applicative` typeclass only contains the operations of an applicative functor.
|
||||
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
|
||||
```
|
||||
pure id <*> v = v
|
||||
pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
|
||||
pure f <*> pure x = pure (f x)
|
||||
u <*> pure y = pure (· y) <*> u
|
||||
```
|
||||
An applicative functor satisfies the laws of an applicative functor.
|
||||
|
||||
The `Applicative` class contains the operations of an applicative functor, but does not require that
|
||||
instances prove they satisfy the laws of an applicative functor. A `LawfulApplicative` instance
|
||||
includes proofs that the laws are satisfied.
|
||||
|
||||
Because `Applicative` instances may provide optimized implementations of `seqLeft` and `seqRight`,
|
||||
`LawfulApplicative` instances must also prove that the optimized implementation is equivalent to the
|
||||
standard implementation.
|
||||
-/
|
||||
class LawfulApplicative (f : Type u → Type v) [Applicative f] : Prop extends LawfulFunctor f where
|
||||
/-- `seqLeft` is equivalent to the default implementation. -/
|
||||
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
|
||||
/-- `seqRight` is equivalent to the default implementation. -/
|
||||
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
|
||||
/--
|
||||
`pure` before `seq` is equivalent to `Functor.map`.
|
||||
|
||||
This means that `pure` really is pure when occurring immediately prior to `seq`.
|
||||
-/
|
||||
pure_seq (g : α → β) (x : f α) : pure g <*> x = g <$> x
|
||||
|
||||
/--
|
||||
Mapping a function over the result of `pure` is equivalent to applying the function under `pure`.
|
||||
|
||||
This means that `pure` really is pure with respect to `Functor.map`.
|
||||
-/
|
||||
map_pure (g : α → β) (x : α) : g <$> (pure x : f α) = pure (g x)
|
||||
|
||||
/--
|
||||
`pure` after `seq` is equivalent to `Functor.map`.
|
||||
|
||||
This means that `pure` really is pure when occurring just after `seq`.
|
||||
-/
|
||||
seq_pure {α β : Type u} (g : f (α → β)) (x : α) : g <*> pure x = (fun h => h x) <$> g
|
||||
|
||||
/--
|
||||
`seq` is associative.
|
||||
|
||||
Changing the nesting of `seq` calls while maintaining the order of computations results in an
|
||||
equivalent computation. This means that `seq` is not doing any more than sequencing.
|
||||
-/
|
||||
seq_assoc {α β γ : Type u} (x : f α) (g : f (α → β)) (h : f (β → γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
|
||||
comp_map g h x := (by
|
||||
repeat rw [← pure_seq]
|
||||
@@ -66,21 +102,36 @@ attribute [simp] map_pure seq_pure
|
||||
simp [pure_seq]
|
||||
|
||||
/--
|
||||
The `Monad` typeclass only contains the operations of a monad.
|
||||
`LawfulMonad` further asserts that these operations satisfy the laws of a monad,
|
||||
including associativity and identity laws for `bind`:
|
||||
```
|
||||
pure x >>= f = f x
|
||||
x >>= pure = x
|
||||
x >>= f >>= g = x >>= (fun x => f x >>= g)
|
||||
```
|
||||
Lawful monads are those that satisfy a certain behavioral specification. While all instances of
|
||||
`Monad` should satisfy these laws, not all implementations are required to prove this.
|
||||
|
||||
`LawfulMonad.mk'` is an alternative constructor containing useful defaults for many fields.
|
||||
`LawfulMonad.mk'` is an alternative constructor that contains useful defaults for many fields.
|
||||
-/
|
||||
class LawfulMonad (m : Type u → Type v) [Monad m] : Prop extends LawfulApplicative m where
|
||||
/--
|
||||
A `bind` followed by `pure` composed with a function is equivalent to a functorial map.
|
||||
|
||||
This means that `pure` really is pure after a `bind` and has no effects.
|
||||
-/
|
||||
bind_pure_comp (f : α → β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
|
||||
/--
|
||||
A `bind` followed by a functorial map is equivalent to `Applicative` sequencing.
|
||||
|
||||
This means that the effect sequencing from `Monad` and `Applicative` are the same.
|
||||
-/
|
||||
bind_map {α β : Type u} (f : m (α → β)) (x : m α) : f >>= (. <$> x) = f <*> x
|
||||
/--
|
||||
`pure` followed by `bind` is equivalent to function application.
|
||||
|
||||
This means that `pure` really is pure before a `bind` and has no effects.
|
||||
-/
|
||||
pure_bind (x : α) (f : α → m β) : pure x >>= f = f x
|
||||
/--
|
||||
`bind` is associative.
|
||||
|
||||
Changing the nesting of `bind` calls while maintaining the order of computations results in an
|
||||
equivalent computation. This means that `bind` is not doing more than data-dependent sequencing.
|
||||
-/
|
||||
bind_assoc (x : m α) (f : α → m β) (g : β → m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
|
||||
map_pure g x := (by rw [← bind_pure_comp, pure_bind])
|
||||
seq_pure g x := (by rw [← bind_map]; simp [map_pure, bind_pure_comp])
|
||||
|
||||
@@ -8,13 +8,22 @@ import Init.Data.Option.Basic
|
||||
import Init.Control.Basic
|
||||
import Init.Control.Except
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v
|
||||
|
||||
instance : ToBool (Option α) := ⟨Option.isSome⟩
|
||||
|
||||
/--
|
||||
Adds the ability to fail to a monad. Unlike ordinary exceptions, there is no way to signal why a
|
||||
failure occurred.
|
||||
-/
|
||||
def OptionT (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Option α)
|
||||
|
||||
/--
|
||||
Executes an action that might fail in the underlying monad `m`, returning `none` in case of failure.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def OptionT.run {m : Type u → Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
|
||||
x
|
||||
@@ -22,15 +31,25 @@ def OptionT.run {m : Type u → Type v} {α : Type u} (x : OptionT m α) : m (Op
|
||||
namespace OptionT
|
||||
variable {m : Type u → Type v} [Monad m] {α β : Type u}
|
||||
|
||||
/--
|
||||
Converts an action that returns an `Option` into one that might fail, with `none` indicating
|
||||
failure.
|
||||
-/
|
||||
protected def mk (x : m (Option α)) : OptionT m α :=
|
||||
x
|
||||
|
||||
/--
|
||||
Sequences two potentially-failing actions. The second action is run only if the first succeeds.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind (x : OptionT m α) (f : α → OptionT m β) : OptionT m β := OptionT.mk do
|
||||
match (← x) with
|
||||
| some a => f a
|
||||
| none => pure none
|
||||
|
||||
/--
|
||||
Succeeds with the provided value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure (a : α) : OptionT m α := OptionT.mk do
|
||||
pure (some a)
|
||||
@@ -40,11 +59,17 @@ instance : Monad (OptionT m) where
|
||||
pure := OptionT.pure
|
||||
bind := OptionT.bind
|
||||
|
||||
/--
|
||||
Recovers from failures. Typically used via the `<|>` operator.
|
||||
-/
|
||||
@[always_inline, inline] protected def orElse (x : OptionT m α) (y : Unit → OptionT m α) : OptionT m α := OptionT.mk do
|
||||
match (← x) with
|
||||
| some a => pure (some a)
|
||||
| _ => y ()
|
||||
|
||||
/--
|
||||
A recoverable failure.
|
||||
-/
|
||||
@[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do
|
||||
pure none
|
||||
|
||||
@@ -52,6 +77,12 @@ instance : Alternative (OptionT m) where
|
||||
failure := OptionT.fail
|
||||
orElse := OptionT.orElse
|
||||
|
||||
/--
|
||||
Converts a computation from the underlying monad into one that could fail, even though it does not.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
|
||||
return some (← x)
|
||||
|
||||
@@ -59,6 +90,9 @@ instance : MonadLift m (OptionT m) := ⟨OptionT.lift⟩
|
||||
|
||||
instance : MonadFunctor m (OptionT m) := ⟨fun f x => f x⟩
|
||||
|
||||
/--
|
||||
Handles failures by treating them as exceptions of type `Unit`.
|
||||
-/
|
||||
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit → OptionT m α) : OptionT m α := OptionT.mk do
|
||||
let some a ← x | handle ()
|
||||
pure a
|
||||
|
||||
@@ -10,12 +10,21 @@ import Init.Control.Basic
|
||||
import Init.Control.Id
|
||||
import Init.Control.Except
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
namespace ReaderT
|
||||
|
||||
/--
|
||||
Recovers from errors. The same local value is provided to both branches. Typically used via the
|
||||
`<|>` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def orElse [Alternative m] (x₁ : ReaderT ρ m α) (x₂ : Unit → ReaderT ρ m α) : ReaderT ρ m α :=
|
||||
fun s => x₁ s <|> x₂ () s
|
||||
|
||||
/--
|
||||
Fails with a recoverable error.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def failure [Alternative m] : ReaderT ρ m α :=
|
||||
fun _ => failure
|
||||
@@ -35,4 +44,8 @@ instance : MonadControl m (ReaderT ρ m) where
|
||||
instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where
|
||||
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)
|
||||
|
||||
/--
|
||||
A monad with access to a read-only value of type `ρ`. The value can be locally overridden by
|
||||
`withReader`, but it cannot be mutated.
|
||||
-/
|
||||
@[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id
|
||||
|
||||
@@ -9,19 +9,42 @@ prelude
|
||||
import Init.Control.Basic
|
||||
import Init.Control.Id
|
||||
import Init.Control.Except
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v w
|
||||
|
||||
/--
|
||||
Adds a mutable state of type `σ` to a monad.
|
||||
|
||||
Actions in the resulting monad are functions that take an initial state and return, in `m`, a tuple
|
||||
of a value and a state.
|
||||
-/
|
||||
def StateT (σ : Type u) (m : Type u → Type v) (α : Type u) : Type (max u v) :=
|
||||
σ → m (α × σ)
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value paired with the final state.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateT.run {σ : Type u} {m : Type u → Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
|
||||
x s
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value, discarding the final state.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateT.run' {σ : Type u} {m : Type u → Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
|
||||
(·.1) <$> x s
|
||||
|
||||
/--
|
||||
A tuple-based state monad.
|
||||
|
||||
Actions in `StateM σ` are functions that take an initial state and return a value paired with a
|
||||
final state.
|
||||
-/
|
||||
@[reducible]
|
||||
def StateM (σ α : Type u) : Type u := StateT σ Id α
|
||||
|
||||
@@ -38,14 +61,23 @@ section
|
||||
variable {σ : Type u} {m : Type u → Type v}
|
||||
variable [Monad m] {α β : Type u}
|
||||
|
||||
/--
|
||||
Returns the given value without modifying the state. Typically used via `Pure.pure`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure (a : α) : StateT σ m α :=
|
||||
fun s => pure (a, s)
|
||||
|
||||
/--
|
||||
Sequences two actions. Typically used via the `>>=` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind (x : StateT σ m α) (f : α → StateT σ m β) : StateT σ m β :=
|
||||
fun s => do let (a, s) ← x s; f a s
|
||||
|
||||
/--
|
||||
Modifies the value returned by a computation. Typically used via the `<$>` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map (f : α → β) (x : StateT σ m α) : StateT σ m β :=
|
||||
fun s => do let (a, s) ← x s; pure (f a, s)
|
||||
@@ -56,10 +88,17 @@ instance : Monad (StateT σ m) where
|
||||
bind := StateT.bind
|
||||
map := StateT.map
|
||||
|
||||
/--
|
||||
Recovers from errors. The state is rolled back on error recovery. Typically used via the `<|>`
|
||||
operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def orElse [Alternative m] {α : Type u} (x₁ : StateT σ m α) (x₂ : Unit → StateT σ m α) : StateT σ m α :=
|
||||
fun s => x₁ s <|> x₂ () s
|
||||
|
||||
/--
|
||||
Fails with a recoverable error. The state is rolled back on error recovery.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def failure [Alternative m] {α : Type u} : StateT σ m α :=
|
||||
fun _ => failure
|
||||
@@ -68,18 +107,40 @@ instance [Alternative m] : Alternative (StateT σ m) where
|
||||
failure := StateT.failure
|
||||
orElse := StateT.orElse
|
||||
|
||||
/--
|
||||
Retrieves the current value of the monad's mutable state.
|
||||
|
||||
This increments the reference count of the state, which may inhibit in-place updates.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def get : StateT σ m σ :=
|
||||
fun s => pure (s, s)
|
||||
|
||||
/--
|
||||
Replaces the mutable state with a new value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def set : σ → StateT σ m PUnit :=
|
||||
fun s' _ => pure (⟨⟩, s')
|
||||
|
||||
/--
|
||||
Applies a function to the current state that both computes a new state and a value. The new state
|
||||
replaces the current state, and the value is returned.
|
||||
|
||||
It is equivalent to `do let (a, s) := f (← StateT.get); StateT.set s; pure a`. However, using
|
||||
`StateT.modifyGet` may lead to better performance because it doesn't add a new reference to the
|
||||
state value, and additional references can inhibit in-place updates of data.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def modifyGet (f : σ → α × σ) : StateT σ m α :=
|
||||
fun s => pure (f s)
|
||||
|
||||
/--
|
||||
Runs an action from the underlying monad in the monad with state. The state is not modified.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift {α : Type u} (t : m α) : StateT σ m α :=
|
||||
fun s => do let a ← t; pure (a, s)
|
||||
|
||||
@@ -6,24 +6,45 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Control.Lawful.Basic
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/-!
|
||||
The State monad transformer using CPS style.
|
||||
-/
|
||||
|
||||
/--
|
||||
An alternative implementation of a state monad transformer that internally uses continuation passing
|
||||
style instead of tuples.
|
||||
-/
|
||||
def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type u) → σ → (α → σ → m δ) → m δ
|
||||
|
||||
namespace StateCpsT
|
||||
|
||||
variable {α σ : Type u} {m : Type u → Type v}
|
||||
|
||||
/--
|
||||
Runs a stateful computation that's represented using continuation passing style by providing it with
|
||||
an initial state and a continuation.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runK (x : StateCpsT σ m α) (s : σ) (k : α → σ → m β) : m β :=
|
||||
x _ s k
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value paired with the final state.
|
||||
|
||||
While the state is internally represented in continuation passing style, the resulting value is the
|
||||
same as for a non-CPS state monad.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
|
||||
runK x s (fun a s => pure (a, s))
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value, discarding the final state.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
|
||||
runK x s (fun a _ => pure a)
|
||||
@@ -43,6 +64,12 @@ instance : MonadStateOf σ (StateCpsT σ m) where
|
||||
set s := fun _ _ k => k ⟨⟩ s
|
||||
modifyGet f := fun _ s k => let (a, s) := f s; k a s
|
||||
|
||||
/--
|
||||
Runs an action from the underlying monad in the monad with state. The state is not modified.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
|
||||
fun _ s k => x >>= (k . s)
|
||||
|
||||
@@ -8,10 +8,23 @@ The State monad transformer using IO references.
|
||||
prelude
|
||||
import Init.System.ST
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/--
|
||||
A state monad that uses an actual mutable reference cell (i.e. an `ST.Ref ω σ`).
|
||||
|
||||
The macro `StateRefT σ m α` infers `ω` from `m`. It should normally be used instead.
|
||||
-/
|
||||
def StateRefT' (ω : Type) (σ : Type) (m : Type → Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
|
||||
|
||||
/-! Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value paired with the final state.
|
||||
|
||||
The monad `m` must support `ST` effects in order to create and mutate reference cells.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateRefT'.run {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m (α × σ) := do
|
||||
let ref ← ST.mkRef s
|
||||
@@ -19,6 +32,12 @@ def StateRefT'.run {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
|
||||
let s ← ref.get
|
||||
pure (a, s)
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value, discarding the final state.
|
||||
|
||||
The monad `m` must support `ST` effects in order to create and mutate reference cells.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateRefT'.run' {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m α := do
|
||||
let (a, _) ← x.run s
|
||||
@@ -27,6 +46,12 @@ def StateRefT'.run' {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
|
||||
namespace StateRefT'
|
||||
variable {ω σ : Type} {m : Type → Type} {α : Type}
|
||||
|
||||
/--
|
||||
Runs an action from the underlying monad in the monad with state. The state is not modified.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift (x : m α) : StateRefT' ω σ m α :=
|
||||
fun _ => x
|
||||
@@ -36,14 +61,30 @@ instance : MonadLift m (StateRefT' ω σ m) := ⟨StateRefT'.lift⟩
|
||||
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
|
||||
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
|
||||
|
||||
/--
|
||||
Retrieves the current value of the monad's mutable state.
|
||||
|
||||
This increments the reference count of the state, which may inhibit in-place updates.
|
||||
-/
|
||||
@[inline]
|
||||
protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
|
||||
fun ref => ref.get
|
||||
|
||||
/--
|
||||
Replaces the mutable state with a new value.
|
||||
-/
|
||||
@[inline]
|
||||
protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
|
||||
fun ref => ref.set s
|
||||
|
||||
/--
|
||||
Applies a function to the current state that both computes a new state and a value. The new state
|
||||
replaces the current state, and the value is returned.
|
||||
|
||||
It is equivalent to a `get` followed by a `set`. However, using `modifyGet` may lead to higher
|
||||
performance because it doesn't add a new reference to the state value. Additional references can
|
||||
inhibit in-place updates of data.
|
||||
-/
|
||||
@[inline]
|
||||
protected def modifyGet [MonadLiftT (ST ω) m] (f : σ → α × σ) : StateRefT' ω σ m α :=
|
||||
fun ref => ref.modifyGet f
|
||||
|
||||
@@ -52,8 +52,6 @@ attribute [simp] namedPattern
|
||||
/--
|
||||
`Empty.elim : Empty → C` says that a value of any type can be constructed from
|
||||
`Empty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
|
||||
|
||||
This is a non-dependent variant of `Empty.rec`.
|
||||
-/
|
||||
@[macro_inline] def Empty.elim {C : Sort u} : Empty → C := Empty.rec
|
||||
|
||||
@@ -63,8 +61,6 @@ instance : DecidableEq Empty := fun a => a.elim
|
||||
/--
|
||||
`PEmpty.elim : Empty → C` says that a value of any type can be constructed from
|
||||
`PEmpty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
|
||||
|
||||
This is a non-dependent variant of `PEmpty.rec`.
|
||||
-/
|
||||
@[macro_inline] def PEmpty.elim {C : Sort _} : PEmpty → C := fun a => nomatch a
|
||||
|
||||
@@ -72,35 +68,56 @@ This is a non-dependent variant of `PEmpty.rec`.
|
||||
instance : DecidableEq PEmpty := fun a => a.elim
|
||||
|
||||
/--
|
||||
Thunks are "lazy" values that are evaluated when first accessed using `Thunk.get/map/bind`.
|
||||
The value is then stored and not recomputed for all further accesses. -/
|
||||
-- NOTE: the runtime has special support for the `Thunk` type to implement this behavior
|
||||
Delays evaluation. The delayed code is evaluated at most once.
|
||||
|
||||
A thunk is code that constructs a value when it is requested via `Thunk.get`, `Thunk.map`, or
|
||||
`Thunk.bind`. The resulting value is cached, so the code is executed at most once. This is also
|
||||
known as lazy or call-by-need evaluation.
|
||||
|
||||
The Lean runtime has special support for the `Thunk` type in order to implement the caching
|
||||
behavior.
|
||||
-/
|
||||
structure Thunk (α : Type u) : Type u where
|
||||
/-- Constructs a new thunk from a function `Unit → α`
|
||||
that will be called when the thunk is forced. -/
|
||||
/--
|
||||
Constructs a new thunk from a function `Unit → α` that will be called when the thunk is first
|
||||
forced.
|
||||
|
||||
The result is cached. It is re-used when the thunk is forced again.
|
||||
-/
|
||||
mk ::
|
||||
/-- Extract the getter function out of a thunk. Use `Thunk.get` instead. -/
|
||||
private fn : Unit → α
|
||||
|
||||
attribute [extern "lean_mk_thunk"] Thunk.mk
|
||||
|
||||
/-- Store a value in a thunk. Note that the value has already been computed, so there is no laziness. -/
|
||||
/--
|
||||
Stores an already-computed value in a thunk.
|
||||
|
||||
Because the value has already been computed, there is no laziness.
|
||||
-/
|
||||
@[extern "lean_thunk_pure"] protected def Thunk.pure (a : α) : Thunk α :=
|
||||
⟨fun _ => a⟩
|
||||
|
||||
/--
|
||||
Forces a thunk to extract the value. This will cache the result,
|
||||
so a second call to the same function will return the value in O(1)
|
||||
instead of calling the stored getter function.
|
||||
Gets the thunk's value. If the value is cached, it is returned in constant time; if not, it is
|
||||
computed.
|
||||
|
||||
Computed values are cached, so the value is not recomputed.
|
||||
-/
|
||||
-- NOTE: we use `Thunk.get` instead of `Thunk.fn` as the accessor primitive as the latter has an additional `Unit` argument
|
||||
@[extern "lean_thunk_get_own"] protected def Thunk.get (x : @& Thunk α) : α :=
|
||||
x.fn ()
|
||||
|
||||
/-- Map a function over a thunk. -/
|
||||
/--
|
||||
Constructs a new thunk that forces `x` and then applies `x` to the result. Upon forcing, the result
|
||||
of `f` is cached and the reference to the thunk `x` is dropped.
|
||||
-/
|
||||
@[inline] protected def Thunk.map (f : α → β) (x : Thunk α) : Thunk β :=
|
||||
⟨fun _ => f x.get⟩
|
||||
/-- Constructs a thunk that applies `f` to the result of `x` when forced. -/
|
||||
|
||||
/--
|
||||
Constructs a new thunk that applies `f` to the result of `x` when forced.
|
||||
-/
|
||||
@[inline] protected def Thunk.bind (x : Thunk α) (f : α → Thunk β) : Thunk β :=
|
||||
⟨fun _ => (f x.get).get⟩
|
||||
|
||||
@@ -138,46 +155,50 @@ recommended_spelling "iff" for "↔" in [Iff, «term_↔_»]
|
||||
recommended_spelling "iff" for "<->" in [Iff, «term_<->_»]
|
||||
|
||||
/--
|
||||
`Sum α β`, or `α ⊕ β`, is the disjoint union of types `α` and `β`.
|
||||
An element of `α ⊕ β` is either of the form `.inl a` where `a : α`,
|
||||
or `.inr b` where `b : β`.
|
||||
The disjoint union of types `α` and `β`, ordinarily written `α ⊕ β`.
|
||||
|
||||
An element of `α ⊕ β` is either an `a : α` wrapped in `Sum.inl` or a `b : β` wrapped in `Sum.inr`.
|
||||
`α ⊕ β` is not equivalent to the set-theoretic union of `α` and `β` because its values include an
|
||||
indication of which of the two types was chosen. The union of a singleton set with itself contains
|
||||
one element, while `Unit ⊕ Unit` contains distinct values `inl ()` and `inr ()`.
|
||||
-/
|
||||
inductive Sum (α : Type u) (β : Type v) where
|
||||
/-- Left injection into the sum type `α ⊕ β`. If `a : α` then `.inl a : α ⊕ β`. -/
|
||||
/-- Left injection into the sum type `α ⊕ β`. -/
|
||||
| inl (val : α) : Sum α β
|
||||
/-- Right injection into the sum type `α ⊕ β`. If `b : β` then `.inr b : α ⊕ β`. -/
|
||||
/-- Right injection into the sum type `α ⊕ β`. -/
|
||||
| inr (val : β) : Sum α β
|
||||
|
||||
@[inherit_doc] infixr:30 " ⊕ " => Sum
|
||||
|
||||
/--
|
||||
`PSum α β`, or `α ⊕' β`, is the disjoint union of types `α` and `β`.
|
||||
It differs from `α ⊕ β` in that it allows `α` and `β` to have arbitrary sorts
|
||||
`Sort u` and `Sort v`, instead of restricting to `Type u` and `Type v`. This means
|
||||
that it can be used in situations where one side is a proposition, like `True ⊕' Nat`.
|
||||
The disjoint union of arbitrary sorts `α` `β`, or `α ⊕' β`.
|
||||
|
||||
The reason this is not the default is that this type lives in the universe `Sort (max 1 u v)`,
|
||||
which can cause problems for universe level unification,
|
||||
because the equation `max 1 u v = ?u + 1` has no solution in level arithmetic.
|
||||
`PSum` is usually only used in automation that constructs sums of arbitrary types.
|
||||
It differs from `α ⊕ β` in that it allows `α` and `β` to have arbitrary sorts `Sort u` and `Sort v`,
|
||||
instead of restricting them to `Type u` and `Type v`. This means that it can be used in situations
|
||||
where one side is a proposition, like `True ⊕' Nat`. However, the resulting universe level
|
||||
constraints are often more difficult to solve than those that result from `Sum`.
|
||||
-/
|
||||
inductive PSum (α : Sort u) (β : Sort v) where
|
||||
/-- Left injection into the sum type `α ⊕' β`. If `a : α` then `.inl a : α ⊕' β`. -/
|
||||
/-- Left injection into the sum type `α ⊕' β`.-/
|
||||
| inl (val : α) : PSum α β
|
||||
/-- Right injection into the sum type `α ⊕' β`. If `b : β` then `.inr b : α ⊕' β`. -/
|
||||
/-- Right injection into the sum type `α ⊕' β`. -/
|
||||
| inr (val : β) : PSum α β
|
||||
|
||||
@[inherit_doc] infixr:30 " ⊕' " => PSum
|
||||
|
||||
/--
|
||||
`PSum α β` is inhabited if `α` is inhabited.
|
||||
This is not an instance to avoid non-canonical instances.
|
||||
If the left type in a sum is inhabited then the sum is inhabited.
|
||||
|
||||
This is not an instance to avoid non-canonical instances when both the left and right types are
|
||||
inhabited.
|
||||
-/
|
||||
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := ⟨PSum.inl default⟩
|
||||
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := ⟨PSum.inl default⟩
|
||||
|
||||
/--
|
||||
`PSum α β` is inhabited if `β` is inhabited.
|
||||
This is not an instance to avoid non-canonical instances.
|
||||
If the right type in a sum is inhabited then the sum is inhabited.
|
||||
|
||||
This is not an instance to avoid non-canonical instances when both the left and right types are
|
||||
inhabited.
|
||||
-/
|
||||
@[reducible] def PSum.inhabitedRight {α β} [Inhabited β] : Inhabited (PSum α β) := ⟨PSum.inr default⟩
|
||||
|
||||
@@ -188,47 +209,58 @@ instance PSum.nonemptyRight [h : Nonempty β] : Nonempty (PSum α β) :=
|
||||
Nonempty.elim h (fun b => ⟨PSum.inr b⟩)
|
||||
|
||||
/--
|
||||
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
|
||||
whose first component is `a : α` and whose second component is `b : β a`
|
||||
(so the type of the second component can depend on the value of the first component).
|
||||
It is sometimes known as the dependent sum type, since it is the type level version
|
||||
of an indexed summation.
|
||||
Dependent pairs, in which the second element's type depends on the value of the first element. The
|
||||
type `Sigma β` is typically written `Σ a : α, β a` or `(a : α) × β a`.
|
||||
|
||||
Although its values are pairs, `Sigma` is sometimes known as the *dependent sum type*, since it is
|
||||
the type level version of an indexed summation.
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure Sigma {α : Type u} (β : α → Type v) where
|
||||
/-- Constructor for a dependent pair. If `a : α` and `b : β a` then `⟨a, b⟩ : Sigma β`.
|
||||
(This will usually require a type ascription to determine `β`
|
||||
since it is not determined from `a` and `b` alone.) -/
|
||||
/--
|
||||
Constructs a dependent pair.
|
||||
|
||||
Using this constructor in a context in which the type is not known usually requires a type
|
||||
ascription to determine `β`. This is because the desired relationship between the two values can't
|
||||
generally be determined automatically.
|
||||
-/
|
||||
mk ::
|
||||
/-- The first component of a dependent pair. If `p : @Sigma α β` then `p.1 : α`. -/
|
||||
/--
|
||||
The first component of a dependent pair.
|
||||
-/
|
||||
fst : α
|
||||
/-- The second component of a dependent pair. If `p : Sigma β` then `p.2 : β p.1`. -/
|
||||
/--
|
||||
The second component of a dependent pair. Its type depends on the first component.
|
||||
-/
|
||||
snd : β fst
|
||||
|
||||
attribute [unbox] Sigma
|
||||
|
||||
/--
|
||||
`PSigma β`, also denoted `Σ' a : α, β a` or `(a : α) ×' β a`, is the type of dependent pairs
|
||||
whose first component is `a : α` and whose second component is `b : β a`
|
||||
(so the type of the second component can depend on the value of the first component).
|
||||
It differs from `Σ a : α, β a` in that it allows `α` and `β` to have arbitrary sorts
|
||||
`Sort u` and `Sort v`, instead of restricting to `Type u` and `Type v`. This means
|
||||
that it can be used in situations where one side is a proposition, like `(p : Nat) ×' p = p`.
|
||||
Fully universe-polymorphic dependent pairs, in which the second element's type depends on the value
|
||||
of the first element and both types are allowed to be propositions. The type `PSigma β` is typically
|
||||
written `Σ' a : α, β a` or `(a : α) ×' β a`.
|
||||
|
||||
The reason this is not the default is that this type lives in the universe `Sort (max 1 u v)`,
|
||||
which can cause problems for universe level unification,
|
||||
because the equation `max 1 u v = ?u + 1` has no solution in level arithmetic.
|
||||
`PSigma` is usually only used in automation that constructs pairs of arbitrary types.
|
||||
In practice, this generality leads to universe level constraints that are difficult to solve, so
|
||||
`PSigma` is rarely used in manually-written code. It is usually only used in automation that
|
||||
constructs pairs of arbitrary types.
|
||||
|
||||
To pair a value with a proof that a predicate holds for it, use `Subtype`. To demonstrate that a
|
||||
value exists that satisfies a predicate, use `Exists`. A dependent pair with a proposition as its
|
||||
first component is not typically useful due to proof irrelevance: there's no point in depending on a
|
||||
specific proof because all proofs are equal anyway.
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure PSigma {α : Sort u} (β : α → Sort v) where
|
||||
/-- Constructor for a dependent pair. If `a : α` and `b : β a` then `⟨a, b⟩ : PSigma β`.
|
||||
(This will usually require a type ascription to determine `β`
|
||||
since it is not determined from `a` and `b` alone.) -/
|
||||
/-- Constructs a fully universe-polymorphic dependent pair. -/
|
||||
mk ::
|
||||
/-- The first component of a dependent pair. If `p : @PSigma α β` then `p.1 : α`. -/
|
||||
/--
|
||||
The first component of a dependent pair.
|
||||
-/
|
||||
fst : α
|
||||
/-- The second component of a dependent pair. If `p : PSigma β` then `p.2 : β p.1`. -/
|
||||
/--
|
||||
The second component of a dependent pair. Its type depends on the first component.
|
||||
-/
|
||||
snd : β fst
|
||||
|
||||
/--
|
||||
@@ -514,10 +546,21 @@ export Singleton (singleton)
|
||||
class LawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert α β] [Singleton α β] :
|
||||
Prop where
|
||||
/-- `insert x ∅ = {x}` -/
|
||||
insert_emptyc_eq (x : α) : (insert x ∅ : β) = singleton x
|
||||
export LawfulSingleton (insert_emptyc_eq)
|
||||
insert_empty_eq (x : α) : (insert x ∅ : β) = singleton x
|
||||
export LawfulSingleton (insert_empty_eq)
|
||||
|
||||
attribute [simp] insert_empty_eq
|
||||
|
||||
@[deprecated insert_empty_eq (since := "2025-03-12")]
|
||||
theorem insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
|
||||
[LawfulSingleton α β] (x : α) : (insert x ∅ : β) = singleton x :=
|
||||
insert_empty_eq _
|
||||
|
||||
@[deprecated insert_empty_eq (since := "2025-03-12")]
|
||||
theorem LawfulSingleton.insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
|
||||
[LawfulSingleton α β] (x : α) : (insert x ∅ : β) = singleton x :=
|
||||
insert_empty_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
|
||||
@@ -553,7 +596,12 @@ attribute [extern "lean_task_pure"] Task.pure
|
||||
attribute [extern "lean_task_get_own"] Task.get
|
||||
|
||||
namespace Task
|
||||
/-- Task priority. Tasks with higher priority will always be scheduled before ones with lower priority. -/
|
||||
/--
|
||||
Task priority.
|
||||
|
||||
Tasks with higher priority will always be scheduled before tasks with lower priority. Tasks with a
|
||||
priority greater than `Task.Priority.max` are scheduled on dedicated threads.
|
||||
-/
|
||||
abbrev Priority := Nat
|
||||
|
||||
/-- The default priority for spawned tasks, also the lowest priority: `0`. -/
|
||||
@@ -561,16 +609,18 @@ def Priority.default : Priority := 0
|
||||
/--
|
||||
The highest regular priority for spawned tasks: `8`.
|
||||
|
||||
Spawning a task with a priority higher than `Task.Priority.max` is not an error but
|
||||
will spawn a dedicated worker for the task, see `Task.Priority.dedicated`.
|
||||
Regular priority tasks are placed in a thread pool and worked on according to the priority order.
|
||||
Spawning a task with a priority higher than `Task.Priority.max` is not an error but will spawn a
|
||||
dedicated worker for the task. This is indicated using `Task.Priority.dedicated`. Regular priority
|
||||
tasks are placed in a thread pool and worked on according to their priority order.
|
||||
-/
|
||||
-- see `LEAN_MAX_PRIO`
|
||||
def Priority.max : Priority := 8
|
||||
/--
|
||||
Indicates that a task should be scheduled on a dedicated thread.
|
||||
|
||||
Any priority higher than `Task.Priority.max` will result in the task being scheduled
|
||||
immediately on a dedicated thread. This is particularly useful for long-running and/or
|
||||
I/O-bound tasks since Lean will by default allocate no more non-dedicated workers
|
||||
I/O-bound tasks since Lean will, by default, allocate no more non-dedicated workers
|
||||
than the number of cores to reduce context switches.
|
||||
-/
|
||||
def Priority.dedicated : Priority := 9
|
||||
@@ -676,9 +726,11 @@ Unlike `x ≠ y` (which is notation for `Ne x y`), this is `Bool` valued instead
|
||||
recommended_spelling "bne" for "!=" in [bne, «term_!=_»]
|
||||
|
||||
/--
|
||||
`LawfulBEq α` is a typeclass which asserts that the `BEq α` implementation
|
||||
(which supplies the `a == b` notation) coincides with logical equality `a = b`.
|
||||
In other words, `a == b` implies `a = b`, and `a == a` is true.
|
||||
A Boolean equality test coincides with propositional equality.
|
||||
|
||||
In other words:
|
||||
* `a == b` implies `a = b`.
|
||||
* `a == a` is true.
|
||||
-/
|
||||
class LawfulBEq (α : Type u) [BEq α] : Prop where
|
||||
/-- If `a == b` evaluates to `true`, then `a` and `b` are equal in the logic. -/
|
||||
@@ -813,37 +865,55 @@ noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : S
|
||||
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α → Sort v} {b : α} (h₁ : HEq a b) (h₂ : p a) : p b :=
|
||||
eq_of_heq h₁ ▸ h₂
|
||||
|
||||
/-- Substitution with heterogeneous equality. -/
|
||||
theorem HEq.subst {p : (T : Sort u) → T → Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
|
||||
HEq.ndrecOn h₁ h₂
|
||||
|
||||
/-- Heterogeneous equality is symmetric. -/
|
||||
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
|
||||
h.rec (HEq.refl a)
|
||||
|
||||
/-- Propositionally equal terms are also heterogeneously equal. -/
|
||||
theorem heq_of_eq (h : a = a') : HEq a a' :=
|
||||
Eq.subst h (HEq.refl a)
|
||||
|
||||
/-- Heterogeneous equality is transitive. -/
|
||||
theorem HEq.trans (h₁ : HEq a b) (h₂ : HEq b c) : HEq a c :=
|
||||
HEq.subst h₂ h₁
|
||||
|
||||
/-- Heterogeneous equality precomposes with propositional equality. -/
|
||||
theorem heq_of_heq_of_eq (h₁ : HEq a b) (h₂ : b = b') : HEq a b' :=
|
||||
HEq.trans h₁ (heq_of_eq h₂)
|
||||
|
||||
/-- Heterogeneous equality postcomposes with propositional equality. -/
|
||||
theorem heq_of_eq_of_heq (h₁ : a = a') (h₂ : HEq a' b) : HEq a b :=
|
||||
HEq.trans (heq_of_eq h₁) h₂
|
||||
|
||||
/-- If two terms are heterogeneously equal then their types are propositionally equal. -/
|
||||
theorem type_eq_of_heq (h : HEq a b) : α = β :=
|
||||
h.rec (Eq.refl α)
|
||||
|
||||
end
|
||||
|
||||
/--
|
||||
Rewriting inside `φ` using `Eq.recOn` yields a term that's heterogeneously equal to the original
|
||||
term.
|
||||
-/
|
||||
theorem eqRec_heq {α : Sort u} {φ : α → Sort v} {a a' : α} : (h : a = a') → (p : φ a) → HEq (Eq.recOn (motive := fun x _ => φ x) h p) p
|
||||
| rfl, p => HEq.refl p
|
||||
|
||||
/--
|
||||
If casting a term with `Eq.rec` to another type makes it equal to some other term, then the two
|
||||
terms are heterogeneously equal.
|
||||
-/
|
||||
theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : Eq.rec (motive := fun α _ => α) a h₁ = b) : HEq a b := by
|
||||
subst h₁
|
||||
apply heq_of_eq
|
||||
exact h₂
|
||||
|
||||
/--
|
||||
The result of casting a term with `cast` is heterogeneously equal to the original term.
|
||||
-/
|
||||
theorem cast_heq {α β : Sort u} : (h : α = β) → (a : α) → HEq (cast h a) a
|
||||
| rfl, a => HEq.refl a
|
||||
|
||||
@@ -935,9 +1005,10 @@ namespace Decidable
|
||||
variable {p q : Prop}
|
||||
|
||||
/--
|
||||
Synonym for `dite` (dependent if-then-else). We can construct an element `q`
|
||||
(of any sort, not just a proposition) by cases on whether `p` is true or false,
|
||||
provided `p` is decidable.
|
||||
Construct a `q` if some proposition `p` is decidable, and both the truth and falsity of `p` are
|
||||
sufficient to construct a `q`.
|
||||
|
||||
This is a synonym for `dite`, the dependent if-then-else operator.
|
||||
-/
|
||||
@[macro_inline] def byCases {q : Sort u} [dec : Decidable p] (h1 : p → q) (h2 : ¬p → q) : q :=
|
||||
match dec with
|
||||
@@ -1070,22 +1141,28 @@ theorem nonempty_of_exists {α : Sort u} {p : α → Prop} : Exists (fun x => p
|
||||
/-! # Subsingleton -/
|
||||
|
||||
/--
|
||||
A "subsingleton" is a type with at most one element.
|
||||
In other words, it is either empty, or has a unique element.
|
||||
All propositions are subsingletons because of proof irrelevance, but some other types
|
||||
are subsingletons as well and they inherit many of the same properties as propositions.
|
||||
`Subsingleton α` is a typeclass, so it is usually used as an implicit argument and
|
||||
inferred by typeclass inference.
|
||||
A _subsingleton_ is a type with at most one element. It is either empty or has a unique element.
|
||||
|
||||
All propositions are subsingletons because of proof irrelevance: false propositions are empty, and
|
||||
all proofs of a true proposition are equal to one another. Some non-propositional types are also
|
||||
subsingletons.
|
||||
-/
|
||||
class Subsingleton (α : Sort u) : Prop where
|
||||
/-- Construct a proof that `α` is a subsingleton by showing that any two elements are equal. -/
|
||||
/-- Prove that `α` is a subsingleton by showing that any two elements are equal. -/
|
||||
intro ::
|
||||
/-- Any two elements of a subsingleton are equal. -/
|
||||
allEq : (a b : α) → a = b
|
||||
|
||||
/--
|
||||
If a type is a subsingleton, then all of its elements are equal.
|
||||
-/
|
||||
protected theorem Subsingleton.elim {α : Sort u} [h : Subsingleton α] : (a b : α) → a = b :=
|
||||
h.allEq
|
||||
|
||||
/--
|
||||
If two types are equal and one of them is a subsingleton, then all of their elements are
|
||||
[heterogeneously equal](lean-manual://section/HEq).
|
||||
-/
|
||||
protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (h₂ : α = β) (a : α) (b : β) : HEq a b := by
|
||||
subst h₂
|
||||
apply heq_of_eq
|
||||
@@ -1123,22 +1200,21 @@ theorem recSubsingleton
|
||||
| isFalse h => h₄ h
|
||||
|
||||
/--
|
||||
An equivalence relation `~ : α → α → Prop` is a relation that is:
|
||||
An equivalence relation `r : α → α → Prop` is a relation that is
|
||||
|
||||
* reflexive: `x ~ x`
|
||||
* symmetric: `x ~ y` implies `y ~ x`
|
||||
* transitive: `x ~ y` and `y ~ z` implies `x ~ z`
|
||||
* reflexive: `r x x`,
|
||||
* symmetric: `r x y` implies `r y x`, and
|
||||
* transitive: `r x y` and `r y z` implies `r x z`.
|
||||
|
||||
Equality is an equivalence relation, and equivalence relations share many of
|
||||
the properties of equality. In particular, `Quot α r` is most well behaved
|
||||
when `r` is an equivalence relation, and in this case we use `Quotient` instead.
|
||||
Equality is an equivalence relation, and equivalence relations share many of the properties of
|
||||
equality.
|
||||
-/
|
||||
structure Equivalence {α : Sort u} (r : α → α → Prop) : Prop where
|
||||
/-- An equivalence relation is reflexive: `x ~ x` -/
|
||||
/-- An equivalence relation is reflexive: `r x x` -/
|
||||
refl : ∀ x, r x x
|
||||
/-- An equivalence relation is symmetric: `x ~ y` implies `y ~ x` -/
|
||||
/-- An equivalence relation is symmetric: `r x y` implies `r y x` -/
|
||||
symm : ∀ {x y}, r x y → r y x
|
||||
/-- An equivalence relation is transitive: `x ~ y` and `y ~ z` implies `x ~ z` -/
|
||||
/-- An equivalence relation is transitive: `r x y` and `r y z` implies `r x z` -/
|
||||
trans : ∀ {x y z}, r x y → r y z → r x z
|
||||
|
||||
/-- The empty relation is the relation on `α` which is always `False`. -/
|
||||
@@ -1171,9 +1247,6 @@ inductive Relation.TransGen {α : Sort u} (r : α → α → Prop) : α → α
|
||||
This is the inductive case of the transitive closure. -/
|
||||
| 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
|
||||
|
||||
/-- The transitive closure is transitive. -/
|
||||
theorem Relation.TransGen.trans {α : Sort u} {r : α → α → Prop} {a b c} :
|
||||
TransGen r a b → TransGen r b c → TransGen r a c := by
|
||||
@@ -1210,12 +1283,12 @@ end Subtype
|
||||
section
|
||||
variable {α : Type u} {β : Type v}
|
||||
|
||||
/-- This is not an instance to avoid non-canonical instances. -/
|
||||
@[reducible] def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
|
||||
@[reducible, inherit_doc PSum.inhabitedLeft]
|
||||
def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
|
||||
default := Sum.inl default
|
||||
|
||||
/-- This is not an instance to avoid non-canonical instances. -/
|
||||
@[reducible] def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
|
||||
@[reducible, inherit_doc PSum.inhabitedRight]
|
||||
def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
|
||||
default := Sum.inr default
|
||||
|
||||
instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
|
||||
@@ -1275,7 +1348,12 @@ instance [DecidableEq α] [DecidableEq β] : DecidableEq (α × β) :=
|
||||
instance [BEq α] [BEq β] : BEq (α × β) where
|
||||
beq := fun (a₁, b₁) (a₂, b₂) => a₁ == a₂ && b₁ == b₂
|
||||
|
||||
/-- Lexicographical order for products -/
|
||||
/--
|
||||
Lexicographical order for products.
|
||||
|
||||
Two pairs are lexicographically ordered if their first elements are ordered or if their first
|
||||
elements are equal and their second elements are ordered.
|
||||
-/
|
||||
def Prod.lexLt [LT α] [LT β] (s : α × β) (t : α × β) : Prop :=
|
||||
s.1 < t.1 ∨ (s.1 = t.1 ∧ s.2 < t.2)
|
||||
|
||||
@@ -1291,8 +1369,11 @@ theorem Prod.lexLt_def [LT α] [LT β] (s t : α × β) : (Prod.lexLt s t) = (s.
|
||||
theorem Prod.eta (p : α × β) : (p.1, p.2) = p := rfl
|
||||
|
||||
/--
|
||||
`Prod.map f g : α₁ × β₁ → α₂ × β₂` maps across a pair
|
||||
by applying `f` to the first component and `g` to the second.
|
||||
Transforms a pair by applying functions to both elements.
|
||||
|
||||
Examples:
|
||||
* `(1, 2).map (· + 1) (· * 3) = (2, 6)`
|
||||
* `(1, 2).map toString (· * 3) = ("1", 6)`
|
||||
-/
|
||||
def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂ : Type v₂}
|
||||
(f : α₁ → α₂) (g : β₁ → β₂) : α₁ × β₁ → α₂ × β₂
|
||||
@@ -1308,10 +1389,6 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
|
||||
theorem Exists.of_psigma_prop {α : Sort 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₁
|
||||
@@ -1339,7 +1416,8 @@ instance : DecidableEq PUnit :=
|
||||
|
||||
/--
|
||||
A setoid is a type with a distinguished equivalence relation, denoted `≈`.
|
||||
This is mainly used as input to the `Quotient` type constructor.
|
||||
|
||||
The `Quotient` type constructor requires a `Setoid` instance.
|
||||
-/
|
||||
class Setoid (α : Sort u) where
|
||||
/-- `x ≈ y` is the distinguished equivalence relation of a setoid. -/
|
||||
@@ -1354,12 +1432,15 @@ namespace Setoid
|
||||
|
||||
variable {α : Sort u} [Setoid α]
|
||||
|
||||
/-- A setoid's equivalence relation is reflexive. -/
|
||||
theorem refl (a : α) : a ≈ a :=
|
||||
iseqv.refl a
|
||||
|
||||
/-- A setoid's equivalence relation is symmetric. -/
|
||||
theorem symm {a b : α} (hab : a ≈ b) : b ≈ a :=
|
||||
iseqv.symm hab
|
||||
|
||||
/-- A setoid's equivalence relation is transitive. -/
|
||||
theorem trans {a b c : α} (hab : a ≈ b) (hbc : b ≈ c) : a ≈ c :=
|
||||
iseqv.trans hab hbc
|
||||
|
||||
@@ -1576,34 +1657,21 @@ theorem imp_iff_not (hb : ¬b) : a → b ↔ ¬a := imp_congr_right fun _ => iff
|
||||
|
||||
namespace Quot
|
||||
/--
|
||||
The **quotient axiom**, or at least the nontrivial part of the quotient
|
||||
axiomatization. Quotient types are introduced by the `init_quot` command
|
||||
in `Init.Prelude` which introduces the axioms:
|
||||
The **quotient axiom**, which asserts the equality of elements related by the quotient's relation.
|
||||
|
||||
```
|
||||
opaque Quot {α : Sort u} (r : α → α → Prop) : Sort u
|
||||
The relation `r` does not need to be an equivalence relation to use this axiom. When `r` is not an
|
||||
equivalence relation, the quotient is with respect to the equivalence relation generated by `r`.
|
||||
|
||||
opaque Quot.mk {α : Sort u} (r : α → α → Prop) (a : α) : Quot r
|
||||
`Quot.sound` is part of the built-in primitive quotient type:
|
||||
* `Quot` is the built-in quotient type.
|
||||
* `Quot.mk` places elements of the underlying type `α` into the quotient.
|
||||
* `Quot.lift` allows the definition of functions from the quotient to some other type.
|
||||
* `Quot.ind` is used to write proofs about quotients by assuming that all elements are constructed
|
||||
with `Quot.mk`; it is analogous to the [recursor](lean-manual://section/recursors) for a
|
||||
structure.
|
||||
|
||||
opaque Quot.lift {α : Sort u} {r : α → α → Prop} {β : Sort v} (f : α → β) :
|
||||
(∀ a b : α, r a b → f a = f b) → Quot r → β
|
||||
|
||||
opaque Quot.ind {α : Sort u} {r : α → α → Prop} {β : Quot r → Prop} :
|
||||
(∀ a : α, β (Quot.mk r a)) → ∀ q : Quot r, β q
|
||||
```
|
||||
All of these axioms are true if we assume `Quot α r = α` and `Quot.mk` and
|
||||
`Quot.lift` are identity functions, so they do not add much. However this axiom
|
||||
cannot be explained in that way (it is false for that interpretation), so the
|
||||
real power of quotient types come from this axiom.
|
||||
|
||||
It says that the quotient by `r` maps elements which are related by `r` to equal
|
||||
values in the quotient. Together with `Quot.lift` which says that functions
|
||||
which respect `r` can be lifted to functions on the quotient, we can deduce that
|
||||
`Quot α r` exactly consists of the equivalence classes with respect to `r`.
|
||||
|
||||
It is important to note that `r` need not be an equivalence relation in this axiom.
|
||||
When `r` is not an equivalence relation, we are actually taking a quotient with
|
||||
respect to the equivalence relation generated by `r`.
|
||||
[Quotient types](lean-manual://section/quotients) are described in more detail in the Lean Language
|
||||
Reference.
|
||||
-/
|
||||
axiom sound : ∀ {α : Sort u} {r : α → α → Prop} {a b : α}, r a b → Quot.mk r a = Quot.mk r b
|
||||
|
||||
@@ -1621,8 +1689,17 @@ protected theorem indBeta {α : Sort u} {r : α → α → Prop} {motive : Quot
|
||||
rfl
|
||||
|
||||
/--
|
||||
`Quot.liftOn q f h` is the same as `Quot.lift f h q`. It just reorders
|
||||
the argument `q : Quot r` to be first.
|
||||
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
|
||||
quotient's relation.
|
||||
|
||||
Given a relation `r : α → α → Prop` and a quotient's value `q : Quot r`, applying a `f : α → β`
|
||||
requires a proof `c` that `f` respects `r`. In this case, `Quot.liftOn q f h : β` evaluates
|
||||
to the result of applying `f` to the underlying value in `α` from `q`.
|
||||
|
||||
`Quot.liftOn` is a version of the built-in primitive `Quot.lift` with its parameters re-ordered.
|
||||
|
||||
[Quotient types](lean-manual://section/quotients) are described in more detail in the Lean Language
|
||||
Reference.
|
||||
-/
|
||||
protected abbrev liftOn {α : Sort u} {β : Sort v} {r : α → α → Prop}
|
||||
(q : Quot r) (f : α → β) (c : (a b : α) → r a b → f a = f b) : β :=
|
||||
@@ -1663,12 +1740,19 @@ protected theorem liftIndepPr1
|
||||
exact rfl
|
||||
|
||||
/--
|
||||
Dependent recursion principle for `Quot`. This constructor can be tricky to use,
|
||||
so you should consider the simpler versions if they apply:
|
||||
* `Quot.lift`, for nondependent functions
|
||||
* `Quot.ind`, for theorems / proofs of propositions about quotients
|
||||
* `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
|
||||
A dependent recursion principle for `Quot`. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
* `Quot.lift` is useful for defining non-dependent functions.
|
||||
* `Quot.ind` is useful for proving theorems about quotients.
|
||||
* `Quot.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quot.hrecOn` uses [heterogeneous equality](lean-manual://section/HEq) instead of rewriting with
|
||||
`Quot.sound`.
|
||||
|
||||
`Quot.recOn` is a version of this recursor that takes the quotient parameter first.
|
||||
-/
|
||||
@[elab_as_elim] protected abbrev rec
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
@@ -1676,7 +1760,22 @@ so you should consider the simpler versions if they apply:
|
||||
(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
|
||||
/--
|
||||
A dependent recursion principle for `Quot` that takes the quotient first. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
* `Quot.lift` is useful for defining non-dependent functions.
|
||||
* `Quot.ind` is useful for proving theorems about quotients.
|
||||
* `Quot.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quot.hrecOn` uses [heterogeneous equality](lean-manual://section/HEq) instead of rewriting with
|
||||
`Quot.sound`.
|
||||
|
||||
`Quot.rec` is a version of this recursor that takes the quotient parameter last.
|
||||
-/
|
||||
@[elab_as_elim] 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)
|
||||
@@ -1684,8 +1783,13 @@ so you should consider the simpler versions if they apply:
|
||||
q.rec f h
|
||||
|
||||
/--
|
||||
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.
|
||||
An alternative induction principle for quotients that can be used when the target type is a
|
||||
subsingleton, in which all elements are equal.
|
||||
|
||||
In these cases, the proof that the function respects the quotient's relation is trivial, so any
|
||||
function can be lifted.
|
||||
|
||||
`Quot.rec` does not assume that the type is a subsingleton.
|
||||
-/
|
||||
@[elab_as_elim] protected abbrev recOnSubsingleton
|
||||
[h : (a : α) → Subsingleton (motive (Quot.mk r a))]
|
||||
@@ -1697,9 +1801,11 @@ In this case the quotient's side condition is trivial so any function can be lif
|
||||
apply Subsingleton.elim
|
||||
|
||||
/--
|
||||
Heterogeneous dependent recursion principle for a quotient.
|
||||
This may be easier to work with since it uses `HEq` instead of
|
||||
an `Eq.ndrec` in the hypothesis.
|
||||
A dependent recursion principle for `Quot` that uses [heterogeneous
|
||||
equality](lean-manual://section/HEq), analogous to a [recursor](lean-manual://section/recursors) for
|
||||
a structure.
|
||||
|
||||
`Quot.recOn` is a version of this recursor that uses `Eq` instead of `HEq`.
|
||||
-/
|
||||
protected abbrev hrecOn
|
||||
(q : Quot r)
|
||||
@@ -1715,48 +1821,101 @@ end Quot
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
`Quotient α s` is the same as `Quot α r`, but it is specialized to a setoid `s`
|
||||
(that is, an equivalence relation) instead of an arbitrary relation.
|
||||
Prefer `Quotient` over `Quot` if your relation is actually an equivalence relation.
|
||||
Quotient types coarsen the propositional equality for a type so that terms related by some
|
||||
equivalence relation are considered equal. The equivalence relation is given by an instance of
|
||||
`Setoid`.
|
||||
|
||||
Set-theoretically, `Quotient s` can seen as the set of equivalence classes of `α` modulo the
|
||||
`Setoid` instance's relation `s.r`. Functions from `Quotient s` must prove that they respect `s.r`:
|
||||
to define a function `f : Quotient s → β`, it is necessary to provide `f' : α → β` and prove that
|
||||
for all `x : α` and `y : α`, `s.r x y → f' x = f' y`. `Quotient.lift` implements this operation.
|
||||
|
||||
The key quotient operators are:
|
||||
* `Quotient.mk` places elements of the underlying type `α` into the quotient.
|
||||
* `Quotient.lift` allows the definition of functions from the quotient to some other type.
|
||||
* `Quotient.sound` asserts the equality of elements related by `r`
|
||||
* `Quotient.ind` is used to write proofs about quotients by assuming that all elements are
|
||||
constructed with `Quotient.mk`.
|
||||
|
||||
`Quotient` is built on top of the primitive quotient type `Quot`, which does not require a proof
|
||||
that the relation is an equivalence relation. `Quotient` should be used instead of `Quot` for
|
||||
relations that actually are equivalence relations.
|
||||
-/
|
||||
def Quotient {α : Sort u} (s : Setoid α) :=
|
||||
@Quot α Setoid.r
|
||||
|
||||
namespace Quotient
|
||||
|
||||
/-- The canonical quotient map into a `Quotient`. -/
|
||||
/--
|
||||
Places an element of a type into the quotient that equates terms according to an equivalence
|
||||
relation.
|
||||
|
||||
The setoid instance is provided explicitly. `Quotient.mk'` uses instance synthesis instead.
|
||||
|
||||
Given `v : α`, `Quotient.mk s v : Quotient s` is like `v`, except all observations of `v`'s value
|
||||
must respect `s.r`. `Quotient.lift` allows values in a quotient to be mapped to other types, so long
|
||||
as the mapping respects `s.r`.
|
||||
-/
|
||||
@[inline]
|
||||
protected def mk {α : Sort u} (s : Setoid α) (a : α) : Quotient s :=
|
||||
Quot.mk Setoid.r a
|
||||
|
||||
/--
|
||||
The canonical quotient map into a `Quotient`.
|
||||
(This synthesizes the setoid by typeclass inference.)
|
||||
Places an element of a type into the quotient that equates terms according to an equivalence
|
||||
relation.
|
||||
|
||||
The equivalence relation is found by synthesizing a `Setoid` instance. `Quotient.mk` instead expects
|
||||
the instance to be provided explicitly.
|
||||
|
||||
Given `v : α`, `Quotient.mk' v : Quotient s` is like `v`, except all observations of `v`'s value
|
||||
must respect `s.r`. `Quotient.lift` allows values in a quotient to be mapped to other types, so long
|
||||
as the mapping respects `s.r`.
|
||||
|
||||
-/
|
||||
protected def mk' {α : Sort u} [s : Setoid α] (a : α) : Quotient s :=
|
||||
Quotient.mk s a
|
||||
|
||||
/--
|
||||
The analogue of `Quot.sound`: If `a` and `b` are related by the equivalence relation,
|
||||
then they have equal equivalence classes.
|
||||
The **quotient axiom**, which asserts the equality of elements related in the setoid.
|
||||
|
||||
Because `Quotient` is built on a lower-level type `Quot`, `Quotient.sound` is implemented as a
|
||||
theorem. It is derived from `Quot.sound`, the soundness axiom for the lower-level quotient type
|
||||
`Quot`.
|
||||
-/
|
||||
theorem sound {α : Sort u} {s : Setoid α} {a b : α} : a ≈ b → Quotient.mk s a = Quotient.mk s b :=
|
||||
Quot.sound
|
||||
|
||||
/--
|
||||
The analogue of `Quot.lift`: if `f : α → β` respects the equivalence relation `≈`,
|
||||
then it lifts to a function on `Quotient s` such that `lift f h (mk a) = f a`.
|
||||
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
|
||||
quotient's equivalence relation.
|
||||
|
||||
Given `s : Setoid α` and a quotient `Quotient s`, applying a function `f : α → β` requires a proof
|
||||
`h` that `f` respects the equivalence relation `s.r`. In this case, the function
|
||||
`Quotient.lift f h : Quotient s → β` computes the same values as `f`.
|
||||
|
||||
`Quotient.liftOn` is a version of this operation that takes the quotient value as its first explicit
|
||||
parameter.
|
||||
-/
|
||||
protected abbrev lift {α : Sort u} {β : Sort v} {s : Setoid α} (f : α → β) : ((a b : α) → a ≈ b → f a = f b) → Quotient s → β :=
|
||||
Quot.lift f
|
||||
|
||||
/-- The analogue of `Quot.ind`: every element of `Quotient s` is of the form `Quotient.mk s a`. -/
|
||||
/--
|
||||
A reasoning principle for quotients that allows proofs about quotients to assume that all values are
|
||||
constructed with `Quotient.mk`.
|
||||
-/
|
||||
protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s → Prop} : ((a : α) → motive (Quotient.mk s a)) → (q : Quotient s) → motive q :=
|
||||
Quot.ind
|
||||
|
||||
/--
|
||||
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`.
|
||||
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
|
||||
quotient's equivalence relation.
|
||||
|
||||
Given `s : Setoid α` and a quotient value `q : Quotient s`, applying a function `f : α → β` requires
|
||||
a proof `c` that `f` respects the equivalence relation `s.r`. In this case, the term
|
||||
`Quotient.liftOn q f h : β` reduces to the result of applying `f` to the underlying `α` value.
|
||||
|
||||
`Quotient.lift` is a version of this operation that takes the quotient value last, rather than
|
||||
first.
|
||||
-/
|
||||
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
|
||||
@@ -1777,7 +1936,21 @@ variable {α : Sort u}
|
||||
variable {s : Setoid α}
|
||||
variable {motive : Quotient s → Sort v}
|
||||
|
||||
/-- The analogue of `Quot.rec` for `Quotient`. See `Quot.rec`. -/
|
||||
/--
|
||||
A dependent recursion principle for `Quotient`. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
|
||||
* `Quotient.lift` is useful for defining non-dependent functions.
|
||||
* `Quotient.ind` is useful for proving theorems about quotients.
|
||||
* `Quotient.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quotient.hrecOn` uses heterogeneous equality instead of rewriting with `Quotient.sound`.
|
||||
|
||||
`Quotient.recOn` is a version of this recursor that takes the quotient parameter first.
|
||||
-/
|
||||
@[inline, elab_as_elim]
|
||||
protected def rec
|
||||
(f : (a : α) → motive (Quotient.mk s a))
|
||||
@@ -1786,7 +1959,21 @@ protected def rec
|
||||
: motive q :=
|
||||
Quot.rec f h q
|
||||
|
||||
/-- The analogue of `Quot.recOn` for `Quotient`. See `Quot.recOn`. -/
|
||||
/--
|
||||
A dependent recursion principle for `Quotient`. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
|
||||
* `Quotient.lift` is useful for defining non-dependent functions.
|
||||
* `Quotient.ind` is useful for proving theorems about quotients.
|
||||
* `Quotient.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quotient.hrecOn` uses heterogeneous equality instead of rewriting with `Quotient.sound`.
|
||||
|
||||
`Quotient.rec` is a version of this recursor that takes the quotient parameter last.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev recOn
|
||||
(q : Quotient s)
|
||||
@@ -1795,7 +1982,15 @@ protected abbrev recOn
|
||||
: motive q :=
|
||||
Quot.recOn q f h
|
||||
|
||||
/-- The analogue of `Quot.recOnSubsingleton` for `Quotient`. See `Quot.recOnSubsingleton`. -/
|
||||
/--
|
||||
An alternative recursion or induction principle for quotients that can be used when the target type
|
||||
is a subsingleton, in which all elements are equal.
|
||||
|
||||
In these cases, the proof that the function respects the quotient's equivalence relation is trivial,
|
||||
so any function can be lifted.
|
||||
|
||||
`Quotient.rec` does not assume that the target type is a subsingleton.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev recOnSubsingleton
|
||||
[h : (a : α) → Subsingleton (motive (Quotient.mk s a))]
|
||||
@@ -1804,7 +1999,13 @@ protected abbrev recOnSubsingleton
|
||||
: motive q :=
|
||||
Quot.recOnSubsingleton (h := h) q f
|
||||
|
||||
/-- The analogue of `Quot.hrecOn` for `Quotient`. See `Quot.hrecOn`. -/
|
||||
/--
|
||||
A dependent recursion principle for `Quotient` that uses [heterogeneous
|
||||
equality](lean-manual://section/HEq), analogous to a [recursor](lean-manual://section/recursors) for
|
||||
a structure.
|
||||
|
||||
`Quotient.recOn` is a version of this recursor that uses `Eq` instead of `HEq`.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev hrecOn
|
||||
(q : Quotient s)
|
||||
@@ -1819,7 +2020,13 @@ universe uA uB uC
|
||||
variable {α : Sort uA} {β : Sort uB} {φ : Sort uC}
|
||||
variable {s₁ : Setoid α} {s₂ : Setoid β}
|
||||
|
||||
/-- Lift a binary function to a quotient on both arguments. -/
|
||||
/--
|
||||
Lifts a binary function from the underlying types to a binary function on quotients. The function
|
||||
must respect both quotients' equivalence relations.
|
||||
|
||||
`Quotient.lift` is a version of this operation for unary functions. `Quotient.liftOn₂` is a version
|
||||
that take the quotient parameters first.
|
||||
-/
|
||||
protected abbrev lift₂
|
||||
(f : α → β → φ)
|
||||
(c : (a₁ : α) → (b₁ : β) → (a₂ : α) → (b₂ : β) → a₁ ≈ a₂ → b₁ ≈ b₂ → f a₁ b₁ = f a₂ b₂)
|
||||
@@ -1830,7 +2037,13 @@ protected abbrev lift₂
|
||||
induction q₂ using Quotient.ind
|
||||
apply c; assumption; apply Setoid.refl
|
||||
|
||||
/-- Lift a binary function to a quotient on both arguments. -/
|
||||
/--
|
||||
Lifts a binary function from the underlying types to a binary function on quotients. The function
|
||||
must respect both quotients' equivalence relations.
|
||||
|
||||
`Quotient.liftOn` is a version of this operation for unary functions. `Quotient.lift₂` is a version
|
||||
that take the quotient parameters last.
|
||||
-/
|
||||
protected abbrev liftOn₂
|
||||
(q₁ : Quotient s₁)
|
||||
(q₂ : Quotient s₂)
|
||||
@@ -1895,6 +2108,9 @@ private theorem rel.refl {s : Setoid α} (q : Quotient s) : rel q q :=
|
||||
private theorem rel_of_eq {s : Setoid α} {q₁ q₂ : Quotient s} : q₁ = q₂ → rel q₁ q₂ :=
|
||||
fun h => Eq.ndrecOn h (rel.refl q₁)
|
||||
|
||||
/--
|
||||
If two values are equal in a quotient, then they are related by its equivalence relation.
|
||||
-/
|
||||
theorem exact {s : Setoid α} {a b : α} : Quotient.mk s a = Quotient.mk s b → a ≈ b :=
|
||||
fun h => rel_of_eq h
|
||||
|
||||
@@ -1905,7 +2121,13 @@ universe uA uB uC
|
||||
variable {α : Sort uA} {β : Sort uB}
|
||||
variable {s₁ : Setoid α} {s₂ : Setoid β}
|
||||
|
||||
/-- Lift a binary function to a quotient on both arguments. -/
|
||||
/--
|
||||
An alternative induction or recursion operator for defining binary operations on quotients that can
|
||||
be used when the target type is a subsingleton.
|
||||
|
||||
In these cases, the proof that the function respects the quotient's equivalence relation is trivial,
|
||||
so any function can be lifted.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev recOnSubsingleton₂
|
||||
{motive : Quotient s₁ → Quotient s₂ → Sort uC}
|
||||
@@ -1937,16 +2159,13 @@ instance Quotient.decidableEq {α : Sort u} {s : Setoid α} [d : ∀ (a b : α),
|
||||
/-! # Function extensionality -/
|
||||
|
||||
/--
|
||||
**Function extensionality** is the statement that if two functions take equal values
|
||||
every point, then the functions themselves are equal: `(∀ x, f x = g x) → f = g`.
|
||||
It is called "extensionality" because it talks about how to prove two objects are equal
|
||||
based on the properties of the object (compare with set extensionality,
|
||||
which is `(∀ x, x ∈ s ↔ x ∈ t) → s = t`).
|
||||
**Function extensionality.** If two functions return equal results for all possible arguments, then
|
||||
they are equal.
|
||||
|
||||
This is often an axiom in dependent type theory systems, because it cannot be proved
|
||||
from the core logic alone. However in lean's type theory this follows from the existence
|
||||
of quotient types (note the `Quot.sound` in the proof, as well as the `show` line
|
||||
which makes use of the definitional equality `Quot.lift f h (Quot.mk x) = f x`).
|
||||
It is called “extensionality” because it provides a way to prove two objects equal based on the
|
||||
properties of the underlying mathematical functions, rather than based on the syntax used to denote
|
||||
them. Function extensionality is a theorem that can be [proved using quotient
|
||||
types](lean-manual://section/quotient-funext).
|
||||
-/
|
||||
theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
|
||||
(h : ∀ x, f x = g x) : f = g := by
|
||||
@@ -1965,28 +2184,39 @@ instance Pi.instSubsingleton {α : Sort u} {β : α → Sort v} [∀ a, Subsingl
|
||||
/-! # Squash -/
|
||||
|
||||
/--
|
||||
`Squash α` is the quotient of `α` by the always true relation.
|
||||
It is empty if `α` is empty, otherwise it is a singleton.
|
||||
(Thus it is unconditionally a `Subsingleton`.)
|
||||
It is the "universal `Subsingleton`" mapped from `α`.
|
||||
The quotient of `α` by the universal relation. The elements of `Squash α` are those of `α`, but all
|
||||
of them are equal and cannot be distinguished.
|
||||
|
||||
It is similar to `Nonempty α`, which has the same properties, but unlike
|
||||
`Nonempty` this is a `Type u`, that is, it is "data", and the compiler
|
||||
represents an element of `Squash α` the same as `α` itself
|
||||
(as compared to `Nonempty α`, whose elements are represented by a dummy value).
|
||||
`Squash α` is a `Subsingleton`: it is empty if `α` is empty, otherwise it has just one element. It
|
||||
is the “universal `Subsingleton`” mapped from `α`.
|
||||
|
||||
`Squash.lift` will extract a value in any subsingleton `β` from a function on `α`,
|
||||
while `Nonempty.rec` can only do the same when `β` is a proposition.
|
||||
`Nonempty α` also has these properties. It is a proposition, which means that its elements (i.e.
|
||||
proofs) are erased from compiled code and represented by a dummy value. `Squash α` is a `Type u`,
|
||||
and its representation in compiled code is identical to that of `α`.
|
||||
|
||||
Consequently, `Squash.lift` may extract an `α` value into any subsingleton type `β`, while
|
||||
`Nonempty.rec` can only do the same when `β` is a proposition.
|
||||
-/
|
||||
def Squash (α : Sort u) := Quot (fun (_ _ : α) => True)
|
||||
|
||||
/-- The canonical quotient map into `Squash α`. -/
|
||||
/--
|
||||
Places a value into its squash type, in which it cannot be distinguished from any other.
|
||||
-/
|
||||
def Squash.mk {α : Sort u} (x : α) : Squash α := Quot.mk _ x
|
||||
|
||||
/--
|
||||
A reasoning principle that allows proofs about squashed types to assume that all values are
|
||||
constructed with `Squash.mk`.
|
||||
-/
|
||||
theorem Squash.ind {α : Sort u} {motive : Squash α → Prop} (h : ∀ (a : α), motive (Squash.mk a)) : ∀ (q : Squash α), motive q :=
|
||||
Quot.ind h
|
||||
|
||||
/-- If `β` is a subsingleton, then a function `α → β` lifts to `Squash α → β`. -/
|
||||
/--
|
||||
Extracts a squashed value into any subsingleton type.
|
||||
|
||||
If `β` is a subsingleton, a function `α → β` cannot distinguish between elements of `α` and thus
|
||||
automatically respects the universal relation that `Squash` quotients with.
|
||||
-/
|
||||
@[inline] def Squash.lift {α β} [Subsingleton β] (s : Squash α) (f : α → β) : β :=
|
||||
Quot.lift f (fun _ _ _ => Subsingleton.elim _ _) s
|
||||
|
||||
@@ -2068,6 +2298,12 @@ So, you are mainly losing the capability of type checking your development using
|
||||
-/
|
||||
axiom ofReduceNat (a b : Nat) (h : reduceNat a = b) : a = b
|
||||
|
||||
|
||||
/--
|
||||
The term `opaqueId x` will not be reduced by the kernel.
|
||||
-/
|
||||
opaque opaqueId {α : Sort u} (x : α) : α := x
|
||||
|
||||
end Lean
|
||||
|
||||
@[simp] theorem ge_iff_le [LE α] {x y : α} : x ≥ y ↔ y ≤ x := Iff.rfl
|
||||
|
||||
@@ -15,13 +15,12 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
namespace Array
|
||||
|
||||
/--
|
||||
`O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
|
||||
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
|
||||
but is defined only when all members of `l` satisfy `P`, using the proof
|
||||
to apply `f`.
|
||||
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over
|
||||
an array `xs : Array α`, given a proof that every element of `xs` in fact satisfies `P`.
|
||||
|
||||
We replace this at runtime with a more efficient version via the `csimp` lemma `pmap_eq_pmapImpl`.
|
||||
`Array.pmap`, named for “partial map,” is the equivalent of `Array.map` for such partial functions.
|
||||
-/
|
||||
|
||||
def pmap {P : α → Prop} (f : ∀ a, P a → β) (xs : Array α) (H : ∀ a ∈ xs, P a) : Array β :=
|
||||
(xs.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
|
||||
|
||||
@@ -32,14 +31,27 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
@[inline] private unsafe def attachWithImpl
|
||||
(xs : Array α) (P : α → Prop) (_ : ∀ x ∈ xs, P x) : Array {x // P x} := unsafeCast xs
|
||||
|
||||
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
|
||||
with the same elements but in the type `{x // P x}`. -/
|
||||
/--
|
||||
“Attaches” individual proofs to an array of values that satisfy a predicate `P`, returning an array
|
||||
of elements in the corresponding subtype `{ x // P x }`.
|
||||
|
||||
`O(1)`.
|
||||
-/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(xs : Array α) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Array {x // P x} :=
|
||||
⟨xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)⟩
|
||||
|
||||
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
|
||||
with the same elements but in the type `{x // x ∈ xs}`. -/
|
||||
/--
|
||||
“Attaches” the proof that the elements of `xs` are in fact elements of `xs`, producing a new array with
|
||||
the same elements but in the subtype `{ x // x ∈ xs }`.
|
||||
|
||||
`O(1)`.
|
||||
|
||||
This function is primarily used to allow definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
|
||||
`Array.map`) to prove that an value taken from a list is smaller than the list. This allows the
|
||||
well-founded recursion mechanism to prove that the function terminates.
|
||||
-/
|
||||
@[inline] def attach (xs : Array α) : Array {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α → Prop} {H : ∀ x ∈ l.toArray, P x} :
|
||||
@@ -542,15 +554,25 @@ Further, we provide simp lemmas that push `unattach` inwards.
|
||||
-/
|
||||
|
||||
/--
|
||||
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
|
||||
It is introduced as in intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
Maps an array of terms in a subtype to the corresponding terms in the type by forgetting that they
|
||||
satisfy the predicate.
|
||||
|
||||
If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
|
||||
This is the inverse of `Array.attachWith` and a synonym for `xs.map (·.val)`.
|
||||
|
||||
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
|
||||
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
This function is usually inserted automatically by Lean as an intermediate step while proving
|
||||
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
|
||||
the elaboration of definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
|
||||
state, the right approach is usually the tactic `simp [Array.unattach, -Array.map_subtype]`.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array α := xs.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem unattach_push {p : α → Prop} {a : { x // p x }} {xs : Array { x // p x }} :
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Array.map_push]
|
||||
@@ -721,10 +743,13 @@ and simplifies these to the function directly taking the value.
|
||||
List.map_toArray, List.map_flatten, map_subtype, map_id_fun', List.unattach_toArray, mk.injEq]
|
||||
simp only [List.unattach]
|
||||
|
||||
@[simp] theorem unattach_mkArray {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(Array.mkArray n x).unattach = Array.mkArray n x.1 := by
|
||||
@[simp] theorem unattach_replicate {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(Array.replicate n x).unattach = Array.replicate n x.1 := by
|
||||
simp [unattach]
|
||||
|
||||
@[deprecated unattach_replicate (since := "2025-03-18")]
|
||||
abbrev unattach_mkArray := @unattach_replicate
|
||||
|
||||
/-! ### Well-founded recursion preprocessing setup -/
|
||||
|
||||
@[wf_preprocess] theorem Array.map_wfParam (xs : Array α) (f : α → β) :
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,11 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
|
||||
theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by
|
||||
simp
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` to every element in the array, left-to-right, and returns the array
|
||||
of results. Furthermore, the resulting array's type guarantees that it contains the same number of
|
||||
elements as the input array.
|
||||
-/
|
||||
def Array.mapM' [Monad m] (f : α → m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
|
||||
go 0 ⟨mkEmpty as.size, rfl⟩ (by simp)
|
||||
where
|
||||
@@ -66,11 +71,19 @@ where
|
||||
return as
|
||||
|
||||
/--
|
||||
Monomorphic `Array.mapM`. The internal implementation uses pointer equality, and does not allocate a new array
|
||||
if the result of each `f a` is a pointer equal value `a`.
|
||||
Applies a monadic function to each element of an array, returning the array of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new array if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
-/
|
||||
@[implemented_by mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α → m α) : m (Array α) :=
|
||||
as.mapM f
|
||||
|
||||
/--
|
||||
Applies a function to each element of an array, returning the array of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new array if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
-/
|
||||
@[inline] def Array.mapMono (as : Array α) (f : α → α) : Array α :=
|
||||
Id.run <| as.mapMonoM f
|
||||
|
||||
@@ -29,6 +29,16 @@ namespace Array
|
||||
else found (some a)
|
||||
termination_by lo hi => hi.1 - lo.1
|
||||
|
||||
/--
|
||||
Binary search for an element equivalent to `k` in the sorted array `as`. Returns the element from
|
||||
the array, if it is found, or `none` otherwise.
|
||||
|
||||
The array `as` must be sorted according to the comparison operator `lt`, which should be a total
|
||||
order.
|
||||
|
||||
The optional parameters `lo` and `hi` determine the region of the array indices to be searched. Both
|
||||
are inclusive, and default to searching the entire array.
|
||||
-/
|
||||
@[inline] def binSearch {α : Type} (as : Array α) (k : α) (lt : α → α → Bool) (lo := 0) (hi := as.size - 1) : Option α :=
|
||||
if h : lo < as.size then
|
||||
let hi := if hi < as.size then hi else as.size - 1
|
||||
@@ -39,6 +49,16 @@ termination_by lo hi => hi.1 - lo.1
|
||||
else
|
||||
none
|
||||
|
||||
/--
|
||||
Binary search for an element equivalent to `k` in the sorted array `as`. Returns `true` if the
|
||||
element is found, or `false` otherwise.
|
||||
|
||||
The array `as` must be sorted according to the comparison operator `lt`, which should be a total
|
||||
order.
|
||||
|
||||
The optional parameters `lo` and `hi` determine the region of the array indices to be searched. Both
|
||||
are inclusive, and default to searching the entire array.
|
||||
-/
|
||||
@[inline] def binSearchContains {α : Type} (as : Array α) (k : α) (lt : α → α → Bool) (lo := 0) (hi := as.size - 1) : Bool :=
|
||||
if h : lo < as.size then
|
||||
let hi := if hi < as.size then hi else as.size - 1
|
||||
@@ -68,6 +88,16 @@ termination_by lo hi => hi.1 - lo.1
|
||||
as.modifyM mid <| fun v => merge v
|
||||
termination_by lo hi => hi.1 - lo.1
|
||||
|
||||
/--
|
||||
Inserts an element `k` into a sorted array `as` such that the resulting array is sorted.
|
||||
|
||||
The ordering predicate `lt` should be a total order on elements, and the array `as` should be sorted
|
||||
with respect to `lt`.
|
||||
|
||||
If an element that `lt` equates to `k` is already present in `as`, then `merge` is applied to the
|
||||
existing element to determine the value of that position in the resulting array. If no element equal
|
||||
to `k` is present, then `add` is used to determine the value to be inserted.
|
||||
-/
|
||||
@[specialize] def binInsertM {α : Type u} {m : Type u → Type v} [Monad m]
|
||||
(lt : α → α → Bool)
|
||||
(merge : α → m α)
|
||||
@@ -81,6 +111,21 @@ termination_by lo hi => hi.1 - lo.1
|
||||
else if !lt k as[as.size - 1] then as.modifyM (as.size - 1) <| merge
|
||||
else binInsertAux lt merge add as k ⟨0, by omega⟩ ⟨as.size - 1, by omega⟩ (by simp) (by simpa using h')
|
||||
|
||||
/--
|
||||
Inserts an element into a sorted array such that the resulting array is sorted. If the element is
|
||||
already present in the array, it is not inserted.
|
||||
|
||||
The ordering predicate `lt` should be a total order on elements, and the array `as` should be sorted
|
||||
with respect to `lt`.
|
||||
|
||||
`Array.binInsertM` is a more general operator that provides greater control over the handling of
|
||||
duplicate elements in addition to running in a monad.
|
||||
|
||||
Examples:
|
||||
* `#[0, 1, 3, 5].binInsert (· < ·) 2 = #[0, 1, 2, 3, 5]`
|
||||
* `#[0, 1, 3, 5].binInsert (· < ·) 1 = #[0, 1, 3, 5]`
|
||||
* `#[].binInsert (· < ·) 1 = #[1]`
|
||||
-/
|
||||
@[inline] def binInsert {α : Type u} (lt : α → α → Bool) (as : Array α) (k : α) : Array α :=
|
||||
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k
|
||||
|
||||
|
||||
@@ -88,10 +88,13 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem countP_mkArray (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (mkArray n a) = if p a then n else 0 := by
|
||||
theorem countP_replicate (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (replicate n a) = if p a then n else 0 := by
|
||||
simp [← List.toArray_replicate, List.countP_replicate]
|
||||
|
||||
@[deprecated countP_replicate (since := "2025-03-18")]
|
||||
abbrev countP_mkArray := @countP_replicate
|
||||
|
||||
theorem boole_getElem_le_countP (p : α → Bool) (xs : Array α) (i : Nat) (h : i < xs.size) :
|
||||
(if p xs[i] then 1 else 0) ≤ xs.countP p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -241,25 +244,34 @@ theorem count_eq_size {xs : Array α} : count a xs = xs.size ↔ ∀ b ∈ xs, a
|
||||
· simpa using h b hb
|
||||
· rw [h b hb, beq_self_eq_true]
|
||||
|
||||
@[simp] theorem count_mkArray_self (a : α) (n : Nat) : count a (mkArray n a) = n := by
|
||||
@[simp] theorem count_replicate_self (a : α) (n : Nat) : count a (replicate n a) = n := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
theorem count_mkArray (a b : α) (n : Nat) : count a (mkArray n b) = if b == a then n else 0 := by
|
||||
@[deprecated count_replicate_self (since := "2025-03-18")]
|
||||
abbrev count_mkArray_self := @count_replicate_self
|
||||
|
||||
theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b == a then n else 0 := by
|
||||
simp [← List.toArray_replicate, List.count_replicate]
|
||||
|
||||
theorem filter_beq (xs : Array α) (a : α) : xs.filter (· == a) = mkArray (count a xs) a := by
|
||||
@[deprecated count_replicate (since := "2025-03-18")]
|
||||
abbrev count_mkArray := @count_replicate
|
||||
|
||||
theorem filter_beq (xs : Array α) (a : α) : xs.filter (· == a) = replicate (count a xs) a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.filter_beq]
|
||||
|
||||
theorem filter_eq {α} [DecidableEq α] (xs : Array α) (a : α) : xs.filter (· = a) = mkArray (count a xs) a :=
|
||||
theorem filter_eq {α} [DecidableEq α] (xs : Array α) (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
|
||||
filter_beq xs a
|
||||
|
||||
theorem mkArray_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
|
||||
mkArray (count a xs) a = xs := by
|
||||
theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
|
||||
replicate (count a xs) a = xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.replicate_count_eq_of_count_eq_length (by simpa using h)]
|
||||
|
||||
@[deprecated replicate_count_eq_of_count_eq_size (since := "2025-03-18")]
|
||||
abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
|
||||
|
||||
@[simp] theorem count_filter {xs : Array α} (h : p a) : count a (filter p xs) = count a xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.count_filter, h]
|
||||
|
||||
@@ -21,7 +21,7 @@ open Nat
|
||||
|
||||
/-! ### eraseP -/
|
||||
|
||||
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := rfl
|
||||
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := by simp
|
||||
|
||||
theorem eraseP_of_forall_mem_not {xs : Array α} (h : ∀ a, a ∈ xs → ¬p a) : xs.eraseP p = xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -122,21 +122,30 @@ theorem eraseP_append {xs : Array α} {ys : Array α} :
|
||||
simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray]
|
||||
split <;> simp
|
||||
|
||||
theorem eraseP_mkArray (n : Nat) (a : α) (p : α → Bool) :
|
||||
(mkArray n a).eraseP p = if p a then mkArray (n - 1) a else mkArray n a := by
|
||||
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
|
||||
simp only [← List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
|
||||
split <;> simp
|
||||
|
||||
@[simp] theorem eraseP_mkArray_of_pos {n : Nat} {a : α} (h : p a) :
|
||||
(mkArray n a).eraseP p = mkArray (n - 1) a := by
|
||||
@[deprecated eraseP_replicate (since := "2025-03-18")]
|
||||
abbrev eraseP_mkArray := @eraseP_replicate
|
||||
|
||||
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
|
||||
(replicate n a).eraseP p = replicate (n - 1) a := by
|
||||
simp only [← List.toArray_replicate, List.eraseP_toArray]
|
||||
simp [h]
|
||||
|
||||
@[simp] theorem eraseP_mkArray_of_neg {n : Nat} {a : α} (h : ¬p a) :
|
||||
(mkArray n a).eraseP p = mkArray n a := by
|
||||
@[deprecated eraseP_replicate_of_pos (since := "2025-03-18")]
|
||||
abbrev eraseP_mkArray_of_pos := @eraseP_replicate_of_pos
|
||||
|
||||
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
|
||||
(replicate n a).eraseP p = replicate n a := by
|
||||
simp only [← List.toArray_replicate, List.eraseP_toArray]
|
||||
simp [h]
|
||||
|
||||
@[deprecated eraseP_replicate_of_neg (since := "2025-03-18")]
|
||||
abbrev eraseP_mkArray_of_neg := @eraseP_replicate_of_neg
|
||||
|
||||
theorem eraseP_eq_iff {p} {xs : Array α} :
|
||||
xs.eraseP p = ys ↔
|
||||
((∀ a ∈ xs, ¬ p a) ∧ xs = ys) ∨
|
||||
@@ -243,12 +252,15 @@ theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
|
||||
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
|
||||
split <;> simp
|
||||
|
||||
theorem erase_mkArray [LawfulBEq α] (n : Nat) (a b : α) :
|
||||
(mkArray n a).erase b = if b == a then mkArray (n - 1) a else mkArray n a := by
|
||||
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
|
||||
simp only [← List.toArray_replicate, List.erase_toArray]
|
||||
simp only [List.erase_replicate, beq_iff_eq, List.toArray_replicate]
|
||||
split <;> simp
|
||||
|
||||
@[deprecated erase_replicate (since := "2025-03-18")]
|
||||
abbrev erase_mkArray := @erase_replicate
|
||||
|
||||
theorem erase_comm [LawfulBEq α] (a b : α) (xs : Array α) :
|
||||
(xs.erase a).erase b = (xs.erase b).erase a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -268,16 +280,22 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
|
||||
· left; simp_all
|
||||
· right; refine ⟨a, as, h, rfl, bs, by simp⟩
|
||||
|
||||
@[simp] theorem erase_mkArray_self [LawfulBEq α] {a : α} :
|
||||
(mkArray n a).erase a = mkArray (n - 1) a := by
|
||||
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
|
||||
(replicate n a).erase a = replicate (n - 1) a := by
|
||||
simp only [← List.toArray_replicate, List.erase_toArray]
|
||||
simp [List.erase_replicate]
|
||||
|
||||
@[simp] theorem erase_mkArray_ne [LawfulBEq α] {a b : α} (h : !b == a) :
|
||||
(mkArray n a).erase b = mkArray n a := by
|
||||
@[deprecated erase_replicate_self (since := "2025-03-18")]
|
||||
abbrev erase_mkArray_self := @erase_replicate_self
|
||||
|
||||
@[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
|
||||
|
||||
@[deprecated erase_replicate_ne (since := "2025-03-18")]
|
||||
abbrev erase_mkArray_ne := @erase_replicate_ne
|
||||
|
||||
end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
@@ -353,12 +371,15 @@ theorem eraseIdx_append_of_length_le {xs : Array α} {k : Nat} (hk : xs.size ≤
|
||||
simp at hk
|
||||
simp [List.eraseIdx_append_of_length_le, *]
|
||||
|
||||
theorem eraseIdx_mkArray {n : Nat} {a : α} {k : Nat} {h} :
|
||||
(mkArray n a).eraseIdx k = mkArray (n - 1) a := by
|
||||
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
|
||||
(replicate n a).eraseIdx k = replicate (n - 1) a := by
|
||||
simp at h
|
||||
simp only [← List.toArray_replicate, List.eraseIdx_toArray]
|
||||
simp [List.eraseIdx_replicate, h]
|
||||
|
||||
@[deprecated eraseIdx_replicate (since := "2025-03-18")]
|
||||
abbrev eraseIdx_mkArray := @eraseIdx_replicate
|
||||
|
||||
theorem mem_eraseIdx_iff_getElem {x : α} {xs : Array α} {k} {h} : x ∈ xs.eraseIdx k h ↔ ∃ i w, i ≠ k ∧ xs[i]'w = x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mem_eraseIdx_iff_getElem, *]
|
||||
|
||||
@@ -249,12 +249,15 @@ theorem extract_append_left {as bs : Array α} :
|
||||
· simp only [size_map, size_extract] at h₁ h₂
|
||||
simp only [getElem_map, getElem_extract]
|
||||
|
||||
@[simp] theorem extract_mkArray {a : α} {n i j : Nat} :
|
||||
(mkArray n a).extract i j = mkArray (min j n - i) a := by
|
||||
@[simp] theorem extract_replicate {a : α} {n i j : Nat} :
|
||||
(replicate n a).extract i j = replicate (min j n - i) a := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
· simp only [size_extract, size_mkArray] at h₁ h₂
|
||||
simp only [getElem_extract, getElem_mkArray]
|
||||
· simp only [size_extract, size_replicate] at h₁ h₂
|
||||
simp only [getElem_extract, getElem_replicate]
|
||||
|
||||
@[deprecated extract_replicate (since := "2025-03-18")]
|
||||
abbrev extract_mkArray := @extract_replicate
|
||||
|
||||
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} :
|
||||
as.extract i j = as.extract i j' ↔ min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
|
||||
@@ -387,24 +390,36 @@ theorem popWhile_append {xs ys : Array α} :
|
||||
rw [List.dropWhile_append_of_pos]
|
||||
simpa
|
||||
|
||||
@[simp] theorem takeWhile_mkArray_eq_filter (p : α → Bool) :
|
||||
(mkArray n a).takeWhile p = (mkArray n a).filter p := by
|
||||
@[simp] theorem takeWhile_replicate_eq_filter (p : α → Bool) :
|
||||
(replicate n a).takeWhile p = (replicate n a).filter p := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
theorem takeWhile_mkArray (p : α → Bool) :
|
||||
(mkArray n a).takeWhile p = if p a then mkArray n a else #[] := by
|
||||
simp [takeWhile_mkArray_eq_filter, filter_mkArray]
|
||||
@[deprecated takeWhile_replicate_eq_filter (since := "2025-03-18")]
|
||||
abbrev takeWhile_mkArray_eq_filter := @takeWhile_replicate_eq_filter
|
||||
|
||||
@[simp] theorem popWhile_mkArray_eq_filter_not (p : α → Bool) :
|
||||
(mkArray n a).popWhile p = (mkArray n a).filter (fun a => !p a) := by
|
||||
theorem takeWhile_replicate (p : α → Bool) :
|
||||
(replicate n a).takeWhile p = if p a then replicate n a else #[] := by
|
||||
simp [takeWhile_replicate_eq_filter, filter_replicate]
|
||||
|
||||
@[deprecated takeWhile_replicate (since := "2025-03-18")]
|
||||
abbrev takeWhile_mkArray := @takeWhile_replicate
|
||||
|
||||
@[simp] theorem popWhile_replicate_eq_filter_not (p : α → Bool) :
|
||||
(replicate n a).popWhile p = (replicate n a).filter (fun a => !p a) := by
|
||||
simp [← List.toArray_replicate, ← List.filter_reverse]
|
||||
|
||||
theorem popWhile_mkArray (p : α → Bool) :
|
||||
(mkArray n a).popWhile p = if p a then #[] else mkArray n a := by
|
||||
simp only [popWhile_mkArray_eq_filter_not, size_mkArray, filter_mkArray, Bool.not_eq_eq_eq_not,
|
||||
@[deprecated popWhile_replicate_eq_filter_not (since := "2025-03-18")]
|
||||
abbrev popWhile_mkArray_eq_filter_not := @popWhile_replicate_eq_filter_not
|
||||
|
||||
theorem popWhile_replicate (p : α → Bool) :
|
||||
(replicate n a).popWhile p = if p a then #[] else replicate n a := by
|
||||
simp only [popWhile_replicate_eq_filter_not, size_replicate, filter_replicate, Bool.not_eq_eq_eq_not,
|
||||
Bool.not_true]
|
||||
split <;> simp_all
|
||||
|
||||
@[deprecated popWhile_replicate (since := "2025-03-18")]
|
||||
abbrev popWhile_mkArray := @popWhile_replicate
|
||||
|
||||
theorem extract_takeWhile {as : Array α} {i : Nat} :
|
||||
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
|
||||
rcases as with ⟨as⟩
|
||||
|
||||
@@ -12,7 +12,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
/-- `finRange n` is the array of all elements of `Fin n` in order. -/
|
||||
/--
|
||||
Returns an array of all elements of `Fin n` in order, starting at `0`.
|
||||
|
||||
Examples:
|
||||
* `Array.finRange 0 = (#[] : Array (Fin 0))`
|
||||
* `Array.finRange 2 = (#[0, 1] : Array (Fin 2))`
|
||||
-/
|
||||
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
|
||||
|
||||
@[simp] theorem size_finRange (n) : (Array.finRange n).size = n := by
|
||||
|
||||
@@ -99,21 +99,33 @@ theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
|
||||
simp [getElem?_eq_getElem, h] at t
|
||||
simp [← t]
|
||||
|
||||
theorem findSome?_mkArray : findSome? f (mkArray n a) = if n = 0 then none else f a := by
|
||||
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
|
||||
simp [← List.toArray_replicate, List.findSome?_replicate]
|
||||
|
||||
@[simp] theorem findSome?_mkArray_of_pos (h : 0 < n) : findSome? f (mkArray n a) = f a := by
|
||||
simp [findSome?_mkArray, Nat.ne_of_gt h]
|
||||
@[deprecated findSome?_replicate (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray := @findSome?_replicate
|
||||
|
||||
@[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]
|
||||
|
||||
@[deprecated findSome?_replicate_of_pos (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray_of_pos := @findSome?_replicate_of_pos
|
||||
|
||||
-- Argument is unused, but used to decide whether `simp` should unfold.
|
||||
@[simp] theorem findSome?_mkArray_of_isSome (_ : (f a).isSome) :
|
||||
findSome? f (mkArray n a) = if n = 0 then none else f a := by
|
||||
simp [findSome?_mkArray]
|
||||
@[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?_mkArray_of_isNone (h : (f a).isNone) :
|
||||
findSome? f (mkArray n a) = none := by
|
||||
@[deprecated findSome?_replicate_of_isSome (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray_of_isSome := @findSome?_replicate_of_isSome
|
||||
|
||||
@[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?_mkArray, h]
|
||||
simp [findSome?_replicate, h]
|
||||
|
||||
@[deprecated findSome?_replicate_of_isNone (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
|
||||
|
||||
/-! ### find? -/
|
||||
|
||||
@@ -254,40 +266,58 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
|
||||
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
|
||||
|
||||
theorem find?_mkArray :
|
||||
find? p (mkArray n a) = if n = 0 then none else if p a then some a else none := by
|
||||
theorem find?_replicate :
|
||||
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
|
||||
simp [← List.toArray_replicate, List.find?_replicate]
|
||||
|
||||
@[simp] theorem find?_mkArray_of_length_pos (h : 0 < n) :
|
||||
find? p (mkArray n a) = if p a then some a else none := by
|
||||
simp [find?_mkArray, Nat.ne_of_gt h]
|
||||
@[deprecated find?_replicate (since := "2025-03-18")]
|
||||
abbrev find?_mkArray := @find?_replicate
|
||||
|
||||
@[simp] theorem find?_mkArray_of_pos (h : p a) :
|
||||
find? p (mkArray n a) = if n = 0 then none else some a := by
|
||||
simp [find?_mkArray, h]
|
||||
@[simp] theorem find?_replicate_of_size_pos (h : 0 < n) :
|
||||
find? p (replicate n a) = if p a then some a else none := by
|
||||
simp [find?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
@[simp] theorem find?_mkArray_of_neg (h : ¬ p a) : find? p (mkArray n a) = none := by
|
||||
simp [find?_mkArray, h]
|
||||
@[deprecated find?_replicate_of_size_pos (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_length_pos := @find?_replicate_of_size_pos
|
||||
|
||||
@[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]
|
||||
|
||||
@[deprecated find?_replicate_of_pos (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
|
||||
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[deprecated find?_replicate_of_neg (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_neg := @find?_replicate_of_neg
|
||||
|
||||
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
|
||||
theorem find?_mkArray_eq_none_iff {n : Nat} {a : α} {p : α → Bool} :
|
||||
(mkArray n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
simp [← List.toArray_replicate, List.find?_replicate_eq_none_iff, Classical.or_iff_not_imp_left]
|
||||
|
||||
@[deprecated find?_mkArray_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_mkArray_eq_none := @find?_mkArray_eq_none_iff
|
||||
@[deprecated find?_replicate_eq_none_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
|
||||
|
||||
@[simp] theorem find?_mkArray_eq_some_iff {n : Nat} {a b : α} {p : α → Bool} :
|
||||
(mkArray n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated find?_mkArray_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_mkArray_eq_some := @find?_mkArray_eq_some_iff
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff
|
||||
|
||||
@[simp] theorem get_find?_mkArray (n : Nat) (a : α) (p : α → Bool) (h) :
|
||||
((mkArray n a).find? p).get h = a := by
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
|
||||
|
||||
@[simp] theorem get_find?_replicate (n : Nat) (a : α) (p : α → Bool) (h) :
|
||||
((replicate n a).find? p).get h = a := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated get_find?_replicate (since := "2025-03-18")]
|
||||
abbrev get_find?_mkArray := @get_find?_replicate
|
||||
|
||||
theorem find?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : Array α)
|
||||
(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
|
||||
@@ -408,7 +438,7 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
|
||||
|
||||
/-! ### findIdx? -/
|
||||
|
||||
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := rfl
|
||||
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
|
||||
|
||||
@[simp]
|
||||
theorem findIdx?_eq_none_iff {xs : Array α} {p : α → Bool} :
|
||||
@@ -481,12 +511,15 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
|
||||
cases xss using array₂_induction
|
||||
simp [List.findIdx?_flatten, Function.comp_def]
|
||||
|
||||
@[simp] theorem findIdx?_mkArray :
|
||||
(mkArray n a).findIdx? p = if 0 < n ∧ p a then some 0 else none := by
|
||||
@[simp] theorem findIdx?_replicate :
|
||||
(replicate n a).findIdx? p = if 0 < n ∧ p a then some 0 else none := by
|
||||
rw [← List.toArray_replicate]
|
||||
simp only [List.findIdx?_toArray]
|
||||
simp
|
||||
|
||||
@[deprecated findIdx?_replicate (since := "2025-03-18")]
|
||||
abbrev findIdx?_mkArray := @findIdx?_replicate
|
||||
|
||||
theorem findIdx?_eq_findSome?_zipIdx {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx? p = xs.zipIdx.findSome? fun ⟨a, i⟩ => if p a then some i else none := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -526,7 +559,7 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
|
||||
|
||||
/-! ### findFinIdx? -/
|
||||
|
||||
@[simp] theorem findFinIdx?_empty {p : α → Bool} : findFinIdx? p #[] = none := rfl
|
||||
@[simp] theorem findFinIdx?_empty {p : α → Bool} : findFinIdx? p #[] = none := by simp
|
||||
|
||||
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
|
||||
theorem findFinIdx?_congr {p : α → Bool} {xs ys : Array α} (w : xs = ys) :
|
||||
@@ -595,7 +628,7 @@ The verification API for `idxOf?` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
|
||||
-/
|
||||
|
||||
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := rfl
|
||||
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
|
||||
|
||||
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.idxOf? a = none ↔ a ∉ xs := by
|
||||
@@ -612,7 +645,7 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
|
||||
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
|
||||
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
|
||||
|
||||
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := rfl
|
||||
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.finIdxOf? a = none ↔ a ∉ xs := by
|
||||
|
||||
@@ -9,6 +9,12 @@ import Init.Data.Array.Basic
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
/--
|
||||
Sorts an array using insertion sort.
|
||||
|
||||
The optional parameter `lt` specifies an ordering predicate. It defaults to `LT.lt`, which must be
|
||||
decidable to be used for sorting.
|
||||
-/
|
||||
@[inline] def Array.insertionSort (xs : Array α) (lt : α → α → Bool := by exact (· < ·)) : Array α :=
|
||||
traverse xs 0 xs.size
|
||||
where
|
||||
|
||||
@@ -57,7 +57,10 @@ theorem toArray_eq : List.toArray as = xs ↔ as = xs.toList := by
|
||||
|
||||
theorem size_empty : (#[] : Array α).size = 0 := rfl
|
||||
|
||||
@[simp] theorem mkEmpty_eq (α n) : @mkEmpty α n = #[] := rfl
|
||||
@[simp] theorem emptyWithCapacity_eq (α n) : @emptyWithCapacity α n = #[] := rfl
|
||||
|
||||
@[deprecated emptyWithCapacity_eq (since := "2025-03-12")]
|
||||
theorem mkEmpty_eq (α n) : @mkEmpty α n = #[] := rfl
|
||||
|
||||
/-! ### size -/
|
||||
|
||||
@@ -318,27 +321,45 @@ theorem eq_push_of_size_ne_zero {xs : Array α} (h : xs.size ≠ 0) :
|
||||
theorem singleton_inj : #[a] = #[b] ↔ a = b := by
|
||||
simp
|
||||
|
||||
/-! ### mkArray -/
|
||||
/-! ### replicate -/
|
||||
|
||||
@[simp] theorem size_mkArray (n : Nat) (v : α) : (mkArray n v).size = n :=
|
||||
@[simp] theorem size_replicate (n : Nat) (v : α) : (replicate n v).size = n :=
|
||||
List.length_replicate ..
|
||||
|
||||
@[simp] theorem toList_mkArray : (mkArray n a).toList = List.replicate n a := by
|
||||
simp only [mkArray]
|
||||
@[deprecated size_replicate (since := "2025-03-18")]
|
||||
abbrev size_mkArray := @size_replicate
|
||||
|
||||
@[simp] theorem mkArray_zero : mkArray 0 a = #[] := rfl
|
||||
@[simp] theorem toList_replicate : (replicate n a).toList = List.replicate n a := by
|
||||
simp only [replicate]
|
||||
|
||||
theorem mkArray_succ : mkArray (n + 1) a = (mkArray n a).push a := by
|
||||
@[deprecated toList_replicate (since := "2025-03-18")]
|
||||
abbrev toList_mkArray := @toList_replicate
|
||||
|
||||
@[simp] theorem replicate_zero : replicate 0 a = #[] := rfl
|
||||
|
||||
@[deprecated replicate_zero (since := "2025-03-18")]
|
||||
abbrev mkArray_zero := @replicate_zero
|
||||
|
||||
theorem replicate_succ : replicate (n + 1) a = (replicate n a).push a := by
|
||||
apply toList_inj.1
|
||||
simp [List.replicate_succ']
|
||||
|
||||
@[simp] theorem getElem_mkArray (n : Nat) (v : α) (h : i < (mkArray n v).size) :
|
||||
(mkArray n v)[i] = v := by simp [← getElem_toList]
|
||||
@[deprecated replicate_succ (since := "2025-03-18")]
|
||||
abbrev mkArray_succ := @replicate_succ
|
||||
|
||||
theorem getElem?_mkArray (n : Nat) (v : α) (i : Nat) :
|
||||
(mkArray n v)[i]? = if i < n then some v else none := by
|
||||
@[simp] theorem getElem_replicate (n : Nat) (v : α) (h : i < (replicate n v).size) :
|
||||
(replicate n v)[i] = v := by simp [← getElem_toList]
|
||||
|
||||
@[deprecated getElem_replicate (since := "2025-03-18")]
|
||||
abbrev getElem_mkArray := @getElem_replicate
|
||||
|
||||
@[simp] theorem getElem?_replicate (n : Nat) (v : α) (i : Nat) :
|
||||
(replicate n v)[i]? = if i < n then some v else none := by
|
||||
simp [getElem?_def]
|
||||
|
||||
@[deprecated getElem?_replicate (since := "2025-03-18")]
|
||||
abbrev getElem?_mkArray := @getElem?_replicate
|
||||
|
||||
/-! ### mem -/
|
||||
|
||||
theorem not_mem_empty (a : α) : ¬ a ∈ #[] := by simp
|
||||
@@ -551,7 +572,7 @@ theorem anyM_loop_cons [Monad m] (p : α → m Bool) (a : α) (as : List α) (st
|
||||
@[simp] theorem anyM_toList [Monad m] (p : α → m Bool) (as : Array α) :
|
||||
as.toList.anyM p = as.anyM p :=
|
||||
match as with
|
||||
| ⟨[]⟩ => rfl
|
||||
| ⟨[]⟩ => by simp [anyM, anyM.loop]
|
||||
| ⟨a :: as⟩ => by
|
||||
simp only [List.anyM, anyM, List.size_toArray, List.length_cons, Nat.le_refl, ↓reduceDIte]
|
||||
rw [anyM.loop, dif_pos (by omega)]
|
||||
@@ -1034,15 +1055,18 @@ theorem size_eq_of_beq [BEq α] {xs ys : Array α} (h : xs == ys) : xs.size = ys
|
||||
cases ys
|
||||
simp [List.length_eq_of_beq (by simpa using h)]
|
||||
|
||||
@[simp] theorem mkArray_beq_mkArray [BEq α] {a b : α} {n : Nat} :
|
||||
(mkArray n a == mkArray n b) = (n == 0 || a == b) := by
|
||||
@[simp] theorem replicate_beq_replicate [BEq α] {a b : α} {n : Nat} :
|
||||
(replicate n a == replicate n b) = (n == 0 || a == b) := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
rw [mkArray_succ, mkArray_succ, push_beq_push, mkArray_beq_mkArray]
|
||||
rw [replicate_succ, replicate_succ, push_beq_push, replicate_beq_replicate]
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp +contextual
|
||||
|
||||
@[deprecated replicate_beq_replicate (since := "2025-03-18")]
|
||||
abbrev mkArray_beq_mkArray := @replicate_beq_replicate
|
||||
|
||||
private theorem beq_of_beq_singleton [BEq α] {a b : α} : #[a] == #[b] → a == b := by
|
||||
intro h
|
||||
have : isEqv #[a] #[b] BEq.beq = true := h
|
||||
@@ -1175,7 +1199,7 @@ theorem map_id' (xs : Array α) : map (fun (a : α) => a) xs = xs := map_id xs
|
||||
theorem map_id'' {f : α → α} (h : ∀ x, f x = x) (xs : Array α) : map f xs = xs := by
|
||||
simp [show f = id from funext h]
|
||||
|
||||
theorem map_singleton (f : α → β) (a : α) : map f #[a] = #[f a] := rfl
|
||||
theorem map_singleton (f : α → β) (a : α) : map f #[a] = #[f a] := by simp
|
||||
|
||||
-- We use a lower priority here as there are more specific lemmas in downstream libraries
|
||||
-- which should be able to fire first.
|
||||
@@ -2303,147 +2327,234 @@ theorem flatMap_eq_foldl (f : α → Array β) (xs : Array α) :
|
||||
rw [List.foldl_cons, ih]
|
||||
simp [toArray_append]
|
||||
|
||||
/-! ### mkArray -/
|
||||
/-! ### replicate -/
|
||||
|
||||
@[simp] theorem mkArray_one : mkArray 1 a = #[a] := rfl
|
||||
@[simp] theorem replicate_one : replicate 1 a = #[a] := rfl
|
||||
|
||||
/-- Variant of `mkArray_succ` that prepends `a` at the beginning of the array. -/
|
||||
theorem mkArray_succ' : mkArray (n + 1) a = #[a] ++ mkArray n a := by
|
||||
@[deprecated replicate_one (since := "2025-03-18")]
|
||||
abbrev mkArray_one := @replicate_one
|
||||
|
||||
/-- Variant of `replicate_succ` that prepends `a` at the beginning of the array. -/
|
||||
theorem replicate_succ' : replicate (n + 1) a = #[a] ++ replicate n a := by
|
||||
apply Array.ext'
|
||||
simp [List.replicate_succ]
|
||||
|
||||
@[simp] theorem mem_mkArray {a b : α} {n} : b ∈ mkArray n a ↔ n ≠ 0 ∧ b = a := by
|
||||
unfold mkArray
|
||||
@[deprecated replicate_succ' (since := "2025-03-18")]
|
||||
abbrev mkArray_succ' := @replicate_succ'
|
||||
|
||||
@[simp] theorem mem_replicate {a b : α} {n} : b ∈ replicate n a ↔ n ≠ 0 ∧ b = a := by
|
||||
unfold replicate
|
||||
simp only [mem_toArray, List.mem_replicate]
|
||||
|
||||
theorem eq_of_mem_mkArray {a b : α} {n} (h : b ∈ mkArray n a) : b = a := (mem_mkArray.1 h).2
|
||||
@[deprecated mem_replicate (since := "2025-03-18")]
|
||||
abbrev mem_mkArray := @mem_replicate
|
||||
|
||||
theorem forall_mem_mkArray {p : α → Prop} {a : α} {n} :
|
||||
(∀ b, b ∈ mkArray n a → p b) ↔ n = 0 ∨ p a := by
|
||||
cases n <;> simp [mem_mkArray]
|
||||
theorem eq_of_mem_replicate {a b : α} {n} (h : b ∈ replicate n a) : b = a := (mem_replicate.1 h).2
|
||||
|
||||
@[simp] theorem mkArray_succ_ne_empty (n : Nat) (a : α) : mkArray (n+1) a ≠ #[] := by
|
||||
simp [mkArray_succ]
|
||||
@[deprecated eq_of_mem_mkArray (since := "2025-03-18")]
|
||||
abbrev eq_of_mem_mkArray := @eq_of_mem_replicate
|
||||
|
||||
@[simp] theorem mkArray_eq_empty_iff {n : Nat} (a : α) : mkArray n a = #[] ↔ n = 0 := by
|
||||
theorem forall_mem_replicate {p : α → Prop} {a : α} {n} :
|
||||
(∀ b, b ∈ replicate n a → p b) ↔ n = 0 ∨ p a := by
|
||||
cases n <;> simp [mem_replicate]
|
||||
|
||||
@[deprecated forall_mem_replicate (since := "2025-03-18")]
|
||||
abbrev forall_mem_mkArray := @forall_mem_replicate
|
||||
|
||||
@[simp] theorem replicate_succ_ne_empty (n : Nat) (a : α) : replicate (n+1) a ≠ #[] := by
|
||||
simp [replicate_succ]
|
||||
|
||||
@[deprecated replicate_succ_ne_empty (since := "2025-03-18")]
|
||||
abbrev mkArray_succ_ne_empty := @replicate_succ_ne_empty
|
||||
|
||||
@[simp] theorem replicate_eq_empty_iff {n : Nat} (a : α) : replicate n a = #[] ↔ n = 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem getElem?_mkArray_of_lt {n : Nat} {i : Nat} (h : i < n) : (mkArray n a)[i]? = some a := by
|
||||
simp [getElem?_mkArray, h]
|
||||
@[deprecated replicate_eq_empty_iff (since := "2025-03-18")]
|
||||
abbrev mkArray_eq_empty_iff := @replicate_eq_empty_iff
|
||||
|
||||
@[simp] theorem mkArray_inj : mkArray n a = mkArray m b ↔ n = m ∧ (n = 0 ∨ a = b) := by
|
||||
@[simp] theorem replicate_inj : replicate n a = replicate m b ↔ n = m ∧ (n = 0 ∨ a = b) := by
|
||||
rw [← toList_inj]
|
||||
simp
|
||||
|
||||
theorem eq_mkArray_of_mem {a : α} {xs : Array α} (h : ∀ (b) (_ : b ∈ xs), b = a) : xs = mkArray xs.size a := by
|
||||
@[deprecated replicate_inj (since := "2025-03-18")]
|
||||
abbrev mkArray_inj := @replicate_inj
|
||||
|
||||
theorem eq_replicate_of_mem {a : α} {xs : Array α} (h : ∀ (b) (_ : b ∈ xs), b = a) : xs = replicate xs.size a := by
|
||||
rw [← toList_inj]
|
||||
simpa using List.eq_replicate_of_mem (by simpa using h)
|
||||
|
||||
theorem eq_mkArray_iff {a : α} {n} {xs : Array α} :
|
||||
xs = mkArray n a ↔ xs.size = n ∧ ∀ (b) (_ : b ∈ xs), b = a := by
|
||||
@[deprecated eq_replicate_of_mem (since := "2025-03-18")]
|
||||
abbrev eq_mkArray_of_mem := @eq_replicate_of_mem
|
||||
|
||||
theorem eq_replicate_iff {a : α} {n} {xs : Array α} :
|
||||
xs = replicate n a ↔ xs.size = n ∧ ∀ (b) (_ : b ∈ xs), b = a := by
|
||||
rw [← toList_inj]
|
||||
simpa using List.eq_replicate_iff (l := xs.toList)
|
||||
|
||||
theorem map_eq_mkArray_iff {xs : Array α} {f : α → β} {b : β} :
|
||||
xs.map f = mkArray xs.size b ↔ ∀ x ∈ xs, f x = b := by
|
||||
simp [eq_mkArray_iff]
|
||||
@[deprecated eq_replicate_iff (since := "2025-03-18")]
|
||||
abbrev eq_mkArray_iff := @eq_replicate_iff
|
||||
|
||||
@[simp] theorem map_const (xs : Array α) (b : β) : map (Function.const α b) xs = mkArray xs.size b :=
|
||||
map_eq_mkArray_iff.mpr fun _ _ => rfl
|
||||
theorem map_eq_replicate_iff {xs : Array α} {f : α → β} {b : β} :
|
||||
xs.map f = replicate xs.size b ↔ ∀ x ∈ xs, f x = b := by
|
||||
simp [eq_replicate_iff]
|
||||
|
||||
@[simp] theorem map_const_fun (x : β) : map (Function.const α x) = (mkArray ·.size x) := by
|
||||
@[deprecated map_eq_replicate_iff (since := "2025-03-18")]
|
||||
abbrev map_eq_mkArray_iff := @map_eq_replicate_iff
|
||||
|
||||
@[simp] theorem map_const (xs : Array α) (b : β) : map (Function.const α b) xs = replicate xs.size b :=
|
||||
map_eq_replicate_iff.mpr fun _ _ => rfl
|
||||
|
||||
@[simp] theorem map_const_fun (x : β) : map (Function.const α x) = (replicate ·.size x) := by
|
||||
funext xs
|
||||
simp
|
||||
|
||||
/-- Variant of `map_const` using a lambda rather than `Function.const`. -/
|
||||
-- This can not be a `@[simp]` lemma because it would fire on every `List.map`.
|
||||
theorem map_const' (xs : Array α) (b : β) : map (fun _ => b) xs = mkArray xs.size b :=
|
||||
theorem map_const' (xs : Array α) (b : β) : map (fun _ => b) xs = replicate xs.size b :=
|
||||
map_const xs b
|
||||
|
||||
@[simp] theorem set_mkArray_self : (mkArray n a).set i a h = mkArray n a := by
|
||||
@[simp] theorem set_replicate_self : (replicate n a).set i a h = replicate n a := by
|
||||
apply Array.ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem setIfInBounds_mkArray_self : (mkArray n a).setIfInBounds i a = mkArray n a := by
|
||||
@[deprecated set_replicate_self (since := "2025-03-18")]
|
||||
abbrev set_mkArray_self := @set_replicate_self
|
||||
|
||||
@[simp] theorem setIfInBounds_replicate_self : (replicate n a).setIfInBounds i a = replicate n a := by
|
||||
apply Array.ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem mkArray_append_mkArray : mkArray n a ++ mkArray m a = mkArray (n + m) a := by
|
||||
@[deprecated setIfInBounds_replicate_self (since := "2025-03-18")]
|
||||
abbrev setIfInBounds_mkArray_self := @setIfInBounds_replicate_self
|
||||
|
||||
@[simp] theorem replicate_append_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
|
||||
apply Array.ext'
|
||||
simp
|
||||
|
||||
theorem append_eq_mkArray_iff {xs ys : Array α} {a : α} :
|
||||
xs ++ ys = mkArray n a ↔
|
||||
xs.size + ys.size = n ∧ xs = mkArray xs.size a ∧ ys = mkArray ys.size a := by
|
||||
@[deprecated replicate_append_replicate (since := "2025-03-18")]
|
||||
abbrev mkArray_append_mkArray := @replicate_append_replicate
|
||||
|
||||
theorem append_eq_replicate_iff {xs ys : Array α} {a : α} :
|
||||
xs ++ ys = replicate n a ↔
|
||||
xs.size + ys.size = n ∧ xs = replicate xs.size a ∧ ys = replicate ys.size a := by
|
||||
simp [← toList_inj, List.append_eq_replicate_iff]
|
||||
|
||||
theorem mkArray_eq_append_iff {xs ys : Array α} {a : α} :
|
||||
mkArray n a = xs ++ ys ↔
|
||||
xs.size + ys.size = n ∧ xs = mkArray xs.size a ∧ ys = mkArray ys.size a := by
|
||||
rw [eq_comm, append_eq_mkArray_iff]
|
||||
@[deprecated append_eq_replicate_iff (since := "2025-03-18")]
|
||||
abbrev append_eq_mkArray_iff := @append_eq_replicate_iff
|
||||
|
||||
@[simp] theorem map_mkArray : (mkArray n a).map f = mkArray n (f a) := by
|
||||
theorem replicate_eq_append_iff {xs ys : Array α} {a : α} :
|
||||
replicate n a = xs ++ ys ↔
|
||||
xs.size + ys.size = n ∧ xs = replicate xs.size a ∧ ys = replicate ys.size a := by
|
||||
rw [eq_comm, append_eq_replicate_iff]
|
||||
|
||||
@[deprecated replicate_eq_append_iff (since := "2025-03-18")]
|
||||
abbrev replicate_eq_mkArray_iff := @replicate_eq_append_iff
|
||||
|
||||
@[simp] theorem map_replicate : (replicate n a).map f = replicate n (f a) := by
|
||||
apply Array.ext'
|
||||
simp
|
||||
|
||||
theorem filter_mkArray (w : stop = n) :
|
||||
(mkArray n a).filter p 0 stop = if p a then mkArray n a else #[] := by
|
||||
@[deprecated map_replicate (since := "2025-03-18")]
|
||||
abbrev map_mkArray := @map_replicate
|
||||
|
||||
theorem filter_replicate (w : stop = n) :
|
||||
(replicate n a).filter p 0 stop = if p a then replicate n a else #[] := by
|
||||
apply Array.ext'
|
||||
simp only [w, toList_filter', toList_mkArray, List.filter_replicate]
|
||||
simp only [w, toList_filter', toList_replicate, List.filter_replicate]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem filter_mkArray_of_pos (w : stop = n) (h : p a) :
|
||||
(mkArray n a).filter p 0 stop = mkArray n a := by
|
||||
simp [filter_mkArray, h, w]
|
||||
@[deprecated filter_replicate (since := "2025-03-18")]
|
||||
abbrev filter_mkArray := @filter_replicate
|
||||
|
||||
@[simp] theorem filter_mkArray_of_neg (w : stop = n) (h : ¬ p a) :
|
||||
(mkArray n a).filter p 0 stop = #[] := by
|
||||
simp [filter_mkArray, h, w]
|
||||
@[simp] theorem filter_replicate_of_pos (w : stop = n) (h : p a) :
|
||||
(replicate n a).filter p 0 stop = replicate n a := by
|
||||
simp [filter_replicate, h, w]
|
||||
|
||||
theorem filterMap_mkArray {f : α → Option β} (w : stop = n := by simp) :
|
||||
(mkArray n a).filterMap f 0 stop = match f a with | none => #[] | .some b => mkArray n b := by
|
||||
@[deprecated filter_replicate_of_pos (since := "2025-03-18")]
|
||||
abbrev filter_mkArray_of_pos := @filter_replicate_of_pos
|
||||
|
||||
@[simp] theorem filter_replicate_of_neg (w : stop = n) (h : ¬ p a) :
|
||||
(replicate n a).filter p 0 stop = #[] := by
|
||||
simp [filter_replicate, h, w]
|
||||
|
||||
@[deprecated filter_replicate_of_neg (since := "2025-03-18")]
|
||||
abbrev filter_mkArray_of_neg := @filter_replicate_of_neg
|
||||
|
||||
theorem filterMap_replicate {f : α → Option β} (w : stop = n := by simp) :
|
||||
(replicate n a).filterMap f 0 stop = match f a with | none => #[] | .some b => replicate n b := by
|
||||
apply Array.ext'
|
||||
simp only [w, size_mkArray, toList_filterMap', toList_mkArray, List.filterMap_replicate]
|
||||
simp only [w, size_replicate, toList_filterMap', toList_replicate, List.filterMap_replicate]
|
||||
split <;> simp_all
|
||||
|
||||
@[deprecated filterMap_replicate (since := "2025-03-18")]
|
||||
abbrev filterMap_mkArray := @filterMap_replicate
|
||||
|
||||
-- This is not a useful `simp` lemma because `b` is unknown.
|
||||
theorem filterMap_mkArray_of_some {f : α → Option β} (h : f a = some b) :
|
||||
(mkArray n a).filterMap f = mkArray n b := by
|
||||
simp [filterMap_mkArray, h]
|
||||
theorem filterMap_replicate_of_some {f : α → Option β} (h : f a = some b) :
|
||||
(replicate n a).filterMap f = replicate n b := by
|
||||
simp [filterMap_replicate, h]
|
||||
|
||||
@[simp] theorem filterMap_mkArray_of_isSome {f : α → Option β} (h : (f a).isSome) :
|
||||
(mkArray n a).filterMap f = mkArray n (Option.get _ h) := by
|
||||
@[deprecated filterMap_replicate_of_some (since := "2025-03-18")]
|
||||
abbrev filterMap_mkArray_of_some := @filterMap_replicate_of_some
|
||||
|
||||
@[simp] theorem filterMap_replicate_of_isSome {f : α → Option β} (h : (f a).isSome) :
|
||||
(replicate n a).filterMap f = replicate n (Option.get _ h) := by
|
||||
match w : f a, h with
|
||||
| some b, _ => simp [filterMap_mkArray, h, w]
|
||||
| some b, _ => simp [filterMap_replicate, h, w]
|
||||
|
||||
@[simp] theorem filterMap_mkArray_of_none {f : α → Option β} (h : f a = none) :
|
||||
(mkArray n a).filterMap f = #[] := by
|
||||
simp [filterMap_mkArray, h]
|
||||
@[deprecated filterMap_replicate_of_isSome (since := "2025-03-18")]
|
||||
abbrev filterMap_mkArray_of_isSome := @filterMap_replicate_of_isSome
|
||||
|
||||
@[simp] theorem flatten_mkArray_empty : (mkArray n (#[] : Array α)).flatten = #[] := by
|
||||
@[simp] theorem filterMap_replicate_of_none {f : α → Option β} (h : f a = none) :
|
||||
(replicate n a).filterMap f = #[] := by
|
||||
simp [filterMap_replicate, h]
|
||||
|
||||
@[deprecated filterMap_replicate_of_none (since := "2025-03-18")]
|
||||
abbrev filterMap_mkArray_of_none := @filterMap_replicate_of_none
|
||||
|
||||
@[simp] theorem flatten_replicate_empty : (replicate n (#[] : Array α)).flatten = #[] := by
|
||||
rw [← toList_inj]
|
||||
simp
|
||||
|
||||
@[simp] theorem flatten_mkArray_singleton : (mkArray n #[a]).flatten = mkArray n a := by
|
||||
@[deprecated flatten_replicate_empty (since := "2025-03-18")]
|
||||
abbrev flatten_mkArray_empty := @flatten_replicate_empty
|
||||
|
||||
@[simp] theorem flatten_replicate_singleton : (replicate n #[a]).flatten = replicate n a := by
|
||||
rw [← toList_inj]
|
||||
simp
|
||||
|
||||
@[simp] theorem flatten_mkArray_mkArray : (mkArray n (mkArray m a)).flatten = mkArray (n * m) a := by
|
||||
@[deprecated flatten_replicate_singleton (since := "2025-03-18")]
|
||||
abbrev flatten_mkArray_singleton := @flatten_replicate_singleton
|
||||
|
||||
@[simp] theorem flatten_replicate_replicate : (replicate n (replicate m a)).flatten = replicate (n * m) a := by
|
||||
rw [← toList_inj]
|
||||
simp
|
||||
|
||||
theorem flatMap_mkArray {β} (f : α → Array β) : (mkArray n a).flatMap f = (mkArray n (f a)).flatten := by
|
||||
@[deprecated flatten_replicate_replicate (since := "2025-03-18")]
|
||||
abbrev flatten_mkArray_replicate := @flatten_replicate_replicate
|
||||
|
||||
theorem flatMap_replicate {β} (f : α → Array β) : (replicate n a).flatMap f = (replicate n (f a)).flatten := by
|
||||
rw [← toList_inj]
|
||||
simp [flatMap_toList, List.flatMap_replicate]
|
||||
|
||||
@[simp] theorem isEmpty_mkArray : (mkArray n a).isEmpty = decide (n = 0) := by
|
||||
@[deprecated flatMap_replicate (since := "2025-03-18")]
|
||||
abbrev flatMap_mkArray := @flatMap_replicate
|
||||
|
||||
@[simp] theorem isEmpty_replicate : (replicate n a).isEmpty = decide (n = 0) := by
|
||||
rw [← List.toArray_replicate, List.isEmpty_toArray]
|
||||
simp
|
||||
|
||||
@[simp] theorem sum_mkArray_nat (n : Nat) (a : Nat) : (mkArray n a).sum = n * a := by
|
||||
@[deprecated isEmpty_replicate (since := "2025-03-18")]
|
||||
abbrev isEmpty_mkArray := @isEmpty_replicate
|
||||
|
||||
@[simp] theorem sum_replicate_nat (n : Nat) (a : Nat) : (replicate n a).sum = n * a := by
|
||||
rw [← List.toArray_replicate, List.sum_toArray]
|
||||
simp
|
||||
|
||||
@[deprecated sum_replicate_nat (since := "2025-03-18")]
|
||||
abbrev sum_mkArray_nat := @sum_replicate_nat
|
||||
|
||||
/-! ### Preliminaries about `swap` needed for `reverse`. -/
|
||||
|
||||
theorem getElem?_swap (xs : Array α) (i j : Nat) (hi hj) (k : Nat) : (xs.swap i j hi hj)[k]? =
|
||||
@@ -2622,10 +2733,13 @@ theorem flatMap_reverse {β} (xs : Array α) (f : α → Array β) :
|
||||
cases xs
|
||||
simp [List.flatMap_reverse, Function.comp_def]
|
||||
|
||||
@[simp] theorem reverse_mkArray (n) (a : α) : reverse (mkArray n a) = mkArray n a := by
|
||||
@[simp] theorem reverse_replicate (n) (a : α) : reverse (replicate n a) = replicate n a := by
|
||||
rw [← toList_inj]
|
||||
simp
|
||||
|
||||
@[deprecated reverse_replicate (since := "2025-03-18")]
|
||||
abbrev reverse_mkArray := @reverse_replicate
|
||||
|
||||
/-! ### extract -/
|
||||
|
||||
theorem extract_loop_zero (xs ys : Array α) (start : Nat) : extract.loop xs 0 start ys = ys := by
|
||||
@@ -2655,7 +2769,7 @@ theorem extract_loop_eq_aux (xs ys : Array α) (size start : Nat) :
|
||||
|
||||
theorem extract_loop_eq (xs ys : Array α) (size start : Nat) (h : start + size ≤ xs.size) :
|
||||
extract.loop xs size start ys = ys ++ xs.extract start (start + size) := by
|
||||
simp only [extract, Nat.sub_eq, mkEmpty_eq]
|
||||
simp only [extract, Nat.sub_eq, emptyWithCapacity_eq]
|
||||
rw [extract_loop_eq_aux, Nat.min_eq_left h, Nat.add_sub_cancel_left]
|
||||
|
||||
theorem size_extract_loop (xs ys : Array α) (size start : Nat) :
|
||||
@@ -2672,7 +2786,7 @@ theorem size_extract_loop (xs ys : Array α) (size start : Nat) :
|
||||
|
||||
@[simp] theorem size_extract (xs : Array α) (start stop : Nat) :
|
||||
(xs.extract start stop).size = min stop xs.size - start := by
|
||||
simp only [extract, Nat.sub_eq, mkEmpty_eq]
|
||||
simp only [extract, Nat.sub_eq, emptyWithCapacity_eq]
|
||||
rw [size_extract_loop, size_empty, Nat.zero_add, Nat.sub_min_sub_right, Nat.min_assoc,
|
||||
Nat.min_self]
|
||||
|
||||
@@ -2775,12 +2889,12 @@ abbrev extract_all := @extract_size
|
||||
|
||||
theorem extract_empty_of_stop_le_start (xs : Array α) {start stop : Nat} (h : stop ≤ start) :
|
||||
xs.extract start stop = #[] := by
|
||||
simp only [extract, Nat.sub_eq, mkEmpty_eq]
|
||||
simp only [extract, Nat.sub_eq, emptyWithCapacity_eq]
|
||||
rw [←Nat.sub_min_sub_right, Nat.sub_eq_zero_of_le h, Nat.zero_min, extract_loop_zero]
|
||||
|
||||
theorem extract_empty_of_size_le_start (xs : Array α) {start stop : Nat} (h : xs.size ≤ start) :
|
||||
xs.extract start stop = #[] := by
|
||||
simp only [extract, Nat.sub_eq, mkEmpty_eq]
|
||||
simp only [extract, Nat.sub_eq, emptyWithCapacity_eq]
|
||||
rw [←Nat.sub_min_sub_right, Nat.sub_eq_zero_of_le h, Nat.min_zero, extract_loop_zero]
|
||||
|
||||
@[simp] theorem extract_empty (start stop : Nat) : (#[] : Array α).extract start stop = #[] :=
|
||||
@@ -3461,14 +3575,20 @@ theorem back?_flatten {xss : Array (Array α)} :
|
||||
(flatten xss).back? = xss.reverse.findSome? fun xs => xs.back? := by
|
||||
simp [← flatMap_id, back?_flatMap]
|
||||
|
||||
theorem back?_mkArray (a : α) (n : Nat) :
|
||||
(mkArray n a).back? = if n = 0 then none else some a := by
|
||||
rw [mkArray_eq_toArray_replicate]
|
||||
theorem back?_replicate (a : α) (n : Nat) :
|
||||
(replicate n a).back? = if n = 0 then none else some a := by
|
||||
rw [replicate_eq_toArray_replicate]
|
||||
simp only [List.back?_toArray, List.getLast?_replicate]
|
||||
|
||||
@[simp] theorem back_mkArray (w : 0 < n) : (mkArray n a).back (by simpa using w) = a := by
|
||||
@[deprecated back?_replicate (since := "2025-03-18")]
|
||||
abbrev back?_mkArray := @back?_replicate
|
||||
|
||||
@[simp] theorem back_replicate (w : 0 < n) : (replicate n a).back (by simpa using w) = a := by
|
||||
simp [back_eq_getElem]
|
||||
|
||||
@[deprecated back_replicate (since := "2025-03-18")]
|
||||
abbrev back_mkArray := @back_replicate
|
||||
|
||||
/-! ## Additional operations -/
|
||||
|
||||
/-! ### leftpad -/
|
||||
@@ -3505,9 +3625,12 @@ theorem pop_append {xs ys : Array α} :
|
||||
(xs ++ ys).pop = if ys.isEmpty then xs.pop else xs ++ ys.pop := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem pop_mkArray (n) (a : α) : (mkArray n a).pop = mkArray (n - 1) a := by
|
||||
@[simp] theorem pop_replicate (n) (a : α) : (replicate n a).pop = replicate (n - 1) a := by
|
||||
ext <;> simp
|
||||
|
||||
@[deprecated pop_replicate (since := "2025-03-18")]
|
||||
abbrev pop_mkArray := @pop_replicate
|
||||
|
||||
/-! ### modify -/
|
||||
|
||||
@[simp] theorem size_modify (xs : Array α) (i : Nat) (f : α → α) : (xs.modify i f).size = xs.size := by
|
||||
@@ -3522,7 +3645,7 @@ theorem getElem_modify {xs : Array α} {j i} (h : i < (xs.modify j f).size) :
|
||||
· rw [if_neg (mt (by rintro rfl; exact h) (by simp_all))]
|
||||
|
||||
@[simp] theorem toList_modify (xs : Array α) (f : α → α) :
|
||||
(xs.modify i f).toList = xs.toList.modify f i := by
|
||||
(xs.modify i f).toList = xs.toList.modify i f := by
|
||||
apply List.ext_getElem
|
||||
· simp
|
||||
· simp [getElem_modify, List.getElem_modify]
|
||||
@@ -3672,15 +3795,21 @@ theorem replace_extract {xs : Array α} {i : Nat} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.replace_take]
|
||||
|
||||
@[simp] theorem replace_mkArray_self {a : α} (h : 0 < n) :
|
||||
(mkArray n a).replace a b = #[b] ++ mkArray (n - 1) a := by
|
||||
cases n <;> simp_all [mkArray_succ', replace_append]
|
||||
@[simp] theorem replace_replicate_self {a : α} (h : 0 < n) :
|
||||
(replicate n a).replace a b = #[b] ++ replicate (n - 1) a := by
|
||||
cases n <;> simp_all [replicate_succ', replace_append]
|
||||
|
||||
@[simp] theorem replace_mkArray_ne {a b c : α} (h : !b == a) :
|
||||
(mkArray n a).replace b c = mkArray n a := by
|
||||
@[deprecated replace_replicate_self (since := "2025-03-18")]
|
||||
abbrev replace_mkArray_self := @replace_replicate_self
|
||||
|
||||
@[simp] theorem replace_replicate_ne {a b c : α} (h : !b == a) :
|
||||
(replicate n a).replace b c = replicate n a := by
|
||||
rw [replace_of_not_mem]
|
||||
simp_all
|
||||
|
||||
@[deprecated replace_replicate_ne (since := "2025-03-18")]
|
||||
abbrev replace_mkArray_ne := @replace_replicate_ne
|
||||
|
||||
end replace
|
||||
|
||||
/-! ## Logic -/
|
||||
@@ -3908,17 +4037,31 @@ theorem any_reverse {xs : Array α} : xs.reverse.any f 0 = xs.any f := by
|
||||
theorem all_reverse {xs : Array α} : xs.reverse.all f 0 = xs.all f := by
|
||||
simp
|
||||
|
||||
@[simp] theorem any_mkArray {n : Nat} {a : α} :
|
||||
(mkArray n a).any f = if n = 0 then false else f a := by
|
||||
induction n <;> simp_all [mkArray_succ']
|
||||
@[simp] theorem any_replicate {n : Nat} {a : α} :
|
||||
(replicate n a).any f = if n = 0 then false else f a := by
|
||||
induction n <;> simp_all [replicate_succ']
|
||||
|
||||
@[simp] theorem all_mkArray {n : Nat} {a : α} :
|
||||
(mkArray n a).all f = if n = 0 then true else f a := by
|
||||
induction n <;> simp_all +contextual [mkArray_succ']
|
||||
@[deprecated any_replicate (since := "2025-03-18")]
|
||||
abbrev any_mkArray := @any_replicate
|
||||
|
||||
@[simp] theorem all_replicate {n : Nat} {a : α} :
|
||||
(replicate n a).all f = if n = 0 then true else f a := by
|
||||
induction n <;> simp_all +contextual [replicate_succ']
|
||||
|
||||
@[deprecated all_replicate (since := "2025-03-18")]
|
||||
abbrev all_mkArray := @all_replicate
|
||||
|
||||
/-! ### toListRev -/
|
||||
|
||||
/-- A more efficient version of `arr.toList.reverse`; for verification purposes we immediately simplify it. -/
|
||||
/--
|
||||
Converts an array to a list that contains the same elements in the opposite order.
|
||||
|
||||
This is equivalent to, but more efficient than, `Array.toList ∘ List.reverse`.
|
||||
|
||||
Examples:
|
||||
* `#[1, 2, 3].toListRev = [3, 2, 1]`
|
||||
* `#["blue", "yellow"].toListRev = ["yellow", "blue"]`
|
||||
-/
|
||||
@[inline] def toListRev (xs : Array α) : List α := xs.foldl (fun l t => t :: l) []
|
||||
|
||||
@[simp] theorem toListRev_eq (xs : Array α) : xs.toListRev = xs.toList.reverse := by
|
||||
@@ -4220,7 +4363,7 @@ theorem uset_toArray (l : List α) (i : USize) (a : α) (h : i.toNat < l.toArray
|
||||
l.toArray.uset i a h = (l.set i.toNat a).toArray := by simp
|
||||
|
||||
@[simp] theorem modify_toArray (f : α → α) (l : List α) :
|
||||
l.toArray.modify i f = (l.modify f i).toArray := by
|
||||
l.toArray.modify i f = (l.modify i f).toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
namespace Array
|
||||
|
||||
/--
|
||||
Lexicographic comparator for arrays.
|
||||
Compares arrays lexicographically with respect to a comparison `lt` on their elements.
|
||||
|
||||
`lex as bs lt` is true if
|
||||
- `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`, or
|
||||
- there is an index `i` such that `lt as[i] bs[i]`, and for all `j < i`, `as[j] == bs[j]`.
|
||||
Specifically, `Array.lex as bs lt` is true if
|
||||
* `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`,
|
||||
or
|
||||
* there is an index `i` such that `lt as[i] bs[i]`, and for all `j < i`, `as[j] == bs[j]`.
|
||||
-/
|
||||
def lex [BEq α] (as bs : Array α) (lt : α → α → Bool := by exact (· < ·)) : Bool := Id.run do
|
||||
for h : i in [0 : min as.size bs.size] do
|
||||
|
||||
@@ -292,12 +292,15 @@ theorem mapFinIdx_eq_mapFinIdx_iff {xs : Array α} {f g : (i : Nat) → α → (
|
||||
(xs.mapFinIdx f).mapFinIdx g = xs.mapFinIdx (fun i a h => g i (f i a h) (by simpa using h)) := by
|
||||
simp [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapFinIdx_eq_mkArray_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {b : β} :
|
||||
xs.mapFinIdx f = mkArray xs.size b ↔ ∀ (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
|
||||
theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {b : β} :
|
||||
xs.mapFinIdx f = replicate xs.size b ↔ ∀ (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
|
||||
rcases xs with ⟨l⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.mapFinIdx_eq_replicate_iff]
|
||||
|
||||
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
|
||||
abbrev mapFinIdx_eq_mkArray_iff := @mapFinIdx_eq_replicate_iff
|
||||
|
||||
@[simp] theorem mapFinIdx_reverse {xs : Array α} {f : (i : Nat) → α → (h : i < xs.reverse.size) → β} :
|
||||
xs.reverse.mapFinIdx f = (xs.mapFinIdx (fun i a h => f (xs.size - 1 - i) a (by simp; omega))).reverse := by
|
||||
rcases xs with ⟨l⟩
|
||||
@@ -431,12 +434,15 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
|
||||
(xs.mapIdx f).mapIdx g = xs.mapIdx (fun i => g i ∘ f i) := by
|
||||
simp [mapIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mkArray_iff {xs : Array α} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f xs = mkArray xs.size b ↔ ∀ (i : Nat) (h : i < xs.size), f i xs[i] = b := by
|
||||
theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f xs = replicate xs.size b ↔ ∀ (i : Nat) (h : i < xs.size), f i xs[i] = b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.mapIdx_eq_replicate_iff]
|
||||
|
||||
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
|
||||
abbrev mapIdx_eq_mkArray_iff := @mapIdx_eq_replicate_iff
|
||||
|
||||
@[simp] theorem mapIdx_reverse {xs : Array α} {f : Nat → α → β} :
|
||||
xs.reverse.mapIdx f = (mapIdx (fun i => f (xs.size - 1 - i)) xs).reverse := by
|
||||
rcases xs with ⟨xs⟩
|
||||
|
||||
@@ -169,7 +169,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(xs : Array α) (f : (a : α) → a ∈ xs → β → β) (init : β) :
|
||||
forIn' xs init (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (xs.attach.foldl (fun b ⟨a, h⟩ => f a h b) init) := by
|
||||
@@ -211,7 +211,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(xs : Array α) (f : α → β → β) (init : β) :
|
||||
forIn xs init (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (xs.foldl (fun b a => f a b) init) := by
|
||||
|
||||
@@ -16,7 +16,8 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
@[simp] theorem ofFn_zero (f : Fin 0 → α) : ofFn f = #[] := rfl
|
||||
@[simp] theorem ofFn_zero (f : Fin 0 → α) : ofFn f = #[] := by
|
||||
simp [ofFn, ofFn.go]
|
||||
|
||||
theorem ofFn_succ (f : Fin (n+1) → α) :
|
||||
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f ⟨n, by omega⟩) := by
|
||||
|
||||
@@ -30,6 +30,16 @@ private def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi :
|
||||
(⟨i, ilo⟩, as.swap i hi)
|
||||
loop as lo lo
|
||||
|
||||
/--
|
||||
Sorts an array using the Quicksort algorithm.
|
||||
|
||||
The optional parameter `lt` specifies an ordering predicate. It defaults to `LT.lt`, which must be
|
||||
decidable to be used for sorting. Use `Array.qsortOrd` to sort the array according to the `Ord α`
|
||||
instance.
|
||||
|
||||
The optional parameters `low` and `high` delimit the region of the array that is sorted. Both are
|
||||
inclusive, and default to sorting the entire array.
|
||||
-/
|
||||
@[inline] def qsort (as : Array α) (lt : α → α → Bool := by exact (· < ·))
|
||||
(low := 0) (high := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
|
||||
@@ -50,7 +60,7 @@ private def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi :
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
Sort an array using `compare` to compare elements.
|
||||
Sorts an array using the Quicksort algorithm, using `Ord.compare` to compare elements.
|
||||
-/
|
||||
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
|
||||
xs.qsort fun x y => compare x y |>.isLT
|
||||
|
||||
@@ -39,7 +39,8 @@ theorem range'_ne_empty_iff (s : Nat) {n step : Nat} : range' s n step ≠ #[]
|
||||
@[simp] theorem range'_zero : range' s 0 step = #[] := by
|
||||
simp
|
||||
|
||||
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := rfl
|
||||
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := by
|
||||
simp [range', ofFn, ofFn.go]
|
||||
|
||||
@[simp] theorem range'_inj : range' s n = range' s' n' ↔ n = n' ∧ (n = 0 ∨ s = s') := by
|
||||
rw [← toList_inj]
|
||||
@@ -77,7 +78,7 @@ theorem range'_append (s m n step : Nat) :
|
||||
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append s m n 1
|
||||
|
||||
theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ #[s + step * n] := by
|
||||
exact (range'_append s n 1 step).symm
|
||||
simpa using (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]
|
||||
|
||||
@@ -11,11 +11,16 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
|
||||
/--
|
||||
Set an element in an array, using a proof that the index is in bounds.
|
||||
(This proof can usually be omitted, and will be synthesized automatically.)
|
||||
Replaces the element at a given index in an array.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
No bounds check is performed, but the function requires a proof that the index is in bounds. This
|
||||
proof can usually be omitted, and will be synthesized automatically.
|
||||
|
||||
The array is modified in-place if there are no other references to it.
|
||||
|
||||
Examples:
|
||||
* `#[0, 1, 2].set 1 5 = #[0, 5, 2]`
|
||||
* `#["orange", "apple"].set 1 "grape" = #["orange", "grape"]`
|
||||
-/
|
||||
@[extern "lean_array_fset"]
|
||||
def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_elem_tactic) :
|
||||
@@ -23,10 +28,15 @@ def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_e
|
||||
toList := xs.toList.set i v
|
||||
|
||||
/--
|
||||
Set an element in an array, or do nothing if the index is out of bounds.
|
||||
Replaces the element at the provided index in an array. The array is returned unmodified if the
|
||||
index is out of bounds.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
The array is modified in-place if there are no other references to it.
|
||||
|
||||
Examples:
|
||||
* `#[0, 1, 2].setIfInBounds 1 5 = #[0, 5, 2]`
|
||||
* `#["orange", "apple"].setIfInBounds 1 "grape" = #["orange", "grape"]`
|
||||
* `#["orange", "apple"].setIfInBounds 5 "grape" = #["orange", "apple"]`
|
||||
-/
|
||||
@[inline] def Array.setIfInBounds (xs : Array α) (i : Nat) (v : α) : Array α :=
|
||||
dite (LT.lt i xs.size) (fun h => xs.set i v h) (fun _ => xs)
|
||||
|
||||
@@ -7,18 +7,44 @@ prelude
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v w
|
||||
|
||||
structure Subarray (α : Type u) where
|
||||
/--
|
||||
A region of some underlying array.
|
||||
|
||||
A subarray contains an array together with the start and end indices of a region of interest.
|
||||
Subarrays can be used to avoid copying or allocating space, while being more convenient than
|
||||
tracking the bounds by hand. The region of interest consists of every index that is both greater
|
||||
than or equal to `start` and strictly less than `stop`.
|
||||
-/
|
||||
structure Subarray (α : Type u) where
|
||||
/-- The underlying array. -/
|
||||
array : Array α
|
||||
/-- The starting index of the region of interest (inclusive). -/
|
||||
start : Nat
|
||||
/-- The ending index of the region of interest (exclusive). -/
|
||||
stop : Nat
|
||||
/--
|
||||
The starting index is no later than the ending index.
|
||||
|
||||
The ending index is exclusive. If the starting and ending indices are equal, then the subarray is
|
||||
empty.
|
||||
-/
|
||||
start_le_stop : start ≤ stop
|
||||
/-- The stopping index is no later than the end of the array.
|
||||
|
||||
The ending index is exclusive. If it is equal to the size of the array, then the last element of
|
||||
the array is in the subarray.
|
||||
-/
|
||||
stop_le_array_size : stop ≤ array.size
|
||||
|
||||
namespace Subarray
|
||||
|
||||
/--
|
||||
Computes the size of the subarray.
|
||||
-/
|
||||
def size (s : Subarray α) : Nat :=
|
||||
s.stop - s.start
|
||||
|
||||
@@ -28,6 +54,11 @@ theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
|
||||
apply Nat.le_trans (Nat.sub_le stop start)
|
||||
assumption
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray.
|
||||
|
||||
The index is relative to the start of the subarray, rather than the underlying array.
|
||||
-/
|
||||
def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
have : s.start + i.val < s.array.size := by
|
||||
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size
|
||||
@@ -40,12 +71,33 @@ def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray, or returns a default value `v₀` when the index is out of
|
||||
bounds.
|
||||
|
||||
The index is relative to the start and end of the subarray, rather than the underlying array.
|
||||
-/
|
||||
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
|
||||
if h : i < s.size then s[i] else v₀
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray, or returns a default value when the index is out of bounds.
|
||||
|
||||
The index is relative to the start and end of the subarray, rather than the underlying array. The
|
||||
default value is that provided by the `Inhabited α` instance.
|
||||
-/
|
||||
abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
|
||||
getD s i default
|
||||
|
||||
/--
|
||||
Shrinks the subarray by incrementing its starting index if possible, returning it unchanged if not.
|
||||
|
||||
Examples:
|
||||
* `#[1,2,3].toSubarray.popFront.toArray = #[2, 3]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.toArray = #[3]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.popFront.toArray = #[]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.popFront.popFront.toArray = #[]`
|
||||
-/
|
||||
def popFront (s : Subarray α) : Subarray α :=
|
||||
if h : s.start < s.stop then
|
||||
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
|
||||
@@ -54,6 +106,8 @@ def popFront (s : Subarray α) : Subarray α :=
|
||||
|
||||
/--
|
||||
The empty subarray.
|
||||
|
||||
This empty subarray is backed by an empty array.
|
||||
-/
|
||||
protected def empty : Subarray α where
|
||||
array := #[]
|
||||
@@ -68,6 +122,12 @@ instance : EmptyCollection (Subarray α) :=
|
||||
instance : Inhabited (Subarray α) :=
|
||||
⟨{}⟩
|
||||
|
||||
/--
|
||||
The run-time implementation of `ForIn.forIn` for `Subarray`, which allows it to be used with `for`
|
||||
loops in `do`-notation.
|
||||
|
||||
This definition replaces `Subarray.forIn`.
|
||||
-/
|
||||
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
|
||||
let sz := USize.ofNat s.stop
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
@@ -80,6 +140,10 @@ instance : Inhabited (Subarray α) :=
|
||||
pure b
|
||||
loop (USize.ofNat s.start) b
|
||||
|
||||
/--
|
||||
The implementation of `ForIn.forIn` for `Subarray`, which allows it to be used with `for` loops in
|
||||
`do`-notation.
|
||||
-/
|
||||
-- TODO: provide reference implementation
|
||||
@[implemented_by Subarray.forInUnsafe]
|
||||
protected opaque forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
|
||||
@@ -88,46 +152,197 @@ protected opaque forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Mona
|
||||
instance : ForIn m (Subarray α) α where
|
||||
forIn := Subarray.forIn
|
||||
|
||||
/--
|
||||
Folds a monadic operation from left to right over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
|
||||
element of the subarray with the current accumulator value in turn. The monad in question may permit
|
||||
early termination or repetition.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldlM (init := "") fun acc x => do
|
||||
let l ← Option.guard (· ≠ 0) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
some "(3)red (5)green (4)blue "
|
||||
```
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldlM (init := 0) fun acc x => do
|
||||
let l ← Option.guard (· ≠ 5) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldlM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldlM f (init := init) (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Folds a monadic operation from right to left over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
|
||||
element of the subarray with the current accumulator value in turn, moving from the end to the
|
||||
start. The monad in question may permit early termination or repetition.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldrM (init := "") fun x acc => do
|
||||
let l ← Option.guard (· ≠ 0) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
some "(4)blue (5)green (3)red "
|
||||
```
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldrM (init := 0) fun x acc => do
|
||||
let l ← Option.guard (· ≠ 5) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldrM f (init := init) (start := as.stop) (stop := as.start)
|
||||
|
||||
/--
|
||||
Checks whether any of the elements in a subarray satisfy a monadic Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that satisfies the predicate is found.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.anyM fun x => do
|
||||
IO.println x
|
||||
pure (x == "blue")
|
||||
```
|
||||
```output
|
||||
green
|
||||
blue
|
||||
```
|
||||
```output
|
||||
true
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.anyM p (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Checks whether all of the elements in a subarray satisfy a monadic Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that does not satisfy the predicate is found.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.allM fun x => do
|
||||
IO.println x
|
||||
pure (x.length == 5)
|
||||
```
|
||||
```output
|
||||
green
|
||||
blue
|
||||
```
|
||||
```output
|
||||
false
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.allM p (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Runs a monadic action on each element of a subarray.
|
||||
|
||||
The elements are processed starting at the lowest index and moving up.
|
||||
-/
|
||||
@[inline]
|
||||
def forM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forM f (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Runs a monadic action on each element of a subarray, in reverse order.
|
||||
|
||||
The elements are processed starting at the highest index and moving down.
|
||||
-/
|
||||
@[inline]
|
||||
def forRevM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forRevM f (start := as.stop) (stop := as.start)
|
||||
|
||||
/--
|
||||
Folds an operation from left to right over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and combining each
|
||||
element of the subarray with the current accumulator value in turn.
|
||||
|
||||
Examples:
|
||||
* `#["red", "green", "blue"].toSubarray.foldl (· + ·.length) 0 = 12`
|
||||
* `#["red", "green", "blue"].toSubarray.popFront.foldl (· + ·.length) 0 = 9`
|
||||
-/
|
||||
@[inline]
|
||||
def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldlM f (init := init)
|
||||
|
||||
/--
|
||||
Folds an operation from right to left over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and combining each element of the
|
||||
subarray with the current accumulator value in turn, moving from the end to the start.
|
||||
|
||||
Examples:
|
||||
* `#eval #["red", "green", "blue"].toSubarray.foldr (·.length + ·) 0 = 12`
|
||||
* `#["red", "green", "blue"].toSubarray.popFront.foldlr (·.length + ·) 0 = 9`
|
||||
-/
|
||||
@[inline]
|
||||
def foldr {α : Type u} {β : Type v} (f : α → β → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldrM f (init := init)
|
||||
|
||||
/--
|
||||
Checks whether any of the elements in a subarray satisfy a Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that satisfies the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def any {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.anyM p
|
||||
|
||||
/--
|
||||
Checks whether all of the elements in a subarray satisfy a Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that does not satisfy the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def all {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.allM p
|
||||
|
||||
/--
|
||||
Applies a monadic function to each element in a subarray in reverse order, stopping at the first
|
||||
element for which the function succeeds by returning a value other than `none`. The succeeding value
|
||||
is returned, or `none` if there is no success.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.findSomeRevM? fun x => do
|
||||
IO.println x
|
||||
return Option.guard (· = 5) x.length
|
||||
```
|
||||
```output
|
||||
blue
|
||||
green
|
||||
```
|
||||
```output
|
||||
some 5
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Subarray α) (f : α → m (Option β)) : m (Option β) :=
|
||||
let rec @[specialize] find : (i : Nat) → i ≤ as.size → m (Option β)
|
||||
@@ -142,10 +357,39 @@ def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
find i this
|
||||
find as.size (Nat.le_refl _)
|
||||
|
||||
/--
|
||||
Applies a monadic Boolean predicate to each element in a subarray in reverse order, stopping at the
|
||||
first element that satisfies the predicate. The element that satisfies the predicate is returned, or
|
||||
`none` if no element satisfies it.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.findRevM? fun x => do
|
||||
IO.println x
|
||||
return (x.length = 5)
|
||||
```
|
||||
```output
|
||||
blue
|
||||
green
|
||||
```
|
||||
```output
|
||||
some 5
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def findRevM? {α : Type} {m : Type → Type w} [Monad m] (as : Subarray α) (p : α → m Bool) : m (Option α) :=
|
||||
as.findSomeRevM? fun a => return if (← p a) then some a else none
|
||||
|
||||
/--
|
||||
Tests each element in a subarray with a Boolean predicate in reverse order, stopping at the first
|
||||
element that satisfies the predicate. The element that satisfies the predicate is returned, or
|
||||
`none` if no element satisfies the predicate.
|
||||
|
||||
Examples:
|
||||
* `#["red", "green", "blue"].toSubarray.findRev? (·.length ≠ 4) = some "green"`
|
||||
* `#["red", "green", "blue"].toSubarray.findRev? (fun _ => true) = some "blue"`
|
||||
* `#["red", "green", "blue"].toSubarray 0 0 |>.findRev? (fun _ => true) = none`
|
||||
-/
|
||||
@[inline]
|
||||
def findRev? {α : Type} (as : Subarray α) (p : α → Bool) : Option α :=
|
||||
Id.run <| as.findRevM? p
|
||||
@@ -155,6 +399,12 @@ end Subarray
|
||||
namespace Array
|
||||
variable {α : Type u}
|
||||
|
||||
/--
|
||||
Returns a subarray of an array, with the given bounds.
|
||||
|
||||
If `start` or `stop` are not valid bounds for a subarray, then they are clamped to array's size.
|
||||
Additionally, the starting index is clamped to the ending index.
|
||||
-/
|
||||
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
|
||||
if h₂ : stop ≤ as.size then
|
||||
if h₁ : start ≤ stop then
|
||||
@@ -177,6 +427,9 @@ def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Suba
|
||||
start_le_stop := Nat.le_refl _,
|
||||
stop_le_array_size := Nat.le_refl _ }
|
||||
|
||||
/--
|
||||
Allocates a new array that contains the contents of the subarray.
|
||||
-/
|
||||
@[coe]
|
||||
def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
let mut as := mkEmpty (s.stop - s.start)
|
||||
@@ -186,8 +439,11 @@ def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
|
||||
instance : Coe (Subarray α) (Array α) := ⟨ofSubarray⟩
|
||||
|
||||
/-- A subarray with the provided bounds.-/
|
||||
syntax:max term noWs "[" withoutPosition(term ":" term) "]" : term
|
||||
/-- A subarray with the provided lower bound that extends to the rest of the array. -/
|
||||
syntax:max term noWs "[" withoutPosition(term ":") "]" : term
|
||||
/-- A subarray with the provided upper bound, starting at the index 0. -/
|
||||
syntax:max term noWs "[" withoutPosition(":" term) "]" : term
|
||||
|
||||
macro_rules
|
||||
@@ -197,6 +453,7 @@ macro_rules
|
||||
|
||||
end Array
|
||||
|
||||
@[inherit_doc Array.ofSubarray]
|
||||
def Subarray.toArray (s : Subarray α) : Array α :=
|
||||
Array.ofSubarray s
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Subarray
|
||||
/--
|
||||
Splits a subarray into two parts.
|
||||
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
|
||||
of which contains the remainder.
|
||||
-/
|
||||
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
|
||||
let ⟨i', isLt⟩ := i
|
||||
|
||||
@@ -149,10 +149,13 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array
|
||||
· rintro ⟨⟨ws⟩, ⟨xs⟩, ⟨ys⟩, ⟨zs⟩, h, rfl, rfl, h₁, h₂⟩
|
||||
exact ⟨ws, xs, ys, zs, by simp_all⟩
|
||||
|
||||
@[simp] theorem zipWith_mkArray {a : α} {b : β} {m n : Nat} :
|
||||
zipWith f (mkArray m a) (mkArray n b) = mkArray (min m n) (f a b) := by
|
||||
@[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
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated zipWith_replicate (since := "2025-03-18")]
|
||||
abbrev zipWith_mkArray := @zipWith_replicate
|
||||
|
||||
theorem map_uncurry_zip_eq_zipWith (f : α → β → γ) (as : Array α) (bs : Array β) :
|
||||
map (Function.uncurry f) (as.zip bs) = zipWith f as bs := by
|
||||
cases as
|
||||
@@ -270,10 +273,13 @@ theorem zip_eq_append_iff {as : Array α} {bs : Array β} :
|
||||
∃ as₁ as₂ bs₁ bs₂, as₁.size = bs₁.size ∧ as = as₁ ++ as₂ ∧ bs = bs₁ ++ bs₂ ∧ xs = zip as₁ bs₁ ∧ ys = zip as₂ bs₂ := by
|
||||
simp [zip_eq_zipWith, zipWith_eq_append_iff]
|
||||
|
||||
@[simp] theorem zip_mkArray {a : α} {b : β} {m n : Nat} :
|
||||
zip (mkArray m a) (mkArray n b) = mkArray (min m n) (a, b) := by
|
||||
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
|
||||
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated zip_replicate (since := "2025-03-18")]
|
||||
abbrev zip_mkArray := @zip_replicate
|
||||
|
||||
theorem zip_eq_zip_take_min (as : Array α) (bs : Array β) :
|
||||
zip as bs = zip (as.take (min as.size bs.size)) (bs.take (min as.size bs.size)) := by
|
||||
cases as
|
||||
@@ -317,9 +323,12 @@ theorem map_zipWithAll {δ : Type _} (f : α → β) (g : Option γ → Option
|
||||
simp [List.map_zipWithAll]
|
||||
|
||||
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
|
||||
zipWithAll f (mkArray n a) (mkArray n b) = mkArray n (f a b) := by
|
||||
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated zipWithAll_replicate (since := "2025-03-18")]
|
||||
abbrev zipWithAll_mkArray := @zipWithAll_replicate
|
||||
|
||||
/-! ### unzip -/
|
||||
|
||||
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
|
||||
@@ -360,6 +369,9 @@ theorem zip_of_prod {as : Array α} {bs : Array β} {xs : Array (α × β)} (hl
|
||||
(hr : xs.map Prod.snd = bs) : xs = as.zip bs := by
|
||||
rw [← hl, ← hr, ← zip_unzip xs, ← unzip_fst, ← unzip_snd, zip_unzip, zip_unzip]
|
||||
|
||||
@[simp] theorem unzip_mkArray {n : Nat} {a : α} {b : β} :
|
||||
unzip (mkArray n (a, b)) = (mkArray n a, mkArray n b) := by
|
||||
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
|
||||
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
|
||||
ext1 <;> simp
|
||||
|
||||
@[deprecated unzip_replicate (since := "2025-03-18")]
|
||||
abbrev unzip_mkArray := @unzip_replicate
|
||||
|
||||
@@ -18,7 +18,7 @@ operations on `Fin` are already defined. Some other possible representations are
|
||||
|
||||
We define many of the bitvector operations from the
|
||||
[`QF_BV` logic](https://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV).
|
||||
of SMT-LIBv2.
|
||||
of SMT-LIB v2.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
@@ -96,16 +96,10 @@ This will be renamed `getMsb` after the existing deprecated alias is removed.
|
||||
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
|
||||
x.toNat.testBit i
|
||||
|
||||
@[deprecated getLsbD (since := "2024-08-29"), inherit_doc getLsbD]
|
||||
def getLsb (x : BitVec w) (i : Nat) : Bool := x.getLsbD i
|
||||
|
||||
/-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/
|
||||
@[inline] def getMsbD (x : BitVec w) (i : Nat) : Bool :=
|
||||
i < w && x.getLsbD (w-1-i)
|
||||
|
||||
@[deprecated getMsbD (since := "2024-08-29"), inherit_doc getMsbD]
|
||||
def getMsb (x : BitVec w) (i : Nat) : Bool := x.getMsbD i
|
||||
|
||||
/-- Return most-significant bit in bitvector. -/
|
||||
@[inline] protected def msb (x : BitVec n) : Bool := getMsbD x 0
|
||||
|
||||
@@ -202,7 +196,7 @@ section arithmetic
|
||||
Negation for bit vectors. This can be interpreted as either signed or unsigned negation
|
||||
modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvneg`.
|
||||
SMT-LIB name: `bvneg`.
|
||||
-/
|
||||
protected def neg (x : BitVec n) : BitVec n := .ofNat n (2^n - x.toNat)
|
||||
instance : Neg (BitVec n) := ⟨.neg⟩
|
||||
@@ -216,7 +210,7 @@ protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
|
||||
Multiplication for bit vectors. This can be interpreted as either signed or unsigned
|
||||
multiplication modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvmul`.
|
||||
SMT-LIB name: `bvmul`.
|
||||
-/
|
||||
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
|
||||
instance : Mul (BitVec n) := ⟨.mul⟩
|
||||
@@ -231,7 +225,7 @@ instance : Div (BitVec n) := ⟨.udiv⟩
|
||||
/--
|
||||
Unsigned modulo for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvurem`.
|
||||
SMT-LIB name: `bvurem`.
|
||||
-/
|
||||
def umod (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat % y.toNat)#'(Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt)
|
||||
@@ -239,10 +233,10 @@ instance : Mod (BitVec n) := ⟨.umod⟩
|
||||
|
||||
/--
|
||||
Unsigned division for bit vectors using the
|
||||
[SMT-Lib convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml)
|
||||
[SMT-LIB convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml)
|
||||
where division by zero returns the `allOnes` bitvector.
|
||||
|
||||
SMT-Lib name: `bvudiv`.
|
||||
SMT-LIB name: `bvudiv`.
|
||||
-/
|
||||
def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then allOnes n else udiv x y
|
||||
|
||||
@@ -265,11 +259,11 @@ def sdiv (x y : BitVec n) : BitVec n :=
|
||||
| true, true => udiv (.neg x) (.neg y)
|
||||
|
||||
/--
|
||||
Signed division for bit vectors using SMTLIB rules for division by zero.
|
||||
Signed division for bit vectors using SMT-LIB rules for division by zero.
|
||||
|
||||
Specifically, `smtSDiv x 0 = if x >= 0 then -1 else 1`
|
||||
|
||||
SMT-Lib name: `bvsdiv`.
|
||||
SMT-LIB name: `bvsdiv`.
|
||||
-/
|
||||
def smtSDiv (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
@@ -281,7 +275,7 @@ def smtSDiv (x y : BitVec n) : BitVec n :=
|
||||
/--
|
||||
Remainder for signed division rounding to zero.
|
||||
|
||||
SMT_Lib name: `bvsrem`.
|
||||
SMT-LIB name: `bvsrem`.
|
||||
-/
|
||||
def srem (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
@@ -293,7 +287,7 @@ def srem (x y : BitVec n) : BitVec n :=
|
||||
/--
|
||||
Remainder for signed division rounded to negative infinity.
|
||||
|
||||
SMT_Lib name: `bvsmod`.
|
||||
SMT-LIB name: `bvsmod`.
|
||||
-/
|
||||
def smod (x y : BitVec m) : BitVec m :=
|
||||
match x.msb, y.msb with
|
||||
@@ -327,14 +321,14 @@ section relations
|
||||
/--
|
||||
Unsigned less-than for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvult`.
|
||||
SMT-LIB name: `bvult`.
|
||||
-/
|
||||
protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat
|
||||
|
||||
/--
|
||||
Unsigned less-than-or-equal-to for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvule`.
|
||||
SMT-LIB name: `bvule`.
|
||||
-/
|
||||
protected def ule (x y : BitVec n) : Bool := x.toNat ≤ y.toNat
|
||||
|
||||
@@ -345,14 +339,14 @@ Signed less-than for bit vectors.
|
||||
BitVec.slt 6#4 7 = true
|
||||
BitVec.slt 7#4 8 = false
|
||||
```
|
||||
SMT-Lib name: `bvslt`.
|
||||
SMT-LIB name: `bvslt`.
|
||||
-/
|
||||
protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt
|
||||
|
||||
/--
|
||||
Signed less-than-or-equal-to for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvsle`.
|
||||
SMT-LIB name: `bvsle`.
|
||||
-/
|
||||
protected def sle (x y : BitVec n) : Bool := x.toInt ≤ y.toInt
|
||||
|
||||
@@ -384,7 +378,7 @@ def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toN
|
||||
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
|
||||
yield a new bitvector of size `hi - lo + 1`.
|
||||
|
||||
SMT-Lib name: `extract`.
|
||||
SMT-LIB name: `extract`.
|
||||
-/
|
||||
def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x
|
||||
|
||||
@@ -415,7 +409,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
SMT-LIB name: `zero_extend`.
|
||||
-/
|
||||
def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
|
||||
if h : w ≤ v then
|
||||
@@ -428,7 +422,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
SMT-LIB name: `zero_extend`.
|
||||
-/
|
||||
abbrev zeroExtend := @setWidth
|
||||
|
||||
@@ -437,7 +431,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
SMT-LIB name: `zero_extend`.
|
||||
-/
|
||||
abbrev truncate := @setWidth
|
||||
|
||||
@@ -445,7 +439,7 @@ abbrev truncate := @setWidth
|
||||
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant
|
||||
bit in `x`. If `x` is an empty vector, then the sign is treated as zero.
|
||||
|
||||
SMT-Lib name: `sign_extend`.
|
||||
SMT-LIB name: `sign_extend`.
|
||||
-/
|
||||
def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt
|
||||
|
||||
@@ -460,7 +454,7 @@ Bitwise AND for bit vectors.
|
||||
0b1010#4 &&& 0b0110#4 = 0b0010#4
|
||||
```
|
||||
|
||||
SMT-Lib name: `bvand`.
|
||||
SMT-LIB name: `bvand`.
|
||||
-/
|
||||
protected def and (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat &&& y.toNat)#'(Nat.and_lt_two_pow x.toNat y.isLt)
|
||||
@@ -473,7 +467,7 @@ Bitwise OR for bit vectors.
|
||||
0b1010#4 ||| 0b0110#4 = 0b1110#4
|
||||
```
|
||||
|
||||
SMT-Lib name: `bvor`.
|
||||
SMT-LIB name: `bvor`.
|
||||
-/
|
||||
protected def or (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat ||| y.toNat)#'(Nat.or_lt_two_pow x.isLt y.isLt)
|
||||
@@ -486,7 +480,7 @@ instance : OrOp (BitVec w) := ⟨.or⟩
|
||||
0b1010#4 ^^^ 0b0110#4 = 0b1100#4
|
||||
```
|
||||
|
||||
SMT-Lib name: `bvxor`.
|
||||
SMT-LIB name: `bvxor`.
|
||||
-/
|
||||
protected def xor (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt)
|
||||
@@ -498,7 +492,7 @@ Bitwise NOT for bit vectors.
|
||||
```lean
|
||||
~~~(0b0101#4) == 0b1010
|
||||
```
|
||||
SMT-Lib name: `bvnot`.
|
||||
SMT-LIB name: `bvnot`.
|
||||
-/
|
||||
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
|
||||
instance : Complement (BitVec w) := ⟨.not⟩
|
||||
@@ -507,7 +501,7 @@ 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`.
|
||||
|
||||
SMT-Lib name: `bvshl` except this operator uses a `Nat` shift value.
|
||||
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)
|
||||
instance : HShiftLeft (BitVec w) Nat (BitVec w) := ⟨.shiftLeft⟩
|
||||
@@ -516,7 +510,7 @@ 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.
|
||||
|
||||
SMT-Lib name: `bvlshr` except this operator uses a `Nat` shift value.
|
||||
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
|
||||
@@ -532,7 +526,7 @@ 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`.
|
||||
|
||||
SMT-Lib name: `bvashr` except this operator uses a `Nat` shift value.
|
||||
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)
|
||||
|
||||
@@ -544,7 +538,7 @@ 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`.
|
||||
SMT-LIB name: `bvashr`.
|
||||
-/
|
||||
def sshiftRight' (a : BitVec n) (s : BitVec m) : BitVec n := a.sshiftRight s.toNat
|
||||
|
||||
@@ -560,7 +554,7 @@ bits wrapping around to fill the low bits.
|
||||
```lean
|
||||
rotateLeft 0b0011#4 3 = 0b1001
|
||||
```
|
||||
SMT-Lib name: `rotate_left` except this operator uses a `Nat` shift amount.
|
||||
SMT-LIB name: `rotate_left` except this operator uses a `Nat` shift amount.
|
||||
-/
|
||||
def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := rotateLeftAux x (n % w)
|
||||
|
||||
@@ -579,7 +573,7 @@ bottom `n` bits wrapping around to fill the high bits.
|
||||
```lean
|
||||
rotateRight 0b01001#5 1 = 0b10100
|
||||
```
|
||||
SMT-Lib name: `rotate_right` except this operator uses a `Nat` shift amount.
|
||||
SMT-LIB name: `rotate_right` except this operator uses a `Nat` shift amount.
|
||||
-/
|
||||
def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
|
||||
|
||||
@@ -587,7 +581,7 @@ def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
|
||||
Concatenation of bitvectors. This uses the "big endian" convention that the more significant
|
||||
input is on the left, so `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
|
||||
|
||||
SMT-Lib name: `concat`.
|
||||
SMT-LIB name: `concat`.
|
||||
-/
|
||||
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
|
||||
shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs
|
||||
@@ -684,18 +678,26 @@ def ofBoolListLE : (bs : List Bool) → BitVec bs.length
|
||||
|
||||
/-- `uaddOverflow x y` returns `true` if addition of `x` and `y` results in *unsigned* overflow.
|
||||
|
||||
SMT-Lib name: `bvuaddo`.
|
||||
SMT-LIB name: `bvuaddo`.
|
||||
-/
|
||||
def uaddOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat + y.toNat ≥ 2 ^ w
|
||||
|
||||
/-- `saddOverflow x y` returns `true` if addition of `x` and `y` results in *signed* overflow,
|
||||
treating `x` and `y` as 2's complement signed bitvectors.
|
||||
|
||||
SMT-Lib name: `bvsaddo`.
|
||||
SMT-LIB name: `bvsaddo`.
|
||||
-/
|
||||
def saddOverflow {w : Nat} (x y : BitVec w) : Bool :=
|
||||
(x.toInt + y.toInt ≥ 2 ^ (w - 1)) || (x.toInt + y.toInt < - 2 ^ (w - 1))
|
||||
|
||||
/-- `negOverflow x` returns `true` if the negation of `x` results in overflow.
|
||||
For a BitVec `x` with width `0 < w`, this only happens if `x = intMin`.
|
||||
|
||||
SMT-Lib name: `bvnego`.
|
||||
-/
|
||||
def negOverflow {w : Nat} (x : BitVec w) : Bool :=
|
||||
x.toInt == - 2 ^ (w - 1)
|
||||
|
||||
/- ### reverse -/
|
||||
|
||||
/-- Reverse the bits in a bitvector. -/
|
||||
|
||||
@@ -35,7 +35,7 @@ section arithmetic
|
||||
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
|
||||
modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvadd`.
|
||||
SMT-LIB name: `bvadd`.
|
||||
-/
|
||||
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
|
||||
instance : Add (BitVec n) := ⟨BitVec.add⟩
|
||||
|
||||
@@ -91,7 +91,7 @@ First, we prove bitvector lemmas to unfold a high-level operation (such as multi
|
||||
into already bitblastable operations (such as addition and left shift).
|
||||
We then use these lemmas to prove the correctness of the circuit that `bv_decide` builds.
|
||||
|
||||
We use this workflow to implement bitblasting for all SMT-LIB2 operations.
|
||||
We use this workflow to implement bitblasting for all SMT-LIB v2 operations.
|
||||
|
||||
## Main results
|
||||
* `x + y : BitVec w` is `(adc x y false).2`.
|
||||
@@ -513,6 +513,9 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
|
||||
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
|
||||
omega
|
||||
|
||||
@[simp] theorem BitVec.setWidth_neg_of_le {x : BitVec v} (h : w ≤ v) : BitVec.setWidth w (-x) = -BitVec.setWidth w x := by
|
||||
simp [← BitVec.signExtend_eq_setWidth_of_le _ h, BitVec.signExtend_neg_of_le h]
|
||||
|
||||
/-! ### abs -/
|
||||
|
||||
theorem msb_abs {w : Nat} {x : BitVec w} :
|
||||
@@ -1296,6 +1299,15 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem negOverflow_eq {w : Nat} (x : BitVec w) :
|
||||
(negOverflow x) = (decide (0 < w) && (x == intMin w)) := by
|
||||
simp only [negOverflow]
|
||||
rcases w with _|w
|
||||
· simp [toInt_of_zero_length, Int.min_eq_right]
|
||||
· suffices - 2 ^ w = (intMin (w + 1)).toInt by simp [beq_eq_decide_eq, ← toInt_inj, this]
|
||||
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod, Int.neg_inj]
|
||||
rw_mod_cast [Nat.mod_eq_of_lt (by simp [Nat.pow_lt_pow_succ])]
|
||||
|
||||
/- ### umod -/
|
||||
|
||||
theorem getElem_umod {n d : BitVec w} (hi : i < w) :
|
||||
@@ -1338,4 +1350,53 @@ theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x,
|
||||
have := congrArg g h'
|
||||
simpa [h] using this
|
||||
|
||||
/-! ### Lemmas that use Bitblasting circuits -/
|
||||
|
||||
theorem add_sub_comm {x y : BitVec w} : x + y - z = x - z + y := by
|
||||
apply eq_of_toNat_eq
|
||||
simp only [toNat_sub, toNat_add, add_mod_mod, mod_add_mod]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem sub_add_comm {x y : BitVec w} : x - y + z = x + z - y := by
|
||||
rw [add_sub_comm]
|
||||
|
||||
theorem not_add_one {x : BitVec w} : ~~~ (x + 1#w) = ~~~ x - 1#w := by
|
||||
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
|
||||
|
||||
theorem not_add_eq_not_neg {x y : BitVec w} : ~~~ (x + y) = ~~~ x - y := by
|
||||
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
|
||||
simp only [sub_toAdd]
|
||||
rw [BitVec.add_assoc, @BitVec.add_comm _ (-y), ← BitVec.add_assoc]
|
||||
|
||||
theorem not_sub_one_eq_not_add_one {x : BitVec w} : ~~~ (x - 1#w) = ~~~ x + 1#w := by
|
||||
rw [not_eq_neg_add, not_eq_neg_add, neg_sub,
|
||||
BitVec.add_sub_cancel, BitVec.sub_add_cancel]
|
||||
|
||||
theorem not_sub_eq_not_add {x y : BitVec w} : ~~~ (x - y) = ~~~ x + y := by
|
||||
rw [BitVec.sub_toAdd, not_add_eq_not_neg, sub_neg]
|
||||
|
||||
/-- The value of `(carry i x y false)` can be computed by truncating `x` and `y`
|
||||
to `len` bits where `len ≥ i`. -/
|
||||
theorem carry_extractLsb'_eq_carry {w i len : Nat} (hi : i < len)
|
||||
{x y : BitVec w} {b : Bool}:
|
||||
(carry i (extractLsb' 0 len x) (extractLsb' 0 len y) b)
|
||||
= (carry i x y b) := by
|
||||
simp only [carry, extractLsb'_toNat, shiftRight_zero, toNat_false, Nat.add_zero, ge_iff_le,
|
||||
decide_eq_decide]
|
||||
have : 2 ^ i ∣ 2^len := by
|
||||
apply Nat.pow_dvd_pow
|
||||
omega
|
||||
rw [Nat.mod_mod_of_dvd _ this, Nat.mod_mod_of_dvd _ this]
|
||||
|
||||
/--
|
||||
The `[0..len)` low bits of `x + y` can be computed by truncating `x` and `y`
|
||||
to `len` bits and then adding.
|
||||
-/
|
||||
theorem extractLsb'_add {w len : Nat} {x y : BitVec w} (hlen : len ≤ w) :
|
||||
(x + y).extractLsb' 0 len = x.extractLsb' 0 len + y.extractLsb' 0 len := by
|
||||
ext i hi
|
||||
rw [getElem_extractLsb', Nat.zero_add, getLsbD_add (by omega)]
|
||||
simp [getElem_add, carry_extractLsb'_eq_carry hi, getElem_extractLsb', Nat.zero_add]
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -260,7 +260,7 @@ theorem msb_of_zero_length (h : w = 0) (x : BitVec w) : x.msb = false := by
|
||||
|
||||
theorem ofFin_ofNat (n : Nat) :
|
||||
ofFin (no_index (OfNat.ofNat n : Fin (2^w))) = OfNat.ofNat n := by
|
||||
simp only [OfNat.ofNat, Fin.ofNat', BitVec.ofNat, Nat.and_pow_two_sub_one_eq_mod]
|
||||
simp only [OfNat.ofNat, Fin.ofNat', BitVec.ofNat, Nat.and_two_pow_sub_one_eq_mod]
|
||||
|
||||
theorem eq_of_toFin_eq : ∀ {x y : BitVec w}, x.toFin = y.toFin → x = y
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
@@ -650,7 +650,7 @@ theorem two_mul_toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by
|
||||
rcases w with _|w'
|
||||
· omega
|
||||
· rw [← Nat.two_pow_pred_add_two_pow_pred (by omega), ← Nat.two_mul, Nat.add_sub_cancel]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
|
||||
norm_cast; omega
|
||||
|
||||
theorem two_mul_toInt_le {w : Nat} {x : BitVec w} : 2 * x.toInt ≤ 2 ^ w - 1 :=
|
||||
@@ -676,7 +676,7 @@ theorem le_two_mul_toInt {w : Nat} {x : BitVec w} : -2 ^ w ≤ 2 * x.toInt := by
|
||||
rcases w with _|w'
|
||||
· omega
|
||||
· rw [← Nat.two_pow_pred_add_two_pow_pred (by omega), ← Nat.two_mul, Nat.add_sub_cancel]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
|
||||
norm_cast; omega
|
||||
|
||||
|
||||
@@ -1072,6 +1072,26 @@ theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h
|
||||
apply eq_of_toNat_eq
|
||||
simp [extractLsb']
|
||||
|
||||
theorem getLsbD_eq_extractLsb' (x : BitVec w) (i : Nat) :
|
||||
x.getLsbD i = (x.extractLsb' i 1 == 1#1) := by
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp [BitVec.eq_of_getLsbD_eq_iff]
|
||||
|
||||
theorem getElem_eq_extractLsb' (x : BitVec w) (i : Nat) (h : i < w) :
|
||||
x[i] = (x.extractLsb' i 1 == 1#1) := by
|
||||
rw [← getLsbD_eq_getElem, getLsbD_eq_extractLsb']
|
||||
|
||||
@[simp]
|
||||
theorem extractLsb'_zero {w start len : Nat} : (0#w).extractLsb' start len = 0#len := by
|
||||
apply eq_of_toNat_eq
|
||||
simp [extractLsb']
|
||||
|
||||
@[simp]
|
||||
theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
|
||||
x.extractLsb' start 0 = 0#0 := by
|
||||
ext i hi
|
||||
omega
|
||||
|
||||
/-! ### allOnes -/
|
||||
|
||||
@[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
|
||||
@@ -1553,6 +1573,22 @@ theorem not_self_ne {a : BitVec w} (h : 0 < w) : ~~~a ≠ a := by
|
||||
rw [ne_comm]
|
||||
simp [h]
|
||||
|
||||
theorem not_and {x y : BitVec w} : ~~~ (x &&& y) = ~~~ x ||| ~~~ y := by
|
||||
ext i
|
||||
simp
|
||||
|
||||
theorem not_or {x y : BitVec w} : ~~~ (x ||| y) = ~~~ x &&& ~~~ y := by
|
||||
ext i
|
||||
simp
|
||||
|
||||
theorem not_xor_left {x y : BitVec w} : ~~~ (x ^^^ y) = ~~~ x ^^^ y := by
|
||||
ext i
|
||||
simp
|
||||
|
||||
theorem not_xor_right {x y : BitVec w} : ~~~ (x ^^^ y) = x ^^^ ~~~ y := by
|
||||
ext i
|
||||
simp
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
|
||||
@@ -1702,6 +1738,10 @@ theorem allOnes_shiftLeft_or_shiftLeft {x : BitVec w} {n : Nat} :
|
||||
BitVec.allOnes w <<< n ||| x <<< n = BitVec.allOnes w <<< n := by
|
||||
simp [← shiftLeft_or_distrib]
|
||||
|
||||
@[simp] theorem setWidth_shiftLeft_of_le {x : BitVec w} {y : Nat} (hi : i ≤ w) :
|
||||
(x <<< y).setWidth i = x.setWidth i <<< y :=
|
||||
eq_of_getElem_eq (fun j hj => Bool.eq_iff_iff.2 (by simp; omega))
|
||||
|
||||
/-! ### shiftLeft reductions from BitVec to Nat -/
|
||||
|
||||
@[simp]
|
||||
@@ -1853,6 +1893,15 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
|
||||
case succ nn ih =>
|
||||
simp [BitVec.ushiftRight_eq, getMsbD_ushiftRight, BitVec.msb, ih, show nn + 1 > 0 by omega]
|
||||
|
||||
@[simp] theorem setWidth_ushiftRight {x : BitVec w} {y : Nat} (hi : w ≤ i) :
|
||||
(x >>> y).setWidth i = x.setWidth i >>> y := by
|
||||
refine eq_of_getElem_eq (fun j hj => ?_)
|
||||
simp only [getElem_setWidth, getLsbD_ushiftRight, getElem_ushiftRight, getLsbD_setWidth,
|
||||
Bool.iff_and_self, decide_eq_true_eq]
|
||||
intro ha
|
||||
have := lt_of_getLsbD ha
|
||||
omega
|
||||
|
||||
/-! ### ushiftRight reductions from BitVec to Nat -/
|
||||
|
||||
@[simp]
|
||||
@@ -2253,7 +2302,7 @@ private theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
|
||||
rw [hk, testBit_toNat, getLsbD_signExtend, Nat.pow_add, ← Nat.mul_sub_one, Nat.add_comm (x.toNat)]
|
||||
by_cases hx : x.msb
|
||||
· simp only [hx, Bool.if_true_right, ↓reduceIte,
|
||||
Nat.testBit_mul_pow_two_add _ x.isLt,
|
||||
Nat.testBit_two_pow_mul_add _ x.isLt,
|
||||
testBit_toNat, Nat.testBit_two_pow_sub_one]
|
||||
-- Case analysis on i being in the intervals [0..w), [w..w + k), [w+k..∞)
|
||||
have hi : i < w ∨ (w ≤ i ∧ i < w + k) ∨ w + k ≤ i := by omega
|
||||
@@ -2507,7 +2556,7 @@ theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v ≤ w) :
|
||||
· simp [hiv, getLsbD_ge x i (by omega)]
|
||||
|
||||
theorem setWidth_eq_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} (h : w ≤ v) :
|
||||
x.setWidth w = (x.extractLsb' 0 w).cast (by omega) := by
|
||||
x.setWidth w = x.extractLsb' 0 w := by
|
||||
rw [setWidth_eq_append_extractLsb']
|
||||
ext i hi
|
||||
simp only [getElem_cast, getElem_append]
|
||||
@@ -2556,6 +2605,97 @@ theorem not_extractLsb'_append_not_extractLsb'_eq_not_extractLsb' {x : BitVec w}
|
||||
congr 1
|
||||
omega
|
||||
|
||||
/-- A sign extension of `x : BitVec w` equals high bits of either `0` or `1` depending on `x.msb`,
|
||||
followed by the low bits of `x`. -/
|
||||
theorem signExtend_eq_append_extractLsb' {w v : Nat} {x : BitVec w} :
|
||||
x.signExtend v =
|
||||
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x.extractLsb' 0 (min w v)).cast (by omega) := by
|
||||
ext i hi
|
||||
simp only [getElem_cast]
|
||||
cases hx : x.msb
|
||||
· simp only [hx, signExtend_eq_setWidth_of_msb_false, getElem_setWidth, Bool.false_eq_true,
|
||||
↓reduceIte, getElem_append, getElem_extractLsb', Nat.zero_add, getElem_zero, dite_eq_ite,
|
||||
Bool.if_false_right, Bool.iff_and_self, decide_eq_true_eq]
|
||||
intros hi
|
||||
have hw : i < w := lt_of_getLsbD hi
|
||||
omega
|
||||
· simp [signExtend_eq_not_setWidth_not_of_msb_true hx, getElem_append, Nat.lt_min, hi]
|
||||
|
||||
/-- A sign extension of `x : BitVec w` to a larger bitwidth `v ≥ w`
|
||||
equals high bits of either `0` or `1` depending on `x.msb`, followed by `x`. -/
|
||||
theorem signExtend_eq_append_of_le {w v : Nat} {x : BitVec w} (h : w ≤ v) :
|
||||
x.signExtend v =
|
||||
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x).cast (by omega) := by
|
||||
ext i hi
|
||||
cases hx : x.msb <;>
|
||||
simp [getElem_cast, hx, getElem_append, getElem_signExtend]
|
||||
|
||||
/--
|
||||
The 'master theorem' for extracting bits from `(xhi ++ xlo)`,
|
||||
which performs a case analysis on the start index, length, and the lengths of `xlo, xhi`.
|
||||
· If the start index is entirely out of the `xlo` bitvector, then grab the bits from `xhi`.
|
||||
· If the start index is entirely contained in the `xlo` bitvector, then grab the bits from `xlo`.
|
||||
· If the start index is split between the two bitvectors,
|
||||
then append `(w - start)` bits from `xlo` with `(len - (w - start))` bits from xhi.
|
||||
Diagramatically:
|
||||
```
|
||||
xhi xlo
|
||||
(<---------------------](<-------w--------]
|
||||
start+len..start: (<-----len---*------]
|
||||
w - start: *------*
|
||||
len - (w -start): *------------*
|
||||
```
|
||||
-/
|
||||
theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start len : Nat} :
|
||||
extractLsb' start len (xhi ++ xlo) =
|
||||
if hstart : start < w
|
||||
then
|
||||
if hlen : start + len < w
|
||||
then extractLsb' start len xlo
|
||||
else
|
||||
(((extractLsb' (start - w) (len - (w - start)) xhi) ++
|
||||
extractLsb' start (w - start) xlo)).cast (by omega)
|
||||
else
|
||||
extractLsb' (start - w) len xhi := by
|
||||
by_cases hstart : start < w
|
||||
· simp only [hstart, ↓reduceDIte]
|
||||
by_cases hlen : start + len < w
|
||||
· simp only [hlen, ↓reduceDIte]
|
||||
ext i hi
|
||||
simp only [getElem_extractLsb', getLsbD_append, ite_eq_left_iff, Nat.not_lt]
|
||||
intros hcontra
|
||||
omega
|
||||
· simp only [hlen, ↓reduceDIte]
|
||||
ext i hi
|
||||
simp only [getElem_extractLsb', getLsbD_append, getElem_cast,
|
||||
getElem_append, dite_eq_ite]
|
||||
by_cases hi₂ : start + i < w
|
||||
· simp [hi₂, show i < min len w by omega, show i < w - start by omega]
|
||||
· simp [hi₂, ↓reduceIte, show ¬i < w - start by omega,
|
||||
show start + i - w = start - w + (i - (w - start)) by omega]
|
||||
· simp only [hstart, ↓reduceDIte]
|
||||
ext i hi
|
||||
simp [getElem_extractLsb', getLsbD_append,
|
||||
show ¬start + i < w by omega, ↓reduceIte,
|
||||
show start + i - w = start - w + i by omega]
|
||||
|
||||
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
|
||||
the bits from `xlo` when `start + len` is within `xlo`.
|
||||
-/
|
||||
theorem extractLsb'_append_eq_of_lt {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||||
{start len : Nat} (h : start + len < w) :
|
||||
extractLsb' start len (xhi ++ xlo) = extractLsb' start len xlo := by
|
||||
simp [extractLsb'_append_eq_ite, h]
|
||||
omega
|
||||
|
||||
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
|
||||
the bits from `xhi` when `start` is outside `xlo`.
|
||||
-/
|
||||
theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||||
{start len : Nat} (h : w ≤ start) :
|
||||
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
|
||||
simp [extractLsb'_append_eq_ite, h, show ¬ start < w by omega]
|
||||
|
||||
/-! ### rev -/
|
||||
|
||||
theorem getLsbD_rev (x : BitVec w) (i : Fin w) :
|
||||
@@ -2587,7 +2727,7 @@ theorem getMsbD_rev (x : BitVec w) (i : Fin w) :
|
||||
/-- Variant of `toNat_cons` using `+` instead of `|||`. -/
|
||||
theorem toNat_cons' {x : BitVec w} :
|
||||
(cons a x).toNat = (a.toNat <<< w) + x.toNat := by
|
||||
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.mul_add_lt_is_or, x.isLt]
|
||||
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.two_pow_add_eq_or_of_lt, x.isLt]
|
||||
|
||||
theorem getLsbD_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
|
||||
getLsbD (cons b x) i = if i = n then b else getLsbD x i := by
|
||||
@@ -3017,6 +3157,9 @@ theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1#w := 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)]
|
||||
|
||||
theorem not_eq_neg_add (x : BitVec w) : ~~~ x = -x - 1#w := by
|
||||
rw [eq_sub_iff_add_eq, neg_eq_not_add, BitVec.add_comm]
|
||||
|
||||
@[simp]
|
||||
theorem neg_neg {x : BitVec w} : - - x = x := by
|
||||
by_cases h : x = 0#w
|
||||
@@ -3069,6 +3212,22 @@ theorem not_neg (x : BitVec w) : ~~~(-x) = x + -1#w := by
|
||||
show (_ - x.toNat) % _ = _ by rw [Nat.mod_eq_of_lt (by omega)]]
|
||||
omega
|
||||
|
||||
theorem neg_add {x y : BitVec w} : - (x + y) = - x - y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg, toInt_add, Int.neg_add, Int.add_neg_eq_sub]
|
||||
|
||||
theorem add_neg_eq_sub {x y : BitVec w} : x + - y = (x - y) := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg, Int.sub_eq_add_neg]
|
||||
|
||||
@[simp]
|
||||
theorem sub_neg {x y : BitVec w} : x - - y = x + y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg, Int.bmod_neg]
|
||||
|
||||
theorem neg_sub {x y : BitVec w} : - (x - y) = - x + y := by
|
||||
rw [sub_toAdd, neg_add, sub_neg]
|
||||
|
||||
/- ### add/sub injectivity -/
|
||||
|
||||
@[simp]
|
||||
@@ -3228,7 +3387,7 @@ theorem mul_eq_and {a b : BitVec 1} : a * b = a &&& b := by
|
||||
|
||||
@[simp] protected theorem neg_mul (x y : BitVec w) : -x * y = -(x * y) := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg]
|
||||
simp [toInt_neg, Int.neg_mul]
|
||||
|
||||
@[simp] protected theorem mul_neg (x y : BitVec w) : x * -y = -(x * y) := by
|
||||
rw [BitVec.mul_comm, BitVec.neg_mul, BitVec.mul_comm]
|
||||
@@ -3237,6 +3396,24 @@ protected theorem neg_mul_neg (x y : BitVec w) : -x * -y = x * y := by simp
|
||||
|
||||
protected theorem neg_mul_comm (x y : BitVec w) : -x * y = x * -y := by simp
|
||||
|
||||
theorem mul_sub {x y z : BitVec w} :
|
||||
x * (y - z) = x * y - x * z := by
|
||||
rw [← add_neg_eq_sub, mul_add, BitVec.mul_neg, add_neg_eq_sub]
|
||||
|
||||
theorem neg_add_mul_eq_mul_not {x y : BitVec w} :
|
||||
- (x + x * y) = x * ~~~ y := by
|
||||
rw [neg_add, sub_toAdd, ← BitVec.mul_neg, neg_eq_not_add y, mul_add,
|
||||
BitVec.mul_one, BitVec.add_comm, BitVec.add_assoc,
|
||||
BitVec.add_right_eq_self, add_neg_eq_sub, BitVec.sub_self]
|
||||
|
||||
theorem neg_mul_not_eq_add_mul {x y : BitVec w} :
|
||||
- (x * ~~~ y) = x + x * y := by
|
||||
rw [not_eq_neg_add, mul_sub, neg_sub, ← BitVec.mul_neg, neg_neg,
|
||||
BitVec.mul_one, BitVec.add_comm]
|
||||
|
||||
theorem neg_eq_neg_one_mul (b : BitVec w) : -b = -1#w * b :=
|
||||
BitVec.eq_of_toInt_eq (by simp)
|
||||
|
||||
/-! ### le and lt -/
|
||||
|
||||
@[bitvec_to_nat] theorem le_def {x y : BitVec n} :
|
||||
@@ -3341,6 +3518,12 @@ theorem allOnes_le_iff {x : BitVec w} : allOnes w ≤ x ↔ x = allOnes w := by
|
||||
exact Eq.symm (BitVec.le_antisymm h this)
|
||||
· simp_all
|
||||
|
||||
@[simp]
|
||||
theorem lt_allOnes_iff {x : BitVec w} : x < allOnes w ↔ x ≠ allOnes w := by
|
||||
have := not_congr (@allOnes_le_iff w x)
|
||||
rw [BitVec.not_le] at this
|
||||
exact this
|
||||
|
||||
/-! ### udiv -/
|
||||
|
||||
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
|
||||
@@ -3991,6 +4174,22 @@ theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
|
||||
have h1 : 1 < 2 ^ (w + 1) := Nat.one_lt_two_pow (by omega)
|
||||
rw [Nat.mod_eq_of_lt h1, Nat.shiftLeft_eq, Nat.one_mul]
|
||||
|
||||
theorem toNat_twoPow_of_le {i w : Nat} (h : w ≤ i) : (twoPow w i).toNat = 0 := by
|
||||
rw [toNat_twoPow]
|
||||
apply Nat.mod_eq_zero_of_dvd
|
||||
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
|
||||
|
||||
theorem toNat_twoPow_of_lt {i w : Nat} (h : i < w) : (twoPow w i).toNat = 2^i := by
|
||||
rw [toNat_twoPow]
|
||||
apply Nat.mod_eq_of_lt
|
||||
apply Nat.pow_lt_pow_of_lt (by omega) (by omega)
|
||||
|
||||
theorem toNat_twoPow_eq_ite {i w : Nat} : (twoPow w i).toNat = if i < w then 2^i else 0 := by
|
||||
by_cases h : i < w
|
||||
· simp only [h, toNat_twoPow_of_lt, if_true]
|
||||
· simp only [h, if_false]
|
||||
rw [toNat_twoPow_of_le (by omega)]
|
||||
|
||||
@[simp]
|
||||
theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j)) := by
|
||||
rcases w with rfl | w
|
||||
@@ -4009,6 +4208,33 @@ theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j
|
||||
simp at hi
|
||||
simp_all
|
||||
|
||||
@[simp]
|
||||
theorem msb_twoPow {i w: Nat} :
|
||||
(twoPow w i).msb = (decide (i < w) && decide (i = w - 1)) := by
|
||||
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.sub_zero, getLsbD_twoPow,
|
||||
Bool.and_iff_right_iff_imp, Bool.and_eq_true, decide_eq_true_eq, and_imp]
|
||||
intros
|
||||
omega
|
||||
|
||||
theorem toInt_twoPow {w i : Nat} :
|
||||
(BitVec.twoPow w i).toInt = if w ≤ i then 0
|
||||
else if i + 1 = w then (-(2^i : Nat) : Int) else 2^i := by
|
||||
simp only [BitVec.toInt_eq_msb_cond, toNat_twoPow_eq_ite]
|
||||
rcases w with _ | w
|
||||
· simp
|
||||
· by_cases h : i = w
|
||||
· simp [h, show ¬ (w + 1 ≤ w) by omega]
|
||||
omega
|
||||
· by_cases h' : w + 1 ≤ i
|
||||
· simp [h', show ¬ i < w + 1 by omega]
|
||||
· simp [h, h', show i < w + 1 by omega, Int.natCast_pow]
|
||||
|
||||
theorem toFin_twoPow {w i : Nat} :
|
||||
(BitVec.twoPow w i).toFin = Fin.ofNat' (2^w) (2^i) := by
|
||||
rcases w with rfl | w
|
||||
· simp [BitVec.twoPow, BitVec.toFin, toFin_shiftLeft, Fin.fin_one_eq_zero]
|
||||
· simp [BitVec.twoPow, BitVec.toFin, toFin_shiftLeft, Nat.shiftLeft_eq]
|
||||
|
||||
@[simp]
|
||||
theorem getElem_twoPow {i j : Nat} (h : j < w) : (twoPow w i)[j] = decide (j = i) := by
|
||||
rw [←getLsbD_eq_getElem, getLsbD_twoPow]
|
||||
@@ -4022,14 +4248,6 @@ theorem getMsbD_twoPow {i j w: Nat} :
|
||||
by_cases h₀ : i < w <;> by_cases h₁ : j < w <;>
|
||||
simp [h₀, h₁] <;> omega
|
||||
|
||||
@[simp]
|
||||
theorem msb_twoPow {i w: Nat} :
|
||||
(twoPow w i).msb = (decide (i < w) && decide (i = w - 1)) := by
|
||||
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.sub_zero, getLsbD_twoPow,
|
||||
Bool.and_iff_right_iff_imp, Bool.and_eq_true, decide_eq_true_eq, and_imp]
|
||||
intros
|
||||
omega
|
||||
|
||||
theorem and_twoPow (x : BitVec w) (i : Nat) :
|
||||
x &&& (twoPow w i) = if x.getLsbD i then twoPow w i else 0#w := by
|
||||
ext j h
|
||||
@@ -4081,6 +4299,10 @@ theorem udiv_twoPow_eq_of_lt {w : Nat} {x : BitVec w} {k : Nat} (hk : k < w) : x
|
||||
have : 2^k < 2^w := Nat.pow_lt_pow_of_lt (by decide) hk
|
||||
simp [bitvec_to_nat, Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt this]
|
||||
|
||||
theorem shiftLeft_neg {x : BitVec w} {y : Nat} :
|
||||
(-x) <<< y = - (x <<< y) := by
|
||||
rw [shiftLeft_eq_mul_twoPow, shiftLeft_eq_mul_twoPow, BitVec.neg_mul]
|
||||
|
||||
/- ### cons -/
|
||||
|
||||
@[simp] theorem true_cons_zero : cons true 0#w = twoPow (w + 1) w := by
|
||||
@@ -4201,6 +4423,15 @@ theorem replicate_succ' {x : BitVec w} :
|
||||
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
|
||||
simp [replicate_append_self]
|
||||
|
||||
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [toNat_setWidth]
|
||||
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
|
||||
by_cases h : i ≤ w
|
||||
· rw [Nat.mod_eq_zero_of_dvd (Nat.pow_dvd_pow 2 h), Nat.mod_zero, Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 h)]
|
||||
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
|
||||
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
|
||||
|
||||
/-! ### intMin -/
|
||||
|
||||
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
|
||||
|
||||
@@ -18,10 +18,13 @@ attribute [extern "lean_byte_array_data"] ByteArray.data
|
||||
|
||||
namespace ByteArray
|
||||
@[extern "lean_mk_empty_byte_array"]
|
||||
def mkEmpty (c : @& Nat) : ByteArray :=
|
||||
def emptyWithCapacity (c : @& Nat) : ByteArray :=
|
||||
{ data := #[] }
|
||||
|
||||
def empty : ByteArray := mkEmpty 0
|
||||
@[deprecated emptyWithCapacity (since := "2025-03-12")]
|
||||
abbrev mkEmpty := emptyWithCapacity
|
||||
|
||||
def empty : ByteArray := emptyWithCapacity 0
|
||||
|
||||
instance : Inhabited ByteArray where
|
||||
default := empty
|
||||
@@ -334,6 +337,9 @@ def prevn : Iterator → Nat → Iterator
|
||||
end Iterator
|
||||
end ByteArray
|
||||
|
||||
/--
|
||||
Converts a list of bytes into a `ByteArray`.
|
||||
-/
|
||||
def List.toByteArray (bs : List UInt8) : ByteArray :=
|
||||
let rec loop
|
||||
| [], r => r
|
||||
|
||||
@@ -14,22 +14,23 @@ instance coeToNat : CoeOut (Fin n) Nat :=
|
||||
⟨fun v => v.val⟩
|
||||
|
||||
/--
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is similar to `Empty.elim`.
|
||||
The type `Fin 0` is uninhabited, so it can be used to derive any result whatsoever.
|
||||
|
||||
This is similar to `Empty.elim`. It can be thought of as a compiler-checked assertion that a code
|
||||
path is unreachable, or a logical contradiction from which `False` and thus anything else could be
|
||||
derived.
|
||||
-/
|
||||
def elim0.{u} {α : Sort u} : Fin 0 → α
|
||||
| ⟨_, h⟩ => absurd h (not_lt_zero _)
|
||||
|
||||
/--
|
||||
Returns the successor of the argument.
|
||||
The successor, with an increased bound.
|
||||
|
||||
The bound in the result type is increased:
|
||||
```
|
||||
(2 : Fin 3).succ = (3 : Fin 4)
|
||||
```
|
||||
This differs from addition, which wraps around:
|
||||
```
|
||||
(2 : Fin 3) + 1 = (0 : Fin 3)
|
||||
```
|
||||
This differs from adding `1`, which instead wraps around.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 3).succ = (3 : Fin 4)`
|
||||
* `(2 : Fin 3) + 1 = (0 : Fin 3)`
|
||||
-/
|
||||
def succ : Fin n → Fin (n + 1)
|
||||
| ⟨i, h⟩ => ⟨i+1, Nat.succ_lt_succ h⟩
|
||||
@@ -53,7 +54,13 @@ protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
|
||||
|
||||
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
|
||||
-- `i.toNat` to `i.val`.
|
||||
@[inline, inherit_doc val]
|
||||
/--
|
||||
Extracts the underlying `Nat` value.
|
||||
|
||||
This function is a synonym for `Fin.val`, which is the simp normal form. `Fin.val` is also a
|
||||
coercion, so values of type `Fin n` are automatically converted to `Nat`s as needed.
|
||||
-/
|
||||
@[inline]
|
||||
protected def toNat (i : Fin n) : Nat :=
|
||||
i.val
|
||||
|
||||
@@ -65,15 +72,34 @@ private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
|
||||
have : n > 0 := Nat.lt_trans (Nat.zero_lt_succ _) h;
|
||||
Nat.mod_lt _ this
|
||||
|
||||
/-- Addition modulo `n` -/
|
||||
/--
|
||||
Addition modulo `n`, usually invoked via the `+` operator.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 8) + (2 : Fin 8) = (4 : Fin 8)`
|
||||
* `(2 : Fin 3) + (2 : Fin 3) = (1 : Fin 3)`
|
||||
-/
|
||||
protected def add : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + b) % n, mlt h⟩
|
||||
|
||||
/-- Multiplication modulo `n` -/
|
||||
/--
|
||||
Multiplication modulo `n`, usually invoked via the `*` operator.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 10) * (2 : Fin 10) = (4 : Fin 10)`
|
||||
* `(2 : Fin 10) * (7 : Fin 10) = (4 : Fin 10)`
|
||||
* `(3 : Fin 10) * (7 : Fin 10) = (1 : Fin 10)`
|
||||
-/
|
||||
protected def mul : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a * b) % n, mlt h⟩
|
||||
|
||||
/-- Subtraction modulo `n` -/
|
||||
/--
|
||||
Subtraction modulo `n`, usually invoked via the `-` operator.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 11) - (3 : Fin 11) = (2 : Fin 11)`
|
||||
* `(3 : Fin 11) - (5 : Fin 11) = (9 : Fin 11)`
|
||||
-/
|
||||
protected def sub : Fin n → Fin n → Fin n
|
||||
/-
|
||||
The definition of `Fin.sub` has been updated to improve performance.
|
||||
@@ -100,27 +126,76 @@ we are trying to minimize the number of Nat theorems
|
||||
needed to bootstrap Lean.
|
||||
-/
|
||||
|
||||
/--
|
||||
Modulus of bounded numbers, usually invoked via the `%` operator.
|
||||
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
protected def mod : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a % b, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Division of bounded numbers, usually invoked via the `/` operator.
|
||||
|
||||
The resulting value is that computed by the `/` operator on `Nat`. In particular, the result of
|
||||
division by `0` is `0`.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 10) / (2 : Fin 10) = (2 : Fin 10)`
|
||||
* `(5 : Fin 10) / (0 : Fin 10) = (0 : Fin 10)`
|
||||
* `(5 : Fin 10) / (7 : Fin 10) = (0 : Fin 10)`
|
||||
-/
|
||||
protected def div : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a / b, Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h⟩
|
||||
|
||||
/--
|
||||
Modulus of bounded numbers with respect to a `Nat`.
|
||||
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
def modn : Fin n → Nat → Fin n
|
||||
| ⟨a, h⟩, m => ⟨a % m, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Bitwise and.
|
||||
-/
|
||||
def land : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.land a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise or.
|
||||
-/
|
||||
def lor : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.lor a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise xor (“exclusive or”).
|
||||
-/
|
||||
def xor : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.xor a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise left shift of bounded numbers, with wraparound on overflow.
|
||||
|
||||
Examples:
|
||||
* `(1 : Fin 10) <<< (1 : Fin 10) = (2 : Fin 10)`
|
||||
* `(1 : Fin 10) <<< (3 : Fin 10) = (8 : Fin 10)`
|
||||
* `(1 : Fin 10) <<< (4 : Fin 10) = (6 : Fin 10)`
|
||||
-/
|
||||
def shiftLeft : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a <<< b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise right shift of bounded numbers.
|
||||
|
||||
This operator corresponds to logical rather than arithmetic bit shifting. The new bits are always
|
||||
`0`.
|
||||
|
||||
Examples:
|
||||
* `(15 : Fin 16) >>> (1 : Fin 16) = (7 : Fin 16)`
|
||||
* `(15 : Fin 16) >>> (2 : Fin 16) = (3 : Fin 16)`
|
||||
* `(15 : Fin 17) >>> (2 : Fin 17) = (3 : Fin 17)`
|
||||
-/
|
||||
def shiftRight : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a >>> b) % n, mlt h⟩
|
||||
|
||||
@@ -174,39 +249,125 @@ theorem val_lt_of_le (i : Fin b) (h : b ≤ n) : i.val < n :=
|
||||
protected theorem pos (i : Fin n) : 0 < n :=
|
||||
Nat.lt_of_le_of_lt (Nat.zero_le _) i.2
|
||||
|
||||
/-- The greatest value of `Fin (n+1)`. -/
|
||||
/--
|
||||
The greatest value of `Fin (n+1)`, namely `n`.
|
||||
|
||||
Examples:
|
||||
* `Fin.last 4 = (4 : Fin 5)`
|
||||
* `(Fin.last 0).val = (0 : Nat)`
|
||||
-/
|
||||
@[inline] def last (n : Nat) : Fin (n + 1) := ⟨n, n.lt_succ_self⟩
|
||||
|
||||
/-- `castLT i h` embeds `i` into a `Fin` where `h` proves it belongs into. -/
|
||||
/--
|
||||
Replaces the bound with another that is suitable for the value.
|
||||
|
||||
The proof embedded in `i` can be used to cast to a larger bound even if the concrete value is not
|
||||
known.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
example : Fin 12 := (7 : Fin 10).castLT (by decide : 7 < 12)
|
||||
```
|
||||
```lean example
|
||||
example (i : Fin 10) : Fin 12 :=
|
||||
i.castLT <| by
|
||||
cases i; simp; omega
|
||||
```
|
||||
-/
|
||||
@[inline] def castLT (i : Fin m) (h : i.1 < n) : Fin n := ⟨i.1, h⟩
|
||||
|
||||
/-- `castLE h i` embeds `i` into a larger `Fin` type. -/
|
||||
/--
|
||||
Coarsens a bound to one at least as large.
|
||||
|
||||
See also `Fin.castAdd` for a version that represents the larger bound with addition rather than an
|
||||
explicit inequality proof.
|
||||
-/
|
||||
@[inline] def castLE (h : n ≤ m) (i : Fin n) : Fin m := ⟨i, Nat.lt_of_lt_of_le i.2 h⟩
|
||||
|
||||
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
|
||||
/--
|
||||
Uses a proof that two bounds are equal to allow a value bounded by one to be used with the other.
|
||||
|
||||
In other words, when `eq : n = m`, `Fin.cast eq i` converts `i : Fin n` into a `Fin m`.
|
||||
-/
|
||||
@[inline] protected def cast (eq : n = m) (i : Fin n) : Fin m := ⟨i, eq ▸ i.2⟩
|
||||
|
||||
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
|
||||
/--
|
||||
Coarsens a bound to one at least as large.
|
||||
|
||||
See also `Fin.natAdd` and `Fin.addNat` for addition functions that increase the bound, and
|
||||
`Fin.castLE` for a version that uses an explicit inequality proof.
|
||||
-/
|
||||
@[inline] def castAdd (m) : Fin n → Fin (n + m) :=
|
||||
castLE <| Nat.le_add_right n m
|
||||
|
||||
/-- `castSucc i` embeds `i : Fin n` in `Fin (n+1)`. -/
|
||||
/--
|
||||
Coarsens a bound by one.
|
||||
-/
|
||||
@[inline] def castSucc : Fin n → Fin (n + 1) := castAdd 1
|
||||
|
||||
/-- `addNat m i` adds `m` to `i`, generalizes `Fin.succ`. -/
|
||||
/--
|
||||
Adds a natural number to a `Fin`, increasing the bound.
|
||||
|
||||
This is a generalization of `Fin.succ`.
|
||||
|
||||
`Fin.natAdd` is a version of this function that takes its `Nat` parameter first.
|
||||
|
||||
Examples:
|
||||
* `Fin.addNat (5 : Fin 8) 3 = (8 : Fin 11)`
|
||||
* `Fin.addNat (0 : Fin 8) 1 = (1 : Fin 9)`
|
||||
* `Fin.addNat (1 : Fin 8) 2 = (3 : Fin 10)`
|
||||
|
||||
-/
|
||||
def addNat (i : Fin n) (m) : Fin (n + m) := ⟨i + m, Nat.add_lt_add_right i.2 _⟩
|
||||
|
||||
/-- `natAdd n i` adds `n` to `i` "on the left". -/
|
||||
/--
|
||||
Adds a natural number to a `Fin`, increasing the bound.
|
||||
|
||||
This is a generalization of `Fin.succ`.
|
||||
|
||||
`Fin.addNat` is a version of this function that takes its `Nat` parameter second.
|
||||
|
||||
Examples:
|
||||
* `Fin.natAdd 3 (5 : Fin 8) = (8 : Fin 11)`
|
||||
* `Fin.natAdd 1 (0 : Fin 8) = (1 : Fin 9)`
|
||||
* `Fin.natAdd 1 (2 : Fin 8) = (3 : Fin 9)`
|
||||
-/
|
||||
def natAdd (n) (i : Fin m) : Fin (n + m) := ⟨n + i, Nat.add_lt_add_left i.2 _⟩
|
||||
|
||||
/-- Maps `0` to `n-1`, `1` to `n-2`, ..., `n-1` to `0`. -/
|
||||
/--
|
||||
Replaces a value with its difference from the largest value in the type.
|
||||
|
||||
Considering the values of `Fin n` as a sequence `0`, `1`, …, `n-2`, `n-1`, `Fin.rev` finds the
|
||||
corresponding element of the reversed sequence. In other words, it maps `0` to `n-1`, `1` to `n-2`,
|
||||
..., and `n-1` to `0`.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 6).rev = (0 : Fin 6)`
|
||||
* `(0 : Fin 6).rev = (5 : Fin 6)`
|
||||
* `(2 : Fin 5).rev = (2 : Fin 5)`
|
||||
-/
|
||||
@[inline] def rev (i : Fin n) : Fin n := ⟨n - (i + 1), Nat.sub_lt i.pos (Nat.succ_pos _)⟩
|
||||
|
||||
/-- `subNat i h` subtracts `m` from `i`, generalizes `Fin.pred`. -/
|
||||
/--
|
||||
Subtraction of a natural number from a `Fin`, with the bound narrowed.
|
||||
|
||||
This is a generalization of `Fin.pred`. It is guaranteed to not underflow or wrap around.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 9).subNat 2 (by decide) = (3 : Fin 7)`
|
||||
* `(5 : Fin 9).subNat 0 (by decide) = (5 : Fin 9)`
|
||||
* `(3 : Fin 9).subNat 3 (by decide) = (0 : Fin 6)`
|
||||
-/
|
||||
@[inline] def subNat (m) (i : Fin (n + m)) (h : m ≤ i) : Fin n :=
|
||||
⟨i - m, Nat.sub_lt_right_of_lt_add h i.2⟩
|
||||
|
||||
/-- Predecessor of a nonzero element of `Fin (n+1)`. -/
|
||||
/--
|
||||
The predecessor of a non-zero element of `Fin (n+1)`, with the bound decreased.
|
||||
|
||||
Examples:
|
||||
* `(4 : Fin 8).pred (by decide) = (3 : Fin 7)`
|
||||
* `(1 : Fin 2).pred (by decide) = (0 : Fin 1)`
|
||||
-/
|
||||
@[inline] def pred {n : Nat} (i : Fin (n + 1)) (h : i ≠ 0) : Fin n :=
|
||||
subNat 1 i <| Nat.pos_of_ne_zero <| mt (Fin.eq_of_val_eq (j := 0)) h
|
||||
|
||||
|
||||
@@ -12,4 +12,31 @@ 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)
|
||||
|
||||
@[simp] theorem or_val_of_two_pow {w} (a b : Fin (2 ^ w)) : (a ||| b).val = a.val ||| b.val :=
|
||||
Nat.mod_eq_of_lt (Nat.or_lt_two_pow a.isLt b.isLt)
|
||||
|
||||
@[simp] theorem or_val_of_uInt8Size (a b : Fin UInt8.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 8) a b
|
||||
@[simp] theorem or_val_of_uInt16Size (a b : Fin UInt16.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 16) a b
|
||||
@[simp] theorem or_val_of_uInt32Size (a b : Fin UInt32.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 32) a b
|
||||
@[simp] theorem or_val_of_uInt64Size (a b : Fin UInt64.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 64) a b
|
||||
@[simp] theorem or_val_of_uSizeSize (a b : Fin USize.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow a b
|
||||
|
||||
theorem or_val (a b : Fin n) : (a ||| b).val = (a.val ||| b.val) % n := rfl
|
||||
|
||||
@[simp] theorem xor_val_of_two_pow {w} (a b : Fin (2 ^ w)) : (a ^^^ b).val = a.val ^^^ b.val :=
|
||||
Nat.mod_eq_of_lt (Nat.xor_lt_two_pow a.isLt b.isLt)
|
||||
|
||||
@[simp] theorem xor_val_of_uInt8Size (a b : Fin UInt8.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 8) a b
|
||||
@[simp] theorem xor_val_of_uInt16Size (a b : Fin UInt16.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 16) a b
|
||||
@[simp] theorem xor_val_of_uInt32Size (a b : Fin UInt32.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 32) a b
|
||||
@[simp] theorem xor_val_of_uInt64Size (a b : Fin UInt64.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 64) a b
|
||||
@[simp] theorem xor_val_of_uSizeSize (a b : Fin USize.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow a b
|
||||
|
||||
theorem xor_val (a b : Fin n) : (a ^^^ b).val = (a.val ^^^ b.val) % n := rfl
|
||||
|
||||
@[simp] theorem shiftLeft_val (a b : Fin n) : (a <<< b).val = (a.val <<< b.val) % n := rfl
|
||||
|
||||
@[simp] theorem shiftRight_val (a b : Fin n) : (a >>> b).val = a.val >>> b.val :=
|
||||
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt (Nat.shiftRight_le _ _) a.isLt)
|
||||
|
||||
end Fin
|
||||
|
||||
@@ -10,14 +10,26 @@ import Init.Data.Fin.Lemmas
|
||||
|
||||
namespace Fin
|
||||
|
||||
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
|
||||
/--
|
||||
Combine all the values that can be represented by `Fin n` with an initial value, starting at `0` and
|
||||
nesting to the left.
|
||||
|
||||
Example:
|
||||
* `Fin.foldl 3 (· + ·.val) (0 : Nat) = ((0 + (0 : Fin 3).val) + (1 : Fin 3).val) + (2 : Fin 3).val`
|
||||
-/
|
||||
@[inline] def foldl (n) (f : α → Fin n → α) (init : α) : α := loop init 0 where
|
||||
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
|
||||
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
|
||||
if h : i < n then loop (f x ⟨i, h⟩) (i+1) else x
|
||||
termination_by n - i
|
||||
|
||||
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
|
||||
/--
|
||||
Combine all the values that can be represented by `Fin n` with an initial value, starting at `n - 1`
|
||||
and nesting to the right.
|
||||
|
||||
Example:
|
||||
* `Fin.foldr 3 (·.val + ·) (0 : Nat) = (0 : Fin 3).val + ((1 : Fin 3).val + ((2 : Fin 3).val + 0))`
|
||||
-/
|
||||
@[inline] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop n (Nat.le_refl n) init where
|
||||
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
|
||||
@[specialize] loop : (i : _) → i ≤ n → α → α
|
||||
@@ -26,7 +38,9 @@ namespace Fin
|
||||
termination_by structural i => i
|
||||
|
||||
/--
|
||||
Folds a monadic function over `Fin n` from left to right:
|
||||
Folds a monadic function over all the values in `Fin n` from left to right, starting with `0`.
|
||||
|
||||
It is the sequence of steps:
|
||||
```
|
||||
Fin.foldlM n f x₀ = do
|
||||
let x₁ ← f x₀ 0
|
||||
@@ -53,7 +67,9 @@ Fin.foldlM n f x₀ = do
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
Folds a monadic function over `Fin n` from right to left:
|
||||
Folds a monadic function over `Fin n` from right to left, starting with `n-1`.
|
||||
|
||||
It is the sequence of steps:
|
||||
```
|
||||
Fin.foldrM n f xₙ = do
|
||||
let xₙ₋₁ ← f (n-1) xₙ
|
||||
@@ -125,7 +141,9 @@ theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) → α → m α) (x
|
||||
| zero =>
|
||||
rw [foldrM_loop_zero, foldrM_loop_succ, pure_bind]
|
||||
conv => rhs; rw [←bind_pure (f 0 x)]
|
||||
congr; funext
|
||||
congr
|
||||
funext
|
||||
try simp only [foldrM.loop] -- the try makes this proof work with and without opaque wf rec
|
||||
| succ i ih =>
|
||||
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
|
||||
congr; funext; exact ih ..
|
||||
|
||||
@@ -10,10 +10,18 @@ import Init.Data.Fin.Basic
|
||||
namespace Fin
|
||||
|
||||
/--
|
||||
`hIterateFrom f i bnd a` applies `f` over indices `[i:n]` to compute `P n`
|
||||
from `P i`.
|
||||
Applies an index-dependent function `f` to all of the values in `[i:n]`, starting at `i` with an
|
||||
initial accumulator `a`.
|
||||
|
||||
See `hIterate` below for more details.
|
||||
Concretely, `Fin.hIterateFrom P f i a` is equal to
|
||||
```lean
|
||||
a |> f i |> f (i + 1) |> ... |> f (n - 1)
|
||||
```
|
||||
|
||||
Theorems about `Fin.hIterateFrom` can be proven using the general theorem `Fin.hIterateFrom_elim` or
|
||||
other more specialized theorems.
|
||||
|
||||
`Fin.hIterate` is a variant that always starts at `0`.
|
||||
-/
|
||||
def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.val+1))
|
||||
(i : Nat) (ubnd : i ≤ n) (a : P i) : P n :=
|
||||
@@ -26,20 +34,18 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
`hIterate` is a heterogeneous iterative operation that applies a
|
||||
index-dependent function `f` to a value `init : P start` a total of
|
||||
`stop - start` times to produce a value of type `P stop`.
|
||||
Applies an index-dependent function to all the values less than the given bound `n`, starting at
|
||||
`0` with an accumulator.
|
||||
|
||||
Concretely, `hIterate start stop f init` is equal to
|
||||
Concretely, `Fin.hIterate P init f` is equal to
|
||||
```lean
|
||||
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
|
||||
init |> f 0 |> f 1 |> ... |> f (n-1)
|
||||
```
|
||||
|
||||
Because it is heterogeneous and must return a value of type `P stop`,
|
||||
`hIterate` requires proof that `start ≤ stop`.
|
||||
Theorems about `Fin.hIterate` can be proven using the general theorem `Fin.hIterate_elim` or other more
|
||||
specialized theorems.
|
||||
|
||||
One can prove properties of `hIterate` using the general theorem
|
||||
`hIterate_elim` or other more specialized theorems.
|
||||
`Fin.hIterateFrom` is a variant that takes a custom starting value instead of `0`.
|
||||
-/
|
||||
def hIterate (P : Nat → Sort _) {n : Nat} (init : P 0) (f : ∀(i : Fin n), P i.val → P (i.val+1)) :
|
||||
P n :=
|
||||
|
||||
@@ -670,12 +670,20 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n ≤ i) :
|
||||
natAdd n (subNat n (i.cast (Nat.add_comm ..)) h) = i := by simp [← cast_addNat]
|
||||
|
||||
/-! ### recursion and induction principles -/
|
||||
/-! ### Recursion and induction principles -/
|
||||
|
||||
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
|
||||
This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0` of an
|
||||
`(n+1)`-tuple, and `succ n i` defines `(i+1)`-st element of `(n+1)`-tuple based on `n`, `i`, and
|
||||
`i`-th element of `n`-tuple. -/
|
||||
/--
|
||||
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
|
||||
applications of `Fin.succ`.
|
||||
|
||||
The cases in the induction are:
|
||||
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
|
||||
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
|
||||
bound `n`
|
||||
|
||||
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
|
||||
step. `Fin.succRecOn` is a version of this induction principle that takes the `Fin` argument first.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def succRec {motive : ∀ n, Fin n → Sort _}
|
||||
(zero : ∀ n, motive n.succ (0 : Fin (n + 1)))
|
||||
@@ -684,13 +692,18 @@ This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0
|
||||
| Nat.succ n, ⟨0, _⟩ => by rw [mk_zero]; exact zero n
|
||||
| Nat.succ _, ⟨Nat.succ i, h⟩ => succ _ _ (succRec zero succ ⟨i, Nat.lt_of_succ_lt_succ h⟩)
|
||||
|
||||
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
|
||||
This function has two arguments:
|
||||
`zero n` defines the `0`-th element `motive (n+1) 0` of an `(n+1)`-tuple, and
|
||||
`succ n i` defines the `(i+1)`-st element of an `(n+1)`-tuple based on `n`, `i`,
|
||||
and the `i`-th element of an `n`-tuple.
|
||||
/--
|
||||
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
|
||||
applications of `Fin.succ`.
|
||||
|
||||
A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
|
||||
The cases in the induction are:
|
||||
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
|
||||
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
|
||||
bound `n`
|
||||
|
||||
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
|
||||
step. `Fin.succRec` is a version of this induction principle that takes the `Fin` argument last.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def succRecOn {n : Nat} (i : Fin n) {motive : ∀ n, Fin n → Sort _}
|
||||
(zero : ∀ n, motive (n + 1) 0) (succ : ∀ n i, motive n i → motive (Nat.succ n) i.succ) :
|
||||
@@ -705,9 +718,17 @@ A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
|
||||
cases i; rfl
|
||||
|
||||
|
||||
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
|
||||
This function has two arguments: `zero` handles the base case on `motive 0`,
|
||||
and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
/--
|
||||
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
For the induction:
|
||||
* `zero` is the base case, demonstrating `motive 0`.
|
||||
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
|
||||
`Fin.castSucc`) and demonstrating it for `i.succ`.
|
||||
|
||||
`Fin.inductionOn` is a version of this induction principle that takes the `Fin` as its first
|
||||
parameter, `Fin.cases` is the corresponding case analysis operator, and `Fin.reverseInduction` is a
|
||||
version that starts at the greatest value instead of `0`.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def induction {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
@@ -728,18 +749,30 @@ where
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) (i : Fin n) :
|
||||
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
|
||||
|
||||
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
|
||||
This function has two arguments: `zero` handles the base case on `motive 0`,
|
||||
and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
/--
|
||||
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
A version of `Fin.induction` taking `i : Fin (n + 1)` as the first argument.
|
||||
For the induction:
|
||||
* `zero` is the base case, demonstrating `motive 0`.
|
||||
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
|
||||
`Fin.castSucc`) and demonstrating it for `i.succ`.
|
||||
|
||||
`Fin.induction` is a version of this induction principle that takes the `Fin` as its last
|
||||
parameter.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def inductionOn (i : Fin (n + 1)) {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) : motive i := induction zero succ i
|
||||
|
||||
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = 0` and
|
||||
`i = j.succ`, `j : Fin n`. -/
|
||||
/--
|
||||
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
The two cases are:
|
||||
* `zero`, used when the value is of the form `(0 : Fin (n + 1))`
|
||||
* `succ`, used when the value is of the form `(j : Fin n).succ`
|
||||
|
||||
The corresponding induction principle is `Fin.induction`.
|
||||
-/
|
||||
@[elab_as_elim] def cases {motive : Fin (n + 1) → Sort _}
|
||||
(zero : motive 0) (succ : ∀ i : Fin n, motive i.succ) :
|
||||
∀ i : Fin (n + 1), motive i := induction zero fun i _ => succ i
|
||||
@@ -777,9 +810,14 @@ theorem fin_two_eq_of_eq_zero_iff : ∀ {a b : Fin 2}, (a = 0 ↔ b = 0) → a =
|
||||
simp only [forall_fin_two]; decide
|
||||
|
||||
/--
|
||||
Define `motive i` by reverse induction on `i : Fin (n + 1)` via induction on the underlying `Nat`
|
||||
value. This function has two arguments: `last` handles the base case on `motive (Fin.last n)`,
|
||||
and `cast` defines the inductive step using `motive i.succ`, inducting downwards.
|
||||
Proves a statement by reverse induction on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
For the induction:
|
||||
* `last` is the base case, demonstrating `motive (Fin.last n)`.
|
||||
* `cast` is the inductive step, assuming the motive for `(j : Fin n).succ` and demonstrating it for
|
||||
the predecessor `j.castSucc`.
|
||||
|
||||
`Fin.induction` is the non-reverse induction principle.
|
||||
-/
|
||||
@[elab_as_elim] def reverseInduction {motive : Fin (n + 1) → Sort _} (last : motive (Fin.last n))
|
||||
(cast : ∀ i : Fin n, motive i.succ → motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
|
||||
@@ -802,8 +840,16 @@ decreasing_by decreasing_with
|
||||
succ i (reverseInduction zero succ i.succ) := by
|
||||
rw [reverseInduction, dif_neg (Fin.ne_of_lt (Fin.castSucc_lt_last i))]; rfl
|
||||
|
||||
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = Fin.last n` and
|
||||
`i = j.castSucc`, `j : Fin n`. -/
|
||||
/--
|
||||
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`, checking whether the
|
||||
value is the greatest representable or a predecessor of some other.
|
||||
|
||||
The two cases are:
|
||||
* `last`, used when the value is `Fin.last n`
|
||||
* `cast`, used when the value is of the form `(j : Fin n).succ`
|
||||
|
||||
The corresponding induction principle is `Fin.reverseInduction`.
|
||||
-/
|
||||
@[elab_as_elim] def lastCases {n : Nat} {motive : Fin (n + 1) → Sort _} (last : motive (Fin.last n))
|
||||
(cast : ∀ i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
|
||||
reverseInduction last (fun i _ => cast i) i
|
||||
@@ -816,8 +862,16 @@ decreasing_by decreasing_with
|
||||
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
|
||||
reverseInduction_castSucc ..
|
||||
|
||||
/-- Define `f : Π i : Fin (m + n), motive i` by separately handling the cases `i = castAdd n i`,
|
||||
`j : Fin m` and `i = natAdd m j`, `j : Fin n`. -/
|
||||
/--
|
||||
A case analysis operator for `i : Fin (m + n)` that separately handles the cases where `i < m` and
|
||||
where `m ≤ i < m + n`.
|
||||
|
||||
The first case, where `i < m`, is handled by `left`. In this case, `i` can be represented as
|
||||
`Fin.castAdd n (j : Fin m)`.
|
||||
|
||||
The second case, where `m ≤ i < m + n`, is handled by `right`. In this case, `i` can be represented
|
||||
as `Fin.natAdd m (j : Fin n)`.
|
||||
-/
|
||||
@[elab_as_elim] def addCases {m n : Nat} {motive : Fin (m + n) → Sort u}
|
||||
(left : ∀ i, motive (castAdd n i)) (right : ∀ i, motive (natAdd m i))
|
||||
(i : Fin (m + n)) : motive i :=
|
||||
|
||||
@@ -6,4 +6,20 @@ Authors: Henrik Böving
|
||||
prelude
|
||||
import Init.Data.Nat.Log2
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/--
|
||||
Logarithm base 2 for bounded numbers.
|
||||
|
||||
The resulting value is the same as that computed by `Nat.log2`. In particular, the result for `0` is
|
||||
`0`.
|
||||
|
||||
Examples:
|
||||
* `(8 : Fin 10).log2 = (3 : Fin 10)`
|
||||
* `(7 : Fin 10).log2 = (2 : Fin 10)`
|
||||
* `(4 : Fin 10).log2 = (2 : Fin 10)`
|
||||
* `(3 : Fin 10).log2 = (1 : Fin 10)`
|
||||
* `(1 : Fin 10).log2 = (0 : Fin 10)`
|
||||
* `(0 : Fin 10).log2 = (0 : Fin 10)`
|
||||
-/
|
||||
def Fin.log2 (n : Fin m) : Fin m := ⟨Nat.log2 n.val, Nat.lt_of_le_of_lt (Nat.log2_le_self n.val) n.isLt⟩
|
||||
|
||||
@@ -140,15 +140,27 @@ instance : ToString Float where
|
||||
@[extern "lean_uint16_to_float"] opaque UInt16.toFloat (n : UInt16) : Float
|
||||
/-- Obtains the `Float` whose value is the same as the given `UInt32`. -/
|
||||
@[extern "lean_uint32_to_float"] opaque UInt32.toFloat (n : UInt32) : Float
|
||||
/-- Obtains a `Float` whose value is near the given `UInt64`. It will be exactly the value of the
|
||||
given `UInt64` if such a `Float` exists. If no such `Float` exists, the returned value will either
|
||||
be the smallest `Float` this is larger than the given value, or the largest `Float` this is smaller
|
||||
than the given value. -/
|
||||
/--
|
||||
Obtains a `Float` whose value is near the given `UInt64`.
|
||||
|
||||
It will be exactly the value of the given `UInt64` if such a `Float` exists. If no such `Float`
|
||||
exists, the returned value will either be the smallest `Float` that is larger than the given value,
|
||||
or the largest `Float` that is smaller than the given value.
|
||||
|
||||
This function is opaque in the kernel, but is overridden at runtime with an efficient
|
||||
implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
|
||||
/-- Obtains a `Float` whose value is near the given `USize`. It will be exactly the value of the
|
||||
given `USize` if such a `Float` exists. If no such `Float` exists, the returned value will either
|
||||
be the smallest `Float` this is larger than the given value, or the largest `Float` this is smaller
|
||||
than the given value. -/
|
||||
/--
|
||||
Obtains a `Float` whose value is near the given `USize`.
|
||||
|
||||
It will be exactly the value of the given `USize` if such a `Float` exists. If no such `Float`
|
||||
exists, the returned value will either be the smallest `Float` that is larger than the given value,
|
||||
or the largest `Float` that is smaller than the given value.
|
||||
|
||||
This function is opaque in the kernel, but is overridden at runtime with an efficient
|
||||
implementation.
|
||||
-/
|
||||
@[extern "lean_usize_to_float"] opaque USize.toFloat (n : USize) : Float
|
||||
|
||||
instance : Inhabited Float where
|
||||
|
||||
@@ -131,20 +131,37 @@ instance : ToString Float32 where
|
||||
@[extern "lean_uint8_to_float32"] opaque UInt8.toFloat32 (n : UInt8) : Float32
|
||||
/-- Obtains the `Float32` whose value is the same as the given `UInt16`. -/
|
||||
@[extern "lean_uint16_to_float32"] opaque UInt16.toFloat32 (n : UInt16) : Float32
|
||||
/-- Obtains a `Float32` whose value is near the given `UInt32`. It will be exactly the value of the
|
||||
given `UInt32` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
|
||||
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
|
||||
than the given value. -/
|
||||
/--
|
||||
Obtains a `Float32` whose value is near the given `UInt32`.
|
||||
|
||||
It will be exactly the value of the given `UInt32` if such a `Float32` exists. If no such `Float32`
|
||||
exists, the returned value will either be the smallest `Float32` that is larger than the given
|
||||
value, or the largest `Float32` that is smaller than the given value.
|
||||
|
||||
This function is opaque in the kernel, but is overridden at runtime with an efficient
|
||||
implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_to_float32"] opaque UInt32.toFloat32 (n : UInt32) : Float32
|
||||
/-- Obtains a `Float32` whose value is near the given `UInt64`. It will be exactly the value of the
|
||||
given `UInt64` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
|
||||
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
|
||||
than the given value. -/
|
||||
/--
|
||||
Obtains a `Float32` whose value is near the given `UInt64`.
|
||||
|
||||
It will be exactly the value of the given `UInt64` if such a `Float32` exists. If no such `Float32`
|
||||
exists, the returned value will either be the smallest `Float32` that is larger than the given
|
||||
value, or the largest `Float32` that is smaller than the given value.
|
||||
|
||||
This function is opaque in the kernel, but is overridden at runtime with an efficient
|
||||
implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_to_float32"] opaque UInt64.toFloat32 (n : UInt64) : Float32
|
||||
/-- Obtains a `Float32` whose value is near the given `USize`. It will be exactly the value of the
|
||||
given `USize` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
|
||||
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
|
||||
than the given value. -/
|
||||
/-- Obtains a `Float32` whose value is near the given `USize`.
|
||||
|
||||
It will be exactly the value of the given `USize` if such a `Float32` exists. If no such `Float32`
|
||||
exists, the returned value will either be the smallest `Float32` that is larger than the given
|
||||
value, or the largest `Float32` that is smaller than the given value.
|
||||
|
||||
This function is opaque in the kernel, but is overridden at runtime with an efficient
|
||||
implementation.
|
||||
-/
|
||||
@[extern "lean_usize_to_float32"] opaque USize.toFloat32 (n : USize) : Float32
|
||||
|
||||
instance : Inhabited Float32 where
|
||||
|
||||
@@ -17,11 +17,14 @@ attribute [extern "lean_float_array_data"] FloatArray.data
|
||||
|
||||
namespace FloatArray
|
||||
@[extern "lean_mk_empty_float_array"]
|
||||
def mkEmpty (c : @& Nat) : FloatArray :=
|
||||
def emptyWithCapacity (c : @& Nat) : FloatArray :=
|
||||
{ data := #[] }
|
||||
|
||||
@[deprecated emptyWithCapacity (since := "2025-03-12")]
|
||||
abbrev mkEmpty := emptyWithCapacity
|
||||
|
||||
def empty : FloatArray :=
|
||||
mkEmpty 0
|
||||
emptyWithCapacity 0
|
||||
|
||||
instance : Inhabited FloatArray where
|
||||
default := empty
|
||||
@@ -164,6 +167,9 @@ def foldl {β : Type v} (f : β → Float → β) (init : β) (as : FloatArray)
|
||||
|
||||
end FloatArray
|
||||
|
||||
/--
|
||||
Converts a list of floats into a `FloatArray`.
|
||||
-/
|
||||
def List.toFloatArray (ds : List Float) : FloatArray :=
|
||||
let rec loop
|
||||
| [], r => r
|
||||
|
||||
@@ -23,6 +23,12 @@ instance [ToFormat α] : ToFormat (List α) where
|
||||
instance [ToFormat α] : ToFormat (Array α) where
|
||||
format a := "#" ++ format a.toList
|
||||
|
||||
/--
|
||||
Formats an optional value, with no expectation that the Lean parser should be able to parse the
|
||||
result.
|
||||
|
||||
This function is usually accessed through the `ToFormat (Option α)` instance.
|
||||
-/
|
||||
def Option.format {α : Type u} [ToFormat α] : Option α → Format
|
||||
| none => "none"
|
||||
| some a => "some " ++ Std.format a
|
||||
@@ -33,6 +39,10 @@ instance {α : Type u} [ToFormat α] : ToFormat (Option α) :=
|
||||
instance {α : Type u} {β : Type v} [ToFormat α] [ToFormat β] : ToFormat (Prod α β) where
|
||||
format := fun (a, b) => Format.paren <| format a ++ "," ++ Format.line ++ format b
|
||||
|
||||
/--
|
||||
Converts a string to a pretty-printer document, replacing newlines in the string with
|
||||
`Std.Format.line`.
|
||||
-/
|
||||
def String.toFormat (s : String) : Std.Format :=
|
||||
Std.Format.joinSep (s.splitOn "\n") Std.Format.line
|
||||
|
||||
|
||||
@@ -9,10 +9,23 @@ import Init.Core
|
||||
|
||||
namespace Function
|
||||
|
||||
/--
|
||||
Transforms a function from pairs into an equivalent two-parameter function.
|
||||
|
||||
Examples:
|
||||
* `Function.curry (fun (x, y) => x + y) 3 5 = 8`
|
||||
* `Function.curry Prod.swap 3 "five" = ("five", 3)`
|
||||
-/
|
||||
@[inline]
|
||||
def curry : (α × β → φ) → α → β → φ := fun f a b => f (a, b)
|
||||
|
||||
/-- Interpret a function with two arguments as a function on `α × β` -/
|
||||
/--
|
||||
Transforms a two-parameter function into an equivalent function from pairs.
|
||||
|
||||
Examples:
|
||||
* `Function.uncurry List.drop (1, ["a", "b", "c"]) = ["b", "c"]`
|
||||
* `[("orange", 2), ("android", 3) ].map (Function.uncurry String.take) = ["or", "and"]`
|
||||
-/
|
||||
@[inline]
|
||||
def uncurry : (α → β → φ) → α × β → φ := fun f a => f a.1 a.2
|
||||
|
||||
|
||||
@@ -75,13 +75,19 @@ instance (P : Prop) : Hashable P where
|
||||
@[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.
|
||||
/--
|
||||
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
|
||||
`hash a = hash b`.
|
||||
|
||||
This is automatic if the `BEq` instance is lawful.
|
||||
-/
|
||||
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
|
||||
/-- If `a == b`, then `hash a = hash b`. -/
|
||||
hash_eq (a b : α) : a == b → hash a = hash b
|
||||
|
||||
/--
|
||||
A lawful hash function respects its Boolean equality test.
|
||||
-/
|
||||
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b → hash a = hash b :=
|
||||
LawfulHashable.hash_eq a b
|
||||
|
||||
|
||||
@@ -26,24 +26,27 @@ Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
|
||||
-/
|
||||
|
||||
/--
|
||||
The type of integers. It is defined as an inductive type based on the
|
||||
natural number type `Nat` featuring two constructors: "a natural
|
||||
number is an integer", and "the negation of a successor of a natural
|
||||
number is an integer". The former represents integers between `0`
|
||||
(inclusive) and `∞`, and the latter integers between `-∞` and `-1`
|
||||
(inclusive).
|
||||
The integers.
|
||||
|
||||
This type is special-cased by the compiler. The runtime has a special
|
||||
representation for `Int` which stores "small" signed numbers directly,
|
||||
and larger numbers use an arbitrary precision "bignum" library
|
||||
(usually [GMP](https://gmplib.org/)). A "small number" is an integer
|
||||
that can be encoded with 63 bits (31 bits on 32-bits architectures).
|
||||
This type is special-cased by the compiler and overridden with an efficient implementation. The
|
||||
runtime has a special representation for `Int` that stores “small” signed numbers directly, while
|
||||
larger numbers use a fast arbitrary-precision arithmetic library (usually
|
||||
[GMP](https://gmplib.org/)). A “small number” is an integer that can be encoded with one fewer bits
|
||||
than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit
|
||||
architectures).
|
||||
-/
|
||||
inductive Int : Type where
|
||||
/-- A natural number is an integer (`0` to `∞`). -/
|
||||
/--
|
||||
A natural number is an integer.
|
||||
|
||||
This constructor covers the non-negative integers (from `0` to `∞`).
|
||||
-/
|
||||
| ofNat : Nat → Int
|
||||
/-- The negation of the successor of a natural number is an integer
|
||||
(`-1` to `-∞`). -/
|
||||
/--
|
||||
The negation of the successor of a natural number is an integer.
|
||||
|
||||
This constructor covers the negative integers (from `-1` to `-∞`).
|
||||
-/
|
||||
| negSucc : Nat → Int
|
||||
|
||||
attribute [extern "lean_nat_to_int"] Int.ofNat
|
||||
@@ -78,15 +81,29 @@ protected theorem zero_ne_one : (0 : Int) ≠ 1 := nofun
|
||||
|
||||
theorem ofNat_two : ((2 : Nat) : Int) = 2 := rfl
|
||||
|
||||
/-- Negation of a natural number. -/
|
||||
/--
|
||||
Negation of natural numbers.
|
||||
|
||||
Examples:
|
||||
* `Int.negOfNat 6 = -6`
|
||||
* `Int.negOfNat 0 = 0`
|
||||
-/
|
||||
def negOfNat : Nat → Int
|
||||
| 0 => 0
|
||||
| succ m => negSucc m
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/-- Negation of an integer.
|
||||
/--
|
||||
Negation of integers, usually accessed via the `-` prefix operator.
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `-(6 : Int) = -6`
|
||||
* `-(-6 : Int) = 6`
|
||||
* `(12 : Int).neg = -12`
|
||||
-/
|
||||
@[extern "lean_int_neg"]
|
||||
protected def neg (n : @& Int) : Int :=
|
||||
match n with
|
||||
@@ -105,21 +122,30 @@ protected def neg (n : @& Int) : Int :=
|
||||
instance instNegInt : Neg Int where
|
||||
neg := Int.neg
|
||||
|
||||
/-- Subtraction of two natural numbers. -/
|
||||
/--
|
||||
Non-truncating subtraction of two natural numbers.
|
||||
|
||||
Examples:
|
||||
* `Int.subNatNat 5 2 = 3`
|
||||
* `Int.subNatNat 2 5 = -3`
|
||||
* `Int.subNatNat 0 13 = -13`
|
||||
-/
|
||||
def subNatNat (m n : Nat) : Int :=
|
||||
match (n - m : Nat) with
|
||||
| 0 => ofNat (m - n) -- m ≥ n
|
||||
| (succ k) => negSucc k
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/-- Addition of two integers.
|
||||
/--
|
||||
Addition of integers, usually accessed via the `+` operator.
|
||||
|
||||
```
|
||||
#eval (7 : Int) + (6 : Int) -- 13
|
||||
#eval (6 : Int) + (-6 : Int) -- 0
|
||||
```
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
Examples:
|
||||
* `(7 : Int) + (6 : Int) = 13`
|
||||
* `(6 : Int) + (-6 : Int) = 0`
|
||||
-/
|
||||
@[extern "lean_int_add"]
|
||||
protected def add (m n : @& Int) : Int :=
|
||||
match m, n with
|
||||
@@ -132,15 +158,17 @@ instance : Add Int where
|
||||
add := Int.add
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/-- Multiplication of two integers.
|
||||
/--
|
||||
Multiplication of integers, usually accessed via the `*` operator.
|
||||
|
||||
```
|
||||
#eval (63 : Int) * (6 : Int) -- 378
|
||||
#eval (6 : Int) * (-6 : Int) -- -36
|
||||
#eval (7 : Int) * (0 : Int) -- 0
|
||||
```
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
Examples:
|
||||
* `(63 : Int) * (6 : Int) = 378`
|
||||
* `(6 : Int) * (-6 : Int) = -36`
|
||||
* `(7 : Int) * (0 : Int) = 0`
|
||||
-/
|
||||
@[extern "lean_int_mul"]
|
||||
protected def mul (m n : @& Int) : Int :=
|
||||
match m, n with
|
||||
@@ -152,48 +180,65 @@ protected def mul (m n : @& Int) : Int :=
|
||||
instance : Mul Int where
|
||||
mul := Int.mul
|
||||
|
||||
/-- Subtraction of two integers.
|
||||
|
||||
```
|
||||
#eval (63 : Int) - (6 : Int) -- 57
|
||||
#eval (7 : Int) - (0 : Int) -- 7
|
||||
#eval (0 : Int) - (7 : Int) -- -7
|
||||
```
|
||||
/--
|
||||
Subtraction of integers, usually accessed via the `-` operator.
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `(63 : Int) - (6 : Int) = 57`
|
||||
* `(7 : Int) - (0 : Int) = 7`
|
||||
* `(0 : Int) - (7 : Int) = -7`
|
||||
-/
|
||||
@[extern "lean_int_sub"]
|
||||
protected def sub (m n : @& Int) : Int := m + (- n)
|
||||
|
||||
instance : Sub Int where
|
||||
sub := Int.sub
|
||||
|
||||
/-- A proof that an `Int` is non-negative. -/
|
||||
/--
|
||||
An integer is non-negative if it is equal to a natural number.
|
||||
-/
|
||||
inductive NonNeg : Int → Prop where
|
||||
/-- Sole constructor, proving that `ofNat n` is positive. -/
|
||||
/--
|
||||
For all natural numbers `n`, `Int.ofNat n` is non-negative.
|
||||
-/
|
||||
| mk (n : Nat) : NonNeg (ofNat n)
|
||||
|
||||
/-- Definition of `a ≤ b`, encoded as `b - a ≥ 0`. -/
|
||||
/--
|
||||
Non-strict inequality of integers, usually accessed via the `≤` operator.
|
||||
|
||||
`a ≤ b` is defined as `b - a ≥ 0`, using `Int.NonNeg`.
|
||||
-/
|
||||
protected def le (a b : Int) : Prop := NonNeg (b - a)
|
||||
|
||||
instance instLEInt : LE Int where
|
||||
le := Int.le
|
||||
|
||||
/-- Definition of `a < b`, encoded as `a + 1 ≤ b`. -/
|
||||
/--
|
||||
Strict inequality of integers, usually accessed via the `<` operator.
|
||||
|
||||
`a < b` when `a + 1 ≤ b`.
|
||||
-/
|
||||
protected def lt (a b : Int) : Prop := (a + 1) ≤ b
|
||||
|
||||
instance instLTInt : LT Int where
|
||||
lt := Int.lt
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/-- Decides equality between two `Int`s.
|
||||
/--
|
||||
Decides whether two integers are equal. Usually accessed via the `DecidableEq Int` instance.
|
||||
|
||||
```
|
||||
#eval (7 : Int) = (3 : Int) + (4 : Int) -- true
|
||||
#eval (6 : Int) = (3 : Int) * (2 : Int) -- true
|
||||
#eval ¬ (6 : Int) = (3 : Int) -- true
|
||||
```
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is the
|
||||
logical model.
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
Examples:
|
||||
* `show (7 : Int) = (3 : Int) + (4 : Int) by decide`
|
||||
* `if (6 : Int) = (3 : Int) * (2 : Int) then "yes" else "no" = "yes"`
|
||||
* `(¬ (6 : Int) = (3 : Int)) = true`
|
||||
-/
|
||||
@[extern "lean_int_dec_eq"]
|
||||
protected def decEq (a b : @& Int) : Decidable (a = b) :=
|
||||
match a, b with
|
||||
@@ -206,6 +251,7 @@ protected def decEq (a b : @& Int) : Decidable (a = b) :=
|
||||
| isTrue h => isTrue <| h ▸ rfl
|
||||
| isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h)
|
||||
|
||||
@[inherit_doc Int.decEq]
|
||||
instance : DecidableEq Int := Int.decEq
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
@@ -251,15 +297,17 @@ instance decLt (a b : @& Int) : Decidable (a < b) :=
|
||||
decNonneg _
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/-- Absolute value (`Nat`) of an integer.
|
||||
/--
|
||||
The absolute value of an integer is its distance from `0`.
|
||||
|
||||
```
|
||||
#eval (7 : Int).natAbs -- 7
|
||||
#eval (0 : Int).natAbs -- 0
|
||||
#eval (-11 : Int).natAbs -- 11
|
||||
```
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
Examples:
|
||||
* `(7 : Int).natAbs = 7`
|
||||
* `(0 : Int).natAbs = 0`
|
||||
* `((-11 : Int).natAbs = 11`
|
||||
-/
|
||||
@[extern "lean_nat_abs"]
|
||||
def natAbs (m : @& Int) : Nat :=
|
||||
match m with
|
||||
@@ -269,8 +317,17 @@ def natAbs (m : @& Int) : Nat :=
|
||||
/-! ## sign -/
|
||||
|
||||
/--
|
||||
Returns the "sign" of the integer as another integer: `1` for positive numbers,
|
||||
`-1` for negative numbers, and `0` for `0`.
|
||||
Returns the “sign” of the integer as another integer:
|
||||
* `1` for positive numbers,
|
||||
* `-1` for negative numbers, and
|
||||
* `0` for `0`.
|
||||
|
||||
Examples:
|
||||
* `Int.sign 34 = 1`
|
||||
* `Int.sign 2 = 1`
|
||||
* `Int.sign 0 = 0`
|
||||
* `Int.sign -1 = -1`
|
||||
* `Int.sign -362 = -1`
|
||||
-/
|
||||
def sign : Int → Int
|
||||
| Int.ofNat (succ _) => 1
|
||||
@@ -279,27 +336,33 @@ def sign : Int → Int
|
||||
|
||||
/-! ## Conversion -/
|
||||
|
||||
/-- Turns an integer into a natural number, negative numbers become
|
||||
`0`.
|
||||
/--
|
||||
Converts an integer into a natural number. Negative numbers are converted to `0`.
|
||||
|
||||
```
|
||||
#eval (7 : Int).toNat -- 7
|
||||
#eval (0 : Int).toNat -- 0
|
||||
#eval (-7 : Int).toNat -- 0
|
||||
```
|
||||
Examples:
|
||||
* `(7 : Int).toNat = 7`
|
||||
* `(0 : Int).toNat = 0`
|
||||
* `(-7 : Int).toNat = 0`
|
||||
-/
|
||||
def toNat : Int → Nat
|
||||
| ofNat n => n
|
||||
| negSucc _ => 0
|
||||
|
||||
/--
|
||||
* If `n : Nat`, then `int.toNat' n = some n`
|
||||
* If `n : Int` is negative, then `int.toNat' n = none`.
|
||||
Converts an integer into a natural number. Returns `none` for negative numbers.
|
||||
|
||||
Examples:
|
||||
* `(7 : Int).toNat? = some 7`
|
||||
* `(0 : Int).toNat? = some 0`
|
||||
* `(-7 : Int).toNat? = none`
|
||||
-/
|
||||
def toNat' : Int → Option Nat
|
||||
def toNat? : Int → Option Nat
|
||||
| (n : Nat) => some n
|
||||
| -[_+1] => none
|
||||
|
||||
@[deprecated toNat? (since := "2025-03-11"), inherit_doc toNat?]
|
||||
abbrev toNat' := toNat?
|
||||
|
||||
/-! ## divisibility -/
|
||||
|
||||
/--
|
||||
@@ -311,14 +374,14 @@ instance : Dvd Int where
|
||||
|
||||
/-! ## Powers -/
|
||||
|
||||
/-- Power of an integer to some natural number.
|
||||
/--
|
||||
Power of an integer to a natural number, usually accessed via the `^` operator.
|
||||
|
||||
```
|
||||
#eval (2 : Int) ^ 4 -- 16
|
||||
#eval (10 : Int) ^ 0 -- 1
|
||||
#eval (0 : Int) ^ 10 -- 0
|
||||
#eval (-7 : Int) ^ 3 -- -343
|
||||
```
|
||||
Examples:
|
||||
* `(2 : Int) ^ 4 = 16`
|
||||
* `(10 : Int) ^ 0 = 1`
|
||||
* `(0 : Int) ^ 10 = 0`
|
||||
* `(-7 : Int) ^ 3 = -343`
|
||||
-/
|
||||
protected def pow (m : Int) : Nat → Int
|
||||
| 0 => 1
|
||||
|
||||
@@ -1,50 +1,8 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## bit operations -/
|
||||
|
||||
/--
|
||||
Bitwise not
|
||||
|
||||
Interprets the integer as an infinite sequence of bits in two's complement
|
||||
and complements each bit.
|
||||
```
|
||||
~~~(0:Int) = -1
|
||||
~~~(1:Int) = -2
|
||||
~~~(-1:Int) = 0
|
||||
```
|
||||
-/
|
||||
protected def not : Int -> Int
|
||||
| Int.ofNat n => Int.negSucc n
|
||||
| Int.negSucc n => Int.ofNat n
|
||||
|
||||
instance : Complement Int := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Bitwise shift right.
|
||||
|
||||
Conceptually, this treats the integer as an infinite sequence of bits in two's
|
||||
complement and shifts the value to the right.
|
||||
|
||||
```lean
|
||||
( 0b0111:Int) >>> 1 = 0b0011
|
||||
( 0b1000:Int) >>> 1 = 0b0100
|
||||
(-0b1000:Int) >>> 1 = -0b0100
|
||||
(-0b0111:Int) >>> 1 = -0b0100
|
||||
```
|
||||
-/
|
||||
protected def shiftRight : Int → Nat → Int
|
||||
| Int.ofNat n, s => Int.ofNat (n >>> s)
|
||||
| Int.negSucc n, s => Int.negSucc (n >>> s)
|
||||
|
||||
instance : HShiftRight Int Nat Int := ⟨.shiftRight⟩
|
||||
|
||||
end Int
|
||||
import Init.Data.Int.Bitwise.Basic
|
||||
import Init.Data.Int.Bitwise.Lemmas
|
||||
|
||||
48
src/Init/Data/Int/Bitwise/Basic.lean
Normal file
48
src/Init/Data/Int/Bitwise/Basic.lean
Normal file
@@ -0,0 +1,48 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## bit operations -/
|
||||
|
||||
/--
|
||||
Bitwise not, usually accessed via the `~~~` prefix operator.
|
||||
|
||||
Interprets the integer as an infinite sequence of bits in two's complement and complements each bit.
|
||||
|
||||
Examples:
|
||||
* `~~~(0 : Int) = -1`
|
||||
* `~~~(1 : Int) = -2`
|
||||
* `~~~(-1 : Int) = 0`
|
||||
-/
|
||||
protected def not : Int → Int
|
||||
| Int.ofNat n => Int.negSucc n
|
||||
| Int.negSucc n => Int.ofNat n
|
||||
|
||||
instance : Complement Int := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Bitwise right shift, usually accessed via the `>>>` operator.
|
||||
|
||||
Interprets the integer as an infinite sequence of bits in two's complement and shifts the value to
|
||||
the right.
|
||||
|
||||
Examples:
|
||||
* `( 0b0111 : Int) >>> 1 = 0b0011`
|
||||
* `( 0b1000 : Int) >>> 1 = 0b0100`
|
||||
* `(-0b1000 : Int) >>> 1 = -0b0100`
|
||||
* `(-0b0111 : Int) >>> 1 = -0b0100`
|
||||
-/
|
||||
protected def shiftRight : Int → Nat → Int
|
||||
| Int.ofNat n, s => Int.ofNat (n >>> s)
|
||||
| Int.negSucc n, s => Int.negSucc (n >>> s)
|
||||
|
||||
instance : HShiftRight Int Nat Int := ⟨.shiftRight⟩
|
||||
|
||||
end Int
|
||||
@@ -5,12 +5,13 @@ Authors: Siddharth Bhat, Jeremy Avigad
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Bitwise.Lemmas
|
||||
import Init.Data.Int.Bitwise
|
||||
import Init.Data.Int.Bitwise.Basic
|
||||
import Init.Data.Int.DivMod.Lemmas
|
||||
|
||||
namespace Int
|
||||
|
||||
theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl
|
||||
|
||||
@[simp]
|
||||
theorem natCast_shiftRight (n s : Nat) : (n : Int) >>> s = n >>> s := rfl
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ In early versions of Lean, the typeclasses provided by `/` and `%`
|
||||
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.
|
||||
|
||||
However we decided it was better to use `ediv` and `emod` for the default typeclass instances,
|
||||
as they are consistent with the conventions used in SMTLib, and Mathlib,
|
||||
as they are consistent with the conventions used in SMT-LIB, and Mathlib,
|
||||
and often mathematical reasoning is easier with these conventions.
|
||||
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
|
||||
|
||||
@@ -38,31 +38,29 @@ This pair satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`.
|
||||
-/
|
||||
|
||||
/--
|
||||
Integer division. This version of integer division uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x` for `y ≠ 0`.
|
||||
Integer division that uses the E-rounding convention. Usually accessed via the `/` operator.
|
||||
Division by zero is defined to be zero, rather than an error.
|
||||
|
||||
This means that `Int.ediv x y = floor (x / y)` when `y > 0` and `Int.ediv x y = ceil (x / y)` when `y < 0`.
|
||||
In the E-rounding convention (Euclidean division), `Int.emod x y` satisfies `0 ≤ Int.emod x y < Int.natAbs y`
|
||||
for `y ≠ 0` and `Int.ediv` is the unique function satisfying `Int.emod x y + (Int.edivx y) * y = x`
|
||||
for `y ≠ 0`.
|
||||
|
||||
This is the function powering the `/` notation on integers.
|
||||
This means that `Int.ediv x y` is `⌊x / y⌋` when `y > 0` and `⌈x / y⌉` when `y < 0`.
|
||||
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int) / (0 : Int) -- 0
|
||||
#eval (0 : Int) / (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) / (6 : Int) -- 2
|
||||
#eval (12 : Int) / (-6 : Int) -- -2
|
||||
#eval (-12 : Int) / (6 : Int) -- -2
|
||||
#eval (-12 : Int) / (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int) / (7 : Int) -- 1
|
||||
#eval (12 : Int) / (-7 : Int) -- -1
|
||||
#eval (-12 : Int) / (7 : Int) -- -2
|
||||
#eval (-12 : Int) / (-7 : Int) -- 2
|
||||
```
|
||||
|
||||
Implemented by efficient native code.
|
||||
* `(7 : Int) / (0 : Int) = 0`
|
||||
* `(0 : Int) / (7 : Int) = 0`
|
||||
* `(12 : Int) / (6 : Int) = 2`
|
||||
* `(12 : Int) / (-6 : Int) = -2`
|
||||
* `(-12 : Int) / (6 : Int) = -2`
|
||||
* `(-12 : Int) / (-6 : Int) = 2`
|
||||
* `(12 : Int) / (7 : Int) = 1`
|
||||
* `(12 : Int) / (-7 : Int) = -1`
|
||||
* `(-12 : Int) / (7 : Int) = -2`
|
||||
* `(-12 : Int) / (-7 : Int) = 2`
|
||||
-/
|
||||
@[extern "lean_int_ediv"]
|
||||
def ediv : (@& Int) → (@& Int) → Int
|
||||
@@ -73,29 +71,26 @@ def ediv : (@& Int) → (@& Int) → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
|
||||
|
||||
/--
|
||||
Integer modulus. This version of integer modulus uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
Integer modulus that uses the E-rounding convention. Usually accessed via the `%` operator.
|
||||
|
||||
This is the function powering the `%` notation on integers.
|
||||
In the E-rounding convention (Euclidean division), `Int.emod x y` satisfies `0 ≤ Int.emod x y < Int.natAbs y`
|
||||
for `y ≠ 0` and `Int.ediv` is the unique function satisfying `Int.emod x y + (Int.edivx y) * y = x`
|
||||
for `y ≠ 0`.
|
||||
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int) % (0 : Int) -- 7
|
||||
#eval (0 : Int) % (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (6 : Int) -- 0
|
||||
#eval (12 : Int) % (-6 : Int) -- 0
|
||||
#eval (-12 : Int) % (6 : Int) -- 0
|
||||
#eval (-12 : Int) % (-6 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (7 : Int) -- 5
|
||||
#eval (12 : Int) % (-7 : Int) -- 5
|
||||
#eval (-12 : Int) % (7 : Int) -- 2
|
||||
#eval (-12 : Int) % (-7 : Int) -- 2
|
||||
```
|
||||
|
||||
Implemented by efficient native code.
|
||||
* `(7 : Int) % (0 : Int) = 7`
|
||||
* `(0 : Int) % (7 : Int) = 0`
|
||||
* `(12 : Int) % (6 : Int) = 0`
|
||||
* `(12 : Int) % (-6 : Int) = 0`
|
||||
* `(-12 : Int) % (6 : Int) = 0`
|
||||
* `(-12 : Int) % (-6 : Int) = 0`
|
||||
* `(12 : Int) % (7 : Int) = 5`
|
||||
* `(12 : Int) % (-7 : Int) = 5`
|
||||
* `(-12 : Int) % (7 : Int) = 2`
|
||||
* `(-12 : Int) % (-7 : Int) = 2`
|
||||
-/
|
||||
@[extern "lean_int_emod"]
|
||||
def emod : (@& Int) → (@& Int) → Int
|
||||
@@ -103,11 +98,15 @@ def emod : (@& Int) → (@& Int) → Int
|
||||
| -[m+1], n => subNatNat (natAbs n) (succ (m % natAbs n))
|
||||
|
||||
/--
|
||||
The Div and Mod syntax uses ediv and emod for compatibility with SMTLIb and mathematical
|
||||
reasoning tends to be easier.
|
||||
The `Div Int` and `Mod Int` instances use `Int.ediv` and `Int.emod` for compatibility with SMT-LIB and
|
||||
because mathematical reasoning tends to be easier.
|
||||
-/
|
||||
instance : Div Int where
|
||||
div := Int.ediv
|
||||
/--
|
||||
The `Div Int` and `Mod Int` instances use `Int.ediv` and `Int.emod` for compatibility with SMT-LIB and
|
||||
because mathematical reasoning tends to be easier.
|
||||
-/
|
||||
instance : Mod Int where
|
||||
mod := Int.emod
|
||||
|
||||
@@ -124,35 +123,27 @@ theorem negSucc_emod_negSucc {a b : Nat} : -[a+1] % -[b+1] = subNatNat (b + 1) (
|
||||
/-! ### T-rounding division -/
|
||||
|
||||
/--
|
||||
`tdiv` uses the [*"T-rounding"*][t-rounding]
|
||||
(**T**runcation-rounding) convention, meaning that it rounds toward
|
||||
zero. Also note that division by zero is defined to equal zero.
|
||||
Integer division using the T-rounding convention.
|
||||
|
||||
The relation between integer division and modulo is found in
|
||||
`Int.tmod_add_tdiv` which states that
|
||||
`tmod a b + b * (tdiv a b) = a`, unconditionally.
|
||||
In [the T-rounding convention][t-rounding] (division with truncation), all rounding is towards zero.
|
||||
Division by 0 is defined to be 0. In this convention, `Int.tmod a b + b * (Int.tdiv a b) = a`.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
|
||||
Examples:
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is the
|
||||
logical model.
|
||||
|
||||
```
|
||||
#eval (7 : Int).tdiv (0 : Int) -- 0
|
||||
#eval (0 : Int).tdiv (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).tdiv (6 : Int) -- 2
|
||||
#eval (12 : Int).tdiv (-6 : Int) -- -2
|
||||
#eval (-12 : Int).tdiv (6 : Int) -- -2
|
||||
#eval (-12 : Int).tdiv (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int).tdiv (7 : Int) -- 1
|
||||
#eval (12 : Int).tdiv (-7 : Int) -- -1
|
||||
#eval (-12 : Int).tdiv (7 : Int) -- -1
|
||||
#eval (-12 : Int).tdiv (-7 : Int) -- 1
|
||||
```
|
||||
|
||||
Implemented by efficient native code.
|
||||
Examples:
|
||||
* `(7 : Int).tdiv (0 : Int) = 0`
|
||||
* `(0 : Int).tdiv (7 : Int) = 0`
|
||||
* `(12 : Int).tdiv (6 : Int) = 2`
|
||||
* `(12 : Int).tdiv (-6 : Int) = -2`
|
||||
* `(-12 : Int).tdiv (6 : Int) = -2`
|
||||
* `(-12 : Int).tdiv (-6 : Int) = 2`
|
||||
* `(12 : Int).tdiv (7 : Int) = 1`
|
||||
* `(12 : Int).tdiv (-7 : Int) = -1`
|
||||
* `(-12 : Int).tdiv (7 : Int) = -1`
|
||||
* `(-12 : Int).tdiv (-7 : Int) = 1`
|
||||
-/
|
||||
@[extern "lean_int_div"]
|
||||
def tdiv : (@& Int) → (@& Int) → Int
|
||||
@@ -161,36 +152,32 @@ def tdiv : (@& Int) → (@& Int) → Int
|
||||
| -[m +1], ofNat n => -ofNat (succ m / n)
|
||||
| -[m +1], -[n +1] => ofNat (succ m / succ n)
|
||||
|
||||
/-- Integer modulo. This function uses the
|
||||
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
|
||||
to pair with `Int.tdiv`, meaning that `tmod a b + b * (tdiv a b) = a`
|
||||
unconditionally (see [`Int.tmod_add_tdiv`][theo tmod_add_tdiv]). In
|
||||
particular, `a % 0 = a`.
|
||||
/-- Integer modulo using the T-rounding convention.
|
||||
|
||||
`tmod` satisfies `natAbs (tmod a b) = natAbs a % natAbs b`,
|
||||
and when `b` does not divide `a`, `tmod a b` has the same sign as `a`.
|
||||
In [the T-rounding convention][t-rounding] (division with truncation), all rounding is towards zero.
|
||||
Division by 0 is defined to be 0 and `Int.tmod a 0 = a`.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
|
||||
In this convention, `Int.tmod a b + b * (Int.tdiv a b) = a`. Additionally,
|
||||
`Int.natAbs (Int.tmod a b) = Int.natAbs a % Int.natAbs b`, and when `b` does not divide `a`,
|
||||
`Int.tmod a b` has the same sign as `a`.
|
||||
|
||||
Examples:
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
|
||||
```
|
||||
#eval (7 : Int).tmod (0 : Int) -- 7
|
||||
#eval (0 : Int).tmod (7 : Int) -- 0
|
||||
This function is overridden by the compiler with an efficient implementation. This definition is the
|
||||
logical model.
|
||||
|
||||
#eval (12 : Int).tmod (6 : Int) -- 0
|
||||
#eval (12 : Int).tmod (-6 : Int) -- 0
|
||||
#eval (-12 : Int).tmod (6 : Int) -- 0
|
||||
#eval (-12 : Int).tmod (-6 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).tmod (7 : Int) -- 5
|
||||
#eval (12 : Int).tmod (-7 : Int) -- 5
|
||||
#eval (-12 : Int).tmod (7 : Int) -- -5
|
||||
#eval (-12 : Int).tmod (-7 : Int) -- -5
|
||||
```
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
Examples:
|
||||
* `(7 : Int).tmod (0 : Int) = 7`
|
||||
* `(0 : Int).tmod (7 : Int) = 0`
|
||||
* `(12 : Int).tmod (6 : Int) = 0`
|
||||
* `(12 : Int).tmod (-6 : Int) = 0`
|
||||
* `(-12 : Int).tmod (6 : Int) = 0`
|
||||
* `(-12 : Int).tmod (-6 : Int) = 0`
|
||||
* `(12 : Int).tmod (7 : Int) = 5`
|
||||
* `(12 : Int).tmod (-7 : Int) = 5`
|
||||
* `(-12 : Int).tmod (7 : Int) = -5`
|
||||
* `(-12 : Int).tmod (-7 : Int) = -5`
|
||||
-/
|
||||
@[extern "lean_int_mod"]
|
||||
def tmod : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m % n)
|
||||
@@ -205,25 +192,22 @@ This pair satisfies `fdiv x y = floor (x / y)`.
|
||||
-/
|
||||
|
||||
/--
|
||||
Integer division. This version of division uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
Integer division using the F-rounding convention.
|
||||
|
||||
In the F-rounding convention (flooring division), `Int.fdiv x y` satisfies `Int.fdiv x y = ⌊x / y⌋`
|
||||
and `Int.fmod` is the unique function satisfying `Int.fmod x y + (Int.fdiv x y) * y = x`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int).fdiv (0 : Int) -- 0
|
||||
#eval (0 : Int).fdiv (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int).fdiv (6 : Int) -- 2
|
||||
#eval (12 : Int).fdiv (-6 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (6 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int).fdiv (7 : Int) -- 1
|
||||
#eval (12 : Int).fdiv (-7 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (7 : Int) -- -2
|
||||
#eval (-12 : Int).fdiv (-7 : Int) -- 1
|
||||
```
|
||||
* `(7 : Int).fdiv (0 : Int) = 0`
|
||||
* `(0 : Int).fdiv (7 : Int) = 0`
|
||||
* `(12 : Int).fdiv (6 : Int) = 2`
|
||||
* `(12 : Int).fdiv (-6 : Int) = -2`
|
||||
* `(-12 : Int).fdiv (6 : Int) = -2`
|
||||
* `(-12 : Int).fdiv (-6 : Int) = 2`
|
||||
* `(12 : Int).fdiv (7 : Int) = 1`
|
||||
* `(12 : Int).fdiv (-7 : Int) = -2`
|
||||
* `(-12 : Int).fdiv (7 : Int) = -2`
|
||||
* `(-12 : Int).fdiv (-7 : Int) = 1`
|
||||
-/
|
||||
def fdiv : Int → Int → Int
|
||||
| 0, _ => 0
|
||||
@@ -234,26 +218,26 @@ def fdiv : Int → Int → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ m / succ n)
|
||||
|
||||
/--
|
||||
Integer modulus. This version of integer modulus uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
Integer modulus using the F-rounding convention.
|
||||
|
||||
In the F-rounding convention (flooring division), `Int.fdiv x y` satisfies `Int.fdiv x y = ⌊x / y⌋`
|
||||
and `Int.fmod` is the unique function satisfying `Int.fmod x y + (Int.fdiv x y) * y = x`.
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int).fmod (0 : Int) -- 7
|
||||
#eval (0 : Int).fmod (7 : Int) -- 0
|
||||
* `(7 : Int).fmod (0 : Int) = 7`
|
||||
* `(0 : Int).fmod (7 : Int) = 0`
|
||||
|
||||
#eval (12 : Int).fmod (6 : Int) -- 0
|
||||
#eval (12 : Int).fmod (-6 : Int) -- 0
|
||||
#eval (-12 : Int).fmod (6 : Int) -- 0
|
||||
#eval (-12 : Int).fmod (-6 : Int) -- 0
|
||||
* `(12 : Int).fmod (6 : Int) = 0`
|
||||
* `(12 : Int).fmod (-6 : Int) = 0`
|
||||
* `(-12 : Int).fmod (6 : Int) = 0`
|
||||
* `(-12 : Int).fmod (-6 : Int) = 0`
|
||||
|
||||
* `(12 : Int).fmod (7 : Int) = 5`
|
||||
* `(12 : Int).fmod (-7 : Int) = -2`
|
||||
* `(-12 : Int).fmod (7 : Int) = 2`
|
||||
* `(-12 : Int).fmod (-7 : Int) = -5`
|
||||
|
||||
#eval (12 : Int).fmod (7 : Int) -- 5
|
||||
#eval (12 : Int).fmod (-7 : Int) -- -2
|
||||
#eval (-12 : Int).fmod (7 : Int) -- 2
|
||||
#eval (-12 : Int).fmod (-7 : Int) -- -5
|
||||
```
|
||||
-/
|
||||
def fmod : Int → Int → Int
|
||||
| 0, _ => 0
|
||||
@@ -280,28 +264,24 @@ This function is used in `omega` as well as signed bitvectors.
|
||||
-/
|
||||
|
||||
/--
|
||||
Balanced modulus. This version of integer modulus uses the
|
||||
balanced rounding convention, which guarantees that
|
||||
`-m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
|
||||
to `x` modulo `m`.
|
||||
Balanced modulus.
|
||||
|
||||
If `m = 0`, then `bmod x m = x`.
|
||||
This version of integer modulus uses the balanced rounding convention, which guarantees that
|
||||
`-m / 2 ≤ Int.bmod x m < m/2` for `m ≠ 0` and `Int.bmod x m` is congruent to `x` modulo `m`.
|
||||
|
||||
If `m = 0`, then `Int.bmod x m = x`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int).bdiv 0 -- 0
|
||||
#eval (0 : Int).bdiv 7 -- 0
|
||||
|
||||
#eval (12 : Int).bdiv 6 -- 2
|
||||
#eval (12 : Int).bdiv 7 -- 2
|
||||
#eval (12 : Int).bdiv 8 -- 2
|
||||
#eval (12 : Int).bdiv 9 -- 1
|
||||
|
||||
#eval (-12 : Int).bdiv 6 -- -2
|
||||
#eval (-12 : Int).bdiv 7 -- -2
|
||||
#eval (-12 : Int).bdiv 8 -- -1
|
||||
#eval (-12 : Int).bdiv 9 -- -1
|
||||
```
|
||||
* `(7 : Int).bmod 0 = 7`
|
||||
* `(0 : Int).bmod 7 = 0`
|
||||
* `(12 : Int).bmod 6 = 0`
|
||||
* `(12 : Int).bmod 7 = -2`
|
||||
* `(12 : Int).bmod 8 = -4`
|
||||
* `(12 : Int).bmod 9 = 3`
|
||||
* `(-12 : Int).bmod 6 = 0`
|
||||
* `(-12 : Int).bmod 7 = 2`
|
||||
* `(-12 : Int).bmod 8 = -4`
|
||||
* `(-12 : Int).bmod 9 = -3`
|
||||
-/
|
||||
def bmod (x : Int) (m : Nat) : Int :=
|
||||
let r := x % m
|
||||
@@ -311,24 +291,21 @@ def bmod (x : Int) (m : Nat) : Int :=
|
||||
r - m
|
||||
|
||||
/--
|
||||
Balanced division. This returns the unique integer so that
|
||||
`b * (Int.bdiv a b) + Int.bmod a b = a`.
|
||||
Balanced division.
|
||||
|
||||
This returns the unique integer so that `b * (Int.bdiv a b) + Int.bmod a b = a`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int).bmod 0 -- 7
|
||||
#eval (0 : Int).bmod 7 -- 0
|
||||
|
||||
#eval (12 : Int).bmod 6 -- 0
|
||||
#eval (12 : Int).bmod 7 -- -2
|
||||
#eval (12 : Int).bmod 8 -- -4
|
||||
#eval (12 : Int).bmod 9 -- 3
|
||||
|
||||
#eval (-12 : Int).bmod 6 -- 0
|
||||
#eval (-12 : Int).bmod 7 -- 2
|
||||
#eval (-12 : Int).bmod 8 -- -4
|
||||
#eval (-12 : Int).bmod 9 -- -3
|
||||
```
|
||||
* `(7 : Int).bdiv 0 = 0`
|
||||
* `(0 : Int).bdiv 7 = 0`
|
||||
* `(12 : Int).bdiv 6 = 2`
|
||||
* `(12 : Int).bdiv 7 = 2`
|
||||
* `(12 : Int).bdiv 8 = 2`
|
||||
* `(12 : Int).bdiv 9 = 1`
|
||||
* `(-12 : Int).bdiv 6 = -2`
|
||||
* `(-12 : Int).bdiv 7 = -2`
|
||||
* `(-12 : Int).bdiv 8 = -1`
|
||||
* `(-12 : Int).bdiv 9 = -1`
|
||||
-/
|
||||
def bdiv (x : Int) (m : Nat) : Int :=
|
||||
if m = 0 then
|
||||
|
||||
@@ -122,24 +122,22 @@ protected theorem mul_dvd_mul_iff_right {a b c : Int} (h : a ≠ 0) : (b * a)
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => show -ofNat _ = _ by simp
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem tdiv_zero : ∀ a : Int, tdiv a 0 = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => rfl
|
||||
| -[_+1] => by simp [tdiv]
|
||||
|
||||
@[simp] theorem zero_fdiv (b : Int) : fdiv 0 b = 0 := by cases b <;> rfl
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem fdiv_zero : ∀ a : Int, fdiv a 0 = 0
|
||||
| 0 => rfl
|
||||
| succ _ => rfl
|
||||
| succ _ => by simp [fdiv]
|
||||
| -[_+1] => rfl
|
||||
|
||||
/-! ### preliminaries for div equivalences -/
|
||||
|
||||
theorem negSucc_emod_ofNat_succ_eq_zero_iff {a b : Nat} :
|
||||
-[a+1] % (b + 1 : Int) = 0 ↔ (a + 1) % (b + 1) = 0 := by
|
||||
rw [← natCast_one, ← natCast_add]
|
||||
rw [← Int.natCast_one, ← Int.natCast_add]
|
||||
change Int.emod _ _ = 0 ↔ _
|
||||
rw [emod, natAbs_ofNat]
|
||||
simp only [Nat.succ_eq_add_one, subNat_eq_zero_iff, Nat.add_right_cancel_iff]
|
||||
@@ -170,7 +168,7 @@ theorem tdiv_eq_ediv {a b : Int} :
|
||||
| ofNat a, -[b+1] => simp [tdiv_eq_ediv_of_nonneg]
|
||||
| -[a+1], 0 => simp
|
||||
| -[a+1], ofNat (succ b) =>
|
||||
simp only [tdiv, Nat.succ_eq_add_one, ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int,
|
||||
simp only [tdiv, Nat.succ_eq_add_one, ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int,
|
||||
negSucc_not_nonneg, sign_of_add_one]
|
||||
simp only [negSucc_emod_ofNat_succ_eq_zero_iff]
|
||||
norm_cast
|
||||
@@ -184,12 +182,12 @@ theorem tdiv_eq_ediv {a b : Int} :
|
||||
simp only [tdiv, ofNat_eq_coe, negSucc_not_nonneg, false_or, sign_negSucc]
|
||||
norm_cast
|
||||
simp only [negSucc_ediv_negSucc]
|
||||
rw [natCast_add, natCast_one]
|
||||
rw [Int.natCast_add, Int.natCast_one]
|
||||
simp only [negSucc_emod_negSucc_eq_zero_iff]
|
||||
split <;> rename_i h
|
||||
· norm_cast
|
||||
exact Nat.succ_div_of_mod_eq_zero h
|
||||
· rw [← Int.sub_eq_add_neg, Int.add_sub_cancel]
|
||||
· rw [Int.add_neg_eq_sub, Int.add_sub_cancel]
|
||||
norm_cast
|
||||
exact Nat.succ_div_of_mod_ne_zero h
|
||||
|
||||
@@ -220,7 +218,7 @@ theorem fdiv_eq_ediv {a b : Int} :
|
||||
simp only [fdiv, ofNat_eq_coe, negSucc_ediv_negSucc, negSucc_not_nonneg, dvd_negSucc, negSucc_dvd,
|
||||
false_or]
|
||||
norm_cast
|
||||
rw [natCast_add, natCast_one, Nat.succ_div]
|
||||
rw [Int.natCast_add, Int.natCast_one, Nat.succ_div]
|
||||
split <;> simp
|
||||
|
||||
theorem ediv_eq_fdiv {a b : Int} :
|
||||
@@ -558,7 +556,7 @@ protected theorem eq_ediv_of_mul_eq_left {a b c : Int}
|
||||
|
||||
theorem sign_ediv (a b : Int) : sign (a / b) = if 0 ≤ a ∧ a < b.natAbs then 0 else sign a * sign b := by
|
||||
induction b using wlog_sign
|
||||
case inv => simp; split <;> simp
|
||||
case inv => simp; split <;> simp [Int.mul_neg]
|
||||
case w b =>
|
||||
match b with
|
||||
| 0 => simp
|
||||
@@ -569,8 +567,8 @@ theorem sign_ediv (a b : Int) : sign (a / b) = if 0 ≤ a ∧ a < b.natAbs then
|
||||
| 0 => simp
|
||||
| (a + 1 : Nat) =>
|
||||
norm_cast
|
||||
simp only [Nat.le_add_left, Nat.add_lt_add_iff_right, true_and, natCast_add,
|
||||
Nat.cast_ofNat_Int, sign_of_add_one, Int.mul_one]
|
||||
simp only [Nat.le_add_left, Nat.add_lt_add_iff_right, true_and, Int.natCast_add,
|
||||
cast_ofNat_Int, sign_of_add_one, Int.mul_one]
|
||||
split
|
||||
· rw [Nat.div_eq_of_lt (by omega)]
|
||||
simp
|
||||
@@ -699,7 +697,7 @@ protected theorem ediv_emod_unique {a b r q : Int} (h : 0 < b) :
|
||||
protected theorem ediv_emod_unique' {a b r q : Int} (h : b < 0) :
|
||||
a / b = q ∧ a % b = r ↔ r + b * q = a ∧ 0 ≤ r ∧ r < -b := by
|
||||
have := Int.ediv_emod_unique (a := a) (b := -b) (r := r) (q := -q) (by omega)
|
||||
simpa [Int.neg_inj]
|
||||
simpa [Int.neg_inj, Int.neg_mul, Int.mul_neg]
|
||||
|
||||
@[simp] theorem mul_emod_mul_of_pos
|
||||
{a : Int} (b c : Int) (H : 0 < a) : (a * b) % (a * c) = a * (b % c) := by
|
||||
@@ -793,7 +791,7 @@ theorem natAbs_emod (a : Int) {b : Int} (hb : b ≠ 0):
|
||||
omega
|
||||
|
||||
theorem natAbs_ediv_le_natAbs (a b : Int) : natAbs (a / b) ≤ natAbs a :=
|
||||
match b, eq_nat_or_neg b with
|
||||
match b, Int.eq_nat_or_neg b with
|
||||
| _, ⟨n, .inl rfl⟩ => aux _ _
|
||||
| _, ⟨n, .inr rfl⟩ => by rw [Int.ediv_neg, natAbs_neg]; apply aux
|
||||
where
|
||||
@@ -844,7 +842,7 @@ theorem emod_sub_cancel (x y : Int) : (x - y) % y = x % y := by
|
||||
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]
|
||||
rw [Int.add_neg_eq_sub, 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]
|
||||
@@ -1013,11 +1011,11 @@ protected theorem ediv_le_ediv {a b c : Int} (H : 0 < c) (H' : a ≤ b) : a / c
|
||||
|
||||
-- `tdiv` analogues of `ediv` lemmas from `Bootstrap.lean`
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem tdiv_neg : ∀ a b : Int, a.tdiv (-b) = -(a.tdiv b)
|
||||
| ofNat m, 0 => show ofNat (m / 0) = -↑(m / 0) by rw [Nat.div_zero]; rfl
|
||||
| ofNat _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
|
||||
| ofNat _, succ _ | -[_+1], 0 | -[_+1], -[_+1] => rfl
|
||||
| ofNat _, succ _ | -[_+1], 0 => by simp [Int.tdiv, Int.neg_zero, ← Int.negSucc_eq]
|
||||
| -[_+1], -[_+1] => by simp only [tdiv, neg_negSucc]
|
||||
|
||||
/-!
|
||||
There are no lemmas
|
||||
@@ -1112,10 +1110,11 @@ protected theorem eq_tdiv_of_mul_eq_left {a b c : Int}
|
||||
@[simp] protected theorem tdiv_self {a : Int} (H : a ≠ 0) : a.tdiv a = 1 := by
|
||||
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem neg_tdiv : ∀ a b : Int, (-a).tdiv b = -(a.tdiv b)
|
||||
| 0, n => by simp [Int.neg_zero]
|
||||
| succ _, (n:Nat) | -[_+1], 0 | -[_+1], -[_+1] => rfl
|
||||
| succ _, (n:Nat) => by simp [tdiv, ← Int.negSucc_eq]
|
||||
| -[_+1], 0 | -[_+1], -[_+1] => by
|
||||
simp only [tdiv, neg_negSucc, ← Int.natCast_succ, Int.neg_neg]
|
||||
| succ _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
|
||||
|
||||
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
|
||||
@@ -1123,16 +1122,16 @@ protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
|
||||
|
||||
theorem sign_tdiv (a b : Int) : sign (a.tdiv b) = if natAbs a < natAbs b then 0 else sign a * sign b := by
|
||||
induction b using wlog_sign
|
||||
case inv => simp; split <;> simp
|
||||
case inv => simp; split <;> simp [Int.mul_neg]
|
||||
case w b =>
|
||||
induction a using wlog_sign
|
||||
case inv => simp; split <;> simp
|
||||
case inv => simp; split <;> simp [Int.neg_mul]
|
||||
case w a =>
|
||||
rw [tdiv_eq_ediv_of_nonneg (by simp), sign_ediv]
|
||||
simp
|
||||
|
||||
@[simp] theorem natAbs_tdiv (a b : Int) : natAbs (a.tdiv b) = (natAbs a).div (natAbs b) :=
|
||||
match a, b, eq_nat_or_neg a, eq_nat_or_neg b with
|
||||
match a, b, Int.eq_nat_or_neg a, Int.eq_nat_or_neg b with
|
||||
| _, _, ⟨_, .inl rfl⟩, ⟨_, .inl rfl⟩ => rfl
|
||||
| _, _, ⟨_, .inl rfl⟩, ⟨_, .inr rfl⟩ => by rw [Int.tdiv_neg, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inl rfl⟩ => by rw [Int.neg_tdiv, natAbs_neg, natAbs_neg]; rfl
|
||||
@@ -1162,10 +1161,10 @@ theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
|
||||
|
||||
theorem lt_tmod_of_pos (a : Int) {b : Int} (H : 0 < b) : -b < tmod a b :=
|
||||
match a, b, eq_succ_of_zero_lt H with
|
||||
| ofNat _, _, ⟨n, rfl⟩ => by rw [ofNat_eq_coe, ← ofNat_tmod]; omega
|
||||
| ofNat _, _, ⟨n, rfl⟩ => by rw [ofNat_eq_coe, ← Int.natCast_succ, ← ofNat_tmod]; omega
|
||||
| -[a+1], _, ⟨n, rfl⟩ => by
|
||||
rw [negSucc_eq, neg_tmod, ← ofNat_succ, ← ofNat_tmod]
|
||||
have : a.succ % n.succ < n.succ := Nat.mod_lt _ (Nat.zero_lt_succ n)
|
||||
rw [negSucc_eq, neg_tmod, ← Int.natCast_succ, ← Int.natCast_succ, ← ofNat_tmod]
|
||||
have : (a + 1) % (n + 1) < n + 1 := Nat.mod_lt _ (Nat.zero_lt_succ n)
|
||||
omega
|
||||
|
||||
-- The following statements for `tmod` are false:
|
||||
@@ -1185,13 +1184,11 @@ theorem lt_tmod_of_pos (a : Int) {b : Int} (H : 0 < b) : -b < tmod a b :=
|
||||
@[simp] theorem mul_tmod_right (a b : Int) : (a * b).tmod a = 0 := by
|
||||
rw [Int.mul_comm, mul_tmod_left]
|
||||
|
||||
attribute [local simp] Int.neg_inj
|
||||
|
||||
theorem mul_tmod (a b n : Int) : (a * b).tmod n = (a.tmod n * b.tmod n).tmod n := by
|
||||
induction a using wlog_sign
|
||||
case inv => simp
|
||||
case inv => simp [Int.neg_mul]
|
||||
induction b using wlog_sign
|
||||
case inv => simp
|
||||
case inv => simp [Int.mul_neg]
|
||||
induction n using wlog_sign
|
||||
case inv => simp
|
||||
simp only [← Int.natCast_mul, ← ofNat_tmod]
|
||||
@@ -1334,14 +1331,14 @@ protected theorem tdiv_tmod_unique {a b r q : Int} (ha : 0 ≤ a) (hb : b ≠ 0)
|
||||
· replace hb' : 0 < -b := by omega
|
||||
have := Int.ediv_emod_unique (a := a) (q := -q) (r := r) hb'
|
||||
simp at this
|
||||
simp [this]
|
||||
simp [this, Int.neg_mul, Int.mul_neg]
|
||||
omega
|
||||
|
||||
protected theorem tdiv_tmod_unique' {a b r q : Int} (ha : a ≤ 0) (hb : b ≠ 0) :
|
||||
a.tdiv b = q ∧ a.tmod b = r ↔ r + b * q = a ∧ -natAbs b < r ∧ r ≤ 0 := by
|
||||
have := Int.tdiv_tmod_unique (a := -a) (q := -q) (r := -r) (by omega) hb
|
||||
simp at this
|
||||
simp [this]
|
||||
simp [this, Int.mul_neg]
|
||||
omega
|
||||
|
||||
@[simp] theorem mul_tmod_mul_of_pos
|
||||
@@ -1500,7 +1497,7 @@ theorem le_mod_self_add_one_iff {a b : Int} (h : 0 < b) : b ≤ a % b + 1 ↔ b
|
||||
match b, h with
|
||||
| .ofNat 1, h => simp
|
||||
| .ofNat (b + 2), h =>
|
||||
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int] at *
|
||||
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int] at *
|
||||
constructor
|
||||
· rw [dvd_iff_emod_eq_zero]
|
||||
intro w
|
||||
@@ -1523,7 +1520,7 @@ theorem add_one_tdiv_of_pos {a b : Int} (h : 0 < b) :
|
||||
| .ofNat (b + 2), h =>
|
||||
simp only [ofNat_eq_coe]
|
||||
rw [tdiv_eq_ediv, add_ediv (by omega), tdiv_eq_ediv]
|
||||
simp only [natCast_add, Nat.cast_ofNat_Int]
|
||||
simp only [Int.natCast_add, cast_ofNat_Int]
|
||||
have : 1 / (b + 2 : Int) = 0 := by rw [one_ediv]; omega
|
||||
rw [this]
|
||||
have one_mod : 1 % (b + 2 : Int) = 1 := emod_eq_of_lt (by omega) (by omega)
|
||||
@@ -1574,7 +1571,7 @@ protected theorem tdiv_le_tdiv {a b c : Int} (H : 0 < c) (H' : a ≤ b) : a.tdiv
|
||||
induction d with
|
||||
| zero => simp
|
||||
| succ d ih =>
|
||||
simp only [natCast_add, Nat.cast_ofNat_Int, ← Int.add_assoc]
|
||||
simp only [Int.natCast_add, cast_ofNat_Int, ← Int.add_assoc]
|
||||
rw [add_one_tdiv_of_pos (by omega)]
|
||||
omega
|
||||
|
||||
@@ -1647,9 +1644,9 @@ theorem fdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0
|
||||
· have : 0 < a / b := ediv_pos_of_neg_of_neg (by omega) (by omega)
|
||||
split <;> omega
|
||||
|
||||
unseal Nat.div in
|
||||
theorem fdiv_nonpos_of_nonneg_of_nonpos : ∀ {a b : Int}, 0 ≤ a → b ≤ 0 → a.fdiv b ≤ 0
|
||||
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => ⟨_⟩
|
||||
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => by
|
||||
simp [fdiv, negSucc_le_zero]
|
||||
|
||||
@[deprecated fdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
|
||||
abbrev fdiv_nonpos := @fdiv_nonpos_of_nonneg_of_nonpos
|
||||
@@ -1726,20 +1723,20 @@ theorem neg_fdiv {a b : Int} : (-a).fdiv b = -(a.fdiv b) - if b = 0 ∨ b ∣ a
|
||||
| ofNat (a + 1), 0 => simp
|
||||
| ofNat (a + 1), ofNat (b + 1) =>
|
||||
unfold fdiv
|
||||
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
|
||||
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
|
||||
rw [← negSucc_eq, ← negSucc_eq]
|
||||
| ofNat (a + 1), -[b+1] =>
|
||||
unfold fdiv
|
||||
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
|
||||
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
|
||||
rw [← negSucc_eq, neg_negSucc]
|
||||
| -[a+1], 0 => simp
|
||||
| -[a+1], ofNat (b + 1) =>
|
||||
unfold fdiv
|
||||
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
|
||||
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
|
||||
rw [neg_negSucc, ← negSucc_eq]
|
||||
| -[a+1], -[b+1] =>
|
||||
unfold fdiv
|
||||
simp only [ofNat_eq_coe, ofNat_ediv, Nat.succ_eq_add_one, natCast_add, Nat.cast_ofNat_Int]
|
||||
simp only [ofNat_eq_coe, ofNat_ediv, Nat.succ_eq_add_one, Int.natCast_add, cast_ofNat_Int]
|
||||
rw [neg_negSucc, neg_negSucc]
|
||||
simp
|
||||
|
||||
@@ -1974,7 +1971,7 @@ protected theorem fdiv_fmod_unique' {a b r q : Int} (h : b < 0) :
|
||||
a.fdiv b = q ∧ a.fmod b = r ↔ r + b * q = a ∧ b < r ∧ r ≤ 0 := by
|
||||
have := Int.fdiv_fmod_unique (a := -a) (b := -b) (r := -r) (q := q) (by omega)
|
||||
simp at this
|
||||
simp [this]
|
||||
simp [this, Int.neg_mul]
|
||||
omega
|
||||
|
||||
@[simp] theorem mul_fmod_mul_of_pos
|
||||
@@ -2039,7 +2036,7 @@ theorem fmod_sub_cancel (x y : Int) : (x - y).fmod y = x.fmod y := by
|
||||
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
|
||||
|
||||
@[simp] theorem add_neg_fmod_self (a b : Int) : (a + -b).fmod b = a.fmod b := by
|
||||
rw [← Int.sub_eq_add_neg, fmod_sub_cancel]
|
||||
rw [Int.add_neg_eq_sub, fmod_sub_cancel]
|
||||
|
||||
@[simp] theorem neg_add_fmod_self (a b : Int) : (-a + b).fmod a = b.fmod a := by
|
||||
rw [Int.add_comm, add_neg_fmod_self]
|
||||
@@ -2087,7 +2084,7 @@ theorem emod_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n) n = Int.bmod x n :=
|
||||
theorem bdiv_add_bmod (x : Int) (m : Nat) : m * bdiv x m + bmod x m = x := by
|
||||
unfold bdiv bmod
|
||||
split
|
||||
· simp_all only [Nat.cast_ofNat_Int, Int.mul_zero, emod_zero, Int.zero_add, Int.sub_zero,
|
||||
· simp_all only [cast_ofNat_Int, Int.mul_zero, emod_zero, Int.zero_add, Int.sub_zero,
|
||||
ite_self]
|
||||
· dsimp only
|
||||
split
|
||||
@@ -2213,7 +2210,7 @@ theorem emod_bmod {x : Int} {m : Nat} : bmod (x % m) m = bmod x m := by
|
||||
| 0 => rfl
|
||||
| (m+1) =>
|
||||
exfalso
|
||||
rw [natCast_add, ofNat_one, Int.add_assoc, add_ediv_of_dvd_right] at h
|
||||
rw [Int.natCast_add, ofNat_one, Int.add_assoc, add_ediv_of_dvd_right] at h
|
||||
change _ + 2 / 2 ≤ 0 at h
|
||||
rw [Int.ediv_self, ← ofNat_two, ← ofNat_ediv, add_one_le_iff, ← @Int.not_le] at h
|
||||
exact h (ofNat_nonneg _)
|
||||
@@ -2294,7 +2291,7 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
|
||||
| Int.ofNat x, _ => erw [← Int.ofNat_two, ← ofNat_add, ← ofNat_emod, t₁]; rfl
|
||||
cases x with
|
||||
| ofNat x =>
|
||||
simp only [bmod, ofNat_eq_coe, natAbs_ofNat, natCast_add, ofNat_one,
|
||||
simp only [bmod, ofNat_eq_coe, natAbs_ofNat, Int.natCast_add, ofNat_one,
|
||||
emod_self_add_one (ofNat_nonneg x)]
|
||||
match x with
|
||||
| 0 => rw [if_pos] <;> simp +decide
|
||||
@@ -2302,7 +2299,7 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
|
||||
rw [if_neg]
|
||||
· simp [← Int.sub_sub]
|
||||
· refine Int.not_lt.mpr ?_
|
||||
simp only [← natCast_add, ← ofNat_one, ← ofNat_two, ← ofNat_ediv]
|
||||
simp only [← Int.natCast_add, ← ofNat_one, ← ofNat_two, ← ofNat_ediv]
|
||||
match x with
|
||||
| 0 => apply Int.le_refl
|
||||
| (x+1) =>
|
||||
@@ -2312,10 +2309,10 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
|
||||
apply Nat.add_le_add_left (Nat.add_le_add_left (Nat.add_le_add_left (Nat.le_add_left
|
||||
_ _) _) _)
|
||||
| negSucc x =>
|
||||
rw [bmod, natAbs_negSucc, natCast_add, ofNat_one, sign_negSucc, Int.neg_neg,
|
||||
rw [bmod, natAbs_negSucc, Int.natCast_add, ofNat_one, sign_negSucc, Int.neg_neg,
|
||||
Nat.succ_eq_add_one, negSucc_emod]
|
||||
erw [t₂]
|
||||
· rw [natCast_add, ofNat_one, Int.add_sub_cancel, Int.add_comm, Int.add_sub_cancel, if_pos]
|
||||
· rw [Int.natCast_add, ofNat_one, Int.add_sub_cancel, Int.add_comm, Int.add_sub_cancel, if_pos]
|
||||
· match x, w with
|
||||
| (x+1), _ =>
|
||||
rw [Int.add_assoc, add_ediv_of_dvd_right, show (1 + 1 : Int) = 2 by decide, Int.ediv_self]
|
||||
|
||||
@@ -11,12 +11,30 @@ import Init.Data.Int.DivMod.Lemmas
|
||||
|
||||
/-!
|
||||
Definition and lemmas for gcd and lcm over Int
|
||||
|
||||
## Future work
|
||||
Most of the material about `Nat.gcd` and `Nat.lcm` from `Init.Data.Nat.Gcd` and `Init.Data.Nat.Lcm`
|
||||
has analogues for `Int.gcd` and `Int.lcm` that should be added to this file.
|
||||
-/
|
||||
namespace Int
|
||||
|
||||
/-! ## gcd -/
|
||||
|
||||
/-- Computes the greatest common divisor of two integers, as a `Nat`. -/
|
||||
/--
|
||||
Computes the greatest common divisor of two integers as a natural number. The GCD of two integers is
|
||||
the largest natural number that evenly divides both. However, the GCD of a number and `0` is the
|
||||
number's absolute value.
|
||||
|
||||
This implementation uses `Nat.gcd`, which is overridden in both the kernel and the compiler to
|
||||
efficiently evaluate using arbitrary-precision arithmetic.
|
||||
|
||||
Examples:
|
||||
* `Int.gcd 10 15 = 5`
|
||||
* `Int.gcd 10 (-15) = 5`
|
||||
* `Int.gcd (-6) (-9) = 3`
|
||||
* `Int.gcd 0 5 = 5`
|
||||
* `Int.gcd (-7) 0 = 7`
|
||||
-/
|
||||
def gcd (m n : Int) : Nat := m.natAbs.gcd n.natAbs
|
||||
|
||||
theorem gcd_dvd_left {a b : Int} : (gcd a b : Int) ∣ a := by
|
||||
@@ -37,7 +55,18 @@ theorem gcd_dvd_right {a b : Int} : (gcd a b : Int) ∣ b := by
|
||||
|
||||
/-! ## lcm -/
|
||||
|
||||
/-- Computes the least common multiple of two integers, as a `Nat`. -/
|
||||
/--
|
||||
Computes the least common multiple of two integers as a natural number. The LCM of two integers is
|
||||
the smallest natural number that's evenly divisible by the absolute values of both.
|
||||
|
||||
Examples:
|
||||
* `Int.lcm 9 6 = 18`
|
||||
* `Int.lcm 9 (-6) = 18`
|
||||
* `Int.lcm 9 3 = 9`
|
||||
* `Int.lcm 9 (-3) = 9`
|
||||
* `Int.lcm 0 3 = 0`
|
||||
* `Int.lcm (-3) 0 = 0`
|
||||
-/
|
||||
def lcm (m n : Int) : Nat := m.natAbs.lcm n.natAbs
|
||||
|
||||
theorem lcm_ne_zero (hm : m ≠ 0) (hn : n ≠ 0) : lcm m n ≠ 0 := by
|
||||
|
||||
@@ -25,31 +25,32 @@ theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat
|
||||
|
||||
@[norm_cast] theorem ofNat_add (n m : Nat) : (↑(n + m) : Int) = n + m := rfl
|
||||
@[norm_cast] theorem ofNat_mul (n m : Nat) : (↑(n * m) : Int) = n * m := rfl
|
||||
theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
|
||||
@[norm_cast] theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
|
||||
|
||||
@[local simp] theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
|
||||
@[local simp] theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
|
||||
@[local simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
|
||||
|
||||
theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
|
||||
theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
|
||||
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
|
||||
theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
|
||||
|
||||
theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
|
||||
|
||||
/-! ## These are only for internal use -/
|
||||
|
||||
@[simp] theorem add_def {a b : Int} : Int.add a b = a + b := rfl
|
||||
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
|
||||
|
||||
/-!
|
||||
## These are only for internal use
|
||||
|
||||
Ideally these could all be made private, but they are used in downstream libraries.
|
||||
-/
|
||||
|
||||
@[local simp] theorem ofNat_add_ofNat (m n : Nat) : (↑m + ↑n : Int) = ↑(m + n) := rfl
|
||||
@[local simp] theorem ofNat_add_negSucc (m n : Nat) : ↑m + -[n+1] = subNatNat m (succ n) := rfl
|
||||
@[local simp] theorem negSucc_add_ofNat (m n : Nat) : -[m+1] + ↑n = subNatNat n (succ m) := rfl
|
||||
@[local simp] theorem negSucc_add_negSucc (m n : Nat) : -[m+1] + -[n+1] = -[succ (m + n) +1] := rfl
|
||||
|
||||
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
|
||||
|
||||
@[local simp] theorem ofNat_mul_ofNat (m n : Nat) : (↑m * ↑n : Int) = ↑(m * n) := rfl
|
||||
@[local simp] theorem ofNat_mul_negSucc' (m n : Nat) : ↑m * -[n+1] = negOfNat (m * succ n) := rfl
|
||||
@[local simp] theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * ↑n = negOfNat (succ m * n) := rfl
|
||||
@[local simp] theorem negSucc_mul_negSucc' (m n : Nat) :
|
||||
@[local simp] private theorem ofNat_mul_negSucc' (m n : Nat) : ↑m * -[n+1] = negOfNat (m * succ n) := rfl
|
||||
@[local simp] private theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * ↑n = negOfNat (succ m * n) := rfl
|
||||
@[local simp] private theorem negSucc_mul_negSucc' (m n : Nat) :
|
||||
-[m+1] * -[n+1] = ofNat (succ m * succ n) := rfl
|
||||
|
||||
/- ## some basic functions and properties -/
|
||||
@@ -64,11 +65,14 @@ theorem negSucc_inj : negSucc m = negSucc n ↔ m = n := ⟨negSucc.inj, fun H =
|
||||
|
||||
theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
|
||||
|
||||
@[deprecated negSucc_eq (since := "2025-03-11")]
|
||||
theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
|
||||
|
||||
@[simp] theorem negSucc_ne_zero (n : Nat) : -[n+1] ≠ 0 := nofun
|
||||
|
||||
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 ≠ -[n+1] := nofun
|
||||
|
||||
@[simp, norm_cast] theorem Nat.cast_ofNat_Int :
|
||||
@[simp, norm_cast] theorem cast_ofNat_Int :
|
||||
(Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
|
||||
|
||||
/- ## neg -/
|
||||
@@ -86,6 +90,7 @@ theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
|
||||
protected theorem neg_ne_zero : -a ≠ 0 ↔ a ≠ 0 := not_congr Int.neg_eq_zero
|
||||
|
||||
protected theorem sub_eq_add_neg {a b : Int} : a - b = a + -b := rfl
|
||||
protected theorem add_neg_eq_sub {a b : Int} : a + -b = a - b := rfl
|
||||
|
||||
theorem add_neg_one (i : Int) : i + -1 = i - 1 := rfl
|
||||
|
||||
@@ -140,29 +145,6 @@ theorem subNatNat_of_lt {m n : Nat} (h : m < n) : subNatNat m n = -[pred (n - m)
|
||||
rw [Nat.sub_eq_iff_eq_add' h]
|
||||
simp
|
||||
|
||||
/- # Additive group properties -/
|
||||
|
||||
/- addition -/
|
||||
|
||||
protected theorem add_comm : ∀ a b : Int, a + b = b + a
|
||||
| ofNat n, ofNat m => by simp [Nat.add_comm]
|
||||
| ofNat _, -[_+1] => rfl
|
||||
| -[_+1], ofNat _ => rfl
|
||||
| -[_+1], -[_+1] => by simp [Nat.add_comm]
|
||||
instance : Std.Commutative (α := Int) (· + ·) := ⟨Int.add_comm⟩
|
||||
|
||||
@[simp] protected theorem add_zero : ∀ a : Int, a + 0 = a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. ▸ a.add_zero
|
||||
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
|
||||
left_id := Int.zero_add
|
||||
right_id := Int.add_zero
|
||||
|
||||
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
|
||||
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
|
||||
|
||||
theorem subNatNat_sub (h : n ≤ m) (k : Nat) : subNatNat (m - n) k = subNatNat m (k + n) := by
|
||||
rwa [← subNatNat_add_add _ _ n, Nat.sub_add_cancel]
|
||||
|
||||
@@ -191,6 +173,34 @@ theorem subNatNat_add_negSucc (m n k : Nat) :
|
||||
← Nat.add_assoc, succ_sub_succ_eq_sub, Nat.add_comm n,Nat.add_sub_assoc (Nat.le_of_lt h'),
|
||||
Nat.add_comm]
|
||||
|
||||
theorem subNatNat_self : ∀ n, subNatNat n n = 0
|
||||
| 0 => rfl
|
||||
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
|
||||
|
||||
/- # Additive group properties -/
|
||||
|
||||
/- addition -/
|
||||
|
||||
protected theorem add_comm : ∀ a b : Int, a + b = b + a
|
||||
| ofNat n, ofNat m => by simp [Nat.add_comm]
|
||||
| ofNat _, -[_+1] => rfl
|
||||
| -[_+1], ofNat _ => rfl
|
||||
| -[_+1], -[_+1] => by simp [Nat.add_comm]
|
||||
|
||||
instance : Std.Commutative (α := Int) (· + ·) := ⟨Int.add_comm⟩
|
||||
|
||||
@[simp] protected theorem add_zero : ∀ a : Int, a + 0 = a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. ▸ a.add_zero
|
||||
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
|
||||
left_id := Int.zero_add
|
||||
right_id := Int.add_zero
|
||||
|
||||
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
|
||||
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
|
||||
|
||||
protected theorem add_assoc : ∀ a b c : Int, a + b + c = a + (b + c)
|
||||
| (m:Nat), (n:Nat), _ => aux1 ..
|
||||
| Nat.cast m, b, Nat.cast k => by
|
||||
@@ -222,18 +232,12 @@ protected theorem add_right_comm (a b c : Int) : a + b + c = a + c + b := by
|
||||
|
||||
/- ## negation -/
|
||||
|
||||
theorem subNatNat_self : ∀ n, subNatNat n n = 0
|
||||
protected theorem add_left_neg : ∀ a : Int, -a + a = 0
|
||||
| 0 => rfl
|
||||
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
|
||||
| succ m => by simp [neg_ofNat_succ]
|
||||
| -[m+1] => by simp [neg_negSucc]
|
||||
|
||||
attribute [local simp] subNatNat_self
|
||||
|
||||
@[local simp] protected theorem add_left_neg : ∀ a : Int, -a + a = 0
|
||||
| 0 => rfl
|
||||
| succ m => by simp
|
||||
| -[m+1] => by simp
|
||||
|
||||
@[local simp] protected theorem add_right_neg (a : Int) : a + -a = 0 := by
|
||||
protected theorem add_right_neg (a : Int) : a + -a = 0 := by
|
||||
rw [Int.add_comm, Int.add_left_neg]
|
||||
|
||||
protected theorem neg_eq_of_add_eq_zero {a b : Int} (h : a + b = 0) : -a = b := by
|
||||
@@ -264,7 +268,7 @@ protected theorem add_left_cancel {a b c : Int} (h : a + b = a + c) : b = c := b
|
||||
have h₁ : -a + (a + b) = -a + (a + c) := by rw [h]
|
||||
simp [← Int.add_assoc, Int.add_left_neg, Int.zero_add] at h₁; exact h₁
|
||||
|
||||
@[local simp] protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
|
||||
protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
|
||||
apply Int.add_left_cancel (a := a + b)
|
||||
rw [Int.add_right_neg, Int.add_comm a, ← Int.add_assoc, Int.add_assoc b,
|
||||
Int.add_right_neg, Int.add_zero, Int.add_right_neg]
|
||||
@@ -273,8 +277,8 @@ protected theorem add_left_cancel {a b c : Int} (h : a + b = a + c) : b = c := b
|
||||
If a predicate on the integers is invariant under negation,
|
||||
then it is sufficient to prove it for the nonnegative integers.
|
||||
-/
|
||||
theorem wlog_sign {P : Int → Prop} (inv : ∀ a, P a ↔ P (-a)) (w : ∀ n : Nat, P n) (a : Int) : P a := by
|
||||
cases a with
|
||||
theorem wlog_sign {P : Int → Prop} (inv : ∀ i, P i ↔ P (-i)) (w : ∀ n : Nat, P n) (i : Int) : P i := by
|
||||
cases i with
|
||||
| ofNat n => exact w n
|
||||
| negSucc n =>
|
||||
rw [negSucc_eq, ← inv, ← ofNat_succ]
|
||||
@@ -303,13 +307,13 @@ protected theorem sub_eq_zero {a b : Int} : a - b = 0 ↔ a = b :=
|
||||
⟨Int.eq_of_sub_eq_zero, Int.sub_eq_zero_of_eq⟩
|
||||
|
||||
protected theorem sub_sub (a b c : Int) : a - b - c = a - (b + c) := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_assoc]
|
||||
simp [Int.sub_eq_add_neg, Int.add_assoc, Int.neg_add]
|
||||
|
||||
protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_comm]
|
||||
simp [Int.sub_eq_add_neg, Int.add_comm, Int.neg_add]
|
||||
|
||||
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
|
||||
simp [Int.sub_eq_add_neg, ← Int.add_assoc]
|
||||
simp [Int.sub_eq_add_neg, ← Int.add_assoc, Int.neg_add, Int.add_right_neg]
|
||||
|
||||
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
|
||||
|
||||
@@ -320,7 +324,7 @@ protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
|
||||
Int.add_neg_cancel_right a b
|
||||
|
||||
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, ← Int.sub_eq_add_neg]
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_neg_eq_sub]
|
||||
|
||||
@[norm_cast] theorem ofNat_sub (h : m ≤ n) : ((n - m : Nat) : Int) = n - m := by
|
||||
match m with
|
||||
@@ -329,6 +333,7 @@ protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
|
||||
show ofNat (n - succ m) = subNatNat n (succ m)
|
||||
rw [subNatNat, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
@[deprecated negSucc_eq (since := "2025-03-11")]
|
||||
theorem negSucc_coe' (n : Nat) : -[n+1] = -↑n - 1 := by
|
||||
rw [Int.sub_eq_add_neg, ← Int.neg_add]; rfl
|
||||
|
||||
@@ -338,11 +343,11 @@ protected theorem subNatNat_eq_coe {m n : Nat} : subNatNat m n = ↑m - ↑n :=
|
||||
rw [Int.ofNat_add, Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm,
|
||||
Int.add_right_neg, Int.add_zero]
|
||||
· intros i n
|
||||
simp only [negSucc_coe, ofNat_add, Int.sub_eq_add_neg, Int.neg_add, ← Int.add_assoc]
|
||||
rw [← @Int.sub_eq_add_neg n, ← ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
|
||||
simp only [negSucc_eq, ofNat_add, ofNat_one, Int.sub_eq_add_neg, Int.neg_add, ← Int.add_assoc]
|
||||
rw [Int.add_neg_eq_sub (a := n), ← ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
|
||||
apply Nat.le_refl
|
||||
|
||||
theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
|
||||
@[simp] theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
|
||||
rw [← Int.subNatNat_eq_coe]
|
||||
refine subNatNat_elim m n (fun m n i => toNat i = m - n) (fun i n => ?_) (fun i n => ?_)
|
||||
· exact (Nat.add_sub_cancel_left ..).symm
|
||||
@@ -358,6 +363,9 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
|
||||
norm_cast
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem negSucc_eq_neg_ofNat_iff {a b : Nat} : -[a+1] = - (b : Int) ↔ a + 1 = b := by
|
||||
rw [eq_comm, neg_ofNat_eq_negSucc_iff, eq_comm]
|
||||
|
||||
@[simp] theorem neg_ofNat_eq_negSucc_add_one_iff {a b : Nat} : - (a : Int) = -[b+1] + 1 ↔ a = b := by
|
||||
cases b with
|
||||
| zero => simp; norm_cast
|
||||
@@ -366,30 +374,33 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
|
||||
norm_cast
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem negSucc_add_one_eq_neg_ofNat_iff {a b : Nat} : -[a+1] + 1 = - (b : Int) ↔ a = b := by
|
||||
rw [eq_comm, neg_ofNat_eq_negSucc_add_one_iff, eq_comm]
|
||||
|
||||
/- ## add/sub injectivity -/
|
||||
|
||||
protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
@[simp] protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
apply Iff.intro
|
||||
· intro p
|
||||
rw [←Int.add_sub_cancel i k, ←Int.add_sub_cancel j k, p]
|
||||
· exact congrArg (· + k)
|
||||
|
||||
protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) ↔ i = j := by
|
||||
@[simp] protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) ↔ i = j := by
|
||||
simp [Int.add_comm k, Int.add_left_inj]
|
||||
|
||||
protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) ↔ i = j := by
|
||||
@[simp] protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg, Int.neg_inj, Int.add_right_inj]
|
||||
|
||||
protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
@[simp] protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_left_inj]
|
||||
|
||||
/- ## Ring properties -/
|
||||
|
||||
@[simp] theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -↑(m * succ n) := rfl
|
||||
theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -↑(m * succ n) := rfl
|
||||
|
||||
@[simp] theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -↑(succ m * n) := rfl
|
||||
theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -↑(succ m * n) := rfl
|
||||
|
||||
@[simp] theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
|
||||
theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
|
||||
|
||||
protected theorem mul_comm (a b : Int) : a * b = b * a := by
|
||||
cases a <;> cases b <;> simp [Nat.mul_comm]
|
||||
@@ -407,11 +418,10 @@ theorem negSucc_mul_negOfNat (m n : Nat) : -[m+1] * negOfNat n = ofNat (succ m *
|
||||
theorem negOfNat_mul_negSucc (m n : Nat) : negOfNat n * -[m+1] = ofNat (n * succ m) := by
|
||||
rw [Int.mul_comm, negSucc_mul_negOfNat, Nat.mul_comm]
|
||||
|
||||
attribute [local simp] ofNat_mul_negOfNat negOfNat_mul_ofNat
|
||||
negSucc_mul_negOfNat negOfNat_mul_negSucc
|
||||
|
||||
protected theorem mul_assoc (a b c : Int) : a * b * c = a * (b * c) := by
|
||||
cases a <;> cases b <;> cases c <;> simp [Nat.mul_assoc]
|
||||
cases a <;> cases b <;> cases c <;>
|
||||
simp [Nat.mul_assoc, ofNat_mul_negOfNat, negOfNat_mul_ofNat, negSucc_mul_negOfNat, negOfNat_mul_negSucc]
|
||||
|
||||
instance : Std.Associative (α := Int) (· * ·) := ⟨Int.mul_assoc⟩
|
||||
|
||||
protected theorem mul_left_comm (a b c : Int) : a * (b * c) = b * (a * c) := by
|
||||
@@ -429,7 +439,7 @@ theorem negOfNat_eq_subNatNat_zero (n) : negOfNat n = subNatNat 0 n := by cases
|
||||
theorem ofNat_mul_subNatNat (m n k : Nat) :
|
||||
m * subNatNat n k = subNatNat (m * n) (m * k) := by
|
||||
cases m with
|
||||
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul]
|
||||
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul, subNatNat_self]
|
||||
| succ m => cases n.lt_or_ge k with
|
||||
| inl h =>
|
||||
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m)
|
||||
@@ -457,8 +467,7 @@ theorem negSucc_mul_subNatNat (m n k : Nat) :
|
||||
Nat.mul_sub_left_distrib, ← succ_pred_eq_of_pos (Nat.sub_pos_of_lt h₁)]; rfl
|
||||
| inr h' => rw [Nat.le_antisymm h h', subNatNat_self, subNatNat_self, Int.mul_zero]
|
||||
|
||||
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat
|
||||
|
||||
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat in
|
||||
protected theorem mul_add : ∀ a b c : Int, a * (b + c) = a * b + a * c
|
||||
| (m:Nat), (n:Nat), (k:Nat) => by simp [Nat.left_distrib]
|
||||
| (m:Nat), (n:Nat), -[k+1] => by
|
||||
@@ -481,21 +490,23 @@ 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) :=
|
||||
-- Note, this is not a `@[simp]` lemma because it interferes with normalization in `simp +arith`.
|
||||
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) :=
|
||||
-- Note, this is not a `@[simp]` lemma because it interferes with normalization in `simp +arith`.
|
||||
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
|
||||
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp [Int.neg_mul, Int.mul_neg]
|
||||
|
||||
protected theorem neg_mul_comm (a b : Int) : -a * b = a * -b := by simp
|
||||
protected theorem neg_mul_comm (a b : Int) : -a * b = a * -b := by simp [Int.neg_mul, Int.mul_neg]
|
||||
|
||||
protected theorem mul_sub (a b c : Int) : a * (b - c) = a * b - a * c := by
|
||||
simp [Int.sub_eq_add_neg, Int.mul_add]
|
||||
simp [Int.sub_eq_add_neg, Int.mul_add, Int.mul_neg]
|
||||
|
||||
protected theorem sub_mul (a b c : Int) : (a - b) * c = a * c - b * c := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_mul]
|
||||
simp [Int.sub_eq_add_neg, Int.add_mul, Int.neg_mul]
|
||||
|
||||
@[simp] protected theorem one_mul : ∀ a : Int, 1 * a = a
|
||||
| ofNat n => show ofNat (1 * n) = ofNat n by rw [Nat.one_mul]
|
||||
@@ -506,7 +517,9 @@ instance : Std.LawfulIdentity (α := Int) (· * ·) 1 where
|
||||
left_id := Int.one_mul
|
||||
right_id := Int.mul_one
|
||||
|
||||
protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
|
||||
@[simp] protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
|
||||
|
||||
@[simp] protected theorem neg_one_mul (a : Int) : -1 * a = -a := by rw [Int.neg_mul, Int.one_mul]
|
||||
|
||||
protected theorem neg_eq_neg_one_mul : ∀ a : Int, -a = -1 * a
|
||||
| 0 => rfl
|
||||
@@ -555,16 +568,18 @@ The following lemmas are later subsumed by e.g. `Nat.cast_add` and `Nat.cast_mul
|
||||
but it is convenient to have these earlier, for users who only need `Nat` and `Int`.
|
||||
-/
|
||||
|
||||
theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
|
||||
protected theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
|
||||
|
||||
theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
|
||||
protected theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
|
||||
|
||||
@[simp] theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
|
||||
@[simp] protected theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
|
||||
-- Note this only works because of local simp attributes in this file,
|
||||
-- so it still makes sense to tag the lemmas with `@[simp]`.
|
||||
simp
|
||||
|
||||
@[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
|
||||
protected theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : Int) = (n : Int) + 1 := rfl
|
||||
|
||||
@[simp] protected theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
|
||||
simp
|
||||
|
||||
end Int
|
||||
|
||||
@@ -15,6 +15,32 @@ import Init.Omega
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ### miscellaneous lemmas -/
|
||||
|
||||
@[simp] theorem natCast_le_zero : {n : Nat} → (n : Int) ≤ 0 ↔ n = 0 := by omega
|
||||
|
||||
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c ↔ a = c + b := by omega
|
||||
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c ↔ a = b + c := by omega
|
||||
|
||||
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i ≤ 0 ↔ 0 ≤ i := by omega
|
||||
|
||||
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ≤ ((no_index (OfNat.ofNat n)) : Int) :=
|
||||
ofNat_nonneg _
|
||||
|
||||
@[simp] theorem neg_natCast_le_natCast (n m : Nat) : -(n : Int) ≤ (m : Int) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_natCast_le_ofNat (n m : Nat) : -(n : Int) ≤ (no_index (OfNat.ofNat m)) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_ofNat_le_ofNat (n m : Nat) : -(no_index (OfNat.ofNat n)) ≤ (no_index (OfNat.ofNat m)) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_ofNat_le_natCast (n m : Nat) : -(no_index (OfNat.ofNat n)) ≤ (m : Int) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
/-! ### toNat -/
|
||||
|
||||
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by
|
||||
symm
|
||||
simp only [Int.toNat]
|
||||
@@ -39,17 +65,13 @@ namespace Int
|
||||
simp [toNat]
|
||||
split <;> simp_all <;> omega
|
||||
|
||||
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
(x.bmod m) < 0 ↔ (-(m / 2) ≤ x ∧ x < 0) ∨ ((m + 1) / 2 ≤ x) := by
|
||||
simp only [Int.bmod_def]
|
||||
by_cases xpos : 0 ≤ x
|
||||
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
|
||||
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
|
||||
|
||||
@[simp] theorem natCast_le_zero : {n : Nat} → (n : Int) ≤ 0 ↔ n = 0 := by omega
|
||||
|
||||
@[simp] theorem toNat_eq_zero : ∀ {n : Int}, n.toNat = 0 ↔ n ≤ 0 := by omega
|
||||
|
||||
@[simp] theorem toNat_le {m : Int} {n : Nat} : m.toNat ≤ n ↔ m ≤ n := by omega
|
||||
@[simp] theorem toNat_lt' {m : Int} {n : Nat} (hn : 0 < n) : m.toNat < n ↔ m < n := by omega
|
||||
|
||||
/-! ### natAbs -/
|
||||
|
||||
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d ∣ n) (h₁ : n.natAbs < d.natAbs) :
|
||||
n = 0 := by
|
||||
obtain ⟨a, rfl⟩ := h
|
||||
@@ -57,6 +79,53 @@ theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d ∣ n) (h₁ : n.n
|
||||
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
|
||||
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
|
||||
|
||||
/-! ### min and max -/
|
||||
|
||||
@[simp] protected theorem min_assoc : ∀ (a b c : Int), min (min a b) c = min a (min b c) := by omega
|
||||
instance : Std.Associative (α := Nat) min := ⟨Nat.min_assoc⟩
|
||||
|
||||
@[simp] protected theorem min_self_assoc {m n : Int} : min m (min m n) = min m n := by
|
||||
rw [← Int.min_assoc, Int.min_self]
|
||||
|
||||
@[simp] protected theorem min_self_assoc' {m n : Int} : min n (min m n) = min n m := by
|
||||
rw [Int.min_comm m n, ← Int.min_assoc, Int.min_self]
|
||||
|
||||
@[simp] protected theorem max_assoc (a b c : Int) : max (max a b) c = max a (max b c) := by omega
|
||||
instance : Std.Associative (α := Nat) max := ⟨Nat.max_assoc⟩
|
||||
|
||||
@[simp] protected theorem max_self_assoc {m n : Int} : max m (max m n) = max m n := by
|
||||
rw [← Int.max_assoc, Int.max_self]
|
||||
|
||||
@[simp] protected theorem max_self_assoc' {m n : Int} : max n (max m n) = max n m := by
|
||||
rw [Int.max_comm m n, ← Int.max_assoc, Int.max_self]
|
||||
|
||||
protected theorem max_min_distrib_left (a b c : Int) : max a (min b c) = min (max a b) (max a c) := by omega
|
||||
|
||||
protected theorem min_max_distrib_left (a b c : Int) : min a (max b c) = max (min a b) (min a c) := by omega
|
||||
|
||||
protected theorem max_min_distrib_right (a b c : Int) :
|
||||
max (min a b) c = min (max a c) (max b c) := by omega
|
||||
|
||||
protected theorem min_max_distrib_right (a b c : Int) :
|
||||
min (max a b) c = max (min a c) (min b c) := by omega
|
||||
|
||||
protected theorem sub_min_sub_right (a b c : Int) : min (a - c) (b - c) = min a b - c := by omega
|
||||
|
||||
protected theorem sub_max_sub_right (a b c : Int) : max (a - c) (b - c) = max a b - c := by omega
|
||||
|
||||
protected theorem sub_min_sub_left (a b c : Int) : min (a - b) (a - c) = a - max b c := by omega
|
||||
|
||||
protected theorem sub_max_sub_left (a b c : Int) : max (a - b) (a - c) = a - min b c := by omega
|
||||
|
||||
/-! ### bmod -/
|
||||
|
||||
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
(x.bmod m) < 0 ↔ (-(m / 2) ≤ x ∧ x < 0) ∨ ((m + 1) / 2 ≤ x) := by
|
||||
simp only [Int.bmod_def]
|
||||
by_cases xpos : 0 ≤ x
|
||||
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
|
||||
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
|
||||
|
||||
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n < (m + 1) / 2) :
|
||||
n.bmod m = n := by
|
||||
rw [← Int.sub_eq_zero]
|
||||
@@ -65,33 +134,10 @@ theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n <
|
||||
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
|
||||
omega
|
||||
|
||||
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c ↔ a = c + b := by omega
|
||||
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c ↔ a = b + c := by omega
|
||||
|
||||
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n ∣ m) :
|
||||
(a.bmod m).bmod n = a.bmod n := by
|
||||
rw [← Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
|
||||
obtain ⟨k, rfl⟩ := hnm
|
||||
simp [Int.mul_assoc]
|
||||
|
||||
@[simp] theorem toNat_le {m : Int} {n : Nat} : m.toNat ≤ n ↔ m ≤ n := by omega
|
||||
@[simp] theorem toNat_lt' {m : Int} {n : Nat} (hn : 0 < n) : m.toNat < n ↔ m < n := by omega
|
||||
|
||||
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i ≤ 0 ↔ 0 ≤ i := by omega
|
||||
|
||||
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ≤ ((no_index (OfNat.ofNat n)) : Int) :=
|
||||
ofNat_nonneg _
|
||||
|
||||
@[simp] theorem neg_natCast_le_natCast (n m : Nat) : -(n : Int) ≤ (m : Int) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_natCast_le_ofNat (n m : Nat) : -(n : Int) ≤ (no_index (OfNat.ofNat m)) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_ofNat_le_ofNat (n m : Nat) : -(no_index (OfNat.ofNat n)) ≤ (no_index (OfNat.ofNat m)) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_ofNat_le_natCast (n m : Nat) : -(no_index (OfNat.ofNat n)) ≤ (m : Int) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
end Int
|
||||
|
||||
@@ -320,7 +320,6 @@ theorem Poly.denote_div_eq_of_divAll (ctx : Context) (p : Poly) (k : Int) : p.di
|
||||
replace h₁ := div_mul_cancel_of_mod_zero h₁
|
||||
have ih := ih h₂
|
||||
simp [ih]
|
||||
apply congrArg (denote ctx p + ·)
|
||||
rw [Int.mul_right_comm, h₁]
|
||||
|
||||
attribute [local simp] Poly.divCoeffs Poly.getConst
|
||||
@@ -362,7 +361,7 @@ theorem Expr.denote_toPoly'_go (ctx : Context) (e : Expr) :
|
||||
simp only [mul_def, denote]
|
||||
rw [Int.mul_comm (denote _ _) _]
|
||||
simpa [Int.mul_assoc] using ih
|
||||
| case10 k a ih => simp [toPoly'.go, ih]
|
||||
| case10 k a ih => simp [toPoly'.go, ih, Int.neg_mul, Int.mul_neg]
|
||||
|
||||
theorem Expr.denote_norm (ctx : Context) (e : Expr) : e.norm.denote ctx = e.denote ctx := by
|
||||
simp [norm, toPoly', Expr.denote_toPoly'_go]
|
||||
@@ -420,7 +419,7 @@ theorem norm_eq_var_const (ctx : Context) (lhs rhs : Expr) (x : Var) (k : Int) (
|
||||
simp [norm_eq_var_const_cert] at h
|
||||
replace h := congrArg (Poly.denote ctx) h
|
||||
simp at h
|
||||
rw [←Int.sub_eq_zero, h, Int.add_comm, ← Int.sub_eq_add_neg, Int.sub_eq_zero]
|
||||
rw [←Int.sub_eq_zero, h, Int.add_comm, Int.add_neg_eq_sub, Int.sub_eq_zero]
|
||||
|
||||
private theorem mul_eq_zero_iff (a k : Int) (h₁ : k > 0) : k * a = 0 ↔ a = 0 := by
|
||||
conv => lhs; rw [← Int.mul_zero k]
|
||||
@@ -799,10 +798,10 @@ theorem dvd_solve_elim (ctx : Context) (d₁ : Int) (p₁ : Poly) (d₂ : Int) (
|
||||
simp [dvd_solve_elim_cert]
|
||||
split <;> simp
|
||||
next a₁ x₁ p₁ a₂ x₂ p₂ =>
|
||||
intro _ hd _; subst x₁ p; simp
|
||||
intro _ hd _; subst x₁ p; simp [Int.neg_mul]
|
||||
intro h₁ h₂
|
||||
rw [Int.add_comm] at h₁ h₂
|
||||
rw [← Int.sub_eq_add_neg]
|
||||
rw [Int.add_neg_eq_sub]
|
||||
exact dvd_solve_elim' hd h₁ h₂
|
||||
|
||||
theorem dvd_norm (ctx : Context) (d : Int) (p₁ p₂ : Poly) : p₁.norm == p₂ → d ∣ p₁.denote' ctx → d ∣ p₂.denote' ctx := by
|
||||
@@ -909,7 +908,7 @@ def Poly.coeff (p : Poly) (x : Var) : Int :=
|
||||
| .num _ => 0
|
||||
|
||||
private theorem eq_add_coeff_insert (ctx : Context) (p : Poly) (x : Var) : p.denote ctx = (p.coeff x) * (x.denote ctx) + (p.insert (-p.coeff x) x).denote ctx := by
|
||||
simp; rw [← Int.add_assoc, Int.add_neg_cancel_right]
|
||||
simp; rw [← Int.add_assoc, Int.neg_mul, Int.add_neg_cancel_right]
|
||||
|
||||
private theorem dvd_of_eq' {a x p : Int} : a*x + p = 0 → a ∣ p := by
|
||||
intro h
|
||||
@@ -978,7 +977,7 @@ theorem eq_dvd_subst (ctx : Context) (x : Var) (p₁ : Poly) (d₂ : Int) (p₂
|
||||
have := eq_dvd_subst' h₁ h₂
|
||||
rw [Int.sub_eq_add_neg, Int.add_comm] at this
|
||||
apply abs_dvd
|
||||
simp [this]
|
||||
simp [this, Int.neg_mul]
|
||||
|
||||
def eq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
|
||||
let a := p₁.coeff x
|
||||
@@ -1018,10 +1017,8 @@ theorem eq_le_subst_nonpos (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly)
|
||||
intro h
|
||||
intro; subst p₃
|
||||
intro h₁ h₂
|
||||
simp [*, -Int.neg_nonpos_iff]
|
||||
simp [*, -Int.neg_nonpos_iff, Int.neg_mul]
|
||||
replace h₂ := Int.mul_le_mul_of_nonpos_left h₂ h; simp at h₂; clear h
|
||||
rw [← Int.neg_zero]
|
||||
apply Int.neg_le_neg
|
||||
rw [Int.mul_comm]
|
||||
assumption
|
||||
|
||||
@@ -1032,7 +1029,7 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: eq_of_core_cert p₁ p₂ p₃ → p₁.denote' ctx = p₂.denote' ctx → p₃.denote' ctx = 0 := by
|
||||
simp [eq_of_core_cert]
|
||||
intro; subst p₃; simp
|
||||
intro h; rw [h, ←Int.sub_eq_add_neg, Int.sub_self]
|
||||
intro h; rw [h, Int.add_neg_eq_sub, Int.sub_self]
|
||||
|
||||
def Poly.isUnsatDiseq (p : Poly) : Bool :=
|
||||
match p with
|
||||
@@ -1072,7 +1069,7 @@ theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
simp [eq_of_core_cert]
|
||||
intro; subst p₃; simp
|
||||
intro h; rw [← Int.sub_eq_zero] at h
|
||||
rw [←Int.sub_eq_add_neg]; assumption
|
||||
rw [Int.add_neg_eq_sub]; assumption
|
||||
|
||||
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
|
||||
p₂ == p₁.mul (-1)
|
||||
@@ -1082,7 +1079,6 @@ theorem eq_of_le_ge (ctx : Context) (p₁ : Poly) (p₂ : Poly)
|
||||
simp [eq_of_le_ge_cert]
|
||||
intro; subst p₂; simp [-Int.neg_nonpos_iff]
|
||||
intro h₁ h₂
|
||||
replace h₂ := Int.neg_le_of_neg_le h₂; simp at h₂
|
||||
simp [Int.eq_iff_le_and_ge, *]
|
||||
|
||||
def le_of_le_diseq_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
|
||||
@@ -1157,12 +1153,10 @@ private theorem orOver_of_exists {n p} : (∃ k, k < n ∧ p k) → OrOver n p :
|
||||
private theorem ofNat_toNat {a : Int} : a ≥ 0 → Int.ofNat a.toNat = a := by cases a <;> simp
|
||||
private theorem cast_toNat {a : Int} : a ≥ 0 → a.toNat = a := by cases a <;> simp
|
||||
private theorem ofNat_lt {a : Int} {n : Nat} : a ≥ 0 → a < Int.ofNat n → a.toNat < n := by cases a <;> simp
|
||||
@[local simp] private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
|
||||
@[local simp] private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
|
||||
@[local simp] private theorem gcd_neg_left (a b : Int) : Int.gcd (-a) b = Int.gcd a b := by simp [Int.gcd]
|
||||
@[local simp] private theorem gcd_neg_right (a b : Int) : Int.gcd a (-b) = Int.gcd a b := by simp [Int.gcd]
|
||||
@[local simp] private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
|
||||
@[local simp] private theorem lcm_one (a : Int) : Int.lcm a 1 = a.natAbs := by simp [Int.lcm]
|
||||
private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
|
||||
private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
|
||||
private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
|
||||
private theorem lcm_one (a : Int) : Int.lcm a 1 = a.natAbs := by simp [Int.lcm]
|
||||
|
||||
private theorem cooper_dvd_left_core
|
||||
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
|
||||
@@ -1624,7 +1618,7 @@ theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β :
|
||||
theorem ediv_emod (x y : Int) : -1 * x + y * (x / y) + x % y = 0 := by
|
||||
rw [Int.add_assoc, Int.ediv_add_emod x y, Int.add_comm]
|
||||
simp
|
||||
rw [← Int.sub_eq_add_neg, Int.sub_self]
|
||||
rw [Int.add_neg_eq_sub, Int.sub_self]
|
||||
|
||||
theorem emod_nonneg (x y : Int) : y != 0 → -1 * (x % y) ≤ 0 := by
|
||||
simp; intro h
|
||||
@@ -1664,6 +1658,29 @@ theorem emod_le (x y : Int) (n : Int) : emod_le_cert y n → x % y + n ≤ 0 :=
|
||||
theorem natCast_nonneg (x : Nat) : (-1:Int) * NatCast.natCast x ≤ 0 := by
|
||||
simp
|
||||
|
||||
theorem natCast_sub (x y : Nat)
|
||||
: (NatCast.natCast (x - y) : Int)
|
||||
=
|
||||
if (NatCast.natCast y : Int) + (-1)*NatCast.natCast x ≤ 0 then
|
||||
(NatCast.natCast x : Int) + -1*NatCast.natCast y
|
||||
else
|
||||
(0 : Int) := by
|
||||
show (↑(x - y) : Int) = if (↑y : Int) + (-1)*↑x ≤ 0 then ↑x + (-1)*↑y else 0
|
||||
rw [Int.neg_mul, ← Int.sub_eq_add_neg, Int.one_mul]
|
||||
rw [Int.neg_mul, ← Int.sub_eq_add_neg, Int.one_mul]
|
||||
split
|
||||
next h =>
|
||||
replace h := Int.le_of_sub_nonpos h
|
||||
rw [Int.ofNat_le] at h
|
||||
rw [Int.ofNat_sub h]
|
||||
next h =>
|
||||
have : ¬ (↑y : Int) ≤ ↑x := by
|
||||
intro h
|
||||
replace h := Int.sub_nonpos_of_le h
|
||||
contradiction
|
||||
rw [Int.ofNat_le] at this
|
||||
rw [Lean.Omega.Int.ofNat_sub_eq_zero this]
|
||||
|
||||
private theorem dvd_le_tight' {d p b₁ b₂ : Int} (hd : d > 0) (h₁ : d ∣ p + b₁) (h₂ : p + b₂ ≤ 0)
|
||||
: p + (b₁ - d*((b₁-b₂) / d)) ≤ 0 := by
|
||||
have ⟨k, h⟩ := h₁
|
||||
@@ -1675,7 +1692,7 @@ private theorem dvd_le_tight' {d p b₁ b₂ : Int} (hd : d > 0) (h₁ : d ∣ p
|
||||
rw [h₁] at h₂; assumption
|
||||
have : d*k ≤ b₁ - b₂ := by
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, Lean.Omega.Int.add_le_zero_iff_le_neg,
|
||||
Int.neg_add, Int.neg_neg, ← Int.sub_eq_add_neg] at h₂
|
||||
Int.neg_add, Int.neg_neg, Int.add_neg_eq_sub] at h₂
|
||||
assumption
|
||||
replace this : k ≤ (b₁ - b₂)/d := by
|
||||
rw [Int.mul_comm] at this; exact Int.le_ediv_of_mul_le hd this
|
||||
@@ -1743,6 +1760,42 @@ theorem dvd_neg_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
|
||||
intro h₁ h₂; rw [Int.add_comm] at h₁
|
||||
exact dvd_le_tight' hd h₂ h₁
|
||||
|
||||
theorem le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
|
||||
: norm_eq_cert lhs rhs p → lhs.denote ctx ≤ rhs.denote ctx → p.denote' ctx ≤ 0 := by
|
||||
intro h₁ h₂; rwa [norm_le ctx lhs rhs p h₁] at h₂
|
||||
|
||||
def not_le_norm_expr_cert (lhs rhs : Expr) (p : Poly) : Bool :=
|
||||
p == (((lhs.sub rhs).norm).mul (-1)).addConst 1
|
||||
|
||||
theorem not_le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
|
||||
: not_le_norm_expr_cert lhs rhs p → ¬ lhs.denote ctx ≤ rhs.denote ctx → p.denote' ctx ≤ 0 := by
|
||||
simp [not_le_norm_expr_cert]
|
||||
intro; subst p; simp
|
||||
intro h
|
||||
replace h := Int.sub_nonpos_of_le h
|
||||
rw [Int.add_comm, Int.add_sub_assoc] at h
|
||||
rw [Int.neg_sub]; assumption
|
||||
|
||||
theorem dvd_norm_expr (ctx : Context) (d : Int) (e : Expr) (p : Poly)
|
||||
: p == e.norm → d ∣ e.denote ctx → d ∣ p.denote' ctx := by
|
||||
simp; intro; subst p; simp
|
||||
|
||||
theorem eq_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
|
||||
: norm_eq_cert lhs rhs p → lhs.denote ctx = rhs.denote ctx → p.denote' ctx = 0 := by
|
||||
intro h₁ h₂; rwa [norm_eq ctx lhs rhs p h₁] at h₂
|
||||
|
||||
theorem not_eq_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
|
||||
: norm_eq_cert lhs rhs p → ¬ lhs.denote ctx = rhs.denote ctx → ¬ p.denote' ctx = 0 := by
|
||||
simp [norm_eq_cert]
|
||||
intro; subst p; simp
|
||||
intro; rwa [Int.sub_eq_zero]
|
||||
|
||||
theorem of_not_dvd (a b : Int) : a != 0 → ¬ (a ∣ b) → b % a > 0 := by
|
||||
simp; intro h₁ h₂
|
||||
replace h₂ := Int.emod_pos_of_not_dvd h₂
|
||||
simp [h₁] at h₂
|
||||
assumption
|
||||
|
||||
end Int.Linear
|
||||
|
||||
theorem Int.not_le_eq (a b : Int) : (¬a ≤ b) = (b + 1 ≤ a) := by
|
||||
|
||||
@@ -26,6 +26,7 @@ inductive Expr where
|
||||
| mul (a b : Expr)
|
||||
| div (a b : Expr)
|
||||
| mod (a b : Expr)
|
||||
deriving BEq
|
||||
|
||||
def Expr.denote (ctx : Context) : Expr → Nat
|
||||
| .num k => k
|
||||
@@ -43,9 +44,6 @@ def Expr.denoteAsInt (ctx : Context) : Expr → Int
|
||||
| .div a b => Int.ediv (denoteAsInt ctx a) (denoteAsInt ctx b)
|
||||
| .mod a b => Int.emod (denoteAsInt ctx a) (denoteAsInt ctx b)
|
||||
|
||||
@[local simp] private theorem fold_div (a b : Nat) : a.div b = a / b := rfl
|
||||
@[local simp] private theorem fold_mod (a b : Nat) : a.mod b = a % b := rfl
|
||||
|
||||
theorem Expr.denoteAsInt_eq (ctx : Context) (e : Expr) : e.denoteAsInt ctx = e.denote ctx := by
|
||||
induction e <;> simp [denote, denoteAsInt, Int.ofNat_ediv, *] <;> rfl
|
||||
|
||||
@@ -57,8 +55,36 @@ theorem Expr.le (ctx : Context) (lhs rhs : Expr)
|
||||
: (lhs.denote ctx ≤ rhs.denote ctx) = (lhs.denoteAsInt ctx ≤ rhs.denoteAsInt ctx) := by
|
||||
simp [denoteAsInt_eq, Int.ofNat_le]
|
||||
|
||||
theorem Expr.dvd (ctx : Context) (lhs rhs : Expr)
|
||||
: (lhs.denote ctx ∣ rhs.denote ctx) = (lhs.denoteAsInt ctx ∣ rhs.denoteAsInt ctx) := by
|
||||
simp [denoteAsInt_eq, Int.ofNat_dvd]
|
||||
theorem of_le (ctx : Context) (lhs rhs : Expr)
|
||||
: lhs.denote ctx ≤ rhs.denote ctx → lhs.denoteAsInt ctx ≤ rhs.denoteAsInt ctx := by
|
||||
rw [Expr.le ctx lhs rhs]; simp
|
||||
|
||||
theorem of_not_le (ctx : Context) (lhs rhs : Expr)
|
||||
: ¬ lhs.denote ctx ≤ rhs.denote ctx → ¬ lhs.denoteAsInt ctx ≤ rhs.denoteAsInt ctx := by
|
||||
rw [Expr.le ctx lhs rhs]; simp
|
||||
|
||||
theorem of_dvd (ctx : Context) (d : Nat) (e : Expr)
|
||||
: d ∣ e.denote ctx → Int.ofNat d ∣ e.denoteAsInt ctx := by
|
||||
simp [Expr.denoteAsInt_eq, Int.ofNat_dvd]
|
||||
|
||||
theorem of_eq (ctx : Context) (lhs rhs : Expr)
|
||||
: lhs.denote ctx = rhs.denote ctx → lhs.denoteAsInt ctx = rhs.denoteAsInt ctx := by
|
||||
rw [Expr.eq ctx lhs rhs]; simp
|
||||
|
||||
theorem of_not_eq (ctx : Context) (lhs rhs : Expr)
|
||||
: ¬ lhs.denote ctx = rhs.denote ctx → ¬ lhs.denoteAsInt ctx = rhs.denoteAsInt ctx := by
|
||||
rw [Expr.eq ctx lhs rhs]; simp
|
||||
|
||||
theorem ofNat_toNat (a : Int) : (NatCast.natCast a.toNat : Int) = if a ≤ 0 then 0 else a := by
|
||||
split
|
||||
next h =>
|
||||
rw [Int.toNat_of_nonpos h]; rfl
|
||||
next h =>
|
||||
simp at h
|
||||
have := Int.toNat_of_nonneg (Int.le_of_lt h)
|
||||
assumption
|
||||
|
||||
theorem Expr.denoteAsInt_nonneg (ctx : Context) (e : Expr) : e.denoteAsInt ctx ≥ 0 := by
|
||||
simp [Expr.denoteAsInt_eq]
|
||||
|
||||
end Int.OfNat
|
||||
|
||||
@@ -33,20 +33,18 @@ theorem lt_iff_add_one_le {a b : Int} : a < b ↔ a + 1 ≤ b := .rfl
|
||||
theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a ≤ b := by
|
||||
simp [le_def, h]; constructor
|
||||
|
||||
attribute [local simp] Int.add_left_neg Int.add_right_neg Int.neg_add
|
||||
|
||||
theorem le.intro {a b : Int} (n : Nat) (h : a + n = b) : a ≤ b :=
|
||||
le.intro_sub n <| by rw [← h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]
|
||||
le.intro_sub n <| by rw [← h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_right_neg]
|
||||
|
||||
theorem le.dest_sub {a b : Int} (h : a ≤ b) : ∃ n : Nat, b - a = n := nonneg_def.1 h
|
||||
|
||||
theorem le.dest {a b : Int} (h : a ≤ b) : ∃ n : Nat, a + n = b :=
|
||||
let ⟨n, h₁⟩ := le.dest_sub h
|
||||
⟨n, by rw [← h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]⟩
|
||||
⟨n, by rw [← h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_neg]⟩
|
||||
|
||||
protected theorem le_total (a b : Int) : a ≤ b ∨ b ≤ a :=
|
||||
(nonneg_or_nonneg_neg (b - a)).imp_right fun H => by
|
||||
rwa [show -(b - a) = a - b by simp [Int.add_comm, Int.sub_eq_add_neg]] at H
|
||||
rwa [show -(b - a) = a - b by simp [Int.neg_add,Int.add_comm, Int.sub_eq_add_neg]] at H
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_le {m n : Nat} : (↑m : Int) ≤ ↑n ↔ m ≤ n :=
|
||||
⟨fun h =>
|
||||
@@ -61,14 +59,14 @@ protected theorem le_total (a b : Int) : a ≤ b ∨ b ≤ a :=
|
||||
theorem eq_ofNat_of_zero_le {a : Int} (h : 0 ≤ a) : ∃ n : Nat, a = n := by
|
||||
have t := le.dest_sub h; rwa [Int.sub_zero] at t
|
||||
|
||||
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : ∃ n : Nat, a = n.succ :=
|
||||
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : ∃ n : Nat, a = n + 1 :=
|
||||
let ⟨n, (h : ↑(1 + n) = a)⟩ := le.dest h
|
||||
⟨n, by rw [Nat.add_comm] at h; exact h.symm⟩
|
||||
|
||||
theorem lt_add_succ (a : Int) (n : Nat) : a < a + Nat.succ n :=
|
||||
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]; rfl
|
||||
theorem lt_add_succ (a : Int) (n : Nat) : a < a + (n + 1) :=
|
||||
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]
|
||||
|
||||
theorem lt.intro {a b : Int} {n : Nat} (h : a + Nat.succ n = b) : a < b :=
|
||||
theorem lt.intro {a b : Int} {n : Nat} (h : a + (n + 1) = b) : a < b :=
|
||||
h ▸ lt_add_succ a n
|
||||
|
||||
theorem lt.dest {a b : Int} (h : a < b) : ∃ n : Nat, a + Nat.succ n = b :=
|
||||
@@ -117,7 +115,7 @@ protected theorem lt_iff_le_and_ne {a b : Int} : a < b ↔ a ≤ b ∧ a ≠ b :
|
||||
have : n ≠ 0 := aneb.imp fun eq => by rw [← hn, eq, ofNat_zero, Int.add_zero]
|
||||
apply lt.intro; rwa [← Nat.succ_pred_eq_of_pos (Nat.pos_of_ne_zero this)] at hn
|
||||
|
||||
theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
|
||||
protected theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
|
||||
|
||||
protected theorem zero_lt_one : (0 : Int) < 1 := ⟨_⟩
|
||||
|
||||
@@ -186,61 +184,19 @@ instance : Trans (α := Int) (· ≤ ·) (· < ·) (· < ·) := ⟨Int.lt_of_le_
|
||||
|
||||
instance : Trans (α := Int) (· < ·) (· < ·) (· < ·) := ⟨Int.lt_trans⟩
|
||||
|
||||
protected theorem min_def (n m : Int) : min n m = if n ≤ m then n else m := rfl
|
||||
|
||||
protected theorem max_def (n m : Int) : max n m = if n ≤ m then m else n := rfl
|
||||
|
||||
protected theorem min_comm (a b : Int) : min a b = min b a := by
|
||||
simp [Int.min_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₁ h₂
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) min := ⟨Int.min_comm⟩
|
||||
|
||||
protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def]; split <;> simp [*]
|
||||
|
||||
protected theorem min_le_left (a b : Int) : min a b ≤ a := Int.min_comm .. ▸ Int.min_le_right ..
|
||||
|
||||
protected theorem min_eq_left {a b : Int} (h : a ≤ b) : min a b = a := by simp [Int.min_def, h]
|
||||
|
||||
protected theorem min_eq_right {a b : Int} (h : b ≤ a) : min a b = b := by
|
||||
rw [Int.min_comm a b]; exact Int.min_eq_left h
|
||||
|
||||
protected theorem le_min {a b c : Int} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..)⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.min_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_comm (a b : Int) : max a b = max b a := by
|
||||
simp only [Int.max_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) max := ⟨Int.max_comm⟩
|
||||
|
||||
protected theorem le_max_left (a b : Int) : a ≤ max a b := by rw [Int.max_def]; split <;> simp [*]
|
||||
|
||||
protected theorem le_max_right (a b : Int) : b ≤ max a b := Int.max_comm .. ▸ Int.le_max_left ..
|
||||
|
||||
protected theorem max_le {a b c : Int} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.max_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_eq_right {a b : Int} (h : a ≤ b) : max a b = b := by
|
||||
simp [Int.max_def, h, Int.not_lt.2 h]
|
||||
|
||||
protected theorem max_eq_left {a b : Int} (h : b ≤ a) : max a b = a := by
|
||||
rw [← Int.max_comm b a]; exact Int.max_eq_right h
|
||||
|
||||
theorem eq_natAbs_of_zero_le {a : Int} (h : 0 ≤ a) : a = natAbs a := by
|
||||
theorem eq_natAbs_of_nonneg {a : Int} (h : 0 ≤ a) : a = natAbs a := by
|
||||
let ⟨n, e⟩ := eq_ofNat_of_zero_le h
|
||||
rw [e]; rfl
|
||||
|
||||
@[deprecated eq_natAbs_of_nonneg (since := "2025-03-11")]
|
||||
abbrev eq_natAbs_of_zero_le := @eq_natAbs_of_nonneg
|
||||
|
||||
theorem le_natAbs {a : Int} : a ≤ natAbs a :=
|
||||
match Int.le_total 0 a with
|
||||
| .inl h => by rw [eq_natAbs_of_zero_le h]; apply Int.le_refl
|
||||
| .inl h => by rw [eq_natAbs_of_nonneg h]; apply Int.le_refl
|
||||
| .inr h => Int.le_trans h (ofNat_zero_le _)
|
||||
|
||||
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
|
||||
@[simp] 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 :=
|
||||
@@ -249,18 +205,6 @@ theorem negSucc_le_zero (n : Nat) : -[n+1] ≤ 0 :=
|
||||
@[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]
|
||||
|
||||
@@ -282,10 +226,10 @@ protected theorem le_of_add_le_add_left {a b c : Int} (h : a + b ≤ a + c) : b
|
||||
protected theorem le_of_add_le_add_right {a b c : Int} (h : a + b ≤ c + b) : a ≤ c :=
|
||||
Int.le_of_add_le_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
|
||||
|
||||
protected theorem add_le_add_iff_left (a : Int) : a + b ≤ a + c ↔ b ≤ c :=
|
||||
@[simp] protected theorem add_le_add_iff_left (a : Int) : a + b ≤ a + c ↔ b ≤ c :=
|
||||
⟨Int.le_of_add_le_add_left, (Int.add_le_add_left · _)⟩
|
||||
|
||||
protected theorem add_le_add_iff_right (c : Int) : a + c ≤ b + c ↔ a ≤ b :=
|
||||
@[simp] protected theorem add_le_add_iff_right (c : Int) : a + c ≤ b + c ↔ a ≤ b :=
|
||||
⟨Int.le_of_add_le_add_right, (Int.add_le_add_right · _)⟩
|
||||
|
||||
protected theorem add_le_add {a b c d : Int} (h₁ : a ≤ b) (h₂ : c ≤ d) : a + c ≤ b + d :=
|
||||
@@ -304,6 +248,15 @@ protected theorem neg_le_neg {a b : Int} (h : a ≤ b) : -b ≤ -a := by
|
||||
have : 0 + -b ≤ -a + b + -b := Int.add_le_add_right this (-b)
|
||||
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
|
||||
|
||||
@[simp] protected theorem neg_le_neg_iff {a b : Int} : -a ≤ -b ↔ b ≤ a :=
|
||||
⟨fun h => by simpa using Int.neg_le_neg h, Int.neg_le_neg⟩
|
||||
|
||||
@[simp] protected theorem neg_le_zero_iff {a : Int} : -a ≤ 0 ↔ 0 ≤ a := by
|
||||
rw [← Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
|
||||
|
||||
@[simp] protected theorem zero_le_neg_iff {a : Int} : 0 ≤ -a ↔ a ≤ 0 := by
|
||||
rw [← Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
|
||||
|
||||
protected theorem le_of_neg_le_neg {a b : Int} (h : -b ≤ -a) : a ≤ b :=
|
||||
suffices - -a ≤ - -b by simp [Int.neg_neg] at this; assumption
|
||||
Int.neg_le_neg h
|
||||
@@ -321,6 +274,15 @@ protected theorem neg_lt_neg {a b : Int} (h : a < b) : -b < -a := by
|
||||
have : 0 + -b < -a + b + -b := Int.add_lt_add_right this (-b)
|
||||
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
|
||||
|
||||
@[simp] protected theorem neg_lt_neg_iff {a b : Int} : -a < -b ↔ b < a :=
|
||||
⟨fun h => by simpa using Int.neg_lt_neg h, Int.neg_lt_neg⟩
|
||||
|
||||
@[simp] protected theorem neg_lt_zero_iff {a : Int} : -a < 0 ↔ 0 < a := by
|
||||
rw [← Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
|
||||
|
||||
@[simp] protected theorem zero_lt_neg_iff {a : Int} : 0 < -a ↔ a < 0 := by
|
||||
rw [← Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
|
||||
|
||||
protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 := by
|
||||
have : -a < -0 := Int.neg_lt_neg h
|
||||
rwa [Int.neg_zero] at this
|
||||
@@ -365,8 +327,120 @@ protected theorem le_iff_lt_add_one {a b : Int} : a ≤ b ↔ a < b + 1 := by
|
||||
rw [Int.lt_iff_add_one_le]
|
||||
exact (Int.add_le_add_iff_right 1).symm
|
||||
|
||||
/- ### Order properties and multiplication -/
|
||||
/- ### min and max -/
|
||||
|
||||
protected theorem min_def (n m : Int) : min n m = if n ≤ m then n else m := rfl
|
||||
|
||||
protected theorem max_def (n m : Int) : max n m = if n ≤ m then m else n := rfl
|
||||
|
||||
@[simp] protected theorem neg_min_neg (a b : Int) : min (-a) (-b) = -max a b := by
|
||||
rw [Int.min_def, Int.max_def]
|
||||
simp
|
||||
split <;> rename_i h₁ <;> split <;> rename_i h₂
|
||||
· simpa using Int.le_antisymm h₂ h₁
|
||||
· simp
|
||||
· simp
|
||||
· simp only [Int.not_le] at h₁ h₂
|
||||
exfalso
|
||||
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
|
||||
|
||||
@[simp] protected theorem min_add_right (a b c : Int) : min (a + c) (b + c) = min a b + c := by
|
||||
rw [Int.min_def, Int.min_def]
|
||||
simp only [Int.add_le_add_iff_right]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem min_add_left (a b c : Int) : min (a + b) (a + c) = a + min b c := by
|
||||
rw [Int.min_def, Int.min_def]
|
||||
simp only [Int.add_le_add_iff_left]
|
||||
split <;> simp
|
||||
|
||||
protected theorem min_comm (a b : Int) : min a b = min b a := by
|
||||
simp [Int.min_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₁ h₂
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) min := ⟨Int.min_comm⟩
|
||||
|
||||
protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def]; split <;> simp [*]
|
||||
|
||||
protected theorem min_le_left (a b : Int) : min a b ≤ a := Int.min_comm .. ▸ Int.min_le_right ..
|
||||
|
||||
protected theorem min_eq_left {a b : Int} (h : a ≤ b) : min a b = a := by simp [Int.min_def, h]
|
||||
|
||||
protected theorem min_eq_right {a b : Int} (h : b ≤ a) : min a b = b := by
|
||||
rw [Int.min_comm a b]; exact Int.min_eq_left h
|
||||
|
||||
protected theorem le_min {a b c : Int} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..)⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.min_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem lt_min {a b c : Int} : a < min b c ↔ a < b ∧ a < c := Int.le_min
|
||||
|
||||
@[simp] protected theorem neg_max_neg (a b : Int) : max (-a) (-b) = -min a b := by
|
||||
rw [Int.min_def, Int.max_def]
|
||||
simp
|
||||
split <;> rename_i h₁ <;> split <;> rename_i h₂
|
||||
· simpa using Int.le_antisymm h₁ h₂
|
||||
· simp
|
||||
· simp
|
||||
· simp only [Int.not_le] at h₁ h₂
|
||||
exfalso
|
||||
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
|
||||
|
||||
@[simp] protected theorem max_add_right (a b c : Int) : max (a + c) (b + c) = max a b + c := by
|
||||
rw [Int.max_def, Int.max_def]
|
||||
simp only [Int.add_le_add_iff_right]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem max_add_left (a b c : Int) : max (a + b) (a + c) = a + max b c := by
|
||||
rw [Int.max_def, Int.max_def]
|
||||
simp only [Int.add_le_add_iff_left]
|
||||
split <;> simp
|
||||
|
||||
protected theorem max_comm (a b : Int) : max a b = max b a := by
|
||||
simp only [Int.max_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) max := ⟨Int.max_comm⟩
|
||||
|
||||
protected theorem le_max_left (a b : Int) : a ≤ max a b := by rw [Int.max_def]; split <;> simp [*]
|
||||
|
||||
protected theorem le_max_right (a b : Int) : b ≤ max a b := Int.max_comm .. ▸ Int.le_max_left ..
|
||||
|
||||
protected theorem max_eq_right {a b : Int} (h : a ≤ b) : max a b = b := by
|
||||
simp [Int.max_def, h, Int.not_lt.2 h]
|
||||
|
||||
protected theorem max_eq_left {a b : Int} (h : b ≤ a) : max a b = a := by
|
||||
rw [← Int.max_comm b a]; exact Int.max_eq_right h
|
||||
|
||||
protected theorem max_le {a b c : Int} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.max_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_lt {a b c : Int} : max a b < c ↔ a < c ∧ b < c := by
|
||||
simp only [Int.lt_iff_add_one_le]
|
||||
simpa using Int.max_le (a := a + 1) (b := b + 1) (c := c)
|
||||
|
||||
@[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 _)]
|
||||
|
||||
@[simp] protected theorem min_self (a : Int) : min a a = a := Int.min_eq_left (Int.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Int) min := ⟨Int.min_self⟩
|
||||
|
||||
@[simp] protected theorem max_self (a : Int) : max a a = a := Int.max_eq_right (Int.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Int) max := ⟨Int.max_self⟩
|
||||
|
||||
/- ### Order properties and multiplication -/
|
||||
|
||||
protected theorem mul_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a * b := by
|
||||
let ⟨n, hn⟩ := eq_ofNat_of_zero_le ha
|
||||
@@ -376,7 +450,8 @@ protected theorem mul_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a
|
||||
protected theorem mul_pos {a b : Int} (ha : 0 < a) (hb : 0 < b) : 0 < a * b := by
|
||||
let ⟨n, hn⟩ := eq_succ_of_zero_lt ha
|
||||
let ⟨m, hm⟩ := eq_succ_of_zero_lt hb
|
||||
rw [hn, hm, ← ofNat_mul]; apply ofNat_succ_pos
|
||||
rw [hn, hm]
|
||||
apply ofNat_succ_pos
|
||||
|
||||
protected theorem mul_lt_mul_of_pos_left {a b c : Int}
|
||||
(h₁ : a < b) (h₂ : 0 < c) : c * a < c * b := by
|
||||
@@ -506,8 +581,8 @@ theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
|
||||
|
||||
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
|
||||
match a with
|
||||
| Int.ofNat n => simp
|
||||
| Int.negSucc n => simp
|
||||
| (n : Nat) => simp
|
||||
| -(n + 1 : Nat) => norm_cast
|
||||
|
||||
theorem self_le_toNat (a : Int) : a ≤ toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
|
||||
|
||||
@@ -543,12 +618,15 @@ theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
|
||||
| 0 => rfl
|
||||
| _+1 => rfl
|
||||
|
||||
/-! ### toNat' -/
|
||||
/-! ### toNat? -/
|
||||
|
||||
theorem mem_toNat' : ∀ {a : Int} {n : Nat}, toNat' a = some n ↔ a = n
|
||||
| (m : Nat), n => by simp [toNat', Int.ofNat_inj]
|
||||
theorem mem_toNat? : ∀ {a : Int} {n : Nat}, toNat? a = some n ↔ a = n
|
||||
| (m : Nat), n => by simp [toNat?, Int.ofNat_inj]
|
||||
| -[m+1], n => by constructor <;> nofun
|
||||
|
||||
@[deprecated mem_toNat? (since := "2025-03-11")]
|
||||
abbrev mem_toNat' := @mem_toNat?
|
||||
|
||||
/-! ## Order properties of the integers -/
|
||||
|
||||
protected theorem le_of_not_le {a b : Int} : ¬ a ≤ b → b ≤ a := (Int.le_total a b).resolve_left
|
||||
@@ -568,10 +646,10 @@ protected theorem lt_of_add_lt_add_left {a b c : Int} (h : a + b < a + c) : b <
|
||||
protected theorem lt_of_add_lt_add_right {a b c : Int} (h : a + b < c + b) : a < c :=
|
||||
Int.lt_of_add_lt_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
|
||||
|
||||
protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c ↔ b < c :=
|
||||
@[simp] protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c ↔ b < c :=
|
||||
⟨Int.lt_of_add_lt_add_left, (Int.add_lt_add_left · _)⟩
|
||||
|
||||
protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c ↔ a < b :=
|
||||
@[simp] protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c ↔ a < b :=
|
||||
⟨Int.lt_of_add_lt_add_right, (Int.add_lt_add_right · _)⟩
|
||||
|
||||
protected theorem add_lt_add {a b c d : Int} (h₁ : a < b) (h₂ : c < d) : a + c < b + d :=
|
||||
@@ -766,6 +844,18 @@ protected theorem sub_le_sub_right {a b : Int} (h : a ≤ b) (c : Int) : a - c
|
||||
protected theorem sub_le_sub {a b c d : Int} (hab : a ≤ b) (hcd : c ≤ d) : a - d ≤ b - c :=
|
||||
Int.add_le_add hab (Int.neg_le_neg hcd)
|
||||
|
||||
protected theorem le_of_sub_le_sub_left {a b c : Int} (h : c - a ≤ c - b) : b ≤ a :=
|
||||
Int.le_of_neg_le_neg <| Int.le_of_add_le_add_left h
|
||||
|
||||
protected theorem le_of_sub_le_sub_right {a b c : Int} (h : a - c ≤ b - c) : a ≤ b :=
|
||||
Int.le_of_add_le_add_right h
|
||||
|
||||
@[simp] protected theorem sub_le_sub_left_iff {a b c : Int} : c - a ≤ c - b ↔ b ≤ a :=
|
||||
⟨Int.le_of_sub_le_sub_left, (Int.sub_le_sub_left · c)⟩
|
||||
|
||||
@[simp] protected theorem sub_le_sub_right_iff {a b c : Int} : a - c ≤ b - c ↔ a ≤ b :=
|
||||
⟨Int.le_of_sub_le_sub_right, (Int.sub_le_sub_right · c)⟩
|
||||
|
||||
protected theorem add_lt_of_lt_neg_add {a b c : Int} (h : b < -a + c) : a + b < c := by
|
||||
have h := Int.add_lt_add_left h a
|
||||
rwa [Int.add_neg_cancel_left] at h
|
||||
@@ -874,11 +964,11 @@ protected theorem lt_of_sub_lt_sub_right {a b c : Int} (h : a - c < b - c) : a <
|
||||
⟨Int.lt_of_sub_lt_sub_right, (Int.sub_lt_sub_right · c)⟩
|
||||
|
||||
protected theorem sub_lt_sub_of_le_of_lt {a b c d : Int}
|
||||
(hab : a ≤ b) (hcd : c < d) : a - d < b - c :=
|
||||
(hab : a ≤ b) (hcd : c < d) : a - d < b - c :=
|
||||
Int.add_lt_add_of_le_of_lt hab (Int.neg_lt_neg hcd)
|
||||
|
||||
protected theorem sub_lt_sub_of_lt_of_le {a b c d : Int}
|
||||
(hab : a < b) (hcd : c ≤ d) : a - d < b - c :=
|
||||
(hab : a < b) (hcd : c ≤ d) : a - d < b - c :=
|
||||
Int.add_lt_add_of_lt_of_le hab (Int.neg_le_neg hcd)
|
||||
|
||||
protected theorem add_le_add_three {a b c d e f : Int}
|
||||
@@ -1018,10 +1108,10 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
|
||||
| 0, h => nomatch h
|
||||
| -[_+1], _ => negSucc_lt_zero _
|
||||
|
||||
theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 ↔ 0 < a :=
|
||||
@[simp] theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 ↔ 0 < a :=
|
||||
⟨pos_of_sign_eq_one, sign_eq_one_of_pos⟩
|
||||
|
||||
theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
@[simp] theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
⟨neg_of_sign_eq_neg_one, sign_eq_neg_one_of_neg⟩
|
||||
|
||||
@[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 ↔ a = 0 :=
|
||||
@@ -1033,7 +1123,7 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
| .ofNat (_ + 1) => rfl
|
||||
| .negSucc _ => rfl
|
||||
|
||||
@[simp] theorem sign_nonneg : 0 ≤ sign x ↔ 0 ≤ x := by
|
||||
@[simp] theorem sign_nonneg_iff : 0 ≤ sign x ↔ 0 ≤ x := by
|
||||
match x with
|
||||
| 0 => rfl
|
||||
| .ofNat (_ + 1) =>
|
||||
@@ -1041,6 +1131,26 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
exact Int.le_add_one (ofNat_nonneg _)
|
||||
| .negSucc _ => simp +decide [sign]
|
||||
|
||||
@[deprecated sign_nonneg_iff (since := "2025-03-11")] abbrev sign_nonneg := @sign_nonneg_iff
|
||||
|
||||
@[simp] theorem sign_pos_iff : 0 < sign x ↔ 0 < x := by
|
||||
match x with
|
||||
| 0
|
||||
| .ofNat (_ + 1) => simp
|
||||
| .negSucc x => simp
|
||||
|
||||
@[simp] theorem sign_nonpos_iff : sign x ≤ 0 ↔ x ≤ 0 := by
|
||||
match x with
|
||||
| 0 => rfl
|
||||
| .ofNat (_ + 1) => simp
|
||||
| .negSucc _ => simpa using negSucc_le_zero _
|
||||
|
||||
@[simp] theorem sign_neg_iff : sign x < 0 ↔ x < 0 := by
|
||||
match x with
|
||||
| 0 => simp
|
||||
| .ofNat (_ + 1) => simpa using le.intro_sub _ rfl
|
||||
| .negSucc _ => simp
|
||||
|
||||
@[simp] theorem mul_sign_self : ∀ i : Int, i * sign i = natAbs i
|
||||
| succ _ => Int.mul_one _
|
||||
| 0 => Int.mul_zero _
|
||||
@@ -1065,7 +1175,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
theorem eq_nat_or_neg (a : Int) : ∃ n : Nat, a = n ∨ a = -↑n := ⟨_, natAbs_eq a⟩
|
||||
protected 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]
|
||||
@@ -1079,7 +1189,7 @@ theorem natAbs_eq_iff {a : Int} {n : Nat} : a.natAbs = n ↔ a = n ∨ a = -↑n
|
||||
theorem natAbs_add_le (a b : Int) : natAbs (a + b) ≤ natAbs a + natAbs b := by
|
||||
suffices ∀ a b : Nat, natAbs (subNatNat a b.succ) ≤ (a + b).succ by
|
||||
match a, b with
|
||||
| (a:Nat), (b:Nat) => rw [ofNat_add_ofNat, natAbs_ofNat]; apply Nat.le_refl
|
||||
| (a:Nat), (b:Nat) => rw [← ofNat_add, natAbs_ofNat]; apply Nat.le_refl
|
||||
| (a:Nat), -[b+1] => rw [natAbs_ofNat, natAbs_negSucc]; apply this
|
||||
| -[a+1], (b:Nat) =>
|
||||
rw [natAbs_negSucc, natAbs_ofNat, Nat.succ_add, Nat.add_comm a b]; apply this
|
||||
@@ -1098,6 +1208,7 @@ theorem natAbs_add_le (a b : Int) : natAbs (a + b) ≤ natAbs a + natAbs b := by
|
||||
theorem natAbs_sub_le (a b : Int) : natAbs (a - b) ≤ natAbs a + natAbs b := by
|
||||
rw [← Int.natAbs_neg b]; apply natAbs_add_le
|
||||
|
||||
@[deprecated negSucc_eq (since := "2025-03-11")]
|
||||
theorem negSucc_eq' (m : Nat) : -[m+1] = -m - 1 := by simp only [negSucc_eq, Int.neg_add]; rfl
|
||||
|
||||
theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
|
||||
@@ -1105,7 +1216,10 @@ theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
|
||||
match a, b, eq_ofNat_of_zero_le w₁, eq_ofNat_of_zero_le (Int.le_trans w₁ (Int.le_of_lt w₂)) with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_lt.1 w₂
|
||||
|
||||
theorem eq_natAbs_iff_mul_eq_zero : natAbs a = n ↔ (a - n) * (a + n) = 0 := by
|
||||
theorem natAbs_eq_iff_mul_eq_zero : natAbs a = n ↔ (a - n) * (a + n) = 0 := by
|
||||
rw [natAbs_eq_iff, Int.mul_eq_zero, ← Int.sub_neg, Int.sub_eq_zero, Int.sub_eq_zero]
|
||||
|
||||
@[deprecated natAbs_eq_iff_mul_eq_zero (since := "2025-03-11")]
|
||||
abbrev eq_natAbs_iff_mul_eq_zero := @natAbs_eq_iff_mul_eq_zero
|
||||
|
||||
end Int
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Int
|
||||
|
||||
/-! # pow -/
|
||||
|
||||
protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
|
||||
@[simp] protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
|
||||
|
||||
protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
|
||||
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
|
||||
@@ -27,11 +27,11 @@ abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
|
||||
abbrev pos_pow_of_pos := @Nat.pow_pos
|
||||
|
||||
@[norm_cast]
|
||||
theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
|
||||
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
|
||||
match n with
|
||||
| 0 => rfl
|
||||
| n + 1 =>
|
||||
simp only [Nat.pow_succ, Int.pow_succ, natCast_mul, natCast_pow _ n]
|
||||
simp only [Nat.pow_succ, Int.pow_succ, Int.natCast_mul, Int.natCast_pow _ n]
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) :
|
||||
|
||||
@@ -13,29 +13,44 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace List
|
||||
|
||||
/-- `O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
|
||||
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
|
||||
but is defined only when all members of `l` satisfy `P`, using the proof
|
||||
to apply `f`. -/
|
||||
/--
|
||||
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over
|
||||
a list `l : List α`, given a proof that every element of `l` in fact satisfies `P`.
|
||||
|
||||
`O(|l|)`. `List.pmap`, named for “partial map,” is the equivalent of `List.map` for such partial
|
||||
functions.
|
||||
-/
|
||||
def pmap {P : α → Prop} (f : ∀ a, P a → β) : ∀ l : List α, (H : ∀ a ∈ l, P a) → List β
|
||||
| [], _ => []
|
||||
| a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2
|
||||
|
||||
/--
|
||||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||||
Unsafe implementation of `attachWith` that takes advantage of the fact that the representation of
|
||||
`List {x // P x}` is the same as the input `List α`.
|
||||
(Someday, the compiler might do this optimization automatically, but until then...)
|
||||
-/
|
||||
@[inline] private unsafe def attachWithImpl
|
||||
(l : List α) (P : α → Prop) (_ : ∀ x ∈ l, P x) : List {x // P x} := unsafeCast l
|
||||
|
||||
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `l` to produce a new list
|
||||
with the same elements but in the type `{x // P x}`. -/
|
||||
/--
|
||||
“Attaches” individual proofs to a list of values that satisfy a predicate `P`, returning a list of
|
||||
elements in the corresponding subtype `{ x // P x }`.
|
||||
|
||||
`O(1)`.
|
||||
-/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(l : List α) (P : α → Prop) (H : ∀ x ∈ l, P x) : List {x // P x} := pmap Subtype.mk l H
|
||||
|
||||
/-- `O(1)`. "Attach" the proof that the elements of `l` are in `l` to produce a new list
|
||||
with the same elements but in the type `{x // x ∈ l}`. -/
|
||||
/--
|
||||
“Attaches” the proof that the elements of `l` are in fact elements of `l`, producing a new list with
|
||||
the same elements but in the subtype `{ x // x ∈ l }`.
|
||||
|
||||
`O(1)`.
|
||||
|
||||
This function is primarily used to allow definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
|
||||
`List.map`) to prove that an value taken from a list is smaller than the list. This allows the
|
||||
well-founded recursion mechanism to prove that the function terminates.
|
||||
-/
|
||||
@[inline] def attach (l : List α) : List {x // x ∈ l} := attachWith l _ fun _ => id
|
||||
|
||||
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
|
||||
@@ -650,11 +665,19 @@ Further, we provide simp lemmas that push `unattach` inwards.
|
||||
-/
|
||||
|
||||
/--
|
||||
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
|
||||
It is introduced as an intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
Maps a list of terms in a subtype to the corresponding terms in the type by forgetting that they
|
||||
satisfy the predicate.
|
||||
|
||||
If not, usually the right approach is `simp [List.unattach, -List.map_subtype]` to unfold.
|
||||
This is the inverse of `List.attachWith` and a synonym for `l.map (·.val)`.
|
||||
|
||||
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
|
||||
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
This function is usually inserted automatically by Lean as an intermediate step while proving
|
||||
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
|
||||
the elaboration of definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
|
||||
state, the right approach is usually the tactic `simp [List.unattach, -List.map_subtype]`.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α := l.map (·.val)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -79,10 +79,16 @@ theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a
|
||||
/-! ### getD -/
|
||||
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
Returns the element at the provided index, counting from `0`. Returns `fallback` if the index is out
|
||||
of bounds.
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
|
||||
See also `get?` and `get!`.
|
||||
To return an `Option` depending on whether the index is in bounds, use `as[i]?`. To panic if the
|
||||
index is out of bounds, use `as[i]!`.
|
||||
|
||||
Examples:
|
||||
* `["spring", "summer", "fall", "winter"].getD 2 "never" = "fall"`
|
||||
* `["spring", "summer", "fall", "winter"].getD 0 "never" = "spring"`
|
||||
* `["spring", "summer", "fall", "winter"].getD 4 "never" = "never"`
|
||||
-/
|
||||
def getD (as : List α) (i : Nat) (fallback : α) : α :=
|
||||
as[i]?.getD fallback
|
||||
@@ -92,10 +98,16 @@ def getD (as : List α) (i : Nat) (fallback : α) : α :=
|
||||
/-! ### getLast! -/
|
||||
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
Returns the last element in the list. Panics and returns `default` if the list is empty.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `getLast` and `getLastD` for safer alternatives.
|
||||
Safer alternatives include:
|
||||
* `getLast?`, which returns an `Option`,
|
||||
* `getLastD`, which takes a fallback value for empty lists, and
|
||||
* `getLast`, which requires a proof that the list is non-empty.
|
||||
|
||||
Examples:
|
||||
* `["circle", "rectangle"].getLast! = "rectangle"`
|
||||
* `["circle"].getLast! = "circle"`
|
||||
-/
|
||||
def getLast! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
@@ -106,10 +118,12 @@ def getLast! [Inhabited α] : List α → α
|
||||
/-! ### head! -/
|
||||
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
Returns the first element in the list. If the list is empty, panics and returns `default`.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `head` and `headD` for safer alternatives.
|
||||
Safer alternatives include:
|
||||
* `List.head`, which requires a proof that the list is non-empty,
|
||||
* `List.head?`, which returns an `Option`, and
|
||||
* `List.headD`, which returns an explicitly-provided fallback value on empty lists.
|
||||
-/
|
||||
def head! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
@@ -118,10 +132,17 @@ def head! [Inhabited α] : List α → α
|
||||
/-! ### tail! -/
|
||||
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
Drops the first element of a nonempty list, returning the tail. If the list is empty, this function
|
||||
panics when executed and returns the empty list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns the empty list.
|
||||
See `tail` and `tailD` for safer alternatives.
|
||||
Safer alternatives include
|
||||
* `tail`, which returns the empty list without panicking,
|
||||
* `tail?`, which returns an `Option`, and
|
||||
* `tailD`, which returns a fallback value when passed the empty list.
|
||||
|
||||
Examples:
|
||||
* `["apple", "banana", "grape"].tail! = ["banana", "grape"]`
|
||||
* `["banana", "grape"].tail! = ["grape"]`
|
||||
-/
|
||||
def tail! : List α → List α
|
||||
| [] => panic! "empty list"
|
||||
@@ -132,17 +153,30 @@ def tail! : List α → List α
|
||||
/-! ### partitionM -/
|
||||
|
||||
/--
|
||||
Monadic generalization of `List.partition`.
|
||||
Returns a pair of lists that together contain all the elements of `as`. The first list contains
|
||||
those elements for which the monadic predicate `p` returns `true`, and the second contains those for
|
||||
which `p` returns `false`. The list's elements are examined in order, from left to right.
|
||||
|
||||
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic` or `Init.Data.List.Control`.
|
||||
```
|
||||
This is a monadic version of `List.partition`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
def posOrNeg (x : Int) : Except String Bool :=
|
||||
if x > 0 then pure true
|
||||
else if x < 0 then pure false
|
||||
else throw "Zero is not positive or negative"
|
||||
|
||||
partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
|
||||
partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
|
||||
```
|
||||
```lean example
|
||||
#eval [-1, 2, 3].partitionM posOrNeg
|
||||
```
|
||||
```output
|
||||
Except.ok ([2, 3], [-1])
|
||||
```
|
||||
```lean example
|
||||
#eval [0, 2, 3].partitionM posOrNeg
|
||||
```
|
||||
```output
|
||||
Except.error "Zero is not positive or negative"
|
||||
```
|
||||
-/
|
||||
@[inline] def partitionM [Monad m] (p : α → m Bool) (l : List α) : m (List α × List α) :=
|
||||
@@ -162,12 +196,12 @@ where
|
||||
/-! ### partitionMap -/
|
||||
|
||||
/--
|
||||
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
|
||||
whilst partitioning the result into a pair of lists, `List β × List γ`,
|
||||
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
|
||||
```
|
||||
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
|
||||
```
|
||||
Applies a function that returns a disjoint union to each element of a list, collecting the `Sum.inl`
|
||||
and `Sum.inr` results into separate lists.
|
||||
|
||||
Examples:
|
||||
* `[0, 1, 2, 3].partitionMap (fun x => if x % 2 = 0 then .inl x else .inr x) = ([0, 2], [1, 3])`
|
||||
* `[0, 1, 2, 3].partitionMap (fun x => if x = 0 then .inl x else .inr x) = ([0], [1, 2, 3])`
|
||||
-/
|
||||
@[inline] def partitionMap (f : α → β ⊕ γ) (l : List α) : List β × List γ := go l #[] #[] where
|
||||
/-- Auxiliary for `partitionMap`:
|
||||
@@ -199,14 +233,24 @@ For verification purposes, `List.mapMono = List.map`.
|
||||
return b' :: bs'
|
||||
|
||||
/--
|
||||
Monomorphic `List.mapM`. The internal implementation uses pointer equality, and does not allocate a new list
|
||||
if the result of each `f a` is a pointer equal value `a`.
|
||||
Applies a monadic function to each element of a list, returning the list of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new list if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
-/
|
||||
@[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α → m α) : m (List α) :=
|
||||
match as with
|
||||
| [] => return []
|
||||
| a :: as => return (← f a) :: (← mapMonoM as f)
|
||||
|
||||
/--
|
||||
Applies a function to each element of a list, returning the list of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new list if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
|
||||
For verification purposes, `List.mapMono = List.map`.
|
||||
-/
|
||||
def mapMono (as : List α) (f : α → α) : List α :=
|
||||
Id.run <| as.mapMonoM f
|
||||
|
||||
|
||||
@@ -46,11 +46,12 @@ Users that want to use `mapM` with `Applicative` should use `mapA` instead.
|
||||
-/
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
|
||||
Applies the monadic action `f` to every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
See `List.forM` for the variant that discards the results.
|
||||
See `List.mapA` for the variant that works with `Applicative`.
|
||||
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
|
||||
more convenient to reason about. `List.forM` is the variant that discards the results and
|
||||
`List.mapA` is the variant that works with `Applicative`.
|
||||
-/
|
||||
@[inline]
|
||||
def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m β) (as : List α) : m (List β) :=
|
||||
@@ -60,15 +61,15 @@ def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α
|
||||
loop as []
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list
|
||||
of results.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `mapM` can be more efficient.
|
||||
If `m` is also a `Monad`, then using `mapM` can be more efficient.
|
||||
|
||||
See `List.forA` for the variant that discards the results.
|
||||
See `List.mapM` for the variant that works with `Monad`.
|
||||
See `List.forA` for the variant that discards the results. See `List.mapM` for the variant that
|
||||
works with `Monad`.
|
||||
|
||||
**Warning**: this function is not tail-recursive, meaning that it may fail with a stack overflow on long lists.
|
||||
This function is not tail-recursive, so it may fail with a stack overflow on long lists.
|
||||
-/
|
||||
@[specialize]
|
||||
def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m (List β)
|
||||
@@ -76,10 +77,10 @@ def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f
|
||||
| a::as => List.cons <$> f a <*> mapA f as
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right.
|
||||
Applies the monadic action `f` to every element in the list, in order.
|
||||
|
||||
See `List.mapM` for the variant that collects results.
|
||||
See `List.forA` for the variant that works with `Applicative`.
|
||||
`List.mapM` is a variant that collects results. `List.forA` is a variant that works on any
|
||||
`Applicative`.
|
||||
-/
|
||||
@[specialize]
|
||||
protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
@@ -88,12 +89,11 @@ protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α
|
||||
| a :: as => do f a; List.forM as f
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right.
|
||||
Applies the applicative action `f` to every element in the list, in order.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `forM` can be more efficient.
|
||||
If `m` is also a `Monad`, then using `List.forM` can be more efficient.
|
||||
|
||||
See `List.mapA` for the variant that collects results.
|
||||
See `List.forM` for the variant that works with `Monad`.
|
||||
`List.mapA` is a variant that collects results.
|
||||
-/
|
||||
@[specialize]
|
||||
def forA {m : Type u → Type v} [Applicative m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
@@ -110,8 +110,28 @@ def filterAuxM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) :
|
||||
filterAuxM f t (cond b (h :: acc) acc)
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, left-to-right, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
Applies the monadic predicate `p` to every element in the list, in order from left to right, and
|
||||
returns the list of elements for which `p` returns `true`.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterM fun x => do
|
||||
IO.println s!"Checking {x}"
|
||||
return x < 3
|
||||
```
|
||||
```output
|
||||
Checking 1
|
||||
Checking 2
|
||||
Checking 5
|
||||
Checking 2
|
||||
Checking 7
|
||||
Checking 7
|
||||
```
|
||||
```output
|
||||
[1, 2, 2]
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) := do
|
||||
@@ -119,16 +139,56 @@ def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as
|
||||
pure as.reverse
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, right-to-left, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
Applies the monadic predicate `p` on every element in the list in reverse order, from right to left,
|
||||
and returns those elements for which `p` returns `true`. The elements of the returned list are in
|
||||
the same order as in the input list.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterRevM fun x => do
|
||||
IO.println s!"Checking {x}"
|
||||
return x < 3
|
||||
```
|
||||
```output
|
||||
Checking 7
|
||||
Checking 7
|
||||
Checking 2
|
||||
Checking 5
|
||||
Checking 2
|
||||
Checking 1
|
||||
```
|
||||
```output
|
||||
[1, 2, 2]
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def filterRevM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) :=
|
||||
filterAuxM p as.reverse []
|
||||
|
||||
/--
|
||||
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns those
|
||||
results `y` for which `f x` returns `some y`.
|
||||
Applies a monadic function that returns an `Option` to each element of a list, collecting the
|
||||
non-`none` values.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterMapM fun x => do
|
||||
IO.println s!"Examining {x}"
|
||||
if x > 2 then return some (2 * x)
|
||||
else return none
|
||||
```
|
||||
```output
|
||||
Examining 1
|
||||
Examining 2
|
||||
Examining 5
|
||||
Examining 2
|
||||
Examining 7
|
||||
Examining 7
|
||||
```
|
||||
```output
|
||||
[10, 14, 14]
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (Option β)) (as : List α) : m (List β) :=
|
||||
@@ -141,8 +201,8 @@ def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
|
||||
loop as []
|
||||
|
||||
/--
|
||||
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns the
|
||||
concatenation of the results.
|
||||
Applies a monadic function that returns a list to each element of a list, from left to right, and
|
||||
concatenates the resulting lists.
|
||||
-/
|
||||
@[inline]
|
||||
def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (List β)) (as : List α) : m (List β) :=
|
||||
@@ -153,14 +213,20 @@ def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f :
|
||||
loop as (bs' :: bs)
|
||||
loop as []
|
||||
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from left to right:
|
||||
```
|
||||
foldlM f x₀ [a, b, c] = do
|
||||
let x₁ ← f x₀ a
|
||||
let x₂ ← f x₁ b
|
||||
let x₃ ← f x₂ c
|
||||
pure x₃
|
||||
Folds a monadic function over a list from the left, accumulating a value starting with `init`. The
|
||||
accumulated value is combined with the each element of the list in order, using `f`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example [Monad m] (f : α → β → m α) :
|
||||
List.foldlM (m := m) f x₀ [a, b, c] = (do
|
||||
let x₁ ← f x₀ a
|
||||
let x₂ ← f x₁ b
|
||||
let x₃ ← f x₂ c
|
||||
pure x₃)
|
||||
:= by rfl
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
@@ -176,13 +242,18 @@ def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s
|
||||
simp [List.foldlM]
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from right to left:
|
||||
```
|
||||
foldrM f x₀ [a, b, c] = do
|
||||
let x₁ ← f c x₀
|
||||
let x₂ ← f b x₁
|
||||
let x₃ ← f a x₂
|
||||
pure x₃
|
||||
Folds a monadic function over a list from the right, accumulating a value starting with `init`. The
|
||||
accumulated value is combined with the each element of the list in reverse order, using `f`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example [Monad m] (f : α → β → m β) :
|
||||
List.foldrM (m := m) f x₀ [a, b, c] = (do
|
||||
let x₁ ← f c x₀
|
||||
let x₂ ← f b x₁
|
||||
let x₃ ← f a x₂
|
||||
pure x₃)
|
||||
:= by rfl
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
@@ -192,32 +263,70 @@ def foldrM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} (f : α
|
||||
@[simp] theorem foldrM_nil [Monad m] (f : α → β → m β) (b) : [].foldrM f b = pure b := rfl
|
||||
|
||||
/--
|
||||
Maps `f` over the list and collects the results with `<|>`.
|
||||
```
|
||||
firstM f [a, b, c] = f a <|> f b <|> f c <|> failure
|
||||
```
|
||||
Maps `f` over the list and collects the results with `<|>`. The result for the end of the list is
|
||||
`failure`.
|
||||
|
||||
Examples:
|
||||
* `[[], [1, 2], [], [2]].firstM List.head? = some 1`
|
||||
* `[[], [], []].firstM List.head? = none`
|
||||
* `[].firstM List.head? = none`
|
||||
-/
|
||||
@[specialize]
|
||||
def firstM {m : Type u → Type v} [Alternative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m β
|
||||
| [] => failure
|
||||
| a::as => f a <|> firstM f as
|
||||
|
||||
/--
|
||||
Returns true if the monadic predicate `p` returns `true` for any element of `l`.
|
||||
|
||||
`O(|l|)`. Short-circuits upon encountering the first `true`. The elements in `l` are examined in
|
||||
order from left to right.
|
||||
-/
|
||||
@[specialize]
|
||||
def anyM {m : Type → Type u} [Monad m] {α : Type v} (f : α → m Bool) : List α → m Bool
|
||||
def anyM {m : Type → Type u} [Monad m] {α : Type v} (p : α → m Bool) : (l : List α) → m Bool
|
||||
| [] => pure false
|
||||
| a::as => do
|
||||
match (← f a) with
|
||||
match (← p a) with
|
||||
| true => pure true
|
||||
| false => anyM f as
|
||||
| false => anyM p as
|
||||
|
||||
/--
|
||||
Returns true if the monadic predicate `p` returns `true` for every element of `l`.
|
||||
|
||||
`O(|l|)`. Short-circuits upon encountering the first `false`. The elements in `l` are examined in
|
||||
order from left to right.
|
||||
-/
|
||||
@[specialize]
|
||||
def allM {m : Type → Type u} [Monad m] {α : Type v} (f : α → m Bool) : List α → m Bool
|
||||
def allM {m : Type → Type u} [Monad m] {α : Type v} (p : α → m Bool) : (l : List α) → m Bool
|
||||
| [] => pure true
|
||||
| a::as => do
|
||||
match (← f a) with
|
||||
| true => allM f as
|
||||
match (← p a) with
|
||||
| true => allM p as
|
||||
| false => pure false
|
||||
|
||||
/--
|
||||
Returns the first element of the list for which the monadic predicate `p` returns `true`, or `none`
|
||||
if no such element is found. Elements of the list are checked in order.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [7, 6, 5, 8, 1, 2, 6].findM? fun i => do
|
||||
if i < 5 then
|
||||
return true
|
||||
if i ≤ 6 then
|
||||
IO.println s!"Almost! {i}"
|
||||
return false
|
||||
```
|
||||
```output
|
||||
Almost! 6
|
||||
Almost! 5
|
||||
```
|
||||
```output
|
||||
some 1
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
def findM? {m : Type → Type u} [Monad m] {α : Type} (p : α → m Bool) : List α → m (Option α)
|
||||
| [] => pure none
|
||||
@@ -241,6 +350,29 @@ theorem findM?_pure {m} [Monad m] [LawfulMonad m] (p : α → Bool) (as : List
|
||||
theorem findM?_id (p : α → Bool) (as : List α) : findM? (m := Id) p as = as.find? p :=
|
||||
findM?_pure _ _
|
||||
|
||||
/--
|
||||
Returns the first non-`none` result of applying the monadic function `f` to each element of the
|
||||
list, in order. Returns `none` if `f` returns `none` for all elements.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [7, 6, 5, 8, 1, 2, 6].findSomeM? fun i => do
|
||||
if i < 5 then
|
||||
return some (i * 10)
|
||||
if i ≤ 6 then
|
||||
IO.println s!"Almost! {i}"
|
||||
return none
|
||||
```
|
||||
```output
|
||||
Almost! 6
|
||||
Almost! 5
|
||||
```
|
||||
```output
|
||||
some 10
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (Option β)) : List α → m (Option β)
|
||||
| [] => pure none
|
||||
|
||||
@@ -90,7 +90,7 @@ theorem countP_le_length : countP p l ≤ l.length := by
|
||||
simp only [countP_eq_length_filter, length_eq_zero_iff, filter_eq_nil_iff]
|
||||
|
||||
@[simp] theorem countP_eq_length {p} : countP p l = l.length ↔ ∀ a ∈ l, p a := by
|
||||
rw [countP_eq_length_filter, filter_length_eq_length]
|
||||
rw [countP_eq_length_filter, length_filter_eq_length_iff]
|
||||
|
||||
theorem countP_replicate (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (replicate n a) = if p a then n else 0 := by
|
||||
|
||||
@@ -11,7 +11,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace List
|
||||
|
||||
/-- `finRange n` lists all elements of `Fin n` in order -/
|
||||
/--
|
||||
Lists all elements of `Fin n` in order, starting at `0`.
|
||||
|
||||
Examples:
|
||||
* `List.finRange 0 = ([] : List (Fin 0))`
|
||||
* `List.finRange 2 = ([0, 1] : List (Fin 2))`
|
||||
-/
|
||||
def finRange (n : Nat) : List (Fin n) := ofFn fun i => i
|
||||
|
||||
@[simp] theorem length_finRange (n) : (List.finRange n).length = n := by
|
||||
|
||||
@@ -540,11 +540,6 @@ 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_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
|
||||
|
||||
@@ -17,7 +17,8 @@ then at runtime you will get non-tail recursive versions of the following defini
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace List
|
||||
|
||||
@@ -49,7 +50,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### set -/
|
||||
|
||||
/-- Tail recursive version of `List.set`. -/
|
||||
/--
|
||||
Replaces the value at (zero-based) index `n` in `l` with `a`. If the index is out of bounds, then
|
||||
the list is returned unmodified.
|
||||
|
||||
This is a tail-recursive version of `List.set` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `["water", "coffee", "soda", "juice"].set 1 "tea" = ["water", "tea", "soda", "juice"]`
|
||||
* `["water", "coffee", "soda", "juice"].set 4 "tea" = ["water", "coffee", "soda", "juice"]`
|
||||
-/
|
||||
@[inline] def setTR (l : List α) (n : Nat) (a : α) : List α := go l n #[] where
|
||||
/-- Auxiliary for `setTR`: `setTR.go l a xs n acc = acc.toList ++ set xs a`,
|
||||
unless `n ≥ l.length` in which case it returns `l` -/
|
||||
@@ -69,7 +79,22 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### filterMap -/
|
||||
|
||||
/-- Tail recursive version of `filterMap`. -/
|
||||
|
||||
/--
|
||||
Applies a function that returns an `Option` to each element of a list, collecting the non-`none`
|
||||
values.
|
||||
|
||||
`O(|l|)`. This is a tail-recursive version of `List.filterMap`, used at runtime.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterMapTR fun x =>
|
||||
if x > 2 then some (2 * x) else none
|
||||
```
|
||||
```output
|
||||
[10, 14, 14]
|
||||
```
|
||||
-/
|
||||
@[inline] def filterMapTR (f : α → Option β) (l : List α) : List β := go l #[] where
|
||||
/-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/
|
||||
@[specialize] go : List α → Array β → List β
|
||||
@@ -90,7 +115,17 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### foldr -/
|
||||
|
||||
/-- Tail recursive version of `List.foldr`. -/
|
||||
/--
|
||||
Folds a function over a list from the right, accumulating a value starting with `init`. The
|
||||
accumulated value is combined with the each element of the list in reverse order, using `f`.
|
||||
|
||||
`O(|l|)`. This is the tail-recursive replacement for `List.foldr` in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c].foldrTR f init = f a (f b (f c init))`
|
||||
* `[1, 2, 3].foldrTR (toString · ++ ·) "" = "123"`
|
||||
* `[1, 2, 3].foldrTR (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
|
||||
-/
|
||||
@[specialize] def foldrTR (f : α → β → β) (init : β) (l : List α) : β := l.toArray.foldr f init
|
||||
|
||||
@[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by
|
||||
@@ -98,7 +133,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### flatMap -/
|
||||
|
||||
/-- Tail recursive version of `List.flatMap`. -/
|
||||
/--
|
||||
Applies a function that returns a list to each element of a list, and concatenates the resulting
|
||||
lists.
|
||||
|
||||
This is the tail-recursive version of `List.flatMap` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[2, 3, 2].flatMapTR List.range = [0, 1, 0, 1, 2, 0, 1]`
|
||||
* `["red", "blue"].flatMapTR String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
|
||||
-/
|
||||
@[inline] def flatMapTR (f : α → List β) (as : List α) : List β := go as #[] where
|
||||
/-- Auxiliary for `flatMap`: `flatMap.go f as = acc.toList ++ bind f as` -/
|
||||
@[specialize] go : List α → Array β → List β
|
||||
@@ -114,7 +158,15 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### flatten -/
|
||||
|
||||
/-- Tail recursive version of `List.flatten`. -/
|
||||
/--
|
||||
Concatenates a list of lists into a single list, preserving the order of the elements.
|
||||
|
||||
`O(|flatten L|)`. This is a tail-recursive version of `List.flatten`, used in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[["a"], ["b", "c"]].flattenTR = ["a", "b", "c"]`
|
||||
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flattenTR = ["a", "b", "c", "d", "e", "f"]`
|
||||
-/
|
||||
@[inline] def flattenTR (l : List (List α)) : List α := l.flatMapTR id
|
||||
|
||||
@[csimp] theorem flatten_eq_flattenTR : @flatten = @flattenTR := by
|
||||
@@ -124,7 +176,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### take -/
|
||||
|
||||
/-- Tail recursive version of `List.take`. -/
|
||||
/--
|
||||
Extracts the first `n` elements of `xs`, or the whole list if `n` is greater than `xs.length`.
|
||||
|
||||
`O(min n |xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c, d, e].takeTR 0 = []`
|
||||
* `[a, b, c, d, e].takeTR 3 = [a, b, c]`
|
||||
* `[a, b, c, d, e].takeTR 6 = [a, b, c, d, e]`
|
||||
-/
|
||||
@[inline] def takeTR (n : Nat) (l : List α) : List α := go l n #[] where
|
||||
/-- Auxiliary for `take`: `take.go l xs n acc = acc.toList ++ take n xs`,
|
||||
unless `n ≥ xs.length` in which case it returns `l`. -/
|
||||
@@ -146,7 +207,17 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### takeWhile -/
|
||||
|
||||
/-- Tail recursive version of `List.takeWhile`. -/
|
||||
|
||||
/--
|
||||
Returns the longest initial segment of `xs` for which `p` returns true.
|
||||
|
||||
`O(|xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[7, 6, 4, 8].takeWhileTR (· > 5) = [7, 6]`
|
||||
* `[7, 6, 6, 5].takeWhileTR (· > 5) = [7, 6, 6]`
|
||||
* `[7, 6, 6, 8].takeWhileTR (· > 5) = [7, 6, 6, 8]`
|
||||
-/
|
||||
@[inline] def takeWhileTR (p : α → Bool) (l : List α) : List α := go l #[] where
|
||||
/-- Auxiliary for `takeWhile`: `takeWhile.go p l xs acc = acc.toList ++ takeWhile p xs`,
|
||||
unless no element satisfying `p` is found in `xs` in which case it returns `l`. -/
|
||||
@@ -169,7 +240,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### dropLast -/
|
||||
|
||||
/-- Tail recursive version of `dropLast`. -/
|
||||
/--
|
||||
Removes the last element of the list, if one exists.
|
||||
|
||||
This is a tail-recursive version of `List.dropLast`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[].dropLastTR = []`
|
||||
* `["tea"].dropLastTR = []`
|
||||
* `["tea", "coffee", "juice"].dropLastTR = ["tea", "coffee"]`
|
||||
-/
|
||||
@[inline] def dropLastTR (l : List α) : List α := l.toArray.pop.toList
|
||||
|
||||
@[csimp] theorem dropLast_eq_dropLastTR : @dropLast = @dropLastTR := by
|
||||
@@ -179,7 +259,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### replace -/
|
||||
|
||||
/-- Tail recursive version of `List.replace`. -/
|
||||
/--
|
||||
Replaces the first element of the list `l` that is equal to `a` with `b`. If no element is equal to
|
||||
`a`, then the list is returned unchanged.
|
||||
|
||||
`O(|l|)`. This is a tail-recursive version of `List.replace` that's used in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[1, 4, 2, 3, 3, 7].replaceTR 3 6 = [1, 4, 2, 6, 3, 7]`
|
||||
* `[1, 4, 2, 3, 3, 7].replaceTR 5 6 = [1, 4, 2, 3, 3, 7]`
|
||||
-/
|
||||
@[inline] def replaceTR [BEq α] (l : List α) (b c : α) : List α := go l #[] where
|
||||
/-- Auxiliary for `replace`: `replace.go l b c xs acc = acc.toList ++ replace xs b c`,
|
||||
unless `b` is not found in `xs` in which case it returns `l`. -/
|
||||
@@ -202,42 +291,76 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### modify -/
|
||||
|
||||
/-- Tail-recursive version of `modify`. -/
|
||||
def modifyTR (f : α → α) (n : Nat) (l : List α) : List α := go l n #[] where
|
||||
/-- Auxiliary for `modifyTR`: `modifyTR.go f l n acc = acc.toList ++ modify f n l`. -/
|
||||
/--
|
||||
Replaces the element at the given index, if it exists, with the result of applying `f` to it.
|
||||
|
||||
This is a tail-recursive version of `List.modify`.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3].modifyTR 0 (· * 10) = [10, 2, 3]`
|
||||
* `[1, 2, 3].modifyTR 2 (· * 10) = [1, 2, 30]`
|
||||
* `[1, 2, 3].modifyTR 3 (· * 10) = [1, 2, 3]`
|
||||
-/
|
||||
def modifyTR (l : List α) (i : Nat) (f : α → α) : List α := go l i #[] where
|
||||
/-- Auxiliary for `modifyTR`: `modifyTR.go f l i acc = acc.toList ++ modify f i l`. -/
|
||||
go : List α → Nat → Array α → List α
|
||||
| [], _, acc => acc.toList
|
||||
| a :: l, 0, acc => acc.toListAppend (f a :: l)
|
||||
| a :: l, n+1, acc => go l n (acc.push a)
|
||||
| a :: l, i+1, acc => go l i (acc.push a)
|
||||
|
||||
theorem modifyTR_go_eq : ∀ l i, modifyTR.go f l i acc = acc.toList ++ modify f i l
|
||||
| [], n => by cases n <;> simp [modifyTR.go, modify]
|
||||
theorem modifyTR_go_eq : ∀ l i, modifyTR.go f l i acc = acc.toList ++ modify l i f
|
||||
| [], i => by cases i <;> simp [modifyTR.go, modify]
|
||||
| a :: l, 0 => by simp [modifyTR.go, modify]
|
||||
| a :: l, n+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
|
||||
| a :: l, i+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
|
||||
|
||||
@[csimp] theorem modify_eq_modifyTR : @modify = @modifyTR := by
|
||||
funext α f n l; simp [modifyTR, modifyTR_go_eq]
|
||||
funext α l i f; simp [modifyTR, modifyTR_go_eq]
|
||||
|
||||
/-! ### insertIdx -/
|
||||
|
||||
/-- Tail-recursive version of `insertIdx`. -/
|
||||
@[inline] def insertIdxTR (n : Nat) (a : α) (l : List α) : List α := go n l #[] where
|
||||
/--
|
||||
Inserts an element into a list at the specified index. If the index is greater than the length of
|
||||
the list, then the list is returned unmodified.
|
||||
|
||||
In other words, the new element is inserted into the list `l` after the first `i` elements of `l`.
|
||||
|
||||
This is a tail-recursive version of `List.insertIdx`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `["tues", "thur", "sat"].insertIdxTR 1 "wed" = ["tues", "wed", "thur", "sat"]`
|
||||
* `["tues", "thur", "sat"].insertIdxTR 2 "wed" = ["tues", "thur", "wed", "sat"]`
|
||||
* `["tues", "thur", "sat"].insertIdxTR 3 "wed" = ["tues", "thur", "sat", "wed"]`
|
||||
* `["tues", "thur", "sat"].insertIdxTR 4 "wed" = ["tues", "thur", "sat"]`
|
||||
|
||||
-/
|
||||
@[inline] def insertIdxTR (l : List α) (n : Nat) (a : α) : List α := go n l #[] where
|
||||
/-- Auxiliary for `insertIdxTR`: `insertIdxTR.go a n l acc = acc.toList ++ insertIdx n a l`. -/
|
||||
go : Nat → List α → Array α → List α
|
||||
| 0, l, acc => acc.toListAppend (a :: l)
|
||||
| _, [], acc => acc.toList
|
||||
| n+1, a :: l, acc => go n l (acc.push a)
|
||||
|
||||
theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx i a l
|
||||
theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a
|
||||
| 0, l | _+1, [] => by simp [insertIdxTR.go, insertIdx]
|
||||
| n+1, a :: l => by simp [insertIdxTR.go, insertIdx, insertIdxTR_go_eq n l]
|
||||
|
||||
@[csimp] theorem insertIdx_eq_insertIdxTR : @insertIdx = @insertIdxTR := by
|
||||
funext α f n l; simp [insertIdxTR, insertIdxTR_go_eq]
|
||||
funext α l i a; simp [insertIdxTR, insertIdxTR_go_eq]
|
||||
|
||||
/-! ### erase -/
|
||||
|
||||
/-- Tail recursive version of `List.erase`. -/
|
||||
/--
|
||||
Removes the first occurrence of `a` from `l`. If `a` does not occur in `l`, the list is returned
|
||||
unmodified.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
This is a tail-recursive version of `List.erase`, used in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[1, 5, 3, 2, 5].eraseTR 5 = [1, 3, 2, 5]`
|
||||
* `[1, 5, 3, 2, 5].eraseTR 6 = [1, 5, 3, 2, 5]`
|
||||
-/
|
||||
@[inline] def eraseTR [BEq α] (l : List α) (a : α) : List α := go l #[] where
|
||||
/-- Auxiliary for `eraseTR`: `eraseTR.go l a xs acc = acc.toList ++ erase xs a`,
|
||||
unless `a` is not present in which case it returns `l` -/
|
||||
@@ -257,7 +380,17 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
· rw [IH] <;> simp_all
|
||||
· simp
|
||||
|
||||
/-- Tail-recursive version of `eraseP`. -/
|
||||
/--
|
||||
Removes the first element of a list for which `p` returns `true`. If no element satisfies `p`, then
|
||||
the list is returned unchanged.
|
||||
|
||||
This is a tail-recursive version of `eraseP`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[2, 1, 2, 1, 3, 4].erasePTR (· < 2) = [2, 2, 1, 3, 4]`
|
||||
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 2) = [2, 1, 2, 1, 4]`
|
||||
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 8) = [2, 1, 2, 1, 3, 4]`
|
||||
-/
|
||||
@[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`. -/
|
||||
@@ -277,7 +410,20 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
/-- Tail recursive version of `List.eraseIdx`. -/
|
||||
|
||||
/--
|
||||
Removes the element at the specified index. If the index is out of bounds, the list is returned
|
||||
unmodified.
|
||||
|
||||
`O(i)`.
|
||||
|
||||
This is a tail-recursive version of `List.eraseIdx`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[0, 1, 2, 3, 4].eraseIdxTR 0 = [1, 2, 3, 4]`
|
||||
* `[0, 1, 2, 3, 4].eraseIdxTR 1 = [0, 2, 3, 4]`
|
||||
* `[0, 1, 2, 3, 4].eraseIdxTR 5 = [0, 1, 2, 3, 4]`
|
||||
-/
|
||||
@[inline] def eraseIdxTR (l : List α) (n : Nat) : List α := go l n #[] where
|
||||
/-- Auxiliary for `eraseIdxTR`: `eraseIdxTR.go l n xs acc = acc.toList ++ eraseIdx xs a`,
|
||||
unless `a` is not present in which case it returns `l` -/
|
||||
@@ -303,7 +449,18 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
/-- Tail recursive version of `List.zipWith`. -/
|
||||
/--
|
||||
Applies a function to the corresponding elements of two lists, stopping at the end of the shorter
|
||||
list.
|
||||
|
||||
`O(min |xs| |ys|)`. This is a tail-recursive version of `List.zipWith` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[1, 2].zipWithTR (· + ·) [5, 6] = [6, 8]`
|
||||
* `[1, 2, 3].zipWithTR (· + ·) [5, 6, 10] = [6, 8, 13]`
|
||||
* `[].zipWithTR (· + ·) [5, 6] = []`
|
||||
* `[x₁, x₂, x₃].zipWithTR f [y₁, y₂, y₃, y₄] = [f x₁ y₁, f x₂ y₂, f x₃ y₃]`
|
||||
-/
|
||||
@[inline] def zipWithTR (f : α → β → γ) (as : List α) (bs : List β) : List γ := go as bs #[] where
|
||||
/-- Auxiliary for `zipWith`: `zipWith.go f as bs acc = acc.toList ++ zipWith f as bs` -/
|
||||
go : List α → List β → Array γ → List γ
|
||||
@@ -321,7 +478,16 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
|
||||
/-! ### zipIdx -/
|
||||
|
||||
/-- Tail recursive version of `List.zipIdx`. -/
|
||||
|
||||
/--
|
||||
Pairs each element of a list with its index, optionally starting from an index other than `0`.
|
||||
|
||||
`O(|l|)`. This is a tail-recursive version of `List.zipIdx` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c].zipIdxTR = [(a, 0), (b, 1), (c, 2)]`
|
||||
* `[a, b, c].zipIdxTR 5 = [(a, 5), (b, 6), (c, 7)]`
|
||||
-/
|
||||
def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) :=
|
||||
let as := l.toArray
|
||||
(as.foldr (fun a (n, acc) => (n-1, (a, n-1) :: acc)) (n + as.size, [])).2
|
||||
@@ -363,8 +529,18 @@ theorem enumFrom_eq_enumFromTR : @enumFrom = @enumFromTR := by
|
||||
/-! ### intercalate -/
|
||||
|
||||
set_option linter.listVariables false in
|
||||
/-- Tail recursive version of `List.intercalate`. -/
|
||||
def intercalateTR (sep : List α) : List (List α) → List α
|
||||
/--
|
||||
Alternates the lists in `xs` with the separator `sep`.
|
||||
|
||||
This is a tail-recursive version of `List.intercalate` used at runtime.
|
||||
|
||||
Examples:
|
||||
* `List.intercalateTR sep [] = []`
|
||||
* `List.intercalateTR sep [a] = a`
|
||||
* `List.intercalateTR sep [a, b] = a ++ sep ++ b`
|
||||
* `List.intercalateTR sep [a, b, c] = a ++ sep ++ b ++ sep ++ c`
|
||||
-/
|
||||
def intercalateTR (sep : List α) : (xs : List (List α)) → List α
|
||||
| [] => []
|
||||
| [x] => x
|
||||
| x::xs => go sep.toArray x xs #[]
|
||||
|
||||
@@ -815,14 +815,6 @@ theorem getElem_length_sub_one_eq_getLast (l : List α) (h : l.length - 1 < l.le
|
||||
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
|
||||
rw [← getLast_eq_getElem]
|
||||
|
||||
@[deprecated getLast_eq_getElem (since := "2024-07-15")]
|
||||
theorem getLast_eq_get (l : List α) (h : l ≠ []) :
|
||||
getLast l h = l.get ⟨l.length - 1, by
|
||||
match l with
|
||||
| [] => contradiction
|
||||
| a :: l => exact Nat.le_refl _⟩ := by
|
||||
simp [getLast_eq_getElem]
|
||||
|
||||
theorem getLast_cons {a : α} {l : List α} : ∀ (h : l ≠ nil),
|
||||
getLast (a :: l) (cons_ne_nil a l) = getLast l h := by
|
||||
induction l <;> intros; {contradiction}; rfl
|
||||
@@ -947,9 +939,12 @@ theorem head_eq_iff_head?_eq_some {xs : List α} (h) : xs.head h = a ↔ xs.head
|
||||
theorem head?_eq_some_iff {xs : List α} {a : α} : xs.head? = some a ↔ ∃ ys, xs = a :: ys := by
|
||||
cases xs <;> simp_all
|
||||
|
||||
@[simp] theorem head?_isSome : l.head?.isSome ↔ l ≠ [] := by
|
||||
@[simp] theorem isSome_head? : l.head?.isSome ↔ l ≠ [] := by
|
||||
cases l <;> simp
|
||||
|
||||
@[deprecated isSome_head? (since := "2025-03-18")]
|
||||
abbrev head?_isSome := @isSome_head?
|
||||
|
||||
@[simp] theorem head_mem : ∀ {l : List α} (h : l ≠ []), head l h ∈ l
|
||||
| [], h => absurd rfl h
|
||||
| _::_, _ => .head ..
|
||||
@@ -1098,8 +1093,6 @@ theorem forall_mem_map {f : α → β} {l : List α} {P : β → Prop} :
|
||||
(∀ (i) (_ : i ∈ l.map f), P i) ↔ ∀ (j) (_ : j ∈ l), P (f j) := by
|
||||
simp
|
||||
|
||||
@[deprecated forall_mem_map (since := "2024-07-25")] abbrev forall_mem_map_iff := @forall_mem_map
|
||||
|
||||
@[simp] theorem map_eq_nil_iff {f : α → β} {l : List α} : map f l = [] ↔ l = [] := by
|
||||
constructor <;> exact fun _ => match l with | [] => rfl
|
||||
|
||||
@@ -1256,7 +1249,7 @@ theorem filter_eq_self {l} : filter p l = l ↔ ∀ a ∈ l, p a := by
|
||||
intro h; exact Nat.lt_irrefl _ (h ▸ length_filter_le p l)
|
||||
|
||||
@[simp]
|
||||
theorem filter_length_eq_length {l} : (filter p l).length = l.length ↔ ∀ a ∈ l, p a := by
|
||||
theorem length_filter_eq_length_iff {l} : (filter p l).length = l.length ↔ ∀ a ∈ l, p a := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
@@ -1266,6 +1259,9 @@ theorem filter_length_eq_length {l} : (filter p l).length = l.length ↔ ∀ a
|
||||
· have := Nat.ne_of_lt (Nat.lt_succ.mpr (length_filter_le p l))
|
||||
simp_all
|
||||
|
||||
@[deprecated length_filter_eq_length_iff (since := "2024-09-05")]
|
||||
abbrev filter_length_eq_length := @length_filter_eq_length_iff
|
||||
|
||||
@[simp] theorem mem_filter : x ∈ filter p as ↔ x ∈ as ∧ p x := by
|
||||
induction as with
|
||||
| nil => simp [filter]
|
||||
@@ -1283,8 +1279,6 @@ theorem forall_mem_filter {l : List α} {p : α → Bool} {P : α → Prop} :
|
||||
(∀ (i) (_ : i ∈ l.filter p), P i) ↔ ∀ (j) (_ : j ∈ l), p j → P j := by
|
||||
simp
|
||||
|
||||
@[deprecated forall_mem_filter (since := "2024-07-25")] abbrev forall_mem_filter_iff := @forall_mem_filter
|
||||
|
||||
@[simp] theorem filter_filter (q) : ∀ l, filter p (filter q l) = filter (fun a => p a && q a) l
|
||||
| [] => rfl
|
||||
| a :: l => by by_cases hp : p a <;> by_cases hq : q a <;> simp [hp, hq, filter_filter _ l]
|
||||
@@ -1457,8 +1451,6 @@ theorem forall_mem_filterMap {f : α → Option β} {l : List α} {P : β → Pr
|
||||
intro a
|
||||
rw [forall_comm]
|
||||
|
||||
@[deprecated forall_mem_filterMap (since := "2024-07-25")] abbrev forall_mem_filterMap_iff := @forall_mem_filterMap
|
||||
|
||||
@[simp] theorem filterMap_append {α β : Type _} (l l' : List α) (f : α → Option β) :
|
||||
filterMap f (l ++ l') = filterMap f l ++ filterMap f l' := by
|
||||
induction l <;> simp [filterMap_cons]; split <;> simp [*]
|
||||
@@ -1659,29 +1651,18 @@ theorem getLast_concat {a : α} : ∀ (l : List α), getLast (l ++ [a]) (by simp
|
||||
@[simp] theorem nil_eq_append_iff : [] = a ++ b ↔ a = [] ∧ b = [] := by
|
||||
rw [eq_comm, append_eq_nil_iff]
|
||||
|
||||
@[deprecated nil_eq_append_iff (since := "2024-07-24")] abbrev nil_eq_append := @nil_eq_append_iff
|
||||
|
||||
theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s ≠ []) (t : List α) : s ++ t ≠ [] := by simp_all
|
||||
theorem append_ne_nil_of_right_ne_nil (s : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
|
||||
|
||||
@[deprecated append_ne_nil_of_left_ne_nil (since := "2024-07-24")]
|
||||
theorem append_ne_nil_of_ne_nil_left {s : List α} (h : s ≠ []) (t : List α) : s ++ t ≠ [] := by simp_all
|
||||
@[deprecated append_ne_nil_of_right_ne_nil (since := "2024-07-24")]
|
||||
theorem append_ne_nil_of_ne_nil_right (s : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
|
||||
|
||||
theorem append_eq_cons_iff :
|
||||
as ++ bs = x :: c ↔ (as = [] ∧ bs = x :: c) ∨ (∃ as', as = x :: as' ∧ c = as' ++ bs) := by
|
||||
cases as with simp | cons a as => ?_
|
||||
exact ⟨fun h => ⟨as, by simp [h]⟩, fun ⟨as', ⟨aeq, aseq⟩, h⟩ => ⟨aeq, by rw [aseq, h]⟩⟩
|
||||
|
||||
@[deprecated append_eq_cons_iff (since := "2024-07-24")] abbrev append_eq_cons := @append_eq_cons_iff
|
||||
|
||||
theorem cons_eq_append_iff :
|
||||
x :: cs = as ++ bs ↔ (as = [] ∧ bs = x :: cs) ∨ (∃ as', as = x :: as' ∧ cs = as' ++ bs) := by
|
||||
rw [eq_comm, append_eq_cons_iff]
|
||||
|
||||
@[deprecated cons_eq_append_iff (since := "2024-07-24")] abbrev cons_eq_append := @cons_eq_append_iff
|
||||
|
||||
theorem append_eq_singleton_iff :
|
||||
a ++ b = [x] ↔ (a = [] ∧ b = [x]) ∨ (a = [x] ∧ b = []) := by
|
||||
cases a <;> cases b <;> simp
|
||||
@@ -1696,9 +1677,6 @@ theorem append_eq_append_iff {ws xs ys zs : List α} :
|
||||
| nil => simp_all
|
||||
| cons a as ih => cases ys <;> simp [eq_comm, and_assoc, ih, and_or_left]
|
||||
|
||||
@[deprecated append_inj (since := "2024-07-24")] abbrev append_inj_of_length_left := @append_inj
|
||||
@[deprecated append_inj' (since := "2024-07-24")] abbrev append_inj_of_length_right := @append_inj'
|
||||
|
||||
@[simp] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) :
|
||||
head (l ++ l') w₁ = head l w₂ := by
|
||||
match l, w₂ with
|
||||
@@ -1746,8 +1724,6 @@ theorem tail_append {l l' : List α} : (l ++ l').tail = if l.isEmpty then l'.tai
|
||||
(xs ++ ys).tail = xs.tail ++ ys := by
|
||||
simp_all [tail_append]
|
||||
|
||||
@[deprecated tail_append_of_ne_nil (since := "2024-07-24")] abbrev tail_append_left := @tail_append_of_ne_nil
|
||||
|
||||
theorem set_append {s t : List α} :
|
||||
(s ++ t).set i x = if i < s.length then s.set i x ++ t else s ++ t.set (i - s.length) x := by
|
||||
induction s generalizing i with
|
||||
@@ -2097,8 +2073,6 @@ theorem head?_flatMap {l : List α} {f : α → List β} :
|
||||
(xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by
|
||||
induction xs; {rfl}; simp_all [flatMap_cons, append_assoc]
|
||||
|
||||
@[deprecated flatMap_append (since := "2024-07-24")] abbrev append_bind := @flatMap_append
|
||||
|
||||
theorem flatMap_assoc {α β} (l : List α) (f : α → List β) (g : β → List γ) :
|
||||
(l.flatMap f).flatMap g = l.flatMap fun x => (f x).flatMap g := by
|
||||
induction l <;> simp [*]
|
||||
@@ -2575,8 +2549,6 @@ theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
|
||||
l.foldr cons l' = l ++ l' := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@[deprecated foldr_cons_eq_append (since := "2024-08-22")] abbrev foldr_self_append := @foldr_cons_eq_append
|
||||
|
||||
@[simp] theorem foldl_flip_cons_eq_append (l : List α) (f : α → β) (l' : List β) :
|
||||
l.foldl (fun xs y => f y :: xs) l' = (l.map f).reverse ++ l' := by
|
||||
induction l generalizing l' <;> simp [*]
|
||||
@@ -2697,12 +2669,20 @@ theorem foldr_hom (f : β₁ → β₂) (g₁ : α → β₁ → β₁) (g₂ :
|
||||
induction l <;> simp [*, H]
|
||||
|
||||
/--
|
||||
Prove a proposition about the result of `List.foldl`,
|
||||
by proving it for the initial data,
|
||||
and the implication that the operation applied to any element of the list preserves the property.
|
||||
A reasoning principle for proving propositions about the result of `List.foldl` by establishing an
|
||||
invariant that is true for the initial data and preserved by the operation being folded.
|
||||
|
||||
The motive can take values in `Sort _`, so this may be used to construct data,
|
||||
as well as to prove propositions.
|
||||
Because the motive can return a type in any sort, this function may be used to construct data as
|
||||
well as to prove propositions.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example {xs : List Nat} : xs.foldl (· + ·) 1 > 0 := by
|
||||
apply List.foldlRecOn
|
||||
. show 0 < 1; trivial
|
||||
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < b + a
|
||||
intros; omega
|
||||
```
|
||||
-/
|
||||
def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α → β) (b : β) (_ : motive b)
|
||||
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op b a)), motive (List.foldl op b l)
|
||||
@@ -2723,12 +2703,20 @@ def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α →
|
||||
rfl
|
||||
|
||||
/--
|
||||
Prove a proposition about the result of `List.foldr`,
|
||||
by proving it for the initial data,
|
||||
and the implication that the operation applied to any element of the list preserves the property.
|
||||
A reasoning principle for proving propositions about the result of `List.foldr` by establishing an
|
||||
invariant that is true for the initial data and preserved by the operation being folded.
|
||||
|
||||
The motive can take values in `Sort _`, so this may be used to construct data,
|
||||
as well as to prove propositions.
|
||||
Because the motive can return a type in any sort, this function may be used to construct data as
|
||||
well as to prove propositions.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example {xs : List Nat} : xs.foldr (· + ·) 1 > 0 := by
|
||||
apply List.foldrRecOn
|
||||
. show 0 < 1; trivial
|
||||
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < a + b
|
||||
intros; omega
|
||||
```
|
||||
-/
|
||||
def foldrRecOn {motive : β → Sort _} : ∀ (l : List α) (op : α → β → β) (b : β) (_ : motive b)
|
||||
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op a b)), motive (List.foldr op b l)
|
||||
@@ -2825,7 +2813,7 @@ theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔
|
||||
exact ⟨fun ⟨ys, h⟩ => ⟨ys.reverse, by simpa using h⟩, fun ⟨ys, h⟩ => ⟨ys.reverse, by simpa using h⟩⟩
|
||||
|
||||
@[simp] theorem getLast?_isSome : l.getLast?.isSome ↔ l ≠ [] := by
|
||||
rw [getLast?_eq_head?_reverse, head?_isSome]
|
||||
rw [getLast?_eq_head?_reverse, isSome_head?]
|
||||
simp
|
||||
|
||||
theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a ∈ xs := by
|
||||
@@ -3521,14 +3509,6 @@ theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a :=
|
||||
|
||||
/-! ### Deprecations -/
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
|
||||
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
|
||||
|
||||
@[deprecated filter_flatten (since := "2024-08-26")]
|
||||
theorem join_map_filter (p : α → Bool) (l : List (List α)) :
|
||||
(l.map (filter p)).flatten = (l.flatten).filter p := by
|
||||
rw [filter_flatten]
|
||||
|
||||
@[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? :=
|
||||
@getElem_eq_getElem?_get
|
||||
@[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff
|
||||
|
||||
@@ -19,8 +19,11 @@ namespace List
|
||||
/-! ## Operations using indexes -/
|
||||
|
||||
/--
|
||||
Given a list `as = [a₀, a₁, ...]` and a function `f : (i : Nat) → α → (h : i < as.length) → β`, returns the list
|
||||
`[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
|
||||
Applies a function to each element of the list along with the index at which that element is found,
|
||||
returning the list of results. In addition to the index, the function is also provided with a proof
|
||||
that the index is valid.
|
||||
|
||||
`List.mapIdx` is a variant that does not provide the function with evidence that the index is valid.
|
||||
-/
|
||||
@[inline] def mapFinIdx (as : List α) (f : (i : Nat) → α → (h : i < as.length) → β) : List β :=
|
||||
go as #[] (by simp)
|
||||
@@ -33,8 +36,11 @@ where
|
||||
go as (acc.push (f acc.size a (by simp at h; omega))) (by simp at h ⊢; omega)
|
||||
|
||||
/--
|
||||
Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁, ...]`, returns the list
|
||||
`[f 0 a₀, f 1 a₁, ...]`.
|
||||
Applies a function to each element of the list along with the index at which that element is found,
|
||||
returning the list of results.
|
||||
|
||||
`List.mapFinIdx` is a variant that additionally provides the function with a proof that the index
|
||||
is valid.
|
||||
-/
|
||||
@[inline] def mapIdx (f : Nat → α → β) (as : List α) : List β := go as #[] where
|
||||
/-- Auxiliary for `mapIdx`:
|
||||
@@ -44,8 +50,12 @@ Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁,
|
||||
| a :: as, acc => go as (acc.push (f acc.size a))
|
||||
|
||||
/--
|
||||
Given a list `as = [a₀, a₁, ...]` and a monadic function `f : (i : Nat) → α → (h : i < as.length) → m β`,
|
||||
returns the list `[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
|
||||
Applies a monadic function to each element of the list along with the index at which that element is
|
||||
found, returning the list of results. In addition to the index, the function is also provided with a
|
||||
proof that the index is valid.
|
||||
|
||||
`List.mapIdxM` is a variant that does not provide the function with evidence that the index is
|
||||
valid.
|
||||
-/
|
||||
@[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) → α → (h : i < as.length) → m β) : m (List β) :=
|
||||
go as #[] (by simp)
|
||||
@@ -58,8 +68,11 @@ where
|
||||
go as (acc.push (← f acc.size a (by simp at h; omega))) (by simp at h ⊢; omega)
|
||||
|
||||
/--
|
||||
Given a monadic function `f : Nat → α → m β` and `as : List α`, `as = [a₀, a₁, ...]`,
|
||||
returns the list `[f 0 a₀, f 1 a₁, ...]`.
|
||||
Applies a monadic function to each element of the list along with the index at which that element is
|
||||
found, returning the list of results.
|
||||
|
||||
`List.mapFinIdxM` is a variant that additionally provides the function with a proof that the index
|
||||
is valid.
|
||||
-/
|
||||
@[inline] def mapIdxM [Monad m] (f : Nat → α → m β) (as : List α) : m (List β) := go as #[] where
|
||||
/-- Auxiliary for `mapIdxM`:
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.Pairwise
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.min?` and `List.max?.
|
||||
@@ -38,6 +39,18 @@ theorem isSome_min?_of_mem {l : List α} [Min α] {a : α} (h : a ∈ l) :
|
||||
l.min?.isSome := by
|
||||
cases l <;> simp_all [List.min?_cons']
|
||||
|
||||
theorem min?_eq_head? {α : Type u} [Min α] {l : List α}
|
||||
(h : l.Pairwise (fun a b => min a b = a)) : l.min? = l.head? := by
|
||||
cases l with
|
||||
| nil => rfl
|
||||
| cons x l =>
|
||||
rw [List.head?_cons, List.min?_cons', Option.some.injEq]
|
||||
induction l generalizing x with
|
||||
| nil => simp
|
||||
| cons y l ih =>
|
||||
have hx : min x y = x := List.rel_of_pairwise_cons h (List.mem_cons_self _ _)
|
||||
rw [List.foldl_cons, ih _ (hx.symm ▸ h.sublist (by simp)), hx]
|
||||
|
||||
theorem min?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b) :
|
||||
{xs : List α} → xs.min? = some a → a ∈ xs := by
|
||||
intro xs
|
||||
@@ -78,17 +91,19 @@ theorem le_min?_iff [Min α] [LE α]
|
||||
|
||||
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
|
||||
-- and `le_min_iff`.
|
||||
theorem min?_eq_some_iff [Min α] [LE α] [anti : Std.Antisymm ((· : α) ≤ ·)]
|
||||
theorem min?_eq_some_iff [Min α] [LE α]
|
||||
(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 α} :
|
||||
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) {xs : List α}
|
||||
(anti : ∀ a b, a ∈ xs → b ∈ xs → a ≤ b → b ≤ a → a = b := by
|
||||
exact fun a b _ _ => Std.Antisymm.antisymm a b) :
|
||||
xs.min? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||||
refine ⟨fun h => ⟨min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _)⟩, ?_⟩
|
||||
intro ⟨h₁, h₂⟩
|
||||
cases xs with
|
||||
| nil => simp at h₁
|
||||
| cons x xs =>
|
||||
exact congrArg some <| anti.1 _ _
|
||||
exact congrArg some <| anti _ _ (min?_mem min_eq_or rfl) h₁
|
||||
((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl))
|
||||
|
||||
|
||||
@@ -31,10 +31,13 @@ attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
/-- Alternate (non-tail-recursive) form of mapM for proofs.
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
Note that we can not have this as the main definition and replace it using a `@[csimp]` lemma,
|
||||
because they are only equal when `m` is a `LawfulMonad`.
|
||||
This is a non-tail-recursive variant of `List.mapM` that's easier to reason about. It cannot be used
|
||||
as the main definition and replaced by the tail-recursive version because they can only be proved
|
||||
equal when `m` is a `LawfulMonad`.
|
||||
-/
|
||||
def mapM' [Monad m] (f : α → m β) : List α → m (List β)
|
||||
| [] => pure []
|
||||
@@ -327,7 +330,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
simp only [forIn'_eq_foldlM]
|
||||
induction l.attach generalizing init <;> simp_all
|
||||
|
||||
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : List α) (f : (a : α) → a ∈ l → β → β) (init : β) :
|
||||
forIn' l init (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (l.attach.foldl (fun b ⟨a, h⟩ => f a h b) init) := by
|
||||
@@ -380,7 +383,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
simp only [forIn_eq_foldlM]
|
||||
induction l generalizing init <;> simp_all
|
||||
|
||||
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : List α) (f : α → β → β) (init : β) :
|
||||
forIn l init (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (l.foldl (fun b a => f a b) init) := by
|
||||
|
||||
@@ -13,7 +13,8 @@ Proves various lemmas about `List.insertIdx`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
open Function Nat
|
||||
|
||||
@@ -28,29 +29,29 @@ section InsertIdx
|
||||
variable {a : α}
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_zero (s : List α) (x : α) : insertIdx 0 x s = x :: s :=
|
||||
theorem insertIdx_zero (xs : List α) (x : α) : xs.insertIdx 0 x = x :: xs :=
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_succ_nil (n : Nat) (a : α) : insertIdx (n + 1) a [] = [] :=
|
||||
theorem insertIdx_succ_nil (n : Nat) (a : α) : ([] : List α).insertIdx (n + 1) a = [] :=
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_succ_cons (s : List α) (hd x : α) (i : Nat) :
|
||||
insertIdx (i + 1) x (hd :: s) = hd :: insertIdx i x s :=
|
||||
theorem insertIdx_succ_cons (xs : List α) (hd x : α) (i : Nat) :
|
||||
(hd :: xs).insertIdx (i + 1) x = hd :: xs.insertIdx i x :=
|
||||
rfl
|
||||
|
||||
theorem length_insertIdx : ∀ i as, (insertIdx i a as).length = if i ≤ as.length then as.length + 1 else as.length
|
||||
theorem length_insertIdx : ∀ i (as : List α), (as.insertIdx i a).length = if i ≤ as.length then as.length + 1 else as.length
|
||||
| 0, _ => by simp
|
||||
| n + 1, [] => by simp
|
||||
| n + 1, a :: as => by
|
||||
simp only [insertIdx_succ_cons, length_cons, length_insertIdx, Nat.add_le_add_iff_right]
|
||||
split <;> rfl
|
||||
|
||||
theorem length_insertIdx_of_le_length (h : i ≤ length as) : length (insertIdx i a as) = length as + 1 := by
|
||||
theorem length_insertIdx_of_le_length (h : i ≤ length as) : (as.insertIdx i a).length = as.length + 1 := by
|
||||
simp [length_insertIdx, h]
|
||||
|
||||
theorem length_insertIdx_of_length_lt (h : length as < i) : length (insertIdx i a as) = length as := by
|
||||
theorem length_insertIdx_of_length_lt (h : length as < i) : (as.insertIdx i a).length = as.length := by
|
||||
simp [length_insertIdx, h]
|
||||
|
||||
@[simp]
|
||||
@@ -60,7 +61,7 @@ theorem eraseIdx_insertIdx (i : Nat) (l : List α) : (l.insertIdx i a).eraseIdx
|
||||
|
||||
theorem insertIdx_eraseIdx_of_ge :
|
||||
∀ i m as,
|
||||
i < length as → i ≤ m → insertIdx m a (as.eraseIdx i) = (as.insertIdx (m + 1) a).eraseIdx i
|
||||
i < length as → i ≤ m → (as.eraseIdx i).insertIdx m a = (as.insertIdx (m + 1) a).eraseIdx i
|
||||
| 0, 0, [], has, _ => (Nat.lt_irrefl _ has).elim
|
||||
| 0, 0, _ :: as, _, _ => by simp [eraseIdx, insertIdx]
|
||||
| 0, _ + 1, _ :: _, _, _ => rfl
|
||||
@@ -70,7 +71,7 @@ theorem insertIdx_eraseIdx_of_ge :
|
||||
|
||||
theorem insertIdx_eraseIdx_of_le :
|
||||
∀ i j as,
|
||||
i < length as → j ≤ i → insertIdx j a (as.eraseIdx i) = (as.insertIdx j a).eraseIdx (i + 1)
|
||||
i < length as → j ≤ i → (as.eraseIdx i).insertIdx j a = (as.insertIdx j a).eraseIdx (i + 1)
|
||||
| _, 0, _ :: _, _, _ => rfl
|
||||
| n + 1, m + 1, a :: as, has, hmn =>
|
||||
congrArg (cons a) <|
|
||||
@@ -95,7 +96,7 @@ theorem mem_insertIdx {a b : α} :
|
||||
← or_assoc, @or_comm (a = a'), or_assoc, mem_cons]
|
||||
|
||||
theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length < i) :
|
||||
insertIdx i x l = l := by
|
||||
l.insertIdx i x = l := by
|
||||
induction l generalizing i with
|
||||
| nil =>
|
||||
cases i
|
||||
@@ -108,24 +109,24 @@ theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length <
|
||||
simpa using ih _ h
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_length_self (l : List α) (x : α) : insertIdx l.length x l = l ++ [x] := by
|
||||
theorem insertIdx_length_self (l : List α) (x : α) : l.insertIdx l.length x = l ++ [x] := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x l ih => simpa using ih
|
||||
|
||||
theorem length_le_length_insertIdx (l : List α) (x : α) (i : Nat) :
|
||||
l.length ≤ (insertIdx i x l).length := by
|
||||
l.length ≤ (l.insertIdx i x).length := by
|
||||
simp only [length_insertIdx]
|
||||
split <;> simp
|
||||
|
||||
theorem length_insertIdx_le_succ (l : List α) (x : α) (i : Nat) :
|
||||
(insertIdx i x l).length ≤ l.length + 1 := by
|
||||
(l.insertIdx i x).length ≤ l.length + 1 := by
|
||||
simp only [length_insertIdx]
|
||||
split <;> simp
|
||||
|
||||
theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
|
||||
(hk : j < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
(hk : j < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
induction i generalizing j l with
|
||||
| zero => simp at hn
|
||||
| succ n ih =>
|
||||
@@ -138,8 +139,8 @@ theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
|
||||
simpa using ih hn _
|
||||
|
||||
@[simp]
|
||||
theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[i] = x := by
|
||||
theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[i] = x := by
|
||||
induction l generalizing i with
|
||||
| nil =>
|
||||
simp [length_insertIdx] at hi
|
||||
@@ -153,8 +154,8 @@ theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (inser
|
||||
simpa using ih hi
|
||||
|
||||
theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
|
||||
(hk : j < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
(hk : j < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
induction l generalizing i j with
|
||||
| nil =>
|
||||
cases i with
|
||||
@@ -182,8 +183,8 @@ theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
|
||||
@[deprecated getElem_insertIdx_of_gt (since := "2025-02-04")]
|
||||
abbrev getElem_insertIdx_of_ge := @getElem_insertIdx_of_gt
|
||||
|
||||
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[j] =
|
||||
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[j] =
|
||||
if h₁ : j < i then
|
||||
l[j]'(by simp [length_insertIdx] at h; split at h <;> omega)
|
||||
else
|
||||
@@ -199,7 +200,7 @@ theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (insertIdx
|
||||
· rw [getElem_insertIdx_of_gt (by omega)]
|
||||
|
||||
theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
|
||||
(insertIdx i x l)[j]? =
|
||||
(l.insertIdx i x)[j]? =
|
||||
if j < i then
|
||||
l[j]?
|
||||
else
|
||||
@@ -230,16 +231,16 @@ theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
|
||||
split at h <;> omega
|
||||
|
||||
theorem getElem?_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (h : j < i) :
|
||||
(insertIdx i x l)[j]? = l[j]? := by
|
||||
(l.insertIdx i x)[j]? = l[j]? := by
|
||||
rw [getElem?_insertIdx, if_pos h]
|
||||
|
||||
theorem getElem?_insertIdx_self {l : List α} {x : α} {i : Nat} :
|
||||
(insertIdx i x l)[i]? = if i ≤ l.length then some x else none := by
|
||||
(l.insertIdx i x)[i]? = if i ≤ l.length then some x else none := by
|
||||
rw [getElem?_insertIdx, if_neg (by omega)]
|
||||
simp
|
||||
|
||||
theorem getElem?_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (h : i < j) :
|
||||
(insertIdx i x l)[j]? = l[j - 1]? := by
|
||||
(l.insertIdx i x)[j]? = l[j - 1]? := by
|
||||
rw [getElem?_insertIdx, if_neg (by omega), if_neg (by omega)]
|
||||
|
||||
@[deprecated getElem?_insertIdx_of_gt (since := "2025-02-04")]
|
||||
|
||||
@@ -87,75 +87,75 @@ theorem eraseIdx_modifyHead_zero {f : α → α} {l : List α} :
|
||||
|
||||
/-! ### modifyTailIdx -/
|
||||
|
||||
@[simp] theorem modifyTailIdx_id : ∀ n (l : List α), l.modifyTailIdx id n = l
|
||||
@[simp] theorem modifyTailIdx_id : ∀ i (l : List α), l.modifyTailIdx i id = l
|
||||
| 0, _ => rfl
|
||||
| _+1, [] => rfl
|
||||
| n+1, a :: l => congrArg (cons a) (modifyTailIdx_id n l)
|
||||
|
||||
theorem eraseIdx_eq_modifyTailIdx : ∀ i (l : List α), eraseIdx l i = modifyTailIdx tail i l
|
||||
theorem eraseIdx_eq_modifyTailIdx : ∀ i (l : List α), eraseIdx l i = l.modifyTailIdx i tail
|
||||
| 0, l => by cases l <;> rfl
|
||||
| _+1, [] => rfl
|
||||
| _+1, _ :: _ => congrArg (cons _) (eraseIdx_eq_modifyTailIdx _ _)
|
||||
|
||||
@[simp] theorem length_modifyTailIdx (f : List α → List α) (H : ∀ l, length (f l) = length l) :
|
||||
∀ n l, length (modifyTailIdx f n l) = length l
|
||||
| 0, _ => H _
|
||||
| _+1, [] => rfl
|
||||
| _+1, _ :: _ => congrArg (·+1) (length_modifyTailIdx _ H _ _)
|
||||
@[simp] theorem length_modifyTailIdx (f : List α → List α) (H : ∀ l, (f l).length = l.length) :
|
||||
∀ (l : List α) i, (l.modifyTailIdx i f).length = l.length
|
||||
| _, 0 => H _
|
||||
| [], _+1 => rfl
|
||||
| _ :: _, _+1 => congrArg (·+1) (length_modifyTailIdx _ H _ _)
|
||||
|
||||
theorem modifyTailIdx_add (f : List α → List α) (n) (l₁ l₂ : List α) :
|
||||
modifyTailIdx f (l₁.length + n) (l₁ ++ l₂) = l₁ ++ modifyTailIdx f n l₂ := by
|
||||
theorem modifyTailIdx_add (f : List α → List α) (i) (l₁ l₂ : List α) :
|
||||
(l₁ ++ l₂).modifyTailIdx (l₁.length + i) f = l₁ ++ l₂.modifyTailIdx i f := by
|
||||
induction l₁ <;> simp [*, Nat.succ_add]
|
||||
|
||||
theorem modifyTailIdx_eq_take_drop (f : List α → List α) (H : f [] = []) :
|
||||
∀ i l, modifyTailIdx f i l = take i l ++ f (drop i l)
|
||||
| 0, _ => rfl
|
||||
| _ + 1, [] => H.symm
|
||||
| n + 1, b :: l => congrArg (cons b) (modifyTailIdx_eq_take_drop f H n l)
|
||||
∀ (l : List α) i, l.modifyTailIdx i f = l.take i ++ f (l.drop i)
|
||||
| _, 0 => rfl
|
||||
| [], _ + 1 => H.symm
|
||||
| b :: l, i + 1 => congrArg (cons b) (modifyTailIdx_eq_take_drop f H l i)
|
||||
|
||||
theorem exists_of_modifyTailIdx (f : List α → List α) {n} {l : List α} (h : n ≤ l.length) :
|
||||
∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = n ∧ modifyTailIdx f n l = l₁ ++ f l₂ :=
|
||||
have ⟨_, _, eq, hl⟩ : ∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = n :=
|
||||
⟨_, _, (take_append_drop n l).symm, length_take_of_le h⟩
|
||||
⟨_, _, eq, hl, hl ▸ eq ▸ modifyTailIdx_add (n := 0) ..⟩
|
||||
theorem exists_of_modifyTailIdx (f : List α → List α) {i} {l : List α} (h : i ≤ l.length) :
|
||||
∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = i ∧ l.modifyTailIdx i f = l₁ ++ f l₂ := by
|
||||
obtain ⟨l₁, l₂, rfl, rfl⟩ : ∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = i :=
|
||||
⟨_, _, (take_append_drop i l).symm, length_take_of_le h⟩
|
||||
exact ⟨l₁, l₂, rfl, rfl, modifyTailIdx_add f 0 l₁ l₂⟩
|
||||
|
||||
theorem modifyTailIdx_modifyTailIdx {f g : List α → List α} (m : Nat) :
|
||||
∀ (n) (l : List α),
|
||||
(l.modifyTailIdx f n).modifyTailIdx g (m + n) =
|
||||
l.modifyTailIdx (fun l => (f l).modifyTailIdx g m) n
|
||||
theorem modifyTailIdx_modifyTailIdx {f g : List α → List α} (i : Nat) :
|
||||
∀ (j) (l : List α),
|
||||
(l.modifyTailIdx j f).modifyTailIdx (i + j) g =
|
||||
l.modifyTailIdx j (fun l => (f l).modifyTailIdx i g)
|
||||
| 0, _ => rfl
|
||||
| _ + 1, [] => rfl
|
||||
| n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx m n l)
|
||||
| n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx i n l)
|
||||
|
||||
theorem modifyTailIdx_modifyTailIdx_le {f g : List α → List α} (m n : Nat) (l : List α)
|
||||
(h : n ≤ m) :
|
||||
(l.modifyTailIdx f n).modifyTailIdx g m =
|
||||
l.modifyTailIdx (fun l => (f l).modifyTailIdx g (m - n)) n := by
|
||||
theorem modifyTailIdx_modifyTailIdx_le {f g : List α → List α} (i j : Nat) (l : List α)
|
||||
(h : j ≤ i) :
|
||||
(l.modifyTailIdx j f).modifyTailIdx i g =
|
||||
l.modifyTailIdx j (fun l => (f l).modifyTailIdx (i - j) g) := by
|
||||
rcases Nat.exists_eq_add_of_le h with ⟨m, rfl⟩
|
||||
rw [Nat.add_comm, modifyTailIdx_modifyTailIdx, Nat.add_sub_cancel]
|
||||
|
||||
theorem modifyTailIdx_modifyTailIdx_self {f g : List α → List α} (n : Nat) (l : List α) :
|
||||
(l.modifyTailIdx f n).modifyTailIdx g n = l.modifyTailIdx (g ∘ f) n := by
|
||||
rw [modifyTailIdx_modifyTailIdx_le n n l (Nat.le_refl n), Nat.sub_self]; rfl
|
||||
theorem modifyTailIdx_modifyTailIdx_self {f g : List α → List α} (i : Nat) (l : List α) :
|
||||
(l.modifyTailIdx i f).modifyTailIdx i g = l.modifyTailIdx i (g ∘ f) := by
|
||||
rw [modifyTailIdx_modifyTailIdx_le i i l (Nat.le_refl i), Nat.sub_self]; rfl
|
||||
|
||||
/-! ### modify -/
|
||||
|
||||
@[simp] theorem modify_nil (f : α → α) (i) : [].modify f i = [] := by cases i <;> rfl
|
||||
@[simp] theorem modify_nil (f : α → α) (i) : [].modify i f = [] := by cases i <;> rfl
|
||||
|
||||
@[simp] theorem modify_zero_cons (f : α → α) (a : α) (l : List α) :
|
||||
(a :: l).modify f 0 = f a :: l := rfl
|
||||
(a :: l).modify 0 f = f a :: l := rfl
|
||||
|
||||
@[simp] theorem modify_succ_cons (f : α → α) (a : α) (l : List α) (i) :
|
||||
(a :: l).modify f (i + 1) = a :: l.modify f i := by rfl
|
||||
(a :: l).modify (i + 1) f = a :: l.modify i f := by rfl
|
||||
|
||||
theorem modifyHead_eq_modify_zero (f : α → α) (l : List α) :
|
||||
l.modifyHead f = l.modify f 0 := by cases l <;> simp
|
||||
l.modifyHead f = l.modify 0 f := by cases l <;> simp
|
||||
|
||||
@[simp] theorem modify_eq_nil_iff {f : α → α} {i} {l : List α} :
|
||||
l.modify f i = [] ↔ l = [] := by cases l <;> cases i <;> simp
|
||||
l.modify i f = [] ↔ l = [] := by cases l <;> cases i <;> simp
|
||||
|
||||
theorem getElem?_modify (f : α → α) :
|
||||
∀ i (l : List α) j, (modify f i l)[j]? = (fun a => if i = j then f a else a) <$> l[j]?
|
||||
∀ i (l : List α) j, (l.modify i f)[j]? = (fun a => if i = j then f a else a) <$> l[j]?
|
||||
| n, l, 0 => by cases l <;> cases n <;> simp
|
||||
| n, [], _+1 => by cases n <;> rfl
|
||||
| 0, _ :: l, j+1 => by cases h : l[j]? <;> simp [h, modify, j.succ_ne_zero.symm]
|
||||
@@ -165,32 +165,32 @@ theorem getElem?_modify (f : α → α) :
|
||||
cases h' : l[j]? <;> by_cases h : i = j <;>
|
||||
simp [h, if_pos, if_neg, Option.map, mt Nat.succ.inj, not_false_iff, h']
|
||||
|
||||
@[simp] theorem length_modify (f : α → α) : ∀ i l, length (modify f i l) = length l :=
|
||||
@[simp] theorem length_modify (f : α → α) : ∀ (l : List α) i, (l.modify i f).length = l.length :=
|
||||
length_modifyTailIdx _ fun l => by cases l <;> rfl
|
||||
|
||||
@[simp] theorem getElem?_modify_eq (f : α → α) (i) (l : List α) :
|
||||
(modify f i l)[i]? = f <$> l[i]? := by
|
||||
(l.modify i f)[i]? = f <$> l[i]? := by
|
||||
simp only [getElem?_modify, if_pos]
|
||||
|
||||
@[simp] theorem getElem?_modify_ne (f : α → α) {i j} (l : List α) (h : i ≠ j) :
|
||||
(modify f i l)[j]? = l[j]? := by
|
||||
(l.modify i f)[j]? = l[j]? := by
|
||||
simp only [getElem?_modify, if_neg h, id_map']
|
||||
|
||||
theorem getElem_modify (f : α → α) (i) (l : List α) (j) (h : j < (modify f i l).length) :
|
||||
(modify f i l)[j] =
|
||||
theorem getElem_modify (f : α → α) (i) (l : List α) (j) (h : j < (l.modify i f).length) :
|
||||
(l.modify i f)[j] =
|
||||
if i = j then f (l[j]'(by simp at h; omega)) else l[j]'(by simp at h; omega) := by
|
||||
rw [getElem_eq_iff, getElem?_modify]
|
||||
simp at h
|
||||
simp [h]
|
||||
|
||||
@[simp] theorem getElem_modify_eq (f : α → α) (i) (l : List α) (h) :
|
||||
(modify f i l)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify]
|
||||
(l.modify i f)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify]
|
||||
|
||||
@[simp] theorem getElem_modify_ne (f : α → α) {i j} (l : List α) (h : i ≠ j) (h') :
|
||||
(modify f i l)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h]
|
||||
(l.modify i f)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h]
|
||||
|
||||
theorem modify_eq_self {f : α → α} {i} {l : List α} (h : l.length ≤ i) :
|
||||
l.modify f i = l := by
|
||||
l.modify i f = l := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m h₁ h₂
|
||||
@@ -199,7 +199,7 @@ theorem modify_eq_self {f : α → α} {i} {l : List α} (h : l.length ≤ i) :
|
||||
omega
|
||||
|
||||
theorem modify_modify_eq (f g : α → α) (i) (l : List α) :
|
||||
(modify f i l).modify g i = modify (g ∘ f) i l := by
|
||||
(l.modify i f).modify i g = l.modify i (g ∘ f) := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m h₁ h₂
|
||||
@@ -207,7 +207,7 @@ theorem modify_modify_eq (f g : α → α) (i) (l : List α) :
|
||||
split <;> simp
|
||||
|
||||
theorem modify_modify_ne (f g : α → α) {i j} (l : List α) (h : i ≠ j) :
|
||||
(modify f i l).modify g j = (l.modify g j).modify f i := by
|
||||
(l.modify i f).modify j g = (l.modify j g).modify i f := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m' h₁ h₂
|
||||
@@ -215,7 +215,7 @@ theorem modify_modify_ne (f g : α → α) {i j} (l : List α) (h : i ≠ j) :
|
||||
split <;> split <;> first | rfl | omega
|
||||
|
||||
theorem modify_eq_set [Inhabited α] (f : α → α) (i) (l : List α) :
|
||||
modify f i l = l.set i (f (l[i]?.getD default)) := by
|
||||
l.modify i f = l.set i (f (l[i]?.getD default)) := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m h₁ h₂
|
||||
@@ -227,24 +227,24 @@ theorem modify_eq_set [Inhabited α] (f : α → α) (i) (l : List α) :
|
||||
· rfl
|
||||
|
||||
theorem modify_eq_take_drop (f : α → α) :
|
||||
∀ i l, modify f i l = take i l ++ modifyHead f (drop i l) :=
|
||||
∀ (l : List α) i, l.modify i f = l.take i ++ modifyHead f (l.drop i) :=
|
||||
modifyTailIdx_eq_take_drop _ rfl
|
||||
|
||||
theorem modify_eq_take_cons_drop {f : α → α} {i} {l : List α} (h : i < l.length) :
|
||||
modify f i l = take i l ++ f l[i] :: drop (i + 1) l := by
|
||||
l.modify i f = l.take i ++ f l[i] :: l.drop (i + 1) := by
|
||||
rw [modify_eq_take_drop, drop_eq_getElem_cons h]; rfl
|
||||
|
||||
theorem exists_of_modify (f : α → α) {i} {l : List α} (h : i < l.length) :
|
||||
∃ l₁ a l₂, l = l₁ ++ a :: l₂ ∧ l₁.length = i ∧ modify f i l = l₁ ++ f a :: l₂ :=
|
||||
∃ l₁ a l₂, l = l₁ ++ a :: l₂ ∧ l₁.length = i ∧ l.modify i f = l₁ ++ f a :: l₂ :=
|
||||
match exists_of_modifyTailIdx _ (Nat.le_of_lt h) with
|
||||
| ⟨_, _::_, eq, hl, H⟩ => ⟨_, _, _, eq, hl, H⟩
|
||||
| ⟨_, [], eq, hl, _⟩ => nomatch Nat.ne_of_gt h (eq ▸ append_nil _ ▸ hl)
|
||||
|
||||
@[simp] theorem modify_id (i) (l : List α) : l.modify id i = l := by
|
||||
@[simp] theorem modify_id (i) (l : List α) : l.modify i id = l := by
|
||||
simp [modify]
|
||||
|
||||
theorem take_modify (f : α → α) (i j) (l : List α) :
|
||||
(modify f i l).take j = (take j l).modify f i := by
|
||||
(l.modify i f).take j = (l.take j).modify i f := by
|
||||
induction j generalizing l i with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
@@ -256,7 +256,7 @@ theorem take_modify (f : α → α) (i j) (l : List α) :
|
||||
| succ i => simp [ih]
|
||||
|
||||
theorem drop_modify_of_lt (f : α → α) (i j) (l : List α) (h : i < j) :
|
||||
(modify f i l).drop j = l.drop j := by
|
||||
(l.modify i f).drop j = l.drop j := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m' h₁ h₂
|
||||
@@ -265,7 +265,7 @@ theorem drop_modify_of_lt (f : α → α) (i j) (l : List α) (h : i < j) :
|
||||
omega
|
||||
|
||||
theorem drop_modify_of_ge (f : α → α) (i j) (l : List α) (h : i ≥ j) :
|
||||
(modify f i l).drop j = modify f (i - j) (drop j l) := by
|
||||
(l.modify i f).drop j = (l.drop j).modify (i - j) f := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m' h₁ h₂
|
||||
@@ -273,7 +273,7 @@ theorem drop_modify_of_ge (f : α → α) (i j) (l : List α) (h : i ≥ j) :
|
||||
split <;> split <;> first | rfl | omega
|
||||
|
||||
theorem eraseIdx_modify_of_eq (f : α → α) (i) (l : List α) :
|
||||
(modify f i l).eraseIdx i = l.eraseIdx i := by
|
||||
(l.modify i f).eraseIdx i = l.eraseIdx i := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro m h₁ h₂
|
||||
@@ -281,7 +281,7 @@ theorem eraseIdx_modify_of_eq (f : α → α) (i) (l : List α) :
|
||||
split <;> split <;> first | rfl | omega
|
||||
|
||||
theorem eraseIdx_modify_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
(modify f i l).eraseIdx j = (l.eraseIdx j).modify f (i - 1) := by
|
||||
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify (i - 1) f := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
@@ -291,7 +291,7 @@ theorem eraseIdx_modify_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
all_goals (first | rfl | omega)
|
||||
|
||||
theorem eraseIdx_modify_of_gt (f : α → α) (i j) (l : List α) (h : j > i) :
|
||||
(modify f i l).eraseIdx j = (l.eraseIdx j).modify f i := by
|
||||
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify i f := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
@@ -301,7 +301,7 @@ theorem eraseIdx_modify_of_gt (f : α → α) (i j) (l : List α) (h : j > i) :
|
||||
all_goals (first | rfl | omega)
|
||||
|
||||
theorem modify_eraseIdx_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
(l.eraseIdx i).modify f j = (l.modify f j).eraseIdx i := by
|
||||
(l.eraseIdx i).modify j f = (l.modify j f).eraseIdx i := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
@@ -311,7 +311,7 @@ theorem modify_eraseIdx_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
all_goals (first | rfl | omega)
|
||||
|
||||
theorem modify_eraseIdx_of_ge (f : α → α) (i j) (l : List α) (h : j ≥ i) :
|
||||
(l.eraseIdx i).modify f j = (l.modify f (j + 1)).eraseIdx i := by
|
||||
(l.eraseIdx i).modify j f = (l.modify (j + 1) f).eraseIdx i := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
|
||||
@@ -36,12 +36,6 @@ theorem map_getElem_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pai
|
||||
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]
|
||||
|
||||
set_option linter.listVariables false in
|
||||
@[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
|
||||
|
||||
set_option linter.listVariables false in
|
||||
/-- Given a sublist `l' <+ l`, there exists an increasing list of indices `is` such that
|
||||
`l' = is.map fun i => l[i]`. -/
|
||||
@@ -58,12 +52,6 @@ theorem sublist_eq_map_getElem {l l' : List α} (h : l' <+ l) : ∃ is : List (F
|
||||
refine ⟨⟨0, by simp [Nat.zero_lt_succ]⟩ :: is.map (·.succ), ?_⟩
|
||||
simp [Function.comp_def, pairwise_map, IH, ← get_eq_getElem, get_cons_zero, get_cons_succ']
|
||||
|
||||
set_option linter.listVariables false in
|
||||
@[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
|
||||
|
||||
set_option linter.listVariables false in
|
||||
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
|
||||
|
||||
@@ -581,8 +581,6 @@ theorem reverse_zipWith (h : l.length = l'.length) :
|
||||
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]
|
||||
|
||||
@@ -17,10 +17,11 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
namespace List
|
||||
|
||||
/--
|
||||
`ofFn f` with `f : fin n → α` returns the list whose ith element is `f i`
|
||||
```
|
||||
ofFn f = [f 0, f 1, ... , f (n - 1)]
|
||||
```
|
||||
Creates a list by applying `f` to each potential index in order, starting at `0`.
|
||||
|
||||
Examples:
|
||||
* `List.ofFn (n := 3) toString = ["0", "1", "2"]`
|
||||
* `List.ofFn (fun i => #["red", "green", "blue"].get i.val i.isLt) = ["red", "green", "blue"]`
|
||||
-/
|
||||
def ofFn {n} (f : Fin n → α) : List α := Fin.foldr n (f · :: ·) []
|
||||
|
||||
|
||||
@@ -137,8 +137,6 @@ theorem Pairwise.filterMap {S : β → β → Prop} (f : α → Option β)
|
||||
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]
|
||||
|
||||
@@ -19,7 +19,8 @@ The notation `~` is used for permutation equivalence.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
open Nat
|
||||
|
||||
@@ -522,7 +523,7 @@ theorem Perm.eraseP (f : α → Bool) {l₁ l₂ : List α}
|
||||
exact fun h h₁ h₂ => h h₂ h₁
|
||||
|
||||
theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i ≤ l.length) :
|
||||
insertIdx i x l ~ x :: l := by
|
||||
l.insertIdx i x ~ x :: l := by
|
||||
induction l generalizing i with
|
||||
| nil =>
|
||||
cases i with
|
||||
|
||||
@@ -20,10 +20,14 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
namespace List
|
||||
|
||||
/--
|
||||
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
|
||||
Merges two lists, using `le` to select the first element of the resulting list if both are
|
||||
non-empty.
|
||||
|
||||
This version is not tail-recursive,
|
||||
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
|
||||
If both input lists are sorted according to `le`, then the resulting list is also sorted according
|
||||
to `le`. `O(min |l| |r|)`.
|
||||
|
||||
This implementation is not tail-recursive, but it is replaced at runtime by a proven-equivalent
|
||||
tail-recursive merge.
|
||||
-/
|
||||
def merge (xs ys : List α) (le : α → α → Bool := by exact fun a b => a ≤ b) : List α :=
|
||||
match xs, ys with
|
||||
@@ -54,16 +58,16 @@ def MergeSort.Internal.splitInTwo (l : { l : List α // l.length = n }) :
|
||||
open MergeSort.Internal in
|
||||
set_option linter.unusedVariables false in
|
||||
/--
|
||||
Simplified implementation of stable merge sort.
|
||||
A 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.
|
||||
This function is a simplified implementation that's designed to be easy to reason about, rather than
|
||||
for efficiency. In particular, it uses the non-tail-recursive `List.merge` function and traverses
|
||||
lists unnecessarily.
|
||||
|
||||
Because we want the sort to be stable,
|
||||
it is essential that we split the list in two contiguous sublists.
|
||||
It is replaced at runtime by an efficient implementation that has been proven to be equivalent.
|
||||
-/
|
||||
-- Because we want the sort to be stable, it is essential that we split the list in two contiguous
|
||||
-- sublists.
|
||||
def mergeSort : ∀ (xs : List α) (le : α → α → Bool := by exact fun a b => a ≤ b), List α
|
||||
| [], _ => []
|
||||
| [a], _ => [a]
|
||||
|
||||
@@ -57,9 +57,6 @@ theorem take_of_length_le {l : List α} (h : l.length ≤ i) : take i l = l := b
|
||||
theorem lt_length_of_take_ne_self {l : List α} {i} (h : l.take i ≠ l) : i < l.length :=
|
||||
gt_of_not_le (mt take_of_length_le h)
|
||||
|
||||
@[deprecated drop_of_length_le (since := "2024-07-07")] abbrev drop_length_le := @drop_of_length_le
|
||||
@[deprecated take_of_length_le (since := "2024-07-07")] abbrev take_length_le := @take_of_length_le
|
||||
|
||||
@[simp] theorem drop_length (l : List α) : drop l.length l = [] := drop_of_length_le (Nat.le_refl _)
|
||||
|
||||
@[simp] theorem take_length (l : List α) : take l.length l = l := take_of_length_le (Nat.le_refl _)
|
||||
@@ -206,13 +203,6 @@ theorem take_succ_eq_append_getElem {i} {l : List α} (h : i < l.length) : l.tak
|
||||
| x :: xs =>
|
||||
simpa using take_append_getLast (x :: xs) (by simp)
|
||||
|
||||
@[deprecated take_succ_cons (since := "2024-07-25")]
|
||||
theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
|
||||
@[deprecated take_of_length_le (since := "2024-07-25")]
|
||||
theorem take_all_of_le {l : List α} {i} (h : length l ≤ i) : take i l = l :=
|
||||
take_of_length_le h
|
||||
|
||||
theorem drop_left : ∀ l₁ l₂ : List α, drop (length l₁) (l₁ ++ l₂) = l₂
|
||||
| [], _ => rfl
|
||||
| _ :: l₁, l₂ => drop_left l₁ l₂
|
||||
@@ -238,16 +228,6 @@ theorem take_succ {l : List α} {i : Nat} : l.take (i + 1) = l.take i ++ l[i]?.t
|
||||
· simp only [take, Option.toList, getElem?_cons_zero, nil_append]
|
||||
· simp only [take, hl, getElem?_cons_succ, cons_append]
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2024-07-25")]
|
||||
theorem drop_sizeOf_le [SizeOf α] (l : List α) (i : Nat) : sizeOf (l.drop i) ≤ sizeOf l := by
|
||||
induction l generalizing i with
|
||||
| nil => rw [drop_nil]; apply Nat.le_refl
|
||||
| cons _ _ lih =>
|
||||
induction i with
|
||||
| zero => apply Nat.le_refl
|
||||
| succ n =>
|
||||
exact Trans.trans (lih _) (Nat.le_add_left _ _)
|
||||
|
||||
theorem dropLast_eq_take (l : List α) : l.dropLast = l.take (l.length - 1) := by
|
||||
cases l with
|
||||
| nil => simp [dropLast]
|
||||
|
||||
@@ -16,7 +16,8 @@ We prefer to pull `List.toArray` outwards past `Array` operations.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
@@ -537,11 +538,16 @@ private theorem popWhile_toArray_aux (p : α → Bool) (l : List α) :
|
||||
· simp
|
||||
· simp_all [List.set_eq_of_length_le]
|
||||
|
||||
@[simp] theorem toArray_replicate (n : Nat) (v : α) : (List.replicate n v).toArray = mkArray n v := rfl
|
||||
@[simp] theorem toArray_replicate (n : Nat) (v : α) :
|
||||
(List.replicate n v).toArray = Array.replicate n v := rfl
|
||||
|
||||
theorem _root_.Array.mkArray_eq_toArray_replicate : mkArray n v = (List.replicate n v).toArray := by
|
||||
theorem _root_.Array.replicate_eq_toArray_replicate :
|
||||
Array.replicate n v = (List.replicate n v).toArray := by
|
||||
simp
|
||||
|
||||
@[deprecated _root_.Array.replicate_eq_toArray_replicate (since := "2025-03-18")]
|
||||
abbrev _root_.Array.mkArray_eq_toArray_replicate := @_root_.Array.replicate_eq_toArray_replicate
|
||||
|
||||
@[simp] theorem flatMap_empty {β} (f : α → Array β) : (#[] : Array α).flatMap f = #[] := rfl
|
||||
|
||||
theorem flatMap_toArray_cons {β} (f : α → Array β) (a : α) (as : List α) :
|
||||
|
||||
@@ -18,7 +18,13 @@ def List.toArrayAux : List α → Array α → Array α
|
||||
| nil, xs => xs
|
||||
| cons a as, xs => toArrayAux as (xs.push a)
|
||||
|
||||
/-- Convert a `List α` into an `Array α`. This is O(n) in the length of the list. -/
|
||||
/--
|
||||
Converts a `List α` into an `Array α` by repeatedly pushing elements from the list onto an empty
|
||||
array. `O(|xs|)`.
|
||||
|
||||
Use `List.toArray` instead of calling this function directly. At runtime, this operation implements
|
||||
both `List.toArray` and `Array.mk`.
|
||||
-/
|
||||
-- This function is exported to C, where it is called by `Array.mk`
|
||||
-- (the constructor) to implement this functionality.
|
||||
@[inline, match_pattern, pp_nodot, export lean_list_to_array]
|
||||
|
||||
@@ -135,8 +135,6 @@ theorem take_zipWith : (zipWith f l l').take i = zipWith f (l.take i) (l'.take i
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
@[deprecated take_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_take := @take_zipWith
|
||||
|
||||
theorem drop_zipWith : (zipWith f l l').drop i = zipWith f (l.drop i) (l'.drop i) := by
|
||||
induction l generalizing l' i with
|
||||
| nil => simp
|
||||
@@ -147,14 +145,10 @@ theorem drop_zipWith : (zipWith f l l').drop i = zipWith f (l.drop i) (l'.drop i
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
@[deprecated drop_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_drop := @drop_zipWith
|
||||
|
||||
@[simp]
|
||||
theorem tail_zipWith : (zipWith f l l').tail = zipWith f l.tail l'.tail := by
|
||||
rw [← drop_one]; simp [drop_zipWith]
|
||||
|
||||
@[deprecated tail_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_tail := @tail_zipWith
|
||||
|
||||
theorem zipWith_append (f : α → β → γ) (l₁ l₁' : List α) (l₂ l₂' : List β)
|
||||
(h : l₁.length = l₂.length) :
|
||||
zipWith f (l₁ ++ l₁') (l₂ ++ l₂') = zipWith f l₁ l₂ ++ zipWith f l₁' l₂' := by
|
||||
@@ -295,8 +289,6 @@ theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip
|
||||
· have := of_mem_zip h
|
||||
exact ⟨Mem.tail _ this.1, Mem.tail _ this.2⟩
|
||||
|
||||
@[deprecated of_mem_zip (since := "2024-07-28")] abbrev mem_zip := @of_mem_zip
|
||||
|
||||
theorem map_fst_zip :
|
||||
∀ (l₁ : List α) (l₂ : List β), l₁.length ≤ l₂.length → map Prod.fst (zip l₁ l₂) = l₁
|
||||
| [], _, _ => rfl
|
||||
@@ -420,9 +412,6 @@ theorem map_zipWithAll {δ : Type _} (f : α → β) (g : Option γ → Option
|
||||
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[deprecated unzip_fst (since := "2024-07-28")] abbrev unzip_left := @unzip_fst
|
||||
@[deprecated unzip_snd (since := "2024-07-28")] abbrev unzip_right := @unzip_snd
|
||||
|
||||
theorem unzip_eq_map : ∀ l : List (α × β), unzip l = (l.map Prod.fst, l.map Prod.snd)
|
||||
| [] => rfl
|
||||
| (a, b) :: l => by simp only [unzip_cons, map_cons, unzip_eq_map l]
|
||||
|
||||
@@ -23,35 +23,69 @@ private theorem rec_eq_recCompiled : @Nat.rec = @Nat.recCompiled :=
|
||||
funext fun _ => funext fun _ => funext fun succ => funext fun t =>
|
||||
Nat.recOn t rfl (fun n ih => congrArg (succ n) ih)
|
||||
|
||||
/-- Recursor identical to `Nat.rec` but uses notations `0` for `Nat.zero` and `· + 1` for `Nat.succ`.
|
||||
Used as the default `Nat` eliminator by the `induction` tactic. -/
|
||||
/--
|
||||
A recursor for `Nat` that uses the notations `0` for `Nat.zero` and `n + 1` for `Nat.succ`.
|
||||
|
||||
It is otherwise identical to the default recursor `Nat.rec`. It is used by the `induction` tactic
|
||||
by default for `Nat`.
|
||||
-/
|
||||
@[elab_as_elim, induction_eliminator]
|
||||
protected abbrev recAux {motive : Nat → Sort u} (zero : motive 0) (succ : (n : Nat) → motive n → motive (n + 1)) (t : Nat) : motive t :=
|
||||
Nat.rec zero succ t
|
||||
|
||||
/-- Recursor identical to `Nat.casesOn` but uses notations `0` for `Nat.zero` and `· + 1` for `Nat.succ`.
|
||||
Used as the default `Nat` eliminator by the `cases` tactic. -/
|
||||
/--
|
||||
A case analysis principle for `Nat` that uses the notations `0` for `Nat.zero` and `n + 1` for
|
||||
`Nat.succ`.
|
||||
|
||||
It is otherwise identical to the default recursor `Nat.casesOn`. It is used as the default `Nat`
|
||||
case analysis principle for `Nat` by the `cases` tactic.
|
||||
-/
|
||||
@[elab_as_elim, cases_eliminator]
|
||||
protected abbrev casesAuxOn {motive : Nat → Sort u} (t : Nat) (zero : motive 0) (succ : (n : Nat) → motive (n + 1)) : motive t :=
|
||||
Nat.casesOn t zero succ
|
||||
|
||||
|
||||
/--
|
||||
`Nat.repeat f n a` is `f^(n) a`; that is, it iterates `f` `n` times on `a`.
|
||||
Applies a function to a starting value the specified number of times.
|
||||
|
||||
In other words, `f` is iterated `n` times on `a`.
|
||||
|
||||
Examples:
|
||||
* `Nat.repeat f 3 a = f <| f <| f <| a`
|
||||
* `Nat.repeat (· ++ "!") 4 "Hello" = "Hello!!!!"`
|
||||
-/
|
||||
@[specialize] def repeat {α : Type u} (f : α → α) : (n : Nat) → (a : α) → α
|
||||
| 0, a => a
|
||||
| succ n, a => f (repeat f n a)
|
||||
|
||||
/-- Tail-recursive version of `Nat.repeat`. -/
|
||||
/--
|
||||
Applies a function to a starting value the specified number of times.
|
||||
|
||||
In other words, `f` is iterated `n` times on `a`.
|
||||
|
||||
This is a tail-recursive version of `Nat.repeat` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `Nat.repeatTR f 3 a = f <| f <| f <| a`
|
||||
* `Nat.repeatTR (· ++ "!") 4 "Hello" = "Hello!!!!"`
|
||||
-/
|
||||
@[inline] def repeatTR {α : Type u} (f : α → α) (n : Nat) (a : α) : α :=
|
||||
let rec @[specialize] loop
|
||||
| 0, a => a
|
||||
| succ n, a => loop n (f a)
|
||||
loop n a
|
||||
|
||||
/-- Boolean less-than of natural numbers. -/
|
||||
/--
|
||||
The Boolean less-than comparison on natural numbers.
|
||||
|
||||
This function is overridden in both the kernel and the compiler to efficiently evaluate using the
|
||||
arbitrary-precision arithmetic library. The definition provided here is the logical model.
|
||||
|
||||
Examples:
|
||||
* `Nat.blt 2 5 = true`
|
||||
* `Nat.blt 5 2 = false`
|
||||
* `Nat.blt 5 5 = false`
|
||||
-/
|
||||
def blt (a b : Nat) : Bool :=
|
||||
ble a.succ b
|
||||
|
||||
@@ -500,6 +534,9 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
|
||||
@[simp] protected theorem add_le_add_iff_right {n : Nat} : m + n ≤ k + n ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_right, fun h => Nat.add_le_add_right h _⟩
|
||||
|
||||
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m ≤ n + k ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _⟩
|
||||
|
||||
/-! ### le/lt -/
|
||||
|
||||
protected theorem lt_asymm {a b : Nat} (h : a < b) : ¬ b < a := Nat.not_lt.2 (Nat.le_of_lt h)
|
||||
@@ -709,6 +746,16 @@ protected theorem le_of_mul_le_mul_left {a b c : Nat} (h : c * a ≤ c * b) (hc
|
||||
have h' : c * b < c * a := Nat.mul_lt_mul_of_pos_left hlt hc
|
||||
absurd h (Nat.not_le_of_gt h')
|
||||
|
||||
protected theorem le_of_mul_le_mul_right {a b c : Nat} (h : a * c ≤ b * c) (hc : 0 < c) : a ≤ b := by
|
||||
rw [Nat.mul_comm a c, Nat.mul_comm b c] at h
|
||||
exact Nat.le_of_mul_le_mul_left h hc
|
||||
|
||||
protected theorem mul_le_mul_left_iff {n m k : Nat} (w : 0 < k) : k * n ≤ k * m ↔ n ≤ m :=
|
||||
⟨fun h => Nat.le_of_mul_le_mul_left h w, fun h => mul_le_mul_left _ h⟩
|
||||
|
||||
protected theorem mul_le_mul_right_iff {n m k : Nat} (w : 0 < k) : n * k ≤ m * k ↔ n ≤ m :=
|
||||
⟨fun h => Nat.le_of_mul_le_mul_right h w, fun h => mul_le_mul_right _ h⟩
|
||||
|
||||
protected theorem eq_of_mul_eq_mul_left {m k n : Nat} (hn : 0 < n) (h : n * m = n * k) : m = k :=
|
||||
Nat.le_antisymm (Nat.le_of_mul_le_mul_left (Nat.le_of_eq h) hn)
|
||||
(Nat.le_of_mul_le_mul_left (Nat.le_of_eq h.symm) hn)
|
||||
@@ -772,9 +819,15 @@ instance {n m : Nat} [NeZero n] : NeZero (n^m) :=
|
||||
/-! # min/max -/
|
||||
|
||||
/--
|
||||
`Nat.min a b` is the minimum of `a` and `b`:
|
||||
* if `a ≤ b` then `Nat.min a b = a`
|
||||
* if `b ≤ a` then `Nat.min a b = b`
|
||||
Returns the lesser of two natural numbers. Usually accessed via `Min.min`.
|
||||
|
||||
Returns `n` if `n ≤ m`, or `m` if `m ≤ n`.
|
||||
|
||||
Examples:
|
||||
* `min 0 5 = 0`
|
||||
* `min 4 5 = 4`
|
||||
* `min 4 3 = 3`
|
||||
* `min 8 8 = 8`
|
||||
-/
|
||||
protected abbrev min (n m : Nat) := min n m
|
||||
|
||||
@@ -783,9 +836,15 @@ protected theorem min_def {n m : Nat} : min n m = if n ≤ m then n else m := rf
|
||||
instance : Max Nat := maxOfLe
|
||||
|
||||
/--
|
||||
`Nat.max a b` is the maximum of `a` and `b`:
|
||||
* if `a ≤ b` then `Nat.max a b = b`
|
||||
* if `b ≤ a` then `Nat.max a b = a`
|
||||
Returns the greater of two natural numbers. Usually accessed via `Max.max`.
|
||||
|
||||
Returns `m` if `n ≤ m`, or `n` if `m ≤ n`.
|
||||
|
||||
Examples:
|
||||
* `max 0 5 = 5`
|
||||
* `max 4 5 = 5`
|
||||
* `max 4 3 = 4`
|
||||
* `max 8 8 = 8`
|
||||
-/
|
||||
protected abbrev max (n m : Nat) := max n m
|
||||
|
||||
|
||||
@@ -13,6 +13,12 @@ namespace Nat
|
||||
theorem bitwise_rec_lemma {n : Nat} (hNe : n ≠ 0) : n / 2 < n :=
|
||||
Nat.div_lt_self (Nat.zero_lt_of_ne_zero hNe) (Nat.lt_succ_self _)
|
||||
|
||||
/--
|
||||
A helper for implementing bitwise operators on `Nat`.
|
||||
|
||||
Each bit of the resulting `Nat` is the result of applying `f` to the corresponding bits of the input
|
||||
`Nat`s, up to the position of the highest set bit in either input.
|
||||
-/
|
||||
def bitwise (f : Bool → Bool → Bool) (n m : Nat) : Nat :=
|
||||
if n = 0 then
|
||||
if f false true then m else 0
|
||||
@@ -30,16 +36,56 @@ def bitwise (f : Bool → Bool → Bool) (n m : Nat) : Nat :=
|
||||
r+r
|
||||
decreasing_by apply bitwise_rec_lemma; assumption
|
||||
|
||||
/--
|
||||
Bitwise and. Usually accessed via the `&&&` operator.
|
||||
|
||||
Each bit of the resulting value is set if the corresponding bit is set in both of the inputs.
|
||||
-/
|
||||
@[extern "lean_nat_land"]
|
||||
def land : @& Nat → @& Nat → Nat := bitwise and
|
||||
|
||||
/--
|
||||
Bitwise or. Usually accessed via the `|||` operator.
|
||||
|
||||
Each bit of the resulting value is set if the corresponding bit is set in at least one of the inputs.
|
||||
-/
|
||||
@[extern "lean_nat_lor"]
|
||||
def lor : @& Nat → @& Nat → Nat := bitwise or
|
||||
|
||||
/--
|
||||
Bitwise exclusive or. Usually accessed via the `^^^` operator.
|
||||
|
||||
Each bit of the resulting value is set if the corresponding bit is set in exactly one of the inputs.
|
||||
-/
|
||||
@[extern "lean_nat_lxor"]
|
||||
def xor : @& Nat → @& Nat → Nat := bitwise bne
|
||||
|
||||
/--
|
||||
Shifts the binary representation of a value left by the specified number of bits. Usually accessed
|
||||
via the `<<<` operator.
|
||||
|
||||
Examples:
|
||||
* `1 <<< 2 = 4`
|
||||
* `1 <<< 3 = 8`
|
||||
* `0 <<< 3 = 0`
|
||||
* `0xf1 <<< 4 = 0xf10`
|
||||
-/
|
||||
@[extern "lean_nat_shiftl"]
|
||||
def shiftLeft : @& Nat → @& Nat → Nat
|
||||
| n, 0 => n
|
||||
| n, succ m => shiftLeft (2*n) m
|
||||
|
||||
/--
|
||||
Shifts the binary representation of a value right by the specified number of bits. Usually accessed
|
||||
via the `>>>` operator.
|
||||
|
||||
Examples:
|
||||
* `4 >>> 2 = 1`
|
||||
* `8 >>> 2 = 2`
|
||||
* `8 >>> 3 = 1`
|
||||
* `0 >>> 3 = 0`
|
||||
* `0xf13a >>> 8 = 0xf1`
|
||||
-/
|
||||
@[extern "lean_nat_shiftr"]
|
||||
def shiftRight : @& Nat → @& Nat → Nat
|
||||
| n, 0 => n
|
||||
@@ -84,7 +130,9 @@ We define an operation for testing individual bits in the binary representation
|
||||
of a number.
|
||||
-/
|
||||
|
||||
/-- `testBit m n` returns whether the `(n+1)` least significant bit is `1` or `0`-/
|
||||
/--
|
||||
Returns `true` if the `(n+1)`th least significant bit is `1`, or `false` if it is `0`.
|
||||
-/
|
||||
def testBit (m n : Nat) : Bool :=
|
||||
-- `1 &&& n` is faster than `n &&& 1` for big `n`.
|
||||
1 &&& (m >>> n) != 0
|
||||
|
||||
@@ -36,7 +36,10 @@ private theorem two_mul_sub_one {n : Nat} (n_pos : n > 0) : (2*n - 1) % 2 = 1 :=
|
||||
/-! ### Preliminaries -/
|
||||
|
||||
/--
|
||||
An induction principal that works on division by two.
|
||||
An induction principle for the natural numbers with two cases:
|
||||
* `n = 0`, and the motive is satisfied for `0`
|
||||
* `n > 0`, and the motive should be satisfied for `n` on the assumption that it is satisfied for
|
||||
`n / 2`.
|
||||
-/
|
||||
noncomputable def div2Induction {motive : Nat → Sort u}
|
||||
(n : Nat) (ind : ∀(n : Nat), (n > 0 → motive (n/2)) → motive n) : motive n := by
|
||||
@@ -493,19 +496,25 @@ theorem and_lt_two_pow (x : Nat) {y n : Nat} (right : y < 2^n) : (x &&& y) < 2^n
|
||||
exact pow_le_pow_right Nat.zero_lt_two i_ge_n
|
||||
simp [testBit_and, yf]
|
||||
|
||||
@[simp] theorem and_pow_two_sub_one_eq_mod (x n : Nat) : x &&& 2^n - 1 = x % 2^n := by
|
||||
@[simp] theorem and_two_pow_sub_one_eq_mod (x n : Nat) : x &&& 2^n - 1 = x % 2^n := by
|
||||
apply eq_of_testBit_eq
|
||||
intro i
|
||||
simp only [testBit_and, testBit_mod_two_pow]
|
||||
cases testBit x i <;> simp
|
||||
|
||||
@[deprecated and_pow_two_sub_one_eq_mod (since := "2024-09-11")] abbrev and_pow_two_is_mod := @and_pow_two_sub_one_eq_mod
|
||||
@[deprecated and_two_pow_sub_one_eq_mod (since := "2025-03-18")]
|
||||
abbrev and_pow_two_sub_one_eq_mod := @and_two_pow_sub_one_eq_mod
|
||||
@[deprecated and_two_pow_sub_one_eq_mod (since := "2024-09-11")]
|
||||
abbrev and_pow_two_is_mod := @and_two_pow_sub_one_eq_mod
|
||||
|
||||
theorem and_pow_two_sub_one_of_lt_two_pow {x : Nat} (lt : x < 2^n) : x &&& 2^n - 1 = x := by
|
||||
rw [and_pow_two_sub_one_eq_mod]
|
||||
theorem and_two_pow_sub_one_of_lt_two_pow {x : Nat} (lt : x < 2^n) : x &&& 2^n - 1 = x := by
|
||||
rw [and_two_pow_sub_one_eq_mod]
|
||||
apply Nat.mod_eq_of_lt lt
|
||||
|
||||
@[deprecated and_pow_two_sub_one_of_lt_two_pow (since := "2024-09-11")] abbrev and_two_pow_identity := @and_pow_two_sub_one_of_lt_two_pow
|
||||
@[deprecated and_two_pow_sub_one_of_lt_two_pow (since := "2025-03-18")]
|
||||
abbrev and_pow_two_sub_one_of_lt_two_pow := @and_two_pow_sub_one_of_lt_two_pow
|
||||
@[deprecated and_two_pow_sub_one_of_lt_two_pow (since := "2024-09-11")]
|
||||
abbrev and_two_pow_identity := @and_two_pow_sub_one_of_lt_two_pow
|
||||
|
||||
@[simp] theorem and_mod_two_eq_one : (a &&& b) % 2 = 1 ↔ a % 2 = 1 ∧ b % 2 = 1 := by
|
||||
simp only [mod_two_eq_one_iff_testBit_zero]
|
||||
@@ -648,7 +657,7 @@ theorem xor_div_two : (a ^^^ b) / 2 = a / 2 ^^^ b / 2 :=
|
||||
|
||||
/-! ### Arithmetic -/
|
||||
|
||||
theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat) :
|
||||
theorem testBit_two_pow_mul_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat) :
|
||||
testBit (2 ^ i * a + b) j =
|
||||
if j < i then
|
||||
testBit b j
|
||||
@@ -676,19 +685,26 @@ theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat)
|
||||
Nat.div_eq_of_lt b_lt,
|
||||
Nat.two_pow_pos i]
|
||||
|
||||
theorem testBit_mul_pow_two :
|
||||
@[deprecated testBit_two_pow_mul_add (since := "2025-03-18")]
|
||||
abbrev testBit_mul_pow_two_add := @testBit_two_pow_mul_add
|
||||
|
||||
theorem testBit_two_pow_mul :
|
||||
testBit (2 ^ i * a) j = (decide (j ≥ i) && testBit a (j-i)) := by
|
||||
have gen := testBit_mul_pow_two_add a (Nat.two_pow_pos i) j
|
||||
have gen := testBit_two_pow_mul_add a (Nat.two_pow_pos i) j
|
||||
simp at gen
|
||||
rw [gen]
|
||||
cases Nat.lt_or_ge j i with
|
||||
| _ p => simp [p, Nat.not_le_of_lt, Nat.not_lt_of_le]
|
||||
|
||||
theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^i * a ||| b := by
|
||||
@[deprecated testBit_two_pow_mul (since := "2025-03-18")]
|
||||
abbrev testBit_mul_pow_two := @testBit_two_pow_mul
|
||||
|
||||
theorem two_pow_add_eq_or_of_lt {b : Nat} (b_lt : b < 2^i) (a : Nat) :
|
||||
2^i * a + b = 2^i * a ||| b := by
|
||||
apply eq_of_testBit_eq
|
||||
intro j
|
||||
simp only [testBit_mul_pow_two_add _ b_lt,
|
||||
testBit_or, testBit_mul_pow_two]
|
||||
simp only [testBit_two_pow_mul_add _ b_lt,
|
||||
testBit_or, testBit_two_pow_mul]
|
||||
if j_lt : j < i then
|
||||
simp [Nat.not_le_of_lt, j_lt]
|
||||
else
|
||||
@@ -698,11 +714,14 @@ theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^
|
||||
_ ≤ 2 ^ j := Nat.pow_le_pow_right Nat.zero_lt_two i_le
|
||||
simp [i_le, j_lt, testBit_lt_two_pow, b_lt_j]
|
||||
|
||||
@[deprecated two_pow_add_eq_or_of_lt (since := "2025-03-18")]
|
||||
abbrev mul_add_lt_is_or := @two_pow_add_eq_or_of_lt
|
||||
|
||||
/-! ### shiftLeft and shiftRight -/
|
||||
|
||||
@[simp] theorem testBit_shiftLeft (x : Nat) : testBit (x <<< i) j =
|
||||
(decide (j ≥ i) && testBit x (j-i)) := by
|
||||
simp [shiftLeft_eq, Nat.mul_comm _ (2^_), testBit_mul_pow_two]
|
||||
simp [shiftLeft_eq, Nat.mul_comm _ (2^_), testBit_two_pow_mul]
|
||||
|
||||
@[simp] theorem testBit_shiftRight (x : Nat) : testBit (x >>> i) j = testBit x (i+j) := by
|
||||
simp [testBit, ←shiftRight_add]
|
||||
|
||||
@@ -8,33 +8,79 @@ import Init.Control.Basic
|
||||
import Init.Data.Nat.Basic
|
||||
import Init.Omega
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
namespace Nat
|
||||
universe u v
|
||||
|
||||
/--
|
||||
Executes a monadic action on all the numbers less than some bound, in increasing order.
|
||||
|
||||
Example:
|
||||
````lean example
|
||||
#eval Nat.forM 5 fun i _ => IO.println i
|
||||
````
|
||||
````output
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
````
|
||||
-/
|
||||
@[inline] def forM {m} [Monad m] (n : Nat) (f : (i : Nat) → i < n → m Unit) : m Unit :=
|
||||
let rec @[specialize] loop : ∀ i, i ≤ n → m Unit
|
||||
| 0, _ => pure ()
|
||||
| i+1, h => do f (n-i-1) (by omega); loop i (Nat.le_of_succ_le h)
|
||||
loop n (by simp)
|
||||
|
||||
/--
|
||||
Executes a monadic action on all the numbers less than some bound, in decreasing order.
|
||||
|
||||
Example:
|
||||
````lean example
|
||||
#eval Nat.forRevM 5 fun i _ => IO.println i
|
||||
````
|
||||
````output
|
||||
4
|
||||
3
|
||||
2
|
||||
1
|
||||
0
|
||||
````
|
||||
-/
|
||||
@[inline] def forRevM {m} [Monad m] (n : Nat) (f : (i : Nat) → i < n → m Unit) : m Unit :=
|
||||
let rec @[specialize] loop : ∀ i, i ≤ n → m Unit
|
||||
| 0, _ => pure ()
|
||||
| i+1, h => do f i (by omega); loop i (Nat.le_of_succ_le h)
|
||||
loop n (by simp)
|
||||
|
||||
/--
|
||||
Iterates the application of a monadic function `f` to a starting value `init`, `n` times. At each
|
||||
step, `f` is applied to the current value and to the next natural number less than `n`, in
|
||||
increasing order.
|
||||
-/
|
||||
@[inline] def foldM {α : Type u} {m : Type u → Type v} [Monad m] (n : Nat) (f : (i : Nat) → i < n → α → m α) (init : α) : m α :=
|
||||
let rec @[specialize] loop : ∀ i, i ≤ n → α → m α
|
||||
| 0, h, a => pure a
|
||||
| i+1, h, a => f (n-i-1) (by omega) a >>= loop i (Nat.le_of_succ_le h)
|
||||
loop n (by omega) init
|
||||
|
||||
/--
|
||||
Iterates the application of a monadic function `f` to a starting value `init`, `n` times. At each
|
||||
step, `f` is applied to the current value and to the next natural number less than `n`, in
|
||||
decreasing order.
|
||||
-/
|
||||
@[inline] def foldRevM {α : Type u} {m : Type u → Type v} [Monad m] (n : Nat) (f : (i : Nat) → i < n → α → m α) (init : α) : m α :=
|
||||
let rec @[specialize] loop : ∀ i, i ≤ n → α → m α
|
||||
| 0, h, a => pure a
|
||||
| i+1, h, a => f i (by omega) a >>= loop i (Nat.le_of_succ_le h)
|
||||
loop n (by omega) init
|
||||
|
||||
/--
|
||||
Checks whether the monadic predicate `p` returns `true` for all numbers less that the given bound.
|
||||
Numbers are checked in increasing order until `p` returns false, after which no further are checked.
|
||||
-/
|
||||
@[inline] def allM {m} [Monad m] (n : Nat) (p : (i : Nat) → i < n → m Bool) : m Bool :=
|
||||
let rec @[specialize] loop : ∀ i, i ≤ n → m Bool
|
||||
| 0, _ => pure true
|
||||
@@ -44,6 +90,11 @@ universe u v
|
||||
| false => pure false
|
||||
loop n (by simp)
|
||||
|
||||
/--
|
||||
Checks whether there is some number less that the given bound for which the monadic predicate `p`
|
||||
returns `true`. Numbers are checked in increasing order until `p` returns true, after which
|
||||
no further are checked.
|
||||
-/
|
||||
@[inline] def anyM {m} [Monad m] (n : Nat) (p : (i : Nat) → i < n → m Bool) : m Bool :=
|
||||
let rec @[specialize] loop : ∀ i, i ≤ n → m Bool
|
||||
| 0, _ => pure false
|
||||
|
||||
@@ -20,21 +20,76 @@ instance : Dvd Nat where
|
||||
theorem div_rec_lemma {x y : Nat} : 0 < y ∧ y ≤ x → x - y < x :=
|
||||
fun ⟨ypos, ylex⟩ => sub_lt (Nat.lt_of_lt_of_le ypos ylex) ypos
|
||||
|
||||
@[extern "lean_nat_div"]
|
||||
theorem div_rec_fuel_lemma {x y fuel : Nat} (hy : 0 < y) (hle : y ≤ x) (hfuel : x < fuel + 1) :
|
||||
x - y < fuel :=
|
||||
Nat.lt_of_lt_of_le (div_rec_lemma ⟨hy, hle⟩) (Nat.le_of_lt_succ hfuel)
|
||||
|
||||
/--
|
||||
Division of natural numbers, discarding the remainder. Division by `0` returns `0`. Usually accessed
|
||||
via the `/` operator.
|
||||
|
||||
This operation is sometimes called “floor division.”
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `21 / 3 = 7`
|
||||
* `21 / 5 = 4`
|
||||
* `0 / 22 = 0`
|
||||
* `5 / 0 = 0`
|
||||
-/
|
||||
@[extern "lean_nat_div", irreducible]
|
||||
protected def div (x y : @& Nat) : Nat :=
|
||||
if 0 < y ∧ y ≤ x then
|
||||
Nat.div (x - y) y + 1
|
||||
if hy : 0 < y then
|
||||
let rec
|
||||
go (fuel : Nat) (x : Nat) (hfuel : x < fuel) : Nat :=
|
||||
match fuel with
|
||||
| 0 => by contradiction
|
||||
| succ fuel =>
|
||||
if h : y ≤ x then
|
||||
go fuel (x - y) (div_rec_fuel_lemma hy h hfuel) + 1
|
||||
else
|
||||
0
|
||||
termination_by structural fuel
|
||||
go (x + 1) x (Nat.lt_succ_self _)
|
||||
else
|
||||
0
|
||||
decreasing_by apply div_rec_lemma; assumption
|
||||
|
||||
instance instDiv : Div Nat := ⟨Nat.div⟩
|
||||
|
||||
theorem div_eq (x y : Nat) : x / y = if 0 < y ∧ y ≤ x then (x - y) / y + 1 else 0 := by
|
||||
show Nat.div x y = _
|
||||
rw [Nat.div]
|
||||
rfl
|
||||
private theorem div.go.fuel_congr (x y fuel1 fuel2 : Nat) (hy : 0 < y) (h1 : x < fuel1) (h2 : x < fuel2) :
|
||||
Nat.div.go y hy fuel1 x h1 = Nat.div.go y hy fuel2 x h2 := by
|
||||
match fuel1, fuel2 with
|
||||
| 0, _ => contradiction
|
||||
| _, 0 => contradiction
|
||||
| succ fuel1, succ fuel2 =>
|
||||
simp only [Nat.div.go]
|
||||
split
|
||||
next => rw [Nat.div.go.fuel_congr]
|
||||
next => rfl
|
||||
termination_by structural fuel1
|
||||
|
||||
theorem div_eq (x y : Nat) : x / y = if 0 < y ∧ y ≤ x then (x - y) / y + 1 else 0 := by
|
||||
show Nat.div _ _ = ite _ (Nat.div _ _ + 1) _
|
||||
unfold Nat.div
|
||||
split
|
||||
next =>
|
||||
rw [Nat.div.go]
|
||||
split
|
||||
next =>
|
||||
simp only [and_self, ↓reduceIte, *]
|
||||
congr 1
|
||||
apply div.go.fuel_congr
|
||||
next =>
|
||||
simp only [and_false, ↓reduceIte, *]
|
||||
next =>
|
||||
simp only [false_and, ↓reduceIte, *]
|
||||
|
||||
/--
|
||||
An induction principle customized for reasoning about the recursion pattern of natural number
|
||||
division by iterated subtraction.
|
||||
-/
|
||||
def div.inductionOn.{u}
|
||||
{motive : Nat → Nat → Sort u}
|
||||
(x y : Nat)
|
||||
@@ -72,22 +127,100 @@ theorem div_lt_self {n k : Nat} (hLtN : 0 < n) (hLtK : 1 < k) : n / k < n := by
|
||||
have := Nat.add_le_of_le_sub hKN this
|
||||
exact Nat.lt_of_lt_of_le (Nat.add_lt_add_left hLtK _) this
|
||||
|
||||
@[extern "lean_nat_mod"]
|
||||
protected def modCore (x y : @& Nat) : Nat :=
|
||||
/--
|
||||
The modulo operator, which computes the remainder when dividing one natural number by another.
|
||||
Usually accessed via the `%` operator. When the divisor is `0`, the result is the dividend rather
|
||||
than an error.
|
||||
|
||||
This is the core implementation of `Nat.mod`. It computes the correct result for any two closed
|
||||
natural numbers, but it does not have some convenient [definitional
|
||||
reductions](lean-manual://section/type-system) when the `Nat`s contain free variables. The wrapper
|
||||
`Nat.mod` handles those cases specially and then calls `Nat.modCore`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is the
|
||||
logical model.
|
||||
-/
|
||||
@[extern "lean_nat_mod", irreducible]
|
||||
protected noncomputable def modCore (x y : Nat) : Nat :=
|
||||
if hy : 0 < y then
|
||||
let rec
|
||||
go (fuel : Nat) (x : Nat) (hfuel : x < fuel) : Nat :=
|
||||
match fuel with
|
||||
| 0 => by contradiction
|
||||
| succ fuel =>
|
||||
if h : y ≤ x then
|
||||
go fuel (x - y) (div_rec_fuel_lemma hy h hfuel)
|
||||
else
|
||||
x
|
||||
termination_by structural fuel
|
||||
go (x + 1) x (Nat.lt_succ_self _)
|
||||
else
|
||||
x
|
||||
|
||||
private theorem modCore.go.fuel_congr (x y fuel1 fuel2 : Nat) (hy : 0 < y) (h1 : x < fuel1) (h2 : x < fuel2) :
|
||||
Nat.modCore.go y hy fuel1 x h1 = Nat.modCore.go y hy fuel2 x h2 := by
|
||||
match fuel1, fuel2 with
|
||||
| 0, _ => contradiction
|
||||
| _, 0 => contradiction
|
||||
| succ fuel1, succ fuel2 =>
|
||||
simp only [Nat.modCore.go]
|
||||
split
|
||||
next => rw [Nat.modCore.go.fuel_congr]
|
||||
next => rfl
|
||||
termination_by structural fuel1
|
||||
|
||||
protected theorem modCore_eq (x y : Nat) : Nat.modCore x y =
|
||||
if 0 < y ∧ y ≤ x then
|
||||
Nat.modCore (x - y) y
|
||||
else
|
||||
x
|
||||
decreasing_by apply div_rec_lemma; assumption
|
||||
x := by
|
||||
unfold Nat.modCore
|
||||
split
|
||||
next =>
|
||||
rw [Nat.modCore.go]
|
||||
split
|
||||
next =>
|
||||
simp only [and_self, ↓reduceIte, *]
|
||||
apply modCore.go.fuel_congr
|
||||
next =>
|
||||
simp only [and_false, ↓reduceIte, *]
|
||||
next =>
|
||||
simp only [false_and, ↓reduceIte, *]
|
||||
|
||||
|
||||
/--
|
||||
The modulo operator, which computes the remainder when dividing one natural number by another.
|
||||
Usually accessed via the `%` operator. When the divisor is `0`, the result is the dividend rather
|
||||
than an error.
|
||||
|
||||
`Nat.mod` is a wrapper around `Nat.modCore` that special-cases two situations, giving better
|
||||
definitional reductions:
|
||||
* `Nat.mod 0 m` should reduce to `m`, for all terms `m : Nat`.
|
||||
* `Nat.mod n (m + n + 1)` should reduce to `n` for concrete `Nat` literals `n`.
|
||||
|
||||
These reductions help `Fin n` literals work well, because the `OfNat` instance for `Fin` uses
|
||||
`Nat.mod`. In particular, `(0 : Fin (n + 1)).val` should reduce definitionally to `0`. `Nat.modCore`
|
||||
can handle all numbers, but its definitional reductions are not as convenient.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is the
|
||||
logical model.
|
||||
|
||||
Examples:
|
||||
* `7 % 2 = 1`
|
||||
* `9 % 3 = 0`
|
||||
* `5 % 7 = 5`
|
||||
* `5 % 0 = 5`
|
||||
* `show ∀ (n : Nat), 0 % n = 0 from fun _ => rfl`
|
||||
* `show ∀ (m : Nat), 5 % (m + 6) = 5 from fun _ => rfl`
|
||||
-/
|
||||
@[extern "lean_nat_mod"]
|
||||
protected def mod : @& Nat → @& Nat → Nat
|
||||
/-
|
||||
Nat.modCore is defined by well-founded recursion and thus irreducible. Nevertheless it is
|
||||
desirable if trivial `Nat.mod` calculations, namely
|
||||
Nat.modCore is defined with fuel and thus does not reduce with open terms very well.
|
||||
Nevertheless it is desirable for trivial `Nat.mod` calculations, namely
|
||||
* `Nat.mod 0 m` for all `m`
|
||||
* `Nat.mod n (m+n)` for concrete literals `n`
|
||||
reduce definitionally.
|
||||
* `Nat.mod n (m + n + 1)` for concrete literals `n`,
|
||||
to reduce definitionally.
|
||||
This property is desirable for `Fin n` literals, as it means `(ofNat 0 : Fin n).val = 0` by
|
||||
definition.
|
||||
-/
|
||||
@@ -103,17 +236,20 @@ protected theorem modCore_eq_mod (n m : Nat) : Nat.modCore n m = n % m := by
|
||||
show Nat.modCore n m = Nat.mod n m
|
||||
match n, m with
|
||||
| 0, _ =>
|
||||
rw [Nat.modCore]
|
||||
rw [Nat.modCore_eq]
|
||||
exact if_neg fun ⟨hlt, hle⟩ => Nat.lt_irrefl _ (Nat.lt_of_lt_of_le hlt hle)
|
||||
| (_ + 1), _ =>
|
||||
rw [Nat.mod]; dsimp
|
||||
refine iteInduction (fun _ => rfl) (fun h => ?false) -- cannot use `split` this early yet
|
||||
rw [Nat.modCore]
|
||||
rw [Nat.modCore_eq]
|
||||
exact if_neg fun ⟨_hlt, hle⟩ => h hle
|
||||
|
||||
theorem mod_eq (x y : Nat) : x % y = if 0 < y ∧ y ≤ x then (x - y) % y else x := by
|
||||
rw [←Nat.modCore_eq_mod, ←Nat.modCore_eq_mod, Nat.modCore]
|
||||
rw [←Nat.modCore_eq_mod, ←Nat.modCore_eq_mod, Nat.modCore_eq]
|
||||
|
||||
/--
|
||||
An induction principle customized for reasoning about the recursion pattern of `Nat.mod`.
|
||||
-/
|
||||
def mod.inductionOn.{u}
|
||||
{motive : Nat → Nat → Sort u}
|
||||
(x y : Nat)
|
||||
@@ -143,7 +279,7 @@ theorem mod_eq_of_lt {a b : Nat} (h : a < b) : a % b = a :=
|
||||
simp only [add_one_ne_zero, false_iff, ne_eq]
|
||||
exact ne_of_beq_eq_false rfl
|
||||
|
||||
@[simp] theorem Nat.zero_eq_one_mod_iff {n : Nat} : 0 = 1 % n ↔ n = 1 := by
|
||||
@[simp] theorem zero_eq_one_mod_iff {n : Nat} : 0 = 1 % n ↔ n = 1 := by
|
||||
rw [eq_comm]
|
||||
simp
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user