Compare commits

..

4 Commits

Author SHA1 Message Date
Kim Morrison
aef3269113 finish 2025-02-27 10:30:44 +11:00
Kim Morrison
c4ec0a9f9c . 2025-02-27 09:29:46 +11:00
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
1628 changed files with 16530 additions and 60661 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 - name: Identify stage0 changes
run: | 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" if test -s "$RUNNER_TEMP/stage0"
then then
echo "CHANGES=yes" >> "$GITHUB_ENV" echo "CHANGES=yes" >> "$GITHUB_ENV"

View File

@@ -36,9 +36,7 @@ jobs:
# 2: PRs with `release-ci` label, releases (incl. nightlies) # 2: PRs with `release-ci` label, releases (incl. nightlies)
check-level: ${{ steps.set-level.outputs.check-level }} check-level: ${{ steps.set-level.outputs.check-level }}
# The build matrix, dynamically generated here # The build matrix, dynamically generated here
matrix: ${{ steps.set-matrix.outputs.matrix }} matrix: ${{ steps.set-matrix.outputs.result }}
# secondary build jobs that should not block the CI success/merge queue
matrix-secondary: ${{ steps.set-matrix.outputs.matrix-secondary }}
# Should we make a nightly release? If so, this output contains the lean version string, else it is empty # 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 }} nightly: ${{ steps.set-nightly.outputs.nightly }}
# Should this be the CI for a tagged release? # Should this be the CI for a tagged release?
@@ -137,7 +135,6 @@ jobs:
console.log(`level: ${level}`); console.log(`level: ${level}`);
// use large runners where available (original repo) // use large runners where available (original repo)
let large = ${{ github.repository == 'leanprover/lean4' }}; let large = ${{ github.repository == 'leanprover/lean4' }};
const isPr = "${{ github.event_name }}" == "pull_request";
let matrix = [ let matrix = [
{ {
"name": "Linux LLVM", "name": "Linux LLVM",
@@ -166,14 +163,6 @@ jobs:
// foreign code may be linked against more recent glibc // foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'" "CTEST_OPTIONS": "-E 'foreign'"
}, },
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
// just a secondary PR build job for now
"check-level": isPr ? 0 : 3,
"secondary": true,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON"
},
{ {
"name": "Linux", "name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest", "os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
@@ -215,18 +204,12 @@ jobs:
"os": "macos-14", "os": "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64", "CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true, "release": true,
"check-level": 0,
"shell": "bash -euxo pipefail {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", "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*", "prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L", "binary-check": "otool -L",
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619 "tar": "gtar" // https://github.com/actions/runner-images/issues/2619
// Special handling for MacOS aarch64, we want:
// 1. To run it in PRs so Mac devs get PR toolchains (so secondary is sufficient)
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
// little value in the merge queue
// 3. To run it in release (obviously)
"check-level": isPr ? 0 : 2,
"secondary": isPr,
}, },
{ {
"name": "Windows", "name": "Windows",
@@ -277,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\"" // "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)}`); console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
matrix = matrix.filter((job) => level >= job["check-level"]); return matrix.filter((job) => level >= job["check-level"])
core.setOutput('matrix', matrix.filter((job) => !job["secondary"]));
core.setOutput('matrix-secondary', matrix.filter((job) => job["secondary"]));
build: build:
needs: [configure]
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4' if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
needs: [configure] strategy:
uses: ./.github/workflows/build-template.yml matrix:
with: include: ${{fromJson(needs.configure.outputs.matrix)}}
config: ${{needs.configure.outputs.matrix}} # complete all jobs
check-level: ${{ needs.configure.outputs.check-level }} fail-fast: false
nightly: ${{ needs.configure.outputs.nightly }} runs-on: ${{ matrix.os }}
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }} defaults:
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }} run:
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }} shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }} name: ${{ matrix.name }}
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }} env:
secrets: inherit # must be inside workspace
CCACHE_DIR: ${{ github.workspace }}/.ccache
# build jobs that should not be considered by `all-done` below CCACHE_COMPRESS: true
build-secondary: # current cache limit
needs: [configure] CCACHE_MAXSIZE: 200M
if: needs.configure.outputs.matrix-secondary != '[]' # squelch error message about missing nixpkgs channel
uses: ./.github/workflows/build-template.yml NIX_BUILD_SHELL: bash
with: LSAN_OPTIONS: max_leaks=10
config: ${{needs.configure.outputs.matrix-secondary}} # somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
check-level: ${{ needs.configure.outputs.check-level }} CXX: c++
nightly: ${{ needs.configure.outputs.nightly }} MACOSX_DEPLOYMENT_TARGET: 10.15
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }} steps:
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }} - name: Install Nix
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }} uses: DeterminateSystems/nix-installer-action@main
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }} if: runner.os == 'Linux' && !matrix.cmultilib
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }} - name: Install MSYS2
secrets: inherit 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 job collects results from all the matrix jobs
# This can be made the "required" job, instead of listing each # 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. - name: Download artifact from the previous workflow.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }} if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: download-artifact 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: with:
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
path: artifacts path: artifacts
@@ -155,20 +155,6 @@ jobs:
fi fi
if [[ -n "$MESSAGE" ]]; then 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" echo "Checking existing messages"
@@ -215,12 +201,7 @@ jobs:
else else
echo "The message already exists in the comment body." echo "The message already exists in the comment body."
fi fi
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
if [[ "$FORCE_CI" == "true" ]]; then
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
else
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
fi
else else
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT" echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
fi fi
@@ -271,7 +252,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}" BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else 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 BASE=nightly-testing
fi fi
@@ -335,7 +316,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}" BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else 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 BASE=nightly-testing
fi fi

View File

@@ -15,7 +15,10 @@ foreach(var ${vars})
# must forward options that generate incompatible .olean format # must forward options that generate incompatible .olean format
list(APPEND STAGE0_ARGS "-D${var}=${${var}}") list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif() endif()
if("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE") if("${var}" MATCHES "LLVM*")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
if("${var}" MATCHES "PKG_CONFIG*")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}") list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif() endif()
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY")) elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
@@ -44,11 +47,10 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED") string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif() endif()
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
ExternalProject_add(cadical ExternalProject_add(cadical
PREFIX cadical PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-2.1.2 GIT_TAG rel-1.9.5
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build # 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_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
@@ -65,8 +67,8 @@ ExternalProject_add(stage0
SOURCE_SUBDIR src SOURCE_SUBDIR src
BINARY_DIR stage0 BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway # 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 # (however, `CHECK_OLEAN_VERSION=ON` in CI will override this as we need to
# by stage 0) # embed the githash into the stage 1 library built by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS} CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install INSTALL_COMMAND "" # skip install
@@ -80,7 +82,6 @@ ExternalProject_add(stage1
BUILD_ALWAYS ON BUILD_ALWAYS ON
INSTALL_COMMAND "" INSTALL_COMMAND ""
DEPENDS stage0 DEPENDS stage0
STEP_TARGETS configure
) )
ExternalProject_add(stage2 ExternalProject_add(stage2
SOURCE_DIR "${LEAN_SOURCE_DIR}" SOURCE_DIR "${LEAN_SOURCE_DIR}"

8
flake.lock generated
View File

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

View File

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

File diff suppressed because it is too large Load Diff

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})" link = f"[#{pr_number}](https://github.com/leanprover/lean4/pull/{pr_number})"
return f"{link} {description}" 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): def count_commit_types(commits):
counts = { counts = {
'total': len(commits), '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 _, first_line, _ in commits:
for commit_type in commit_types(): for commit_type in ['feat:', 'fix:', 'refactor:', 'doc:', 'chore:']:
if first_line.startswith(f'{commit_type}:'): if first_line.startswith(commit_type):
counts[commit_type] += 1 counts[commit_type.rstrip(':')] += 1
break break
return counts return counts
@@ -159,9 +158,8 @@ def main():
counts = count_commit_types(commits) counts = count_commit_types(commits)
print(f"For this release, {counts['total']} changes landed. " 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"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"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['chore']} chores.\n")
f"and {counts['style'] + counts['chore']} other changes.\n")
section_order = sort_sections_order() 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)) 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" section_title = format_section_title(label) if label != "Uncategorised" else "Uncategorised"
print(f"## {section_title}\n") print(f"## {section_title}\n")
for _, entry in sorted(entries, key=lambda x: x[0]): for _, entry in sorted(entries, key=lambda x: x[0]):
# Split entry into lines and indent all lines after the first print(f"* {entry}\n")
lines = entry.splitlines()
print(f"* {lines[0]}")
for line in lines[1:]:
print(f" {line}")
print() # Empty line after each entry
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject) include(ExternalProject)
project(LEAN CXX C) project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4) set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 19) set(LEAN_VERSION_MINOR 18)
set(LEAN_VERSION_PATCH 0) set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise. 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'") set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -455,20 +455,20 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec") string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC") string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN") 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 LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=$ORIGIN/../lib:$ORIGIN/../lib/lean") string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec") string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib") 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_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.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") string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND CMAKE_CXX_FLAGS " -fPIC") string(APPEND CMAKE_CXX_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC") string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 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() endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -515,16 +515,7 @@ if(USE_GITHASH)
message(STATUS "git commit sha1: ${GIT_SHA1}") message(STATUS "git commit sha1: ${GIT_SHA1}")
endif() endif()
else() else()
if(USE_LAKE AND ${STAGE} EQUAL 0) set(GIT_SHA1 "")
# we need to embed *some* hash for Lake to invalidate stage 1 on stage 0 changes
execute_process(
COMMAND git ls-tree HEAD "${CMAKE_CURRENT_SOURCE_DIR}/../../stage0" --object-only
OUTPUT_VARIABLE GIT_SHA1
OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "stage0 sha1: ${GIT_SHA1}")
else()
set(GIT_SHA1 "")
endif()
endif() endif()
configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h") configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h")
@@ -551,9 +542,6 @@ include_directories(${LEAN_SOURCE_DIR})
include_directories(${CMAKE_BINARY_DIR}) # version.h etc., "private" headers include_directories(${CMAKE_BINARY_DIR}) # version.h etc., "private" headers
include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" 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` # 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(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}") string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
@@ -766,12 +754,7 @@ add_custom_target(clean-olean
DEPENDS clean-stdlib) DEPENDS clean-stdlib)
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
PATTERN temp PATTERN temp EXCLUDE)
PATTERN "*.export"
PATTERN "*.hash"
PATTERN "*.trace"
PATTERN "*.rsp"
EXCLUDE)
# symlink source into expected installation location for go-to-definition, if file system allows it # symlink source into expected installation location for go-to-definition, if file system allows it
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
@@ -802,32 +785,10 @@ if(LEAN_INSTALL_PREFIX)
endif() endif()
# Escape for `make`. Yes, twice. # 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}") 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) 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) 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}/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/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.Internal
import Init.Try import Init.Try
import Init.BinderNameHint 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_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 theorem not_and_iff_or_not_not : ¬(a b) ¬a ¬b := Decidable.not_and_iff_or_not_not
@[deprecated not_and_iff_not_or_not (since := "2025-03-18")]
abbrev not_and_iff_or_not_not := @not_and_iff_not_or_not
theorem not_iff : ¬(a b) (¬a b) := Decidable.not_iff theorem not_iff : ¬(a b) (¬a b) := Decidable.not_iff

View File

@@ -51,28 +51,14 @@ def ForInStep.value (x : ForInStep α) : α :=
@[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl @[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl
@[simp] theorem ForInStep.value_yield (b : β) : (ForInStep.yield 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] @[reducible]
def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β := def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β :=
fun a f => f <$> a fun a f => f <$> a
@[inherit_doc Functor.mapRev]
infixr:100 " <&> " => Functor.mapRev infixr:100 " <&> " => Functor.mapRev
recommended_spelling "mapRev" for "<&>" in [Functor.mapRev, «term_<&>_»] 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] @[always_inline, inline]
def Functor.discard {f : Type u Type v} {α : Type u} [Functor f] (x : f α) : f PUnit := def Functor.discard {f : Type u Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
Functor.mapConst PUnit.unit x Functor.mapConst PUnit.unit x
@@ -135,13 +121,6 @@ instance : ToBool Bool where
| true => t | true => t
| false => f | 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 @[macro_inline] def orM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
let b x let b x
match toBool b with match toBool b with
@@ -152,13 +131,6 @@ infixr:30 " <||> " => orM
recommended_spelling "orM" for "<||>" in [orM, «term_<||>_»] 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 @[macro_inline] def andM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
let b x let b x
match toBool b with match toBool b with
@@ -169,9 +141,6 @@ infixr:35 " <&&> " => andM
recommended_spelling "andM" for "<&&>" in [andM, «term_<&&>_»] 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 := @[macro_inline] def notM {m : Type Type v} [Applicative m] (x : m Bool) : m Bool :=
not <$> x not <$> x
@@ -287,61 +256,21 @@ Using `control` means that `runInBase` can be used multiple times.
-/ -/
/-- /-- MonadControl is a way of stating that the monad `m` can be 'run inside' the monad `n`.
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 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.
higher-order operations automatically. 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 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 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 α 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 α restoreM : {α : Type u} m (stM α) n α
/-- /-- Transitive closure of MonadControl. -/
A way to lift a computation from one monad to another while providing the lifted computation with a
means of interpreting computations from the outer monad. This provides a means of lifting
higher-order operations automatically.
Clients should typically use `control` or `controlAt`, which request an instance of `MonadControlT`:
the reflexive, transitive closure of `MonadControl`. New instances should be defined for
`MonadControl` itself.
-/
class MonadControlT (m : Type u Type v) (n : Type u Type w) where 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 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 α 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 α restoreM {α : Type u} : stM α n α
export MonadControlT (stM liftWith restoreM) export MonadControlT (stM liftWith restoreM)
@@ -357,28 +286,11 @@ instance (m : Type u → Type v) [Pure m] : MonadControlT m m where
liftWith f := f fun x => x liftWith f := f fun x => x
restoreM x := pure 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] @[always_inline, inline]
def controlAt (m : Type u Type v) {n : Type u Type w} [MonadControlT m n] [Bind n] {α : Type u} 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 α := (f : ({β : Type u} n β m (stM m n β)) m (stM m n α)) : n α :=
liftWith f >>= restoreM 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] @[always_inline, inline]
def control {m : Type u Type v} {n : Type u Type w} [MonadControlT m n] [Bind n] {α : Type u} 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 α := (f : ({β : Type u} n β m (stM m n β)) m (stM m n α)) : n α :=

View File

@@ -13,20 +13,10 @@ import Init.Coe
namespace Except namespace Except
variable {ε : Type u} variable {ε : Type u}
/--
A successful computation in the `Except ε` monad: `a` is returned, and no exception is thrown.
-/
@[always_inline, inline] @[always_inline, inline]
protected def pure (a : α) : Except ε α := protected def pure (a : α) : Except ε α :=
Except.ok a 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] @[always_inline, inline]
protected def map (f : α β) : Except ε α Except ε β protected def map (f : α β) : Except ε α Except ε β
| Except.error err => Except.error err | Except.error err => Except.error err
@@ -37,78 +27,36 @@ protected def map (f : α → β) : Except ε α → Except ε β
intro e intro e
simp [Except.map]; cases e <;> rfl 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] @[always_inline, inline]
protected def mapError (f : ε ε') : Except ε α Except ε' α protected def mapError (f : ε ε') : Except ε α Except ε' α
| Except.error err => Except.error <| f err | Except.error err => Except.error <| f err
| Except.ok v => Except.ok v | 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] @[always_inline, inline]
protected def bind (ma : Except ε α) (f : α Except ε β) : Except ε β := protected def bind (ma : Except ε α) (f : α Except ε β) : Except ε β :=
match ma with match ma with
| Except.error err => Except.error err | Except.error err => Except.error err
| Except.ok v => f v | 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] @[always_inline, inline]
protected def toBool : Except ε α Bool protected def toBool : Except ε α Bool
| Except.ok _ => true | Except.ok _ => true
| Except.error _ => false | Except.error _ => false
@[inherit_doc Except.toBool]
abbrev isOk : Except ε α Bool := 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] @[always_inline, inline]
protected def toOption : Except ε α Option α protected def toOption : Except ε α Option α
| Except.ok a => some a | Except.ok a => some a
| Except.error _ => none | 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] @[always_inline, inline]
protected def tryCatch (ma : Except ε α) (handle : ε Except ε α) : Except ε α := protected def tryCatch (ma : Except ε α) (handle : ε Except ε α) : Except ε α :=
match ma with match ma with
| Except.ok a => Except.ok a | Except.ok a => Except.ok a
| Except.error e => handle e | 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 ε α := def orElseLazy (x : Except ε α) (y : Unit Except ε α) : Except ε α :=
match x with match x with
| Except.ok a => Except.ok a | Except.ok a => Except.ok a
@@ -122,26 +70,12 @@ instance : Monad (Except ε) where
end Except end Except
/--
Adds exceptions of type `ε` to a monad `m`.
-/
def ExceptT (ε : Type u) (m : Type u Type v) (α : Type u) : Type v := def ExceptT (ε : Type u) (m : Type u Type v) (α : Type u) : Type v :=
m (Except ε α) 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] @[always_inline, inline]
def ExceptT.mk {ε : Type u} {m : Type u Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x 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] @[always_inline, inline]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x 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] 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] @[always_inline, inline]
protected def pure {α : Type u} (a : α) : ExceptT ε m α := protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
ExceptT.mk <| pure (Except.ok a) ExceptT.mk <| pure (Except.ok a)
/--
Handles exceptions thrown by an action that can have no effects _other_ than throwing exceptions.
-/
@[always_inline, inline] @[always_inline, inline]
protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β) protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β)
| Except.ok a => f a | Except.ok a => f a
| Except.error e => pure (Except.error e) | 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] @[always_inline, inline]
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α ExceptT ε m β) : ExceptT ε m β := protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α ExceptT ε m β) : ExceptT ε m β :=
ExceptT.mk <| ma >>= ExceptT.bindCont f ExceptT.mk <| ma >>= ExceptT.bindCont f
/--
Transforms a successful computation's value using `f`. Typically used via the `<$>` operator.
-/
@[always_inline, inline] @[always_inline, inline]
protected def map {α β : Type u} (f : α β) (x : ExceptT ε m α) : ExceptT ε m β := protected def map {α β : Type u} (f : α β) (x : ExceptT ε m α) : ExceptT ε m β :=
ExceptT.mk <| x >>= fun a => match a with ExceptT.mk <| x >>= fun a => match a with
| (Except.ok a) => pure <| Except.ok (f a) | (Except.ok a) => pure <| Except.ok (f a)
| (Except.error e) => pure <| Except.error e | (Except.error e) => pure <| Except.error e
/--
Runs a computation from an underlying monad in the transformed monad with exceptions.
-/
@[always_inline, inline] @[always_inline, inline]
protected def lift {α : Type u} (t : m α) : ExceptT ε m α := protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
ExceptT.mk <| Except.ok <$> t 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 (Except ε) (ExceptT ε m) := fun e => ExceptT.mk <| pure e
instance : MonadLift m (ExceptT ε m) := ExceptT.lift instance : MonadLift m (ExceptT ε m) := ExceptT.lift
/--
Handles exceptions produced in the `ExceptT ε` transformer.
-/
@[always_inline, inline] @[always_inline, inline]
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α := protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α :=
ExceptT.mk <| ma >>= fun res => match res with ExceptT.mk <| ma >>= fun res => match res with
@@ -209,11 +124,6 @@ instance : Monad (ExceptT ε m) where
bind := ExceptT.bind bind := ExceptT.bind
map := ExceptT.map map := ExceptT.map
/--
Transforms exceptions using the function `f`.
This is the `ExceptT` version of `Except.mapError`.
-/
@[always_inline, inline] @[always_inline, inline]
protected def adapt {ε' α : Type u} (f : ε ε') : ExceptT ε m α ExceptT ε' m α := fun x => protected def adapt {ε' α : Type u} (f : ε ε') : ExceptT ε m α ExceptT ε' m α := fun x =>
ExceptT.mk <| Except.mapError f <$> x ExceptT.mk <| Except.mapError f <$> x
@@ -240,12 +150,8 @@ instance (ε) : MonadExceptOf ε (Except ε) where
namespace MonadExcept namespace MonadExcept
variable {ε : Type u} {m : Type v Type w} variable {ε : Type u} {m : Type v Type w}
/-- /-- Alternative orelse operator that allows to select which exception should be used.
An alternative unconditional error recovery operator that allows callers to specify which exception The default is to use the first exception since the standard `orelse` uses the second. -/
to throw in cases where both operations throw exceptions.
By default, the first is thrown, because the `<|>` operator throws the second.
-/
@[always_inline, inline] @[always_inline, inline]
def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α := 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₂) 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 liftWith f := liftM <| f fun x => x.run
restoreM x := x 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 class MonadFinally (m : Type u Type v) where
/-- /-- `tryFinally' x f` runs `x` and then the "finally" computation `f`.
Runs an action, ensuring that some other action always happens afterward. 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'`
More specifically, `tryFinally' x f` runs `x` and then the “finally” computation `f`. If `x` can be thought of as performing the same role as a `finally` block in
succeeds with some value `a : α`, `f (some a)` is returned. If `x` fails for `m`'s definition of an imperative programming language. -/
failure, `f none` is returned. tryFinally' {α β} : m α (Option α m β) m (α × β)
`tryFinally'` can be thought of as performing the same role as a `finally` block in an imperative
programming language.
-/
tryFinally' {α β} : (x : m α) (f : Option α m β) m (α × β)
export MonadFinally (tryFinally') export MonadFinally (tryFinally')

View File

@@ -10,37 +10,19 @@ import Init.Control.Lawful.Basic
The Exception monad transformer using CPS style. 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 β def ExceptCpsT (ε : Type u) (m : Type u Type v) (α : Type u) := (β : Type u) (α m β) (ε m β) m β
namespace ExceptCpsT namespace ExceptCpsT
/--
Use a monadic action that may throw an exception as an action that may return an exception's value.
-/
@[always_inline, inline] @[always_inline, inline]
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) := def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e)) x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
set_option linter.unusedVariables false in -- `s` unused 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] @[always_inline, inline]
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α m β) (error : ε m β) : m β := def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α m β) (error : ε m β) : m β :=
x _ ok error 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] @[always_inline, inline]
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α := def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
x α pure pure x α pure pure
@@ -58,9 +40,6 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
throw e := fun _ _ k => k e throw e := fun _ _ k => k e
tryCatch x handle := fun _ k₁ k₂ => x _ k₁ (fun e => handle e _ k₁ k₂) 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] @[always_inline, inline]
def lift [Monad m] (x : m α) : ExceptCpsT ε m α := def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
fun _ k _ => x >>= k fun _ k _ => x >>= k

View File

@@ -10,28 +10,6 @@ import Init.Core
universe u 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 def Id (type : Type u) : Type u := type
namespace Id namespace Id
@@ -42,18 +20,9 @@ instance : Monad Id where
bind x f := f x bind x f := f x
map f x := f x map f x := f x
/--
The identity monad has a `bind` operator.
-/
def hasBind : Bind Id := def hasBind : Bind Id :=
inferInstance 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] @[always_inline, inline]
protected def run (x : Id α) : α := x protected def run (x : Id α) : α := x

View File

@@ -13,26 +13,17 @@ open Function
rfl rfl
/-- /--
A functor satisfies the functor laws. The `Functor` typeclass only contains the operations of a functor.
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
The `Functor` class contains the operations of a functor, but does not require that instances including the preservation of the identity and composition laws:
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`, id <$> x = x
`LawfulFunctor` instances must also prove that the optimized implementation is equivalent to the (h ∘ g) <$> x = h <$> g <$> x
standard implementation. ```
-/ -/
class LawfulFunctor (f : Type u Type v) [Functor f] : Prop where 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 β map_const : (Functor.mapConst : α f β f α) = Functor.map const β
/--
The `map` implementation preserves identity.
-/
id_map (x : f α) : id <$> x = x 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 comp_map (g : α β) (h : β γ) (x : f α) : (h g) <$> x = h <$> g <$> x
export LawfulFunctor (map_const id_map comp_map) export LawfulFunctor (map_const id_map comp_map)
@@ -47,48 +38,21 @@ attribute [simp] id_map
(comp_map _ _ _).symm (comp_map _ _ _).symm
/-- /--
An applicative functor satisfies the laws of an applicative functor. The `Applicative` typeclass only contains the operations of an applicative functor.
`LawfulApplicative` further asserts that these operations satisfy 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 pure id <*> v = v
includes proofs that the laws are satisfied. pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (f x)
Because `Applicative` instances may provide optimized implementations of `seqLeft` and `seqRight`, u <*> pure y = pure (· y) <*> u
`LawfulApplicative` instances must also prove that the optimized implementation is equivalent to the ```
standard implementation.
-/ -/
class LawfulApplicative (f : Type u Type v) [Applicative f] : Prop extends LawfulFunctor f where 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 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 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 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) 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_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 seq_assoc {α β γ : Type u} (x : f α) (g : f (α β)) (h : f (β γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
comp_map g h x := (by comp_map g h x := (by
repeat rw [ pure_seq] repeat rw [ pure_seq]
@@ -102,36 +66,21 @@ attribute [simp] map_pure seq_pure
simp [pure_seq] simp [pure_seq]
/-- /--
Lawful monads are those that satisfy a certain behavioral specification. While all instances of The `Monad` typeclass only contains the operations of a monad.
`Monad` should satisfy these laws, not all implementations are required to prove this. `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 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 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 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 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 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]) 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]) 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.Basic
import Init.Control.Except import Init.Control.Except
set_option linter.missingDocs true
universe u v universe u v
instance : ToBool (Option α) := Option.isSome 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 := def OptionT (m : Type u Type v) (α : Type u) : Type v :=
m (Option α) m (Option α)
/--
Executes an action that might fail in the underlying monad `m`, returning `none` in case of failure.
-/
@[always_inline, inline] @[always_inline, inline]
def OptionT.run {m : Type u Type v} {α : Type u} (x : OptionT m α) : m (Option α) := def OptionT.run {m : Type u Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
x x
@@ -31,25 +22,15 @@ def OptionT.run {m : Type u → Type v} {α : Type u} (x : OptionT m α) : m (Op
namespace OptionT namespace OptionT
variable {m : Type u Type v} [Monad m] {α β : Type u} 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 α := protected def mk (x : m (Option α)) : OptionT m α :=
x x
/--
Sequences two potentially-failing actions. The second action is run only if the first succeeds.
-/
@[always_inline, inline] @[always_inline, inline]
protected def bind (x : OptionT m α) (f : α OptionT m β) : OptionT m β := OptionT.mk do protected def bind (x : OptionT m α) (f : α OptionT m β) : OptionT m β := OptionT.mk do
match ( x) with match ( x) with
| some a => f a | some a => f a
| none => pure none | none => pure none
/--
Succeeds with the provided value.
-/
@[always_inline, inline] @[always_inline, inline]
protected def pure (a : α) : OptionT m α := OptionT.mk do protected def pure (a : α) : OptionT m α := OptionT.mk do
pure (some a) pure (some a)
@@ -59,17 +40,11 @@ instance : Monad (OptionT m) where
pure := OptionT.pure pure := OptionT.pure
bind := OptionT.bind 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 @[always_inline, inline] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
match ( x) with match ( x) with
| some a => pure (some a) | some a => pure (some a)
| _ => y () | _ => y ()
/--
A recoverable failure.
-/
@[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do @[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do
pure none pure none
@@ -77,12 +52,6 @@ instance : Alternative (OptionT m) where
failure := OptionT.fail failure := OptionT.fail
orElse := OptionT.orElse 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 @[always_inline, inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
return some ( x) return some ( x)
@@ -90,9 +59,6 @@ instance : MonadLift m (OptionT m) := ⟨OptionT.lift⟩
instance : MonadFunctor m (OptionT m) := fun f x => f x 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 @[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle () let some a x | handle ()
pure a pure a

View File

@@ -10,21 +10,12 @@ import Init.Control.Basic
import Init.Control.Id import Init.Control.Id
import Init.Control.Except import Init.Control.Except
set_option linter.missingDocs true
namespace ReaderT namespace ReaderT
/--
Recovers from errors. The same local value is provided to both branches. Typically used via the
`<|>` operator.
-/
@[always_inline, inline] @[always_inline, inline]
protected def orElse [Alternative m] (x₁ : ReaderT ρ m α) (x₂ : Unit ReaderT ρ m α) : ReaderT ρ m α := protected def orElse [Alternative m] (x₁ : ReaderT ρ m α) (x₂ : Unit ReaderT ρ m α) : ReaderT ρ m α :=
fun s => x₁ s <|> x₂ () s fun s => x₁ s <|> x₂ () s
/--
Fails with a recoverable error.
-/
@[always_inline, inline] @[always_inline, inline]
protected def failure [Alternative m] : ReaderT ρ m α := protected def failure [Alternative m] : ReaderT ρ m α :=
fun _ => failure fun _ => failure
@@ -44,8 +35,4 @@ instance : MonadControl m (ReaderT ρ m) where
instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx) 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 @[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id

View File

@@ -9,42 +9,19 @@ prelude
import Init.Control.Basic import Init.Control.Basic
import Init.Control.Id import Init.Control.Id
import Init.Control.Except import Init.Control.Except
set_option linter.missingDocs true
universe u v w 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) := def StateT (σ : Type u) (m : Type u Type v) (α : Type u) : Type (max u v) :=
σ m (α × σ) σ 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] @[always_inline, inline]
def StateT.run {σ : Type u} {m : Type u Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) := def StateT.run {σ : Type u} {m : Type u Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
x s 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] @[always_inline, inline]
def StateT.run' {σ : Type u} {m : Type u Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α := def StateT.run' {σ : Type u} {m : Type u Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
(·.1) <$> x s (·.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] @[reducible]
def StateM (σ α : Type u) : Type u := StateT σ Id α def StateM (σ α : Type u) : Type u := StateT σ Id α
@@ -61,23 +38,14 @@ section
variable {σ : Type u} {m : Type u Type v} variable {σ : Type u} {m : Type u Type v}
variable [Monad m] {α β : Type u} variable [Monad m] {α β : Type u}
/--
Returns the given value without modifying the state. Typically used via `Pure.pure`.
-/
@[always_inline, inline] @[always_inline, inline]
protected def pure (a : α) : StateT σ m α := protected def pure (a : α) : StateT σ m α :=
fun s => pure (a, s) fun s => pure (a, s)
/--
Sequences two actions. Typically used via the `>>=` operator.
-/
@[always_inline, inline] @[always_inline, inline]
protected def bind (x : StateT σ m α) (f : α StateT σ m β) : StateT σ m β := protected def bind (x : StateT σ m α) (f : α StateT σ m β) : StateT σ m β :=
fun s => do let (a, s) x s; f a s 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] @[always_inline, inline]
protected def map (f : α β) (x : StateT σ m α) : StateT σ m β := protected def map (f : α β) (x : StateT σ m α) : StateT σ m β :=
fun s => do let (a, s) x s; pure (f a, s) fun s => do let (a, s) x s; pure (f a, s)
@@ -88,17 +56,10 @@ instance : Monad (StateT σ m) where
bind := StateT.bind bind := StateT.bind
map := StateT.map map := StateT.map
/--
Recovers from errors. The state is rolled back on error recovery. Typically used via the `<|>`
operator.
-/
@[always_inline, inline] @[always_inline, inline]
protected def orElse [Alternative m] {α : Type u} (x₁ : StateT σ m α) (x₂ : Unit StateT σ m α) : StateT σ m α := protected def orElse [Alternative m] {α : Type u} (x₁ : StateT σ m α) (x₂ : Unit StateT σ m α) : StateT σ m α :=
fun s => x₁ s <|> x₂ () s fun s => x₁ s <|> x₂ () s
/--
Fails with a recoverable error. The state is rolled back on error recovery.
-/
@[always_inline, inline] @[always_inline, inline]
protected def failure [Alternative m] {α : Type u} : StateT σ m α := protected def failure [Alternative m] {α : Type u} : StateT σ m α :=
fun _ => failure fun _ => failure
@@ -107,40 +68,18 @@ instance [Alternative m] : Alternative (StateT σ m) where
failure := StateT.failure failure := StateT.failure
orElse := StateT.orElse 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] @[always_inline, inline]
protected def get : StateT σ m σ := protected def get : StateT σ m σ :=
fun s => pure (s, s) fun s => pure (s, s)
/--
Replaces the mutable state with a new value.
-/
@[always_inline, inline] @[always_inline, inline]
protected def set : σ StateT σ m PUnit := protected def set : σ StateT σ m PUnit :=
fun s' _ => pure (, s') 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] @[always_inline, inline]
protected def modifyGet (f : σ α × σ) : StateT σ m α := protected def modifyGet (f : σ α × σ) : StateT σ m α :=
fun s => pure (f s) 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] @[always_inline, inline]
protected def lift {α : Type u} (t : m α) : StateT σ m α := protected def lift {α : Type u} (t : m α) : StateT σ m α :=
fun s => do let a t; pure (a, s) fun s => do let a t; pure (a, s)

View File

@@ -6,45 +6,24 @@ Authors: Leonardo de Moura
prelude prelude
import Init.Control.Lawful.Basic import Init.Control.Lawful.Basic
set_option linter.missingDocs true
/-! /-!
The State monad transformer using CPS style. 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 δ def StateCpsT (σ : Type u) (m : Type u Type v) (α : Type u) := (δ : Type u) σ (α σ m δ) m δ
namespace StateCpsT namespace StateCpsT
variable {α σ : Type u} {m : Type u Type v} 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] @[always_inline, inline]
def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β := def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k 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] @[always_inline, inline]
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) := def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s)) 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] @[always_inline, inline]
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α := def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a) runK x s (fun a _ => pure a)
@@ -64,12 +43,6 @@ instance : MonadStateOf σ (StateCpsT σ m) where
set s := fun _ _ k => k s set s := fun _ _ k => k s
modifyGet f := fun _ s k => let (a, s) := f s; k a 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] @[always_inline, inline]
protected def lift [Monad m] (x : m α) : StateCpsT σ m α := protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
fun _ s k => x >>= (k . s) fun _ s k => x >>= (k . s)

View File

