Compare commits

..

2 Commits

Author SHA1 Message Date
Kim Morrison
896b3f8933 copy across some Find API to Array 2025-02-26 16:44:05 +11:00
Kim Morrison
816fadb57b feat: add Array/Vector.replace 2025-02-26 16:33:16 +11:00
1987 changed files with 21333 additions and 94681 deletions

View File

@@ -1,20 +0,0 @@
name: Check awaiting-mathlib label
on:
merge_group:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
check-awaiting-mathlib:
runs-on: ubuntu-latest
steps:
- name: Check awaiting-mathlib label
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
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');
}

View File

@@ -1,246 +0,0 @@
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

View File

@@ -20,7 +20,9 @@ jobs:
- name: Identify stage0 changes
run: |
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0/stdlib > "$RUNNER_TEMP/stage0" || true
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0 |
grep -v -x -F $'stage0/src/stdlib_flags.h\nstage0/src/lean.mk.in' \
> "$RUNNER_TEMP/stage0" || true
if test -s "$RUNNER_TEMP/stage0"
then
echo "CHANGES=yes" >> "$GITHUB_ENV"

View File

@@ -36,9 +36,7 @@ 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.matrix }}
# secondary build jobs that should not block the CI success/merge queue
matrix-secondary: ${{ steps.set-matrix.outputs.matrix-secondary }}
matrix: ${{ steps.set-matrix.outputs.result }}
# 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?
@@ -137,7 +135,6 @@ 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",
@@ -166,19 +163,6 @@ jobs:
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'"
},
// deactivated due to bugs
/*
{
"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",
// TODO: why does this fail?
"CTEST_OPTIONS": "-E 'scopedMacros'"
},
*/
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
@@ -220,18 +204,12 @@ jobs:
"os": "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"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
// 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,
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
},
{
"name": "Windows",
@@ -282,41 +260,196 @@ jobs:
// "CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_tempfile.lean\\.|leanruntest_libuv\\.lean\""
// }
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
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"]));
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
return matrix.filter((job) => level >= job["check-level"])
build:
needs: [configure]
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
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
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
# This job collects results from all the matrix jobs
# This can be made the "required" job, instead of listing each

View File

@@ -34,7 +34,7 @@ jobs:
- name: Download artifact from the previous workflow.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: download-artifact
uses: dawidd6/action-download-artifact@v9 # https://github.com/marketplace/actions/download-workflow-artifact
uses: dawidd6/action-download-artifact@v8 # https://github.com/marketplace/actions/download-workflow-artifact
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts
@@ -155,20 +155,6 @@ jobs:
fi
if [[ -n "$MESSAGE" ]]; then
# Check if force-mathlib-ci label is present
LABELS="$(curl --retry 3 --location --silent \
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
| jq -r '.[].name')"
if echo "$LABELS" | grep -q "^force-mathlib-ci$"; then
echo "force-mathlib-ci label detected, forcing CI despite issues"
MESSAGE="Forcing Mathlib CI because the \`force-mathlib-ci\` label is present, despite problem: $MESSAGE"
FORCE_CI=true
else
MESSAGE="$MESSAGE You can force Mathlib CI using the \`force-mathlib-ci\` label."
fi
echo "Checking existing messages"
@@ -215,12 +201,7 @@ jobs:
else
echo "The message already exists in the comment body."
fi
if [[ "$FORCE_CI" == "true" ]]; then
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
else
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
fi
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
else
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
fi
@@ -271,7 +252,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi
@@ -335,7 +316,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi

View File

@@ -1,7 +1,4 @@
cmake_minimum_required(VERSION 3.11)
option(USE_MIMALLOC "use mimalloc" ON)
# store all variables passed on the command line into CL_ARGS so we can pass them to the stage builds
# https://stackoverflow.com/a/48555098/161659
# MUST be done before call to 'project'
@@ -17,12 +14,13 @@ foreach(var ${vars})
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION")
# must forward options that generate incompatible .olean format
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
elseif("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE|USE_MIMALLOC")
endif()
if("${var}" MATCHES "LLVM*")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
if("${var}" MATCHES "PKG_CONFIG*")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
elseif("${var}" MATCHES "USE_MIMALLOC")
list(APPEND CL_ARGS "-D${var}=${${var}}")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
list(APPEND PLATFORM_ARGS "-D${var}=${${var}}")
endif()
@@ -49,41 +47,28 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif()
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
ExternalProject_add(cadical
PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-2.1.2
GIT_TAG rel-1.9.5
CONFIGURE_COMMAND ""
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
BUILD_IN_SOURCE ON
INSTALL_COMMAND "")
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
list(APPEND EXTRA_DEPENDS cadical)
set(EXTRA_DEPENDS "cadical")
endif()
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
endif()
if (USE_MIMALLOC)
ExternalProject_add(mimalloc
PREFIX mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc
GIT_TAG v2.2.3
# just download, we compile it as part of each stage as it is small
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
list(APPEND EXTRA_DEPENDS mimalloc)
endif()
ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src
BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
# (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)
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
@@ -97,7 +82,6 @@ ExternalProject_add(stage1
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage0
STEP_TARGETS configure
)
ExternalProject_add(stage2
SOURCE_DIR "${LEAN_SOURCE_DIR}"

View File

@@ -16,7 +16,6 @@
"name": "debug",
"displayName": "Debug build config",
"cacheVariables": {
"LEAN_EXTRA_CXX_FLAGS": "-DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
"CMAKE_BUILD_TYPE": "Debug"
},
"generator": "Unix Makefiles",
@@ -26,13 +25,10 @@
"name": "sanitize",
"displayName": "Sanitize build config",
"cacheVariables": {
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined -DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined",
"LEAN_EXTRA_LINKER_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined",
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
"SMALL_ALLOCATOR": "OFF",
"USE_MIMALLOC": "OFF",
"BSYMBOLIC": "OFF",
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000"
"BSYMBOLIC": "OFF"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/sanitize"

View File

@@ -5,7 +5,7 @@ See below for the checklist for release candidates.
We'll use `v4.6.0` as the intended release version as a running example.
- Run `script/release_checklist.py v4.6.0` to check the status of the release.
- Run `scripts/release_checklist.py v4.6.0` to check the status of the release.
This script is purely informational, idempotent, and safe to run at any stage of the release process.
- `git checkout releases/v4.6.0`
(This branch should already exist, from the release candidates.)
@@ -13,8 +13,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
- In `src/CMakeLists.txt`, verify you see
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
- `set(LEAN_VERSION_IS_RELEASE 1)`
- (all of these should already be in place from the release candidates)
- (both of these should already be in place from the release candidates)
- `git tag v4.6.0`
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
- Now wait, while CI runs.
@@ -42,10 +41,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- In order to have the access rights to push to these repositories and merge PRs,
you will need to be a member of the `lean-release-managers` team at both `leanprover-community` and `leanprover`.
Contact Kim Morrison (@kim-em) to arrange access.
- There is an experimental script that will guide you through the steps for each of the repositories below.
The script should be invoked as
`script/release_steps.py vx.y.x <repo>` where `<repo>` is a case-insensitive substring of the repo name.
For example: `script/release_steps.py v4.6.0 batt` will guide you through the steps for the Batteries repository.
- For each of the repositories listed below:
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
- The usual branch name would be `bump_to_v4.6.0`.
@@ -85,11 +80,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Toolchain bump PR including updated Lake manifest
- Create and push the tag
- There is no `stable` branch; skip this step
- [Reference Manual](https://github.com/leanprover/reference-manual)
- Dependencies: Verso
- Toolchain bump PR including updated Lake manifest
- Pushing the tag (whether for an RC or a final) triggers a deployment.
- There is no `stable` branch; skip this step
- [Cli](https://github.com/leanprover/lean4-cli)
- No dependencies
- Toolchain bump PR
@@ -133,10 +123,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Toolchain bump PR including updated Lake manifest
- Create and push the tag
- Merge the tag into `stable`
- An awkward situtation that sometimes occurs (e.g. with Verso) is that one of these upstream dependencies has
already moved its `master`/`main` branch to a nightly toolchain that comes *after* the stable toolchain we are
targeting. In this case it is necessary to create a branch `releases/v4.6.0` from the last commit which was on
an earlier toolchain, move that branch to the stable toolchain, and create the toolchain tag from that branch.
- Run `script/release_checklist.py v4.6.0` again to check that everything is in order.
- Finally, make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
@@ -174,7 +160,6 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git fetch nightly tag nightly-2024-02-29
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
git push --set-upstream origin releases/v4.18.0
```
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.`
- In `src/CMakeLists.txt`,
@@ -184,7 +169,6 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- `git tag v4.7.0-rc1`
- `git push origin v4.7.0-rc1`
- Now wait, while CI runs.
- The CI setup parses the tag to discover the `-rc1` special description, and passes it to `cmake` using a `-D` option. The `-rc1` doesn't need to be placed in the configuration file.
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
- This step can take up to an hour.
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/

View File

@@ -7,7 +7,7 @@ Platforms built & tested by our CI, available as binary releases via elan (see b
* x86-64 Linux with glibc 2.26+
* x86-64 macOS 10.15+
* aarch64 (Apple Silicon) macOS 10.15+
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022, Windows Server 2025
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022
### Tier 2

8
flake.lock generated
View File

@@ -36,17 +36,17 @@
},
"nixpkgs-cadical": {
"locked": {
"lastModified": 1740791350,
"narHash": "sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0=",
"lastModified": 1722221733,
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"type": "github"
}
},

View File

@@ -8,8 +8,8 @@
# old nixpkgs used for portable release with older glibc (2.26)
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
inputs.nixpkgs-older.flake = false;
# for cadical 2.1.2; sync with CMakeLists.txt by taking commit from https://www.nixhub.io/packages/cadical
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d";
# for cadical 1.9.5; sync with CMakeLists.txt
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
@@ -63,7 +63,6 @@
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
# for CI coredumps
GDB = pkgsDist.gdb;
});
in {

View File

@@ -17,7 +17,7 @@ lib.warn "The Nix-based build is deprecated" rec {
'';
} // args // {
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
cmakeFlags = ["-DSMALL_ALLOCATOR=ON" "-DUSE_MIMALLOC=OFF"] ++ (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
preConfigure = args.preConfigure or "" + ''
# ignore absence of submodule
sed -i 's!lake/Lake.lean!!' CMakeLists.txt

File diff suppressed because it is too large Load Diff

View File

@@ -1,165 +0,0 @@
#!/usr/bin/env python3
"""
Merge a tag into a branch on a GitHub repository.
This script checks if a specified tag can be merged cleanly into a branch and performs
the merge if possible. If the merge cannot be done cleanly, it prints a helpful message.
Usage:
python3 merge_remote.py <org/repo> <branch> <tag>
Arguments:
org/repo: GitHub repository in the format 'organization/repository'
branch: The target branch to merge into
tag: The tag to merge from
Example:
python3 merge_remote.py leanprover/mathlib4 stable v4.6.0
The script uses the GitHub CLI (`gh`), so make sure it's installed and authenticated.
"""
import argparse
import subprocess
import sys
import tempfile
import os
import shutil
def run_command(command, check=True, capture_output=True):
"""Run a shell command and return the result."""
try:
result = subprocess.run(
command,
check=check,
shell=True,
text=True,
capture_output=capture_output
)
return result
except subprocess.CalledProcessError as e:
if capture_output:
print(f"Command failed: {command}")
print(f"Error: {e.stderr}")
return e
def clone_repo(repo, temp_dir):
"""Clone the repository to a temporary directory using shallow clone."""
print(f"Shallow cloning {repo}...")
# Use --depth=1 for shallow clone
clone_result = run_command(f"gh repo clone {repo} {temp_dir} -- --depth=1", check=False)
if clone_result.returncode != 0:
print(f"Failed to clone repository {repo}.")
print(f"Error: {clone_result.stderr}")
return False
return True
def check_and_merge(repo, branch, tag, temp_dir):
"""Check if tag can be merged into branch and perform the merge if possible."""
# Change to the temporary directory
os.chdir(temp_dir)
# Fetch only the specific branch and tag we need
print(f"Fetching branch '{branch}' and tag '{tag}'...")
fetch_branch = run_command(f"git fetch origin {branch}")
if fetch_branch.returncode != 0:
print(f"Error: Failed to fetch branch '{branch}'.")
return False
fetch_tag = run_command(f"git fetch origin tag {tag}")
if fetch_tag.returncode != 0:
print(f"Error: Failed to fetch tag '{tag}'.")
return False
# Check if branch exists
branch_check = run_command(f"git branch -r | grep origin/{branch}")
if branch_check.returncode != 0:
print(f"Error: Branch '{branch}' does not exist in repository.")
return False
# Check if tag exists
tag_check = run_command(f"git tag -l {tag}")
if tag_check.returncode != 0 or not tag_check.stdout.strip():
print(f"Error: Tag '{tag}' does not exist in repository.")
return False
# Checkout the branch
print(f"Checking out branch '{branch}'...")
checkout_result = run_command(f"git checkout -b {branch} origin/{branch}")
if checkout_result.returncode != 0:
return False
# Try merging the tag in a dry-run to check if it can be merged cleanly
print(f"Checking if {tag} can be merged cleanly into {branch}...")
merge_check = run_command(f"git merge --no-commit --no-ff {tag}", check=False)
if merge_check.returncode != 0:
print(f"Cannot merge {tag} cleanly into {branch}.")
print("Merge conflicts would occur. Aborting merge.")
run_command("git merge --abort")
return False
# Abort the test merge
run_command("git reset --hard HEAD")
# Now perform the actual merge and push to remote
print(f"Merging {tag} into {branch}...")
merge_result = run_command(f"git merge {tag} --no-edit")
if merge_result.returncode != 0:
print(f"Failed to merge {tag} into {branch}.")
return False
print(f"Pushing changes to remote...")
push_result = run_command(f"git push origin {branch}")
if push_result.returncode != 0:
print(f"Failed to push changes to remote.")
return False
print(f"Successfully merged {tag} into {branch} and pushed to remote.")
return True
def main():
parser = argparse.ArgumentParser(
description="Merge a tag into a branch on a GitHub repository.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s leanprover/mathlib4 stable v4.6.0 Merge tag v4.6.0 into stable branch
The script will:
1. Clone the repository
2. Check if the tag and branch exist
3. Check if the tag can be merged cleanly into the branch
4. Perform the merge and push to remote if possible
"""
)
parser.add_argument("repo", help="GitHub repository in the format 'organization/repository'")
parser.add_argument("branch", help="The target branch to merge into")
parser.add_argument("tag", help="The tag to merge from")
args = parser.parse_args()
# Create a temporary directory for the repository
temp_dir = tempfile.mkdtemp()
try:
# Clone the repository
if not clone_repo(args.repo, temp_dir):
sys.exit(1)
# Check if the tag can be merged and perform the merge
if not check_and_merge(args.repo, args.branch, args.tag, temp_dir):
sys.exit(1)
finally:
# Clean up the temporary directory
print(f"Cleaning up temporary files...")
shutil.rmtree(temp_dir)
if __name__ == "__main__":
main()

View File

@@ -25,10 +25,7 @@ cp llvm/lib/clang/*/include/{std*,__std*,limits}.h stage1/include/clang
echo '
// https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode
#define SEM_FAILCRITICALERRORS 0x0001
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);
// https://docs.microsoft.com/en-us/windows/console/setconsoleoutputcp
#define CP_UTF8 65001
__declspec(dllimport) __stdcall int SetConsoleOutputCP(unsigned int wCodePageID);' > stage1/include/clang/windows.h
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);' > stage1/include/clang/windows.h
# COFF dependencies
cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
# runtime

View File

@@ -8,11 +8,6 @@ import subprocess
import sys
import os
def debug(verbose, message):
"""Print debug message if verbose mode is enabled."""
if verbose:
print(f" [DEBUG] {message}")
def parse_repos_config(file_path):
with open(file_path, "r") as f:
return yaml.safe_load(f)["repositories"]
@@ -90,12 +85,9 @@ def parse_version(version_str):
def is_version_gte(version1, version2):
"""Check if version1 >= version2, including proper handling of release candidates."""
# Check if version1 is a nightly toolchain
if version1.startswith("leanprover/lean4:nightly-"):
return False
return parse_version(version1) >= parse_version(version2)
def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token, verbose=False):
def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
# First get the commit SHA for the tag
api_base = repo_url.replace("https://github.com/", "https://api.github.com/repos/")
headers = {'Authorization': f'token {github_token}'} if github_token else {}
@@ -103,7 +95,6 @@ def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token, verbo
# Get tag's commit SHA
tag_response = requests.get(f"{api_base}/git/refs/tags/{tag_name}", headers=headers)
if tag_response.status_code != 200:
debug(verbose, f"Could not fetch tag {tag_name}, status code: {tag_response.status_code}")
return False
# Handle both single object and array responses
@@ -112,48 +103,22 @@ def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token, verbo
# Find the exact matching tag in the list
matching_tags = [tag for tag in tag_data if tag['ref'] == f'refs/tags/{tag_name}']
if not matching_tags:
debug(verbose, f"No matching tag found for {tag_name} in response list")
return False
tag_sha = matching_tags[0]['object']['sha']
else:
tag_sha = tag_data['object']['sha']
# Check if the tag is an annotated tag and get the actual commit SHA
if tag_data.get('object', {}).get('type') == 'tag' or (
isinstance(tag_data, list) and
matching_tags and
matching_tags[0].get('object', {}).get('type') == 'tag'):
# Get the commit that this tag points to
tag_obj_response = requests.get(f"{api_base}/git/tags/{tag_sha}", headers=headers)
if tag_obj_response.status_code == 200:
tag_obj = tag_obj_response.json()
if 'object' in tag_obj and tag_obj['object']['type'] == 'commit':
commit_sha = tag_obj['object']['sha']
debug(verbose, f"Tag is annotated. Resolved commit SHA: {commit_sha}")
tag_sha = commit_sha # Use the actual commit SHA
# Get commits on stable branch containing this SHA
commits_response = requests.get(
f"{api_base}/commits?sha={stable_branch}&per_page=100",
headers=headers
)
if commits_response.status_code != 200:
debug(verbose, f"Could not fetch commits for branch {stable_branch}, status code: {commits_response.status_code}")
return False
# Check if any commit in stable's history matches our tag's SHA
stable_commits = [commit['sha'] for commit in commits_response.json()]
is_merged = tag_sha in stable_commits
debug(verbose, f"Tag SHA: {tag_sha}")
debug(verbose, f"First 5 stable commits: {stable_commits[:5]}")
debug(verbose, f"Total stable commits fetched: {len(stable_commits)}")
if not is_merged:
debug(verbose, f"Tag SHA not found in first {len(stable_commits)} commits of stable branch")
return is_merged
return tag_sha in stable_commits
def is_release_candidate(version):
return "-rc" in version
@@ -213,73 +178,51 @@ def check_bump_branch_toolchain(url, bump_branch, github_token):
print(f" ✅ Bump branch correctly uses toolchain: {content}")
return True
def pr_exists_with_title(repo_url, title, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + "/pulls"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
params = {'state': 'open'}
response = requests.get(api_url, headers=headers, params=params)
if response.status_code != 200:
return None
pull_requests = response.json()
for pr in pull_requests:
if pr['title'] == title:
return pr['number'], pr['html_url']
return None
def main():
parser = argparse.ArgumentParser(description="Check release status of Lean4 repositories")
parser.add_argument("toolchain", help="The toolchain version to check (e.g., v4.6.0)")
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose debugging output")
args = parser.parse_args()
github_token = get_github_token()
toolchain = args.toolchain
verbose = args.verbose
if len(sys.argv) != 2:
print("Usage: python3 release_checklist.py <toolchain>")
sys.exit(1)
toolchain = sys.argv[1]
stripped_toolchain = strip_rc_suffix(toolchain)
lean_repo_url = "https://github.com/leanprover/lean4"
# Track repository status
repo_status = {} # Will store True for success, False for failure
# Preliminary checks for lean4 itself
# Preliminary checks
print("\nPerforming preliminary checks...")
lean4_success = True
# Check for branch releases/v4.Y.0
version_major, version_minor, _ = map(int, stripped_toolchain.lstrip('v').split('.'))
branch_name = f"releases/v{version_major}.{version_minor}.0"
if not branch_exists(lean_repo_url, branch_name, github_token):
print(f" ❌ Branch {branch_name} does not exist")
lean4_success = False
else:
if branch_exists(lean_repo_url, branch_name, github_token):
print(f" ✅ Branch {branch_name} exists")
# Check CMake version settings
if not check_cmake_version(lean_repo_url, branch_name, version_major, version_minor, github_token):
lean4_success = False
# Check for tag and release page
if not tag_exists(lean_repo_url, toolchain, github_token):
print(f" ❌ Tag {toolchain} does not exist.")
lean4_success = False
check_cmake_version(lean_repo_url, branch_name, version_major, version_minor, github_token)
else:
print(f" ❌ Branch {branch_name} does not exist")
# Check for tag v4.X.Y(-rcZ)
if tag_exists(lean_repo_url, toolchain, github_token):
print(f" ✅ Tag {toolchain} exists")
if not release_page_exists(lean_repo_url, toolchain, github_token):
print(f" ❌ Release page for {toolchain} does not exist")
lean4_success = False
else:
print(f" ❌ Tag {toolchain} does not exist.")
# Check for release page
if release_page_exists(lean_repo_url, toolchain, github_token):
print(f" ✅ Release page for {toolchain} exists")
# Check the first line of the release notes
release_notes = get_release_notes(lean_repo_url, toolchain, github_token)
if not (release_notes and toolchain in release_notes.splitlines()[0].strip()):
if release_notes and toolchain in release_notes.splitlines()[0].strip():
print(f" ✅ Release notes look good.")
else:
previous_minor_version = version_minor - 1
previous_release = f"v{version_major}.{previous_minor_version}.0"
print(f" ❌ Release notes not published. Please run `script/release_notes.py --since {previous_release}` on branch `{branch_name}`.")
lean4_success = False
else:
print(f" ✅ Release notes look good.")
repo_status["lean4"] = lean4_success
else:
print(f" ❌ Release page for {toolchain} does not exist")
# Load repositories and perform further checks
print("\nChecking repositories...")
@@ -294,73 +237,46 @@ def main():
check_stable = repo["stable-branch"]
check_tag = repo.get("toolchain-tag", True)
check_bump = repo.get("bump-branch", False)
dependencies = repo.get("dependencies", [])
print(f"\nRepository: {name}")
# Check if any dependencies have failed
failed_deps = [dep for dep in dependencies if dep in repo_status and not repo_status[dep]]
if failed_deps:
print(f" 🟡 Dependencies not ready: {', '.join(failed_deps)}")
repo_status[name] = False
continue
# Initialize success flag for this repo
success = True
# Check if branch is on at least the target toolchain
lean_toolchain_content = get_branch_content(url, branch, "lean-toolchain", github_token)
if lean_toolchain_content is None:
print(f" ❌ No lean-toolchain file found in {branch} branch")
repo_status[name] = False
continue
on_target_toolchain = is_version_gte(lean_toolchain_content.strip(), toolchain)
if not on_target_toolchain:
print(f" ❌ Not on target toolchain (needs ≥ {toolchain}, but {branch} is on {lean_toolchain_content.strip()})")
pr_title = f"chore: bump toolchain to {toolchain}"
pr_info = pr_exists_with_title(url, pr_title, github_token)
if pr_info:
pr_number, pr_url = pr_info
print(f" ✅ PR with title '{pr_title}' exists: #{pr_number} ({pr_url})")
else:
print(f" ❌ PR with title '{pr_title}' does not exist")
print(f" Run `script/release_steps.py {toolchain} {name}` to create it")
repo_status[name] = False
continue
print(f" ✅ On compatible toolchain (>= {toolchain})")
# Only check for tag if toolchain-tag is true
if check_tag:
if not tag_exists(url, toolchain, github_token):
print(f" ❌ Tag {toolchain} does not exist. Run `script/push_repo_release_tag.py {extract_org_repo_from_url(url)} {branch} {toolchain}`.")
repo_status[name] = False
continue
print(f" ✅ Tag {toolchain} exists")
else:
print(f" ✅ Tag {toolchain} exists")
# Only check merging into stable if stable-branch is true and not a release candidate
if check_stable and not is_release_candidate(toolchain):
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
org_repo = extract_org_repo_from_url(url)
if not is_merged_into_stable(url, toolchain, "stable", github_token):
print(f" ❌ Tag {toolchain} is not merged into stable")
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
repo_status[name] = False
continue
print(f" ✅ Tag {toolchain} is merged into stable")
else:
print(f" ✅ Tag {toolchain} is merged into stable")
# Check for bump branch if configured
if check_bump:
next_version = get_next_version(toolchain)
bump_branch = f"bump/{next_version}"
if not branch_exists(url, bump_branch, github_token):
if branch_exists(url, bump_branch, github_token):
print(f" ✅ Bump branch {bump_branch} exists")
check_bump_branch_toolchain(url, bump_branch, github_token)
else:
print(f" ❌ Bump branch {bump_branch} does not exist")
repo_status[name] = False
continue
print(f" ✅ Bump branch {bump_branch} exists")
if not check_bump_branch_toolchain(url, bump_branch, github_token):
repo_status[name] = False
continue
repo_status[name] = success
# Final check for lean4 master branch
# Check lean4 master branch for next development cycle
print("\nChecking lean4 master branch configuration...")
next_version = get_next_version(toolchain)
next_minor = int(next_version.split('.')[1])

View File

@@ -65,21 +65,20 @@ def format_markdown_description(pr_number, description):
link = f"[#{pr_number}](https://github.com/leanprover/lean4/pull/{pr_number})"
return f"{link} {description}"
def commit_types():
# see doc/dev/commit_convention.md
return ['feat', 'fix', 'doc', 'style', 'refactor', 'test', 'chore', 'perf']
def count_commit_types(commits):
counts = {
'total': len(commits),
'feat': 0,
'fix': 0,
'refactor': 0,
'doc': 0,
'chore': 0
}
for commit_type in commit_types():
counts[commit_type] = 0
for _, first_line, _ in commits:
for commit_type in commit_types():
if first_line.startswith(f'{commit_type}:'):
counts[commit_type] += 1
for commit_type in ['feat:', 'fix:', 'refactor:', 'doc:', 'chore:']:
if first_line.startswith(commit_type):
counts[commit_type.rstrip(':')] += 1
break
return counts
@@ -159,9 +158,8 @@ def main():
counts = count_commit_types(commits)
print(f"For this release, {counts['total']} changes landed. "
f"In addition to the {counts['feat']} feature additions and {counts['fix']} fixes listed below "
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements, "
f"{counts['perf']} performance improvements, {counts['test']} improvements to the test suite "
f"and {counts['style'] + counts['chore']} other changes.\n")
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements "
f"and {counts['chore']} chores.\n")
section_order = sort_sections_order()
sorted_changelog = sorted(changelog.items(), key=lambda item: section_order.index(format_section_title(item[0])) if format_section_title(item[0]) in section_order else len(section_order))
@@ -170,12 +168,7 @@ def main():
section_title = format_section_title(label) if label != "Uncategorised" else "Uncategorised"
print(f"## {section_title}\n")
for _, entry in sorted(entries, key=lambda x: x[0]):
# Split entry into lines and indent all lines after the first
lines = entry.splitlines()
print(f"* {lines[0]}")
for line in lines[1:]:
print(f" {line}")
print() # Empty line after each entry
print(f"* {entry}\n")
if __name__ == "__main__":
main()

View File

@@ -85,7 +85,6 @@ repositories:
- Batteries
- doc-gen4
- import-graph
- plausible
- name: REPL
url: https://github.com/leanprover-community/repl

View File

@@ -1,145 +0,0 @@
#!/usr/bin/env python3
"""
Generate release steps script for Lean4 repositories.
This script helps automate the release process for Lean4 and its dependent repositories
by generating step-by-step instructions for updating toolchains, creating tags,
and managing branches.
Usage:
python3 release_steps.py <version> <repo>
Arguments:
version: The version to set in the lean-toolchain file (e.g., v4.6.0)
repo: A substring of the repository name as specified in release_repos.yml
Example:
python3 release_steps.py v4.6.0 mathlib
python3 release_steps.py v4.6.0 batt
The script reads repository configurations from release_repos.yml in the same directory.
Each repository may have specific requirements for:
- Branch management
- Toolchain updates
- Dependency updates
- Tagging conventions
- Stable branch handling
"""
import argparse
import yaml
import os
import sys
import re
def load_repos_config(file_path):
with open(file_path, "r") as f:
return yaml.safe_load(f)["repositories"]
def find_repo(repo_substring, config):
pattern = re.compile(re.escape(repo_substring), re.IGNORECASE)
matching_repos = [r for r in config if pattern.search(r["name"])]
if not matching_repos:
print(f"Error: No repository matching '{repo_substring}' found in configuration.")
sys.exit(1)
if len(matching_repos) > 1:
print(f"Error: Multiple repositories matching '{repo_substring}' found in configuration: {', '.join(r['name'] for r in matching_repos)}")
sys.exit(1)
return matching_repos[0]
def generate_script(repo, version, config):
repo_config = find_repo(repo, config)
repo_name = repo_config['name']
repo_url = repo_config['url']
# Extract the last component of the URL, removing the .git extension if present
repo_dir = repo_url.split('/')[-1].replace('.git', '')
default_branch = repo_config.get("branch", "main")
dependencies = repo_config.get("dependencies", [])
requires_tagging = repo_config.get("toolchain-tag", True)
has_stable_branch = repo_config.get("stable-branch", True)
script_lines = [
f"cd {repo_dir}",
"git fetch",
f"git checkout {default_branch} && git pull",
f"git checkout -b bump_to_{version}",
f"echo leanprover/lean4:{version} > lean-toolchain",
]
# Special cases for specific repositories
if repo_name == "REPL":
script_lines.extend([
"lake update",
"cd test/Mathlib",
f"perl -pi -e 's/rev = \"v\\d+\\.\\d+\\.\\d+(-rc\\d+)?\"/rev = \"{version}\"/g' lakefile.toml",
f"echo leanprover/lean4:{version} > lean-toolchain",
"lake update",
"cd ../..",
"./test.sh"
])
elif dependencies:
script_lines.append('echo "Please update the dependencies in lakefile.{lean,toml}"')
script_lines.append("lake update")
script_lines.append("")
if re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
script_lines.extend([
"echo 'This repo has nightly-testing infrastructure'",
f"git merge bump/{version}",
"echo 'Please resolve any conflicts.'",
""
])
script_lines.extend([
f'git commit -am "chore: bump toolchain to {version}"',
"gh pr create",
"echo 'Please review the PR and merge it.'",
""
])
# Special cases for specific repositories
if repo_name == "ProofWidgets4":
script_lines.append(f"echo 'Note: Follow the version convention of the repository for tagging.'")
elif requires_tagging:
script_lines.append(f"git checkout {default_branch} && git pull")
script_lines.append(f'[ "$(cat lean-toolchain)" = "leanprover/lean4:{version}" ] && git tag -a {version} -m \'Release {version}\' && git push origin --tags || echo "Error: lean-toolchain does not contain expected version {version}"')
if has_stable_branch:
script_lines.extend([
"git checkout stable && git pull",
f"git merge {version} --no-edit",
"git push origin stable"
])
return "\n".join(script_lines)
def main():
parser = argparse.ArgumentParser(
description="Generate release steps script for Lean4 repositories.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s v4.6.0 mathlib Generate steps for updating Mathlib to v4.6.0
%(prog)s v4.6.0 batt Generate steps for updating Batteries to v4.6.0
The script will generate shell commands to:
1. Update the lean-toolchain file
2. Create appropriate branches and commits
3. Create pull requests
4. Create version tags
5. Update stable branches where applicable"""
)
parser.add_argument("version", help="The version to set in the lean-toolchain file (e.g., v4.6.0)")
parser.add_argument("repo", help="A substring of the repository name as specified in release_repos.yml")
args = parser.parse_args()
config_path = os.path.join(os.path.dirname(__file__), "release_repos.yml")
config = load_repos_config(config_path)
script = generate_script(args.repo, args.version, config)
print(script)
if __name__ == "__main__":
main()

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 19)
set(LEAN_VERSION_MINOR 18)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -20,11 +20,6 @@ if (LEAN_SPECIAL_VERSION_DESC)
elseif (NOT LEAN_VERSION_IS_RELEASE)
string(APPEND LEAN_VERSION_STRING "-pre")
endif()
if (LEAN_VERSION_IS_RELEASE)
set(LEAN_MANUAL_ROOT "https://lean-lang.org/doc/reference/${LEAN_VERSION_STRING}/")
else()
set(LEAN_MANUAL_ROOT "")
endif()
set(LEAN_PLATFORM_TARGET "" CACHE STRING "LLVM triple of the target platform")
if (NOT LEAN_PLATFORM_TARGET)
@@ -74,13 +69,12 @@ option(TRACK_LIVE_EXPRS "TRACK_LIVE_EXPRS" OFF)
option(CUSTOM_ALLOCATORS "CUSTOM_ALLOCATORS" ON)
option(SAVE_SNAPSHOT "SAVE_SNAPSHOT" ON)
option(SAVE_INFO "SAVE_INFO" ON)
option(SMALL_ALLOCATOR "SMALL_ALLOCATOR" OFF)
option(SMALL_ALLOCATOR "SMALL_ALLOCATOR" ON)
option(MMAP "MMAP" ON)
option(LAZY_RC "LAZY_RC" OFF)
option(RUNTIME_STATS "RUNTIME_STATS" OFF)
option(BSYMBOLIC "Link with -Bsymbolic to reduce call overhead in shared libraries (Linux)" ON)
option(USE_GMP "USE_GMP" ON)
option(USE_MIMALLOC "use mimalloc" ON)
# development-specific options
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
@@ -93,11 +87,6 @@ if ("${LAZY_RC}" MATCHES "ON")
set(LEAN_LAZY_RC "#define LEAN_LAZY_RC")
endif()
if (USE_MIMALLOC)
set(SMALL_ALLOCATOR OFF)
set(LEAN_MIMALLOC "#define LEAN_MIMALLOC")
endif()
if ("${SMALL_ALLOCATOR}" MATCHES "ON")
set(LEAN_SMALL_ALLOCATOR "#define LEAN_SMALL_ALLOCATOR")
endif()
@@ -466,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/lean/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/temp/libLake.a.export -Wl,--no-whole-archive")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_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/lean/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/temp/libLake.a.export -Wl,--no-whole-archive")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -526,16 +515,7 @@ if(USE_GITHASH)
message(STATUS "git commit sha1: ${GIT_SHA1}")
endif()
else()
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()
set(GIT_SHA1 "")
endif()
configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h")
@@ -554,9 +534,6 @@ else()
set(LEAN_IS_STAGE0 "#define LEAN_IS_STAGE0 0")
endif()
configure_file("${LEAN_SOURCE_DIR}/config.h.in" "${LEAN_BINARY_DIR}/include/lean/config.h")
if (USE_MIMALLOC)
file(COPY "${LEAN_BINARY_DIR}/../mimalloc/src/mimalloc/include/mimalloc.h" DESTINATION "${LEAN_BINARY_DIR}/include/lean")
endif()
install(DIRECTORY ${LEAN_BINARY_DIR}/include/ DESTINATION include)
configure_file(${LEAN_SOURCE_DIR}/lean.mk.in ${LEAN_BINARY_DIR}/share/lean/lean.mk)
install(DIRECTORY ${LEAN_BINARY_DIR}/share/ DESTINATION share)
@@ -565,9 +542,6 @@ 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}}")
@@ -780,12 +754,7 @@ add_custom_target(clean-olean
DEPENDS clean-stdlib)
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
PATTERN temp
PATTERN "*.export"
PATTERN "*.hash"
PATTERN "*.trace"
PATTERN "*.rsp"
EXCLUDE)
PATTERN temp EXCLUDE)
# symlink source into expected installation location for go-to-definition, if file system allows it
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
@@ -816,32 +785,10 @@ 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)

View File

@@ -40,4 +40,3 @@ import Init.Syntax
import Init.Internal
import Init.Try
import Init.BinderNameHint
import Init.Task

View File

@@ -175,10 +175,7 @@ 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_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_and_iff_or_not_not : ¬(a b) ¬a ¬b := Decidable.not_and_iff_or_not_not
theorem not_iff : ¬(a b) (¬a b) := Decidable.not_iff

View File

@@ -42,9 +42,7 @@ instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α
simp [binderNameHint]
rfl -- very strange why `simp` did not close it
/--
Extracts the value from a `ForInStep`, ignoring whether it is `ForInStep.done` or `ForInStep.yield`.
-/
/-- Extract the value from a `ForInStep`, ignoring whether it is `done` or `yield`. -/
def ForInStep.value (x : ForInStep α) : α :=
match x with
| ForInStep.done b => b
@@ -53,28 +51,14 @@ def ForInStep.value (x : ForInStep α) : α :=
@[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl
@[simp] theorem ForInStep.value_yield (b : β) : (ForInStep.yield b).value = b := rfl
/--
Maps a function over a functor, with parameters swapped so that the function comes last.
This function is `Functor.map` with the parameters reversed, typically used via the `<&>` operator.
-/
@[reducible]
def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β :=
fun a f => f <$> a
@[inherit_doc Functor.mapRev]
infixr:100 " <&> " => Functor.mapRev
recommended_spelling "mapRev" for "<&>" in [Functor.mapRev, «term_<&>_»]
/--
Discards the value in a functor, retaining the functor's structure.
Discarding values is especially useful when using `Applicative` functors or `Monad`s to implement
effects, and some operation should be carried out only for its effects. In `do`-notation, statements
whose values are discarded must return `Unit`, and `discard` can be used to explicitly discard their
values.
-/
@[always_inline, inline]
def Functor.discard {f : Type u Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
Functor.mapConst PUnit.unit x
@@ -137,13 +121,6 @@ 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
@@ -154,13 +131,6 @@ 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
@@ -171,9 +141,6 @@ 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
@@ -289,61 +256,21 @@ Using `control` means that `runInBase` can be used multiple times.
-/
/--
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.
/-- 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.
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 α
/--
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.
-/
/-- Transitive closure of MonadControl. -/
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)
@@ -359,48 +286,25 @@ 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 α :=
controlAt m f
/--
Overloaded monadic iteration over some container type.
An instance of `ForM m γ α` describes how to iterate a monadic operator over a container of type `γ`
with elements of type `α` in the monad `m`. The element type should be uniquely determined by the
monad and the container.
Use `ForM.forIn` to construct a `ForIn` instance from a `ForM` instance, thus enabling the use of
the `for` operator in `do`-notation.
Typeclass for the polymorphic `forM` operation described in the "do unchained" paper.
Remark:
- `γ` is a "container" type of elements of type `α`.
- `α` is treated as an output parameter by the typeclass resolution procedure.
That is, it tries to find an instance using only `m` and `γ`.
-/
class ForM (m : Type u Type v) (γ : Type w₁) (α : outParam (Type w₂)) where
/--
Runs the monadic action `f` on each element of the collection `coll`.
-/
forM [Monad m] (coll : γ) (f : α m PUnit) : m PUnit
forM [Monad m] : γ (α m PUnit) m PUnit
export ForM (forM)

View File

@@ -29,11 +29,8 @@ namespace EStateM
variable {ε σ α β : Type u}
/--
Alternative orElse operator that allows callers to select which exception should be used when both
operations fail. The default is to use the first exception since the standard `orElse` uses the
second.
-/
/-- Alternative orElse operator that allows to select which exception should be used.
The default is to use the first exception since the standard `orElse` uses the second. -/
@[always_inline, inline]
protected def orElse' {δ} [Backtrackable δ σ] (x₁ x₂ : EStateM ε σ α) (useFirstEx := true) : EStateM ε σ α := fun s =>
let d := Backtrackable.save s;
@@ -57,11 +54,6 @@ instance : MonadFinally (EStateM ε σ) := {
| Result.error e₂ s => Result.error e₂ s
}
/--
Converts a state monad action into a state monad action with exceptions.
The resulting action does not throw an exception.
-/
@[always_inline, inline] def fromStateM {ε σ α : Type} (x : StateM σ α) : EStateM ε σ α := fun s =>
match x.run s with
| (a, s') => EStateM.Result.ok a s'

View File

@@ -13,20 +13,10 @@ import Init.Coe
namespace Except
variable {ε : Type u}
/--
A successful computation in the `Except ε` monad: `a` is returned, and no exception is thrown.
-/
@[always_inline, inline]
protected def pure (a : α) : Except ε α :=
Except.ok a
/--
Transforms a successful result with a function, doing nothing when an exception is thrown.
Examples:
* `(pure 2 : Except String Nat).map toString = pure 2`
* `(throw "Error" : Except String Nat).map toString = throw "Error"`
-/
@[always_inline, inline]
protected def map (f : α β) : Except ε α Except ε β
| Except.error err => Except.error err
@@ -37,78 +27,36 @@ protected def map (f : α → β) : Except ε α → Except ε β
intro e
simp [Except.map]; cases e <;> rfl
/--
Transforms exceptions with a function, doing nothing on successful results.
Examples:
* `(pure 2 : Except String Nat).mapError (·.length) = pure 2`
* `(throw "Error" : Except String Nat).mapError (·.length) = throw 5`
-/
@[always_inline, inline]
protected def mapError (f : ε ε') : Except ε α Except ε' α
| Except.error err => Except.error <| f err
| Except.ok v => Except.ok v
/--
Sequences two operations that may throw exceptions, allowing the second to depend on the value
returned by the first.
If the first operation throws an exception, then it is the result of the computation. If the first
succeeds but the second throws an exception, then that exception is the result. If both succeed,
then the result is the result of the second computation.
This is the implementation of the `>>=` operator for `Except ε`.
-/
@[always_inline, inline]
protected def bind (ma : Except ε α) (f : α Except ε β) : Except ε β :=
match ma with
| Except.error err => Except.error err
| Except.ok v => f v
/-- Returns `true` if the value is `Except.ok`, `false` otherwise. -/
/-- Returns true if the value is `Except.ok`, false otherwise. -/
@[always_inline, inline]
protected def toBool : Except ε α Bool
| Except.ok _ => true
| Except.error _ => false
@[inherit_doc Except.toBool]
abbrev isOk : Except ε α Bool := Except.toBool
/--
Returns `none` if an exception was thrown, or `some` around the value on success.
Examples:
* `(pure 10 : Except String Nat).toOption = some 10`
* `(throw "Failure" : Except String Nat).toOption = none`
-/
@[always_inline, inline]
protected def toOption : Except ε α Option α
| Except.ok a => some a
| Except.error _ => none
/--
Handles exceptions thrown in the `Except ε` monad.
If `ma` is successful, its result is returned. If it throws an exception, then `handle` is invoked
on the exception's value.
Examples:
* `(pure 2 : Except String Nat).tryCatch (pure ·.length) = pure 2`
* `(throw "Error" : Except String Nat).tryCatch (pure ·.length) = pure 5`
* `(throw "Error" : Except String Nat).tryCatch (fun x => throw ("E: " ++ x)) = throw "E: Error"`
-/
@[always_inline, inline]
protected def tryCatch (ma : Except ε α) (handle : ε Except ε α) : Except ε α :=
match ma with
| Except.ok a => Except.ok a
| Except.error e => handle e
/--
Recovers from exceptions thrown in the `Except ε` monad. Typically used via the `<|>` operator.
`Except.tryCatch` is a related operator that allows the recovery procedure to depend on _which_
exception was thrown.
-/
def orElseLazy (x : Except ε α) (y : Unit Except ε α) : Except ε α :=
match x with
| Except.ok a => Except.ok a
@@ -122,26 +70,12 @@ instance : Monad (Except ε) where
end Except
/--
Adds exceptions of type `ε` to a monad `m`.
-/
def ExceptT (ε : Type u) (m : Type u Type v) (α : Type u) : Type v :=
m (Except ε α)
/--
Use a monadic action that may return an exception's value as an action in the transformed monad that
may throw the corresponding exception.
This is the inverse of `ExceptT.run`.
-/
@[always_inline, inline]
def ExceptT.mk {ε : Type u} {m : Type u Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
/--
Use a monadic action that may throw an exception as an action that may return an exception's value.
This is the inverse of `ExceptT.mk`.
-/
@[always_inline, inline]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
@@ -149,41 +83,25 @@ namespace ExceptT
variable {ε : Type u} {m : Type u Type v} [Monad m]
/--
Returns the value `a` without throwing exceptions or having any other effect.
-/
@[always_inline, inline]
protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
ExceptT.mk <| pure (Except.ok a)
/--
Handles exceptions thrown by an action that can have no effects _other_ than throwing exceptions.
-/
@[always_inline, inline]
protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β)
| Except.ok a => f a
| Except.error e => pure (Except.error e)
/--
Sequences two actions that may throw exceptions. Typically used via `do`-notation or the `>>=`
operator.
-/
@[always_inline, inline]
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α ExceptT ε m β) : ExceptT ε m β :=
ExceptT.mk <| ma >>= ExceptT.bindCont f
/--
Transforms a successful computation's value using `f`. Typically used via the `<$>` operator.
-/
@[always_inline, inline]
protected def map {α β : Type u} (f : α β) (x : ExceptT ε m α) : ExceptT ε m β :=
ExceptT.mk <| x >>= fun a => match a with
| (Except.ok a) => pure <| Except.ok (f a)
| (Except.error e) => pure <| Except.error e
/--
Runs a computation from an underlying monad in the transformed monad with exceptions.
-/
@[always_inline, inline]
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
ExceptT.mk <| Except.ok <$> t
@@ -192,9 +110,6 @@ protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
instance : MonadLift (Except ε) (ExceptT ε m) := fun e => ExceptT.mk <| pure e
instance : MonadLift m (ExceptT ε m) := ExceptT.lift
/--
Handles exceptions produced in the `ExceptT ε` transformer.
-/
@[always_inline, inline]
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α :=
ExceptT.mk <| ma >>= fun res => match res with
@@ -209,11 +124,6 @@ instance : Monad (ExceptT ε m) where
bind := ExceptT.bind
map := ExceptT.map
/--
Transforms exceptions using the function `f`.
This is the `ExceptT` version of `Except.mapError`.
-/
@[always_inline, inline]
protected def adapt {ε' α : Type u} (f : ε ε') : ExceptT ε m α ExceptT ε' m α := fun x =>
ExceptT.mk <| Except.mapError f <$> x
@@ -240,12 +150,8 @@ instance (ε) : MonadExceptOf ε (Except ε) where
namespace MonadExcept
variable {ε : Type u} {m : Type v Type w}
/--
An alternative unconditional error recovery operator that allows callers to specify which exception
to throw in cases where both operations throw exceptions.
By default, the first is thrown, because the `<|>` operator throws the second.
-/
/-- Alternative orelse operator that allows to select which exception should be used.
The default is to use the first exception since the standard `orelse` uses the second. -/
@[always_inline, inline]
def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α :=
tryCatch t₁ fun e₁ => tryCatch t₂ fun e₂ => throw (if useFirstEx then e₁ else e₂)
@@ -265,24 +171,13 @@ 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
/--
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 (α × β)
/-- `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 (α × β)
export MonadFinally (tryFinally')

View File

@@ -10,37 +10,19 @@ import Init.Control.Lawful.Basic
The Exception monad transformer using CPS style.
-/
/--
Adds exceptions of type `ε` to a monad `m`.
Instead of using `Except ε` to model exceptions, this implementation uses continuation passing
style. This has different performance characteristics from `ExceptT ε`.
-/
def ExceptCpsT (ε : Type u) (m : Type u Type v) (α : Type u) := (β : Type u) (α m β) (ε m β) m β
namespace ExceptCpsT
/--
Use a monadic action that may throw an exception as an action that may return an exception's value.
-/
@[always_inline, inline]
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
set_option linter.unusedVariables false in -- `s` unused
/--
Use a monadic action that may throw an exception by providing explicit success and failure
continuations.
-/
@[always_inline, inline]
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α m β) (error : ε m β) : m β :=
x _ ok error
/--
Returns the value of a computation, forgetting whether it was an exception or a success.
This corresponds to early return.
-/
@[always_inline, inline]
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
x α pure pure
@@ -58,9 +40,6 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
throw e := fun _ _ k => k e
tryCatch x handle := fun _ k₁ k₂ => x _ k₁ (fun e => handle e _ k₁ k₂)
/--
Run an action from the transformed monad in the exception monad.
-/
@[always_inline, inline]
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
fun _ k _ => x >>= k

View File

@@ -10,28 +10,6 @@ import Init.Core
universe u
/--
The identity function on types, used primarily for its `Monad` instance.
The identity monad is useful together with monad transformers to construct monads for particular
purposes. Additionally, it can be used with `do`-notation in order to use control structures such as
local mutability, `for`-loops, and early returns in code that does not otherwise use monads.
Examples:
```lean example
def containsFive (xs : List Nat) : Bool := Id.run do
for x in xs do
if x == 5 then return true
return false
```
```lean example
#eval containsFive [1, 3, 5, 7]
```
```output
true
```
-/
def Id (type : Type u) : Type u := type
namespace Id
@@ -42,18 +20,9 @@ instance : Monad Id where
bind x f := f x
map f x := f x
/--
The identity monad has a `bind` operator.
-/
def hasBind : Bind Id :=
inferInstance
/--
Runs a computation in the identity monad.
This function is the identity function. Because its parameter has type `Id α`, it causes
`do`-notation in its arguments to use the `Monad Id` instance.
-/
@[always_inline, inline]
protected def run (x : Id α) : α := x

View File

@@ -13,26 +13,17 @@ open Function
rfl
/--
A functor satisfies the functor laws.
The `Functor` class contains the operations of a functor, but does not require that instances
prove they satisfy the laws of a functor. A `LawfulFunctor` instance includes proofs that the laws
are satisfied. Because `Functor` instances may provide optimized implementations of `mapConst`,
`LawfulFunctor` instances must also prove that the optimized implementation is equivalent to the
standard implementation.
The `Functor` typeclass only contains the operations of a functor.
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
including the preservation of the identity and composition laws:
```
id <$> x = x
(h ∘ g) <$> x = h <$> g <$> x
```
-/
class LawfulFunctor (f : Type u Type v) [Functor f] : Prop where
/--
The `mapConst` implementation is equivalent to the default implementation.
-/
map_const : (Functor.mapConst : α f β f α) = Functor.map const β
/--
The `map` implementation preserves identity.
-/
id_map (x : f α) : id <$> x = x
/--
The `map` implementation preserves function composition.
-/
comp_map (g : α β) (h : β γ) (x : f α) : (h g) <$> x = h <$> g <$> x
export LawfulFunctor (map_const id_map comp_map)
@@ -47,48 +38,21 @@ attribute [simp] id_map
(comp_map _ _ _).symm
/--
An applicative functor satisfies the laws of an applicative functor.
The `Applicative` class contains the operations of an applicative functor, but does not require that
instances prove they satisfy the laws of an applicative functor. A `LawfulApplicative` instance
includes proofs that the laws are satisfied.
Because `Applicative` instances may provide optimized implementations of `seqLeft` and `seqRight`,
`LawfulApplicative` instances must also prove that the optimized implementation is equivalent to the
standard implementation.
The `Applicative` typeclass only contains the operations of an applicative functor.
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
```
pure id <*> v = v
pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (f x)
u <*> pure y = pure (· y) <*> u
```
-/
class LawfulApplicative (f : Type u Type v) [Applicative f] : Prop extends LawfulFunctor f where
/-- `seqLeft` is equivalent to the default implementation. -/
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
/-- `seqRight` is equivalent to the default implementation. -/
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
/--
`pure` before `seq` is equivalent to `Functor.map`.
This means that `pure` really is pure when occurring immediately prior to `seq`.
-/
pure_seq (g : α β) (x : f α) : pure g <*> x = g <$> x
/--
Mapping a function over the result of `pure` is equivalent to applying the function under `pure`.
This means that `pure` really is pure with respect to `Functor.map`.
-/
map_pure (g : α β) (x : α) : g <$> (pure x : f α) = pure (g x)
/--
`pure` after `seq` is equivalent to `Functor.map`.
This means that `pure` really is pure when occurring just after `seq`.
-/
seq_pure {α β : Type u} (g : f (α β)) (x : α) : g <*> pure x = (fun h => h x) <$> g
/--
`seq` is associative.
Changing the nesting of `seq` calls while maintaining the order of computations results in an
equivalent computation. This means that `seq` is not doing any more than sequencing.
-/
seq_assoc {α β γ : Type u} (x : f α) (g : f (α β)) (h : f (β γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
comp_map g h x := (by
repeat rw [ pure_seq]
@@ -102,36 +66,21 @@ attribute [simp] map_pure seq_pure
simp [pure_seq]
/--
Lawful monads are those that satisfy a certain behavioral specification. While all instances of
`Monad` should satisfy these laws, not all implementations are required to prove this.
The `Monad` typeclass only contains the operations of a monad.
`LawfulMonad` further asserts that these operations satisfy the laws of a monad,
including associativity and identity laws for `bind`:
```
pure x >>= f = f x
x >>= pure = x
x >>= f >>= g = x >>= (fun x => f x >>= g)
```
`LawfulMonad.mk'` is an alternative constructor that contains useful defaults for many fields.
`LawfulMonad.mk'` is an alternative constructor containing useful defaults for many fields.
-/
class LawfulMonad (m : Type u Type v) [Monad m] : Prop extends LawfulApplicative m where
/--
A `bind` followed by `pure` composed with a function is equivalent to a functorial map.
This means that `pure` really is pure after a `bind` and has no effects.
-/
bind_pure_comp (f : α β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
/--
A `bind` followed by a functorial map is equivalent to `Applicative` sequencing.
This means that the effect sequencing from `Monad` and `Applicative` are the same.
-/
bind_map {α β : Type u} (f : m (α β)) (x : m α) : f >>= (. <$> x) = f <*> x
/--
`pure` followed by `bind` is equivalent to function application.
This means that `pure` really is pure before a `bind` and has no effects.
-/
pure_bind (x : α) (f : α m β) : pure x >>= f = f x
/--
`bind` is associative.
Changing the nesting of `bind` calls while maintaining the order of computations results in an
equivalent computation. This means that `bind` is not doing more than data-dependent sequencing.
-/
bind_assoc (x : m α) (f : α m β) (g : β m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
map_pure g x := (by rw [ bind_pure_comp, pure_bind])
seq_pure g x := (by rw [ bind_map]; simp [map_pure, bind_pure_comp])

View File

@@ -8,22 +8,13 @@ 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
@@ -31,25 +22,15 @@ 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)
@@ -59,17 +40,11 @@ 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
@@ -77,12 +52,6 @@ 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)
@@ -90,9 +59,6 @@ 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

View File

@@ -10,21 +10,12 @@ 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
@@ -44,8 +35,4 @@ 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

View File

@@ -9,42 +9,19 @@ 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 α
@@ -61,23 +38,14 @@ 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)
@@ -88,17 +56,10 @@ 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
@@ -107,40 +68,18 @@ 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)
@@ -159,9 +98,7 @@ instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := {
end
end StateT
/--
Creates a suitable implementation of `ForIn.forIn` from a `ForM` instance.
-/
/-- Adapter to create a ForIn instance from a ForM instance -/
@[always_inline, inline]
def ForM.forIn [Monad m] [ForM (StateT β (ExceptT β m)) ρ α]
(x : ρ) (b : β) (f : α β m (ForInStep β)) : m β := do

View File

@@ -6,45 +6,24 @@ 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)
@@ -64,12 +43,6 @@ 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)

View File

@@ -8,23 +8,10 @@ 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
@@ -32,12 +19,6 @@ 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
@@ -46,12 +27,6 @@ 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
@@ -61,30 +36,14 @@ 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

View File

@@ -306,10 +306,6 @@ syntax (name := first) "first " withPosition((ppDedent(ppLine) colGe "| " convSe
/-- `try tac` runs `tac` and succeeds even if `tac` failed. -/
macro "try " t:convSeq : conv => `(conv| first | $t | skip)
/--
`tac <;> tac'` runs `tac` on the main goal and `tac'` on each produced goal, concatenating all goals
produced by `tac'`.
-/
macro:1 x:conv tk:" <;> " y:conv:0 : conv =>
`(conv| tactic' => (conv' => $x:conv) <;>%$tk (conv' => $y:conv))

File diff suppressed because it is too large Load Diff

View File

@@ -15,12 +15,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/--
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`.
`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`.
`Array.pmap`, named for “partial map,” is the equivalent of `Array.map` for such partial functions.
We replace this at runtime with a more efficient version via the `csimp` lemma `pmap_eq_pmapImpl`.
-/
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
@@ -31,27 +32,14 @@ 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
/--
“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)`.
-/
/-- `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}`. -/
@[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)
/--
“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.
-/
/-- `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}`. -/
@[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} :
@@ -109,7 +97,7 @@ well-founded recursion mechanism to prove that the function terminates.
simp
@[simp]
theorem pmap_eq_map {p : α Prop} {f : α β} {xs : Array α} (H) :
theorem pmap_eq_map (p : α Prop) (f : α β) (xs : Array α) (H) :
@pmap _ _ p (fun a _ => f a) xs H = map f xs := by
cases xs; simp
@@ -120,13 +108,13 @@ theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a,
simp only [List.pmap_toArray, mk.injEq]
rw [List.pmap_congr_left _ h]
theorem map_pmap {p : α Prop} {g : β γ} {f : a, p a β} {xs : Array α} (H) :
theorem map_pmap {p : α Prop} (g : β γ) (f : a, p a β) (xs H) :
map g (pmap f xs H) = pmap (fun a h => g (f a h)) xs H := by
cases xs
simp [List.map_pmap]
theorem pmap_map {p : β Prop} {g : b, p b γ} {f : α β} {xs : Array α} (H) :
pmap g (map f xs) H = pmap (fun a h => g (f a) h) xs fun _ h => H _ (mem_map_of_mem h) := by
theorem pmap_map {p : β Prop} (g : b, p b γ) (f : α β) (xs H) :
pmap g (map f xs) H = pmap (fun a h => g (f a) h) xs fun _ h => H _ (mem_map_of_mem _ h) := by
cases xs
simp [List.pmap_map]
@@ -153,13 +141,13 @@ theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H :
cases xs
simp [attachWith_congr (List.push_toArray _ _)]
theorem pmap_eq_map_attach {p : α Prop} {f : a, p a β} {xs : Array α} (H) :
theorem pmap_eq_map_attach {p : α Prop} (f : a, p a β) (xs H) :
pmap f xs H = xs.attach.map fun x => f x.1 (H _ x.2) := by
cases xs
simp [List.pmap_eq_map_attach]
@[simp]
theorem pmap_eq_attachWith {p q : α Prop} {f : a, p a q a} {xs : Array α} (H) :
theorem pmap_eq_attachWith {p q : α Prop} (f : a, p a q a) (xs H) :
pmap (fun a h => a, f a h) xs H = xs.attachWith q (fun x h => f x (H x h)) := by
cases xs
simp [List.pmap_eq_attachWith]
@@ -172,18 +160,17 @@ theorem attach_map_val (xs : Array α) (f : α → β) :
@[deprecated attach_map_val (since := "2025-02-17")]
abbrev attach_map_coe := @attach_map_val
-- The argument `xs : Array α` is explicit to allow rewriting from right to left.
theorem attach_map_subtype_val (xs : Array α) : xs.attach.map Subtype.val = xs := by
cases xs; simp
theorem attachWith_map_val {p : α Prop} {f : α β} {xs : Array α} (H : a xs, p a) :
theorem attachWith_map_val {p : α Prop} (f : α β) (xs : Array α) (H : a xs, p a) :
((xs.attachWith p H).map fun (i : { i // p i}) => f i) = xs.map f := by
cases xs; simp
@[deprecated attachWith_map_val (since := "2025-02-17")]
abbrev attachWith_map_coe := @attachWith_map_val
theorem attachWith_map_subtype_val {p : α Prop} {xs : Array α} (H : a xs, p a) :
theorem attachWith_map_subtype_val {p : α Prop} (xs : Array α) (H : a xs, p a) :
(xs.attachWith p H).map Subtype.val = xs := by
cases xs; simp
@@ -195,7 +182,7 @@ theorem mem_attach (xs : Array α) : ∀ x, x ∈ xs.attach
exact m
@[simp]
theorem mem_attachWith {xs : Array α} {q : α Prop} (H) (x : {x // q x}) :
theorem mem_attachWith (xs : Array α) {q : α Prop} (H) (x : {x // q x}) :
x xs.attachWith q H x.1 xs := by
cases xs
simp
@@ -251,11 +238,10 @@ theorem attachWith_ne_empty_iff {xs : Array α} {P : α → Prop} {H : ∀ a ∈
cases xs; simp
@[simp]
theorem getElem?_pmap {p : α Prop} {f : a, p a β} {xs : Array α} (h : a xs, p a) (i : Nat) :
theorem getElem?_pmap {p : α Prop} (f : a, p a β) {xs : Array α} (h : a xs, p a) (i : Nat) :
(pmap f xs h)[i]? = Option.pmap f xs[i]? fun x H => h x (mem_of_getElem? H) := by
cases xs; simp
-- The argument `f` is explicit to allow rewriting from right to left.
@[simp]
theorem getElem_pmap {p : α Prop} (f : a, p a β) {xs : Array α} (h : a xs, p a) {i : Nat}
(hi : i < (pmap f xs h).size) :
@@ -285,37 +271,37 @@ theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
xs.attach[i] = xs[i]'(by simpa using h), getElem_mem (by simpa using h) :=
getElem_attachWith h
@[simp] theorem pmap_attach {xs : Array α} {p : {x // x xs} Prop} {f : a, p a β} (H) :
@[simp] theorem pmap_attach (xs : Array α) {p : {x // x xs} Prop} (f : a, p a β) (H) :
pmap f xs.attach H =
xs.pmap (P := fun a => h : a xs, p a, h)
(fun a h => f a, h.1 h.2) (fun a h => h, H a, h (by simp)) := by
ext <;> simp
@[simp] theorem pmap_attachWith {xs : Array α} {p : {x // q x} Prop} {f : a, p a β} (H₁ H₂) :
@[simp] theorem pmap_attachWith (xs : Array α) {p : {x // q x} Prop} (f : a, p a β) (H₁ H₂) :
pmap f (xs.attachWith q H₁) H₂ =
xs.pmap (P := fun a => h : q a, p a, h)
(fun a h => f a, h.1 h.2) (fun a h => H₁ _ h, H₂ a, H₁ _ h (by simpa)) := by
ext <;> simp
theorem foldl_pmap {xs : Array α} {P : α Prop} {f : (a : α) P a β}
(H : (a : α), a xs P a) (g : γ β γ) (x : γ) :
theorem foldl_pmap (xs : Array α) {P : α Prop} (f : (a : α) P a β)
(H : (a : α), a xs P a) (g : γ β γ) (x : γ) :
(xs.pmap f H).foldl g x = xs.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
rw [pmap_eq_map_attach, foldl_map]
theorem foldr_pmap {xs : Array α} {P : α Prop} {f : (a : α) P a β}
(H : (a : α), a xs P a) (g : β γ γ) (x : γ) :
theorem foldr_pmap (xs : Array α) {P : α Prop} (f : (a : α) P a β)
(H : (a : α), a xs P a) (g : β γ γ) (x : γ) :
(xs.pmap f H).foldr g x = xs.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
rw [pmap_eq_map_attach, foldr_map]
@[simp] theorem foldl_attachWith
{xs : Array α} {q : α Prop} (H : a, a xs q a) {f : β { x // q x} β} {b} (w : stop = xs.size) :
(xs : Array α) {q : α Prop} (H : a, a xs q a) {f : β { x // q x} β} {b} (w : stop = xs.size) :
(xs.attachWith q H).foldl f b 0 stop = xs.attach.foldl (fun b a, h => f b a, H _ h) b := by
subst w
rcases xs with xs
simp [List.foldl_attachWith, List.foldl_map]
@[simp] theorem foldr_attachWith
{xs : Array α} {q : α Prop} (H : a, a xs q a) {f : { x // q x} β β} {b} (w : start = xs.size) :
(xs : Array α) {q : α Prop} (H : a, a xs q a) {f : { x // q x} β β} {b} (w : start = xs.size) :
(xs.attachWith q H).foldr f b start 0 = xs.attach.foldr (fun a acc => f a.1, H _ a.2 acc) b := by
subst w
rcases xs with xs
@@ -331,7 +317,7 @@ Unfortunately this can't be applied by `simp` because of the higher order unific
and even when rewriting we need to specify the function explicitly.
See however `foldl_subtype` below.
-/
theorem foldl_attach {xs : Array α} {f : β α β} {b : β} :
theorem foldl_attach (xs : Array α) (f : β α β) (b : β) :
xs.attach.foldl (fun acc t => f acc t.1) b = xs.foldl f b := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
@@ -350,7 +336,7 @@ Unfortunately this can't be applied by `simp` because of the higher order unific
and even when rewriting we need to specify the function explicitly.
See however `foldr_subtype` below.
-/
theorem foldr_attach {xs : Array α} {f : α β β} {b : β} :
theorem foldr_attach (xs : Array α) (f : α β β) (b : β) :
xs.attach.foldr (fun t acc => f t.1 acc) b = xs.foldr f b := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
@@ -359,31 +345,31 @@ theorem foldr_attach {xs : Array α} {f : α → β → β} {b : β} :
ext
simpa using fun a => List.mem_of_getElem? a
theorem attach_map {xs : Array α} {f : α β} :
(xs.map f).attach = xs.attach.map (fun x, h => f x, mem_map_of_mem h) := by
theorem attach_map {xs : Array α} (f : α β) :
(xs.map f).attach = xs.attach.map (fun x, h => f x, mem_map_of_mem f h) := by
cases xs
ext <;> simp
theorem attachWith_map {xs : Array α} {f : α β} {P : β Prop} (H : (b : β), b xs.map f P b) :
(xs.map f).attachWith P H = (xs.attachWith (P f) (fun _ h => H _ (mem_map_of_mem h))).map
theorem attachWith_map {xs : Array α} (f : α β) {P : β Prop} {H : (b : β), b xs.map f P b} :
(xs.map f).attachWith P H = (xs.attachWith (P f) (fun _ h => H _ (mem_map_of_mem f h))).map
fun x, h => f x, h := by
cases xs
simp [List.attachWith_map]
@[simp] theorem map_attachWith {xs : Array α} {P : α Prop} {H : (a : α), a xs P a}
{f : { x // P x } β} :
(f : { x // P x } β) :
(xs.attachWith P H).map f = xs.attach.map fun x, h => f x, H _ h := by
cases xs <;> simp_all
theorem map_attachWith_eq_pmap {xs : Array α} {P : α Prop} {H : (a : α), a xs P a}
{f : { x // P x } β} :
(f : { x // P x } β) :
(xs.attachWith P H).map f =
xs.pmap (fun a (h : a xs P a) => f a, H _ h.1) (fun a h => h, H a h) := by
cases xs
ext <;> simp
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
theorem map_attach_eq_pmap {xs : Array α} {f : { x // x xs } β} :
theorem map_attach_eq_pmap {xs : Array α} (f : { x // x xs } β) :
xs.attach.map f = xs.pmap (fun a h => f a, h) (fun _ => id) := by
cases xs
ext <;> simp
@@ -395,14 +381,14 @@ theorem attach_filterMap {xs : Array α} {f : α → Option β} :
(xs.filterMap f).attach = xs.attach.filterMap
fun x, h => (f x).pbind (fun b m => some b, mem_filterMap.mpr x, h, m) := by
cases xs
rw [attach_congr List.filterMap_toArray]
rw [attach_congr (List.filterMap_toArray f _)]
simp [List.attach_filterMap, List.map_filterMap, Function.comp_def]
theorem attach_filter {xs : Array α} (p : α Bool) :
(xs.filter p).attach = xs.attach.filterMap
fun x => if w : p x.1 then some x.1, mem_filter.mpr x.2, w else none := by
cases xs
rw [attach_congr List.filter_toArray]
rw [attach_congr (List.filter_toArray p _)]
simp [List.attach_filter, List.map_filterMap, Function.comp_def]
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
@@ -424,14 +410,14 @@ theorem filter_attachWith {q : α → Prop} {xs : Array α} {p : {x // q x} →
cases xs
simp [Function.comp_def, List.filter_map]
theorem pmap_pmap {p : α Prop} {q : β Prop} {g : a, p a β} {f : b, q b γ} {xs} (H₁ H₂) :
theorem pmap_pmap {p : α Prop} {q : β Prop} (g : a, p a β) (f : b, q b γ) (xs H₁ H₂) :
pmap f (pmap g xs H₁) H₂ =
pmap (α := { x // x xs }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) xs.attach
(fun a _ => H₁ a a.2) := by
cases xs
simp [List.pmap_pmap, List.pmap_map]
@[simp] theorem pmap_append {p : ι Prop} {f : a : ι, p a α} {xs ys : Array ι}
@[simp] theorem pmap_append {p : ι Prop} (f : a : ι, p a α) (xs ys : Array ι)
(h : a xs ++ ys, p a) :
(xs ++ ys).pmap f h =
(xs.pmap f fun a ha => h a (mem_append_left ys ha)) ++
@@ -440,13 +426,13 @@ theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f
cases ys
simp
theorem pmap_append' {p : α Prop} {f : a : α, p a β} {xs ys : Array α}
theorem pmap_append' {p : α Prop} (f : a : α, p a β) (xs ys : Array α)
(h₁ : a xs, p a) (h₂ : a ys, p a) :
((xs ++ ys).pmap f fun a ha => (mem_append.1 ha).elim (h₁ a) (h₂ a)) =
xs.pmap f h₁ ++ ys.pmap f h₂ :=
pmap_append _
pmap_append f xs ys _
@[simp] theorem attach_append {xs ys : Array α} :
@[simp] theorem attach_append (xs ys : Array α) :
(xs ++ ys).attach = xs.attach.map (fun x, h => x, mem_append_left ys h) ++
ys.attach.map fun x, h => x, mem_append_right xs h := by
cases xs
@@ -460,12 +446,12 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
simp [attachWith, attach_append, map_pmap, pmap_append]
@[simp] theorem pmap_reverse {P : α Prop} {f : (a : α) P a β} {xs : Array α}
@[simp] theorem pmap_reverse {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs.reverse P a) :
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
induction xs <;> simp_all
theorem reverse_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
theorem reverse_pmap {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs P a) :
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
rw [pmap_reverse]
@@ -483,18 +469,18 @@ theorem reverse_attachWith {P : α → Prop} {xs : Array α}
cases xs
simp
@[simp] theorem attach_reverse {xs : Array α} :
@[simp] theorem attach_reverse (xs : Array α) :
xs.reverse.attach = xs.attach.reverse.map fun x, h => x, by simpa using h := by
cases xs
rw [attach_congr List.reverse_toArray]
rw [attach_congr (List.reverse_toArray _)]
simp
theorem reverse_attach {xs : Array α} :
theorem reverse_attach (xs : Array α) :
xs.attach.reverse = xs.reverse.attach.map fun x, h => x, by simpa using h := by
cases xs
simp
@[simp] theorem back?_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
@[simp] theorem back?_pmap {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs P a) :
(xs.pmap f H).back? = xs.attach.back?.map fun a, m => f a (H a m) := by
cases xs
@@ -513,19 +499,19 @@ theorem back?_attach {xs : Array α} :
simp
@[simp]
theorem countP_attach {xs : Array α} {p : α Bool} :
theorem countP_attach (xs : Array α) (p : α Bool) :
xs.attach.countP (fun a : {x // x xs} => p a) = xs.countP p := by
cases xs
simp [Function.comp_def]
@[simp]
theorem countP_attachWith {p : α Prop} {q : α Bool} {xs : Array α} {H : a xs, p a} :
theorem countP_attachWith {p : α Prop} (xs : Array α) (H : a xs, p a) (q : α Bool) :
(xs.attachWith p H).countP (fun a : {x // p x} => q a) = xs.countP q := by
cases xs
simp
@[simp]
theorem count_attach [DecidableEq α] {xs : Array α} {a : {x // x xs}} :
theorem count_attach [DecidableEq α] (xs : Array α) (a : {x // x xs}) :
xs.attach.count a = xs.count a := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.count_toArray]
@@ -534,12 +520,12 @@ theorem count_attach [DecidableEq α] {xs : Array α} {a : {x // x ∈ xs}} :
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
@[simp]
theorem count_attachWith [DecidableEq α] {p : α Prop} {xs : Array α} (H : a xs, p a) {a : {x // p x}} :
theorem count_attachWith [DecidableEq α] {p : α Prop} (xs : Array α) (H : a xs, p a) (a : {x // p x}) :
(xs.attachWith p H).count a = xs.count a := by
cases xs
simp
@[simp] theorem countP_pmap {p : α Prop} {g : a, p a β} {f : β Bool} {xs : Array α} (H₁) :
@[simp] theorem countP_pmap {p : α Prop} (g : a, p a β) (f : β Bool) (xs : Array α) (H₁) :
(xs.pmap g H₁).countP f =
xs.attach.countP (fun a, m => f (g a (H₁ a m))) := by
simp [pmap_eq_map_attach, countP_map, Function.comp_def]
@@ -556,33 +542,19 @@ Further, we provide simp lemmas that push `unattach` inwards.
-/
/--
Maps an array of terms in a subtype to the corresponding terms in the type by forgetting that they
satisfy the predicate.
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`.
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]`.
If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
-/
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 = #[] := by
simp [unattach]
@[simp] theorem unattach_nil {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
@[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]
@[simp] theorem mem_unattach {p : α Prop} {xs : Array { x // p x }} {a} :
a xs.unattach h : p a, a, h xs := by
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
@[simp] theorem size_unattach {p : α Prop} {xs : Array { x // p x }} :
xs.unattach.size = xs.size := by
unfold unattach
@@ -704,20 +676,6 @@ and simplifies these to the function directly taking the value.
simp
rw [List.find?_subtype hf]
@[simp] theorem all_subtype {p : α Prop} {xs : Array { x // p x }} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) (w : stop = xs.size) :
xs.all f 0 stop = xs.unattach.all g := by
subst w
rcases xs with xs
simp [hf]
@[simp] theorem any_subtype {p : α Prop} {xs : Array { x // p x }} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) (w : stop = xs.size) :
xs.any f 0 stop = xs.unattach.any g := by
subst w
rcases xs with xs
simp [hf]
/-! ### Simp lemmas pushing `unattach` inwards. -/
@[simp] theorem unattach_filter {p : α Prop} {xs : Array { x // p x }}
@@ -745,73 +703,71 @@ 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_replicate {p : α Prop} {n : Nat} {x : { x // p x }} :
(Array.replicate n x).unattach = Array.replicate n x.1 := by
@[simp] theorem unattach_mkArray {p : α Prop} {n : Nat} {x : { x // p x }} :
(Array.mkArray n x).unattach = Array.mkArray 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 map_wfParam {xs : Array α} {f : α β} :
@[wf_preprocess] theorem Array.map_wfParam (xs : Array α) (f : α β) :
(wfParam xs).map f = xs.attach.unattach.map f := by
simp [wfParam]
@[wf_preprocess] theorem map_unattach {P : α Prop} {xs : Array (Subtype P)} {f : α β} :
@[wf_preprocess] theorem Array.map_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α β) :
xs.unattach.map f = xs.map fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[wf_preprocess] theorem foldl_wfParam {xs : Array α} {f : β α β} {x : β} :
@[wf_preprocess] theorem foldl_wfParam (xs : Array α) (f : β α β) (x : β) :
(wfParam xs).foldl f x = xs.attach.unattach.foldl f x := by
simp [wfParam]
@[wf_preprocess] theorem foldl_unattach {P : α Prop} {xs : Array (Subtype P)} {f : β α β} {x : β} :
@[wf_preprocess] theorem foldl_unattach (P : α Prop) (xs : Array (Subtype P)) (f : β α β) (x : β):
xs.unattach.foldl f x = xs.foldl (fun s x, h =>
binderNameHint s f <| binderNameHint x (f s) <| binderNameHint h () <| f s (wfParam x)) x := by
simp [wfParam]
@[wf_preprocess] theorem foldr_wfParam {xs : Array α} {f : α β β} {x : β} :
@[wf_preprocess] theorem foldr_wfParam (xs : Array α) (f : α β β) (x : β) :
(wfParam xs).foldr f x = xs.attach.unattach.foldr f x := by
simp [wfParam]
@[wf_preprocess] theorem foldr_unattach {P : α Prop} {xs : Array (Subtype P)} {f : α β β} {x : β} :
@[wf_preprocess] theorem foldr_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α β β) (x : β):
xs.unattach.foldr f x = xs.foldr (fun x, h s =>
binderNameHint x f <| binderNameHint s (f x) <| binderNameHint h () <| f (wfParam x) s) x := by
simp [wfParam]
@[wf_preprocess] theorem filter_wfParam {xs : Array α} {f : α Bool} :
@[wf_preprocess] theorem filter_wfParam (xs : Array α) (f : α Bool) :
(wfParam xs).filter f = xs.attach.unattach.filter f:= by
simp [wfParam]
@[wf_preprocess] theorem filter_unattach {P : α Prop} {xs : Array (Subtype P)} {f : α Bool} :
@[wf_preprocess] theorem filter_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α Bool) :
xs.unattach.filter f = (xs.filter (fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x))).unattach := by
simp [wfParam]
@[wf_preprocess] theorem reverse_wfParam {xs : Array α} :
@[wf_preprocess] theorem reverse_wfParam (xs : Array α) :
(wfParam xs).reverse = xs.attach.unattach.reverse := by simp [wfParam]
@[wf_preprocess] theorem reverse_unattach {P : α Prop} {xs : Array (Subtype P)} :
@[wf_preprocess] theorem reverse_unattach (P : α Prop) (xs : Array (Subtype P)) :
xs.unattach.reverse = xs.reverse.unattach := by simp
@[wf_preprocess] theorem filterMap_wfParam {xs : Array α} {f : α Option β} :
@[wf_preprocess] theorem filterMap_wfParam (xs : Array α) (f : α Option β) :
(wfParam xs).filterMap f = xs.attach.unattach.filterMap f := by
simp [wfParam]
@[wf_preprocess] theorem filterMap_unattach {P : α Prop} {xs : Array (Subtype P)} {f : α Option β} :
@[wf_preprocess] theorem filterMap_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α Option β) :
xs.unattach.filterMap f = xs.filterMap fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[wf_preprocess] theorem flatMap_wfParam {xs : Array α} {f : α Array β} :
@[wf_preprocess] theorem flatMap_wfParam (xs : Array α) (f : α Array β) :
(wfParam xs).flatMap f = xs.attach.unattach.flatMap f := by
simp [wfParam]
@[wf_preprocess] theorem flatMap_unattach {P : α Prop} {xs : Array (Subtype P)} {f : α Array β} :
@[wf_preprocess] theorem flatMap_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α Array β) :
xs.unattach.flatMap f = xs.flatMap fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
end Array

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ theorem Array.of_push_eq_push {as bs : Array α} (h : as.push a = bs.push b) : a
cases as; cases bs
simp_all
private theorem List.size_toArrayAux {as : List α} {bs : Array α} : (as.toArrayAux bs).size = as.length + bs.size := by
private theorem List.size_toArrayAux (as : List α) (bs : Array α) : (as.toArrayAux bs).size = as.length + bs.size := by
induction as generalizing bs with
| nil => simp [toArrayAux]
| cons a as ih => simp +arith [toArrayAux, *]
@@ -35,14 +35,9 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
have := Array.of_push_eq_push ih₂
simp [this]
theorem List.toArray_eq_toArray_eq {as bs : List α} : (as.toArray = bs.toArray) = (as = bs) := by
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
@@ -71,19 +66,11 @@ where
return as
/--
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.
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`.
-/
@[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

View File

@@ -29,16 +29,6 @@ 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
@@ -49,16 +39,6 @@ are inclusive, and default to searching the entire array.
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
@@ -88,16 +68,6 @@ are inclusive, and default to searching the entire array.
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 α)
@@ -111,21 +81,6 @@ to `k` is present, then `add` is used to determine the value to be inserted.
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

View File

@@ -42,35 +42,35 @@ def get! {α : Type u} [Inhabited α] (a : @& Array α) (i : @& Nat) : α :=
Array.getD a i default
theorem foldlM_toList.aux [Monad m]
{f : β α m β} {xs : Array α} {i j} (H : xs.size i + j) {b} :
(f : β α m β) (xs : Array α) (i j) (H : xs.size i + j) (b) :
foldlM.loop f xs xs.size (Nat.le_refl _) i j b = (xs.toList.drop j).foldlM f b := by
unfold foldlM.loop
split; split
· cases Nat.not_le_of_gt _ (Nat.zero_add _ H)
· rename_i i; rw [Nat.succ_add] at H
simp [foldlM_toList.aux (j := j+1) H]
simp [foldlM_toList.aux f xs i (j+1) H]
rw (occs := [2]) [ List.getElem_cons_drop_succ_eq_drop _]
rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
@[simp] theorem foldlM_toList [Monad m]
{f : β α m β} {init : β} {xs : Array α} :
(f : β α m β) (init : β) (xs : Array α) :
xs.toList.foldlM f init = xs.foldlM f init := by
simp [foldlM, foldlM_toList.aux]
@[simp] theorem foldl_toList (f : β α β) {init : β} {xs : Array α} :
@[simp] theorem foldl_toList (f : β α β) (init : β) (xs : Array α) :
xs.toList.foldl f init = xs.foldl f init :=
List.foldl_eq_foldlM .. foldlM_toList ..
theorem foldrM_eq_reverse_foldlM_toList.aux [Monad m]
{f : α β m β} {xs : Array α} {init : β} {i} (h) :
(f : α β m β) (xs : Array α) (init : β) (i h) :
(xs.toList.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f xs 0 i h init := by
unfold foldrM.fold
match i with
| 0 => simp [List.foldlM, List.take]
| i+1 => rw [ List.take_concat_get h]; simp [ aux]
| i+1 => rw [ List.take_concat_get _ _ h]; simp [ (aux f xs · i)]
theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α β m β} {init : β} {xs : Array α} :
theorem foldrM_eq_reverse_foldlM_toList [Monad m] (f : α β m β) (init : β) (xs : Array α) :
xs.foldrM f init = xs.toList.reverse.foldlM (fun x y => f y x) init := by
have : xs = #[] 0 < xs.size :=
match xs with | [] => .inl rfl | a::l => .inr (Nat.zero_lt_succ _)
@@ -78,31 +78,31 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init
simp [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
@[simp] theorem foldrM_toList [Monad m]
{f : α β m β} {init : β} {xs : Array α} :
(f : α β m β) (init : β) (xs : Array α) :
xs.toList.foldrM f init = xs.foldrM f init := by
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
@[simp] theorem foldr_toList (f : α β β) {init : β} {xs : Array α} :
@[simp] theorem foldr_toList (f : α β β) (init : β) (xs : Array α) :
xs.toList.foldr f init = xs.foldr f init :=
List.foldr_eq_foldrM .. foldrM_toList ..
@[simp] theorem push_toList {xs : Array α} {a : α} : (xs.push a).toList = xs.toList ++ [a] := by
@[simp] theorem push_toList (xs : Array α) (a : α) : (xs.push a).toList = xs.toList ++ [a] := by
simp [push, List.concat_eq_append]
@[simp] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
@[simp] theorem toListAppend_eq (xs : Array α) (l : List α) : xs.toListAppend l = xs.toList ++ l := by
simp [toListAppend, foldr_toList]
@[simp] theorem toListImpl_eq {xs : Array α} : xs.toListImpl = xs.toList := by
@[simp] theorem toListImpl_eq (xs : Array α) : xs.toListImpl = xs.toList := by
simp [toListImpl, foldr_toList]
@[simp] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
@[simp] theorem toList_pop (xs : Array α) : xs.pop.toList = xs.toList.dropLast := rfl
@[deprecated toList_pop (since := "2025-02-17")]
abbrev pop_toList := @Array.toList_pop
@[simp] theorem append_eq_append {xs ys : Array α} : xs.append ys = xs ++ ys := rfl
@[simp] theorem append_eq_append (xs ys : Array α) : xs.append ys = xs ++ ys := rfl
@[simp] theorem toList_append {xs ys : Array α} :
@[simp] theorem toList_append (xs ys : Array α) :
(xs ++ ys).toList = xs.toList ++ ys.toList := by
rw [ append_eq_append]; unfold Array.append
rw [ foldl_toList]
@@ -110,24 +110,25 @@ abbrev pop_toList := @Array.toList_pop
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
@[simp] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
@[simp] theorem append_empty (xs : Array α) : xs ++ #[] = xs := by
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
@[deprecated append_empty (since := "2025-01-13")]
abbrev append_nil := @append_empty
@[simp] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
@[simp] theorem empty_append (xs : Array α) : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
@[deprecated empty_append (since := "2025-01-13")]
abbrev nil_append := @empty_append
@[simp] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
@[simp] theorem append_assoc (xs ys zs : Array α) : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
apply ext'; simp only [toList_append, List.append_assoc]
@[simp] theorem appendList_eq_append {xs : Array α} {l : List α} : xs.appendList l = xs ++ l := rfl
@[simp] theorem appendList_eq_append
(xs : Array α) (l : List α) : xs.appendList l = xs ++ l := rfl
@[simp] theorem toList_appendList {xs : Array α} {l : List α} :
@[simp] theorem toList_appendList (xs : Array α) (l : List α) :
(xs ++ l).toList = xs.toList ++ l := by
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing xs <;> simp [*]
@@ -137,23 +138,25 @@ abbrev appendList_toList := @toList_appendList
@[deprecated "Use the reverse direction of `foldrM_toList`." (since := "2024-11-13")]
theorem foldrM_eq_foldrM_toList [Monad m]
{f : α β m β} {init : β} {xs : Array α} :
(f : α β m β) (init : β) (xs : Array α) :
xs.foldrM f init = xs.toList.foldrM f init := by
simp
@[deprecated "Use the reverse direction of `foldlM_toList`." (since := "2024-11-13")]
theorem foldlM_eq_foldlM_toList [Monad m]
{f : β α m β} {init : β} {xs : Array α} :
(f : β α m β) (init : β) (xs : Array α) :
xs.foldlM f init = xs.toList.foldlM f init:= by
simp
@[deprecated "Use the reverse direction of `foldr_toList`." (since := "2024-11-13")]
theorem foldr_eq_foldr_toList {f : α β β} {init : β} {xs : Array α} :
theorem foldr_eq_foldr_toList
(f : α β β) (init : β) (xs : Array α) :
xs.foldr f init = xs.toList.foldr f init := by
simp
@[deprecated "Use the reverse direction of `foldl_toList`." (since := "2024-11-13")]
theorem foldl_eq_foldl_toList {f : β α β} {init : β} {xs : Array α} :
theorem foldl_eq_foldl_toList
(f : β α β) (init : β) (xs : Array α) :
xs.foldl f init = xs.toList.foldl f init:= by
simp

View File

@@ -21,42 +21,30 @@ open Nat
/-! ### countP -/
section countP
variable {p q : α Bool}
@[simp] theorem _root_.List.countP_toArray {l : List α} : countP p l.toArray = l.countP p := by
simp [countP]
induction l with
| nil => rfl
| cons hd tl ih =>
simp only [List.foldr_cons, ih, List.countP_cons]
split <;> simp_all
@[simp] theorem countP_toList {xs : Array α} : xs.toList.countP p = countP p xs := by
cases xs
simp
variable (p q : α Bool)
@[simp] theorem countP_empty : countP p #[] = 0 := rfl
@[simp] theorem countP_push_of_pos {xs : Array α} (pa : p a) : countP p (xs.push a) = countP p xs + 1 := by
@[simp] theorem countP_push_of_pos (xs) (pa : p a) : countP p (xs.push a) = countP p xs + 1 := by
rcases xs with xs
simp_all
@[simp] theorem countP_push_of_neg {xs : Array α} (pa : ¬p a) : countP p (xs.push a) = countP p xs := by
@[simp] theorem countP_push_of_neg (xs) (pa : ¬p a) : countP p (xs.push a) = countP p xs := by
rcases xs with xs
simp_all
theorem countP_push {a : α} {xs : Array α} : countP p (xs.push a) = countP p xs + if p a then 1 else 0 := by
theorem countP_push (a : α) (xs) : countP p (xs.push a) = countP p xs + if p a then 1 else 0 := by
rcases xs with xs
simp_all
@[simp] theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
@[simp] theorem countP_singleton (a : α) : countP p #[a] = if p a then 1 else 0 := by
simp [countP_push]
theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + countP (fun a => ¬p a) xs := by
theorem size_eq_countP_add_countP (xs) : xs.size = countP p xs + countP (fun a => ¬p a) xs := by
rcases xs with xs
simp [List.length_eq_countP_add_countP (p := p)]
theorem countP_eq_size_filter {xs : Array α} : countP p xs = (filter p xs).size := by
theorem countP_eq_size_filter (xs) : countP p xs = (filter p xs).size := by
rcases xs with xs
simp [List.countP_eq_length_filter]
@@ -68,7 +56,7 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
simp only [countP_eq_size_filter]
apply size_filter_le
@[simp] theorem countP_append {xs ys : Array α} : countP p (xs ++ ys) = countP p xs + countP p ys := by
@[simp] theorem countP_append (xs ys) : countP p (xs ++ ys) = countP p xs + countP p ys := by
rcases xs with xs
rcases ys with ys
simp
@@ -88,23 +76,21 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
rcases xs with xs
simp
theorem countP_replicate {a : α} {n : Nat} : countP p (replicate n a) = if p a then n else 0 := by
theorem countP_mkArray (p : α Bool) (a : α) (n : Nat) :
countP p (mkArray 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 {xs : Array α} {i : Nat} (h : i < xs.size) :
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
simp [List.boole_getElem_le_countP]
theorem countP_set {xs : Array α} {i : Nat} {a : α} (h : i < xs.size) :
theorem countP_set (p : α Bool) (xs : Array α) (i : Nat) (a : α) (h : i < xs.size) :
(xs.set i a).countP p = xs.countP p - (if p xs[i] then 1 else 0) + (if p a then 1 else 0) := by
rcases xs with xs
simp [List.countP_set, h]
theorem countP_filter {xs : Array α} :
theorem countP_filter (xs : Array α) :
countP p (filter q xs) = countP (fun a => p a && q a) xs := by
rcases xs with xs
simp [List.countP_filter]
@@ -117,35 +103,37 @@ theorem countP_filter {xs : Array α} :
funext xs
simp
@[simp] theorem countP_map {p : β Bool} {f : α β} {xs : Array α} :
@[simp] theorem countP_map (p : β Bool) (f : α β) (xs : Array α) :
countP p (map f xs) = countP (p f) xs := by
rcases xs with xs
simp
theorem size_filterMap_eq_countP {f : α Option β} {xs : Array α} :
theorem size_filterMap_eq_countP (f : α Option β) (xs : Array α) :
(filterMap f xs).size = countP (fun a => (f a).isSome) xs := by
rcases xs with xs
simp [List.length_filterMap_eq_countP]
theorem countP_filterMap {p : β Bool} {f : α Option β} {xs : Array α} :
theorem countP_filterMap (p : β Bool) (f : α Option β) (xs : Array α) :
countP p (filterMap f xs) = countP (fun a => ((f a).map p).getD false) xs := by
rcases xs with xs
simp [List.countP_filterMap]
@[simp] theorem countP_flatten {xss : Array (Array α)} :
@[simp] theorem countP_flatten (xss : Array (Array α)) :
countP p xss.flatten = (xss.map (countP p)).sum := by
cases xss using array₂_induction
simp [List.countP_flatten, Function.comp_def]
theorem countP_flatMap {p : β Bool} {xs : Array α} {f : α Array β} :
theorem countP_flatMap (p : β Bool) (xs : Array α) (f : α Array β) :
countP p (xs.flatMap f) = sum (map (countP p f) xs) := by
rcases xs with xs
simp [List.countP_flatMap, Function.comp_def]
@[simp] theorem countP_reverse {xs : Array α} : countP p xs.reverse = countP p xs := by
@[simp] theorem countP_reverse (xs : Array α) : countP p xs.reverse = countP p xs := by
rcases xs with xs
simp [List.countP_reverse]
variable {p q}
theorem countP_mono_left (h : x xs, p x q x) : countP p xs countP q xs := by
rcases xs with xs
simpa using List.countP_mono_left (by simpa using h)
@@ -162,62 +150,55 @@ section count
variable [BEq α]
@[simp] theorem _root_.List.count_toArray {l : List α} {a : α} : count a l.toArray = l.count a := by
simp [count, List.count_eq_countP]
@[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl
@[simp] theorem count_toList {xs : Array α} {a : α} : xs.toList.count a = xs.count a := by
cases xs
simp
@[simp] theorem count_empty {a : α} : count a #[] = 0 := rfl
theorem count_push {a b : α} {xs : Array α} :
theorem count_push (a b : α) (xs : Array α) :
count a (xs.push b) = count a xs + if b == a then 1 else 0 := by
simp [count, countP_push]
theorem count_eq_countP {a : α} {xs : Array α} : count a xs = countP (· == a) xs := rfl
theorem count_eq_countP (a : α) (xs : Array α) : count a xs = countP (· == a) xs := rfl
theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
funext xs
apply count_eq_countP
theorem count_le_size {a : α} {xs : Array α} : count a xs xs.size := countP_le_size
theorem count_le_size (a : α) (xs : Array α) : count a xs xs.size := countP_le_size _
theorem count_le_count_push {a b : α} {xs : Array α} : count a xs count a (xs.push b) := by
theorem count_le_count_push (a b : α) (xs : Array α) : count a xs count a (xs.push b) := by
simp [count_push]
theorem count_singleton {a b : α} : count a #[b] = if b == a then 1 else 0 := by
theorem count_singleton (a b : α) : count a #[b] = if b == a then 1 else 0 := by
simp [count_eq_countP]
@[simp] theorem count_append {a : α} {xs ys : Array α} : count a (xs ++ ys) = count a xs + count a ys :=
countP_append
@[simp] theorem count_append (a : α) : xs ys, count a (xs ++ ys) = count a xs + count a ys :=
countP_append _
@[simp] theorem count_flatten {a : α} {xss : Array (Array α)} :
@[simp] theorem count_flatten (a : α) (xss : Array (Array α)) :
count a xss.flatten = (xss.map (count a)).sum := by
cases xss using array₂_induction
simp [List.count_flatten, Function.comp_def]
@[simp] theorem count_reverse {a : α} {xs : Array α} : count a xs.reverse = count a xs := by
@[simp] theorem count_reverse (a : α) (xs : Array α) : count a xs.reverse = count a xs := by
rcases xs with xs
simp
theorem boole_getElem_le_count {xs : Array α} {i : Nat} {a : α} (h : i < xs.size) :
theorem boole_getElem_le_count (a : α) (xs : Array α) (i : Nat) (h : i < xs.size) :
(if xs[i] == a then 1 else 0) xs.count a := by
rw [count_eq_countP]
apply boole_getElem_le_countP (p := (· == a))
apply boole_getElem_le_countP (· == a)
theorem count_set {xs : Array α} {i : Nat} {a b : α} (h : i < xs.size) :
theorem count_set (a b : α) (xs : Array α) (i : Nat) (h : i < xs.size) :
(xs.set i a).count b = xs.count b - (if xs[i] == b then 1 else 0) + (if a == b then 1 else 0) := by
simp [count_eq_countP, countP_set, h]
variable [LawfulBEq α]
@[simp] theorem count_push_self {a : α} {xs : Array α} : count a (xs.push a) = count a xs + 1 := by
@[simp] theorem count_push_self (a : α) (xs : Array α) : count a (xs.push a) = count a xs + 1 := by
simp [count_push]
@[simp] theorem count_push_of_ne {xs : Array α} (h : b a) : count a (xs.push b) = count a xs := by
@[simp] theorem count_push_of_ne (h : b a) (xs : Array α) : count a (xs.push b) = count a xs := by
simp_all [count_push, h]
theorem count_singleton_self {a : α} : count a #[a] = 1 := by simp
theorem count_singleton_self (a : α) : count a #[a] = 1 := by simp
@[simp]
theorem count_pos_iff {a : α} {xs : Array α} : 0 < count a xs a xs := by
@@ -241,49 +222,40 @@ 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_replicate_self {a : α} {n : Nat} : count a (replicate n a) = n := by
@[simp] theorem count_mkArray_self (a : α) (n : Nat) : count a (mkArray n a) = n := by
simp [ List.toArray_replicate]
@[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
theorem count_mkArray (a b : α) (n : Nat) : count a (mkArray n b) = if b == a then n else 0 := by
simp [ List.toArray_replicate, List.count_replicate]
@[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
theorem filter_beq (xs : Array α) (a : α) : xs.filter (· == a) = mkArray (count a xs) a := by
rcases xs with xs
simp [List.filter_beq]
theorem filter_eq {α} [DecidableEq α] {xs : Array α} (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
filter_beq a
theorem filter_eq {α} [DecidableEq α] (xs : Array α) (a : α) : xs.filter (· = a) = mkArray (count a xs) a :=
filter_beq xs a
theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
replicate (count a xs) a = xs := by
theorem mkArray_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
mkArray (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]
theorem count_le_count_map [DecidableEq β] {xs : Array α} {f : α β} {x : α} :
theorem count_le_count_map [DecidableEq β] (xs : Array α) (f : α β) (x : α) :
count x xs count (f x) (map f xs) := by
rcases xs with xs
simp [List.count_le_count_map, countP_map]
theorem count_filterMap {α} [BEq β] {b : β} {f : α Option β} {xs : Array α} :
theorem count_filterMap {α} [BEq β] (b : β) (f : α Option β) (xs : Array α) :
count b (filterMap f xs) = countP (fun a => f a == some b) xs := by
rcases xs with xs
simp [List.count_filterMap, countP_filterMap]
theorem count_flatMap {α} [BEq β] {xs : Array α} {f : α Array β} {x : β} :
theorem count_flatMap {α} [BEq β] (xs : Array α) (f : α Array β) (x : β) :
count x (xs.flatMap f) = sum (map (count x f) xs) := by
rcases xs with xs
simp [List.count_flatMap, countP_flatMap, Function.comp_def]

View File

@@ -71,7 +71,7 @@ theorem isEqv_eq_decide (xs ys : Array α) (r) :
theorem eq_of_isEqv [DecidableEq α] (xs ys : Array α) (h : Array.isEqv xs ys (fun x y => x = y)) : xs = ys := by
have h, h' := rel_of_isEqv h
exact ext h (fun i lt _ => by simpa using h' i lt)
exact ext _ _ h (fun i lt _ => by simpa using h' i lt)
private theorem isEqvAux_self (r : α α Bool) (hr : a, r a a) (xs : Array α) (i : Nat) (h : i xs.size) :
Array.isEqvAux xs xs rfl r i h = true := by

View File

@@ -21,7 +21,7 @@ open Nat
/-! ### eraseP -/
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := by simp
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := rfl
theorem eraseP_of_forall_mem_not {xs : Array α} (h : a, a xs ¬p a) : xs.eraseP p = xs := by
rcases xs with xs
@@ -46,7 +46,6 @@ theorem exists_of_eraseP {xs : Array α} {a} (hm : a ∈ xs) (hp : p a) :
obtain a, l₁, l₂, h₁, h₂, rfl, h₃ := List.exists_of_eraseP (by simpa using hm) (hp)
refine a, l₁, l₂, by simpa using h₁, h₂, by simp, by simpa using h₃
-- The arguments are explicit here, so this lemma can be used as a case split.
theorem exists_or_eq_self_of_eraseP (p) (xs : Array α) :
xs.eraseP p = xs
a ys zs, ( b ys, ¬p b) p a xs = ys.push a ++ zs xs.eraseP p = ys ++ zs :=
@@ -70,13 +69,13 @@ theorem size_eraseP {xs : Array α} : (xs.eraseP p).size = if xs.any p then xs.s
rw [eraseP_of_forall_getElem_not]
simp_all
theorem size_eraseP_le {xs : Array α} : (xs.eraseP p).size xs.size := by
theorem size_eraseP_le (xs : Array α) : (xs.eraseP p).size xs.size := by
rcases xs with xs
simpa using List.length_eraseP_le
simpa using List.length_eraseP_le xs
theorem le_size_eraseP {xs : Array α} : xs.size - 1 (xs.eraseP p).size := by
theorem le_size_eraseP (xs : Array α) : xs.size - 1 (xs.eraseP p).size := by
rcases xs with xs
simpa using List.le_length_eraseP
simpa using List.le_length_eraseP xs
theorem mem_of_mem_eraseP {xs : Array α} : a xs.eraseP p a xs := by
rcases xs with xs
@@ -90,19 +89,19 @@ theorem mem_of_mem_eraseP {xs : Array α} : a ∈ xs.eraseP p → a ∈ xs := by
rcases xs with xs
simp
theorem eraseP_map {f : β α} {xs : Array β} : (xs.map f).eraseP p = (xs.eraseP (p f)).map f := by
theorem eraseP_map (f : β α) (xs : Array β) : (xs.map f).eraseP p = (xs.eraseP (p f)).map f := by
rcases xs with xs
simpa using List.eraseP_map
simpa using List.eraseP_map f xs
theorem eraseP_filterMap {f : α Option β} {xs : Array α} :
theorem eraseP_filterMap (f : α Option β) (xs : Array α) :
(filterMap f xs).eraseP p = filterMap f (xs.eraseP (fun x => match f x with | some y => p y | none => false)) := by
rcases xs with xs
simpa using List.eraseP_filterMap
simpa using List.eraseP_filterMap f xs
theorem eraseP_filter {f : α Bool} {xs : Array α} :
theorem eraseP_filter (f : α Bool) (xs : Array α) :
(filter f xs).eraseP p = filter f (xs.eraseP (fun x => p x && f x)) := by
rcases xs with xs
simpa using List.eraseP_filter
simpa using List.eraseP_filter f xs
theorem eraseP_append_left {a : α} (pa : p a) {xs : Array α} {ys : Array α} (h : a xs) :
(xs ++ ys).eraseP p = xs.eraseP p ++ ys := by
@@ -123,30 +122,21 @@ 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_replicate {n : Nat} {a : α} {p : α Bool} :
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
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
simp only [ List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
split <;> simp
@[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] theorem eraseP_mkArray_of_pos {n : Nat} {a : α} (h : p a) :
(mkArray n a).eraseP p = mkArray (n - 1) a := by
simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h]
@[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] theorem eraseP_mkArray_of_neg {n : Nat} {a : α} (h : ¬p a) :
(mkArray n a).eraseP p = mkArray 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)
@@ -176,12 +166,10 @@ theorem erase_of_not_mem [LawfulBEq α] {a : α} {xs : Array α} (h : a ∉ xs)
rcases xs with xs
simp [List.erase_of_not_mem (by simpa using h)]
-- The arguments are intentionally explicit.
theorem erase_eq_eraseP' (a : α) (xs : Array α) : xs.erase a = xs.eraseP (· == a) := by
rcases xs with xs
simp [List.erase_eq_eraseP']
-- The arguments are intentionally explicit.
theorem erase_eq_eraseP [LawfulBEq α] (a : α) (xs : Array α) : xs.erase a = xs.eraseP (a == ·) := by
rcases xs with xs
simp [List.erase_eq_eraseP]
@@ -205,19 +193,19 @@ theorem exists_erase_eq [LawfulBEq α] {a : α} {xs : Array α} (h : a ∈ xs) :
(xs.erase a).size = xs.size - 1 := by
rw [erase_eq_eraseP]; exact size_eraseP_of_mem h (beq_self_eq_true a)
theorem size_erase [LawfulBEq α] {a : α} {xs : Array α} :
theorem size_erase [LawfulBEq α] (a : α) (xs : Array α) :
(xs.erase a).size = if a xs then xs.size - 1 else xs.size := by
rw [erase_eq_eraseP, size_eraseP]
congr
simp [mem_iff_getElem, eq_comm (a := a)]
theorem size_erase_le {a : α} {xs : Array α} : (xs.erase a).size xs.size := by
theorem size_erase_le (a : α) (xs : Array α) : (xs.erase a).size xs.size := by
rcases xs with xs
simpa using List.length_erase_le
simpa using List.length_erase_le a xs
theorem le_size_erase [LawfulBEq α] {a : α} {xs : Array α} : xs.size - 1 (xs.erase a).size := by
theorem le_size_erase [LawfulBEq α] (a : α) (xs : Array α) : xs.size - 1 (xs.erase a).size := by
rcases xs with xs
simpa using List.le_length_erase
simpa using List.le_length_erase a xs
theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a xs.erase b) : a xs := by
rcases xs with xs
@@ -231,10 +219,10 @@ theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a ∈ xs.erase b) : a
rw [erase_eq_eraseP', eraseP_eq_self_iff]
simp [forall_mem_ne']
theorem erase_filter [LawfulBEq α] {f : α Bool} {xs : Array α} :
theorem erase_filter [LawfulBEq α] (f : α Bool) (xs : Array α) :
(filter f xs).erase a = filter f (xs.erase a) := by
rcases xs with xs
simpa using List.erase_filter
simpa using List.erase_filter f xs
theorem erase_append_left [LawfulBEq α] {xs : Array α} (ys) (h : a xs) :
(xs ++ ys).erase a = xs.erase a ++ ys := by
@@ -255,21 +243,16 @@ 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_replicate [LawfulBEq α] {n : Nat} {a b : α} :
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
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
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
-- The arguments `a b` are explicit,
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
theorem erase_comm [LawfulBEq α] (a b : α) {xs : Array α} :
theorem erase_comm [LawfulBEq α] (a b : α) (xs : Array α) :
(xs.erase a).erase b = (xs.erase b).erase a := by
rcases xs with xs
simpa using List.erase_comm a b
simpa using List.erase_comm a b xs
theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
xs.erase a = ys
@@ -285,32 +268,21 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
· left; simp_all
· right; refine a, as, h, rfl, bs, by simp
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
(replicate n a).erase a = replicate (n - 1) a := by
@[simp] theorem erase_mkArray_self [LawfulBEq α] {a : α} :
(mkArray n a).erase a = mkArray (n - 1) a := by
simp only [ List.toArray_replicate, List.erase_toArray]
simp [List.erase_replicate]
@[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
@[simp] theorem erase_mkArray_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(mkArray n a).erase b = mkArray 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 -/
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
simp [eraseIdxIfInBounds, h]
theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
xs.eraseIdx i h = xs.take i ++ xs.drop (i + 1) := by
theorem eraseIdx_eq_take_drop_succ (xs : Array α) (i : Nat) (h) : xs.eraseIdx i = xs.take i ++ xs.drop (i + 1) := by
rcases xs with xs
simp only [List.size_toArray] at h
simp only [List.eraseIdx_toArray, List.eraseIdx_eq_take_drop_succ, take_eq_extract,
@@ -319,24 +291,24 @@ theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
rw [List.take_of_length_le]
simp
theorem getElem?_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} :
theorem getElem?_eraseIdx (xs : Array α) (i : Nat) (h : i < xs.size) (j : Nat) :
(xs.eraseIdx i)[j]? = if j < i then xs[j]? else xs[j + 1]? := by
rcases xs with xs
simp [List.getElem?_eraseIdx]
theorem getElem?_eraseIdx_of_lt {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : j < i) :
theorem getElem?_eraseIdx_of_lt (xs : Array α) (i : Nat) (h : i < xs.size) (j : Nat) (h' : j < i) :
(xs.eraseIdx i)[j]? = xs[j]? := by
rw [getElem?_eraseIdx]
simp [h']
theorem getElem?_eraseIdx_of_ge {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : i j) :
theorem getElem?_eraseIdx_of_ge (xs : Array α) (i : Nat) (h : i < xs.size) (j : Nat) (h' : i j) :
(xs.eraseIdx i)[j]? = xs[j + 1]? := by
rw [getElem?_eraseIdx]
simp only [dite_eq_ite, ite_eq_right_iff]
intro h'
omega
theorem getElem_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : j < (xs.eraseIdx i).size) :
theorem getElem_eraseIdx (xs : Array α) (i : Nat) (h : i < xs.size) (j : Nat) (h' : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i)[j] = if h'' : j < i then
xs[j]
else
@@ -377,15 +349,12 @@ 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_replicate {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by
theorem eraseIdx_mkArray {n : Nat} {a : α} {k : Nat} {h} :
(mkArray n a).eraseIdx k = mkArray (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, *]
@@ -394,18 +363,18 @@ theorem mem_eraseIdx_iff_getElem? {x : α} {xs : Array α} {k} {h} : x ∈ xs.er
rcases xs with xs
simp [List.mem_eraseIdx_iff_getElem?, *]
theorem erase_eq_eraseIdx_of_idxOf [BEq α] [LawfulBEq α] {xs : Array α} {a : α} {i : Nat} (w : xs.idxOf a = i) (h : i < xs.size) :
theorem erase_eq_eraseIdx_of_idxOf [BEq α] [LawfulBEq α] (xs : Array α) (a : α) (i : Nat) (w : xs.idxOf a = i) (h : i < xs.size) :
xs.erase a = xs.eraseIdx i := by
rcases xs with xs
simp at w
simp [List.erase_eq_eraseIdx_of_idxOf, *]
theorem getElem_eraseIdx_of_lt {xs : Array α} {i : Nat} (w : i < xs.size) {j : Nat} (h : j < (xs.eraseIdx i).size) (h' : j < i) :
theorem getElem_eraseIdx_of_lt (xs : Array α) (i : Nat) (w : i < xs.size) (j : Nat) (h : j < (xs.eraseIdx i).size) (h' : j < i) :
(xs.eraseIdx i)[j] = xs[j] := by
rcases xs with xs
simp [List.getElem_eraseIdx_of_lt, *]
theorem getElem_eraseIdx_of_ge {xs : Array α} {i : Nat} (w : i < xs.size) {j : Nat} (h : j < (xs.eraseIdx i).size) (h' : i j) :
theorem getElem_eraseIdx_of_ge (xs : Array α) (i : Nat) (w : i < xs.size) (j : Nat) (h : j < (xs.eraseIdx i).size) (h' : i j) :
(xs.eraseIdx i)[j] = xs[j + 1]'(by simp at h; omega) := by
rcases xs with xs
simp [List.getElem_eraseIdx_of_ge, *]

View File

@@ -249,15 +249,12 @@ 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_replicate {a : α} {n i j : Nat} :
(replicate n a).extract i j = replicate (min j n - i) a := by
@[simp] theorem extract_mkArray {a : α} {n i j : Nat} :
(mkArray n a).extract i j = mkArray (min j n - i) a := by
ext l h₁ h₂
· simp
· 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
· simp only [size_extract, size_mkArray] at h₁ h₂
simp only [getElem_extract, getElem_mkArray]
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
@@ -322,32 +319,32 @@ theorem reverse_extract {as : Array α} {i j : Nat} :
/-! ### takeWhile -/
theorem takeWhile_map {f : α β} {p : β Bool} {as : Array α} :
theorem takeWhile_map (f : α β) (p : β Bool) (as : Array α) :
(as.map f).takeWhile p = (as.takeWhile (p f)).map f := by
rcases as with as
simp [List.takeWhile_map]
theorem popWhile_map {f : α β} {p : β Bool} {as : Array α} :
theorem popWhile_map (f : α β) (p : β Bool) (as : Array α) :
(as.map f).popWhile p = (as.popWhile (p f)).map f := by
rcases as with as
simp [List.dropWhile_map, List.map_reverse]
theorem takeWhile_filterMap {f : α Option β} {p : β Bool} {as : Array α} :
theorem takeWhile_filterMap (f : α Option β) (p : β Bool) (as : Array α) :
(as.filterMap f).takeWhile p = (as.takeWhile fun a => (f a).all p).filterMap f := by
rcases as with as
simp [List.takeWhile_filterMap]
theorem popWhile_filterMap {f : α Option β} {p : β Bool} {as : Array α} :
theorem popWhile_filterMap (f : α Option β) (p : β Bool) (as : Array α) :
(as.filterMap f).popWhile p = (as.popWhile fun a => (f a).all p).filterMap f := by
rcases as with as
simp [List.dropWhile_filterMap, List.filterMap_reverse]
theorem takeWhile_filter {p q : α Bool} {as : Array α} :
theorem takeWhile_filter (p q : α Bool) (as : Array α) :
(as.filter p).takeWhile q = (as.takeWhile fun a => !p a || q a).filter p := by
rcases as with as
simp [List.takeWhile_filter]
theorem popWhile_filter {p q : α Bool} {as : Array α} :
theorem popWhile_filter (p q : α Bool) (as : Array α) :
(as.filter p).popWhile q = (as.popWhile fun a => !p a || q a).filter p := by
rcases as with as
simp [List.dropWhile_filter, List.filter_reverse]
@@ -390,36 +387,24 @@ theorem popWhile_append {xs ys : Array α} :
rw [List.dropWhile_append_of_pos]
simpa
@[simp] theorem takeWhile_replicate_eq_filter {p : α Bool} :
(replicate n a).takeWhile p = (replicate n a).filter p := by
@[simp] theorem takeWhile_mkArray_eq_filter (p : α Bool) :
(mkArray n a).takeWhile p = (mkArray n a).filter p := by
simp [ List.toArray_replicate]
@[deprecated takeWhile_replicate_eq_filter (since := "2025-03-18")]
abbrev takeWhile_mkArray_eq_filter := @takeWhile_replicate_eq_filter
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]
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] theorem popWhile_mkArray_eq_filter_not (p : α Bool) :
(mkArray n a).popWhile p = (mkArray n a).filter (fun a => !p a) := by
simp [ List.toArray_replicate, List.filter_reverse]
@[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,
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,
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

View File

@@ -12,32 +12,26 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/--
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))`
-/
/-- `finRange n` is the array of all elements of `Fin n` in order. -/
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
@[simp] theorem size_finRange {n} : (Array.finRange n).size = n := by
@[simp] theorem size_finRange (n) : (Array.finRange n).size = n := by
simp [Array.finRange]
@[simp] theorem getElem_finRange {i : Nat} (h : i < (Array.finRange n).size) :
(Array.finRange n)[i] = Fin.cast size_finRange i, h := by
@[simp] theorem getElem_finRange (i : Nat) (h : i < (Array.finRange n).size) :
(Array.finRange n)[i] = Fin.cast (size_finRange n) i, h := by
simp [Array.finRange]
@[simp] theorem finRange_zero : Array.finRange 0 = #[] := by simp [Array.finRange]
theorem finRange_succ {n} : Array.finRange (n+1) = #[0] ++ (Array.finRange n).map Fin.succ := by
theorem finRange_succ (n) : Array.finRange (n+1) = #[0] ++ (Array.finRange n).map Fin.succ := by
ext
· simp [Nat.add_comm]
· simp [getElem_append]
split <;>
· simp; omega
theorem finRange_succ_last {n} :
theorem finRange_succ_last (n) :
Array.finRange (n+1) = (Array.finRange n).map Fin.castSucc ++ #[Fin.last n] := by
ext
· simp
@@ -47,7 +41,7 @@ theorem finRange_succ_last {n} :
· simp_all
omega
theorem finRange_reverse {n} : (Array.finRange n).reverse = (Array.finRange n).map Fin.rev := by
theorem finRange_reverse (n) : (Array.finRange n).reverse = (Array.finRange n).map Fin.rev := by
ext i h
· simp
· simp

View File

@@ -21,10 +21,10 @@ open Nat
/-! ### findSome? -/
@[simp] theorem findSomeRev?_push_of_isSome {xs : Array α} (h : (f a).isSome) : (xs.push a).findSomeRev? f = f a := by
@[simp] theorem findSomeRev?_push_of_isSome (xs : Array α) (h : (f a).isSome) : (xs.push a).findSomeRev? f = f a := by
cases xs; simp_all
@[simp] theorem findSomeRev?_push_of_isNone {xs : Array α} (h : (f a).isNone) : (xs.push a).findSomeRev? f = xs.findSomeRev? f := by
@[simp] theorem findSomeRev?_push_of_isNone (xs : Array α) (h : (f a).isNone) : (xs.push a).findSomeRev? f = xs.findSomeRev? f := by
cases xs; simp_all
theorem exists_of_findSome?_eq_some {f : α Option β} {xs : Array α} (w : xs.findSome? f = some b) :
@@ -48,31 +48,31 @@ theorem findSome?_eq_some_iff {f : α → Option β} {xs : Array α} {b : β} :
· rintro xs, a, ys, h₀, h₁, h₂
exact xs.toList, a, ys.toList, by simpa using congrArg toList h₀, h₁, by simpa
@[simp] theorem findSome?_guard {xs : Array α} : findSome? (Option.guard fun x => p x) xs = find? p xs := by
@[simp] theorem findSome?_guard (xs : Array α) : findSome? (Option.guard fun x => p x) xs = find? p xs := by
cases xs; simp
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard fun x => p x) xs :=
findSome?_guard.symm
theorem find?_eq_findSome?_guard (xs : Array α) : find? p xs = findSome? (Option.guard fun x => p x) xs :=
(findSome?_guard xs).symm
@[simp] theorem getElem?_zero_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f)[0]? = xs.findSome? f := by
@[simp] theorem getElem?_zero_filterMap (f : α Option β) (xs : Array α) : (xs.filterMap f)[0]? = xs.findSome? f := by
cases xs; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filterMap {f : α Option β} {xs : Array α} (h) :
@[simp] theorem getElem_zero_filterMap (f : α Option β) (xs : Array α) (h) :
(xs.filterMap f)[0] = (xs.findSome? f).get (by cases xs; simpa [List.length_filterMap_eq_countP] using h) := by
cases xs; simp [ List.head_eq_getElem, getElem?_zero_filterMap]
@[simp] theorem back?_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
@[simp] theorem back?_filterMap (f : α Option β) (xs : Array α) : (xs.filterMap f).back? = xs.findSomeRev? f := by
cases xs; simp
@[simp] theorem back!_filterMap [Inhabited β] {f : α Option β} {xs : Array α} :
@[simp] theorem back!_filterMap [Inhabited β] (f : α Option β) (xs : Array α) :
(xs.filterMap f).back! = (xs.findSomeRev? f).getD default := by
cases xs; simp
@[simp] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Array α} :
@[simp] theorem map_findSome? (f : α Option β) (g : β γ) (xs : Array α) :
(xs.findSome? f).map g = xs.findSome? (Option.map g f) := by
cases xs; simp
theorem findSome?_map {f : β γ} {xs : Array β} : findSome? p (xs.map f) = xs.findSome? (p f) := by
theorem findSome?_map (f : β γ) (xs : Array β) : findSome? p (xs.map f) = xs.findSome? (p f) := by
cases xs; simp [List.findSome?_map]
theorem findSome?_append {xs ys : Array α} : (xs ++ ys).findSome? f = (xs.findSome? f).or (ys.findSome? f) := by
@@ -99,45 +99,33 @@ theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
simp [getElem?_eq_getElem, h] at t
simp [ t]
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
theorem findSome?_mkArray : findSome? f (mkArray n a) = if n = 0 then none else f a := by
simp [ List.toArray_replicate, List.findSome?_replicate]
@[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
@[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]
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) :
findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate]
@[simp] theorem findSome?_mkArray_of_isSome (_ : (f a).isSome) :
findSome? f (mkArray n a) = if n = 0 then none else f a := by
simp [findSome?_mkArray]
@[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
@[simp] theorem findSome?_mkArray_of_isNone (h : (f a).isNone) :
findSome? f (mkArray n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_replicate, h]
@[deprecated findSome?_replicate_of_isNone (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
simp [findSome?_mkArray, h]
/-! ### find? -/
@[simp] theorem find?_singleton {a : α} {p : α Bool} :
@[simp] theorem find?_singleton (a : α) (p : α Bool) :
#[a].find? p = if p a then some a else none := by
simp [singleton_eq_toArray_singleton]
@[simp] theorem findRev?_push_of_pos {xs : Array α} (h : p a) :
@[simp] theorem findRev?_push_of_pos (xs : Array α) (h : p a) :
findRev? p (xs.push a) = some a := by
cases xs; simp [h]
@[simp] theorem findRev?_cons_of_neg {xs : Array α} (h : ¬p a) :
@[simp] theorem findRev?_cons_of_neg (xs : Array α) (h : ¬p a) :
findRev? p (xs.push a) = findRev? p xs := by
cases xs; simp [h]
@@ -183,28 +171,28 @@ theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h ∈ xs := by
(xs.filter p).find? q = xs.find? (fun a => p a q a) := by
cases xs; simp
@[simp] theorem getElem?_zero_filter {p : α Bool} {xs : Array α} :
@[simp] theorem getElem?_zero_filter (p : α Bool) (xs : Array α) :
(xs.filter p)[0]? = xs.find? p := by
cases xs; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filter {p : α Bool} {xs : Array α} (h) :
@[simp] theorem getElem_zero_filter (p : α Bool) (xs : Array α) (h) :
(xs.filter p)[0] =
(xs.find? p).get (by cases xs; simpa [ List.countP_eq_length_filter] using h) := by
cases xs
simp [List.getElem_zero_eq_head]
@[simp] theorem back?_filter {p : α Bool} {xs : Array α} : (xs.filter p).back? = xs.findRev? p := by
@[simp] theorem back?_filter (p : α Bool) (xs : Array α) : (xs.filter p).back? = xs.findRev? p := by
cases xs; simp
@[simp] theorem back!_filter [Inhabited α] {p : α Bool} {xs : Array α} :
@[simp] theorem back!_filter [Inhabited α] (p : α Bool) (xs : Array α) :
(xs.filter p).back! = (xs.findRev? p).get! := by
cases xs; simp [Option.get!_eq_getD]
@[simp] theorem find?_filterMap {xs : Array α} {f : α Option β} {p : β Bool} :
@[simp] theorem find?_filterMap (xs : Array α) (f : α Option β) (p : β Bool) :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
cases xs; simp
@[simp] theorem find?_map {f : β α} {xs : Array β} :
@[simp] theorem find?_map (f : β α) (xs : Array β) :
find? p (xs.map f) = (xs.find? (p f)).map f := by
cases xs; simp
@@ -214,7 +202,7 @@ theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h ∈ xs := by
cases ys
simp
@[simp] theorem find?_flatten {xss : Array (Array α)} {p : α Bool} :
@[simp] theorem find?_flatten (xss : Array (Array α)) (p : α Bool) :
xss.flatten.find? p = xss.findSome? (·.find? p) := by
cases xss using array₂_induction
simp [List.findSome?_map, Function.comp_def]
@@ -254,7 +242,7 @@ theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
@[simp] theorem find?_flatMap {xs : Array α} {f : α Array β} {p : β Bool} :
@[simp] theorem find?_flatMap (xs : Array α) (f : α Array β) (p : β Bool) :
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
cases xs
simp [List.find?_flatMap, Array.flatMap_toArray]
@@ -266,60 +254,42 @@ 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?_replicate :
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
theorem find?_mkArray :
find? p (mkArray n a) = if n = 0 then none else if p a then some a else none := by
simp [ List.toArray_replicate, List.find?_replicate]
@[deprecated find?_replicate (since := "2025-03-18")]
abbrev find?_mkArray := @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]
@[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_pos (h : p a) :
find? p (mkArray n a) = if n = 0 then none else some a := 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
@[simp] theorem find?_mkArray_of_neg (h : ¬ p a) : find? p (mkArray n a) = none := by
simp [find?_mkArray, h]
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α Bool} :
(replicate n a).find? p = none n = 0 !p a := by
theorem find?_mkArray_eq_none_iff {n : Nat} {a : α} {p : α Bool} :
(mkArray 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?_replicate_eq_none_iff (since := "2025-03-18")]
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
@[deprecated find?_mkArray_eq_none_iff (since := "2025-02-03")]
abbrev find?_mkArray_eq_none := @find?_mkArray_eq_none_iff
@[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] 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 [ List.toArray_replicate]
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")]
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff
@[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-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] theorem get_find?_mkArray (n : Nat) (a : α) (p : α Bool) (h) :
((mkArray 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} :
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
simp only [pmap_eq_map_attach, find?_map]
rfl
@@ -359,7 +329,7 @@ theorem findIdx_eq_size_of_false {p : α → Bool} {xs : Array α} (h : ∀ x
rcases xs with xs
simp_all
theorem findIdx_le_size {p : α Bool} {xs : Array α} : xs.findIdx p xs.size := by
theorem findIdx_le_size (p : α Bool) {xs : Array α} : xs.findIdx p xs.size := by
by_cases e : x xs, p x
· exact Nat.le_of_lt (findIdx_lt_size_of_exists e)
· simp at e
@@ -373,7 +343,7 @@ theorem findIdx_lt_size {p : α → Bool} {xs : Array α} :
/-- `p` does not hold for elements with indices less than `xs.findIdx p`. -/
theorem not_of_lt_findIdx {p : α Bool} {xs : Array α} {i : Nat} (h : i < xs.findIdx p) :
p (xs[i]'(Nat.le_trans h findIdx_le_size)) = false := by
p (xs[i]'(Nat.le_trans h (findIdx_le_size p))) = false := by
rcases xs with xs
simpa using List.not_of_lt_findIdx (by simpa using h)
@@ -404,7 +374,7 @@ theorem findIdx_eq {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
simp at h3
simp_all [not_of_lt_findIdx h3]
theorem findIdx_append {p : α Bool} {xs ys : Array α} :
theorem findIdx_append (p : α Bool) (xs ys : Array α) :
(xs ++ ys).findIdx p =
if xs.findIdx p < xs.size then xs.findIdx p else ys.findIdx p + xs.size := by
rcases xs with xs
@@ -438,7 +408,7 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := rfl
@[simp]
theorem findIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
@@ -492,8 +462,7 @@ theorem of_findIdx?_eq_none {xs : Array α} {p : α → Bool} (w : xs.findIdx? p
rcases xs with xs
simpa using List.of_findIdx?_eq_none (by simpa using w)
@[simp] theorem findIdx?_map {f : β α} {xs : Array β} {p : α Bool} :
findIdx? p (xs.map f) = xs.findIdx? (p f) := by
@[simp] theorem findIdx?_map (f : β α) (xs : Array β) : findIdx? p (xs.map f) = xs.findIdx? (p f) := by
rcases xs with xs
simp [List.findIdx?_map]
@@ -512,15 +481,12 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
cases xss using array₂_induction
simp [List.findIdx?_flatten, Function.comp_def]
@[simp] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by
@[simp] theorem findIdx?_mkArray :
(mkArray 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
@@ -560,7 +526,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 := by simp
@[simp] theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := rfl
-- 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) :
@@ -629,7 +595,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 := by simp
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := rfl
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf? a = none a xs := by
@@ -646,7 +612,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 := by simp
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := rfl
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.finIdxOf? a = none a xs := by

View File

@@ -23,7 +23,7 @@ theorem extLit {n : Nat}
(xs ys : Array α)
(hsz₁ : xs.size = n) (hsz₂ : ys.size = n)
(h : (i : Nat) (hi : i < n) xs.getLit i hsz₁ hi = ys.getLit i hsz₂ hi) : xs = ys :=
Array.ext (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ hi₁)
Array.ext xs ys (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ hi₁)
def toListLitAux (xs : Array α) (n : Nat) (hsz : xs.size = n) : (i : Nat), i xs.size List α List α
| 0, _, acc => acc

View File

@@ -30,13 +30,13 @@ section InsertIdx
variable {a : α}
@[simp] theorem toList_insertIdx {xs : Array α} {i : Nat} {x : α} (h : i xs.size) :
@[simp] theorem toList_insertIdx (xs : Array α) (i x) (h) :
(xs.insertIdx i x h).toList = xs.toList.insertIdx i x := by
rcases xs with xs
simp
@[simp]
theorem insertIdx_zero {xs : Array α} {x : α} : xs.insertIdx 0 x = #[x] ++ xs := by
theorem insertIdx_zero (xs : Array α) (x : α) : xs.insertIdx 0 x = #[x] ++ xs := by
rcases xs with xs
simp
@@ -44,7 +44,7 @@ theorem insertIdx_zero {xs : Array α} {x : α} : xs.insertIdx 0 x = #[x] ++ xs
rcases xs with xs
simp [List.length_insertIdx, h]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
theorem eraseIdx_insertIdx (i : Nat) (xs : Array α) (h : i xs.size) :
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
rcases xs with xs
simp_all
@@ -54,27 +54,27 @@ theorem insertIdx_eraseIdx_of_ge {as : Array α}
(as.eraseIdx i).insertIdx j a =
(as.insertIdx (j + 1) a (by simp at w₂; omega)).eraseIdx i (by simp_all; omega) := by
cases as
simpa using List.insertIdx_eraseIdx_of_ge (by simpa) (by simpa)
simpa using List.insertIdx_eraseIdx_of_ge _ _ _ (by simpa) (by simpa)
theorem insertIdx_eraseIdx_of_le {as : Array α}
(w₁ : i < as.size) (w₂ : j (as.eraseIdx i).size) (h : j i) :
(as.eraseIdx i).insertIdx j a =
(as.insertIdx j a (by simp at w₂; omega)).eraseIdx (i + 1) (by simp_all) := by
cases as
simpa using List.insertIdx_eraseIdx_of_le (by simpa) (by simpa)
simpa using List.insertIdx_eraseIdx_of_le _ _ _ (by simpa) (by simpa)
theorem insertIdx_comm (a b : α) {i j : Nat} {xs : Array α} (_ : i j) (_ : j xs.size) :
theorem insertIdx_comm (a b : α) (i j : Nat) (xs : Array α) (_ : i j) (_ : j xs.size) :
(xs.insertIdx i a).insertIdx (j + 1) b (by simpa) =
(xs.insertIdx j b).insertIdx i a (by simp; omega) := by
rcases xs with xs
simpa using List.insertIdx_comm a b (by simpa) (by simpa)
simpa using List.insertIdx_comm a b i j _ (by simpa) (by simpa)
theorem mem_insertIdx {xs : Array α} {h : i xs.size} : a xs.insertIdx i b h a = b a xs := by
rcases xs with xs
simpa using List.mem_insertIdx (by simpa)
@[simp]
theorem insertIdx_size_self {xs : Array α} {x : α} : xs.insertIdx xs.size x = xs.push x := by
theorem insertIdx_size_self (xs : Array α) (x : α) : xs.insertIdx xs.size x = xs.push x := by
rcases xs with xs
simp

View File

@@ -9,12 +9,6 @@ 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

File diff suppressed because it is too large Load Diff

View File

@@ -14,12 +14,11 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/--
Compares arrays lexicographically with respect to a comparison `lt` on their elements.
Lexicographic comparator for arrays.
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]`.
`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

View File

@@ -14,18 +14,18 @@ namespace Array
/-! ### Lexicographic ordering -/
@[simp] theorem _root_.List.lt_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray < l₂.toArray l₁ < l₂ := Iff.rfl
@[simp] theorem _root_.List.le_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray l₂.toArray l₁ l₂ := Iff.rfl
@[simp] theorem _root_.List.lt_toArray [LT α] (l₁ l₂ : List α) : l₁.toArray < l₂.toArray l₁ < l₂ := Iff.rfl
@[simp] theorem _root_.List.le_toArray [LT α] (l₁ l₂ : List α) : l₁.toArray l₂.toArray l₁ l₂ := Iff.rfl
@[simp] theorem lt_toList [LT α] {xs ys : Array α} : xs.toList < ys.toList xs < ys := Iff.rfl
@[simp] theorem le_toList [LT α] {xs ys : Array α} : xs.toList ys.toList xs ys := Iff.rfl
@[simp] theorem lt_toList [LT α] (xs ys : Array α) : xs.toList < ys.toList xs < ys := Iff.rfl
@[simp] theorem le_toList [LT α] (xs ys : Array α) : xs.toList ys.toList xs ys := Iff.rfl
protected theorem not_lt_iff_ge [LT α] {l₁ l₂ : List α} : ¬ l₁ < l₂ l₂ l₁ := Iff.rfl
protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] {l₁ l₂ : List α} :
protected theorem not_lt_iff_ge [LT α] (l₁ l₂ : List α) : ¬ l₁ < l₂ l₂ l₁ := Iff.rfl
protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) :
¬ l₁ l₂ l₂ < l₁ :=
Decidable.not_not
@[simp] theorem lex_empty [BEq α] {lt : α α Bool} {xs : Array α} : xs.lex #[] lt = false := by
@[simp] theorem lex_empty [BEq α] {lt : α α Bool} (xs : Array α) : xs.lex #[] lt = false := by
simp [lex, Id.run]
@[simp] theorem singleton_lex_singleton [BEq α] {lt : α α Bool} : #[a].lex #[b] lt = lt a b := by
@@ -45,7 +45,7 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
cases a == b <;> simp
· simp
@[simp] theorem _root_.List.lex_toArray [BEq α] {lt : α α Bool} {l₁ l₂ : List α} :
@[simp] theorem _root_.List.lex_toArray [BEq α] (lt : α α Bool) (l₁ l₂ : List α) :
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by
induction l₁ generalizing l₂ with
| nil => cases l₂ <;> simp [lex, Id.run]
@@ -55,7 +55,7 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
| cons y l₂ =>
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
@[simp] theorem lex_toList [BEq α] {lt : α α Bool} {xs ys : Array α} :
@[simp] theorem lex_toList [BEq α] (lt : α α Bool) (xs ys : Array α) :
xs.toList.lex ys.toList lt = xs.lex ys lt := by
cases xs <;> cases ys <;> simp
@@ -68,7 +68,7 @@ instance ltIrrefl [LT α] [Std.Irrefl (· < · : αα → Prop)] : Std.Irre
@[simp] theorem not_lt_empty [LT α] (xs : Array α) : ¬ xs < #[] := List.not_lt_nil xs.toList
@[simp] theorem empty_le [LT α] (xs : Array α) : #[] xs := List.nil_le xs.toList
@[simp] theorem le_empty [LT α] {xs : Array α} : xs #[] xs = #[] := by
@[simp] theorem le_empty [LT α] (xs : Array α) : xs #[] xs = #[] := by
cases xs
simp

View File

@@ -6,7 +6,6 @@ Authors: Mario Carneiro, Kim Morrison
prelude
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
import Init.Data.Array.OfFn
import Init.Data.List.MapIdx
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
@@ -42,66 +41,66 @@ theorem mapFinIdx_induction (xs : Array α) (f : (i : Nat) → α → (h : i < x
· exact (hs j (by omega) hm).2
simp [mapFinIdx, mapFinIdxM]; exact go rfl nofun h0
theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β}
{p : (i : Nat) β (h : i < xs.size) Prop} (hs : i h, p i (f i xs[i] h) h) :
theorem mapFinIdx_spec (xs : Array α) (f : (i : Nat) α (h : i < xs.size) β)
(p : (i : Nat) β (h : i < xs.size) Prop) (hs : i h, p i (f i xs[i] h) h) :
eq : (Array.mapFinIdx xs f).size = xs.size,
i h, p i ((Array.mapFinIdx xs f)[i]) h :=
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => hs .., trivial).2
@[simp] theorem size_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp] theorem size_mapFinIdx (xs : Array α) (f : (i : Nat) α (h : i < xs.size) β) :
(xs.mapFinIdx f).size = xs.size :=
(mapFinIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
@[simp] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
Array.size_mapFinIdx
@[simp] theorem size_zipIdx (xs : Array α) (k : Nat) : (xs.zipIdx k).size = xs.size :=
Array.size_mapFinIdx _ _
@[deprecated size_zipIdx (since := "2025-01-21")] abbrev size_zipWithIndex := @size_zipIdx
@[simp] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
@[simp] theorem getElem_mapFinIdx (xs : Array α) (f : (i : Nat) α (h : i < xs.size) β) (i : Nat)
(h : i < (xs.mapFinIdx f).size) :
(xs.mapFinIdx f)[i] = f i (xs[i]'(by simp_all)) (by simp_all) :=
(mapFinIdx_spec (p := fun i b h => b = f i xs[i] h) fun _ _ => rfl).2 i _
(mapFinIdx_spec _ _ (fun i b h => b = f i xs[i] h) fun _ _ => rfl).2 i _
@[simp] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat} :
@[simp] theorem getElem?_mapFinIdx (xs : Array α) (f : (i : Nat) α (h : i < xs.size) β) (i : Nat) :
(xs.mapFinIdx f)[i]? =
xs[i]?.pbind fun b h => f i b (getElem?_eq_some_iff.1 h).1 := by
simp only [getElem?_def, size_mapFinIdx, getElem_mapFinIdx]
split <;> simp_all
@[simp] theorem toList_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp] theorem toList_mapFinIdx (xs : Array α) (f : (i : Nat) α (h : i < xs.size) β) :
(xs.mapFinIdx f).toList = xs.toList.mapFinIdx (fun i a h => f i a (by simpa)) := by
apply List.ext_getElem <;> simp
/-! ### mapIdx -/
theorem mapIdx_induction {f : Nat α β} {xs : Array α}
{motive : Nat Prop} (h0 : motive 0)
{p : (i : Nat) β (h : i < xs.size) Prop}
theorem mapIdx_induction (f : Nat α β) (xs : Array α)
(motive : Nat Prop) (h0 : motive 0)
(p : (i : Nat) β (h : i < xs.size) Prop)
(hs : i h, motive i p i (f i xs[i]) h motive (i + 1)) :
motive xs.size eq : (xs.mapIdx f).size = xs.size,
i h, p i ((xs.mapIdx f)[i]) h :=
mapFinIdx_induction xs (fun i a _ => f i a) motive h0 p hs
theorem mapIdx_spec {f : Nat α β} {xs : Array α}
{p : (i : Nat) β (h : i < xs.size) Prop} (hs : i h, p i (f i xs[i]) h) :
theorem mapIdx_spec (f : Nat α β) (xs : Array α)
(p : (i : Nat) β (h : i < xs.size) Prop) (hs : i h, p i (f i xs[i]) h) :
eq : (xs.mapIdx f).size = xs.size,
i h, p i ((xs.mapIdx f)[i]) h :=
(mapIdx_induction (motive := fun _ => True) trivial fun _ _ _ => hs .., trivial).2
(mapIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => hs .., trivial).2
@[simp] theorem size_mapIdx {f : Nat α β} {xs : Array α} : (xs.mapIdx f).size = xs.size :=
@[simp] theorem size_mapIdx (f : Nat α β) (xs : Array α) : (xs.mapIdx f).size = xs.size :=
(mapIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
@[simp] theorem getElem_mapIdx {f : Nat α β} {xs : Array α} {i : Nat}
@[simp] theorem getElem_mapIdx (f : Nat α β) (xs : Array α) (i : Nat)
(h : i < (xs.mapIdx f).size) :
(xs.mapIdx f)[i] = f i (xs[i]'(by simp_all)) :=
(mapIdx_spec (p := fun i b h => b = f i xs[i]) fun _ _ => rfl).2 i (by simp_all)
(mapIdx_spec _ _ (fun i b h => b = f i xs[i]) fun _ _ => rfl).2 i (by simp_all)
@[simp] theorem getElem?_mapIdx {f : Nat α β} {xs : Array α} {i : Nat} :
@[simp] theorem getElem?_mapIdx (f : Nat α β) (xs : Array α) (i : Nat) :
(xs.mapIdx f)[i]? =
xs[i]?.map (f i) := by
simp [getElem?_def, size_mapIdx, getElem_mapIdx]
@[simp] theorem toList_mapIdx {f : Nat α β} {xs : Array α} :
@[simp] theorem toList_mapIdx (f : Nat α β) (xs : Array α) :
(xs.mapIdx f).toList = xs.toList.mapIdx (fun i a => f i a) := by
apply List.ext_getElem <;> simp
@@ -109,11 +108,11 @@ end Array
namespace List
@[simp] theorem mapFinIdx_toArray {l : List α} {f : (i : Nat) α (h : i < l.length) β} :
@[simp] theorem mapFinIdx_toArray (l : List α) (f : (i : Nat) α (h : i < l.length) β) :
l.toArray.mapFinIdx f = (l.mapFinIdx f).toArray := by
ext <;> simp
@[simp] theorem mapIdx_toArray {f : Nat α β} {l : List α} :
@[simp] theorem mapIdx_toArray (f : Nat α β) (l : List α) :
l.toArray.mapIdx f = (l.mapIdx f).toArray := by
ext <;> simp
@@ -123,7 +122,7 @@ namespace Array
/-! ### zipIdx -/
@[simp] theorem getElem_zipIdx {xs : Array α} {k : Nat} {i : Nat} (h : i < (xs.zipIdx k).size) :
@[simp] theorem getElem_zipIdx (xs : Array α) (k : Nat) (i : Nat) (h : i < (xs.zipIdx k).size) :
(xs.zipIdx k)[i] = (xs[i]'(by simp_all), k + i) := by
simp [zipIdx]
@@ -137,7 +136,7 @@ abbrev getElem_zipWithIndex := @getElem_zipIdx
@[deprecated zipIdx_toArray (since := "2025-01-21")]
abbrev zipWithIndex_toArray := @zipIdx_toArray
@[simp] theorem toList_zipIdx {xs : Array α} {k : Nat} :
@[simp] theorem toList_zipIdx (xs : Array α) (k : Nat) :
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
rcases xs with xs
simp
@@ -292,15 +291,12 @@ 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_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
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
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
@@ -434,15 +430,12 @@ 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_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
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
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
@@ -452,8 +445,8 @@ end Array
namespace List
theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : (i : Nat) α (h : i < l.length) m β} :
theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] (l : List α)
(f : (i : Nat) α (h : i < l.length) m β) :
l.toArray.mapFinIdxM f = toArray <$> l.mapFinIdxM f := by
let rec go (i : Nat) (acc : Array β) (inv : i + acc.size = l.length) :
Array.mapFinIdxM.map l.toArray f i acc.size inv acc
@@ -464,17 +457,17 @@ theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
rw [Nat.zero_add] at inv
simp only [Array.mapFinIdxM.map, inv, drop_length, mapFinIdxM.go, map_pure]
| k + 1 =>
conv => enter [2, 2, 3]; rw [ getElem_cons_drop (by omega)]
conv => enter [2, 2, 3]; rw [ getElem_cons_drop l acc.size (by omega)]
simp only [Array.mapFinIdxM.map, mapFinIdxM.go, _root_.map_bind]
congr; funext x
conv => enter [1, 4]; rw [ Array.size_push x]
conv => enter [2, 2, 3]; rw [ Array.size_push x]
conv => enter [1, 4]; rw [ Array.size_push _ x]
conv => enter [2, 2, 3]; rw [ Array.size_push _ x]
refine go k (acc.push x) _
simp only [Array.mapFinIdxM, mapFinIdxM]
exact go _ #[] _
theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : Nat α m β} :
theorem mapIdxM_toArray [Monad m] [LawfulMonad m] (l : List α)
(f : Nat α m β) :
l.toArray.mapIdxM f = toArray <$> l.mapIdxM f := by
let rec go (bs : List α) (acc : Array β) (inv : bs.length + acc.size = l.length) :
mapFinIdxM.go l (fun i a h => f i a) bs acc inv = mapIdxM.go f bs acc := by
@@ -490,14 +483,14 @@ end List
namespace Array
theorem toList_mapFinIdxM [Monad m] [LawfulMonad m] {xs : Array α}
{f : (i : Nat) α (h : i < xs.size) m β} :
theorem toList_mapFinIdxM [Monad m] [LawfulMonad m] (xs : Array α)
(f : (i : Nat) α (h : i < xs.size) m β) :
toList <$> xs.mapFinIdxM f = xs.toList.mapFinIdxM f := by
rw [List.mapFinIdxM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_mapIdxM [Monad m] [LawfulMonad m] {xs : Array α}
{f : Nat α m β} :
theorem toList_mapIdxM [Monad m] [LawfulMonad m] (xs : Array α)
(f : Nat α m β) :
toList <$> xs.mapIdxM f = xs.toList.mapIdxM f := by
rw [List.mapIdxM_toArray]
simp only [Functor.map_map, id_map']

View File

@@ -23,20 +23,13 @@ open Nat
/-! ### mapM -/
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] {xs : Array α} {f : α β} :
xs.mapM (m := m) (pure <| f ·) = pure (xs.map f) := by
induction xs; simp_all
@[simp] theorem mapM_id {xs : Array α} {f : α Id β} : xs.mapM f = xs.map f :=
mapM_pure
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] {f : α m β} {xs ys : Array α} :
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α m β) {xs ys : Array α} :
(xs ++ ys).mapM f = (return ( xs.mapM f) ++ ( ys.mapM f)) := by
rcases xs with xs
rcases ys with ys
simp
theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] {f : α m β} {xs : Array α} :
theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] (f : α m β) (xs : Array α) :
mapM f xs = xs.foldlM (fun acc a => return (acc.push ( f a))) #[] := by
rcases xs with xs
simp only [List.mapM_toArray, bind_pure_comp, List.size_toArray, List.foldlM_toArray']
@@ -53,21 +46,21 @@ theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] {f : α → m β} {xs : Ar
/-! ### foldlM and foldrM -/
theorem foldlM_map [Monad m] {f : β₁ β₂} {g : α β₂ m α} {xs : Array β₁} {init : α} {w : stop = xs.size} :
theorem foldlM_map [Monad m] (f : β₁ β₂) (g : α β₂ m α) (xs : Array β₁) (init : α) (w : stop = xs.size) :
(xs.map f).foldlM g init 0 stop = xs.foldlM (fun x y => g x (f y)) init 0 stop := by
subst w
cases xs
simp [List.foldlM_map]
theorem foldrM_map [Monad m] [LawfulMonad m] {f : β₁ β₂} {g : β₂ α m α} {xs : Array β₁}
{init : α} {w : start = xs.size} :
theorem foldrM_map [Monad m] [LawfulMonad m] (f : β₁ β₂) (g : β₂ α m α) (xs : Array β₁)
(init : α) (w : start = xs.size) :
(xs.map f).foldrM g init start 0 = xs.foldrM (fun x y => g (f x) y) init start 0 := by
subst w
cases xs
simp [List.foldrM_map]
theorem foldlM_filterMap [Monad m] [LawfulMonad m] {f : α Option β} {g : γ β m γ} {xs : Array α}
{init : γ} {w : stop = (xs.filterMap f).size} :
theorem foldlM_filterMap [Monad m] [LawfulMonad m] (f : α Option β) (g : γ β m γ)
(xs : Array α) (init : γ) (w : stop = (xs.filterMap f).size) :
(xs.filterMap f).foldlM g init 0 stop =
xs.foldlM (fun x y => match f y with | some b => g x b | none => pure x) init := by
subst w
@@ -75,8 +68,8 @@ theorem foldlM_filterMap [Monad m] [LawfulMonad m] {f : α → Option β} {g :
simp [List.foldlM_filterMap]
rfl
theorem foldrM_filterMap [Monad m] [LawfulMonad m] {f : α Option β} {g : β γ m γ} {xs : Array α}
{init : γ} {w : start = (xs.filterMap f).size} :
theorem foldrM_filterMap [Monad m] [LawfulMonad m] (f : α Option β) (g : β γ m γ)
(xs : Array α) (init : γ) (w : start = (xs.filterMap f).size) :
(xs.filterMap f).foldrM g init start 0 =
xs.foldrM (fun x y => match f x with | some b => g b y | none => pure y) init := by
subst w
@@ -84,16 +77,16 @@ theorem foldrM_filterMap [Monad m] [LawfulMonad m] {f : α → Option β} {g :
simp [List.foldrM_filterMap]
rfl
theorem foldlM_filter [Monad m] [LawfulMonad m] {p : α Bool} {g : β α m β} {xs : Array α}
{init : β} {w : stop = (xs.filter p).size} :
theorem foldlM_filter [Monad m] [LawfulMonad m] (p : α Bool) (g : β α m β)
(xs : Array α) (init : β) (w : stop = (xs.filter p).size) :
(xs.filter p).foldlM g init 0 stop =
xs.foldlM (fun x y => if p y then g x y else pure x) init := by
subst w
cases xs
simp [List.foldlM_filter]
theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α Bool} {g : α β m β} {xs : Array α}
{init : β} {w : start = (xs.filter p).size} :
theorem foldrM_filter [Monad m] [LawfulMonad m] (p : α Bool) (g : α β m β)
(xs : Array α) (init : β) (w : start = (xs.filter p).size) :
(xs.filter p).foldrM g init start 0 =
xs.foldrM (fun x y => if p x then g x y else pure y) init := by
subst w
@@ -101,7 +94,7 @@ theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β
simp [List.foldrM_filter]
@[simp] theorem foldlM_attachWith [Monad m]
{xs : Array α} {q : α Prop} (H : a, a xs q a) {f : β { x // q x} m β} {b} (w : stop = xs.size):
(xs : Array α) {q : α Prop} (H : a, a xs q a) {f : β { x // q x} m β} {b} (w : stop = xs.size):
(xs.attachWith q H).foldlM f b 0 stop =
xs.attach.foldlM (fun b a, h => f b a, H _ h) b := by
subst w
@@ -109,8 +102,7 @@ theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β
simp [List.foldlM_map]
@[simp] theorem foldrM_attachWith [Monad m] [LawfulMonad m]
{xs : Array α} {q : α Prop} (H : a, a xs q a) {f : { x // q x} β m β} {b}
{w : start = xs.size} :
(xs : Array α) {q : α Prop} (H : a, a xs q a) {f : { x // q x} β m β} {b} (w : start = xs.size):
(xs.attachWith q H).foldrM f b start 0 =
xs.attach.foldrM (fun a acc => f a.1, H _ a.2 acc) b := by
subst w
@@ -125,13 +117,13 @@ theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β
cases as <;> cases bs
simp_all
@[simp] theorem forM_append [Monad m] [LawfulMonad m] {xs ys : Array α} {f : α m PUnit} :
@[simp] theorem forM_append [Monad m] [LawfulMonad m] (xs ys : Array α) (f : α m PUnit) :
forM (xs ++ ys) f = (do forM xs f; forM ys f) := by
rcases xs with xs
rcases ys with ys
simp
@[simp] theorem forM_map [Monad m] [LawfulMonad m] {xs : Array α} {g : α β} {f : β m PUnit} :
@[simp] theorem forM_map [Monad m] [LawfulMonad m] (xs : Array α) (g : α β) (f : β m PUnit) :
forM (xs.map g) f = forM xs (fun a => f (g a)) := by
rcases xs with xs
simp
@@ -153,7 +145,7 @@ We can express a for loop over an array as a fold,
in which whenever we reach `.done b` we keep that value through the rest of the fold.
-/
theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
{xs : Array α} (f : (a : α) a xs β m (ForInStep β)) (init : β) :
(xs : Array α) (f : (a : α) a xs β m (ForInStep β)) (init : β) :
forIn' xs init f = ForInStep.value <$>
xs.attach.foldlM (fun b a, m => match b with
| .yield b => f a m b
@@ -164,29 +156,29 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
/-- We can express a for loop over an array which always yields as a fold. -/
@[simp] theorem forIn'_yield_eq_foldlM [Monad m] [LawfulMonad m]
{xs : Array α} (f : (a : α) a xs β m γ) (g : (a : α) a xs β γ β) (init : β) :
(xs : Array α) (f : (a : α) a xs β m γ) (g : (a : α) a xs β γ β) (init : β) :
forIn' xs init (fun a m b => (fun c => .yield (g a m b c)) <$> f a m b) =
xs.attach.foldlM (fun b a, m => g a m b <$> f a m b) init := by
rcases xs with xs
simp [List.foldlM_map]
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
{xs : Array α} (f : (a : α) a xs β β) (init : β) :
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(xs : Array α) (f : (a : α) a xs β β) (init : β) :
forIn' xs init (fun a m b => pure (.yield (f a m b))) =
pure (f := m) (xs.attach.foldl (fun b a, h => f a h b) init) := by
rcases xs with xs
simp [List.forIn'_pure_yield_eq_foldl, List.foldl_map]
@[simp] theorem forIn'_yield_eq_foldl
{xs : Array α} (f : (a : α) a xs β β) (init : β) :
(xs : Array α) (f : (a : α) a xs β β) (init : β) :
forIn' (m := Id) xs init (fun a m b => .yield (f a m b)) =
xs.attach.foldl (fun b a, h => f a h b) init := by
rcases xs with xs
simp [List.foldl_map]
@[simp] theorem forIn'_map [Monad m] [LawfulMonad m]
{xs : Array α} (g : α β) (f : (b : β) b xs.map g γ m (ForInStep γ)) :
forIn' (xs.map g) init f = forIn' xs init fun a h y => f (g a) (mem_map_of_mem h) y := by
(xs : Array α) (g : α β) (f : (b : β) b xs.map g γ m (ForInStep γ)) :
forIn' (xs.map g) init f = forIn' xs init fun a h y => f (g a) (mem_map_of_mem g h) y := by
rcases xs with xs
simp
@@ -195,7 +187,7 @@ We can express a for loop over an array as a fold,
in which whenever we reach `.done b` we keep that value through the rest of the fold.
-/
theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
{xs : Array α} (f : α β m (ForInStep β)) (init : β) :
(f : α β m (ForInStep β)) (init : β) (xs : Array α) :
forIn xs init f = ForInStep.value <$>
xs.foldlM (fun b a => match b with
| .yield b => f a b
@@ -206,63 +198,37 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
/-- We can express a for loop over an array which always yields as a fold. -/
@[simp] theorem forIn_yield_eq_foldlM [Monad m] [LawfulMonad m]
{xs : Array α} (f : α β m γ) (g : α β γ β) (init : β) :
(xs : Array α) (f : α β m γ) (g : α β γ β) (init : β) :
forIn xs init (fun a b => (fun c => .yield (g a b c)) <$> f a b) =
xs.foldlM (fun b a => g a b <$> f a b) init := by
rcases xs with xs
simp [List.foldlM_map]
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
{xs : Array α} (f : α β β) (init : β) :
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(xs : Array α) (f : α β β) (init : β) :
forIn xs init (fun a b => pure (.yield (f a b))) =
pure (f := m) (xs.foldl (fun b a => f a b) init) := by
rcases xs with xs
simp [List.forIn_pure_yield_eq_foldl, List.foldl_map]
@[simp] theorem forIn_yield_eq_foldl
{xs : Array α} (f : α β β) (init : β) :
(xs : Array α) (f : α β β) (init : β) :
forIn (m := Id) xs init (fun a b => .yield (f a b)) =
xs.foldl (fun b a => f a b) init := by
rcases xs with xs
simp [List.foldl_map]
@[simp] theorem forIn_map [Monad m] [LawfulMonad m]
{xs : Array α} {g : α β} {f : β γ m (ForInStep γ)} :
(xs : Array α) (g : α β) (f : β γ m (ForInStep γ)) :
forIn (xs.map g) init f = forIn xs init fun a y => f (g a) y := by
rcases xs with xs
simp
/-! ### allM and anyM -/
@[simp] theorem anyM_pure [Monad m] [LawfulMonad m] {p : α Bool} {xs : Array α} :
xs.anyM (m := m) (pure <| p ·) = pure (xs.any p) := by
cases xs
simp
@[simp] theorem allM_pure [Monad m] [LawfulMonad m] {p : α Bool} {xs : Array α} :
xs.allM (m := m) (pure <| p ·) = pure (xs.all p) := by
cases xs
simp
/-! ### findM? and findSomeM? -/
@[simp]
theorem findM?_pure {m} [Monad m] [LawfulMonad m] {p : α Bool} {xs : Array α} :
findM? (m := m) (pure <| p ·) xs = pure (xs.find? p) := by
cases xs
simp
@[simp]
theorem findSomeM?_pure [Monad m] [LawfulMonad m] {f : α Option β} {xs : Array α} :
findSomeM? (m := m) (pure <| f ·) xs = pure (xs.findSome? f) := by
cases xs
simp
end Array
namespace List
theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} :
theorem filterM_toArray [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) :
l.toArray.filterM p = toArray <$> l.filterM p := by
simp only [Array.filterM, filterM, foldlM_toArray, bind_pure_comp, Functor.map_map]
conv => lhs; rw [ reverse_nil]
@@ -277,24 +243,24 @@ theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bo
exact ih (x :: acc)
/-- Variant of `filterM_toArray` with a side condition for the stop position. -/
@[simp] theorem filterM_toArray' [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} (w : stop = l.length) :
@[simp] theorem filterM_toArray' [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) (w : stop = l.length) :
l.toArray.filterM p 0 stop = toArray <$> l.filterM p := by
subst w
rw [filterM_toArray]
theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} :
theorem filterRevM_toArray [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) :
l.toArray.filterRevM p = toArray <$> l.filterRevM p := by
simp [Array.filterRevM, filterRevM]
rw [ foldlM_reverse, foldlM_toArray, Array.filterM, filterM_toArray]
simp only [filterM, bind_pure_comp, Functor.map_map, reverse_toArray, reverse_reverse]
/-- Variant of `filterRevM_toArray` with a side condition for the start position. -/
@[simp] theorem filterRevM_toArray' [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} (w : start = l.length) :
@[simp] theorem filterRevM_toArray' [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) (w : start = l.length) :
l.toArray.filterRevM p start 0 = toArray <$> l.filterRevM p := by
subst w
rw [filterRevM_toArray]
theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α m (Option β)} :
theorem filterMapM_toArray [Monad m] [LawfulMonad m] (l : List α) (f : α m (Option β)) :
l.toArray.filterMapM f = toArray <$> l.filterMapM f := by
simp [Array.filterMapM, filterMapM]
conv => lhs; rw [ reverse_nil]
@@ -307,12 +273,12 @@ theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m
· simp only [pure_bind]; rw [ List.reverse_cons]; exact ih _
/-- Variant of `filterMapM_toArray` with a side condition for the stop position. -/
@[simp] theorem filterMapM_toArray' [Monad m] [LawfulMonad m] {l : List α} {f : α m (Option β)} (w : stop = l.length) :
@[simp] theorem filterMapM_toArray' [Monad m] [LawfulMonad m] (l : List α) (f : α m (Option β)) (w : stop = l.length) :
l.toArray.filterMapM f 0 stop = toArray <$> l.filterMapM f := by
subst w
rw [filterMapM_toArray]
@[simp] theorem flatMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α m (Array β)} :
@[simp] theorem flatMapM_toArray [Monad m] [LawfulMonad m] (l : List α) (f : α m (Array β)) :
l.toArray.flatMapM f = toArray <$> l.flatMapM (fun a => Array.toList <$> f a) := by
simp only [Array.flatMapM, bind_pure_comp, foldlM_toArray, flatMapM]
conv => lhs; arg 2; change [].reverse.flatten.toArray
@@ -353,22 +319,22 @@ namespace Array
subst w
simp [flatMapM, h]
theorem toList_filterM [Monad m] [LawfulMonad m] {xs : Array α} {p : α m Bool} :
theorem toList_filterM [Monad m] [LawfulMonad m] (xs : Array α) (p : α m Bool) :
toList <$> xs.filterM p = xs.toList.filterM p := by
rw [List.filterM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_filterRevM [Monad m] [LawfulMonad m] {xs : Array α} {p : α m Bool} :
theorem toList_filterRevM [Monad m] [LawfulMonad m] (xs : Array α) (p : α m Bool) :
toList <$> xs.filterRevM p = xs.toList.filterRevM p := by
rw [List.filterRevM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_filterMapM [Monad m] [LawfulMonad m] {xs : Array α} {f : α m (Option β)} :
theorem toList_filterMapM [Monad m] [LawfulMonad m] (xs : Array α) (f : α m (Option β)) :
toList <$> xs.filterMapM f = xs.toList.filterMapM f := by
rw [List.filterMapM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_flatMapM [Monad m] [LawfulMonad m] {xs : Array α} {f : α m (Array β)} :
theorem toList_flatMapM [Monad m] [LawfulMonad m] (xs : Array α) (f : α m (Array β)) :
toList <$> xs.flatMapM f = xs.toList.flatMapM (fun a => toList <$> f a) := by
rw [List.flatMapM_toArray]
simp only [Functor.map_map, id_map']
@@ -388,12 +354,12 @@ and simplifies these to the function directly taking the value.
simp
rw [List.foldlM_subtype hf]
@[wf_preprocess] theorem foldlM_wfParam [Monad m] {xs : Array α} {f : β α m β} {init : β} :
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β α m β) :
(wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
simp [wfParam]
@[wf_preprocess] theorem foldlM_unattach [Monad m] {P : α Prop} {xs : Array (Subtype P)} {f : β α m β} {init : β} :
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b x, h =>
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : Array (Subtype P)) (f : β α m β) :
xs.unattach.foldlM f = xs.foldlM fun b x, h =>
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
f b (wfParam x) := by
simp [wfParam]
@@ -412,12 +378,12 @@ and simplifies these to the function directly taking the value.
rw [List.foldrM_subtype hf]
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] {xs : Array α} {f : α β m β} {init : β} :
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α β m β) :
(wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
simp [wfParam]
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] {P : α Prop} {xs : Array (Subtype P)} {f : α β m β} {init : β} :
xs.unattach.foldrM f init = xs.foldrM (init := init) fun x, h b =>
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α β m β) :
xs.unattach.foldrM f = xs.foldrM fun x, h b =>
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
f (wfParam x) b := by
simp [wfParam]
@@ -433,11 +399,11 @@ and simplifies these to the function directly taking the value.
simp
rw [List.mapM_subtype hf]
@[wf_preprocess] theorem mapM_wfParam [Monad m] [LawfulMonad m] {xs : Array α} {f : α m β} :
@[wf_preprocess] theorem mapM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α m β) :
(wfParam xs).mapM f = xs.attach.unattach.mapM f := by
simp [wfParam]
@[wf_preprocess] theorem mapM_unattach [Monad m] [LawfulMonad m] {P : α Prop} {xs : Array (Subtype P)} {f : α m β} :
@[wf_preprocess] theorem mapM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α m β) :
xs.unattach.mapM f = xs.mapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@@ -452,12 +418,12 @@ and simplifies these to the function directly taking the value.
@[wf_preprocess] theorem filterMapM_wfParam [Monad m] [LawfulMonad m]
{xs : Array α} {f : α m (Option β)} :
(xs : Array α) (f : α m (Option β)) :
(wfParam xs).filterMapM f = xs.attach.unattach.filterMapM f := by
simp [wfParam]
@[wf_preprocess] theorem filterMapM_unattach [Monad m] [LawfulMonad m]
{P : α Prop} {xs : Array (Subtype P)} {f : α m (Option β)} :
(P : α Prop) (xs : Array (Subtype P)) (f : α m (Option β)) :
xs.unattach.filterMapM f = xs.filterMapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@@ -471,12 +437,12 @@ and simplifies these to the function directly taking the value.
simp [hf]
@[wf_preprocess] theorem flatMapM_wfParam [Monad m] [LawfulMonad m]
{xs : Array α} {f : α m (Array β)} :
(xs : Array α) (f : α m (Array β)) :
(wfParam xs).flatMapM f = xs.attach.unattach.flatMapM f := by
simp [wfParam]
@[wf_preprocess] theorem flatMapM_unattach [Monad m] [LawfulMonad m]
{P : α Prop} {xs : Array (Subtype P)} {f : α m (Array β)} :
(P : α Prop) (xs : Array (Subtype P)) (f : α m (Array β)) :
xs.unattach.flatMapM f = xs.flatMapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]

View File

@@ -16,33 +16,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
@[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
ext i h₁ h₂
· simp
· simp [getElem_push]
split <;> rename_i h₃
· rfl
· congr
simp at h₁ h₂
omega
@[simp] theorem _root_.List.toArray_ofFn {f : Fin n α} : (List.ofFn f).toArray = Array.ofFn f := by
ext <;> simp
@[simp] theorem toList_ofFn {f : Fin n α} : (Array.ofFn f).toList = List.ofFn f := by
apply List.ext_getElem <;> simp
@[simp]
theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by
rw [ Array.toList_inj]
simp
@[simp 500]
theorem mem_ofFn {n} {f : Fin n α} {a : α} : a ofFn f i, f i = a := by
theorem mem_ofFn {n} (f : Fin n α) (a : α) : a ofFn f i, f i = a := by
constructor
· intro w
obtain i, h, rfl := getElem_of_mem w

View File

@@ -30,16 +30,6 @@ 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)
@@ -60,7 +50,7 @@ inclusive, and default to sorting the entire array.
set_option linter.unusedVariables.funArgs false in
/--
Sorts an array using the Quicksort algorithm, using `Ord.compare` to compare elements.
Sort an array using `compare` to compare elements.
-/
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
xs.qsort fun x y => compare x y |>.isLT

View File

@@ -26,21 +26,20 @@ open Nat
/-! ### range' -/
theorem range'_succ {s n step} : range' s (n + 1) step = #[s] ++ range' (s + step) n step := by
theorem range'_succ (s n step) : range' s (n + 1) step = #[s] ++ range' (s + step) n step := by
rw [ toList_inj]
simp [List.range'_succ]
@[simp] theorem range'_eq_empty_iff : range' s n step = #[] n = 0 := by
rw [ size_eq_zero_iff, size_range']
theorem range'_ne_empty_iff : range' s n step #[] n 0 := by
theorem range'_ne_empty_iff (s : Nat) {n step : Nat} : range' s n step #[] n 0 := by
cases n <;> simp
@[simp] theorem range'_zero : range' s 0 step = #[] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := by
simp [range', ofFn, ofFn.go]
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := rfl
@[simp] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by
rw [ toList_inj]
@@ -57,13 +56,13 @@ theorem mem_range' {n} : m ∈ range' s n step ↔ ∃ i < n, m = s + step * i :
theorem pop_range' : (range' s n step).pop = range' s (n - 1) step := by
ext <;> simp
theorem map_add_range' {a} (s n step) : map (a + ·) (range' s n step) = range' (a + s) n step := by
theorem map_add_range' (a) (s n step) : map (a + ·) (range' s n step) = range' (a + s) n step := by
ext <;> simp <;> omega
theorem range'_succ_left : range' (s + 1) n step = (range' s n step).map (· + 1) := by
ext <;> simp <;> omega
theorem range'_append {s m n step : Nat} :
theorem range'_append (s m n step : Nat) :
range' s m step ++ range' (s + step * m) n step = range' s (m + n) step := by
ext i h₁ h₂
· simp
@@ -74,13 +73,13 @@ theorem range'_append {s m n step : Nat} :
have : step * m step * i := by exact mul_le_mul_left step h
omega
@[simp] theorem range'_append_1 {s m n : Nat} :
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append (step := 1)
@[simp] theorem range'_append_1 (s m n : 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
simpa using range'_append.symm
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
theorem range'_1_concat {s n : Nat} : range' s (n + 1) = range' s n ++ #[s + n] := by
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ #[s + n] := by
simp [range'_concat]
@[simp] theorem mem_range'_1 : m range' s n s m m < s + n := by
@@ -88,7 +87,7 @@ theorem range'_1_concat {s n : Nat} : range' s (n + 1) = range' s n ++ #[s + n]
fun i, h, e => e Nat.le_add_right .., Nat.add_lt_add_left h _,
fun h₁, h₂ => m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm
theorem map_sub_range' {a s : Nat} (h : a s) (n : Nat) :
theorem map_sub_range' (a s n : Nat) (h : a s) :
map (· - a) (range' s n step) = range' (s - a) n step := by
conv => lhs; rw [ Nat.add_sub_cancel' h]
rw [ map_add_range', map_map, (?_ : __ = _), map_id]
@@ -121,10 +120,10 @@ theorem erase_range' :
/-! ### range -/
theorem range_eq_range' {n : Nat} : range n = range' 0 n := by
theorem range_eq_range' (n : Nat) : range n = range' 0 n := by
simp [range, range']
theorem range_succ_eq_map {n : Nat} : range (n + 1) = #[0] ++ map succ (range n) := by
theorem range_succ_eq_map (n : Nat) : range (n + 1) = #[0] ++ map succ (range n) := by
ext i h₁ h₂
· simp
omega
@@ -133,7 +132,7 @@ theorem range_succ_eq_map {n : Nat} : range (n + 1) = #[0] ++ map succ (range n)
succ_eq_add_one, dite_eq_ite]
split <;> omega
theorem range'_eq_map_range {s n : Nat} : range' s n = map (s + ·) (range n) := by
theorem range'_eq_map_range (s n : Nat) : range' s n = map (s + ·) (range n) := by
rw [range_eq_range', map_add_range']; rfl
@[simp] theorem range_eq_empty_iff {n : Nat} : range n = #[] n = 0 := by
@@ -142,7 +141,7 @@ theorem range'_eq_map_range {s n : Nat} : range' s n = map (s + ·) (range n) :=
theorem range_ne_empty_iff {n : Nat} : range n #[] n 0 := by
cases n <;> simp
theorem range_succ {n : Nat} : range (succ n) = range n ++ #[n] := by
theorem range_succ (n : Nat) : range (succ n) = range n ++ #[n] := by
ext i h₁ h₂
· simp
· simp only [succ_eq_add_one, size_range] at h₁
@@ -150,11 +149,11 @@ theorem range_succ {n : Nat} : range (succ n) = range n ++ #[n] := by
dite_eq_ite]
split <;> omega
theorem range_add {n m : Nat} : range (n + m) = range n ++ (range m).map (n + ·) := by
theorem range_add (n m : Nat) : range (n + m) = range n ++ (range m).map (n + ·) := by
rw [ range'_eq_map_range]
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 (s := 0)).symm
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 0 n m).symm
theorem reverse_range' {s n : Nat} : reverse (range' s n) = map (s + n - 1 - ·) (range n) := by
theorem reverse_range' (s n : Nat) : reverse (range' s n) = map (s + n - 1 - ·) (range n) := by
simp [ toList_inj, List.reverse_range']
@[simp]
@@ -163,9 +162,9 @@ theorem mem_range {m n : Nat} : m ∈ range n ↔ m < n := by
theorem not_mem_range_self {n : Nat} : n range n := by simp
theorem self_mem_range_succ {n : Nat} : n range (n + 1) := by simp
theorem self_mem_range_succ (n : Nat) : n range (n + 1) := by simp
@[simp] theorem take_range {i n : Nat} : take (range n) i = range (min i n) := by
@[simp] theorem take_range (i n : Nat) : take (range n) i = range (min i n) := by
ext <;> simp
@[simp] theorem find?_range_eq_some {n : Nat} {i : Nat} {p : Nat Bool} :
@@ -188,50 +187,48 @@ theorem zipIdx_eq_empty_iff {xs : Array α} {i : Nat} : xs.zipIdx i = #[] ↔ xs
simp
@[simp]
theorem getElem?_zipIdx {xs : Array α} {i j} : (zipIdx xs i)[j]? = xs[j]?.map fun a => (a, i + j) := by
theorem getElem?_zipIdx (xs : Array α) (i j) : (zipIdx xs i)[j]? = xs[j]?.map fun a => (a, i + j) := by
simp [getElem?_def]
theorem map_snd_add_zipIdx_eq_zipIdx {xs : Array α} {n k : Nat} :
theorem map_snd_add_zipIdx_eq_zipIdx (xs : Array α) (n k : Nat) :
map (Prod.map id (· + n)) (zipIdx xs k) = zipIdx xs (n + k) :=
ext_getElem? fun i by simp [(· ·), Nat.add_comm, Nat.add_left_comm]; rfl
-- Arguments are explicit for parity with `zipIdx_map_fst`.
@[simp]
theorem zipIdx_map_snd (i) (xs : Array α) : map Prod.snd (zipIdx xs i) = range' i xs.size := by
cases xs
simp
-- Arguments are explicit so we can rewrite from right to left.
@[simp]
theorem zipIdx_map_fst (i) (xs : Array α) : map Prod.fst (zipIdx xs i) = xs := by
cases xs
simp
theorem zipIdx_eq_zip_range' {xs : Array α} {i : Nat} : xs.zipIdx i = xs.zip (range' i xs.size) := by
theorem zipIdx_eq_zip_range' (xs : Array α) {i : Nat} : xs.zipIdx i = xs.zip (range' i xs.size) := by
simp [zip_of_prod (zipIdx_map_fst _ _) (zipIdx_map_snd _ _)]
@[simp]
theorem unzip_zipIdx_eq_prod {xs : Array α} {i : Nat} :
theorem unzip_zipIdx_eq_prod (xs : Array α) {i : Nat} :
(xs.zipIdx i).unzip = (xs, range' i xs.size) := by
simp only [zipIdx_eq_zip_range', unzip_zip, size_range']
/-- Replace `zipIdx` with a starting index `n+1` with `zipIdx` starting from `n`,
followed by a `map` increasing the indices by one. -/
theorem zipIdx_succ {xs : Array α} {i : Nat} :
theorem zipIdx_succ (xs : Array α) (i : Nat) :
xs.zipIdx (i + 1) = (xs.zipIdx i).map (fun a, j => (a, j + 1)) := by
cases xs
simp [List.zipIdx_succ]
/-- Replace `zipIdx` with a starting index with `zipIdx` starting from 0,
followed by a `map` increasing the indices. -/
theorem zipIdx_eq_map_add {xs : Array α} {i : Nat} :
theorem zipIdx_eq_map_add (xs : Array α) (i : Nat) :
xs.zipIdx i = (xs.zipIdx 0).map (fun a, j => (a, i + j)) := by
cases xs
simp only [zipIdx_toArray, List.map_toArray, mk.injEq]
rw [List.zipIdx_eq_map_add]
@[simp]
theorem zipIdx_singleton {x : α} {k : Nat} : zipIdx #[x] k = #[(x, k)] :=
theorem zipIdx_singleton (x : α) (k : Nat) : zipIdx #[x] k = #[(x, k)] :=
rfl
theorem mk_add_mem_zipIdx_iff_getElem? {k i : Nat} {x : α} {xs : Array α} :
@@ -250,13 +247,13 @@ theorem snd_lt_add_of_mem_zipIdx {x : α × Nat} {k : Nat} {xs : Array α} (h :
theorem snd_lt_of_mem_zipIdx {x : α × Nat} {k : Nat} {xs : Array α} (h : x zipIdx xs k) : x.2 < xs.size + k := by
simpa [Nat.add_comm] using snd_lt_add_of_mem_zipIdx h
theorem map_zipIdx {f : α β} {xs : Array α} {k : Nat} :
theorem map_zipIdx (f : α β) (xs : Array α) (k : Nat) :
map (Prod.map f id) (zipIdx xs k) = zipIdx (xs.map f) k := by
cases xs
simp [List.map_zipIdx]
theorem fst_mem_of_mem_zipIdx {x : α × Nat} {xs : Array α} {k : Nat} (h : x zipIdx xs k) : x.1 xs :=
zipIdx_map_fst k xs mem_map_of_mem h
zipIdx_map_fst k xs mem_map_of_mem _ h
theorem fst_eq_of_mem_zipIdx {x : α × Nat} {xs : Array α} {k : Nat} (h : x zipIdx xs k) :
x.1 = xs[x.2 - k]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) := by
@@ -273,12 +270,12 @@ theorem mem_zipIdx' {x : α} {i : Nat} {xs : Array α} (h : (x, i) ∈ xs.zipIdx
i < xs.size x = xs[i]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) :=
by simpa using snd_lt_add_of_mem_zipIdx h, fst_eq_of_mem_zipIdx h
theorem zipIdx_map {xs : Array α} {k : Nat} {f : α β} :
theorem zipIdx_map (xs : Array α) (k : Nat) (f : α β) :
zipIdx (xs.map f) k = (zipIdx xs k).map (Prod.map f id) := by
cases xs
simp [List.zipIdx_map]
theorem zipIdx_append {xs ys : Array α} {k : Nat} :
theorem zipIdx_append (xs ys : Array α) (k : Nat) :
zipIdx (xs ++ ys) k = zipIdx xs k ++ zipIdx ys (k + xs.size) := by
cases xs
cases ys

View File

@@ -11,16 +11,11 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
/--
Replaces the element at a given index in an array.
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.)
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"]`
This will perform the update destructively provided that `a` has a reference
count of 1 when called.
-/
@[extern "lean_array_fset"]
def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_elem_tactic) :
@@ -28,15 +23,10 @@ def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_e
toList := xs.toList.set i v
/--
Replaces the element at the provided index in an array. The array is returned unmodified if the
index is out of bounds.
Set an element in an array, or do nothing if the index is out of bounds.
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"]`
This will perform the update destructively provided that `a` has a reference
count of 1 when called.
-/
@[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)

View File

@@ -7,44 +7,18 @@ 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
/--
A region of some underlying array.
A subarray contains an array together with the start and end indices of a region of interest.
Subarrays can be used to avoid copying or allocating space, while being more convenient than
tracking the bounds by hand. The region of interest consists of every index that is both greater
than or equal to `start` and strictly less than `stop`.
-/
structure Subarray (α : Type u) where
/-- The underlying array. -/
structure Subarray (α : Type u) where
array : Array α
/-- The starting index of the region of interest (inclusive). -/
start : Nat
/-- The ending index of the region of interest (exclusive). -/
stop : Nat
/--
The starting index is no later than the ending index.
The ending index is exclusive. If the starting and ending indices are equal, then the subarray is
empty.
-/
start_le_stop : start stop
/-- The stopping index is no later than the end of the array.
The ending index is exclusive. If it is equal to the size of the array, then the last element of
the array is in the subarray.
-/
stop_le_array_size : stop array.size
namespace Subarray
/--
Computes the size of the subarray.
-/
def size (s : Subarray α) : Nat :=
s.stop - s.start
@@ -54,11 +28,6 @@ theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
apply Nat.le_trans (Nat.sub_le stop start)
assumption
/--
Extracts an element from the subarray.
The index is relative to the start of the subarray, rather than the underlying array.
-/
def get (s : Subarray α) (i : Fin s.size) : α :=
have : s.start + i.val < s.array.size := by
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size
@@ -71,33 +40,12 @@ def get (s : Subarray α) (i : Fin s.size) : α :=
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
/--
Extracts an element from the subarray, or returns a default value `v₀` when the index is out of
bounds.
The index is relative to the start and end of the subarray, rather than the underlying array.
-/
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
if h : i < s.size then s[i] else v₀
/--
Extracts an element from the subarray, or returns a default value when the index is out of bounds.
The index is relative to the start and end of the subarray, rather than the underlying array. The
default value is that provided by the `Inhabited α` instance.
-/
abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
getD s i default
/--
Shrinks the subarray by incrementing its starting index if possible, returning it unchanged if not.
Examples:
* `#[1,2,3].toSubarray.popFront.toArray = #[2, 3]`
* `#[1,2,3].toSubarray.popFront.popFront.toArray = #[3]`
* `#[1,2,3].toSubarray.popFront.popFront.popFront.toArray = #[]`
* `#[1,2,3].toSubarray.popFront.popFront.popFront.popFront.toArray = #[]`
-/
def popFront (s : Subarray α) : Subarray α :=
if h : s.start < s.stop then
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
@@ -106,8 +54,6 @@ def popFront (s : Subarray α) : Subarray α :=
/--
The empty subarray.
This empty subarray is backed by an empty array.
-/
protected def empty : Subarray α where
array := #[]
@@ -122,12 +68,6 @@ 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
@@ -140,10 +80,6 @@ This definition replaces `Subarray.forIn`.
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 β :=
@@ -152,197 +88,46 @@ protected opaque forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Mona
instance : ForIn m (Subarray α) α where
forIn := Subarray.forIn
/--
Folds a monadic operation from left to right over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
element of the subarray with the current accumulator value in turn. The monad in question may permit
early termination or repetition.
Examples:
```lean example
#eval #["red", "green", "blue"].toSubarray.foldlM (init := "") fun acc x => do
let l ← Option.guard (· ≠ 0) x.length
return s!"{acc}({l}){x} "
```
```output
some "(3)red (5)green (4)blue "
```
```lean example
#eval #["red", "green", "blue"].toSubarray.foldlM (init := 0) fun acc x => do
let l ← Option.guard (· ≠ 5) x.length
return s!"{acc}({l}){x} "
```
```output
none
```
-/
@[inline]
def foldlM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : β α m β) (init : β) (as : Subarray α) : m β :=
as.array.foldlM f (init := init) (start := as.start) (stop := as.stop)
/--
Folds a monadic operation from right to left over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
element of the subarray with the current accumulator value in turn, moving from the end to the
start. The monad in question may permit early termination or repetition.
Examples:
```lean example
#eval #["red", "green", "blue"].toSubarray.foldrM (init := "") fun x acc => do
let l ← Option.guard (· ≠ 0) x.length
return s!"{acc}({l}){x} "
```
```output
some "(4)blue (5)green (3)red "
```
```lean example
#eval #["red", "green", "blue"].toSubarray.foldrM (init := 0) fun x acc => do
let l ← Option.guard (· ≠ 5) x.length
return s!"{acc}({l}){x} "
```
```output
none
```
-/
@[inline]
def foldrM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α β m β) (init : β) (as : Subarray α) : m β :=
as.array.foldrM f (init := init) (start := as.stop) (stop := as.start)
/--
Checks whether any of the elements in a subarray satisfy a monadic Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that satisfies the predicate is found.
Example:
```lean example
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.anyM fun x => do
IO.println x
pure (x == "blue")
```
```output
green
blue
```
```output
true
```
-/
@[inline]
def anyM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Subarray α) : m Bool :=
as.array.anyM p (start := as.start) (stop := as.stop)
/--
Checks whether all of the elements in a subarray satisfy a monadic Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that does not satisfy the predicate is found.
Example:
```lean example
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.allM fun x => do
IO.println x
pure (x.length == 5)
```
```output
green
blue
```
```output
false
```
-/
@[inline]
def allM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Subarray α) : m Bool :=
as.array.allM p (start := as.start) (stop := as.stop)
/--
Runs a monadic action on each element of a subarray.
The elements are processed starting at the lowest index and moving up.
-/
@[inline]
def forM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Subarray α) : m PUnit :=
as.array.forM f (start := as.start) (stop := as.stop)
/--
Runs a monadic action on each element of a subarray, in reverse order.
The elements are processed starting at the highest index and moving down.
-/
@[inline]
def forRevM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Subarray α) : m PUnit :=
as.array.forRevM f (start := as.stop) (stop := as.start)
/--
Folds an operation from left to right over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and combining each
element of the subarray with the current accumulator value in turn.
Examples:
* `#["red", "green", "blue"].toSubarray.foldl (· + ·.length) 0 = 12`
* `#["red", "green", "blue"].toSubarray.popFront.foldl (· + ·.length) 0 = 9`
-/
@[inline]
def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldlM f (init := init)
/--
Folds an operation from right to left over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and combining each element of the
subarray with the current accumulator value in turn, moving from the end to the start.
Examples:
* `#eval #["red", "green", "blue"].toSubarray.foldr (·.length + ·) 0 = 12`
* `#["red", "green", "blue"].toSubarray.popFront.foldlr (·.length + ·) 0 = 9`
-/
@[inline]
def foldr {α : Type u} {β : Type v} (f : α β β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldrM f (init := init)
/--
Checks whether any of the elements in a subarray satisfy a Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that satisfies the predicate is found.
-/
@[inline]
def any {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.anyM p
/--
Checks whether all of the elements in a subarray satisfy a Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that does not satisfy the predicate is found.
-/
@[inline]
def all {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.allM p
/--
Applies a monadic function to each element in a subarray in reverse order, stopping at the first
element for which the function succeeds by returning a value other than `none`. The succeeding value
is returned, or `none` if there is no success.
Example:
```lean example
#eval #["red", "green", "blue"].toSubarray.findSomeRevM? fun x => do
IO.println x
return Option.guard (· = 5) x.length
```
```output
blue
green
```
```output
some 5
```
-/
@[inline]
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Subarray α) (f : α m (Option β)) : m (Option β) :=
let rec @[specialize] find : (i : Nat) i as.size m (Option β)
@@ -357,39 +142,10 @@ def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
find i this
find as.size (Nat.le_refl _)
/--
Applies a monadic Boolean predicate to each element in a subarray in reverse order, stopping at the
first element that satisfies the predicate. The element that satisfies the predicate is returned, or
`none` if no element satisfies it.
Example:
```lean example
#eval #["red", "green", "blue"].toSubarray.findRevM? fun x => do
IO.println x
return (x.length = 5)
```
```output
blue
green
```
```output
some 5
```
-/
@[inline]
def findRevM? {α : Type} {m : Type Type w} [Monad m] (as : Subarray α) (p : α m Bool) : m (Option α) :=
as.findSomeRevM? fun a => return if ( p a) then some a else none
/--
Tests each element in a subarray with a Boolean predicate in reverse order, stopping at the first
element that satisfies the predicate. The element that satisfies the predicate is returned, or
`none` if no element satisfies the predicate.
Examples:
* `#["red", "green", "blue"].toSubarray.findRev? (·.length ≠ 4) = some "green"`
* `#["red", "green", "blue"].toSubarray.findRev? (fun _ => true) = some "blue"`
* `#["red", "green", "blue"].toSubarray 0 0 |>.findRev? (fun _ => true) = none`
-/
@[inline]
def findRev? {α : Type} (as : Subarray α) (p : α Bool) : Option α :=
Id.run <| as.findRevM? p
@@ -399,12 +155,6 @@ end Subarray
namespace Array
variable {α : Type u}
/--
Returns a subarray of an array, with the given bounds.
If `start` or `stop` are not valid bounds for a subarray, then they are clamped to array's size.
Additionally, the starting index is clamped to the ending index.
-/
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
if h₂ : stop as.size then
if h₁ : start stop then
@@ -427,9 +177,6 @@ def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Suba
start_le_stop := Nat.le_refl _,
stop_le_array_size := Nat.le_refl _ }
/--
Allocates a new array that contains the contents of the subarray.
-/
@[coe]
def ofSubarray (s : Subarray α) : Array α := Id.run do
let mut as := mkEmpty (s.stop - s.start)
@@ -439,11 +186,8 @@ def ofSubarray (s : Subarray α) : Array α := Id.run do
instance : Coe (Subarray α) (Array α) := ofSubarray
/-- A subarray with the provided bounds.-/
syntax:max term noWs "[" withoutPosition(term ":" term) "]" : term
/-- A subarray with the provided lower bound that extends to the rest of the array. -/
syntax:max term noWs "[" withoutPosition(term ":") "]" : term
/-- A subarray with the provided upper bound, starting at the index 0. -/
syntax:max term noWs "[" withoutPosition(":" term) "]" : term
macro_rules
@@ -453,7 +197,6 @@ macro_rules
end Array
@[inherit_doc Array.ofSubarray]
def Subarray.toArray (s : Subarray α) : Array α :=
Array.ofSubarray s

View File

@@ -20,8 +20,7 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Subarray
/--
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
of which contains the remainder.
Splits a subarray into two parts.
-/
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
let i', isLt := i

View File

@@ -26,7 +26,7 @@ end List
namespace Array
theorem exists_of_uset {xs : Array α} {i d} (h) :
theorem exists_of_uset (xs : Array α) (i d h) :
l₁ l₂, xs.toList = l₁ ++ xs[i] :: l₂ List.length l₁ = i.toNat
(xs.uset i d h).toList = l₁ ++ d :: l₂ := by
simpa only [ugetElem_eq_getElem, getElem_toList, uset, toList_set] using

View File

@@ -22,19 +22,19 @@ open Nat
/-! ### zipWith -/
theorem zipWith_comm {f : α β γ} {as : Array α} {bs : Array β} :
theorem zipWith_comm (f : α β γ) (as : Array α) (bs : Array β) :
zipWith f as bs = zipWith (fun b a => f a b) bs as := by
cases as
cases bs
simpa using List.zipWith_comm
simpa using List.zipWith_comm _ _ _
theorem zipWith_comm_of_comm {f : α α β} (comm : x y : α, f x y = f y x) {xs ys : Array α} :
theorem zipWith_comm_of_comm (f : α α β) (comm : x y : α, f x y = f y x) (xs ys : Array α) :
zipWith f xs ys = zipWith f ys xs := by
rw [zipWith_comm]
simp only [comm]
@[simp]
theorem zipWith_self {f : α α δ} {xs : Array α} : zipWith f xs xs = xs.map fun a => f a a := by
theorem zipWith_self (f : α α δ) (xs : Array α) : zipWith f xs xs = xs.map fun a => f a a := by
cases xs
simp
@@ -74,31 +74,31 @@ theorem getElem?_zip_eq_some {as : Array α} {bs : Array β} {z : α × β} {i :
exact _, _, h₀, h₁, rfl
@[simp]
theorem zipWith_map {μ} {f : γ δ μ} {g : α γ} {h : β δ} {as : Array α} {bs : Array β} :
theorem zipWith_map {μ} (f : γ δ μ) (g : α γ) (h : β δ) (as : Array α) (bs : Array β) :
zipWith f (as.map g) (bs.map h) = zipWith (fun a b => f (g a) (h b)) as bs := by
cases as
cases bs
simp [List.zipWith_map]
theorem zipWith_map_left {as : Array α} {bs : Array β} {f : α α'} {g : α' β γ} :
theorem zipWith_map_left (as : Array α) (bs : Array β) (f : α α') (g : α' β γ) :
zipWith g (as.map f) bs = zipWith (fun a b => g (f a) b) as bs := by
cases as
cases bs
simp [List.zipWith_map_left]
theorem zipWith_map_right {as : Array α} {bs : Array β} {f : β β'} {g : α β' γ} :
theorem zipWith_map_right (as : Array α) (bs : Array β) (f : β β') (g : α β' γ) :
zipWith g as (bs.map f) = zipWith (fun a b => g a (f b)) as bs := by
cases as
cases bs
simp [List.zipWith_map_right]
theorem zipWith_foldr_eq_zip_foldr {f : α β γ} {i : δ} :
theorem zipWith_foldr_eq_zip_foldr {f : α β γ} (i : δ):
(zipWith f as bs).foldr g i = (zip as bs).foldr (fun p r => g (f p.1 p.2) r) i := by
cases as
cases bs
simp [List.zipWith_foldr_eq_zip_foldr]
theorem zipWith_foldl_eq_zip_foldl {f : α β γ} {i : δ} :
theorem zipWith_foldl_eq_zip_foldl {f : α β γ} (i : δ):
(zipWith f as bs).foldl g i = (zip as bs).foldl (fun r p => g r (f p.1 p.2)) i := by
cases as
cases bs
@@ -108,7 +108,7 @@ theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} :
theorem zipWith_eq_empty_iff {f : α β γ} {as : Array α} {bs : Array β} : zipWith f as bs = #[] as = #[] bs = #[] := by
cases as <;> cases bs <;> simp
theorem map_zipWith {δ : Type _} {f : α β} {g : γ δ α} {cs : Array γ} {ds : Array δ} :
theorem map_zipWith {δ : Type _} (f : α β) (g : γ δ α) (cs : Array γ) (ds : Array δ) :
map f (zipWith g cs ds) = zipWith (fun x y => f (g x y)) cs ds := by
cases cs
cases ds
@@ -124,7 +124,7 @@ theorem extract_zipWith : (zipWith f as bs).extract i j = zipWith f (as.extract
cases bs
simp [List.drop_zipWith, List.take_zipWith]
theorem zipWith_append {f : α β γ} {as as' : Array α} {bs bs' : Array β}
theorem zipWith_append (f : α β γ) (as as' : Array α) (bs bs' : Array β)
(h : as.size = bs.size) :
zipWith f (as ++ as') (bs ++ bs') = zipWith f as bs ++ zipWith f as' bs' := by
cases as
@@ -149,20 +149,17 @@ 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_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
@[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 [ 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 β} :
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
cases bs
simp [List.map_uncurry_zip_eq_zipWith]
theorem map_zip_eq_zipWith {f : α × β γ} {as : Array α} {bs : Array β} :
theorem map_zip_eq_zipWith (f : α × β γ) (as : Array α) (bs : Array β) :
map f (as.zip bs) = zipWith (Function.curry f) as bs := by
cases as
cases bs
@@ -203,21 +200,21 @@ theorem getElem_zip {as : Array α} {bs : Array β} {i : Nat} {h : i < (zip as b
(as[i]'(lt_size_left_of_zip h), bs[i]'(lt_size_right_of_zip h)) :=
getElem_zipWith (hi := by simpa using h)
theorem zip_eq_zipWith {as : Array α} {bs : Array β} : zip as bs = zipWith Prod.mk as bs := by
theorem zip_eq_zipWith (as : Array α) (bs : Array β) : zip as bs = zipWith Prod.mk as bs := by
cases as
cases bs
simp [List.zip_eq_zipWith]
theorem zip_map {f : α γ} {g : β δ} {as : Array α} {bs : Array β} :
theorem zip_map (f : α γ) (g : β δ) (as : Array α) (bs : Array β) :
zip (as.map f) (bs.map g) = (zip as bs).map (Prod.map f g) := by
cases as
cases bs
simp [List.zip_map]
theorem zip_map_left {f : α γ} {as : Array α} {bs : Array β} :
theorem zip_map_left (f : α γ) (as : Array α) (bs : Array β) :
zip (as.map f) bs = (zip as bs).map (Prod.map f id) := by rw [ zip_map, map_id]
theorem zip_map_right {f : β γ} {as : Array α} {bs : Array β} :
theorem zip_map_right (f : β γ) (as : Array α) (bs : Array β) :
zip as (bs.map f) = (zip as bs).map (Prod.map id f) := by rw [ zip_map, map_id]
theorem zip_append {as bs : Array α} {cs ds : Array β} (_h : as.size = cs.size) :
@@ -228,7 +225,7 @@ theorem zip_append {as bs : Array α} {cs ds : Array β} (_h : as.size = cs.size
cases ds
simp_all [List.zip_append]
theorem zip_map' {f : α β} {g : α γ} {xs : Array α} :
theorem zip_map' (f : α β) (g : α γ) (xs : Array α) :
zip (xs.map f) (xs.map g) = xs.map fun a => (f a, g a) := by
cases xs
simp [List.zip_map']
@@ -238,25 +235,25 @@ theorem of_mem_zip {a b} {as : Array α} {bs : Array β} : (a, b) ∈ zip as bs
cases bs
simpa using List.of_mem_zip
theorem map_fst_zip {as : Array α} {bs : Array β} (h : as.size bs.size) :
theorem map_fst_zip (as : Array α) (bs : Array β) (h : as.size bs.size) :
map Prod.fst (zip as bs) = as := by
cases as
cases bs
simp_all [List.map_fst_zip]
theorem map_snd_zip {as : Array α} {bs : Array β} (h : bs.size as.size) :
theorem map_snd_zip (as : Array α) (bs : Array β) (h : bs.size as.size) :
map Prod.snd (zip as bs) = bs := by
cases as
cases bs
simp_all [List.map_snd_zip]
theorem map_prod_left_eq_zip {xs : Array α} {f : α β} :
theorem map_prod_left_eq_zip {xs : Array α} (f : α β) :
(xs.map fun x => (x, f x)) = xs.zip (xs.map f) := by
rw [ zip_map']
congr
simp
theorem map_prod_right_eq_zip {xs : Array α} {f : α β} :
theorem map_prod_right_eq_zip {xs : Array α} (f : α β) :
(xs.map fun x => (f x, x)) = (xs.map f).zip xs := by
rw [ zip_map']
congr
@@ -273,14 +270,11 @@ 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_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
@[simp] theorem zip_mkArray {a : α} {b : β} {m n : Nat} :
zip (mkArray m a) (mkArray n b) = mkArray (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 β} :
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
cases bs
@@ -298,37 +292,34 @@ theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
simp [List.getElem?_zipWithAll]
rfl
theorem zipWithAll_map {μ} {f : Option γ Option δ μ} {g : α γ} {h : β δ} {as : Array α} {bs : Array β} :
theorem zipWithAll_map {μ} (f : Option γ Option δ μ) (g : α γ) (h : β δ) (as : Array α) (bs : Array β) :
zipWithAll f (as.map g) (bs.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) as bs := by
cases as
cases bs
simp [List.zipWithAll_map]
theorem zipWithAll_map_left {as : Array α} {bs : Array β} {f : α α'} {g : Option α' Option β γ} :
theorem zipWithAll_map_left (as : Array α) (bs : Array β) (f : α α') (g : Option α' Option β γ) :
zipWithAll g (as.map f) bs = zipWithAll (fun a b => g (f <$> a) b) as bs := by
cases as
cases bs
simp [List.zipWithAll_map_left]
theorem zipWithAll_map_right {as : Array α} {bs : Array β} {f : β β'} {g : Option α Option β' γ} :
theorem zipWithAll_map_right (as : Array α) (bs : Array β) (f : β β') (g : Option α Option β' γ) :
zipWithAll g as (bs.map f) = zipWithAll (fun a b => g a (f <$> b)) as bs := by
cases as
cases bs
simp [List.zipWithAll_map_right]
theorem map_zipWithAll {δ : Type _} {f : α β} {g : Option γ Option δ α} {cs : Array γ} {ds : Array δ} :
theorem map_zipWithAll {δ : Type _} (f : α β) (g : Option γ Option δ α) (cs : Array γ) (ds : Array δ) :
map f (zipWithAll g cs ds) = zipWithAll (fun x y => f (g x y)) cs ds := by
cases cs
cases ds
simp [List.map_zipWithAll]
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
zipWithAll f (mkArray n a) (mkArray n b) = mkArray 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
@@ -337,11 +328,10 @@ abbrev zipWithAll_mkArray := @zipWithAll_replicate
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
induction l <;> simp_all
theorem unzip_eq_map {xs : Array (α × β)} : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
theorem unzip_eq_map (xs : Array (α × β)) : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
cases xs
simp [List.unzip_eq_map]
-- The argument `xs` is explicit so we can rewrite from right to left.
theorem zip_unzip (xs : Array (α × β)) : zip (unzip xs).1 (unzip xs).2 = xs := by
cases xs
simp only [List.unzip_toArray, Prod.map_fst, Prod.map_snd, List.zip_toArray, List.zip_unzip]
@@ -370,9 +360,6 @@ 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_replicate {n : Nat} {a : α} {b : β} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
@[simp] theorem unzip_mkArray {n : Nat} {a : α} {b : β} :
unzip (mkArray n (a, b)) = (mkArray n a, mkArray n b) := by
ext1 <;> simp
@[deprecated unzip_replicate (since := "2025-03-18")]
abbrev unzip_mkArray := @unzip_replicate

View File

@@ -49,14 +49,6 @@ theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = fal
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b b == c a == c :=
PartialEquivBEq.trans
theorem BEq.congr_left [BEq α] [PartialEquivBEq α] {a b c : α} (h : a == b) :
(a == c) = (b == c) :=
Bool.eq_iff_iff.mpr BEq.trans (BEq.symm h), BEq.trans h
theorem BEq.congr_right [BEq α] [PartialEquivBEq α] {a b c : α} (h : b == c) :
(a == b) = (a == c) :=
Bool.eq_iff_iff.mpr fun h' => BEq.trans h' h, fun h' => BEq.trans h' (BEq.symm h)
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
(a == b) = false b == c (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))

View File

@@ -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-LIB v2.
of SMT-LIBv2.
-/
set_option linter.missingDocs true
@@ -33,7 +33,7 @@ section Nat
instance natCastInst : NatCast (BitVec w) := BitVec.ofNat w
/-- Theorem for normalizing the bitvector literal representation. -/
/-- Theorem for normalizing the bit vector literal representation. -/
-- TODO: This needs more usage data to assess which direction the simp should go.
@[simp, bitvec_to_nat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
@@ -48,7 +48,7 @@ section subsingleton
instance : Subsingleton (BitVec 0) where
allEq := by intro 0, _ 0, _; rfl
/-- The empty bitvector. -/
/-- The empty bitvector -/
abbrev nil : BitVec 0 := 0
/-- Every bitvector of length 0 is equal to `nil`, i.e., there is only one empty bitvector -/
@@ -58,11 +58,11 @@ end subsingleton
section zero_allOnes
/-- Returns a bitvector of size `n` where all bits are `0`. -/
/-- Return a bitvector `0` of size `n`. This is the bitvector with all zero bits. -/
protected def zero (n : Nat) : BitVec n := .ofNatLT 0 (Nat.two_pow_pos n)
instance : Inhabited (BitVec n) where default := .zero n
/-- Returns a bitvector of size `n` where all bits are `1`. -/
/-- Bit vector of size `n` where all bits are `1`s -/
def allOnes (n : Nat) : BitVec n :=
.ofNatLT (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
@@ -71,36 +71,42 @@ end zero_allOnes
section getXsb
/--
Returns the `i`th least significant bit.
Return the `i`-th least significant bit.
This will be renamed `getLsb` after the existing deprecated alias is removed.
-/
@[inline] def getLsb' (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
/-- Returns the `i`th least significant bit, or `none` if `i ≥ w`. -/
/-- Return the `i`-th least significant bit or `none` if `i ≥ w`. -/
@[inline] def getLsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getLsb' x i, h) else none
/--
Returns the `i`th most significant bit.
Return the `i`-th most significant bit.
This will be renamed `BitVec.getMsb` after the existing deprecated alias is removed.
This will be renamed `getMsb` after the existing deprecated alias is removed.
-/
@[inline] def getMsb' (x : BitVec w) (i : Fin w) : Bool := x.getLsb' w-1-i, by omega
/-- Returns the `i`th most significant bit or `none` if `i ≥ w`. -/
/-- Return the `i`-th most significant bit or `none` if `i ≥ w`. -/
@[inline] def getMsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getMsb' x i, h) else none
/-- Returns the `i`th least significant bit or `false` if `i ≥ w`. -/
/-- Return the `i`-th least significant bit or `false` if `i ≥ w`. -/
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
x.toNat.testBit i
/-- Returns the `i`th most significant bit, or `false` if `i ≥ w`. -/
@[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)
/-- Returns the most significant bit in a bitvector. -/
@[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
end getXsb
@@ -129,22 +135,14 @@ end getElem
section Int
/--
Interprets the bitvector as an integer stored in two's complement form.
-/
/-- Interpret the bitvector as an integer stored in two's complement form. -/
protected def toInt (x : BitVec n) : Int :=
if 2 * x.toNat < 2^n then
x.toNat
else
(x.toNat : Int) - (2^n : Nat)
/--
Converts an integer to its two's complement representation as a bitvector of the given width `n`,
over- and underflowing as needed.
The underlying `Nat` is `(2^n + (i mod 2^n)) mod 2^n`. Converting the bitvector back to an `Int`
with `BitVec.toInt` results in the value `i.bmod (2^n)`.
-/
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLT (i % (Int.ofNat (2^n))).toNat (by
apply (Int.toNat_lt _).mpr
· apply Int.emod_lt_of_pos
@@ -160,7 +158,7 @@ end Int
section Syntax
/-- Notation for bitvector literals. `i#n` is a shorthand for `BitVec.ofNat n i`. -/
/-- Notation for bit vector literals. `i#n` is a shorthand for `BitVec.ofNat n i`. -/
syntax:max num noWs "#" noWs term:max : term
macro_rules | `($i:num#$n) => `(BitVec.ofNat $n $i)
@@ -169,16 +167,16 @@ recommended_spelling "zero" for "0#n" in [BitVec.ofNat, «term__#__»]
/-- not `ofNat_one` -/
recommended_spelling "one" for "1#n" in [BitVec.ofNat, «term__#__»]
/-- Unexpander for bitvector literals. -/
/-- Unexpander for bit vector literals. -/
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
| `($(_) $n $i:num) => `($i:num#$n)
| _ => throw ()
/-- Notation for bitvector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLT i lt`. -/
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLT i lt`. -/
scoped syntax:max term:max noWs "#'" noWs term:max : term
macro_rules | `($i#'$p) => `(BitVec.ofNatLT $i $p)
/-- Unexpander for bitvector literals without truncation. -/
/-- Unexpander for bit vector literals without truncation. -/
@[app_unexpander BitVec.ofNatLT] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
| `($(_) $i $p) => `($i#'$p)
| _ => throw ()
@@ -187,11 +185,7 @@ end Syntax
section repr_toString
/--
Converts a bitvector into a fixed-width hexadecimal number with enough digits to represent it.
If `n` is `0`, then one digit is returned. Otherwise, `⌊(n + 3) / 4⌋` digits are returned.
-/
/-- Convert bitvector into a fixed-width hex number. -/
protected def toHex {n : Nat} (x : BitVec n) : String :=
let s := (Nat.toDigits 16 x.toNat).asString
let t := (List.replicate ((n+3) / 4 - s.length) '0').asString
@@ -205,63 +199,63 @@ end repr_toString
section arithmetic
/--
Negation of bitvectors. This can be interpreted as either signed or unsigned negation modulo `2^n`.
Usually accessed via the `-` prefix operator.
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
/--
Returns the absolute value of a signed bitvector.
Return the absolute value of a signed bitvector.
-/
protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
/--
Multiplies two bitvectors. This can be interpreted as either signed or unsigned multiplication
modulo `2^n`. Usually accessed via the `*` operator.
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
/--
Unsigned division of bitvectors using the Lean convention where division by zero returns zero.
Usually accessed via the `/` operator.
Unsigned division for bit vectors using the Lean convention where division by zero returns zero.
-/
def udiv (x y : BitVec n) : BitVec n :=
(x.toNat / y.toNat)#'(Nat.lt_of_le_of_lt (Nat.div_le_self _ _) x.isLt)
instance : Div (BitVec n) := .udiv
/--
Unsigned modulo for bitvectors. Usually accessed via the `%` operator.
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)
instance : Mod (BitVec n) := .umod
/--
Unsigned division of bitvectors using the
[SMT-LIB convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml),
where division by zero returns `BitVector.allOnes n`.
Unsigned division for bit vectors using the
[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
/--
Signed T-division (using the truncating rounding convention) for bitvectors. This function obeys the
Lean convention that division by zero returns zero.
Signed t-division for bit vectors using the Lean convention where division
by zero returns zero.
Examples:
* `(7#4).sdiv 2 = 3#4`
* `(-9#4).sdiv 2 = -4#4`
* `(5#4).sdiv -2 = -2#4`
* `(-7#4).sdiv (-2) = 3#4`
```lean
sdiv 7#4 2 = 3#4
sdiv (-9#4) 2 = -4#4
sdiv 5#4 -2 = -2#4
sdiv (-7#4) (-2) = 3#4
```
-/
def sdiv (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with
@@ -271,13 +265,11 @@ def sdiv (x y : BitVec n) : BitVec n :=
| true, true => udiv (.neg x) (.neg y)
/--
Signed division for bitvectors using the SMT-LIB using the
[SMT-LIB convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml),
where division by zero returns `BitVector.allOnes n`.
Signed division for bit vectors using SMTLIB rules for division by zero.
Specifically, `x.smtSDiv 0 = if x >= 0 then -1 else 1`
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
@@ -289,7 +281,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
@@ -301,7 +293,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
@@ -319,7 +311,7 @@ end arithmetic
section bool
/-- Turns a `Bool` into a bitvector of length `1`. -/
/-- Turn a `Bool` into a bitvector of length `1` -/
def ofBool (b : Bool) : BitVec 1 := cond b 1 0
@[simp] theorem ofBool_false : ofBool false = 0 := by trivial
@@ -333,34 +325,34 @@ end bool
section relations
/--
Unsigned less-than for bitvectors.
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 bitvectors.
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
/--
Signed less-than for bitvectors.
Signed less-than for bit vectors.
SMT-LIB name: `bvslt`.
Examples:
* `BitVec.slt 6#4 7 = true`
* `BitVec.slt 7#4 8 = false`
```lean
BitVec.slt 6#4 7 = true
BitVec.slt 7#4 8 = false
```
SMT-Lib name: `bvslt`.
-/
protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt
/--
Signed less-than-or-equal-to for bitvectors.
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
@@ -368,13 +360,7 @@ end relations
section cast
/--
If two natural numbers `n` and `m` are equal, then a bitvector of width `n` is also a bitvector of
width `m`.
Using `x.cast eq` should be preferred over `eq ▸ x` because there are special-purpose `simp` lemmas
that can more consistently simplify `BitVec.cast` away.
-/
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLT x.toNat (eq x.isLt)
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
@@ -388,26 +374,23 @@ that can more consistently simplify `BitVec.cast` away.
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : x.cast h = x := rfl
/--
Extracts the bits `start` to `start + len - 1` from a bitvector of size `n` to yield a
new bitvector of size `len`. If `start + len > n`, then the bitvector is zero-extended.
Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to yield a
new bitvector of size `len`. If `start + len > n`, then the vector will be zero-padded in the
high bits.
-/
def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toNat >>> start)
/--
Extracts the bits from `hi` down to `lo` (both inclusive) from a bitvector, which is implicitly
zero-extended if necessary.
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`.
The resulting bitvector has 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
/--
Increases the width of a bitvector to one that is at least as large by zero-extending it.
This is a constant-time operation because the underlying `Nat` is unmodified; because the new width
is at least as large as the old one, no overflow is possible.
A version of `setWidth` that requires a proof the new width is at least as large,
and is a computational noop.
-/
def setWidth' {n w : Nat} (le : n w) (x : BitVec n) : BitVec w :=
x.toNat#'(by
@@ -417,7 +400,8 @@ def setWidth' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
@[deprecated setWidth' (since := "2024-09-18"), inherit_doc setWidth'] abbrev zeroExtend' := @setWidth'
/--
Returns `zeroExtend (w+n) x <<< n` without needing to compute `x % 2^(2+n)`.
`shiftLeftZeroExtend x n` returns `zeroExtend (w+n) x <<< n` without
needing to compute `x % 2^(2+n)`.
-/
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w + m) :=
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w + m) := by
@@ -426,20 +410,12 @@ def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w + m) :=
exact (Nat.two_pow_pos m)
(msbs.toNat <<< m)#'(shiftLeftLt msbs.isLt m)
/--
Transforms a bitvector of length `w` into a bitvector of length `v`, padding with `0` as needed.
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`.
The specific behavior depends on the relationship between the starting width `w` and the final width
`v`:
* If `v > w`, it is zero-extended; the high bits are padded with zeroes until the bitvector has `v`
bits.
* If `v = w`, the bitvector is returned unchanged.
* If `v < w`, the high bits are truncated.
`BitVec.setWidth`, `BitVec.zeroExtend`, and `BitVec.truncate` are aliases for this operation.
SMT-LIB name: `zero_extend`.
SMT-Lib name: `zero_extend`.
-/
def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
if h : w v then
@@ -447,19 +423,29 @@ def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
else
.ofNat v x.toNat
@[inherit_doc setWidth]
/--
Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
- truncating the high bits, if `v < w`.
SMT-Lib name: `zero_extend`.
-/
abbrev zeroExtend := @setWidth
@[inherit_doc setWidth]
/--
Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
- truncating the high bits, if `v < w`.
SMT-Lib name: `zero_extend`.
-/
abbrev truncate := @setWidth
/--
Transforms a bitvector of length `w` into a bitvector of length `v`, padding as needed with the most
significant bit's value.
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.
If `x` is an empty bitvector, 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
@@ -468,68 +454,69 @@ end cast
section bitwise
/--
Bitwise and for bitvectors. Usually accessed via the `&&&` operator.
Bitwise AND for bit vectors.
SMT-LIB name: `bvand`.
```lean
0b1010#4 &&& 0b0110#4 = 0b0010#4
```
Example:
* `0b1010#4 &&& 0b0110#4 = 0b0010#4`
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)
instance : AndOp (BitVec w) := .and
/--
Bitwise or for bitvectors. Usually accessed via the `|||` operator.
Bitwise OR for bit vectors.
SMT-LIB name: `bvor`.
```lean
0b1010#4 ||| 0b0110#4 = 0b1110#4
```
Example:
* `0b1010#4 ||| 0b0110#4 = 0b1110#4`
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)
instance : OrOp (BitVec w) := .or
/--
Bitwise xor for bitvectors. Usually accessed via the `^^^` operator.
Bitwise XOR for bit vectors.
SMT-LIB name: `bvxor`.
```lean
0b1010#4 ^^^ 0b0110#4 = 0b1100#4
```
Example:
* `0b1010#4 ^^^ 0b0110#4 = 0b1100#4`
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)
instance : Xor (BitVec w) := .xor
/--
Bitwise complement for bitvectors. Usually accessed via the `~~~` prefix operator.
Bitwise NOT for bit vectors.
SMT-LIB name: `bvnot`.
Example:
* `~~~(0b0101#4) == 0b1010`
```lean
~~~(0b0101#4) == 0b1010
```
SMT-Lib name: `bvnot`.
-/
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
instance : Complement (BitVec w) := .not
/--
Shifts a bitvector to the left. The low bits are filled with zeros. As a numeric operation, this is
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
/--
Shifts a bitvector to the right. This is a logical right shift - the high bits are filled with
zeros.
(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
@@ -541,12 +528,11 @@ def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
instance : HShiftRight (BitVec w) Nat (BitVec w) := .ushiftRight
/--
Shifts a bitvector to the right. This is an arithmetic right shift - the high bits are filled with
most significant bit's value.
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)
@@ -554,12 +540,11 @@ instance {n} : HShiftLeft (BitVec m) (BitVec n) (BitVec m) := ⟨fun x y => x <
instance {n} : HShiftRight (BitVec m) (BitVec n) (BitVec m) := fun x y => x >>> y.toNat
/--
Shifts a bitvector to the right. This is an arithmetic right shift - the high bits are filled with
most significant bit's value.
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
@@ -569,15 +554,13 @@ def rotateLeftAux (x : BitVec w) (n : Nat) : BitVec w :=
x <<< n ||| x >>> (w - n)
/--
Rotates the bits in a bitvector to the left.
Rotate left for bit vectors. All the bits of `x` are shifted to higher positions, with the top `n`
bits wrapping around to fill the low bits.
All the bits of `x` are shifted to higher positions, with the top `n` bits wrapping around to fill
the vacated low bits.
SMT-LIB name: `rotate_left`, except this operator uses a `Nat` shift amount.
Example:
* `(0b0011#4).rotateLeft 3 = 0b1001`
```lean
rotateLeft 0b0011#4 3 = 0b1001
```
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)
@@ -590,26 +573,21 @@ def rotateRightAux (x : BitVec w) (n : Nat) : BitVec w :=
x >>> n ||| x <<< (w - n)
/--
Rotates the bits in a bitvector to the right.
Rotate right for bit vectors. All the bits of `x` are shifted to lower positions, with the
bottom `n` bits wrapping around to fill the high bits.
All the bits of `x` are shifted to lower positions, with the bottom `n` bits wrapping around to fill
the vacated high bits.
SMT-LIB name: `rotate_right`, except this operator uses a `Nat` shift amount.
Example:
* `rotateRight 0b01001#5 1 = 0b10100`
```lean
rotateRight 0b01001#5 1 = 0b10100
```
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)
/--
Concatenates two bitvectors using the big-endian convention that the more significant
input is on the left. Usually accessed via the `++` operator.
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`.
Example:
* `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
SMT-Lib name: `concat`.
-/
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs
@@ -617,7 +595,7 @@ def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
instance : HAppend (BitVec w) (BitVec v) (BitVec (w + v)) := .append
-- TODO: write this using multiplication
/-- Concatenates `i` copies of `x` into a new vector of length `w * i`. -/
/-- `replicate i x` concatenates `i` copies of `x` into a new vector of length `w*i`. -/
def replicate : (i : Nat) BitVec w BitVec (w*i)
| 0, _ => 0#0
| n+1, x =>
@@ -636,18 +614,14 @@ result of appending a single bit to the front in the naive implementation).
def concat {n} (msbs : BitVec n) (lsb : Bool) : BitVec (n+1) := msbs ++ (ofBool lsb)
/--
Shifts all bits of `x` to the left by `1` and sets the least significant bit to `b`.
This is a non-dependent version of `BitVec.concat` that does not change the total bitwidth.
`x.shiftConcat b` shifts all bits of `x` to the left by `1` and sets the least significant bit to `b`.
It is a non-dependent version of `concat` that does not change the total bitwidth.
-/
def shiftConcat (x : BitVec n) (b : Bool) : BitVec n :=
(x.concat b).truncate n
/--
Prepends a single bit to the front of a bitvector, using big-endian order (see `append`).
The new bit is the most significant bit.
-/
/-- Prepend a single bit to the front of a bitvector, using big endian order (see `append`).
That is, the new bit is the most significant bit. -/
def cons {n} (msb : Bool) (lsbs : BitVec n) : BitVec (n+1) :=
((ofBool msb) ++ lsbs).cast (Nat.add_comm ..)
@@ -660,18 +634,15 @@ theorem ofBool_append (msb : Bool) (lsbs : BitVec w) :
rfl
/--
`twoPow w i` is the bitvector `2^i` if `i < w`, and `0` otherwise. In other words, it is 2 to the
power `i`.
From the bitwise point of view, it has the `i`th bit as `1` and all other bits as `0`.
`twoPow w i` is the bitvector `2^i` if `i < w`, and `0` otherwise.
That is, 2 to the power `i`.
For the bitwise point of view, it has the `i`th bit as `1` and all other bits as `0`.
-/
def twoPow (w : Nat) (i : Nat) : BitVec w := 1#w <<< i
end bitwise
/--
Computes a hash of a bitvector, combining 64-bit words using `mixHash`.
-/
/-- Compute a hash of a bitvector, combining 64-bit words using `mixHash`. -/
def hash (bv : BitVec n) : UInt64 :=
if n 64 then
bv.toFin.val.toUInt64
@@ -699,63 +670,35 @@ section normalization_eqs
@[simp] theorem zero_eq : BitVec.zero n = 0#n := rfl
end normalization_eqs
/-- Converts a list of `Bool`s into a big-endian `BitVec`. -/
/-- Converts a list of `Bool`s to a big-endian `BitVec`. -/
def ofBoolListBE : (bs : List Bool) BitVec bs.length
| [] => 0#0
| b :: bs => cons b (ofBoolListBE bs)
/-- Converts a list of `Bool`s into a little-endian `BitVec`. -/
/-- Converts a list of `Bool`s to a little-endian `BitVec`. -/
def ofBoolListLE : (bs : List Bool) BitVec bs.length
| [] => 0#0
| b :: bs => concat (ofBoolListLE bs) b
/-! ## Overflow -/
/--
Checks whether addition of `x` and `y` results in *unsigned* overflow.
/-- `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
/--
Checks whether addition of `x` and `y` results in *signed* overflow, treating `x` and `y` as 2's
complement signed bitvectors.
/-- `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))
/--
Checks whether subtraction of `x` and `y` results in *unsigned* overflow.
SMT-Lib name: `bvusubo`.
-/
def usubOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat < y.toNat
/--
Checks whether the subtraction of `x` and `y` results in *signed* overflow, treating `x` and `y` as
2's complement signed bitvectors.
SMT-Lib name: `bvssubo`.
-/
def ssubOverflow {w : Nat} (x y : BitVec w) : Bool :=
(x.toInt - y.toInt 2 ^ (w - 1)) || (x.toInt - y.toInt < - 2 ^ (w - 1))
/--
Checks whether the negation of a bitvector results in overflow.
For a bitvector `x` with nonzero width, this only happens if `x = intMin`.
SMT-Lib name: `bvnego`.
-/
def negOverflow {w : Nat} (x : BitVec w) : Bool :=
x.toInt == - 2 ^ (w - 1)
/- ### reverse -/
/-- Reverses the bits in a bitvector. -/
/-- Reverse the bits in a bitvector. -/
def reverse : {w : Nat} BitVec w BitVec w
| 0, x => x
| w + 1, x => concat (reverse (x.truncate w)) (x.msb)

View File

@@ -17,9 +17,7 @@ namespace BitVec
section Nat
/--
The bitvector with value `i mod 2^n`.
-/
/-- The `BitVec` with value `i mod 2^n`. -/
@[match_pattern]
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat' (2^n) i
@@ -34,18 +32,17 @@ end Nat
section arithmetic
/--
Adds two bitvectors. This can be interpreted as either signed or unsigned addition modulo `2^n`.
Usually accessed via the `+` operator.
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
/--
Subtracts one bitvector from another. This can be interpreted as either signed or unsigned subtraction
modulo `2^n`. Usually accessed via the `-` operator.
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
modulo `2^n`.
-/
protected def sub (x y : BitVec n) : BitVec n := .ofNat n ((2^n - y.toNat) + x.toNat)
instance : Sub (BitVec n) := BitVec.sub

View File

@@ -9,7 +9,7 @@ import Init.Data.Nat.Mod
import Init.Data.Int.LemmasAux
/-!
# Bit blasting of bitvectors
# Bitblasting of bitvectors
This module provides theorems for showing the equivalence between BitVec operations using
the `Fin 2^n` representation and Boolean vectors. It is still under development, but
@@ -19,21 +19,21 @@ as vectors of bits into proofs about Lean `BitVec` values.
The module is named for the bit-blasting operation in an SMT solver that converts bitvector
expressions into expressions about individual bits in each vector.
### Example: How bit blasting works for multiplication
### Example: How bitblasting works for multiplication
We explain how the lemmas here are used for bit blasting,
We explain how the lemmas here are used for bitblasting,
by using multiplication as a prototypical example.
Other bit blasters for other operations follow the same pattern.
To bit blast a multiplication of the form `x * y`,
Other bitblasters for other operations follow the same pattern.
To bitblast a multiplication of the form `x * y`,
we must unfold the above into a form that the SAT solver understands.
We assume that the solver already knows how to bit blast addition.
We assume that the solver already knows how to bitblast addition.
This is known to `bv_decide`, by exploiting the lemma `add_eq_adc`,
which says that `x + y : BitVec w` equals `(adc x y false).2`,
where `adc` builds an add-carry circuit in terms of the primitive operations
(bitwise and, bitwise or, bitwise xor) that bv_decide already understands.
In this way, we layer bit blasters on top of each other,
by reducing the multiplication bit blaster to an addition operation.
In this way, we layer bitblasters on top of each other,
by reducing the multiplication bitblaster to an addition operation.
The core lemma is given by `getLsbD_mul`:
@@ -65,7 +65,7 @@ mulRec_succ_eq
By repeatedly applying the lemmas `mulRec_zero_eq` and `mulRec_succ_eq`,
one obtains a circuit for multiplication.
Note that this circuit uses `BitVec.add`, `BitVec.getLsbD`, `BitVec.shiftLeft`.
Here, `BitVec.add` and `BitVec.shiftLeft` are (recursively) bit blasted by `bv_decide`,
Here, `BitVec.add` and `BitVec.shiftLeft` are (recursively) bitblasted by `bv_decide`,
using the lemmas `add_eq_adc` and `shiftLeft_eq_shiftLeftRec`,
and `BitVec.getLsbD` is a primitive that `bv_decide` knows how to reduce to SAT.
@@ -88,10 +88,10 @@ computes the correct value for multiplication.
To zoom out, therefore, we follow two steps:
First, we prove bitvector lemmas to unfold a high-level operation (such as multiplication)
into already bit blastable operations (such as addition and left shift).
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 bit blasting for all SMT-LIB v2 operations.
We use this workflow to implement bitblasting for all SMT-LIB2 operations.
## Main results
* `x + y : BitVec w` is `(adc x y false).2`.
@@ -109,12 +109,7 @@ open Nat Bool
namespace Bool
/--
At least two out of three Booleans are true.
This function is typically used to model addition of binary numbers, to combine a carry bit with two
addend bits.
-/
/-- At least two out of three booleans are true. -/
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c
@[simp] theorem atLeastTwo_false_left : atLeastTwo false b c = (b && c) := by simp [atLeastTwo]
@@ -483,39 +478,6 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
case zero => exact hmsb
case succ => exact getMsbD_x _ hi (by omega)
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_ne_intMin`. -/
@[simp] theorem signExtend_neg_of_le {v w : Nat} (h : w v) (b : BitVec v) :
(-b).signExtend w = -b.signExtend w := by
apply BitVec.eq_of_getElem_eq
intro i hi
simp only [getElem_signExtend, getElem_neg]
rw [dif_pos (by omega), dif_pos (by omega)]
simp only [getLsbD_signExtend, Bool.and_eq_true, decide_eq_true_eq, Bool.ite_eq_true_distrib,
Bool.bne_right_inj, decide_eq_decide]
exact fun j, hj₁, hj₂ => j, hj₁, by omega, by rwa [if_pos (by omega)],
fun j, hj₁, hj₂, hj₃ => j, hj₁, by rwa [if_pos (by omega)] at hj₃
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_le`. -/
@[simp] theorem signExtend_neg_of_ne_intMin {v w : Nat} (b : BitVec v) (hb : b intMin v) :
(-b).signExtend w = -b.signExtend w := by
refine (by omega : w v v < w).elim (fun h => signExtend_neg_of_le h b) (fun h => ?_)
apply BitVec.eq_of_toInt_eq
rw [toInt_signExtend_of_le (by omega), toInt_neg_of_ne_intMin hb, toInt_neg_of_ne_intMin,
toInt_signExtend_of_le (by omega)]
apply ne_of_apply_ne BitVec.toInt
rw [toInt_signExtend_of_le (by omega), toInt_intMin_of_pos (by omega)]
have := b.le_two_mul_toInt
have : -2 ^ w < -2 ^ v := by
apply Int.neg_lt_neg
norm_cast
rwa [Nat.pow_lt_pow_iff_right (by omega)]
have : 2 * b.toInt -2 ^ w := by omega
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} :
@@ -567,64 +529,30 @@ theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb ≠ y.msb) :
simp only [BitVec.slt, toInt_eq_msb_cond, Bool.eq_not_of_ne h, ult_eq_msb_of_msb_neq h]
cases y.msb <;> (simp; omega)
theorem slt_eq_ult {x y : BitVec w} :
theorem slt_eq_ult (x y : BitVec w) :
x.slt y = (x.msb != y.msb).xor (x.ult y) := by
by_cases h : x.msb = y.msb
· simp [h, slt_eq_ult_of_msb_eq]
· have h' : x.msb != y.msb := by simp_all
simp [slt_eq_not_ult_of_msb_neq h, h']
theorem slt_eq_not_carry {x y : BitVec w} :
theorem slt_eq_not_carry (x y : BitVec w) :
x.slt y = (x.msb == y.msb).xor (carry w x (~~~y) true) := by
simp only [slt_eq_ult, bne, ult_eq_not_carry]
cases x.msb == y.msb <;> simp
theorem sle_eq_not_slt {x y : BitVec w} : x.sle y = !y.slt x := by
theorem sle_eq_not_slt (x y : BitVec w) : x.sle y = !y.slt x := by
simp only [BitVec.sle, BitVec.slt, decide_not, decide_eq_decide]; omega
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x x.msb = false := by
simp [zero_sle_eq_not_msb]
theorem toNat_toInt_of_sle {w : Nat} {x : BitVec w} (hx : BitVec.sle 0#w x) : x.toInt.toNat = x.toNat :=
toNat_toInt_of_msb x (zero_sle_iff_msb_eq_false.1 hx)
theorem sle_eq_carry {x y : BitVec w} :
theorem sle_eq_carry (x y : BitVec w) :
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
theorem neg_slt_zero (h : 0 < w) {x : BitVec w} :
(-x).slt 0#w = ((x == intMin w) || (0#w).slt x) := by
rw [slt_zero_eq_msb, msb_neg, slt_eq_sle_and_ne, zero_sle_eq_not_msb]
apply Bool.eq_iff_iff.2
cases hmsb : x.msb with
| false => simpa [ne_intMin_of_msb_eq_false h hmsb] using Decidable.not_iff_not.2 (eq_comm)
| true =>
simp only [Bool.bne_true, Bool.not_and, Bool.or_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true,
bne_eq_false_iff_eq, Bool.false_and, Bool.or_false, beq_iff_eq,
_root_.or_iff_right_iff_imp]
rintro rfl
simp at hmsb
theorem neg_sle_zero (h : 0 < w) {x : BitVec w} :
(-x).sle 0#w = (x == intMin w || (0#w).sle x) := by
rw [sle_eq_slt_or_eq, neg_slt_zero h, sle_eq_slt_or_eq]
simp [Bool.beq_eq_decide_eq (-x), Bool.beq_eq_decide_eq _ x, Eq.comm (a := x), Bool.or_assoc]
theorem sle_eq_ule {x y : BitVec w} : x.sle y = (x.msb != y.msb ^^ x.ule y) := by
rw [sle_eq_not_slt, slt_eq_ult, Bool.xor_not, ule_eq_not_ult, bne_comm]
theorem sle_eq_ule_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) : x.sle y = x.ule y := by
simp [BitVec.sle_eq_ule, h]
/-! ### mul recurrence for bit blasting -/
/-! ### mul recurrence for bitblasting -/
/--
A recurrence that describes multiplication as repeated addition.
This function is useful for bit blasting multiplication.
Is useful for bitblasting multiplication.
-/
def mulRec (x y : BitVec w) (s : Nat) : BitVec w :=
let cur := if y.getLsbD s then (x <<< s) else 0
@@ -707,12 +635,6 @@ theorem getLsbD_mul (x y : BitVec w) (i : Nat) :
· simp
· omega
theorem mul_eq_mulRec {x y : BitVec w} :
x * y = mulRec x y w := by
apply eq_of_getLsbD_eq
intro i hi
apply getLsbD_mul
theorem getMsbD_mul (x y : BitVec w) (i : Nat) :
(x * y).getMsbD i = (mulRec x y w).getMsbD i := by
simp only [mulRec_eq_mul_signExtend_setWidth]
@@ -724,16 +646,15 @@ theorem getElem_mul {x y : BitVec w} {i : Nat} (h : i < w) :
(x * y)[i] = (mulRec x y w)[i] := by
simp [mulRec_eq_mul_signExtend_setWidth]
/-! ## shiftLeft recurrence for bit blasting -/
/-! ## shiftLeft recurrence for bitblasting -/
/--
Shifts `x` to the left by the first `n` bits of `y`.
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
The theorem `BitVec.shiftLeft_eq_shiftLeftRec` proves the equivalence of `(x <<< y)` and
`BitVec.shiftLeftRec x y`.
The theorem `shiftLeft_eq_shiftLeftRec` proves the equivalence of `(x <<< y)` and `shiftLeftRec`.
Together with equations `BitVec.shiftLeftRec_zero` and `BitVec.shiftLeftRec_succ`, this allows
`BitVec.shiftLeft` to be unfolded into a circuit for bit blasting.
Together with equations `shiftLeftRec_zero`, `shiftLeftRec_succ`,
this allows us to unfold `shiftLeft` into a circuit for bitblasting.
-/
def shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
@@ -787,7 +708,7 @@ theorem shiftLeftRec_eq {x : BitVec w₁} {y : BitVec w₂} {n : Nat} :
/--
Show that `x <<< y` can be written in terms of `shiftLeftRec`.
This can be unfolded in terms of `shiftLeftRec_zero`, `shiftLeftRec_succ` for bit blasting.
This can be unfolded in terms of `shiftLeftRec_zero`, `shiftLeftRec_succ` for bitblasting.
-/
theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
x <<< y = shiftLeftRec x y (w₂ - 1) := by
@@ -795,7 +716,7 @@ theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
· simp [of_length_zero]
· simp [shiftLeftRec_eq]
/-! # udiv/urem recurrence for bit blasting
/-! # udiv/urem recurrence for bitblasting
In order to prove the correctness of the division algorithm on the integers,
one shows that `n.div d = q` and `n.mod d = r` iff `n = d * q + r` and `0 ≤ r < d`.
@@ -1002,9 +923,8 @@ def DivModState.wr_lt_w {qr : DivModState w} (h : qr.Poised args) : qr.wr < w :=
/-! ### Division shift subtractor -/
/--
One round of the division algorithm. It tries to perform a subtract shift.
This should only be called when `r.msb = false`, so it will not overflow.
One round of the division algorithm, that tries to perform a subtract shift.
Note that this should only be called when `r.msb = false`, so we will not overflow.
-/
def divSubtractShift (args : DivModArgs w) (qr : DivModState w) : DivModState w :=
let {n, d} := args
@@ -1094,7 +1014,7 @@ theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
/-! ### Core division algorithm circuit -/
/-- A recursive definition of division for bit blasting, in terms of a shift-subtraction circuit. -/
/-- A recursive definition of division for bitblasting, in terms of a shift-subtraction circuit. -/
def divRec {w : Nat} (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
DivModState w :=
match m with
@@ -1191,12 +1111,10 @@ theorem getMsbD_udiv (n d : BitVec w) (hd : 0#w < d) (i : Nat) :
/- ### Arithmetic shift right (sshiftRight) recurrence -/
/--
Shifts `x` arithmetically (signed) to the right by the first `n` bits of `y`.
The theorem `BitVec.sshiftRight_eq_sshiftRightRec` proves the equivalence of `(x.sshiftRight y)` and
`BitVec.sshiftRightRec x y`. Together with equations `BitVec.sshiftRightRec_zero`, and
`BitVec.sshiftRightRec_succ`, this allows `BitVec.sshiftRight` to be unfolded into a circuit for
bit blasting.
`sshiftRightRec x y n` shifts `x` arithmetically/signed to the right by the first `n` bits of `y`.
The theorem `sshiftRight_eq_sshiftRightRec` proves the equivalence of `(x.sshiftRight y)` and `sshiftRightRec`.
Together with equations `sshiftRightRec_zero`, `sshiftRightRec_succ`,
this allows us to unfold `sshiftRight` into a circuit for bitblasting.
-/
def sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
@@ -1243,7 +1161,7 @@ theorem sshiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
/--
Show that `x.sshiftRight y` can be written in terms of `sshiftRightRec`.
This can be unfolded in terms of `sshiftRightRec_zero_eq`, `sshiftRightRec_succ_eq` for bit blasting.
This can be unfolded in terms of `sshiftRightRec_zero_eq`, `sshiftRightRec_succ_eq` for bitblasting.
-/
theorem sshiftRight_eq_sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
(x.sshiftRight' y).getLsbD i = (sshiftRightRec x y (w₂ - 1)).getLsbD i := by
@@ -1251,16 +1169,16 @@ theorem sshiftRight_eq_sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
· simp [of_length_zero]
· simp [sshiftRightRec_eq]
/- ### Logical shift right (ushiftRight) recurrence for bit blasting -/
/- ### Logical shift right (ushiftRight) recurrence for bitblasting -/
/--
Shifts `x` logically to the right by the first `n` bits of `y`.
`ushiftRightRec x y n` shifts `x` logically to the right by the first `n` bits of `y`.
The theorem `BitVec.shiftRight_eq_ushiftRightRec` proves the equivalence
of `(x >>> y)` and `BitVec.ushiftRightRec`.
The theorem `shiftRight_eq_ushiftRightRec` proves the equivalence
of `(x >>> y)` and `ushiftRightRec`.
Together with equations `BitVec.ushiftRightRec_zero` and `BitVec.ushiftRightRec_succ`,
this allows `BitVec.ushiftRight` to be unfolded into a circuit for bit blasting.
Together with equations `ushiftRightRec_zero`, `ushiftRightRec_succ`,
this allows us to unfold `ushiftRight` into a circuit for bitblasting.
-/
def ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
@@ -1306,7 +1224,7 @@ theorem ushiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
/--
Show that `x >>> y` can be written in terms of `ushiftRightRec`.
This can be unfolded in terms of `ushiftRightRec_zero`, `ushiftRightRec_succ` for bit blasting.
This can be unfolded in terms of `ushiftRightRec_zero`, `ushiftRightRec_succ` for bitblasting.
-/
theorem shiftRight_eq_ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
x >>> y = ushiftRightRec x y (w₂ - 1) := by
@@ -1326,38 +1244,14 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
simp only [saddOverflow]
rcases w with _|w
· revert x y; decide
· have := le_two_mul_toInt (x := x); have := two_mul_toInt_lt (x := x)
have := le_two_mul_toInt (x := y); have := two_mul_toInt_lt (x := y)
· have := le_toInt (x := x); have := toInt_lt (x := x)
have := le_toInt (x := y); have := toInt_lt (x := y)
simp only [ decide_or, msb_eq_toInt, decide_beq_decide, toInt_add, decide_not, decide_and,
decide_eq_decide]
rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)]
simp
omega
theorem usubOverflow_eq {w : Nat} (x y : BitVec w) :
usubOverflow x y = decide (x < y) := rfl
theorem ssubOverflow_eq {w : Nat} (x y : BitVec w) :
ssubOverflow x y = ((!x.msb && y.msb && (x - y).msb) || (x.msb && !y.msb && !(x - y).msb)) := by
simp only [ssubOverflow]
rcases w with _|w
· simp [BitVec.of_length_zero]
· have h₁ := BitVec.toInt_sub_toInt_lt_twoPow_iff (x := x) (y := y)
have h₂ := BitVec.twoPow_le_toInt_sub_toInt_iff (x := x) (y := y)
simp only [Nat.add_one_sub_one] at h₁ h₂
simp only [Nat.add_one_sub_one, ge_iff_le, msb_eq_toInt, decide_not, Int.not_lt, toInt_sub]
simp only [bool_to_prop]
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) :
@@ -1400,361 +1294,4 @@ theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x,
have := congrArg g h'
simpa [h] using this
@[simp]
theorem ne_intMin_of_lt_of_msb_false {x : BitVec w} (hw : 0 < w) (hx : x.msb = false) :
x intMin w := by
have := toNat_lt_of_msb_false hx
simp [toNat_eq, Nat.two_pow_pred_mod_two_pow hw]
omega
@[simp]
theorem ne_zero_of_msb_true {x : BitVec w} (hx : x.msb = true) :
x 0#w := by
have := Nat.two_pow_pos (w-1)
have := le_toNat_of_msb_true hx
simp [toNat_eq]
omega
@[simp]
theorem msb_neg_of_ne_intMin_of_ne_zero {x : BitVec w} (h : x intMin w) (h' : x 0#w) :
(-x).msb = !x.msb := by
simp only [msb_neg, bool_to_prop]
simp [h, h']
@[simp]
theorem udiv_intMin_of_msb_false {x : BitVec w} (h : x.msb = false) :
x / intMin w = 0#w := by
by_cases hw : w = 0
· subst hw
decide +revert
have wpos : 0 < w := by omega
have := Nat.two_pow_pos (w-1)
simp [toNat_eq, wpos]
rw [Nat.div_eq_zero_iff_lt (by omega)]
exact toNat_lt_of_msb_false h
theorem sdiv_intMin {x : BitVec w} :
x.sdiv (intMin w) = if x = intMin w then 1#w else 0#w := by
by_cases hw : w = 0
· subst hw
decide +revert
have wpos : 0 < w := by omega
by_cases h : x = intMin w
· subst h
simp
omega
· simp only [sdiv_eq, msb_intMin, show 0 < w by omega, h]
have := Nat.two_pow_pos (w-1)
by_cases hx : x.msb
· simp [msb_neg_of_ne_intMin_of_ne_zero (by simp [h])
(BitVec.ne_zero_of_msb_true hx), hx]
· simp [hx]
theorem sdiv_neg {x y : BitVec w} (h : y intMin w) :
x.sdiv (-y) = -(x.sdiv y) := by
by_cases h' : y = 0#w
· subst h'
simp
· simp only [BitVec.sdiv, msb_neg_of_ne_intMin_of_ne_zero h (by simp [h'])]
cases x.msb <;> cases y.msb <;> simp
theorem neg_sdiv {x y : BitVec w} (h : x intMin w) :
(-x).sdiv y = -(x.sdiv y) := by
by_cases hx0 : x = 0#w
· subst hx0
simp
· simp only [BitVec.sdiv, msb_neg_of_ne_intMin_of_ne_zero h (by simp [hx0])]
cases x.msb <;> cases y.msb <;> simp
theorem neg_sdiv_neg {x y : BitVec w} (h : x intMin w) :
(-x).sdiv (-y) = x.sdiv y := by
by_cases h' : y = intMin w
· subst h'
simp [sdiv_intMin, neg_intMin]
· by_cases hy0 : y = 0#w
· subst hy0
simp
· by_cases hx0 : x = 0#w
· subst hx0
simp
· simp only [BitVec.sdiv,
msb_neg_of_ne_intMin_of_ne_zero h (by simp [hx0]),
msb_neg_of_ne_intMin_of_ne_zero h' (by simp [hy0])]
cases x.msb <;> cases y.msb <;> simp
theorem intMin_eq_neg_two_pow : intMin w = BitVec.ofInt w (-2 ^ (w - 1)) := by
apply BitVec.eq_of_toInt_eq
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; simp [BitVec.toInt_zero_length]) (fun hw => ?_)
rw [BitVec.toInt_intMin_of_pos hw, BitVec.toInt_ofInt_eq_self hw (Int.le_refl _)]
have := Nat.two_pow_pos (w - 1)
norm_cast
omega
theorem toInt_intMin_eq_bmod : (intMin w).toInt = (-2 ^ (w - 1)).bmod (2 ^ w) := by
rw [intMin_eq_neg_two_pow, toInt_ofInt]
@[simp]
theorem toInt_bmod_cancel(b : BitVec w) : b.toInt.bmod (2 ^ w) = b.toInt := by
rw [toInt_eq_toNat_bmod, Int.bmod_bmod]
theorem sdiv_ne_intMin_of_ne_intMin {x y : BitVec w} (h : x intMin w) :
x.sdiv y intMin w := by
by_cases hw : w = 0
· subst hw
simp [BitVec.eq_nil x] at h
contradiction
simp only [sdiv, udiv_eq, neg_eq]
by_cases hx : x.msb <;> by_cases hy : y.msb
<;> simp only [hx, hy, neg_ne_intMin_inj]
<;> simp only [Bool.not_eq_true] at hx hy
<;> apply ne_intMin_of_lt_of_msb_false (by omega)
<;> rw [msb_udiv]
<;> try simp only [hx, Bool.false_and]
· simp [h, ne_zero_of_msb_true, hx]
· simp [h, ne_zero_of_msb_true, hx]
theorem toInt_eq_neg_toNat_neg_of_msb_true {x : BitVec w} (h : x.msb = true) :
x.toInt = -((-x).toNat) := by
simp only [toInt_eq_msb_cond, h, reduceIte, toNat_neg, Int.ofNat_emod]
norm_cast
rw [Nat.mod_eq_of_lt]
· omega
· have := @BitVec.isLt w x
have ne_zero := ne_zero_of_msb_true h
simp only [ne_eq, toNat_eq, toNat_ofNat, zero_mod] at ne_zero
omega
theorem toInt_eq_neg_toNat_neg_of_nonpos {x : BitVec w} (h : x = 0#w x.msb = true) :
x.toInt = -((-x).toNat) := by
cases h
case inl h' =>
simp [h']
case inr h' =>
simp [toInt_eq_neg_toNat_neg_of_msb_true h']
theorem intMin_udiv_eq_intMin_iff (x : BitVec w) :
intMin w / x = intMin w x = 1#w := by
by_cases hw : w = 0; subst hw; decide +revert
by_cases hx : x = 1#w; subst hx; simp
have wpos : 0 < w := by omega
have : 0 (2 ^ (w - 1) / x.toNat) := by simp
have := Nat.two_pow_pos (w - 1)
constructor
· intro h
rw [ toInt_inj, toInt_eq_msb_cond] at h
have : (intMin w / x).msb = false := by simp [msb_udiv, msb_intMin, wpos, hx]
simp [this, wpos, toInt_intMin] at h
omega
· intro h
subst h
simp
theorem intMin_udiv_ne_zero_of_ne_zero {b : BitVec w} (hb : b.msb = false) (hb0 : b 0#w) :
intMin w / b 0#w := by
by_cases hw : w = 0; subst hw; decide +revert
have wpos : 0 < w := by omega
simp [toNat_eq] at hb0
have := @Nat.div_eq_zero_iff_lt b.toNat (2 ^ (w-1)) (by omega)
have := toNat_lt_of_msb_false hb
simp [toNat_eq, wpos]
omega
theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a intMin w b -1#w) :
(a.sdiv b).toInt = a.toInt.tdiv b.toInt := by
by_cases hw0 : w = 0; subst hw0; decide +revert
by_cases hw1 : w = 1; subst hw1; decide +revert
by_cases ha0 : a = 0#w; subst ha0; simp
by_cases hb0 : b = 0#w; subst hb0; simp
by_cases hb1 : b = 1#w; subst hb1; simp [show 1 < w by omega]
have wpos : 0 < w := by omega
have := Nat.two_pow_pos (w - 1)
by_cases hbintMin : b = intMin w
· simp only [ne_eq, Decidable.not_not] at hbintMin
subst hbintMin
have toIntA_lt := @BitVec.toInt_lt w a; norm_cast at toIntA_lt
have le_toIntA := @BitVec.le_toInt w a; norm_cast at le_toIntA
simp only [sdiv_intMin, h, reduceIte, toInt_zero, toInt_intMin, wpos,
Nat.two_pow_pred_mod_two_pow, Int.tdiv_neg]
· by_cases ha_intMin : a = intMin w
· simp only [ha_intMin, reduceIte, show 1 < w by omega, toInt_one, toInt_intMin, wpos,
Nat.two_pow_pred_mod_two_pow, Int.neg_tdiv, Int.neg_neg]
rw [Int.tdiv_self (by omega)]
· by_cases ha_nonneg : 0 a.toInt
· simp [Int.tdiv_eq_zero_of_lt ha_nonneg (by norm_cast at *), ha_intMin]
· simp only [ne_eq, toInt_inj, toInt_intMin, wpos, Nat.two_pow_pred_mod_two_pow] at h
rw [ Int.neg_tdiv, Int.tdiv_eq_zero_of_lt (by omega)]
· simp [ha_intMin]
· simp [wpos, toInt_ne, toInt_intMin] at ha_intMin
omega
· by_cases ha : a.msb <;> by_cases hb : b.msb
<;> simp only [not_eq_true] at ha hb
· simp only [sdiv_eq, ha, hb, udiv_eq]
rw [toInt_eq_neg_toNat_neg_of_nonpos (x := a) (by simp [ha]),
toInt_eq_neg_toNat_neg_of_nonpos (x := b) (by simp [hb]),
Int.neg_tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
rw [toInt_eq_toNat_of_msb]
· rfl
· by_cases ha_intMin : a = intMin w
· simp only [ha_intMin, ne_eq, not_true_eq_false, _root_.false_or] at h
simp [msb_udiv, neg_eq_iff_eq_neg, h]
· simp [msb_udiv, ha_intMin, ha]
· have sdiv_toInt_of_msb_true_of_msb_false :
(a.sdiv b).toInt = -((-a).toNat / b.toNat) := by
simp only [sdiv_eq, ha, hb, udiv_eq]
rw [toInt_eq_neg_toNat_neg_of_nonpos]
· rw [neg_neg, toNat_udiv, toNat_neg, Int.ofNat_emod, Int.neg_inj]
norm_cast
· rw [neg_eq_zero_iff]
by_cases h' : -a / b = 0#w
· simp [h']
· by_cases ha_intMin : a = intMin w
· have ry := (intMin_udiv_eq_intMin_iff b).mp
simp only [hb1, imp_false] at ry
simp [msb_udiv, ha_intMin, hb1, ry, intMin_udiv_ne_zero_of_ne_zero, hb, hb0]
· have := @BitVec.ne_intMin_of_lt_of_msb_false w ((-a) / b) wpos (by simp [ha, ha0, ha_intMin])
simp [msb_neg, h', this, ha, ha_intMin]
rw [toInt_eq_toNat_of_msb hb, toInt_eq_neg_toNat_neg_of_msb_true ha, Int.neg_tdiv,
Int.tdiv_eq_ediv_of_nonneg (by omega), sdiv_toInt_of_msb_true_of_msb_false]
· rw [ @BitVec.neg_neg w (a.sdiv b), sdiv_neg hbintMin]
have hmb : (-b).msb = false := by simp [hbintMin, hb]
rw [toInt_neg_of_ne_intMin]
· simp [sdiv, ha, hmb]
rw [toInt_udiv_of_msb ha, toInt_eq_toNat_of_msb ha]
rw [toInt_eq_neg_toNat_neg_of_msb_true hb, Int.tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
· apply sdiv_ne_intMin_of_ne_intMin
apply ne_intMin_of_lt_of_msb_false (by omega) ha
· rw [sdiv, Int.tdiv_cases, udiv_eq, neg_eq, if_pos (toInt_nonneg_of_msb_false ha),
if_pos (toInt_nonneg_of_msb_false hb), ha, hb, toInt_udiv_of_msb ha,
toInt_eq_toNat_of_msb ha, toInt_eq_toNat_of_msb hb]
theorem intMin_sdiv_neg_one : (intMin w).sdiv (-1#w) = intMin w := by
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; exact Subsingleton.elim _ _) (fun hw => ?_)
apply BitVec.eq_of_toNat_eq
rw [sdiv]
simp [msb_intMin, hw, negOne_eq_allOnes, msb_allOnes]
have : 2 2 ^ w := Nat.pow_one 2 (Nat.pow_le_pow_iff_right (by omega)).2 (by omega)
rw [Nat.sub_sub_self (by omega), Nat.mod_eq_of_lt, Nat.div_one]
omega
theorem toInt_sdiv (a b : BitVec w) : (a.sdiv b).toInt = (a.toInt.tdiv b.toInt).bmod (2 ^ w) := by
by_cases h : a = intMin w b = -1#w
· rcases h with rfl, rfl
rw [BitVec.intMin_sdiv_neg_one]
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; simp [toInt_of_zero_length]) (fun hw => ?_)
rw [toInt_intMin_of_pos hw, negOne_eq_allOnes, toInt_allOnes, if_pos hw, Int.tdiv_neg,
Int.tdiv_one, Int.neg_neg, Int.bmod_eq_neg (Int.pow_nonneg (by omega))]
conv => lhs; rw [(by omega: w = (w - 1) + 1)]
simp [Nat.pow_succ, Int.natCast_pow, Int.mul_comm]
· rw [ toInt_bmod_cancel]
rw [BitVec.toInt_sdiv_of_ne_or_ne _ _ (by simpa only [Decidable.not_and_iff_not_or_not] using h)]
theorem msb_umod_eq_false_of_left {x : BitVec w} (hx : x.msb = false) (y : BitVec w) : (x % y).msb = false := by
rw [msb_eq_false_iff_two_mul_lt] at hx
rw [toNat_umod]
refine Nat.lt_of_le_of_lt ?_ hx
rw [Nat.mul_le_mul_left_iff (by decide)]
exact Nat.mod_le _ _
theorem msb_umod_of_le_of_ne_zero_of_le {x y : BitVec w}
(hx : x intMin w) (hy : y 0#w) (hy' : y intMin w) : (x % y).msb = false := by
simp only [msb_umod, Bool.and_eq_false_imp, Bool.or_eq_false_iff, decide_eq_false_iff_not,
BitVec.not_lt, beq_eq_false_iff_ne, ne_eq, hy, not_false_eq_true, _root_.and_true]
intro h
rw [ intMin_le_iff_msb_eq_true (length_pos_of_ne hy)] at h
rwa [BitVec.le_antisymm hx h]
@[simp]
theorem toInt_srem (x y : BitVec w) : (x.srem y).toInt = x.toInt.tmod y.toInt := by
rw [srem_eq]
by_cases hyz : y = 0#w
· simp only [hyz, ofNat_eq_ofNat, msb_zero, umod_zero, neg_zero, neg_neg, toInt_zero, Int.tmod_zero]
cases x.msb <;> rfl
cases h : x.msb
· cases h' : y.msb
· dsimp only
rw [toInt_eq_toNat_of_msb (msb_umod_eq_false_of_left h y), toNat_umod]
rw [toInt_eq_toNat_of_msb h, toInt_eq_toNat_of_msb h', Int.ofNat_tmod]
· dsimp only
rw [toInt_eq_toNat_of_msb (msb_umod_eq_false_of_left h _), toNat_umod]
rw [toInt_eq_toNat_of_msb h, toInt_eq_neg_toNat_neg_of_msb_true h']
rw [Int.tmod_neg, Int.ofNat_tmod]
· cases h' : y.msb
· dsimp only
rw [toInt_eq_neg_toNat_neg_of_msb_true h, toInt_eq_toNat_of_msb h', Int.neg_tmod]
rw [ Int.ofNat_tmod, toNat_umod, toInt_neg_eq_of_msb ?msb, toInt_eq_toNat_of_msb ?msb]
rw [BitVec.msb_umod_of_le_of_ne_zero_of_le (neg_le_intMin_of_msb_eq_true h) hyz]
exact le_intMin_of_msb_eq_false h'
· dsimp only
rw [toInt_eq_neg_toNat_neg_of_msb_true h, toInt_eq_neg_toNat_neg_of_msb_true h', Int.neg_tmod, Int.tmod_neg]
rw [ Int.ofNat_tmod, toNat_umod, toInt_neg_eq_of_msb ?msb', toInt_eq_toNat_of_msb ?msb']
rw [BitVec.msb_umod_of_le_of_ne_zero_of_le (neg_le_intMin_of_msb_eq_true h)
((not_congr neg_eq_zero_iff).mpr hyz)]
exact neg_le_intMin_of_msb_eq_true h'
/-! ### Lemmas that use bit blasting 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]
/-- `extractLsb'` commutes with multiplication. -/
theorem extractLsb'_mul {w len} {x y : BitVec w} (hlen : len w) :
(x * y).extractLsb' 0 len = (x.extractLsb' 0 len) * (y.extractLsb' 0 len) := by
simp [ setWidth_eq_extractLsb' hlen, setWidth_mul _ _ hlen]
/-- Adding bitvectors that are zero in complementary positions equals concatenation. -/
theorem append_add_append_eq_append {v w : Nat} {x : BitVec v} {y : BitVec w} :
(x ++ 0#w) + (0#v ++ y) = x ++ y := by
rw [add_eq_or_of_and_eq_zero] <;> ext i <;> simp
end BitVec

View File

@@ -13,18 +13,15 @@ set_option linter.missingDocs true
namespace BitVec
/--
Constructs a bitvector by iteratively computing a state for each bit using the function `f`,
starting with the initial state `s`. At each step, the prior state and the current bit index are
passed to `f`, and it produces a bit along with the next state value. These bits are assembled into
the final bitvector.
iunfoldr is an iterative operation that applies a function `f` repeatedly.
It produces a sequence of state values `[s_0, s_1 .. s_w]` and a bitvector `v` where `f i s_i =
(s_{i+1}, b_i)` and `b_i` is bit `i`th least-significant bit in `v` (e.g., `getLsb v i = b_i`).
It produces a sequence of state values `[s_0, s_1 .. s_w]` and a bitvector
`v` where `f i s_i = (s_{i+1}, b_i)` and `b_i` is bit `i`th least-significant bit
in `v` (e.g., `getLsb v i = b_i`).
The theorem `iunfoldr_replace` allows uses of `BitVec.iunfoldr` to be replaced wiht declarative
specifications that are easier to reason about.
Theorems involving `iunfoldr` can be eliminated using `iunfoldr_replace` below.
-/
def iunfoldr (f : Fin w α α × Bool) (s : α) : α × BitVec w :=
def iunfoldr (f : Fin w -> α α × Bool) (s : α) : α × BitVec w :=
Fin.hIterate (fun i => α × BitVec i) (s, nil) fun i q =>
(fun p => p.fst, cons p.snd q.snd) (f i q.fst)
@@ -99,12 +96,7 @@ theorem iunfoldr_getLsbD {f : Fin w → αα × Bool} (state : Nat → α)
exact (iunfoldr_getLsbD' state ind).1 i
/--
Given a function `state` that provides the correct state for every potential iteration count and a
function that computes these states from the correct initial state, the result of applying
`BitVec.iunfoldr f` to the initial state is the state corresponding to the bitvector's width paired
with the bitvector that consists of each computed bit.
This theorem can be used to prove properties of functions that are defined using `BitVec.iunfoldr`.
Correctness theorem for `iunfoldr`.
-/
theorem iunfoldr_replace
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)

File diff suppressed because it is too large Load Diff

View File

@@ -9,19 +9,7 @@ import Init.NotationExtra
namespace Bool
/--
Boolean “exclusive or”. `xor x y` can be written `x ^^ y`.
`x ^^ y` is `true` when precisely one of `x` or `y` is `true`. Unlike `and` and `or`, it does not
have short-circuiting behavior, because one argument's value never determines the final value. Also
unlike `and` and `or`, there is no commonly-used corresponding propositional connective.
Examples:
* `false ^^ false = false`
* `true ^^ false = true`
* `false ^^ true = true`
* `true ^^ true = false`
-/
/-- Boolean exclusive or -/
abbrev xor : Bool Bool Bool := bne
@[inherit_doc] infixl:33 " ^^ " => xor
@@ -379,9 +367,7 @@ theorem and_or_inj_left_iff :
/-! ## toNat -/
/--
Converts `true` to `1` and `false` to `0`.
-/
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
def toNat (b : Bool) : Nat := cond b 1 0
@[simp, bitvec_to_nat] theorem toNat_false : false.toNat = 0 := rfl
@@ -402,9 +388,7 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
/-! ## toInt -/
/--
Converts `true` to `1` and `false` to `0`.
-/
/-- convert a `Bool` to an `Int`, `false -> 0`, `true -> 1` -/
def toInt (b : Bool) : Int := cond b 1 0
@[simp] theorem toInt_false : false.toInt = 0 := rfl
@@ -555,8 +539,8 @@ theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
@[simp] theorem cond_eq_false_distrib : (c t f : Bool),
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
protected theorem cond_true {α : Sort u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Sort u} {a b : α} : cond false a b = b := cond_false a b
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
@[simp] theorem cond_true_left : (c f : Bool), cond c true f = ( c || f) := by decide
@[simp] theorem cond_false_left : (c f : Bool), cond c false f = (!c && f) := by decide

View File

@@ -18,13 +18,10 @@ attribute [extern "lean_byte_array_data"] ByteArray.data
namespace ByteArray
@[extern "lean_mk_empty_byte_array"]
def emptyWithCapacity (c : @& Nat) : ByteArray :=
def mkEmpty (c : @& Nat) : ByteArray :=
{ data := #[] }
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
def empty : ByteArray := emptyWithCapacity 0
def empty : ByteArray := mkEmpty 0
instance : Inhabited ByteArray where
default := empty
@@ -337,9 +334,6 @@ def prevn : Iterator → Nat → Iterator
end Iterator
end ByteArray
/--
Converts a list of bytes into a `ByteArray`.
-/
def List.toByteArray (bs : List UInt8) : ByteArray :=
let rec loop
| [], r => r

View File

@@ -44,26 +44,25 @@ Nat → Nat → ...`. Sometimes we also need to declare the `CoeHTCT`
instance if we need to shadow another coercion.
-/
/--
The canonical homomorphism `Nat → R`. In most use cases, the target type will have a (semi)ring
structure, and this homomorphism should be a (semi)ring homomorphism.
`NatCast` and `IntCast` exist to allow different libraries with their own types that can be notated
as natural numbers to have consistent `simp` normal forms without needing to create coercion
simplification sets that are aware of all combinations. Libraries should make it easy to work with
`NatCast` where possible. For instance, in Mathlib there will be such a homomorphism (and thus a
`NatCast R` instance) whenever `R` is an additive monoid with a `1`.
The prototypical example is `Int.ofNat`.
-/
/-- Type class for the canonical homomorphism `Nat → R`. -/
class NatCast (R : Type u) where
/-- The canonical map `Nat → R`. -/
protected natCast : Nat R
instance : NatCast Nat where natCast n := n
@[coe, reducible, match_pattern, inherit_doc NatCast]
protected def Nat.cast {R : Type u} [NatCast R] : Nat R :=
/--
Canonical homomorphism from `Nat` to a type `R`.
It contains just the function, with no axioms.
In practice, the target type will likely have a (semi)ring structure,
and this homomorphism should be a ring homomorphism.
The prototypical example is `Int.ofNat`.
This class and `IntCast` exist to allow different libraries with their own types that can be notated as natural numbers to have consistent `simp` normal forms without needing to create coercion simplification sets that are aware of all combinations. Libraries should make it easy to work with `NatCast` where possible. For instance, in Mathlib there will be such a homomorphism (and thus a `NatCast R` instance) whenever `R` is an additive monoid with a `1`.
-/
@[coe, reducible, match_pattern] protected def Nat.cast {R : Type u} [NatCast R] : Nat R :=
NatCast.natCast
-- see the notes about coercions into arbitrary types in the module doc-string

View File

@@ -15,15 +15,7 @@ Note that values in `[0xd800, 0xdfff]` are reserved for [UTF-16 surrogate pairs]
namespace Char
/--
One character is less than another if its code point is strictly less than the other's.
-/
protected def lt (a b : Char) : Prop := a.val < b.val
/--
One character is less than or equal to another if its code point is less than or equal to the
other's.
-/
protected def le (a b : Char) : Prop := a.val b.val
instance : LT Char := Char.lt
@@ -35,10 +27,7 @@ instance (a b : Char) : Decidable (a < b) :=
instance (a b : Char) : Decidable (a b) :=
UInt32.decLe _ _
/--
True for natural numbers that are valid [Unicode scalar
values](https://www.unicode.org/glossary/#unicode_scalar_value).
-/
/-- Determines if the given nat is a valid [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value).-/
abbrev isValidCharNat (n : Nat) : Prop :=
n < 0xd800 (0xdfff < n n < 0x110000)
@@ -61,93 +50,55 @@ theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValid
theorem isValidChar_zero : isValidChar 0 :=
Or.inl (by decide)
/--
The character's Unicode code point as a `Nat`.
-/
/-- Underlying unicode code point as a `Nat`. -/
@[inline] def toNat (c : Char) : Nat :=
c.val.toNat
/--
Converts a character into a `UInt8` that contains its code point.
If the code point is larger than 255, it is truncated (reduced modulo 256).
-/
/-- Convert a character into a `UInt8`, by truncating (reducing modulo 256) if necessary. -/
@[inline] def toUInt8 (c : Char) : UInt8 :=
c.val.toUInt8
/--
Converts an 8-bit unsigned integer into a character.
The integer's value is interpreted as a Unicode code point.
-/
/-- The numbers from 0 to 256 are all valid UTF-8 characters, so we can embed one in the other. -/
def ofUInt8 (n : UInt8) : Char := n.toUInt32, .inl (Nat.lt_trans n.toBitVec.isLt (by decide))
instance : Inhabited Char where
default := 'A'
/--
Returns `true` if the character is a space `(' ', U+0020)`, a tab `('\t', U+0009)`, a carriage
return `('\r', U+000D)`, or a newline `('\n', U+000A)`.
-/
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
@[inline] def isWhitespace (c : Char) : Bool :=
c = ' ' || c = '\t' || c = '\r' || c = '\n'
/--
Returns `true` if the character is a uppercase ASCII letter.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
-/
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
@[inline] def isUpper (c : Char) : Bool :=
c.val 65 && c.val 90
/--
Returns `true` if the character is a lowercase ASCII letter.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
-/
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
@[inline] def isLower (c : Char) : Bool :=
c.val 97 && c.val 122
/--
Returns `true` if the character is an ASCII letter.
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
-/
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
@[inline] def isAlpha (c : Char) : Bool :=
c.isUpper || c.isLower
/--
Returns `true` if the character is an ASCII digit.
The ASCII digits are the following: `0123456789`.
-/
/-- Is the character in `0123456789`? -/
@[inline] def isDigit (c : Char) : Bool :=
c.val 48 && c.val 57
/--
Returns `true` if the character is an ASCII letter or digit.
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
The ASCII digits are the following: `0123456789`.
-/
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
@[inline] def isAlphanum (c : Char) : Bool :=
c.isAlpha || c.isDigit
/--
Converts an uppercase ASCII letter to the corresponding lowercase letter. Letters outside the ASCII
alphabet are returned unchanged.
/-- Convert an upper case character to its lower case character.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
Only works on basic latin letters.
-/
def toLower (c : Char) : Char :=
let n := toNat c;
if n >= 65 n <= 90 then ofNat (n + 32) else c
/--
Converts a lowercase ASCII letter to the corresponding uppercase letter. Letters outside the ASCII
alphabet are returned unchanged.
/-- Convert a lower case character to its upper case character.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
Only works on basic latin letters.
-/
def toUpper (c : Char) : Char :=
let n := toNat c;

View File

@@ -14,23 +14,22 @@ instance coeToNat : CoeOut (Fin n) Nat :=
fun v => v.val
/--
The type `Fin 0` is uninhabited, so it can be used to derive any result whatsoever.
This is similar to `Empty.elim`. It can be thought of as a compiler-checked assertion that a code
path is unreachable, or a logical contradiction from which `False` and thus anything else could be
derived.
From the empty type `Fin 0`, any desired result `α` can be derived. This is similar to `Empty.elim`.
-/
def elim0.{u} {α : Sort u} : Fin 0 α
| _, h => absurd h (not_lt_zero _)
/--
The successor, with an increased bound.
Returns the successor of the argument.
This differs from adding `1`, which instead wraps around.
Examples:
* `(2 : Fin 3).succ = (3 : Fin 4)`
* `(2 : Fin 3) + 1 = (0 : Fin 3)`
The bound in the result type is increased:
```
(2 : Fin 3).succ = (3 : Fin 4)
```
This differs from addition, which wraps around:
```
(2 : Fin 3) + 1 = (0 : Fin 3)
```
-/
def succ : Fin n Fin (n + 1)
| i, h => i+1, Nat.succ_lt_succ h
@@ -54,13 +53,7 @@ protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
-- `i.toNat` to `i.val`.
/--
Extracts the underlying `Nat` value.
This function is a synonym for `Fin.val`, which is the simp normal form. `Fin.val` is also a
coercion, so values of type `Fin n` are automatically converted to `Nat`s as needed.
-/
@[inline]
@[inline, inherit_doc val]
protected def toNat (i : Fin n) : Nat :=
i.val
@@ -72,34 +65,15 @@ private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
have : n > 0 := Nat.lt_trans (Nat.zero_lt_succ _) h;
Nat.mod_lt _ this
/--
Addition modulo `n`, usually invoked via the `+` operator.
Examples:
* `(2 : Fin 8) + (2 : Fin 8) = (4 : Fin 8)`
* `(2 : Fin 3) + (2 : Fin 3) = (1 : Fin 3)`
-/
/-- Addition modulo `n` -/
protected def add : Fin n Fin n Fin n
| a, h, b, _ => (a + b) % n, mlt h
/--
Multiplication modulo `n`, usually invoked via the `*` operator.
Examples:
* `(2 : Fin 10) * (2 : Fin 10) = (4 : Fin 10)`
* `(2 : Fin 10) * (7 : Fin 10) = (4 : Fin 10)`
* `(3 : Fin 10) * (7 : Fin 10) = (1 : Fin 10)`
-/
/-- Multiplication modulo `n` -/
protected def mul : Fin n Fin n Fin n
| a, h, b, _ => (a * b) % n, mlt h
/--
Subtraction modulo `n`, usually invoked via the `-` operator.
Examples:
* `(5 : Fin 11) - (3 : Fin 11) = (2 : Fin 11)`
* `(3 : Fin 11) - (5 : Fin 11) = (9 : Fin 11)`
-/
/-- Subtraction modulo `n` -/
protected def sub : Fin n Fin n Fin n
/-
The definition of `Fin.sub` has been updated to improve performance.
@@ -126,76 +100,27 @@ we are trying to minimize the number of Nat theorems
needed to bootstrap Lean.
-/
/--
Modulus of bounded numbers, usually invoked via the `%` operator.
The resulting value is that computed by the `%` operator on `Nat`.
-/
protected def mod : Fin n Fin n Fin n
| a, h, b, _ => a % b, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h
/--
Division of bounded numbers, usually invoked via the `/` operator.
The resulting value is that computed by the `/` operator on `Nat`. In particular, the result of
division by `0` is `0`.
Examples:
* `(5 : Fin 10) / (2 : Fin 10) = (2 : Fin 10)`
* `(5 : Fin 10) / (0 : Fin 10) = (0 : Fin 10)`
* `(5 : Fin 10) / (7 : Fin 10) = (0 : Fin 10)`
-/
protected def div : Fin n Fin n Fin n
| a, h, b, _ => a / b, Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h
/--
Modulus of bounded numbers with respect to a `Nat`.
The resulting value is that computed by the `%` operator on `Nat`.
-/
def modn : Fin n Nat Fin n
| a, h, m => a % m, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h
/--
Bitwise and.
-/
def land : Fin n Fin n Fin n
| a, h, b, _ => (Nat.land a b) % n, mlt h
/--
Bitwise or.
-/
def lor : Fin n Fin n Fin n
| a, h, b, _ => (Nat.lor a b) % n, mlt h
/--
Bitwise xor (“exclusive or”).
-/
def xor : Fin n Fin n Fin n
| a, h, b, _ => (Nat.xor a b) % n, mlt h
/--
Bitwise left shift of bounded numbers, with wraparound on overflow.
Examples:
* `(1 : Fin 10) <<< (1 : Fin 10) = (2 : Fin 10)`
* `(1 : Fin 10) <<< (3 : Fin 10) = (8 : Fin 10)`
* `(1 : Fin 10) <<< (4 : Fin 10) = (6 : Fin 10)`
-/
def shiftLeft : Fin n Fin n Fin n
| a, h, b, _ => (a <<< b) % n, mlt h
/--
Bitwise right shift of bounded numbers.
This operator corresponds to logical rather than arithmetic bit shifting. The new bits are always
`0`.
Examples:
* `(15 : Fin 16) >>> (1 : Fin 16) = (7 : Fin 16)`
* `(15 : Fin 16) >>> (2 : Fin 16) = (3 : Fin 16)`
* `(15 : Fin 17) >>> (2 : Fin 17) = (3 : Fin 17)`
-/
def shiftRight : Fin n Fin n Fin n
| a, h, b, _ => (a >>> b) % n, mlt h
@@ -249,125 +174,39 @@ theorem val_lt_of_le (i : Fin b) (h : b ≤ n) : i.val < n :=
protected theorem pos (i : Fin n) : 0 < n :=
Nat.lt_of_le_of_lt (Nat.zero_le _) i.2
/--
The greatest value of `Fin (n+1)`, namely `n`.
Examples:
* `Fin.last 4 = (4 : Fin 5)`
* `(Fin.last 0).val = (0 : Nat)`
-/
/-- The greatest value of `Fin (n+1)`. -/
@[inline] def last (n : Nat) : Fin (n + 1) := n, n.lt_succ_self
/--
Replaces the bound with another that is suitable for the value.
The proof embedded in `i` can be used to cast to a larger bound even if the concrete value is not
known.
Examples:
```lean example
example : Fin 12 := (7 : Fin 10).castLT (by decide : 7 < 12)
```
```lean example
example (i : Fin 10) : Fin 12 :=
i.castLT <| by
cases i; simp; omega
```
-/
/-- `castLT i h` embeds `i` into a `Fin` where `h` proves it belongs into. -/
@[inline] def castLT (i : Fin m) (h : i.1 < n) : Fin n := i.1, h
/--
Coarsens a bound to one at least as large.
See also `Fin.castAdd` for a version that represents the larger bound with addition rather than an
explicit inequality proof.
-/
/-- `castLE h i` embeds `i` into a larger `Fin` type. -/
@[inline] def castLE (h : n m) (i : Fin n) : Fin m := i, Nat.lt_of_lt_of_le i.2 h
/--
Uses a proof that two bounds are equal to allow a value bounded by one to be used with the other.
In other words, when `eq : n = m`, `Fin.cast eq i` converts `i : Fin n` into a `Fin m`.
-/
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
@[inline] protected def cast (eq : n = m) (i : Fin n) : Fin m := i, eq i.2
/--
Coarsens a bound to one at least as large.
See also `Fin.natAdd` and `Fin.addNat` for addition functions that increase the bound, and
`Fin.castLE` for a version that uses an explicit inequality proof.
-/
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
@[inline] def castAdd (m) : Fin n Fin (n + m) :=
castLE <| Nat.le_add_right n m
/--
Coarsens a bound by one.
-/
/-- `castSucc i` embeds `i : Fin n` in `Fin (n+1)`. -/
@[inline] def castSucc : Fin n Fin (n + 1) := castAdd 1
/--
Adds a natural number to a `Fin`, increasing the bound.
This is a generalization of `Fin.succ`.
`Fin.natAdd` is a version of this function that takes its `Nat` parameter first.
Examples:
* `Fin.addNat (5 : Fin 8) 3 = (8 : Fin 11)`
* `Fin.addNat (0 : Fin 8) 1 = (1 : Fin 9)`
* `Fin.addNat (1 : Fin 8) 2 = (3 : Fin 10)`
-/
/-- `addNat m i` adds `m` to `i`, generalizes `Fin.succ`. -/
def addNat (i : Fin n) (m) : Fin (n + m) := i + m, Nat.add_lt_add_right i.2 _
/--
Adds a natural number to a `Fin`, increasing the bound.
This is a generalization of `Fin.succ`.
`Fin.addNat` is a version of this function that takes its `Nat` parameter second.
Examples:
* `Fin.natAdd 3 (5 : Fin 8) = (8 : Fin 11)`
* `Fin.natAdd 1 (0 : Fin 8) = (1 : Fin 9)`
* `Fin.natAdd 1 (2 : Fin 8) = (3 : Fin 9)`
-/
/-- `natAdd n i` adds `n` to `i` "on the left". -/
def natAdd (n) (i : Fin m) : Fin (n + m) := n + i, Nat.add_lt_add_left i.2 _
/--
Replaces a value with its difference from the largest value in the type.
Considering the values of `Fin n` as a sequence `0`, `1`, …, `n-2`, `n-1`, `Fin.rev` finds the
corresponding element of the reversed sequence. In other words, it maps `0` to `n-1`, `1` to `n-2`,
..., and `n-1` to `0`.
Examples:
* `(5 : Fin 6).rev = (0 : Fin 6)`
* `(0 : Fin 6).rev = (5 : Fin 6)`
* `(2 : Fin 5).rev = (2 : Fin 5)`
-/
/-- Maps `0` to `n-1`, `1` to `n-2`, ..., `n-1` to `0`. -/
@[inline] def rev (i : Fin n) : Fin n := n - (i + 1), Nat.sub_lt i.pos (Nat.succ_pos _)
/--
Subtraction of a natural number from a `Fin`, with the bound narrowed.
This is a generalization of `Fin.pred`. It is guaranteed to not underflow or wrap around.
Examples:
* `(5 : Fin 9).subNat 2 (by decide) = (3 : Fin 7)`
* `(5 : Fin 9).subNat 0 (by decide) = (5 : Fin 9)`
* `(3 : Fin 9).subNat 3 (by decide) = (0 : Fin 6)`
-/
/-- `subNat i h` subtracts `m` from `i`, generalizes `Fin.pred`. -/
@[inline] def subNat (m) (i : Fin (n + m)) (h : m i) : Fin n :=
i - m, Nat.sub_lt_right_of_lt_add h i.2
/--
The predecessor of a non-zero element of `Fin (n+1)`, with the bound decreased.
Examples:
* `(4 : Fin 8).pred (by decide) = (3 : Fin 7)`
* `(1 : Fin 2).pred (by decide) = (0 : Fin 1)`
-/
/-- Predecessor of a nonzero element of `Fin (n+1)`. -/
@[inline] def pred {n : Nat} (i : Fin (n + 1)) (h : i 0) : Fin n :=
subNat 1 i <| Nat.pos_of_ne_zero <| mt (Fin.eq_of_val_eq (j := 0)) h

View File

@@ -12,31 +12,4 @@ 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

View File

@@ -10,26 +10,14 @@ import Init.Data.Fin.Lemmas
namespace Fin
/--
Combine all the values that can be represented by `Fin n` with an initial value, starting at `0` and
nesting to the left.
Example:
* `Fin.foldl 3 (· + ·.val) (0 : Nat) = ((0 + (0 : Fin 3).val) + (1 : Fin 3).val) + (2 : Fin 3).val`
-/
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
@[inline] def foldl (n) (f : α Fin n α) (init : α) : α := loop init 0 where
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
if h : i < n then loop (f x i, h) (i+1) else x
termination_by n - i
/--
Combine all the values that can be represented by `Fin n` with an initial value, starting at `n - 1`
and nesting to the right.
Example:
* `Fin.foldr 3 (·.val + ·) (0 : Nat) = (0 : Fin 3).val + ((1 : Fin 3).val + ((2 : Fin 3).val + 0))`
-/
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
@[inline] def foldr (n) (f : Fin n α α) (init : α) : α := loop n (Nat.le_refl n) init where
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
@[specialize] loop : (i : _) i n α α
@@ -38,9 +26,7 @@ Example:
termination_by structural i => i
/--
Folds a monadic function over all the values in `Fin n` from left to right, starting with `0`.
It is the sequence of steps:
Folds a monadic function over `Fin n` from left to right:
```
Fin.foldlM n f x₀ = do
let x₁ ← f x₀ 0
@@ -67,9 +53,7 @@ Fin.foldlM n f x₀ = do
decreasing_by decreasing_trivial_pre_omega
/--
Folds a monadic function over `Fin n` from right to left, starting with `n-1`.
It is the sequence of steps:
Folds a monadic function over `Fin n` from right to left:
```
Fin.foldrM n f xₙ = do
let xₙ₋₁ ← f (n-1) xₙ
@@ -141,9 +125,7 @@ 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
try simp only [foldrM.loop] -- the try makes this proof work with and without opaque wf rec
congr; funext
| succ i ih =>
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
congr; funext; exact ih ..

View File

@@ -10,18 +10,10 @@ import Init.Data.Fin.Basic
namespace Fin
/--
Applies an index-dependent function `f` to all of the values in `[i:n]`, starting at `i` with an
initial accumulator `a`.
`hIterateFrom f i bnd a` applies `f` over indices `[i:n]` to compute `P n`
from `P i`.
Concretely, `Fin.hIterateFrom P f i a` is equal to
```lean
a |> f i |> f (i + 1) |> ... |> f (n - 1)
```
Theorems about `Fin.hIterateFrom` can be proven using the general theorem `Fin.hIterateFrom_elim` or
other more specialized theorems.
`Fin.hIterate` is a variant that always starts at `0`.
See `hIterate` below for more details.
-/
def hIterateFrom (P : Nat Sort _) {n} (f : (i : Fin n), P i.val P (i.val+1))
(i : Nat) (ubnd : i n) (a : P i) : P n :=
@@ -34,18 +26,20 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
decreasing_by decreasing_trivial_pre_omega
/--
Applies an index-dependent function to all the values less than the given bound `n`, starting at
`0` with an accumulator.
`hIterate` is a heterogeneous iterative operation that applies a
index-dependent function `f` to a value `init : P start` a total of
`stop - start` times to produce a value of type `P stop`.
Concretely, `Fin.hIterate P init f` is equal to
Concretely, `hIterate start stop f init` is equal to
```lean
init |> f 0 |> f 1 |> ... |> f (n-1)
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
```
Theorems about `Fin.hIterate` can be proven using the general theorem `Fin.hIterate_elim` or other more
specialized theorems.
Because it is heterogeneous and must return a value of type `P stop`,
`hIterate` requires proof that `start ≤ stop`.
`Fin.hIterateFrom` is a variant that takes a custom starting value instead of `0`.
One can prove properties of `hIterate` using the general theorem
`hIterate_elim` or other more specialized theorems.
-/
def hIterate (P : Nat Sort _) {n : Nat} (init : P 0) (f : (i : Fin n), P i.val P (i.val+1)) :
P n :=

View File

@@ -45,7 +45,6 @@ theorem val_ne_iff {a b : Fin n} : a.1 ≠ b.1 ↔ a ≠ b := not_congr val_inj
theorem forall_iff {p : Fin n Prop} : ( i, p i) i h, p i, h :=
fun h i hi => h i, hi, fun h i, hi => h i hi
/-- Restatement of `Fin.mk.injEq` as an `iff`. -/
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
(a, ha : Fin n) = b, hb a = b := Fin.ext_iff
@@ -56,14 +55,6 @@ theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
theorem mk_val (i : Fin n) : (i, i.isLt : Fin n) = i := Fin.eta ..
@[simp] theorem mk_eq_zero {n a : Nat} {ha : a < n} [NeZero n] :
(a, ha : Fin n) = 0 a = 0 :=
mk.inj_iff
@[simp] theorem zero_eq_mk {n a : Nat} {ha : a < n} [NeZero n] :
0 = (a, ha : Fin n) a = 0 := by
simp [eq_comm]
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat' n a).val = a % n := rfl
@@ -127,33 +118,6 @@ protected theorem ne_of_gt {a b : Fin n} (h : a < b) : b ≠ a := Fin.ne_of_val_
protected theorem le_of_lt {a b : Fin n} (h : a < b) : a b := Nat.le_of_lt h
protected theorem lt_of_le_of_lt {a b c : Fin n} : a b b < c a < c := Nat.lt_of_le_of_lt
protected theorem lt_of_lt_of_le {a b c : Fin n} : a < b b c a < c := Nat.lt_of_lt_of_le
protected theorem le_rfl {a : Fin n} : a a := Nat.le_refl _
protected theorem lt_iff_le_and_ne {a b : Fin n} : a < b a b a b := by
rw [ val_ne_iff]; exact Nat.lt_iff_le_and_ne
protected theorem lt_or_lt_of_ne {a b : Fin n} (h : a b) : a < b b < a :=
Nat.lt_or_lt_of_ne <| val_ne_iff.2 h
protected theorem lt_or_le (a b : Fin n) : a < b b a := Nat.lt_or_ge _ _
protected theorem le_or_lt (a b : Fin n) : a b b < a := (b.lt_or_le a).symm
protected theorem le_of_eq {a b : Fin n} (hab : a = b) : a b :=
Nat.le_of_eq <| congrArg val hab
protected theorem ge_of_eq {a b : Fin n} (hab : a = b) : b a := Fin.le_of_eq hab.symm
protected theorem eq_or_lt_of_le {a b : Fin n} : a b a = b a < b := by
rw [Fin.ext_iff]; exact Nat.eq_or_lt_of_le
protected theorem lt_or_eq_of_le {a b : Fin n} : a b a < b a = b := by
rw [Fin.ext_iff]; exact Nat.lt_or_eq_of_le
theorem is_le (i : Fin (n + 1)) : i n := Nat.le_of_lt_succ i.is_lt
@[simp] theorem is_le' {a : Fin n} : a n := Nat.le_of_lt a.is_lt
@@ -697,20 +661,12 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n i) :
natAdd n (subNat n (i.cast (Nat.add_comm ..)) h) = i := by simp [ cast_addNat]
/-! ### Recursion and induction principles -/
/-! ### recursion and induction principles -/
/--
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
applications of `Fin.succ`.
The cases in the induction are:
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
bound `n`
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
step. `Fin.succRecOn` is a version of this induction principle that takes the `Fin` argument first.
-/
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0` of an
`(n+1)`-tuple, and `succ n i` defines `(i+1)`-st element of `(n+1)`-tuple based on `n`, `i`, and
`i`-th element of `n`-tuple. -/
-- FIXME: Performance review
@[elab_as_elim] def succRec {motive : n, Fin n Sort _}
(zero : n, motive n.succ (0 : Fin (n + 1)))
@@ -719,18 +675,13 @@ step. `Fin.succRecOn` is a version of this induction principle that takes the `F
| Nat.succ n, 0, _ => by rw [mk_zero]; exact zero n
| Nat.succ _, Nat.succ i, h => succ _ _ (succRec zero succ i, Nat.lt_of_succ_lt_succ h)
/--
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
applications of `Fin.succ`.
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
This function has two arguments:
`zero n` defines the `0`-th element `motive (n+1) 0` of an `(n+1)`-tuple, and
`succ n i` defines the `(i+1)`-st element of an `(n+1)`-tuple based on `n`, `i`,
and the `i`-th element of an `n`-tuple.
The cases in the induction are:
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
bound `n`
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
step. `Fin.succRec` is a version of this induction principle that takes the `Fin` argument last.
-/
A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
-- FIXME: Performance review
@[elab_as_elim] def succRecOn {n : Nat} (i : Fin n) {motive : n, Fin n Sort _}
(zero : n, motive (n + 1) 0) (succ : n i, motive n i motive (Nat.succ n) i.succ) :
@@ -745,17 +696,9 @@ step. `Fin.succRec` is a version of this induction principle that takes the `Fin
cases i; rfl
/--
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
For the induction:
* `zero` is the base case, demonstrating `motive 0`.
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
`Fin.castSucc`) and demonstrating it for `i.succ`.
`Fin.inductionOn` is a version of this induction principle that takes the `Fin` as its first
parameter, `Fin.cases` is the corresponding case analysis operator, and `Fin.reverseInduction` is a
version that starts at the greatest value instead of `0`.
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
This function has two arguments: `zero` handles the base case on `motive 0`,
and `succ` defines the inductive step using `motive i.castSucc`.
-/
-- FIXME: Performance review
@[elab_as_elim] def induction {motive : Fin (n + 1) Sort _} (zero : motive 0)
@@ -776,30 +719,18 @@ where
(succ : i : Fin n, motive (castSucc i) motive i.succ) (i : Fin n) :
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
/--
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
This function has two arguments: `zero` handles the base case on `motive 0`,
and `succ` defines the inductive step using `motive i.castSucc`.
For the induction:
* `zero` is the base case, demonstrating `motive 0`.
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
`Fin.castSucc`) and demonstrating it for `i.succ`.
`Fin.induction` is a version of this induction principle that takes the `Fin` as its last
parameter.
A version of `Fin.induction` taking `i : Fin (n + 1)` as the first argument.
-/
-- FIXME: Performance review
@[elab_as_elim] def inductionOn (i : Fin (n + 1)) {motive : Fin (n + 1) Sort _} (zero : motive 0)
(succ : i : Fin n, motive (castSucc i) motive i.succ) : motive i := induction zero succ i
/--
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`.
The two cases are:
* `zero`, used when the value is of the form `(0 : Fin (n + 1))`
* `succ`, used when the value is of the form `(j : Fin n).succ`
The corresponding induction principle is `Fin.induction`.
-/
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = 0` and
`i = j.succ`, `j : Fin n`. -/
@[elab_as_elim] def cases {motive : Fin (n + 1) Sort _}
(zero : motive 0) (succ : i : Fin n, motive i.succ) :
i : Fin (n + 1), motive i := induction zero fun i _ => succ i
@@ -837,14 +768,9 @@ theorem fin_two_eq_of_eq_zero_iff : ∀ {a b : Fin 2}, (a = 0 ↔ b = 0) → a =
simp only [forall_fin_two]; decide
/--
Proves a statement by reverse induction on the underlying `Nat` value in a `Fin (n + 1)`.
For the induction:
* `last` is the base case, demonstrating `motive (Fin.last n)`.
* `cast` is the inductive step, assuming the motive for `(j : Fin n).succ` and demonstrating it for
the predecessor `j.castSucc`.
`Fin.induction` is the non-reverse induction principle.
Define `motive i` by reverse induction on `i : Fin (n + 1)` via induction on the underlying `Nat`
value. This function has two arguments: `last` handles the base case on `motive (Fin.last n)`,
and `cast` defines the inductive step using `motive i.succ`, inducting downwards.
-/
@[elab_as_elim] def reverseInduction {motive : Fin (n + 1) Sort _} (last : motive (Fin.last n))
(cast : i : Fin n, motive i.succ motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
@@ -867,16 +793,8 @@ decreasing_by decreasing_with
succ i (reverseInduction zero succ i.succ) := by
rw [reverseInduction, dif_neg (Fin.ne_of_lt (Fin.castSucc_lt_last i))]; rfl
/--
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`, checking whether the
value is the greatest representable or a predecessor of some other.
The two cases are:
* `last`, used when the value is `Fin.last n`
* `cast`, used when the value is of the form `(j : Fin n).succ`
The corresponding induction principle is `Fin.reverseInduction`.
-/
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = Fin.last n` and
`i = j.castSucc`, `j : Fin n`. -/
@[elab_as_elim] def lastCases {n : Nat} {motive : Fin (n + 1) Sort _} (last : motive (Fin.last n))
(cast : i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
reverseInduction last (fun i _ => cast i) i
@@ -889,16 +807,8 @@ The corresponding induction principle is `Fin.reverseInduction`.
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
reverseInduction_castSucc ..
/--
A case analysis operator for `i : Fin (m + n)` that separately handles the cases where `i < m` and
where `m ≤ i < m + n`.
The first case, where `i < m`, is handled by `left`. In this case, `i` can be represented as
`Fin.castAdd n (j : Fin m)`.
The second case, where `m ≤ i < m + n`, is handled by `right`. In this case, `i` can be represented
as `Fin.natAdd m (j : Fin n)`.
-/
/-- Define `f : Π i : Fin (m + n), motive i` by separately handling the cases `i = castAdd n i`,
`j : Fin m` and `i = natAdd m j`, `j : Fin n`. -/
@[elab_as_elim] def addCases {m n : Nat} {motive : Fin (m + n) Sort u}
(left : i, motive (castAdd n i)) (right : i, motive (natAdd m i))
(i : Fin (m + n)) : motive i :=

View File

@@ -6,20 +6,4 @@ Authors: Henrik Böving
prelude
import Init.Data.Nat.Log2
set_option linter.missingDocs true
/--
Logarithm base 2 for bounded numbers.
The resulting value is the same as that computed by `Nat.log2`. In particular, the result for `0` is
`0`.
Examples:
* `(8 : Fin 10).log2 = (3 : Fin 10)`
* `(7 : Fin 10).log2 = (2 : Fin 10)`
* `(4 : Fin 10).log2 = (2 : Fin 10)`
* `(3 : Fin 10).log2 = (1 : Fin 10)`
* `(1 : Fin 10).log2 = (0 : Fin 10)`
* `(0 : Fin 10).log2 = (0 : Fin 10)`
-/
def Fin.log2 (n : Fin m) : Fin m := Nat.log2 n.val, Nat.lt_of_le_of_lt (Nat.log2_le_self n.val) n.isLt

View File

@@ -26,97 +26,43 @@ opaque floatSpec : FloatSpec := {
decLe := fun _ _ => inferInstanceAs (Decidable True)
}
/--
64-bit floating-point numbers.
`Float` corresponds to the IEEE 754 *binary64* format (`double` in C or `f64` in Rust).
Floating-point numbers are a finite representation of a subset of the real numbers, extended with
extra “sentinel” values that represent undefined and infinite results as well as separate positive
and negative zeroes. Arithmetic on floating-point numbers approximates the corresponding operations
on the real numbers by rounding the results to numbers that are representable, propagating error and
infinite values.
Floating-point numbers include [subnormal numbers](https://en.wikipedia.org/wiki/Subnormal_number).
Their special values are:
* `NaN`, which denotes a class of “not a number” values that result from operations such as
dividing zero by zero, and
* `Inf` and `-Inf`, which represent positive and infinities that result from dividing non-zero
values by zero.
-/
/-- Native floating point type, corresponding to the IEEE 754 *binary64* format
(`double` in C or `f64` in Rust). -/
structure Float where
val : floatSpec.float
instance : Nonempty Float := { val := floatSpec.val }
/--
Adds two 64-bit floating-point numbers according to IEEE 754. Typically used via the `+` operator.
This function does not reduce in the kernel. It is compiled to the C addition operator.
-/
@[extern "lean_float_add"] opaque Float.add : Float Float Float
/--
Subtracts 64-bit floating-point numbers according to IEEE 754. Typically used via the `-` operator.
This function does not reduce in the kernel. It is compiled to the C subtraction operator.
-/
@[extern "lean_float_sub"] opaque Float.sub : Float Float Float
/--
Multiplies 64-bit floating-point numbers according to IEEE 754. Typically used via the `*` operator.
This function does not reduce in the kernel. It is compiled to the C multiplication operator.
-/
@[extern "lean_float_mul"] opaque Float.mul : Float Float Float
/--
Divides 64-bit floating-point numbers according to IEEE 754. Typically used via the `/` operator.
In Lean, division by zero typically yields zero. For `Float`, it instead yields either `Inf`,
`-Inf`, or `NaN`.
This function does not reduce in the kernel. It is compiled to the C division operator.
-/
@[extern "lean_float_div"] opaque Float.div : Float Float Float
/--
Negates 64-bit floating-point numbers according to IEEE 754. Typically used via the `-` prefix
operator.
This function does not reduce in the kernel. It is compiled to the C negation operator.
-/
@[extern "lean_float_negate"] opaque Float.neg : Float Float
set_option bootstrap.genMatcherCode false
/--
Strict inequality of floating-point numbers. Typically used via the `<` operator.
-/
def Float.lt : Float Float Prop := fun a b =>
match a, b with
| a, b => floatSpec.lt a b
/--
Non-strict inequality of floating-point numbers. Typically used via the `≤` operator.
-/
def Float.le : Float Float Prop := fun a b =>
floatSpec.le a.val b.val
/--
Bit-for-bit conversion from `UInt64`. Interprets a `UInt64` as a `Float`, ignoring the numeric value
and treating the `UInt64`'s bit pattern as a `Float`.
Raw transmutation from `UInt64`.
`Float`s and `UInt64`s have the same endianness on all supported platforms. IEEE 754 very precisely
specifies the bit layout of floats.
This function does not reduce in the kernel.
Floats and UInts have the same endianness on all supported platforms.
IEEE 754 very precisely specifies the bit layout of floats.
-/
@[extern "lean_float_of_bits"] opaque Float.ofBits : UInt64 Float
/--
Bit-for-bit conversion to `UInt64`. Interprets a `Float` as a `UInt64`, ignoring the numeric value
and treating the `Float`'s bit pattern as a `UInt64`.
Raw transmutation to `UInt64`.
`Float`s and `UInt64`s have the same endianness on all supported platforms. IEEE 754 very precisely
specifies the bit layout of floats.
Floats and UInts have the same endianness on all supported platforms.
IEEE 754 very precisely specifies the bit layout of floats.
This function is distinct from `Float.toUInt64`, which attempts to preserve the numeric value rather
than reinterpreting the bit pattern.
Note that this function is distinct from `Float.toUInt64`, which attempts
to preserve the numeric value, and not the bitwise value.
-/
@[extern "lean_float_to_bits"] opaque Float.toBits : Float UInt64
@@ -128,33 +74,15 @@ instance : Neg Float := ⟨Float.neg⟩
instance : LT Float := Float.lt
instance : LE Float := Float.le
/--
Checks whether two floating-point numbers are equal according to IEEE 754.
Floating-point equality does not correspond with propositional equality. In particular, it is not
reflexive since `NaN != NaN`, and it is not a congruence because `0.0 == -0.0`, but
`1.0 / 0.0 != 1.0 / -0.0`.
This function does not reduce in the kernel. It is compiled to the C equality operator.
-/
/-- Note: this is not reflexive since `NaN != NaN`.-/
@[extern "lean_float_beq"] opaque Float.beq (a b : Float) : Bool
instance : BEq Float := Float.beq
/--
Compares two floating point numbers for strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float_decLt"] opaque Float.decLt (a b : Float) : Decidable (a < b) :=
match a, b with
| a, b => floatSpec.decLt a b
/--
Compares two floating point numbers for non-strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float_decLe"] opaque Float.decLe (a b : Float) : Decidable (a b) :=
match a, b with
| a, b => floatSpec.decLe a b
@@ -162,95 +90,44 @@ This function does not reduce in the kernel. It is compiled to the C inequality
instance floatDecLt (a b : Float) : Decidable (a < b) := Float.decLt a b
instance floatDecLe (a b : Float) : Decidable (a b) := Float.decLe a b
/--
Converts a floating-point number to a string.
This function does not reduce in the kernel.
-/
@[extern "lean_float_to_string"] opaque Float.toString : Float String
/--
Converts a floating-point number to an 8-bit unsigned integer.
If the given `Float` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt8`. Returns `0` if the `Float` is negative or `NaN`, and returns the
largest `UInt8` value (i.e. `UInt8.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt8` (including Inf), returns the maximum value of `UInt8`
(i.e. `UInt8.size - 1`).
-/
@[extern "lean_float_to_uint8"] opaque Float.toUInt8 : Float UInt8
/--
Converts a floating-point number to a 16-bit unsigned integer.
If the given `Float` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt16`. Returns `0` if the `Float` is negative or `NaN`, and returns the
largest `UInt16` value (i.e. `UInt16.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt16` (including Inf), returns the maximum value of `UInt16`
(i.e. `UInt16.size - 1`).
-/
@[extern "lean_float_to_uint16"] opaque Float.toUInt16 : Float UInt16
/--
Converts a floating-point number to a 32-bit unsigned integer.
If the given `Float` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt32`. Returns `0` if the `Float` is negative or `NaN`, and returns the
largest `UInt32` value (i.e. `UInt32.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt32` (including Inf), returns the maximum value of `UInt32`
(i.e. `UInt32.size - 1`).
-/
@[extern "lean_float_to_uint32"] opaque Float.toUInt32 : Float UInt32
/--
Converts a floating-point number to a 64-bit unsigned integer.
If the given `Float` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt64`. Returns `0` if the `Float` is negative or `NaN`, and returns the
largest `UInt64` value (i.e. `UInt64.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt64` (including Inf), returns the maximum value of `UInt64`
(i.e. `UInt64.size - 1`).
-/
@[extern "lean_float_to_uint64"] opaque Float.toUInt64 : Float UInt64
/--
Converts a floating-point number to a word-sized unsigned integer.
If the given `Float` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `USize`. Returns `0` if the `Float` is negative or `NaN`, and returns the
largest `USize` value (i.e. `USize.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `USize` (including Inf), returns the maximum value of `USize`
(i.e. `USize.size - 1`). This value is platform dependent).
-/
@[extern "lean_float_to_usize"] opaque Float.toUSize : Float USize
/--
Checks whether a floating point number is `NaN` (“not a number”) value.
`NaN` values result from operations that might otherwise be errors, such as dividing zero by zero.
This function does not reduce in the kernel. It is compiled to the C operator `isnan`.
-/
@[extern "lean_float_isnan"] opaque Float.isNaN : Float Bool
/--
Checks whether a floating-point number is finite, that is, whether it is normal, subnormal, or zero,
but not infinite or `NaN`.
This function does not reduce in the kernel. It is compiled to the C operator `isfinite`.
-/
@[extern "lean_float_isfinite"] opaque Float.isFinite : Float Bool
/--
Checks whether a floating-point number is a positive or negative infinite number, but not a finite
number or `NaN`.
This function does not reduce in the kernel. It is compiled to the C operator `isinf`.
-/
@[extern "lean_float_isinf"] opaque Float.isInf : Float Bool
/--
Splits the given float `x` into a significand/exponent pair `(s, i)` such that `x = s * 2^i` where
`s ∈ (-1;-0.5] [0.5; 1)`. Returns an undefined value if `x` is not finite.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`frexp`.
/-- Splits the given float `x` into a significand/exponent pair `(s, i)`
such that `x = s * 2^i` where `s ∈ (-1;-0.5] [0.5; 1)`.
Returns an undefined value if `x` is not finite.
-/
@[extern "lean_float_frexp"] opaque Float.frExp : Float Float × Int
@@ -263,27 +140,15 @@ 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` 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.
-/
/-- 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. -/
@[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` 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.
-/
/-- 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. -/
@[extern "lean_usize_to_float"] opaque USize.toFloat (n : USize) : Float
instance : Inhabited Float where
@@ -294,191 +159,30 @@ instance : Repr Float where
instance : ReprAtom Float :=
/--
Computes the sine of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`sin`.
-/
@[extern "sin"] opaque Float.sin : Float Float
/--
Computes the cosine of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`cos`.
-/
@[extern "cos"] opaque Float.cos : Float Float
/--
Computes the tangent of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`tan`.
-/
@[extern "tan"] opaque Float.tan : Float Float
/--
Computes the arc sine (inverse sine) of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`asin`.
-/
@[extern "asin"] opaque Float.asin : Float Float
/--
Computes the arc cosine (inverse cosine) of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`acos`.
-/
@[extern "acos"] opaque Float.acos : Float Float
/--
Computes the arc tangent (inverse tangent) of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`atan`.
-/
@[extern "atan"] opaque Float.atan : Float Float
/--
Computes the arc tangent (inverse tangent) of `y / x` in radians, in the range `-π``π`. The signs
of the arguments determine the quadrant of the result.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`atan2`.
-/
@[extern "atan2"] opaque Float.atan2 (y x : Float) : Float
/--
Computes the hyperbolic sine of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`sinh`.
-/
@[extern "atan2"] opaque Float.atan2 : Float Float Float
@[extern "sinh"] opaque Float.sinh : Float Float
/--
Computes the hyperbolic cosine of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`cosh`.
-/
@[extern "cosh"] opaque Float.cosh : Float Float
/--
Computes the hyperbolic tangent of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`tanh`.
-/
@[extern "tanh"] opaque Float.tanh : Float Float
/--
Computes the hyperbolic arc sine (inverse sine) of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`asinh`.
-/
@[extern "asinh"] opaque Float.asinh : Float Float
/--
Computes the hyperbolic arc cosine (inverse cosine) of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`acosh`.
-/
@[extern "acosh"] opaque Float.acosh : Float Float
/--
Computes the hyperbolic arc tangent (inverse tangent) of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`atanh`.
-/
@[extern "atanh"] opaque Float.atanh : Float Float
/--
Computes the exponential `e^x` of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`exp`.
-/
@[extern "exp"] opaque Float.exp (x : Float) : Float
/--
Computes the base-2 exponential `2^x` of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`exp2`.
-/
@[extern "exp2"] opaque Float.exp2 (x : Float) : Float
/--
Computes the natural logarithm `ln x` of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`log`.
-/
@[extern "log"] opaque Float.log (x : Float) : Float
/--
Computes the base-2 logarithm of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`log2`.
-/
@[extern "exp"] opaque Float.exp : Float Float
@[extern "exp2"] opaque Float.exp2 : Float Float
@[extern "log"] opaque Float.log : Float Float
@[extern "log2"] opaque Float.log2 : Float Float
/--
Computes the base-10 logarithm of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`log10`.
-/
@[extern "log10"] opaque Float.log10 : Float Float
/--
Raises one floating-point number to the power of another. Typically used via the `^` operator.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`pow`.
-/
@[extern "pow"] opaque Float.pow : Float Float Float
/--
Computes the square root of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`sqrt`.
-/
@[extern "sqrt"] opaque Float.sqrt : Float Float
/--
Computes the cube root of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`cbrt`.
-/
@[extern "cbrt"] opaque Float.cbrt : Float Float
/--
Computes the ceiling of a floating-point number, which is the smallest integer that's no smaller
than the given number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`ceil`.
Examples:
* `Float.ceil 1.5 = 2`
* `Float.ceil (-1.5) = (-1)`
-/
@[extern "ceil"] opaque Float.ceil : Float Float
/--
Computes the floor of a floating-point number, which is the largest integer that's no larger
than the given number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`floor`.
Examples:
* `Float.floor 1.5 = 1`
* `Float.floor (-1.5) = (-2)`
-/
@[extern "floor"] opaque Float.floor : Float Float
/--
Rounds to the nearest integer, rounding away from zero at half-way points.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`round`.
-/
@[extern "round"] opaque Float.round : Float Float
/--
Computes the absolute value of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`fabs`.
-/
@[extern "fabs"] opaque Float.abs : Float Float
instance : HomogeneousPow Float := Float.pow
@@ -489,8 +193,6 @@ instance : Max Float := maxOfLe
/--
Efficiently computes `x * 2^i`.
This function does not reduce in the kernel.
-/
@[extern "lean_float_scaleb"]
opaque Float.scaleB (x : Float) (i : @& Int) : Float

View File

@@ -19,101 +19,43 @@ opaque float32Spec : FloatSpec := {
decLe := fun _ _ => inferInstanceAs (Decidable True)
}
/--
32-bit floating-point numbers.
`Float32` corresponds to the IEEE 754 *binary32* format (`float` in C or `f32` in Rust).
Floating-point numbers are a finite representation of a subset of the real numbers, extended with
extra “sentinel” values that represent undefined and infinite results as well as separate positive
and negative zeroes. Arithmetic on floating-point numbers approximates the corresponding operations
on the real numbers by rounding the results to numbers that are representable, propagating error and
infinite values.
Floating-point numbers include [subnormal numbers](https://en.wikipedia.org/wiki/Subnormal_number).
Their special values are:
* `NaN`, which denotes a class of “not a number” values that result from operations such as
dividing zero by zero, and
* `Inf` and `-Inf`, which represent positive and infinities that result from dividing non-zero
values by zero.
-/
/-- Native floating point type, corresponding to the IEEE 754 *binary32* format
(`float` in C or `f32` in Rust). -/
structure Float32 where
val : float32Spec.float
instance : Nonempty Float32 := { val := float32Spec.val }
/--
Adds two 32-bit floating-point numbers according to IEEE 754. Typically used via the `+` operator.
This function does not reduce in the kernel. It is compiled to the C addition operator.
-/
@[extern "lean_float32_add"] opaque Float32.add : Float32 Float32 Float32
/--
Subtracts 32-bit floating-point numbers according to IEEE 754. Typically used via the `-` operator.
This function does not reduce in the kernel. It is compiled to the C subtraction operator.
-/
@[extern "lean_float32_sub"] opaque Float32.sub : Float32 Float32 Float32
/--
Multiplies 32-bit floating-point numbers according to IEEE 754. Typically used via the `*` operator.
This function does not reduce in the kernel. It is compiled to the C multiplication operator.
-/
@[extern "lean_float32_mul"] opaque Float32.mul : Float32 Float32 Float32
/--
Divides 32-bit floating-point numbers according to IEEE 754. Typically used via the `/` operator.
In Lean, division by zero typically yields zero. For `Float32`, it instead yields either `Inf`,
`-Inf`, or `NaN`.
This function does not reduce in the kernel. It is compiled to the C division operator.
-/
@[extern "lean_float32_div"] opaque Float32.div : Float32 Float32 Float32
/--
Negates 32-bit floating-point numbers according to IEEE 754. Typically used via the `-` prefix
operator.
This function does not reduce in the kernel. It is compiled to the C negation operator.
-/
@[extern "lean_float32_negate"] opaque Float32.neg : Float32 Float32
set_option bootstrap.genMatcherCode false
/--
Strict inequality of floating-point numbers. Typically used via the `<` operator.
-/
def Float32.lt : Float32 Float32 Prop := fun a b =>
match a, b with
| a, b => float32Spec.lt a b
/--
Non-strict inequality of floating-point numbers. Typically used via the `≤` operator.
-/
def Float32.le : Float32 Float32 Prop := fun a b =>
float32Spec.le a.val b.val
/--
Bit-for-bit conversion from `UInt32`. Interprets a `UInt32` as a `Float32`, ignoring the numeric
value and treating the `UInt32`'s bit pattern as a `Float32`.
Raw transmutation from `UInt32`.
`Float32`s and `UInt32`s have the same endianness on all supported platforms. IEEE 754 very
precisely specifies the bit layout of floats.
This function does not reduce in the kernel.
Float32s and UInts have the same endianness on all supported platforms.
IEEE 754 very precisely specifies the bit layout of floats.
-/
@[extern "lean_float32_of_bits"] opaque Float32.ofBits : UInt32 Float32
/--
Bit-for-bit conversion to `UInt32`. Interprets a `Float32` as a `UInt32`, ignoring the numeric value
and treating the `Float32`'s bit pattern as a `UInt32`.
Raw transmutation to `UInt32`.
`Float32`s and `UInt32`s have the same endianness on all supported platforms. IEEE 754 very
precisely specifies the bit layout of floats.
Float32s and UInts have the same endianness on all supported platforms.
IEEE 754 very precisely specifies the bit layout of floats.
This function is distinct from `Float.toUInt32`, which attempts to preserve the numeric value rather
than reinterpreting the bit pattern.
This function does not reduce in the kernel.
Note that this function is distinct from `Float32.toUInt32`, which attempts
to preserve the numeric value, and not the bitwise value.
-/
@[extern "lean_float32_to_bits"] opaque Float32.toBits : Float32 UInt32
@@ -125,33 +67,15 @@ instance : Neg Float32 := ⟨Float32.neg⟩
instance : LT Float32 := Float32.lt
instance : LE Float32 := Float32.le
/--
Checks whether two floating-point numbers are equal according to IEEE 754.
Floating-point equality does not correspond with propositional equality. In particular, it is not
reflexive since `NaN != NaN`, and it is not a congruence because `0.0 == -0.0`, but
`1.0 / 0.0 != 1.0 / -0.0`.
This function does not reduce in the kernel. It is compiled to the C equality operator.
-/
/-- Note: this is not reflexive since `NaN != NaN`.-/
@[extern "lean_float32_beq"] opaque Float32.beq (a b : Float32) : Bool
instance : BEq Float32 := Float32.beq
/--
Compares two floating point numbers for strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float32_decLt"] opaque Float32.decLt (a b : Float32) : Decidable (a < b) :=
match a, b with
| a, b => float32Spec.decLt a b
/--
Compares two floating point numbers for non-strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float32_decLe"] opaque Float32.decLe (a b : Float32) : Decidable (a b) :=
match a, b with
| a, b => float32Spec.decLe a b
@@ -159,91 +83,44 @@ This function does not reduce in the kernel. It is compiled to the C inequality
instance float32DecLt (a b : Float32) : Decidable (a < b) := Float32.decLt a b
instance float32DecLe (a b : Float32) : Decidable (a b) := Float32.decLe a b
/--
Converts a floating-point number to a string.
This function does not reduce in the kernel.
-/
@[extern "lean_float32_to_string"] opaque Float32.toString : Float32 String
/--
Converts a floating-point number to an 8-bit unsigned integer.
If the given `Float32` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt8`. Returns `0` if the `Float32` is negative or `NaN`, and returns the
largest `UInt8` value (i.e. `UInt8.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt8` (including Inf), returns the maximum value of `UInt8`
(i.e. `UInt8.size - 1`).
-/
@[extern "lean_float32_to_uint8"] opaque Float32.toUInt8 : Float32 UInt8
/--
Converts a floating-point number to a 16-bit unsigned integer.
If the given `Float32` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt16`. Returns `0` if the `Float32` is negative or `NaN`, and returns
the largest `UInt16` value (i.e. `UInt16.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt16` (including Inf), returns the maximum value of `UInt16`
(i.e. `UInt16.size - 1`).
-/
@[extern "lean_float32_to_uint16"] opaque Float32.toUInt16 : Float32 UInt16
/--
Converts a floating-point number to a 32-bit unsigned integer.
If the given `Float32` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt32`. Returns `0` if the `Float32` is negative or `NaN`, and returns
the largest `UInt32` value (i.e. `UInt32.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt32` (including Inf), returns the maximum value of `UInt32`
(i.e. `UInt32.size - 1`).
-/
@[extern "lean_float32_to_uint32"] opaque Float32.toUInt32 : Float32 UInt32
/--
Converts a floating-point number to a 64-bit unsigned integer.
If the given `Float32` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `UInt64`. Returns `0` if the `Float32` is negative or `NaN`, and returns
the largest `UInt64` value (i.e. `UInt64.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `UInt64` (including Inf), returns the maximum value of `UInt64`
(i.e. `UInt64.size - 1`).
-/
@[extern "lean_float32_to_uint64"] opaque Float32.toUInt64 : Float32 UInt64
/--
Converts a floating-point number to a word-sized unsigned integer.
If the given `Float32` is non-negative, truncates the value to a positive integer, rounding down and
clamping to the range of `USize`. Returns `0` if the `Float32` is negative or `NaN`, and returns the
largest `USize` value (i.e. `USize.size - 1`) if the float is larger than it.
This function does not reduce in the kernel.
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
If negative or NaN, returns `0`.
If larger than the maximum value for `USize` (including Inf), returns the maximum value of `USize`
(i.e. `USize.size - 1`). This value is platform dependent).
-/
@[extern "lean_float32_to_usize"] opaque Float32.toUSize : Float32 USize
/--
Checks whether a floating point number is `NaN` ("not a number") value.
`NaN` values result from operations that might otherwise be errors, such as dividing zero by zero.
This function does not reduce in the kernel. It is compiled to the C operator `isnan`.
-/
@[extern "lean_float32_isnan"] opaque Float32.isNaN : Float32 Bool
/--
Checks whether a floating-point number is finite, that is, whether it is normal, subnormal, or zero,
but not infinite or `NaN`.
This function does not reduce in the kernel. It is compiled to the C operator `isfinite`.
-/
@[extern "lean_float32_isfinite"] opaque Float32.isFinite : Float32 Bool
/--
Checks whether a floating-point number is a positive or negative infinite number, but not a finite
number or `NaN`.
This function does not reduce in the kernel. It is compiled to the C operator `isinf`.
-/
@[extern "lean_float32_isinf"] opaque Float32.isInf : Float32 Bool
/--
Splits the given float `x` into a significand/exponent pair `(s, i)` such that `x = s * 2^i` where
`s ∈ (-1;-0.5] [0.5; 1)`. Returns an undefined value if `x` is not finite.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`frexp`.
/-- Splits the given float `x` into a significand/exponent pair `(s, i)`
such that `x = s * 2^i` where `s ∈ (-1;-0.5] [0.5; 1)`.
Returns an undefined value if `x` is not finite.
-/
@[extern "lean_float32_frexp"] opaque Float32.frExp : Float32 Float32 × Int
@@ -254,37 +131,20 @@ 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` 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.
-/
/-- 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. -/
@[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` 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.
-/
/-- 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. -/
@[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` 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.
-/
/-- 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. -/
@[extern "lean_usize_to_float32"] opaque USize.toFloat32 (n : USize) : Float32
instance : Inhabited Float32 where
@@ -295,191 +155,30 @@ instance : Repr Float32 where
instance : ReprAtom Float32 :=
/--
Computes the sine of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`sinf`.
-/
@[extern "sinf"] opaque Float32.sin : Float32 Float32
/--
Computes the cosine of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`cosf`.
-/
@[extern "cosf"] opaque Float32.cos : Float32 Float32
/--
Computes the tangent of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`tanf`.
-/
@[extern "tanf"] opaque Float32.tan : Float32 Float32
/--
Computes the arc sine (inverse sine) of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`asinf`.
-/
@[extern "asinf"] opaque Float32.asin : Float32 Float32
/--
Computes the arc cosine (inverse cosine) of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`acosf`.
-/
@[extern "acosf"] opaque Float32.acos : Float32 Float32
/--
Computes the arc tangent (inverse tangent) of a floating-point number in radians.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`atanf`.
-/
@[extern "atanf"] opaque Float32.atan : Float32 Float32
/--
Computes the arc tangent (inverse tangent) of `y / x` in radians, in the range `-π``π`. The signs
of the arguments determine the quadrant of the result.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`atan2f`.
-/
@[extern "atan2f"] opaque Float32.atan2 : Float32 Float32 Float32
/--
Computes the hyperbolic sine of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`sinhf`.
-/
@[extern "sinhf"] opaque Float32.sinh : Float32 Float32
/--
Computes the hyperbolic cosine of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`coshf`.
-/
@[extern "coshf"] opaque Float32.cosh : Float32 Float32
/--
Computes the hyperbolic tangent of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`tanhf`.
-/
@[extern "tanhf"] opaque Float32.tanh : Float32 Float32
/--
Computes the hyperbolic arc sine (inverse sine) of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`asinhf`.
-/
@[extern "asinhf"] opaque Float32.asinh : Float32 Float32
/--
Computes the hyperbolic arc cosine (inverse cosine) of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`acoshf`.
-/
@[extern "acoshf"] opaque Float32.acosh : Float32 Float32
/--
Computes the hyperbolic arc tangent (inverse tangent) of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`atanhf`.
-/
@[extern "atanhf"] opaque Float32.atanh : Float32 Float32
/--
Computes the exponential `e^x` of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`expf`.
-/
@[extern "expf"] opaque Float32.exp : Float32 Float32
/--
Computes the base-2 exponential `2^x` of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`exp2f`.
-/
@[extern "exp2f"] opaque Float32.exp2 : Float32 Float32
/--
Computes the natural logarithm `ln x` of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`logf`.
-/
@[extern "logf"] opaque Float32.log : Float32 Float32
/--
Computes the base-2 logarithm of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`log2f`.
-/
@[extern "log2f"] opaque Float32.log2 : Float32 Float32
/--
Computes the base-10 logarithm of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`log10f`.
-/
@[extern "log10f"] opaque Float32.log10 : Float32 Float32
/--
Raises one floating-point number to the power of another. Typically used via the `^` operator.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`powf`.
-/
@[extern "powf"] opaque Float32.pow : Float32 Float32 Float32
/--
Computes the square root of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`sqrtf`.
-/
@[extern "sqrtf"] opaque Float32.sqrt : Float32 Float32
/--
Computes the cube root of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`cbrtf`.
-/
@[extern "cbrtf"] opaque Float32.cbrt : Float32 Float32
/--
Computes the ceiling of a floating-point number, which is the smallest integer that's no smaller
than the given number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`ceilf`.
Examples:
* `Float32.ceil 1.5 = 2`
* `Float32.ceil (-1.5) = (-1)`
-/
@[extern "ceilf"] opaque Float32.ceil : Float32 Float32
/--
Computes the floor of a floating-point number, which is the largest integer that's no larger
than the given number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`floorf`.
Examples:
* `Float32.floor 1.5 = 1`
* `Float32.floor (-1.5) = (-2)`
-/
@[extern "floorf"] opaque Float32.floor : Float32 Float32
/--
Rounds to the nearest integer, rounding away from zero at half-way points.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`roundf`.
-/
@[extern "roundf"] opaque Float32.round : Float32 Float32
/--
Computes the absolute value of a floating-point number.
This function does not reduce in the kernel. It is implemented in compiled code by the C function
`fabsf`.
-/
@[extern "fabsf"] opaque Float32.abs : Float32 Float32
instance : HomogeneousPow Float32 := Float32.pow
@@ -490,22 +189,9 @@ instance : Max Float32 := maxOfLe
/--
Efficiently computes `x * 2^i`.
This function does not reduce in the kernel.
-/
@[extern "lean_float32_scaleb"]
opaque Float32.scaleB (x : Float32) (i : @& Int) : Float32
/--
Converts a 32-bit floating-point number to a 64-bit floating-point number.
This function does not reduce in the kernel.
-/
@[extern "lean_float32_to_float"] opaque Float32.toFloat : Float32 Float
/--
Converts a 64-bit floating-point number to a 32-bit floating-point number.
This may lose precision.
This function does not reduce in the kernel.
-/
@[extern "lean_float_to_float32"] opaque Float.toFloat32 : Float Float32

View File

@@ -17,14 +17,11 @@ attribute [extern "lean_float_array_data"] FloatArray.data
namespace FloatArray
@[extern "lean_mk_empty_float_array"]
def emptyWithCapacity (c : @& Nat) : FloatArray :=
def mkEmpty (c : @& Nat) : FloatArray :=
{ data := #[] }
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
def empty : FloatArray :=
emptyWithCapacity 0
mkEmpty 0
instance : Inhabited FloatArray where
default := empty
@@ -167,9 +164,6 @@ def foldl {β : Type v} (f : β → Float → β) (init : β) (as : FloatArray)
end FloatArray
/--
Converts a list of floats into a `FloatArray`.
-/
def List.toFloatArray (ds : List Float) : FloatArray :=
let rec loop
| [], r => r

View File

@@ -23,12 +23,6 @@ instance [ToFormat α] : ToFormat (List α) where
instance [ToFormat α] : ToFormat (Array α) where
format a := "#" ++ format a.toList
/--
Formats an optional value, with no expectation that the Lean parser should be able to parse the
result.
This function is usually accessed through the `ToFormat (Option α)` instance.
-/
def Option.format {α : Type u} [ToFormat α] : Option α Format
| none => "none"
| some a => "some " ++ Std.format a
@@ -39,10 +33,6 @@ 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

View File

@@ -9,23 +9,10 @@ import Init.Core
namespace Function
/--
Transforms a function from pairs into an equivalent two-parameter function.
Examples:
* `Function.curry (fun (x, y) => x + y) 3 5 = 8`
* `Function.curry Prod.swap 3 "five" = ("five", 3)`
-/
@[inline]
def curry : (α × β φ) α β φ := fun f a b => f (a, b)
/--
Transforms a two-parameter function into an equivalent function from pairs.
Examples:
* `Function.uncurry List.drop (1, ["a", "b", "c"]) = ["b", "c"]`
* `[("orange", 2), ("android", 3) ].map (Function.uncurry String.take) = ["or", "and"]`
-/
/-- Interpret a function with two arguments as a function on `α × β` -/
@[inline]
def uncurry : (α β φ) α × β φ := fun f a => f a.1 a.2

View File

@@ -75,19 +75,13 @@ instance (P : Prop) : Hashable P where
@[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
mixHash u 11
/--
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
`hash a = hash b`.
This is automatic if the `BEq` instance is lawful.
/-- `LawfulHashable α` says that the `BEq α` and `Hashable α` instances on `α` are compatible, i.e.,
that `a == b` implies `hash a = hash b`. This is automatic if the `BEq` instance is lawful.
-/
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
/-- If `a == b`, then `hash a = hash b`. -/
hash_eq (a b : α) : a == b hash a = hash b
/--
A lawful hash function respects its Boolean equality test.
-/
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b hash a = hash b :=
LawfulHashable.hash_eq a b

View File

@@ -14,4 +14,3 @@ import Init.Data.Int.Order
import Init.Data.Int.Pow
import Init.Data.Int.Cooper
import Init.Data.Int.Linear
import Init.Data.Int.OfNat

View File

@@ -17,36 +17,31 @@ open Nat
This file defines the `Int` type as well as
* coercions, conversions, and compatibility with numeric literals,
* basic arithmetic operations add/sub/mul/pow,
* basic arithmetic operations add/sub/mul/div/mod/pow,
* a few `Nat`-related operations such as `negOfNat` and `subNatNat`,
* relations `<`/`≤`/`≥`/`>`, the `NonNeg` property and `min`/`max`,
* decidability of equality, relations and `NonNeg`.
Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
-/
/--
The integers.
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).
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).
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).
-/
inductive Int : Type where
/--
A natural number is an integer.
This constructor covers the non-negative integers (from `0` to `∞`).
-/
/-- A natural number is an integer (`0` to `∞`). -/
| ofNat : Nat Int
/--
The negation of the successor of a natural number is an integer.
This constructor covers the negative integers (from `-1` to `-∞`).
-/
/-- The negation of the successor of a natural number is an integer
(`-1` to `-∞`). -/
| negSucc : Nat Int
attribute [extern "lean_nat_to_int"] Int.ofNat
@@ -81,29 +76,15 @@ protected theorem zero_ne_one : (0 : Int) ≠ 1 := nofun
theorem ofNat_two : ((2 : Nat) : Int) = 2 := rfl
/--
Negation of natural numbers.
Examples:
* `Int.negOfNat 6 = -6`
* `Int.negOfNat 0 = 0`
-/
/-- Negation of a natural number. -/
def negOfNat : Nat Int
| 0 => 0
| succ m => negSucc m
set_option bootstrap.genMatcherCode false in
/--
Negation of integers, usually accessed via the `-` prefix operator.
/-- Negation of an integer.
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`
-/
Implemented by efficient native code. -/
@[extern "lean_int_neg"]
protected def neg (n : @& Int) : Int :=
match n with
@@ -122,30 +103,21 @@ protected def neg (n : @& Int) : Int :=
instance instNegInt : Neg Int where
neg := Int.neg
/--
Non-truncating subtraction of two natural numbers.
Examples:
* `Int.subNatNat 5 2 = 3`
* `Int.subNatNat 2 5 = -3`
* `Int.subNatNat 0 13 = -13`
-/
/-- Subtraction of two natural numbers. -/
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 integers, usually accessed via the `+` operator.
/-- Addition of two integers.
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
```
#eval (7 : Int) + (6 : Int) -- 13
#eval (6 : Int) + (-6 : Int) -- 0
```
Examples:
* `(7 : Int) + (6 : Int) = 13`
* `(6 : Int) + (-6 : Int) = 0`
-/
Implemented by efficient native code. -/
@[extern "lean_int_add"]
protected def add (m n : @& Int) : Int :=
match m, n with
@@ -158,17 +130,15 @@ instance : Add Int where
add := Int.add
set_option bootstrap.genMatcherCode false in
/--
Multiplication of integers, usually accessed via the `*` operator.
/-- Multiplication of two integers.
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
```
#eval (63 : Int) * (6 : Int) -- 378
#eval (6 : Int) * (-6 : Int) -- -36
#eval (7 : Int) * (0 : Int) -- 0
```
Examples:
* `(63 : Int) * (6 : Int) = 378`
* `(6 : Int) * (-6 : Int) = -36`
* `(7 : Int) * (0 : Int) = 0`
-/
Implemented by efficient native code. -/
@[extern "lean_int_mul"]
protected def mul (m n : @& Int) : Int :=
match m, n with
@@ -180,65 +150,48 @@ protected def mul (m n : @& Int) : Int :=
instance : Mul Int where
mul := Int.mul
/-- Subtraction of two integers.
/--
Subtraction of integers, usually accessed via the `-` operator.
```
#eval (63 : Int) - (6 : Int) -- 57
#eval (7 : Int) - (0 : Int) -- 7
#eval (0 : Int) - (7 : Int) -- -7
```
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`
-/
Implemented by efficient native code. -/
@[extern "lean_int_sub"]
protected def sub (m n : @& Int) : Int := m + (- n)
instance : Sub Int where
sub := Int.sub
/--
An integer is non-negative if it is equal to a natural number.
-/
/-- A proof that an `Int` is non-negative. -/
inductive NonNeg : Int Prop where
/--
For all natural numbers `n`, `Int.ofNat n` is non-negative.
-/
/-- Sole constructor, proving that `ofNat n` is positive. -/
| mk (n : Nat) : NonNeg (ofNat n)
/--
Non-strict inequality of integers, usually accessed via the `≤` operator.
`a ≤ b` is defined as `b - a ≥ 0`, using `Int.NonNeg`.
-/
/-- Definition of `a ≤ b`, encoded as `b - a ≥ 0`. -/
protected def le (a b : Int) : Prop := NonNeg (b - a)
instance instLEInt : LE Int where
le := Int.le
/--
Strict inequality of integers, usually accessed via the `<` operator.
`a < b` when `a + 1 ≤ b`.
-/
/-- Definition of `a < b`, encoded as `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 whether two integers are equal. Usually accessed via the `DecidableEq Int` instance.
/-- Decides equality between two `Int`s.
This function is overridden by the compiler with an efficient implementation. This definition is the
logical model.
```
#eval (7 : Int) = (3 : Int) + (4 : Int) -- true
#eval (6 : Int) = (3 : Int) * (2 : Int) -- true
#eval ¬ (6 : Int) = (3 : Int) -- true
```
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`
-/
Implemented by efficient native code. -/
@[extern "lean_int_dec_eq"]
protected def decEq (a b : @& Int) : Decidable (a = b) :=
match a, b with
@@ -251,7 +204,6 @@ 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
@@ -297,17 +249,15 @@ instance decLt (a b : @& Int) : Decidable (a < b) :=
decNonneg _
set_option bootstrap.genMatcherCode false in
/--
The absolute value of an integer is its distance from `0`.
/-- Absolute value (`Nat`) of an integer.
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
```
#eval (7 : Int).natAbs -- 7
#eval (0 : Int).natAbs -- 0
#eval (-11 : Int).natAbs -- 11
```
Examples:
* `(7 : Int).natAbs = 7`
* `(0 : Int).natAbs = 0`
* `((-11 : Int).natAbs = 11`
-/
Implemented by efficient native code. -/
@[extern "lean_nat_abs"]
def natAbs (m : @& Int) : Nat :=
match m with
@@ -317,17 +267,8 @@ 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`.
Examples:
* `Int.sign 34 = 1`
* `Int.sign 2 = 1`
* `Int.sign 0 = 0`
* `Int.sign -1 = -1`
* `Int.sign -362 = -1`
Returns the "sign" of the integer as another integer: `1` for positive numbers,
`-1` for negative numbers, and `0` for `0`.
-/
def sign : Int Int
| Int.ofNat (succ _) => 1
@@ -336,33 +277,27 @@ def sign : Int → Int
/-! ## Conversion -/
/--
Converts an integer into a natural number. Negative numbers are converted to `0`.
/-- Turns an integer into a natural number, negative numbers become
`0`.
Examples:
* `(7 : Int).toNat = 7`
* `(0 : Int).toNat = 0`
* `(-7 : Int).toNat = 0`
```
#eval (7 : Int).toNat -- 7
#eval (0 : Int).toNat -- 0
#eval (-7 : Int).toNat -- 0
```
-/
def toNat : Int Nat
| ofNat n => n
| negSucc _ => 0
/--
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`
* If `n : Nat`, then `int.toNat' n = some n`
* If `n : Int` is negative, then `int.toNat' n = none`.
-/
def toNat? : Int Option Nat
def toNat' : Int Option Nat
| (n : Nat) => some n
| -[_+1] => none
@[deprecated toNat? (since := "2025-03-11"), inherit_doc toNat?]
abbrev toNat' := toNat?
/-! ## divisibility -/
/--
@@ -374,14 +309,14 @@ instance : Dvd Int where
/-! ## Powers -/
/--
Power of an integer to a natural number, usually accessed via the `^` operator.
/-- Power of an integer to some natural number.
Examples:
* `(2 : Int) ^ 4 = 16`
* `(10 : Int) ^ 0 = 1`
* `(0 : Int) ^ 10 = 0`
* `(-7 : Int) ^ 3 = -343`
```
#eval (2 : Int) ^ 4 -- 16
#eval (10 : Int) ^ 0 -- 1
#eval (0 : Int) ^ 10 -- 0
#eval (-7 : Int) ^ 3 -- -343
```
-/
protected def pow (m : Int) : Nat Int
| 0 => 1
@@ -401,14 +336,8 @@ instance : Max Int := maxOfLe
end Int
/--
The canonical homomorphism `Int → R`. In most use cases, the target type will have a ring structure,
and this homomorphism should be a ring homomorphism.
`IntCast` and `NatCast` exist to allow different libraries with their own types that can be notated
as natural numbers to have consistent `simp` normal forms without needing to create coercion
simplification sets that are aware of all combinations. Libraries should make it easy to work with
`IntCast` where possible. For instance, in Mathlib there will be such a homomorphism (and thus an
`IntCast R` instance) whenever `R` is an additive group with a `1`.
The canonical homomorphism `Int → R`.
In most use cases `R` will have a ring structure and this will be a ring homomorphism.
-/
class IntCast (R : Type u) where
/-- The canonical map `Int → R`. -/
@@ -416,8 +345,12 @@ class IntCast (R : Type u) where
instance : IntCast Int where intCast n := n
@[coe, reducible, match_pattern, inherit_doc IntCast]
protected def Int.cast {R : Type u} [IntCast R] : Int R :=
/--
Apply the canonical homomorphism from `Int` to a type `R` from an `IntCast R` instance.
In Mathlib there will be such a homomorphism whenever `R` is an additive group with a `1`.
-/
@[coe, reducible, match_pattern] protected def Int.cast {R : Type u} [IntCast R] : Int R :=
IntCast.intCast
-- see the notes about coercions into arbitrary types in the module doc-string

View File

@@ -1,8 +1,50 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
Authors: Mario Carneiro
-/
prelude
import Init.Data.Int.Bitwise.Basic
import Init.Data.Int.Bitwise.Lemmas
import Init.Data.Int.Basic
import Init.Data.Nat.Bitwise.Basic
namespace Int
/-! ## bit operations -/
/--
Bitwise not
Interprets the integer as an infinite sequence of bits in two's complement
and complements each bit.
```
~~~(0:Int) = -1
~~~(1:Int) = -2
~~~(-1:Int) = 0
```
-/
protected def not : Int -> Int
| Int.ofNat n => Int.negSucc n
| Int.negSucc n => Int.ofNat n
instance : Complement Int := .not
/--
Bitwise shift right.
Conceptually, this treats the integer as an infinite sequence of bits in two's
complement and shifts the value to the right.
```lean
( 0b0111:Int) >>> 1 = 0b0011
( 0b1000:Int) >>> 1 = 0b0100
(-0b1000:Int) >>> 1 = -0b0100
(-0b0111:Int) >>> 1 = -0b0100
```
-/
protected def shiftRight : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n >>> s)
| Int.negSucc n, s => Int.negSucc (n >>> s)
instance : HShiftRight Int Nat Int := .shiftRight
end Int

View File

@@ -1,48 +0,0 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.Int.Basic
import Init.Data.Nat.Bitwise.Basic
namespace Int
/-! ## bit operations -/
/--
Bitwise not, usually accessed via the `~~~` prefix operator.
Interprets the integer as an infinite sequence of bits in two's complement and complements each bit.
Examples:
* `~~~(0 : Int) = -1`
* `~~~(1 : Int) = -2`
* `~~~(-1 : Int) = 0`
-/
protected def not : Int Int
| Int.ofNat n => Int.negSucc n
| Int.negSucc n => Int.ofNat n
instance : Complement Int := .not
/--
Bitwise right shift, usually accessed via the `>>>` operator.
Interprets the integer as an infinite sequence of bits in two's complement and shifts the value to
the right.
Examples:
* `( 0b0111 : Int) >>> 1 = 0b0011`
* `( 0b1000 : Int) >>> 1 = 0b0100`
* `(-0b1000 : Int) >>> 1 = -0b0100`
* `(-0b0111 : Int) >>> 1 = -0b0100`
-/
protected def shiftRight : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n >>> s)
| Int.negSucc n, s => Int.negSucc (n >>> s)
instance : HShiftRight Int Nat Int := .shiftRight
end Int

View File

@@ -5,13 +5,12 @@ Authors: Siddharth Bhat, Jeremy Avigad
-/
prelude
import Init.Data.Nat.Bitwise.Lemmas
import Init.Data.Int.Bitwise.Basic
import Init.Data.Int.Bitwise
import Init.Data.Int.DivMod.Lemmas
namespace Int
theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl
@[simp]
theorem natCast_shiftRight (n s : Nat) : (n : Int) >>> s = n >>> s := rfl
@@ -28,7 +27,7 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
m >>> n = m / ((2 ^ n) : Nat) := by
simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow]
split
· simp; norm_cast
· simp
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
rfl
@@ -40,47 +39,4 @@ theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow]
theorem le_shiftRight_of_nonpos {n : Int} {s : Nat} (h : n 0) : n n >>> s := by
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
split
case _ _ _ m =>
simp only [ofNat_eq_coe] at h
by_cases hm : m = 0
· simp [hm]
· omega
case _ _ _ m =>
by_cases hm : m = 0
· simp [hm]
· have := Nat.shiftRight_le m s
omega
theorem shiftRight_le_of_nonneg {n : Int} {s : Nat} (h : 0 n) : n >>> s n := by
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
split
case _ _ _ m =>
simp only [Int.ofNat_eq_coe] at h
by_cases hm : m = 0
· simp [hm]
· have := Nat.shiftRight_le m s
simp
omega
case _ _ _ m =>
omega
theorem le_shiftRight_of_nonneg {n : Int} {s : Nat} (h : 0 n) : 0 (n >>> s) := by
rw [Int.shiftRight_eq_div_pow]
by_cases h' : s = 0
· simp [h', h]
· have := @Nat.pow_pos 2 s (by omega)
have := @Int.ediv_nonneg n (2^s) h (by norm_cast at *; omega)
norm_cast at *
theorem shiftRight_le_of_nonpos {n : Int} {s : Nat} (h : n 0) : (n >>> s) 0 := by
rw [Int.shiftRight_eq_div_pow]
by_cases h' : s = 0
· simp [h', h]
· have : 1 < 2 ^ s := Nat.one_lt_two_pow (by omega)
have rl : n / 2 ^ s 0 := Int.ediv_nonpos_of_nonpos_of_neg (by omega) (by norm_cast at *; omega)
norm_cast at *
end Int

View File

@@ -1,74 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro, Paul Reichert
-/
prelude
import Init.Data.Ord
import Init.Data.Int.Order
/-! # Basic lemmas about comparing integers
This file introduces some basic lemmas about `compare` as applied to integers.
Import `Std.Classes.Ord` in order to obtain the `TransOrd` and `LawfulEqOrd` instances for `Int`.
-/
namespace Int
protected theorem lt_or_eq_of_le {n m : Int} (h : n m) : n < m n = m := by
omega
protected theorem le_iff_lt_or_eq {n m : Int} : n m n < m n = m :=
Int.lt_or_eq_of_le, fun | .inl h => Int.le_of_lt h | .inr rfl => Int.le_refl _
theorem compare_eq_ite_lt (a b : Int) :
compare a b = if a < b then .lt else if b < a then .gt else .eq := by
simp only [compare, compareOfLessAndEq]
split
· rfl
· next h =>
match Int.lt_or_eq_of_le (Int.not_lt.1 h) with
| .inl h => simp [h, Int.ne_of_gt h]
| .inr rfl => simp
theorem compare_eq_ite_le (a b : Int) :
compare a b = if a b then if b a then .eq else .lt else .gt := by
rw [compare_eq_ite_lt]
split
· next hlt => simp [Int.le_of_lt hlt, Int.not_le.2 hlt]
· next hge =>
split
· next hgt => simp [Int.le_of_lt hgt, Int.not_le.2 hgt]
· next hle => simp [Int.not_lt.1 hge, Int.not_lt.1 hle]
protected theorem compare_swap (a b : Int) : (compare a b).swap = compare b a := by
simp only [compare_eq_ite_le]; (repeat' split) <;> try rfl
next h1 h2 => cases h1 (Int.le_of_not_le h2)
protected theorem compare_eq_eq {a b : Int} : compare a b = .eq a = b := by
rw [compare_eq_ite_lt]; (repeat' split) <;> simp [Int.ne_of_lt, Int.ne_of_gt, *]
next hlt hgt => exact Int.le_antisymm (Int.not_lt.1 hgt) (Int.not_lt.1 hlt)
protected theorem compare_eq_lt {a b : Int} : compare a b = .lt a < b := by
rw [compare_eq_ite_lt]; (repeat' split) <;> simp [*]
protected theorem compare_eq_gt {a b : Int} : compare a b = .gt b < a := by
rw [compare_eq_ite_lt]; (repeat' split) <;> simp [Int.le_of_lt, *]
protected theorem compare_ne_gt {a b : Int} : compare a b .gt a b := by
rw [compare_eq_ite_le]; (repeat' split) <;> simp [*]
protected theorem compare_ne_lt {a b : Int} : compare a b .lt b a := by
rw [compare_eq_ite_le]; (repeat' split) <;> simp [Int.le_of_not_le, *]
protected theorem isLE_compare {a b : Int} :
(compare a b).isLE a b := by
simp only [Int.compare_eq_ite_le]
repeat' split <;> simp_all
protected theorem isGE_compare {a b : Int} :
(compare a b).isGE b a := by
rw [ Int.compare_swap, Ordering.isGE_swap]
exact Int.isLE_compare
end Int

View File

@@ -227,4 +227,33 @@ theorem cooper_resolution_dvd_right
· exact Int.mul_neg _ _ Int.neg_le_of_neg_le lower
· exact Int.mul_neg _ _ Int.neg_mul _ _ dvd
end Int
/--
Left Cooper resolution of an upper and lower bound.
-/
theorem cooper_resolution_left
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
( x, p a * x b * x q)
( k : Int, 0 k k < a b * k + b * p a * q a k + p) := by
have h := cooper_resolution_dvd_left
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt a_pos), Int.one_dvd, and_true,
and_self] at h
exact h
/--
Right Cooper resolution of an upper and lower bound.
-/
theorem cooper_resolution_right
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
( x, p a * x b * x q)
( k : Int, 0 k k < b a * k + b * p a * q b k - q) := by
have h := cooper_resolution_dvd_right
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
have : k : Int, (b -k + q) (b k - q) := by
intro k
rw [ Int.dvd_neg, Int.neg_add, Int.neg_neg, Int.sub_eq_add_neg]
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.one_dvd, and_true,
and_self, Int.neg_eq_neg_one_mul, this] at h
exact h

View File

@@ -21,46 +21,46 @@ and satisfy `x / 0 = 0` and `x % 0 = x`.
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 SMT-LIB, and Mathlib,
However we decided it was better to use `ediv` and `emod`,
as they are consistent with the conventions used in SMTLib, and Mathlib,
and often mathematical reasoning is easier with these conventions.
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
In September 2024, we decided to do this rename (with deprecations in place),
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
ever need to use these functions and their associated lemmas.
In December 2024, we removed `div` and `mod`, but have not yet renamed `ediv` and `emod`.
In December 2024, we removed `tdiv` and `tmod`, but have not yet renamed `ediv` and `emod`.
-/
/-! ### E-rounding division
This pair satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`.
This pair satisfies `0 ≤ mod x y < natAbs y` 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.
Integer division. This version of `Int.div` uses the E-rounding convention
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
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 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.
This is the function powering the `/` notation on integers.
Examples:
* `(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`
```
#eval (7 : Int) / (0 : Int) -- 0
#eval (0 : Int) / (7 : Int) -- 0
#eval (12 : Int) / (6 : Int) -- 2
#eval (12 : Int) / (-6 : Int) -- -2
#eval (-12 : Int) / (6 : Int) -- -2
#eval (-12 : Int) / (-6 : Int) -- 2
#eval (12 : Int) / (7 : Int) -- 1
#eval (12 : Int) / (-7 : Int) -- -1
#eval (-12 : Int) / (7 : Int) -- -2
#eval (-12 : Int) / (-7 : Int) -- 2
```
Implemented by efficient native code.
-/
@[extern "lean_int_ediv"]
def ediv : (@& Int) (@& Int) Int
@@ -71,26 +71,29 @@ def ediv : (@& Int) → (@& Int) → Int
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
/--
Integer modulus that uses the E-rounding convention. Usually accessed via the `%` operator.
Integer modulus. This version of `Int.mod` uses the E-rounding convention
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
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.
This is the function powering the `%` notation on integers.
Examples:
* `(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`
```
#eval (7 : Int) % (0 : Int) -- 7
#eval (0 : Int) % (7 : Int) -- 0
#eval (12 : Int) % (6 : Int) -- 0
#eval (12 : Int) % (-6 : Int) -- 0
#eval (-12 : Int) % (6 : Int) -- 0
#eval (-12 : Int) % (-6 : Int) -- 0
#eval (12 : Int) % (7 : Int) -- 5
#eval (12 : Int) % (-7 : Int) -- 5
#eval (-12 : Int) % (7 : Int) -- 2
#eval (-12 : Int) % (-7 : Int) -- 2
```
Implemented by efficient native code.
-/
@[extern "lean_int_emod"]
def emod : (@& Int) (@& Int) Int
@@ -98,19 +101,15 @@ def emod : (@& Int) → (@& Int) → Int
| -[m+1], n => subNatNat (natAbs n) (succ (m % natAbs n))
/--
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.
The Div and Mod syntax uses ediv and emod for compatibility with SMTLIb and 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
@[norm_cast] theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
@[simp, norm_cast] theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
theorem ofNat_ediv_ofNat {a b : Nat} : (a / b : Int) = (a / b : Nat) := rfl
@[norm_cast]
@@ -123,27 +122,35 @@ theorem negSucc_emod_negSucc {a b : Nat} : -[a+1] % -[b+1] = subNatNat (b + 1) (
/-! ### T-rounding division -/
/--
Integer division using the T-rounding convention.
`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.
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`.
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.
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
[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
This function is overridden by the compiler with an efficient implementation. This definition is the
logical model.
Examples:
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`
```
#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.
-/
@[extern "lean_int_div"]
def tdiv : (@& Int) (@& Int) Int
@@ -152,32 +159,33 @@ def tdiv : (@& Int) → (@& Int) → Int
| -[m +1], ofNat n => -ofNat (succ m / n)
| -[m +1], -[n +1] => ofNat (succ m / succ n)
/-- Integer modulo using the T-rounding convention.
/-- 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`.
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
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`
-/
#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. -/
@[extern "lean_int_mod"]
def tmod : (@& Int) (@& Int) Int
| ofNat m, ofNat n => ofNat (m % n)
@@ -192,22 +200,25 @@ This pair satisfies `fdiv x y = floor (x / y)`.
-/
/--
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`.
Integer division. This version of division uses the F-rounding convention
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
Examples:
* `(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`
```
#eval (7 : Int).fdiv (0 : Int) -- 0
#eval (0 : Int).fdiv (7 : Int) -- 0
#eval (12 : Int).fdiv (6 : Int) -- 2
#eval (12 : Int).fdiv (-6 : Int) -- -2
#eval (-12 : Int).fdiv (6 : Int) -- -2
#eval (-12 : Int).fdiv (-6 : Int) -- 2
#eval (12 : Int).fdiv (7 : Int) -- 1
#eval (12 : Int).fdiv (-7 : Int) -- -2
#eval (-12 : Int).fdiv (7 : Int) -- -2
#eval (-12 : Int).fdiv (-7 : Int) -- 1
```
-/
def fdiv : Int Int Int
| 0, _ => 0
@@ -218,26 +229,26 @@ def fdiv : Int → Int → Int
| -[m+1], -[n+1] => ofNat (succ m / succ n)
/--
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`.
Integer modulus. This version of `Int.mod` uses the F-rounding convention
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
Examples:
* `(7 : Int).fmod (0 : Int) = 7`
* `(0 : Int).fmod (7 : Int) = 0`
```
#eval (7 : Int).fmod (0 : Int) -- 7
#eval (0 : Int).fmod (7 : 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 (6 : Int) -- 0
#eval (12 : Int).fmod (-6 : Int) -- 0
#eval (-12 : Int).fmod (6 : Int) -- 0
#eval (-12 : Int).fmod (-6 : Int) -- 0
#eval (12 : Int).fmod (7 : Int) -- 5
#eval (12 : Int).fmod (-7 : Int) -- -2
#eval (-12 : Int).fmod (7 : Int) -- 2
#eval (-12 : Int).fmod (-7 : Int) -- -5
```
-/
def fmod : Int Int Int
| 0, _ => 0
@@ -257,31 +268,32 @@ Balanced mod (and balanced div) are a division and modulus pair such
that `b * (Int.bdiv a b) + Int.bmod a b = a` and
`-b/2 ≤ Int.bmod a b < b/2` for all `a : Int` and `b > 0`.
Note that unlike `emod`, `fmod`, and `tmod`,
`bmod` takes a natural number as the second argument, rather than an integer.
This function is used in `omega` as well as signed bitvectors.
This is used in Omega as well as signed bitvectors.
-/
/--
Balanced modulus.
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`.
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`.
If `m = 0`, then `bmod x m = x`.
Examples:
* `(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`
```
#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
```
-/
def bmod (x : Int) (m : Nat) : Int :=
let r := x % m
@@ -291,21 +303,24 @@ 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:
* `(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`
```
#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
```
-/
def bdiv (x : Int) (m : Nat) : Int :=
if m = 0 then

View File

@@ -18,7 +18,7 @@ open Nat (succ)
namespace Int
/-! ### dvd -/
-- /-! ### dvd -/
protected theorem dvd_def (a b : Int) : (a b) = Exists (fun c => b = a * c) := rfl
@@ -53,7 +53,7 @@ protected theorem dvd_mul_left (a b : Int) : b a * b := ⟨_, Int.mul_comm .
constructor <;> exact fun k, e =>
-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]
@[simp] protected theorem dvd_neg {a b : Int} : a -b a b := by
protected theorem dvd_neg {a b : Int} : a -b a b := by
constructor <;> exact fun k, e =>
-k, by simp [ e, Int.neg_mul, Int.mul_neg, Int.neg_neg]
@@ -67,7 +67,7 @@ protected theorem dvd_mul_left (a b : Int) : b a * b := ⟨_, Int.mul_comm .
theorem ofNat_dvd_left {n : Nat} {z : Int} : (n : Int) z n z.natAbs := by
rw [ natAbs_dvd_natAbs, natAbs_ofNat]
/-! ### ediv zero -/
/-! ### *div zero -/
@[simp] theorem zero_ediv : b : Int, 0 / b = 0
| ofNat _ => show ofNat _ = _ by simp
@@ -77,7 +77,7 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) z ↔ n z.natA
| ofNat _ => show ofNat _ = _ by simp
| -[_+1] => rfl
/-! ### emod zero -/
/-! ### mod zero -/
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := rfl
@@ -89,6 +89,7 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) z ↔ n z.natA
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : ((m % n) : Int) = m % n := rfl
/-! ### mod definitions -/
theorem emod_add_ediv : a b : Int, a % b + b * (a / b) = a
@@ -105,23 +106,18 @@ where
Int.neg_neg (_-_), Int.neg_sub, Int.sub_sub_self, Int.add_right_comm]
exact congrArg (fun x => -(ofNat x + 1)) (Nat.mod_add_div ..)
/-- Variant of `emod_add_ediv` with the multiplication written the other way around. -/
theorem emod_add_ediv' (a b : Int) : a % b + a / b * b = a := by
rw [Int.mul_comm]; exact emod_add_ediv ..
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
rw [Int.add_comm]; exact emod_add_ediv ..
/-- Variant of `ediv_add_emod` with the multiplication written the other way around. -/
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
rw [Int.mul_comm]; exact ediv_add_emod ..
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
rw [ Int.add_sub_cancel (a % b), emod_add_ediv]
/-! ### `/` ediv -/
@[simp] theorem ediv_neg : a b : Int, a / (-b) = -(a / b)
@[simp] protected theorem ediv_neg : a b : Int, a / (-b) = -(a / b)
| ofNat m, 0 => show ofNat (m / 0) = -(m / 0) by rw [Nat.div_zero]; rfl
| ofNat _, -[_+1] => (Int.neg_neg _).symm
| ofNat _, succ _ | -[_+1], 0 | -[_+1], succ _ | -[_+1], -[_+1] => rfl
@@ -158,10 +154,6 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
apply congrArg negSucc
rw [Nat.mul_comm, Nat.sub_mul_div]; rwa [Nat.mul_comm]
theorem add_mul_ediv_left (a : Int) {b : Int}
(c : Int) (H : b 0) : (a + b * c) / b = a / b + c :=
Int.mul_comm .. Int.add_mul_ediv_right _ _ H
theorem add_ediv_of_dvd_right {a b c : Int} (H : c b) : (a + b) / c = a / c + b / c :=
if h : c = 0 then by simp [h] else by
let k, hk := H
@@ -178,14 +170,13 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c a) : (a + b) / c = a / c +
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a 0) : (a * b) / a = b :=
Int.mul_comm .. Int.mul_ediv_cancel _ H
theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 a / b 0 a := by
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b 0 a 0 := by
rw [Int.div_def]
match b, h with
| Int.ofNat (b+1), _ =>
rcases a with a <;> simp [Int.ediv]
@[deprecated ediv_nonneg_iff_of_pos (since := "2025-02-28")]
abbrev div_nonneg_iff_of_pos := @ediv_nonneg_iff_of_pos
norm_cast
simp
/-! ### emod -/
@@ -198,6 +189,16 @@ theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
| ofNat _, _, _, rfl => ofNat_lt.2 (Nat.mod_lt _ (Nat.succ_pos _))
| -[_+1], _, _, rfl => Int.sub_lt_self _ (ofNat_lt.2 <| Nat.succ_pos _)
theorem mul_ediv_self_le {x k : Int} (h : k 0) : k * (x / k) x :=
calc k * (x / k)
_ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
_ = x := ediv_add_emod _ _
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
calc x
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
@[simp] theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
if cz : c = 0 then by
rw [cz, Int.mul_zero, Int.add_zero]
@@ -305,18 +306,6 @@ theorem emod_pos_of_not_dvd {a b : Int} (h : ¬ a b) : a = 0 0 < b % a :
· simp_all
· exact Or.inr (Int.lt_iff_le_and_ne.mpr emod_nonneg b w, Ne.symm h)
/-! ### `/` and ordering -/
theorem mul_ediv_self_le {x k : Int} (h : k 0) : k * (x / k) x :=
calc k * (x / k)
_ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
_ = x := ediv_add_emod _ _
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
calc x
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
/-! ### bmod -/
@[simp] theorem bmod_emod : bmod x m % m = x % m := by

File diff suppressed because it is too large Load Diff

View File

@@ -11,30 +11,12 @@ import Init.Data.Int.DivMod.Lemmas
/-!
Definition and lemmas for gcd and lcm over Int
## Future work
Most of the material about `Nat.gcd` and `Nat.lcm` from `Init.Data.Nat.Gcd` and `Init.Data.Nat.Lcm`
has analogues for `Int.gcd` and `Int.lcm` that should be added to this file.
-/
namespace Int
/-! ## gcd -/
/--
Computes the greatest common divisor of two integers as a 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`
-/
/-- Computes the greatest common divisor of two integers, as a `Nat`. -/
def gcd (m n : Int) : Nat := m.natAbs.gcd n.natAbs
theorem gcd_dvd_left {a b : Int} : (gcd a b : Int) a := by
@@ -55,18 +37,7 @@ theorem gcd_dvd_right {a b : Int} : (gcd a b : Int) b := by
/-! ## lcm -/
/--
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`
-/
/-- Computes the least common multiple of two integers, as a `Nat`. -/
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

View File

@@ -25,32 +25,31 @@ theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat
@[norm_cast] theorem ofNat_add (n m : Nat) : ((n + m) : Int) = n + m := rfl
@[norm_cast] theorem ofNat_mul (n m : Nat) : ((n * m) : Int) = n * m := rfl
@[norm_cast] theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
@[local simp] theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
@[local simp] theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
@[local simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
theorem negSucc_coe (n : Nat) : -[n+1] = -(n + 1) := rfl
theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
/-! ## These are only for internal use -/
@[simp] theorem add_def {a b : Int} : Int.add a b = a + b := rfl
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
/-!
## These are only for internal use
Ideally these could all be made private, but they are used in downstream libraries.
-/
@[local simp] theorem ofNat_add_ofNat (m n : Nat) : (m + n : Int) = (m + n) := rfl
@[local simp] theorem ofNat_add_negSucc (m n : Nat) : m + -[n+1] = subNatNat m (succ n) := rfl
@[local simp] theorem negSucc_add_ofNat (m n : Nat) : -[m+1] + n = subNatNat n (succ m) := rfl
@[local simp] theorem negSucc_add_negSucc (m n : Nat) : -[m+1] + -[n+1] = -[succ (m + n) +1] := rfl
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
@[local simp] theorem ofNat_mul_ofNat (m n : Nat) : (m * n : Int) = (m * n) := rfl
@[local simp] private theorem ofNat_mul_negSucc' (m n : Nat) : m * -[n+1] = negOfNat (m * succ n) := rfl
@[local simp] private theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * n = negOfNat (succ m * n) := rfl
@[local simp] private theorem negSucc_mul_negSucc' (m n : Nat) :
@[local simp] theorem ofNat_mul_negSucc' (m n : Nat) : m * -[n+1] = negOfNat (m * succ n) := rfl
@[local simp] theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * n = negOfNat (succ m * n) := rfl
@[local simp] theorem negSucc_mul_negSucc' (m n : Nat) :
-[m+1] * -[n+1] = ofNat (succ m * succ n) := rfl
/- ## some basic functions and properties -/
@@ -65,14 +64,11 @@ theorem negSucc_inj : negSucc m = negSucc n ↔ m = n := ⟨negSucc.inj, fun H =
theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_coe (n : Nat) : -[n+1] = -(n + 1) := rfl
@[simp] theorem negSucc_ne_zero (n : Nat) : -[n+1] 0 := nofun
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 -[n+1] := nofun
@[simp, norm_cast] theorem cast_ofNat_Int :
@[simp, norm_cast] theorem Nat.cast_ofNat_Int :
(Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
/- ## neg -/
@@ -82,7 +78,7 @@ theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
| succ _ => rfl
| -[_+1] => rfl
@[simp] protected theorem neg_inj {a b : Int} : -a = -b a = b :=
protected theorem neg_inj {a b : Int} : -a = -b a = b :=
fun h => by rw [ Int.neg_neg a, Int.neg_neg b, h], congrArg _
@[simp] protected theorem neg_eq_zero : -a = 0 a = 0 := Int.neg_inj (b := 0)
@@ -90,13 +86,12 @@ theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
protected theorem neg_ne_zero : -a 0 a 0 := not_congr Int.neg_eq_zero
protected theorem sub_eq_add_neg {a b : Int} : a - b = a + -b := rfl
protected theorem add_neg_eq_sub {a b : Int} : a + -b = a - b := rfl
theorem add_neg_one (i : Int) : i + -1 = i - 1 := rfl
/- ## basic properties of subNatNat -/
@[elab_as_elim]
-- @[elabAsElim] -- TODO(Mario): unexpected eliminator resulting type
theorem subNatNat_elim (m n : Nat) (motive : Nat Nat Int Prop)
(hp : i n, motive (n + i) n i)
(hn : i m, motive m (m + i + 1) -[i+1]) :
@@ -145,6 +140,29 @@ theorem subNatNat_of_lt {m n : Nat} (h : m < n) : subNatNat m n = -[pred (n - m)
rw [Nat.sub_eq_iff_eq_add' h]
simp
/- # Additive group properties -/
/- addition -/
protected theorem add_comm : a b : Int, a + b = b + a
| ofNat n, ofNat m => by simp [Nat.add_comm]
| ofNat _, -[_+1] => rfl
| -[_+1], ofNat _ => rfl
| -[_+1], -[_+1] => by simp [Nat.add_comm]
instance : Std.Commutative (α := Int) (· + ·) := Int.add_comm
@[simp] protected theorem add_zero : a : Int, a + 0 = a
| ofNat _ => rfl
| -[_+1] => rfl
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. a.add_zero
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
left_id := Int.zero_add
right_id := Int.add_zero
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
theorem subNatNat_sub (h : n m) (k : Nat) : subNatNat (m - n) k = subNatNat m (k + n) := by
rwa [ subNatNat_add_add _ _ n, Nat.sub_add_cancel]
@@ -173,34 +191,6 @@ theorem subNatNat_add_negSucc (m n k : Nat) :
Nat.add_assoc, succ_sub_succ_eq_sub, Nat.add_comm n,Nat.add_sub_assoc (Nat.le_of_lt h'),
Nat.add_comm]
theorem subNatNat_self : n, subNatNat n n = 0
| 0 => rfl
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
/- # Additive group properties -/
/- addition -/
protected theorem add_comm : a b : Int, a + b = b + a
| ofNat n, ofNat m => by simp [Nat.add_comm]
| ofNat _, -[_+1] => rfl
| -[_+1], ofNat _ => rfl
| -[_+1], -[_+1] => by simp [Nat.add_comm]
instance : Std.Commutative (α := Int) (· + ·) := Int.add_comm
@[simp] protected theorem add_zero : a : Int, a + 0 = a
| ofNat _ => rfl
| -[_+1] => rfl
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. a.add_zero
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
left_id := Int.zero_add
right_id := Int.add_zero
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
protected theorem add_assoc : a b c : Int, a + b + c = a + (b + c)
| (m:Nat), (n:Nat), _ => aux1 ..
| Nat.cast m, b, Nat.cast k => by
@@ -232,12 +222,18 @@ protected theorem add_right_comm (a b c : Int) : a + b + c = a + c + b := by
/- ## negation -/
protected theorem add_left_neg : a : Int, -a + a = 0
theorem subNatNat_self : n, subNatNat n n = 0
| 0 => rfl
| succ m => by simp [neg_ofNat_succ]
| -[m+1] => by simp [neg_negSucc]
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
protected theorem add_right_neg (a : Int) : a + -a = 0 := by
attribute [local simp] subNatNat_self
@[local simp] protected theorem add_left_neg : a : Int, -a + a = 0
| 0 => rfl
| succ m => by simp
| -[m+1] => by simp
@[local simp] protected theorem add_right_neg (a : Int) : a + -a = 0 := by
rw [Int.add_comm, Int.add_left_neg]
protected theorem neg_eq_of_add_eq_zero {a b : Int} (h : a + b = 0) : -a = b := by
@@ -268,22 +264,11 @@ protected theorem add_left_cancel {a b c : Int} (h : a + b = a + c) : b = c := b
have h₁ : -a + (a + b) = -a + (a + c) := by rw [h]
simp [ Int.add_assoc, Int.add_left_neg, Int.zero_add] at h₁; exact h₁
protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
@[local simp] protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
apply Int.add_left_cancel (a := a + b)
rw [Int.add_right_neg, Int.add_comm a, Int.add_assoc, Int.add_assoc b,
Int.add_right_neg, Int.add_zero, Int.add_right_neg]
/--
If a predicate on the integers is invariant under negation,
then it is sufficient to prove it for the nonnegative integers.
-/
theorem wlog_sign {P : Int Prop} (inv : i, P i P (-i)) (w : n : Nat, P n) (i : Int) : P i := by
cases i with
| ofNat n => exact w n
| negSucc n =>
rw [negSucc_eq, inv, ofNat_succ]
apply w
/- ## subtraction -/
@[simp] theorem negSucc_sub_one (n : Nat) : -[n+1] - 1 = -[n + 1 +1] := rfl
@@ -307,13 +292,13 @@ protected theorem sub_eq_zero {a b : Int} : a - b = 0 ↔ a = b :=
Int.eq_of_sub_eq_zero, Int.sub_eq_zero_of_eq
protected theorem sub_sub (a b c : Int) : a - b - c = a - (b + c) := by
simp [Int.sub_eq_add_neg, Int.add_assoc, Int.neg_add]
simp [Int.sub_eq_add_neg, Int.add_assoc]
protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
simp [Int.sub_eq_add_neg, Int.add_comm, Int.neg_add]
simp [Int.sub_eq_add_neg, Int.add_comm]
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
simp [Int.sub_eq_add_neg, Int.add_assoc, Int.neg_add, Int.add_right_neg]
simp [Int.sub_eq_add_neg, Int.add_assoc]
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
@@ -324,7 +309,7 @@ protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
Int.add_neg_cancel_right a b
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_neg_eq_sub]
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.sub_eq_add_neg]
@[norm_cast] theorem ofNat_sub (h : m n) : ((n - m : Nat) : Int) = n - m := by
match m with
@@ -333,7 +318,6 @@ protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
show ofNat (n - succ m) = subNatNat n (succ m)
rw [subNatNat, Nat.sub_eq_zero_of_le h]
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_coe' (n : Nat) : -[n+1] = -n - 1 := by
rw [Int.sub_eq_add_neg, Int.neg_add]; rfl
@@ -343,11 +327,11 @@ protected theorem subNatNat_eq_coe {m n : Nat} : subNatNat m n = ↑m - ↑n :=
rw [Int.ofNat_add, Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm,
Int.add_right_neg, Int.add_zero]
· intros i n
simp only [negSucc_eq, ofNat_add, ofNat_one, Int.sub_eq_add_neg, Int.neg_add, Int.add_assoc]
rw [Int.add_neg_eq_sub (a := n), ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
simp only [negSucc_coe, ofNat_add, Int.sub_eq_add_neg, Int.neg_add, Int.add_assoc]
rw [ @Int.sub_eq_add_neg n, ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
apply Nat.le_refl
@[simp] theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
rw [ Int.subNatNat_eq_coe]
refine subNatNat_elim m n (fun m n i => toNat i = m - n) (fun i n => ?_) (fun i n => ?_)
· exact (Nat.add_sub_cancel_left ..).symm
@@ -363,9 +347,6 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
norm_cast
simp [eq_comm]
@[simp] theorem negSucc_eq_neg_ofNat_iff {a b : Nat} : -[a+1] = - (b : Int) a + 1 = b := by
rw [eq_comm, neg_ofNat_eq_negSucc_iff, eq_comm]
@[simp] theorem neg_ofNat_eq_negSucc_add_one_iff {a b : Nat} : - (a : Int) = -[b+1] + 1 a = b := by
cases b with
| zero => simp; norm_cast
@@ -374,33 +355,30 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
norm_cast
simp [eq_comm]
@[simp] theorem negSucc_add_one_eq_neg_ofNat_iff {a b : Nat} : -[a+1] + 1 = - (b : Int) a = b := by
rw [eq_comm, neg_ofNat_eq_negSucc_add_one_iff, eq_comm]
/- ## add/sub injectivity -/
@[simp] protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by
protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by
apply Iff.intro
· intro p
rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p]
· exact congrArg (· + k)
@[simp] protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) i = j := by
protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) i = j := by
simp [Int.add_comm k, Int.add_left_inj]
@[simp] protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) i = j := by
protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) i = j := by
simp [Int.sub_eq_add_neg, Int.neg_inj, Int.add_right_inj]
@[simp] protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) i = j := by
protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) i = j := by
simp [Int.sub_eq_add_neg, Int.add_left_inj]
/- ## Ring properties -/
theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -(m * succ n) := rfl
@[simp] theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -(m * succ n) := rfl
theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -(succ m * n) := rfl
@[simp] theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -(succ m * n) := rfl
theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
@[simp] theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
protected theorem mul_comm (a b : Int) : a * b = b * a := by
cases a <;> cases b <;> simp [Nat.mul_comm]
@@ -418,10 +396,11 @@ theorem negSucc_mul_negOfNat (m n : Nat) : -[m+1] * negOfNat n = ofNat (succ m *
theorem negOfNat_mul_negSucc (m n : Nat) : negOfNat n * -[m+1] = ofNat (n * succ m) := by
rw [Int.mul_comm, negSucc_mul_negOfNat, Nat.mul_comm]
protected theorem mul_assoc (a b c : Int) : a * b * c = a * (b * c) := by
cases a <;> cases b <;> cases c <;>
simp [Nat.mul_assoc, ofNat_mul_negOfNat, negOfNat_mul_ofNat, negSucc_mul_negOfNat, negOfNat_mul_negSucc]
attribute [local simp] ofNat_mul_negOfNat negOfNat_mul_ofNat
negSucc_mul_negOfNat negOfNat_mul_negSucc
protected theorem mul_assoc (a b c : Int) : a * b * c = a * (b * c) := by
cases a <;> cases b <;> cases c <;> simp [Nat.mul_assoc]
instance : Std.Associative (α := Int) (· * ·) := Int.mul_assoc
protected theorem mul_left_comm (a b c : Int) : a * (b * c) = b * (a * c) := by
@@ -439,7 +418,7 @@ theorem negOfNat_eq_subNatNat_zero (n) : negOfNat n = subNatNat 0 n := by cases
theorem ofNat_mul_subNatNat (m n k : Nat) :
m * subNatNat n k = subNatNat (m * n) (m * k) := by
cases m with
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul, subNatNat_self]
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul]
| succ m => cases n.lt_or_ge k with
| inl h =>
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m)
@@ -467,7 +446,8 @@ theorem negSucc_mul_subNatNat (m n k : Nat) :
Nat.mul_sub_left_distrib, succ_pred_eq_of_pos (Nat.sub_pos_of_lt h₁)]; rfl
| inr h' => rw [Nat.le_antisymm h h', subNatNat_self, subNatNat_self, Int.mul_zero]
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat in
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat
protected theorem mul_add : a b c : Int, a * (b + c) = a * b + a * c
| (m:Nat), (n:Nat), (k:Nat) => by simp [Nat.left_distrib]
| (m:Nat), (n:Nat), -[k+1] => by
@@ -490,23 +470,21 @@ 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]
-- 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) :=
@[simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
(Int.neg_mul_eq_neg_mul a b).symm
-- 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) :=
@[simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
(Int.neg_mul_eq_mul_neg a b).symm
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp [Int.neg_mul, Int.mul_neg]
protected theorem neg_mul_neg (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 neg_mul_comm (a b : Int) : -a * b = a * -b := by simp
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, Int.mul_neg]
simp [Int.sub_eq_add_neg, Int.mul_add]
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, Int.neg_mul]
simp [Int.sub_eq_add_neg, Int.add_mul]
@[simp] protected theorem one_mul : a : Int, 1 * a = a
| ofNat n => show ofNat (1 * n) = ofNat n by rw [Nat.one_mul]
@@ -517,9 +495,7 @@ instance : Std.LawfulIdentity (α := Int) (· * ·) 1 where
left_id := Int.one_mul
right_id := Int.mul_one
@[simp] protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
@[simp] protected theorem neg_one_mul (a : Int) : -1 * a = -a := by rw [Int.neg_mul, Int.one_mul]
protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
protected theorem neg_eq_neg_one_mul : a : Int, -a = -1 * a
| 0 => rfl
@@ -568,18 +544,16 @@ The following lemmas are later subsumed by e.g. `Nat.cast_add` and `Nat.cast_mul
but it is convenient to have these earlier, for users who only need `Nat` and `Int`.
-/
protected theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
protected theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
@[simp] protected theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
@[simp] theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
-- Note this only works because of local simp attributes in this file,
-- so it still makes sense to tag the lemmas with `@[simp]`.
simp
protected theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : Int) = (n : Int) + 1 := rfl
@[simp] protected theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
@[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
simp
end Int

View File

@@ -15,35 +15,6 @@ import Init.Omega
namespace Int
/-! ### miscellaneous lemmas -/
@[simp] theorem natCast_le_zero : {n : Nat} (n : Int) 0 n = 0 := by omega
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c a = c + b := by omega
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c a = b + c := by omega
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i 0 0 i := by omega
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ((no_index (OfNat.ofNat n)) : Int) :=
ofNat_nonneg _
@[simp] theorem neg_natCast_le_natCast (n m : Nat) : -(n : Int) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_natCast_le_ofNat (n m : Nat) : -(n : Int) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_ofNat_le_ofNat (n m : Nat) : -(no_index (OfNat.ofNat n)) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_ofNat_le_natCast (n m : Nat) : -(no_index (OfNat.ofNat n)) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
theorem neg_lt_self_iff {n : Int} : -n < n 0 < n := by
omega
/-! ### toNat -/
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by
symm
simp only [Int.toNat]
@@ -68,68 +39,6 @@ theorem neg_lt_self_iff {n : Int} : -n < n ↔ 0 < n := by
simp [toNat]
split <;> simp_all <;> omega
@[simp] theorem toNat_eq_zero : {n : Int}, n.toNat = 0 n 0 := by omega
@[simp] theorem toNat_le {m : Int} {n : Nat} : m.toNat n m n := by omega
@[simp] theorem toNat_lt' {m : Int} {n : Nat} (hn : 0 < n) : m.toNat < n m < n := by omega
theorem pos_iff_toNat_pos {n : Int} : 0 < n 0 < n.toNat := by
omega
theorem ofNat_toNat_eq_self {a : Int} : a.toNat = a 0 a := by omega
theorem eq_ofNat_toNat {a : Int} : a = a.toNat 0 a := by omega
theorem toNat_le_toNat {n m : Int} (h : n m) : n.toNat m.toNat := by omega
theorem toNat_lt_toNat {n m : Int} (hn : 0 < m) : n.toNat < m.toNat n < m := by omega
/-! ### natAbs -/
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d n) (h₁ : n.natAbs < d.natAbs) :
n = 0 := by
obtain a, rfl := h
rw [natAbs_mul] at h₁
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
/-! ### min and max -/
@[simp] protected theorem min_assoc : (a b c : Int), min (min a b) c = min a (min b c) := by omega
instance : Std.Associative (α := Nat) min := Nat.min_assoc
@[simp] protected theorem min_self_assoc {m n : Int} : min m (min m n) = min m n := by
rw [ Int.min_assoc, Int.min_self]
@[simp] protected theorem min_self_assoc' {m n : Int} : min n (min m n) = min n m := by
rw [Int.min_comm m n, Int.min_assoc, Int.min_self]
@[simp] protected theorem max_assoc (a b c : Int) : max (max a b) c = max a (max b c) := by omega
instance : Std.Associative (α := Nat) max := Nat.max_assoc
@[simp] protected theorem max_self_assoc {m n : Int} : max m (max m n) = max m n := by
rw [ Int.max_assoc, Int.max_self]
@[simp] protected theorem max_self_assoc' {m n : Int} : max n (max m n) = max n m := by
rw [Int.max_comm m n, Int.max_assoc, Int.max_self]
protected theorem max_min_distrib_left (a b c : Int) : max a (min b c) = min (max a b) (max a c) := by omega
protected theorem min_max_distrib_left (a b c : Int) : min a (max b c) = max (min a b) (min a c) := by omega
protected theorem max_min_distrib_right (a b c : Int) :
max (min a b) c = min (max a c) (max b c) := by omega
protected theorem min_max_distrib_right (a b c : Int) :
min (max a b) c = max (min a c) (min b c) := by omega
protected theorem sub_min_sub_right (a b c : Int) : min (a - c) (b - c) = min a b - c := by omega
protected theorem sub_max_sub_right (a b c : Int) : max (a - c) (b - c) = max a b - c := by omega
protected theorem sub_min_sub_left (a b c : Int) : min (a - b) (a - c) = a - max b c := by omega
protected theorem sub_max_sub_left (a b c : Int) : max (a - b) (a - c) = a - min b c := by omega
/-! ### bmod -/
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m x) (h1 : x < m) :
(x.bmod m) < 0 (-(m / 2) x x < 0) ((m + 1) / 2 x) := by
simp only [Int.bmod_def]
@@ -137,18 +46,4 @@ theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) n) (hn : n < (m + 1) / 2) :
n.bmod m = n := by
rw [ Int.sub_eq_zero]
have := le_bmod (x := n) (m := m) (by omega)
have := bmod_lt (x := n) (m := m) (by omega)
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
omega
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n m) :
(a.bmod m).bmod n = a.bmod n := by
rw [ Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
obtain k, rfl := hnm
simp [Int.mul_assoc]
end Int

View File

@@ -9,7 +9,6 @@ import Init.Data.Prod
import Init.Data.Int.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.Cooper
import Init.Data.Int.Gcd
import Init.Data.RArray
import Init.Data.AC
@@ -187,13 +186,14 @@ theorem cmod_gt_of_pos (a : Int) {b : Int} (h : 0 < b) : cmod a b > -b :=
theorem cmod_nonpos (a : Int) {b : Int} (h : b 0) : cmod a b 0 := by
have := Int.neg_le_neg (Int.emod_nonneg (-a) h)
simpa [cmod] using this
simp at this
assumption
theorem cmod_eq_zero_iff_emod_eq_zero (a b : Int) : cmod a b = 0 a%b = 0 := by
unfold cmod
have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a
simp at this
simp [Int.neg_emod_eq_sub_emod, this, Eq.comm]
simp [Int.neg_emod, this, Eq.comm]
private abbrev div_mul_cancel_of_mod_zero :=
@Int.ediv_mul_cancel_of_emod_eq_zero
@@ -250,24 +250,14 @@ def Poly.divCoeffs (k : Int) : Poly → Bool
/--
`p.mul k` multiplies all coefficients and constant of the polynomial `p` by `k`.
-/
def Poly.mul' (p : Poly) (k : Int) : Poly :=
def Poly.mul (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k*k')
| .add k' v p => .add (k*k') v (mul' p k)
def Poly.mul (p : Poly) (k : Int) : Poly :=
if k == 0 then
.num 0
else
p.mul' k
| .add k' v p => .add (k*k') v (mul p k)
@[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
simp [mul]
split
next => simp [*, denote]
next =>
induction p <;> simp [mul', denote, *]
rw [Int.mul_assoc, Int.mul_add]
induction p <;> simp [mul, denote, *]
rw [Int.mul_assoc, Int.mul_add]
attribute [local simp] Int.add_comm Int.add_assoc Int.add_left_comm Int.add_mul Int.mul_add
attribute [local simp] Poly.insert Poly.denote Poly.norm Poly.addConst
@@ -320,6 +310,7 @@ theorem Poly.denote_div_eq_of_divAll (ctx : Context) (p : Poly) (k : Int) : p.di
replace h₁ := div_mul_cancel_of_mod_zero h₁
have ih := ih h₂
simp [ih]
apply congrArg (denote ctx p + ·)
rw [Int.mul_right_comm, h₁]
attribute [local simp] Poly.divCoeffs Poly.getConst
@@ -361,7 +352,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, Int.neg_mul, Int.mul_neg]
| case10 k a ih => simp [toPoly'.go, ih]
theorem Expr.denote_norm (ctx : Context) (e : Expr) : e.norm.denote ctx = e.denote ctx := by
simp [norm, toPoly', Expr.denote_toPoly'_go]
@@ -419,7 +410,7 @@ theorem norm_eq_var_const (ctx : Context) (lhs rhs : Expr) (x : Var) (k : Int) (
simp [norm_eq_var_const_cert] at h
replace h := congrArg (Poly.denote ctx) h
simp at h
rw [Int.sub_eq_zero, h, Int.add_comm, Int.add_neg_eq_sub, Int.sub_eq_zero]
rw [Int.sub_eq_zero, h, Int.add_comm, Int.sub_eq_add_neg, Int.sub_eq_zero]
private theorem mul_eq_zero_iff (a k : Int) (h₁ : k > 0) : k * a = 0 a = 0 := by
conv => lhs; rw [ Int.mul_zero k]
@@ -540,9 +531,8 @@ def Poly.isValidLe (p : Poly) : Bool :=
| .num k => k 0
| _ => false
attribute [-simp] Int.not_le in
theorem le_eq_false (ctx : Context) (lhs rhs : Expr) : (lhs.sub rhs).norm.isUnsatLe (lhs.denote ctx rhs.denote ctx) = False := by
simp only [Poly.isUnsatLe] <;> split <;> simp
simp [Poly.isUnsatLe] <;> split <;> simp
next p k h =>
intro h'
replace h := congrArg (Poly.denote ctx) h
@@ -798,10 +788,10 @@ theorem dvd_solve_elim (ctx : Context) (d₁ : Int) (p₁ : Poly) (d₂ : Int) (
simp [dvd_solve_elim_cert]
split <;> simp
next a₁ x₁ p₁ a₂ x₂ p₂ =>
intro _ hd _; subst x₁ p; simp [Int.neg_mul]
intro _ hd _; subst x₁ p; simp
intro h₁ h₂
rw [Int.add_comm] at h₁ h₂
rw [Int.add_neg_eq_sub]
rw [ Int.sub_eq_add_neg]
exact dvd_solve_elim' hd h₁ h₂
theorem dvd_norm (ctx : Context) (d : Int) (p₁ p₂ : Poly) : p₁.norm == p₂ d p₁.denote' ctx d p₂.denote' ctx := by
@@ -830,7 +820,7 @@ def le_neg_cert (p₁ p₂ : Poly) : Bool :=
theorem le_neg (ctx : Context) (p₁ p₂ : Poly) : le_neg_cert p₁ p₂ ¬ p₁.denote' ctx 0 p₂.denote' ctx 0 := by
simp [le_neg_cert]
intro; subst p₂; simp; intro h
replace h : _ + 1 -0 := Int.neg_lt_neg h
replace h : _ + 1 -0 := Int.neg_lt_neg <| Int.lt_of_not_ge h
simp at h
exact h
@@ -854,28 +844,11 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
· rw [ Int.zero_mul (Poly.denote ctx p₂)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
· rw [ Int.zero_mul (Poly.denote ctx p₁)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
def le_combine_coeff_cert (p₁ p₂ p₃ : Poly) (k : Int) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
let p := p₁.mul a₂ |>.combine (p₂.mul a₁)
k > 0 && (p.divCoeffs k && p₃ == p.div k)
theorem le_combine_coeff (ctx : Context) (p₁ p₂ p₃ : Poly) (k : Int)
: le_combine_coeff_cert p₁ p₂ p₃ k p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp only [le_combine_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
generalize h : (p₁.mul a₂ |>.combine (p₂.mul a₁)) = p
intro h₁ h₂ h₃ h₄ h₅
have := le_combine ctx p₁ p₂ p
simp only [le_combine_cert, beq_iff_eq] at this
have aux₁ := this h.symm h₄ h₅
have := le_coeff ctx p p₃ k
simp only [le_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp] at this
exact this h₁ h₂ h₃ aux₁
theorem le_unsat (ctx : Context) (p : Poly) : p.isUnsatLe p.denote' ctx 0 False := by
simp [Poly.isUnsatLe]; split <;> simp
intro h₁ h₂
have := Int.lt_of_le_of_lt h₂ h₁
simp at this
theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx = 0 p₂.denote' ctx = 0 := by
simp at h
@@ -908,7 +881,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.neg_mul, Int.add_neg_cancel_right]
simp; rw [ Int.add_assoc, 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 +950,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, Int.neg_mul]
simp [this]
def eq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
@@ -1017,8 +990,10 @@ 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, Int.neg_mul]
simp [*]
replace h₂ := Int.mul_le_mul_of_nonpos_left h₂ h; simp at h₂; clear h
rw [ Int.neg_zero]
apply Int.neg_le_neg
rw [Int.mul_comm]
assumption
@@ -1029,772 +1004,7 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: eq_of_core_cert p₁ p₂ p₃ p₁.denote' ctx = p₂.denote' ctx p₃.denote' ctx = 0 := by
simp [eq_of_core_cert]
intro; subst p₃; simp
intro h; rw [h, Int.add_neg_eq_sub, Int.sub_self]
def Poly.isUnsatDiseq (p : Poly) : Bool :=
match p with
| .num 0 => true
| _ => false
theorem diseq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx 0 p₂.denote' ctx 0 := by
simp at h
replace h := congrArg (Poly.denote ctx) h
simp at h
simp [*]
theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p' k p.denote' ctx 0 p'.denote' ctx 0 := by
simp [eq_coeff_cert]
intro _ _; simp [mul_eq_zero_iff, *]
theorem diseq_neg (ctx : Context) (p p' : Poly) : p' == p.mul (-1) p.denote' ctx 0 p'.denote' ctx 0 := by
simp; intro _ _; simp [mul_eq_zero_iff, *]
theorem diseq_unsat (ctx : Context) (p : Poly) : p.isUnsatDiseq p.denote' ctx 0 False := by
simp [Poly.isUnsatDiseq] <;> split <;> simp
def diseq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
let b := p₂.coeff x
a != 0 && p₃ == (p₁.mul b |>.combine (p₂.mul (-a)))
theorem eq_diseq_subst (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: diseq_eq_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [diseq_eq_subst_cert]
intros _ _; subst p₃
intro h₁ h₂
simp [*]
theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: eq_of_core_cert p₁ p₂ p₃ p₁.denote' ctx p₂.denote' ctx p₃.denote' ctx 0 := by
simp [eq_of_core_cert]
intro; subst p₃; simp
intro h; rw [ Int.sub_eq_zero] at h
rw [Int.add_neg_eq_sub]; assumption
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
p₂ == p₁.mul (-1)
theorem eq_of_le_ge (ctx : Context) (p₁ : Poly) (p₂ : Poly)
: eq_of_le_ge_cert p₁ p₂ p₁.denote' ctx 0 p₂.denote' ctx 0 p₁.denote' ctx = 0 := by
simp [eq_of_le_ge_cert]
intro; subst p₂; simp [-Int.neg_nonpos_iff]
intro h₁ h₂
simp [Int.eq_iff_le_and_ge, *]
def le_of_le_diseq_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
-- Remark: we can generate two different certificates in the future, and avoid the `||` in the certificate.
(p₂ == p₁ || p₂ == p₁.mul (-1)) &&
p₃ == p₁.addConst 1
theorem le_of_le_diseq (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: le_of_le_diseq_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [le_of_le_diseq_cert]
have (a : Int) : a 0 ¬ a = 0 1 + a 0 := by
intro h₁ h₂; cases (Int.lt_or_gt_of_ne h₂)
next => apply Int.le_of_lt_add_one; rw [Int.add_comm, Int.add_lt_add_iff_right]; assumption
next h => have := Int.lt_of_le_of_lt h₁ h; simp at this
intro h; cases h <;> intro <;> subst p₂ p₃ <;> simp <;> apply this
def diseq_split_cert (p₁ p₂ p₃ : Poly) : Bool :=
p₂ == p₁.addConst 1 &&
p₃ == (p₁.mul (-1)).addConst 1
theorem diseq_split (ctx : Context) (p₁ p₂ p₃ : Poly)
: diseq_split_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [diseq_split_cert]
intro _ _; subst p₂ p₃; simp
generalize p₁.denote ctx = p
intro h; cases Int.lt_or_gt_of_ne h
next h => have := Int.add_one_le_of_lt h; rw [Int.add_comm]; simp [*]
next h => have := Int.add_one_le_of_lt (Int.neg_lt_neg h); simp at this; simp [*]
theorem diseq_split_resolve (ctx : Context) (p₁ p₂ p₃ : Poly)
: diseq_split_cert p₁ p₂ p₃ p₁.denote' ctx 0 ¬p₂.denote' ctx 0 p₃.denote' ctx 0 := by
intro h₁ h₂ h₃
exact (diseq_split ctx p₁ p₂ p₃ h₁ h₂).resolve_left h₃
def OrOver (n : Nat) (p : Nat Prop) : Prop :=
match n with
| 0 => False
| n+1 => p n OrOver n p
theorem orOver_one {p} : OrOver 1 p p 0 := by simp [OrOver]
theorem orOver_resolve {n p} : OrOver (n+1) p ¬ p n OrOver n p := by
intro h₁ h₂
rw [OrOver] at h₁
cases h₁
· contradiction
· assumption
def OrOver_cases_type (n : Nat) (p : Nat Prop) : Prop :=
match n with
| 0 => p 0
| n+1 => ¬ p (n+1) OrOver_cases_type n p
theorem orOver_cases {n p} : OrOver (n+1) p OrOver_cases_type n p := by
induction n <;> simp [OrOver_cases_type]
next => exact orOver_one
next n ih => intro h₁ h₂; exact ih (orOver_resolve h₁ h₂)
private theorem orOver_of_p {i n p} (h₁ : i < n) (h₂ : p i) : OrOver n p := by
induction n
next => simp at h₁
next n ih =>
simp [OrOver]
cases Nat.eq_or_lt_of_le <| Nat.le_of_lt_add_one h₁
next h => subst i; exact Or.inl h₂
next h => exact Or.inr (ih h)
private theorem orOver_of_exists {n p} : ( k, k < n p k) OrOver n p := by
intro k, h₁, h₂
apply orOver_of_p h₁ h₂
private theorem ofNat_toNat {a : Int} : a 0 Int.ofNat a.toNat = a := by cases a <;> simp
private theorem cast_toNat {a : Int} : a 0 a.toNat = a := by cases a <;> simp
private theorem ofNat_lt {a : Int} {n : Nat} : a 0 a < Int.ofNat n a.toNat < n := by cases a <;> simp
private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
private theorem lcm_one (a : Int) : Int.lcm a 1 = a.natAbs := by simp [Int.lcm]
private theorem cooper_dvd_left_core
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
(h₃ : d c * x + s)
: OrOver (Int.lcm a (a * d / Int.gcd (a * d) c)) fun k =>
b * p + (-a) * q + b * k 0
a p + k
a * d c * p + (-a) * s + c * k := by
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
have h₁' : p (-a)*x := by rw [Int.neg_mul, Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
have h₂' : b * x -q := by rw [ Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
have k, h₁, h₂, h₃, h₄, h₅ := Int.cooper_resolution_dvd_left a_pos' b_pos d_pos |>.mp x, h₁', h₂', h₃
rw [Int.neg_mul] at h₂
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
rw [Int.neg_ediv_of_dvd Int.gcd_dvd_left] at h₂
simp only [lcm_neg_right] at h₂
have : c * k + c * p + -(a * s) = c * p + -(a * s) + c * k := by ac_rfl
rw [this] at h₅; clear this
rw [ ofNat_toNat h₁] at h₃ h₄ h₅
rw [Int.add_comm] at h₄
have := ofNat_lt h₁ h₂
apply orOver_of_exists
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
have : b * Int.ofNat k.toNat + b * p + -(a * q) = b * p + -(a * q) + b * Int.ofNat k.toNat := by ac_rfl
rw [this] at h₃
exists k.toNat
def cooper_dvd_left_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
p₃.casesOn (fun _ => false) fun c z _ =>
.and (x == y) <| .and (x == z) <|
.and (a < 0) <| .and (b > 0) <|
.and (d > 0) <| n == Int.lcm a (a * d / Int.gcd (a * d) c)
def Poly.tail (p : Poly) : Poly :=
match p with
| .add _ _ p => p
| _ => p
def cooper_dvd_left_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let s := p₃.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let c := p₃.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
let p₂ := p.mul c |>.combine (s.mul (-a))
(p₁.addConst (b*k)).denote' ctx 0
a (p.addConst k).denote' ctx
a*d (p₂.addConst (c*k)).denote' ctx
private theorem denote'_mul_combine_mul_addConst_eq (ctx : Context) (p q : Poly) (a b c : Int)
: ((p.mul b |>.combine (q.mul a)).addConst c).denote' ctx = b*p.denote ctx + a*q.denote ctx + c := by
simp
private theorem denote'_addConst_eq (ctx : Context) (p : Poly) (a : Int)
: (p.addConst a).denote' ctx = p.denote ctx + a := by
simp
theorem cooper_dvd_left (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
: cooper_dvd_left_cert p₁ p₂ p₃ d n
p₁.denote' ctx 0
p₂.denote' ctx 0
d p₃.denote' ctx
OrOver n (cooper_dvd_left_split ctx p₁ p₂ p₃ d) := by
unfold cooper_dvd_left_split
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_left_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q c z s =>
intro _ _; subst y z
intro ha hb hd
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂ h₃
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq]
exact cooper_dvd_left_core ha hb hd h₁ h₂ h₃
def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_dvd_left_split_ineq_cert, cooper_dvd_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_dvd_left_split_dvd1_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
a == p₁.leadCoeff && p' == p₁.tail.addConst k
theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd1_cert p₁ p' a k a p'.denote' ctx := by
simp [cooper_dvd_left_split_dvd1_cert, cooper_dvd_left_split]
intros; subst a p'; simp; assumption
def cooper_dvd_left_split_dvd2_cert (p₁ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
let p := p₁.tail
let s := p₃.tail
let a := p₁.leadCoeff
let c := p₃.leadCoeff
let p₂ := p.mul c |>.combine (s.mul (-a))
d' == a*d && p' == p₂.addConst (c*k)
theorem cooper_dvd_left_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p' d' p'.denote' ctx := by
simp [cooper_dvd_left_split_dvd2_cert, cooper_dvd_left_split]
intros; subst d' p'; simp; assumption
private theorem cooper_left_core
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
: OrOver a.natAbs fun k =>
b * p + (-a) * q + b * k 0
a p + k := by
have d_pos : (0 : Int) < 1 := by decide
have h₃ : 1 0*x + 0 := Int.one_dvd _
have h := cooper_dvd_left_core a_neg b_pos d_pos h₁ h₂ h₃
simp only [Int.mul_one, gcd_zero, ofNat_natAbs_of_nonpos (Int.le_of_lt a_neg), Int.ediv_neg,
Int.ediv_self (Int.ne_of_lt a_neg), Int.reduceNeg, lcm_neg_right, lcm_one,
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
and_true] at h
assumption
def cooper_left_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
.and (x == y) <| .and (a < 0) <| .and (b > 0) <|
n == a.natAbs
def cooper_left_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
(p₁.addConst (b*k)).denote' ctx 0
a (p.addConst k).denote' ctx
theorem cooper_left (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
: cooper_left_cert p₁ p₂ n
p₁.denote' ctx 0
p₂.denote' ctx 0
OrOver n (cooper_left_split ctx p₁ p₂) := by
unfold cooper_left_split
cases p₁ <;> cases p₂ <;> simp [cooper_left_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q =>
intro; subst y
intro ha hb
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂
have := cooper_left_core ha hb h₁ h₂
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq]
assumption
def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
theorem cooper_left_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
: cooper_left_split ctx p₁ p₂ k cooper_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_left_split_ineq_cert, cooper_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_left_split_dvd_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
a == p₁.leadCoeff && p' == p₁.tail.addConst k
theorem cooper_left_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
: cooper_left_split ctx p₁ p₂ k cooper_left_split_dvd_cert p₁ p' a k a p'.denote' ctx := by
simp [cooper_left_split_dvd_cert, cooper_left_split]
intros; subst a p'; simp; assumption
private theorem cooper_dvd_right_core
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
(h₃ : d c * x + s)
: OrOver (Int.lcm b (b * d / Int.gcd (b * d) c)) fun k =>
b * p + (-a) * q + (-a) * k 0
b q + k
b * d (-c) * q + b * s + (-c) * k := by
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
have h₁' : p (-a)*x := by rw [Int.neg_mul, Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
have h₂' : b * x -q := by rw [ Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
have k, h₁, h₂, h₃, h₄, h₅ := Int.cooper_resolution_dvd_right a_pos' b_pos d_pos |>.mp x, h₁', h₂', h₃
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
apply orOver_of_exists
have hlt := ofNat_lt h₁ h₂
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
have : -(a * k) + b * p + -(a * q) = b * p + -(a * q) + -(a * k) := by ac_rfl
rw [this] at h₃; clear this
rw [Int.sub_neg, Int.add_comm] at h₄
have : -(c * k) + -(c * q) + b * s = -(c * q) + b * s + -(c * k) := by ac_rfl
rw [this] at h₅; clear this
exists k.toNat
simp only [hlt, true_and, and_true, cast_toNat h₁, h₃, h₄, h₅]
def cooper_dvd_right_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
p₃.casesOn (fun _ => false) fun c z _ =>
.and (x == y) <| .and (x == z) <|
.and (a < 0) <| .and (b > 0) <|
.and (d > 0) <| n == Int.lcm b (b * d / Int.gcd (b * d) c)
def cooper_dvd_right_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let s := p₃.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let c := p₃.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
let p₂ := q.mul (-c) |>.combine (s.mul b)
(p₁.addConst ((-a)*k)).denote' ctx 0
b (q.addConst k).denote' ctx
b*d (p₂.addConst ((-c)*k)).denote' ctx
theorem cooper_dvd_right (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
: cooper_dvd_right_cert p₁ p₂ p₃ d n
p₁.denote' ctx 0
p₂.denote' ctx 0
d p₃.denote' ctx
OrOver n (cooper_dvd_right_split ctx p₁ p₂ p₃ d) := by
unfold cooper_dvd_right_split
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_right_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q c z s =>
intro _ _; subst y z
intro ha hb hd
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂ h₃
have := cooper_dvd_right_core ha hb hd h₁ h₂ h₃
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq, Int.neg_mul]
exact cooper_dvd_right_core ha hb hd h₁ h₂ h₃
def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let b := p₂.leadCoeff
let p₂ := p.mul b |>.combine (q.mul (-a))
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
theorem cooper_dvd_right_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_dvd_right_split_ineq_cert, cooper_dvd_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_dvd_right_split_dvd1_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
b == p₂.leadCoeff && p' == p₂.tail.addConst k
theorem cooper_dvd_right_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd1_cert p₂ p' b k b p'.denote' ctx := by
simp [cooper_dvd_right_split_dvd1_cert, cooper_dvd_right_split]
intros; subst b p'; simp; assumption
def cooper_dvd_right_split_dvd2_cert (p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
let q := p₂.tail
let s := p₃.tail
let b := p₂.leadCoeff
let c := p₃.leadCoeff
let p₂ := q.mul (-c) |>.combine (s.mul b)
d' == b*d && p' == p₂.addConst ((-c)*k)
theorem cooper_dvd_right_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd2_cert p₂ p₃ d k d' p' d' p'.denote' ctx := by
simp [cooper_dvd_right_split_dvd2_cert, cooper_dvd_right_split]
intros; subst d' p'; simp; assumption
private theorem cooper_right_core
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
: OrOver b.natAbs fun k =>
b * p + (-a) * q + (-a) * k 0
b q + k := by
have d_pos : (0 : Int) < 1 := by decide
have h₃ : 1 0*x + 0 := Int.one_dvd _
have h := cooper_dvd_right_core a_neg b_pos d_pos h₁ h₂ h₃
simp only [Int.mul_one, gcd_zero, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.ediv_neg,
Int.ediv_self (Int.ne_of_gt b_pos), Int.reduceNeg, lcm_neg_right, lcm_one,
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
and_true, Int.neg_zero] at h
assumption
def cooper_right_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
.and (x == y) <| .and (a < 0) <| .and (b > 0) <| n == b.natAbs
def cooper_right_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
(p₁.addConst ((-a)*k)).denote' ctx 0
b (q.addConst k).denote' ctx
theorem cooper_right (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
: cooper_right_cert p₁ p₂ n
p₁.denote' ctx 0
p₂.denote' ctx 0
OrOver n (cooper_right_split ctx p₁ p₂) := by
unfold cooper_right_split
cases p₁ <;> cases p₂ <;> simp [cooper_right_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q =>
intro; subst y
intro ha hb
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂
have := cooper_right_core ha hb h₁ h₂
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq, Int.neg_mul]
assumption
def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let b := p₂.leadCoeff
let p₂ := p.mul b |>.combine (q.mul (-a))
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
theorem cooper_right_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
: cooper_right_split ctx p₁ p₂ k cooper_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_right_split_ineq_cert, cooper_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_right_split_dvd_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
b == p₂.leadCoeff && p' == p₂.tail.addConst k
theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
: cooper_right_split ctx p₁ p₂ k cooper_right_split_dvd_cert p₂ p' b k b p'.denote' ctx := by
simp [cooper_right_split_dvd_cert, cooper_right_split]
intros; subst b p'; simp; assumption
private theorem one_emod_eq_one {a : Int} (h : a > 1) : 1 % a = 1 := by
have aux₁ := Int.ediv_add_emod 1 a
have : 1 / a = 0 := Int.ediv_eq_zero_of_lt (by decide) h
simp [this] at aux₁
assumption
private theorem ex_of_dvd {α β a b d x : Int}
(h₀ : d > 1)
(h₁ : d a*x + b)
(h₂ : α * a + β * d = 1)
: k, x = k * d + (- α * b) % d := by
have k, h₁ := h₁
have aux₁ : (α * a) % d = 1 := by
replace h₂ := congrArg (· % d) h₂; simp at h₂
rw [one_emod_eq_one h₀] at h₂
assumption
have : ((α * a) * x) % d = (- α * b) % d := by
replace h₁ := congrArg (α * ·) h₁; simp only at h₁
rw [Int.mul_add] at h₁
replace h₁ := congrArg (· - α * b) h₁; simp only [Int.add_sub_cancel] at h₁
rw [ Int.mul_assoc, Int.mul_left_comm, Int.sub_eq_add_neg] at h₁
replace h₁ := congrArg (· % d) h₁; simp only at h₁
rw [Int.add_emod, Int.mul_emod_right, Int.zero_add, Int.emod_emod, Int.neg_mul] at h₁
assumption
have : x % d = (- α * b) % d := by
rw [Int.mul_emod, aux₁, Int.one_mul, Int.emod_emod] at this
assumption
have : x = (x / d)*d + (- α * b) % d := by
conv => lhs; rw [ Int.ediv_add_emod x d]
rw [Int.mul_comm, this]
exists x / d
private theorem cdiv_le {a d k : Int} : d > 0 a k * d cdiv a d k := by
intro h₁ h₂
simp [cdiv]
replace h₂ := Int.neg_le_neg h₂
rw [ Int.neg_mul] at h₂
replace h₂ := Int.le_ediv_of_mul_le h₁ h₂
replace h₂ := Int.neg_le_neg h₂
simp at h₂
assumption
private theorem cooper_unsat'_helper {a b d c k x : Int}
(d_pos : d > 0)
(h₁ : x = k * d + c)
(h₂ : a x)
(h₃ : x b)
: ¬ b < (cdiv (a - c) d) * d + c := by
intro h₄
have aux₁ : cdiv (a - c) d k := by
rw [h₁] at h₂
replace h₂ := Int.sub_right_le_of_le_add h₂
exact cdiv_le d_pos h₂
have aux₂ : cdiv (a - c) d * d k * d := Int.mul_le_mul_of_nonneg_right aux₁ (Int.le_of_lt d_pos)
have aux₃ : cdiv (a - c) d * d + c k * d + c := Int.add_le_add_right aux₂ _
have aux₄ : cdiv (a - c) d * d + c x := by rw [h₁] at aux₃; assumption
have aux₅ : cdiv (a - c) d * d + c b := Int.le_trans aux₄ h₃
have := Int.lt_of_le_of_lt aux₅ h₄
exact Int.lt_irrefl _ this
private theorem cooper_unsat' {a c b d e α β x : Int}
(h₁ : d > 1)
(h₂ : d c*x + e)
(h₃ : α * c + β * d = 1)
(h₄ : (-1)*x + a 0)
(h₅ : x + b 0)
(h₆ : -b < cdiv (a - -α * e % d) d * d + -α * e % d)
: False := by
have k, h := ex_of_dvd h₁ h₂ h₃
have d_pos : d > 0 := Int.lt_trans (by decide) h₁
replace h₄ := Int.le_neg_add_of_add_le h₄; simp at h₄
replace h₅ := Int.neg_le_neg (Int.le_neg_add_of_add_le h₅); simp at h₅
have := cooper_unsat'_helper d_pos h h₄ h₅
exact this h₆
abbrev Poly.casesOnAdd (p : Poly) (k : Int Var Poly Bool) : Bool :=
p.casesOn (fun _ => false) k
abbrev Poly.casesOnNum (p : Poly) (k : Int Bool) : Bool :=
p.casesOn k (fun _ _ _ => false)
def cooper_unsat_cert (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int) : Bool :=
p₁.casesOnAdd fun k₁ x p₁ =>
p₂.casesOnAdd fun k₂ y p₂ =>
p₃.casesOnAdd fun c z p₃ =>
p₁.casesOnNum fun a =>
p₂.casesOnNum fun b =>
p₃.casesOnNum fun e =>
(k₁ == -1) |>.and (k₂ == 1) |>.and
(x == y) |>.and (x == z) |>.and
(d > 1) |>.and (α * c + β * d == 1) |>.and
(-b < cdiv (a - -α * e % d) d * d + -α * e % d)
theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int)
: cooper_unsat_cert p₁ p₂ p₃ d α β
p₁.denote' ctx 0 p₂.denote' ctx 0 d p₃.denote' ctx False := by
unfold cooper_unsat_cert <;> cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnAdd,
Bool.false_eq_true, Poly.denote'_add, mul_def, add_def, false_implies]
next k₁ x p₁ k₂ y p₂ c z p₃ =>
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnNum, Int.reduceNeg,
Bool.and_eq_true, beq_iff_eq, decide_eq_true_eq, and_imp, Bool.false_eq_true,
mul_def, add_def, false_implies, Poly.denote]
next a b e =>
intro _ _ _ _; subst k₁ k₂ y z
intro h₁ h₃ h₆; generalize Var.denote ctx x = x'
intro h₄ h₅ h₂
rw [Int.one_mul] at h₅
exact cooper_unsat' h₁ h₂ h₃ h₄ h₅ h₆
theorem ediv_emod (x y : Int) : -1 * x + y * (x / y) + x % y = 0 := by
rw [Int.add_assoc, Int.ediv_add_emod x y, Int.add_comm]
simp
rw [Int.add_neg_eq_sub, Int.sub_self]
theorem emod_nonneg (x y : Int) : y != 0 -1 * (x % y) 0 := by
simp; intro h
have := Int.neg_le_neg (Int.emod_nonneg x h)
simp at this
assumption
def emod_le_cert (y n : Int) : Bool :=
y != 0 && n == 1 - y.natAbs
theorem emod_le (x y : Int) (n : Int) : emod_le_cert y n x % y + n 0 := by
simp [emod_le_cert]
intro h₁
cases Int.lt_or_gt_of_ne h₁
next h =>
rw [Int.ofNat_natAbs_of_nonpos (Int.le_of_lt h)]
simp only [Int.sub_neg]
intro; subst n
rw [Int.add_assoc, Int.add_left_comm]
apply Int.add_le_of_le_sub_left
rw [Int.zero_sub, Int.add_comm]
have : 0 < -y := by
have := Int.neg_lt_neg h
rw [Int.neg_zero] at this
assumption
have := Int.emod_lt_of_pos x this
rw [Int.emod_neg] at this
exact this
next h =>
rw [Int.natAbs_of_nonneg (Int.le_of_lt h)]
intro; subst n
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm]
apply Int.add_le_of_le_sub_left
simp only [Int.add_comm, Int.sub_neg, Int.add_zero]
exact Int.emod_lt_of_pos x h
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₁
replace h₁ : p = d*k - b₁ := by
replace h := congrArg (· - b₁) h
simp only [Int.add_sub_cancel] at h
assumption
replace h₂ : d*k - b₁ + b₂ 0 := by
rw [h₁] at h₂; assumption
have : d*k b₁ - b₂ := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Lean.Omega.Int.add_le_zero_iff_le_neg,
Int.neg_add, Int.neg_neg, Int.add_neg_eq_sub] at h₂
assumption
replace this : k (b₁ - b₂)/d := by
rw [Int.mul_comm] at this; exact Int.le_ediv_of_mul_le hd this
replace this := Int.mul_le_mul_of_nonneg_left this (Int.le_of_lt hd)
rw [h] at this
replace this := Int.sub_nonpos_of_le this
rw [Int.add_sub_assoc] at this
exact this
private theorem eq_neg_addConst_add (ctx : Context) (p : Poly)
: p.denote' ctx = (p.addConst (-p.getConst)).denote' ctx + p.getConst := by
simp only [Poly.denote'_eq_denote, Poly.denote_addConst, Int.add_comm, Int.add_left_comm]
rw [Int.add_right_neg]
simp
def dvd_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
let b₁ := p₁.getConst
let b₂ := p₂.getConst
let p := p₁.addConst (-b₁)
d > 0 && (p₂ == p.addConst b₂ && p₃ == p.addConst (b₁ - d*((b₁ - b₂)/d)))
theorem dvd_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
: dvd_le_tight_cert d p₁ p₂ p₃ d p₁.denote' ctx p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp only [dvd_le_tight_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
generalize p₂.getConst = b₂
intro hd _ _; subst p₂ p₃
have := eq_neg_addConst_add ctx p₁
revert this
generalize p₁.getConst = b₁
generalize p₁.addConst (-b₁) = p
intro h₁; rw [h₁]; clear h₁
simp only [denote'_addConst_eq]
simp only [Poly.denote'_eq_denote]
exact dvd_le_tight' hd
def dvd_neg_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
let b₁ := p₁.getConst
let b₂ := p₂.getConst
let p := p₁.addConst (-b₁)
let b₁ := -b₁
let p := p.mul (-1)
d > 0 && (p₂ == p.addConst b₂ && p₃ == p.addConst (b₁ - d*((b₁ - b₂)/d)))
theorem Poly.mul_minus_one_getConst_eq (p : Poly) : (p.mul (-1)).getConst = -p.getConst := by
simp [Poly.mul, Poly.getConst]
induction p <;> simp [Poly.mul', Poly.getConst, *]
theorem dvd_neg_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
: dvd_neg_le_tight_cert d p₁ p₂ p₃ d p₁.denote' ctx p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp only [dvd_neg_le_tight_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
generalize p₂.getConst = b₂
intro hd _ _; subst p₂ p₃
simp only [Poly.denote'_eq_denote, Int.reduceNeg, Poly.denote_addConst, Poly.denote_mul,
Int.mul_add, Int.neg_mul, Int.one_mul, Int.mul_neg, Int.neg_neg, Int.add_comm, Int.add_assoc]
intro h₁ h₂
replace h₁ := Int.dvd_neg.mpr h₁
have := eq_neg_addConst_add ctx (p₁.mul (-1))
simp [Poly.mul_minus_one_getConst_eq] at this
rw [ Int.add_assoc] at this
rw [this] at h₁; clear this
rw [ Int.add_assoc]
revert h₁ h₂
generalize -Poly.denote ctx p₁ + p₁.getConst = p
generalize -p₁.getConst = b₁
intro h₁ h₂; rw [Int.add_comm] at h₁
exact dvd_le_tight' hd h₂ h₁
theorem le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: norm_eq_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote' ctx 0 := by
intro h₁ h₂; rwa [norm_le ctx lhs rhs p h₁] at h₂
def not_le_norm_expr_cert (lhs rhs : Expr) (p : Poly) : Bool :=
p == (((lhs.sub rhs).norm).mul (-1)).addConst 1
theorem not_le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: not_le_norm_expr_cert lhs rhs p ¬ lhs.denote ctx rhs.denote ctx p.denote' ctx 0 := by
simp [not_le_norm_expr_cert]
intro; subst p; simp
intro h
replace h := Int.sub_nonpos_of_le h
rw [Int.add_comm, Int.add_sub_assoc] at h
rw [Int.neg_sub]; assumption
theorem dvd_norm_expr (ctx : Context) (d : Int) (e : Expr) (p : Poly)
: p == e.norm d e.denote ctx d p.denote' ctx := by
simp; intro; subst p; simp
theorem eq_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: norm_eq_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote' ctx = 0 := by
intro h₁ h₂; rwa [norm_eq ctx lhs rhs p h₁] at h₂
theorem not_eq_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: norm_eq_cert lhs rhs p ¬ lhs.denote ctx = rhs.denote ctx ¬ p.denote' ctx = 0 := by
simp [norm_eq_cert]
intro; subst p; simp
intro; rwa [Int.sub_eq_zero]
theorem of_not_dvd (a b : Int) : a != 0 ¬ (a b) b % a > 0 := by
simp; intro h₁ h₂
replace h₂ := Int.emod_pos_of_not_dvd h₂
simp [h₁] at h₂
assumption
intro h; rw [h, Int.sub_eq_add_neg, Int.sub_self]
end Int.Linear

View File

@@ -1,90 +0,0 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.Int.Lemmas
import Init.Data.Int.DivMod
import Init.Data.RArray
namespace Int.OfNat
/-!
Helper definitions and theorems for converting `Nat` expressions into `Int` one.
We use them to implement the arithmetic theories in `grind`
-/
abbrev Var := Nat
abbrev Context := Lean.RArray Nat
def Var.denote (ctx : Context) (v : Var) : Nat :=
ctx.get v
inductive Expr where
| num (v : Nat)
| var (i : Var)
| add (a b : Expr)
| mul (a b : Expr)
| div (a b : Expr)
| mod (a b : Expr)
deriving BEq
def Expr.denote (ctx : Context) : Expr Nat
| .num k => k
| .var v => v.denote ctx
| .add a b => Nat.add (denote ctx a) (denote ctx b)
| .mul a b => Nat.mul (denote ctx a) (denote ctx b)
| .div a b => Nat.div (denote ctx a) (denote ctx b)
| .mod a b => Nat.mod (denote ctx a) (denote ctx b)
def Expr.denoteAsInt (ctx : Context) : Expr Int
| .num k => Int.ofNat k
| .var v => Int.ofNat (v.denote ctx)
| .add a b => Int.add (denoteAsInt ctx a) (denoteAsInt ctx b)
| .mul a b => Int.mul (denoteAsInt ctx a) (denoteAsInt ctx b)
| .div a b => Int.ediv (denoteAsInt ctx a) (denoteAsInt ctx b)
| .mod a b => Int.emod (denoteAsInt ctx a) (denoteAsInt ctx b)
theorem Expr.denoteAsInt_eq (ctx : Context) (e : Expr) : e.denoteAsInt ctx = e.denote ctx := by
induction e <;> simp [denote, denoteAsInt, Int.ofNat_ediv, *] <;> rfl
theorem Expr.eq (ctx : Context) (lhs rhs : Expr)
: (lhs.denote ctx = rhs.denote ctx) = (lhs.denoteAsInt ctx = rhs.denoteAsInt ctx) := by
simp [denoteAsInt_eq, Int.ofNat_inj]
theorem Expr.le (ctx : Context) (lhs rhs : Expr)
: (lhs.denote ctx rhs.denote ctx) = (lhs.denoteAsInt ctx rhs.denoteAsInt ctx) := by
simp [denoteAsInt_eq, Int.ofNat_le]
theorem of_le (ctx : Context) (lhs rhs : Expr)
: lhs.denote ctx rhs.denote ctx lhs.denoteAsInt ctx rhs.denoteAsInt ctx := by
rw [Expr.le ctx lhs rhs]; simp
theorem of_not_le (ctx : Context) (lhs rhs : Expr)
: ¬ lhs.denote ctx rhs.denote ctx ¬ lhs.denoteAsInt ctx rhs.denoteAsInt ctx := by
rw [Expr.le ctx lhs rhs]; simp
theorem of_dvd (ctx : Context) (d : Nat) (e : Expr)
: d e.denote ctx Int.ofNat d e.denoteAsInt ctx := by
simp [Expr.denoteAsInt_eq, Int.ofNat_dvd]
theorem of_eq (ctx : Context) (lhs rhs : Expr)
: lhs.denote ctx = rhs.denote ctx lhs.denoteAsInt ctx = rhs.denoteAsInt ctx := by
rw [Expr.eq ctx lhs rhs]; simp
theorem of_not_eq (ctx : Context) (lhs rhs : Expr)
: ¬ lhs.denote ctx = rhs.denote ctx ¬ lhs.denoteAsInt ctx = rhs.denoteAsInt ctx := by
rw [Expr.eq ctx lhs rhs]; simp
theorem ofNat_toNat (a : Int) : (NatCast.natCast a.toNat : Int) = if a 0 then 0 else a := by
split
next h =>
rw [Int.toNat_of_nonpos h]; rfl
next h =>
simp at h
have := Int.toNat_of_nonneg (Int.le_of_lt h)
assumption
theorem Expr.denoteAsInt_nonneg (ctx : Context) (e : Expr) : e.denoteAsInt ctx 0 := by
simp [Expr.denoteAsInt_eq]
end Int.OfNat

View File

@@ -33,18 +33,20 @@ theorem lt_iff_add_one_le {a b : Int} : a < b ↔ a + 1 ≤ b := .rfl
theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a b := by
simp [le_def, h]; constructor
attribute [local simp] Int.add_left_neg Int.add_right_neg Int.neg_add
theorem le.intro {a b : Int} (n : Nat) (h : a + n = b) : a b :=
le.intro_sub n <| by rw [ h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_right_neg]
le.intro_sub n <| by rw [ h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]
theorem le.dest_sub {a b : Int} (h : a b) : n : Nat, b - a = n := nonneg_def.1 h
theorem le.dest {a b : Int} (h : a b) : n : Nat, a + n = b :=
let n, h₁ := le.dest_sub h
n, by rw [ h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_neg]
n, by rw [ h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]
protected theorem le_total (a b : Int) : a b b a :=
(nonneg_or_nonneg_neg (b - a)).imp_right fun H => by
rwa [show -(b - a) = a - b by simp [Int.neg_add,Int.add_comm, Int.sub_eq_add_neg]] at H
rwa [show -(b - a) = a - b by simp [Int.add_comm, Int.sub_eq_add_neg]] at H
@[simp, norm_cast] theorem ofNat_le {m n : Nat} : (m : Int) n m n :=
fun h =>
@@ -59,14 +61,14 @@ protected theorem le_total (a b : Int) : a ≤ b b ≤ a :=
theorem eq_ofNat_of_zero_le {a : Int} (h : 0 a) : n : Nat, a = n := by
have t := le.dest_sub h; rwa [Int.sub_zero] at t
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : n : Nat, a = n + 1 :=
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : n : Nat, a = n.succ :=
let n, (h : (1 + n) = a) := le.dest h
n, by rw [Nat.add_comm] at h; exact h.symm
theorem lt_add_succ (a : Int) (n : Nat) : a < a + (n + 1) :=
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]
theorem lt_add_succ (a : Int) (n : Nat) : a < a + Nat.succ n :=
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]; rfl
theorem lt.intro {a b : Int} {n : Nat} (h : a + (n + 1) = b) : a < b :=
theorem lt.intro {a b : Int} {n : Nat} (h : a + Nat.succ n = b) : a < b :=
h lt_add_succ a n
theorem lt.dest {a b : Int} (h : a < b) : n : Nat, a + Nat.succ n = b :=
@@ -115,7 +117,7 @@ protected theorem lt_iff_le_and_ne {a b : Int} : a < b ↔ a ≤ b ∧ a ≠ b :
have : n 0 := aneb.imp fun eq => by rw [ hn, eq, ofNat_zero, Int.add_zero]
apply lt.intro; rwa [ Nat.succ_pred_eq_of_pos (Nat.pos_of_ne_zero this)] at hn
protected theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
protected theorem zero_lt_one : (0 : Int) < 1 := _
@@ -131,15 +133,12 @@ protected theorem lt_of_not_ge {a b : Int} (h : ¬a ≤ b) : b < a :=
protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a b :=
(Int.lt_iff_le_not_le.mp h).right
@[simp] protected theorem not_le {a b : Int} : ¬a b b < a :=
protected theorem not_le {a b : Int} : ¬a b b < a :=
Iff.intro Int.lt_of_not_ge Int.not_le_of_gt
@[simp] protected theorem not_lt {a b : Int} : ¬a < b b a :=
protected theorem not_lt {a b : Int} : ¬a < b b a :=
by rw [ Int.not_le, Decidable.not_not]
protected theorem le_of_not_gt {a b : Int} (h : ¬ a > b) : a b :=
Int.not_lt.mp h
protected theorem lt_trichotomy (a b : Int) : a < b a = b b < a :=
if eq : a = b then .inr <| .inl eq else
if le : a b then .inl <| Int.lt_iff_le_and_ne.2 le, eq else
@@ -184,19 +183,61 @@ instance : Trans (α := Int) (· ≤ ·) (· < ·) (· < ·) := ⟨Int.lt_of_le_
instance : Trans (α := Int) (· < ·) (· < ·) (· < ·) := Int.lt_trans
theorem eq_natAbs_of_nonneg {a : Int} (h : 0 a) : a = natAbs a := by
protected theorem min_def (n m : Int) : min n m = if n m then n else m := rfl
protected theorem max_def (n m : Int) : max n m = if n m then m else n := rfl
protected theorem min_comm (a b : Int) : min a b = min b a := by
simp [Int.min_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₁ h₂
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) min := Int.min_comm
protected theorem min_le_right (a b : Int) : min a b b := by rw [Int.min_def]; split <;> simp [*]
protected theorem min_le_left (a b : Int) : min a b a := Int.min_comm .. Int.min_le_right ..
protected theorem min_eq_left {a b : Int} (h : a b) : min a b = a := by simp [Int.min_def, h]
protected theorem min_eq_right {a b : Int} (h : b a) : min a b = b := by
rw [Int.min_comm a b]; exact Int.min_eq_left h
protected theorem le_min {a b c : Int} : a min b c a b a c :=
fun h => Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..),
fun h₁, h₂ => by rw [Int.min_def]; split <;> assumption
protected theorem max_comm (a b : Int) : max a b = max b a := by
simp only [Int.max_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₂ h₁
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) max := Int.max_comm
protected theorem le_max_left (a b : Int) : a max a b := by rw [Int.max_def]; split <;> simp [*]
protected theorem le_max_right (a b : Int) : b max a b := Int.max_comm .. Int.le_max_left ..
protected theorem max_le {a b c : Int} : max a b c a c b c :=
fun h => Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h,
fun h₁, h₂ => by rw [Int.max_def]; split <;> assumption
protected theorem max_eq_right {a b : Int} (h : a b) : max a b = b := by
simp [Int.max_def, h, Int.not_lt.2 h]
protected theorem max_eq_left {a b : Int} (h : b a) : max a b = a := by
rw [ Int.max_comm b a]; exact Int.max_eq_right h
theorem eq_natAbs_of_zero_le {a : Int} (h : 0 a) : a = natAbs a := by
let n, e := eq_ofNat_of_zero_le h
rw [e]; rfl
@[deprecated eq_natAbs_of_nonneg (since := "2025-03-11")]
abbrev eq_natAbs_of_zero_le := @eq_natAbs_of_nonneg
theorem le_natAbs {a : Int} : a natAbs a :=
match Int.le_total 0 a with
| .inl h => by rw [eq_natAbs_of_nonneg h]; apply Int.le_refl
| .inl h => by rw [eq_natAbs_of_zero_le h]; apply Int.le_refl
| .inr h => Int.le_trans h (ofNat_zero_le _)
@[simp] theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
Int.not_le.1 fun h => let _, h := eq_ofNat_of_zero_le h; nomatch h
theorem negSucc_le_zero (n : Nat) : -[n+1] 0 :=
@@ -205,6 +246,18 @@ theorem negSucc_le_zero (n : Nat) : -[n+1] ≤ 0 :=
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 -[n+1] False := by
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (ofNat_zero_le n)]
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
rw [Int.max_eq_right (ofNat_zero_le n)]
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
rw [Int.max_eq_right (negSucc_le_zero _)]
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
rw [Int.max_eq_left (negSucc_le_zero _)]
protected theorem add_le_add_left {a b : Int} (h : a b) (c : Int) : c + a c + b :=
let n, hn := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
@@ -226,10 +279,10 @@ protected theorem le_of_add_le_add_left {a b c : Int} (h : a + b ≤ a + c) : b
protected theorem le_of_add_le_add_right {a b c : Int} (h : a + b c + b) : a c :=
Int.le_of_add_le_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
@[simp] protected theorem add_le_add_iff_left (a : Int) : a + b a + c b c :=
protected theorem add_le_add_iff_left (a : Int) : a + b a + c b c :=
Int.le_of_add_le_add_left, (Int.add_le_add_left · _)
@[simp] protected theorem add_le_add_iff_right (c : Int) : a + c b + c a b :=
protected theorem add_le_add_iff_right (c : Int) : a + c b + c a b :=
Int.le_of_add_le_add_right, (Int.add_le_add_right · _)
protected theorem add_le_add {a b c d : Int} (h₁ : a b) (h₂ : c d) : a + c b + d :=
@@ -248,15 +301,6 @@ protected theorem neg_le_neg {a b : Int} (h : a ≤ b) : -b ≤ -a := by
have : 0 + -b -a + b + -b := Int.add_le_add_right this (-b)
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
@[simp] protected theorem neg_le_neg_iff {a b : Int} : -a -b b a :=
fun h => by simpa using Int.neg_le_neg h, Int.neg_le_neg
@[simp] protected theorem neg_le_zero_iff {a : Int} : -a 0 0 a := by
rw [ Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
@[simp] protected theorem zero_le_neg_iff {a : Int} : 0 -a a 0 := by
rw [ Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
protected theorem le_of_neg_le_neg {a b : Int} (h : -b -a) : a b :=
suffices - -a - -b by simp [Int.neg_neg] at this; assumption
Int.neg_le_neg h
@@ -274,15 +318,6 @@ protected theorem neg_lt_neg {a b : Int} (h : a < b) : -b < -a := by
have : 0 + -b < -a + b + -b := Int.add_lt_add_right this (-b)
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
@[simp] protected theorem neg_lt_neg_iff {a b : Int} : -a < -b b < a :=
fun h => by simpa using Int.neg_lt_neg h, Int.neg_lt_neg
@[simp] protected theorem neg_lt_zero_iff {a : Int} : -a < 0 0 < a := by
rw [ Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
@[simp] protected theorem zero_lt_neg_iff {a : Int} : 0 < -a a < 0 := by
rw [ Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 := by
have : -a < -0 := Int.neg_lt_neg h
rwa [Int.neg_zero] at this
@@ -323,125 +358,9 @@ protected theorem sub_lt_self (a : Int) {b : Int} (h : 0 < b) : a - b < a :=
theorem add_one_le_of_lt {a b : Int} (H : a < b) : a + 1 b := H
protected theorem le_iff_lt_add_one {a b : Int} : a b a < b + 1 := by
rw [Int.lt_iff_add_one_le]
exact (Int.add_le_add_iff_right 1).symm
/- ### min and max -/
protected theorem min_def (n m : Int) : min n m = if n m then n else m := rfl
protected theorem max_def (n m : Int) : max n m = if n m then m else n := rfl
@[simp] protected theorem neg_min_neg (a b : Int) : min (-a) (-b) = -max a b := by
rw [Int.min_def, Int.max_def]
simp
split <;> rename_i h₁ <;> split <;> rename_i h₂
· simpa using Int.le_antisymm h₂ h₁
· simp
· simp
· simp only [Int.not_le] at h₁ h₂
exfalso
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
@[simp] protected theorem min_add_right (a b c : Int) : min (a + c) (b + c) = min a b + c := by
rw [Int.min_def, Int.min_def]
simp only [Int.add_le_add_iff_right]
split <;> simp
@[simp] protected theorem min_add_left (a b c : Int) : min (a + b) (a + c) = a + min b c := by
rw [Int.min_def, Int.min_def]
simp only [Int.add_le_add_iff_left]
split <;> simp
protected theorem min_comm (a b : Int) : min a b = min b a := by
simp [Int.min_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₁ h₂
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) min := Int.min_comm
protected theorem min_le_right (a b : Int) : min a b b := by rw [Int.min_def]; split <;> simp [*]
protected theorem min_le_left (a b : Int) : min a b a := Int.min_comm .. Int.min_le_right ..
protected theorem min_eq_left {a b : Int} (h : a b) : min a b = a := by simp [Int.min_def, h]
protected theorem min_eq_right {a b : Int} (h : b a) : min a b = b := by
rw [Int.min_comm a b]; exact Int.min_eq_left h
protected theorem le_min {a b c : Int} : a min b c a b a c :=
fun h => Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..),
fun h₁, h₂ => by rw [Int.min_def]; split <;> assumption
protected theorem lt_min {a b c : Int} : a < min b c a < b a < c := Int.le_min
@[simp] protected theorem neg_max_neg (a b : Int) : max (-a) (-b) = -min a b := by
rw [Int.min_def, Int.max_def]
simp
split <;> rename_i h₁ <;> split <;> rename_i h₂
· simpa using Int.le_antisymm h₁ h₂
· simp
· simp
· simp only [Int.not_le] at h₁ h₂
exfalso
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
@[simp] protected theorem max_add_right (a b c : Int) : max (a + c) (b + c) = max a b + c := by
rw [Int.max_def, Int.max_def]
simp only [Int.add_le_add_iff_right]
split <;> simp
@[simp] protected theorem max_add_left (a b c : Int) : max (a + b) (a + c) = a + max b c := by
rw [Int.max_def, Int.max_def]
simp only [Int.add_le_add_iff_left]
split <;> simp
protected theorem max_comm (a b : Int) : max a b = max b a := by
simp only [Int.max_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₂ h₁
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) max := Int.max_comm
protected theorem le_max_left (a b : Int) : a max a b := by rw [Int.max_def]; split <;> simp [*]
protected theorem le_max_right (a b : Int) : b max a b := Int.max_comm .. Int.le_max_left ..
protected theorem max_eq_right {a b : Int} (h : a b) : max a b = b := by
simp [Int.max_def, h, Int.not_lt.2 h]
protected theorem max_eq_left {a b : Int} (h : b a) : max a b = a := by
rw [ Int.max_comm b a]; exact Int.max_eq_right h
protected theorem max_le {a b c : Int} : max a b c a c b c :=
fun h => Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h,
fun h₁, h₂ => by rw [Int.max_def]; split <;> assumption
protected theorem max_lt {a b c : Int} : max a b < c a < c b < c := by
simp only [Int.lt_iff_add_one_le]
simpa using Int.max_le (a := a + 1) (b := b + 1) (c := c)
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (ofNat_zero_le n)]
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
rw [Int.max_eq_right (ofNat_zero_le n)]
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
rw [Int.max_eq_right (negSucc_le_zero _)]
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
rw [Int.max_eq_left (negSucc_le_zero _)]
@[simp] protected theorem min_self (a : Int) : min a a = a := Int.min_eq_left (Int.le_refl _)
instance : Std.IdempotentOp (α := Int) min := Int.min_self
@[simp] protected theorem max_self (a : Int) : max a a = a := Int.max_eq_right (Int.le_refl _)
instance : Std.IdempotentOp (α := Int) max := Int.max_self
/- ### Order properties and multiplication -/
protected theorem mul_nonneg {a b : Int} (ha : 0 a) (hb : 0 b) : 0 a * b := by
let n, hn := eq_ofNat_of_zero_le ha
let m, hm := eq_ofNat_of_zero_le hb
@@ -450,8 +369,7 @@ protected theorem mul_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a
protected theorem mul_pos {a b : Int} (ha : 0 < a) (hb : 0 < b) : 0 < a * b := by
let n, hn := eq_succ_of_zero_lt ha
let m, hm := eq_succ_of_zero_lt hb
rw [hn, hm]
apply ofNat_succ_pos
rw [hn, hm, ofNat_mul]; apply ofNat_succ_pos
protected theorem mul_lt_mul_of_pos_left {a b c : Int}
(h₁ : a < b) (h₂ : 0 < c) : c * a < c * b := by
@@ -507,7 +425,7 @@ protected theorem mul_le_mul_of_nonpos_left {a b c : Int}
/- ## natAbs -/
@[simp, norm_cast] theorem natAbs_ofNat (n : Nat) : natAbs n = n := rfl
@[simp] theorem natAbs_ofNat (n : Nat) : natAbs n = n := rfl
@[simp] theorem natAbs_negSucc (n : Nat) : natAbs -[n+1] = n.succ := rfl
@[simp] theorem natAbs_zero : natAbs (0 : Int) = (0 : Nat) := rfl
@[simp] theorem natAbs_one : natAbs (1 : Int) = (1 : Nat) := rfl
@@ -552,13 +470,6 @@ theorem natAbs_of_nonneg {a : Int} (H : 0 ≤ a) : (natAbs a : Int) = a :=
theorem ofNat_natAbs_of_nonpos {a : Int} (H : a 0) : (natAbs a : Int) = -a := by
rw [ natAbs_neg, natAbs_of_nonneg (Int.neg_nonneg_of_nonpos H)]
theorem natAbs_sub_of_nonneg_of_le {a b : Int} (h₁ : 0 b) (h₂ : b a) :
(a - b).natAbs = a.natAbs - b.natAbs := by
rw [ Int.ofNat_inj]
rw [natAbs_of_nonneg, ofNat_sub, natAbs_of_nonneg (Int.le_trans h₁ h₂), natAbs_of_nonneg h₁]
· rwa [ Int.ofNat_le, natAbs_of_nonneg h₁, natAbs_of_nonneg (Int.le_trans h₁ h₂)]
· exact Int.sub_nonneg_of_le h₂
/-! ### toNat -/
theorem toNat_eq_max : a : Int, (toNat a : Int) = max a 0
@@ -581,8 +492,8 @@ theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
match a with
| (n : Nat) => simp
| -(n + 1 : Nat) => norm_cast
| Int.ofNat n => simp
| Int.negSucc n => simp
theorem self_le_toNat (a : Int) : a toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
@@ -618,15 +529,12 @@ theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
| 0 => rfl
| _+1 => rfl
/-! ### toNat? -/
/-! ### toNat' -/
theorem mem_toNat? : {a : Int} {n : Nat}, toNat? a = some n a = n
| (m : Nat), n => by simp [toNat?, Int.ofNat_inj]
theorem mem_toNat' : {a : Int} {n : Nat}, toNat' a = some n a = n
| (m : Nat), n => by simp [toNat', Int.ofNat_inj]
| -[m+1], n => by constructor <;> nofun
@[deprecated mem_toNat? (since := "2025-03-11")]
abbrev mem_toNat' := @mem_toNat?
/-! ## Order properties of the integers -/
protected theorem le_of_not_le {a b : Int} : ¬ a b b a := (Int.le_total a b).resolve_left
@@ -646,10 +554,10 @@ protected theorem lt_of_add_lt_add_left {a b c : Int} (h : a + b < a + c) : b <
protected theorem lt_of_add_lt_add_right {a b c : Int} (h : a + b < c + b) : a < c :=
Int.lt_of_add_lt_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
@[simp] protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c b < c :=
protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c b < c :=
Int.lt_of_add_lt_add_left, (Int.add_lt_add_left · _)
@[simp] protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c a < b :=
protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c a < b :=
Int.lt_of_add_lt_add_right, (Int.add_lt_add_right · _)
protected theorem add_lt_add {a b c d : Int} (h₁ : a < b) (h₂ : c < d) : a + c < b + d :=
@@ -844,18 +752,6 @@ protected theorem sub_le_sub_right {a b : Int} (h : a ≤ b) (c : Int) : a - c
protected theorem sub_le_sub {a b c d : Int} (hab : a b) (hcd : c d) : a - d b - c :=
Int.add_le_add hab (Int.neg_le_neg hcd)
protected theorem le_of_sub_le_sub_left {a b c : Int} (h : c - a c - b) : b a :=
Int.le_of_neg_le_neg <| Int.le_of_add_le_add_left h
protected theorem le_of_sub_le_sub_right {a b c : Int} (h : a - c b - c) : a b :=
Int.le_of_add_le_add_right h
@[simp] protected theorem sub_le_sub_left_iff {a b c : Int} : c - a c - b b a :=
Int.le_of_sub_le_sub_left, (Int.sub_le_sub_left · c)
@[simp] protected theorem sub_le_sub_right_iff {a b c : Int} : a - c b - c a b :=
Int.le_of_sub_le_sub_right, (Int.sub_le_sub_right · c)
protected theorem add_lt_of_lt_neg_add {a b c : Int} (h : b < -a + c) : a + b < c := by
have h := Int.add_lt_add_left h a
rwa [Int.add_neg_cancel_left] at h
@@ -964,11 +860,11 @@ protected theorem lt_of_sub_lt_sub_right {a b c : Int} (h : a - c < b - c) : a <
Int.lt_of_sub_lt_sub_right, (Int.sub_lt_sub_right · c)
protected theorem sub_lt_sub_of_le_of_lt {a b c d : Int}
(hab : a b) (hcd : c < d) : a - d < b - c :=
(hab : a b) (hcd : c < d) : a - d < b - c :=
Int.add_lt_add_of_le_of_lt hab (Int.neg_lt_neg hcd)
protected theorem sub_lt_sub_of_lt_of_le {a b c d : Int}
(hab : a < b) (hcd : c d) : a - d < b - c :=
(hab : a < b) (hcd : c d) : a - d < b - c :=
Int.add_lt_add_of_lt_of_le hab (Int.neg_le_neg hcd)
protected theorem add_le_add_three {a b c d e f : Int}
@@ -1042,22 +938,6 @@ protected theorem mul_self_le_mul_self {a b : Int} (h1 : 0 ≤ a) (h2 : a ≤ b)
protected theorem mul_self_lt_mul_self {a b : Int} (h1 : 0 a) (h2 : a < b) : a * a < b * b :=
Int.mul_lt_mul' (Int.le_of_lt h2) h2 h1 (Int.lt_of_le_of_lt h1 h2)
protected theorem nonneg_of_mul_nonneg_left {a b : Int}
(h : 0 a * b) (hb : 0 < b) : 0 a :=
Int.le_of_not_gt fun ha => Int.not_le_of_gt (Int.mul_neg_of_neg_of_pos ha hb) h
protected theorem nonneg_of_mul_nonneg_right {a b : Int}
(h : 0 a * b) (ha : 0 < a) : 0 b :=
Int.le_of_not_gt fun hb => Int.not_le_of_gt (Int.mul_neg_of_pos_of_neg ha hb) h
protected theorem nonpos_of_mul_nonpos_left {a b : Int}
(h : a * b 0) (hb : 0 < b) : a 0 :=
Int.le_of_not_gt fun ha : a > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
protected theorem nonpos_of_mul_nonpos_right {a b : Int}
(h : a * b 0) (ha : 0 < a) : b 0 :=
Int.le_of_not_gt fun hb : b > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
/- ## sign -/
@[simp] theorem sign_zero : sign 0 = 0 := rfl
@@ -1108,10 +988,10 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
| 0, h => nomatch h
| -[_+1], _ => negSucc_lt_zero _
@[simp] theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 0 < a :=
theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 0 < a :=
pos_of_sign_eq_one, sign_eq_one_of_pos
@[simp] theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 a < 0 :=
theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 a < 0 :=
neg_of_sign_eq_neg_one, sign_eq_neg_one_of_neg
@[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 a = 0 :=
@@ -1123,7 +1003,7 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
| .ofNat (_ + 1) => rfl
| .negSucc _ => rfl
@[simp] theorem sign_nonneg_iff : 0 sign x 0 x := by
@[simp] theorem sign_nonneg : 0 sign x 0 x := by
match x with
| 0 => rfl
| .ofNat (_ + 1) =>
@@ -1131,26 +1011,6 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
exact Int.le_add_one (ofNat_nonneg _)
| .negSucc _ => simp +decide [sign]
@[deprecated sign_nonneg_iff (since := "2025-03-11")] abbrev sign_nonneg := @sign_nonneg_iff
@[simp] theorem sign_pos_iff : 0 < sign x 0 < x := by
match x with
| 0
| .ofNat (_ + 1) => simp
| .negSucc x => simp
@[simp] theorem sign_nonpos_iff : sign x 0 x 0 := by
match x with
| 0 => rfl
| .ofNat (_ + 1) => simp
| .negSucc _ => simpa using negSucc_le_zero _
@[simp] theorem sign_neg_iff : sign x < 0 x < 0 := by
match x with
| 0 => simp
| .ofNat (_ + 1) => simpa using le.intro_sub _ rfl
| .negSucc _ => simp
@[simp] theorem mul_sign_self : i : Int, i * sign i = natAbs i
| succ _ => Int.mul_one _
| 0 => Int.mul_zero _
@@ -1161,12 +1021,6 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
@[simp] theorem sign_mul_self : sign i * i = natAbs i := by
rw [Int.mul_comm, mul_sign_self]
theorem sign_trichotomy (a : Int) : sign a = 1 sign a = 0 sign a = -1 := by
match a with
| 0 => simp
| .ofNat (_ + 1) => simp
| .negSucc _ => simp
/- ## natAbs -/
theorem natAbs_ne_zero {a : Int} : a.natAbs 0 a 0 := not_congr Int.natAbs_eq_zero
@@ -1175,7 +1029,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
| ofNat _ => rfl
| -[_+1] => rfl
protected theorem eq_nat_or_neg (a : Int) : n : Nat, a = n a = -n := _, natAbs_eq a
theorem eq_nat_or_neg (a : Int) : n : Nat, a = n a = -n := _, natAbs_eq a
theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs.eq_def]
@@ -1189,7 +1043,7 @@ theorem natAbs_eq_iff {a : Int} {n : Nat} : a.natAbs = n ↔ a = n a = -↑n
theorem natAbs_add_le (a b : Int) : natAbs (a + b) natAbs a + natAbs b := by
suffices a b : Nat, natAbs (subNatNat a b.succ) (a + b).succ by
match a, b with
| (a:Nat), (b:Nat) => rw [ ofNat_add, natAbs_ofNat]; apply Nat.le_refl
| (a:Nat), (b:Nat) => rw [ofNat_add_ofNat, natAbs_ofNat]; apply Nat.le_refl
| (a:Nat), -[b+1] => rw [natAbs_ofNat, natAbs_negSucc]; apply this
| -[a+1], (b:Nat) =>
rw [natAbs_negSucc, natAbs_ofNat, Nat.succ_add, Nat.add_comm a b]; apply this
@@ -1208,7 +1062,6 @@ theorem natAbs_add_le (a b : Int) : natAbs (a + b) ≤ natAbs a + natAbs b := by
theorem natAbs_sub_le (a b : Int) : natAbs (a - b) natAbs a + natAbs b := by
rw [ Int.natAbs_neg b]; apply natAbs_add_le
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_eq' (m : Nat) : -[m+1] = -m - 1 := by simp only [negSucc_eq, Int.neg_add]; rfl
theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
@@ -1216,10 +1069,7 @@ theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
match a, b, eq_ofNat_of_zero_le w₁, eq_ofNat_of_zero_le (Int.le_trans w₁ (Int.le_of_lt w₂)) with
| _, _, _, rfl, _, rfl => ofNat_lt.1 w₂
theorem natAbs_eq_iff_mul_eq_zero : natAbs a = n (a - n) * (a + n) = 0 := by
theorem eq_natAbs_iff_mul_eq_zero : natAbs a = n (a - n) * (a + n) = 0 := by
rw [natAbs_eq_iff, Int.mul_eq_zero, Int.sub_neg, Int.sub_eq_zero, Int.sub_eq_zero]
@[deprecated natAbs_eq_iff_mul_eq_zero (since := "2025-03-11")]
abbrev eq_natAbs_iff_mul_eq_zero := @natAbs_eq_iff_mul_eq_zero
end Int

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