mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-21 20:34:07 +00:00
Compare commits
117 Commits
list_array
...
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 |
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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -135,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
|
||||
@@ -145,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
|
||||
@@ -155,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
|
||||
|
||||
@@ -270,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)
|
||||
@@ -300,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 α :=
|
||||
|
||||
@@ -265,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')
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -68,35 +68,56 @@ instance : DecidableEq Empty := fun a => a.elim
|
||||
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⟩
|
||||
|
||||
@@ -575,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`. -/
|
||||
@@ -583,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
|
||||
@@ -837,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
|
||||
|
||||
@@ -1201,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
|
||||
@@ -1346,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₁
|
||||
@@ -2120,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
|
||||
@@ -2262,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
|
||||
|
||||
@@ -321,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
|
||||
@@ -554,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)]
|
||||
@@ -1037,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
|
||||
@@ -1178,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.
|
||||
@@ -2306,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]? =
|
||||
@@ -2625,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
|
||||
@@ -3464,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 -/
|
||||
@@ -3508,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
|
||||
@@ -3675,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 -/
|
||||
@@ -3911,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
|
||||
|
||||
@@ -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⟩
|
||||
|
||||
@@ -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,6 +7,7 @@ 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
|
||||
|
||||
@@ -25,7 +26,18 @@ structure Subarray (α : Type u) where
|
||||
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
|
||||
@@ -110,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
|
||||
@@ -122,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 β :=
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -2627,7 +2676,7 @@ theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start
|
||||
· simp only [hstart, ↓reduceDIte]
|
||||
ext i hi
|
||||
simp [getElem_extractLsb', getLsbD_append,
|
||||
show ¬start + i < w by omega, ↓reduceIte,
|
||||
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
|
||||
@@ -2678,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
|
||||
@@ -3108,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
|
||||
@@ -3168,6 +3220,14 @@ 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]
|
||||
@@ -3327,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]
|
||||
@@ -3336,11 +3396,23 @@ 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]
|
||||
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 -/
|
||||
|
||||
@@ -4102,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
|
||||
@@ -4120,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]
|
||||
@@ -4133,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
|
||||
@@ -4192,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
|
||||
@@ -4312,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. -/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -141,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 ..
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,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
|
||||
|
||||
|
||||
@@ -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,22 +336,25 @@ 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
|
||||
| (n : Nat) => some n
|
||||
@@ -314,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
|
||||
|
||||
@@ -12,15 +12,14 @@ namespace Int
|
||||
/-! ## bit operations -/
|
||||
|
||||
/--
|
||||
Bitwise not
|
||||
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.
|
||||
```
|
||||
~~~(0:Int) = -1
|
||||
~~~(1:Int) = -2
|
||||
~~~(-1:Int) = 0
|
||||
```
|
||||
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
|
||||
@@ -29,17 +28,16 @@ protected def not : Int → Int
|
||||
instance : Complement Int := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Bitwise shift right.
|
||||
Bitwise right shift, usually accessed via the `>>>` operator.
|
||||
|
||||
Conceptually, this treats the integer as an infinite sequence of bits in two's
|
||||
complement and shifts the value to the right.
|
||||
Interprets 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
|
||||
```
|
||||
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)
|
||||
|
||||
@@ -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,17 +122,15 @@ 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 -/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,10 +1122,10 @@ 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
|
||||
@@ -1187,9 +1186,9 @@ theorem lt_tmod_of_pos (a : Int) {b : Int} (H : 0 < b) : -b < tmod a b :=
|
||||
|
||||
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]
|
||||
@@ -1332,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
|
||||
@@ -1645,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
|
||||
@@ -1972,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
|
||||
|
||||
@@ -20,7 +20,21 @@ 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
|
||||
@@ -41,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
|
||||
|
||||
@@ -490,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]
|
||||
|
||||
@@ -361,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]
|
||||
@@ -798,7 +798,7 @@ 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.add_neg_eq_sub]
|
||||
@@ -908,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
|
||||
@@ -977,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
|
||||
@@ -1017,7 +1017,7 @@ 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.mul_comm]
|
||||
assumption
|
||||
@@ -1658,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₁
|
||||
@@ -1757,6 +1780,22 @@ 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
|
||||
|
||||
@@ -67,4 +67,24 @@ 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
|
||||
|
||||
@@ -41,7 +41,7 @@ elements in the corresponding subtype `{ x // P x }`.
|
||||
(l : List α) (P : α → Prop) (H : ∀ x ∈ l, P x) : List {x // P x} := pmap Subtype.mk l H
|
||||
|
||||
/--
|
||||
"Attaches" the proof that the elements of `l` are in fact elements of `l`, producing a new list with
|
||||
“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)`.
|
||||
|
||||
@@ -259,7 +259,7 @@ instance decidableLE [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List
|
||||
inferInstanceAs (Decidable (Not _))
|
||||
|
||||
/--
|
||||
Compare lists lexicographically with respect to a comparison on their elements.
|
||||
Compares lists lexicographically with respect to a comparison on their elements.
|
||||
|
||||
The lexicographic order with respect to `lt` is:
|
||||
* `[].lex (b :: bs)` is `true`
|
||||
@@ -493,7 +493,7 @@ Returns the list of elements in `l` for which `p` returns `true`.
|
||||
`O(|l|)`.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 5, 2, 7, 7].filter (· > 2) = [5, 7, 7]`
|
||||
* `[1, 2, 5, 2, 7, 7].filter (· > 2) = [5, 7, 7]`
|
||||
* `[1, 2, 5, 2, 7, 7].filter (fun _ => false) = []`
|
||||
* `[1, 2, 5, 2, 7, 7].filter (fun _ => true) = [1, 2, 5, 2, 7, 7]`
|
||||
-/
|
||||
@@ -766,10 +766,10 @@ Pads `l : List α` on the left with repeated occurrences of `a : α` until it is
|
||||
already has at least `n` elements, it is returned unmodified.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3].leftPad 5 0 = [0, 0, 1, 2, 3]`
|
||||
* `["red", "green", "blue"].leftPad 4 "blank" = ["blank", "red", "green", "blue"]`
|
||||
* `["red", "green", "blue"].leftPad 3 "blank" = ["red", "green", "blue"]`
|
||||
* `["red", "green", "blue"].leftPad 1 "blank" = ["red", "green", "blue"]`
|
||||
* `[1, 2, 3].leftpad 5 0 = [0, 0, 1, 2, 3]`
|
||||
* `["red", "green", "blue"].leftpad 4 "blank" = ["blank", "red", "green", "blue"]`
|
||||
* `["red", "green", "blue"].leftpad 3 "blank" = ["red", "green", "blue"]`
|
||||
* `["red", "green", "blue"].leftpad 1 "blank" = ["red", "green", "blue"]`
|
||||
-/
|
||||
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
|
||||
|
||||
@@ -779,10 +779,10 @@ Pads `l : List α` on the right with repeated occurrences of `a : α` until it i
|
||||
`l` already has at least `n` elements, it is returned unmodified.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3].rightPad 5 0 = [1, 2, 3, 0, 0]`
|
||||
* `["red", "green", "blue"].rightPad 4 "blank" = ["red", "green", "blue", "blank"]`
|
||||
* `["red", "green", "blue"].rightPad 3 "blank" = ["red", "green", "blue"]`
|
||||
* `["red", "green", "blue"].rightPad 1 "blank" = ["red", "green", "blue"]`
|
||||
* `[1, 2, 3].rightpad 5 0 = [1, 2, 3, 0, 0]`
|
||||
* `["red", "green", "blue"].rightpad 4 "blank" = ["red", "green", "blue", "blank"]`
|
||||
* `["red", "green", "blue"].rightpad 3 "blank" = ["red", "green", "blue"]`
|
||||
* `["red", "green", "blue"].rightpad 1 "blank" = ["red", "green", "blue"]`
|
||||
-/
|
||||
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
|
||||
|
||||
@@ -1451,25 +1451,25 @@ using `f` if the index is larger than the length of the List.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
["circle", "square", "triangle"].modifyTailIdx List.reverse 1
|
||||
["circle", "square", "triangle"].modifyTailIdx 1 List.reverse
|
||||
```
|
||||
```output
|
||||
["circle", "triangle", "square"]
|
||||
```
|
||||
```lean example
|
||||
["circle", "square", "triangle"].modifyTailIdx (fun xs => xs ++ xs) 1
|
||||
["circle", "square", "triangle"].modifyTailIdx 1 (fun xs => xs ++ xs)
|
||||
```
|
||||
```output
|
||||
["circle", "square", "triangle", "square", "triangle"]
|
||||
```
|
||||
```lean example
|
||||
["circle", "square", "triangle"].modifyTailIdx (fun xs => xs ++ xs) 2
|
||||
["circle", "square", "triangle"].modifyTailIdx 2 (fun xs => xs ++ xs)
|
||||
```
|
||||
```output
|
||||
["circle", "square", "triangle", "triangle"]
|
||||
```
|
||||
```lean example
|
||||
["circle", "square", "triangle"].modifyTailIdx (fun xs => xs ++ xs) 5
|
||||
["circle", "square", "triangle"].modifyTailIdx 5 (fun xs => xs ++ xs)
|
||||
```
|
||||
```output
|
||||
["circle", "square", "triangle"]
|
||||
@@ -1509,9 +1509,9 @@ Replaces the element at the given index, if it exists, with the result of applyi
|
||||
index is invalid, the list is returned unmodified.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3].modify (· * 10) 0 = [10, 2, 3]`
|
||||
* `[1, 2, 3].modify (· * 10) 2 = [1, 2, 30]`
|
||||
* `[1, 2, 3].modify (· * 10) 3 = [1, 2, 3]`
|
||||
* `[1, 2, 3].modify 0 (· * 10) = [10, 2, 3]`
|
||||
* `[1, 2, 3].modify 2 (· * 10) = [1, 2, 30]`
|
||||
* `[1, 2, 3].modify 3 (· * 10) = [1, 2, 3]`
|
||||
-/
|
||||
def modify (l : List α) (i : Nat) (f : α → α) : List α :=
|
||||
l.modifyTailIdx i (modifyHead f)
|
||||
@@ -1702,7 +1702,6 @@ Examples:
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = some 4`
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = none`
|
||||
-/
|
||||
|
||||
def findIdx? (p : α → Bool) (l : List α) : Option Nat :=
|
||||
go l 0
|
||||
where
|
||||
@@ -1866,7 +1865,7 @@ def isPerm [BEq α] : List α → List α → Bool
|
||||
/-! ### any -/
|
||||
|
||||
/--
|
||||
Returns true if `p` returns `true` for any element of `l`.
|
||||
Returns `true` if `p` returns `true` for any element of `l`.
|
||||
|
||||
`O(|l|)`. Short-circuits upon encountering the first `true`.
|
||||
|
||||
@@ -1886,7 +1885,7 @@ def any : (l : List α) → (p : α → Bool) → Bool
|
||||
/-! ### all -/
|
||||
|
||||
/--
|
||||
Returns true if `p` returns `true` for every element of `l`.
|
||||
Returns `true` if `p` returns `true` for every element of `l`.
|
||||
|
||||
`O(|l|)`. Short-circuits upon encountering the first `false`.
|
||||
|
||||
@@ -1966,7 +1965,7 @@ Examples:
|
||||
|
||||
/--
|
||||
Combines two lists into a list of pairs in which the first and second components are the
|
||||
corresponding elements of each list. The resulting list is the length of the shorter of the inputs
|
||||
corresponding elements of each list. The resulting list is the length of the shorter of the input
|
||||
lists.
|
||||
|
||||
`O(min |xs| |ys|)`.
|
||||
|
||||
@@ -46,7 +46,7 @@ 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.
|
||||
|
||||
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
|
||||
@@ -243,7 +243,7 @@ def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s
|
||||
|
||||
/--
|
||||
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 order, using `f`.
|
||||
accumulated value is combined with the each element of the list in reverse order, using `f`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace List
|
||||
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)`
|
||||
* `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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -297,9 +297,9 @@ Replaces the element at the given index, if it exists, with the result of applyi
|
||||
This is a tail-recursive version of `List.modify`.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3].modifyTR (· * 10) 0 = [10, 2, 3]`
|
||||
* `[1, 2, 3].modifyTR (· * 10) 2 = [1, 2, 30]`
|
||||
* `[1, 2, 3].modifyTR (· * 10) 3 = [1, 2, 3]`
|
||||
* `[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`. -/
|
||||
|
||||
@@ -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 [*]
|
||||
@@ -2841,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
|
||||
@@ -3537,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
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -538,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 α) :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -785,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
|
||||
|
||||
@@ -796,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)
|
||||
|
||||
@@ -168,4 +168,40 @@ theorem add_div_of_dvd_add_add_one (h : c ∣ a + b + 1) : (a + b) / c = a / c +
|
||||
have := mod_add_mod_lt_of_add_mod_eq_sub_one w ((mod_eq_sub_iff Nat.zero_lt_one w).mpr h)
|
||||
omega
|
||||
|
||||
theorem div_lt_of_lt {a b c : Nat} (ha : a < c) : a / b < c := by
|
||||
obtain (rfl|hb) := Nat.eq_zero_or_pos b
|
||||
· simp
|
||||
omega
|
||||
· rw [Nat.div_lt_iff_lt_mul hb, ← Nat.mul_one a]
|
||||
apply Nat.mul_lt_mul_of_lt_of_le ha (by omega) (by omega)
|
||||
|
||||
theorem div_mod_eq_div {a b c : Nat} (ha : a < c) : (a / b) % c = a / b :=
|
||||
Nat.mod_eq_of_lt (Nat.div_lt_of_lt ha)
|
||||
|
||||
theorem div_mod_eq_mod_div_mod {a b c : Nat} (ha : a < c) (hb : b < c) :
|
||||
(a / b) % c = a % c / (b % c) := by
|
||||
rw [Nat.mod_eq_of_lt (Nat.div_lt_of_lt ha), Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb]
|
||||
|
||||
theorem mod_mod_eq_mod_of_lt_right {a b c : Nat} (ha : a < c) : (a % b) % c = a % b :=
|
||||
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt (Nat.mod_le _ _) ha)
|
||||
|
||||
theorem mod_mod_eq_mod_mod_mod {a b c : Nat} (ha : a < c) (hb : b < c) :
|
||||
(a % b) % c = (a % c) % (b % c) := by
|
||||
rw [Nat.mod_mod_eq_mod_of_lt_right ha, Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb]
|
||||
|
||||
theorem mod_mod_eq_mod_mod_of_dvd {a b c : Nat} (h : b ∣ c) : a % b % c = a % c % b := by
|
||||
refine Or.elim (Nat.eq_zero_or_pos b) (by rintro rfl; simp) (fun hb => ?_)
|
||||
refine Or.elim (Nat.eq_zero_or_pos c) (by rintro rfl; simp) (fun hc => ?_)
|
||||
rw [Nat.mod_mod_of_dvd _ h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le (Nat.mod_lt a hb) (Nat.le_of_dvd hc h))]
|
||||
|
||||
theorem mod_mod_of_dvd' {a b c : Nat} (h : b ∣ c) : a % b % c = a % b := by
|
||||
rw [Nat.mod_mod_eq_mod_mod_of_dvd h, Nat.mod_mod_of_dvd _ h]
|
||||
|
||||
theorem mod_mod_eq_mod_mod_mod_of_dvd {a b c : Nat} (hb : b ∣ c) :
|
||||
(a % b) % c = (a % c) % (b % c) := by
|
||||
refine (Decidable.em (b = c)).elim (by rintro rfl; simp) (fun hb' => ?_)
|
||||
refine Or.elim (Nat.eq_zero_or_pos c) (by rintro rfl; simp) (fun hc => ?_)
|
||||
have : b < c := Nat.lt_of_le_of_ne (Nat.le_of_dvd hc hb) hb'
|
||||
rw [Nat.mod_mod_of_dvd' hb, Nat.mod_eq_of_lt this, Nat.mod_mod_of_dvd _ hb]
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -13,14 +13,30 @@ universe u
|
||||
namespace Nat
|
||||
|
||||
/--
|
||||
`Nat.fold` evaluates `f` on the numbers up to `n` exclusive, in increasing order:
|
||||
* `Nat.fold f 3 init = init |> f 0 |> f 1 |> f 2`
|
||||
Iterates the application of a 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.
|
||||
|
||||
Examples:
|
||||
* `Nat.fold 3 f init = (init |> f 0 (by simp) |> f 1 (by simp) |> f 2 (by simp))`
|
||||
* `Nat.fold 4 (fun i _ xs => xs.push i) #[] = #[0, 1, 2, 3]`
|
||||
* `Nat.fold 0 (fun i _ xs => xs.push i) #[] = #[]`
|
||||
-/
|
||||
@[specialize] def fold {α : Type u} : (n : Nat) → (f : (i : Nat) → i < n → α → α) → (init : α) → α
|
||||
| 0, f, a => a
|
||||
| succ n, f, a => f n (by omega) (fold n (fun i h => f i (by omega)) a)
|
||||
|
||||
/-- Tail-recursive version of `Nat.fold`. -/
|
||||
|
||||
/--
|
||||
Iterates the application of a 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.
|
||||
|
||||
This is a tail-recursive version of `Nat.fold` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `Nat.foldTR 3 f init = (init |> f 0 (by simp) |> f 1 (by simp) |> f 2 (by simp))`
|
||||
* `Nat.foldTR 4 (fun i _ xs => xs.push i) #[] = #[0, 1, 2, 3]`
|
||||
* `Nat.foldTR 0 (fun i _ xs => xs.push i) #[] = #[]`
|
||||
-/
|
||||
@[inline] def foldTR {α : Type u} (n : Nat) (f : (i : Nat) → i < n → α → α) (init : α) : α :=
|
||||
let rec @[specialize] loop : ∀ j, j ≤ n → α → α
|
||||
| 0, h, a => a
|
||||
@@ -28,31 +44,72 @@ namespace Nat
|
||||
loop n (by omega) init
|
||||
|
||||
/--
|
||||
`Nat.foldRev` evaluates `f` on the numbers up to `n` exclusive, in decreasing order:
|
||||
* `Nat.foldRev f 3 init = f 0 <| f 1 <| f 2 <| init`
|
||||
Iterates the application of a 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.
|
||||
|
||||
Examples:
|
||||
* `Nat.foldRev 3 f init = (f 0 (by simp) <| f 1 (by simp) <| f 2 (by simp) init)`
|
||||
* `Nat.foldRev 4 (fun i _ xs => xs.push i) #[] = #[3, 2, 1, 0]`
|
||||
* `Nat.foldRev 0 (fun i _ xs => xs.push i) #[] = #[]`
|
||||
-/
|
||||
@[specialize] def foldRev {α : Type u} : (n : Nat) → (f : (i : Nat) → i < n → α → α) → (init : α) → α
|
||||
| 0, f, a => a
|
||||
| succ n, f, a => foldRev n (fun i h => f i (by omega)) (f n (by omega) a)
|
||||
|
||||
/-- `any f n = true` iff there is `i in [0, n-1]` s.t. `f i = true` -/
|
||||
/--
|
||||
Checks whether there is some number less that the given bound for which `f` returns `true`.
|
||||
|
||||
Examples:
|
||||
* `Nat.any 4 (fun i _ => i < 5) = true`
|
||||
* `Nat.any 7 (fun i _ => i < 5) = true`
|
||||
* `Nat.any 7 (fun i _ => i % 2 = 0) = true`
|
||||
* `Nat.any 1 (fun i _ => i % 2 = 1) = false`
|
||||
-/
|
||||
@[specialize] def any : (n : Nat) → (f : (i : Nat) → i < n → Bool) → Bool
|
||||
| 0, f => false
|
||||
| succ n, f => any n (fun i h => f i (by omega)) || f n (by omega)
|
||||
|
||||
/-- Tail-recursive version of `Nat.any`. -/
|
||||
/--
|
||||
Checks whether there is some number less that the given bound for which `f` returns `true`.
|
||||
|
||||
This is a tail-recursive equivalent of `Nat.any` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `Nat.anyTR 4 (fun i _ => i < 5) = true`
|
||||
* `Nat.anyTR 7 (fun i _ => i < 5) = true`
|
||||
* `Nat.anyTR 7 (fun i _ => i % 2 = 0) = true`
|
||||
* `Nat.anyTR 1 (fun i _ => i % 2 = 1) = false`
|
||||
-/
|
||||
@[inline] def anyTR (n : Nat) (f : (i : Nat) → i < n → Bool) : Bool :=
|
||||
let rec @[specialize] loop : (i : Nat) → i ≤ n → Bool
|
||||
| 0, h => false
|
||||
| succ m, h => f (n - succ m) (by omega) || loop m (by omega)
|
||||
loop n (by omega)
|
||||
|
||||
/-- `all f n = true` iff every `i in [0, n-1]` satisfies `f i = true` -/
|
||||
/--
|
||||
Checks whether `f` returns `true` for every number strictly less than a bound.
|
||||
|
||||
Examples:
|
||||
* `Nat.all 4 (fun i _ => i < 5) = true`
|
||||
* `Nat.all 7 (fun i _ => i < 5) = false`
|
||||
* `Nat.all 7 (fun i _ => i % 2 = 0) = false`
|
||||
* `Nat.all 1 (fun i _ => i % 2 = 0) = true`
|
||||
-/
|
||||
@[specialize] def all : (n : Nat) → (f : (i : Nat) → i < n → Bool) → Bool
|
||||
| 0, f => true
|
||||
| succ n, f => all n (fun i h => f i (by omega)) && f n (by omega)
|
||||
|
||||
/-- Tail-recursive version of `Nat.all`. -/
|
||||
/--
|
||||
Checks whether `f` returns `true` for every number strictly less than a bound.
|
||||
|
||||
This is a tail-recursive equivalent of `Nat.all` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `Nat.allTR 4 (fun i _ => i < 5) = true`
|
||||
* `Nat.allTR 7 (fun i _ => i < 5) = false`
|
||||
* `Nat.allTR 7 (fun i _ => i % 2 = 0) = false`
|
||||
* `Nat.allTR 1 (fun i _ => i % 2 = 0) = true`
|
||||
-/
|
||||
@[inline] def allTR (n : Nat) (f : (i : Nat) → i < n → Bool) : Bool :=
|
||||
let rec @[specialize] loop : (i : Nat) → i ≤ n → Bool
|
||||
| 0, h => true
|
||||
|
||||
@@ -11,22 +11,19 @@ import Init.RCases
|
||||
namespace Nat
|
||||
|
||||
/--
|
||||
Computes the greatest common divisor of two natural numbers.
|
||||
Computes the greatest common divisor of two natural numbers. The GCD of two natural numbers is the
|
||||
largest natural number that evenly divides both.
|
||||
|
||||
This reference implementation via the Euclidean algorithm
|
||||
is overridden in both the kernel and the compiler to efficiently
|
||||
evaluate using the "bignum" representation (see `Nat`).
|
||||
The definition provided here is the logical model
|
||||
(and it is soundness-critical that they coincide).
|
||||
In particular, the GCD of a number and `0` is the number itself.
|
||||
|
||||
The GCD of two natural numbers is the largest natural number
|
||||
that divides both arguments.
|
||||
In particular, the GCD of a number and `0` is the number itself:
|
||||
```
|
||||
example : Nat.gcd 10 15 = 5 := rfl
|
||||
example : Nat.gcd 0 5 = 5 := rfl
|
||||
example : Nat.gcd 7 0 = 7 := rfl
|
||||
```
|
||||
This reference implementation via the Euclidean algorithm is overridden in both the kernel and the
|
||||
compiler to efficiently evaluate using arbitrary-precision arithmetic. The definition provided here
|
||||
is the logical model.
|
||||
|
||||
Examples:
|
||||
* `Nat.gcd 10 15 = 5`
|
||||
* `Nat.gcd 0 5 = 5`
|
||||
* `Nat.gcd 7 0 = 7`
|
||||
-/
|
||||
@[extern "lean_nat_gcd"]
|
||||
def gcd (m n : @& Nat) : Nat :=
|
||||
|
||||
@@ -17,7 +17,16 @@ that should be added to this file.
|
||||
|
||||
namespace Nat
|
||||
|
||||
/-- The least common multiple of `m` and `n`, defined using `gcd`. -/
|
||||
/--
|
||||
The least common multiple of `m` and `n` is the smallest natural number that's evenly divisible by
|
||||
both `m` and `n`. Returns `0` if either `m` or `n` is `0`.
|
||||
|
||||
Examples:
|
||||
* `Nat.lcm 9 6 = 18`
|
||||
* `Nat.lcm 9 3 = 9`
|
||||
* `Nat.lcm 0 3 = 0`
|
||||
* `Nat.lcm 3 0 = 0`
|
||||
-/
|
||||
def lcm (m n : Nat) : Nat := m * n / gcd m n
|
||||
|
||||
theorem lcm_comm (m n : Nat) : lcm m n = lcm n m := by
|
||||
|
||||
@@ -20,9 +20,6 @@ and later these lemmas should be organised into other files more systematically.
|
||||
|
||||
namespace Nat
|
||||
|
||||
@[deprecated and_forall_add_one (since := "2024-07-30")] abbrev and_forall_succ := @and_forall_add_one
|
||||
@[deprecated or_exists_add_one (since := "2024-07-30")] abbrev or_exists_succ := @or_exists_add_one
|
||||
|
||||
@[simp] theorem exists_ne_zero {P : Nat → Prop} : (∃ n, ¬ n = 0 ∧ P n) ↔ ∃ n, P (n + 1) :=
|
||||
⟨fun ⟨n, h, w⟩ => by cases n with | zero => simp at h | succ n => exact ⟨n, w⟩,
|
||||
fun ⟨n, w⟩ => ⟨n + 1, by simp, w⟩⟩
|
||||
@@ -580,9 +577,15 @@ theorem mul_mod (a b n : Nat) : a * b % n = (a % n) * (b % n) % n := by
|
||||
have := (add_mul_mod_self_left (m % n + k) n (m / n)).symm
|
||||
rwa [Nat.add_right_comm, mod_add_div] at this
|
||||
|
||||
@[simp] theorem mul_mod_mod (m n l : Nat) : (m * (n % l)) % l = (m * n) % l := by
|
||||
rw [mul_mod, mod_mod, ← mul_mod]
|
||||
|
||||
@[simp] theorem add_mod_mod (m n k : Nat) : (m + n % k) % k = (m + n) % k := by
|
||||
rw [Nat.add_comm, mod_add_mod, Nat.add_comm]
|
||||
|
||||
@[simp] theorem mod_mul_mod (m n l : Nat) : ((m % l) * n) % l = (m * n) % l := by
|
||||
rw [Nat.mul_comm, mul_mod_mod, Nat.mul_comm]
|
||||
|
||||
theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
|
||||
rw [add_mod_mod, mod_add_mod]
|
||||
|
||||
@@ -595,9 +598,6 @@ theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
|
||||
| zero => simp_all
|
||||
| succ k => omega
|
||||
|
||||
@[simp] theorem mod_mul_mod {a b c : Nat} : (a % c * b) % c = a * b % c := by
|
||||
rw [mul_mod, mod_mod, ← mul_mod]
|
||||
|
||||
theorem mod_eq_sub (x w : Nat) : x % w = x - w * (x / w) := by
|
||||
conv => rhs; congr; rw [← mod_add_div x w]
|
||||
simp
|
||||
|
||||
@@ -32,7 +32,7 @@ inductive Expr where
|
||||
| add (a b : Expr)
|
||||
| mulL (k : Nat) (a : Expr)
|
||||
| mulR (a : Expr) (k : Nat)
|
||||
deriving Inhabited
|
||||
deriving Inhabited, BEq
|
||||
|
||||
def Expr.denote (ctx : Context) : Expr → Nat
|
||||
| .add a b => Nat.add (denote ctx a) (denote ctx b)
|
||||
|
||||
@@ -18,9 +18,18 @@ theorem log2_terminates : ∀ n, n ≥ 2 → n / 2 < n
|
||||
simp
|
||||
|
||||
/--
|
||||
Computes `⌊max 0 (log₂ n)⌋`.
|
||||
Base-two logarithm of natural numbers. Returns `⌊max 0 (log₂ n)⌋`.
|
||||
|
||||
`log2 0 = log2 1 = 0`, `log2 2 = 1`, ..., `log2 (2^i) = i`, etc.
|
||||
This function is overridden at runtime with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `Nat.log2 0 = 0`
|
||||
* `Nat.log2 1 = 0`
|
||||
* `Nat.log2 2 = 1`
|
||||
* `Nat.log2 4 = 2`
|
||||
* `Nat.log2 7 = 2`
|
||||
* `Nat.log2 8 = 3`
|
||||
-/
|
||||
@[extern "lean_nat_log2"]
|
||||
def log2 (n : @& Nat) : Nat :=
|
||||
|
||||
@@ -13,6 +13,16 @@ theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n)
|
||||
rw [this, Nat.sub_add_eq]
|
||||
exact Nat.sub_lt (Nat.zero_lt_sub_of_lt h₂) h₁
|
||||
|
||||
/--
|
||||
Returns the least power of two that's greater than or equal to `n`.
|
||||
|
||||
Examples:
|
||||
* `Nat.nextPowerOfTwo 0 = 1`
|
||||
* `Nat.nextPowerOfTwo 1 = 1`
|
||||
* `Nat.nextPowerOfTwo 2 = 2`
|
||||
* `Nat.nextPowerOfTwo 3 = 4`
|
||||
* `Nat.nextPowerOfTwo 5 = 8`
|
||||
-/
|
||||
def nextPowerOfTwo (n : Nat) : Nat :=
|
||||
go 1 (by decide)
|
||||
where
|
||||
@@ -24,11 +34,17 @@ where
|
||||
termination_by n - power
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
|
||||
/--
|
||||
A natural number `n` is a power of two if there exists some `k : Nat` such that `n = 2 ^ k`.
|
||||
-/
|
||||
def isPowerOfTwo (n : Nat) := ∃ k, n = 2 ^ k
|
||||
|
||||
theorem one_isPowerOfTwo : isPowerOfTwo 1 :=
|
||||
theorem isPowerOfTwo_one : isPowerOfTwo 1 :=
|
||||
⟨0, by decide⟩
|
||||
|
||||
@[deprecated isPowerOfTwo_one (since := "2025-03-18")]
|
||||
abbrev one_isPowerOfTwo := @isPowerOfTwo_one
|
||||
|
||||
theorem mul2_isPowerOfTwo_of_isPowerOfTwo (h : isPowerOfTwo n) : isPowerOfTwo (n * 2) :=
|
||||
have ⟨k, h⟩ := h
|
||||
⟨k+1, by simp [h, Nat.pow_succ]⟩
|
||||
@@ -41,7 +57,7 @@ theorem pos_of_isPowerOfTwo (h : isPowerOfTwo n) : n > 0 := by
|
||||
|
||||
theorem isPowerOfTwo_nextPowerOfTwo (n : Nat) : n.nextPowerOfTwo.isPowerOfTwo := by
|
||||
apply isPowerOfTwo_go
|
||||
apply one_isPowerOfTwo
|
||||
apply isPowerOfTwo_one
|
||||
where
|
||||
isPowerOfTwo_go (power : Nat) (h₁ : power > 0) (h₂ : power.isPowerOfTwo) : (nextPowerOfTwo.go n power h₁).isPowerOfTwo := by
|
||||
unfold nextPowerOfTwo.go
|
||||
|
||||
@@ -17,6 +17,16 @@ import Init.Data.Nat.Log2
|
||||
Note the use of `nat_lit`; there is no wrapping `OfNat.ofNat` in the resulting term.
|
||||
-/
|
||||
class OfScientific (α : Type u) where
|
||||
/--
|
||||
Produces a value from the given mantissa, exponent sign, and decimal exponent. For the exponent
|
||||
sign, `true` indicates a negative exponent.
|
||||
|
||||
Examples:
|
||||
- `1.23` is syntax for `OfScientific.ofScientific (nat_lit 123) true (nat_lit 2)`
|
||||
- `121e100` is syntax for `OfScientific.ofScientific (nat_lit 121) false (nat_lit 100)`
|
||||
|
||||
Note the use of `nat_lit`; there is no wrapping `OfNat.ofNat` in the resulting term.
|
||||
-/
|
||||
ofScientific (mantissa : Nat) (exponentSign : Bool) (decimalExponent : Nat) : α
|
||||
|
||||
/-- Computes `m * 2^e`. -/
|
||||
@@ -45,6 +55,9 @@ protected opaque Float.ofScientific (m : Nat) (s : Bool) (e : Nat) : Float :=
|
||||
instance : OfScientific Float where
|
||||
ofScientific := Float.ofScientific
|
||||
|
||||
/--
|
||||
Converts a natural number into a 64-bit floating point number.
|
||||
-/
|
||||
@[export lean_float_of_nat]
|
||||
def Float.ofNat (n : Nat) : Float :=
|
||||
OfScientific.ofScientific n false 0
|
||||
@@ -55,7 +68,7 @@ def Float.ofInt : Int → Float
|
||||
|
||||
instance : OfNat Float n := ⟨Float.ofNat n⟩
|
||||
|
||||
abbrev Nat.toFloat (n : Nat) : Float :=
|
||||
@[inherit_doc Float.ofNat] abbrev Nat.toFloat (n : Nat) : Float :=
|
||||
Float.ofNat n
|
||||
|
||||
/-- Computes `m * 2^e`. -/
|
||||
@@ -76,6 +89,9 @@ protected opaque Float32.ofScientific (m : Nat) (s : Bool) (e : Nat) : Float32 :
|
||||
instance : OfScientific Float32 where
|
||||
ofScientific := Float32.ofScientific
|
||||
|
||||
/--
|
||||
Converts a natural number into a 32-bit floating point number.
|
||||
-/
|
||||
@[export lean_float32_of_nat]
|
||||
def Float32.ofNat (n : Nat) : Float32 :=
|
||||
OfScientific.ofScientific n false 0
|
||||
@@ -86,5 +102,5 @@ def Float32.ofInt : Int → Float32
|
||||
|
||||
instance : OfNat Float32 n := ⟨Float32.ofNat n⟩
|
||||
|
||||
abbrev Nat.toFloat32 (n : Nat) : Float32 :=
|
||||
@[inherit_doc Float32.ofNat] abbrev Nat.toFloat32 (n : Nat) : Float32 :=
|
||||
Float32.ofNat n
|
||||
|
||||
@@ -138,6 +138,10 @@ theorem bind_comm {f : α → β → Option γ} (a : Option α) (b : Option β)
|
||||
theorem bind_assoc (x : Option α) (f : α → Option β) (g : β → Option γ) :
|
||||
(x.bind f).bind g = x.bind fun y => (f y).bind g := by cases x <;> rfl
|
||||
|
||||
theorem bind_congr {α β} {o : Option α} {f g : α → Option β} :
|
||||
(h : ∀ a, o = some a → f a = g a) → o.bind f = o.bind g := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem join_eq_some : x.join = some a ↔ x = some (some a) := by
|
||||
simp [bind_eq_some]
|
||||
|
||||
@@ -229,10 +233,13 @@ theorem map_inj_right {f : α → β} {o o' : Option α} (w : ∀ x y, f x = f y
|
||||
|
||||
theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
|
||||
|
||||
theorem isSome_filter_of_isSome (p : α → Bool) (o : Option α) (h : (o.filter p).isSome) :
|
||||
theorem isSome_of_isSome_filter (p : α → Bool) (o : Option α) (h : (o.filter p).isSome) :
|
||||
o.isSome := by
|
||||
cases o <;> simp at h ⊢
|
||||
|
||||
@[deprecated isSome_of_isSome_filter (since := "2025-03-18")]
|
||||
abbrev isSome_filter_of_isSome := @isSome_of_isSome_filter
|
||||
|
||||
@[simp] theorem filter_eq_none {p : α → Bool} :
|
||||
o.filter p = none ↔ o = none ∨ ∀ a, a ∈ o → ¬ p a := by
|
||||
cases o <;> simp [filter_some]
|
||||
@@ -295,9 +302,12 @@ theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) :
|
||||
@[simp] theorem guard_eq_some [DecidablePred p] : guard p a = some b ↔ a = b ∧ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[simp] theorem guard_isSome [DecidablePred p] : (Option.guard p a).isSome ↔ p a :=
|
||||
@[simp] theorem isSome_guard [DecidablePred p] : (Option.guard p a).isSome ↔ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@[deprecated isSome_guard (since := "2025-03-18")]
|
||||
abbrev guard_isSome := @isSome_guard
|
||||
|
||||
@[simp] theorem guard_eq_none [DecidablePred p] : Option.guard p a = none ↔ ¬ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
@@ -366,9 +376,12 @@ theorem choice_eq {α : Type _} [Subsingleton α] (a : α) : choice α = some a
|
||||
rw [dif_pos (⟨a⟩ : Nonempty α)]
|
||||
simp; apply Subsingleton.elim
|
||||
|
||||
theorem choice_isSome_iff_nonempty {α : Type _} : (choice α).isSome ↔ Nonempty α :=
|
||||
theorem isSome_choice_iff_nonempty {α : Type _} : (choice α).isSome ↔ Nonempty α :=
|
||||
⟨fun h => ⟨(choice α).get h⟩, fun h => by simp only [choice, dif_pos h, isSome_some]⟩
|
||||
|
||||
@[deprecated isSome_choice_iff_nonempty (since := "2025-03-18")]
|
||||
abbrev choice_isSome_iff_nonempty := @isSome_choice_iff_nonempty
|
||||
|
||||
end choice
|
||||
|
||||
@[simp] theorem toList_some (a : α) : (a : Option α).toList = [a] := rfl
|
||||
|
||||
@@ -34,11 +34,15 @@ structure StdGen where
|
||||
|
||||
instance : Inhabited StdGen := ⟨{ s1 := 0, s2 := 0 }⟩
|
||||
|
||||
/-- The range of values returned by `StdGen` -/
|
||||
def stdRange := (1, 2147483562)
|
||||
|
||||
instance : Repr StdGen where
|
||||
reprPrec | ⟨s1, s2⟩, _ => Std.Format.bracket "⟨" (repr s1 ++ ", " ++ repr s2) "⟩"
|
||||
|
||||
/--
|
||||
The next value from a `StdGen`, paired with an updated generator state.
|
||||
-/
|
||||
def stdNext : StdGen → Nat × StdGen
|
||||
| ⟨s1, s2⟩ =>
|
||||
let k : Int := Int.ofNat (s1 / 53668)
|
||||
@@ -51,6 +55,9 @@ def stdNext : StdGen → Nat × StdGen
|
||||
let z' : Nat := if z < 1 then (z + 2147483562).toNat else z.toNat % 2147483562
|
||||
(z', ⟨s1'', s2''⟩)
|
||||
|
||||
/--
|
||||
Splits a `StdGen` into two separate states.
|
||||
-/
|
||||
def stdSplit : StdGen → StdGen × StdGen
|
||||
| g@⟨s1, s2⟩ =>
|
||||
let newS1 := if s1 = 2147483562 then 1 else s1 + 1
|
||||
@@ -66,7 +73,7 @@ instance : RandomGen StdGen := {
|
||||
split := stdSplit
|
||||
}
|
||||
|
||||
/-- Return a standard number generator. -/
|
||||
/-- Returns a standard number generator. -/
|
||||
def mkStdGen (s : Nat := 0) : StdGen :=
|
||||
let q := s / 2147483562
|
||||
let s1 := s % 2147483562
|
||||
@@ -86,7 +93,7 @@ private partial def randNatAux {gen : Type u} [RandomGen gen] (genLo genMag : Na
|
||||
let v' := v*genMag + (x - genLo)
|
||||
randNatAux genLo genMag (r' / genMag - 1) (v', g')
|
||||
|
||||
/-- Generate a random natural number in the interval [lo, hi]. -/
|
||||
/-- Generates a random natural number in the interval [lo, hi]. -/
|
||||
def randNat {gen : Type u} [RandomGen gen] (g : gen) (lo hi : Nat) : Nat × gen :=
|
||||
let lo' := if lo > hi then hi else lo
|
||||
let hi' := if lo > hi then lo else hi
|
||||
@@ -104,7 +111,7 @@ def randNat {gen : Type u} [RandomGen gen] (g : gen) (lo hi : Nat) : Nat × gen
|
||||
let v' := lo' + (v % k)
|
||||
(v', g')
|
||||
|
||||
/-- Generate a random Boolean. -/
|
||||
/-- Generates a random Boolean. -/
|
||||
def randBool {gen : Type u} [RandomGen gen] (g : gen) : Bool × gen :=
|
||||
let (v, g') := randNat g 0 1
|
||||
(v = 1, g')
|
||||
@@ -113,9 +120,18 @@ initialize IO.stdGenRef : IO.Ref StdGen ←
|
||||
let seed := UInt64.toNat (ByteArray.toUInt64LE! (← IO.getRandomBytes 8))
|
||||
IO.mkRef (mkStdGen seed)
|
||||
|
||||
/--
|
||||
Seeds the random number generator state used by `IO.rand`.
|
||||
-/
|
||||
def IO.setRandSeed (n : Nat) : BaseIO Unit :=
|
||||
IO.stdGenRef.set (mkStdGen n)
|
||||
|
||||
/--
|
||||
Returns a pseudorandom number between `lo` and `hi`, using and updating a saved random generator
|
||||
state.
|
||||
|
||||
This state can be seeded using `IO.setRandSeed`.
|
||||
-/
|
||||
def IO.rand (lo hi : Nat) : BaseIO Nat := do
|
||||
let gen ← IO.stdGenRef.get
|
||||
let (r, gen) := randNat gen lo hi
|
||||
|
||||
@@ -129,6 +129,17 @@ We have pure functions for calculating the decimal representation of a `Nat` (`t
|
||||
a fast variant that handles small numbers (`USize`) via C code (`lean_string_of_usize`).
|
||||
-/
|
||||
|
||||
/--
|
||||
Returns a single digit representation of `n`, which is assumed to be in a base less than or equal to
|
||||
`16`. Returns `'*'` if `n > 15`.
|
||||
|
||||
Examples:
|
||||
* `Nat.digitChar 5 = '5'`
|
||||
* `Nat.digitChar 12 = 'c'`
|
||||
* `Nat.digitChar 15 = 'f'`
|
||||
* `Nat.digitChar 16 = '*'`
|
||||
* `Nat.digitChar 85 = '*'`
|
||||
-/
|
||||
def digitChar (n : Nat) : Char :=
|
||||
if n = 0 then '0' else
|
||||
if n = 1 then '1' else
|
||||
@@ -156,9 +167,29 @@ def toDigitsCore (base : Nat) : Nat → Nat → List Char → List Char
|
||||
if n' = 0 then d::ds
|
||||
else toDigitsCore base fuel n' (d::ds)
|
||||
|
||||
/--
|
||||
Returns the decimal representation of a natural number as a list of digit characters in the given
|
||||
base. If the base is greater than `16` then `'*'` is returned for digits greater than `0xf`.
|
||||
|
||||
Examples:
|
||||
* `Nat.toDigits 10 0xff = ['2', '5', '5']`
|
||||
* `Nat.toDigits 8 0xc = ['1', '4']`
|
||||
* `Nat.toDigits 16 0xcafe = ['c', 'a', 'f', 'e']`
|
||||
* `Nat.toDigits 80 200 = ['2', '*']`
|
||||
-/
|
||||
def toDigits (base : Nat) (n : Nat) : List Char :=
|
||||
toDigitsCore base (n+1) n []
|
||||
|
||||
/--
|
||||
Converts a word-sized unsigned integer into a decimal string.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `USize.repr 0 = "0"`
|
||||
* `USize.repr 28 = "28"`
|
||||
* `USize.repr 307 = "307"`
|
||||
-/
|
||||
@[extern "lean_string_of_usize"]
|
||||
protected def _root_.USize.repr (n : @& USize) : String :=
|
||||
(toDigits 10 n.toNat).asString
|
||||
@@ -172,10 +203,22 @@ private def reprFast (n : Nat) : String :=
|
||||
if h : n < USize.size then (USize.ofNatLT n h).repr
|
||||
else (toDigits 10 n).asString
|
||||
|
||||
/--
|
||||
Converts a natural number to its decimal string representation.
|
||||
-/
|
||||
@[implemented_by reprFast]
|
||||
protected def repr (n : Nat) : String :=
|
||||
(toDigits 10 n).asString
|
||||
|
||||
/--
|
||||
Converts a natural number less than `10` to the corresponding Unicode superscript digit character.
|
||||
Returns `'*'` for other numbers.
|
||||
|
||||
Examples:
|
||||
* `Nat.superDigitChar 3 = '³'`
|
||||
* `Nat.superDigitChar 7 = '⁷'`
|
||||
* `Nat.superDigitChar 10 = '*'`
|
||||
-/
|
||||
def superDigitChar (n : Nat) : Char :=
|
||||
if n = 0 then '⁰' else
|
||||
if n = 1 then '¹' else
|
||||
@@ -196,12 +239,37 @@ partial def toSuperDigitsAux : Nat → List Char → List Char
|
||||
if n' = 0 then d::ds
|
||||
else toSuperDigitsAux n' (d::ds)
|
||||
|
||||
/--
|
||||
Converts a natural number to the list of Unicode superscript digit characters that corresponds to
|
||||
its decimal representation.
|
||||
|
||||
Examples:
|
||||
* `Nat.toSuperDigits 0 = ['⁰']`
|
||||
* `Nat.toSuperDigits 35 = ['³', '⁵']`
|
||||
-/
|
||||
def toSuperDigits (n : Nat) : List Char :=
|
||||
toSuperDigitsAux n []
|
||||
|
||||
/--
|
||||
Converts a natural number to a string that contains the its decimal representation as Unicode
|
||||
superscript digit characters.
|
||||
|
||||
Examples:
|
||||
* `Nat.toSuperscriptString 0 = "⁰"`
|
||||
* `Nat.toSuperscriptString 35 = "³⁵"`
|
||||
-/
|
||||
def toSuperscriptString (n : Nat) : String :=
|
||||
(toSuperDigits n).asString
|
||||
|
||||
/--
|
||||
Converts a natural number less than `10` to the corresponding Unicode subscript digit character.
|
||||
Returns `'*'` for other numbers.
|
||||
|
||||
Examples:
|
||||
* `Nat.subDigitChar 3 = '₃'`
|
||||
* `Nat.subDigitChar 7 = '₇'`
|
||||
* `Nat.subDigitChar 10 = '*'`
|
||||
-/
|
||||
def subDigitChar (n : Nat) : Char :=
|
||||
if n = 0 then '₀' else
|
||||
if n = 1 then '₁' else
|
||||
@@ -222,9 +290,25 @@ partial def toSubDigitsAux : Nat → List Char → List Char
|
||||
if n' = 0 then d::ds
|
||||
else toSubDigitsAux n' (d::ds)
|
||||
|
||||
/--
|
||||
Converts a natural number to the list of Unicode subscript digit characters that corresponds to
|
||||
its decimal representation.
|
||||
|
||||
Examples:
|
||||
* `Nat.toSubDigits 0 = ['₀']`
|
||||
* `Nat.toSubDigits 35 = ['₃', '₅']`
|
||||
-/
|
||||
def toSubDigits (n : Nat) : List Char :=
|
||||
toSubDigitsAux n []
|
||||
|
||||
/--
|
||||
Converts a natural number to a string that contains the its decimal representation as Unicode
|
||||
subscript digit characters.
|
||||
|
||||
Examples:
|
||||
* `Nat.toSubscriptString 0 = "₀"`
|
||||
* `Nat.toSubscriptString 35 = "₃₅"`
|
||||
-/
|
||||
def toSubscriptString (n : Nat) : String :=
|
||||
(toSubDigits n).asString
|
||||
|
||||
@@ -233,6 +317,9 @@ end Nat
|
||||
instance : Repr Nat where
|
||||
reprPrec n _ := Nat.repr n
|
||||
|
||||
/--
|
||||
Returns the decimal string representation of an integer.
|
||||
-/
|
||||
protected def Int.repr : Int → String
|
||||
| ofNat m => Nat.repr m
|
||||
| negSucc m => "-" ++ Nat.repr (succ m)
|
||||
@@ -274,6 +361,14 @@ instance : Repr Char where
|
||||
protected def Char.repr (c : Char) : String :=
|
||||
c.quote
|
||||
|
||||
/--
|
||||
Converts a string to its corresponding Lean string literal syntax. Double quotes are added to each
|
||||
end, and internal characters are escaped as needed.
|
||||
|
||||
Examples:
|
||||
* `"abc".quote = "\"abc\""`
|
||||
* `"\"".quote = "\"\\\"\""`
|
||||
-/
|
||||
def String.quote (s : String) : String :=
|
||||
if s.isEmpty then "\"\""
|
||||
else s.foldl (fun s c => s ++ c.quoteCore) "\"" ++ "\""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.UInt.Bitwise
|
||||
import Init.Data.SInt.Lemmas
|
||||
|
||||
set_option hygiene false in
|
||||
@@ -55,3 +56,51 @@ theorem Bool.toBitVec_toISize {b : Bool} :
|
||||
· simp [toISize]
|
||||
· apply BitVec.eq_of_toNat_eq
|
||||
simp [toISize]
|
||||
|
||||
@[simp] theorem UInt8.toInt8_add (a b : UInt8) : (a + b).toInt8 = a.toInt8 + b.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_add (a b : UInt16) : (a + b).toInt16 = a.toInt16 + b.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_add (a b : UInt32) : (a + b).toInt32 = a.toInt32 + b.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_add (a b : UInt64) : (a + b).toInt64 = a.toInt64 + b.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_add (a b : USize) : (a + b).toISize = a.toISize + b.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toInt8_neg (a : UInt8) : (-a).toInt8 = -a.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_neg (a : UInt16) : (-a).toInt16 = -a.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_neg (a : UInt32) : (-a).toInt32 = -a.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_neg (a : UInt64) : (-a).toInt64 = -a.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_neg (a : USize) : (-a).toISize = -a.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toInt8_sub (a b : UInt8) : (a - b).toInt8 = a.toInt8 - b.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_sub (a b : UInt16) : (a - b).toInt16 = a.toInt16 - b.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_sub (a b : UInt32) : (a - b).toInt32 = a.toInt32 - b.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_sub (a b : UInt64) : (a - b).toInt64 = a.toInt64 - b.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_sub (a b : USize) : (a - b).toISize = a.toISize - b.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toInt8_mul (a b : UInt8) : (a * b).toInt8 = a.toInt8 * b.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_mul (a b : UInt16) : (a * b).toInt16 = a.toInt16 * b.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_mul (a b : UInt32) : (a * b).toInt32 = a.toInt32 * b.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_mul (a b : UInt64) : (a * b).toInt64 = a.toInt64 * b.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_mul (a b : USize) : (a * b).toISize = a.toISize * b.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toInt8_and (a b : UInt8) : (a &&& b).toInt8 = a.toInt8 &&& b.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_and (a b : UInt16) : (a &&& b).toInt16 = a.toInt16 &&& b.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_and (a b : UInt32) : (a &&& b).toInt32 = a.toInt32 &&& b.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_and (a b : UInt64) : (a &&& b).toInt64 = a.toInt64 &&& b.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_and (a b : USize) : (a &&& b).toISize = a.toISize &&& b.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toInt8_or (a b : UInt8) : (a ||| b).toInt8 = a.toInt8 ||| b.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_or (a b : UInt16) : (a ||| b).toInt16 = a.toInt16 ||| b.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_or (a b : UInt32) : (a ||| b).toInt32 = a.toInt32 ||| b.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_or (a b : UInt64) : (a ||| b).toInt64 = a.toInt64 ||| b.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_or (a b : USize) : (a ||| b).toISize = a.toISize ||| b.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toInt8_xor (a b : UInt8) : (a ^^^ b).toInt8 = a.toInt8 ^^^ b.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_xor (a b : UInt16) : (a ^^^ b).toInt16 = a.toInt16 ^^^ b.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_xor (a b : UInt32) : (a ^^^ b).toInt32 = a.toInt32 ^^^ b.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_xor (a b : UInt64) : (a ^^^ b).toInt64 = a.toInt64 ^^^ b.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_xor (a b : USize) : (a ^^^ b).toISize = a.toISize ^^^ b.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toInt8_not (a : UInt8) : (~~~a).toInt8 = ~~~a.toInt8 := rfl
|
||||
@[simp] theorem UInt16.toInt16_not (a : UInt16) : (~~~a).toInt16 = ~~~a.toInt16 := rfl
|
||||
@[simp] theorem UInt32.toInt32_not (a : UInt32) : (~~~a).toInt32 = ~~~a.toInt32 := rfl
|
||||
@[simp] theorem UInt64.toInt64_not (a : UInt64) : (~~~a).toInt64 = ~~~a.toInt64 := rfl
|
||||
@[simp] theorem USize.toISize_not (a : USize) : (~~~a).toISize = ~~~a.toISize := rfl
|
||||
|
||||
@@ -7,6 +7,8 @@ prelude
|
||||
import Init.Data.Float
|
||||
import Init.Data.SInt.Basic
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/--
|
||||
Truncates the value to the nearest integer, rounding towards zero.
|
||||
If NaN, returns `0`.
|
||||
@@ -59,13 +61,25 @@ If smaller than the minimum value for `ISize` (including -Inf), returns the mini
|
||||
@[extern "lean_int16_to_float"] opaque Int16.toFloat (n : Int16) : Float
|
||||
/-- Obtains the `Float` whose value is the same as the given `Int32`. -/
|
||||
@[extern "lean_int32_to_float"] opaque Int32.toFloat (n : Int32) : Float
|
||||
/-- Obtains a `Float` whose value is near the given `Int64`. It will be exactly the value of the
|
||||
given `Int64` 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 `Int64`.
|
||||
|
||||
It will be exactly the value of the given `Int64` 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_int64_to_float"] opaque Int64.toFloat (n : Int64) : Float
|
||||
/-- Obtains a `Float` whose value is near the given `ISize`. It will be exactly the value of the
|
||||
given `ISize` 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 `ISize`.
|
||||
|
||||
It will be exactly the value of the given `ISize` 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_isize_to_float"] opaque ISize.toFloat (n : ISize) : Float
|
||||
|
||||
@@ -7,6 +7,8 @@ prelude
|
||||
import Init.Data.Float32
|
||||
import Init.Data.SInt.Basic
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/--
|
||||
Truncates the value to the nearest integer, rounding towards zero.
|
||||
If NaN, returns `0`.
|
||||
@@ -57,18 +59,27 @@ If smaller than the minimum value for `ISize` (including -Inf), returns the mini
|
||||
@[extern "lean_int8_to_float32"] opaque Int8.toFloat32 (n : Int8) : Float32
|
||||
/-- Obtains the `Float32` whose value is the same as the given `Int16`. -/
|
||||
@[extern "lean_int16_to_float32"] opaque Int16.toFloat32 (n : Int16) : Float32
|
||||
/-- Obtains a `Float32` whose value is near the given `Int32`. It will be exactly the value of the
|
||||
given `Int32` 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 `Int32`.
|
||||
|
||||
It will be exactly the value of the given `Int32` 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.
|
||||
-/
|
||||
@[extern "lean_int32_to_float32"] opaque Int32.toFloat32 (n : Int32) : Float32
|
||||
/-- Obtains a `Float32` whose value is near the given `Int64`. It will be exactly the value of the
|
||||
given `Int64` 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 `Int64`.
|
||||
|
||||
It will be exactly the value of the given `Int64` 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.
|
||||
-/
|
||||
@[extern "lean_int64_to_float32"] opaque Int64.toFloat32 (n : Int64) : Float32
|
||||
/-- Obtains a `Float32` whose value is near the given `ISize`. It will be exactly the value of the
|
||||
given `ISize` 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 `ISize`.
|
||||
|
||||
It will be exactly the value of the given `ISize` 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.
|
||||
-/
|
||||
@[extern "lean_isize_to_float32"] opaque ISize.toFloat32 (n : ISize) : Float32
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,15 +9,31 @@ import Init.Data.UInt.Lemmas
|
||||
|
||||
namespace String
|
||||
|
||||
/-- Interpret the string as the decimal representation of a natural number.
|
||||
/--
|
||||
Interprets a string as the decimal representation of a natural number, returning it. Panics if the
|
||||
string does not contain a decimal natural number.
|
||||
|
||||
Panics if the string is not a string of digits. -/
|
||||
A string can be interpreted as a decimal natural number if it is not empty and all the characters in
|
||||
it are digits.
|
||||
|
||||
Use `String.isNat` to check whether `String.toNat!` would return a value. `String.toNat?` is a safer
|
||||
alternative that returns `none` instead of panicking when the string is not a natural number.
|
||||
|
||||
Examples:
|
||||
* `"0".toNat! = 0`
|
||||
* `"5".toNat! = 5`
|
||||
* `"587".toNat! = 587`
|
||||
-/
|
||||
def toNat! (s : String) : Nat :=
|
||||
if s.isNat then
|
||||
s.foldl (fun n c => n*10 + (c.toNat - '0'.toNat)) 0
|
||||
else
|
||||
panic! "Nat expected"
|
||||
|
||||
/--
|
||||
Decodes the UTF-8 character sequence that starts at a given index in a byte array, or `none` if
|
||||
index `i` is out of bounds or is not the start of a valid UTF-8 character.
|
||||
-/
|
||||
def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
|
||||
let c ← a[i]?
|
||||
if c &&& 0x80 == 0 then
|
||||
@@ -28,7 +44,7 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
|
||||
let r := ((c &&& 0x1f).toUInt32 <<< 6) ||| (c1 &&& 0x3f).toUInt32
|
||||
guard (0x80 ≤ r)
|
||||
-- TODO: Prove h from the definition of r once we have the necessary lemmas
|
||||
if h : r < 0xd800 then some ⟨r, .inl (UInt32.toNat_lt_of_lt (by decide) h)⟩ else none
|
||||
if h : r < 0xd800 then some ⟨r, .inl ((UInt32.lt_ofNat_iff (by decide)).1 h)⟩ else none
|
||||
else if c &&& 0xf0 == 0xe0 then
|
||||
let c1 ← a[i+1]?
|
||||
let c2 ← a[i+2]?
|
||||
@@ -42,8 +58,8 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
|
||||
if h : r < 0xd800 ∨ 0xdfff < r ∧ r < 0x110000 then
|
||||
have :=
|
||||
match h with
|
||||
| .inl h => Or.inl (UInt32.toNat_lt_of_lt (by decide) h)
|
||||
| .inr h => Or.inr ⟨UInt32.lt_toNat_of_lt (by decide) h.left, UInt32.toNat_lt_of_lt (by decide) h.right⟩
|
||||
| .inl h => Or.inl ((UInt32.lt_ofNat_iff (by decide)).1 h)
|
||||
| .inr h => Or.inr ⟨(UInt32.ofNat_lt_iff (by decide)).1 h.left, (UInt32.lt_ofNat_iff (by decide)).1 h.right⟩
|
||||
some ⟨r, this⟩
|
||||
else
|
||||
none
|
||||
@@ -58,12 +74,14 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
|
||||
((c2 &&& 0x3f).toUInt32 <<< 6) |||
|
||||
(c3 &&& 0x3f).toUInt32
|
||||
if h : 0x10000 ≤ r ∧ r < 0x110000 then
|
||||
some ⟨r, .inr ⟨Nat.lt_of_lt_of_le (by decide) (UInt32.le_toNat_of_le (by decide) h.left), UInt32.toNat_lt_of_lt (by decide) h.right⟩⟩
|
||||
some ⟨r, .inr ⟨Nat.lt_of_lt_of_le (by decide) ((UInt32.ofNat_le_iff (by decide)).1 h.left), (UInt32.lt_ofNat_iff (by decide)).1 h.right⟩⟩
|
||||
else none
|
||||
else
|
||||
none
|
||||
|
||||
/-- Returns true if the given byte array consists of valid UTF-8. -/
|
||||
/--
|
||||
Checks whether an array of bytes is a valid UTF-8 encoding of a string.
|
||||
-/
|
||||
@[extern "lean_string_validate_utf8"]
|
||||
def validateUTF8 (a : @& ByteArray) : Bool :=
|
||||
(loop 0).isSome
|
||||
@@ -76,7 +94,11 @@ where
|
||||
termination_by a.size - i
|
||||
decreasing_by exact Nat.sub_lt_sub_left ‹_› (Nat.lt_add_of_pos_right c.utf8Size_pos)
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`. -/
|
||||
/--
|
||||
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
|
||||
the corresponding string. Invalid UTF-8 characters in the byte array result in `(default : Char)`,
|
||||
or `'A'`, in the string.
|
||||
-/
|
||||
@[extern "lean_string_from_utf8_unchecked"]
|
||||
def fromUTF8 (a : @& ByteArray) (h : validateUTF8 a) : String :=
|
||||
loop 0 ""
|
||||
@@ -89,16 +111,23 @@ where
|
||||
termination_by a.size - i
|
||||
decreasing_by exact Nat.sub_lt_sub_left ‹_› (Nat.lt_add_of_pos_right c.utf8Size_pos)
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
|
||||
or returns `none` if `a` is not properly UTF-8 encoded. -/
|
||||
/--
|
||||
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
|
||||
the corresponding string, or returns `none` if the array is not a valid UTF-8 encoding of a string.
|
||||
-/
|
||||
@[inline] def fromUTF8? (a : ByteArray) : Option String :=
|
||||
if h : validateUTF8 a then fromUTF8 a h else none
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
|
||||
or panics if `a` is not properly UTF-8 encoded. -/
|
||||
/--
|
||||
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
|
||||
the corresponding string, or panics if the array is not a valid UTF-8 encoding of a string.
|
||||
-/
|
||||
@[inline] def fromUTF8! (a : ByteArray) : String :=
|
||||
if h : validateUTF8 a then fromUTF8 a h else panic! "invalid UTF-8 string"
|
||||
|
||||
/--
|
||||
Returns the sequence of bytes in a character's UTF-8 encoding.
|
||||
-/
|
||||
def utf8EncodeChar (c : Char) : List UInt8 :=
|
||||
let v := c.val
|
||||
if v ≤ 0x7f then
|
||||
@@ -122,7 +151,9 @@ def utf8EncodeChar (c : Char) : List UInt8 :=
|
||||
cases Decidable.em (c.val ≤ 0x7ff) <;> simp [*]
|
||||
cases Decidable.em (c.val ≤ 0xffff) <;> simp [*]
|
||||
|
||||
/-- Converts the given `String` to a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded byte array. -/
|
||||
/--
|
||||
Encodes a string in UTF-8 as an array of bytes.
|
||||
-/
|
||||
@[extern "lean_string_to_utf8"]
|
||||
def toUTF8 (a : @& String) : ByteArray :=
|
||||
⟨⟨a.data.flatMap utf8EncodeChar⟩⟩
|
||||
@@ -131,7 +162,11 @@ def toUTF8 (a : @& String) : ByteArray :=
|
||||
simp [toUTF8, ByteArray.size, Array.size, utf8ByteSize, List.flatMap]
|
||||
induction s.data <;> simp [List.map, List.flatten, utf8ByteSize.go, Nat.add_comm, *]
|
||||
|
||||
/-- Accesses a byte in the UTF-8 encoding of the `String`. O(1) -/
|
||||
/--
|
||||
Accesses the indicated byte in the UTF-8 encoding of a string.
|
||||
|
||||
At runtime, this function is implemented by efficient, constant-time code.
|
||||
-/
|
||||
@[extern "lean_string_get_byte_fast"]
|
||||
def getUtf8Byte (s : @& String) (n : Nat) (h : n < s.utf8ByteSize) : UInt8 :=
|
||||
(toUTF8 s)[n]'(size_toUTF8 _ ▸ h)
|
||||
@@ -154,12 +189,21 @@ macro_rules
|
||||
|
||||
namespace Iterator
|
||||
|
||||
/-- Advance the given iterator until the predicate returns true or the end of the string is reached. -/
|
||||
/--
|
||||
Moves the iterator forward until the Boolean predicate `p` returns `true` for the iterator's current
|
||||
character or until the end of the string is reached. Does nothing if the current character already
|
||||
satisfies `p`.
|
||||
-/
|
||||
@[specialize] def find (it : Iterator) (p : Char → Bool) : Iterator :=
|
||||
if it.atEnd then it
|
||||
else if p it.curr then it
|
||||
else find it.next p
|
||||
|
||||
/--
|
||||
Iterates over a string, updating a state at each character using the provided function `f`, until
|
||||
`f` returns `none`. Begins with the state `init`. Returns the state and character for which `f`
|
||||
returns `none`.
|
||||
-/
|
||||
@[specialize] def foldUntil (it : Iterator) (init : α) (f : α → Char → Option α) : α × Iterator :=
|
||||
if it.atEnd then
|
||||
(init, it)
|
||||
@@ -202,14 +246,33 @@ where
|
||||
else saveLine it.next (r.push it.curr)
|
||||
termination_by (it, 0)
|
||||
|
||||
/--
|
||||
Consistently de-indents the lines in a string, removing the same amount of leading whitespace from
|
||||
each line such that the least-indented line has no leading whitespace.
|
||||
|
||||
The number of leading whitespace characters to remove from each line is determined by counting the
|
||||
number of leading space (`' '`) and tab (`'\t'`) characters on lines after the first line that also
|
||||
contain non-whitespace characters. No distinction is made between tab and space characters; both
|
||||
count equally.
|
||||
|
||||
The least number of leading whitespace characters found is then removed from the beginning of each
|
||||
line. The first line's leading whitespace is not counted when determining how far to de-indent the
|
||||
string, but leading whitespace is removed from it.
|
||||
|
||||
Examples:
|
||||
* `"Here:\n fun x =>\n x + 1".removeLeadingSpaces = "Here:\nfun x =>\n x + 1"`
|
||||
* `"Here:\n\t\tfun x =>\n\t \tx + 1".removeLeadingSpaces = "Here:\nfun x =>\n \tx + 1"`
|
||||
* `"Here:\n\t\tfun x =>\n \n\t \tx + 1".removeLeadingSpaces = "Here:\nfun x =>\n\n \tx + 1"`
|
||||
-/
|
||||
def removeLeadingSpaces (s : String) : String :=
|
||||
let n := findLeadingSpacesSize s
|
||||
if n == 0 then s else removeNumLeadingSpaces n s
|
||||
|
||||
/--
|
||||
Replaces each `\r\n` with `\n` to normalize line endings,
|
||||
but does not validate that there are no isolated `\r` characters.
|
||||
It is an optimized version of `String.replace text "\r\n" "\n"`.
|
||||
Replaces each `\r\n` with `\n` to normalize line endings, but does not validate that there are no
|
||||
isolated `\r` characters.
|
||||
|
||||
This is an optimized version of `String.replace text "\r\n" "\n"`.
|
||||
-/
|
||||
def crlfToLf (text : String) : String :=
|
||||
go "" 0 0
|
||||
|
||||
@@ -128,6 +128,27 @@ instance {α : Type u} {β : α → Type v} [ToString α] [∀ x, ToString (β x
|
||||
instance {α : Type u} {p : α → Prop} [ToString α] : ToString (Subtype p) := ⟨fun s =>
|
||||
toString (val s)⟩
|
||||
|
||||
/--
|
||||
Interprets a string as the decimal representation of an integer, returning it. Returns `none` if the
|
||||
string does not contain a decimal integer.
|
||||
|
||||
A string can be interpreted as a decimal integer if it is not empty, its first character is either
|
||||
`'-'` or a digit, and all remaining characters are digits.
|
||||
|
||||
Use `String.isInt` to check whether `String.toInt?` would return `some`. `String.toInt!` is an
|
||||
alternative that panics instead of returning `none` when the string is not an integer.
|
||||
|
||||
Examples:
|
||||
* `"".toInt? = none`
|
||||
* `"0".toInt? = some 0`
|
||||
* `"5".toInt? = some 5`
|
||||
* `"-5".toInt? = some (-5)`
|
||||
* `"587".toInt? = some 587`
|
||||
* `"-587".toInt? = some (-587)`
|
||||
* `" 5".toInt? = none`
|
||||
* `"2-3".toInt? = none`
|
||||
* `"0xff".toInt? = none`
|
||||
-/
|
||||
def String.toInt? (s : String) : Option Int := do
|
||||
if s.get 0 = '-' then do
|
||||
let v ← (s.toSubstring.drop 1).toNat?;
|
||||
@@ -135,12 +156,48 @@ def String.toInt? (s : String) : Option Int := do
|
||||
else
|
||||
Int.ofNat <$> s.toNat?
|
||||
|
||||
/--
|
||||
Checks whether the string can be interpreted as the decimal representation of an integer.
|
||||
|
||||
A string can be interpreted as a decimal integer if it is not empty, its first character is
|
||||
`'-'` or a digit, and all subsequent characters are digits. Leading `+` characters are not allowed.
|
||||
|
||||
Use `String.toInt?` or `String.toInt!` to convert such a string to an integer.
|
||||
|
||||
Examples:
|
||||
* `"".isInt = false`
|
||||
* `"0".isInt = true`
|
||||
* `"-0".isInt = true`
|
||||
* `"5".isInt = true`
|
||||
* `"587".isInt = true`
|
||||
* `"-587".isInt = true`
|
||||
* `"+587".isInt = false`
|
||||
* `" 5".isInt = false`
|
||||
* `"2-3".isInt = false`
|
||||
* `"0xff".isInt = false`
|
||||
-/
|
||||
def String.isInt (s : String) : Bool :=
|
||||
if s.get 0 = '-' then
|
||||
(s.toSubstring.drop 1).isNat
|
||||
else
|
||||
s.isNat
|
||||
|
||||
/--
|
||||
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
|
||||
does not contain a decimal integer.
|
||||
|
||||
A string can be interpreted as a decimal integer if it is not empty, its first character is `'-'` or
|
||||
a digit, and all remaining characters are digits.
|
||||
|
||||
Use `String.isInt` to check whether `String.toInt!` would return a value. `String.toInt?` is a safer
|
||||
alternative that returns `none` instead of panicking when the string is not an integer.
|
||||
|
||||
Examples:
|
||||
* `"0".toInt! = 0`
|
||||
* `"5".toInt! = 5`
|
||||
* `"587".toInt! = 587`
|
||||
* `"-587".toInt! = -587`
|
||||
-/
|
||||
def String.toInt! (s : String) : Int :=
|
||||
match s.toInt? with
|
||||
| some v => v
|
||||
|
||||
@@ -7,6 +7,8 @@ prelude
|
||||
import Init.Data.UInt.BasicAux
|
||||
import Init.Data.BitVec.Basic
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
open Nat
|
||||
|
||||
/-- Converts a `Fin UInt8.size` into the corresponding `UInt8`. -/
|
||||
@@ -18,29 +20,110 @@ def UInt8.mk (bitVec : BitVec 8) : UInt8 :=
|
||||
def UInt8.ofNatCore (n : Nat) (h : n < UInt8.size) : UInt8 :=
|
||||
UInt8.ofNatLT n h
|
||||
|
||||
/--
|
||||
Adds two 8-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_add"]
|
||||
def UInt8.add (a b : UInt8) : UInt8 := ⟨a.toBitVec + b.toBitVec⟩
|
||||
/--
|
||||
Subtracts one 8-bit unsigned integer from another, wrapping around on underflow. Usually accessed
|
||||
via the `-` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_sub"]
|
||||
def UInt8.sub (a b : UInt8) : UInt8 := ⟨a.toBitVec - b.toBitVec⟩
|
||||
/--
|
||||
Multiplies two 8-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_mul"]
|
||||
def UInt8.mul (a b : UInt8) : UInt8 := ⟨a.toBitVec * b.toBitVec⟩
|
||||
/--
|
||||
Unsigned division for 8-bit unsigned integers, discarding the remainder. Usually accessed
|
||||
via the `/` operator.
|
||||
|
||||
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_div"]
|
||||
def UInt8.div (a b : UInt8) : UInt8 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The modulo operator for 8-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
When the divisor is `0`, the result is the dividend rather than an error.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt8.mod 5 2 = 1`
|
||||
* `UInt8.mod 4 2 = 0`
|
||||
* `UInt8.mod 4 0 = 4`
|
||||
-/
|
||||
@[extern "lean_uint8_mod"]
|
||||
def UInt8.mod (a b : UInt8) : UInt8 := ⟨BitVec.umod a.toBitVec b.toBitVec⟩
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated UInt8.mod (since := "2024-09-23")]
|
||||
def UInt8.modn (a : UInt8) (n : Nat) : UInt8 := ⟨Fin.modn a.toFin n⟩
|
||||
/--
|
||||
Bitwise and for 8-bit unsigned integers. Usually accessed via the `&&&` operator.
|
||||
|
||||
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_land"]
|
||||
def UInt8.land (a b : UInt8) : UInt8 := ⟨a.toBitVec &&& b.toBitVec⟩
|
||||
/--
|
||||
Bitwise or for 8-bit unsigned integers. Usually accessed via the `|||` operator.
|
||||
|
||||
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_lor"]
|
||||
def UInt8.lor (a b : UInt8) : UInt8 := ⟨a.toBitVec ||| b.toBitVec⟩
|
||||
/--
|
||||
Bitwise exclusive or for 8-bit unsigned integers. Usually accessed via the `^^^` operator.
|
||||
|
||||
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_xor"]
|
||||
def UInt8.xor (a b : UInt8) : UInt8 := ⟨a.toBitVec ^^^ b.toBitVec⟩
|
||||
/--
|
||||
Bitwise left shift for 8-bit unsigned integers. Usually accessed via the `<<<` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_shift_left"]
|
||||
def UInt8.shiftLeft (a b : UInt8) : UInt8 := ⟨a.toBitVec <<< (mod b 8).toBitVec⟩
|
||||
/--
|
||||
Bitwise right shift for 8-bit unsigned integers. Usually accessed via the `>>>` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_shift_right"]
|
||||
def UInt8.shiftRight (a b : UInt8) : UInt8 := ⟨a.toBitVec >>> (mod b 8).toBitVec⟩
|
||||
/--
|
||||
Strict inequality of 8-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `<` operator.
|
||||
-/
|
||||
def UInt8.lt (a b : UInt8) : Prop := a.toBitVec < b.toBitVec
|
||||
/--
|
||||
Non-strict inequality of 8-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `≤` operator.
|
||||
-/
|
||||
def UInt8.le (a b : UInt8) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
|
||||
instance : Add UInt8 := ⟨UInt8.add⟩
|
||||
@@ -55,10 +138,28 @@ instance : Div UInt8 := ⟨UInt8.div⟩
|
||||
instance : LT UInt8 := ⟨UInt8.lt⟩
|
||||
instance : LE UInt8 := ⟨UInt8.le⟩
|
||||
|
||||
/--
|
||||
Bitwise complement, also known as bitwise negation, for 8-bit unsigned integers. Usually accessed
|
||||
via the `~~~` prefix operator.
|
||||
|
||||
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_complement"]
|
||||
def UInt8.complement (a : UInt8) : UInt8 := ⟨~~~a.toBitVec⟩
|
||||
/--
|
||||
Negation of 8-bit unsigned integers, computed modulo `UInt8.size`.
|
||||
|
||||
`UInt8.neg a` is equivalent to `255 - a + 1`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_neg"]
|
||||
def UInt8.neg (a : UInt8) : UInt8 := ⟨-a.toBitVec⟩
|
||||
|
||||
instance : Complement UInt8 := ⟨UInt8.complement⟩
|
||||
instance : Neg UInt8 := ⟨UInt8.neg⟩
|
||||
instance : AndOp UInt8 := ⟨UInt8.land⟩
|
||||
instance : OrOp UInt8 := ⟨UInt8.lor⟩
|
||||
instance : Xor UInt8 := ⟨UInt8.xor⟩
|
||||
@@ -71,10 +172,33 @@ Converts `true` to `1` and `false` to `0`.
|
||||
@[extern "lean_bool_to_uint8"]
|
||||
def Bool.toUInt8 (b : Bool) : UInt8 := if b then 1 else 0
|
||||
|
||||
/--
|
||||
Decides whether one 8-bit unsigned integer is strictly less than another. Usually accessed via the
|
||||
`DecidableLT UInt8` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (6 : UInt8) < 7 then "yes" else "no") = "yes"`
|
||||
* `(if (5 : UInt8) < 5 then "yes" else "no") = "no"`
|
||||
* `show ¬((7 : UInt8) < 7) by decide`
|
||||
-/
|
||||
@[extern "lean_uint8_dec_lt"]
|
||||
def UInt8.decLt (a b : UInt8) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
|
||||
|
||||
/--
|
||||
Decides whether one 8-bit unsigned integer is less than or equal to another. Usually accessed via the
|
||||
`DecidableLE UInt8` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (15 : UInt8) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `(if (15 : UInt8) ≤ 5 then "yes" else "no") = "no"`
|
||||
* `(if (5 : UInt8) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `show (7 : UInt8) ≤ 7 by decide`
|
||||
-/
|
||||
@[extern "lean_uint8_dec_le"]
|
||||
def UInt8.decLe (a b : UInt8) : Decidable (a ≤ b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec ≤ b.toBitVec))
|
||||
@@ -93,29 +217,110 @@ def UInt16.mk (bitVec : BitVec 16) : UInt16 :=
|
||||
def UInt16.ofNatCore (n : Nat) (h : n < UInt16.size) : UInt16 :=
|
||||
UInt16.ofNatLT n h
|
||||
|
||||
/--
|
||||
Adds two 16-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_add"]
|
||||
def UInt16.add (a b : UInt16) : UInt16 := ⟨a.toBitVec + b.toBitVec⟩
|
||||
/--
|
||||
Subtracts one 16-bit unsigned integer from another, wrapping around on underflow. Usually accessed
|
||||
via the `-` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_sub"]
|
||||
def UInt16.sub (a b : UInt16) : UInt16 := ⟨a.toBitVec - b.toBitVec⟩
|
||||
/--
|
||||
Multiplies two 16-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_mul"]
|
||||
def UInt16.mul (a b : UInt16) : UInt16 := ⟨a.toBitVec * b.toBitVec⟩
|
||||
/--
|
||||
Unsigned division for 16-bit unsigned integers, discarding the remainder. Usually accessed
|
||||
via the `/` operator.
|
||||
|
||||
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_div"]
|
||||
def UInt16.div (a b : UInt16) : UInt16 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The modulo operator for 16-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
When the divisor is `0`, the result is the dividend rather than an error.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt16.mod 5 2 = 1`
|
||||
* `UInt16.mod 4 2 = 0`
|
||||
* `UInt16.mod 4 0 = 4`
|
||||
-/
|
||||
@[extern "lean_uint16_mod"]
|
||||
def UInt16.mod (a b : UInt16) : UInt16 := ⟨BitVec.umod a.toBitVec b.toBitVec⟩
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated UInt16.mod (since := "2024-09-23")]
|
||||
def UInt16.modn (a : UInt16) (n : Nat) : UInt16 := ⟨Fin.modn a.toFin n⟩
|
||||
/--
|
||||
Bitwise and for 16-bit unsigned integers. Usually accessed via the `&&&` operator.
|
||||
|
||||
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_land"]
|
||||
def UInt16.land (a b : UInt16) : UInt16 := ⟨a.toBitVec &&& b.toBitVec⟩
|
||||
/--
|
||||
Bitwise or for 16-bit unsigned integers. Usually accessed via the `|||` operator.
|
||||
|
||||
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_lor"]
|
||||
def UInt16.lor (a b : UInt16) : UInt16 := ⟨a.toBitVec ||| b.toBitVec⟩
|
||||
/--
|
||||
Bitwise exclusive or for 8-bit unsigned integers. Usually accessed via the `^^^` operator.
|
||||
|
||||
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_xor"]
|
||||
def UInt16.xor (a b : UInt16) : UInt16 := ⟨a.toBitVec ^^^ b.toBitVec⟩
|
||||
/--
|
||||
Bitwise left shift for 16-bit unsigned integers. Usually accessed via the `<<<` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_shift_left"]
|
||||
def UInt16.shiftLeft (a b : UInt16) : UInt16 := ⟨a.toBitVec <<< (mod b 16).toBitVec⟩
|
||||
/--
|
||||
Bitwise right shift for 16-bit unsigned integers. Usually accessed via the `>>>` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_shift_right"]
|
||||
def UInt16.shiftRight (a b : UInt16) : UInt16 := ⟨a.toBitVec >>> (mod b 16).toBitVec⟩
|
||||
/--
|
||||
Strict inequality of 16-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `<` operator.
|
||||
-/
|
||||
def UInt16.lt (a b : UInt16) : Prop := a.toBitVec < b.toBitVec
|
||||
/--
|
||||
Non-strict inequality of 16-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `≤` operator.
|
||||
-/
|
||||
def UInt16.le (a b : UInt16) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
|
||||
instance : Add UInt16 := ⟨UInt16.add⟩
|
||||
@@ -130,10 +335,28 @@ instance : Div UInt16 := ⟨UInt16.div⟩
|
||||
instance : LT UInt16 := ⟨UInt16.lt⟩
|
||||
instance : LE UInt16 := ⟨UInt16.le⟩
|
||||
|
||||
/--
|
||||
Bitwise complement, also known as bitwise negation, for 16-bit unsigned integers. Usually accessed
|
||||
via the `~~~` prefix operator.
|
||||
|
||||
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_complement"]
|
||||
def UInt16.complement (a : UInt16) : UInt16 := ⟨~~~a.toBitVec⟩
|
||||
/--
|
||||
Negation of 16-bit unsigned integers, computed modulo `UInt16.size`.
|
||||
|
||||
`UInt16.neg a` is equivalent to `65_535 - a + 1`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_neg"]
|
||||
def UInt16.neg (a : UInt16) : UInt16 := ⟨-a.toBitVec⟩
|
||||
|
||||
instance : Complement UInt16 := ⟨UInt16.complement⟩
|
||||
instance : Neg UInt16 := ⟨UInt16.neg⟩
|
||||
instance : AndOp UInt16 := ⟨UInt16.land⟩
|
||||
instance : OrOp UInt16 := ⟨UInt16.lor⟩
|
||||
instance : Xor UInt16 := ⟨UInt16.xor⟩
|
||||
@@ -147,11 +370,34 @@ Converts `true` to `1` and `false` to `0`.
|
||||
def Bool.toUInt16 (b : Bool) : UInt16 := if b then 1 else 0
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/--
|
||||
Decides whether one 16-bit unsigned integer is strictly less than another. Usually accessed via the
|
||||
`DecidableLT UInt16` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (6 : UInt16) < 7 then "yes" else "no") = "yes"`
|
||||
* `(if (5 : UInt16) < 5 then "yes" else "no") = "no"`
|
||||
* `show ¬((7 : UInt16) < 7) by decide`
|
||||
-/
|
||||
@[extern "lean_uint16_dec_lt"]
|
||||
def UInt16.decLt (a b : UInt16) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/--
|
||||
Decides whether one 16-bit unsigned integer is less than or equal to another. Usually accessed via the
|
||||
`DecidableLE UInt16` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (15 : UInt16) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `(if (15 : UInt16) ≤ 5 then "yes" else "no") = "no"`
|
||||
* `(if (5 : UInt16) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `show (7 : UInt16) ≤ 7 by decide`
|
||||
-/
|
||||
@[extern "lean_uint16_dec_le"]
|
||||
def UInt16.decLe (a b : UInt16) : Decidable (a ≤ b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec ≤ b.toBitVec))
|
||||
@@ -170,29 +416,110 @@ def UInt32.mk (bitVec : BitVec 32) : UInt32 :=
|
||||
def UInt32.ofNatCore (n : Nat) (h : n < UInt32.size) : UInt32 :=
|
||||
UInt32.ofNatLT n h
|
||||
|
||||
/--
|
||||
Adds two 32-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_add"]
|
||||
def UInt32.add (a b : UInt32) : UInt32 := ⟨a.toBitVec + b.toBitVec⟩
|
||||
/--
|
||||
Subtracts one 32-bit unsigned integer from another, wrapping around on underflow. Usually accessed
|
||||
via the `-` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_sub"]
|
||||
def UInt32.sub (a b : UInt32) : UInt32 := ⟨a.toBitVec - b.toBitVec⟩
|
||||
/--
|
||||
Multiplies two 32-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_mul"]
|
||||
def UInt32.mul (a b : UInt32) : UInt32 := ⟨a.toBitVec * b.toBitVec⟩
|
||||
/--
|
||||
Unsigned division for 32-bit unsigned integers, discarding the remainder. Usually accessed
|
||||
via the `/` operator.
|
||||
|
||||
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_div"]
|
||||
def UInt32.div (a b : UInt32) : UInt32 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The modulo operator for 32-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
When the divisor is `0`, the result is the dividend rather than an error.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt32.mod 5 2 = 1`
|
||||
* `UInt32.mod 4 2 = 0`
|
||||
* `UInt32.mod 4 0 = 4`
|
||||
-/
|
||||
@[extern "lean_uint32_mod"]
|
||||
def UInt32.mod (a b : UInt32) : UInt32 := ⟨BitVec.umod a.toBitVec b.toBitVec⟩
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated UInt32.mod (since := "2024-09-23")]
|
||||
def UInt32.modn (a : UInt32) (n : Nat) : UInt32 := ⟨Fin.modn a.toFin n⟩
|
||||
/--
|
||||
Bitwise and for 32-bit unsigned integers. Usually accessed via the `&&&` operator.
|
||||
|
||||
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_land"]
|
||||
def UInt32.land (a b : UInt32) : UInt32 := ⟨a.toBitVec &&& b.toBitVec⟩
|
||||
/--
|
||||
Bitwise or for 32-bit unsigned integers. Usually accessed via the `|||` operator.
|
||||
|
||||
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_lor"]
|
||||
def UInt32.lor (a b : UInt32) : UInt32 := ⟨a.toBitVec ||| b.toBitVec⟩
|
||||
/--
|
||||
Bitwise exclusive or for 32-bit unsigned integers. Usually accessed via the `^^^` operator.
|
||||
|
||||
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_xor"]
|
||||
def UInt32.xor (a b : UInt32) : UInt32 := ⟨a.toBitVec ^^^ b.toBitVec⟩
|
||||
/--
|
||||
Bitwise left shift for 32-bit unsigned integers. Usually accessed via the `<<<` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_shift_left"]
|
||||
def UInt32.shiftLeft (a b : UInt32) : UInt32 := ⟨a.toBitVec <<< (mod b 32).toBitVec⟩
|
||||
/--
|
||||
Bitwise right shift for 32-bit unsigned integers. Usually accessed via the `>>>` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_shift_right"]
|
||||
def UInt32.shiftRight (a b : UInt32) : UInt32 := ⟨a.toBitVec >>> (mod b 32).toBitVec⟩
|
||||
/--
|
||||
Strict inequality of 32-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `<` operator.
|
||||
-/
|
||||
def UInt32.lt (a b : UInt32) : Prop := a.toBitVec < b.toBitVec
|
||||
/--
|
||||
Non-strict inequality of 32-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `≤` operator.
|
||||
-/
|
||||
def UInt32.le (a b : UInt32) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
|
||||
instance : Add UInt32 := ⟨UInt32.add⟩
|
||||
@@ -207,10 +534,28 @@ instance : Div UInt32 := ⟨UInt32.div⟩
|
||||
instance : LT UInt32 := ⟨UInt32.lt⟩
|
||||
instance : LE UInt32 := ⟨UInt32.le⟩
|
||||
|
||||
/--
|
||||
Bitwise complement, also known as bitwise negation, for 32-bit unsigned integers. Usually accessed
|
||||
via the `~~~` prefix operator.
|
||||
|
||||
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_complement"]
|
||||
def UInt32.complement (a : UInt32) : UInt32 := ⟨~~~a.toBitVec⟩
|
||||
/--
|
||||
Negation of 32-bit unsigned integers, computed modulo `UInt32.size`.
|
||||
|
||||
`UInt32.neg a` is equivalent to `429_4967_295 - a + 1`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_neg"]
|
||||
def UInt32.neg (a : UInt32) : UInt32 := ⟨-a.toBitVec⟩
|
||||
|
||||
instance : Complement UInt32 := ⟨UInt32.complement⟩
|
||||
instance : Neg UInt32 := ⟨UInt32.neg⟩
|
||||
instance : AndOp UInt32 := ⟨UInt32.land⟩
|
||||
instance : OrOp UInt32 := ⟨UInt32.lor⟩
|
||||
instance : Xor UInt32 := ⟨UInt32.xor⟩
|
||||
@@ -232,29 +577,110 @@ def UInt64.mk (bitVec : BitVec 64) : UInt64 :=
|
||||
def UInt64.ofNatCore (n : Nat) (h : n < UInt64.size) : UInt64 :=
|
||||
UInt64.ofNatLT n h
|
||||
|
||||
/--
|
||||
Adds two 64-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_add"]
|
||||
def UInt64.add (a b : UInt64) : UInt64 := ⟨a.toBitVec + b.toBitVec⟩
|
||||
/--
|
||||
Subtracts one 64-bit unsigned integer from another, wrapping around on underflow. Usually accessed
|
||||
via the `-` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_sub"]
|
||||
def UInt64.sub (a b : UInt64) : UInt64 := ⟨a.toBitVec - b.toBitVec⟩
|
||||
/--
|
||||
Multiplies two 64-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_mul"]
|
||||
def UInt64.mul (a b : UInt64) : UInt64 := ⟨a.toBitVec * b.toBitVec⟩
|
||||
/--
|
||||
Unsigned division for 64-bit unsigned integers, discarding the remainder. Usually accessed
|
||||
via the `/` operator.
|
||||
|
||||
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_div"]
|
||||
def UInt64.div (a b : UInt64) : UInt64 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
|
||||
/--
|
||||
The modulo operator for 64-bit unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
When the divisor is `0`, the result is the dividend rather than an error.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt64.mod 5 2 = 1`
|
||||
* `UInt64.mod 4 2 = 0`
|
||||
* `UInt64.mod 4 0 = 4`
|
||||
-/
|
||||
@[extern "lean_uint64_mod"]
|
||||
def UInt64.mod (a b : UInt64) : UInt64 := ⟨BitVec.umod a.toBitVec b.toBitVec⟩
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated UInt64.mod (since := "2024-09-23")]
|
||||
def UInt64.modn (a : UInt64) (n : Nat) : UInt64 := ⟨Fin.modn a.toFin n⟩
|
||||
/--
|
||||
Bitwise and for 64-bit unsigned integers. Usually accessed via the `&&&` operator.
|
||||
|
||||
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_land"]
|
||||
def UInt64.land (a b : UInt64) : UInt64 := ⟨a.toBitVec &&& b.toBitVec⟩
|
||||
/--
|
||||
Bitwise or for 64-bit unsigned integers. Usually accessed via the `|||` operator.
|
||||
|
||||
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_lor"]
|
||||
def UInt64.lor (a b : UInt64) : UInt64 := ⟨a.toBitVec ||| b.toBitVec⟩
|
||||
/--
|
||||
Bitwise exclusive or for 64-bit unsigned integers. Usually accessed via the `^^^` operator.
|
||||
|
||||
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_xor"]
|
||||
def UInt64.xor (a b : UInt64) : UInt64 := ⟨a.toBitVec ^^^ b.toBitVec⟩
|
||||
/--
|
||||
Bitwise left shift for 64-bit unsigned integers. Usually accessed via the `<<<` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_shift_left"]
|
||||
def UInt64.shiftLeft (a b : UInt64) : UInt64 := ⟨a.toBitVec <<< (mod b 64).toBitVec⟩
|
||||
/--
|
||||
Bitwise right shift for 64-bit unsigned integers. Usually accessed via the `>>>` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_shift_right"]
|
||||
def UInt64.shiftRight (a b : UInt64) : UInt64 := ⟨a.toBitVec >>> (mod b 64).toBitVec⟩
|
||||
/--
|
||||
Strict inequality of 64-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `<` operator.
|
||||
-/
|
||||
def UInt64.lt (a b : UInt64) : Prop := a.toBitVec < b.toBitVec
|
||||
/--
|
||||
Non-strict inequality of 64-bit unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `≤` operator.
|
||||
-/
|
||||
def UInt64.le (a b : UInt64) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
|
||||
instance : Add UInt64 := ⟨UInt64.add⟩
|
||||
@@ -269,10 +695,28 @@ instance : Div UInt64 := ⟨UInt64.div⟩
|
||||
instance : LT UInt64 := ⟨UInt64.lt⟩
|
||||
instance : LE UInt64 := ⟨UInt64.le⟩
|
||||
|
||||
/--
|
||||
Bitwise complement, also known as bitwise negation, for 64-bit unsigned integers. Usually accessed
|
||||
via the `~~~` prefix operator.
|
||||
|
||||
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_complement"]
|
||||
def UInt64.complement (a : UInt64) : UInt64 := ⟨~~~a.toBitVec⟩
|
||||
/--
|
||||
Negation of 32-bit unsigned integers, computed modulo `UInt64.size`.
|
||||
|
||||
`UInt64.neg a` is equivalent to `18_446_744_073_709_551_615 - a + 1`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_neg"]
|
||||
def UInt64.neg (a : UInt64) : UInt64 := ⟨-a.toBitVec⟩
|
||||
|
||||
instance : Complement UInt64 := ⟨UInt64.complement⟩
|
||||
instance : Neg UInt64 := ⟨UInt64.neg⟩
|
||||
instance : AndOp UInt64 := ⟨UInt64.land⟩
|
||||
instance : OrOp UInt64 := ⟨UInt64.lor⟩
|
||||
instance : Xor UInt64 := ⟨UInt64.xor⟩
|
||||
@@ -285,10 +729,33 @@ Converts `true` to `1` and `false` to `0`.
|
||||
@[extern "lean_bool_to_uint64"]
|
||||
def Bool.toUInt64 (b : Bool) : UInt64 := if b then 1 else 0
|
||||
|
||||
/--
|
||||
Decides whether one 64-bit unsigned integer is strictly less than another. Usually accessed via the
|
||||
`DecidableLT UInt64` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (6 : UInt64) < 7 then "yes" else "no") = "yes"`
|
||||
* `(if (5 : UInt64) < 5 then "yes" else "no") = "no"`
|
||||
* `show ¬((7 : UInt64) < 7) by decide`
|
||||
-/
|
||||
@[extern "lean_uint64_dec_lt"]
|
||||
def UInt64.decLt (a b : UInt64) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
|
||||
|
||||
/--
|
||||
Decides whether one 64-bit unsigned integer is less than or equal to another. Usually accessed via the
|
||||
`DecidableLE UInt64` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (15 : UInt64) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `(if (15 : UInt64) ≤ 5 then "yes" else "no") = "no"`
|
||||
* `(if (5 : UInt64) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `show (7 : UInt64) ≤ 7 by decide`
|
||||
-/
|
||||
@[extern "lean_uint64_dec_le"]
|
||||
def UInt64.decLe (a b : UInt64) : Decidable (a ≤ b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec ≤ b.toBitVec))
|
||||
@@ -318,53 +785,152 @@ theorem usize_size_le : USize.size ≤ 18446744073709551616 :=
|
||||
theorem le_usize_size : 4294967296 ≤ USize.size :=
|
||||
USize.le_size
|
||||
|
||||
/--
|
||||
Multiplies two word-sized unsigned integers, wrapping around on overflow. Usually accessed via the
|
||||
`*` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_mul"]
|
||||
def USize.mul (a b : USize) : USize := ⟨a.toBitVec * b.toBitVec⟩
|
||||
/--
|
||||
Unsigned division for word-sized unsigned integers, discarding the remainder. Usually accessed
|
||||
via the `/` operator.
|
||||
|
||||
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_div"]
|
||||
def USize.div (a b : USize) : USize := ⟨a.toBitVec / b.toBitVec⟩
|
||||
/--
|
||||
The modulo operator for word-sized unsigned integers, which computes the remainder when dividing one
|
||||
integer by another. Usually accessed via the `%` operator.
|
||||
|
||||
When the divisor is `0`, the result is the dividend rather than an error.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `USize.mod 5 2 = 1`
|
||||
* `USize.mod 4 2 = 0`
|
||||
* `USize.mod 4 0 = 4`
|
||||
-/
|
||||
@[extern "lean_usize_mod"]
|
||||
def USize.mod (a b : USize) : USize := ⟨a.toBitVec % b.toBitVec⟩
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated USize.mod (since := "2024-09-23")]
|
||||
def USize.modn (a : USize) (n : Nat) : USize := ⟨Fin.modn a.toFin n⟩
|
||||
/--
|
||||
Bitwise and for word-sized unsigned integers. Usually accessed via the `&&&` operator.
|
||||
|
||||
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_land"]
|
||||
def USize.land (a b : USize) : USize := ⟨a.toBitVec &&& b.toBitVec⟩
|
||||
/--
|
||||
Bitwise or for word-sized unsigned integers. Usually accessed via the `|||` operator.
|
||||
|
||||
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_lor"]
|
||||
def USize.lor (a b : USize) : USize := ⟨a.toBitVec ||| b.toBitVec⟩
|
||||
/--
|
||||
Bitwise exclusive or for word-sized unsigned integers. Usually accessed via the `^^^` operator.
|
||||
|
||||
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
|
||||
integers are set.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_xor"]
|
||||
def USize.xor (a b : USize) : USize := ⟨a.toBitVec ^^^ b.toBitVec⟩
|
||||
/--
|
||||
Bitwise left shift for word-sized unsigned integers. Usually accessed via the `<<<` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_shift_left"]
|
||||
def USize.shiftLeft (a b : USize) : USize := ⟨a.toBitVec <<< (mod b (USize.ofNat System.Platform.numBits)).toBitVec⟩
|
||||
/--
|
||||
Bitwise right shift for word-sized unsigned integers. Usually accessed via the `>>>` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_shift_right"]
|
||||
def USize.shiftRight (a b : USize) : USize := ⟨a.toBitVec >>> (mod b (USize.ofNat System.Platform.numBits)).toBitVec⟩
|
||||
/--
|
||||
Upcast a `Nat` less than `2^32` to a `USize`.
|
||||
This is lossless because `USize.size` is either `2^32` or `2^64`.
|
||||
This function is overridden with a native implementation.
|
||||
Converts a natural number to a `USize`. Overflow is impossible on any supported platform because
|
||||
`USize.size` is either `2^32` or `2^64`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_of_nat"]
|
||||
def USize.ofNat32 (n : @& Nat) (h : n < 4294967296) : USize :=
|
||||
USize.ofNatLT n (Nat.lt_of_lt_of_le h USize.le_size)
|
||||
/--
|
||||
Converts 8-bit unsigned integers to word-sized unsigned integers.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_to_usize"]
|
||||
def UInt8.toUSize (a : UInt8) : USize :=
|
||||
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
|
||||
/--
|
||||
Converts word-sized unsigned integers to 8-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_to_uint8"]
|
||||
def USize.toUInt8 (a : USize) : UInt8 := a.toNat.toUInt8
|
||||
/--
|
||||
Converts 16-bit unsigned integers to word-sized unsigned integers.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_to_usize"]
|
||||
def UInt16.toUSize (a : UInt16) : USize :=
|
||||
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
|
||||
/--
|
||||
Converts word-sized unsigned integers to 16-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_to_uint16"]
|
||||
def USize.toUInt16 (a : USize) : UInt16 := a.toNat.toUInt16
|
||||
/--
|
||||
Converts 32-bit unsigned integers to word-sized unsigned integers.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_to_usize"]
|
||||
def UInt32.toUSize (a : UInt32) : USize := USize.ofNat32 a.toBitVec.toNat a.toBitVec.isLt
|
||||
/--
|
||||
Converts word-sized unsigned integers to 32-bit unsigned integers. Wraps around on overflow, which
|
||||
might occur on 64-bit architectures.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_to_uint32"]
|
||||
def USize.toUInt32 (a : USize) : UInt32 := a.toNat.toUInt32
|
||||
/-- Converts a `UInt64` to a `USize` by reducing modulo `USize.size`. -/
|
||||
/--
|
||||
Converts 64-bit unsigned integers to word-sized unsigned integers. On 32-bit machines, this may
|
||||
overflow, which results in the value wrapping around (that is, it is reduced modulo `USize.size`).
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_to_usize"]
|
||||
def UInt64.toUSize (a : UInt64) : USize := a.toNat.toUSize
|
||||
/--
|
||||
Upcast a `USize` to a `UInt64`.
|
||||
This is lossless because `USize.size` is either `2^32` or `2^64`.
|
||||
This function is overridden with a native implementation.
|
||||
Converts word-sized unsigned integers to 32-bit unsigned integers. This cannot overflow because
|
||||
`USize.size` is either `2^32` or `2^64`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_to_uint64"]
|
||||
def USize.toUInt64 (a : USize) : UInt64 :=
|
||||
@@ -378,10 +944,26 @@ instance : HMod USize Nat USize := ⟨USize.modn⟩
|
||||
|
||||
instance : Div USize := ⟨USize.div⟩
|
||||
|
||||
/--
|
||||
Bitwise complement, also known as bitwise negation, for word-sized unsigned integers. Usually
|
||||
accessed via the `~~~` prefix operator.
|
||||
|
||||
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_complement"]
|
||||
def USize.complement (a : USize) : USize := ⟨~~~a.toBitVec⟩
|
||||
/--
|
||||
Negation of word-sized unsigned integers, computed modulo `USize.size`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_neg"]
|
||||
def USize.neg (a : USize) : USize := ⟨-a.toBitVec⟩
|
||||
|
||||
instance : Complement USize := ⟨USize.complement⟩
|
||||
instance : Neg USize := ⟨USize.neg⟩
|
||||
instance : AndOp USize := ⟨USize.land⟩
|
||||
instance : OrOp USize := ⟨USize.lor⟩
|
||||
instance : Xor USize := ⟨USize.xor⟩
|
||||
|
||||
@@ -7,6 +7,8 @@ prelude
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.BitVec.BasicAux
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/-!
|
||||
This module exists to provide the very basic `UInt8` etc. definitions required for
|
||||
`Init.Data.Char.Basic` and `Init.Data.Array.Basic`. These are very important as they are used in
|
||||
@@ -20,17 +22,53 @@ open Nat
|
||||
def UInt8.toFin (x : UInt8) : Fin UInt8.size := x.toBitVec.toFin
|
||||
@[deprecated UInt8.toFin (since := "2025-02-12"), inherit_doc UInt8.toFin]
|
||||
def UInt8.val (x : UInt8) : Fin UInt8.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts a natural number to an 8-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt8.ofNat 5 = 5`
|
||||
* `UInt8.ofNat 255 = 255`
|
||||
* `UInt8.ofNat 256 = 0`
|
||||
* `UInt8.ofNat 259 = 3`
|
||||
* `UInt8.ofNat 32770 = 2`
|
||||
-/
|
||||
@[extern "lean_uint8_of_nat"]
|
||||
def UInt8.ofNat (n : @& Nat) : UInt8 := ⟨BitVec.ofNat 8 n⟩
|
||||
|
||||
/--
|
||||
Converts the given natural number to `UInt8`, but returns `2^8 - 1` for natural numbers `>= 2^8`.
|
||||
Converts a natural number to an 8-bit unsigned integer, returning the largest representable value if
|
||||
the number is too large.
|
||||
|
||||
Returns `2^8 - 1` for natural numbers greater than or equal to `2^8`.
|
||||
-/
|
||||
def UInt8.ofNatTruncate (n : Nat) : UInt8 :=
|
||||
if h : n < UInt8.size then
|
||||
UInt8.ofNatLT n h
|
||||
else
|
||||
UInt8.ofNatLT (UInt8.size - 1) (by decide)
|
||||
|
||||
/--
|
||||
Converts a natural number to an 8-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `Nat.toUInt8 5 = 5`
|
||||
* `Nat.toUInt8 255 = 255`
|
||||
* `Nat.toUInt8 256 = 0`
|
||||
* `Nat.toUInt8 259 = 3`
|
||||
* `Nat.toUInt8 32770 = 2`
|
||||
-/
|
||||
abbrev Nat.toUInt8 := UInt8.ofNat
|
||||
|
||||
/--
|
||||
Converts an 8-bit unsigned integer to an arbitrary-precision natural number.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_to_nat"]
|
||||
def UInt8.toNat (n : UInt8) : Nat := n.toBitVec.toNat
|
||||
|
||||
@@ -40,21 +78,64 @@ instance UInt8.instOfNat : OfNat UInt8 n := ⟨UInt8.ofNat n⟩
|
||||
def UInt16.toFin (x : UInt16) : Fin UInt16.size := x.toBitVec.toFin
|
||||
@[deprecated UInt16.toFin (since := "2025-02-12"), inherit_doc UInt16.toFin]
|
||||
def UInt16.val (x : UInt16) : Fin UInt16.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts a natural number to a 16-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt16.ofNat 5 = 5`
|
||||
* `UInt16.ofNat 255 = 255`
|
||||
* `UInt16.ofNat 32770 = 32770`
|
||||
* `UInt16.ofNat 65537 = 1`
|
||||
-/
|
||||
@[extern "lean_uint16_of_nat"]
|
||||
def UInt16.ofNat (n : @& Nat) : UInt16 := ⟨BitVec.ofNat 16 n⟩
|
||||
/--
|
||||
Converts the given natural number to `UInt16`, but returns `2^16 - 1` for natural numbers `>= 2^16`.
|
||||
Converts a natural number to a 16-bit unsigned integer, returning the largest representable value if
|
||||
the number is too large.
|
||||
|
||||
Returns `2^16 - 1` for natural numbers greater than or equal to `2^16`.
|
||||
-/
|
||||
def UInt16.ofNatTruncate (n : Nat) : UInt16 :=
|
||||
if h : n < UInt16.size then
|
||||
UInt16.ofNatLT n h
|
||||
else
|
||||
UInt16.ofNatLT (UInt16.size - 1) (by decide)
|
||||
|
||||
/--
|
||||
Converts a natural number to a 16-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `Nat.toUInt16 5 = 5`
|
||||
* `Nat.toUInt16 255 = 255`
|
||||
* `Nat.toUInt16 32770 = 32770`
|
||||
* `Nat.toUInt16 65537 = 1`
|
||||
-/
|
||||
abbrev Nat.toUInt16 := UInt16.ofNat
|
||||
|
||||
/--
|
||||
Converts a 16-bit unsigned integer to an arbitrary-precision natural number.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_to_nat"]
|
||||
def UInt16.toNat (n : UInt16) : Nat := n.toBitVec.toNat
|
||||
/--
|
||||
Converts 16-bit unsigned integers to 8-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_to_uint8"]
|
||||
def UInt16.toUInt8 (a : UInt16) : UInt8 := a.toNat.toUInt8
|
||||
/--
|
||||
Converts 8-bit unsigned integers to 16-bit unsigned integers.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_to_uint16"]
|
||||
def UInt8.toUInt16 (a : UInt8) : UInt16 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)⟩⟩
|
||||
|
||||
@@ -64,25 +145,70 @@ instance UInt16.instOfNat : OfNat UInt16 n := ⟨UInt16.ofNat n⟩
|
||||
def UInt32.toFin (x : UInt32) : Fin UInt32.size := x.toBitVec.toFin
|
||||
@[deprecated UInt32.toFin (since := "2025-02-12"), inherit_doc UInt32.toFin]
|
||||
def UInt32.val (x : UInt32) : Fin UInt32.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts a natural number to a 32-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt32.ofNat 5 = 5`
|
||||
* `UInt32.ofNat 65539 = 65539`
|
||||
* `UInt32.ofNat 4_294_967_299 = 3`
|
||||
-/
|
||||
@[extern "lean_uint32_of_nat"]
|
||||
def UInt32.ofNat (n : @& Nat) : UInt32 := ⟨BitVec.ofNat 32 n⟩
|
||||
@[inline, deprecated UInt32.ofNatLT (since := "2025-02-13"), inherit_doc UInt32.ofNatLT]
|
||||
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := UInt32.ofNatLT n h
|
||||
/--
|
||||
Converts the given natural number to `UInt32`, but returns `2^32 - 1` for natural numbers `>= 2^32`.
|
||||
Converts a natural number to a 32-bit unsigned integer, returning the largest representable value if
|
||||
the number is too large.
|
||||
|
||||
Returns `2^32 - 1` for natural numbers greater than or equal to `2^32`.
|
||||
-/
|
||||
def UInt32.ofNatTruncate (n : Nat) : UInt32 :=
|
||||
if h : n < UInt32.size then
|
||||
UInt32.ofNatLT n h
|
||||
else
|
||||
UInt32.ofNatLT (UInt32.size - 1) (by decide)
|
||||
/--
|
||||
Converts a natural number to a 32-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `Nat.toUInt32 5 = 5`
|
||||
* `Nat.toUInt32 65_539 = 65_539`
|
||||
* `Nat.toUInt32 4_294_967_299 = 3`
|
||||
-/
|
||||
abbrev Nat.toUInt32 := UInt32.ofNat
|
||||
|
||||
/--
|
||||
Converts a 32-bit unsigned integer to an 8-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_to_uint8"]
|
||||
def UInt32.toUInt8 (a : UInt32) : UInt8 := a.toNat.toUInt8
|
||||
/--
|
||||
Converts 32-bit unsigned integers to 16-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_to_uint16"]
|
||||
def UInt32.toUInt16 (a : UInt32) : UInt16 := a.toNat.toUInt16
|
||||
/--
|
||||
Converts 8-bit unsigned integers to 32-bit unsigned integers.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_to_uint32"]
|
||||
def UInt8.toUInt32 (a : UInt8) : UInt32 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)⟩⟩
|
||||
/--
|
||||
Converts 16-bit unsigned integers to 32-bit unsigned integers.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_to_uint32"]
|
||||
def UInt16.toUInt32 (a : UInt16) : UInt32 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)⟩⟩
|
||||
|
||||
@@ -110,29 +236,89 @@ theorem UInt32.lt_ofNat'_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt
|
||||
def UInt64.toFin (x : UInt64) : Fin UInt64.size := x.toBitVec.toFin
|
||||
@[deprecated UInt64.toFin (since := "2025-02-12"), inherit_doc UInt64.toFin]
|
||||
def UInt64.val (x : UInt64) : Fin UInt64.size := x.toFin
|
||||
/--
|
||||
Converts a natural number to a 64-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `UInt64.ofNat 5 = 5`
|
||||
* `UInt64.ofNat 65539 = 65539`
|
||||
* `UInt64.ofNat 4_294_967_299 = 4_294_967_299`
|
||||
* `UInt64.ofNat 18_446_744_073_709_551_620 = 4`
|
||||
-/
|
||||
@[extern "lean_uint64_of_nat"]
|
||||
def UInt64.ofNat (n : @& Nat) : UInt64 := ⟨BitVec.ofNat 64 n⟩
|
||||
/--
|
||||
Converts the given natural number to `UInt64`, but returns `2^64 - 1` for natural numbers `>= 2^64`.
|
||||
Converts a natural number to a 64-bit unsigned integer, returning the largest representable value if
|
||||
the number is too large.
|
||||
|
||||
Returns `2^64 - 1` for natural numbers greater than or equal to `2^64`.
|
||||
-/
|
||||
def UInt64.ofNatTruncate (n : Nat) : UInt64 :=
|
||||
if h : n < UInt64.size then
|
||||
UInt64.ofNatLT n h
|
||||
else
|
||||
UInt64.ofNatLT (UInt64.size - 1) (by decide)
|
||||
/--
|
||||
Converts a natural number to a 64-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `Nat.toUInt64 5 = 5`
|
||||
* `Nat.toUInt64 65539 = 65539`
|
||||
* `Nat.toUInt64 4_294_967_299 = 4_294_967_299`
|
||||
* `Nat.toUInt64 18_446_744_073_709_551_620 = 4`
|
||||
-/
|
||||
abbrev Nat.toUInt64 := UInt64.ofNat
|
||||
/--
|
||||
Converts a 64-bit unsigned integer to an arbitrary-precision natural number.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_to_nat"]
|
||||
def UInt64.toNat (n : UInt64) : Nat := n.toBitVec.toNat
|
||||
/--
|
||||
Converts 64-bit unsigned integers to 8-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_to_uint8"]
|
||||
def UInt64.toUInt8 (a : UInt64) : UInt8 := a.toNat.toUInt8
|
||||
/--
|
||||
Converts 64-bit unsigned integers to 16-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_to_uint16"]
|
||||
def UInt64.toUInt16 (a : UInt64) : UInt16 := a.toNat.toUInt16
|
||||
/--
|
||||
Converts 64-bit unsigned integers to 32-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint64_to_uint32"]
|
||||
def UInt64.toUInt32 (a : UInt64) : UInt32 := a.toNat.toUInt32
|
||||
/--
|
||||
Converts 8-bit unsigned integers to 64-bit unsigned integers.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint8_to_uint64"]
|
||||
def UInt8.toUInt64 (a : UInt8) : UInt64 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)⟩⟩
|
||||
/--
|
||||
Converts 16-bit unsigned integers to 64-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint16_to_uint64"]
|
||||
def UInt16.toUInt64 (a : UInt16) : UInt64 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)⟩⟩
|
||||
/--
|
||||
Converts 32-bit unsigned integers to 64-bit unsigned integers. Wraps around on overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_uint32_to_uint64"]
|
||||
def UInt32.toUInt64 (a : UInt32) : UInt64 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)⟩⟩
|
||||
|
||||
@@ -153,26 +339,60 @@ theorem usize_size_pos : 0 < USize.size :=
|
||||
def USize.toFin (x : USize) : Fin USize.size := x.toBitVec.toFin
|
||||
@[deprecated USize.toFin (since := "2025-02-12"), inherit_doc USize.toFin]
|
||||
def USize.val (x : USize) : Fin USize.size := x.toFin
|
||||
/--
|
||||
Converts an arbitrary-precision natural number to an unsigned word-sized integer, wrapping around on
|
||||
overflow.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_of_nat"]
|
||||
def USize.ofNat (n : @& Nat) : USize := ⟨BitVec.ofNat _ n⟩
|
||||
/--
|
||||
Converts the given natural number to `USize`, but returns `USize.size - 1` (i.e., `2^64 - 1` or
|
||||
`2^32 - 1` depending on the platform) for natural numbers `>= USize.size`.
|
||||
Converts a natural number to `USize`, returning the largest representable value if the number is too
|
||||
large.
|
||||
|
||||
Returns `USize.size - 1`, which is `2^64 - 1` or `2^32 - 1` depending on the platform, for natural
|
||||
numbers greater than or equal to `USize.size`.
|
||||
-/
|
||||
def USize.ofNatTruncate (n : Nat) : USize :=
|
||||
if h : n < USize.size then
|
||||
USize.ofNatLT n h
|
||||
else
|
||||
USize.ofNatLT (USize.size - 1) (Nat.pred_lt (Nat.ne_zero_of_lt USize.size_pos))
|
||||
abbrev Nat.toUSize := USize.ofNat
|
||||
@[inherit_doc USize.ofNat] abbrev Nat.toUSize := USize.ofNat
|
||||
/--
|
||||
Converts a word-sized unsigned integer to an arbitrary-precision natural number.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_to_nat"]
|
||||
def USize.toNat (n : USize) : Nat := n.toBitVec.toNat
|
||||
/--
|
||||
Adds two word-sized unsigned integers, wrapping around on overflow. Usually accessed via the `+`
|
||||
operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_add"]
|
||||
def USize.add (a b : USize) : USize := ⟨a.toBitVec + b.toBitVec⟩
|
||||
/--
|
||||
Subtracts one word-sized-bit unsigned integer from another, wrapping around on underflow. Usually
|
||||
accessed via the `-` operator.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
-/
|
||||
@[extern "lean_usize_sub"]
|
||||
def USize.sub (a b : USize) : USize := ⟨a.toBitVec - b.toBitVec⟩
|
||||
|
||||
/--
|
||||
Strict inequality of word-sized unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `<` operator.
|
||||
-/
|
||||
def USize.lt (a b : USize) : Prop := a.toBitVec < b.toBitVec
|
||||
/--
|
||||
Non-strict inequality of word-sized unsigned integers, defined as inequality of the corresponding
|
||||
natural numbers. Usually accessed via the `≤` operator.
|
||||
-/
|
||||
def USize.le (a b : USize) : Prop := a.toBitVec ≤ b.toBitVec
|
||||
|
||||
instance USize.instOfNat : OfNat USize n := ⟨USize.ofNat n⟩
|
||||
@@ -182,10 +402,33 @@ instance : Sub USize := ⟨USize.sub⟩
|
||||
instance : LT USize := ⟨USize.lt⟩
|
||||
instance : LE USize := ⟨USize.le⟩
|
||||
|
||||
/--
|
||||
Decides whether one word-sized unsigned integer is strictly less than another. Usually accessed via
|
||||
the `DecidableLT USize` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (6 : USize) < 7 then "yes" else "no") = "yes"`
|
||||
* `(if (5 : USize) < 5 then "yes" else "no") = "no"`
|
||||
* `show ¬((7 : USize) < 7) by decide`
|
||||
-/
|
||||
@[extern "lean_usize_dec_lt"]
|
||||
def USize.decLt (a b : USize) : Decidable (a < b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
|
||||
|
||||
/--
|
||||
Decides whether one word-sized unsigned integer is less than or equal to another. Usually accessed
|
||||
via the `DecidableLE USize` instance.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation.
|
||||
|
||||
Examples:
|
||||
* `(if (15 : USize) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `(if (15 : USize) ≤ 5 then "yes" else "no") = "no"`
|
||||
* `(if (5 : USize) ≤ 15 then "yes" else "no") = "yes"`
|
||||
* `show (7 : USize) ≤ 7 by decide`
|
||||
-/
|
||||
@[extern "lean_usize_dec_le"]
|
||||
def USize.decLe (a b : USize) : Decidable (a ≤ b) :=
|
||||
inferInstanceAs (Decidable (a.toBitVec ≤ b.toBitVec))
|
||||
|
||||
@@ -6,18 +6,12 @@ Authors: Markus Himmel, Mac Malone
|
||||
prelude
|
||||
import Init.Data.UInt.Lemmas
|
||||
import Init.Data.Fin.Bitwise
|
||||
import Init.Data.BitVec.Lemmas
|
||||
|
||||
set_option hygiene false in
|
||||
macro "declare_bitwise_uint_theorems" typeName:ident bits:term:arg : command =>
|
||||
`(
|
||||
namespace $typeName
|
||||
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec / b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec % b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_not {a : $typeName} : (~~~a).toBitVec = ~~~a.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
|
||||
@@ -70,3 +64,362 @@ theorem Bool.toBitVec_toUSize {b : Bool} :
|
||||
· simp [toUSize]
|
||||
· apply BitVec.eq_of_toNat_eq
|
||||
simp [toUSize]
|
||||
|
||||
@[simp] theorem UInt8.toFin_and (a b : UInt8) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt16.toFin_and (a b : UInt16) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toFin_and (a b : UInt32) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toFin_and (a b : UInt64) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem USize.toFin_and (a b : USize) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_and (a b : UInt8) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := rfl
|
||||
@[simp] theorem UInt8.toUInt32_and (a b : UInt8) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_and (a b : UInt8) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUSize_and (a b : UInt8) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_and (a b : UInt16) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt16.toUInt32_and (a b : UInt16) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_and (a b : UInt16) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
|
||||
@[simp] theorem UInt16.toUSize_and (a b : UInt16) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_and (a b : UInt32) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt16_and (a b : UInt32) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt64_and (a b : UInt32) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
|
||||
@[simp] theorem UInt32.toUSize_and (a b : UInt32) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt8_and (a b : USize) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt16_and (a b : USize) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt32_and (a b : USize) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt64_and (a b : USize) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_and (a b : UInt64) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_and (a b : UInt64) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_and (a b : UInt64) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUSize_and (a b : UInt64) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toFin_or (a b : UInt8) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt16.toFin_or (a b : UInt16) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toFin_or (a b : UInt32) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toFin_or (a b : UInt64) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem USize.toFin_or (a b : USize) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_or (a b : UInt8) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := rfl
|
||||
@[simp] theorem UInt8.toUInt32_or (a b : UInt8) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_or (a b : UInt8) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUSize_or (a b : UInt8) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_or (a b : UInt16) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt16.toUInt32_or (a b : UInt16) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_or (a b : UInt16) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
|
||||
@[simp] theorem UInt16.toUSize_or (a b : UInt16) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_or (a b : UInt32) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt16_or (a b : UInt32) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt64_or (a b : UInt32) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
|
||||
@[simp] theorem UInt32.toUSize_or (a b : UInt32) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt8_or (a b : USize) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt16_or (a b : USize) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt32_or (a b : USize) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt64_or (a b : USize) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_or (a b : UInt64) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_or (a b : UInt64) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_or (a b : UInt64) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUSize_or (a b : UInt64) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toFin_xor (a b : UInt8) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt16.toFin_xor (a b : UInt16) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toFin_xor (a b : UInt32) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toFin_xor (a b : UInt64) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
|
||||
@[simp] theorem USize.toFin_xor (a b : USize) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_xor (a b : UInt8) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := rfl
|
||||
@[simp] theorem UInt8.toUInt32_xor (a b : UInt8) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_xor (a b : UInt8) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUSize_xor (a b : UInt8) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_xor (a b : UInt16) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt16.toUInt32_xor (a b : UInt16) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_xor (a b : UInt16) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
|
||||
@[simp] theorem UInt16.toUSize_xor (a b : UInt16) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_xor (a b : UInt32) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt16_xor (a b : UInt32) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt64_xor (a b : UInt32) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
|
||||
@[simp] theorem UInt32.toUSize_xor (a b : UInt32) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt8_xor (a b : USize) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt16_xor (a b : USize) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt32_xor (a b : USize) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt64_xor (a b : USize) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_xor (a b : UInt64) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_xor (a b : UInt64) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_xor (a b : UInt64) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUSize_xor (a b : UInt64) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toNat_not (a : UInt8) : (~~~a).toNat = UInt8.size - 1 - a.toNat := by
|
||||
rw [← toNat_toBitVec, UInt8.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
|
||||
@[simp] theorem UInt16.toNat_not (a : UInt16) : (~~~a).toNat = UInt16.size - 1 - a.toNat := by
|
||||
rw [← toNat_toBitVec, UInt16.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
|
||||
@[simp] theorem UInt32.toNat_not (a : UInt32) : (~~~a).toNat = UInt32.size - 1 - a.toNat := by
|
||||
rw [← toNat_toBitVec, UInt32.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
|
||||
@[simp] theorem UInt64.toNat_not (a : UInt64) : (~~~a).toNat = UInt64.size - 1 - a.toNat := by
|
||||
rw [← toNat_toBitVec, UInt64.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
|
||||
@[simp] theorem USize.toNat_not (a : USize) : (~~~a).toNat = USize.size - 1 - a.toNat := by
|
||||
rw [← toNat_toBitVec, USize.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
|
||||
|
||||
@[simp] theorem UInt8.toFin_not (a : UInt8) : (~~~a).toFin = a.toFin.rev := by
|
||||
rw [← toFin_toBitVec, UInt8.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
|
||||
@[simp] theorem UInt16.toFin_not (a : UInt16) : (~~~a).toFin = a.toFin.rev := by
|
||||
rw [← toFin_toBitVec, UInt16.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
|
||||
@[simp] theorem UInt32.toFin_not (a : UInt32) : (~~~a).toFin = a.toFin.rev := by
|
||||
rw [← toFin_toBitVec, UInt32.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
|
||||
@[simp] theorem UInt64.toFin_not (a : UInt64) : (~~~a).toFin = a.toFin.rev := by
|
||||
rw [← toFin_toBitVec, UInt64.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
|
||||
@[simp] theorem USize.toFin_not (a : USize) : (~~~a).toFin = a.toFin.rev := by
|
||||
rw [← toFin_toBitVec, USize.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_not (a : UInt16) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt8_not (a : UInt32) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_not (a : UInt64) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt8_not (a : USize) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_not (a : UInt32) : (~~~a).toUInt16 = ~~~a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_not (a : UInt64) : (~~~a).toUInt16 = ~~~a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt16_not (a : USize) : (~~~a).toUInt16 = ~~~a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_not (a : UInt64) : (~~~a).toUInt32 = ~~~a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt32_not (a : USize) : (~~~a).toUInt32 = ~~~a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUSize_not (a : UInt64) : (~~~a).toUSize = ~~~a.toUSize := USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_not (a : UInt8) : (~~~a).toUInt16 = ~~~a.toUInt16 % 256 := by
|
||||
simp [UInt8.toUInt16_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt32_not (a : UInt8) : (~~~a).toUInt32 = ~~~a.toUInt32 % 256 := by
|
||||
simp [UInt8.toUInt32_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt64_not (a : UInt8) : (~~~a).toUInt64 = ~~~a.toUInt64 % 256 := by
|
||||
simp [UInt8.toUInt64_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUSize_not (a : UInt8) : (~~~a).toUSize = ~~~a.toUSize % 256 := by
|
||||
simp [UInt8.toUSize_eq_mod_256_iff]
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_not (a : UInt16) : (~~~a).toUInt32 = ~~~a.toUInt32 % 65536 := by
|
||||
simp [UInt16.toUInt32_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUInt64_not (a : UInt16) : (~~~a).toUInt64 = ~~~a.toUInt64 % 65536 := by
|
||||
simp [UInt16.toUInt64_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUSize_not (a : UInt16) : (~~~a).toUSize = ~~~a.toUSize % 65536 := by
|
||||
simp [UInt16.toUSize_eq_mod_65536_iff]
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_not (a : UInt32) : (~~~a).toUInt64 = ~~~a.toUInt64 % 4294967296 := by
|
||||
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
|
||||
@[simp] theorem UInt32.toUSize_not (a : UInt32) : (~~~a).toUSize = ~~~a.toUSize % 4294967296 := by
|
||||
simp [UInt32.toUSize_eq_mod_4294967296_iff]
|
||||
|
||||
@[simp] theorem USize.toUInt64_not (a : USize) : (~~~a).toUInt64 = ~~~a.toUInt64 % UInt64.ofNat USize.size := by
|
||||
simp [USize.toUInt64_eq_mod_usizeSize_iff]
|
||||
|
||||
@[simp] theorem UInt8.toFin_shiftLeft (a b : UInt8) (hb : b < 8) : (a <<< b).toFin = a.toFin <<< b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 8) hb])
|
||||
@[simp] theorem UInt16.toFin_shiftLeft (a b : UInt16) (hb : b < 16) : (a <<< b).toFin = a.toFin <<< b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 16) hb])
|
||||
@[simp] theorem UInt32.toFin_shiftLeft (a b : UInt32) (hb : b < 32) : (a <<< b).toFin = a.toFin <<< b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 32) hb])
|
||||
@[simp] theorem UInt64.toFin_shiftLeft (a b : UInt64) (hb : b < 64) : (a <<< b).toFin = a.toFin <<< b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 64) hb])
|
||||
@[simp] theorem USize.toFin_shiftLeft (a b : USize) (hb : b.toNat < System.Platform.numBits) : (a <<< b).toFin = a.toFin <<< b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := System.Platform.numBits) hb])
|
||||
|
||||
|
||||
theorem UInt8.shiftLeft_eq_shiftLeft_mod (a b : UInt8) : a <<< b = a <<< (b % 8) := UInt8.toBitVec_inj.1 (by simp)
|
||||
theorem UInt16.shiftLeft_eq_shiftLeft_mod (a b : UInt16) : a <<< b = a <<< (b % 16) := UInt16.toBitVec_inj.1 (by simp)
|
||||
theorem UInt32.shiftLeft_eq_shiftLeft_mod (a b : UInt32) : a <<< b = a <<< (b % 32) := UInt32.toBitVec_inj.1 (by simp)
|
||||
theorem UInt64.shiftLeft_eq_shiftLeft_mod (a b : UInt64) : a <<< b = a <<< (b % 64) := UInt64.toBitVec_inj.1 (by simp)
|
||||
theorem USize.shiftLeft_eq_shiftLeft_mod (a b : USize) : a <<< b = a <<< (b % USize.ofNat System.Platform.numBits) :=
|
||||
USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
theorem UInt8.shiftRight_eq_shiftRight_mod (a b : UInt8) : a >>> b = a >>> (b % 8) := UInt8.toBitVec_inj.1 (by simp)
|
||||
theorem UInt16.shiftRight_eq_shiftRight_mod (a b : UInt16) : a >>> b = a >>> (b % 16) := UInt16.toBitVec_inj.1 (by simp)
|
||||
theorem UInt32.shiftRight_eq_shiftRight_mod (a b : UInt32) : a >>> b = a >>> (b % 32) := UInt32.toBitVec_inj.1 (by simp)
|
||||
theorem UInt64.shiftRight_eq_shiftRight_mod (a b : UInt64) : a >>> b = a >>> (b % 64) := UInt64.toBitVec_inj.1 (by simp)
|
||||
theorem USize.shiftRight_eq_shiftRight_mod (a b : USize) : a >>> b = a >>> (b % USize.ofNat System.Platform.numBits) :=
|
||||
USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_shiftLeft (a b : UInt16) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
apply UInt8.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, UInt16.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_trans hb (by decide : 8 < 16))]
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_shiftLeft (a b : UInt32) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
apply UInt8.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, UInt32.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_trans hb (by decide : 8 < 32))]
|
||||
@[simp] theorem UInt32.toUInt16_shiftLeft (a b : UInt32) (hb : b < 16) : (a <<< b).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
|
||||
apply UInt16.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, UInt32.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_trans hb (by decide : 16 < 32))]
|
||||
|
||||
@[simp] theorem USize.toUInt8_shiftLeft (a b : USize) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
apply UInt8.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, USize.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.eight_le_numBits)]
|
||||
@[simp] theorem USize.toUInt16_shiftLeft (a b : USize) (hb : b < 16) : (a <<< b).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
|
||||
apply UInt16.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, USize.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.sixteen_le_numBits)]
|
||||
@[simp] theorem USize.toUInt32_shiftLeft (a b : USize) (hb : b < 32) : (a <<< b).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
|
||||
apply UInt32.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, USize.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.le_numBits)]
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_shiftLeft (a b : UInt64) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
apply UInt8.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, UInt64.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb (by decide : 8 ≤ 64))]
|
||||
@[simp] theorem UInt64.toUInt16_shiftLeft (a b : UInt64) (hb : b < 16) : (a <<< b).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
|
||||
apply UInt16.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, UInt64.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb (by decide : 16 ≤ 64))]
|
||||
@[simp] theorem UInt64.toUInt32_shiftLeft (a b : UInt64) (hb : b < 32) : (a <<< b).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
|
||||
apply UInt32.toBitVec_inj.1
|
||||
simp only [lt_iff_toNat_lt, UInt64.reduceToNat] at hb
|
||||
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb (by decide : 32 ≤ 64))]
|
||||
@[simp] theorem UInt64.toUSize_shiftLeft (a b : UInt64) (hb : b.toNat < System.Platform.numBits) :
|
||||
(a <<< b).toUSize = a.toUSize <<< b.toUSize := by
|
||||
apply USize.toBitVec_inj.1
|
||||
have h₁ : b.toNat % 64 = b.toNat := Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.numBits_le)
|
||||
have h₂ : b.toNat % (2 ^ System.Platform.numBits) % System.Platform.numBits = b.toNat := by
|
||||
rw [Nat.mod_eq_of_lt (a := b.toNat), Nat.mod_eq_of_lt hb]
|
||||
exact Nat.lt_trans hb (Nat.lt_pow_self Nat.one_lt_two)
|
||||
simp [h₁, h₂]
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_shiftLeft_mod (a b : UInt16) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
rw [UInt16.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt16.toUInt8_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt8.shiftLeft_eq_shiftLeft_mod]
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_shiftLeft_mod (a b : UInt32) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
rw [UInt32.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt32.toUInt8_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt8.shiftLeft_eq_shiftLeft_mod]
|
||||
@[simp] theorem UInt32.toUInt16_shiftLeft_mod (a b : UInt32) : (a <<< (b % 16)).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
|
||||
rw [UInt32.toUInt16_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt32.toUInt16_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt16.shiftLeft_eq_shiftLeft_mod]
|
||||
|
||||
@[simp] theorem USize.toUInt8_shiftLeft_mod (a b : USize) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
rw [USize.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by simp [-toBitVec_ofNat])), USize.toUInt8_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt8.shiftLeft_eq_shiftLeft_mod]
|
||||
@[simp] theorem USize.toUInt16_shiftLeft_mod (a b : USize) : (a <<< (b % 16)).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
|
||||
rw [USize.toUInt16_shiftLeft _ _ (Nat.mod_lt _ (by simp [-toBitVec_ofNat])), USize.toUInt16_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt16.shiftLeft_eq_shiftLeft_mod]
|
||||
@[simp] theorem USize.toUInt32_shiftLeft_mod (a b : USize) : (a <<< (b % 32)).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
|
||||
rw [USize.toUInt32_shiftLeft _ _ (Nat.mod_lt _ (by simp [-toBitVec_ofNat])), USize.toUInt32_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt32.shiftLeft_eq_shiftLeft_mod]
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_shiftLeft_mod (a b : UInt64) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
|
||||
rw [UInt64.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt64.toUInt8_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt8.shiftLeft_eq_shiftLeft_mod]
|
||||
@[simp] theorem UInt64.toUInt16_shiftLeft_mod (a b : UInt64) : (a <<< (b % 16)).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
|
||||
rw [UInt64.toUInt16_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt64.toUInt16_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt16.shiftLeft_eq_shiftLeft_mod]
|
||||
@[simp] theorem UInt64.toUInt32_shiftLeft_mod (a b : UInt64) : (a <<< (b % 32)).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
|
||||
rw [UInt64.toUInt32_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt64.toUInt32_mod_of_dvd _ _ (by simp)]
|
||||
simp [← UInt32.shiftLeft_eq_shiftLeft_mod]
|
||||
@[simp] theorem UInt64.toUSize_shiftLeft_mod (a b : UInt64) : (a <<< (b % UInt64.ofNat System.Platform.numBits)).toUSize = a.toUSize <<< b.toUSize := by
|
||||
rw [UInt64.toUSize_shiftLeft, UInt64.toUSize_mod_of_dvd]
|
||||
· simp [← USize.shiftLeft_eq_shiftLeft_mod]
|
||||
· cases System.Platform.numBits_eq <;> simp_all
|
||||
· cases System.Platform.numBits_eq <;> simp_all [Nat.mod_lt]
|
||||
|
||||
theorem UInt8.toUInt16_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUInt16 = (a.toUInt16 <<< b.toUInt16) % 256 := by
|
||||
rwa [UInt8.toUInt16_eq_mod_256_iff, UInt16.toUInt8_shiftLeft, toUInt8_toUInt16, toUInt8_toUInt16]
|
||||
theorem UInt8.toUInt32_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUInt32 = (a.toUInt32 <<< b.toUInt32) % 256 := by
|
||||
rwa [UInt8.toUInt32_eq_mod_256_iff, UInt32.toUInt8_shiftLeft, toUInt8_toUInt32, toUInt8_toUInt32]
|
||||
theorem UInt8.toUInt64_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % 256 := by
|
||||
rwa [UInt8.toUInt64_eq_mod_256_iff, UInt64.toUInt8_shiftLeft, toUInt8_toUInt64, toUInt8_toUInt64]
|
||||
theorem UInt8.toUSize_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUSize = (a.toUSize <<< b.toUSize) % 256 := by
|
||||
rw [UInt8.toUSize_eq_mod_256_iff, USize.toUInt8_shiftLeft, toUInt8_toUSize, toUInt8_toUSize]
|
||||
simpa [USize.lt_iff_toNat_lt]
|
||||
|
||||
theorem UInt16.toUInt32_shiftLeft_of_lt (a b : UInt16) (hb : b < 16) : (a <<< b).toUInt32 = (a.toUInt32 <<< b.toUInt32) % 65536 := by
|
||||
rwa [UInt16.toUInt32_eq_mod_65536_iff, UInt32.toUInt16_shiftLeft, toUInt16_toUInt32, toUInt16_toUInt32]
|
||||
theorem UInt16.toUInt64_shiftLeft_of_lt (a b : UInt16) (hb : b < 16) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % 65536 := by
|
||||
rwa [UInt16.toUInt64_eq_mod_65536_iff, UInt64.toUInt16_shiftLeft, toUInt16_toUInt64, toUInt16_toUInt64]
|
||||
theorem UInt16.toUSize_shiftLeft_of_lt (a b : UInt16) (hb : b < 16) : (a <<< b).toUSize = (a.toUSize <<< b.toUSize) % 65536 := by
|
||||
rw [UInt16.toUSize_eq_mod_65536_iff, USize.toUInt16_shiftLeft, toUInt16_toUSize, toUInt16_toUSize]
|
||||
simpa [USize.lt_iff_toNat_lt]
|
||||
|
||||
theorem UInt32.toUInt64_shiftLeft_of_lt (a b : UInt32) (hb : b < 32) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % 4294967296 := by
|
||||
rwa [UInt32.toUInt64_eq_mod_4294967296_iff, UInt64.toUInt32_shiftLeft, toUInt32_toUInt64, toUInt32_toUInt64]
|
||||
theorem UInt32.toUSize_shiftLeft_of_lt (a b : UInt32) (hb : b < 32) : (a <<< b).toUSize = (a.toUSize <<< b.toUSize) % 4294967296 := by
|
||||
rw [UInt32.toUSize_eq_mod_4294967296_iff, USize.toUInt32_shiftLeft, toUInt32_toUSize, toUInt32_toUSize]
|
||||
simpa [USize.lt_iff_toNat_lt]
|
||||
|
||||
theorem USize.toUInt64_shiftLeft_of_lt (a b : USize) (hb : b.toNat < System.Platform.numBits) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % UInt64.ofNat USize.size := by
|
||||
rwa [USize.toUInt64_eq_mod_usizeSize_iff, UInt64.toUSize_shiftLeft, toUSize_toUInt64, toUSize_toUInt64]
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_shiftLeft (a b : UInt8) : (a <<< b).toUInt16 = (a.toUInt16 <<< (b % 8).toUInt16) % 256 := by
|
||||
simp [UInt8.toUInt16_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt32_shiftLeft (a b : UInt8) : (a <<< b).toUInt32 = (a.toUInt32 <<< (b % 8).toUInt32) % 256 := by
|
||||
simp [UInt8.toUInt32_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt64_shiftLeft (a b : UInt8) : (a <<< b).toUInt64 = (a.toUInt64 <<< (b % 8).toUInt64) % 256 := by
|
||||
simp [UInt8.toUInt64_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUSize_shiftLeft (a b : UInt8) : (a <<< b).toUSize = (a.toUSize <<< (b % 8).toUSize) % 256 := by
|
||||
simp [UInt8.toUSize_eq_mod_256_iff]
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_shiftLeft (a b : UInt16) : (a <<< b).toUInt32 = (a.toUInt32 <<< (b % 16).toUInt32) % 65536 := by
|
||||
simp [UInt16.toUInt32_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUInt64_shiftLeft (a b : UInt16) : (a <<< b).toUInt64 = (a.toUInt64 <<< (b % 16).toUInt64) % 65536 := by
|
||||
simp [UInt16.toUInt64_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUSize_shiftLeft (a b : UInt16) : (a <<< b).toUSize = (a.toUSize <<< (b % 16).toUSize) % 65536 := by
|
||||
simp [UInt16.toUSize_eq_mod_65536_iff]
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_shiftLeft (a b : UInt32) : (a <<< b).toUInt64 = (a.toUInt64 <<< (b % 32).toUInt64) % 4294967296 := by
|
||||
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
|
||||
@[simp] theorem UInt32.toUSize_shiftLeft (a b : UInt32) : (a <<< b).toUSize = (a.toUSize <<< (b % 32).toUSize) % 4294967296 := by
|
||||
simp [UInt32.toUSize_eq_mod_4294967296_iff]
|
||||
|
||||
@[simp] theorem USize.toUInt64_shiftLeft (a b : USize) :
|
||||
(a <<< b).toUInt64 = (a.toUInt64 <<< (b % USize.ofNat System.Platform.numBits).toUInt64) % UInt64.ofNat USize.size := by
|
||||
have : System.Platform.numBits < USize.size := Nat.lt_of_le_of_lt System.Platform.numBits_le (Nat.lt_of_lt_of_le (by decide) USize.le_size)
|
||||
simp [USize.toUInt64_eq_mod_usizeSize_iff, toUInt64_ofNat' this]
|
||||
|
||||
@[simp] theorem UInt8.toFin_shiftRight (a b : UInt8) (hb : b < 8) : (a >>> b).toFin = a.toFin >>> b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 8) hb])
|
||||
@[simp] theorem UInt16.toFin_shiftRight (a b : UInt16) (hb : b < 16) : (a >>> b).toFin = a.toFin >>> b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 16) hb])
|
||||
@[simp] theorem UInt32.toFin_shiftRight (a b : UInt32) (hb : b < 32) : (a >>> b).toFin = a.toFin >>> b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 32) hb])
|
||||
@[simp] theorem UInt64.toFin_shiftRight (a b : UInt64) (hb : b < 64) : (a >>> b).toFin = a.toFin >>> b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 64) hb])
|
||||
@[simp] theorem USize.toFin_shiftRight (a b : USize) (hb : b.toNat < System.Platform.numBits) : (a >>> b).toFin = a.toFin >>> b.toFin :=
|
||||
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := System.Platform.numBits) hb])
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_shiftRight (a b : UInt8) : (a >>> b).toUInt16 = a.toUInt16 >>> (b.toUInt16 % 8) :=
|
||||
UInt16.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 8 ∣ 16)])
|
||||
@[simp] theorem UInt8.toUInt32_shiftRight (a b : UInt8) : (a >>> b).toUInt32 = a.toUInt32 >>> (b.toUInt32 % 8) :=
|
||||
UInt32.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 8 ∣ 32)])
|
||||
@[simp] theorem UInt8.toUInt64_shiftRight (a b : UInt8) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % 8) :=
|
||||
UInt64.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 8 ∣ 64)])
|
||||
@[simp] theorem UInt8.toUSize_shiftRight (a b : UInt8) : (a >>> b).toUSize = a.toUSize >>> (b.toUSize % 8) :=
|
||||
USize.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;>
|
||||
simp_all [Nat.mod_mod_of_dvd' (by decide : 8 ∣ 32), Nat.mod_mod_of_dvd' (by decide : 8 ∣ 64)])
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_shiftRight (a b : UInt16) : (a >>> b).toUInt32 = a.toUInt32 >>> (b.toUInt32 % 16) :=
|
||||
UInt32.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 16 ∣ 32)])
|
||||
@[simp] theorem UInt16.toUInt64_shiftRight (a b : UInt16) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % 16) :=
|
||||
UInt64.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 16 ∣ 64)])
|
||||
@[simp] theorem UInt16.toUSize_shiftRight (a b : UInt16) : (a >>> b).toUSize = a.toUSize >>> (b.toUSize % 16) :=
|
||||
USize.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;>
|
||||
simp_all [Nat.mod_mod_of_dvd' (by decide : 16 ∣ 32), Nat.mod_mod_of_dvd' (by decide : 16 ∣ 64)])
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_shiftRight (a b : UInt32) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % 32) :=
|
||||
UInt64.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 32 ∣ 64)])
|
||||
@[simp] theorem UInt32.toUSize_shiftRight (a b : UInt32) : (a >>> b).toUSize = a.toUSize >>> (b.toUSize % 32) :=
|
||||
USize.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;>
|
||||
simp_all [Nat.mod_mod_of_dvd' (by decide : 32 ∣ 32), Nat.mod_mod_of_dvd' (by decide : 32 ∣ 64)])
|
||||
|
||||
@[simp] theorem USize.toUInt64_shiftRight (a b : USize) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % UInt64.ofNat System.Platform.numBits) :=
|
||||
UInt64.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;> simp_all [Nat.mod_mod_of_dvd' (by decide : 32 ∣ 64)])
|
||||
|
||||
/-!
|
||||
There is no reasonable statement for`UInt16.toUInt8_shiftRight`; in fact for `a b : UInt16` the
|
||||
expression `(a >>> b).toUInt8` is not a function of `a.toUInt8` and `b.toUInt8`.
|
||||
-/
|
||||
|
||||
@@ -6,8 +6,11 @@ Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone, Mar
|
||||
prelude
|
||||
import Init.Data.UInt.Basic
|
||||
import Init.Data.Fin.Lemmas
|
||||
import Init.Data.Fin.Bitwise
|
||||
import Init.Data.BitVec.Lemmas
|
||||
import Init.Data.BitVec.Bitblast
|
||||
import Init.Data.Nat.Div.Lemmas
|
||||
import Init.System.Platform
|
||||
|
||||
open Lean in
|
||||
set_option hygiene false in
|
||||
@@ -220,6 +223,13 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
@[deprecated ofBitVec_ofNat (since := "2025-02-12")]
|
||||
theorem mk_ofNat (n : Nat) : ofBitVec (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
|
||||
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec / b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec % b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_neg {a : $typeName} : (-a).toBitVec = -a.toBitVec := rfl
|
||||
|
||||
)
|
||||
if let some nbits := bits.raw.isNatLit? then
|
||||
if nbits > 8 then
|
||||
@@ -265,21 +275,72 @@ declare_uint_theorems USize System.Platform.numBits
|
||||
theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat n) = n :=
|
||||
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h USize.le_size)
|
||||
|
||||
theorem UInt32.toNat_lt_of_lt {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m → n.toNat < m := by
|
||||
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
theorem UInt8.lt_ofNat_iff {n : UInt8} {m : Nat} (h : m < size) : n < ofNat m ↔ n.toNat < m := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt8.ofNat_lt_iff {n : UInt8} {m : Nat} (h : m < size) : ofNat m < n ↔ m < n.toNat := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt8.le_ofNat_iff {n : UInt8} {m : Nat} (h : m < size) : n ≤ ofNat m ↔ n.toNat ≤ m := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
theorem UInt8.ofNat_le_iff {n : UInt8} {m : Nat} (h : m < size) : ofNat m ≤ n ↔ m ≤ n.toNat := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
|
||||
theorem UInt32.lt_toNat_of_lt {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n → m < n.toNat := by
|
||||
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
theorem UInt16.lt_ofNat_iff {n : UInt16} {m : Nat} (h : m < size) : n < ofNat m ↔ n.toNat < m := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt16.ofNat_lt_iff {n : UInt16} {m : Nat} (h : m < size) : ofNat m < n ↔ m < n.toNat := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt16.le_ofNat_iff {n : UInt16} {m : Nat} (h : m < size) : n ≤ ofNat m ↔ n.toNat ≤ m := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
theorem UInt16.ofNat_le_iff {n : UInt16} {m : Nat} (h : m < size) : ofNat m ≤ n ↔ m ≤ n.toNat := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
|
||||
theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ≤ ofNat m → n.toNat ≤ m := by
|
||||
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
theorem UInt32.lt_ofNat_iff {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m ↔ n.toNat < m := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt32.ofNat_lt_iff {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n ↔ m < n.toNat := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt32.le_ofNat_iff {n : UInt32} {m : Nat} (h : m < size) : n ≤ ofNat m ↔ n.toNat ≤ m := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
theorem UInt32.ofNat_le_iff {n : UInt32} {m : Nat} (h : m < size) : ofNat m ≤ n ↔ m ≤ n.toNat := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
|
||||
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m ≤ n → m ≤ n.toNat := by
|
||||
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
@[deprecated UInt32.lt_ofNat_iff (since := "2025-03-18")]
|
||||
theorem UInt32.toNat_lt_of_lt {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m → n.toNat < m :=
|
||||
(UInt32.lt_ofNat_iff h).1
|
||||
|
||||
@[deprecated UInt32.ofNat_lt_iff (since := "2025-03-18")]
|
||||
theorem UInt32.lt_toNat_of_lt {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n → m < n.toNat :=
|
||||
(UInt32.ofNat_lt_iff h).1
|
||||
|
||||
@[deprecated UInt32.le_ofNat_iff (since := "2025-03-18")]
|
||||
theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ≤ ofNat m → n.toNat ≤ m :=
|
||||
(UInt32.le_ofNat_iff h).1
|
||||
|
||||
@[deprecated UInt32.ofNat_le_iff (since := "2025-03-18")]
|
||||
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m ≤ n → m ≤ n.toNat :=
|
||||
(UInt32.ofNat_le_iff h).1
|
||||
|
||||
theorem UInt64.lt_ofNat_iff {n : UInt64} {m : Nat} (h : m < size) : n < ofNat m ↔ n.toNat < m := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt64.ofNat_lt_iff {n : UInt64} {m : Nat} (h : m < size) : ofNat m < n ↔ m < n.toNat := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem UInt64.le_ofNat_iff {n : UInt64} {m : Nat} (h : m < size) : n ≤ ofNat m ↔ n.toNat ≤ m := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
theorem UInt64.ofNat_le_iff {n : UInt64} {m : Nat} (h : m < size) : ofNat m ≤ n ↔ m ≤ n.toNat := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
|
||||
theorem USize.lt_ofNat_iff {n : USize} {m : Nat} (h : m < size) : n < ofNat m ↔ n.toNat < m := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem USize.ofNat_lt_iff {n : USize} {m : Nat} (h : m < size) : ofNat m < n ↔ m < n.toNat := by
|
||||
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
|
||||
theorem USize.le_ofNat_iff {n : USize} {m : Nat} (h : m < size) : n ≤ ofNat m ↔ n.toNat ≤ m := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
theorem USize.ofNat_le_iff {n : USize} {m : Nat} (h : m < size) : ofNat m ≤ n ↔ m ≤ n.toNat := by
|
||||
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
|
||||
|
||||
theorem UInt8.mod_eq_of_lt {a b : UInt8} (h : a < b) : a % b = a := UInt8.toNat_inj.1 (Nat.mod_eq_of_lt h)
|
||||
theorem UInt16.mod_eq_of_lt {a b : UInt16} (h : a < b) : a % b = a := UInt16.toNat_inj.1 (Nat.mod_eq_of_lt h)
|
||||
theorem UInt32.mod_eq_of_lt {a b : UInt32} (h : a < b) : a % b = a := UInt32.toNat_inj.1 (Nat.mod_eq_of_lt h)
|
||||
theorem UInt64.mod_eq_of_lt {a b : UInt64} (h : a < b) : a % b = a := UInt64.toNat_inj.1 (Nat.mod_eq_of_lt h)
|
||||
theorem USize.mod_eq_of_lt {a b : USize} (h : a < b) : a % b = a := USize.toNat_inj.1 (Nat.mod_eq_of_lt h)
|
||||
|
||||
@[simp] theorem UInt8.toNat_lt (n : UInt8) : n.toNat < 2 ^ 8 := n.toFin.isLt
|
||||
@[simp] theorem UInt16.toNat_lt (n : UInt16) : n.toNat < 2 ^ 16 := n.toFin.isLt
|
||||
@@ -800,12 +861,19 @@ theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
|
||||
@[simp] theorem USize.toUSize_toUInt64 (n : USize) : n.toUInt64.toUSize = n :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toUInt32.toUSize = n % 4294967296 := by
|
||||
apply USize.toNat.inj
|
||||
simp only [UInt32.toNat_toUSize, toNat_toUInt32, Nat.reducePow, USize.toNat_mod]
|
||||
cases USize.size_eq
|
||||
· next h => rw [Nat.mod_eq_of_lt (h ▸ n.toNat_lt_size), USize.toNat_ofNat,
|
||||
← USize.size_eq_two_pow, h, Nat.mod_self, Nat.mod_zero]
|
||||
· next h => rw [USize.toNat_ofNat_of_lt]; simp_all
|
||||
|
||||
-- Note: we are currently missing the following four results for which there does not seem to
|
||||
-- be a good candidate for the RHS:
|
||||
-- @[simp] theorem UInt64.toUInt64_toUSize (n : UInt64) : n.toUSize.toUInt64 = ? :=
|
||||
-- @[simp] theorem UInt64.toUSize_toUInt32 (n : UInt64) : n.toUInt32.toUSize = ? :=
|
||||
-- @[simp] theorem USize.toUInt64_toUInt32 (n : USize) : n.toUInt32.toUInt64 = ? :=
|
||||
-- @[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toInt32.toUSize = ? :=
|
||||
|
||||
@[simp] theorem UInt8.toNat_ofFin (x : Fin UInt8.size) : (UInt8.ofFin x).toNat = x.val := rfl
|
||||
@[simp] theorem UInt16.toNat_ofFin (x : Fin UInt16.size) : (UInt16.ofFin x).toNat = x.val := rfl
|
||||
@@ -1344,3 +1412,531 @@ theorem USize.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem BitVec.ofNat_uSizeToNat (n : USize) : BitVec.ofNat System.Platform.numBits n.toNat = n.toBitVec :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
|
||||
@[simp] protected theorem UInt8.toFin_div (a b : UInt8) : (a / b).toFin = a.toFin / b.toFin := rfl
|
||||
@[simp] protected theorem UInt16.toFin_div (a b : UInt16) : (a / b).toFin = a.toFin / b.toFin := rfl
|
||||
@[simp] protected theorem UInt32.toFin_div (a b : UInt32) : (a / b).toFin = a.toFin / b.toFin := rfl
|
||||
@[simp] protected theorem UInt64.toFin_div (a b : UInt64) : (a / b).toFin = a.toFin / b.toFin := rfl
|
||||
@[simp] protected theorem USize.toFin_div (a b : USize) : (a / b).toFin = a.toFin / b.toFin := rfl
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_div (a b : UInt8) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 := rfl
|
||||
@[simp] theorem UInt8.toUInt32_div (a b : UInt8) : (a / b).toUInt32 = a.toUInt32 / b.toUInt32 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_div (a b : UInt8) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUSize_div (a b : UInt8) : (a / b).toUSize = a.toUSize / b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_div (a b : UInt16) : (a / b).toUInt32 = a.toUInt32 / b.toUInt32 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_div (a b : UInt16) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
|
||||
@[simp] theorem UInt16.toUSize_div (a b : UInt16) : (a / b).toUSize = a.toUSize / b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_div (a b : UInt32) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
|
||||
@[simp] theorem UInt32.toUSize_div (a b : UInt32) : (a / b).toUSize = a.toUSize / b.toUSize := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt64_div (a b : USize) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
|
||||
|
||||
theorem UInt16.toUInt8_div (a b : UInt16) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
|
||||
theorem UInt32.toUInt8_div (a b : UInt32) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
theorem UInt32.toUInt16_div (a b : UInt32) (ha : a < 65536) (hb : b < 65536) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
|
||||
theorem USize.toUInt8_div (a b : USize) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt8.size_lt_usizeSize] using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
theorem USize.toUInt16_div (a b : USize) (ha : a < 65536) (hb : b < 65536) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt16.size_lt_usizeSize] using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
|
||||
theorem UInt64.toUInt8_div (a b : UInt64) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
theorem UInt64.toUInt16_div (a b : UInt64) (ha : a < 65536) (hb : b < 65536) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
theorem UInt64.toUInt32_div (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a / b).toUInt32 = a.toUInt32 / b.toUInt32 :=
|
||||
UInt32.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
theorem UInt64.toUSize_div (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a / b).toUSize = a.toUSize / b.toUSize :=
|
||||
USize.toNat.inj (Nat.div_mod_eq_mod_div_mod (Nat.lt_of_lt_of_le ha UInt32.size_le_usizeSize) (Nat.lt_of_lt_of_le hb UInt32.size_le_usizeSize))
|
||||
theorem UInt64.toUSize_div_of_toNat_lt (a b : UInt64) (ha : a.toNat < USize.size) (hb : b.toNat < USize.size) :
|
||||
(a / b).toUSize = a.toUSize / b.toUSize :=
|
||||
USize.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
|
||||
|
||||
@[simp] protected theorem UInt8.toFin_mod (a b : UInt8) : (a % b).toFin = a.toFin % b.toFin := rfl
|
||||
@[simp] protected theorem UInt16.toFin_mod (a b : UInt8) : (a % b).toFin = a.toFin % b.toFin := rfl
|
||||
@[simp] protected theorem UInt32.toFin_mod (a b : UInt32) : (a % b).toFin = a.toFin % b.toFin := rfl
|
||||
@[simp] protected theorem UInt64.toFin_mod (a b : UInt64) : (a % b).toFin = a.toFin % b.toFin := rfl
|
||||
@[simp] protected theorem USize.toFin_mod (a b : USize) : (a % b).toFin = a.toFin % b.toFin := rfl
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_mod (a b : UInt8) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 := rfl
|
||||
@[simp] theorem UInt8.toUInt32_mod (a b : UInt8) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_mod (a b : UInt8) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUSize_mod (a b : UInt8) : (a % b).toUSize = a.toUSize % b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_mod (a b : UInt16) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_mod (a b : UInt16) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
|
||||
@[simp] theorem UInt16.toUSize_mod (a b : UInt16) : (a % b).toUSize = a.toUSize % b.toUSize := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_mod (a b : UInt32) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
|
||||
@[simp] theorem UInt32.toUSize_mod (a b : UInt32) : (a % b).toUSize = a.toUSize % b.toUSize := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt64_mod (a b : USize) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
|
||||
|
||||
theorem UInt16.toUInt8_mod (a b : UInt16) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem UInt16.toUInt8_mod_of_dvd (a b : UInt16) (hb : b.toNat ∣ 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
|
||||
theorem UInt32.toUInt8_mod (a b : UInt32) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem UInt32.toUInt16_mod (a b : UInt32) (ha : a < 65536) (hb : b < 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem UInt32.toUInt8_mod_of_dvd (a b : UInt32) (hb : b.toNat ∣ 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
theorem UInt32.toUInt16_mod_of_dvd (a b : UInt32) (hb : b.toNat ∣ 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
|
||||
theorem USize.toUInt8_mod (a b : USize) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt8.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem USize.toUInt16_mod (a b : USize) (ha : a < 65536) (hb : b < 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt16.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem USize.toUInt32_mod (a b : USize) (ha : a < 4294967296) (hb : b < 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := by
|
||||
apply UInt32.toNat.inj
|
||||
simp only [toNat_toUInt32, USize.toNat_mod, Nat.reducePow, UInt32.toNat_mod]
|
||||
have := Nat.mod_mod_eq_mod_mod_mod ha hb
|
||||
obtain (h|h) := USize.size_eq
|
||||
· have ha' := h ▸ a.toNat_lt_size
|
||||
have hb' := h ▸ b.toNat_lt_size
|
||||
rw [Nat.mod_eq_of_lt ha', Nat.mod_eq_of_lt hb', Nat.mod_eq_of_lt]
|
||||
exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) ha'
|
||||
· simp_all
|
||||
theorem USize.toUInt8_mod_of_dvd (a b : USize) (hb : b.toNat ∣ 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt8.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
theorem USize.toUInt16_mod_of_dvd (a b : USize) (hb : b.toNat ∣ 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt16.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
theorem USize.toUInt32_mod_of_dvd (a b : USize) (hb : b.toNat ∣ 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := by
|
||||
apply UInt32.toNat.inj
|
||||
simp only [toNat_toUInt32, USize.toNat_mod, Nat.reducePow, UInt32.toNat_mod]
|
||||
have := Nat.mod_mod_eq_mod_mod_mod_of_dvd (a := a.toNat) hb
|
||||
obtain (h|h) := USize.size_eq
|
||||
· have ha' := h ▸ a.toNat_lt_size
|
||||
have hb' := h ▸ b.toNat_lt_size
|
||||
rw [Nat.mod_eq_of_lt ha', Nat.mod_eq_of_lt hb', Nat.mod_eq_of_lt]
|
||||
exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) ha'
|
||||
· simp_all
|
||||
|
||||
theorem UInt64.toUInt8_mod (a b : UInt64) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem UInt64.toUInt16_mod (a b : UInt64) (ha : a < 65536) (hb : b < 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem UInt64.toUInt32_mod (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 :=
|
||||
UInt32.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem UInt64.toUSize_mod (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a % b).toUSize = a.toUSize % b.toUSize :=
|
||||
USize.toNat.inj (Nat.mod_mod_eq_mod_mod_mod (Nat.lt_of_lt_of_le ha UInt32.size_le_usizeSize) (Nat.lt_of_lt_of_le hb UInt32.size_le_usizeSize))
|
||||
theorem UInt64.toUSize_mod_of_toNat_lt (a b : UInt64) (ha : a.toNat < USize.size) (hb : b.toNat < USize.size) : (a % b).toUSize = a.toUSize % b.toUSize :=
|
||||
USize.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
|
||||
theorem UInt64.toUInt8_mod_of_dvd (a b : UInt64) (hb : b.toNat ∣ 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
|
||||
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
theorem UInt64.toUInt16_mod_of_dvd (a b : UInt64)(hb : b.toNat ∣ 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
|
||||
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
theorem UInt64.toUInt32_mod_of_dvd (a b : UInt64) (hb : b.toNat ∣ 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 :=
|
||||
UInt32.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
theorem UInt64.toUSize_mod_of_dvd (a b : UInt64) (hb : b.toNat ∣ 4294967296) : (a % b).toUSize = a.toUSize % b.toUSize :=
|
||||
USize.toNat.inj (Nat.mod_mod_eq_mod_mod_mod_of_dvd (Nat.dvd_trans hb UInt32.size_dvd_usizeSize))
|
||||
theorem UInt64.toUSize_mod_of_dvd_usizeSize (a b : UInt64) (hb : b.toNat ∣ USize.size) : (a % b).toUSize = a.toUSize % b.toUSize :=
|
||||
USize.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
|
||||
|
||||
@[simp] protected theorem UInt8.toFin_add (a b : UInt8) : (a + b).toFin = a.toFin + b.toFin := rfl
|
||||
@[simp] protected theorem UInt16.toFin_add (a b : UInt16) : (a + b).toFin = a.toFin + b.toFin := rfl
|
||||
@[simp] protected theorem UInt32.toFin_add (a b : UInt32) : (a + b).toFin = a.toFin + b.toFin := rfl
|
||||
@[simp] protected theorem UInt64.toFin_add (a b : UInt64) : (a + b).toFin = a.toFin + b.toFin := rfl
|
||||
@[simp] protected theorem USize.toFin_add (a b : USize) : (a + b).toFin = a.toFin + b.toFin := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_add (a b : UInt16) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt8_add (a b : UInt32) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_add (a b : UInt64) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt8_add (a b : USize) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_add (a b : UInt32) : (a + b).toUInt16 = a.toUInt16 + b.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_add (a b : UInt64) : (a + b).toUInt16 = a.toUInt16 + b.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt16_add (a b : USize) : (a + b).toUInt16 = a.toUInt16 + b.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_add (a b : UInt64) : (a + b).toUInt32 = a.toUInt32 + b.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt32_add (a b : USize) : (a + b).toUInt32 = a.toUInt32 + b.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUSize_add (a b : UInt64) : (a + b).toUSize = a.toUSize + b.toUSize := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_add (a b : UInt8) : (a + b).toUInt16 = (a.toUInt16 + b.toUInt16) % 256 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt32_add (a b : UInt8) : (a + b).toUInt32 = (a.toUInt32 + b.toUInt32) % 256 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt64_add (a b : UInt8) : (a + b).toUInt64 = (a.toUInt64 + b.toUInt64) % 256 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUSize_add (a b : UInt8) : (a + b).toUSize = (a.toUSize + b.toUSize) % 256 := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_add (a b : UInt16) : (a + b).toUInt32 = (a.toUInt32 + b.toUInt32) % 65536 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt64_add (a b : UInt16) : (a + b).toUInt64 = (a.toUInt64 + b.toUInt64) % 65536 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_add (a b : UInt16) : (a + b).toUSize = (a.toUSize + b.toUSize) % 65536 := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_add (a b : UInt32) : (a + b).toUInt64 = (a.toUInt64 + b.toUInt64) % 4294967296 := UInt64.toNat.inj (by simp)
|
||||
/-- Note that on 32-bit machines we are doing a modulo by zero. -/
|
||||
@[simp] theorem UInt32.toUSize_add (a b : UInt32) : (a + b).toUSize = (a.toUSize + b.toUSize) % 4294967296 :=
|
||||
USize.toNat.inj (by cases System.Platform.numBits_eq <;> simp_all [USize.toNat_ofNat])
|
||||
|
||||
@[simp] protected theorem UInt8.toFin_sub (a b : UInt8) : (a - b).toFin = a.toFin - b.toFin := rfl
|
||||
@[simp] protected theorem UInt16.toFin_sub (a b : UInt16) : (a - b).toFin = a.toFin - b.toFin := rfl
|
||||
@[simp] protected theorem UInt32.toFin_sub (a b : UInt32) : (a - b).toFin = a.toFin - b.toFin := rfl
|
||||
@[simp] protected theorem UInt64.toFin_sub (a b : UInt64) : (a - b).toFin = a.toFin - b.toFin := rfl
|
||||
@[simp] protected theorem USize.toFin_sub (a b : USize) : (a - b).toFin = a.toFin - b.toFin := rfl
|
||||
|
||||
@[simp] protected theorem UInt8.toFin_mul (a b : UInt8) : (a * b).toFin = a.toFin * b.toFin := rfl
|
||||
@[simp] protected theorem UInt16.toFin_mul (a b : UInt16) : (a * b).toFin = a.toFin * b.toFin := rfl
|
||||
@[simp] protected theorem UInt32.toFin_mul (a b : UInt32) : (a * b).toFin = a.toFin * b.toFin := rfl
|
||||
@[simp] protected theorem UInt64.toFin_mul (a b : UInt64) : (a * b).toFin = a.toFin * b.toFin := rfl
|
||||
@[simp] protected theorem USize.toFin_mul (a b : USize) : (a * b).toFin = a.toFin * b.toFin := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_mul (a b : UInt16) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt8_mul (a b : UInt32) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_mul (a b : UInt64) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt8_mul (a b : USize) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_mul (a b : UInt32) : (a * b).toUInt16 = a.toUInt16 * b.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_mul (a b : UInt64) : (a * b).toUInt16 = a.toUInt16 * b.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt16_mul (a b : USize) : (a * b).toUInt16 = a.toUInt16 * b.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_mul (a b : UInt64) : (a * b).toUInt32 = a.toUInt32 * b.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt32_mul (a b : USize) : (a * b).toUInt32 = a.toUInt32 * b.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUSize_mul (a b : UInt64) : (a * b).toUSize = a.toUSize * b.toUSize := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_mul (a b : UInt8) : (a * b).toUInt16 = (a.toUInt16 * b.toUInt16) % 256 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt32_mul (a b : UInt8) : (a * b).toUInt32 = (a.toUInt32 * b.toUInt32) % 256 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt64_mul (a b : UInt8) : (a * b).toUInt64 = (a.toUInt64 * b.toUInt64) % 256 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUSize_mul (a b : UInt8) : (a * b).toUSize = (a.toUSize * b.toUSize) % 256 := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_mul (a b : UInt16) : (a * b).toUInt32 = (a.toUInt32 * b.toUInt32) % 65536 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt64_mul (a b : UInt16) : (a * b).toUInt64 = (a.toUInt64 * b.toUInt64) % 65536 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_mul (a b : UInt16) : (a * b).toUSize = (a.toUSize * b.toUSize) % 65536 := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_mul (a b : UInt32) : (a * b).toUInt64 = (a.toUInt64 * b.toUInt64) % 4294967296 := UInt64.toNat.inj (by simp)
|
||||
/-- Note that on 32-bit machines we are doing a modulo by zero. -/
|
||||
@[simp] theorem UInt32.toUSize_mul (a b : UInt32) : (a * b).toUSize = (a.toUSize * b.toUSize) % 4294967296 :=
|
||||
USize.toNat.inj (by cases System.Platform.numBits_eq <;> simp_all [USize.toNat_ofNat])
|
||||
|
||||
theorem UInt16.toUInt8_eq (a b : UInt16) : a.toUInt8 = b.toUInt8 ↔ a % 256 = b % 256 := by
|
||||
simp [← UInt8.toNat_inj, ← UInt16.toNat_inj]
|
||||
theorem UInt32.toUInt8_eq (a b : UInt32) : a.toUInt8 = b.toUInt8 ↔ a % 256 = b % 256 := by
|
||||
simp [← UInt8.toNat_inj, ← UInt32.toNat_inj]
|
||||
theorem UInt64.toUInt8_eq (a b : UInt64) : a.toUInt8 = b.toUInt8 ↔ a % 256 = b % 256 := by
|
||||
simp [← UInt8.toNat_inj, ← UInt64.toNat_inj]
|
||||
theorem USize.toUInt8_eq (a b : USize) : a.toUInt8 = b.toUInt8 ↔ a % 256 = b % 256 := by
|
||||
simp [← UInt8.toNat_inj, ← USize.toNat_inj]
|
||||
|
||||
theorem UInt32.toUInt16_eq (a b : UInt32) : a.toUInt16 = b.toUInt16 ↔ a % 65536 = b % 65536 := by
|
||||
simp [← UInt16.toNat_inj, ← UInt32.toNat_inj]
|
||||
theorem UInt64.toUInt16_eq (a b : UInt64) : a.toUInt16 = b.toUInt16 ↔ a % 65536 = b % 65536 := by
|
||||
simp [← UInt16.toNat_inj, ← UInt64.toNat_inj]
|
||||
theorem USize.toUInt16_eq (a b : USize) : a.toUInt16 = b.toUInt16 ↔ a % 65536 = b % 65536 := by
|
||||
simp [← UInt16.toNat_inj, ← USize.toNat_inj]
|
||||
|
||||
theorem UInt64.toUInt32_eq (a b : UInt64) : a.toUInt32 = b.toUInt32 ↔ a % 4294967296 = b % 4294967296 := by
|
||||
simp [← UInt32.toNat_inj, ← UInt64.toNat_inj]
|
||||
theorem USize.toUInt32_eq (a b : USize) : a.toUInt32 = b.toUInt32 ↔ a % 4294967296 = b % 4294967296 := by
|
||||
simp [← UInt32.toNat_inj, ← USize.toNat_inj, USize.toNat_ofNat]
|
||||
have := Nat.mod_eq_of_lt a.toNat_lt_two_pow_numBits
|
||||
have := Nat.mod_eq_of_lt b.toNat_lt_two_pow_numBits
|
||||
cases System.Platform.numBits_eq <;> simp_all
|
||||
|
||||
theorem UInt8.toUInt16_eq_mod_256_iff (a : UInt8) (b : UInt16) : a.toUInt16 = b % 256 ↔ a = b.toUInt8 := by
|
||||
simp [← UInt8.toNat_inj, ← UInt16.toNat_inj]
|
||||
theorem UInt8.toUInt32_eq_mod_256_iff (a : UInt8) (b : UInt32) : a.toUInt32 = b % 256 ↔ a = b.toUInt8 := by
|
||||
simp [← UInt8.toNat_inj, ← UInt32.toNat_inj]
|
||||
theorem UInt8.toUInt64_eq_mod_256_iff (a : UInt8) (b : UInt64) : a.toUInt64 = b % 256 ↔ a = b.toUInt8 := by
|
||||
simp [← UInt8.toNat_inj, ← UInt64.toNat_inj]
|
||||
theorem UInt8.toUSize_eq_mod_256_iff (a : UInt8) (b : USize) : a.toUSize = b % 256 ↔ a = b.toUInt8 := by
|
||||
simp [← UInt8.toNat_inj, ← USize.toNat_inj]
|
||||
|
||||
theorem UInt16.toUInt32_eq_mod_65536_iff (a : UInt16) (b : UInt32) : a.toUInt32 = b % 65536 ↔ a = b.toUInt16 := by
|
||||
simp [← UInt16.toNat_inj, ← UInt32.toNat_inj]
|
||||
theorem UInt16.toUInt64_eq_mod_65536_iff (a : UInt16) (b : UInt64) : a.toUInt64 = b % 65536 ↔ a = b.toUInt16 := by
|
||||
simp [← UInt16.toNat_inj, ← UInt64.toNat_inj]
|
||||
theorem UInt16.toUSize_eq_mod_65536_iff (a : UInt16) (b : USize) : a.toUSize = b % 65536 ↔ a = b.toUInt16 := by
|
||||
simp [← UInt16.toNat_inj, ← USize.toNat_inj]
|
||||
|
||||
theorem UInt32.toUInt64_eq_mod_4294967296_iff (a : UInt32) (b : UInt64) : a.toUInt64 = b % 4294967296 ↔ a = b.toUInt32 := by
|
||||
simp [← UInt32.toNat_inj, ← UInt64.toNat_inj]
|
||||
theorem UInt32.toUSize_eq_mod_4294967296_iff (a : UInt32) (b : USize) : a.toUSize = b % 4294967296 ↔ a = b.toUInt32 := by
|
||||
simp [← UInt32.toNat_inj, ← USize.toNat_inj, USize.toNat_ofNat]
|
||||
have := Nat.mod_eq_of_lt b.toNat_lt_two_pow_numBits
|
||||
cases System.Platform.numBits_eq <;> simp_all
|
||||
|
||||
theorem USize.toUInt64_eq_mod_usizeSize_iff (a : USize) (b : UInt64) : a.toUInt64 = b % UInt64.ofNat USize.size ↔ a = b.toUSize := by
|
||||
simp [← USize.toNat_inj, ← UInt64.toNat_inj, USize.size_eq_two_pow]
|
||||
cases System.Platform.numBits_eq <;> simp_all
|
||||
|
||||
theorem UInt8.toUInt16_inj {a b : UInt8} : a.toUInt16 = b.toUInt16 ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt8_toUInt16 a, h, toUInt8_toUInt16], by rintro rfl; rfl⟩
|
||||
theorem UInt8.toUInt32_inj {a b : UInt8} : a.toUInt32 = b.toUInt32 ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt8_toUInt32 a, h, toUInt8_toUInt32], by rintro rfl; rfl⟩
|
||||
theorem UInt8.toUInt64_inj {a b : UInt8} : a.toUInt64 = b.toUInt64 ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt8_toUInt64 a, h, toUInt8_toUInt64], by rintro rfl; rfl⟩
|
||||
theorem UInt8.toUSize_inj {a b : UInt8} : a.toUSize = b.toUSize ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt8_toUSize a, h, toUInt8_toUSize], by rintro rfl; rfl⟩
|
||||
|
||||
theorem UInt16.toUInt32_inj {a b : UInt16} : a.toUInt32 = b.toUInt32 ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt16_toUInt32 a, h, toUInt16_toUInt32], by rintro rfl; rfl⟩
|
||||
theorem UInt16.toUInt64_inj {a b : UInt16} : a.toUInt64 = b.toUInt64 ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt16_toUInt64 a, h, toUInt16_toUInt64], by rintro rfl; rfl⟩
|
||||
theorem UInt16.toUSize_inj {a b : UInt16} : a.toUSize = b.toUSize ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt16_toUSize a, h, toUInt16_toUSize], by rintro rfl; rfl⟩
|
||||
|
||||
theorem UInt32.toUInt64_inj {a b : UInt32} : a.toUInt64 = b.toUInt64 ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt32_toUInt64 a, h, toUInt32_toUInt64], by rintro rfl; rfl⟩
|
||||
theorem UInt32.toUSize_inj {a b : UInt32} : a.toUSize = b.toUSize ↔ a = b :=
|
||||
⟨fun h => by rw [← toUInt32_toUSize a, h, toUInt32_toUSize], by rintro rfl; rfl⟩
|
||||
|
||||
theorem USize.toUInt64_inj {a b : USize} : a.toUInt64 = b.toUInt64 ↔ a = b :=
|
||||
⟨fun h => by rw [← toUSize_toUInt64 a, h, toUSize_toUInt64], by rintro rfl; rfl⟩
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_lt {a b : UInt8} : a.toUInt16 < b.toUInt16 ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt8.toUInt32_lt {a b : UInt8} : a.toUInt32 < b.toUInt32 ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt8.toUInt64_lt {a b : UInt8} : a.toUInt64 < b.toUInt64 ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt8.toUSize_lt {a b : UInt8} : a.toUSize < b.toUSize ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, USize.lt_iff_toNat_lt]
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_lt {a b : UInt16} : a.toUInt32 < b.toUInt32 ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt16.toUInt64_lt {a b : UInt16} : a.toUInt64 < b.toUInt64 ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt16.toUSize_lt {a b : UInt16} : a.toUSize < b.toUSize ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, USize.lt_iff_toNat_lt]
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_lt {a b : UInt32} : a.toUInt64 < b.toUInt64 ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt32.toUSize_lt {a b : UInt32} : a.toUSize < b.toUSize ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, USize.lt_iff_toNat_lt]
|
||||
|
||||
@[simp] theorem USize.toUInt64_lt {a b : USize} : a.toUInt64 < b.toUInt64 ↔ a < b := by
|
||||
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_lt {a b : UInt16} : a.toUInt8 < b.toUInt8 ↔ a % 256 < b % 256 := by
|
||||
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt32.toUInt8_lt {a b : UInt32} : a.toUInt8 < b.toUInt8 ↔ a % 256 < b % 256 := by
|
||||
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt64.toUInt8_lt {a b : UInt64} : a.toUInt8 < b.toUInt8 ↔ a % 256 < b % 256 := by
|
||||
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
|
||||
@[simp] theorem USize.toUInt8_lt {a b : USize} : a.toUInt8 < b.toUInt8 ↔ a % 256 < b % 256 := by
|
||||
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_lt {a b : UInt32} : a.toUInt16 < b.toUInt16 ↔ a % 65536 < b % 65536 := by
|
||||
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
|
||||
@[simp] theorem UInt64.toUInt16_lt {a b : UInt64} : a.toUInt16 < b.toUInt16 ↔ a % 65536 < b % 65536 := by
|
||||
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
|
||||
@[simp] theorem USize.toUInt16_lt {a b : USize} : a.toUInt16 < b.toUInt16 ↔ a % 65536 < b % 65536 := by
|
||||
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_lt {a b : UInt64} : a.toUInt32 < b.toUInt32 ↔ a % 4294967296 < b % 4294967296 := by
|
||||
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
|
||||
@[simp] theorem USize.toUInt32_lt {a b : USize} : a.toUInt32 < b.toUInt32 ↔ a % 4294967296 < b % 4294967296 := by
|
||||
rw [← UInt32.toUSize_lt, toUSize_toUInt32]
|
||||
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
|
||||
|
||||
@[simp] theorem UInt64.toUSize_lt {a b : UInt64} : a.toUSize < b.toUSize ↔ a % UInt64.ofNat USize.size < b % UInt64.ofNat USize.size := by
|
||||
simp only [USize.lt_iff_toNat_lt, toNat_toUSize, lt_iff_toNat_lt, UInt64.toNat_mod, toNat_ofNat', Nat.reducePow]
|
||||
cases System.Platform.numBits_eq <;> simp_all [USize.size]
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_le {a b : UInt8} : a.toUInt16 ≤ b.toUInt16 ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
|
||||
@[simp] theorem UInt8.toUInt32_le {a b : UInt8} : a.toUInt32 ≤ b.toUInt32 ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
|
||||
@[simp] theorem UInt8.toUInt64_le {a b : UInt8} : a.toUInt64 ≤ b.toUInt64 ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
|
||||
@[simp] theorem UInt8.toUSize_le {a b : UInt8} : a.toUSize ≤ b.toUSize ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, USize.le_iff_toNat_le]
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_le {a b : UInt16} : a.toUInt32 ≤ b.toUInt32 ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
|
||||
@[simp] theorem UInt16.toUInt64_le {a b : UInt16} : a.toUInt64 ≤ b.toUInt64 ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
|
||||
@[simp] theorem UInt16.toUSize_le {a b : UInt16} : a.toUSize ≤ b.toUSize ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, USize.le_iff_toNat_le]
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_le {a b : UInt32} : a.toUInt64 ≤ b.toUInt64 ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
|
||||
@[simp] theorem UInt32.toUSize_le {a b : UInt32} : a.toUSize ≤ b.toUSize ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, USize.le_iff_toNat_le]
|
||||
|
||||
@[simp] theorem USize.toUInt64_le {a b : USize} : a.toUInt64 ≤ b.toUInt64 ↔ a ≤ b := by
|
||||
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_le {a b : UInt16} : a.toUInt8 ≤ b.toUInt8 ↔ a % 256 ≤ b % 256 := by
|
||||
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
|
||||
@[simp] theorem UInt32.toUInt8_le {a b : UInt32} : a.toUInt8 ≤ b.toUInt8 ↔ a % 256 ≤ b % 256 := by
|
||||
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
|
||||
@[simp] theorem UInt64.toUInt8_le {a b : UInt64} : a.toUInt8 ≤ b.toUInt8 ↔ a % 256 ≤ b % 256 := by
|
||||
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
|
||||
@[simp] theorem USize.toUInt8_le {a b : USize} : a.toUInt8 ≤ b.toUInt8 ↔ a % 256 ≤ b % 256 := by
|
||||
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_le {a b : UInt32} : a.toUInt16 ≤ b.toUInt16 ↔ a % 65536 ≤ b % 65536 := by
|
||||
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
|
||||
@[simp] theorem UInt64.toUInt16_le {a b : UInt64} : a.toUInt16 ≤ b.toUInt16 ↔ a % 65536 ≤ b % 65536 := by
|
||||
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
|
||||
@[simp] theorem USize.toUInt16_le {a b : USize} : a.toUInt16 ≤ b.toUInt16 ↔ a % 65536 ≤ b % 65536 := by
|
||||
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_le {a b : UInt64} : a.toUInt32 ≤ b.toUInt32 ↔ a % 4294967296 ≤ b % 4294967296 := by
|
||||
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
|
||||
@[simp] theorem USize.toUInt32_le {a b : USize} : a.toUInt32 ≤ b.toUInt32 ↔ a % 4294967296 ≤ b % 4294967296 := by
|
||||
rw [← UInt32.toUSize_le, toUSize_toUInt32]
|
||||
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
|
||||
|
||||
@[simp] theorem UInt64.toUSize_le {a b : UInt64} : a.toUSize ≤ b.toUSize ↔ a % UInt64.ofNat USize.size ≤ b % UInt64.ofNat USize.size := by
|
||||
simp only [USize.le_iff_toNat_le, toNat_toUSize, le_iff_toNat_le, UInt64.toNat_mod, UInt64.reduceToNat]
|
||||
cases System.Platform.numBits_eq <;> simp_all [USize.size]
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_neg (a : UInt16) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_neg (a : UInt32) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt32.toUInt16_neg (a : UInt32) : (-a).toUInt16 = -a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_neg (a : UInt64) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_neg (a : UInt64) : (-a).toUInt16 = -a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_neg (a : UInt64) : (-a).toUInt32 = -a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem UInt64.toUSize_neg (a : UInt64) : (-a).toUSize = -a.toUSize := USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem USize.toUInt8_neg (a : USize) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt16_neg (a : USize) : (-a).toUInt16 = -a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
|
||||
@[simp] theorem USize.toUInt32_neg (a : USize) : (-a).toUInt32 = -a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_neg (a : UInt8) : (-a).toUInt16 = -a.toUInt16 % 256 := by
|
||||
simp [UInt8.toUInt16_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt32_neg (a : UInt8) : (-a).toUInt32 = -a.toUInt32 % 256 := by
|
||||
simp [UInt8.toUInt32_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt64_neg (a : UInt8) : (-a).toUInt64 = -a.toUInt64 % 256 := by
|
||||
simp [UInt8.toUInt64_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUSize_neg (a : UInt8) : (-a).toUSize = -a.toUSize % 256 := by
|
||||
simp [UInt8.toUSize_eq_mod_256_iff]
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_neg (a : UInt16) : (-a).toUInt32 = -a.toUInt32 % 65536 := by
|
||||
simp [UInt16.toUInt32_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUInt64_neg (a : UInt16) : (-a).toUInt64 = -a.toUInt64 % 65536 := by
|
||||
simp [UInt16.toUInt64_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUSize_neg (a : UInt16) : (-a).toUSize = -a.toUSize % 65536 := by
|
||||
simp [UInt16.toUSize_eq_mod_65536_iff]
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_neg (a : UInt32) : (-a).toUInt64 = -a.toUInt64 % 4294967296 := by
|
||||
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
|
||||
@[simp] theorem UInt32.toUSize_neg (a : UInt32) : (-a).toUSize = -a.toUSize % 4294967296 := by
|
||||
simp [UInt32.toUSize_eq_mod_4294967296_iff]
|
||||
|
||||
@[simp] theorem USize.toUInt64_neg (a : USize) : (-a).toUInt64 = -a.toUInt64 % UInt64.ofNat USize.size := by
|
||||
simp [USize.toUInt64_eq_mod_usizeSize_iff]
|
||||
|
||||
@[simp] theorem UInt8.toNat_neg (a : UInt8) : (-a).toNat = (UInt8.size - a.toNat) % UInt8.size := rfl
|
||||
@[simp] theorem UInt16.toNat_neg (a : UInt16) : (-a).toNat = (UInt16.size - a.toNat) % UInt16.size := rfl
|
||||
@[simp] theorem UInt32.toNat_neg (a : UInt32) : (-a).toNat = (UInt32.size - a.toNat) % UInt32.size := rfl
|
||||
@[simp] theorem UInt64.toNat_neg (a : UInt64) : (-a).toNat = (UInt64.size - a.toNat) % UInt64.size := rfl
|
||||
@[simp] theorem USize.toNat_neg (a : USize) : (-a).toNat = (USize.size - a.toNat) % USize.size := rfl
|
||||
|
||||
theorem UInt8.sub_eq_add_neg (a b : UInt8) : a - b = a + (-b) := UInt8.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
|
||||
theorem UInt16.sub_eq_add_neg (a b : UInt16) : a - b = a + (-b) := UInt16.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
|
||||
theorem UInt32.sub_eq_add_neg (a b : UInt32) : a - b = a + (-b) := UInt32.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
|
||||
theorem UInt64.sub_eq_add_neg (a b : UInt64) : a - b = a + (-b) := UInt64.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
|
||||
theorem USize.sub_eq_add_neg (a b : USize) : a - b = a + (-b) := USize.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
|
||||
|
||||
theorem UInt8.neg_one_eq : (-1 : UInt8) = 255 := rfl
|
||||
theorem UInt16.neg_one_eq : (-1 : UInt16) = 65535 := rfl
|
||||
theorem UInt32.neg_one_eq : (-1 : UInt32) = 4294967295 := rfl
|
||||
theorem UInt64.neg_one_eq : (-1 : UInt64) = 18446744073709551615 := rfl
|
||||
theorem USize.neg_one_eq : (-1 : USize) = USize.ofNatLT (USize.size - 1) (Nat.sub_one_lt (Nat.pos_iff_ne_zero.1 size_pos)) :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
theorem UInt8.toBitVec_zero : toBitVec 0 = 0#8 := rfl
|
||||
theorem UInt16.toBitVec_zero : toBitVec 0 = 0#16 := rfl
|
||||
theorem UInt32.toBitVec_zero : toBitVec 0 = 0#32 := rfl
|
||||
theorem UInt64.toBitVec_zero : toBitVec 0 = 0#64 := rfl
|
||||
theorem USize.toBitVec_zero : toBitVec 0 = 0#System.Platform.numBits := rfl
|
||||
|
||||
theorem UInt8.toBitVec_one : toBitVec 1 = 1#8 := rfl
|
||||
theorem UInt16.toBitVec_one : toBitVec 1 = 1#16 := rfl
|
||||
theorem UInt32.toBitVec_one : toBitVec 1 = 1#32 := rfl
|
||||
theorem UInt64.toBitVec_one : toBitVec 1 = 1#64 := rfl
|
||||
theorem USize.toBitVec_one : toBitVec 1 = 1#System.Platform.numBits := rfl
|
||||
|
||||
theorem UInt8.neg_eq_neg_one_mul (a : UInt8) : -a = -1 * a := by
|
||||
apply UInt8.toBitVec_inj.1
|
||||
rw [UInt8.toBitVec_neg, UInt8.toBitVec_mul, UInt8.toBitVec_neg, UInt8.toBitVec_one, BitVec.neg_eq_neg_one_mul]
|
||||
theorem UInt16.neg_eq_neg_one_mul (a : UInt16) : -a = -1 * a := by
|
||||
apply UInt16.toBitVec_inj.1
|
||||
rw [UInt16.toBitVec_neg, UInt16.toBitVec_mul, UInt16.toBitVec_neg, UInt16.toBitVec_one, BitVec.neg_eq_neg_one_mul]
|
||||
theorem UInt32.neg_eq_neg_one_mul (a : UInt32) : -a = -1 * a := by
|
||||
apply UInt32.toBitVec_inj.1
|
||||
rw [UInt32.toBitVec_neg, UInt32.toBitVec_mul, UInt32.toBitVec_neg, UInt32.toBitVec_one, BitVec.neg_eq_neg_one_mul]
|
||||
theorem UInt64.neg_eq_neg_one_mul (a : UInt64) : -a = -1 * a := by
|
||||
apply UInt64.toBitVec_inj.1
|
||||
rw [UInt64.toBitVec_neg, UInt64.toBitVec_mul, UInt64.toBitVec_neg, UInt64.toBitVec_one, BitVec.neg_eq_neg_one_mul]
|
||||
theorem USize.neg_eq_neg_one_mul (a : USize) : -a = -1 * a := by
|
||||
apply USize.toBitVec_inj.1
|
||||
rw [USize.toBitVec_neg, USize.toBitVec_mul, USize.toBitVec_neg, USize.toBitVec_one, BitVec.neg_eq_neg_one_mul]
|
||||
|
||||
theorem UInt8.sub_eq_add_mul (a b : UInt8) : a - b = a + 255 * b := by
|
||||
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
|
||||
theorem UInt16.sub_eq_add_mul (a b : UInt16) : a - b = a + 65535 * b := by
|
||||
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
|
||||
theorem UInt32.sub_eq_add_mul (a b : UInt32) : a - b = a + 4294967295 * b := by
|
||||
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
|
||||
theorem UInt64.sub_eq_add_mul (a b : UInt64) : a - b = a + 18446744073709551615 * b := by
|
||||
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
|
||||
theorem USize.sub_eq_add_mul (a b : USize) : a - b = a + USize.ofNatLT (USize.size - 1) (Nat.sub_one_lt (Nat.pos_iff_ne_zero.1 size_pos)) * b := by
|
||||
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
|
||||
|
||||
@[simp] theorem UInt8.ofNat_usizeSize_sub_one : UInt8.ofNat (USize.size - 1) = 255 := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.ofNat_usizeSize_sub_one : UInt16.ofNat (USize.size - 1) = 65535 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.ofNat_usizeSize_sub_one : UInt32.ofNat (USize.size - 1) = 4294967295 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofNat_uInt64Size_sub_one : USize.ofNat (UInt64.size - 1) = USize.ofNatLT (USize.size - 1) (Nat.sub_one_lt (Nat.pos_iff_ne_zero.1 size_pos)) :=
|
||||
USize.toNat.inj (by simp [USize.toNat_ofNat])
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_sub (a b : UInt16) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
|
||||
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_sub (a b : UInt32) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
|
||||
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
|
||||
@[simp] theorem UInt32.toUInt16_sub (a b : UInt32) : (a - b).toUInt16 = a.toUInt16 - b.toUInt16 := by
|
||||
simp [sub_eq_add_neg, UInt16.sub_eq_add_neg]
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_sub (a b : UInt64) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
|
||||
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
|
||||
@[simp] theorem UInt64.toUInt16_sub (a b : UInt64) : (a - b).toUInt16 = a.toUInt16 - b.toUInt16 := by
|
||||
simp [sub_eq_add_neg, UInt16.sub_eq_add_neg]
|
||||
@[simp] theorem UInt64.toUInt32_sub (a b : UInt64) : (a - b).toUInt32 = a.toUInt32 - b.toUInt32 := by
|
||||
simp [sub_eq_add_neg, UInt32.sub_eq_add_neg]
|
||||
@[simp] theorem UInt64.toUSize_sub (a b : UInt64) : (a - b).toUSize = a.toUSize - b.toUSize := by
|
||||
simp [sub_eq_add_neg, USize.sub_eq_add_neg]
|
||||
|
||||
@[simp] theorem USize.toUInt8_sub (a b : USize) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
|
||||
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
|
||||
@[simp] theorem USize.toUInt16_sub (a b : USize) : (a - b).toUInt16 = a.toUInt16 - b.toUInt16 := by
|
||||
simp [sub_eq_add_neg, UInt16.sub_eq_add_neg]
|
||||
@[simp] theorem USize.toUInt32_sub (a b : USize) : (a - b).toUInt32 = a.toUInt32 - b.toUInt32 := by
|
||||
simp [sub_eq_add_neg, UInt32.sub_eq_add_neg]
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_sub (a b : UInt8) : (a - b).toUInt16 = (a.toUInt16 - b.toUInt16) % 256 := by
|
||||
simp [UInt8.toUInt16_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt32_sub (a b : UInt8) : (a - b).toUInt32 = (a.toUInt32 - b.toUInt32) % 256 := by
|
||||
simp [UInt8.toUInt32_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUInt64_sub (a b : UInt8) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % 256 := by
|
||||
simp [UInt8.toUInt64_eq_mod_256_iff]
|
||||
@[simp] theorem UInt8.toUSize_sub (a b : UInt8) : (a - b).toUSize = (a.toUSize - b.toUSize) % 256 := by
|
||||
simp [UInt8.toUSize_eq_mod_256_iff]
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_sub (a b : UInt16) : (a - b).toUInt32 = (a.toUInt32 - b.toUInt32) % 65536 := by
|
||||
simp [UInt16.toUInt32_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUInt64_sub (a b : UInt16) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % 65536 := by
|
||||
simp [UInt16.toUInt64_eq_mod_65536_iff]
|
||||
@[simp] theorem UInt16.toUSize_sub (a b : UInt16) : (a - b).toUSize = (a.toUSize - b.toUSize) % 65536 := by
|
||||
simp [UInt16.toUSize_eq_mod_65536_iff]
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_sub (a b : UInt32) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % 4294967296 := by
|
||||
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
|
||||
@[simp] theorem UInt32.toUSize_sub (a b : UInt32) : (a - b).toUSize = (a.toUSize - b.toUSize) % 4294967296 := by
|
||||
simp [UInt32.toUSize_eq_mod_4294967296_iff]
|
||||
|
||||
@[simp] theorem USize.toUInt64_sub (a b : USize) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % UInt64.ofNat USize.size := by
|
||||
simp [USize.toUInt64_eq_mod_usizeSize_iff]
|
||||
|
||||
@@ -6,17 +6,87 @@ Authors: Henrik Böving
|
||||
prelude
|
||||
import Init.Data.Fin.Log2
|
||||
|
||||
/--
|
||||
Base-two logarithm of 8-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `UInt8.log2 0 = 0`
|
||||
* `UInt8.log2 1 = 0`
|
||||
* `UInt8.log2 2 = 1`
|
||||
* `UInt8.log2 4 = 2`
|
||||
* `UInt8.log2 7 = 2`
|
||||
* `UInt8.log2 8 = 3`
|
||||
-/
|
||||
@[extern "lean_uint8_log2"]
|
||||
def UInt8.log2 (a : UInt8) : UInt8 := ⟨⟨Fin.log2 a.toFin⟩⟩
|
||||
|
||||
/--
|
||||
Base-two logarithm of 16-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `UInt16.log2 0 = 0`
|
||||
* `UInt16.log2 1 = 0`
|
||||
* `UInt16.log2 2 = 1`
|
||||
* `UInt16.log2 4 = 2`
|
||||
* `UInt16.log2 7 = 2`
|
||||
* `UInt16.log2 8 = 3`
|
||||
-/
|
||||
@[extern "lean_uint16_log2"]
|
||||
def UInt16.log2 (a : UInt16) : UInt16 := ⟨⟨Fin.log2 a.toFin⟩⟩
|
||||
|
||||
/--
|
||||
Base-two logarithm of 32-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `UInt32.log2 0 = 0`
|
||||
* `UInt32.log2 1 = 0`
|
||||
* `UInt32.log2 2 = 1`
|
||||
* `UInt32.log2 4 = 2`
|
||||
* `UInt32.log2 7 = 2`
|
||||
* `UInt32.log2 8 = 3`
|
||||
-/
|
||||
@[extern "lean_uint32_log2"]
|
||||
def UInt32.log2 (a : UInt32) : UInt32 := ⟨⟨Fin.log2 a.toFin⟩⟩
|
||||
|
||||
/--
|
||||
Base-two logarithm of 64-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `UInt64.log2 0 = 0`
|
||||
* `UInt64.log2 1 = 0`
|
||||
* `UInt64.log2 2 = 1`
|
||||
* `UInt64.log2 4 = 2`
|
||||
* `UInt64.log2 7 = 2`
|
||||
* `UInt64.log2 8 = 3`
|
||||
-/
|
||||
@[extern "lean_uint64_log2"]
|
||||
def UInt64.log2 (a : UInt64) : UInt64 := ⟨⟨Fin.log2 a.toFin⟩⟩
|
||||
|
||||
/--
|
||||
Base-two logarithm of word-sized unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
|
||||
|
||||
This function is overridden at runtime with an efficient implementation. This definition is
|
||||
the logical model.
|
||||
|
||||
Examples:
|
||||
* `USize.log2 0 = 0`
|
||||
* `USize.log2 1 = 0`
|
||||
* `USize.log2 2 = 1`
|
||||
* `USize.log2 4 = 2`
|
||||
* `USize.log2 7 = 2`
|
||||
* `USize.log2 8 = 3`
|
||||
-/
|
||||
@[extern "lean_usize_log2"]
|
||||
def USize.log2 (a : USize) : USize := ⟨⟨Fin.log2 a.toFin⟩⟩
|
||||
|
||||
@@ -468,7 +468,8 @@ If not, usually the right approach is `simp [Vector.unattach, -Vector.map_subtyp
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (xs : Vector { x // p x } n) : Vector α n := xs.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#v[] : Vector { x // p x } 0).unattach = #v[] := rfl
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#v[] : Vector { x // p x } 0).unattach = #v[] := by simp
|
||||
|
||||
@[simp] theorem unattach_push {p : α → Prop} {a : { x // p x }} {xs : Vector { x // p x } n} :
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Vector.map_push]
|
||||
@@ -592,8 +593,11 @@ and simplifies these to the function directly taking the value.
|
||||
unfold Array.unattach
|
||||
rfl
|
||||
|
||||
@[simp] theorem unattach_mkVector {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(mkVector n x).unattach = mkVector n x.1 := by
|
||||
@[simp] theorem unattach_replicate {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(replicate n x).unattach = replicate n x.1 := by
|
||||
simp [unattach]
|
||||
|
||||
@[deprecated unattach_replicate (since := "2025-03-18")]
|
||||
abbrev unattach_mkVector := @unattach_replicate
|
||||
|
||||
end Vector
|
||||
|
||||
@@ -29,7 +29,9 @@ deriving Repr, DecidableEq
|
||||
|
||||
attribute [simp] Vector.size_toArray
|
||||
|
||||
/-- Convert `xs : Array α` to `Vector α xs.size`. -/
|
||||
/--
|
||||
Converts an array to a vector. The resulting vector's size is the array's size.
|
||||
-/
|
||||
abbrev Array.toVector (xs : Array α) : Vector α xs.size := .mk xs rfl
|
||||
|
||||
namespace Vector
|
||||
@@ -65,16 +67,19 @@ def elimAsList {motive : Vector α n → Sort u}
|
||||
abbrev mkEmpty := @emptyWithCapacity
|
||||
|
||||
/-- Makes a vector of size `n` with all cells containing `v`. -/
|
||||
@[inline] def mkVector (n) (v : α) : Vector α n := ⟨mkArray n v, by simp⟩
|
||||
@[inline] def replicate (n) (v : α) : Vector α n := ⟨Array.replicate n v, by simp⟩
|
||||
|
||||
@[deprecated replicate (since := "2025-03-18")]
|
||||
abbrev mkVector := @replicate
|
||||
|
||||
instance : Nonempty (Vector α 0) := ⟨#v[]⟩
|
||||
instance [Nonempty α] : Nonempty (Vector α n) := ⟨mkVector _ Classical.ofNonempty⟩
|
||||
instance [Nonempty α] : Nonempty (Vector α n) := ⟨replicate _ Classical.ofNonempty⟩
|
||||
|
||||
/-- Returns a vector of size `1` with element `v`. -/
|
||||
@[inline] def singleton (v : α) : Vector α 1 := ⟨#[v], rfl⟩
|
||||
|
||||
instance [Inhabited α] : Inhabited (Vector α n) where
|
||||
default := mkVector n default
|
||||
default := replicate n default
|
||||
|
||||
/-- Get an element of a vector using a `Fin` index. -/
|
||||
@[inline] def get (xs : Vector α n) (i : Fin n) : α :=
|
||||
@@ -469,7 +474,7 @@ Note that we immediately simplify this to an `++` operation,
|
||||
and do not provide separate verification theorems.
|
||||
-/
|
||||
@[inline, simp] def leftpad (n : Nat) (a : α) (xs : Vector α m) : Vector α (max n m) :=
|
||||
(mkVector (n - m) a ++ xs).cast (by omega)
|
||||
(replicate (n - m) a ++ xs).cast (by omega)
|
||||
|
||||
/--
|
||||
Pad a vector on the right with a given element.
|
||||
@@ -478,7 +483,7 @@ Note that we immediately simplify this to an `++` operation,
|
||||
and do not provide separate verification theorems.
|
||||
-/
|
||||
@[inline, simp] def rightpad (n : Nat) (a : α) (xs : Vector α m) : Vector α (max n m) :=
|
||||
(xs ++ mkVector (n - m) a).cast (by omega)
|
||||
(xs ++ replicate (n - m) a).cast (by omega)
|
||||
|
||||
/-! ### ForIn instance -/
|
||||
|
||||
|
||||
@@ -72,10 +72,13 @@ theorem countP_le_size {xs : Vector α n} : countP p xs ≤ n := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
theorem countP_mkVector (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (mkVector n a) = if p a then n else 0 := by
|
||||
simp only [mkVector_eq_mk_mkArray, countP_cast, countP_mk]
|
||||
simp [Array.countP_mkArray]
|
||||
theorem countP_replicate (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (replicate n a) = if p a then n else 0 := by
|
||||
simp only [replicate_eq_mk_replicate, countP_cast, countP_mk]
|
||||
simp [Array.countP_replicate]
|
||||
|
||||
@[deprecated countP_replicate (since := "2025-03-18")]
|
||||
abbrev countP_mkVector := @countP_replicate
|
||||
|
||||
theorem boole_getElem_le_countP (p : α → Bool) (xs : Vector α n) (i : Nat) (h : i < n) :
|
||||
(if p xs[i] then 1 else 0) ≤ xs.countP p := by
|
||||
@@ -217,13 +220,19 @@ theorem count_eq_size {xs : Vector α n} : count a xs = xs.size ↔ ∀ b ∈ xs
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.count_eq_size]
|
||||
|
||||
@[simp] theorem count_mkVector_self (a : α) (n : Nat) : count a (mkVector n a) = n := by
|
||||
simp only [mkVector_eq_mk_mkArray, count_cast, count_mk]
|
||||
@[simp] theorem count_replicate_self (a : α) (n : Nat) : count a (replicate n a) = n := by
|
||||
simp only [replicate_eq_mk_replicate, count_cast, count_mk]
|
||||
simp
|
||||
|
||||
theorem count_mkVector (a b : α) (n : Nat) : count a (mkVector n b) = if b == a then n else 0 := by
|
||||
simp only [mkVector_eq_mk_mkArray, count_cast, count_mk]
|
||||
simp [Array.count_mkArray]
|
||||
@[deprecated count_replicate_self (since := "2025-03-18")]
|
||||
abbrev count_mkVector_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 only [replicate_eq_mk_replicate, count_cast, count_mk]
|
||||
simp [Array.count_replicate]
|
||||
|
||||
@[deprecated count_replicate (since := "2025-03-18")]
|
||||
abbrev count_mkVector := @count_replicate
|
||||
|
||||
theorem count_le_count_map [DecidableEq β] (xs : Vector α n) (f : α → β) (x : α) :
|
||||
count x xs ≤ count (f x) (map f xs) := by
|
||||
|
||||
@@ -69,10 +69,13 @@ theorem eraseIdx_cast {xs : Vector α n} {k : Nat} (h : k < m) :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem eraseIdx_mkVector {n : Nat} {a : α} {k : Nat} {h} :
|
||||
(mkVector n a).eraseIdx k = mkVector (n - 1) a := by
|
||||
rw [mkVector_eq_mk_mkArray, eraseIdx_mk]
|
||||
simp [Array.eraseIdx_mkArray, *]
|
||||
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
|
||||
(replicate n a).eraseIdx k = replicate (n - 1) a := by
|
||||
rw [replicate_eq_mk_replicate, eraseIdx_mk]
|
||||
simp [Array.eraseIdx_replicate, *]
|
||||
|
||||
@[deprecated eraseIdx_replicate (since := "2025-03-18")]
|
||||
abbrev eraseIdx_mkVector := @eraseIdx_replicate
|
||||
|
||||
theorem mem_eraseIdx_iff_getElem {x : α} {xs : Vector α n} {k} {h} : x ∈ xs.eraseIdx k h ↔ ∃ i w, i ≠ k ∧ xs[i]'w = x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user