@@ -8,23 +8,10 @@ The State monad transformer using IO references.
prelude prelude
import Init.System.ST 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 α def StateRefT' (ω : Type) (σ : Type) (m : Type Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
/-! Recall that `StateRefT` is a macro that infers `ω` from the `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] @[always_inline, inline]
def StateRefT'.run {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m (α × σ) := do def StateRefT'.run {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m (α × σ) := do
let ref ST.mkRef s let ref ST.mkRef s
@@ -32,12 +19,6 @@ def StateRefT'.run {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
let s ref.get let s ref.get
pure (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.
The monad `m` must support `ST` effects in order to create and mutate reference cells.
-/
@[always_inline, inline] @[always_inline, inline]
def StateRefT'.run' {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m α := do def StateRefT'.run' {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m α := do
let (a, _) x.run s let (a, _) x.run s
@@ -46,12 +27,6 @@ def StateRefT'.run' {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
namespace StateRefT' namespace StateRefT'
variable {ω σ : Type} {m : Type Type} {α : Type} 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] @[always_inline, inline]
protected def lift (x : m α) : StateRefT' ω σ m α := protected def lift (x : m α) : StateRefT' ω σ m α :=
fun _ => x fun _ => x
@@ -61,30 +36,14 @@ instance : MonadLift m (StateRefT' ω σ m) := ⟨StateRefT'.lift⟩
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _)) instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (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] @[inline]
protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ := protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
fun ref => ref.get fun ref => ref.get
/--
Replaces the mutable state with a new value.
-/
@[inline] @[inline]
protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit := protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
fun ref => ref.set s 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] @[inline]
protected def modifyGet [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α := protected def modifyGet [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
fun ref => ref.modifyGet f fun ref => ref.modifyGet f

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 namespace Array
/-- /--
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over `O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
an array `xs : Array α`, given a proof that every element of `xs` in fact satisfies `P`. `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 β := 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 (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 @[inline] private unsafe def attachWithImpl
(xs : Array α) (P : α Prop) (_ : x xs, P x) : Array {x // P x} := unsafeCast xs (xs : Array α) (P : α Prop) (_ : x xs, P x) : Array {x // P x} := unsafeCast xs
/-- /-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
“Attaches” individual proofs to an array of values that satisfy a predicate `P`, returning an array with the same elements but in the type `{x // P x}`. -/
of elements in the corresponding subtype `{ x // P x }`.
`O(1)`.
-/
@[implemented_by attachWithImpl] def attachWith @[implemented_by attachWithImpl] def attachWith
(xs : Array α) (P : α Prop) (H : x xs, P x) : Array {x // P x} := (xs : Array α) (P : α Prop) (H : x xs, P x) : Array {x // P x} :=
xs.toList.attachWith P fun x h => H x (Array.Mem.mk h) xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)
/-- /-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
“Attaches” the proof that the elements of `xs` are in fact elements of `xs`, producing a new array with with the same elements but in the type `{x // x ∈ xs}`. -/
the same elements but in the subtype `{ x // x ∈ xs }`.
`O(1)`.
This function is primarily used to allow definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
`Array.map`) to prove that an value taken from a list is smaller than the list. This allows the
well-founded recursion mechanism to prove that the function terminates.
-/
@[inline] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id @[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} : @[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α Prop} {H : x l.toArray, P x} :
@@ -554,25 +542,15 @@ 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 A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
satisfy the predicate. 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)`. If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
This function is usually inserted automatically by Lean as an intermediate step while proving
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
the elaboration of definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
state, the right approach is usually the tactic `simp [Array.unattach, -Array.map_subtype]`.
-/ -/
def unattach {α : Type _} {p : α Prop} (xs : Array { x // p x }) : Array α := xs.map (·.val) 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] theorem unattach_nil {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
simp [unattach]
@[simp] theorem unattach_push {p : α Prop} {a : { x // p x }} {xs : Array { x // p x }} : @[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 (xs.push a).unattach = xs.unattach.push a.1 := by
simp only [unattach, Array.map_push] simp only [unattach, Array.map_push]
@@ -743,13 +721,10 @@ 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] List.map_toArray, List.map_flatten, map_subtype, map_id_fun', List.unattach_toArray, mk.injEq]
simp only [List.unattach] simp only [List.unattach]
@[simp] theorem unattach_replicate {p : α Prop} {n : Nat} {x : { x // p x }} : @[simp] theorem unattach_mkArray {p : α Prop} {n : Nat} {x : { x // p x }} :
(Array.replicate n x).unattach = Array.replicate n x.1 := by (Array.mkArray n x).unattach = Array.mkArray n x.1 := by
simp [unattach] simp [unattach]
@[deprecated unattach_replicate (since := "2025-03-18")]
abbrev unattach_mkArray := @unattach_replicate
/-! ### Well-founded recursion preprocessing setup -/ /-! ### Well-founded recursion preprocessing setup -/
@[wf_preprocess] theorem Array.map_wfParam (xs : Array α) (f : α β) : @[wf_preprocess] theorem Array.map_wfParam (xs : Array α) (f : α β) :

File diff suppressed because it is too large Load Diff

View File

@@ -38,11 +38,6 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by
simp 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 } := def Array.mapM' [Monad m] (f : α m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
go 0 mkEmpty as.size, rfl (by simp) go 0 mkEmpty as.size, rfl (by simp)
where where
@@ -71,19 +66,11 @@ where
return as return as
/-- /--
Applies a monadic function to each element of an array, returning the array of results. The function is Monomorphic `Array.mapM`. The internal implementation uses pointer equality, and does not allocate a new array
monomorphic: it is required to return a value of the same type. The internal implementation uses if the result of each `f a` is a pointer equal value `a`.
pointer equality, and does not allocate a new array if the result of each function call is
pointer-equal to its argument.
-/ -/
@[implemented_by mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α m α) : m (Array α) := @[implemented_by mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α m α) : m (Array α) :=
as.mapM f 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 α := @[inline] def Array.mapMono (as : Array α) (f : α α) : Array α :=
Id.run <| as.mapMonoM f Id.run <| as.mapMonoM f

View File

@@ -29,16 +29,6 @@ namespace Array
else found (some a) else found (some a)
termination_by lo hi => hi.1 - lo.1 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 α := @[inline] def binSearch {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Option α :=
if h : lo < as.size then if h : lo < as.size then
let hi := if hi < as.size then hi else as.size - 1 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 else
none 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 := @[inline] def binSearchContains {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Bool :=
if h : lo < as.size then if h : lo < as.size then
let hi := if hi < as.size then hi else as.size - 1 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 as.modifyM mid <| fun v => merge v
termination_by lo hi => hi.1 - lo.1 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] @[specialize] def binInsertM {α : Type u} {m : Type u Type v} [Monad m]
(lt : α α Bool) (lt : α α Bool)
(merge : α m α) (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 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') 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 α := @[inline] def binInsert {α : Type u} (lt : α α Bool) (as : Array α) (k : α) : Array α :=
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k

View File

@@ -23,18 +23,6 @@ section countP
variable (p q : α Bool) 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
@[simp] theorem countP_empty : countP p #[] = 0 := rfl @[simp] theorem countP_empty : countP p #[] = 0 := rfl
@[simp] theorem countP_push_of_pos (xs) (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
@@ -88,13 +76,10 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
rcases xs with xs rcases xs with xs
simp simp
theorem countP_replicate (p : α Bool) (a : α) (n : Nat) : theorem countP_mkArray (p : α Bool) (a : α) (n : Nat) :
countP p (replicate n a) = if p a then n else 0 := by countP p (mkArray n a) = if p a then n else 0 := by
simp [ List.toArray_replicate, List.countP_replicate] simp [ List.toArray_replicate, List.countP_replicate]
@[deprecated countP_replicate (since := "2025-03-18")]
abbrev countP_mkArray := @countP_replicate
theorem boole_getElem_le_countP (p : α Bool) (xs : Array α) (i : Nat) (h : i < xs.size) : 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 (if p xs[i] then 1 else 0) xs.countP p := by
rcases xs with xs rcases xs with xs
@@ -165,13 +150,6 @@ section count
variable [BEq α] 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_toList (xs : Array α) (a : α) : xs.toList.count a = xs.count a := by
cases xs
simp
@[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl @[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl
theorem count_push (a b : α) (xs : Array α) : theorem count_push (a b : α) (xs : Array α) :
@@ -244,34 +222,25 @@ theorem count_eq_size {xs : Array α} : count a xs = xs.size ↔ ∀ b ∈ xs, a
· simpa using h b hb · simpa using h b hb
· rw [h b hb, beq_self_eq_true] · 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] simp [ List.toArray_replicate]
@[deprecated count_replicate_self (since := "2025-03-18")] theorem count_mkArray (a b : α) (n : Nat) : count a (mkArray n b) = if b == a then n else 0 := by
abbrev count_mkArray_self := @count_replicate_self
theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b == a then n else 0 := by
simp [ List.toArray_replicate, List.count_replicate] simp [ List.toArray_replicate, List.count_replicate]
@[deprecated count_replicate (since := "2025-03-18")] theorem filter_beq (xs : Array α) (a : α) : xs.filter (· == a) = mkArray (count a xs) a := by
abbrev count_mkArray := @count_replicate
theorem filter_beq (xs : Array α) (a : α) : xs.filter (· == a) = replicate (count a xs) a := by
rcases xs with xs rcases xs with xs
simp [List.filter_beq] simp [List.filter_beq]
theorem filter_eq {α} [DecidableEq α] (xs : Array α) (a : α) : xs.filter (· = a) = replicate (count a xs) a := theorem filter_eq {α} [DecidableEq α] (xs : Array α) (a : α) : xs.filter (· = a) = mkArray (count a xs) a :=
filter_beq xs a filter_beq xs a
theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) : theorem mkArray_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
replicate (count a xs) a = xs := by mkArray (count a xs) a = xs := by
rcases xs with xs rcases xs with xs
rw [ toList_inj] rw [ toList_inj]
simp [List.replicate_count_eq_of_count_eq_length (by simpa using h)] 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 @[simp] theorem count_filter {xs : Array α} (h : p a) : count a (filter p xs) = count a xs := by
rcases xs with xs rcases xs with xs
simp [List.count_filter, h] simp [List.count_filter, h]

View File

@@ -21,7 +21,7 @@ open Nat
/-! ### eraseP -/ /-! ### 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 theorem eraseP_of_forall_mem_not {xs : Array α} (h : a, a xs ¬p a) : xs.eraseP p = xs := by
rcases xs with xs rcases xs with xs
@@ -122,30 +122,21 @@ theorem eraseP_append {xs : Array α} {ys : Array α} :
simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray] simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray]
split <;> simp split <;> simp
theorem eraseP_replicate (n : Nat) (a : α) (p : α Bool) : theorem eraseP_mkArray (n : Nat) (a : α) (p : α Bool) :
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by (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] simp only [ List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
split <;> simp split <;> simp
@[deprecated eraseP_replicate (since := "2025-03-18")] @[simp] theorem eraseP_mkArray_of_pos {n : Nat} {a : α} (h : p a) :
abbrev eraseP_mkArray := @eraseP_replicate (mkArray n a).eraseP p = mkArray (n - 1) a := by
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
(replicate n a).eraseP p = replicate (n - 1) a := by
simp only [ List.toArray_replicate, List.eraseP_toArray] simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h] simp [h]
@[deprecated eraseP_replicate_of_pos (since := "2025-03-18")] @[simp] theorem eraseP_mkArray_of_neg {n : Nat} {a : α} (h : ¬p a) :
abbrev eraseP_mkArray_of_pos := @eraseP_replicate_of_pos (mkArray n a).eraseP p = mkArray n a := by
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
(replicate n a).eraseP p = replicate n a := by
simp only [ List.toArray_replicate, List.eraseP_toArray] simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h] 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 α} : theorem eraseP_eq_iff {p} {xs : Array α} :
xs.eraseP p = ys xs.eraseP p = ys
(( a xs, ¬ p a) xs = ys) (( a xs, ¬ p a) xs = ys)
@@ -252,15 +243,12 @@ theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray] simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
split <;> simp split <;> simp
theorem erase_replicate [LawfulBEq α] (n : Nat) (a b : α) : theorem erase_mkArray [LawfulBEq α] (n : Nat) (a b : α) :
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by (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.toArray_replicate, List.erase_toArray]
simp only [List.erase_replicate, beq_iff_eq, List.toArray_replicate] simp only [List.erase_replicate, beq_iff_eq, List.toArray_replicate]
split <;> simp split <;> simp
@[deprecated erase_replicate (since := "2025-03-18")]
abbrev erase_mkArray := @erase_replicate
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 (xs.erase a).erase b = (xs.erase b).erase a := by
rcases xs with xs rcases xs with xs
@@ -280,30 +268,20 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
· left; simp_all · left; simp_all
· right; refine a, as, h, rfl, bs, by simp · right; refine a, as, h, rfl, bs, by simp
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} : @[simp] theorem erase_mkArray_self [LawfulBEq α] {a : α} :
(replicate n a).erase a = replicate (n - 1) a := by (mkArray n a).erase a = mkArray (n - 1) a := by
simp only [ List.toArray_replicate, List.erase_toArray] simp only [ List.toArray_replicate, List.erase_toArray]
simp [List.erase_replicate] simp [List.erase_replicate]
@[deprecated erase_replicate_self (since := "2025-03-18")] @[simp] theorem erase_mkArray_ne [LawfulBEq α] {a b : α} (h : !b == a) :
abbrev erase_mkArray_self := @erase_replicate_self (mkArray n a).erase b = mkArray n a := by
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(replicate n a).erase b = replicate n a := by
rw [erase_of_not_mem] rw [erase_of_not_mem]
simp_all simp_all
@[deprecated erase_replicate_ne (since := "2025-03-18")]
abbrev erase_mkArray_ne := @erase_replicate_ne
end erase end erase
/-! ### eraseIdx -/ /-! ### 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 = 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 rcases xs with xs
simp only [List.size_toArray] at h simp only [List.size_toArray] at h
@@ -371,15 +349,12 @@ theorem eraseIdx_append_of_length_le {xs : Array α} {k : Nat} (hk : xs.size ≤
simp at hk simp at hk
simp [List.eraseIdx_append_of_length_le, *] simp [List.eraseIdx_append_of_length_le, *]
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} : theorem eraseIdx_mkArray {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by (mkArray n a).eraseIdx k = mkArray (n - 1) a := by
simp at h simp at h
simp only [ List.toArray_replicate, List.eraseIdx_toArray] simp only [ List.toArray_replicate, List.eraseIdx_toArray]
simp [List.eraseIdx_replicate, h] 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 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 rcases xs with xs
simp [List.mem_eraseIdx_iff_getElem, *] simp [List.mem_eraseIdx_iff_getElem, *]

View File

@@ -249,15 +249,12 @@ theorem extract_append_left {as bs : Array α} :
· simp only [size_map, size_extract] at h₁ h₂ · simp only [size_map, size_extract] at h₁ h₂
simp only [getElem_map, getElem_extract] simp only [getElem_map, getElem_extract]
@[simp] theorem extract_replicate {a : α} {n i j : Nat} : @[simp] theorem extract_mkArray {a : α} {n i j : Nat} :
(replicate n a).extract i j = replicate (min j n - i) a := by (mkArray n a).extract i j = mkArray (min j n - i) a := by
ext l h₁ h₂ ext l h₁ h₂
· simp · simp
· simp only [size_extract, size_replicate] at h₁ h₂ · simp only [size_extract, size_mkArray] at h₁ h₂
simp only [getElem_extract, getElem_replicate] simp only [getElem_extract, getElem_mkArray]
@[deprecated extract_replicate (since := "2025-03-18")]
abbrev extract_mkArray := @extract_replicate
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} : 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 as.extract i j = as.extract i j' min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
@@ -390,36 +387,24 @@ theorem popWhile_append {xs ys : Array α} :
rw [List.dropWhile_append_of_pos] rw [List.dropWhile_append_of_pos]
simpa simpa
@[simp] theorem takeWhile_replicate_eq_filter (p : α Bool) : @[simp] theorem takeWhile_mkArray_eq_filter (p : α Bool) :
(replicate n a).takeWhile p = (replicate n a).filter p := by (mkArray n a).takeWhile p = (mkArray n a).filter p := by
simp [ List.toArray_replicate] simp [ List.toArray_replicate]
@[deprecated takeWhile_replicate_eq_filter (since := "2025-03-18")] theorem takeWhile_mkArray (p : α Bool) :
abbrev takeWhile_mkArray_eq_filter := @takeWhile_replicate_eq_filter (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) : @[simp] theorem popWhile_mkArray_eq_filter_not (p : α Bool) :
(replicate n a).takeWhile p = if p a then replicate n a else #[] := by (mkArray n a).popWhile p = (mkArray n a).filter (fun a => !p a) := by
simp [takeWhile_replicate_eq_filter, filter_replicate]
@[deprecated takeWhile_replicate (since := "2025-03-18")]
abbrev takeWhile_mkArray := @takeWhile_replicate
@[simp] theorem popWhile_replicate_eq_filter_not (p : α Bool) :
(replicate n a).popWhile p = (replicate n a).filter (fun a => !p a) := by
simp [ List.toArray_replicate, List.filter_reverse] simp [ List.toArray_replicate, List.filter_reverse]
@[deprecated popWhile_replicate_eq_filter_not (since := "2025-03-18")] theorem popWhile_mkArray (p : α Bool) :
abbrev popWhile_mkArray_eq_filter_not := @popWhile_replicate_eq_filter_not (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,
theorem popWhile_replicate (p : α Bool) :
(replicate n a).popWhile p = if p a then #[] else replicate n a := by
simp only [popWhile_replicate_eq_filter_not, size_replicate, filter_replicate, Bool.not_eq_eq_eq_not,
Bool.not_true] Bool.not_true]
split <;> simp_all split <;> simp_all
@[deprecated popWhile_replicate (since := "2025-03-18")]
abbrev popWhile_mkArray := @popWhile_replicate
theorem extract_takeWhile {as : Array α} {i : Nat} : theorem extract_takeWhile {as : Array α} {i : Nat} :
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by (as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
rcases as with as rcases as with as

View File

@@ -12,13 +12,7 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array namespace Array
/-- /-- `finRange n` is the array of all elements of `Fin n` in order. -/
Returns an array of all elements of `Fin n` in order, starting at `0`.
Examples:
* `Array.finRange 0 = (#[] : Array (Fin 0))`
* `Array.finRange 2 = (#[0, 1] : Array (Fin 2))`
-/
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i 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

View File

@@ -99,33 +99,21 @@ theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
simp [getElem?_eq_getElem, h] at t simp [getElem?_eq_getElem, h] at t
simp [ 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] simp [ List.toArray_replicate, List.findSome?_replicate]
@[deprecated findSome?_replicate (since := "2025-03-18")] @[simp] theorem findSome?_mkArray_of_pos (h : 0 < n) : findSome? f (mkArray n a) = f a := by
abbrev findSome?_mkArray := @findSome?_replicate simp [findSome?_mkArray, Nat.ne_of_gt h]
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
simp [findSome?_replicate, Nat.ne_of_gt h]
@[deprecated findSome?_replicate_of_pos (since := "2025-03-18")]
abbrev findSome?_mkArray_of_pos := @findSome?_replicate_of_pos
-- Argument is unused, but used to decide whether `simp` should unfold. -- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) : @[simp] theorem findSome?_mkArray_of_isSome (_ : (f a).isSome) :
findSome? f (replicate n a) = if n = 0 then none else f a := by findSome? f (mkArray n a) = if n = 0 then none else f a := by
simp [findSome?_replicate] simp [findSome?_mkArray]
@[deprecated findSome?_replicate_of_isSome (since := "2025-03-18")] @[simp] theorem findSome?_mkArray_of_isNone (h : (f a).isNone) :
abbrev findSome?_mkArray_of_isSome := @findSome?_replicate_of_isSome findSome? f (mkArray n a) = none := by
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) :
findSome? f (replicate n a) = none := by
rw [Option.isNone_iff_eq_none] at h rw [Option.isNone_iff_eq_none] at h
simp [findSome?_replicate, h] simp [findSome?_mkArray, h]
@[deprecated findSome?_replicate_of_isNone (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/ /-! ### find? -/
@@ -266,58 +254,40 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")] @[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
theorem find?_replicate : theorem find?_mkArray :
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by 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] simp [ List.toArray_replicate, List.find?_replicate]
@[deprecated find?_replicate (since := "2025-03-18")] @[simp] theorem find?_mkArray_of_length_pos (h : 0 < n) :
abbrev find?_mkArray := @find?_replicate 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) : @[simp] theorem find?_mkArray_of_pos (h : p a) :
find? p (replicate n a) = if p a then some a else none := by find? p (mkArray n a) = if n = 0 then none else some a := by
simp [find?_replicate, Nat.ne_of_gt h] simp [find?_mkArray, h]
@[deprecated find?_replicate_of_size_pos (since := "2025-03-18")] @[simp] theorem find?_mkArray_of_neg (h : ¬ p a) : find? p (mkArray n a) = none := by
abbrev find?_mkArray_of_length_pos := @find?_replicate_of_size_pos simp [find?_mkArray, h]
@[simp] theorem find?_replicate_of_pos (h : p a) :
find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[deprecated find?_replicate_of_pos (since := "2025-03-18")]
abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
@[deprecated find?_replicate_of_neg (since := "2025-03-18")]
abbrev find?_mkArray_of_neg := @find?_replicate_of_neg
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`. -- 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} : theorem find?_mkArray_eq_none_iff {n : Nat} {a : α} {p : α Bool} :
(replicate n a).find? p = none n = 0 !p a := by (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] 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")] @[deprecated find?_mkArray_eq_none_iff (since := "2025-02-03")]
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff abbrev find?_mkArray_eq_none := @find?_mkArray_eq_none_iff
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α Bool} : @[simp] theorem find?_mkArray_eq_some_iff {n : Nat} {a b : α} {p : α Bool} :
(replicate n a).find? p = some b n 0 p a a = b := by (mkArray n a).find? p = some b n 0 p a a = b := by
simp [ List.toArray_replicate] simp [ List.toArray_replicate]
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")] @[deprecated find?_mkArray_eq_some_iff (since := "2025-02-03")]
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff abbrev find?_mkArray_eq_some := @find?_mkArray_eq_some_iff
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")] @[simp] theorem get_find?_mkArray (n : Nat) (a : α) (p : α Bool) (h) :
abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff ((mkArray n a).find? p).get h = a := by
@[simp] theorem get_find?_replicate (n : Nat) (a : α) (p : α Bool) (h) :
((replicate n a).find? p).get h = a := by
simp [ List.toArray_replicate] 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 α) theorem find?_pmap {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs P a) (p : β Bool) : (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 (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
@@ -438,7 +408,7 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
/-! ### findIdx? -/ /-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp @[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := rfl
@[simp] @[simp]
theorem findIdx?_eq_none_iff {xs : Array α} {p : α Bool} : theorem findIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
@@ -511,15 +481,12 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
cases xss using array₂_induction cases xss using array₂_induction
simp [List.findIdx?_flatten, Function.comp_def] simp [List.findIdx?_flatten, Function.comp_def]
@[simp] theorem findIdx?_replicate : @[simp] theorem findIdx?_mkArray :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by (mkArray n a).findIdx? p = if 0 < n p a then some 0 else none := by
rw [ List.toArray_replicate] rw [ List.toArray_replicate]
simp only [List.findIdx?_toArray] simp only [List.findIdx?_toArray]
simp simp
@[deprecated findIdx?_replicate (since := "2025-03-18")]
abbrev findIdx?_mkArray := @findIdx?_replicate
theorem findIdx?_eq_findSome?_zipIdx {xs : Array α} {p : α Bool} : 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 xs.findIdx? p = xs.zipIdx.findSome? fun a, i => if p a then some i else none := by
rcases xs with xs rcases xs with xs
@@ -559,7 +526,7 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
/-! ### findFinIdx? -/ /-! ### 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?`. -- 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) : theorem findFinIdx?_congr {p : α Bool} {xs ys : Array α} (w : xs = ys) :
@@ -628,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). 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 : α} : @[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf? a = none a xs := by xs.idxOf? a = none a xs := by
@@ -645,7 +612,7 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val] 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 : α} : @[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.finIdxOf? a = none a xs := by xs.finIdxOf? a = none a xs := by

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.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index 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 α := @[inline] def Array.insertionSort (xs : Array α) (lt : α α Bool := by exact (· < ·)) : Array α :=
traverse xs 0 xs.size traverse xs 0 xs.size
where 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 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 `lex as bs lt` is true if
* `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`, - `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`, or
or - there is an index `i` such that `lt as[i] bs[i]`, and for all `j < i`, `as[j] == bs[j]`.
* 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 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 for h : i in [0 : min as.size bs.size] do

View File

@@ -6,7 +6,6 @@ Authors: Mario Carneiro, Kim Morrison
prelude prelude
import Init.Data.Array.Lemmas import Init.Data.Array.Lemmas
import Init.Data.Array.Attach import Init.Data.Array.Attach
import Init.Data.Array.OfFn
import Init.Data.List.MapIdx import Init.Data.List.MapIdx
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables. set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
@@ -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 (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] simp [mapFinIdx_eq_iff]
theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {b : β} : theorem mapFinIdx_eq_mkArray_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 xs.mapFinIdx f = mkArray xs.size b (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
rcases xs with l rcases xs with l
rw [ toList_inj] rw [ toList_inj]
simp [List.mapFinIdx_eq_replicate_iff] 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) β} : @[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 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 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 (xs.mapIdx f).mapIdx g = xs.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff] simp [mapIdx_eq_iff]
theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat α β} {b : β} : theorem mapIdx_eq_mkArray_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 mapIdx f xs = mkArray xs.size b (i : Nat) (h : i < xs.size), f i xs[i] = b := by
rcases xs with xs rcases xs with xs
rw [ toList_inj] rw [ toList_inj]
simp [List.mapIdx_eq_replicate_iff] 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 α β} : @[simp] theorem mapIdx_reverse {xs : Array α} {f : Nat α β} :
xs.reverse.mapIdx f = (mapIdx (fun i => f (xs.size - 1 - i)) xs).reverse := by xs.reverse.mapIdx f = (mapIdx (fun i => f (xs.size - 1 - i)) xs).reverse := by
rcases xs with xs rcases xs with xs

View File

@@ -23,13 +23,6 @@ open Nat
/-! ### mapM -/ /-! ### 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 (xs ++ ys).mapM f = (return ( xs.mapM f) ++ ( ys.mapM f)) := by
rcases xs with xs rcases xs with xs
@@ -169,7 +162,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
rcases xs with xs rcases xs with xs
simp [List.foldlM_map] simp [List.foldlM_map]
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(xs : Array α) (f : (a : α) a xs β β) (init : β) : (xs : Array α) (f : (a : α) a xs β β) (init : β) :
forIn' xs init (fun a m b => pure (.yield (f a m b))) = 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 pure (f := m) (xs.attach.foldl (fun b a, h => f a h b) init) := by
@@ -211,7 +204,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
rcases xs with xs rcases xs with xs
simp [List.foldlM_map] simp [List.foldlM_map]
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(xs : Array α) (f : α β β) (init : β) : (xs : Array α) (f : α β β) (init : β) :
forIn xs init (fun a b => pure (.yield (f a b))) = forIn xs init (fun a b => pure (.yield (f a b))) =
pure (f := m) (xs.foldl (fun b a => f a b) init) := by pure (f := m) (xs.foldl (fun b a => f a b) init) := by
@@ -231,32 +224,6 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
rcases xs with xs rcases xs with xs
simp 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 end Array
namespace List namespace List
@@ -387,12 +354,12 @@ and simplifies these to the function directly taking the value.
simp simp
rw [List.foldlM_subtype hf] rw [List.foldlM_subtype hf]
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β α m β) (init : β) : @[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β α m β) :
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by (wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
simp [wfParam] simp [wfParam]
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : Array (Subtype P)) (f : β α m β) (init : β) : @[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : Array (Subtype P)) (f : β α m β) :
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b x, h => xs.unattach.foldlM f = xs.foldlM fun b x, h =>
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <| binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
f b (wfParam x) := by f b (wfParam x) := by
simp [wfParam] simp [wfParam]
@@ -411,12 +378,12 @@ and simplifies these to the function directly taking the value.
rw [List.foldrM_subtype hf] rw [List.foldrM_subtype hf]
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α β m β) (init : β) : @[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α β m β) :
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by (wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
simp [wfParam] simp [wfParam]
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α β m β) (init : β): @[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α β m β) :
xs.unattach.foldrM f init = xs.foldrM (init := init) fun x, h b => xs.unattach.foldrM f = xs.foldrM fun x, h b =>
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <| binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
f (wfParam x) b := by f (wfParam x) b := by
simp [wfParam] simp [wfParam]

View File

@@ -16,26 +16,6 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array 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 _rooy_.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] @[simp]
theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by
rw [ Array.toList_inj] rw [ Array.toList_inj]

View File

@@ -30,16 +30,6 @@ private def qpartition {n} (as : Vector α n) (lt : αα → Bool) (lo hi :
(i, ilo, as.swap i hi) (i, ilo, as.swap i hi)
loop as lo lo 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 (· < ·)) @[inline] def qsort (as : Array α) (lt : α α Bool := by exact (· < ·))
(low := 0) (high := as.size - 1) : Array α := (low := 0) (high := as.size - 1) : Array α :=
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat) 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 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 α := def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
xs.qsort fun x y => compare x y |>.isLT xs.qsort fun x y => compare x y |>.isLT

View File

@@ -39,8 +39,7 @@ theorem range'_ne_empty_iff (s : Nat) {n step : Nat} : range' s n step ≠ #[]
@[simp] theorem range'_zero : range' s 0 step = #[] := by @[simp] theorem range'_zero : range' s 0 step = #[] := by
simp simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := by @[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := rfl
simp [range', ofFn, ofFn.go]
@[simp] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by @[simp] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by
rw [ toList_inj] rw [ toList_inj]
@@ -78,7 +77,7 @@ theorem range'_append (s m n step : Nat) :
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append s m n 1 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 theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ #[s + step * n] := by
simpa using (range'_append s n 1 step).symm 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 [range'_concat]

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 This will perform the update destructively provided that `a` has a reference
proof can usually be omitted, and will be synthesized automatically. count of 1 when called.
The array is modified in-place if there are no other references to it.
Examples:
* `#[0, 1, 2].set 1 5 = #[0, 5, 2]`
* `#["orange", "apple"].set 1 "grape" = #["orange", "grape"]`
-/ -/
@[extern "lean_array_fset"] @[extern "lean_array_fset"]
def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_elem_tactic) : 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 toList := xs.toList.set i v
/-- /--
Replaces the element at the provided index in an array. The array is returned unmodified if the Set an element in an array, or do nothing if the index is out of bounds.
index is out of bounds.
The array is modified in-place if there are no other references to it. This will perform the update destructively provided that `a` has a reference
count of 1 when called.
Examples:
* `#[0, 1, 2].setIfInBounds 1 5 = #[0, 5, 2]`
* `#["orange", "apple"].setIfInBounds 1 "grape" = #["orange", "grape"]`
* `#["orange", "apple"].setIfInBounds 5 "grape" = #["orange", "apple"]`
-/ -/
@[inline] def Array.setIfInBounds (xs : Array α) (i : Nat) (v : α) : Array α := @[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) 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 import Init.Data.Array.Basic
set_option linter.indexVariables true -- Enforce naming conventions for index variables. set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.missingDocs true
universe u v w universe u v w
/-- structure Subarray (α : Type u) where
A region of some underlying array.
A subarray contains an array together with the start and end indices of a region of interest.
Subarrays can be used to avoid copying or allocating space, while being more convenient than
tracking the bounds by hand. The region of interest consists of every index that is both greater
than or equal to `start` and strictly less than `stop`.
-/
structure Subarray (α : Type u) where
/-- The underlying array. -/
array : Array α array : Array α
/-- The starting index of the region of interest (inclusive). -/
start : Nat start : Nat
/-- The ending index of the region of interest (exclusive). -/
stop : Nat 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 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 stop_le_array_size : stop array.size
namespace Subarray namespace Subarray
/--
Computes the size of the subarray.
-/
def size (s : Subarray α) : Nat := def size (s : Subarray α) : Nat :=
s.stop - s.start 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) apply Nat.le_trans (Nat.sub_le stop start)
assumption 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) : α := def get (s : Subarray α) (i : Fin s.size) : α :=
have : s.start + i.val < s.array.size := by have : s.start + i.val < s.array.size := by
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size 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 instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i, h 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₀ : α) : α := @[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
if h : i < s.size then s[i] else 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) : α := abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
getD s i default 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 α := def popFront (s : Subarray α) : Subarray α :=
if h : s.start < s.stop then 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) } { 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. The empty subarray.
This empty subarray is backed by an empty array.
-/ -/
protected def empty : Subarray α where protected def empty : Subarray α where
array := #[] array := #[]
@@ -122,12 +68,6 @@ instance : EmptyCollection (Subarray α) :=
instance : Inhabited (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 β := @[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 sz := USize.ofNat s.stop
let rec @[specialize] loop (i : USize) (b : β) : m β := do let rec @[specialize] loop (i : USize) (b : β) : m β := do
@@ -140,10 +80,6 @@ This definition replaces `Subarray.forIn`.
pure b pure b
loop (USize.ofNat s.start) 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 -- TODO: provide reference implementation
@[implemented_by Subarray.forInUnsafe] @[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 β := 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 instance : ForIn m (Subarray α) α where
forIn := Subarray.forIn 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] @[inline]
def foldlM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : β α m β) (init : β) (as : Subarray α) : m β := 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) 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] @[inline]
def foldrM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α β m β) (init : β) (as : Subarray α) : m β := 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) 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] @[inline]
def anyM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Subarray α) : m Bool := 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) 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] @[inline]
def allM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Subarray α) : m Bool := 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) 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] @[inline]
def forM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Subarray α) : m PUnit := 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) 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] @[inline]
def forRevM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Subarray α) : m PUnit := 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) 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] @[inline]
def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Subarray α) : β := def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldlM f (init := init) 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] @[inline]
def foldr {α : Type u} {β : Type v} (f : α β β) (init : β) (as : Subarray α) : β := def foldr {α : Type u} {β : Type v} (f : α β β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldrM f (init := init) 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] @[inline]
def any {α : Type u} (p : α Bool) (as : Subarray α) : Bool := def any {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.anyM p 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] @[inline]
def all {α : Type u} (p : α Bool) (as : Subarray α) : Bool := def all {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.allM p 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] @[inline]
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Subarray α) (f : α m (Option β)) : m (Option β) := 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 β) 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 i this
find as.size (Nat.le_refl _) 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] @[inline]
def findRevM? {α : Type} {m : Type Type w} [Monad m] (as : Subarray α) (p : α m Bool) : m (Option α) := 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 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] @[inline]
def findRev? {α : Type} (as : Subarray α) (p : α Bool) : Option α := def findRev? {α : Type} (as : Subarray α) (p : α Bool) : Option α :=
Id.run <| as.findRevM? p Id.run <| as.findRevM? p
@@ -399,12 +155,6 @@ end Subarray
namespace Array namespace Array
variable {α : Type u} 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 α := def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
if h₂ : stop as.size then if h₂ : stop as.size then
if h₁ : start stop 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 _, start_le_stop := Nat.le_refl _,
stop_le_array_size := Nat.le_refl _ } stop_le_array_size := Nat.le_refl _ }
/--
Allocates a new array that contains the contents of the subarray.
-/
@[coe] @[coe]
def ofSubarray (s : Subarray α) : Array α := Id.run do def ofSubarray (s : Subarray α) : Array α := Id.run do
let mut as := mkEmpty (s.stop - s.start) 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 instance : Coe (Subarray α) (Array α) := ofSubarray
/-- A subarray with the provided bounds.-/
syntax:max term noWs "[" withoutPosition(term ":" term) "]" : term 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 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 syntax:max term noWs "[" withoutPosition(":" term) "]" : term
macro_rules macro_rules
@@ -453,7 +197,6 @@ macro_rules
end Array end Array
@[inherit_doc Array.ofSubarray]
def Subarray.toArray (s : Subarray α) : Array α := def Subarray.toArray (s : Subarray α) : Array α :=
Array.ofSubarray s Array.ofSubarray s

View File

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

View File

@@ -149,13 +149,10 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array
· rintro ws, xs, ys, zs, h, rfl, rfl, h₁, h₂ · rintro ws, xs, ys, zs, h, rfl, rfl, h₁, h₂
exact ws, xs, ys, zs, by simp_all exact ws, xs, ys, zs, by simp_all
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} : @[simp] theorem zipWith_mkArray {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by zipWith f (mkArray m a) (mkArray n b) = mkArray (min m n) (f a b) := by
simp [ List.toArray_replicate] 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 map (Function.uncurry f) (as.zip bs) = zipWith f as bs := by
cases as cases as
@@ -273,13 +270,10 @@ 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 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 [zip_eq_zipWith, zipWith_eq_append_iff]
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} : @[simp] theorem zip_mkArray {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by zip (mkArray m a) (mkArray n b) = mkArray (min m n) (a, b) := by
simp [ List.toArray_replicate] 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 zip as bs = zip (as.take (min as.size bs.size)) (bs.take (min as.size bs.size)) := by
cases as cases as
@@ -323,12 +317,9 @@ theorem map_zipWithAll {δ : Type _} (f : α → β) (g : Option γ → Option
simp [List.map_zipWithAll] simp [List.map_zipWithAll]
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} : @[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] simp [ List.toArray_replicate]
@[deprecated zipWithAll_replicate (since := "2025-03-18")]
abbrev zipWithAll_mkArray := @zipWithAll_replicate
/-! ### unzip -/ /-! ### unzip -/
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by @[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
@@ -369,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 (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] rw [ hl, hr, zip_unzip xs, unzip_fst, unzip_snd, zip_unzip, zip_unzip]
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} : @[simp] theorem unzip_mkArray {n : Nat} {a : α} {b : β} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by unzip (mkArray n (a, b)) = (mkArray n a, mkArray n b) := by
ext1 <;> simp 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 := theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b b == c a == c :=
PartialEquivBEq.trans 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 : α} : theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
(a == b) = false b == c (a == c) = false := (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₂)) 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 We define many of the bitvector operations from the
[`QF_BV` logic](https://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV). [`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 set_option linter.missingDocs true
@@ -96,10 +96,16 @@ This will be renamed `getMsb` after the existing deprecated alias is removed.
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool := @[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
x.toNat.testBit i x.toNat.testBit i
@[deprecated getLsbD (since := "2024-08-29"), inherit_doc getLsbD]
def getLsb (x : BitVec w) (i : Nat) : Bool := x.getLsbD i
/-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/ /-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/
@[inline] def getMsbD (x : BitVec w) (i : Nat) : Bool := @[inline] def getMsbD (x : BitVec w) (i : Nat) : Bool :=
i < w && x.getLsbD (w-1-i) i < w && x.getLsbD (w-1-i)
@[deprecated getMsbD (since := "2024-08-29"), inherit_doc getMsbD]
def getMsb (x : BitVec w) (i : Nat) : Bool := x.getMsbD i
/-- Return most-significant bit in bitvector. -/ /-- Return most-significant bit in bitvector. -/
@[inline] protected def msb (x : BitVec n) : Bool := getMsbD x 0 @[inline] protected def msb (x : BitVec n) : Bool := getMsbD x 0
@@ -196,7 +202,7 @@ section arithmetic
Negation for bit vectors. This can be interpreted as either signed or unsigned negation Negation for bit vectors. This can be interpreted as either signed or unsigned negation
modulo `2^n`. 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) protected def neg (x : BitVec n) : BitVec n := .ofNat n (2^n - x.toNat)
instance : Neg (BitVec n) := .neg instance : Neg (BitVec n) := .neg
@@ -210,7 +216,7 @@ protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
Multiplication for bit vectors. This can be interpreted as either signed or unsigned Multiplication for bit vectors. This can be interpreted as either signed or unsigned
multiplication modulo `2^n`. 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) protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
instance : Mul (BitVec n) := .mul instance : Mul (BitVec n) := .mul
@@ -225,7 +231,7 @@ instance : Div (BitVec n) := ⟨.udiv⟩
/-- /--
Unsigned modulo for bit vectors. Unsigned modulo for bit vectors.
SMT-LIB name: `bvurem`. SMT-Lib name: `bvurem`.
-/ -/
def umod (x y : BitVec n) : BitVec n := def umod (x y : BitVec n) : BitVec n :=
(x.toNat % y.toNat)#'(Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt) (x.toNat % y.toNat)#'(Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt)
@@ -233,10 +239,10 @@ instance : Mod (BitVec n) := ⟨.umod⟩
/-- /--
Unsigned division for bit vectors using the Unsigned division for bit vectors using the
[SMT-LIB convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml) [SMT-Lib convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml)
where division by zero returns the `allOnes` bitvector. 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 def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then allOnes n else udiv x y
@@ -259,11 +265,11 @@ def sdiv (x y : BitVec n) : BitVec n :=
| true, true => udiv (.neg x) (.neg y) | true, true => udiv (.neg x) (.neg y)
/-- /--
Signed division for bit vectors using SMT-LIB rules for division by zero. Signed division for bit vectors using SMTLIB rules for division by zero.
Specifically, `smtSDiv x 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 := def smtSDiv (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with match x.msb, y.msb with
@@ -275,7 +281,7 @@ def smtSDiv (x y : BitVec n) : BitVec n :=
/-- /--
Remainder for signed division rounding to zero. Remainder for signed division rounding to zero.
SMT-LIB name: `bvsrem`. SMT_Lib name: `bvsrem`.
-/ -/
def srem (x y : BitVec n) : BitVec n := def srem (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with match x.msb, y.msb with
@@ -287,7 +293,7 @@ def srem (x y : BitVec n) : BitVec n :=
/-- /--
Remainder for signed division rounded to negative infinity. Remainder for signed division rounded to negative infinity.
SMT-LIB name: `bvsmod`. SMT_Lib name: `bvsmod`.
-/ -/
def smod (x y : BitVec m) : BitVec m := def smod (x y : BitVec m) : BitVec m :=
match x.msb, y.msb with match x.msb, y.msb with
@@ -321,14 +327,14 @@ section relations
/-- /--
Unsigned less-than for bit vectors. 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 protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat
/-- /--
Unsigned less-than-or-equal-to for bit vectors. 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 protected def ule (x y : BitVec n) : Bool := x.toNat y.toNat
@@ -339,14 +345,14 @@ Signed less-than for bit vectors.
BitVec.slt 6#4 7 = true BitVec.slt 6#4 7 = true
BitVec.slt 7#4 8 = false BitVec.slt 7#4 8 = false
``` ```
SMT-LIB name: `bvslt`. SMT-Lib name: `bvslt`.
-/ -/
protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt
/-- /--
Signed less-than-or-equal-to for bit vectors. 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 protected def sle (x y : BitVec n) : Bool := x.toInt y.toInt
@@ -378,7 +384,7 @@ def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toN
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to 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`. yield a new bitvector of size `hi - lo + 1`.
SMT-LIB name: `extract`. SMT-Lib name: `extract`.
-/ -/
def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x
@@ -409,7 +415,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or - 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`. - truncating the high bits, if `v < w`.
SMT-LIB name: `zero_extend`. SMT-Lib name: `zero_extend`.
-/ -/
def setWidth (v : Nat) (x : BitVec w) : BitVec v := def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
if h : w v then if h : w v then
@@ -422,7 +428,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or - 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`. - truncating the high bits, if `v < w`.
SMT-LIB name: `zero_extend`. SMT-Lib name: `zero_extend`.
-/ -/
abbrev zeroExtend := @setWidth abbrev zeroExtend := @setWidth
@@ -431,7 +437,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or - 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`. - truncating the high bits, if `v < w`.
SMT-LIB name: `zero_extend`. SMT-Lib name: `zero_extend`.
-/ -/
abbrev truncate := @setWidth abbrev truncate := @setWidth
@@ -439,7 +445,7 @@ abbrev truncate := @setWidth
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant 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. bit in `x`. If `x` is an empty vector, then the sign is treated as zero.
SMT-LIB name: `sign_extend`. SMT-Lib name: `sign_extend`.
-/ -/
def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt
@@ -454,7 +460,7 @@ Bitwise AND for bit vectors.
0b1010#4 &&& 0b0110#4 = 0b0010#4 0b1010#4 &&& 0b0110#4 = 0b0010#4
``` ```
SMT-LIB name: `bvand`. SMT-Lib name: `bvand`.
-/ -/
protected def and (x y : BitVec n) : BitVec n := protected def and (x y : BitVec n) : BitVec n :=
(x.toNat &&& y.toNat)#'(Nat.and_lt_two_pow x.toNat y.isLt) (x.toNat &&& y.toNat)#'(Nat.and_lt_two_pow x.toNat y.isLt)
@@ -467,7 +473,7 @@ Bitwise OR for bit vectors.
0b1010#4 ||| 0b0110#4 = 0b1110#4 0b1010#4 ||| 0b0110#4 = 0b1110#4
``` ```
SMT-LIB name: `bvor`. SMT-Lib name: `bvor`.
-/ -/
protected def or (x y : BitVec n) : BitVec n := protected def or (x y : BitVec n) : BitVec n :=
(x.toNat ||| y.toNat)#'(Nat.or_lt_two_pow x.isLt y.isLt) (x.toNat ||| y.toNat)#'(Nat.or_lt_two_pow x.isLt y.isLt)
@@ -480,7 +486,7 @@ instance : OrOp (BitVec w) := ⟨.or⟩
0b1010#4 ^^^ 0b0110#4 = 0b1100#4 0b1010#4 ^^^ 0b0110#4 = 0b1100#4
``` ```
SMT-LIB name: `bvxor`. SMT-Lib name: `bvxor`.
-/ -/
protected def xor (x y : BitVec n) : BitVec n := protected def xor (x y : BitVec n) : BitVec n :=
(x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt) (x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt)
@@ -492,7 +498,7 @@ Bitwise NOT for bit vectors.
```lean ```lean
~~~(0b0101#4) == 0b1010 ~~~(0b0101#4) == 0b1010
``` ```
SMT-LIB name: `bvnot`. SMT-Lib name: `bvnot`.
-/ -/
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
instance : Complement (BitVec w) := .not instance : Complement (BitVec w) := .not
@@ -501,7 +507,7 @@ instance : Complement (BitVec w) := ⟨.not⟩
Left shift for bit vectors. 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`. 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) protected def shiftLeft (x : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (x.toNat <<< s)
instance : HShiftLeft (BitVec w) Nat (BitVec w) := .shiftLeft instance : HShiftLeft (BitVec w) Nat (BitVec w) := .shiftLeft
@@ -510,7 +516,7 @@ instance : HShiftLeft (BitVec w) Nat (BitVec w) := ⟨.shiftLeft⟩
(Logical) right shift for bit vectors. 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. 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 := def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
(x.toNat >>> s)#'(by (x.toNat >>> s)#'(by
@@ -526,7 +532,7 @@ Arithmetic right shift for bit vectors. The high bits are filled with the
most-significant bit. most-significant bit.
As a numeric operation, this is equivalent to `x.toInt >>> s`. 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) def sshiftRight (x : BitVec n) (s : Nat) : BitVec n := .ofInt n (x.toInt >>> s)
@@ -538,7 +544,7 @@ Arithmetic right shift for bit vectors. The high bits are filled with the
most-significant bit. most-significant bit.
As a numeric operation, this is equivalent to `a.toInt >>> s.toNat`. 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 def sshiftRight' (a : BitVec n) (s : BitVec m) : BitVec n := a.sshiftRight s.toNat
@@ -554,7 +560,7 @@ bits wrapping around to fill the low bits.
```lean ```lean
rotateLeft 0b0011#4 3 = 0b1001 rotateLeft 0b0011#4 3 = 0b1001
``` ```
SMT-LIB name: `rotate_left` except this operator uses a `Nat` shift amount. SMT-Lib name: `rotate_left` except this operator uses a `Nat` shift amount.
-/ -/
def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := rotateLeftAux x (n % w) def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := rotateLeftAux x (n % w)
@@ -573,7 +579,7 @@ bottom `n` bits wrapping around to fill the high bits.
```lean ```lean
rotateRight 0b01001#5 1 = 0b10100 rotateRight 0b01001#5 1 = 0b10100
``` ```
SMT-LIB name: `rotate_right` except this operator uses a `Nat` shift amount. SMT-Lib name: `rotate_right` except this operator uses a `Nat` shift amount.
-/ -/
def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w) def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
@@ -581,7 +587,7 @@ def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
Concatenation of bitvectors. This uses the "big endian" convention that the more significant 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`. input is on the left, so `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
SMT-LIB name: `concat`. SMT-Lib name: `concat`.
-/ -/
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) := def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs
@@ -678,26 +684,18 @@ def ofBoolListLE : (bs : List Bool) → BitVec bs.length
/-- `uaddOverflow x y` returns `true` if 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 def uaddOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat + y.toNat 2 ^ w
/-- `saddOverflow x y` returns `true` if addition of `x` and `y` results in *signed* overflow, /-- `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. 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 := def saddOverflow {w : Nat} (x y : BitVec w) : Bool :=
(x.toInt + y.toInt 2 ^ (w - 1)) || (x.toInt + y.toInt < - 2 ^ (w - 1)) (x.toInt + y.toInt 2 ^ (w - 1)) || (x.toInt + y.toInt < - 2 ^ (w - 1))
/-- `negOverflow x` returns `true` if the negation of `x` results in overflow.
For a BitVec `x` with width `0 < w`, this only happens if `x = intMin`.
SMT-Lib name: `bvnego`.
-/
def negOverflow {w : Nat} (x : BitVec w) : Bool :=
x.toInt == - 2 ^ (w - 1)
/- ### reverse -/ /- ### reverse -/
/-- Reverse the bits in a bitvector. -/ /-- Reverse the bits in a bitvector. -/

View File

@@ -35,7 +35,7 @@ section arithmetic
Addition for bit vectors. This can be interpreted as either signed or unsigned addition Addition for bit vectors. This can be interpreted as either signed or unsigned addition
modulo `2^n`. 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) protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
instance : Add (BitVec n) := BitVec.add instance : Add (BitVec n) := BitVec.add

View File

@@ -91,7 +91,7 @@ First, we prove bitvector lemmas to unfold a high-level operation (such as multi
into already bitblastable operations (such as addition and left shift). 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 then use these lemmas to prove the correctness of the circuit that `bv_decide` builds.
We use this workflow to implement bitblasting for all SMT-LIB v2 operations. We use this workflow to implement bitblasting for all SMT-LIB2 operations.
## Main results ## Main results
* `x + y : BitVec w` is `(adc x y false).2`. * `x + y : BitVec w` is `(adc x y false).2`.
@@ -109,12 +109,7 @@ open Nat Bool
namespace Bool namespace Bool
/-- /-- At least two out of three booleans are true. -/
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.
-/
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c 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] @[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 zero => exact hmsb
case succ => exact getMsbD_x _ hi (by omega) 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 -/ /-! ### abs -/
theorem msb_abs {w : Nat} {x : BitVec w} : theorem msb_abs {w : Nat} {x : BitVec w} :
@@ -582,15 +544,6 @@ theorem slt_eq_not_carry (x y : BitVec w) :
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 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} (b : BitVec w) (hb : BitVec.sle 0#w b) : b.toInt.toNat = b.toNat :=
toNat_toInt_of_msb b (zero_sle_iff_msb_eq_false.1 hb)
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 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] rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
@@ -1291,23 +1244,14 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
simp only [saddOverflow] simp only [saddOverflow]
rcases w with _|w rcases w with _|w
· revert x y; decide · revert x y; decide
· have := le_two_mul_toInt (x := x); have := two_mul_toInt_lt (x := x) · have := le_toInt (x := x); have := toInt_lt (x := x)
have := le_two_mul_toInt (x := y); have := two_mul_toInt_lt (x := y) 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, simp only [ decide_or, msb_eq_toInt, decide_beq_decide, toInt_add, decide_not, decide_and,
decide_eq_decide] decide_eq_decide]
rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)] rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)]
simp simp
omega 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 -/ /- ### umod -/
theorem getElem_umod {n d : BitVec w} (hi : i < w) : theorem getElem_umod {n d : BitVec w} (hi : i < w) :
@@ -1350,53 +1294,4 @@ theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x,
have := congrArg g h' have := congrArg g h'
simpa [h] using this simpa [h] using this
/-! ### Lemmas that use Bitblasting circuits -/
theorem add_sub_comm {x y : BitVec w} : x + y - z = x - z + y := by
apply eq_of_toNat_eq
simp only [toNat_sub, toNat_add, add_mod_mod, mod_add_mod]
congr 1
omega
theorem sub_add_comm {x y : BitVec w} : x - y + z = x + z - y := by
rw [add_sub_comm]
theorem not_add_one {x : BitVec w} : ~~~ (x + 1#w) = ~~~ x - 1#w := by
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
theorem not_add_eq_not_neg {x y : BitVec w} : ~~~ (x + y) = ~~~ x - y := by
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
simp only [sub_toAdd]
rw [BitVec.add_assoc, @BitVec.add_comm _ (-y), BitVec.add_assoc]
theorem not_sub_one_eq_not_add_one {x : BitVec w} : ~~~ (x - 1#w) = ~~~ x + 1#w := by
rw [not_eq_neg_add, not_eq_neg_add, neg_sub,
BitVec.add_sub_cancel, BitVec.sub_add_cancel]
theorem not_sub_eq_not_add {x y : BitVec w} : ~~~ (x - y) = ~~~ x + y := by
rw [BitVec.sub_toAdd, not_add_eq_not_neg, sub_neg]
/-- The value of `(carry i x y false)` can be computed by truncating `x` and `y`
to `len` bits where `len ≥ i`. -/
theorem carry_extractLsb'_eq_carry {w i len : Nat} (hi : i < len)
{x y : BitVec w} {b : Bool}:
(carry i (extractLsb' 0 len x) (extractLsb' 0 len y) b)
= (carry i x y b) := by
simp only [carry, extractLsb'_toNat, shiftRight_zero, toNat_false, Nat.add_zero, ge_iff_le,
decide_eq_decide]
have : 2 ^ i 2^len := by
apply Nat.pow_dvd_pow
omega
rw [Nat.mod_mod_of_dvd _ this, Nat.mod_mod_of_dvd _ this]
/--
The `[0..len)` low bits of `x + y` can be computed by truncating `x` and `y`
to `len` bits and then adding.
-/
theorem extractLsb'_add {w len : Nat} {x y : BitVec w} (hlen : len w) :
(x + y).extractLsb' 0 len = x.extractLsb' 0 len + y.extractLsb' 0 len := by
ext i hi
rw [getElem_extractLsb', Nat.zero_add, getLsbD_add (by omega)]
simp [getElem_add, carry_extractLsb'_eq_carry hi, getElem_extractLsb', Nat.zero_add]
end BitVec end BitVec

View File

@@ -13,9 +13,7 @@ import Init.Data.Nat.Div.Lemmas
import Init.Data.Nat.Mod import Init.Data.Nat.Mod
import Init.Data.Nat.Div.Lemmas import Init.Data.Nat.Div.Lemmas
import Init.Data.Int.Bitwise.Lemmas import Init.Data.Int.Bitwise.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.Pow import Init.Data.Int.Pow
import Init.Data.Int.LemmasAux
set_option linter.missingDocs true set_option linter.missingDocs true
@@ -241,16 +239,12 @@ theorem eq_of_getMsbD_eq {x y : BitVec w}
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp [ getLsbD_eq_getElem] theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp [ getLsbD_eq_getElem]
theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero] theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
theorem toInt_zero_length (x : BitVec 0) : x.toInt = 0 := by simp [of_length_zero]
theorem getLsbD_zero_length (x : BitVec 0) : x.getLsbD i = false := by simp theorem getLsbD_zero_length (x : BitVec 0) : x.getLsbD i = false := by simp
theorem getMsbD_zero_length (x : BitVec 0) : x.getMsbD i = false := by simp theorem getMsbD_zero_length (x : BitVec 0) : x.getMsbD i = false := by simp
theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero] theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero]
theorem toNat_of_zero_length (h : w = 0) (x : BitVec w) : x.toNat = 0 := by theorem toNat_of_zero_length (h : w = 0) (x : BitVec w) : x.toNat = 0 := by
subst h; simp [toNat_zero_length] subst h; simp [toNat_zero_length]
theorem toInt_of_zero_length (h : w = 0) (x : BitVec w) : x.toInt = 0 := by
subst h; simp [toInt_zero_length]
theorem getLsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getLsbD i = false := by theorem getLsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getLsbD i = false := by
subst h; simp [getLsbD_zero_length] subst h; simp [getLsbD_zero_length]
theorem getMsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getMsbD i = false := by theorem getMsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getMsbD i = false := by
@@ -260,7 +254,7 @@ theorem msb_of_zero_length (h : w = 0) (x : BitVec w) : x.msb = false := by
theorem ofFin_ofNat (n : Nat) : theorem ofFin_ofNat (n : Nat) :
ofFin (no_index (OfNat.ofNat n : Fin (2^w))) = OfNat.ofNat n := by ofFin (no_index (OfNat.ofNat n : Fin (2^w))) = OfNat.ofNat n := by
simp only [OfNat.ofNat, Fin.ofNat', BitVec.ofNat, Nat.and_two_pow_sub_one_eq_mod] simp only [OfNat.ofNat, Fin.ofNat', BitVec.ofNat, Nat.and_pow_two_sub_one_eq_mod]
theorem eq_of_toFin_eq : {x y : BitVec w}, x.toFin = y.toFin x = y theorem eq_of_toFin_eq : {x y : BitVec w}, x.toFin = y.toFin x = y
| _, _, _, _, rfl => rfl | _, _, _, _, rfl => rfl
@@ -329,25 +323,8 @@ theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by @[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat'] simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
@[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' (2^w) x := rfl @[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' (2^w) x := rfl
@[simp] theorem finMk_toNat (x : BitVec w) : Fin.mk x.toNat x.isLt = x.toFin := rfl
@[simp] theorem toFin_ofNatLT {n : Nat} (h : n < 2 ^ w) : (BitVec.ofNatLT n h).toFin = Fin.mk n h := rfl
@[simp] theorem toFin_ofFin (n : Fin (2 ^ w)) : (BitVec.ofFin n).toFin = n := rfl
@[simp] theorem ofFin_toFin (x : BitVec w) : BitVec.ofFin x.toFin = x := rfl
@[simp] theorem ofNatLT_finVal (n : Fin (2 ^ w)) : BitVec.ofNatLT n.val n.isLt = BitVec.ofFin n := rfl
@[simp] theorem ofNatLT_toNat (x : BitVec w) : BitVec.ofNatLT x.toNat x.isLt = x := rfl
@[simp] theorem ofNat_finVal (n : Fin (2 ^ w)) : BitVec.ofNat w n.val = BitVec.ofFin n := by
rw [ BitVec.ofNatLT_eq_ofNat n.isLt, ofNatLT_finVal]
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals. -- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea. -- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) : theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) :
@@ -551,9 +528,6 @@ theorem toInt_eq_toNat_of_msb {x : BitVec w} (h : x.msb = false) :
x.toInt = x.toNat := by x.toInt = x.toNat := by
simp [toInt_eq_msb_cond, h] simp [toInt_eq_msb_cond, h]
theorem toNat_toInt_of_msb {w : Nat} (b : BitVec w) (hb : b.msb = false) : b.toInt.toNat = b.toNat := by
simp [b.toInt_eq_toNat_of_msb hb]
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
simp only [toInt_eq_toNat_cond] simp only [toInt_eq_toNat_cond]
split split
@@ -564,16 +538,6 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
rw [Int.bmod_neg] <;> simp only [Int.ofNat_emod, toNat_mod_cancel] rw [Int.bmod_neg] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
omega omega
theorem toInt_neg_of_msb_true {x : BitVec w} (h : x.msb = true) : x.toInt < 0 := by
simp only [BitVec.toInt]
have : 2 * x.toNat 2 ^ w := msb_eq_true_iff_two_mul_ge.mp h
omega
theorem toInt_nonneg_of_msb_false {x : BitVec w} (h : x.msb = false) : 0 x.toInt := by
simp only [BitVec.toInt]
have : 2 * x.toNat < 2 ^ w := msb_eq_false_iff_two_mul_lt.mp h
omega
/-- Prove equality of bitvectors in terms of nat operations. -/ /-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt x = y := by theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt x = y := by
intro eq intro eq
@@ -605,11 +569,6 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
have p : 0 i % (2^n : Nat) := by omega have p : 0 i % (2^n : Nat) := by omega
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p] simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
(h : -2 ^ (w - 1) n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
have hw : w = (w - 1) + 1 := by omega
rw [toInt_ofInt, Int.bmod_eq_self_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
@[simp] theorem ofInt_natCast (w n : Nat) : @[simp] theorem ofInt_natCast (w n : Nat) :
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
@@ -645,50 +604,26 @@ theorem toInt_zero {w : Nat} : (0#w).toInt = 0 := by
`x.toInt` is less than `2^(w-1)`. `x.toInt` is less than `2^(w-1)`.
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used. We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
-/ -/
theorem two_mul_toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by theorem toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by
simp only [BitVec.toInt] simp only [BitVec.toInt]
rcases w with _|w' rcases w with _|w'
· omega · omega
· rw [ Nat.two_pow_pred_add_two_pow_pred (by omega), Nat.two_mul, Nat.add_sub_cancel] · rw [ Nat.two_pow_pred_add_two_pow_pred (by omega), Nat.two_mul, Nat.add_sub_cancel]
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int] simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
norm_cast; omega norm_cast; omega
theorem two_mul_toInt_le {w : Nat} {x : BitVec w} : 2 * x.toInt 2 ^ w - 1 :=
Int.le_sub_one_of_lt two_mul_toInt_lt
theorem toInt_lt {w : Nat} {x : BitVec w} : x.toInt < 2 ^ (w - 1) := by
by_cases h : w = 0
· subst h
simp [eq_nil x]
· have := @two_mul_toInt_lt w x
rw_mod_cast [ Nat.two_pow_pred_add_two_pow_pred (by omega), Int.mul_comm, Int.natCast_add] at this
omega
theorem toInt_le {w : Nat} {x : BitVec w} : x.toInt 2 ^ (w - 1) - 1 :=
Int.le_sub_one_of_lt toInt_lt
/-- /--
`x.toInt` is greater than or equal to `-2^(w-1)`. `x.toInt` is greater than or equal to `-2^(w-1)`.
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used. We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
-/ -/
theorem le_two_mul_toInt {w : Nat} {x : BitVec w} : -2 ^ w 2 * x.toInt := by theorem le_toInt {w : Nat} {x : BitVec w} : -2 ^ w 2 * x.toInt := by
simp only [BitVec.toInt] simp only [BitVec.toInt]
rcases w with _|w' rcases w with _|w'
· omega · omega
· rw [ Nat.two_pow_pred_add_two_pow_pred (by omega), Nat.two_mul, Nat.add_sub_cancel] · rw [ Nat.two_pow_pred_add_two_pow_pred (by omega), Nat.two_mul, Nat.add_sub_cancel]
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int] simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
norm_cast; omega norm_cast; omega
theorem le_toInt {w : Nat} (x : BitVec w) : -2 ^ (w - 1) x.toInt := by
by_cases h : w = 0
· subst h
simp [BitVec.eq_nil x]
· have := le_two_mul_toInt (w := w) (x := x)
generalize x.toInt = x at *
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
/-! ### slt -/ /-! ### slt -/
/-- /--
@@ -710,12 +645,6 @@ theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w ↔ x.msb = true := by
simp [BitVec.slt, this] simp [BitVec.slt, this]
omega omega
theorem slt_zero_eq_msb {w : Nat} {x : BitVec w} : x.slt 0#w = x.msb := by
rw [Bool.eq_iff_iff, BitVec.slt_zero_iff_msb_cond]
theorem sle_iff_toInt_le {w : Nat} {b b' : BitVec w} : b.sle b' b.toInt b'.toInt :=
decide_eq_true_iff
/-! ### setWidth, zeroExtend and truncate -/ /-! ### setWidth, zeroExtend and truncate -/
@[simp] @[simp]
@@ -1067,31 +996,6 @@ theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h
apply eq_of_toNat_eq apply eq_of_toNat_eq
simp [extractLsb, show len - 1 + 1 = len by omega] simp [extractLsb, show len - 1 + 1 = len by omega]
/-- Extracting all the bits of a bitvector is an identity operation. -/
@[simp] theorem extractLsb'_eq_self {x : BitVec w} : x.extractLsb' 0 w = x := by
apply eq_of_toNat_eq
simp [extractLsb']
theorem getLsbD_eq_extractLsb' (x : BitVec w) (i : Nat) :
x.getLsbD i = (x.extractLsb' i 1 == 1#1) := by
rw [Bool.eq_iff_iff]
simp [BitVec.eq_of_getLsbD_eq_iff]
theorem getElem_eq_extractLsb' (x : BitVec w) (i : Nat) (h : i < w) :
x[i] = (x.extractLsb' i 1 == 1#1) := by
rw [ getLsbD_eq_getElem, getLsbD_eq_extractLsb']
@[simp]
theorem extractLsb'_zero {w start len : Nat} : (0#w).extractLsb' start len = 0#len := by
apply eq_of_toNat_eq
simp [extractLsb']
@[simp]
theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
x.extractLsb' start 0 = 0#0 := by
ext i hi
omega
/-! ### allOnes -/ /-! ### allOnes -/
@[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by @[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
@@ -1563,32 +1467,6 @@ theorem extractLsb_not_of_lt {x : BitVec w} {hi lo : Nat} (hlo : lo ≤ hi) (hhi
simp [hk, show k hi - lo by omega] simp [hk, show k hi - lo by omega]
omega omega
@[simp]
theorem ne_not_self {a : BitVec w} (h : 0 < w) : a ~~~a := by
have : x, x < w := w - 1, by omega
simp [BitVec.eq_of_getElem_eq_iff, this]
@[simp]
theorem not_self_ne {a : BitVec w} (h : 0 < w) : ~~~a a := by
rw [ne_comm]
simp [h]
theorem not_and {x y : BitVec w} : ~~~ (x &&& y) = ~~~ x ||| ~~~ y := by
ext i
simp
theorem not_or {x y : BitVec w} : ~~~ (x ||| y) = ~~~ x &&& ~~~ y := by
ext i
simp
theorem not_xor_left {x y : BitVec w} : ~~~ (x ^^^ y) = ~~~ x ^^^ y := by
ext i
simp
theorem not_xor_right {x y : BitVec w} : ~~~ (x ^^^ y) = x ^^^ ~~~ y := by
ext i
simp
/-! ### cast -/ /-! ### cast -/
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by @[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
@@ -1738,10 +1616,6 @@ theorem allOnes_shiftLeft_or_shiftLeft {x : BitVec w} {n : Nat} :
BitVec.allOnes w <<< n ||| x <<< n = BitVec.allOnes w <<< n := by BitVec.allOnes w <<< n ||| x <<< n = BitVec.allOnes w <<< n := by
simp [ shiftLeft_or_distrib] simp [ shiftLeft_or_distrib]
@[simp] theorem setWidth_shiftLeft_of_le {x : BitVec w} {y : Nat} (hi : i w) :
(x <<< y).setWidth i = x.setWidth i <<< y :=
eq_of_getElem_eq (fun j hj => Bool.eq_iff_iff.2 (by simp; omega))
/-! ### shiftLeft reductions from BitVec to Nat -/ /-! ### shiftLeft reductions from BitVec to Nat -/
@[simp] @[simp]
@@ -1861,7 +1735,7 @@ theorem toInt_ushiftRight {x : BitVec w} {n : Nat} :
simp [hn] simp [hn]
@[simp] @[simp]
theorem toFin_ushiftRight {x : BitVec w} {n : Nat} : theorem toFin_uShiftRight {x : BitVec w} {n : Nat} :
(x >>> n).toFin = x.toFin / (Fin.ofNat' (2^w) (2^n)) := by (x >>> n).toFin = x.toFin / (Fin.ofNat' (2^w) (2^n)) := by
apply Fin.eq_of_val_eq apply Fin.eq_of_val_eq
by_cases hn : n < w by_cases hn : n < w
@@ -1893,15 +1767,6 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
case succ nn ih => case succ nn ih =>
simp [BitVec.ushiftRight_eq, getMsbD_ushiftRight, BitVec.msb, ih, show nn + 1 > 0 by omega] simp [BitVec.ushiftRight_eq, getMsbD_ushiftRight, BitVec.msb, ih, show nn + 1 > 0 by omega]
@[simp] theorem setWidth_ushiftRight {x : BitVec w} {y : Nat} (hi : w i) :
(x >>> y).setWidth i = x.setWidth i >>> y := by
refine eq_of_getElem_eq (fun j hj => ?_)
simp only [getElem_setWidth, getLsbD_ushiftRight, getElem_ushiftRight, getLsbD_setWidth,
Bool.iff_and_self, decide_eq_true_eq]
intro ha
have := lt_of_getLsbD ha
omega
/-! ### ushiftRight reductions from BitVec to Nat -/ /-! ### ushiftRight reductions from BitVec to Nat -/
@[simp] @[simp]
@@ -2076,118 +1941,11 @@ theorem getMsbD_sshiftRight {x : BitVec w} {i n : Nat} :
by_cases h₄ : n + (w - 1 - i) < w <;> (simp only [h₄, reduceIte]; congr; omega) by_cases h₄ : n + (w - 1 - i) < w <;> (simp only [h₄, reduceIte]; congr; omega)
· simp [h] · simp [h]
theorem toInt_shiftRight_lt {x : BitVec w} {n : Nat} :
x.toInt >>> n < 2 ^ (w - 1) := by
have := @Int.shiftRight_le_of_nonneg x.toInt n
have := @Int.shiftRight_le_of_nonpos x.toInt n
have := @BitVec.toInt_lt w x
have := @Nat.one_le_two_pow (w-1)
norm_cast at *
omega
theorem le_toInt_shiftRight {x : BitVec w} {n : Nat} :
-(2 ^ (w - 1)) x.toInt >>> n := by
have := @Int.le_shiftRight_of_nonpos x.toInt n
have := @Int.le_shiftRight_of_nonneg x.toInt n
have := @BitVec.le_toInt w x
have := @Nat.one_le_two_pow (w-1)
norm_cast at *
omega
theorem toNat_sshiftRight_of_msb_true {x : BitVec w} {n : Nat} (h : x.msb = true) :
(x.sshiftRight n).toNat = 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n := by
simp [sshiftRight_eq_of_msb_true, h]
theorem toNat_sshiftRight_of_msb_false {x : BitVec w} {n : Nat} (h : x.msb = false) :
(x.sshiftRight n).toNat = x.toNat >>> n := by
simp [sshiftRight_eq_of_msb_false, h]
theorem toNat_sshiftRight {x : BitVec w} {n : Nat} :
(x.sshiftRight n).toNat =
if x.msb
then 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n
else x.toNat >>> n := by
by_cases h : x.msb
· simp [toNat_sshiftRight_of_msb_true, h]
· rw [Bool.not_eq_true] at h
simp [toNat_sshiftRight_of_msb_false, h]
theorem toFin_sshiftRight_of_msb_true {x : BitVec w} {n : Nat} (h : x.msb = true) :
(x.sshiftRight n).toFin = Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n) := by
apply Fin.eq_of_val_eq
simp only [val_toFin, toNat_sshiftRight, h, reduceIte, Fin.val_ofNat']
rw [Nat.mod_eq_of_lt]
have := x.isLt
have ineq : y, 2 ^ w - 1 - y < 2 ^ w := by omega
exact ineq ((2 ^ w - 1 - x.toNat) >>> n)
theorem toFin_sshiftRight_of_msb_false {x : BitVec w} {n : Nat} (h : x.msb = false) :
(x.sshiftRight n).toFin = Fin.ofNat' (2^w) (x.toNat >>> n) := by
apply Fin.eq_of_val_eq
simp only [val_toFin, toNat_sshiftRight, h, Bool.false_eq_true, reduceIte, Fin.val_ofNat']
have := Nat.shiftRight_le x.toNat n
rw [Nat.mod_eq_of_lt (by omega)]
theorem toFin_sshiftRight {x : BitVec w} {n : Nat} :
(x.sshiftRight n).toFin =
if x.msb
then Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n)
else Fin.ofNat' (2^w) (x.toNat >>> n) := by
by_cases h : x.msb
· simp [toFin_sshiftRight_of_msb_true, h]
· simp [toFin_sshiftRight_of_msb_false, h]
@[simp]
theorem toInt_sshiftRight {x : BitVec w} {n : Nat} :
(x.sshiftRight n).toInt = x.toInt >>> n := by
by_cases h : w = 0
· subst h
simp [BitVec.eq_nil x]
· rw [sshiftRight, toInt_ofInt, Nat.two_pow_pred_add_two_pow_pred (by omega)]
have := @toInt_shiftRight_lt w x n
have := @le_toInt_shiftRight w x n
norm_cast at *
exact Int.bmod_eq_self_of_le (by omega) (by omega)
/-! ### sshiftRight reductions from BitVec to Nat -/ /-! ### sshiftRight reductions from BitVec to Nat -/
@[simp] @[simp]
theorem sshiftRight_eq' (x : BitVec w) : x.sshiftRight' y = x.sshiftRight y.toNat := rfl theorem sshiftRight_eq' (x : BitVec w) : x.sshiftRight' y = x.sshiftRight y.toNat := rfl
theorem toNat_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
(x.sshiftRight' y).toNat = 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat := by
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_true h]
theorem toNat_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
(x.sshiftRight' y).toNat = x.toNat >>> y.toNat := by
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_false h]
theorem toNat_sshiftRight' {x y : BitVec w} :
(x.sshiftRight' y).toNat =
if x.msb
then 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat
else x.toNat >>> y.toNat := by
rw [sshiftRight_eq', toNat_sshiftRight]
theorem toFin_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
(x.sshiftRight' y).toFin = Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat) := by
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_true h]
theorem toFin_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
(x.sshiftRight' y).toFin = Fin.ofNat' (2^w) (x.toNat >>> y.toNat) := by
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_false h]
theorem toFin_sshiftRight' {x y : BitVec w} :
(x.sshiftRight' y).toFin =
if x.msb
then Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat)
else Fin.ofNat' (2^w) (x.toNat >>> y.toNat) := by
rw [sshiftRight_eq', toFin_sshiftRight]
theorem toInt_sshiftRight' {x y : BitVec w} :
(x.sshiftRight' y).toInt = x.toInt >>> y.toNat := by
rw [sshiftRight_eq', toInt_sshiftRight]
-- This should not be a `@[simp]` lemma as the left hand side is not in simp normal form. -- This should not be a `@[simp]` lemma as the left hand side is not in simp normal form.
theorem getLsbD_sshiftRight' {x y : BitVec w} {i : Nat} : theorem getLsbD_sshiftRight' {x y : BitVec w} {i : Nat} :
getLsbD (x.sshiftRight' y) i = getLsbD (x.sshiftRight' y) i =
@@ -2278,18 +2036,14 @@ theorem msb_signExtend {x : BitVec w} :
· simp [h, BitVec.msb, getMsbD_signExtend, show ¬ (v - w = 0) by omega] · simp [h, BitVec.msb, getMsbD_signExtend, show ¬ (v - w = 0) by omega]
/-- Sign extending to a width smaller than the starting width is a truncation. -/ /-- Sign extending to a width smaller than the starting width is a truncation. -/
theorem signExtend_eq_setWidth_of_le (x : BitVec w) {v : Nat} (hv : v w) : theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v w):
x.signExtend v = x.setWidth v := by x.signExtend v = x.setWidth v := by
ext i h ext i h
simp [getElem_signExtend, show i < w by omega] simp [getElem_signExtend, show i < w by omega]
@[deprecated signExtend_eq_setWidth_of_le (since := "2025-03-07")]
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v w) :
x.signExtend v = x.setWidth v := signExtend_eq_setWidth_of_le x hv
/-- Sign extending to the same bitwidth is a no op. -/ /-- Sign extending to the same bitwidth is a no op. -/
@[simp] theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_setWidth_of_le _ (Nat.le_refl _), setWidth_eq] rw [signExtend_eq_setWidth_of_lt _ (Nat.le_refl _), setWidth_eq]
/-- Sign extending to a larger bitwidth depends on the msb. /-- Sign extending to a larger bitwidth depends on the msb.
If the msb is false, then the result equals the original value. If the msb is false, then the result equals the original value.
@@ -2302,7 +2056,7 @@ private theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
rw [hk, testBit_toNat, getLsbD_signExtend, Nat.pow_add, Nat.mul_sub_one, Nat.add_comm (x.toNat)] rw [hk, testBit_toNat, getLsbD_signExtend, Nat.pow_add, Nat.mul_sub_one, Nat.add_comm (x.toNat)]
by_cases hx : x.msb by_cases hx : x.msb
· simp only [hx, Bool.if_true_right, reduceIte, · simp only [hx, Bool.if_true_right, reduceIte,
Nat.testBit_two_pow_mul_add _ x.isLt, Nat.testBit_mul_pow_two_add _ x.isLt,
testBit_toNat, Nat.testBit_two_pow_sub_one] testBit_toNat, Nat.testBit_two_pow_sub_one]
-- Case analysis on i being in the intervals [0..w), [w..w + k), [w+k..∞) -- Case analysis on i being in the intervals [0..w), [w..w + k), [w+k..∞)
have hi : i < w (w i i < w + k) w + k i := by omega have hi : i < w (w i i < w + k) w + k i := by omega
@@ -2326,65 +2080,47 @@ theorem toNat_signExtend (x : BitVec w) {v : Nat} :
(x.signExtend v).toNat = (x.setWidth v).toNat + if x.msb then 2^v - 2^w else 0 := by (x.signExtend v).toNat = (x.setWidth v).toNat + if x.msb then 2^v - 2^w else 0 := by
by_cases h : v w by_cases h : v w
· have : 2^v 2^w := Nat.pow_le_pow_right Nat.two_pos h · have : 2^v 2^w := Nat.pow_le_pow_right Nat.two_pos h
simp [signExtend_eq_setWidth_of_le x h, toNat_setWidth, Nat.sub_eq_zero_of_le this] simp [signExtend_eq_setWidth_of_lt x h, toNat_setWidth, Nat.sub_eq_zero_of_le this]
· have : 2^w 2^v := Nat.pow_le_pow_right Nat.two_pos (by omega) · have : 2^w 2^v := Nat.pow_le_pow_right Nat.two_pos (by omega)
rw [toNat_signExtend_of_le x (by omega), toNat_setWidth, Nat.mod_eq_of_lt (by omega)] rw [toNat_signExtend_of_le x (by omega), toNat_setWidth, Nat.mod_eq_of_lt (by omega)]
/-- /-
If the current width `w` is smaller than the extended width `v`, If the current width `w` is smaller than the extended width `v`,
then the value when interpreted as an integer does not change. then the value when interpreted as an integer does not change.
-/ -/
theorem toInt_signExtend_of_le {x : BitVec w} (h : w v) : theorem toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
(x.signExtend v).toInt = x.toInt := by (x.signExtend v).toInt = x.toInt := by
by_cases hlt : w < v simp only [toInt_eq_msb_cond, toNat_signExtend]
· rw [toInt_signExtend_of_lt hlt] have : (x.signExtend v).msb = x.msb := by
· obtain rfl : w = v := by omega rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
simp simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
where
toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
(x.signExtend v).toInt = x.toInt := by
simp only [toInt_eq_msb_cond, toNat_signExtend]
have : (x.signExtend v).msb = x.msb := by
rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
omega
have H : 2^w 2^v := Nat.pow_le_pow_right (by omega) (by omega)
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
omega omega
have H : 2^w 2^v := Nat.pow_le_pow_right (by omega) (by omega)
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
omega
/-- /-
If the current width `w` is larger than the extended width `v`, If the current width `w` is larger than the extended width `v`,
then the value when interpreted as an integer is truncated, then the value when interpreted as an integer is truncated,
and we compute a modulo by `2^v`. and we compute a modulo by `2^v`.
-/ -/
theorem toInt_signExtend_eq_toNat_bmod_of_le {x : BitVec w} (hv : v w) : theorem toInt_signExtend_of_le {x : BitVec w} (hv : v w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2^v) := by (x.signExtend v).toInt = Int.bmod x.toNat (2^v) := by
simp [signExtend_eq_setWidth_of_le _ hv] simp [signExtend_eq_setWidth_of_lt _ hv]
/-- /-
Interpreting the sign extension of `(x : BitVec w)` to width `v` Interpreting the sign extension of `(x : BitVec w)` to width `v`
computes `x % 2^v` (where `%` is the balanced mod). See `toInt_signExtend` for a version stated computes `x % 2^v` (where `%` is the balanced mod).
in terms of `toInt` instead of `toNat`.
-/ -/
theorem toInt_signExtend_eq_toNat_bmod (x : BitVec w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2 ^ min v w) := by
by_cases hv : v w
· simp [toInt_signExtend_eq_toNat_bmod_of_le hv, Nat.min_eq_left hv]
· simp only [Nat.not_le] at hv
rw [toInt_signExtend_of_le (Nat.le_of_lt hv),
Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
theorem toInt_signExtend (x : BitVec w) : theorem toInt_signExtend (x : BitVec w) :
(x.signExtend v).toInt = x.toInt.bmod (2 ^ min v w) := by (x.signExtend v).toInt = Int.bmod x.toNat (2^(min v w)) := by
rw [toInt_signExtend_eq_toNat_bmod, BitVec.toInt_eq_toNat_bmod, Int.bmod_bmod_of_dvd] by_cases hv : v w
exact Nat.pow_dvd_pow _ (Nat.min_le_right v w) · simp [toInt_signExtend_of_le hv, Nat.min_eq_left hv]
· simp only [Nat.not_le] at hv
theorem toInt_signExtend_eq_toInt_bmod_of_le (x : BitVec w) (h : v w) : rw [toInt_signExtend_of_lt hv, Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
(x.signExtend v).toInt = x.toInt.bmod (2 ^ v) := by
rw [BitVec.toInt_signExtend, Nat.min_eq_left h]
/-! ### append -/ /-! ### append -/
@@ -2529,42 +2265,6 @@ theorem msb_shiftLeft {x : BitVec w} {n : Nat} :
(x <<< n).msb = x.getMsbD n := by (x <<< n).msb = x.getMsbD n := by
simp [BitVec.msb] simp [BitVec.msb]
/--
A `(x : BitVec v)` set to width `w` equals `(v - w)` zeros,
followed by the low `(min v w) bits of `x`
-/
theorem setWidth_eq_append_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} :
x.setWidth w = ((0#(w - v)) ++ x.extractLsb' 0 (min v w)).cast (by omega) := by
ext i hi
simp only [getElem_cast, getElem_append]
by_cases hiv : i < v
· simp [hi]
omega
· simp [getLsbD_ge x i (by omega)]
/--
A `(x : BitVec v)` set to a width `w ≥ v` equals `(w - v)` zeros, followed by `x`.
-/
theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v w) :
x.setWidth w = ((0#(w - v)) ++ x).cast (by omega) := by
rw [setWidth_eq_append_extractLsb']
ext i hi
simp only [getElem_cast, getElem_append]
by_cases hiv : i < v
· simp [hiv]
omega
· simp [hiv, getLsbD_ge x i (by omega)]
theorem setWidth_eq_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} (h : w v) :
x.setWidth w = x.extractLsb' 0 w := by
rw [setWidth_eq_append_extractLsb']
ext i hi
simp only [getElem_cast, getElem_append]
by_cases hiv : i < v
· simp [hi]
omega
· simp [getLsbD_ge x i (by omega)]
theorem ushiftRight_eq_extractLsb'_of_lt {x : BitVec w} {n : Nat} (hn : n < w) : theorem ushiftRight_eq_extractLsb'_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
x >>> n = ((0#n) ++ (x.extractLsb' n (w - n))).cast (by omega) := by x >>> n = ((0#n) ++ (x.extractLsb' n (w - n))).cast (by omega) := by
ext i hi ext i hi
@@ -2582,120 +2282,6 @@ theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
· simp [hi'] · simp [hi']
· simp [hi', show i - n < w by omega] · simp [hi', show i - n < w by omega]
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
(x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
ext i h
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
Nat.not_lt]
intros hi
congr 1
omega
/-- Combine adjacent `~~~ (extractLsb _)'` operations into a single `~~~ (extractLsb _)'`. -/
theorem not_extractLsb'_append_not_extractLsb'_eq_not_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
(~~~ (x.extractLsb' start₂ len₂) ++ ~~~ (x.extractLsb' start₁ len₁)) =
(~~~ x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
ext i h
simp only [getElem_cast, getElem_not, getElem_extractLsb', getElem_append]
by_cases hi : i < len₁
· simp [hi]
· simp only [hi, reduceDIte, Bool.not_eq_eq_eq_not, Bool.not_not]
congr 1
omega
/-- A sign extension of `x : BitVec w` equals high bits of either `0` or `1` depending on `x.msb`,
followed by the low bits of `x`. -/
theorem signExtend_eq_append_extractLsb' {w v : Nat} {x : BitVec w} :
x.signExtend v =
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x.extractLsb' 0 (min w v)).cast (by omega) := by
ext i hi
simp only [getElem_cast]
cases hx : x.msb
· simp only [hx, signExtend_eq_setWidth_of_msb_false, getElem_setWidth, Bool.false_eq_true,
reduceIte, getElem_append, getElem_extractLsb', Nat.zero_add, getElem_zero, dite_eq_ite,
Bool.if_false_right, Bool.iff_and_self, decide_eq_true_eq]
intros hi
have hw : i < w := lt_of_getLsbD hi
omega
· simp [signExtend_eq_not_setWidth_not_of_msb_true hx, getElem_append, Nat.lt_min, hi]
/-- A sign extension of `x : BitVec w` to a larger bitwidth `v ≥ w`
equals high bits of either `0` or `1` depending on `x.msb`, followed by `x`. -/
theorem signExtend_eq_append_of_le {w v : Nat} {x : BitVec w} (h : w v) :
x.signExtend v =
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x).cast (by omega) := by
ext i hi
cases hx : x.msb <;>
simp [getElem_cast, hx, getElem_append, getElem_signExtend]
/--
The 'master theorem' for extracting bits from `(xhi ++ xlo)`,
which performs a case analysis on the start index, length, and the lengths of `xlo, xhi`.
· If the start index is entirely out of the `xlo` bitvector, then grab the bits from `xhi`.
· If the start index is entirely contained in the `xlo` bitvector, then grab the bits from `xlo`.
· If the start index is split between the two bitvectors,
then append `(w - start)` bits from `xlo` with `(len - (w - start))` bits from xhi.
Diagramatically:
```
xhi xlo
(<---------------------](<-------w--------]
start+len..start: (<-----len---*------]
w - start: *------*
len - (w -start): *------------*
```
-/
theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start len : Nat} :
extractLsb' start len (xhi ++ xlo) =
if hstart : start < w
then
if hlen : start + len < w
then extractLsb' start len xlo
else
(((extractLsb' (start - w) (len - (w - start)) xhi) ++
extractLsb' start (w - start) xlo)).cast (by omega)
else
extractLsb' (start - w) len xhi := by
by_cases hstart : start < w
· simp only [hstart, reduceDIte]
by_cases hlen : start + len < w
· simp only [hlen, reduceDIte]
ext i hi
simp only [getElem_extractLsb', getLsbD_append, ite_eq_left_iff, Nat.not_lt]
intros hcontra
omega
· simp only [hlen, reduceDIte]
ext i hi
simp only [getElem_extractLsb', getLsbD_append, getElem_cast,
getElem_append, dite_eq_ite]
by_cases hi₂ : start + i < w
· simp [hi₂, show i < min len w by omega, show i < w - start by omega]
· simp [hi₂, reduceIte, show ¬i < w - start by omega,
show start + i - w = start - w + (i - (w - start)) by omega]
· simp only [hstart, reduceDIte]
ext i hi
simp [getElem_extractLsb', getLsbD_append,
show ¬start + i < w by omega, reduceIte,
show start + i - w = start - w + i by omega]
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
the bits from `xlo` when `start + len` is within `xlo`.
-/
theorem extractLsb'_append_eq_of_lt {v w} {xhi : BitVec v} {xlo : BitVec w}
{start len : Nat} (h : start + len < w) :
extractLsb' start len (xhi ++ xlo) = extractLsb' start len xlo := by
simp [extractLsb'_append_eq_ite, h]
omega
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
the bits from `xhi` when `start` is outside `xlo`.
-/
theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
{start len : Nat} (h : w start) :
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
simp [extractLsb'_append_eq_ite, h, show ¬ start < w by omega]
/-! ### rev -/ /-! ### rev -/
theorem getLsbD_rev (x : BitVec w) (i : Fin w) : theorem getLsbD_rev (x : BitVec w) (i : Fin w) :
@@ -2727,7 +2313,7 @@ theorem getMsbD_rev (x : BitVec w) (i : Fin w) :
/-- Variant of `toNat_cons` using `+` instead of `|||`. -/ /-- Variant of `toNat_cons` using `+` instead of `|||`. -/
theorem toNat_cons' {x : BitVec w} : theorem toNat_cons' {x : BitVec w} :
(cons a x).toNat = (a.toNat <<< w) + x.toNat := by (cons a x).toNat = (a.toNat <<< w) + x.toNat := by
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.two_pow_add_eq_or_of_lt, x.isLt] simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.mul_add_lt_is_or, x.isLt]
theorem getLsbD_cons (b : Bool) {n} (x : BitVec n) (i : Nat) : theorem getLsbD_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
getLsbD (cons b x) i = if i = n then b else getLsbD x i := by getLsbD (cons b x) i = if i = n then b else getLsbD x i := by
@@ -3107,9 +2693,6 @@ theorem toInt_neg {x : BitVec w} :
rw [ BitVec.zero_sub, toInt_sub] rw [ BitVec.zero_sub, toInt_sub]
simp [BitVec.toInt_ofNat] simp [BitVec.toInt_ofNat]
theorem ofInt_neg {w : Nat} {n : Int} : BitVec.ofInt w (-n) = -BitVec.ofInt w n :=
eq_of_toInt_eq (by simp [toInt_neg])
@[simp] theorem toFin_neg (x : BitVec n) : @[simp] theorem toFin_neg (x : BitVec n) :
(-x).toFin = Fin.ofNat' (2^n) (2^n - x.toNat) := (-x).toFin = Fin.ofNat' (2^n) (2^n - x.toNat) :=
rfl rfl
@@ -3157,9 +2740,6 @@ theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1#w := by
have hx : x.toNat < 2^w := x.isLt have hx : x.toNat < 2^w := x.isLt
rw [Nat.sub_sub, Nat.add_comm 1 x.toNat, Nat.sub_sub, Nat.sub_add_cancel (by omega)] rw [Nat.sub_sub, Nat.add_comm 1 x.toNat, Nat.sub_sub, Nat.sub_add_cancel (by omega)]
theorem not_eq_neg_add (x : BitVec w) : ~~~ x = -x - 1#w := by
rw [eq_sub_iff_add_eq, neg_eq_not_add, BitVec.add_comm]
@[simp] @[simp]
theorem neg_neg {x : BitVec w} : - - x = x := by theorem neg_neg {x : BitVec w} : - - x = x := by
by_cases h : x = 0#w by_cases h : x = 0#w
@@ -3212,22 +2792,6 @@ theorem not_neg (x : BitVec w) : ~~~(-x) = x + -1#w := by
show (_ - x.toNat) % _ = _ by rw [Nat.mod_eq_of_lt (by omega)]] show (_ - x.toNat) % _ = _ by rw [Nat.mod_eq_of_lt (by omega)]]
omega omega
theorem neg_add {x y : BitVec w} : - (x + y) = - x - y := by
apply eq_of_toInt_eq
simp [toInt_neg, toInt_add, Int.neg_add, Int.add_neg_eq_sub]
theorem add_neg_eq_sub {x y : BitVec w} : x + - y = (x - y) := by
apply eq_of_toInt_eq
simp [toInt_neg, Int.sub_eq_add_neg]
@[simp]
theorem sub_neg {x y : BitVec w} : x - - y = x + y := by
apply eq_of_toInt_eq
simp [toInt_neg, Int.bmod_neg]
theorem neg_sub {x y : BitVec w} : - (x - y) = - x + y := by
rw [sub_toAdd, neg_add, sub_neg]
/- ### add/sub injectivity -/ /- ### add/sub injectivity -/
@[simp] @[simp]
@@ -3387,7 +2951,7 @@ theorem mul_eq_and {a b : BitVec 1} : a * b = a &&& b := by
@[simp] protected theorem neg_mul (x y : BitVec w) : -x * y = -(x * y) := by @[simp] protected theorem neg_mul (x y : BitVec w) : -x * y = -(x * y) := by
apply eq_of_toInt_eq apply eq_of_toInt_eq
simp [toInt_neg, Int.neg_mul] simp [toInt_neg]
@[simp] protected theorem mul_neg (x y : BitVec w) : x * -y = -(x * y) := by @[simp] protected theorem mul_neg (x y : BitVec w) : x * -y = -(x * y) := by
rw [BitVec.mul_comm, BitVec.neg_mul, BitVec.mul_comm] rw [BitVec.mul_comm, BitVec.neg_mul, BitVec.mul_comm]
@@ -3396,24 +2960,6 @@ protected theorem neg_mul_neg (x y : BitVec w) : -x * -y = x * y := by simp
protected theorem neg_mul_comm (x y : BitVec w) : -x * y = x * -y := by simp protected theorem neg_mul_comm (x y : BitVec w) : -x * y = x * -y := by simp
theorem mul_sub {x y z : BitVec w} :
x * (y - z) = x * y - x * z := by
rw [ add_neg_eq_sub, mul_add, BitVec.mul_neg, add_neg_eq_sub]
theorem neg_add_mul_eq_mul_not {x y : BitVec w} :
- (x + x * y) = x * ~~~ y := by
rw [neg_add, sub_toAdd, BitVec.mul_neg, neg_eq_not_add y, mul_add,
BitVec.mul_one, BitVec.add_comm, BitVec.add_assoc,
BitVec.add_right_eq_self, add_neg_eq_sub, BitVec.sub_self]
theorem neg_mul_not_eq_add_mul {x y : BitVec w} :
- (x * ~~~ y) = x + x * y := by
rw [not_eq_neg_add, mul_sub, neg_sub, BitVec.mul_neg, neg_neg,
BitVec.mul_one, BitVec.add_comm]
theorem neg_eq_neg_one_mul (b : BitVec w) : -b = -1#w * b :=
BitVec.eq_of_toInt_eq (by simp)
/-! ### le and lt -/ /-! ### le and lt -/
@[bitvec_to_nat] theorem le_def {x y : BitVec n} : @[bitvec_to_nat] theorem le_def {x y : BitVec n} :
@@ -3518,12 +3064,6 @@ theorem allOnes_le_iff {x : BitVec w} : allOnes w ≤ x ↔ x = allOnes w := by
exact Eq.symm (BitVec.le_antisymm h this) exact Eq.symm (BitVec.le_antisymm h this)
· simp_all · simp_all
@[simp]
theorem lt_allOnes_iff {x : BitVec w} : x < allOnes w x allOnes w := by
have := not_congr (@allOnes_le_iff w x)
rw [BitVec.not_le] at this
exact this
/-! ### udiv -/ /-! ### udiv -/
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
@@ -3611,7 +3151,6 @@ then `x / y` is nonnegative, thus `toInt` and `toNat` coincide.
theorem toInt_udiv_of_msb {x : BitVec w} (h : x.msb = false) (y : BitVec w) : theorem toInt_udiv_of_msb {x : BitVec w} (h : x.msb = false) (y : BitVec w) :
(x / y).toInt = x.toNat / y.toNat := by (x / y).toInt = x.toNat / y.toNat := by
simp [toInt_eq_msb_cond, msb_udiv_eq_false_of h] simp [toInt_eq_msb_cond, msb_udiv_eq_false_of h]
norm_cast
/-! ### umod -/ /-! ### umod -/
@@ -4174,22 +3713,6 @@ theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
have h1 : 1 < 2 ^ (w + 1) := Nat.one_lt_two_pow (by omega) have h1 : 1 < 2 ^ (w + 1) := Nat.one_lt_two_pow (by omega)
rw [Nat.mod_eq_of_lt h1, Nat.shiftLeft_eq, Nat.one_mul] rw [Nat.mod_eq_of_lt h1, Nat.shiftLeft_eq, Nat.one_mul]
theorem toNat_twoPow_of_le {i w : Nat} (h : w i) : (twoPow w i).toNat = 0 := by
rw [toNat_twoPow]
apply Nat.mod_eq_zero_of_dvd
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
theorem toNat_twoPow_of_lt {i w : Nat} (h : i < w) : (twoPow w i).toNat = 2^i := by
rw [toNat_twoPow]
apply Nat.mod_eq_of_lt
apply Nat.pow_lt_pow_of_lt (by omega) (by omega)
theorem toNat_twoPow_eq_ite {i w : Nat} : (twoPow w i).toNat = if i < w then 2^i else 0 := by
by_cases h : i < w
· simp only [h, toNat_twoPow_of_lt, if_true]
· simp only [h, if_false]
rw [toNat_twoPow_of_le (by omega)]
@[simp] @[simp]
theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j)) := by theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j)) := by
rcases w with rfl | w rcases w with rfl | w
@@ -4208,33 +3731,6 @@ theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j
simp at hi simp at hi
simp_all simp_all
@[simp]
theorem msb_twoPow {i w: Nat} :
(twoPow w i).msb = (decide (i < w) && decide (i = w - 1)) := by
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.sub_zero, getLsbD_twoPow,
Bool.and_iff_right_iff_imp, Bool.and_eq_true, decide_eq_true_eq, and_imp]
intros
omega
theorem toInt_twoPow {w i : Nat} :
(BitVec.twoPow w i).toInt = if w i then 0
else if i + 1 = w then (-(2^i : Nat) : Int) else 2^i := by
simp only [BitVec.toInt_eq_msb_cond, toNat_twoPow_eq_ite]
rcases w with _ | w
· simp
· by_cases h : i = w
· simp [h, show ¬ (w + 1 w) by omega]
omega
· by_cases h' : w + 1 i
· simp [h', show ¬ i < w + 1 by omega]
· simp [h, h', show i < w + 1 by omega, Int.natCast_pow]
theorem toFin_twoPow {w i : Nat} :
(BitVec.twoPow w i).toFin = Fin.ofNat' (2^w) (2^i) := by
rcases w with rfl | w
· simp [BitVec.twoPow, BitVec.toFin, toFin_shiftLeft, Fin.fin_one_eq_zero]
· simp [BitVec.twoPow, BitVec.toFin, toFin_shiftLeft, Nat.shiftLeft_eq]
@[simp] @[simp]
theorem getElem_twoPow {i j : Nat} (h : j < w) : (twoPow w i)[j] = decide (j = i) := by theorem getElem_twoPow {i j : Nat} (h : j < w) : (twoPow w i)[j] = decide (j = i) := by
rw [getLsbD_eq_getElem, getLsbD_twoPow] rw [getLsbD_eq_getElem, getLsbD_twoPow]
@@ -4248,6 +3744,14 @@ theorem getMsbD_twoPow {i j w: Nat} :
by_cases h₀ : i < w <;> by_cases h₁ : j < w <;> by_cases h₀ : i < w <;> by_cases h₁ : j < w <;>
simp [h₀, h₁] <;> omega simp [h₀, h₁] <;> omega
@[simp]
theorem msb_twoPow {i w: Nat} :
(twoPow w i).msb = (decide (i < w) && decide (i = w - 1)) := by
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.sub_zero, getLsbD_twoPow,
Bool.and_iff_right_iff_imp, Bool.and_eq_true, decide_eq_true_eq, and_imp]
intros
omega
theorem and_twoPow (x : BitVec w) (i : Nat) : theorem and_twoPow (x : BitVec w) (i : Nat) :
x &&& (twoPow w i) = if x.getLsbD i then twoPow w i else 0#w := by x &&& (twoPow w i) = if x.getLsbD i then twoPow w i else 0#w := by
ext j h ext j h
@@ -4299,10 +3803,6 @@ theorem udiv_twoPow_eq_of_lt {w : Nat} {x : BitVec w} {k : Nat} (hk : k < w) : x
have : 2^k < 2^w := Nat.pow_lt_pow_of_lt (by decide) hk have : 2^k < 2^w := Nat.pow_lt_pow_of_lt (by decide) hk
simp [bitvec_to_nat, Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt this] simp [bitvec_to_nat, Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt this]
theorem shiftLeft_neg {x : BitVec w} {y : Nat} :
(-x) <<< y = - (x <<< y) := by
rw [shiftLeft_eq_mul_twoPow, shiftLeft_eq_mul_twoPow, BitVec.neg_mul]
/- ### cons -/ /- ### cons -/
@[simp] theorem true_cons_zero : cons true 0#w = twoPow (w + 1) w := by @[simp] theorem true_cons_zero : cons true 0#w = twoPow (w + 1) w := by
@@ -4423,15 +3923,6 @@ theorem replicate_succ' {x : BitVec w} :
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by (replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
simp [replicate_append_self] simp [replicate_append_self]
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
apply BitVec.eq_of_toNat_eq
rw [toNat_setWidth]
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
by_cases h : i w
· rw [Nat.mod_eq_zero_of_dvd (Nat.pow_dvd_pow 2 h), Nat.mod_zero, Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 h)]
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
/-! ### intMin -/ /-! ### intMin -/
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/ /-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
@@ -4459,6 +3950,7 @@ theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
/-- /--
The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `... % 2 ^ w`. The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `... % 2 ^ w`.
-/ -/
@[simp]
theorem toInt_intMin {w : Nat} : theorem toInt_intMin {w : Nat} :
(intMin w).toInt = -((2 ^ (w - 1) % 2 ^ w) : Nat) := by (intMin w).toInt = -((2 ^ (w - 1) % 2 ^ w) : Nat) := by
by_cases h : w = 0 by_cases h : w = 0
@@ -4470,16 +3962,10 @@ theorem toInt_intMin {w : Nat} :
rw [Nat.mul_comm] rw [Nat.mul_comm]
simp [w_pos] simp [w_pos]
theorem toInt_intMin_of_pos {v : Nat} (hv : 0 < v) : (intMin v).toInt = -2 ^ (v - 1) := by
rw [toInt_intMin, Nat.mod_eq_of_lt]
· simp [Int.natCast_pow]
· rw [Nat.pow_lt_pow_iff_right (by omega)]
omega
theorem toInt_intMin_le (x : BitVec w) : theorem toInt_intMin_le (x : BitVec w) :
(intMin w).toInt x.toInt := by (intMin w).toInt x.toInt := by
cases w cases w
case zero => simp [toInt_intMin, @of_length_zero x] case zero => simp [@of_length_zero x]
case succ w => case succ w =>
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod] simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod]
have : 0 < 2 ^ w := Nat.two_pow_pos w have : 0 < 2 ^ w := Nat.two_pow_pos w
@@ -4623,7 +4109,9 @@ theorem sub_le_sub_iff_le {x y z : BitVec w} (hxz : z ≤ x) (hyz : z ≤ y) :
theorem msb_eq_toInt {x : BitVec w}: theorem msb_eq_toInt {x : BitVec w}:
x.msb = decide (x.toInt < 0) := by x.msb = decide (x.toInt < 0) := by
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond] <;> omega by_cases h : x.msb <;>
· simp [h, toInt_eq_msb_cond]
omega
theorem msb_eq_toNat {x : BitVec w}: theorem msb_eq_toNat {x : BitVec w}:
x.msb = decide (x.toNat 2 ^ (w - 1)) := by x.msb = decide (x.toNat 2 ^ (w - 1)) := by
@@ -4858,9 +4346,6 @@ instance instDecidableExistsBitVec :
set_option linter.missingDocs false set_option linter.missingDocs false
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
abbrev toFin_uShiftRight := @toFin_ushiftRight
@[deprecated signExtend_eq_setWidth_of_msb_false (since := "2024-12-08")] @[deprecated signExtend_eq_setWidth_of_msb_false (since := "2024-12-08")]
abbrev signExtend_eq_not_setWidth_not_of_msb_false := @signExtend_eq_setWidth_of_msb_false abbrev signExtend_eq_not_setWidth_not_of_msb_false := @signExtend_eq_setWidth_of_msb_false
@@ -4973,7 +4458,7 @@ abbrev signExtend_eq_not_zeroExtend_not_of_msb_false := @signExtend_eq_setWidth
abbrev signExtend_eq_not_zeroExtend_not_of_msb_true := @signExtend_eq_not_setWidth_not_of_msb_true abbrev signExtend_eq_not_zeroExtend_not_of_msb_true := @signExtend_eq_not_setWidth_not_of_msb_true
@[deprecated signExtend_eq_setWidth_of_lt (since := "2024-09-18")] @[deprecated signExtend_eq_setWidth_of_lt (since := "2024-09-18")]
abbrev signExtend_eq_truncate_of_lt := @signExtend_eq_setWidth_of_le abbrev signExtend_eq_truncate_of_lt := @signExtend_eq_setWidth_of_lt
@[deprecated truncate_append (since := "2024-09-18")] @[deprecated truncate_append (since := "2024-09-18")]
abbrev truncate_append := @setWidth_append abbrev truncate_append := @setWidth_append

View File

@@ -9,19 +9,7 @@ import Init.NotationExtra
namespace Bool namespace Bool
/-- /-- Boolean exclusive or -/
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`
-/
abbrev xor : Bool Bool Bool := bne abbrev xor : Bool Bool Bool := bne
@[inherit_doc] infixl:33 " ^^ " => xor @[inherit_doc] infixl:33 " ^^ " => xor
@@ -379,9 +367,7 @@ theorem and_or_inj_left_iff :
/-! ## toNat -/ /-! ## toNat -/
/-- /-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
Converts `true` to `1` and `false` to `0`.
-/
def toNat (b : Bool) : Nat := cond b 1 0 def toNat (b : Bool) : Nat := cond b 1 0
@[simp, bitvec_to_nat] theorem toNat_false : false.toNat = 0 := rfl @[simp, bitvec_to_nat] theorem toNat_false : false.toNat = 0 := rfl
@@ -402,9 +388,7 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
/-! ## toInt -/ /-! ## toInt -/
/-- /-- convert a `Bool` to an `Int`, `false -> 0`, `true -> 1` -/
Converts `true` to `1` and `false` to `0`.
-/
def toInt (b : Bool) : Int := cond b 1 0 def toInt (b : Bool) : Int := cond b 1 0
@[simp] theorem toInt_false : false.toInt = 0 := rfl @[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), @[simp] theorem cond_eq_false_distrib : (c t f : Bool),
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide (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_true {α : Type 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_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_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 @[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 namespace ByteArray
@[extern "lean_mk_empty_byte_array"] @[extern "lean_mk_empty_byte_array"]
def emptyWithCapacity (c : @& Nat) : ByteArray := def mkEmpty (c : @& Nat) : ByteArray :=
{ data := #[] } { data := #[] }
@[deprecated emptyWithCapacity (since := "2025-03-12")] def empty : ByteArray := mkEmpty 0
abbrev mkEmpty := emptyWithCapacity
def empty : ByteArray := emptyWithCapacity 0
instance : Inhabited ByteArray where instance : Inhabited ByteArray where
default := empty default := empty
@@ -337,9 +334,6 @@ def prevn : Iterator → Nat → Iterator
end Iterator end Iterator
end ByteArray end ByteArray
/--
Converts a list of bytes into a `ByteArray`.
-/
def List.toByteArray (bs : List UInt8) : ByteArray := def List.toByteArray (bs : List UInt8) : ByteArray :=
let rec loop let rec loop
| [], r => r | [], r => r

View File

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

View File

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

View File

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

View File

@@ -10,18 +10,10 @@ import Init.Data.Fin.Basic
namespace Fin namespace Fin
/-- /--
Applies an index-dependent function `f` to all of the values in `[i:n]`, starting at `i` with an `hIterateFrom f i bnd a` applies `f` over indices `[i:n]` to compute `P n`
initial accumulator `a`. from `P i`.
Concretely, `Fin.hIterateFrom P f i a` is equal to See `hIterate` below for more details.
```lean
a |> f i |> f (i + 1) |> ... |> f (n - 1)
```
Theorems about `Fin.hIterateFrom` can be proven using the general theorem `Fin.hIterateFrom_elim` or
other more specialized theorems.
`Fin.hIterate` is a variant that always starts at `0`.
-/ -/
def hIterateFrom (P : Nat Sort _) {n} (f : (i : Fin n), P i.val P (i.val+1)) 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 := (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 decreasing_by decreasing_trivial_pre_omega
/-- /--
Applies an index-dependent function to all the values less than the given bound `n`, starting at `hIterate` is a heterogeneous iterative operation that applies a
`0` with an accumulator. 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 ```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 Because it is heterogeneous and must return a value of type `P stop`,
specialized theorems. `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)) : def hIterate (P : Nat Sort _) {n : Nat} (init : P 0) (f : (i : Fin n), P i.val P (i.val+1)) :
P n := 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 := 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 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} : protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
(a, ha : Fin n) = b, hb a = b := Fin.ext_iff (a, ha : Fin n) = b, hb a = b := 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 .. 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) : @[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat' n a).val = a % n := rfl (Fin.ofNat' n a).val = a % n := rfl
@@ -670,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) : @[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] natAdd n (subNat n (i.cast (Nat.add_comm ..)) h) = i := by simp [ cast_addNat]
/-! ### Recursion and induction principles -/ /-! ### recursion and induction principles -/
/-- /-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i` This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0` of an
applications of `Fin.succ`. `(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. -/
The cases in the induction are:
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
bound `n`
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
step. `Fin.succRecOn` is a version of this induction principle that takes the `Fin` argument first.
-/
-- FIXME: Performance review -- FIXME: Performance review
@[elab_as_elim] def succRec {motive : n, Fin n Sort _} @[elab_as_elim] def succRec {motive : n, Fin n Sort _}
(zero : n, motive n.succ (0 : Fin (n + 1))) (zero : n, motive n.succ (0 : Fin (n + 1)))
@@ -692,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 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) | Nat.succ _, Nat.succ i, h => succ _ _ (succRec zero succ i, Nat.lt_of_succ_lt_succ h)
/-- /-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i` This function has two arguments:
applications of `Fin.succ`. `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: A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
bound `n`
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
step. `Fin.succRec` is a version of this induction principle that takes the `Fin` argument last.
-/
-- FIXME: Performance review -- FIXME: Performance review
@[elab_as_elim] def succRecOn {n : Nat} (i : Fin n) {motive : n, Fin n Sort _} @[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) : (zero : n, motive (n + 1) 0) (succ : n i, motive n i motive (Nat.succ n) i.succ) :
@@ -718,17 +696,9 @@ step. `Fin.succRec` is a version of this induction principle that takes the `Fin
cases i; rfl cases i; rfl
/-- /-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`. 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.inductionOn` is a version of this induction principle that takes the `Fin` as its first
parameter, `Fin.cases` is the corresponding case analysis operator, and `Fin.reverseInduction` is a
version that starts at the greatest value instead of `0`.
-/ -/
-- FIXME: Performance review -- FIXME: Performance review
@[elab_as_elim] def induction {motive : Fin (n + 1) Sort _} (zero : motive 0) @[elab_as_elim] def induction {motive : Fin (n + 1) Sort _} (zero : motive 0)
@@ -749,30 +719,18 @@ where
(succ : i : Fin n, motive (castSucc i) motive i.succ) (i : Fin n) : (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 induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
/-- /-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`. 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: A version of `Fin.induction` taking `i : Fin (n + 1)` as the first argument.
* `zero` is the base case, demonstrating `motive 0`.
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
`Fin.castSucc`) and demonstrating it for `i.succ`.
`Fin.induction` is a version of this induction principle that takes the `Fin` as its last
parameter.
-/ -/
-- FIXME: Performance review -- FIXME: Performance review
@[elab_as_elim] def inductionOn (i : Fin (n + 1)) {motive : Fin (n + 1) Sort _} (zero : motive 0) @[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 (succ : i : Fin n, motive (castSucc i) motive i.succ) : motive i := induction zero succ i
/-- /-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = 0` and
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`. `i = j.succ`, `j : Fin n`. -/
The two cases are:
* `zero`, used when the value is of the form `(0 : Fin (n + 1))`
* `succ`, used when the value is of the form `(j : Fin n).succ`
The corresponding induction principle is `Fin.induction`.
-/
@[elab_as_elim] def cases {motive : Fin (n + 1) Sort _} @[elab_as_elim] def cases {motive : Fin (n + 1) Sort _}
(zero : motive 0) (succ : i : Fin n, motive i.succ) : (zero : motive 0) (succ : i : Fin n, motive i.succ) :
i : Fin (n + 1), motive i := induction zero fun i _ => succ i i : Fin (n + 1), motive i := induction zero fun i _ => succ i
@@ -810,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 simp only [forall_fin_two]; decide
/-- /--
Proves a statement by reverse induction on the underlying `Nat` value in a `Fin (n + 1)`. 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)`,
For the induction: and `cast` defines the inductive step using `motive i.succ`, inducting downwards.
* `last` is the base case, demonstrating `motive (Fin.last n)`.
* `cast` is the inductive step, assuming the motive for `(j : Fin n).succ` and demonstrating it for
the predecessor `j.castSucc`.
`Fin.induction` is the non-reverse induction principle.
-/ -/
@[elab_as_elim] def reverseInduction {motive : Fin (n + 1) Sort _} (last : motive (Fin.last n)) @[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 := (cast : i : Fin n, motive i.succ motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
@@ -840,16 +793,8 @@ decreasing_by decreasing_with
succ i (reverseInduction zero succ i.succ) := by succ i (reverseInduction zero succ i.succ) := by
rw [reverseInduction, dif_neg (Fin.ne_of_lt (Fin.castSucc_lt_last i))]; rfl rw [reverseInduction, dif_neg (Fin.ne_of_lt (Fin.castSucc_lt_last i))]; rfl
/-- /-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = Fin.last n` and
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`, checking whether the `i = j.castSucc`, `j : Fin n`. -/
value is the greatest representable or a predecessor of some other.
The two cases are:
* `last`, used when the value is `Fin.last n`
* `cast`, used when the value is of the form `(j : Fin n).succ`
The corresponding induction principle is `Fin.reverseInduction`.
-/
@[elab_as_elim] def lastCases {n : Nat} {motive : Fin (n + 1) Sort _} (last : motive (Fin.last n)) @[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 := (cast : i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
reverseInduction last (fun i _ => cast i) i reverseInduction last (fun i _ => cast i) i
@@ -862,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 := (i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
reverseInduction_castSucc .. reverseInduction_castSucc ..
/-- /-- Define `f : Π i : Fin (m + n), motive i` by separately handling the cases `i = castAdd n i`,
A case analysis operator for `i : Fin (m + n)` that separately handles the cases where `i < m` and `j : Fin m` and `i = natAdd m j`, `j : Fin n`. -/
where `m ≤ i < m + n`.
The first case, where `i < m`, is handled by `left`. In this case, `i` can be represented as
`Fin.castAdd n (j : Fin m)`.
The second case, where `m ≤ i < m + n`, is handled by `right`. In this case, `i` can be represented
as `Fin.natAdd m (j : Fin n)`.
-/
@[elab_as_elim] def addCases {m n : Nat} {motive : Fin (m + n) Sort u} @[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)) (left : i, motive (castAdd n i)) (right : i, motive (natAdd m i))
(i : Fin (m + n)) : motive i := (i : Fin (m + n)) : motive i :=

View File

@@ -6,20 +6,4 @@ Authors: Henrik Böving
prelude prelude
import Init.Data.Nat.Log2 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 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

@@ -140,27 +140,15 @@ instance : ToString Float where
@[extern "lean_uint16_to_float"] opaque UInt16.toFloat (n : UInt16) : Float @[extern "lean_uint16_to_float"] opaque UInt16.toFloat (n : UInt16) : Float
/-- Obtains the `Float` whose value is the same as the given `UInt32`. -/ /-- Obtains the `Float` whose value is the same as the given `UInt32`. -/
@[extern "lean_uint32_to_float"] opaque UInt32.toFloat (n : UInt32) : Float @[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
Obtains a `Float` whose value is near the given `UInt64`. 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
It will be exactly the value of the given `UInt64` if such a `Float` exists. If no such `Float` than the given value. -/
exists, the returned value will either be the smallest `Float` that is larger than the given value,
or the largest `Float` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float @[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
Obtains a `Float` whose value is near the given `USize`. 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
It will be exactly the value of the given `USize` if such a `Float` exists. If no such `Float` than the given value. -/
exists, the returned value will either be the smallest `Float` that is larger than the given value,
or the largest `Float` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_usize_to_float"] opaque USize.toFloat (n : USize) : Float @[extern "lean_usize_to_float"] opaque USize.toFloat (n : USize) : Float
instance : Inhabited Float where instance : Inhabited Float where

View File

@@ -131,37 +131,20 @@ instance : ToString Float32 where
@[extern "lean_uint8_to_float32"] opaque UInt8.toFloat32 (n : UInt8) : Float32 @[extern "lean_uint8_to_float32"] opaque UInt8.toFloat32 (n : UInt8) : Float32
/-- Obtains the `Float32` whose value is the same as the given `UInt16`. -/ /-- Obtains the `Float32` whose value is the same as the given `UInt16`. -/
@[extern "lean_uint16_to_float32"] opaque UInt16.toFloat32 (n : UInt16) : Float32 @[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
Obtains a `Float32` whose value is near the given `UInt32`. 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
It will be exactly the value of the given `UInt32` if such a `Float32` exists. If no such `Float32` than the given value. -/
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_uint32_to_float32"] opaque UInt32.toFloat32 (n : UInt32) : Float32 @[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
Obtains a `Float32` whose value is near the given `UInt64`. 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
It will be exactly the value of the given `UInt64` if such a `Float32` exists. If no such `Float32` than the given value. -/
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_uint64_to_float32"] opaque UInt64.toFloat32 (n : UInt64) : Float32 @[extern "lean_uint64_to_float32"] opaque UInt64.toFloat32 (n : UInt64) : Float32
/-- Obtains a `Float32` whose value is near the given `USize`. /-- 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
It will be exactly the value of the given `USize` if such a `Float32` exists. If no such `Float32` be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
exists, the returned value will either be the smallest `Float32` that is larger than the given than the given value. -/
value, or the largest `Float32` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_usize_to_float32"] opaque USize.toFloat32 (n : USize) : Float32 @[extern "lean_usize_to_float32"] opaque USize.toFloat32 (n : USize) : Float32
instance : Inhabited Float32 where instance : Inhabited Float32 where

View File

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

View File

@@ -23,12 +23,6 @@ instance [ToFormat α] : ToFormat (List α) where
instance [ToFormat α] : ToFormat (Array α) where instance [ToFormat α] : ToFormat (Array α) where
format a := "#" ++ format a.toList 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 def Option.format {α : Type u} [ToFormat α] : Option α Format
| none => "none" | none => "none"
| some a => "some " ++ Std.format a | 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 instance {α : Type u} {β : Type v} [ToFormat α] [ToFormat β] : ToFormat (Prod α β) where
format := fun (a, b) => Format.paren <| format a ++ "," ++ Format.line ++ format b 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 := def String.toFormat (s : String) : Std.Format :=
Std.Format.joinSep (s.splitOn "\n") Std.Format.line Std.Format.joinSep (s.splitOn "\n") Std.Format.line

View File

@@ -9,23 +9,10 @@ import Init.Core
namespace Function 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] @[inline]
def curry : (α × β φ) α β φ := fun f a b => f (a, b) def curry : (α × β φ) α β φ := fun f a b => f (a, b)
/-- /-- Interpret a function with two arguments as a function on `α × β` -/
Transforms a two-parameter function into an equivalent function from pairs.
Examples:
* `Function.uncurry List.drop (1, ["a", "b", "c"]) = ["b", "c"]`
* `[("orange", 2), ("android", 3) ].map (Function.uncurry String.take) = ["or", "and"]`
-/
@[inline] @[inline]
def uncurry : (α β φ) α × β φ := fun f a => f a.1 a.2 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 := @[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
mixHash u 11 mixHash u 11
/-- /-- `LawfulHashable α` says that the `BEq α` and `Hashable α` instances on `α` are compatible, i.e.,
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies that `a == b` implies `hash a = hash b`. This is automatic if the `BEq` instance is lawful.
`hash a = hash b`.
This is automatic if the `BEq` instance is lawful.
-/ -/
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
/-- If `a == b`, then `hash a = hash b`. -/ /-- If `a == b`, then `hash a = hash b`. -/
hash_eq (a b : α) : a == b 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 := theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b hash a = hash b :=
LawfulHashable.hash_eq a 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.Pow
import Init.Data.Int.Cooper import Init.Data.Int.Cooper
import Init.Data.Int.Linear 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 This file defines the `Int` type as well as
* coercions, conversions, and compatibility with numeric literals, * 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`, * a few `Nat`-related operations such as `negOfNat` and `subNatNat`,
* relations `<`/`≤`/`≥`/`>`, the `NonNeg` property and `min`/`max`, * relations `<`/`≤`/`≥`/`>`, the `NonNeg` property and `min`/`max`,
* decidability of equality, relations and `NonNeg`. * 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 This type is special-cased by the compiler. The runtime has a special
runtime has a special representation for `Int` that stores small signed numbers directly, while representation for `Int` which stores "small" signed numbers directly,
larger numbers use a fast arbitrary-precision arithmetic library (usually and larger numbers use an arbitrary precision "bignum" library
[GMP](https://gmplib.org/)). A small number is an integer that can be encoded with one fewer bits (usually [GMP](https://gmplib.org/)). A "small number" is an integer
than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit that can be encoded with 63 bits (31 bits on 32-bits architectures).
architectures).
-/ -/
inductive Int : Type where inductive Int : Type where
/-- /-- A natural number is an integer (`0` to `∞`). -/
A natural number is an integer.
This constructor covers the non-negative integers (from `0` to `∞`).
-/
| ofNat : Nat Int | ofNat : Nat Int
/-- /-- The negation of the successor of a natural number is an integer
The negation of the successor of a natural number is an integer. (`-1` to `-∞`). -/
This constructor covers the negative integers (from `-1` to `-∞`).
-/
| negSucc : Nat Int | negSucc : Nat Int
attribute [extern "lean_nat_to_int"] Int.ofNat 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 theorem ofNat_two : ((2 : Nat) : Int) = 2 := rfl
/-- /-- Negation of a natural number. -/
Negation of natural numbers.
Examples:
* `Int.negOfNat 6 = -6`
* `Int.negOfNat 0 = 0`
-/
def negOfNat : Nat Int def negOfNat : Nat Int
| 0 => 0 | 0 => 0
| succ m => negSucc m | succ m => negSucc m
set_option bootstrap.genMatcherCode false in set_option bootstrap.genMatcherCode false in
/-- /-- Negation of an integer.
Negation of integers, usually accessed via the `-` prefix operator.
This function is overridden by the compiler with an efficient implementation. This definition is Implemented by efficient native code. -/
the logical model.
Examples:
* `-(6 : Int) = -6`
* `-(-6 : Int) = 6`
* `(12 : Int).neg = -12`
-/
@[extern "lean_int_neg"] @[extern "lean_int_neg"]
protected def neg (n : @& Int) : Int := protected def neg (n : @& Int) : Int :=
match n with match n with
@@ -122,30 +103,21 @@ protected def neg (n : @& Int) : Int :=
instance instNegInt : Neg Int where instance instNegInt : Neg Int where
neg := Int.neg neg := Int.neg
/-- /-- Subtraction of two natural numbers. -/
Non-truncating subtraction of two natural numbers.
Examples:
* `Int.subNatNat 5 2 = 3`
* `Int.subNatNat 2 5 = -3`
* `Int.subNatNat 0 13 = -13`
-/
def subNatNat (m n : Nat) : Int := def subNatNat (m n : Nat) : Int :=
match (n - m : Nat) with match (n - m : Nat) with
| 0 => ofNat (m - n) -- m ≥ n | 0 => ofNat (m - n) -- m ≥ n
| (succ k) => negSucc k | (succ k) => negSucc k
set_option bootstrap.genMatcherCode false in set_option bootstrap.genMatcherCode false in
/-- /-- Addition of two integers.
Addition of integers, usually accessed via the `+` operator.
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: Implemented by efficient native code. -/
* `(7 : Int) + (6 : Int) = 13`
* `(6 : Int) + (-6 : Int) = 0`
-/
@[extern "lean_int_add"] @[extern "lean_int_add"]
protected def add (m n : @& Int) : Int := protected def add (m n : @& Int) : Int :=
match m, n with match m, n with
@@ -158,17 +130,15 @@ instance : Add Int where
add := Int.add add := Int.add
set_option bootstrap.genMatcherCode false in set_option bootstrap.genMatcherCode false in
/-- /-- Multiplication of two integers.
Multiplication of integers, usually accessed via the `*` operator.
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: Implemented by efficient native code. -/
* `(63 : Int) * (6 : Int) = 378`
* `(6 : Int) * (-6 : Int) = -36`
* `(7 : Int) * (0 : Int) = 0`
-/
@[extern "lean_int_mul"] @[extern "lean_int_mul"]
protected def mul (m n : @& Int) : Int := protected def mul (m n : @& Int) : Int :=
match m, n with match m, n with
@@ -180,65 +150,48 @@ protected def mul (m n : @& Int) : Int :=
instance : Mul Int where instance : Mul Int where
mul := Int.mul 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 Implemented by efficient native code. -/
the logical model.
Examples:
* `(63 : Int) - (6 : Int) = 57`
* `(7 : Int) - (0 : Int) = 7`
* `(0 : Int) - (7 : Int) = -7`
-/
@[extern "lean_int_sub"] @[extern "lean_int_sub"]
protected def sub (m n : @& Int) : Int := m + (- n) protected def sub (m n : @& Int) : Int := m + (- n)
instance : Sub Int where instance : Sub Int where
sub := Int.sub sub := Int.sub
/-- /-- A proof that an `Int` is non-negative. -/
An integer is non-negative if it is equal to a natural number.
-/
inductive NonNeg : Int Prop where inductive NonNeg : Int Prop where
/-- /-- Sole constructor, proving that `ofNat n` is positive. -/
For all natural numbers `n`, `Int.ofNat n` is non-negative.
-/
| mk (n : Nat) : NonNeg (ofNat n) | mk (n : Nat) : NonNeg (ofNat n)
/-- /-- Definition of `a ≤ b`, encoded as `b - a ≥ 0`. -/
Non-strict inequality of integers, usually accessed via the `≤` operator.
`a ≤ b` is defined as `b - a ≥ 0`, using `Int.NonNeg`.
-/
protected def le (a b : Int) : Prop := NonNeg (b - a) protected def le (a b : Int) : Prop := NonNeg (b - a)
instance instLEInt : LE Int where instance instLEInt : LE Int where
le := Int.le le := Int.le
/-- /-- Definition of `a < b`, encoded as `a + 1 ≤ b`. -/
Strict inequality of integers, usually accessed via the `<` operator.
`a < b` when `a + 1 ≤ b`.
-/
protected def lt (a b : Int) : Prop := (a + 1) b protected def lt (a b : Int) : Prop := (a + 1) b
instance instLTInt : LT Int where instance instLTInt : LT Int where
lt := Int.lt lt := Int.lt
set_option bootstrap.genMatcherCode false in set_option bootstrap.genMatcherCode false in
/-- /-- Decides equality between two `Int`s.
Decides whether two integers are equal. Usually accessed via the `DecidableEq Int` instance.
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: Implemented by efficient native code. -/
* `show (7 : Int) = (3 : Int) + (4 : Int) by decide`
* `if (6 : Int) = (3 : Int) * (2 : Int) then "yes" else "no" = "yes"`
* `(¬ (6 : Int) = (3 : Int)) = true`
-/
@[extern "lean_int_dec_eq"] @[extern "lean_int_dec_eq"]
protected def decEq (a b : @& Int) : Decidable (a = b) := protected def decEq (a b : @& Int) : Decidable (a = b) :=
match a, b with match a, b with
@@ -251,7 +204,6 @@ protected def decEq (a b : @& Int) : Decidable (a = b) :=
| isTrue h => isTrue <| h rfl | isTrue h => isTrue <| h rfl
| isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h) | isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h)
@[inherit_doc Int.decEq]
instance : DecidableEq Int := Int.decEq instance : DecidableEq Int := Int.decEq
set_option bootstrap.genMatcherCode false in set_option bootstrap.genMatcherCode false in
@@ -297,17 +249,15 @@ instance decLt (a b : @& Int) : Decidable (a < b) :=
decNonneg _ decNonneg _
set_option bootstrap.genMatcherCode false in set_option bootstrap.genMatcherCode false in
/-- /-- Absolute value (`Nat`) of an integer.
The absolute value of an integer is its distance from `0`.
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: Implemented by efficient native code. -/
* `(7 : Int).natAbs = 7`
* `(0 : Int).natAbs = 0`
* `((-11 : Int).natAbs = 11`
-/
@[extern "lean_nat_abs"] @[extern "lean_nat_abs"]
def natAbs (m : @& Int) : Nat := def natAbs (m : @& Int) : Nat :=
match m with match m with
@@ -317,17 +267,8 @@ def natAbs (m : @& Int) : Nat :=
/-! ## sign -/ /-! ## sign -/
/-- /--
Returns the sign of the integer as another integer: Returns the "sign" of the integer as another integer: `1` for positive numbers,
* `1` for positive numbers, `-1` for negative numbers, and `0` for `0`.
* `-1` for negative numbers, and
* `0` for `0`.
Examples:
* `Int.sign 34 = 1`
* `Int.sign 2 = 1`
* `Int.sign 0 = 0`
* `Int.sign -1 = -1`
* `Int.sign -362 = -1`
-/ -/
def sign : Int Int def sign : Int Int
| Int.ofNat (succ _) => 1 | Int.ofNat (succ _) => 1
@@ -336,33 +277,27 @@ def sign : Int → Int
/-! ## Conversion -/ /-! ## Conversion -/
/-- /-- Turns an integer into a natural number, negative numbers become
Converts an integer into a natural number. Negative numbers are converted to `0`. `0`.
Examples: ```
* `(7 : Int).toNat = 7` #eval (7 : Int).toNat -- 7
* `(0 : Int).toNat = 0` #eval (0 : Int).toNat -- 0
* `(-7 : Int).toNat = 0` #eval (-7 : Int).toNat -- 0
```
-/ -/
def toNat : Int Nat def toNat : Int Nat
| ofNat n => n | ofNat n => n
| negSucc _ => 0 | negSucc _ => 0
/-- /--
Converts an integer into a natural number. Returns `none` for negative numbers. * If `n : Nat`, then `int.toNat' n = some n`
* If `n : Int` is negative, then `int.toNat' n = none`.
Examples:
* `(7 : Int).toNat? = some 7`
* `(0 : Int).toNat? = some 0`
* `(-7 : Int).toNat? = none`
-/ -/
def toNat? : Int Option Nat def toNat' : Int Option Nat
| (n : Nat) => some n | (n : Nat) => some n
| -[_+1] => none | -[_+1] => none
@[deprecated toNat? (since := "2025-03-11"), inherit_doc toNat?]
abbrev toNat' := toNat?
/-! ## divisibility -/ /-! ## divisibility -/
/-- /--
@@ -374,14 +309,14 @@ instance : Dvd Int where
/-! ## Powers -/ /-! ## Powers -/
/-- /-- Power of an integer to some natural number.
Power of an integer to a natural number, usually accessed via the `^` operator.
Examples: ```
* `(2 : Int) ^ 4 = 16` #eval (2 : Int) ^ 4 -- 16
* `(10 : Int) ^ 0 = 1` #eval (10 : Int) ^ 0 -- 1
* `(0 : Int) ^ 10 = 0` #eval (0 : Int) ^ 10 -- 0
* `(-7 : Int) ^ 3 = -343` #eval (-7 : Int) ^ 3 -- -343
```
-/ -/
protected def pow (m : Int) : Nat Int protected def pow (m : Int) : Nat Int
| 0 => 1 | 0 => 1

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. Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison Authors: Mario Carneiro
-/ -/
prelude prelude
import Init.Data.Int.Bitwise.Basic import Init.Data.Int.Basic
import Init.Data.Int.Bitwise.Lemmas 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 prelude
import Init.Data.Nat.Bitwise.Lemmas import Init.Data.Nat.Bitwise.Lemmas
import Init.Data.Int.Bitwise.Basic import Init.Data.Int.Bitwise
import Init.Data.Int.DivMod.Lemmas import Init.Data.Int.DivMod.Lemmas
namespace Int namespace Int
theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl
@[simp] @[simp]
theorem natCast_shiftRight (n s : Nat) : (n : Int) >>> s = n >>> s := rfl 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 m >>> n = m / ((2 ^ n) : Nat) := by
simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow] simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow]
split split
· simp; norm_cast · simp
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))] · rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
rfl rfl
@@ -40,47 +39,4 @@ theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow] 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 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_le_of_neg_le lower
· exact Int.mul_neg _ _ Int.neg_mul _ _ dvd · 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 `%` 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`. 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, However we decided it was better to use `ediv` and `emod`,
as they are consistent with the conventions used in SMT-LIB, and Mathlib, as they are consistent with the conventions used in SMTLib, and Mathlib,
and often mathematical reasoning is easier with these conventions. 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), 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 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. 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 /-! ### 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. Integer division. This version of `Int.div` uses the E-rounding convention
Division by zero is defined to be zero, rather than an error. (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` This is the function powering the `/` notation on integers.
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.
Examples: Examples:
* `(7 : Int) / (0 : Int) = 0` ```
* `(0 : Int) / (7 : Int) = 0` #eval (7 : Int) / (0 : Int) -- 0
* `(12 : Int) / (6 : Int) = 2` #eval (0 : Int) / (7 : Int) -- 0
* `(12 : Int) / (-6 : Int) = -2`
* `(-12 : Int) / (6 : Int) = -2` #eval (12 : Int) / (6 : Int) -- 2
* `(-12 : Int) / (-6 : Int) = 2` #eval (12 : Int) / (-6 : Int) -- -2
* `(12 : Int) / (7 : Int) = 1` #eval (-12 : Int) / (6 : Int) -- -2
* `(12 : Int) / (-7 : Int) = -1` #eval (-12 : Int) / (-6 : Int) -- 2
* `(-12 : Int) / (7 : Int) = -2`
* `(-12 : Int) / (-7 : 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"] @[extern "lean_int_ediv"]
def ediv : (@& Int) (@& Int) Int def ediv : (@& Int) (@& Int) Int
@@ -71,26 +71,29 @@ def ediv : (@& Int) → (@& Int) → Int
| -[m+1], -[n+1] => ofNat (succ (m / succ n)) | -[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` This is the function powering the `%` notation on integers.
for `y ≠ 0` and `Int.ediv` is the unique function satisfying `Int.emod x y + (Int.edivx y) * y = x`
for `y ≠ 0`.
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Examples: Examples:
* `(7 : Int) % (0 : Int) = 7` ```
* `(0 : Int) % (7 : Int) = 0` #eval (7 : Int) % (0 : Int) -- 7
* `(12 : Int) % (6 : Int) = 0` #eval (0 : Int) % (7 : Int) -- 0
* `(12 : Int) % (-6 : Int) = 0`
* `(-12 : Int) % (6 : Int) = 0` #eval (12 : Int) % (6 : Int) -- 0
* `(-12 : Int) % (-6 : Int) = 0` #eval (12 : Int) % (-6 : Int) -- 0
* `(12 : Int) % (7 : Int) = 5` #eval (-12 : Int) % (6 : Int) -- 0
* `(12 : Int) % (-7 : Int) = 5` #eval (-12 : Int) % (-6 : Int) -- 0
* `(-12 : Int) % (7 : Int) = 2`
* `(-12 : Int) % (-7 : Int) = 2` #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"] @[extern "lean_int_emod"]
def emod : (@& Int) (@& Int) Int def emod : (@& Int) (@& Int) Int
@@ -98,19 +101,15 @@ def emod : (@& Int) → (@& Int) → Int
| -[m+1], n => subNatNat (natAbs n) (succ (m % natAbs n)) | -[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 The Div and Mod syntax uses ediv and emod for compatibility with SMTLIb and mathematical
because mathematical reasoning tends to be easier. reasoning tends to be easier.
-/ -/
instance : Div Int where instance : Div Int where
div := Int.ediv 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 instance : Mod Int where
mod := Int.emod 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 theorem ofNat_ediv_ofNat {a b : Nat} : (a / b : Int) = (a / b : Nat) := rfl
@[norm_cast] @[norm_cast]
@@ -123,27 +122,35 @@ theorem negSucc_emod_negSucc {a b : Nat} : -[a+1] % -[b+1] = subNatNat (b + 1) (
/-! ### T-rounding division -/ /-! ### 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. The relation between integer division and modulo is found in
Division by 0 is defined to be 0. In this convention, `Int.tmod a b + b * (Int.tdiv a b) = a`. `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 Examples:
logical model.
Examples: ```
* `(7 : Int).tdiv (0 : Int) = 0` #eval (7 : Int).tdiv (0 : Int) -- 0
* `(0 : Int).tdiv (7 : Int) = 0` #eval (0 : Int).tdiv (7 : Int) -- 0
* `(12 : Int).tdiv (6 : Int) = 2`
* `(12 : Int).tdiv (-6 : Int) = -2` #eval (12 : Int).tdiv (6 : Int) -- 2
* `(-12 : Int).tdiv (6 : Int) = -2` #eval (12 : Int).tdiv (-6 : Int) -- -2
* `(-12 : Int).tdiv (-6 : Int) = 2` #eval (-12 : Int).tdiv (6 : Int) -- -2
* `(12 : Int).tdiv (7 : Int) = 1` #eval (-12 : Int).tdiv (-6 : Int) -- 2
* `(12 : Int).tdiv (-7 : Int) = -1`
* `(-12 : Int).tdiv (7 : Int) = -1` #eval (12 : Int).tdiv (7 : Int) -- 1
* `(-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"] @[extern "lean_int_div"]
def tdiv : (@& Int) (@& Int) Int def tdiv : (@& Int) (@& Int) Int
@@ -152,32 +159,33 @@ def tdiv : (@& Int) → (@& Int) → Int
| -[m +1], ofNat n => -ofNat (succ m / n) | -[m +1], ofNat n => -ofNat (succ m / n)
| -[m +1], -[n +1] => ofNat (succ m / succ 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. [t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
Division by 0 is defined to be 0 and `Int.tmod a 0 = a`. [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, Examples:
`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`.
[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 #eval (12 : Int).tmod (6 : Int) -- 0
logical model. #eval (12 : Int).tmod (-6 : Int) -- 0
#eval (-12 : Int).tmod (6 : Int) -- 0
#eval (-12 : Int).tmod (-6 : Int) -- 0
Examples: #eval (12 : Int).tmod (7 : Int) -- 5
* `(7 : Int).tmod (0 : Int) = 7` #eval (12 : Int).tmod (-7 : Int) -- 5
* `(0 : Int).tmod (7 : Int) = 0` #eval (-12 : Int).tmod (7 : Int) -- -5
* `(12 : Int).tmod (6 : Int) = 0` #eval (-12 : Int).tmod (-7 : Int) -- -5
* `(12 : Int).tmod (-6 : Int) = 0` ```
* `(-12 : Int).tmod (6 : Int) = 0`
* `(-12 : Int).tmod (-6 : Int) = 0` Implemented by efficient native code. -/
* `(12 : Int).tmod (7 : Int) = 5`
* `(12 : Int).tmod (-7 : Int) = 5`
* `(-12 : Int).tmod (7 : Int) = -5`
* `(-12 : Int).tmod (-7 : Int) = -5`
-/
@[extern "lean_int_mod"] @[extern "lean_int_mod"]
def tmod : (@& Int) (@& Int) Int def tmod : (@& Int) (@& Int) Int
| ofNat m, ofNat n => ofNat (m % n) | 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. 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)`
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 `fmod x y + (fdiv x y) * y = x`.
and `Int.fmod` is the unique function satisfying `Int.fmod x y + (Int.fdiv x y) * y = x`.
Examples: Examples:
* `(7 : Int).fdiv (0 : Int) = 0` ```
* `(0 : Int).fdiv (7 : Int) = 0` #eval (7 : Int).fdiv (0 : Int) -- 0
* `(12 : Int).fdiv (6 : Int) = 2` #eval (0 : Int).fdiv (7 : Int) -- 0
* `(12 : Int).fdiv (-6 : Int) = -2`
* `(-12 : Int).fdiv (6 : Int) = -2` #eval (12 : Int).fdiv (6 : Int) -- 2
* `(-12 : Int).fdiv (-6 : Int) = 2` #eval (12 : Int).fdiv (-6 : Int) -- -2
* `(12 : Int).fdiv (7 : Int) = 1` #eval (-12 : Int).fdiv (6 : Int) -- -2
* `(12 : Int).fdiv (-7 : Int) = -2` #eval (-12 : Int).fdiv (-6 : Int) -- 2
* `(-12 : Int).fdiv (7 : Int) = -2`
* `(-12 : Int).fdiv (-7 : Int) = 1` #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 def fdiv : Int Int Int
| 0, _ => 0 | 0, _ => 0
@@ -218,26 +229,26 @@ def fdiv : Int → Int → Int
| -[m+1], -[n+1] => ofNat (succ m / succ n) | -[m+1], -[n+1] => ofNat (succ m / succ n)
/-- /--
Integer modulus using the F-rounding convention. 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)`
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 `fmod x y + (fdiv x y) * y = x`.
and `Int.fmod` is the unique function satisfying `Int.fmod x y + (Int.fdiv x y) * y = x`.
Examples: 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` #eval (12 : Int).fmod (6 : Int) -- 0
* `(12 : Int).fmod (-6 : Int) = 0` #eval (12 : Int).fmod (-6 : Int) -- 0
* `(-12 : Int).fmod (6 : Int) = 0` #eval (-12 : Int).fmod (6 : Int) -- 0
* `(-12 : Int).fmod (-6 : Int) = 0` #eval (-12 : Int).fmod (-6 : Int) -- 0
* `(12 : Int).fmod (7 : Int) = 5`
* `(12 : Int).fmod (-7 : Int) = -2`
* `(-12 : Int).fmod (7 : Int) = 2`
* `(-12 : Int).fmod (-7 : Int) = -5`
#eval (12 : Int).fmod (7 : Int) -- 5
#eval (12 : Int).fmod (-7 : Int) -- -2
#eval (-12 : Int).fmod (7 : Int) -- 2
#eval (-12 : Int).fmod (-7 : Int) -- -5
```
-/ -/
def fmod : Int Int Int def fmod : Int Int Int
| 0, _ => 0 | 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 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`. `-b/2 ≤ Int.bmod a b < b/2` for all `a : Int` and `b > 0`.
Note that unlike `emod`, `fmod`, and `tmod`, This is used in Omega as well as signed bitvectors.
`bmod` takes a natural number as the second argument, rather than an integer.
This function 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 If `m = 0`, then `bmod x m = x`.
`-m / 2 ≤ Int.bmod x m < m/2` for `m ≠ 0` and `Int.bmod x m` is congruent to `x` modulo `m`.
If `m = 0`, then `Int.bmod x m = x`.
Examples: Examples:
* `(7 : Int).bmod 0 = 7` ```
* `(0 : Int).bmod 7 = 0` #eval (7 : Int).bdiv 0 -- 0
* `(12 : Int).bmod 6 = 0` #eval (0 : Int).bdiv 7 -- 0
* `(12 : Int).bmod 7 = -2`
* `(12 : Int).bmod 8 = -4` #eval (12 : Int).bdiv 6 -- 2
* `(12 : Int).bmod 9 = 3` #eval (12 : Int).bdiv 7 -- 2
* `(-12 : Int).bmod 6 = 0` #eval (12 : Int).bdiv 8 -- 2
* `(-12 : Int).bmod 7 = 2` #eval (12 : Int).bdiv 9 -- 1
* `(-12 : Int).bmod 8 = -4`
* `(-12 : Int).bmod 9 = -3` #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 := def bmod (x : Int) (m : Nat) : Int :=
let r := x % m let r := x % m
@@ -291,21 +303,24 @@ def bmod (x : Int) (m : Nat) : Int :=
r - m r - m
/-- /--
Balanced division. Balanced division. This returns the unique integer so that
`b * (Int.bdiv a b) + Int.bmod a b = a`.
This returns the unique integer so that `b * (Int.bdiv a b) + Int.bmod a b = a`.
Examples: Examples:
* `(7 : Int).bdiv 0 = 0` ```
* `(0 : Int).bdiv 7 = 0` #eval (7 : Int).bmod 0 -- 7
* `(12 : Int).bdiv 6 = 2` #eval (0 : Int).bmod 7 -- 0
* `(12 : Int).bdiv 7 = 2`
* `(12 : Int).bdiv 8 = 2` #eval (12 : Int).bmod 6 -- 0
* `(12 : Int).bdiv 9 = 1` #eval (12 : Int).bmod 7 -- -2
* `(-12 : Int).bdiv 6 = -2` #eval (12 : Int).bmod 8 -- -4
* `(-12 : Int).bdiv 7 = -2` #eval (12 : Int).bmod 9 -- 3
* `(-12 : Int).bdiv 8 = -1`
* `(-12 : Int).bdiv 9 = -1` #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 := def bdiv (x : Int) (m : Nat) : Int :=
if m = 0 then if m = 0 then

View File

@@ -18,7 +18,7 @@ open Nat (succ)
namespace Int namespace Int
/-! ### dvd -/ -- /-! ### dvd -/
protected theorem dvd_def (a b : Int) : (a b) = Exists (fun c => b = a * c) := rfl 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 => constructor <;> exact fun k, e =>
-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg] -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 => constructor <;> exact fun k, e =>
-k, by simp [ e, Int.neg_mul, Int.mul_neg, Int.neg_neg] -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 theorem ofNat_dvd_left {n : Nat} {z : Int} : (n : Int) z n z.natAbs := by
rw [ natAbs_dvd_natAbs, natAbs_ofNat] rw [ natAbs_dvd_natAbs, natAbs_ofNat]
/-! ### ediv zero -/ /-! ### *div zero -/
@[simp] theorem zero_ediv : b : Int, 0 / b = 0 @[simp] theorem zero_ediv : b : Int, 0 / b = 0
| ofNat _ => show ofNat _ = _ by simp | 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 | ofNat _ => show ofNat _ = _ by simp
| -[_+1] => rfl | -[_+1] => rfl
/-! ### emod zero -/ /-! ### mod zero -/
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := rfl @[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 @[simp, norm_cast] theorem ofNat_emod (m n : Nat) : ((m % n) : Int) = m % n := rfl
/-! ### mod definitions -/ /-! ### mod definitions -/
theorem emod_add_ediv : a b : Int, a % b + b * (a / b) = a 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] 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 ..) 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 theorem emod_add_ediv' (a b : Int) : a % b + a / b * b = a := by
rw [Int.mul_comm]; exact emod_add_ediv .. rw [Int.mul_comm]; exact emod_add_ediv ..
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
rw [Int.add_comm]; exact emod_add_ediv .. 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 theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
rw [ Int.add_sub_cancel (a % b), emod_add_ediv] rw [ Int.add_sub_cancel (a % b), emod_add_ediv]
/-! ### `/` 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 m, 0 => show ofNat (m / 0) = -(m / 0) by rw [Nat.div_zero]; rfl
| ofNat _, -[_+1] => (Int.neg_neg _).symm | ofNat _, -[_+1] => (Int.neg_neg _).symm
| ofNat _, succ _ | -[_+1], 0 | -[_+1], succ _ | -[_+1], -[_+1] => rfl | 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 apply congrArg negSucc
rw [Nat.mul_comm, Nat.sub_mul_div]; rwa [Nat.mul_comm] 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 := 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 if h : c = 0 then by simp [h] else by
let k, hk := H 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 := @[simp] theorem mul_ediv_cancel_left (b : Int) (H : a 0) : (a * b) / a = b :=
Int.mul_comm .. Int.mul_ediv_cancel _ H 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] rw [Int.div_def]
match b, h with match b, h with
| Int.ofNat (b+1), _ => | Int.ofNat (b+1), _ =>
rcases a with a <;> simp [Int.ediv] rcases a with a <;> simp [Int.ediv]
norm_cast
@[deprecated ediv_nonneg_iff_of_pos (since := "2025-02-28")] simp
abbrev div_nonneg_iff_of_pos := @ediv_nonneg_iff_of_pos
/-! ### emod -/ /-! ### 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 _)) | ofNat _, _, _, rfl => ofNat_lt.2 (Nat.mod_lt _ (Nat.succ_pos _))
| -[_+1], _, _, rfl => Int.sub_lt_self _ (ofNat_lt.2 <| 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 := @[simp] theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
if cz : c = 0 then by if cz : c = 0 then by
rw [cz, Int.mul_zero, Int.add_zero] 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 · simp_all
· exact Or.inr (Int.lt_iff_le_and_ne.mpr emod_nonneg b w, Ne.symm h) · 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 -/ /-! ### bmod -/
@[simp] theorem bmod_emod : bmod x m % m = x % m := by @[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 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 namespace Int
/-! ## gcd -/ /-! ## gcd -/
/-- /-- Computes the greatest common divisor of two integers, as a `Nat`. -/
Computes the greatest common divisor of two integers as a natural number. The GCD of two integers is
the largest natural number that evenly divides both. However, the GCD of a number and `0` is the
number's absolute value.
This implementation uses `Nat.gcd`, which is overridden in both the kernel and the compiler to
efficiently evaluate using arbitrary-precision arithmetic.
Examples:
* `Int.gcd 10 15 = 5`
* `Int.gcd 10 (-15) = 5`
* `Int.gcd (-6) (-9) = 3`
* `Int.gcd 0 5 = 5`
* `Int.gcd (-7) 0 = 7`
-/
def gcd (m n : Int) : Nat := m.natAbs.gcd n.natAbs def gcd (m n : Int) : Nat := m.natAbs.gcd n.natAbs
theorem gcd_dvd_left {a b : Int} : (gcd a b : Int) a := by 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 -/ /-! ## lcm -/
/-- /-- Computes the least common multiple of two integers, as a `Nat`. -/
Computes the least common multiple of two integers as a natural number. The LCM of two integers is
the smallest natural number that's evenly divisible by the absolute values of both.
Examples:
* `Int.lcm 9 6 = 18`
* `Int.lcm 9 (-6) = 18`
* `Int.lcm 9 3 = 9`
* `Int.lcm 9 (-3) = 9`
* `Int.lcm 0 3 = 0`
* `Int.lcm (-3) 0 = 0`
-/
def lcm (m n : Int) : Nat := m.natAbs.lcm n.natAbs 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 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_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_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 @[local simp] theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl @[local simp] 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_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 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 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_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 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_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 @[local simp] theorem negSucc_add_negSucc (m n : Nat) : -[m+1] + -[n+1] = -[succ (m + n) +1] := rfl
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
@[local simp] theorem ofNat_mul_ofNat (m n : Nat) : (m * n : Int) = (m * n) := rfl @[local simp] theorem ofNat_mul_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] 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] 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 negSucc_mul_negSucc' (m n : Nat) :
-[m+1] * -[n+1] = ofNat (succ m * succ n) := rfl -[m+1] * -[n+1] = ofNat (succ m * succ n) := rfl
/- ## some basic functions and properties -/ /- ## 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 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 negSucc_ne_zero (n : Nat) : -[n+1] 0 := nofun
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 -[n+1] := 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 (Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
/- ## neg -/ /- ## neg -/
@@ -82,7 +78,7 @@ theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
| succ _ => rfl | succ _ => rfl
| -[_+1] => 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 _ 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) @[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 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 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 theorem add_neg_one (i : Int) : i + -1 = i - 1 := rfl
/- ## basic properties of subNatNat -/ /- ## 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) theorem subNatNat_elim (m n : Nat) (motive : Nat Nat Int Prop)
(hp : i n, motive (n + i) n i) (hp : i n, motive (n + i) n i)
(hn : i m, motive m (m + i + 1) -[i+1]) : (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] rw [Nat.sub_eq_iff_eq_add' h]
simp 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 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] 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_assoc, succ_sub_succ_eq_sub, Nat.add_comm n,Nat.add_sub_assoc (Nat.le_of_lt h'),
Nat.add_comm] 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) protected theorem add_assoc : a b c : Int, a + b + c = a + (b + c)
| (m:Nat), (n:Nat), _ => aux1 .. | (m:Nat), (n:Nat), _ => aux1 ..
| Nat.cast m, b, Nat.cast k => by | 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 -/ /- ## negation -/
protected theorem add_left_neg : a : Int, -a + a = 0 theorem subNatNat_self : n, subNatNat n n = 0
| 0 => rfl | 0 => rfl
| succ m => by simp [neg_ofNat_succ] | succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
| -[m+1] => by simp [neg_negSucc]
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] 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 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] 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₁ 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) apply Int.add_left_cancel (a := a + b)
rw [Int.add_right_neg, Int.add_comm a, Int.add_assoc, Int.add_assoc 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] 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 -/ /- ## subtraction -/
@[simp] theorem negSucc_sub_one (n : Nat) : -[n+1] - 1 = -[n + 1 +1] := rfl @[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 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 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 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 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] @[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 Int.add_neg_cancel_right a b
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by 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 @[norm_cast] theorem ofNat_sub (h : m n) : ((n - m : Nat) : Int) = n - m := by
match m with 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) show ofNat (n - succ m) = subNatNat n (succ m)
rw [subNatNat, Nat.sub_eq_zero_of_le h] 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 theorem negSucc_coe' (n : Nat) : -[n+1] = -n - 1 := by
rw [Int.sub_eq_add_neg, Int.neg_add]; rfl 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, rw [Int.ofNat_add, Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm,
Int.add_right_neg, Int.add_zero] Int.add_right_neg, Int.add_zero]
· intros i n · intros i n
simp only [negSucc_eq, ofNat_add, ofNat_one, Int.sub_eq_add_neg, Int.neg_add, Int.add_assoc] simp only [negSucc_coe, ofNat_add, 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] rw [ @Int.sub_eq_add_neg n, ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
apply Nat.le_refl 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] rw [ Int.subNatNat_eq_coe]
refine subNatNat_elim m n (fun m n i => toNat i = m - n) (fun i n => ?_) (fun i n => ?_) 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 · exact (Nat.add_sub_cancel_left ..).symm
@@ -363,9 +347,6 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
norm_cast norm_cast
simp [eq_comm] 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 @[simp] theorem neg_ofNat_eq_negSucc_add_one_iff {a b : Nat} : - (a : Int) = -[b+1] + 1 a = b := by
cases b with cases b with
| zero => simp; norm_cast | zero => simp; norm_cast
@@ -374,33 +355,30 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
norm_cast norm_cast
simp [eq_comm] 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 -/ /- ## 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 apply Iff.intro
· intro p · intro p
rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p] rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p]
· exact congrArg (· + k) · 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 [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 [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] simp [Int.sub_eq_add_neg, Int.add_left_inj]
/- ## Ring properties -/ /- ## 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 protected theorem mul_comm (a b : Int) : a * b = b * a := by
cases a <;> cases b <;> simp [Nat.mul_comm] 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 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] 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 attribute [local simp] ofNat_mul_negOfNat negOfNat_mul_ofNat
cases a <;> cases b <;> cases c <;> negSucc_mul_negOfNat negOfNat_mul_negSucc
simp [Nat.mul_assoc, 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 instance : Std.Associative (α := Int) (· * ·) := Int.mul_assoc
protected theorem mul_left_comm (a b c : Int) : a * (b * c) = b * (a * c) := by 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) : theorem ofNat_mul_subNatNat (m n k : Nat) :
m * subNatNat n k = subNatNat (m * n) (m * k) := by m * subNatNat n k = subNatNat (m * n) (m * k) := by
cases m with 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 | succ m => cases n.lt_or_ge k with
| inl h => | inl h =>
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m) 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 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] | 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 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:Nat) => by simp [Nat.left_distrib]
| (m:Nat), (n:Nat), -[k+1] => by | (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 := 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] 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`. @[simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
(Int.neg_mul_eq_neg_mul a b).symm (Int.neg_mul_eq_neg_mul a b).symm
-- Note, this is not a `@[simp]` lemma because it interferes with normalization in `simp +arith`. @[simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
(Int.neg_mul_eq_mul_neg a b).symm (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 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 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 @[simp] protected theorem one_mul : a : Int, 1 * a = a
| ofNat n => show ofNat (1 * n) = ofNat n by rw [Nat.one_mul] | 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 left_id := Int.one_mul
right_id := Int.mul_one right_id := Int.mul_one
@[simp] protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one] protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
@[simp] protected theorem neg_one_mul (a : Int) : -1 * a = -a := by rw [Int.neg_mul, Int.one_mul]
protected theorem neg_eq_neg_one_mul : a : Int, -a = -1 * a protected theorem neg_eq_neg_one_mul : a : Int, -a = -1 * a
| 0 => rfl | 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`. 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, -- Note this only works because of local simp attributes in this file,
-- so it still makes sense to tag the lemmas with `@[simp]`. -- so it still makes sense to tag the lemmas with `@[simp]`.
simp simp
protected theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : Int) = (n : Int) + 1 := rfl @[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
@[simp] protected theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
simp simp
end Int end Int

View File

@@ -15,32 +15,6 @@ import Init.Omega
namespace Int namespace Int
/-! ### miscellaneous lemmas -/
@[simp] theorem natCast_le_zero : {n : Nat} (n : Int) 0 n = 0 := by omega
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c a = c + b := by omega
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c a = b + c := by omega
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i 0 0 i := by omega
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ((no_index (OfNat.ofNat n)) : Int) :=
ofNat_nonneg _
@[simp] theorem neg_natCast_le_natCast (n m : Nat) : -(n : Int) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_natCast_le_ofNat (n m : Nat) : -(n : Int) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_ofNat_le_ofNat (n m : Nat) : -(no_index (OfNat.ofNat n)) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_ofNat_le_natCast (n m : Nat) : -(no_index (OfNat.ofNat n)) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
/-! ### toNat -/
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by @[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by
symm symm
simp only [Int.toNat] simp only [Int.toNat]
@@ -65,60 +39,6 @@ protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c ↔ a = b + c :=
simp [toNat] simp [toNat]
split <;> simp_all <;> omega 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
/-! ### 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) : 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 (x.bmod m) < 0 (-(m / 2) x x < 0) ((m + 1) / 2 x) := by
simp only [Int.bmod_def] simp only [Int.bmod_def]
@@ -126,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.emod_eq_of_lt xpos (by omega)]; omega
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (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 end Int

View File

@@ -9,7 +9,6 @@ import Init.Data.Prod
import Init.Data.Int.Lemmas import Init.Data.Int.Lemmas
import Init.Data.Int.LemmasAux import Init.Data.Int.LemmasAux
import Init.Data.Int.DivMod.Bootstrap import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.Cooper
import Init.Data.Int.Gcd import Init.Data.Int.Gcd
import Init.Data.RArray import Init.Data.RArray
import Init.Data.AC 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 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) 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 theorem cmod_eq_zero_iff_emod_eq_zero (a b : Int) : cmod a b = 0 a%b = 0 := by
unfold cmod unfold cmod
have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a
simp at this 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 := private abbrev div_mul_cancel_of_mod_zero :=
@Int.ediv_mul_cancel_of_emod_eq_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`. `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 match p with
| .num k' => .num (k*k') | .num k' => .num (k*k')
| .add k' v p => .add (k*k') v (mul' p 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
@[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by @[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
simp [mul] induction p <;> simp [mul, denote, *]
split rw [Int.mul_assoc, Int.mul_add]
next => simp [*, denote]
next =>
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] 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 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₁ replace h₁ := div_mul_cancel_of_mod_zero h₁
have ih := ih h₂ have ih := ih h₂
simp [ih] simp [ih]
apply congrArg (denote ctx p + ·)
rw [Int.mul_right_comm, h₁] rw [Int.mul_right_comm, h₁]
attribute [local simp] Poly.divCoeffs Poly.getConst 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] simp only [mul_def, denote]
rw [Int.mul_comm (denote _ _) _] rw [Int.mul_comm (denote _ _) _]
simpa [Int.mul_assoc] using ih 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 theorem Expr.denote_norm (ctx : Context) (e : Expr) : e.norm.denote ctx = e.denote ctx := by
simp [norm, toPoly', Expr.denote_toPoly'_go] 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 simp [norm_eq_var_const_cert] at h
replace h := congrArg (Poly.denote ctx) h replace h := congrArg (Poly.denote ctx) h
simp at 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 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] conv => lhs; rw [ Int.mul_zero k]
@@ -540,9 +531,8 @@ def Poly.isValidLe (p : Poly) : Bool :=
| .num k => k 0 | .num k => k 0
| _ => false | _ => 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 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 => next p k h =>
intro h' intro h'
replace h := congrArg (Poly.denote ctx) 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] simp [dvd_solve_elim_cert]
split <;> simp split <;> simp
next a₁ x₁ p₁ a₂ x₂ p₂ => 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₂ intro h₁ h₂
rw [Int.add_comm] at 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₂ 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 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 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] simp [le_neg_cert]
intro; subst p₂; simp; intro h 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 simp at h
exact 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 [*]
· 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 theorem le_unsat (ctx : Context) (p : Poly) : p.isUnsatLe p.denote' ctx 0 False := by
simp [Poly.isUnsatLe]; split <;> simp 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 theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx = 0 p₂.denote' ctx = 0 := by
simp at h simp at h
@@ -908,7 +881,7 @@ def Poly.coeff (p : Poly) (x : Var) : Int :=
| .num _ => 0 | .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 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 private theorem dvd_of_eq' {a x p : Int} : a*x + p = 0 a p := by
intro h 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₂ have := eq_dvd_subst' h₁ h₂
rw [Int.sub_eq_add_neg, Int.add_comm] at this rw [Int.sub_eq_add_neg, Int.add_comm] at this
apply abs_dvd apply abs_dvd
simp [this, Int.neg_mul] simp [this]
def eq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool := def eq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x 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 h
intro; subst p₃ intro; subst p₃
intro h₁ h₂ 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 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] rw [Int.mul_comm]
assumption assumption
@@ -1029,7 +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 : eq_of_core_cert p₁ p₂ p₃ p₁.denote' ctx = p₂.denote' ctx p₃.denote' ctx = 0 := by
simp [eq_of_core_cert] simp [eq_of_core_cert]
intro; subst p₃; simp intro; subst p₃; simp
intro h; rw [h, Int.add_neg_eq_sub, Int.sub_self] intro h; rw [h, Int.sub_eq_add_neg, Int.sub_self]
def Poly.isUnsatDiseq (p : Poly) : Bool := def Poly.isUnsatDiseq (p : Poly) : Bool :=
match p with match p with
@@ -1046,9 +1021,6 @@ theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p'
simp [eq_coeff_cert] simp [eq_coeff_cert]
intro _ _; simp [mul_eq_zero_iff, *] 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 theorem diseq_unsat (ctx : Context) (p : Poly) : p.isUnsatDiseq p.denote' ctx 0 False := by
simp [Poly.isUnsatDiseq] <;> split <;> simp simp [Poly.isUnsatDiseq] <;> split <;> simp
@@ -1069,732 +1041,7 @@ theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
simp [eq_of_core_cert] simp [eq_of_core_cert]
intro; subst p₃; simp intro; subst p₃; simp
intro h; rw [ Int.sub_eq_zero] at h intro h; rw [ Int.sub_eq_zero] at h
rw [Int.add_neg_eq_sub]; assumption rw [Int.sub_eq_add_neg]; 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
end Int.Linear 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 theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a b := by
simp [le_def, h]; constructor 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 := 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_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 := theorem le.dest {a b : Int} (h : a b) : n : Nat, a + n = b :=
let n, h₁ := le.dest_sub h 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 := protected theorem le_total (a b : Int) : a b b a :=
(nonneg_or_nonneg_neg (b - a)).imp_right fun H => by (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 := @[simp, norm_cast] theorem ofNat_le {m n : Nat} : (m : Int) n m n :=
fun h => 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 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 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 let n, (h : (1 + n) = a) := le.dest h
n, by rw [Nat.add_comm] at h; exact h.symm n, by rw [Nat.add_comm] at h; exact h.symm
theorem lt_add_succ (a : Int) (n : Nat) : a < a + (n + 1) := 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] 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 h lt_add_succ a n
theorem lt.dest {a b : Int} (h : a < b) : n : Nat, a + Nat.succ n = b := 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] 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 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 := _ 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 := protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a b :=
(Int.lt_iff_le_not_le.mp h).right (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 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] 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 := protected theorem lt_trichotomy (a b : Int) : a < b a = b b < a :=
if eq : a = b then .inr <| .inl eq else 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 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 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 let n, e := eq_ofNat_of_zero_le h
rw [e]; rfl 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 := theorem le_natAbs {a : Int} : a natAbs a :=
match Int.le_total 0 a with 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 _) | .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 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 := 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] 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 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 := 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] 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 := 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] 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 · _) 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 · _) 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 := 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) have : 0 + -b -a + b + -b := Int.add_le_add_right this (-b)
rwa [Int.add_neg_cancel_right, Int.zero_add] at this 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 := 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 suffices - -a - -b by simp [Int.neg_neg] at this; assumption
Int.neg_le_neg h 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) have : 0 + -b < -a + b + -b := Int.add_lt_add_right this (-b)
rwa [Int.add_neg_cancel_right, Int.zero_add] at this 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 protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 := by
have : -a < -0 := Int.neg_lt_neg h have : -a < -0 := Int.neg_lt_neg h
rwa [Int.neg_zero] at this 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 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 -/ /- ### Order properties and multiplication -/
protected theorem mul_nonneg {a b : Int} (ha : 0 a) (hb : 0 b) : 0 a * b := by 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 n, hn := eq_ofNat_of_zero_le ha
let m, hm := eq_ofNat_of_zero_le hb 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 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 n, hn := eq_succ_of_zero_lt ha
let m, hm := eq_succ_of_zero_lt hb let m, hm := eq_succ_of_zero_lt hb
rw [hn, hm] rw [hn, hm, ofNat_mul]; apply ofNat_succ_pos
apply ofNat_succ_pos
protected theorem mul_lt_mul_of_pos_left {a b c : Int} protected theorem mul_lt_mul_of_pos_left {a b c : Int}
(h₁ : a < b) (h₂ : 0 < c) : c * a < c * b := by (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 -/ /- ## 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_negSucc (n : Nat) : natAbs -[n+1] = n.succ := rfl
@[simp] theorem natAbs_zero : natAbs (0 : Int) = (0 : Nat) := rfl @[simp] theorem natAbs_zero : natAbs (0 : Int) = (0 : Nat) := rfl
@[simp] theorem natAbs_one : natAbs (1 : Int) = (1 : 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 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)] 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 -/ /-! ### toNat -/
theorem toNat_eq_max : a : Int, (toNat a : Int) = max a 0 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 @[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
match a with match a with
| (n : Nat) => simp | Int.ofNat n => simp
| -(n + 1 : Nat) => norm_cast | Int.negSucc n => simp
theorem self_le_toNat (a : Int) : a toNat a := by rw [toNat_eq_max]; apply Int.le_max_left 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 | 0 => rfl
| _+1 => rfl | _+1 => rfl
/-! ### toNat? -/ /-! ### toNat' -/
theorem mem_toNat? : {a : Int} {n : Nat}, toNat? a = some n a = n theorem mem_toNat' : {a : Int} {n : Nat}, toNat' a = some n a = n
| (m : Nat), n => by simp [toNat?, Int.ofNat_inj] | (m : Nat), n => by simp [toNat', Int.ofNat_inj]
| -[m+1], n => by constructor <;> nofun | -[m+1], n => by constructor <;> nofun
@[deprecated mem_toNat? (since := "2025-03-11")]
abbrev mem_toNat' := @mem_toNat?
/-! ## Order properties of the integers -/ /-! ## 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 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 := 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] 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 · _) 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 · _) 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 := 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 := 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) 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 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 have h := Int.add_lt_add_left h a
rwa [Int.add_neg_cancel_left] at h 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) 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} 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) 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} 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) 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} 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 := 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) 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 -/ /- ## sign -/
@[simp] theorem sign_zero : sign 0 = 0 := rfl @[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 | 0, h => nomatch h
| -[_+1], _ => negSucc_lt_zero _ | -[_+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 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 neg_of_sign_eq_neg_one, sign_eq_neg_one_of_neg
@[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 a = 0 := @[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 a = 0 :=
@@ -1123,7 +1003,7 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
| .ofNat (_ + 1) => rfl | .ofNat (_ + 1) => rfl
| .negSucc _ => 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 match x with
| 0 => rfl | 0 => rfl
| .ofNat (_ + 1) => | .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 _) exact Int.le_add_one (ofNat_nonneg _)
| .negSucc _ => simp +decide [sign] | .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 @[simp] theorem mul_sign_self : i : Int, i * sign i = natAbs i
| succ _ => Int.mul_one _ | succ _ => Int.mul_one _
| 0 => Int.mul_zero _ | 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 @[simp] theorem sign_mul_self : sign i * i = natAbs i := by
rw [Int.mul_comm, mul_sign_self] 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 -/ /- ## natAbs -/
theorem natAbs_ne_zero {a : Int} : a.natAbs 0 a 0 := not_congr Int.natAbs_eq_zero 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 | ofNat _ => rfl
| -[_+1] => 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} theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs.eq_def] (h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs.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 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 suffices a b : Nat, natAbs (subNatNat a b.succ) (a + b).succ by
match a, b with 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:Nat), -[b+1] => rw [natAbs_ofNat, natAbs_negSucc]; apply this
| -[a+1], (b:Nat) => | -[a+1], (b:Nat) =>
rw [natAbs_negSucc, natAbs_ofNat, Nat.succ_add, Nat.add_comm a b]; apply this 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 theorem natAbs_sub_le (a b : Int) : natAbs (a - b) natAbs a + natAbs b := by
rw [ Int.natAbs_neg b]; apply natAbs_add_le 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 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} 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 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₂ | _, _, _, 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] 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 end Int

View File

@@ -11,7 +11,7 @@ namespace Int
/-! # pow -/ /-! # pow -/
@[simp] protected theorem pow_zero (b : Int) : b^0 = 1 := rfl protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
@@ -27,11 +27,11 @@ abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
abbrev pos_pow_of_pos := @Nat.pow_pos abbrev pos_pow_of_pos := @Nat.pow_pos
@[norm_cast] @[norm_cast]
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
match n with match n with
| 0 => rfl | 0 => rfl
| n + 1 => | n + 1 =>
simp only [Nat.pow_succ, Int.pow_succ, Int.natCast_mul, Int.natCast_pow _ n] simp only [Nat.pow_succ, Int.pow_succ, natCast_mul, natCast_pow _ n]
@[simp] @[simp]
protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) : protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) :

View File

@@ -13,44 +13,29 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List namespace List
/-- /-- `O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over `a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
a list `l : List α`, given a proof that every element of `l` in fact satisfies `P`. but is defined only when all members of `l` satisfy `P`, using the proof
to apply `f`. -/
`O(|l|)`. `List.pmap`, named for “partial map,” is the equivalent of `List.map` for such partial
functions.
-/
def pmap {P : α Prop} (f : a, P a β) : l : List α, (H : a l, P a) List β def pmap {P : α Prop} (f : a, P a β) : l : List α, (H : a l, P a) List β
| [], _ => [] | [], _ => []
| a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2 | a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2
/-- /--
Unsafe implementation of `attachWith` that takes advantage of the fact that the representation of Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
`List {x // P x}` is the same as the input `List α`. `List {x // P x}` is the same as the input `List α`.
(Someday, the compiler might do this optimization automatically, but until then...)
-/ -/
@[inline] private unsafe def attachWithImpl @[inline] private unsafe def attachWithImpl
(l : List α) (P : α Prop) (_ : x l, P x) : List {x // P x} := unsafeCast l (l : List α) (P : α Prop) (_ : x l, P x) : List {x // P x} := unsafeCast l
/-- /-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `l` to produce a new list
“Attaches” individual proofs to a list of values that satisfy a predicate `P`, returning a list of with the same elements but in the type `{x // P x}`. -/
elements in the corresponding subtype `{ x // P x }`.
`O(1)`.
-/
@[implemented_by attachWithImpl] def attachWith @[implemented_by attachWithImpl] def attachWith
(l : List α) (P : α Prop) (H : x l, P x) : List {x // P x} := pmap Subtype.mk l H (l : List α) (P : α Prop) (H : x l, P x) : List {x // P x} := pmap Subtype.mk l H
/-- /-- `O(1)`. "Attach" the proof that the elements of `l` are in `l` to produce a new list
“Attaches” the proof that the elements of `l` are in fact elements of `l`, producing a new list with with the same elements but in the type `{x // x ∈ l}`. -/
the same elements but in the subtype `{ x // x ∈ l }`.
`O(1)`.
This function is primarily used to allow definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
`List.map`) to prove that an value taken from a list is smaller than the list. This allows the
well-founded recursion mechanism to prove that the function terminates.
-/
@[inline] def attach (l : List α) : List {x // x l} := attachWith l _ fun _ => id @[inline] def attach (l : List α) : List {x // x l} := attachWith l _ fun _ => id
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/ /-- Implementation of `pmap` using the zero-copy version of `attach`. -/
@@ -665,19 +650,11 @@ Further, we provide simp lemmas that push `unattach` inwards.
-/ -/
/-- /--
Maps a list of terms in a subtype to the corresponding terms in the type by forgetting that they A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
satisfy the predicate. It is introduced as an intermediate step by lemmas such as `map_subtype`,
and is ideally subsequently simplified away by `unattach_attach`.
This is the inverse of `List.attachWith` and a synonym for `l.map (·.val)`. If not, usually the right approach is `simp [List.unattach, -List.map_subtype]` to unfold.
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
This function is usually inserted automatically by Lean as an intermediate step while proving
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
the elaboration of definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
state, the right approach is usually the tactic `simp [List.unattach, -List.map_subtype]`.
-/ -/
def unattach {α : Type _} {p : α Prop} (l : List { x // p x }) : List α := l.map (·.val) def unattach {α : Type _} {p : α Prop} (l : List { x // p x }) : List α := l.map (·.val)

File diff suppressed because it is too large Load Diff

View File

@@ -79,16 +79,10 @@ theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a
/-! ### getD -/ /-! ### getD -/
/-- /--
Returns the element at the provided index, counting from `0`. Returns `fallback` if the index is out Returns the `i`-th element in the list (zero-based).
of bounds.
To return an `Option` depending on whether the index is in bounds, use `as[i]?`. To panic if the If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
index is out of bounds, use `as[i]!`. See also `get?` and `get!`.
Examples:
* `["spring", "summer", "fall", "winter"].getD 2 "never" = "fall"`
* `["spring", "summer", "fall", "winter"].getD 0 "never" = "spring"`
* `["spring", "summer", "fall", "winter"].getD 4 "never" = "never"`
-/ -/
def getD (as : List α) (i : Nat) (fallback : α) : α := def getD (as : List α) (i : Nat) (fallback : α) : α :=
as[i]?.getD fallback as[i]?.getD fallback
@@ -98,16 +92,10 @@ def getD (as : List α) (i : Nat) (fallback : α) : α :=
/-! ### getLast! -/ /-! ### getLast! -/
/-- /--
Returns the last element in the list. Panics and returns `default` if the list is empty. Returns the last element in the list.
Safer alternatives include: If the list is empty, this function panics when executed, and returns `default`.
* `getLast?`, which returns an `Option`, See `getLast` and `getLastD` for safer alternatives.
* `getLastD`, which takes a fallback value for empty lists, and
* `getLast`, which requires a proof that the list is non-empty.
Examples:
* `["circle", "rectangle"].getLast! = "rectangle"`
* `["circle"].getLast! = "circle"`
-/ -/
def getLast! [Inhabited α] : List α α def getLast! [Inhabited α] : List α α
| [] => panic! "empty list" | [] => panic! "empty list"
@@ -118,12 +106,10 @@ def getLast! [Inhabited α] : List αα
/-! ### head! -/ /-! ### head! -/
/-- /--
Returns the first element in the list. If the list is empty, panics and returns `default`. Returns the first element in the list.
Safer alternatives include: If the list is empty, this function panics when executed, and returns `default`.
* `List.head`, which requires a proof that the list is non-empty, See `head` and `headD` for safer alternatives.
* `List.head?`, which returns an `Option`, and
* `List.headD`, which returns an explicitly-provided fallback value on empty lists.
-/ -/
def head! [Inhabited α] : List α α def head! [Inhabited α] : List α α
| [] => panic! "empty list" | [] => panic! "empty list"
@@ -132,17 +118,10 @@ def head! [Inhabited α] : List αα
/-! ### tail! -/ /-! ### tail! -/
/-- /--
Drops the first element of a nonempty list, returning the tail. If the list is empty, this function Drops the first element of the list.
panics when executed and returns the empty list.
Safer alternatives include If the list is empty, this function panics when executed, and returns the empty list.
* `tail`, which returns the empty list without panicking, See `tail` and `tailD` for safer alternatives.
* `tail?`, which returns an `Option`, and
* `tailD`, which returns a fallback value when passed the empty list.
Examples:
* `["apple", "banana", "grape"].tail! = ["banana", "grape"]`
* `["banana", "grape"].tail! = ["grape"]`
-/ -/
def tail! : List α List α def tail! : List α List α
| [] => panic! "empty list" | [] => panic! "empty list"
@@ -153,30 +132,17 @@ def tail! : List α → List α
/-! ### partitionM -/ /-! ### partitionM -/
/-- /--
Returns a pair of lists that together contain all the elements of `as`. The first list contains Monadic generalization of `List.partition`.
those elements for which the monadic predicate `p` returns `true`, and the second contains those for
which `p` returns `false`. The list's elements are examined in order, from left to right.
This is a monadic version of `List.partition`. This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic` or `Init.Data.List.Control`.
```
Example:
```lean example
def posOrNeg (x : Int) : Except String Bool := def posOrNeg (x : Int) : Except String Bool :=
if x > 0 then pure true if x > 0 then pure true
else if x < 0 then pure false else if x < 0 then pure false
else throw "Zero is not positive or negative" else throw "Zero is not positive or negative"
```
```lean example partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
#eval [-1, 2, 3].partitionM posOrNeg partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
```
```output
Except.ok ([2, 3], [-1])
```
```lean example
#eval [0, 2, 3].partitionM posOrNeg
```
```output
Except.error "Zero is not positive or negative"
``` ```
-/ -/
@[inline] def partitionM [Monad m] (p : α m Bool) (l : List α) : m (List α × List α) := @[inline] def partitionM [Monad m] (p : α m Bool) (l : List α) : m (List α × List α) :=
@@ -196,12 +162,12 @@ where
/-! ### partitionMap -/ /-! ### partitionMap -/
/-- /--
Applies a function that returns a disjoint union to each element of a list, collecting the `Sum.inl` Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
and `Sum.inr` results into separate lists. whilst partitioning the result into a pair of lists, `List β × List γ`,
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
Examples: ```
* `[0, 1, 2, 3].partitionMap (fun x => if x % 2 = 0 then .inl x else .inr x) = ([0, 2], [1, 3])` partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
* `[0, 1, 2, 3].partitionMap (fun x => if x = 0 then .inl x else .inr x) = ([0], [1, 2, 3])` ```
-/ -/
@[inline] def partitionMap (f : α β γ) (l : List α) : List β × List γ := go l #[] #[] where @[inline] def partitionMap (f : α β γ) (l : List α) : List β × List γ := go l #[] #[] where
/-- Auxiliary for `partitionMap`: /-- Auxiliary for `partitionMap`:
@@ -233,30 +199,19 @@ For verification purposes, `List.mapMono = List.map`.
return b' :: bs' return b' :: bs'
/-- /--
Applies a monadic function to each element of a list, returning the list of results. The function is Monomorphic `List.mapM`. The internal implementation uses pointer equality, and does not allocate a new list
monomorphic: it is required to return a value of the same type. The internal implementation uses if the result of each `f a` is a pointer equal value `a`.
pointer equality, and does not allocate a new list if the result of each function call is
pointer-equal to its argument.
-/ -/
@[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α m α) : m (List α) := @[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α m α) : m (List α) :=
match as with match as with
| [] => return [] | [] => return []
| a :: as => return ( f a) :: ( mapMonoM as f) | a :: as => return ( f a) :: ( mapMonoM as f)
/--
Applies a function to each element of a list, returning the list of results. The function is
monomorphic: it is required to return a value of the same type. The internal implementation uses
pointer equality, and does not allocate a new list if the result of each function call is
pointer-equal to its argument.
For verification purposes, `List.mapMono = List.map`.
-/
def mapMono (as : List α) (f : α α) : List α := def mapMono (as : List α) (f : α α) : List α :=
Id.run <| as.mapMonoM f Id.run <| as.mapMonoM f
/-! ## Additional lemmas required for bootstrapping `Array`. -/ /-! ## Additional lemmas required for bootstrapping `Array`. -/
@[simp]
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} : theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} :
(as ++ bs)[i] = as[i] := by (as ++ bs)[i] = as[i] := by
induction as generalizing i with induction as generalizing i with
@@ -266,7 +221,6 @@ theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as
| zero => rfl | zero => rfl
| succ i => apply ih | succ i => apply ih
@[simp]
theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length i) {h₂} : theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length i) {h₂} :
(as ++ bs)[i]'h₂ = (as ++ bs)[i]'h₂ =
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by

View File

@@ -46,12 +46,11 @@ Users that want to use `mapM` with `Applicative` should use `mapA` instead.
-/ -/
/-- /--
Applies the monadic action `f` to every element in the list, left-to-right, and returns the list of Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
results. results.
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be See `List.forM` for the variant that discards the results.
more convenient to reason about. `List.forM` is the variant that discards the results and See `List.mapA` for the variant that works with `Applicative`.
`List.mapA` is the variant that works with `Applicative`.
-/ -/
@[inline] @[inline]
def mapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m β) (as : List α) : m (List β) := def mapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m β) (as : List α) : m (List β) :=
@@ -61,15 +60,15 @@ def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α
loop as [] loop as []
/-- /--
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list Applies the applicative action `f` on every element in the list, left-to-right, and returns the list of
of results. results.
If `m` is also a `Monad`, then using `mapM` can be more efficient. NB: If `m` is also a `Monad`, then using `mapM` can be more efficient.
See `List.forA` for the variant that discards the results. See `List.mapM` for the variant that See `List.forA` for the variant that discards the results.
works with `Monad`. See `List.mapM` for the variant that works with `Monad`.
This function is not tail-recursive, so it may fail with a stack overflow on long lists. **Warning**: this function is not tail-recursive, meaning that it may fail with a stack overflow on long lists.
-/ -/
@[specialize] @[specialize]
def mapA {m : Type u Type v} [Applicative m] {α : Type w} {β : Type u} (f : α m β) : List α m (List β) def mapA {m : Type u Type v} [Applicative m] {α : Type w} {β : Type u} (f : α m β) : List α m (List β)
@@ -77,10 +76,10 @@ def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f
| a::as => List.cons <$> f a <*> mapA f as | a::as => List.cons <$> f a <*> mapA f as
/-- /--
Applies the monadic action `f` to every element in the list, in order. Applies the monadic action `f` on every element in the list, left-to-right.
`List.mapM` is a variant that collects results. `List.forA` is a variant that works on any See `List.mapM` for the variant that collects results.
`Applicative`. See `List.forA` for the variant that works with `Applicative`.
-/ -/
@[specialize] @[specialize]
protected def forM {m : Type u Type v} [Monad m] {α : Type w} (as : List α) (f : α m PUnit) : m PUnit := protected def forM {m : Type u Type v} [Monad m] {α : Type w} (as : List α) (f : α m PUnit) : m PUnit :=
@@ -89,11 +88,12 @@ protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α
| a :: as => do f a; List.forM as f | a :: as => do f a; List.forM as f
/-- /--
Applies the applicative action `f` to every element in the list, in order. Applies the applicative action `f` on every element in the list, left-to-right.
If `m` is also a `Monad`, then using `List.forM` can be more efficient. NB: If `m` is also a `Monad`, then using `forM` can be more efficient.
`List.mapA` is a variant that collects results. See `List.mapA` for the variant that collects results.
See `List.forM` for the variant that works with `Monad`.
-/ -/
@[specialize] @[specialize]
def forA {m : Type u Type v} [Applicative m] {α : Type w} (as : List α) (f : α m PUnit) : m PUnit := def forA {m : Type u Type v} [Applicative m] {α : Type w} (as : List α) (f : α m PUnit) : m PUnit :=
@@ -110,28 +110,8 @@ def filterAuxM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) :
filterAuxM f t (cond b (h :: acc) acc) filterAuxM f t (cond b (h :: acc) acc)
/-- /--
Applies the monadic predicate `p` to every element in the list, in order from left to right, and Applies the monadic predicate `p` on every element in the list, left-to-right, and returns those
returns the list of elements for which `p` returns `true`. elements `x` for which `p x` returns `true`.
`O(|l|)`.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterM fun x => do
IO.println s!"Checking {x}"
return x < 3
```
```output
Checking 1
Checking 2
Checking 5
Checking 2
Checking 7
Checking 7
```
```output
[1, 2, 2]
```
-/ -/
@[inline] @[inline]
def filterM {m : Type Type v} [Monad m] {α : Type} (p : α m Bool) (as : List α) : m (List α) := do def filterM {m : Type Type v} [Monad m] {α : Type} (p : α m Bool) (as : List α) : m (List α) := do
@@ -139,56 +119,16 @@ def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as
pure as.reverse pure as.reverse
/-- /--
Applies the monadic predicate `p` on every element in the list in reverse order, from right to left, Applies the monadic predicate `p` on every element in the list, right-to-left, and returns those
and returns those elements for which `p` returns `true`. The elements of the returned list are in elements `x` for which `p x` returns `true`.
the same order as in the input list.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterRevM fun x => do
IO.println s!"Checking {x}"
return x < 3
```
```output
Checking 7
Checking 7
Checking 2
Checking 5
Checking 2
Checking 1
```
```output
[1, 2, 2]
```
-/ -/
@[inline] @[inline]
def filterRevM {m : Type Type v} [Monad m] {α : Type} (p : α m Bool) (as : List α) : m (List α) := def filterRevM {m : Type Type v} [Monad m] {α : Type} (p : α m Bool) (as : List α) : m (List α) :=
filterAuxM p as.reverse [] filterAuxM p as.reverse []
/-- /--
Applies a monadic function that returns an `Option` to each element of a list, collecting the Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns those
non-`none` values. results `y` for which `f x` returns `some y`.
`O(|l|)`.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterMapM fun x => do
IO.println s!"Examining {x}"
if x > 2 then return some (2 * x)
else return none
```
```output
Examining 1
Examining 2
Examining 5
Examining 2
Examining 7
Examining 7
```
```output
[10, 14, 14]
```
-/ -/
@[inline] @[inline]
def filterMapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (Option β)) (as : List α) : m (List β) := def filterMapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (Option β)) (as : List α) : m (List β) :=
@@ -201,8 +141,8 @@ def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
loop as [] loop as []
/-- /--
Applies a monadic function that returns a list to each element of a list, from left to right, and Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns the
concatenates the resulting lists. concatenation of the results.
-/ -/
@[inline] @[inline]
def flatMapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (List β)) (as : List α) : m (List β) := def flatMapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (List β)) (as : List α) : m (List β) :=
@@ -213,20 +153,14 @@ def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f :
loop as (bs' :: bs) loop as (bs' :: bs)
loop as [] loop as []
/-- /--
Folds a monadic function over a list from the left, accumulating a value starting with `init`. The Folds a monadic function over a list from left to right:
accumulated value is combined with the each element of the list in order, using `f`. ```
foldlM f x₀ [a, b, c] = do
Example: let x₁ ← f x₀ a
```lean example let x₂ ← f x₁ b
example [Monad m] (f : α → β → m α) : let x₃ ← f x₂ c
List.foldlM (m := m) f x₀ [a, b, c] = (do pure x₃
let x₁ ← f x₀ a
let x₂ ← f x₁ b
let x₃ ← f x₂ c
pure x₃)
:= by rfl
``` ```
-/ -/
@[specialize] @[specialize]
@@ -242,18 +176,13 @@ def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s
simp [List.foldlM] simp [List.foldlM]
/-- /--
Folds a monadic function over a list from the right, accumulating a value starting with `init`. The Folds a monadic function over a list from right to left:
accumulated value is combined with the each element of the list in reverse order, using `f`. ```
foldrM f x₀ [a, b, c] = do
Example: let x₁ ← f c x₀
```lean example let x₂ ← f b x₁
example [Monad m] (f : α → β → m β) : let x₃ ← f a x₂
List.foldrM (m := m) f x₀ [a, b, c] = (do pure x₃
let x₁ ← f c x₀
let x₂ ← f b x₁
let x₃ ← f a x₂
pure x₃)
:= by rfl
``` ```
-/ -/
@[inline] @[inline]
@@ -263,70 +192,32 @@ def foldrM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} (f : α
@[simp] theorem foldrM_nil [Monad m] (f : α β m β) (b) : [].foldrM f b = pure b := rfl @[simp] theorem foldrM_nil [Monad m] (f : α β m β) (b) : [].foldrM f b = pure b := rfl
/-- /--
Maps `f` over the list and collects the results with `<|>`. The result for the end of the list is Maps `f` over the list and collects the results with `<|>`.
`failure`. ```
firstM f [a, b, c] = f a <|> f b <|> f c <|> failure
Examples: ```
* `[[], [1, 2], [], [2]].firstM List.head? = some 1`
* `[[], [], []].firstM List.head? = none`
* `[].firstM List.head? = none`
-/ -/
@[specialize] @[specialize]
def firstM {m : Type u Type v} [Alternative m] {α : Type w} {β : Type u} (f : α m β) : List α m β def firstM {m : Type u Type v} [Alternative m] {α : Type w} {β : Type u} (f : α m β) : List α m β
| [] => failure | [] => failure
| a::as => f a <|> firstM f as | a::as => f a <|> firstM f as
/--
Returns true if the monadic predicate `p` returns `true` for any element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `true`. The elements in `l` are examined in
order from left to right.
-/
@[specialize] @[specialize]
def anyM {m : Type Type u} [Monad m] {α : Type v} (p : α m Bool) : (l : List α) m Bool def anyM {m : Type Type u} [Monad m] {α : Type v} (f : α m Bool) : List α m Bool
| [] => pure false | [] => pure false
| a::as => do | a::as => do
match ( p a) with match ( f a) with
| true => pure true | true => pure true
| false => anyM p as | false => anyM f as
/--
Returns true if the monadic predicate `p` returns `true` for every element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `false`. The elements in `l` are examined in
order from left to right.
-/
@[specialize] @[specialize]
def allM {m : Type Type u} [Monad m] {α : Type v} (p : α m Bool) : (l : List α) m Bool def allM {m : Type Type u} [Monad m] {α : Type v} (f : α m Bool) : List α m Bool
| [] => pure true | [] => pure true
| a::as => do | a::as => do
match ( p a) with match ( f a) with
| true => allM p as | true => allM f as
| false => pure false | false => pure false
/--
Returns the first element of the list for which the monadic predicate `p` returns `true`, or `none`
if no such element is found. Elements of the list are checked in order.
`O(|l|)`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].findM? fun i => do
if i < 5 then
return true
if i ≤ 6 then
IO.println s!"Almost! {i}"
return false
```
```output
Almost! 6
Almost! 5
```
```output
some 1
```
-/
@[specialize] @[specialize]
def findM? {m : Type Type u} [Monad m] {α : Type} (p : α m Bool) : List α m (Option α) def findM? {m : Type Type u} [Monad m] {α : Type} (p : α m Bool) : List α m (Option α)
| [] => pure none | [] => pure none
@@ -336,43 +227,15 @@ def findM? {m : Type → Type u} [Monad m] {α : Type} (p : α → m Bool) : Lis
| false => findM? p as | false => findM? p as
@[simp] @[simp]
theorem findM?_pure {m} [Monad m] [LawfulMonad m] (p : α Bool) (as : List α) : theorem findM?_id (p : α Bool) (as : List α) : findM? (m := Id) p as = as.find? p := by
findM? (m := m) (pure <| p ·) as = pure (as.find? p) := by
induction as with induction as with
| nil => rfl | nil => rfl
| cons a as ih => | cons a as ih =>
simp only [findM?, find?] simp only [findM?, find?]
cases p a with cases p a with
| true => simp | true => rfl
| false => simp [ih] | false => rw [ih]; rfl
@[simp]
theorem findM?_id (p : α Bool) (as : List α) : findM? (m := Id) p as = as.find? p :=
findM?_pure _ _
/--
Returns the first non-`none` result of applying the monadic function `f` to each element of the
list, in order. Returns `none` if `f` returns `none` for all elements.
`O(|l|)`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].findSomeM? fun i => do
if i < 5 then
return some (i * 10)
if i ≤ 6 then
IO.println s!"Almost! {i}"
return none
```
```output
Almost! 6
Almost! 5
```
```output
some 10
```
-/
@[specialize] @[specialize]
def findSomeM? {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (Option β)) : List α m (Option β) def findSomeM? {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (Option β)) : List α m (Option β)
| [] => pure none | [] => pure none
@@ -382,19 +245,14 @@ def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
| none => findSomeM? f as | none => findSomeM? f as
@[simp] @[simp]
theorem findSomeM?_pure [Monad m] [LawfulMonad m] (f : α Option β) (as : List α) : theorem findSomeM?_id (f : α Option β) (as : List α) : findSomeM? (m := Id) f as = as.findSome? f := by
findSomeM? (m := m) (pure <| f ·) as = pure (as.findSome? f) := by
induction as with induction as with
| nil => rfl | nil => rfl
| cons a as ih => | cons a as ih =>
simp only [findSomeM?, findSome?] simp only [findSomeM?, findSome?]
cases f a with cases f a with
| some b => simp | some b => rfl
| none => simp [ih] | none => rw [ih]; rfl
@[simp]
theorem findSomeM?_id (f : α Option β) (as : List α) : findSomeM? (m := Id) f as = as.findSome? f :=
findSomeM?_pure _ _
theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] (p : α m Bool) (as : List α) : theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] (p : α m Bool) (as : List α) :
as.findM? p = as.findSomeM? fun a => return if ( p a) then some a else none := by as.findM? p = as.findSomeM? fun a => return if ( p a) then some a else none := by

View File

@@ -90,7 +90,7 @@ theorem countP_le_length : countP p l ≤ l.length := by
simp only [countP_eq_length_filter, length_eq_zero_iff, filter_eq_nil_iff] simp only [countP_eq_length_filter, length_eq_zero_iff, filter_eq_nil_iff]
@[simp] theorem countP_eq_length {p} : countP p l = l.length a l, p a := by @[simp] theorem countP_eq_length {p} : countP p l = l.length a l, p a := by
rw [countP_eq_length_filter, length_filter_eq_length_iff] rw [countP_eq_length_filter, filter_length_eq_length]
theorem countP_replicate (p : α Bool) (a : α) (n : Nat) : theorem countP_replicate (p : α Bool) (a : α) (n : Nat) :
countP p (replicate n a) = if p a then n else 0 := by countP p (replicate n a) = if p a then n else 0 := by

View File

@@ -11,13 +11,7 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List namespace List
/-- /-- `finRange n` lists all elements of `Fin n` in order -/
Lists all elements of `Fin n` in order, starting at `0`.
Examples:
* `List.finRange 0 = ([] : List (Fin 0))`
* `List.finRange 2 = ([0, 1] : List (Fin 2))`
-/
def finRange (n : Nat) : List (Fin n) := ofFn fun i => i def finRange (n : Nat) : List (Fin n) := ofFn fun i => i
@[simp] theorem length_finRange (n) : (List.finRange n).length = n := by @[simp] theorem length_finRange (n) : (List.finRange n).length = n := by

View File

@@ -540,6 +540,11 @@ theorem findIdx_getElem {xs : List α} {w : xs.findIdx p < xs.length} :
p xs[xs.findIdx p] := p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w) xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
@[deprecated findIdx_getElem (since := "2024-08-12")]
theorem findIdx_get {xs : List α} {w : xs.findIdx p < xs.length} :
p (xs.get xs.findIdx p, w) :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
theorem findIdx_lt_length_of_exists {xs : List α} (h : x xs, p x) : theorem findIdx_lt_length_of_exists {xs : List α} (h : x xs, p x) :
xs.findIdx p < xs.length := by xs.findIdx p < xs.length := by
induction xs with induction xs with

View File

@@ -17,8 +17,7 @@ then at runtime you will get non-tail recursive versions of the following defini
-/ -/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables. set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- TODO: restore after an update-stage0 set_option linter.indexVariables true -- Enforce naming conventions for index variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List namespace List
@@ -50,16 +49,7 @@ The following operations are given `@[csimp]` replacements below:
/-! ### set -/ /-! ### set -/
/-- /-- Tail recursive version of `List.set`. -/
Replaces the value at (zero-based) index `n` in `l` with `a`. If the index is out of bounds, then
the list is returned unmodified.
This is a tail-recursive version of `List.set` that's used at runtime.
Examples:
* `["water", "coffee", "soda", "juice"].set 1 "tea" = ["water", "tea", "soda", "juice"]`
* `["water", "coffee", "soda", "juice"].set 4 "tea" = ["water", "coffee", "soda", "juice"]`
-/
@[inline] def setTR (l : List α) (n : Nat) (a : α) : List α := go l n #[] where @[inline] def setTR (l : List α) (n : Nat) (a : α) : List α := go l n #[] where
/-- Auxiliary for `setTR`: `setTR.go l a xs n acc = acc.toList ++ set xs a`, /-- Auxiliary for `setTR`: `setTR.go l a xs n acc = acc.toList ++ set xs a`,
unless `n ≥ l.length` in which case it returns `l` -/ unless `n ≥ l.length` in which case it returns `l` -/
@@ -79,22 +69,7 @@ Examples:
/-! ### filterMap -/ /-! ### filterMap -/
/-- Tail recursive version of `filterMap`. -/
/--
Applies a function that returns an `Option` to each element of a list, collecting the non-`none`
values.
`O(|l|)`. This is a tail-recursive version of `List.filterMap`, used at runtime.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterMapTR fun x =>
if x > 2 then some (2 * x) else none
```
```output
[10, 14, 14]
```
-/
@[inline] def filterMapTR (f : α Option β) (l : List α) : List β := go l #[] where @[inline] def filterMapTR (f : α Option β) (l : List α) : List β := go l #[] where
/-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/ /-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/
@[specialize] go : List α Array β List β @[specialize] go : List α Array β List β
@@ -115,17 +90,7 @@ Example:
/-! ### foldr -/ /-! ### foldr -/
/-- /-- Tail recursive version of `List.foldr`. -/
Folds a function over a list from the right, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in reverse order, using `f`.
`O(|l|)`. This is the tail-recursive replacement for `List.foldr` in runtime code.
Examples:
* `[a, b, c].foldrTR f init = f a (f b (f c init))`
* `[1, 2, 3].foldrTR (toString · ++ ·) "" = "123"`
* `[1, 2, 3].foldrTR (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
-/
@[specialize] def foldrTR (f : α β β) (init : β) (l : List α) : β := l.toArray.foldr f init @[specialize] def foldrTR (f : α β β) (init : β) (l : List α) : β := l.toArray.foldr f init
@[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by @[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by
@@ -133,16 +98,7 @@ Examples:
/-! ### flatMap -/ /-! ### flatMap -/
/-- /-- Tail recursive version of `List.flatMap`. -/
Applies a function that returns a list to each element of a list, and concatenates the resulting
lists.
This is the tail-recursive version of `List.flatMap` that's used at runtime.
Examples:
* `[2, 3, 2].flatMapTR List.range = [0, 1, 0, 1, 2, 0, 1]`
* `["red", "blue"].flatMapTR String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
-/
@[inline] def flatMapTR (f : α List β) (as : List α) : List β := go as #[] where @[inline] def flatMapTR (f : α List β) (as : List α) : List β := go as #[] where
/-- Auxiliary for `flatMap`: `flatMap.go f as = acc.toList ++ bind f as` -/ /-- Auxiliary for `flatMap`: `flatMap.go f as = acc.toList ++ bind f as` -/
@[specialize] go : List α Array β List β @[specialize] go : List α Array β List β
@@ -158,15 +114,7 @@ Examples:
/-! ### flatten -/ /-! ### flatten -/
/-- /-- Tail recursive version of `List.flatten`. -/
Concatenates a list of lists into a single list, preserving the order of the elements.
`O(|flatten L|)`. This is a tail-recursive version of `List.flatten`, used in runtime code.
Examples:
* `[["a"], ["b", "c"]].flattenTR = ["a", "b", "c"]`
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flattenTR = ["a", "b", "c", "d", "e", "f"]`
-/
@[inline] def flattenTR (l : List (List α)) : List α := l.flatMapTR id @[inline] def flattenTR (l : List (List α)) : List α := l.flatMapTR id
@[csimp] theorem flatten_eq_flattenTR : @flatten = @flattenTR := by @[csimp] theorem flatten_eq_flattenTR : @flatten = @flattenTR := by
@@ -176,16 +124,7 @@ Examples:
/-! ### take -/ /-! ### take -/
/-- /-- Tail recursive version of `List.take`. -/
Extracts the first `n` elements of `xs`, or the whole list if `n` is greater than `xs.length`.
`O(min n |xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
Examples:
* `[a, b, c, d, e].takeTR 0 = []`
* `[a, b, c, d, e].takeTR 3 = [a, b, c]`
* `[a, b, c, d, e].takeTR 6 = [a, b, c, d, e]`
-/
@[inline] def takeTR (n : Nat) (l : List α) : List α := go l n #[] where @[inline] def takeTR (n : Nat) (l : List α) : List α := go l n #[] where
/-- Auxiliary for `take`: `take.go l xs n acc = acc.toList ++ take n xs`, /-- Auxiliary for `take`: `take.go l xs n acc = acc.toList ++ take n xs`,
unless `n ≥ xs.length` in which case it returns `l`. -/ unless `n ≥ xs.length` in which case it returns `l`. -/
@@ -207,17 +146,7 @@ Examples:
/-! ### takeWhile -/ /-! ### takeWhile -/
/-- Tail recursive version of `List.takeWhile`. -/
/--
Returns the longest initial segment of `xs` for which `p` returns true.
`O(|xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
Examples:
* `[7, 6, 4, 8].takeWhileTR (· > 5) = [7, 6]`
* `[7, 6, 6, 5].takeWhileTR (· > 5) = [7, 6, 6]`
* `[7, 6, 6, 8].takeWhileTR (· > 5) = [7, 6, 6, 8]`
-/
@[inline] def takeWhileTR (p : α Bool) (l : List α) : List α := go l #[] where @[inline] def takeWhileTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `takeWhile`: `takeWhile.go p l xs acc = acc.toList ++ takeWhile p xs`, /-- Auxiliary for `takeWhile`: `takeWhile.go p l xs acc = acc.toList ++ takeWhile p xs`,
unless no element satisfying `p` is found in `xs` in which case it returns `l`. -/ unless no element satisfying `p` is found in `xs` in which case it returns `l`. -/
@@ -240,16 +169,7 @@ Examples:
/-! ### dropLast -/ /-! ### dropLast -/
/-- /-- Tail recursive version of `dropLast`. -/
Removes the last element of the list, if one exists.
This is a tail-recursive version of `List.dropLast`, used at runtime.
Examples:
* `[].dropLastTR = []`
* `["tea"].dropLastTR = []`
* `["tea", "coffee", "juice"].dropLastTR = ["tea", "coffee"]`
-/
@[inline] def dropLastTR (l : List α) : List α := l.toArray.pop.toList @[inline] def dropLastTR (l : List α) : List α := l.toArray.pop.toList
@[csimp] theorem dropLast_eq_dropLastTR : @dropLast = @dropLastTR := by @[csimp] theorem dropLast_eq_dropLastTR : @dropLast = @dropLastTR := by
@@ -259,16 +179,7 @@ Examples:
/-! ### replace -/ /-! ### replace -/
/-- /-- Tail recursive version of `List.replace`. -/
Replaces the first element of the list `l` that is equal to `a` with `b`. If no element is equal to
`a`, then the list is returned unchanged.
`O(|l|)`. This is a tail-recursive version of `List.replace` that's used in runtime code.
Examples:
* `[1, 4, 2, 3, 3, 7].replaceTR 3 6 = [1, 4, 2, 6, 3, 7]`
* `[1, 4, 2, 3, 3, 7].replaceTR 5 6 = [1, 4, 2, 3, 3, 7]`
-/
@[inline] def replaceTR [BEq α] (l : List α) (b c : α) : List α := go l #[] where @[inline] def replaceTR [BEq α] (l : List α) (b c : α) : List α := go l #[] where
/-- Auxiliary for `replace`: `replace.go l b c xs acc = acc.toList ++ replace xs b c`, /-- Auxiliary for `replace`: `replace.go l b c xs acc = acc.toList ++ replace xs b c`,
unless `b` is not found in `xs` in which case it returns `l`. -/ unless `b` is not found in `xs` in which case it returns `l`. -/
@@ -291,76 +202,42 @@ Examples:
/-! ### modify -/ /-! ### modify -/
/-- /-- Tail-recursive version of `modify`. -/
Replaces the element at the given index, if it exists, with the result of applying `f` to it. def modifyTR (f : α α) (n : Nat) (l : List α) : List α := go l n #[] where
/-- Auxiliary for `modifyTR`: `modifyTR.go f l n acc = acc.toList ++ modify f n l`. -/
This is a tail-recursive version of `List.modify`.
Examples:
* `[1, 2, 3].modifyTR 0 (· * 10) = [10, 2, 3]`
* `[1, 2, 3].modifyTR 2 (· * 10) = [1, 2, 30]`
* `[1, 2, 3].modifyTR 3 (· * 10) = [1, 2, 3]`
-/
def modifyTR (l : List α) (i : Nat) (f : α α) : List α := go l i #[] where
/-- Auxiliary for `modifyTR`: `modifyTR.go f l i acc = acc.toList ++ modify f i l`. -/
go : List α Nat Array α List α go : List α Nat Array α List α
| [], _, acc => acc.toList | [], _, acc => acc.toList
| a :: l, 0, acc => acc.toListAppend (f a :: l) | a :: l, 0, acc => acc.toListAppend (f a :: l)
| a :: l, i+1, acc => go l i (acc.push a) | a :: l, n+1, acc => go l n (acc.push a)
theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify l i f theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify f i l
| [], i => by cases i <;> simp [modifyTR.go, modify] | [], n => by cases n <;> simp [modifyTR.go, modify]
| a :: l, 0 => by simp [modifyTR.go, modify] | a :: l, 0 => by simp [modifyTR.go, modify]
| a :: l, i+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l] | a :: l, n+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
@[csimp] theorem modify_eq_modifyTR : @modify = @modifyTR := by @[csimp] theorem modify_eq_modifyTR : @modify = @modifyTR := by
funext α l i f; simp [modifyTR, modifyTR_go_eq] funext α f n l; simp [modifyTR, modifyTR_go_eq]
/-! ### insertIdx -/ /-! ### insertIdx -/
/-- /-- Tail-recursive version of `insertIdx`. -/
Inserts an element into a list at the specified index. If the index is greater than the length of @[inline] def insertIdxTR (n : Nat) (a : α) (l : List α) : List α := go n l #[] where
the list, then the list is returned unmodified.
In other words, the new element is inserted into the list `l` after the first `i` elements of `l`.
This is a tail-recursive version of `List.insertIdx`, used at runtime.
Examples:
* `["tues", "thur", "sat"].insertIdxTR 1 "wed" = ["tues", "wed", "thur", "sat"]`
* `["tues", "thur", "sat"].insertIdxTR 2 "wed" = ["tues", "thur", "wed", "sat"]`
* `["tues", "thur", "sat"].insertIdxTR 3 "wed" = ["tues", "thur", "sat", "wed"]`
* `["tues", "thur", "sat"].insertIdxTR 4 "wed" = ["tues", "thur", "sat"]`
-/
@[inline] def insertIdxTR (l : List α) (n : Nat) (a : α) : List α := go n l #[] where
/-- Auxiliary for `insertIdxTR`: `insertIdxTR.go a n l acc = acc.toList ++ insertIdx n a l`. -/ /-- Auxiliary for `insertIdxTR`: `insertIdxTR.go a n l acc = acc.toList ++ insertIdx n a l`. -/
go : Nat List α Array α List α go : Nat List α Array α List α
| 0, l, acc => acc.toListAppend (a :: l) | 0, l, acc => acc.toListAppend (a :: l)
| _, [], acc => acc.toList | _, [], acc => acc.toList
| n+1, a :: l, acc => go n l (acc.push a) | n+1, a :: l, acc => go n l (acc.push a)
theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx i a l
| 0, l | _+1, [] => by simp [insertIdxTR.go, insertIdx] | 0, l | _+1, [] => by simp [insertIdxTR.go, insertIdx]
| n+1, a :: l => by simp [insertIdxTR.go, insertIdx, insertIdxTR_go_eq n l] | n+1, a :: l => by simp [insertIdxTR.go, insertIdx, insertIdxTR_go_eq n l]
@[csimp] theorem insertIdx_eq_insertIdxTR : @insertIdx = @insertIdxTR := by @[csimp] theorem insertIdx_eq_insertIdxTR : @insertIdx = @insertIdxTR := by
funext α l i a; simp [insertIdxTR, insertIdxTR_go_eq] funext α f n l; simp [insertIdxTR, insertIdxTR_go_eq]
/-! ### erase -/ /-! ### erase -/
/-- /-- Tail recursive version of `List.erase`. -/
Removes the first occurrence of `a` from `l`. If `a` does not occur in `l`, the list is returned
unmodified.
`O(|l|)`.
This is a tail-recursive version of `List.erase`, used in runtime code.
Examples:
* `[1, 5, 3, 2, 5].eraseTR 5 = [1, 3, 2, 5]`
* `[1, 5, 3, 2, 5].eraseTR 6 = [1, 5, 3, 2, 5]`
-/
@[inline] def eraseTR [BEq α] (l : List α) (a : α) : List α := go l #[] where @[inline] def eraseTR [BEq α] (l : List α) (a : α) : List α := go l #[] where
/-- Auxiliary for `eraseTR`: `eraseTR.go l a xs acc = acc.toList ++ erase xs a`, /-- Auxiliary for `eraseTR`: `eraseTR.go l a xs acc = acc.toList ++ erase xs a`,
unless `a` is not present in which case it returns `l` -/ unless `a` is not present in which case it returns `l` -/
@@ -380,17 +257,7 @@ Examples:
· rw [IH] <;> simp_all · rw [IH] <;> simp_all
· simp · simp
/-- /-- Tail-recursive version of `eraseP`. -/
Removes the first element of a list for which `p` returns `true`. If no element satisfies `p`, then
the list is returned unchanged.
This is a tail-recursive version of `eraseP`, used at runtime.
Examples:
* `[2, 1, 2, 1, 3, 4].erasePTR (· < 2) = [2, 2, 1, 3, 4]`
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 2) = [2, 1, 2, 1, 4]`
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 8) = [2, 1, 2, 1, 3, 4]`
-/
@[inline] def erasePTR (p : α Bool) (l : List α) : List α := go l #[] where @[inline] def erasePTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`, /-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/ unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
@@ -410,20 +277,7 @@ Examples:
/-! ### eraseIdx -/ /-! ### eraseIdx -/
/-- Tail recursive version of `List.eraseIdx`. -/
/--
Removes the element at the specified index. If the index is out of bounds, the list is returned
unmodified.
`O(i)`.
This is a tail-recursive version of `List.eraseIdx`, used at runtime.
Examples:
* `[0, 1, 2, 3, 4].eraseIdxTR 0 = [1, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdxTR 1 = [0, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdxTR 5 = [0, 1, 2, 3, 4]`
-/
@[inline] def eraseIdxTR (l : List α) (n : Nat) : List α := go l n #[] where @[inline] def eraseIdxTR (l : List α) (n : Nat) : List α := go l n #[] where
/-- Auxiliary for `eraseIdxTR`: `eraseIdxTR.go l n xs acc = acc.toList ++ eraseIdx xs a`, /-- Auxiliary for `eraseIdxTR`: `eraseIdxTR.go l n xs acc = acc.toList ++ eraseIdx xs a`,
unless `a` is not present in which case it returns `l` -/ unless `a` is not present in which case it returns `l` -/
@@ -449,18 +303,7 @@ Examples:
/-! ### zipWith -/ /-! ### zipWith -/
/-- /-- Tail recursive version of `List.zipWith`. -/
Applies a function to the corresponding elements of two lists, stopping at the end of the shorter
list.
`O(min |xs| |ys|)`. This is a tail-recursive version of `List.zipWith` that's used at runtime.
Examples:
* `[1, 2].zipWithTR (· + ·) [5, 6] = [6, 8]`
* `[1, 2, 3].zipWithTR (· + ·) [5, 6, 10] = [6, 8, 13]`
* `[].zipWithTR (· + ·) [5, 6] = []`
* `[x₁, x₂, x₃].zipWithTR f [y₁, y₂, y₃, y₄] = [f x₁ y₁, f x₂ y₂, f x₃ y₃]`
-/
@[inline] def zipWithTR (f : α β γ) (as : List α) (bs : List β) : List γ := go as bs #[] where @[inline] def zipWithTR (f : α β γ) (as : List α) (bs : List β) : List γ := go as bs #[] where
/-- Auxiliary for `zipWith`: `zipWith.go f as bs acc = acc.toList ++ zipWith f as bs` -/ /-- Auxiliary for `zipWith`: `zipWith.go f as bs acc = acc.toList ++ zipWith f as bs` -/
go : List α List β Array γ List γ go : List α List β Array γ List γ
@@ -478,16 +321,7 @@ Examples:
/-! ### zipIdx -/ /-! ### zipIdx -/
/-- Tail recursive version of `List.zipIdx`. -/
/--
Pairs each element of a list with its index, optionally starting from an index other than `0`.
`O(|l|)`. This is a tail-recursive version of `List.zipIdx` that's used at runtime.
Examples:
* `[a, b, c].zipIdxTR = [(a, 0), (b, 1), (c, 2)]`
* `[a, b, c].zipIdxTR 5 = [(a, 5), (b, 6), (c, 7)]`
-/
def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) := def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) :=
let as := l.toArray let as := l.toArray
(as.foldr (fun a (n, acc) => (n-1, (a, n-1) :: acc)) (n + as.size, [])).2 (as.foldr (fun a (n, acc) => (n-1, (a, n-1) :: acc)) (n + as.size, [])).2
@@ -529,18 +363,8 @@ theorem enumFrom_eq_enumFromTR : @enumFrom = @enumFromTR := by
/-! ### intercalate -/ /-! ### intercalate -/
set_option linter.listVariables false in set_option linter.listVariables false in
/-- /-- Tail recursive version of `List.intercalate`. -/
Alternates the lists in `xs` with the separator `sep`. def intercalateTR (sep : List α) : List (List α) List α
This is a tail-recursive version of `List.intercalate` used at runtime.
Examples:
* `List.intercalateTR sep [] = []`
* `List.intercalateTR sep [a] = a`
* `List.intercalateTR sep [a, b] = a ++ sep ++ b`
* `List.intercalateTR sep [a, b, c] = a ++ sep ++ b ++ sep ++ c`
-/
def intercalateTR (sep : List α) : (xs : List (List α)) List α
| [] => [] | [] => []
| [x] => x | [x] => x
| x::xs => go sep.toArray x xs #[] | x::xs => go sep.toArray x xs #[]

View File

@@ -815,6 +815,14 @@ theorem getElem_length_sub_one_eq_getLast (l : List α) (h : l.length - 1 < l.le
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
rw [ getLast_eq_getElem] rw [ getLast_eq_getElem]
@[deprecated getLast_eq_getElem (since := "2024-07-15")]
theorem getLast_eq_get (l : List α) (h : l []) :
getLast l h = l.get l.length - 1, by
match l with
| [] => contradiction
| a :: l => exact Nat.le_refl _ := by
simp [getLast_eq_getElem]
theorem getLast_cons {a : α} {l : List α} : (h : l nil), theorem getLast_cons {a : α} {l : List α} : (h : l nil),
getLast (a :: l) (cons_ne_nil a l) = getLast l h := by getLast (a :: l) (cons_ne_nil a l) = getLast l h := by
induction l <;> intros; {contradiction}; rfl induction l <;> intros; {contradiction}; rfl
@@ -939,12 +947,9 @@ theorem head_eq_iff_head?_eq_some {xs : List α} (h) : xs.head h = a ↔ xs.head
theorem head?_eq_some_iff {xs : List α} {a : α} : xs.head? = some a ys, xs = a :: ys := by theorem head?_eq_some_iff {xs : List α} {a : α} : xs.head? = some a ys, xs = a :: ys := by
cases xs <;> simp_all cases xs <;> simp_all
@[simp] theorem isSome_head? : l.head?.isSome l [] := by @[simp] theorem head?_isSome : l.head?.isSome l [] := by
cases l <;> simp cases l <;> simp
@[deprecated isSome_head? (since := "2025-03-18")]
abbrev head?_isSome := @isSome_head?
@[simp] theorem head_mem : {l : List α} (h : l []), head l h l @[simp] theorem head_mem : {l : List α} (h : l []), head l h l
| [], h => absurd rfl h | [], h => absurd rfl h
| _::_, _ => .head .. | _::_, _ => .head ..
@@ -1093,6 +1098,8 @@ theorem forall_mem_map {f : α → β} {l : List α} {P : β → Prop} :
( (i) (_ : i l.map f), P i) (j) (_ : j l), P (f j) := by ( (i) (_ : i l.map f), P i) (j) (_ : j l), P (f j) := by
simp simp
@[deprecated forall_mem_map (since := "2024-07-25")] abbrev forall_mem_map_iff := @forall_mem_map
@[simp] theorem map_eq_nil_iff {f : α β} {l : List α} : map f l = [] l = [] := by @[simp] theorem map_eq_nil_iff {f : α β} {l : List α} : map f l = [] l = [] := by
constructor <;> exact fun _ => match l with | [] => rfl constructor <;> exact fun _ => match l with | [] => rfl
@@ -1249,7 +1256,7 @@ theorem filter_eq_self {l} : filter p l = l ↔ ∀ a ∈ l, p a := by
intro h; exact Nat.lt_irrefl _ (h length_filter_le p l) intro h; exact Nat.lt_irrefl _ (h length_filter_le p l)
@[simp] @[simp]
theorem length_filter_eq_length_iff {l} : (filter p l).length = l.length a l, p a := by theorem filter_length_eq_length {l} : (filter p l).length = l.length a l, p a := by
induction l with induction l with
| nil => simp | nil => simp
| cons a l ih => | cons a l ih =>
@@ -1259,9 +1266,6 @@ theorem length_filter_eq_length_iff {l} : (filter p l).length = l.length ↔ ∀
· have := Nat.ne_of_lt (Nat.lt_succ.mpr (length_filter_le p l)) · have := Nat.ne_of_lt (Nat.lt_succ.mpr (length_filter_le p l))
simp_all simp_all
@[deprecated length_filter_eq_length_iff (since := "2024-09-05")]
abbrev filter_length_eq_length := @length_filter_eq_length_iff
@[simp] theorem mem_filter : x filter p as x as p x := by @[simp] theorem mem_filter : x filter p as x as p x := by
induction as with induction as with
| nil => simp [filter] | nil => simp [filter]
@@ -1279,6 +1283,8 @@ theorem forall_mem_filter {l : List α} {p : α → Bool} {P : α → Prop} :
( (i) (_ : i l.filter p), P i) (j) (_ : j l), p j P j := by ( (i) (_ : i l.filter p), P i) (j) (_ : j l), p j P j := by
simp simp
@[deprecated forall_mem_filter (since := "2024-07-25")] abbrev forall_mem_filter_iff := @forall_mem_filter
@[simp] theorem filter_filter (q) : l, filter p (filter q l) = filter (fun a => p a && q a) l @[simp] theorem filter_filter (q) : l, filter p (filter q l) = filter (fun a => p a && q a) l
| [] => rfl | [] => rfl
| a :: l => by by_cases hp : p a <;> by_cases hq : q a <;> simp [hp, hq, filter_filter _ l] | a :: l => by by_cases hp : p a <;> by_cases hq : q a <;> simp [hp, hq, filter_filter _ l]
@@ -1451,6 +1457,8 @@ theorem forall_mem_filterMap {f : α → Option β} {l : List α} {P : β → Pr
intro a intro a
rw [forall_comm] rw [forall_comm]
@[deprecated forall_mem_filterMap (since := "2024-07-25")] abbrev forall_mem_filterMap_iff := @forall_mem_filterMap
@[simp] theorem filterMap_append {α β : Type _} (l l' : List α) (f : α Option β) : @[simp] theorem filterMap_append {α β : Type _} (l l' : List α) (f : α Option β) :
filterMap f (l ++ l') = filterMap f l ++ filterMap f l' := by filterMap f (l ++ l') = filterMap f l ++ filterMap f l' := by
induction l <;> simp [filterMap_cons]; split <;> simp [*] induction l <;> simp [filterMap_cons]; split <;> simp [*]
@@ -1651,18 +1659,29 @@ theorem getLast_concat {a : α} : ∀ (l : List α), getLast (l ++ [a]) (by simp
@[simp] theorem nil_eq_append_iff : [] = a ++ b a = [] b = [] := by @[simp] theorem nil_eq_append_iff : [] = a ++ b a = [] b = [] := by
rw [eq_comm, append_eq_nil_iff] rw [eq_comm, append_eq_nil_iff]
@[deprecated nil_eq_append_iff (since := "2024-07-24")] abbrev nil_eq_append := @nil_eq_append_iff
theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s []) (t : List α) : s ++ t [] := by simp_all theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s []) (t : List α) : s ++ t [] := by simp_all
theorem append_ne_nil_of_right_ne_nil (s : List α) : t [] s ++ t [] := by simp_all theorem append_ne_nil_of_right_ne_nil (s : List α) : t [] s ++ t [] := by simp_all
@[deprecated append_ne_nil_of_left_ne_nil (since := "2024-07-24")]
theorem append_ne_nil_of_ne_nil_left {s : List α} (h : s []) (t : List α) : s ++ t [] := by simp_all
@[deprecated append_ne_nil_of_right_ne_nil (since := "2024-07-24")]
theorem append_ne_nil_of_ne_nil_right (s : List α) : t [] s ++ t [] := by simp_all
theorem append_eq_cons_iff : theorem append_eq_cons_iff :
as ++ bs = x :: c (as = [] bs = x :: c) ( as', as = x :: as' c = as' ++ bs) := by as ++ bs = x :: c (as = [] bs = x :: c) ( as', as = x :: as' c = as' ++ bs) := by
cases as with simp | cons a as => ?_ cases as with simp | cons a as => ?_
exact fun h => as, by simp [h], fun as', aeq, aseq, h => aeq, by rw [aseq, h] exact fun h => as, by simp [h], fun as', aeq, aseq, h => aeq, by rw [aseq, h]
@[deprecated append_eq_cons_iff (since := "2024-07-24")] abbrev append_eq_cons := @append_eq_cons_iff
theorem cons_eq_append_iff : theorem cons_eq_append_iff :
x :: cs = as ++ bs (as = [] bs = x :: cs) ( as', as = x :: as' cs = as' ++ bs) := by x :: cs = as ++ bs (as = [] bs = x :: cs) ( as', as = x :: as' cs = as' ++ bs) := by
rw [eq_comm, append_eq_cons_iff] rw [eq_comm, append_eq_cons_iff]
@[deprecated cons_eq_append_iff (since := "2024-07-24")] abbrev cons_eq_append := @cons_eq_append_iff
theorem append_eq_singleton_iff : theorem append_eq_singleton_iff :
a ++ b = [x] (a = [] b = [x]) (a = [x] b = []) := by a ++ b = [x] (a = [] b = [x]) (a = [x] b = []) := by
cases a <;> cases b <;> simp cases a <;> cases b <;> simp
@@ -1677,6 +1696,9 @@ theorem append_eq_append_iff {ws xs ys zs : List α} :
| nil => simp_all | nil => simp_all
| cons a as ih => cases ys <;> simp [eq_comm, and_assoc, ih, and_or_left] | cons a as ih => cases ys <;> simp [eq_comm, and_assoc, ih, and_or_left]
@[deprecated append_inj (since := "2024-07-24")] abbrev append_inj_of_length_left := @append_inj
@[deprecated append_inj' (since := "2024-07-24")] abbrev append_inj_of_length_right := @append_inj'
@[simp] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) : @[simp] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) :
head (l ++ l') w₁ = head l w₂ := by head (l ++ l') w₁ = head l w₂ := by
match l, w₂ with match l, w₂ with
@@ -1724,6 +1746,8 @@ theorem tail_append {l l' : List α} : (l ++ l').tail = if l.isEmpty then l'.tai
(xs ++ ys).tail = xs.tail ++ ys := by (xs ++ ys).tail = xs.tail ++ ys := by
simp_all [tail_append] simp_all [tail_append]
@[deprecated tail_append_of_ne_nil (since := "2024-07-24")] abbrev tail_append_left := @tail_append_of_ne_nil
theorem set_append {s t : List α} : theorem set_append {s t : List α} :
(s ++ t).set i x = if i < s.length then s.set i x ++ t else s ++ t.set (i - s.length) x := by (s ++ t).set i x = if i < s.length then s.set i x ++ t else s ++ t.set (i - s.length) x := by
induction s generalizing i with induction s generalizing i with
@@ -2073,6 +2097,8 @@ theorem head?_flatMap {l : List α} {f : α → List β} :
(xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by (xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by
induction xs; {rfl}; simp_all [flatMap_cons, append_assoc] induction xs; {rfl}; simp_all [flatMap_cons, append_assoc]
@[deprecated flatMap_append (since := "2024-07-24")] abbrev append_bind := @flatMap_append
theorem flatMap_assoc {α β} (l : List α) (f : α List β) (g : β List γ) : theorem flatMap_assoc {α β} (l : List α) (f : α List β) (g : β List γ) :
(l.flatMap f).flatMap g = l.flatMap fun x => (f x).flatMap g := by (l.flatMap f).flatMap g = l.flatMap fun x => (f x).flatMap g := by
induction l <;> simp [*] induction l <;> simp [*]
@@ -2509,14 +2535,6 @@ theorem flatMap_reverse {β} (l : List α) (f : α → List β) : (l.reverse.fla
simp only [foldrM] simp only [foldrM]
induction l <;> simp_all induction l <;> simp_all
@[simp] theorem foldlM_pure [Monad m] [LawfulMonad m] (f : β α β) (b) (l : List α) :
l.foldlM (m := m) (pure <| f · ·) b = pure (l.foldl f b) := by
induction l generalizing b <;> simp [*]
@[simp] theorem foldrM_pure [Monad m] [LawfulMonad m] (f : α β β) (b) (l : List α) :
l.foldrM (m := m) (pure <| f · ·) b = pure (l.foldr f b) := by
induction l generalizing b <;> simp [*]
theorem foldl_eq_foldlM (f : β α β) (b) (l : List α) : theorem foldl_eq_foldlM (f : β α β) (b) (l : List α) :
l.foldl f b = l.foldlM (m := Id) f b := by l.foldl f b = l.foldlM (m := Id) f b := by
induction l generalizing b <;> simp [*, foldl] induction l generalizing b <;> simp [*, foldl]
@@ -2549,6 +2567,8 @@ theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
l.foldr cons l' = l ++ l' := by l.foldr cons l' = l ++ l' := by
induction l <;> simp [*] induction l <;> simp [*]
@[deprecated foldr_cons_eq_append (since := "2024-08-22")] abbrev foldr_self_append := @foldr_cons_eq_append
@[simp] theorem foldl_flip_cons_eq_append (l : List α) (f : α β) (l' : List β) : @[simp] theorem foldl_flip_cons_eq_append (l : List α) (f : α β) (l' : List β) :
l.foldl (fun xs y => f y :: xs) l' = (l.map f).reverse ++ l' := by l.foldl (fun xs y => f y :: xs) l' = (l.map f).reverse ++ l' := by
induction l generalizing l' <;> simp [*] induction l generalizing l' <;> simp [*]
@@ -2669,20 +2689,12 @@ theorem foldr_hom (f : β₁ → β₂) (g₁ : α → β₁ → β₁) (g₂ :
induction l <;> simp [*, H] induction l <;> simp [*, H]
/-- /--
A reasoning principle for proving propositions about the result of `List.foldl` by establishing an Prove a proposition about the result of `List.foldl`,
invariant that is true for the initial data and preserved by the operation being folded. by proving it for the initial data,
and the implication that the operation applied to any element of the list preserves the property.
Because the motive can return a type in any sort, this function may be used to construct data as The motive can take values in `Sort _`, so this may be used to construct data,
well as to prove propositions. as well as to prove propositions.
Example:
```lean example
example {xs : List Nat} : xs.foldl (· + ·) 1 > 0 := by
apply List.foldlRecOn
. show 0 < 1; trivial
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < b + a
intros; omega
```
-/ -/
def foldlRecOn {motive : β Sort _} : (l : List α) (op : β α β) (b : β) (_ : motive b) def foldlRecOn {motive : β Sort _} : (l : List α) (op : β α β) (b : β) (_ : motive b)
(_ : (b : β) (_ : motive b) (a : α) (_ : a l), motive (op b a)), motive (List.foldl op b l) (_ : (b : β) (_ : motive b) (a : α) (_ : a l), motive (op b a)), motive (List.foldl op b l)
@@ -2703,20 +2715,12 @@ def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α
rfl rfl
/-- /--
A reasoning principle for proving propositions about the result of `List.foldr` by establishing an Prove a proposition about the result of `List.foldr`,
invariant that is true for the initial data and preserved by the operation being folded. by proving it for the initial data,
and the implication that the operation applied to any element of the list preserves the property.
Because the motive can return a type in any sort, this function may be used to construct data as The motive can take values in `Sort _`, so this may be used to construct data,
well as to prove propositions. as well as to prove propositions.
Example:
```lean example
example {xs : List Nat} : xs.foldr (· + ·) 1 > 0 := by
apply List.foldrRecOn
. show 0 < 1; trivial
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < a + b
intros; omega
```
-/ -/
def foldrRecOn {motive : β Sort _} : (l : List α) (op : α β β) (b : β) (_ : motive b) def foldrRecOn {motive : β Sort _} : (l : List α) (op : α β β) (b : β) (_ : motive b)
(_ : (b : β) (_ : motive b) (a : α) (_ : a l), motive (op a b)), motive (List.foldr op b l) (_ : (b : β) (_ : motive b) (a : α) (_ : a l), motive (op a b)), motive (List.foldr op b l)
@@ -2813,7 +2817,7 @@ theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔
exact fun ys, h => ys.reverse, by simpa using h, fun ys, h => ys.reverse, by simpa using h exact fun ys, h => ys.reverse, by simpa using h, fun ys, h => ys.reverse, by simpa using h
@[simp] theorem getLast?_isSome : l.getLast?.isSome l [] := by @[simp] theorem getLast?_isSome : l.getLast?.isSome l [] := by
rw [getLast?_eq_head?_reverse, isSome_head?] rw [getLast?_eq_head?_reverse, head?_isSome]
simp simp
theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a xs := by theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a xs := by
@@ -3509,6 +3513,14 @@ theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a :=
/-! ### Deprecations -/ /-! ### Deprecations -/
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
@[deprecated filter_flatten (since := "2024-08-26")]
theorem join_map_filter (p : α Bool) (l : List (List α)) :
(l.map (filter p)).flatten = (l.flatten).filter p := by
rw [filter_flatten]
@[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? := @[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? :=
@getElem_eq_getElem?_get @getElem_eq_getElem?_get
@[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff @[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff

View File

@@ -19,11 +19,8 @@ namespace List
/-! ## Operations using indexes -/ /-! ## Operations using indexes -/
/-- /--
Applies a function to each element of the list along with the index at which that element is found, Given a list `as = [a₀, a₁, ...]` and a function `f : (i : Nat) → α → (h : i < as.length) → β`, returns the list
returning the list of results. In addition to the index, the function is also provided with a proof `[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
that the index is valid.
`List.mapIdx` is a variant that does not provide the function with evidence that the index is valid.
-/ -/
@[inline] def mapFinIdx (as : List α) (f : (i : Nat) α (h : i < as.length) β) : List β := @[inline] def mapFinIdx (as : List α) (f : (i : Nat) α (h : i < as.length) β) : List β :=
go as #[] (by simp) go as #[] (by simp)
@@ -36,11 +33,8 @@ where
go as (acc.push (f acc.size a (by simp at h; omega))) (by simp at h ; omega) go as (acc.push (f acc.size a (by simp at h; omega))) (by simp at h ; omega)
/-- /--
Applies a function to each element of the list along with the index at which that element is found, Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁, ...]`, returns the list
returning the list of results. `[f 0 a₀, f 1 a₁, ...]`.
`List.mapFinIdx` is a variant that additionally provides the function with a proof that the index
is valid.
-/ -/
@[inline] def mapIdx (f : Nat α β) (as : List α) : List β := go as #[] where @[inline] def mapIdx (f : Nat α β) (as : List α) : List β := go as #[] where
/-- Auxiliary for `mapIdx`: /-- Auxiliary for `mapIdx`:
@@ -50,12 +44,8 @@ is valid.
| a :: as, acc => go as (acc.push (f acc.size a)) | a :: as, acc => go as (acc.push (f acc.size a))
/-- /--
Applies a monadic function to each element of the list along with the index at which that element is Given a list `as = [a₀, a₁, ...]` and a monadic function `f : (i : Nat) → α → (h : i < as.length) → m β`,
found, returning the list of results. In addition to the index, the function is also provided with a returns the list `[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
proof that the index is valid.
`List.mapIdxM` is a variant that does not provide the function with evidence that the index is
valid.
-/ -/
@[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) α (h : i < as.length) m β) : m (List β) := @[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) α (h : i < as.length) m β) : m (List β) :=
go as #[] (by simp) go as #[] (by simp)
@@ -68,11 +58,8 @@ where
go as (acc.push ( f acc.size a (by simp at h; omega))) (by simp at h ; omega) go as (acc.push ( f acc.size a (by simp at h; omega))) (by simp at h ; omega)
/-- /--
Applies a monadic function to each element of the list along with the index at which that element is Given a monadic function `f : Nat → α → m β` and `as : List α`, `as = [a₀, a₁, ...]`,
found, returning the list of results. returns the list `[f 0 a₀, f 1 a₁, ...]`.
`List.mapFinIdxM` is a variant that additionally provides the function with a proof that the index
is valid.
-/ -/
@[inline] def mapIdxM [Monad m] (f : Nat α m β) (as : List α) : m (List β) := go as #[] where @[inline] def mapIdxM [Monad m] (f : Nat α m β) (as : List α) : m (List β) := go as #[] where
/-- Auxiliary for `mapIdxM`: /-- Auxiliary for `mapIdxM`:

View File

@@ -5,7 +5,6 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
-/ -/
prelude prelude
import Init.Data.List.Lemmas import Init.Data.List.Lemmas
import Init.Data.List.Pairwise
/-! /-!
# Lemmas about `List.min?` and `List.max?. # Lemmas about `List.min?` and `List.max?.
@@ -39,18 +38,6 @@ theorem isSome_min?_of_mem {l : List α} [Min α] {a : α} (h : a ∈ l) :
l.min?.isSome := by l.min?.isSome := by
cases l <;> simp_all [List.min?_cons'] cases l <;> simp_all [List.min?_cons']
theorem min?_eq_head? {α : Type u} [Min α] {l : List α}
(h : l.Pairwise (fun a b => min a b = a)) : l.min? = l.head? := by
cases l with
| nil => rfl
| cons x l =>
rw [List.head?_cons, List.min?_cons', Option.some.injEq]
induction l generalizing x with
| nil => simp
| cons y l ih =>
have hx : min x y = x := List.rel_of_pairwise_cons h (List.mem_cons_self _ _)
rw [List.foldl_cons, ih _ (hx.symm h.sublist (by simp)), hx]
theorem min?_mem [Min α] (min_eq_or : a b : α, min a b = a min a b = b) : theorem min?_mem [Min α] (min_eq_or : a b : α, min a b = a min a b = b) :
{xs : List α} xs.min? = some a a xs := by {xs : List α} xs.min? = some a a xs := by
intro xs intro xs
@@ -91,19 +78,17 @@ theorem le_min?_iff [Min α] [LE α]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`, -- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
-- and `le_min_iff`. -- and `le_min_iff`.
theorem min?_eq_some_iff [Min α] [LE α] theorem min?_eq_some_iff [Min α] [LE α] [anti : Std.Antisymm ((· : α) ·)]
(le_refl : a : α, a a) (le_refl : a : α, a a)
(min_eq_or : a b : α, min a b = a min a b = b) (min_eq_or : a b : α, min a b = a min a b = b)
(le_min_iff : a b c : α, a min b c a b a c) {xs : List α} (le_min_iff : a b c : α, a min b c a b a c) {xs : List α} :
(anti : a b, a xs b xs a b b a a = b := by
exact fun a b _ _ => Std.Antisymm.antisymm a b) :
xs.min? = some a a xs b, b xs a b := by xs.min? = some a a xs b, b xs a b := by
refine fun h => min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _), ?_ refine fun h => min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _), ?_
intro h₁, h₂ intro h₁, h₂
cases xs with cases xs with
| nil => simp at h₁ | nil => simp at h₁
| cons x xs => | cons x xs =>
exact congrArg some <| anti _ _ (min?_mem min_eq_or rfl) h₁ exact congrArg some <| anti.1 _ _
((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁) ((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
(h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl)) (h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl))

View File

@@ -31,13 +31,10 @@ attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
/-! ### mapM -/ /-! ### mapM -/
/-- /-- Alternate (non-tail-recursive) form of mapM for proofs.
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
results.
This is a non-tail-recursive variant of `List.mapM` that's easier to reason about. It cannot be used Note that we can not have this as the main definition and replace it using a `@[csimp]` lemma,
as the main definition and replaced by the tail-recursive version because they can only be proved because they are only equal when `m` is a `LawfulMonad`.
equal when `m` is a `LawfulMonad`.
-/ -/
def mapM' [Monad m] (f : α m β) : List α m (List β) def mapM' [Monad m] (f : α m β) : List α m (List β)
| [] => pure [] | [] => pure []
@@ -59,13 +56,9 @@ theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α)
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α m β) : @[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α m β) :
(a :: l).mapM f = (return ( f a) :: ( l.mapM f)) := by simp [ mapM'_eq_mapM, mapM'] (a :: l).mapM f = (return ( f a) :: ( l.mapM f)) := by simp [ mapM'_eq_mapM, mapM']
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] (l : List α) (f : α β) : @[simp] theorem mapM_id {l : List α} {f : α Id β} : l.mapM f = l.map f := by
l.mapM (m := m) (pure <| f ·) = pure (l.map f) := by
induction l <;> simp_all induction l <;> simp_all
@[simp] theorem mapM_id {l : List α} {f : α Id β} : l.mapM f = l.map f :=
mapM_pure _ _
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α m β) {l₁ l₂ : List α} : @[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α m β) {l₁ l₂ : List α} :
(l₁ ++ l₂).mapM f = (return ( l₁.mapM f) ++ ( l₂.mapM f)) := by induction l₁ <;> simp [*] (l₁ ++ l₂).mapM f = (return ( l₁.mapM f) ++ ( l₂.mapM f)) := by induction l₁ <;> simp [*]
@@ -330,7 +323,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
simp only [forIn'_eq_foldlM] simp only [forIn'_eq_foldlM]
induction l.attach generalizing init <;> simp_all induction l.attach generalizing init <;> simp_all
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(l : List α) (f : (a : α) a l β β) (init : β) : (l : List α) (f : (a : α) a l β β) (init : β) :
forIn' l init (fun a m b => pure (.yield (f a m b))) = forIn' l init (fun a m b => pure (.yield (f a m b))) =
pure (f := m) (l.attach.foldl (fun b a, h => f a h b) init) := by pure (f := m) (l.attach.foldl (fun b a, h => f a h b) init) := by
@@ -383,7 +376,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
simp only [forIn_eq_foldlM] simp only [forIn_eq_foldlM]
induction l generalizing init <;> simp_all induction l generalizing init <;> simp_all
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(l : List α) (f : α β β) (init : β) : (l : List α) (f : α β β) (init : β) :
forIn l init (fun a b => pure (.yield (f a b))) = forIn l init (fun a b => pure (.yield (f a b))) =
pure (f := m) (l.foldl (fun b a => f a b) init) := by pure (f := m) (l.foldl (fun b a => f a b) init) := by
@@ -402,7 +395,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
forIn (l.map g) init f = forIn l init fun a y => f (g a) y := by forIn (l.map g) init f = forIn l init fun a y => f (g a) y := by
induction l generalizing init <;> simp_all induction l generalizing init <;> simp_all
/-! ### allM and anyM -/ /-! ### allM -/
theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α m Bool) (as : List α) : theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α m Bool) (as : List α) :
allM p as = (! ·) <$> anyM ((! ·) <$> p ·) as := by allM p as = (! ·) <$> anyM ((! ·) <$> p ·) as := by
@@ -414,18 +407,6 @@ theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α → m Bool) (as :
funext b funext b
split <;> simp_all split <;> simp_all
@[simp] theorem anyM_pure [Monad m] [LawfulMonad m] (p : α Bool) (as : List α) :
as.anyM (m := m) (pure <| p ·) = pure (as.any p) := by
induction as with
| nil => simp
| cons a as ih =>
simp only [anyM, ih, pure_bind, all_cons]
split <;> simp_all
@[simp] theorem allM_pure [Monad m] [LawfulMonad m] (p : α Bool) (as : List α) :
as.allM (m := m) (pure <| p ·) = pure (as.all p) := by
simp [allM_eq_not_anyM_not, all_eq_not_any_not]
/-! ### Recognizing higher order functions using a function that only depends on the value. -/ /-! ### Recognizing higher order functions using a function that only depends on the value. -/
/-- /--
@@ -441,12 +422,12 @@ and simplifies these to the function directly taking the value.
| nil => simp | nil => simp
| cons a l ih => simp [ih, hf] | cons a l ih => simp [ih, hf]
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : List α) (f : β α m β) (init : β) : @[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : List α) (f : β α m β) :
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by (wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
simp [wfParam] simp [wfParam]
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : List (Subtype P)) (f : β α m β) (init : β): @[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : List (Subtype P)) (f : β α m β) :
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b x, h => xs.unattach.foldlM f = xs.foldlM fun b x, h =>
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <| binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
f b (wfParam x) := by f b (wfParam x) := by
simp [wfParam] simp [wfParam]
@@ -468,12 +449,12 @@ and simplifies these to the function directly taking the value.
funext b funext b
simp [hf] simp [hf]
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α β m β) (init : β) : @[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α β m β) :
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by (wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
simp [wfParam] simp [wfParam]
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : List (Subtype P)) (f : α β m β) (init : β) : @[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : List (Subtype P)) (f : α β m β) :
xs.unattach.foldrM f init = xs.foldrM (init := init) fun x, h b => xs.unattach.foldrM f = xs.foldrM fun x, h b =>
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <| binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
f (wfParam x) b := by f (wfParam x) b := by
simp [wfParam] simp [wfParam]

View File

@@ -13,8 +13,7 @@ Proves various lemmas about `List.insertIdx`.
-/ -/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables. set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- TODO: restore after an update-stage0 set_option linter.indexVariables true -- Enforce naming conventions for index variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
open Function Nat open Function Nat
@@ -29,29 +28,29 @@ section InsertIdx
variable {a : α} variable {a : α}
@[simp] @[simp]
theorem insertIdx_zero (xs : List α) (x : α) : xs.insertIdx 0 x = x :: xs := theorem insertIdx_zero (s : List α) (x : α) : insertIdx 0 x s = x :: s :=
rfl rfl
@[simp] @[simp]
theorem insertIdx_succ_nil (n : Nat) (a : α) : ([] : List α).insertIdx (n + 1) a = [] := theorem insertIdx_succ_nil (n : Nat) (a : α) : insertIdx (n + 1) a [] = [] :=
rfl rfl
@[simp] @[simp]
theorem insertIdx_succ_cons (xs : List α) (hd x : α) (i : Nat) : theorem insertIdx_succ_cons (s : List α) (hd x : α) (i : Nat) :
(hd :: xs).insertIdx (i + 1) x = hd :: xs.insertIdx i x := insertIdx (i + 1) x (hd :: s) = hd :: insertIdx i x s :=
rfl rfl
theorem length_insertIdx : i (as : List α), (as.insertIdx i a).length = if i as.length then as.length + 1 else as.length theorem length_insertIdx : i as, (insertIdx i a as).length = if i as.length then as.length + 1 else as.length
| 0, _ => by simp | 0, _ => by simp
| n + 1, [] => by simp | n + 1, [] => by simp
| n + 1, a :: as => by | n + 1, a :: as => by
simp only [insertIdx_succ_cons, length_cons, length_insertIdx, Nat.add_le_add_iff_right] simp only [insertIdx_succ_cons, length_cons, length_insertIdx, Nat.add_le_add_iff_right]
split <;> rfl split <;> rfl
theorem length_insertIdx_of_le_length (h : i length as) : (as.insertIdx i a).length = as.length + 1 := by theorem length_insertIdx_of_le_length (h : i length as) : length (insertIdx i a as) = length as + 1 := by
simp [length_insertIdx, h] simp [length_insertIdx, h]
theorem length_insertIdx_of_length_lt (h : length as < i) : (as.insertIdx i a).length = as.length := by theorem length_insertIdx_of_length_lt (h : length as < i) : length (insertIdx i a as) = length as := by
simp [length_insertIdx, h] simp [length_insertIdx, h]
@[simp] @[simp]
@@ -61,7 +60,7 @@ theorem eraseIdx_insertIdx (i : Nat) (l : List α) : (l.insertIdx i a).eraseIdx
theorem insertIdx_eraseIdx_of_ge : theorem insertIdx_eraseIdx_of_ge :
i m as, i m as,
i < length as i m (as.eraseIdx i).insertIdx m a = (as.insertIdx (m + 1) a).eraseIdx i i < length as i m insertIdx m a (as.eraseIdx i) = (as.insertIdx (m + 1) a).eraseIdx i
| 0, 0, [], has, _ => (Nat.lt_irrefl _ has).elim | 0, 0, [], has, _ => (Nat.lt_irrefl _ has).elim
| 0, 0, _ :: as, _, _ => by simp [eraseIdx, insertIdx] | 0, 0, _ :: as, _, _ => by simp [eraseIdx, insertIdx]
| 0, _ + 1, _ :: _, _, _ => rfl | 0, _ + 1, _ :: _, _, _ => rfl
@@ -71,7 +70,7 @@ theorem insertIdx_eraseIdx_of_ge :
theorem insertIdx_eraseIdx_of_le : theorem insertIdx_eraseIdx_of_le :
i j as, i j as,
i < length as j i (as.eraseIdx i).insertIdx j a = (as.insertIdx j a).eraseIdx (i + 1) i < length as j i insertIdx j a (as.eraseIdx i) = (as.insertIdx j a).eraseIdx (i + 1)
| _, 0, _ :: _, _, _ => rfl | _, 0, _ :: _, _, _ => rfl
| n + 1, m + 1, a :: as, has, hmn => | n + 1, m + 1, a :: as, has, hmn =>
congrArg (cons a) <| congrArg (cons a) <|
@@ -96,7 +95,7 @@ theorem mem_insertIdx {a b : α} :
or_assoc, @or_comm (a = a'), or_assoc, mem_cons] or_assoc, @or_comm (a = a'), or_assoc, mem_cons]
theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length < i) : theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length < i) :
l.insertIdx i x = l := by insertIdx i x l = l := by
induction l generalizing i with induction l generalizing i with
| nil => | nil =>
cases i cases i
@@ -109,24 +108,24 @@ theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length <
simpa using ih _ h simpa using ih _ h
@[simp] @[simp]
theorem insertIdx_length_self (l : List α) (x : α) : l.insertIdx l.length x = l ++ [x] := by theorem insertIdx_length_self (l : List α) (x : α) : insertIdx l.length x l = l ++ [x] := by
induction l with induction l with
| nil => simp | nil => simp
| cons x l ih => simpa using ih | cons x l ih => simpa using ih
theorem length_le_length_insertIdx (l : List α) (x : α) (i : Nat) : theorem length_le_length_insertIdx (l : List α) (x : α) (i : Nat) :
l.length (l.insertIdx i x).length := by l.length (insertIdx i x l).length := by
simp only [length_insertIdx] simp only [length_insertIdx]
split <;> simp split <;> simp
theorem length_insertIdx_le_succ (l : List α) (x : α) (i : Nat) : theorem length_insertIdx_le_succ (l : List α) (x : α) (i : Nat) :
(l.insertIdx i x).length l.length + 1 := by (insertIdx i x l).length l.length + 1 := by
simp only [length_insertIdx] simp only [length_insertIdx]
split <;> simp split <;> simp
theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i) theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
(hk : j < (l.insertIdx i x).length) : (hk : j < (insertIdx i x l).length) :
(l.insertIdx i x)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by (insertIdx i x l)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
induction i generalizing j l with induction i generalizing j l with
| zero => simp at hn | zero => simp at hn
| succ n ih => | succ n ih =>
@@ -139,8 +138,8 @@ theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
simpa using ih hn _ simpa using ih hn _
@[simp] @[simp]
theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (l.insertIdx i x).length) : theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (insertIdx i x l).length) :
(l.insertIdx i x)[i] = x := by (insertIdx i x l)[i] = x := by
induction l generalizing i with induction l generalizing i with
| nil => | nil =>
simp [length_insertIdx] at hi simp [length_insertIdx] at hi
@@ -154,8 +153,8 @@ theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (l.ins
simpa using ih hi simpa using ih hi
theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j) theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
(hk : j < (l.insertIdx i x).length) : (hk : j < (insertIdx i x l).length) :
(l.insertIdx i x)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by (insertIdx i x l)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
induction l generalizing i j with induction l generalizing i j with
| nil => | nil =>
cases i with cases i with
@@ -183,8 +182,8 @@ theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
@[deprecated getElem_insertIdx_of_gt (since := "2025-02-04")] @[deprecated getElem_insertIdx_of_gt (since := "2025-02-04")]
abbrev getElem_insertIdx_of_ge := @getElem_insertIdx_of_gt abbrev getElem_insertIdx_of_ge := @getElem_insertIdx_of_gt
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertIdx i x).length) : theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (insertIdx i x l).length) :
(l.insertIdx i x)[j] = (insertIdx i x l)[j] =
if h₁ : j < i then if h₁ : j < i then
l[j]'(by simp [length_insertIdx] at h; split at h <;> omega) l[j]'(by simp [length_insertIdx] at h; split at h <;> omega)
else else
@@ -200,7 +199,7 @@ theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertI
· rw [getElem_insertIdx_of_gt (by omega)] · rw [getElem_insertIdx_of_gt (by omega)]
theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} : theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
(l.insertIdx i x)[j]? = (insertIdx i x l)[j]? =
if j < i then if j < i then
l[j]? l[j]?
else else
@@ -231,16 +230,16 @@ theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
split at h <;> omega split at h <;> omega
theorem getElem?_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (h : j < i) : theorem getElem?_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (h : j < i) :
(l.insertIdx i x)[j]? = l[j]? := by (insertIdx i x l)[j]? = l[j]? := by
rw [getElem?_insertIdx, if_pos h] rw [getElem?_insertIdx, if_pos h]
theorem getElem?_insertIdx_self {l : List α} {x : α} {i : Nat} : theorem getElem?_insertIdx_self {l : List α} {x : α} {i : Nat} :
(l.insertIdx i x)[i]? = if i l.length then some x else none := by (insertIdx i x l)[i]? = if i l.length then some x else none := by
rw [getElem?_insertIdx, if_neg (by omega)] rw [getElem?_insertIdx, if_neg (by omega)]
simp simp
theorem getElem?_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (h : i < j) : theorem getElem?_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (h : i < j) :
(l.insertIdx i x)[j]? = l[j - 1]? := by (insertIdx i x l)[j]? = l[j - 1]? := by
rw [getElem?_insertIdx, if_neg (by omega), if_neg (by omega)] rw [getElem?_insertIdx, if_neg (by omega), if_neg (by omega)]
@[deprecated getElem?_insertIdx_of_gt (since := "2025-02-04")] @[deprecated getElem?_insertIdx_of_gt (since := "2025-02-04")]

View File

@@ -87,75 +87,75 @@ theorem eraseIdx_modifyHead_zero {f : αα} {l : List α} :
/-! ### modifyTailIdx -/ /-! ### modifyTailIdx -/
@[simp] theorem modifyTailIdx_id : i (l : List α), l.modifyTailIdx i id = l @[simp] theorem modifyTailIdx_id : n (l : List α), l.modifyTailIdx id n = l
| 0, _ => rfl | 0, _ => rfl
| _+1, [] => rfl | _+1, [] => rfl
| n+1, a :: l => congrArg (cons a) (modifyTailIdx_id n l) | n+1, a :: l => congrArg (cons a) (modifyTailIdx_id n l)
theorem eraseIdx_eq_modifyTailIdx : i (l : List α), eraseIdx l i = l.modifyTailIdx i tail theorem eraseIdx_eq_modifyTailIdx : i (l : List α), eraseIdx l i = modifyTailIdx tail i l
| 0, l => by cases l <;> rfl | 0, l => by cases l <;> rfl
| _+1, [] => rfl | _+1, [] => rfl
| _+1, _ :: _ => congrArg (cons _) (eraseIdx_eq_modifyTailIdx _ _) | _+1, _ :: _ => congrArg (cons _) (eraseIdx_eq_modifyTailIdx _ _)
@[simp] theorem length_modifyTailIdx (f : List α List α) (H : l, (f l).length = l.length) : @[simp] theorem length_modifyTailIdx (f : List α List α) (H : l, length (f l) = length l) :
(l : List α) i, (l.modifyTailIdx i f).length = l.length n l, length (modifyTailIdx f n l) = length l
| _, 0 => H _ | 0, _ => H _
| [], _+1 => rfl | _+1, [] => rfl
| _ :: _, _+1 => congrArg (·+1) (length_modifyTailIdx _ H _ _) | _+1, _ :: _ => congrArg (·+1) (length_modifyTailIdx _ H _ _)
theorem modifyTailIdx_add (f : List α List α) (i) (l₁ l₂ : List α) : theorem modifyTailIdx_add (f : List α List α) (n) (l₁ l₂ : List α) :
(l₁ ++ l₂).modifyTailIdx (l₁.length + i) f = l₁ ++ l₂.modifyTailIdx i f := by modifyTailIdx f (l₁.length + n) (l₁ ++ l₂) = l₁ ++ modifyTailIdx f n l₂ := by
induction l₁ <;> simp [*, Nat.succ_add] induction l₁ <;> simp [*, Nat.succ_add]
theorem modifyTailIdx_eq_take_drop (f : List α List α) (H : f [] = []) : theorem modifyTailIdx_eq_take_drop (f : List α List α) (H : f [] = []) :
(l : List α) i, l.modifyTailIdx i f = l.take i ++ f (l.drop i) i l, modifyTailIdx f i l = take i l ++ f (drop i l)
| _, 0 => rfl | 0, _ => rfl
| [], _ + 1 => H.symm | _ + 1, [] => H.symm
| b :: l, i + 1 => congrArg (cons b) (modifyTailIdx_eq_take_drop f H l i) | n + 1, b :: l => congrArg (cons b) (modifyTailIdx_eq_take_drop f H n l)
theorem exists_of_modifyTailIdx (f : List α List α) {i} {l : List α} (h : i l.length) : theorem exists_of_modifyTailIdx (f : List α List α) {n} {l : List α} (h : n l.length) :
l₁ l₂, l = l₁ ++ l₂ l₁.length = i l.modifyTailIdx i f = l₁ ++ f l₂ := by l₁ l₂, l = l₁ ++ l₂ l₁.length = n modifyTailIdx f n l = l₁ ++ f l₂ :=
obtain l₁, l₂, rfl, rfl : l₁ l₂, l = l₁ ++ l₂ l₁.length = i := have _, _, eq, hl : l₁ l₂, l = l₁ ++ l₂ l₁.length = n :=
_, _, (take_append_drop i l).symm, length_take_of_le h _, _, (take_append_drop n l).symm, length_take_of_le h
exact l₁, l₂, rfl, rfl, modifyTailIdx_add f 0 l₁ l₂ _, _, eq, hl, hl eq modifyTailIdx_add (n := 0) ..
theorem modifyTailIdx_modifyTailIdx {f g : List α List α} (i : Nat) : theorem modifyTailIdx_modifyTailIdx {f g : List α List α} (m : Nat) :
(j) (l : List α), (n) (l : List α),
(l.modifyTailIdx j f).modifyTailIdx (i + j) g = (l.modifyTailIdx f n).modifyTailIdx g (m + n) =
l.modifyTailIdx j (fun l => (f l).modifyTailIdx i g) l.modifyTailIdx (fun l => (f l).modifyTailIdx g m) n
| 0, _ => rfl | 0, _ => rfl
| _ + 1, [] => rfl | _ + 1, [] => rfl
| n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx i n l) | n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx m n l)
theorem modifyTailIdx_modifyTailIdx_le {f g : List α List α} (i j : Nat) (l : List α) theorem modifyTailIdx_modifyTailIdx_le {f g : List α List α} (m n : Nat) (l : List α)
(h : j i) : (h : n m) :
(l.modifyTailIdx j f).modifyTailIdx i g = (l.modifyTailIdx f n).modifyTailIdx g m =
l.modifyTailIdx j (fun l => (f l).modifyTailIdx (i - j) g) := by l.modifyTailIdx (fun l => (f l).modifyTailIdx g (m - n)) n := by
rcases Nat.exists_eq_add_of_le h with m, rfl rcases Nat.exists_eq_add_of_le h with m, rfl
rw [Nat.add_comm, modifyTailIdx_modifyTailIdx, Nat.add_sub_cancel] rw [Nat.add_comm, modifyTailIdx_modifyTailIdx, Nat.add_sub_cancel]
theorem modifyTailIdx_modifyTailIdx_self {f g : List α List α} (i : Nat) (l : List α) : theorem modifyTailIdx_modifyTailIdx_self {f g : List α List α} (n : Nat) (l : List α) :
(l.modifyTailIdx i f).modifyTailIdx i g = l.modifyTailIdx i (g f) := by (l.modifyTailIdx f n).modifyTailIdx g n = l.modifyTailIdx (g f) n := by
rw [modifyTailIdx_modifyTailIdx_le i i l (Nat.le_refl i), Nat.sub_self]; rfl rw [modifyTailIdx_modifyTailIdx_le n n l (Nat.le_refl n), Nat.sub_self]; rfl
/-! ### modify -/ /-! ### modify -/
@[simp] theorem modify_nil (f : α α) (i) : [].modify i f = [] := by cases i <;> rfl @[simp] theorem modify_nil (f : α α) (i) : [].modify f i = [] := by cases i <;> rfl
@[simp] theorem modify_zero_cons (f : α α) (a : α) (l : List α) : @[simp] theorem modify_zero_cons (f : α α) (a : α) (l : List α) :
(a :: l).modify 0 f = f a :: l := rfl (a :: l).modify f 0 = f a :: l := rfl
@[simp] theorem modify_succ_cons (f : α α) (a : α) (l : List α) (i) : @[simp] theorem modify_succ_cons (f : α α) (a : α) (l : List α) (i) :
(a :: l).modify (i + 1) f = a :: l.modify i f := by rfl (a :: l).modify f (i + 1) = a :: l.modify f i := by rfl
theorem modifyHead_eq_modify_zero (f : α α) (l : List α) : theorem modifyHead_eq_modify_zero (f : α α) (l : List α) :
l.modifyHead f = l.modify 0 f := by cases l <;> simp l.modifyHead f = l.modify f 0 := by cases l <;> simp
@[simp] theorem modify_eq_nil_iff {f : α α} {i} {l : List α} : @[simp] theorem modify_eq_nil_iff {f : α α} {i} {l : List α} :
l.modify i f = [] l = [] := by cases l <;> cases i <;> simp l.modify f i = [] l = [] := by cases l <;> cases i <;> simp
theorem getElem?_modify (f : α α) : theorem getElem?_modify (f : α α) :
i (l : List α) j, (l.modify i f)[j]? = (fun a => if i = j then f a else a) <$> l[j]? i (l : List α) j, (modify f i l)[j]? = (fun a => if i = j then f a else a) <$> l[j]?
| n, l, 0 => by cases l <;> cases n <;> simp | n, l, 0 => by cases l <;> cases n <;> simp
| n, [], _+1 => by cases n <;> rfl | n, [], _+1 => by cases n <;> rfl
| 0, _ :: l, j+1 => by cases h : l[j]? <;> simp [h, modify, j.succ_ne_zero.symm] | 0, _ :: l, j+1 => by cases h : l[j]? <;> simp [h, modify, j.succ_ne_zero.symm]
@@ -165,32 +165,32 @@ theorem getElem?_modify (f : αα) :
cases h' : l[j]? <;> by_cases h : i = j <;> cases h' : l[j]? <;> by_cases h : i = j <;>
simp [h, if_pos, if_neg, Option.map, mt Nat.succ.inj, not_false_iff, h'] simp [h, if_pos, if_neg, Option.map, mt Nat.succ.inj, not_false_iff, h']
@[simp] theorem length_modify (f : α α) : (l : List α) i, (l.modify i f).length = l.length := @[simp] theorem length_modify (f : α α) : i l, length (modify f i l) = length l :=
length_modifyTailIdx _ fun l => by cases l <;> rfl length_modifyTailIdx _ fun l => by cases l <;> rfl
@[simp] theorem getElem?_modify_eq (f : α α) (i) (l : List α) : @[simp] theorem getElem?_modify_eq (f : α α) (i) (l : List α) :
(l.modify i f)[i]? = f <$> l[i]? := by (modify f i l)[i]? = f <$> l[i]? := by
simp only [getElem?_modify, if_pos] simp only [getElem?_modify, if_pos]
@[simp] theorem getElem?_modify_ne (f : α α) {i j} (l : List α) (h : i j) : @[simp] theorem getElem?_modify_ne (f : α α) {i j} (l : List α) (h : i j) :
(l.modify i f)[j]? = l[j]? := by (modify f i l)[j]? = l[j]? := by
simp only [getElem?_modify, if_neg h, id_map'] simp only [getElem?_modify, if_neg h, id_map']
theorem getElem_modify (f : α α) (i) (l : List α) (j) (h : j < (l.modify i f).length) : theorem getElem_modify (f : α α) (i) (l : List α) (j) (h : j < (modify f i l).length) :
(l.modify i f)[j] = (modify f i l)[j] =
if i = j then f (l[j]'(by simp at h; omega)) else l[j]'(by simp at h; omega) := by if i = j then f (l[j]'(by simp at h; omega)) else l[j]'(by simp at h; omega) := by
rw [getElem_eq_iff, getElem?_modify] rw [getElem_eq_iff, getElem?_modify]
simp at h simp at h
simp [h] simp [h]
@[simp] theorem getElem_modify_eq (f : α α) (i) (l : List α) (h) : @[simp] theorem getElem_modify_eq (f : α α) (i) (l : List α) (h) :
(l.modify i f)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify] (modify f i l)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify]
@[simp] theorem getElem_modify_ne (f : α α) {i j} (l : List α) (h : i j) (h') : @[simp] theorem getElem_modify_ne (f : α α) {i j} (l : List α) (h : i j) (h') :
(l.modify i f)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h] (modify f i l)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h]
theorem modify_eq_self {f : α α} {i} {l : List α} (h : l.length i) : theorem modify_eq_self {f : α α} {i} {l : List α} (h : l.length i) :
l.modify i f = l := by l.modify f i = l := by
apply ext_getElem apply ext_getElem
· simp · simp
· intro m h₁ h₂ · intro m h₁ h₂
@@ -199,7 +199,7 @@ theorem modify_eq_self {f : αα} {i} {l : List α} (h : l.length ≤ i) :
omega omega
theorem modify_modify_eq (f g : α α) (i) (l : List α) : theorem modify_modify_eq (f g : α α) (i) (l : List α) :
(l.modify i f).modify i g = l.modify i (g f) := by (modify f i l).modify g i = modify (g f) i l := by
apply ext_getElem apply ext_getElem
· simp · simp
· intro m h₁ h₂ · intro m h₁ h₂
@@ -207,7 +207,7 @@ theorem modify_modify_eq (f g : αα) (i) (l : List α) :
split <;> simp split <;> simp
theorem modify_modify_ne (f g : α α) {i j} (l : List α) (h : i j) : theorem modify_modify_ne (f g : α α) {i j} (l : List α) (h : i j) :
(l.modify i f).modify j g = (l.modify j g).modify i f := by (modify f i l).modify g j = (l.modify g j).modify f i := by
apply ext_getElem apply ext_getElem
· simp · simp
· intro m' h₁ h₂ · intro m' h₁ h₂
@@ -215,7 +215,7 @@ theorem modify_modify_ne (f g : αα) {i j} (l : List α) (h : i ≠ j) :
split <;> split <;> first | rfl | omega split <;> split <;> first | rfl | omega
theorem modify_eq_set [Inhabited α] (f : α α) (i) (l : List α) : theorem modify_eq_set [Inhabited α] (f : α α) (i) (l : List α) :
l.modify i f = l.set i (f (l[i]?.getD default)) := by modify f i l = l.set i (f (l[i]?.getD default)) := by
apply ext_getElem apply ext_getElem
· simp · simp
· intro m h₁ h₂ · intro m h₁ h₂
@@ -227,24 +227,24 @@ theorem modify_eq_set [Inhabited α] (f : αα) (i) (l : List α) :
· rfl · rfl
theorem modify_eq_take_drop (f : α α) : theorem modify_eq_take_drop (f : α α) :
(l : List α) i, l.modify i f = l.take i ++ modifyHead f (l.drop i) := i l, modify f i l = take i l ++ modifyHead f (drop i l) :=
modifyTailIdx_eq_take_drop _ rfl modifyTailIdx_eq_take_drop _ rfl
theorem modify_eq_take_cons_drop {f : α α} {i} {l : List α} (h : i < l.length) : theorem modify_eq_take_cons_drop {f : α α} {i} {l : List α} (h : i < l.length) :
l.modify i f = l.take i ++ f l[i] :: l.drop (i + 1) := by modify f i l = take i l ++ f l[i] :: drop (i + 1) l := by
rw [modify_eq_take_drop, drop_eq_getElem_cons h]; rfl rw [modify_eq_take_drop, drop_eq_getElem_cons h]; rfl
theorem exists_of_modify (f : α α) {i} {l : List α} (h : i < l.length) : theorem exists_of_modify (f : α α) {i} {l : List α} (h : i < l.length) :
l₁ a l₂, l = l₁ ++ a :: l₂ l₁.length = i l.modify i f = l₁ ++ f a :: l₂ := l₁ a l₂, l = l₁ ++ a :: l₂ l₁.length = i modify f i l = l₁ ++ f a :: l₂ :=
match exists_of_modifyTailIdx _ (Nat.le_of_lt h) with match exists_of_modifyTailIdx _ (Nat.le_of_lt h) with
| _, _::_, eq, hl, H => _, _, _, eq, hl, H | _, _::_, eq, hl, H => _, _, _, eq, hl, H
| _, [], eq, hl, _ => nomatch Nat.ne_of_gt h (eq append_nil _ hl) | _, [], eq, hl, _ => nomatch Nat.ne_of_gt h (eq append_nil _ hl)
@[simp] theorem modify_id (i) (l : List α) : l.modify i id = l := by @[simp] theorem modify_id (i) (l : List α) : l.modify id i = l := by
simp [modify] simp [modify]
theorem take_modify (f : α α) (i j) (l : List α) : theorem take_modify (f : α α) (i j) (l : List α) :
(l.modify i f).take j = (l.take j).modify i f := by (modify f i l).take j = (take j l).modify f i := by
induction j generalizing l i with induction j generalizing l i with
| zero => simp | zero => simp
| succ n ih => | succ n ih =>
@@ -256,7 +256,7 @@ theorem take_modify (f : αα) (i j) (l : List α) :
| succ i => simp [ih] | succ i => simp [ih]
theorem drop_modify_of_lt (f : α α) (i j) (l : List α) (h : i < j) : theorem drop_modify_of_lt (f : α α) (i j) (l : List α) (h : i < j) :
(l.modify i f).drop j = l.drop j := by (modify f i l).drop j = l.drop j := by
apply ext_getElem apply ext_getElem
· simp · simp
· intro m' h₁ h₂ · intro m' h₁ h₂
@@ -265,7 +265,7 @@ theorem drop_modify_of_lt (f : αα) (i j) (l : List α) (h : i < j) :
omega omega
theorem drop_modify_of_ge (f : α α) (i j) (l : List α) (h : i j) : theorem drop_modify_of_ge (f : α α) (i j) (l : List α) (h : i j) :
(l.modify i f).drop j = (l.drop j).modify (i - j) f := by (modify f i l).drop j = modify f (i - j) (drop j l) := by
apply ext_getElem apply ext_getElem
· simp · simp
· intro m' h₁ h₂ · intro m' h₁ h₂
@@ -273,7 +273,7 @@ theorem drop_modify_of_ge (f : αα) (i j) (l : List α) (h : i ≥ j) :
split <;> split <;> first | rfl | omega split <;> split <;> first | rfl | omega
theorem eraseIdx_modify_of_eq (f : α α) (i) (l : List α) : theorem eraseIdx_modify_of_eq (f : α α) (i) (l : List α) :
(l.modify i f).eraseIdx i = l.eraseIdx i := by (modify f i l).eraseIdx i = l.eraseIdx i := by
apply ext_getElem apply ext_getElem
· simp [length_eraseIdx] · simp [length_eraseIdx]
· intro m h₁ h₂ · intro m h₁ h₂
@@ -281,7 +281,7 @@ theorem eraseIdx_modify_of_eq (f : αα) (i) (l : List α) :
split <;> split <;> first | rfl | omega split <;> split <;> first | rfl | omega
theorem eraseIdx_modify_of_lt (f : α α) (i j) (l : List α) (h : j < i) : theorem eraseIdx_modify_of_lt (f : α α) (i j) (l : List α) (h : j < i) :
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify (i - 1) f := by (modify f i l).eraseIdx j = (l.eraseIdx j).modify f (i - 1) := by
apply ext_getElem apply ext_getElem
· simp [length_eraseIdx] · simp [length_eraseIdx]
· intro k h₁ h₂ · intro k h₁ h₂
@@ -291,7 +291,7 @@ theorem eraseIdx_modify_of_lt (f : αα) (i j) (l : List α) (h : j < i) :
all_goals (first | rfl | omega) all_goals (first | rfl | omega)
theorem eraseIdx_modify_of_gt (f : α α) (i j) (l : List α) (h : j > i) : theorem eraseIdx_modify_of_gt (f : α α) (i j) (l : List α) (h : j > i) :
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify i f := by (modify f i l).eraseIdx j = (l.eraseIdx j).modify f i := by
apply ext_getElem apply ext_getElem
· simp [length_eraseIdx] · simp [length_eraseIdx]
· intro k h₁ h₂ · intro k h₁ h₂
@@ -301,7 +301,7 @@ theorem eraseIdx_modify_of_gt (f : αα) (i j) (l : List α) (h : j > i) :
all_goals (first | rfl | omega) all_goals (first | rfl | omega)
theorem modify_eraseIdx_of_lt (f : α α) (i j) (l : List α) (h : j < i) : theorem modify_eraseIdx_of_lt (f : α α) (i j) (l : List α) (h : j < i) :
(l.eraseIdx i).modify j f = (l.modify j f).eraseIdx i := by (l.eraseIdx i).modify f j = (l.modify f j).eraseIdx i := by
apply ext_getElem apply ext_getElem
· simp [length_eraseIdx] · simp [length_eraseIdx]
· intro k h₁ h₂ · intro k h₁ h₂
@@ -311,7 +311,7 @@ theorem modify_eraseIdx_of_lt (f : αα) (i j) (l : List α) (h : j < i) :
all_goals (first | rfl | omega) all_goals (first | rfl | omega)
theorem modify_eraseIdx_of_ge (f : α α) (i j) (l : List α) (h : j i) : theorem modify_eraseIdx_of_ge (f : α α) (i j) (l : List α) (h : j i) :
(l.eraseIdx i).modify j f = (l.modify (j + 1) f).eraseIdx i := by (l.eraseIdx i).modify f j = (l.modify f (j + 1)).eraseIdx i := by
apply ext_getElem apply ext_getElem
· simp [length_eraseIdx] · simp [length_eraseIdx]
· intro k h₁ h₂ · intro k h₁ h₂

View File

@@ -36,6 +36,12 @@ theorem map_getElem_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pai
rwa [nil_append, (drop_append_of_le_length ?_), take_append_drop] at this rwa [nil_append, (drop_append_of_le_length ?_), take_append_drop] at this
simp [Nat.min_eq_left (Nat.le_of_lt hd.isLt), his] simp [Nat.min_eq_left (Nat.le_of_lt hd.isLt), his]
set_option linter.listVariables false in
@[deprecated map_getElem_sublist (since := "2024-07-30")]
theorem map_get_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (·.val < ·.val)) :
is.map (get l) <+ l := by
simpa using map_getElem_sublist h
set_option linter.listVariables false in set_option linter.listVariables false in
/-- Given a sublist `l' <+ l`, there exists an increasing list of indices `is` such that /-- Given a sublist `l' <+ l`, there exists an increasing list of indices `is` such that
`l' = is.map fun i => l[i]`. -/ `l' = is.map fun i => l[i]`. -/
@@ -52,6 +58,12 @@ theorem sublist_eq_map_getElem {l l' : List α} (h : l' <+ l) : ∃ is : List (F
refine 0, by simp [Nat.zero_lt_succ] :: is.map (·.succ), ?_ refine 0, by simp [Nat.zero_lt_succ] :: is.map (·.succ), ?_
simp [Function.comp_def, pairwise_map, IH, get_eq_getElem, get_cons_zero, get_cons_succ'] simp [Function.comp_def, pairwise_map, IH, get_eq_getElem, get_cons_zero, get_cons_succ']
set_option linter.listVariables false in
@[deprecated sublist_eq_map_getElem (since := "2024-07-30")]
theorem sublist_eq_map_get (h : l' <+ l) : is : List (Fin l.length),
l' = map (get l) is is.Pairwise (· < ·) := by
simpa using sublist_eq_map_getElem h
set_option linter.listVariables false in set_option linter.listVariables false in
theorem pairwise_iff_getElem : Pairwise R l theorem pairwise_iff_getElem : Pairwise R l
(i j : Nat) (_hi : i < l.length) (_hj : j < l.length) (_hij : i < j), R l[i] l[j] := by (i j : Nat) (_hi : i < l.length) (_hj : j < l.length) (_hij : i < j), R l[i] l[j] := by

View File

@@ -581,6 +581,8 @@ theorem reverse_zipWith (h : l.length = l'.length) :
have : tl.reverse.length = tl'.reverse.length := by simp [h] have : tl.reverse.length = tl'.reverse.length := by simp [h]
simp [hl h, zipWith_append _ _ _ _ _ this] simp [hl h, zipWith_append _ _ _ _ _ this]
@[deprecated reverse_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_reverse := @reverse_zipWith
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} : @[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 zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
rw [zipWith_eq_zipWith_take_min] rw [zipWith_eq_zipWith_take_min]

View File

@@ -17,11 +17,10 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List namespace List
/-- /--
Creates a list by applying `f` to each potential index in order, starting at `0`. `ofFn f` with `f : fin n → α` returns the list whose ith element is `f i`
```
Examples: ofFn f = [f 0, f 1, ... , f (n - 1)]
* `List.ofFn (n := 3) toString = ["0", "1", "2"]` ```
* `List.ofFn (fun i => #["red", "green", "blue"].get i.val i.isLt) = ["red", "green", "blue"]`
-/ -/
def ofFn {n} (f : Fin n α) : List α := Fin.foldr n (f · :: ·) [] def ofFn {n} (f : Fin n α) : List α := Fin.foldr n (f · :: ·) []

View File

@@ -137,6 +137,8 @@ theorem Pairwise.filterMap {S : β → β → Prop} (f : α → Option β)
Pairwise S (filterMap f l) := Pairwise S (filterMap f l) :=
pairwise_filterMap.2 <| p.imp (H _ _) pairwise_filterMap.2 <| p.imp (H _ _)
@[deprecated Pairwise.filterMap (since := "2024-07-29")] abbrev Pairwise.filter_map := @Pairwise.filterMap
theorem pairwise_filter {p : α Prop} [DecidablePred p] {l : List α} : theorem pairwise_filter {p : α Prop} [DecidablePred p] {l : List α} :
Pairwise R (filter p l) Pairwise (fun x y => p x p y R x y) l := by Pairwise R (filter p l) Pairwise (fun x y => p x p y R x y) l := by
rw [ filterMap_eq_filter, pairwise_filterMap] rw [ filterMap_eq_filter, pairwise_filterMap]

View File

@@ -19,8 +19,7 @@ The notation `~` is used for permutation equivalence.
-/ -/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables. set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- TODO: restore after an update-stage0 set_option linter.indexVariables true -- Enforce naming conventions for index variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
open Nat open Nat
@@ -48,14 +47,6 @@ instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
theorem perm_comm {l₁ l₂ : List α} : l₁ ~ l₂ l₂ ~ l₁ := Perm.symm, Perm.symm theorem perm_comm {l₁ l₂ : List α} : l₁ ~ l₂ l₂ ~ l₁ := Perm.symm, Perm.symm
protected theorem Perm.congr_left {l₁ l₂ : List α} (h : l₁ ~ l₂) (l₃ : List α) :
l₁ ~ l₃ l₂ ~ l₃ :=
h.symm.trans, h.trans
protected theorem Perm.congr_right {l₁ l₂ : List α} (h : l₁ ~ l₂) (l₃ : List α) :
l₃ ~ l₁ l₃ ~ l₂ :=
fun h' => h'.trans h, fun h' => h'.trans h.symm
theorem Perm.swap' (x y : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : y :: x :: l₁ ~ x :: y :: l₂ := theorem Perm.swap' (x y : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : y :: x :: l₁ ~ x :: y :: l₂ :=
(swap ..).trans <| p.cons _ |>.cons _ (swap ..).trans <| p.cons _ |>.cons _
@@ -523,7 +514,7 @@ theorem Perm.eraseP (f : α → Bool) {l₁ l₂ : List α}
exact fun h h₁ h₂ => h h₂ h₁ exact fun h h₁ h₂ => h h₂ h₁
theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i l.length) : theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i l.length) :
l.insertIdx i x ~ x :: l := by insertIdx i x l ~ x :: l := by
induction l generalizing i with induction l generalizing i with
| nil => | nil =>
cases i with cases i with

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