Compare commits

..

1 Commits

Author SHA1 Message Date
Henrik Böving
406762f38f perf: annotate our allocators as such 2025-12-27 22:49:23 +00:00
2873 changed files with 15031 additions and 36528 deletions

View File

@@ -29,38 +29,6 @@ After rebuilding, LSP diagnostics may be stale until the user interacts with fil
If the user expresses frustration with you, stop and ask them to help update this `.claude/CLAUDE.md` file with missing guidance.
## Creating pull requests
## Creating pull requests.
Follow the commit convention in `doc/dev/commit_convention.md`.
**Title format:** `<type>: <subject>` where type is one of: `feat`, `fix`, `doc`, `style`, `refactor`, `test`, `chore`, `perf`.
Subject should use imperative present tense ("add" not "added"), no capitalization, no trailing period.
**Body format:** The first paragraph must start with "This PR". This paragraph is automatically incorporated into release notes. Use imperative present tense. Include motivation and contrast with previous behavior when relevant.
Example:
```
feat: add optional binder limit to `mkPatternFromTheorem`
This PR adds a `num?` parameter to `mkPatternFromTheorem` to control how many
leading quantifiers are stripped when creating a pattern.
```
**Changelog labels:** Add one `changelog-*` label to categorize the PR for release notes:
- `changelog-language` - Language features and metaprograms
- `changelog-tactics` - User facing tactics
- `changelog-server` - Language server, widgets, and IDE extensions
- `changelog-pp` - Pretty printing
- `changelog-library` - Library
- `changelog-compiler` - Compiler, runtime, and FFI
- `changelog-lake` - Lake
- `changelog-doc` - Documentation
- `changelog-ffi` - FFI changes
- `changelog-other` - Other changes
- `changelog-no` - Do not include this PR in the release changelog
If you're unsure which label applies, it's fine to omit the label and let reviewers add it.
## CI Log Retrieval
When CI jobs fail, investigate immediately - don't wait for other jobs to complete. Individual job logs are often available even while other jobs are still running. Try `gh run view <run-id> --log` or `gh run view <run-id> --log-failed`, or use `gh run view <run-id> --job=<job-id>` to target the specific failed job. Sleeping is fine when asked to monitor CI and no failures exist yet, but once any job fails, investigate that failure immediately.
All PRs must have a first paragraph starting with "This PR". This paragraph is automatically incorporated into release notes. Read `lean4/doc/dev/commit_convention.md` when making PRs.

View File

@@ -13,54 +13,12 @@ These comments explain the scripts' behavior, which repositories get special han
## Arguments
- `version`: The version to release (e.g., v4.24.0)
## Release Notes (Required for -rc1 releases)
For first release candidates (`-rc1`), you must create release notes BEFORE the reference-manual toolchain bump PR can be merged.
**Steps to create release notes:**
1. Generate the release notes:
```bash
cd /path/to/lean4
python3 script/release_notes.py --since <previous_version> > /tmp/release-notes-<version>.md
```
Replace `<previous_version>` with the last stable release (e.g., `v4.27.0` when releasing `v4.28.0-rc1`).
2. Review `/tmp/release-notes-<version>.md` for common issues:
- **Unterminated code blocks**: Look for code fences that aren't closed. Fetch original PR with `gh pr view <number>` to repair.
- **Truncated descriptions**: Some may end mid-sentence. Complete them from the original PR.
- **Markdown issues**: Other syntax problems that could cause parsing errors.
3. Create the release notes file in the reference-manual repository:
- File path: `Manual/Releases/v<version>.lean` (e.g., `v4_28_0.lean`)
- Use Verso format with proper imports and `#doc (Manual)` block
- **Use `#` for headers, not `##`** (Verso uses level 1 for subsections)
- **Use plain ` ``` ` not ` ```lean `** (the latter executes code)
- **Wrap underscore identifiers in backticks**: `` `bv_decide` `` not `bv_decide`
4. Update `Manual/Releases.lean`:
- Add import: `import Manual.Releases.«v4_28_0»`
- Add include: `{include 0 Manual.Releases.«v4_28_0»}`
5. Build to verify: `lake build Manual.Releases.v4_28_0`
6. Create a **separate PR** for release notes (not bundled with toolchain bump):
```bash
git checkout -b v<version>-release-notes
gh pr create --title "doc: add v<version> release notes"
```
For subsequent RCs (`-rc2`, etc.) and stable releases, just update the version number in the existing release notes file title.
See `doc/dev/release_checklist.md` section "Writing the release notes" for full details.
## Process
1. Run `script/release_checklist.py {version}` to check the current status
2. **CRITICAL: If preliminary lean4 checks fail, STOP immediately and alert the user**
- Check for: release branch exists, CMake version correct, tag exists, release page exists, release notes file exists
- Check for: release branch exists, CMake version correct, tag exists, release page exists, release notes exist
- **IMPORTANT**: The release page is created AUTOMATICALLY by CI after pushing the tag - DO NOT create it manually
- **IMPORTANT**: For -rc1 releases, release notes must be created before proceeding
- Do NOT create any PRs or proceed with repository updates if these checks fail
3. Create a todo list tracking all repositories that need updates
4. **CRITICAL RULE: You can ONLY run `release_steps.py` for a repository if `release_checklist.py` explicitly says to do so**
@@ -103,15 +61,6 @@ Every time you run `release_checklist.py`, you MUST:
This summary should be provided EVERY time you run the checklist, not just after creating new PRs.
The user needs to see the complete picture of what's waiting for review.
## Nightly Infrastructure
The nightly build system uses branches and tags across two repositories:
- `leanprover/lean4` has **branches** `nightly` and `nightly-with-mathlib` tracking the latest nightly builds
- `leanprover/lean4-nightly` has **dated tags** like `nightly-2026-01-23`
When a nightly succeeds with mathlib, all three should point to the same commit. Don't confuse these: branches are in the main lean4 repo, dated tags are in lean4-nightly.
## Error Handling
**CRITICAL**: If something goes wrong or a command fails:

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: actionlint
uses: raven-actions/actionlint@v2
with:

View File

@@ -67,13 +67,13 @@ jobs:
if: runner.os == 'macOS'
- name: Checkout
if: (!endsWith(matrix.os, '-with-cache'))
uses: actions/checkout@v6
uses: actions/checkout@v5
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: Namespace Checkout
if: endsWith(matrix.os, '-with-cache')
uses: namespacelabs/nscloud-checkout-action@v8
uses: namespacelabs/nscloud-checkout-action@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Open Nix shell once
@@ -102,7 +102,7 @@ jobs:
if: matrix.cmultilib
- name: Restore Cache
id: restore-cache
uses: actions/cache/restore@v5
uses: actions/cache/restore@v4
with:
# NOTE: must be in sync with `save` below and with `restore-cache` in `update-stage0.yml`
path: |
@@ -175,7 +175,7 @@ jobs:
# Caching on cancellation created some mysterious issues perhaps related to improper build
# shutdown
if: steps.restore-cache.outputs.cache-hit != 'true' && !cancelled()
uses: actions/cache/save@v5
uses: actions/cache/save@v4
with:
# NOTE: must be in sync with `restore` above
path: |

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
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 }}

View File

@@ -8,7 +8,7 @@ jobs:
check-stage0-on-queue:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

View File

@@ -50,7 +50,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly' || (startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4')
- name: Set Nightly
@@ -115,7 +115,7 @@ jobs:
CMAKE_MAJOR=$(grep -E "^set\(LEAN_VERSION_MAJOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_MINOR=$(grep -E "^set\(LEAN_VERSION_MINOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_PATCH=$(grep -E "^set\(LEAN_VERSION_PATCH " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | sed -nE 's/^set\(LEAN_VERSION_IS_RELEASE ([0-9]+)\).*/\1/p')
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | grep -oE '[0-9]+')
# Expected values from tag parsing
TAG_MAJOR="${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}"
@@ -267,17 +267,14 @@ jobs:
"test": true,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_PRESET": "sanitize",
// * `StackOverflow*` correctly triggers ubsan.
// * `reverse-ffi` fails to link in sanitizers.
// * `interactive` and `async_select_channel` fail nondeterministically, would need
// to be investigated..
// * 9366 is too close to timeout.
// * `bv_` sometimes times out calling into cadical even though we should be using
// the standard compile flags for it.
// * `grind_guide` always times out.
// * `pkg/|lake/` tests sometimes time out (likely even hang), related to Lake CI
// failures?
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel|9366|run/bv_|grind_guide|pkg/|lake/'"
// `StackOverflow*` correctly triggers ubsan.
// `reverse-ffi` fails to link in sanitizers.
// `interactive` and `async_select_channel` fail nondeterministically, would need to
// be investigated..
// 9366 is too close to timeout.
// `bv_` sometimes times out calling into cadical even though we should be using the
// standard compile flags for it.
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel|9366|run/bv_'"
},
{
"name": "macOS",
@@ -433,11 +430,11 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -458,14 +455,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
# needed for tagging
fetch-depth: 0
# Doesn't seem to be working when additionally fetching from lean4-nightly
#filter: tree:0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
path: artifacts
- name: Prepare Nightly Release
@@ -483,7 +480,7 @@ jobs:
echo -e "\n*Full commit log*\n" >> diff.md
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
- name: Release Nightly
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
body_path: diff.md
prerelease: true

View File

@@ -6,7 +6,7 @@ jobs:
check-lean-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Verify .lean files start with a copyright header.
run: |

View File

@@ -20,9 +20,7 @@ on:
jobs:
on-success:
runs-on: ubuntu-latest
# Run even if CI fails, as long as build artifacts are available
# The "Verify release artifacts exist" step will fail if necessary artifacts are missing
if: github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
steps:
- name: Retrieve information about the original workflow
uses: potiuk/get-workflow-origin@v1_1 # https://github.com/marketplace/actions/get-workflow-origin
@@ -43,19 +41,6 @@ jobs:
name: build-.*
name_is_regexp: true
# Verify artifacts were downloaded before any side effects (tag creation, release deletion).
- name: Verify release artifacts exist
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
shopt -s nullglob
files=(artifacts/*/*)
if [ ${#files[@]} -eq 0 ]; then
echo "::error::No artifacts found matching artifacts/*/*"
exit 1
fi
echo "Found ${#files[@]} artifacts to upload:"
printf '%s\n' "${files[@]}"
- name: Push tag
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
@@ -77,44 +62,42 @@ jobs:
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}"
- name: Delete existing releases if present
- name: Delete existing release if present
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
# Delete any existing releases for this PR.
# The short format release is always recreated with the latest commit.
# The SHA-suffixed release should be unique per commit, but delete just in case.
# Try to delete any existing release for the current PR (just the version without the SHA suffix).
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }} -y || true
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
# We use `gh release create` instead of `softprops/action-gh-release` because
# the latter enumerates all releases to check for existing ones, which fails
# when the repository has more than 10000 releases (GitHub API pagination limit).
# Upstream fix: https://github.com/softprops/action-gh-release/pull/725
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
# There are coredump files in deeper subdirectories; artifacts/*/* gets the release archives.
gh release create \
--repo ${{ github.repository_owner }}/lean4-pr-releases \
--title "Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}" \
--notes "" \
pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} \
artifacts/*/*
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
files: artifacts/*/*
fail_on_unmatched_files: true
draft: false
tag_name: pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
repository: ${{ github.repository_owner }}/lean4-pr-releases
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
gh release create \
--repo ${{ github.repository_owner }}/lean4-pr-releases \
--title "Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})" \
--notes "" \
pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }} \
artifacts/*/*
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})
# There are coredumps files here as well, but all in deeper subdirectories.
files: artifacts/*/*
fail_on_unmatched_files: true
draft: false
tag_name: pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}
repository: ${{ github.repository_owner }}/lean4-pr-releases
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Report release status (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
@@ -396,18 +379,6 @@ jobs:
# We next automatically create a Batteries branch using this toolchain.
# Batteries doesn't itself have a mechanism to report results of CI from this branch back to Lean
# Instead this is taken care of by Mathlib CI, which will fail if Batteries fails.
# Generate a token from the mathlib-nightly-testing GitHub App for cross-org access
- name: Generate GitHub App token for leanprover-community repos
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
id: mathlib-app-token
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2
with:
app-id: ${{ secrets.MATHLIB_NIGHTLY_TESTING_APP_ID }}
private-key: ${{ secrets.MATHLIB_NIGHTLY_TESTING_PRIVATE_KEY }}
owner: leanprover-community
repositories: batteries,mathlib4-nightly-testing
- name: Cleanup workspace
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
run: |
@@ -416,10 +387,10 @@ jobs:
# Checkout the Batteries repository with all branches
- name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
repository: leanprover-community/batteries
token: ${{ steps.mathlib-app-token.outputs.token }}
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
@@ -476,10 +447,10 @@ jobs:
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
repository: leanprover-community/mathlib4-nightly-testing
token: ${{ steps.mathlib-app-token.outputs.token }}
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
@@ -559,7 +530,7 @@ jobs:
# Checkout the reference manual repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
repository: leanprover/reference-manual
token: ${{ secrets.MANUAL_PR_BOT }}

View File

@@ -27,7 +27,7 @@ jobs:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"
@@ -58,7 +58,7 @@ jobs:
shell: 'nix develop -c bash -euxo pipefail {0}'
- name: Restore Cache
if: env.should_update_stage0 == 'yes'
uses: actions/cache/restore@v5
uses: actions/cache/restore@v4
with:
# NOTE: must be in sync with `restore-cache` in `build-template.yml`
path: |

View File

@@ -10,22 +10,22 @@ option(USE_MIMALLOC "use mimalloc" ON)
get_cmake_property(vars CACHE_VARIABLES)
foreach(var ${vars})
get_property(currentHelpString CACHE "${var}" PROPERTY HELPSTRING)
if(var MATCHES "STAGE0_(.*)")
if("${var}" MATCHES "STAGE0_(.*)")
list(APPEND STAGE0_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif(var MATCHES "STAGE1_(.*)")
elseif("${var}" MATCHES "STAGE1_(.*)")
list(APPEND STAGE1_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif(currentHelpString MATCHES "No help, variable specified on the command line." OR currentHelpString STREQUAL "")
elseif("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "")
list(APPEND CL_ARGS "-D${var}=${${var}}")
if(var MATCHES "USE_GMP|CHECK_OLEAN_VERSION|LEAN_VERSION_.*|LEAN_SPECIAL_VERSION_DESC")
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION|LEAN_VERSION_.*|LEAN_SPECIAL_VERSION_DESC")
# must forward options that generate incompatible .olean format
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
elseif(var MATCHES "LLVM*|PKG_CONFIG|USE_LAKE|USE_MIMALLOC")
elseif("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE|USE_MIMALLOC")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
elseif(var MATCHES "USE_MIMALLOC")
elseif("${var}" MATCHES "USE_MIMALLOC")
list(APPEND CL_ARGS "-D${var}=${${var}}")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
elseif((var MATCHES "CMAKE_.*") AND NOT (var MATCHES "CMAKE_BUILD_TYPE") AND NOT (var MATCHES "CMAKE_HOME_DIRECTORY"))
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
list(APPEND PLATFORM_ARGS "-D${var}=${${var}}")
endif()
endforeach()
@@ -34,15 +34,15 @@ include(ExternalProject)
project(LEAN CXX C)
if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
endif()
# Don't do anything with cadical on wasm
if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
find_program(CADICAL cadical)
if(NOT CADICAL)
set(CADICAL_CXX c++)
if(CADICAL_USE_CUSTOM_CXX)
if (CADICAL_USE_CUSTOM_CXX)
set(CADICAL_CXX ${CMAKE_CXX_COMPILER})
# Use same platform flags as for Lean executables, in particular from `prepare-llvm-linux.sh`,
# but not Lean-specific `LEAN_EXTRA_CXX_FLAGS` such as fsanitize.
@@ -54,51 +54,42 @@ if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
endif()
# missing stdio locking API on Windows
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif()
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
ExternalProject_Add(
cadical
ExternalProject_add(cadical
PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-2.1.2
CONFIGURE_COMMAND ""
BUILD_COMMAND
$(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS} LDFLAGS=${CADICAL_LDFLAGS}
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk
CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
CXX=${CADICAL_CXX}
CXXFLAGS=${CADICAL_CXXFLAGS}
LDFLAGS=${CADICAL_LDFLAGS}
BUILD_IN_SOURCE ON
INSTALL_COMMAND ""
)
set(
CADICAL
${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX}
CACHE FILEPATH
"path to cadical binary"
FORCE
)
INSTALL_COMMAND "")
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
list(APPEND EXTRA_DEPENDS cadical)
endif()
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
endif()
if(USE_MIMALLOC)
ExternalProject_Add(
mimalloc
if (USE_MIMALLOC)
ExternalProject_add(mimalloc
PREFIX mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc
GIT_TAG v2.2.3
# just download, we compile it as part of each stage as it is small
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
INSTALL_COMMAND "")
list(APPEND EXTRA_DEPENDS mimalloc)
endif()
if(NOT STAGE1_PREV_STAGE)
ExternalProject_Add(
stage0
if (NOT STAGE1_PREV_STAGE)
ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src
BINARY_DIR stage0
@@ -106,49 +97,38 @@ if(NOT STAGE1_PREV_STAGE)
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS
ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND
"" # skip install
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install
DEPENDS ${EXTRA_DEPENDS}
)
list(APPEND EXTRA_DEPENDS stage0)
endif()
ExternalProject_Add(
stage1
ExternalProject_add(stage1
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage1
CMAKE_ARGS
-DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0
-DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS} ${STAGE1_ARGS}
CMAKE_ARGS -DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS} ${STAGE1_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS ${EXTRA_DEPENDS}
STEP_TARGETS configure
)
ExternalProject_Add(
stage2
ExternalProject_add(stage2
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage2
CMAKE_ARGS
-DSTAGE=2 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage1 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
${CL_ARGS}
CMAKE_ARGS -DSTAGE=2 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage1 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage1
EXCLUDE_FROM_ALL ON
STEP_TARGETS configure
)
ExternalProject_Add(
stage3
ExternalProject_add(stage3
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage3
CMAKE_ARGS
-DSTAGE=3 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage2 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
${CL_ARGS}
CMAKE_ARGS -DSTAGE=3 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage2 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage2
@@ -157,14 +137,24 @@ ExternalProject_Add(
# targets forwarded to appropriate stages
add_custom_target(update-stage0 COMMAND $(MAKE) -C stage1 update-stage0 DEPENDS stage1)
add_custom_target(update-stage0
COMMAND $(MAKE) -C stage1 update-stage0
DEPENDS stage1)
add_custom_target(update-stage0-commit COMMAND $(MAKE) -C stage1 update-stage0-commit DEPENDS stage1)
add_custom_target(update-stage0-commit
COMMAND $(MAKE) -C stage1 update-stage0-commit
DEPENDS stage1)
add_custom_target(test COMMAND $(MAKE) -C stage1 test DEPENDS stage1)
add_custom_target(test
COMMAND $(MAKE) -C stage1 test
DEPENDS stage1)
add_custom_target(clean-stdlib COMMAND $(MAKE) -C stage1 clean-stdlib DEPENDS stage1)
add_custom_target(clean-stdlib
COMMAND $(MAKE) -C stage1 clean-stdlib
DEPENDS stage1)
install(CODE "execute_process(COMMAND make -C stage1 install)")
add_custom_target(check-stage3 COMMAND diff "stage2/bin/lean" "stage3/bin/lean" DEPENDS stage3)
add_custom_target(check-stage3
COMMAND diff "stage2/bin/lean" "stage3/bin/lean"
DEPENDS stage3)

View File

@@ -218,21 +218,6 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
# Writing the release notes
Release notes content is only written for the first release candidate (`-rc1`). For subsequent RCs and stable releases,
just update the title in the existing release notes file (see "Release notes title format" below).
## Release notes title format
The title in the `#doc (Manual)` line must follow these formats:
- **For -rc1**: `"Lean 4.7.0-rc1 (2024-03-15)"` — Include the RC suffix and the release date
- **For -rc2, -rc3, etc.**: `"Lean 4.7.0-rc2 (2024-03-20)"` — Update the RC number and date
- **For stable release**: `"Lean 4.7.0 (2024-04-01)"` — Remove the RC suffix but keep the date
The date should be the actual date when the tag was pushed (or when CI completed and created the release page).
## Generating the release notes
Release notes are automatically generated from the commit history, using `script/release_notes.py`.
Run this as `script/release_notes.py --since v4.6.0`, where `v4.6.0` is the *previous* release version.
@@ -247,113 +232,4 @@ Some judgement is required here: ignore commits which look minor,
but manually add items to the release notes for significant PRs that were rebase-merged.
There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
## Reviewing and fixing the generated markdown
Before adding the release notes to the reference manual, carefully review the generated markdown for these common issues:
1. **Unterminated code blocks**: PR descriptions sometimes have unclosed code fences. Look for code blocks
that don't have a closing ` ``` `. If found, fetch the original PR description with `gh pr view <number>`
and repair the code block with the complete content.
2. **Truncated descriptions**: Some PR descriptions may end abruptly mid-sentence. Review these and complete
the descriptions based on the original PR.
3. **Markdown syntax issues**: Check for other markdown problems that could cause parsing errors.
## Creating the release notes file
The release notes go in `Manual/Releases/v4_7_0.lean` in the reference-manual repository.
The file structure must follow the Verso format:
```lean
/-
Copyright (c) 2025 Lean FRO LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: <Your Name>
-/
import VersoManual
import Manual.Meta
import Manual.Meta.Markdown
open Manual
open Verso.Genre
open Verso.Genre.Manual
open Verso.Genre.Manual.InlineLean
#doc (Manual) "Lean 4.7.0-rc1 (2024-03-15)" =>
%%%
tag := "release-v4.7.0"
file := "v4.7.0"
%%%
<release notes content here>
```
**Important formatting rules for Verso:**
- Use `#` for section headers inside the document, not `##` (Verso uses header level 1 for subsections)
- Use plain ` ``` ` for code blocks, not ` ```lean ` (the latter will cause Lean to execute the code)
- Identifiers with underscores like `bv_decide` should be wrapped in backticks: `` `bv_decide` ``
(otherwise the underscore may be interpreted as markdown emphasis)
## Updating Manual/Releases.lean
After creating the release notes file, update `Manual/Releases.lean` to include it:
1. Add the import near the top with other version imports:
```lean
import Manual.Releases.«v4_7_0»
```
2. Add the include statement after the other includes:
```lean
{include 0 Manual.Releases.«v4_7_0»}
```
## Building and verifying
Build the release notes to check for errors:
```bash
lake build Manual.Releases.v4_7_0
```
Common errors and fixes:
- "Wrong header nesting - got ## but expected at most #": Change `##` to `#`
- "Tactic 'X' failed" or similar: Code is being executed; change ` ```lean ` to ` ``` `
- "'_'" errors: Underscore in identifier being parsed as emphasis; wrap in backticks
## Creating the PR
**Important: Timing with the reference-manual tag**
The reference-manual repository deploys documentation when a version tag is pushed. If you merge
release notes AFTER the tag is created, the deployed documentation won't include them.
You have two options:
1. **Preferred**: Include the release notes in the same PR as the toolchain bump (or merge the
release notes PR before creating the tag). This ensures the tag includes the release notes.
2. **If release notes are merged after the tag**: You must regenerate the tag to trigger a new deployment:
```bash
cd /path/to/reference-manual
git fetch origin
git tag -d v4.7.0-rc1 # Delete local tag
git tag v4.7.0-rc1 origin/main # Create tag at current main (which has release notes)
git push origin :refs/tags/v4.7.0-rc1 # Delete remote tag
git push origin v4.7.0-rc1 # Push new tag (triggers Deploy workflow)
```
If creating a separate PR for release notes:
```bash
git checkout -b v4.7.0-release-notes
git add Manual/Releases/v4_7_0.lean Manual/Releases.lean
git commit -m "doc: add v4.7.0 release notes"
git push -u origin v4.7.0-release-notes
gh pr create --title "doc: add v4.7.0 release notes" --body "This PR adds the release notes for Lean v4.7.0."
```
See `./releases_drafts/README.md` for more information about pre-written release note entries.
See `./releases_drafts/README.md` for more information.

View File

@@ -810,7 +810,7 @@ Docstrings for constants should have the following structure:
The **short summary** should be 13 sentences (ideally 1) and provide
enough information for most readers to quickly decide whether the
constant is relevant to their task. The first (or only) sentence of
docstring is relevant to their task. The first (or only) sentence of
the short summary should be a *sentence fragment* in which the subject
is implied to be the documented item, written in present tense
indicative, or a *noun phrase* that characterizes the documented
@@ -1123,110 +1123,6 @@ infix:50 " ⇔ " => Bijection
recommended_spelling "bij" for "⇔" in [Bijection, «term_⇔_»]
```
#### Tactics
Docstrings for tactics should have the following structure:
* Short summary
* Details
* Variants
* Examples
Sometimes more than one declaration is needed to implement what the user
sees as a single tactic. In that case, only one declaration should have
the associated docstring, and the others should have the `tactic_alt`
attribute to mark them as an implementation detail.
The **short summary** should be 13 sentences (ideally 1) and provide
enough information for most readers to quickly decide whether the
tactic is relevant to their task. The first (or only) sentence of
the short summary should be a full sentence in which the subject
is an example invocation of the tactic, written in present tense
indicative. If the example tactic invocation names parameters, then the
short summary may refer to them. For the example invocation, prefer the
simplest or most typical example. Explain more complicated forms in the
variants section. If needed, abbreviate the invocation by naming part of
the syntax and expanding it in the next sentence. The summary should be
written as a single paragraph.
**Details**, if needed, may be 1-3 paragraphs that describe further
relevant information. They may insert links as needed. This section
should fully explain the scope of the tactic: its syntax format,
on which goals it works and what the resulting goal(s) look like. It
should be clear whether the tactic fails if it does not close the main
goal and whether it creates any side goals. The details may include
explanatory examples that cant necessarily be machine checked and
dont fit the format.
If the tactic is extensible using `macro_rules`, mention this in the
details, with a link to `lean-manual://section/tactic-macro-extension`
and give a one-line example. If the tactic provides an attribute or a
command that allows the user to extend its behavior, the documentation
on how to extend the tactic belongs to that attribute or command. In the
tactic docstring, use a single sentence to refer the reader to this
further documentation.
**Variants**, if needed, should be a bulleted list describing different
options and forms of the same tactic. The reader should be able to parse
and understand the parts of a tactic invocation they are hovering over,
using this list. Each list item should describe an individual variant
and take one of two formats: the **short summary** as above, or a
**named list item**. A named list item consists of a title in bold
followed by an indented short paragraph.
Variants should be explained from the perspective of the tactic's users, not
their implementers. A tactic that is implemented as a single Lean parser may
have multiple variants from the perspective of users, while a tactic that is
implemented as multiple parsers may have no variants, but merely an optional
part of the syntax.
**Examples** should start with the line `Examples:` (or `Example:` if
theres exactly one). The section should consist of a sequence of code
blocks, each showing a Lean declaration (usually with the `example`
keyword) that invokes the tactic. When the effect of the tactic is not
clear from the code, you can use code comments to describe this. Do
not include text between examples, because it can be unclear whether
the text refers to the code before or after the example.
##### Example
````
`rw [e]` uses the expression `e` as a rewrite rule on the main goal,
then tries to close the goal by "cheap" (reducible) `rfl`.
If `e` is a defined constant, then the equational theorems associated with `e`
are used. This provides a convenient way to unfold `e`. If `e` has parameters,
the tactic will try to fill these in by unification with the matching part of
the target. Parameters are only filled in once per rule, restricting which
later rewrites can be found. Parameters that are not filled in after
unification will create side goals. If the `rfl` fails to close the main goal,
no error is raised.
`rw` may fail to rewrite terms "under binders", such as `∀ x, ...` or `∃ x,
...`. `rw` can also fail with a "motive is type incorrect" error in the context
of dependent types. In these cases, consider using `simp only`.
* `rw [e₁, ... eₙ]` applies the given rules sequentially.
* `rw [← e]` or `rw [<- e]` applies the rewrite in the reverse direction.
* `rw [e] at l` rewrites with `e` at location(s) `l`.
* `rw (occs := .pos L) [e]`, where `L` is a literal list of natural numbers,
only rewrites the given occurrences in the target. Occurrences count from 1.
* `rw (occs := .neg L) [e]`, where `L` is a literal list of natural numbers,
skips rewriting the given occurrences in the target. Occurrences count from 1.
Examples:
```lean
example {a b : Nat} (h : a + a = b) : (a + a) + (a + a) = b + b := by rw [h]
```
```lean
example {f : Nat -> Nat} (h : ∀ x, f x = 1) (a b : Nat) : f a = f b := by
rw [h] -- `rw` instantiates `h` only once, so this is equivalent to: `rw [h a]`
-- goal: ⊢ 1 = f b
rw [h] -- equivalent to: `rw [h b]`
```
````
## Dictionary

View File

@@ -29,7 +29,7 @@ def main (args : List String) : IO Unit := do
if !msgs.toList.isEmpty then -- skip this file if there are parse errors
msgs.forM fun msg => msg.toString >>= IO.println
throw <| .userError "parse errors in file"
let `(header| $[module%$moduleTk?]? $[prelude%$preludeTk?]? $imps:import*) := header
let `(header| $[module%$moduleTk?]? $imps:import*) := header
| throw <| .userError s!"unexpected header syntax of {path}"
if moduleTk?.isSome then
continue
@@ -38,11 +38,11 @@ def main (args : List String) : IO Unit := do
let startPos := header.raw.getPos? |>.getD parserState.pos
let dummyEnv mkEmptyEnvironment
let (initCmd, parserState', msgs') :=
let (initCmd, parserState', _) :=
Parser.parseCommand inputCtx { env := dummyEnv, options := {} } parserState msgs
-- insert section if any trailing command (or error, which could be from an unknown command)
if !initCmd.isOfKind ``Parser.Command.eoi || msgs'.hasErrors then
-- insert section if any trailing command
if !initCmd.isOfKind ``Parser.Command.eoi then
let insertPos? :=
-- put below initial module docstring if any
guard (initCmd.isOfKind ``Parser.Command.moduleDoc) *> initCmd.getTailPos? <|>
@@ -57,21 +57,19 @@ def main (args : List String) : IO Unit := do
sec := "\n\n" ++ sec
if insertPos?.isNone then
sec := sec ++ "\n\n"
let insertPos := text.pos! insertPos
text := text.extract text.startPos insertPos ++ sec ++ text.extract insertPos text.endPos
text := text.extract 0 insertPos ++ sec ++ text.extract insertPos text.rawEndPos
-- prepend each import with `public `
for imp in imps.reverse do
let insertPos := imp.raw.getPos?.get!
let prfx := if doMeta then "public meta " else "public "
let insertPos := text.pos! insertPos
text := text.extract text.startPos insertPos ++ prfx ++ text.extract insertPos text.endPos
text := text.extract 0 insertPos ++ prfx ++ text.extract insertPos text.rawEndPos
-- insert `module` header
let mut initText := text.extract text.startPos (text.pos! startPos)
if !initText.trimAscii.isEmpty then
let mut initText := text.extract 0 startPos
if !initText.trim.isEmpty then
-- If there is a header comment, preserve it and put `module` in the line after
initText := initText.trimAsciiEnd.toString ++ "\n"
text := initText ++ "module\n\n" ++ text.extract (text.pos! startPos) text.endPos
initText := initText.trimRight ++ "\n"
text := initText ++ "module\n\n" ++ text.extract startPos text.rawEndPos
IO.FS.writeFile path text

View File

@@ -5,13 +5,12 @@ Authors: Mario Carneiro, Sebastian Ullrich
-/
module
prelude
public import Init.Prelude
public import Init.System.IO
public import Lean.Util.Path
import Lean.Environment
import Lean.ExtraModUses
import Lake.CLI.Main
import Lean.Parser.Module
import Lake.Load.Workspace
/-! # Shake: A Lean import minimizer
@@ -21,12 +20,84 @@ ensuring that every import is used to contribute some constant or other elaborat
recorded by `recordExtraModUse` and friends.
-/
/-- help string for the command line interface -/
def help : String := "Lean project tree shaking tool
Usage: lake exe shake [OPTIONS] <MODULE>..
Arguments:
<MODULE>
A module path like `Mathlib`. All files transitively reachable from the
provided module(s) will be checked.
Options:
--force
Skips the `lake build --no-build` sanity check
--keep-implied
Preserves existing imports that are implied by other imports and thus not technically needed
anymore
--keep-prefix
If an import `X` would be replaced in favor of a more specific import `X.Y...` it implies,
preserves the original import instead. More generally, prefers inserting `import X` even if it
was not part of the original imports as long as it was in the original transitive import closure
of the current module.
--keep-public
Preserves all `public` imports to avoid breaking changes for external downstream modules
--add-public
Adds new imports as `public` if they have been in the original public closure of that module.
In other words, public imports will not be removed from a module unless they are unused even
in the private scope, and those that are removed will be re-added as `public` in downstream
modules even if only needed in the private scope there. Unlike `--keep-public`, this may
introduce breaking changes but will still limit the number of inserted imports.
--explain
Gives constants explaining why each module is needed
--fix
Apply the suggested fixes directly. Make sure you have a clean checkout
before running this, so you can review the changes.
--gh-style
Outputs messages that can be parsed by `gh-problem-matcher-wrap`
Annotations:
The following annotations can be added to Lean files in order to configure the behavior of
`shake`. Only the substring `shake: ` directly followed by a directive is checked for, so multiple
directives can be mixed in one line such as `-- shake: keep-downstream, shake: keep-all`, and they
can be surrounded by arbitrary comments such as `-- shake: keep (metaprogram output dependency)`.
* `module -- shake: keep-downstream`:
Preserves this module in all (current) downstream modules, adding new imports of it if needed.
* `module -- shake: keep-all`:
Preserves all existing imports in this module as is. New imports now needed because of upstream
changes may still be added.
* `import X -- shake: keep`:
Preserves this specific import in the current module. The most common use case is to preserve a
public import that will be needed in downstream modules to make sense of the output of a
metaprogram defined in this module. For example, if a tactic is defined that may synthesize a
reference to a theorem when run, there is no way for `shake` to detect this by itself and the
module of that theorem should be publicly imported and annotated with `keep` in the tactic's
module.
```
public import X -- shake: keep (metaprogram output dependency)
...
elab \"my_tactic\" : tactic => do
... mkConst ``f -- `f`, defined in `X`, may appear in the output of this tactic
```
"
open Lean
namespace Lake.Shake
/-- The parsed CLI arguments for shake. -/
public structure Args where
/-- The parsed CLI arguments. See `help` for more information -/
structure Args where
help : Bool := false
keepImplied : Bool := false
keepPrefix : Bool := false
keepPublic : Bool := false
@@ -65,7 +136,7 @@ instance : Union Bitset where
instance : XorOp Bitset where
xor a b := { toNat := a.toNat ^^^ b.toNat }
def has (s : Bitset) (i : Nat) : Bool := s.toNat.testBit i
def has (s : Bitset) (i : Nat) : Bool := s {i}
end Bitset
@@ -166,19 +237,8 @@ structure State where
/-- Edits to be applied to the module imports. -/
edits : Edits := {}
-- Memoizations
reservedNames : Std.HashSet Name := Id.run do
let mut m := {}
for (c, _) in env.constants do
if isReservedName env c then
m := m.insert c
return m
indirectModUses : Std.HashMap Name (Array ModuleIdx) :=
indirectModUseExt.getState env
modNames : Array Name :=
env.header.moduleNames
def State.mods (s : State) := s.env.header.moduleData
def State.modNames (s : State) := s.env.header.moduleNames
/--
Given module `j`'s transitive dependencies, computes the union of `transImps` and the transitive
@@ -233,9 +293,9 @@ def isDeclMeta' (env : Environment) (declName : Name) : Bool :=
Given an `Expr` reference, returns the declaration name that should be considered the reference, if
any.
-/
def getDepConstName? (s : State) (ref : Name) : Option Name := do
def getDepConstName? (env : Environment) (ref : Name) : Option Name := do
-- Ignore references to reserved names, they can be re-generated in-place
guard <| !s.reservedNames.contains ref
guard <| !isReservedName env ref
-- `_simp_...` constants are similar, use base decl instead
return if ref.isStr && ref.getString!.startsWith "_simp_" then
ref.getPrefix
@@ -268,24 +328,22 @@ where
let env := s.env
Lean.Expr.foldConsts e deps fun c deps => Id.run do
let mut deps := deps
if let some c := getDepConstName? s c then
if let some c := getDepConstName? env c then
if let some j := env.getModuleIdxFor? c then
let k := { k with isMeta := k.isMeta && !isDeclMeta' env c }
if j != i then
deps := deps.union k {j}
for indMod in s.indirectModUses[c]?.getD #[] do
for indMod in (indirectModUseExt.getState env)[c]?.getD #[] do
if s.transDeps[i]!.has k indMod then
deps := deps.union k {indMod}
return deps
abbrev Explanations := Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name))
/--
Calculates the same as `calcNeeds` but tracing each module to a use-def declaration pair or
`none` if merely a recorded extra use.
-/
def getExplanations (s : State) (i : ModuleIdx) : Explanations := Id.run do
let env := s.env
def getExplanations (env : Environment) (i : ModuleIdx) :
Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name)) := Id.run do
let mut deps := default
for ci in env.header.moduleData[i]!.constants do
-- Added guard for cases like `structure` that are still exported even if private
@@ -306,25 +364,18 @@ def getExplanations (s : State) (i : ModuleIdx) : Explanations := Id.run do
where
/-- Accumulate the results from expression `e` into `deps`. -/
visitExpr (k : NeedsKind) name e deps :=
let env := s.env
Lean.Expr.foldConsts e deps fun c deps => Id.run do
let mut deps := deps
if let some c := getDepConstName? s c then
if let some c := getDepConstName? env c then
if let some j := env.getModuleIdxFor? c then
let k := { k with isMeta := k.isMeta && !isDeclMeta' env c }
deps := addExplanation j k name c deps
for indMod in s.indirectModUses[c]?.getD #[] do
if s.transDeps[i]!.has k indMod then
deps := addExplanation indMod k name (`_indirect ++ c) deps
if
if let some (some (name', _)) := deps[(j, k)]? then
decide (name.toString.length < name'.toString.length)
else true
then
deps := deps.insert (j, k) (name, c)
return deps
addExplanation (j : ModuleIdx) (k : NeedsKind) (use def_ : Name) (deps : Explanations) : Explanations :=
if
if let some (some (name', _)) := deps[(j, k)]? then
decide (use.toString.length < name'.toString.length)
else true
then
deps.insert (j, k) (use, def_)
else deps
partial def initStateFromEnv (env : Environment) : State := Id.run do
let mut s := { env }
@@ -491,7 +542,7 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
let mut imp : Import := { k with module := s.modNames[j]! }
let mut j := j
if args.trace then
IO.eprintln s!"`{imp}` is needed{if needs.has k j then " (calculated)" else ""}"
IO.eprintln s!"`{imp}` is needed"
if args.addPublic && !k.isExported &&
-- also add as public if previously `public meta`, which could be from automatic porting
(s.transDepsOrig[i]!.has { k with isExported := true } j || s.transDepsOrig[i]!.has { k with isExported := true, isMeta := true } j) then
@@ -580,7 +631,7 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
if toRemove.any fun imp => imp == decodeImport stx then
let pos := inputCtx.fileMap.toPosition stx.raw.getPos?.get!
println! "{path}:{pos.line}:{pos.column+1}: warning: unused import \
(use `lake shake --fix` to fix this, or `lake shake --update` to ignore)"
(use `lake exe shake --fix` to fix this, or `lake exe shake --update` to ignore)"
if !toAdd.isEmpty then
-- we put the insert message on the beginning of the last import line
let pos := inputCtx.fileMap.toPosition endHeader.offset
@@ -609,7 +660,7 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
modify fun s => { s with transDeps := s.transDeps.set! i newTransDepsI }
if args.explain then
let explanation := getExplanations s i
let explanation := getExplanations s.env i
let sanitize n := if n.hasMacroScopes then (sanitizeName n).run' { options := {} } else n
let run (imp : Import) := do
let j := s.env.getModuleIdx? imp.module |>.get!
@@ -625,31 +676,76 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
run j
for i in toAdd do run i
/-- Convert a list of module names to a bitset of module indexes -/
def toBitset (s : State) (ns : List Name) : Bitset :=
ns.foldl (init := ) fun c name =>
match s.env.getModuleIdxFor? name with
| some i => c {i}
| none => c
local instance : Ord Import where
compare :=
let _ := @lexOrd
compareOn fun imp => (!imp.isExported, imp.module.toString)
/--
Run the shake analysis with the given arguments.
/-- The main entry point. See `help` for more information on arguments. -/
public def main (args : List String) : IO UInt32 := do
initSearchPath ( findSysroot)
-- Parse the arguments
let rec parseArgs (args : Args) : List String Args
| [] => args
| "--help" :: rest => parseArgs { args with help := true } rest
| "--keep-implied" :: rest => parseArgs { args with keepImplied := true } rest
| "--keep-prefix" :: rest => parseArgs { args with keepPrefix := true } rest
| "--keep-public" :: rest => parseArgs { args with keepPublic := true } rest
| "--add-public" :: rest => parseArgs { args with addPublic := true } rest
| "--force" :: rest => parseArgs { args with force := true } rest
| "--fix" :: rest => parseArgs { args with fix := true } rest
| "--explain" :: rest => parseArgs { args with explain := true } rest
| "--trace" :: rest => parseArgs { args with trace := true } rest
| "--gh-style" :: rest => parseArgs { args with githubStyle := true } rest
| "--" :: rest => { args with mods := args.mods ++ rest.map (·.toName) }
| other :: rest => parseArgs { args with mods := args.mods.push other.toName } rest
let args := parseArgs {} args
Assumes Lean's search path has already been properly configured.
-/
public def run (args : Args) (h : 0 < args.mods.size)
(srcSearchPath : SearchPath := {}) : IO UInt32 := do
-- Bail if `--help` is passed
if args.help then
IO.println help
IO.Process.exit 0
if !args.force then
if ( IO.Process.output { cmd := "lake", args := #["build", "--no-build"] }).exitCode != 0 then
IO.println "There are out of date oleans. Run `lake build` or `lake exe cache get` first"
IO.Process.exit 1
-- Determine default module(s) to run shake on
let defaultTargetModules : Array Name try
let (elanInstall?, leanInstall?, lakeInstall?) Lake.findInstall?
let config Lake.MonadError.runEIO <| Lake.mkLoadConfig { elanInstall?, leanInstall?, lakeInstall? }
let some workspace Lake.loadWorkspace config |>.toBaseIO
| throw <| IO.userError "failed to load Lake workspace"
let defaultTargetModules := workspace.root.defaultTargets.flatMap fun target =>
if let some lib := workspace.root.findLeanLib? target then
lib.roots
else if let some exe := workspace.root.findLeanExe? target then
#[exe.config.root]
else
#[]
pure defaultTargetModules
catch _ =>
pure #[]
let srcSearchPath getSrcSearchPath
-- the list of root modules
let mods := args.mods
let mods := if args.mods.isEmpty then defaultTargetModules else args.mods
-- Only submodules of `pkg` will be edited or have info reported on them
let pkg := mods[0].getRoot
let pkg := mods[0]!.components.head!
-- Load all the modules
let imps := mods.map ({ module := · })
let (_, s) importModulesCore imps (isExported := true) |>.run
let s := s.markAllExported
let mut env finalizeImport s (isModule := true) imps {} (leakEnv := true) (loadExts := false)
if env.header.moduleData.any (!·.isModule) then
throw <| .userError "`lake shake` only works with `module`s currently"
let mut env finalizeImport s (isModule := true) imps {} (leakEnv := false) (loadExts := false)
-- the one env ext we want to initialize
let is := indirectModUseExt.toEnvExtension.getState env
let newState indirectModUseExt.addImportedFn is.importedEntries { env := env, opts := {} }

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# This script expects to be run from the repo root.
# Format cmake files
find -regex '.*/CMakeLists\.txt\(\.in\)?\|.*\.cmake\(\.in\)?' \
! -path './build/*' \
! -path "./stage0/*" \
-exec \
uvx gersemi --in-place --line-length 120 --indent 2 \
--definitions src/cmake/Modules/ src/CMakeLists.txt \
-- {} +

View File

@@ -3,3 +3,9 @@ name = "scripts"
[[lean_exe]]
name = "modulize"
root = "Modulize"
[[lean_exe]]
name = "shake"
root = "Shake"
# needed by `Lake.loadWorkspace`
supportInterpreter = true

View File

@@ -185,30 +185,6 @@ def get_release_notes(tag_name):
except Exception:
return None
def check_release_notes_file_exists(toolchain, github_token):
"""Check if the release notes file exists in the reference-manual repository.
For -rc1 releases, this checks that the release notes have been created.
For subsequent RCs and stable releases, release notes should already exist.
Returns tuple (exists: bool, is_rc1: bool) where is_rc1 indicates if this is
the first release candidate (when release notes need to be written).
"""
# Determine the release notes file path
# e.g., v4.28.0-rc1 -> Manual/Releases/v4_28_0.lean
base_version = strip_rc_suffix(toolchain.lstrip('v')) # "4.28.0"
file_name = f"v{base_version.replace('.', '_')}.lean" # "v4_28_0.lean"
file_path = f"Manual/Releases/{file_name}"
is_rc1 = toolchain.endswith("-rc1")
repo_url = "https://github.com/leanprover/reference-manual"
# Check if the file exists on main branch
content = get_branch_content(repo_url, "main", file_path, github_token)
return (content is not None, is_rc1)
def get_branch_content(repo_url, branch, file_path, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/contents/{file_path}?ref={branch}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
@@ -525,76 +501,6 @@ def check_proofwidgets4_release(repo_url, target_toolchain, github_token):
print(f" You will need to create and push a tag v0.0.{next_version}")
return False
def check_reference_manual_release_title(repo_url, toolchain, pr_branch, github_token):
"""Check if the reference-manual release notes title matches the release type.
For RC releases (e.g., v4.27.0-rc1), the title should contain the exact RC suffix.
For final releases (e.g., v4.27.0), the title should NOT contain any "-rc".
Returns True if check passes or is not applicable, False if title needs updating.
"""
is_rc = is_release_candidate(toolchain)
# For RC releases, get the base version and RC suffix
# e.g., "v4.27.0-rc1" -> version="4.27.0", rc_suffix="-rc1"
if is_rc:
parts = toolchain.lstrip('v').split('-', 1)
version = parts[0]
rc_suffix = '-' + parts[1] if len(parts) > 1 else ''
else:
version = toolchain.lstrip('v')
rc_suffix = ''
# Construct the release notes file path (e.g., Manual/Releases/v4_27_0.lean for v4.27.0)
file_name = f"v{version.replace('.', '_')}.lean" # "v4_27_0.lean"
file_path = f"Manual/Releases/{file_name}"
# Try to get the file from the PR branch first, then fall back to main branch
content = get_branch_content(repo_url, pr_branch, file_path, github_token)
if content is None:
# Try the default branch
content = get_branch_content(repo_url, "main", file_path, github_token)
if content is None:
print(f" ⚠️ Could not check release notes file: {file_path}")
return True # Don't block on this
# Look for the #doc line with the title
for line in content.splitlines():
if line.strip().startswith('#doc') and 'Manual' in line:
has_rc_in_title = '-rc' in line.lower()
if is_rc:
# For RC releases, title should contain the exact RC suffix (e.g., "-rc1")
# Use regex to match exact suffix followed by non-digit (to avoid -rc1 matching -rc10)
# Pattern matches the RC suffix followed by a non-digit or end-of-string context
# e.g., "-rc1" followed by space, quote, paren, or similar
exact_match = re.search(rf'{re.escape(rc_suffix)}(?![0-9])', line, re.IGNORECASE)
if exact_match:
print(f" ✅ Release notes title correctly shows {rc_suffix}")
return True
elif has_rc_in_title:
print(f" ❌ Release notes title shows wrong RC version (expected {rc_suffix})")
print(f" Update {file_path} to use '{rc_suffix}' in the title")
return False
else:
print(f" ❌ Release notes title missing RC suffix")
print(f" Update {file_path} to include '{rc_suffix}' in the title")
return False
else:
# For final releases, title should NOT contain -rc
if has_rc_in_title:
print(f" ❌ Release notes title still shows RC version")
print(f" Update {file_path} to remove '-rcN' from the title")
return False
else:
print(f" ✅ Release notes title is updated for final release")
return True
# If we didn't find the #doc line, don't block
print(f" ⚠️ Could not find release notes title in {file_path}")
return True
def run_mathlib_verify_version_tags(toolchain, verbose=False):
"""Run mathlib4's verify_version_tags.py script to validate the release tag.
@@ -738,27 +644,6 @@ def main():
else:
print(f" ✅ Release notes page title looks good ('{actual_title}').")
# Check if release notes file exists in reference-manual repository
# For -rc1 releases, this is when release notes need to be written
# For subsequent RCs and stable releases, they should already exist
release_notes_exists, is_rc1 = check_release_notes_file_exists(toolchain, github_token)
base_version = strip_rc_suffix(toolchain.lstrip('v'))
release_notes_file = f"Manual/Releases/v{base_version.replace('.', '_')}.lean"
if not release_notes_exists:
if is_rc1:
print(f" ❌ Release notes file not found: {release_notes_file}")
print(f" This is an -rc1 release, so release notes need to be written.")
print(f" Run `script/release_notes.py --since <previous_version>` to generate them.")
print(f" See doc/dev/release_checklist.md section 'Writing the release notes' for details.")
lean4_success = False
else:
print(f" ❌ Release notes file not found: {release_notes_file}")
print(f" Release notes should have been created for -rc1. Check the reference-manual repository.")
lean4_success = False
else:
print(f" ✅ Release notes file exists: {release_notes_file}")
repo_status["lean4"] = lean4_success
# If the release page doesn't exist, skip repository checks and master branch checks
@@ -824,11 +709,6 @@ def main():
print(f" ⚠️ CI: {ci_message}")
else:
print(f" ❓ CI: {ci_message}")
# For reference-manual, check that the release notes title has been updated
if name == "reference-manual":
pr_branch = f"bump_to_{toolchain}"
check_reference_manual_release_title(url, toolchain, pr_branch, github_token)
else:
print(f" ❌ PR with title '{pr_title}' does not exist")
print(f" Run `script/release_steps.py {toolchain} {name}` to create it")

View File

@@ -14,6 +14,13 @@ repositories:
bump-branch: true
dependencies: []
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: lean4checker
url: https://github.com/leanprover/lean4checker
toolchain-tag: true
@@ -35,14 +42,6 @@ repositories:
branch: main
dependencies: []
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- plausible
- name: import-graph
url: https://github.com/leanprover-community/import-graph
toolchain-tag: true
@@ -143,15 +142,3 @@ repositories:
branch: master
dependencies:
- verso-web-components
- name: comparator
url: https://github.com/leanprover/comparator
toolchain-tag: true
stable-branch: false
branch: master
- name: lean4export
url: https://github.com/leanprover/lean4export
toolchain-tag: true
stable-branch: false
branch: master

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Prelude
public import Init.Notation
@@ -37,7 +38,6 @@ public import Init.Omega
public import Init.MacroTrace
public import Init.Grind
public import Init.GrindInstances
public import Init.Sym
public import Init.While
public import Init.Syntax
public import Init.Internal

View File

@@ -102,7 +102,7 @@ noncomputable def strongIndefiniteDescription {α : Sort u} (p : α → Prop) (h
xp.val, fun _ => xp.property)
(fun hp => choice h, fun h => absurd h hp)
/-- The Hilbert epsilon function. -/
/-- the Hilbert epsilon Function -/
noncomputable def epsilon {α : Sort u} [h : Nonempty α] (p : α Prop) : α :=
(strongIndefiniteDescription p h).val
@@ -142,7 +142,6 @@ is classically true but not constructively. -/
/-- Transfer decidability of `¬ p` to decidability of `p`. -/
-- This can not be an instance as it would be tried everywhere.
@[instance_reducible]
def decidable_of_decidable_not (p : Prop) [h : Decidable (¬ p)] : Decidable p :=
match h with
| isFalse h => isTrue (Classical.not_not.mp h)

View File

@@ -144,7 +144,7 @@ instance : ToBool Bool where
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 is a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
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
@@ -161,7 +161,7 @@ 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 is a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
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

View File

@@ -121,11 +121,6 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
@[simp] theorem run_control [Monad m] [LawfulMonad m] (f : ({β : Type u} ExceptT ε m β m (stM m (ExceptT ε m) β)) m (stM m (ExceptT ε m) α)) :
ExceptT.run (control f) = f fun x => x.run := run_controlAt f
@[simp, grind =]
theorem run_adapt [Monad m] (f : ε ε') (x : ExceptT ε m α)
: run (ExceptT.adapt f x : ExceptT ε' m α) = Except.mapError f <$> run x :=
rfl
end ExceptT
/-! # Except -/
@@ -472,33 +467,15 @@ namespace EStateM
@[simp, grind =] theorem run_throw (e : ε) (s : σ):
EStateM.run (throw e : EStateM ε σ PUnit) s = .error e s := rfl
@[simp, grind =] theorem run_bind (x : EStateM ε σ α) (f : α EStateM ε σ β)
: EStateM.run (x >>= f : EStateM ε σ β) s
=
match EStateM.run x s with
| .ok x s => EStateM.run (f x) s
| .error e s => .error e s :=
rfl
@[simp, grind =]
theorem run_adaptExcept (f : ε ε') (x : EStateM ε σ α) (s : σ)
: EStateM.run (EStateM.adaptExcept f x : EStateM ε' σ α) s
=
match EStateM.run x s with
| .ok x s => .ok x s
| .error e s => .error (f e) s := by
simp only [EStateM.run, EStateM.adaptExcept]
cases (x s) <;> rfl
instance : LawfulMonad (EStateM ε σ) := .mk'
(id_map := fun x => funext <| fun s => by
simp only [Functor.map, EStateM.map]
dsimp only [EStateM.instMonad, EStateM.map]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)
(pure_bind := fun _ _ => by rfl)
(bind_assoc := fun x _ _ => funext <| fun s => by
simp only [bind, EStateM.bind]
dsimp only [EStateM.instMonad, EStateM.bind]
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)

View File

@@ -70,7 +70,7 @@ information to the return value, except a trivial proof of {name}`True`.
This instance is used whenever no more useful {name}`MonadAttach` instance can be implemented.
It always has a {name}`WeaklyLawfulMonadAttach`, but usually no {name}`LawfulMonadAttach` instance.
-/
@[expose, instance_reducible]
@[expose]
public protected def MonadAttach.trivial {m : Type u Type v} [Monad m] : MonadAttach m where
CanReturn _ _ := True
attach x := (·, .intro) <$> x

View File

@@ -51,10 +51,6 @@ scoped syntax (name := withAnnotateState)
/-- `skip` does nothing. -/
syntax (name := skip) "skip" : conv
/-- `cbv` performs simplification that closely mimics call-by-value evaluation,
using equations associated with definitions and the matchers. -/
syntax (name := cbv) "cbv" : conv
/--
Traverses into the left subterm of a binary operator.

View File

@@ -13,10 +13,6 @@ public import Init.SizeOf
public section
set_option linter.missingDocs true -- keep it documented
-- BEq instance for Option defined here so it's available early in the import chain
-- (before Init.Grind.Config and Init.MetaTypes which need BEq (Option Nat))
deriving instance BEq for Option
@[expose] section
universe u v w
@@ -341,7 +337,7 @@ inductive Exists {α : Sort u} (p : α → Prop) : Prop where
An indication of whether a loop's body terminated early that's used to compile the `for x in xs`
notation.
A collection's `ForIn` or `ForIn'` instance describes how to iterate over its elements. The monadic
A collection's `ForIn` or `ForIn'` instance describe's how to iterate over its elements. The monadic
action that represents the body of the loop returns a `ForInStep α`, where `α` is the local state
used to implement features such as `let mut`.
-/
@@ -514,12 +510,12 @@ abbrev SSuperset [HasSSubset α] (a b : α) := SSubset b a
/-- Notation type class for the union operation ``. -/
class Union (α : Type u) where
/-- `a b` is the union of `a` and `b`. -/
/-- `a b` is the union of`a` and `b`. -/
union : α α α
/-- Notation type class for the intersection operation `∩`. -/
class Inter (α : Type u) where
/-- `a ∩ b` is the intersection of `a` and `b`. -/
/-- `a ∩ b` is the intersection of`a` and `b`. -/
inter : α α α
/-- Notation type class for the set difference `\`. -/
@@ -542,10 +538,10 @@ infix:50 " ⊇ " => Superset
/-- Strict superset relation: `a ⊃ b` -/
infix:50 "" => SSuperset
/-- `a b` is the union of `a` and `b`. -/
/-- `a b` is the union of`a` and `b`. -/
infixl:65 " " => Union.union
/-- `a ∩ b` is the intersection of `a` and `b`. -/
/-- `a ∩ b` is the intersection of`a` and `b`. -/
infixl:70 "" => Inter.inter
/--
@@ -932,14 +928,6 @@ noncomputable def HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sor
noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : a b) (m : motive a) : motive b :=
h.rec m
/-- `HEq.ndrec` specialized to homogeneous heterogeneous equality -/
noncomputable def HEq.homo_ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : α Sort u1} (m : motive a) {b : α} (h : a b) : motive b :=
(eq_of_heq h).ndrec m
/-- `HEq.ndrec` specialized to homogeneous heterogeneous equality, symmetric variant -/
noncomputable def HEq.homo_ndrec_symm.{u1, u2} {α : Sort u2} {a : α} {motive : α Sort u1} (m : motive a) {b : α} (h : b a) : motive b :=
(eq_of_heq h).ndrec_symm m
/-- `HEq.ndrec` variant -/
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : a b) (h₂ : p a) : p b :=
eq_of_heq h₁ h₂
@@ -1486,29 +1474,6 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
/-! # Dependent products -/
instance {α : Type u} {β : α Type v} [h₁ : DecidableEq α] [h₂ : a, DecidableEq (β a)] :
DecidableEq (Sigma β)
| a₁, b₁, a₂, b₂ =>
match a₁, b₁, a₂, b₂, h₁ a₁ a₂ with
| _, b₁, _, b₂, isTrue (Eq.refl _) =>
match b₁, b₂, h₂ _ b₁ b₂ with
| _, _, isTrue (Eq.refl _) => isTrue rfl
| _, _, isFalse n => isFalse fun h
Sigma.noConfusion rfl .rfl (heq_of_eq h) fun _ e₂ n (eq_of_heq e₂)
| _, _, _, _, isFalse n => isFalse fun h
Sigma.noConfusion rfl .rfl (heq_of_eq h) fun e₁ _ n (eq_of_heq e₁)
instance {α : Sort u} {β : α Sort v} [h₁ : DecidableEq α] [h₂ : a, DecidableEq (β a)] : DecidableEq (PSigma β)
| a₁, b₁, a₂, b₂ =>
match a₁, b₁, a₂, b₂, h₁ a₁ a₂ with
| _, b₁, _, b₂, isTrue (Eq.refl _) =>
match b₁, b₂, h₂ _ b₁ b₂ with
| _, _, isTrue (Eq.refl _) => isTrue rfl
| _, _, isFalse n => isFalse fun h
PSigma.noConfusion rfl .rfl (heq_of_eq h) fun _ e₂ n (eq_of_heq e₂)
| _, _, _, _, isFalse n => isFalse fun h
PSigma.noConfusion rfl .rfl (heq_of_eq h) fun e₁ _ n (eq_of_heq e₁)
theorem Exists.of_psigma_prop {α : Sort u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
| x, hx => x, hx
@@ -1596,10 +1561,6 @@ instance {p q : Prop} [d : Decidable (p ↔ q)] : Decidable (p = q) :=
| isTrue h => isTrue (propext h)
| isFalse h => isFalse fun heq => h (heq Iff.rfl)
/-- Helper theorem for proving injectivity theorems -/
theorem Lean.injEq_helper {P Q R : Prop} :
(P Q R) (P Q R) := by intro h h₁,h₂; exact h h₁ h₂
gen_injective_theorems% Array
gen_injective_theorems% BitVec
gen_injective_theorems% ByteArray
@@ -2360,10 +2321,8 @@ namespace Lean
/--
Depends on the correctness of the Lean compiler, interpreter, and all `[implemented_by ...]` and `[extern ...]` annotations.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
axiom trustCompiler : True
set_option linter.deprecated false in
/--
When the kernel tries to reduce a term `Lean.reduceBool c`, it will invoke the Lean interpreter to evaluate `c`.
The kernel will not use the interpreter if `c` is not a constant.
@@ -2383,13 +2342,11 @@ Recall that the compiler trusts the correctness of all `[implemented_by ...]` an
If an extern function is executed, then the trusted code base will also include the implementation of the associated
foreign function.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
opaque reduceBool (b : Bool) : Bool :=
-- This ensures that `#print axioms` will track use of `reduceBool`.
have := trustCompiler
b
set_option linter.deprecated false in
/--
Similar to `Lean.reduceBool` for closed `Nat` terms.
@@ -2397,14 +2354,12 @@ Remark: we do not have plans for supporting a generic `reduceValue {α} (a : α)
The main issue is that it is non-trivial to convert an arbitrary runtime object back into a Lean expression.
We believe `Lean.reduceBool` enables most interesting applications (e.g., proof by reflection).
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
opaque reduceNat (n : Nat) : Nat :=
-- This ensures that `#print axioms` will track use of `reduceNat`.
have := trustCompiler
n
set_option linter.deprecated false in
/--
The axiom `ofReduceBool` is used to perform proofs by reflection. See `reduceBool`.
@@ -2418,10 +2373,8 @@ external type checkers that do not implement this feature.
Keep in mind that if you are using Lean as programming language, you are already trusting the Lean compiler and interpreter.
So, you are mainly losing the capability of type checking your development using external checkers.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
axiom ofReduceBool (a b : Bool) (h : reduceBool a = b) : a = b
set_option linter.deprecated false in
/--
The axiom `ofReduceNat` is used to perform proofs by reflection. See `reduceBool`.
@@ -2431,7 +2384,6 @@ external type checkers that do not implement this feature.
Keep in mind that if you are using Lean as programming language, you are already trusting the Lean compiler and interpreter.
So, you are mainly losing the capability of type checking your development using external checkers.
-/
@[deprecated "in-kernel native reduction is deprecated; assert native evaluations with axioms instead" (since := "2026-02-01")]
axiom ofReduceNat (a b : Nat) (h : reduceNat a = b) : a = b
@@ -2482,7 +2434,7 @@ class IdempotentOp (op : ααα) : Prop where
idempotent : (x : α) op x x = x
/--
`LeftIdentity op o` indicates `o` is a left identity of `op`.
`LeftIdentify op o` indicates `o` is a left identity of `op`.
This class does not require a proof that `o` is an identity, and
is used primarily for inferring the identity using class resolution.
@@ -2490,7 +2442,7 @@ is used primarily for inferring the identity using class resolution.
class LeftIdentity (op : α β β) (o : outParam α) : Prop
/--
`LawfulLeftIdentity op o` indicates `o` is a verified left identity of
`LawfulLeftIdentify op o` indicates `o` is a verified left identity of
`op`.
-/
class LawfulLeftIdentity (op : α β β) (o : outParam α) : Prop extends LeftIdentity op o where
@@ -2498,7 +2450,7 @@ class LawfulLeftIdentity (op : α → β → β) (o : outParam α) : Prop extend
left_id : a, op o a = a
/--
`RightIdentity op o` indicates `o` is a right identity `o` of `op`.
`RightIdentify op o` indicates `o` is a right identity `o` of `op`.
This class does not require a proof that `o` is an identity, and is used
primarily for inferring the identity using class resolution.
@@ -2506,7 +2458,7 @@ primarily for inferring the identity using class resolution.
class RightIdentity (op : α β α) (o : outParam β) : Prop
/--
`LawfulRightIdentity op o` indicates `o` is a verified right identity of
`LawfulRightIdentify op o` indicates `o` is a verified right identity of
`op`.
-/
class LawfulRightIdentity (op : α β α) (o : outParam β) : Prop extends RightIdentity op o where

View File

@@ -30,6 +30,3 @@ public import Init.Data.Array.Erase
public import Init.Data.Array.Zip
public import Init.Data.Array.InsertIdx
public import Init.Data.Array.Extract
public import Init.Data.Array.MinMax
public import Init.Data.Array.Nat
public import Init.Data.Array.Int

View File

@@ -9,7 +9,6 @@ prelude
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Count
import Init.Grind.Util
public section
@@ -93,18 +92,6 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
rcases xs with xs
simp
/-- This lemma is only relevant for `grind`. -/
@[grind =]
theorem _root_.Std.Internal.Array.countP_eq_zero_of_forall {xs : Array α} (h : x xs, ¬ p x) : xs.countP p = 0 :=
countP_eq_zero.mpr h
/-- This lemma is only relevant for `grind`. -/
theorem _root_.Std.Internal.Array.not_of_countP_eq_zero_of_mem {xs : Array α} (h : xs.countP p = 0) (h' : x xs) : ¬ p x :=
countP_eq_zero.mp h _ h'
grind_pattern Std.Internal.Array.not_of_countP_eq_zero_of_mem => xs.countP p, x xs where
guard xs.countP p = 0
@[simp] theorem countP_eq_size {p} : countP p xs = xs.size a xs, p a := by
rcases xs with xs
simp

View File

@@ -125,22 +125,6 @@ instance instDecidableEmpEq (ys : Array α) : Decidable (#[] = ys) :=
| [] => isTrue rfl
| _ :: _ => isFalse (fun h => Array.noConfusion rfl (heq_of_eq h) (fun h => List.noConfusion rfl h))
@[inline]
def instDecidableEqEmpImpl (xs : Array α) : Decidable (xs = #[]) :=
decidable_of_iff xs.isEmpty <| by rcases xs with <;> simp [Array.isEmpty]
@[inline]
def instDecidableEmpEqImpl (xs : Array α) : Decidable (#[] = xs) :=
decidable_of_iff xs.isEmpty <| by rcases xs with <;> simp [Array.isEmpty]
@[csimp]
theorem instDecidableEqEmp_csimp : @instDecidableEqEmp = @instDecidableEqEmpImpl :=
Subsingleton.allEq _ _
@[csimp]
theorem instDecidableEmpEq_csimp : @instDecidableEmpEq = @instDecidableEmpEqImpl :=
Subsingleton.allEq _ _
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
(xs == ys) = if h : xs.size = ys.size then
decide ( (i : Nat) (h' : i < xs.size), xs[i] == ys[i]'(h h')) else false := by

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.List.Nat.Find
import Init.Data.List.Nat.Sum
import all Init.Data.Array.Basic
public import Init.Data.Array.Range
@@ -115,7 +114,7 @@ theorem getElem_zero_flatten.proof {xss : Array (Array α)} (h : 0 < xss.flatten
simp only [List.findSome?_toArray, List.findSome?_map, Function.comp_def, List.getElem?_toArray,
List.findSome?_isSome_iff, isSome_getElem?]
simp only [flatten_toArray_map_toArray, List.size_toArray, List.length_flatten,
List.sum_pos_iff_exists_pos_nat, List.mem_map] at h
Nat.sum_pos_iff_exists_pos, List.mem_map] at h
obtain _, xs, m, rfl, h := h
exact xs, m, by simpa using h
@@ -675,39 +674,6 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
simp only [Option.map_map, Function.comp_def, Fin.cast_cast]
simp [Array.size]
/-! ### find? and findFinIdx? -/
theorem find?_eq_map_findFinIdx?_getElem {xs : Array α} {p : α Bool} :
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
cases xs
simp [List.find?_eq_map_findFinIdx?_getElem]
rfl
theorem find?_eq_bind_findIdx?_getElem? {xs : Array α} {p : α Bool} :
xs.find? p = (xs.findIdx? p).bind (xs[·]?) := by
cases xs
simp [List.find?_eq_bind_findIdx?_getElem?]
theorem find?_eq_getElem?_findIdx {xs : Array α} {p : α Bool} :
xs.find? p = xs[xs.findIdx p]? := by
cases xs
simp [List.find?_eq_getElem?_findIdx]
theorem findIdx?_eq_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α Bool} :
xs.findIdx? p = (xs.find? p).bind (xs.idxOf? ·) := by
cases xs
simp [List.findIdx?_eq_bind_find?_idxOf?]
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α Bool} :
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf? ·) := by
cases xs
simp [List.findFinIdx?_eq_bind_find?_finIdxOf?]
theorem findIdx_eq_getD_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α Bool} :
xs.findIdx p = ((xs.find? p).bind (xs.idxOf? ·)).getD xs.size := by
cases xs
simp [List.findIdx_eq_getD_bind_find?_idxOf?]
/-! ### idxOf
The verification API for `idxOf` is still incomplete.

View File

@@ -1,35 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Graf, Paul Reichert
-/
module
prelude
public import Init.Data.List.Int.Sum
public import Init.Data.Array.Lemmas
public import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.DivMod.Lemmas
import Init.Data.List.MinMax
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array
@[simp] theorem sum_replicate_int {n : Nat} {a : Int} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
theorem sum_append_int {as₁ as₂ : Array Int} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
simp [sum_append]
theorem sum_reverse_int (xs : Array Int) : xs.reverse.sum = xs.sum := by
simp [sum_reverse]
theorem sum_eq_foldl_int {xs : Array Int} : xs.sum = xs.foldl (init := 0) (· + ·) := by
simp only [foldl_eq_foldr_reverse, Int.add_comm, sum_eq_foldr, sum_reverse_int]
end Array

View File

@@ -115,8 +115,7 @@ theorem none_eq_getElem?_iff {xs : Array α} {i : Nat} : none = xs[i]? ↔ xs.si
theorem getElem?_eq_none {xs : Array α} (h : xs.size i) : xs[i]? = none := by
simp [h]
grind_pattern Array.getElem?_eq_none => xs.size, xs[i]? where
guard xs.size i
grind_pattern Array.getElem?_eq_none => xs.size, xs[i]?
@[simp] theorem getElem?_eq_getElem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i]? = some xs[i] :=
getElem?_pos ..
@@ -482,18 +481,9 @@ theorem mem_iff_getElem {a} {xs : Array α} : a ∈ xs ↔ ∃ (i : Nat) (h : i
theorem mem_iff_getElem? {a} {xs : Array α} : a xs i : Nat, xs[i]? = some a := by
simp [getElem?_eq_some_iff, mem_iff_getElem]
theorem exists_mem_iff_exists_getElem {P : α Prop} {xs : Array α} :
( x xs, P x) (i : Nat), (hi : i < xs.size), P (xs[i]) := by
cases xs; simp [List.exists_mem_iff_exists_getElem]
theorem forall_mem_iff_forall_getElem {P : α Prop} {xs : Array α} :
( x xs, P x) (i : Nat) (hi : i < xs.size), P (xs[i]) := by
cases xs; simp [List.forall_mem_iff_forall_getElem]
@[deprecated forall_mem_iff_forall_getElem (since := "2026-01-29")]
theorem forall_getElem {xs : Array α} {p : α Prop} :
( (i : Nat) h, p (xs[i]'h)) a, a xs p a := by
exact forall_mem_iff_forall_getElem.symm
cases xs; simp [List.forall_getElem]
/-! ### isEmpty -/
@@ -1978,14 +1968,6 @@ theorem append_eq_append_iff {ws xs ys zs : Array α} :
· left; exact as.toList, by simp
· right; exact cs.toList, by simp
theorem append_eq_append_iff_of_size_eq_left {ws xs ys zs : Array α} (h : ws.size = xs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ Array.toList_inj] using List.append_eq_append_iff_of_size_eq_left h
theorem append_eq_append_iff_of_size_eq_right {ws xs ys zs : Array α} (h : ys.size = zs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ Array.toList_inj] using List.append_eq_append_iff_of_size_eq_right h
@[grind =] theorem set_append {xs ys : Array α} {i : Nat} {x : α} (h : i < (xs ++ ys).size) :
(xs ++ ys).set i x =
if h' : i < xs.size then
@@ -2517,6 +2499,10 @@ theorem flatMap_replicate {f : α → Array β} : (replicate n a).flatMap f = (r
rw [ List.toArray_replicate, List.isEmpty_toArray]
simp
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
/-! ### Preliminaries about `swap` needed for `reverse`. -/
@[grind =]
@@ -3078,18 +3064,6 @@ theorem foldl_eq_foldlM {f : β → α → β} {b} {xs : Array α} {start stop :
theorem foldr_eq_foldrM {f : α β β} {b} {xs : Array α} {start stop : Nat} :
xs.foldr f b start stop = (xs.foldrM (m := Id) (pure <| f · ·) b start stop).run := rfl
public theorem foldl_eq_foldl_extract {xs : Array α} {f : β α β} {init : β} :
xs.foldl (init := init) (start := start) (stop := stop) f =
(xs.extract start stop).foldl (init := init) f := by
simp only [foldl_eq_foldlM]
rw [foldlM_start_stop]
public theorem foldr_eq_foldr_extract {xs : Array α} {f : α β β} {init : β} :
xs.foldr (init := init) (start := start) (stop := stop) f =
(xs.extract stop start).foldr (init := init) f := by
simp only [foldr_eq_foldrM]
rw [foldrM_start_stop]
@[simp] theorem id_run_foldlM {f : β α Id β} {b} {xs : Array α} {start stop : Nat} :
Id.run (xs.foldlM f b start stop) = xs.foldl (f · · |>.run) b start stop := rfl
@@ -4302,31 +4276,19 @@ theorem getElem?_range {n : Nat} {i : Nat} : (Array.range n)[i]? = if i < n then
/-! ### sum -/
@[simp, grind =] theorem sum_empty [Add α] [Zero α] : (#[] : Array α).sum = 0 := rfl
theorem sum_eq_foldr [Add α] [Zero α] {xs : Array α} :
xs.sum = xs.foldr (init := 0) (· + ·) :=
rfl
-- Without further algebraic hypotheses, there's no useful `sum_push` lemma.
@[simp, grind =]
theorem sum_toList [Add α] [Zero α] {as : Array α} : as.toList.sum = as.sum := by
theorem sum_eq_sum_toList [Add α] [Zero α] {as : Array α} : as.toList.sum = as.sum := by
cases as
simp [Array.sum, List.sum]
@[deprecated sum_toList (since := "2026-01-14")]
def sum_eq_sum_toList := @sum_toList
@[simp, grind =]
theorem sum_append [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
[Std.LeftIdentity (α := α) (· + ·) 0] [Std.LawfulLeftIdentity (α := α) (· + ·) 0]
{as as₂ : Array α} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
simp [ sum_toList, List.sum_append]
@[simp, grind =]
theorem sum_reverse [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
[Std.Commutative (α := α) (· + ·)]
[Std.LawfulLeftIdentity (α := α) (· + ·) 0] (xs : Array α) : xs.reverse.sum = xs.sum := by
simp [ sum_toList, List.sum_reverse]
theorem sum_append_nat {as₁ as₂ : Array Nat} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
cases as₁
cases as₂
simp [List.sum_append_nat]
theorem foldl_toList_eq_flatMap {l : List α} {acc : Array β}
{F : Array β α Array β} {G : α List β}

View File

@@ -1,401 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Array.Bootstrap
public import Init.Data.Array.Lemmas
public import Init.Data.Array.DecidableEq
import Init.Data.List.MinMax
import Init.Data.List.ToArray
namespace Array
/-! ## Minima and maxima -/
/-! ### min -/
/--
Returns the smallest element of a non-empty array.
Examples:
* `#[4].min (by decide) = 4`
* `#[1, 4, 2, 10, 6].min (by decide) = 1`
-/
public protected def min [Min α] (arr : Array α) (h : arr #[]) : α :=
haveI : arr.size > 0 := by simp [Array.size_pos_iff, h]
arr.foldl min arr[0] (start := 1)
/-! ### min? -/
/--
Returns the smallest element of the array if it is not empty, or `none` if it is empty.
Examples:
* `#[].min? = none`
* `#[4].min? = some 4`
* `#[1, 4, 2, 10, 6].min? = some 1`
-/
public protected def min? [Min α] (arr : Array α) : Option α :=
if h : arr #[] then
some (arr.min h)
else
none
/-! ### max -/
/--
Returns the largest element of a non-empty array.
Examples:
* `#[4].max (by decide) = 4`
* `#[1, 4, 2, 10, 6].max (by decide) = 10`
-/
public protected def max [Max α] (arr : Array α) (h : arr #[]) : α :=
haveI : arr.size > 0 := by simp [Array.size_pos_iff, h]
arr.foldl max arr[0] (start := 1)
/-! ### max? -/
/--
Returns the largest element of the array if it is not empty, or `none` if it is empty.
Examples:
* `#[].max? = none`
* `#[4].max? = some 4`
* `#[1, 4, 2, 10, 6].max? = some 10`
-/
public protected def max? [Max α] (arr : Array α) : Option α :=
if h : arr #[] then
some (arr.max h)
else
none
/-! ### Compatibility with `List` -/
@[simp, grind =]
public theorem _root_.List.min_toArray [Min α] {l : List α} {h} :
l.toArray.min h = l.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
let h' : l [] := by simpa [List.ne_nil_iff_length_pos] using h
change l.toArray.min h = l.min h'
rw [Array.min]
· induction l
· contradiction
· rename_i x xs
simp only [List.getElem_toArray, List.getElem_cons_zero, List.size_toArray, List.length_cons]
rw [List.toArray_cons, foldl_eq_foldl_extract]
rw [ Array.foldl_toList, Array.toList_extract, List.extract_eq_drop_take]
simp [List.min]
public theorem _root_.List.min_eq_min_toArray [Min α] {l : List α} {h} :
l.min h = l.toArray.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
simp
@[simp, grind =]
public theorem min_toList [Min α] {xs : Array α} {h} :
xs.toList.min h = xs.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
cases xs; simp
public theorem min_eq_min_toList [Min α] {xs : Array α} {h} :
xs.min h = xs.toList.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
simp
@[simp, grind =]
public theorem _root_.List.min?_toArray [Min α] {l : List α} :
l.toArray.min? = l.min? := by
rw [Array.min?]
split
· simp [List.min_toArray, List.min_eq_get_min?, - List.get_min?]
· simp_all
@[simp, grind =]
public theorem min?_toList [Min α] {xs : Array α} :
xs.toList.min? = xs.min? := by
cases xs; simp
@[simp, grind =]
public theorem _root_.List.max_toArray [Max α] {l : List α} {h} :
l.toArray.max h = l.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
let h' : l [] := by simpa [List.ne_nil_iff_length_pos] using h
change l.toArray.max h = l.max h'
rw [Array.max]
· induction l
· contradiction
· rename_i x xs
simp only [List.getElem_toArray, List.getElem_cons_zero, List.size_toArray, List.length_cons]
rw [List.toArray_cons, foldl_eq_foldl_extract]
rw [ Array.foldl_toList, Array.toList_extract, List.extract_eq_drop_take]
simp [List.max]
public theorem _root_.List.max_eq_max_toArray [Max α] {l : List α} {h} :
l.max h = l.toArray.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
simp
@[simp, grind =]
public theorem max_toList [Max α] {xs : Array α} {h} :
xs.toList.max h = xs.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
cases xs; simp
public theorem max_eq_max_toList [Max α] {xs : Array α} {h} :
xs.max h = xs.toList.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
simp
@[simp, grind =]
public theorem _root_.List.max?_toArray [Max α] {l : List α} :
l.toArray.max? = l.max? := by
rw [Array.max?]
split
· simp [List.max_toArray, List.max_eq_get_max?, - List.get_max?]
· simp_all
@[simp, grind =]
public theorem max?_toList [Max α] {xs : Array α} :
xs.toList.max? = xs.max? := by
cases xs; simp
/-! ### Lemmas about `min?` -/
@[simp, grind =]
public theorem min?_empty [Min α] : (#[] : Array α).min? = none :=
(rfl)
@[simp, grind =]
public theorem min?_singleton [Min α] {x : α} : #[x].min? = some x :=
(rfl)
-- We don't put `@[simp]` on `min?_singleton_append'`,
-- because the definition in terms of `foldl` is not useful for proofs.
public theorem min?_singleton_append' [Min α] {xs : Array α} :
(#[x] ++ xs).min? = some (xs.foldl min x) := by
simp [ min?_toList, toList_append, List.min?]
@[simp]
public theorem min?_singleton_append [Min α] [Std.Associative (min : α α α)] {xs : Array α} :
(#[x] ++ xs).min? = some (xs.min?.elim x (min x)) := by
simp [ min?_toList, toList_append, List.min?_cons]
@[simp, grind =]
public theorem min?_eq_none_iff {xs : Array α} [Min α] : xs.min? = none xs = #[] := by
rcases xs with l
simp
@[simp, grind =]
public theorem isSome_min?_iff {xs : Array α} [Min α] : xs.min?.isSome xs #[] := by
rcases xs with l
simp
@[grind .]
public theorem isSome_min?_of_mem {xs : Array α} [Min α] {a : α} (h : a xs) :
xs.min?.isSome := by
rw [ min?_toList]
apply List.isSome_min?_of_mem (a := a)
simpa
public theorem isSome_min?_of_ne_empty [Min α] (xs : Array α) (h : xs #[]) : xs.min?.isSome := by
rw [ min?_toList]
apply List.isSome_min?_of_ne_nil
simpa
public theorem min?_mem [Min α] [Std.MinEqOr α] (xs : Array α) (h : xs.min? = some a) : a xs := by
rw [ min?_toList] at h
simpa using List.min?_mem h
public theorem le_min?_iff [Min α] [LE α] [Std.LawfulOrderInf α] :
{xs : Array α} xs.min? = some a {x}, x a b, b xs x b := by
intro xs h x
simp only [ min?_toList] at h
simpa using List.le_min?_iff h
public theorem min?_eq_some_iff [Min α] [LE α] {xs : Array α} [Std.IsLinearOrder α]
[Std.LawfulOrderMin α] : xs.min? = some a a xs b, b xs a b := by
rcases xs with l
simpa using List.min?_eq_some_iff
public theorem min?_replicate [Min α] [Std.IdempotentOp (min : α α α)] {n : Nat} {a : α} :
(replicate n a).min? = if n = 0 then none else some a := by
rw [ List.toArray_replicate, List.min?_toArray, List.min?_replicate]
@[simp, grind =]
public theorem min?_replicate_of_pos [Min α] [Std.MinEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).min? = some a := by
simp [min?_replicate, Nat.ne_of_gt h]
public theorem foldl_min [Min α] [Std.IdempotentOp (min : α α α)]
[Std.Associative (min : α α α)] {xs : Array α} {a : α} :
xs.foldl (init := a) min = min a (xs.min?.getD a) := by
rcases xs with l
simp [List.foldl_min]
/-! ### Lemmas about `max?` -/
@[simp, grind =]
public theorem max?_empty [Max α] : (#[] : Array α).max? = none :=
(rfl)
@[simp, grind =]
public theorem max?_singleton [Max α] {x : α} : #[x].max? = some x :=
(rfl)
-- We don't put `@[simp]` on `max?_singleton_append'`,
-- because the definition in terms of `foldl` is not useful for proofs.
public theorem max?_singleton_append' [Max α] {xs : Array α} : (#[x] ++ xs).max? = some (xs.foldl max x) := by
simp [ max?_toList, toList_append, List.max?]
@[simp]
public theorem max?_singleton_append [Max α] [Std.Associative (max : α α α)] {xs : Array α} :
(#[x] ++ xs).max? = some (xs.max?.elim x (max x)) := by
simp [ max?_toList, toList_append, List.max?_cons]
@[simp, grind =]
public theorem max?_eq_none_iff {xs : Array α} [Max α] : xs.max? = none xs = #[] := by
rcases xs with l
simp
@[simp, grind =]
public theorem isSome_max?_iff {xs : Array α} [Max α] : xs.max?.isSome xs #[] := by
rcases xs with l
simp
@[grind .]
public theorem isSome_max?_of_mem {xs : Array α} [Max α] {a : α} (h : a xs) :
xs.max?.isSome := by
rw [ max?_toList]
apply List.isSome_max?_of_mem (a := a)
simpa
public theorem isSome_max?_of_ne_empty [Max α] (xs : Array α) (h : xs #[]) : xs.max?.isSome := by
rw [ max?_toList]
apply List.isSome_max?_of_ne_nil
simpa
public theorem max?_mem [Max α] [Std.MaxEqOr α] (xs : Array α) (h : xs.max? = some a) : a xs := by
rw [ max?_toList] at h
simpa using List.max?_mem h
public theorem max?_le_iff [Max α] [LE α] [Std.LawfulOrderSup α] :
{xs : Array α} xs.max? = some a {x}, a x b, b xs b x := by
intro xs h x
simp only [ max?_toList] at h
simpa using List.max?_le_iff h
public theorem max?_eq_some_iff [Max α] [LE α] {xs : Array α} [Std.IsLinearOrder α]
[Std.LawfulOrderMax α] : xs.max? = some a a xs b, b xs b a := by
rcases xs with l
simpa using List.max?_eq_some_iff
public theorem max?_replicate [Max α] [Std.IdempotentOp (max : α α α)] {n : Nat} {a : α} :
(replicate n a).max? = if n = 0 then none else some a := by
rw [ List.toArray_replicate, List.max?_toArray, List.max?_replicate]
@[simp, grind =]
public theorem max?_replicate_of_pos [Max α] [Std.MaxEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).max? = some a := by
simp [max?_replicate, Nat.ne_of_gt h]
public theorem foldl_max [Max α] [Std.IdempotentOp (max : α α α)] [Std.Associative (max : α α α)]
{xs : Array α} {a : α} : xs.foldl (init := a) max = max a (xs.max?.getD a) := by
rcases xs with l
simp [List.foldl_max]
/-! ### Lemmas about `min` -/
@[simp, grind =]
theorem min_singleton [Min α] {x : α} :
#[x].min (ne_empty_of_size_eq_add_one rfl) = x := by
(rfl)
public theorem min?_eq_some_min [Min α] : {xs : Array α} (h : xs #[])
xs.min? = some (xs.min h)
| a::as, _ => by simp [Array.min, Array.min?]
public theorem min_eq_get_min? [Min α] : (xs : Array α) (h : xs #[])
xs.min h = xs.min?.get (xs.isSome_min?_of_ne_empty h)
| a::as, _ => by simp [Array.min, Array.min?]
@[simp, grind =]
public theorem get_min? [Min α] {xs : Array α} {h : xs.min?.isSome} :
xs.min?.get h = xs.min (isSome_min?_iff.mp h) := by
simp [min?_eq_some_min (isSome_min?_iff.mp h)]
@[grind .]
public theorem min_mem [Min α] [Std.MinEqOr α] {xs : Array α} (h : xs #[]) : xs.min h xs :=
xs.min?_mem (min?_eq_some_min h)
@[grind .]
public theorem min_le_of_mem [Min α] [LE α] [Std.IsLinearOrder α] [Std.LawfulOrderMin α]
{xs : Array α} {a : α} (ha : a xs) :
xs.min (ne_empty_of_mem ha) a :=
(Array.min?_eq_some_iff.mp (min?_eq_some_min (ne_empty_of_mem ha))).right a ha
public protected theorem le_min_iff [Min α] [LE α] [Std.LawfulOrderInf α]
{xs : Array α} (h : xs #[]) : {x}, x xs.min h b, b xs x b :=
le_min?_iff (min?_eq_some_min h)
public theorem min_eq_iff [Min α] [LE α] {xs : Array α} [Std.IsLinearOrder α] [Std.LawfulOrderMin α]
(h : xs #[]) : xs.min h = a a xs b, b xs a b := by
simpa [min?_eq_some_min h] using (min?_eq_some_iff (xs := xs))
@[simp, grind =]
public theorem min_replicate [Min α] [Std.MinEqOr α] {n : Nat} {a : α} (h : (replicate n a) #[]) :
(replicate n a).min h = a := by
have n_pos : 0 < n := by simpa [Nat.ne_zero_iff_zero_lt] using h
simpa [min?_eq_some_min h] using (min?_replicate_of_pos (a := a) n_pos)
public theorem foldl_min_eq_min [Min α] [Std.IdempotentOp (min : α α α)]
[Std.Associative (min : α α α)] {xs : Array α} (h : xs #[]) {a : α} :
xs.foldl min a = min a (xs.min h) := by
simpa [min?_eq_some_min h] using foldl_min (xs := xs)
/-! ### Lemmas about `max` -/
@[simp, grind =]
theorem max_singleton [Max α] {x : α} :
#[x].max (ne_empty_of_size_eq_add_one rfl) = x := by
(rfl)
public theorem max?_eq_some_max [Max α] : {xs : Array α} (h : xs #[])
xs.max? = some (xs.max h)
| a::as, _ => by simp [Array.max, Array.max?]
public theorem max_eq_get_max? [Max α] : (xs : Array α) (h : xs #[])
xs.max h = xs.max?.get (xs.isSome_max?_of_ne_empty h)
| a::as, _ => by simp [Array.max, Array.max?]
@[simp, grind =]
public theorem get_max? [Max α] {xs : Array α} {h : xs.max?.isSome} :
xs.max?.get h = xs.max (isSome_max?_iff.mp h) := by
simp [max?_eq_some_max (isSome_max?_iff.mp h)]
@[grind .]
public theorem max_mem [Max α] [Std.MaxEqOr α] {xs : Array α} (h : xs #[]) : xs.max h xs :=
xs.max?_mem (max?_eq_some_max h)
public protected theorem max_le_iff [Max α] [LE α] [Std.LawfulOrderSup α]
{xs : Array α} (h : xs #[]) : {x}, xs.max h x b, b xs b x :=
max?_le_iff (max?_eq_some_max h)
public theorem max_eq_iff [Max α] [LE α] {xs : Array α} [Std.IsLinearOrder α] [Std.LawfulOrderMax α]
(h : xs #[]) : xs.max h = a a xs b, b xs b a := by
simpa [max?_eq_some_max h] using (max?_eq_some_iff (xs := xs))
@[grind .]
public theorem le_max_of_mem [Max α] [LE α] [Std.IsLinearOrder α] [Std.LawfulOrderMax α]
{xs : Array α} {a : α} (ha : a xs) :
a xs.max (ne_empty_of_mem ha) :=
(Array.max?_eq_some_iff.mp (max?_eq_some_max (ne_empty_of_mem ha))).right a ha
@[simp, grind =]
public theorem max_replicate [Max α] [Std.MaxEqOr α] {n : Nat} {a : α} (h : (replicate n a) #[]) :
(replicate n a).max h = a := by
have n_pos : 0 < n := by simpa [Nat.ne_zero_iff_zero_lt] using h
simpa [max?_eq_some_max h] using (max?_replicate_of_pos (a := a) n_pos)
public theorem foldl_max_eq_max [Max α] [Std.IdempotentOp (max : α α α)]
[Std.Associative (max : α α α)] {xs : Array α} (h : xs #[]) {a : α} :
xs.foldl max a = max a (xs.max h) := by
simpa [max?_eq_some_max h] using foldl_max (xs := xs)
end Array

View File

@@ -1,39 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Graf, Paul Reichert
-/
module
prelude
public import Init.Data.Array.Lemmas
import Init.Data.List.Nat.Sum
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array
protected theorem sum_pos_iff_exists_pos_nat {xs : Array Nat} : 0 < xs.sum x xs, 0 < x := by
simp [ sum_toList, List.sum_pos_iff_exists_pos_nat]
protected theorem sum_eq_zero_iff_forall_eq_nat {xs : Array Nat} :
xs.sum = 0 x xs, x = 0 := by
simp [ sum_toList, List.sum_eq_zero_iff_forall_eq_nat]
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
theorem sum_append_nat {as₁ as₂ : Array Nat} : (as₁ ++ as₂).sum = as₁.sum + as₂.sum := by
simp [sum_append]
theorem sum_reverse_nat (xs : Array Nat) : xs.reverse.sum = xs.sum := by
simp [sum_reverse]
theorem sum_eq_foldl_nat {xs : Array Nat} : xs.sum = xs.foldl (init := 0) (· + ·) := by
simp only [foldl_eq_foldr_reverse, Nat.add_comm, sum_eq_foldr, sum_reverse_nat]
end Array

View File

@@ -53,11 +53,6 @@ theorem ofFn_succ' {f : Fin (n+1) → α} :
apply Array.toList_inj.mp
simp [List.ofFn_succ]
@[simp]
theorem ofFn_getElem {xs : Array α} :
Array.ofFn (fun i : Fin xs.size => xs[i.val]) = xs := by
ext <;> simp
@[simp]
theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by
rw [ Array.toList_inj]
@@ -72,12 +67,6 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
· rintro i, rfl
apply mem_of_getElem (i := i) <;> simp
@[simp, grind =]
theorem map_ofFn {f : Fin n α} {g : α β} :
(Array.ofFn f).map g = Array.ofFn (g f) := by
apply Array.ext_getElem?
simp [Array.getElem?_ofFn]
/-! ### ofFnM -/
/-- Construct (in a monadic context) an array by applying a monadic function to each index. -/

View File

@@ -159,17 +159,4 @@ theorem setWidth_neg_of_le {x : BitVec v} (h : w ≤ v) : BitVec.setWidth w (-x)
omega
omega
@[induction_eliminator, elab_as_elim]
theorem cons_induction {motive : (w : Nat) BitVec w Prop} (nil : motive 0 .nil)
(cons : {w : Nat} (b : Bool) (bv : BitVec w), motive w bv motive (w + 1) (.cons b bv)) :
{w : Nat} (x : BitVec w), motive w x := by
intros w x
induction w
case zero =>
simp only [BitVec.eq_nil x, nil]
case succ wl ih =>
rw [ cons_msb_setWidth x]
apply cons
apply ih
end BitVec

View File

@@ -67,9 +67,6 @@ theorem none_eq_getElem?_iff {l : BitVec w} : none = l[n]? ↔ w ≤ n := by
@[simp]
theorem getElem?_eq_none {l : BitVec w} (h : w n) : l[n]? = none := getElem?_eq_none_iff.mpr h
grind_pattern BitVec.getElem?_eq_none => l[n]? where
guard w n
theorem getElem?_eq (l : BitVec w) (i : Nat) :
l[i]? = if h : i < w then some l[i] else none := by
split <;> simp_all
@@ -3362,26 +3359,6 @@ theorem extractLsb'_concat {x : BitVec (w + 1)} {y : Bool} :
· simp
· simp [show i - 1 < t by omega]
theorem concat_extractLsb'_getLsb {x : BitVec (w + 1)} :
BitVec.concat (x.extractLsb' 1 w) (x.getLsb 0) = x := by
ext i hw
by_cases h : i = 0
· simp [h]
· simp [h, hw, show (1 + (i - 1)) = i by omega, getElem_concat]
@[elab_as_elim]
theorem concat_induction {motive : (w : Nat) BitVec w Prop} (nil : motive 0 .nil)
(concat : {w : Nat} (bv : BitVec w) (b : Bool), motive w bv motive (w + 1) (bv.concat b)) :
{w : Nat} (x : BitVec w), motive w x := by
intros w x
induction w
case zero =>
simp only [BitVec.eq_nil x, nil]
case succ wl ih =>
rw [ concat_extractLsb'_getLsb (x := x)]
apply concat
apply ih
/-! ### shiftConcat -/
@[grind =]
@@ -6403,6 +6380,73 @@ theorem cpopNatRec_add {x : BitVec w} {acc n : Nat} :
x.cpopNatRec n (acc + acc') = x.cpopNatRec n acc + acc' := by
rw [cpopNatRec_eq (acc := acc + acc'), cpopNatRec_eq (acc := acc), Nat.add_assoc]
theorem cpopNatRec_le {x : BitVec w} (n : Nat) :
x.cpopNatRec n acc acc + n := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
have : (x.getLsbD n).toNat 1 := by cases x.getLsbD n <;> simp
specialize ihn (acc := acc + (x.getLsbD n).toNat)
simp
omega
@[simp]
theorem cpopNatRec_of_le {x : BitVec w} (k n : Nat) (hn : w n) :
x.cpopNatRec (n + k) acc = x.cpopNatRec n acc := by
induction k
· case zero =>
simp
· case succ k ihk =>
simp [show n + (k + 1) = (n + k) + 1 by omega, ihk, show w n + k by omega]
theorem cpopNatRec_zero_le (x : BitVec w) (n : Nat) :
x.cpopNatRec n 0 w := by
induction n
· case zero =>
simp
· case succ n ihn =>
by_cases hle : n w
· by_cases hx : x.getLsbD n
· have := cpopNatRec_le (x := x) (acc := 1) (by omega)
have := lt_of_getLsbD hx
simp [hx]
omega
· have := cpopNatRec_le (x := x) (acc := 0) (by omega)
simp [hx]
omega
· simp [show w n by omega]
omega
@[simp]
theorem cpopNatRec_allOnes (h : n w) :
(allOnes w).cpopNatRec n acc = acc + n := by
induction n
· case zero =>
simp
· case succ n ihn =>
specialize ihn (by omega)
simp [show n < w by omega, ihn,
cpopNatRec_add (acc := acc) (acc' := 1)]
omega
@[simp]
theorem cpop_allOnes :
(allOnes w).cpop = BitVec.ofNat w w := by
simp [cpop, cpopNatRec_allOnes]
@[simp]
theorem cpop_zero :
(0#w).cpop = 0#w := by
simp [cpop]
theorem toNat_cpop_le (x : BitVec w) :
x.cpop.toNat w := by
have hlt := Nat.lt_two_pow_self (n := w)
have hle := cpopNatRec_zero_le (x := x) (n := w)
simp only [cpop, toNat_ofNat, ge_iff_le]
rw [Nat.mod_eq_of_lt (by omega)]
exact hle
@[simp]
theorem cpopNatRec_cons_of_le {x : BitVec w} {b : Bool} (hn : n w) :
@@ -6428,68 +6472,6 @@ theorem cpopNatRec_cons_of_lt {x : BitVec w} {b : Bool} (hn : w < n) :
· simp [show w = n by omega, getElem_cons,
cpopNatRec_add (acc := acc) (acc' := b.toNat), Nat.add_comm]
theorem cpopNatRec_le {x : BitVec w} (n : Nat) :
x.cpopNatRec n acc acc + n := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
have : (x.getLsbD n).toNat 1 := by cases x.getLsbD n <;> simp
specialize ihn (acc := acc + (x.getLsbD n).toNat)
simp
omega
@[simp]
theorem cpopNatRec_of_le {x : BitVec w} (k n : Nat) (hn : w n) :
x.cpopNatRec (n + k) acc = x.cpopNatRec n acc := by
induction k
· case zero =>
simp
· case succ k ihk =>
simp [show n + (k + 1) = (n + k) + 1 by omega, ihk, show w n + k by omega]
@[simp]
theorem cpopNatRec_allOnes (h : n w) :
(allOnes w).cpopNatRec n acc = acc + n := by
induction n
· case zero =>
simp
· case succ n ihn =>
specialize ihn (by omega)
simp [show n < w by omega, ihn,
cpopNatRec_add (acc := acc) (acc' := 1)]
omega
@[simp]
theorem cpop_allOnes :
(allOnes w).cpop = BitVec.ofNat w w := by
simp [cpop, cpopNatRec_allOnes]
@[simp]
theorem cpop_zero :
(0#w).cpop = 0#w := by
simp [cpop]
theorem cpopNatRec_zero_le (x : BitVec w) (n : Nat) :
x.cpopNatRec n 0 w := by
induction x
· case nil => simp
· case cons w b bv ih =>
by_cases hle : n w
· have := cpopNatRec_cons_of_le (b := b) (x := bv) (n := n) (acc := 0) hle
omega
· rw [cpopNatRec_cons_of_lt (by omega)]
have : b.toNat 1 := by cases b <;> simp
omega
theorem toNat_cpop_le (x : BitVec w) :
x.cpop.toNat w := by
have hlt := Nat.lt_two_pow_self (n := w)
have hle := cpopNatRec_zero_le (x := x) (n := w)
simp only [cpop, toNat_ofNat, ge_iff_le]
rw [Nat.mod_eq_of_lt (by omega)]
exact hle
theorem cpopNatRec_concat_of_lt {x : BitVec w} {b : Bool} (hn : 0 < n) :
(concat x b).cpopNatRec n acc = b.toNat + x.cpopNatRec (n - 1) acc := by
induction n generalizing acc
@@ -6587,12 +6569,12 @@ theorem cpop_cast (x : BitVec w) (h : w = v) :
@[simp]
theorem toNat_cpop_append {x : BitVec w} {y : BitVec u} :
(x ++ y).cpop.toNat = x.cpop.toNat + y.cpop.toNat := by
induction x generalizing y
· case nil =>
simp
· case cons w b bv ih =>
simp [cons_append, ih]
omega
induction w generalizing u
· case zero =>
simp [cpop]
· case succ w ihw =>
rw [ cons_msb_setWidth x, toNat_cpop_cons, cons_append, cpop_cast, toNat_cast,
toNat_cpop_cons, ihw, Nat.add_assoc]
theorem cpop_append {x : BitVec w} {y : BitVec u} :
(x ++ y).cpop = x.cpop.setWidth (w + u) + y.cpop.setWidth (w + u) := by
@@ -6603,14 +6585,4 @@ theorem cpop_append {x : BitVec w} {y : BitVec u} :
simp only [toNat_cpop_append, toNat_add, toNat_setWidth, Nat.add_mod_mod, Nat.mod_add_mod]
rw [Nat.mod_eq_of_lt (by omega)]
theorem toNat_cpop_not {x : BitVec w} :
(~~~x).cpop.toNat = w - x.cpop.toNat := by
induction x
· case nil =>
simp
· case cons b x ih =>
have := toNat_cpop_le x
cases b
<;> (simp [ih]; omega)
end BitVec

View File

@@ -636,7 +636,7 @@ def boolPredToPred : Coe (α → Bool) (α → Prop) where
This should not be turned on globally as an instance because it degrades performance in Mathlib,
but may be used locally.
-/
@[expose, instance_reducible] def boolRelToRel : Coe (α α Bool) (α α Prop) where
@[expose] def boolRelToRel : Coe (α α Bool) (α α Prop) where
coe r := fun a b => Eq (r a b) true
/-! ### subtypes -/

View File

@@ -286,21 +286,4 @@ theorem extract_zero_max_size {a : ByteArray} {i : Nat} : a.extract 0 (max i a.s
ext1
simp [Nat.le_max_right]
theorem append_eq_append_iff_of_size_eq_left {ws xs ys zs : ByteArray} (h : ws.size = xs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ByteArray.ext_iff] using Array.append_eq_append_iff_of_size_eq_left h
theorem append_eq_append_iff_of_size_eq_right {ws xs ys zs : ByteArray} (h : ys.size = zs.size) :
ws ++ ys = xs ++ zs ws = xs ys = zs := by
simpa [ByteArray.ext_iff] using Array.append_eq_append_iff_of_size_eq_right h
@[simp]
theorem size_push {bs : ByteArray} {b : UInt8} : (bs.push b).size = bs.size + 1 := by
rw [ByteArray.size, data_push, Array.size_push, ByteArray.size]
theorem ext_getElem {a b : ByteArray} (h₀ : a.size = b.size) (h : (i : Nat) hi hi', a[i]'hi = b[i]'hi') : a = b := by
rw [ByteArray.ext_iff]
apply Array.ext (by simpa using h₀)
simpa [ ByteArray.getElem_eq_getElem_data]
end ByteArray

View File

@@ -9,4 +9,3 @@ prelude
public import Init.Data.Char.Basic
public import Init.Data.Char.Lemmas
public import Init.Data.Char.Order
public import Init.Data.Char.Ordinal

View File

@@ -48,7 +48,6 @@ instance ltTrans : Trans (· < · : Char → Char → Prop) (· < ·) (· < ·)
trans := Char.lt_trans
-- This instance is useful while setting up instances for `String`.
@[instance_reducible]
def notLTTrans : Trans (¬ · < · : Char Char Prop) (¬ · < ·) (¬ · < ·) where
trans h₁ h₂ := by simpa using Char.le_trans (by simpa using h₂) (by simpa using h₁)

View File

@@ -1,242 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.Fin.OverflowAware
public import Init.Data.UInt.Basic
public import Init.Data.Function
import Init.Data.Char.Lemmas
import Init.Data.Char.Order
import Init.Grind
/-!
# Bijection between `Char` and `Fin Char.numCodePoints`
In this file, we construct a bijection between `Char` and `Fin Char.numCodePoints` and show that
it is compatible with various operations. Since `Fin` is simpler than `Char` due to being based
on natural numbers instead of `UInt32` and not having a hole in the middle (surrogate code points),
this is sometimes useful to simplify reasoning about `Char`.
We use these declarations in the construction of `Char` ranges, see the module
`Init.Data.Range.Polymorphic.Char`.
-/
set_option doc.verso true
public section
namespace Char
/-- The number of surrogate code points. -/
abbrev numSurrogates : Nat :=
-- 0xe000 - 0xd800
2048
/-- The size of the {name}`Char` type. -/
abbrev numCodePoints : Nat :=
-- 0x110000 - numSurrogates
1112064
/--
Packs {name}`Char` bijectively into {lean}`Fin Char.numCodePoints` by shifting code points which are
greater than the surrogate code points by the number of surrogate code points.
The inverse of this function is called {name (scope := "Init.Data.Char.Ordinal")}`Char.ofOrdinal`.
-/
def ordinal (c : Char) : Fin Char.numCodePoints :=
if h : c.val < 0xd800 then
c.val.toNat, by grind [UInt32.lt_iff_toNat_lt]
else
c.val.toNat - Char.numSurrogates, by grind [UInt32.lt_iff_toNat_lt]
/--
Unpacks {lean}`Fin Char.numCodePoints` bijectively to {name}`Char` by shifting code points which are
greater than the surrogate code points by the number of surrogate code points.
The inverse of this function is called {name}`Char.ordinal`.
-/
def ofOrdinal (f : Fin Char.numCodePoints) : Char :=
if h : (f : Nat) < 0xd800 then
UInt32.ofNatLT f (by grind), by grind [UInt32.toNat_ofNatLT]
else
UInt32.ofNatLT (f + Char.numSurrogates) (by grind), by grind [UInt32.toNat_ofNatLT]
/--
Computes the next {name}`Char`, skipping over surrogate code points (which are not valid
{name}`Char`s) as necessary.
This function is specified by its interaction with {name}`Char.ordinal`, see
{name (scope := "Init.Data.Char.Ordinal")}`Char.succ?_eq`.
-/
def succ? (c : Char) : Option Char :=
if h₀ : c.val < 0xd7ff then
some c.val + 1, by grind [UInt32.lt_iff_toNat_lt, UInt32.toNat_add]
else if h₁ : c.val = 0xd7ff then
some 0xe000, by decide
else if h₂ : c.val < 0x10ffff then
some c.val + 1, by
simp only [UInt32.lt_iff_toNat_lt, UInt32.reduceToNat, Nat.not_lt, UInt32.toNat_inj,
UInt32.isValidChar, Nat.isValidChar, UInt32.toNat_add, Nat.reducePow] at *
grind
else none
/--
Computes the {name}`m`-th next {name}`Char`, skipping over surrogate code points (which are not
valid {name}`Char`s) as necessary.
This function is specified by its interaction with {name}`Char.ordinal`, see
{name (scope := "Init.Data.Char.Ordinal")}`Char.succMany?_eq`.
-/
def succMany? (m : Nat) (c : Char) : Option Char :=
c.ordinal.addNat? m |>.map Char.ofOrdinal
@[grind =]
theorem coe_ordinal {c : Char} :
(c.ordinal : Nat) =
if c.val < 0xd800 then
c.val.toNat
else
c.val.toNat - Char.numSurrogates := by
grind [Char.ordinal]
@[simp]
theorem ordinal_zero : '\x00'.ordinal = 0 := by
ext
simp [coe_ordinal]
@[grind =]
theorem val_ofOrdinal {f : Fin Char.numCodePoints} :
(Char.ofOrdinal f).val =
if h : (f : Nat) < 0xd800 then
UInt32.ofNatLT f (by grind)
else
UInt32.ofNatLT (f + Char.numSurrogates) (by grind) := by
grind [Char.ofOrdinal]
@[simp]
theorem ofOrdinal_ordinal {c : Char} : Char.ofOrdinal c.ordinal = c := by
ext
simp only [val_ofOrdinal, coe_ordinal, UInt32.ofNatLT_add]
split
· grind [UInt32.lt_iff_toNat_lt, UInt32.ofNatLT_toNat]
· rw [dif_neg]
· simp only [ UInt32.toNat_inj, UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
grind [UInt32.toNat_lt, UInt32.lt_iff_toNat_lt]
· grind [UInt32.lt_iff_toNat_lt]
@[simp]
theorem ordinal_ofOrdinal {f : Fin Char.numCodePoints} : (Char.ofOrdinal f).ordinal = f := by
ext
simp [coe_ordinal, val_ofOrdinal]
split
· rw [if_pos, UInt32.toNat_ofNatLT]
simpa [UInt32.lt_iff_toNat_lt]
· rw [if_neg, UInt32.toNat_add, UInt32.toNat_ofNatLT, UInt32.toNat_ofNatLT, Nat.mod_eq_of_lt,
Nat.add_sub_cancel]
· grind
· simp only [UInt32.lt_iff_toNat_lt, UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow,
UInt32.reduceToNat, Nat.not_lt]
grind
@[simp]
theorem ordinal_comp_ofOrdinal : Char.ordinal Char.ofOrdinal = id := by
ext; simp
@[simp]
theorem ofOrdinal_comp_ordinal : Char.ofOrdinal Char.ordinal = id := by
ext; simp
@[simp]
theorem ordinal_inj {c d : Char} : c.ordinal = d.ordinal c = d :=
fun h => by simpa using congrArg Char.ofOrdinal h, (· rfl)
theorem ordinal_injective : Function.Injective Char.ordinal :=
fun _ _ => ordinal_inj.1
@[simp]
theorem ofOrdinal_inj {f g : Fin Char.numCodePoints} :
Char.ofOrdinal f = Char.ofOrdinal g f = g :=
fun h => by simpa using congrArg Char.ordinal h, (· rfl)
theorem ofOrdinal_injective : Function.Injective Char.ofOrdinal :=
fun _ _ => ofOrdinal_inj.1
theorem ordinal_le_of_le {c d : Char} (h : c d) : c.ordinal d.ordinal := by
simp only [le_def, UInt32.le_iff_toNat_le] at h
simp only [Fin.le_def, coe_ordinal, UInt32.lt_iff_toNat_lt, UInt32.reduceToNat]
grind
theorem ofOrdinal_le_of_le {f g : Fin Char.numCodePoints} (h : f g) :
Char.ofOrdinal f Char.ofOrdinal g := by
simp only [Fin.le_def] at h
simp only [le_def, val_ofOrdinal, UInt32.ofNatLT_add, UInt32.le_iff_toNat_le]
split
· simp only [UInt32.toNat_ofNatLT]
split
· simpa
· simp only [UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
grind
· simp only [UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
rw [dif_neg (by grind)]
simp only [UInt32.toNat_add, UInt32.toNat_ofNatLT, Nat.reducePow]
grind
theorem le_iff_ordinal_le {c d : Char} : c d c.ordinal d.ordinal :=
ordinal_le_of_le, fun h => by simpa using ofOrdinal_le_of_le h
theorem le_iff_ofOrdinal_le {f g : Fin Char.numCodePoints} :
f g Char.ofOrdinal f Char.ofOrdinal g :=
ofOrdinal_le_of_le, fun h => by simpa using ordinal_le_of_le h
theorem lt_iff_ordinal_lt {c d : Char} : c < d c.ordinal < d.ordinal := by
simp only [Std.lt_iff_le_and_not_ge, le_iff_ordinal_le]
theorem lt_iff_ofOrdinal_lt {f g : Fin Char.numCodePoints} :
f < g Char.ofOrdinal f < Char.ofOrdinal g := by
simp only [Std.lt_iff_le_and_not_ge, le_iff_ofOrdinal_le]
theorem succ?_eq {c : Char} : c.succ? = (c.ordinal.addNat? 1).map Char.ofOrdinal := by
fun_cases Char.succ? with
| case1 h =>
rw [Fin.addNat?_eq_some]
· simp only [coe_ordinal, Option.map_some, Option.some.injEq, Char.ext_iff, val_ofOrdinal,
UInt32.ofNatLT_add, UInt32.reduceOfNatLT]
split
· simp only [UInt32.ofNatLT_toNat, dite_eq_ite, left_eq_ite_iff, Nat.not_lt,
Nat.reduceLeDiff, UInt32.left_eq_add]
grind [UInt32.lt_iff_toNat_lt]
· grind
· simp [coe_ordinal]
grind [UInt32.lt_iff_toNat_lt]
| case2 =>
rw [Fin.addNat?_eq_some]
· simp [coe_ordinal, *, Char.ext_iff, val_ofOrdinal, numSurrogates]
· simp [coe_ordinal, *, numCodePoints]
| case3 =>
rw [Fin.addNat?_eq_some]
· simp only [coe_ordinal, Option.map_some, Option.some.injEq, Char.ext_iff, val_ofOrdinal,
UInt32.ofNatLT_add, UInt32.reduceOfNatLT]
split
· grind
· rw [dif_neg]
· simp only [ UInt32.toNat_inj, UInt32.toNat_add, UInt32.reduceToNat, Nat.reducePow,
UInt32.toNat_ofNatLT, Nat.mod_add_mod]
grind [UInt32.lt_iff_toNat_lt, UInt32.toNat_inj]
· grind [UInt32.lt_iff_toNat_lt, UInt32.toNat_inj]
· grind [UInt32.lt_iff_toNat_lt]
| case4 =>
rw [eq_comm]
grind [UInt32.lt_iff_toNat_lt]
theorem map_ordinal_succ? {c : Char} : c.succ?.map ordinal = c.ordinal.addNat? 1 := by
simp [succ?_eq]
theorem succMany?_eq {m : Nat} {c : Char} :
c.succMany? m = (c.ordinal.addNat? m).map Char.ofOrdinal := by
rfl
end Char

View File

@@ -11,8 +11,6 @@ public import Init.Grind.Ordered.Ring
/-! # Internal `grind` algebra instances for `Dyadic`. -/
@[expose] public section
open Lean.Grind
namespace Dyadic

View File

@@ -4,9 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Init.Data.Dyadic.Basic
import Init.Data.Dyadic.Round
import Init.Grind.Ordered.Ring
@@ -14,8 +12,6 @@ import Init.Grind.Ordered.Ring
# Inversion for dyadic numbers
-/
@[expose] public section
namespace Dyadic
/--

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Data.Dyadic.Basic
import Init.Data.Dyadic.Instances
import all Init.Data.Dyadic.Instances
import Init.Grind.Ordered.Rat
import Init.Grind.Ordered.Field

View File

@@ -11,4 +11,3 @@ public import Init.Data.Fin.Log2
public import Init.Data.Fin.Iterate
public import Init.Data.Fin.Fold
public import Init.Data.Fin.Lemmas
public import Init.Data.Fin.OverflowAware

View File

@@ -118,7 +118,7 @@ For example, for `x : Fin k` and `n : Nat`,
it causes `x < n` to be elaborated as `x < ↑n` rather than `↑x < n`,
silently introducing wraparound arithmetic.
-/
@[expose, instance_reducible]
@[expose]
def instNatCast (n : Nat) [NeZero n] : NatCast (Fin n) where
natCast a := Fin.ofNat n a
@@ -140,7 +140,7 @@ This is not a global instance, but may be activated locally via `open Fin.IntCas
See the doc-string for `Fin.NatCast.instNatCast` for more details.
-/
@[expose, instance_reducible]
@[expose]
def instIntCast (n : Nat) [NeZero n] : IntCast (Fin n) where
intCast := Fin.intCast

View File

@@ -1,51 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.Fin.Basic
import Init.Data.Fin.Lemmas
set_option doc.verso true
public section
namespace Fin
/--
Overflow-aware addition of a natural number to an element of {lean}`Fin n`.
Examples:
* {lean}`(2 : Fin 3).addNat? 1 = (none : Option (Fin 3))`
* {lean}`(2 : Fin 4).addNat? 1 = (some 3 : Option (Fin 4))`
-/
@[inline]
protected def addNat? (i : Fin n) (m : Nat) : Option (Fin n) :=
if h : i + m < n then some i + m, h else none
theorem addNat?_eq_some {i : Fin n} (h : i + m < n) : i.addNat? m = some i + m, h := by
simp [Fin.addNat?, h]
theorem addNat?_eq_some_iff {i : Fin n} :
i.addNat? m = some j i + m < n j = i + m := by
simp only [Fin.addNat?]
split <;> simp [Fin.ext_iff, eq_comm, *]
@[simp]
theorem addNat?_eq_none_iff {i : Fin n} : i.addNat? m = none n i + m := by
simp only [Fin.addNat?]
split <;> simp_all [Nat.not_lt]
@[simp]
theorem addNat?_zero {i : Fin n} : i.addNat? 0 = some i := by
simp [addNat?_eq_some_iff]
@[grind =]
theorem addNat?_eq_dif {i : Fin n} :
i.addNat? m = if h : i + m < n then some i + m, h else none := by
rfl
end Fin

View File

@@ -1153,15 +1153,6 @@ theorem ediv_le_iff_le_mul {k x y : Int} (h : 0 < k) : x / k ≤ y ↔ x < y * k
rw [Int.le_iff_lt_add_one, Int.ediv_lt_iff_lt_mul h, Int.add_mul]
omega
theorem le_mul_iff_le_left {x y z : Int} (hz : 0 < z) :
x y * z (x + z - 1) / z y := by
rw [Int.ediv_le_iff_le_mul hz]
omega
theorem le_mul_iff_le_right {x y z : Int} (hy : 0 < y) :
x y * z (x + y - 1) / y z := by
rw [ le_mul_iff_le_left hy, Int.mul_comm]
protected theorem le_mul_of_ediv_le {a b c : Int} (H1 : 0 b) (H2 : b a) (H3 : a / b c) :
a c * b := by
rw [ Int.ediv_mul_cancel H2]; exact Int.mul_le_mul_of_nonneg_right H3 H1
@@ -1215,11 +1206,6 @@ theorem add_ediv {a b c : Int} (h : c ≠ 0) :
protected theorem ediv_le_ediv {a b c : Int} (H : 0 < c) (H' : a b) : a / c b / c :=
Int.le_ediv_of_mul_le H (Int.le_trans (Int.ediv_mul_le _ (Int.ne_of_gt H)) H')
theorem ediv_add_ediv_le_add_ediv {x y z : Int} (hz : 0 < z) :
x / z + y / z (x + y) / z := by
rw [Int.le_ediv_iff_mul_le hz, Int.add_mul]
apply Int.add_le_add <;> apply Int.ediv_mul_le <;> omega
/-- If `n > 0` then `m` is not divisible by `n` iff it is between `n * k` and `n * (k + 1)`
for some `k`. -/
theorem not_dvd_iff_lt_mul_succ (m : Int) (hn : 0 < n) :
@@ -1797,12 +1783,12 @@ theorem ediv_lt_ediv_iff_of_dvd_of_neg_of_neg {a b c d : Int} (hb : b < 0) (hd :
theorem ediv_lt_ediv_of_lt {a b c : Int} (h : a < b) (hcb : c b) (hc : 0 < c) :
a / c < b / c :=
Int.lt_ediv_of_mul_lt (Int.le_of_lt hc) hcb
Int.lt_ediv_of_mul_lt (Int.le_of_lt hc) hcb
(Int.lt_of_le_of_lt (Int.ediv_mul_le _ (Int.ne_of_gt hc)) h)
theorem ediv_lt_ediv_of_lt_of_neg {a b c : Int} (h : b < a) (hca : c a) (hc : c < 0) :
a / c < b / c :=
(Int.ediv_lt_iff_of_dvd_of_neg hc hca).2
(Int.ediv_lt_iff_of_dvd_of_neg hc hca).2
(Int.lt_of_le_of_lt (Int.mul_ediv_self_le (Int.ne_of_lt hc)) h)
/-! ### `tdiv` and ordering -/

View File

@@ -113,8 +113,6 @@ theorem gcd_eq_right_iff_dvd (hb : 0 ≤ b) : gcd a b = b ↔ b a := by
theorem gcd_assoc (a b c : Int) : gcd (gcd a b) c = gcd a (gcd b c) := Nat.gcd_assoc ..
theorem gcd_left_comm (a b c : Int) : gcd a (gcd b c) = gcd b (gcd a c) := Nat.gcd_left_comm ..
theorem gcd_mul_left (m n k : Int) : gcd (m * n) (m * k) = m.natAbs * gcd n k := by
simp [gcd_eq_natAbs_gcd_natAbs, Nat.gcd_mul_left, natAbs_mul]

View File

@@ -1447,12 +1447,4 @@ instance : LawfulOrderLT Int where
lt_iff := by
simp [ Int.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance : LawfulOrderLeftLeaningMin Int where
min_eq_left _ _ := Int.min_eq_left
min_eq_right _ _ h := Int.min_eq_right (le_of_lt (not_le.1 h))
instance : LawfulOrderLeftLeaningMax Int where
max_eq_left _ _ := Int.max_eq_left
max_eq_right _ _ h := Int.max_eq_right (le_of_lt (not_le.1 h))
end Int

View File

@@ -10,7 +10,6 @@ public import Init.Classical
public import Init.Ext
set_option doc.verso true
set_option linter.missingDocs true
public section
@@ -94,9 +93,8 @@ By convention, the monadic iterator associated with an object can be obtained vi
For example, `List.iterM IO` creates an iterator over a list in the monad `IO`.
See `Init.Data.Iterators.Consumers` for ways to use an iterator. For example, `it.toList` will
convert an iterator `it` into a list and `it.ensureTermination.toList` guarantees that this
operation will terminate, given a proof that the iterator is finite.
It is also always possible to manually iterate using
convert a provably finite iterator `it` into a list and `it.allowNontermination.toList` will
do so even if finiteness cannot be proved. It is also always possible to manually iterate using
`it.step`, relying on the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
See `Iter` for a more convenient interface in case that no monadic effects are needed (`m = Id`).
@@ -140,9 +138,8 @@ By convention, the monadic iterator associated with an object can be obtained vi
For example, `List.iterM IO` creates an iterator over a list in the monad `IO`.
See `Init.Data.Iterators.Consumers` for ways to use an iterator. For example, `it.toList` will
convert an iterator `it` into a list and `it.ensureTermination.toList` guarantees that this
operation will terminate, given a proof that the iterator is finite.
It is also always possible to manually iterate using
convert a provably finite iterator `it` into a list and `it.allowNontermination.toList` will
do so even if finiteness cannot be proved. It is also always possible to manually iterate using
`it.step`, relying on the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
See `IterM` for iterators that operate in a monad.
@@ -352,24 +349,14 @@ abbrev PlausibleIterStep.casesOn {IsPlausibleStep : IterStep α β → Prop}
end IterStep
/--
The step function of an iterator in `Iter (α := α) β` or `IterM (α := α) m β`.
The typeclass providing the step function of an iterator in `Iter (α := α) β` or
`IterM (α := α) m β`.
In order to allow intrinsic termination proofs when iterating with the `step` function, the
step object is bundled with a proof that it is a "plausible" step for the given current iterator.
-/
class Iterator (α : Type w) (m : Type w Type w') (β : outParam (Type w)) where
/--
A relation that governs the allowed steps from a given iterator.
The "plausible" steps are those which make sense for a given state; plausibility can ensure
properties such as the successor iterator being drawn from the same collection, that an iterator
resulting from a skip will return the same next value, or that the next item yielded is next one
in the original collection.
-/
IsPlausibleStep : IterM (α := α) m β IterStep (IterM (α := α) m β) β Prop
/--
Carries out a step of iteration.
-/
step : (it : IterM (α := α) m β) m (Shrink <| PlausibleIterStep <| IsPlausibleStep it)
section Monadic
@@ -382,7 +369,7 @@ def IterM.mk {α : Type w} (it : α) (m : Type w → Type w') (β : Type w) :
IterM (α := α) m β :=
it
@[deprecated IterM.mk (since := "2025-12-01"), inline, expose, inherit_doc IterM.mk]
@[deprecated IterM.mk (since := "2025-12-01"), inline, expose]
def Iterators.toIterM := @IterM.mk
@[simp]
@@ -390,7 +377,6 @@ theorem IterM.mk_internalState {α m β} (it : IterM (α := α) m β) :
.mk it.internalState m β = it :=
rfl
set_option linter.missingDocs false in
@[deprecated IterM.mk_internalState (since := "2025-12-01")]
def Iterators.toIterM_internalState := @IterM.mk_internalState
@@ -473,10 +459,8 @@ number of steps.
-/
inductive IterM.IsPlausibleIndirectOutput {α β : Type w} {m : Type w Type w'} [Iterator α m β]
: IterM (α := α) m β β Prop where
/-- The output value could plausibly be emitted in the next step. -/
| direct {it : IterM (α := α) m β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
/-- The output value could plausibly be emitted in a step after the next step. -/
| indirect {it it' : IterM (α := α) m β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
@@ -486,9 +470,7 @@ finitely many steps. This relation is reflexive.
-/
inductive IterM.IsPlausibleIndirectSuccessorOf {α β : Type w} {m : Type w Type w'}
[Iterator α m β] : IterM (α := α) m β IterM (α := α) m β Prop where
/-- Every iterator is a plausible indirect successor of itself. -/
| refl (it : IterM (α := α) m β) : it.IsPlausibleIndirectSuccessorOf it
/-- The iterator is a plausible successor of one of the current iterator's successors. -/
| cons_right {it'' it' it : IterM (α := α) m β} (h' : it''.IsPlausibleIndirectSuccessorOf it')
(h : it'.IsPlausibleSuccessorOf it) : it''.IsPlausibleIndirectSuccessorOf it
@@ -613,10 +595,8 @@ number of steps.
-/
inductive Iter.IsPlausibleIndirectOutput {α β : Type w} [Iterator α Id β] :
Iter (α := α) β β Prop where
/-- The output value could plausibly be emitted in the next step. -/
| direct {it : Iter (α := α) β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
/-- The output value could plausibly be emitted in a step after the next step. -/
| indirect {it it' : Iter (α := α) β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
@@ -647,9 +627,7 @@ finitely many steps. This relation is reflexive.
-/
inductive Iter.IsPlausibleIndirectSuccessorOf {α : Type w} {β : Type w} [Iterator α Id β] :
Iter (α := α) β Iter (α := α) β Prop where
/-- Every iterator is a plausible indirect successor of itself. -/
| refl (it : Iter (α := α) β) : IsPlausibleIndirectSuccessorOf it it
/-- The iterator is a plausible indirect successor of one of the current iterator's successors. -/
| cons_right {it'' it' it : Iter (α := α) β} (h' : it''.IsPlausibleIndirectSuccessorOf it')
(h : it'.IsPlausibleSuccessorOf it) : it''.IsPlausibleIndirectSuccessorOf it
@@ -723,11 +701,6 @@ recursion over finite iterators. See also `IterM.finitelyManySteps` and `Iter.fi
-/
structure IterM.TerminationMeasures.Finite
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
The wrapped iterator.
In the wrapper, its finiteness is used as a termination measure.
-/
it : IterM (α := α) m β
/--
@@ -756,8 +729,8 @@ def IterM.finitelyManySteps {α : Type w} {m : Type w → Type w'} {β : Type w}
it
/--
Termination measure to be used in recursive functions built with `WellFounded.extrinsicFix`
recursing over a finite iterator without requiring a proof of finiteness (see also `Finite`).
Termination measure to be used in well-founded recursive functions recursing over a finite iterator
(see also `Finite`).
-/
@[expose]
def IterM.finitelyManySteps! {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
@@ -798,11 +771,6 @@ def Iter.finitelyManySteps {α : Type w} {β : Type w} [Iterator α Id β] [Iter
(it : Iter (α := α) β) : IterM.TerminationMeasures.Finite α Id :=
it.toIterM.finitelyManySteps
@[inherit_doc IterM.finitelyManySteps!, expose]
def Iter.finitelyManySteps! {α : Type w} {β : Type w} [Iterator α Id β]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Finite α Id :=
it.toIterM.finitelyManySteps!
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `IterM.finitelyManySteps`.
@@ -859,11 +827,6 @@ recursion over productive iterators. See also `IterM.finitelyManySkips` and `Ite
-/
structure IterM.TerminationMeasures.Productive
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
The wrapped iterator.
In the wrapper, its productivity is used as a termination measure.
-/
it : IterM (α := α) m β
/--
@@ -909,16 +872,6 @@ def IterM.finitelyManySkips {α : Type w} {m : Type w → Type w'} {β : Type w}
[Iterators.Productive α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
it
/--
Termination measure to be used in recursive functions built with `WellFounded.extrinsicFix`
recursing over a productive iterator without requiring a proof of productiveness
(see also `Productive`).
-/
@[expose]
def IterM.finitelyManySkips! {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
it
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `IterM.finitelyManySkips`.
@@ -939,11 +892,6 @@ def Iter.finitelyManySkips {α : Type w} {β : Type w} [Iterator α Id β] [Iter
(it : Iter (α := α) β) : IterM.TerminationMeasures.Productive α Id :=
it.toIterM.finitelyManySkips
@[inherit_doc IterM.finitelyManySkips!, expose]
def Iter.finitelyManySkips! {α : Type w} {β : Type w} [Iterator α Id β]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Productive α Id :=
it.toIterM.finitelyManySkips!
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `Iter.finitelyManySkips`.
@@ -982,63 +930,6 @@ library.
-/
class LawfulDeterministicIterator (α : Type w) (m : Type w Type w') [Iterator α m β]
where
/--
Every iterator with state `α` in monad `m` has exactly one plausible step.
-/
isPlausibleStep_eq_eq : it : IterM (α := α) m β, step, it.IsPlausibleStep = (· = step)
namespace Iterators
/--
This structure provides a more convenient way to define `Finite α m` instances using
`Finite.of_finitenessRelation : FinitenessRelation α m → Finite α m`.
-/
structure FinitenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/--
A well-founded relation such that if `it'` is a successor iterator of `it`, then `Rel it' it`.
-/
Rel (it' it : IterM (α := α) m β) : Prop
/-- `Rel` is well-founded. -/
wf : WellFounded Rel
/-- If `it'` is a successor iterator of `it`, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSuccessorOf it Rel it' it
theorem Finite.of_finitenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : FinitenessRelation α m) : Finite α m where
wf := by
refine Subrelation.wf (r := r.Rel) ?_ ?_
· intro x y h
apply FinitenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
/--
This structure provides a more convenient way to define `Productive α m` instances using
`Productive.of_productivenessRelation : ProductivenessRelation α m → Productive α m`.
-/
structure ProductivenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/--
A well-founded relation such that if `it'` is obtained from `it` by skipping, then `Rel it' it`.
-/
Rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
/-- `Rel` is well-founded. -/
wf : WellFounded Rel
/-- If `it'` is obtained from `it` by skipping, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSkipSuccessorOf it Rel it' it
theorem Productive.of_productivenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : ProductivenessRelation α m) : Productive α m where
wf := by
refine Subrelation.wf (r := r.Rel) ?_ ?_
· intro x y h
apply ProductivenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
end Std.Iterators
end Std

View File

@@ -6,6 +6,7 @@ Authors: Paul Reichert
module
prelude
public import Init.Data.Iterators.Internal.Termination
public import Init.Data.Iterators.Consumers.Loop
public section
@@ -46,7 +47,7 @@ instance Attach.instIterator {α β : Type w} {m : Type w → Type w'} [Monad m]
def Attach.instFinitenessRelation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Finite α m] {P : β Prop} :
FinitenessRelation (Attach α m P) m where
Rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySteps
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySteps
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single
@@ -67,7 +68,7 @@ instance Attach.instFinite {α β : Type w} {m : Type w → Type w'} [Monad m]
def Attach.instProductivenessRelation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Productive α m] {P : β Prop} :
ProductivenessRelation (Attach α m P) m where
Rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySkips
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySkips
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single

View File

@@ -8,6 +8,7 @@ module
prelude
public import Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.PostconditionMonad
public import Init.Data.Iterators.Internal.Termination
public section
@@ -171,7 +172,7 @@ private def FilterMap.instFinitenessRelation {α β γ : Type w} {m : Type w →
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} [Finite α m] :
FinitenessRelation (FilterMap α m n lift f) n where
Rel := InvImage IterM.IsPlausibleSuccessorOf (FilterMap.inner IterM.internalState)
rel := InvImage IterM.IsPlausibleSuccessorOf (FilterMap.inner IterM.internalState)
wf := InvImage.wf _ Finite.wf
subrelation {it it'} h := by
obtain step, h, h' := h
@@ -204,7 +205,7 @@ private def Map.instProductivenessRelation {α β γ : Type w} {m : Type w → T
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n γ} [Productive α m] :
ProductivenessRelation (Map α m n lift f) n where
Rel := InvImage IterM.IsPlausibleSkipSuccessorOf (FilterMap.inner IterM.internalState)
rel := InvImage IterM.IsPlausibleSkipSuccessorOf (FilterMap.inner IterM.internalState)
wf := InvImage.wf _ Productive.wf
subrelation {it it'} h := by
cases h

View File

@@ -277,7 +277,7 @@ theorem Flatten.rel_of_right₂ [Monad m] [Iterator α m (IterM (α := α₂) m
def Flatten.instFinitenessRelation [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Finite α₂ m] :
FinitenessRelation (Flatten α α₂ β m) m where
Rel := Rel α β m
rel := Rel α β m
wf := by
apply InvImage.wf
refine fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b
@@ -342,7 +342,7 @@ theorem Flatten.productiveRel_of_right₂ [Monad m] [Iterator α m (IterM (α :=
def Flatten.instProductivenessRelation [Monad m] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] [Finite α m] [Productive α₂ m] :
ProductivenessRelation (Flatten α α₂ β m) m where
Rel := ProductiveRel α β m
rel := ProductiveRel α β m
wf := by
apply InvImage.wf
refine fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Loop
public import Init.Data.Iterators.Internal.Termination
@[expose] public section
@@ -164,7 +165,7 @@ theorem Take.rel_of_zero_of_inner [Monad m] [Iterator α m β]
private def Take.instFinitenessRelation [Monad m] [Iterator α m β]
[Productive α m] :
FinitenessRelation (Take α m) m where
Rel := Take.Rel m
rel := Take.Rel m
wf := by
rw [Rel]
split

View File

@@ -6,6 +6,7 @@ Authors: Paul Reichert
module
prelude
public import Init.Data.Iterators.Internal.Termination
public import Init.Data.Iterators.Consumers.Monadic
public section
@@ -98,7 +99,7 @@ instance ULiftIterator.instIterator [Iterator α m β] [Monad n] :
private def ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m] [Monad n] :
FinitenessRelation (ULiftIterator α m n β lift) n where
Rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySteps)
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySteps)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation h := by
rcases h with _, hs, step, hp, rfl
@@ -114,7 +115,7 @@ instance ULiftIterator.instFinite [Iterator α m β] [Finite α m] [Monad n] :
private def ULiftIterator.instProductivenessRelation [Iterator α m β] [Productive α m] [Monad n] :
ProductivenessRelation (ULiftIterator α m n β lift) n where
Rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySkips)
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySkips)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation h := by
rcases h with step, hp, hs

View File

@@ -9,8 +9,6 @@ prelude
public import Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.Consumers.Monadic.Access
set_option linter.missingDocs true
@[expose] public section
namespace Std
@@ -21,70 +19,21 @@ If possible, takes `n` steps with the iterator `it` and
returns the `n`-th emitted value, or `none` if `it` finished
before emitting `n` values.
If the iterator is not productive, this function might run forever in an endless loop of iterator
steps. The variant `it.ensureTermination.atIdxSlow?` is guaranteed to terminate after finitely many
steps.
This function requires a `Productive` instance proving that the iterator will always emit a value
after a finite number of skips. If the iterator is not productive or such an instance is not
available, consider using `it.allowNontermination.atIdxSlow?` instead of `it.atIdxSlow?`. However,
it is not possible to formally verify the behavior of the partial variant.
-/
@[specialize]
def Iter.atIdxSlow? {α β} [Iterator α Id β]
def Iter.atIdxSlow? {α β} [Iterator α Id β] [Productive α Id]
(n : Nat) (it : Iter (α := α) β) : Option β :=
WellFounded.extrinsicFix₂ (C₂ := fun _ _ => Option β) (α := Iter (α := α) β) (β := fun _ => Nat)
(InvImage
(Prod.Lex WellFoundedRelation.rel IterM.TerminationMeasures.Productive.Rel)
(fun p => (p.2, p.1.finitelyManySkips!)))
(fun it n recur =>
match it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => recur it' k (by decreasing_tactic)
| .skip it' _ => recur it' n (by decreasing_tactic)
| .done _ => none) it n
-- We provide the functional induction principle by hand because `atIdxSlow?` is implemented using
-- `extrinsicFix₂` and not using well-founded recursion.
/-
An induction principle for `Iter.atIdxSlow?`.
This lemma provides a functional induction principle for reasoning about `Iter.atIdxSlow? n it`.
The induction follows the structure of iterator steps.
- base case: when we reach the desired index (`n = 0`) and get a `.yield` step
- inductive case: when we have a `.yield` step but need to continue (`n > 0`)
- skip case: when we encounter a `.skip` step and continue with the same index
- done case: when the iterator is exhausted and we return `none`
-/
theorem Iter.atIdxSlow?.induct_unfolding {α β : Type u} [Iterator α Id β] [Productive α Id]
(motive : Nat Iter β Option β Prop)
-- Base case: we have reached index 0 and found a value
(yield_zero : (it it' : Iter (α := α) β) (out : β) (property : it.IsPlausibleStep (IterStep.yield it' out)),
it.step = IterStep.yield it' out, property motive 0 it (some out))
-- Inductive case: we have a yield but need to continue to a higher index
(yield_succ : (it it' : Iter (α := α) β) (out : β) (property : it.IsPlausibleStep (IterStep.yield it' out)),
it.step = IterStep.yield it' out, property
(k : Nat), motive k it' (Iter.atIdxSlow? k it') motive k.succ it (Iter.atIdxSlow? k it'))
-- Skip case: we encounter a skip and continue with the same index
(skip_case : (n : Nat) (it it' : Iter β) (property : it.IsPlausibleStep (IterStep.skip it')),
it.step = IterStep.skip it', property
motive n it' (Iter.atIdxSlow? n it') motive n it (Iter.atIdxSlow? n it'))
-- Done case: the iterator is exhausted, return none
(done_case : (n : Nat) (it : Iter β) (property : it.IsPlausibleStep IterStep.done),
it.step = IterStep.done, property motive n it none)
-- The conclusion: the property holds for all indices and iterators
(n : Nat) (it : Iter β) : motive n it (Iter.atIdxSlow? n it) := by
simp only [atIdxSlow?] at *
rw [WellFounded.extrinsicFix₂_eq_apply]
· split
· split
· apply yield_zero <;> assumption
· apply yield_succ
all_goals try assumption
apply Iter.atIdxSlow?.induct_unfolding <;> assumption
· apply skip_case
all_goals try assumption
apply Iter.atIdxSlow?.induct_unfolding <;> assumption
· apply done_case <;> assumption
· exact InvImage.wf _ WellFoundedRelation.wf
match it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => it'.atIdxSlow? k
| .skip it' _ => it'.atIdxSlow? n
| .done _ => none
termination_by (n, it.finitelyManySkips)
/--
@@ -92,21 +41,22 @@ If possible, takes `n` steps with the iterator `it` and
returns the `n`-th emitted value, or `none` if `it` finished
before emitting `n` values.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `Iter.toArray`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Productive` instance, consider using `Iter.atIdxSlow?` instead.
-/
@[inline]
def Iter.Total.atIdxSlow? {α β} [Iterator α Id β] [Productive α Id]
(n : Nat) (it : Iter.Total (α := α) β) : Option β :=
it.it.atIdxSlow? n
@[inline, inherit_doc Iter.atIdxSlow?, deprecated Iter.atIdxSlow? (since := "2026-01-28")]
def Iter.Partial.atIdxSlow? {α β} [Iterator α Id β]
(n : Nat) (it : Iter.Partial (α := α) β) : Option β :=
it.it.atIdxSlow? n
@[specialize]
partial def Iter.Partial.atIdxSlow? {α β} [Iterator α Id β] [Monad Id]
(n : Nat) (it : Iter.Partial (α := α) β) : Option β := do
match it.it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => (it' : Iter.Partial (α := α) β).atIdxSlow? k
| .skip it' _ => (it' : Iter.Partial (α := α) β).atIdxSlow? n
| .done _ => none
@[always_inline, inline, inherit_doc IterM.atIdx?]
def Iter.atIdx? {α β} [Iterator α Id β] [IteratorAccess α Id]
def Iter.atIdx? {α β} [Iterator α Id β] [Productive α Id] [IteratorAccess α Id]
(n : Nat) (it : Iter (α := α) β) : Option β :=
match (IteratorAccess.nextAtIdx? it.toIterM n).run.val with
| .yield _ out => some out

View File

@@ -630,79 +630,6 @@ def Iter.Total.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id
(it : Iter.Total (α := α) β) (f : β Bool) : Option β :=
it.it.find? f
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
If the iterator is not productive, this function might run forever. The variant
`it.ensureTermination.first?` always terminates after finitely many steps.
Examples:
* `[7, 6].iter.first? = some 7`
* `[].iter.first? = none`
-/
@[inline]
def Iter.first? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Option β :=
it.toIterM.first?.run
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. The elements in `it` are examined in order of
iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `Iter.first?`.
Examples:
* `[7, 6].iter.first? = some 7`
* `[].iter.first? = none`
-/
@[inline]
def Iter.Total.first? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id] [Productive α Id]
(it : Iter.Total (α := α) β) : Option β :=
it.it.first?
/--
Returns `true` if the iterator yields no values.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
If the iterator is not productive, this function might run forever. The variant
`it.ensureTermination.isEmpty` always terminates after finitely many steps.
Examples:
* `[].iter.isEmpty = true`
* `[1].iter.isEmpty = false`
-/
@[inline]
def Iter.isEmpty {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Bool :=
it.toIterM.isEmpty.run.down
/--
Returns `true` if the iterator yields no values.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `Iter.isEmpty`.
Examples:
* `[].iter.isEmpty = true`
* `[1].iter.isEmpty = false`
-/
@[inline]
def Iter.Total.isEmpty {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id] [Productive α Id]
(it : Iter.Total (α := α) β) : Bool :=
it.it.isEmpty
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -711,15 +638,9 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose]
def Iter.length {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
def Iter.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.toIterM.length.run.down
@[inline, inherit_doc Iter.length, deprecated Iter.length (since := "2026-01-28"), expose]
def Iter.count := @Iter.length
@[inline, inherit_doc Iter.length, deprecated Iter.length (since := "2025-10-29"), expose]
def Iter.size := @Iter.length
it.toIterM.count.run.down
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -728,10 +649,22 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.length (since := "2025-12-04")]
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.count
/--
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-12-04")]
def Iter.Partial.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.it.toIterM.length.run.down
it.it.toIterM.count.run.down
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -740,9 +673,9 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.length (since := "2025-10-29")]
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.it.length
it.it.count
end Std

View File

@@ -8,8 +8,6 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std
@@ -59,8 +57,8 @@ theorem IterM.not_isPlausibleNthOutputStep_yield {α β : Type w} {m : Type w
/--
`IteratorAccess α m` provides efficient implementations for random access or iterators that support
it. `it.nextAtIdx? n` either returns the step in which the `n`th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`th
it. `it.nextAtIdx? n` either returns the step in which the `n`-th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`-th
value.
For monadic iterators, the monadic effects of this operation may differ from manually iterating
@@ -70,11 +68,6 @@ is guaranteed to plausible in the sense of `IterM.IsPlausibleNthOutputStep`.
This class is experimental and users of the iterator API should not explicitly depend on it.
-/
class IteratorAccess (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
`nextAtIdx? it n` either returns the step in which the `n`th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`th
value.
-/
nextAtIdx? (it : IterM (α := α) m β) (n : Nat) :
m (PlausibleIterStep (it.IsPlausibleNthOutputStep n))

View File

@@ -11,8 +11,6 @@ public import Init.Data.Iterators.Consumers.Monadic.Total
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.WFExtrinsicFix
set_option linter.missingDocs true
@[expose] public section
/-!

View File

@@ -11,8 +11,6 @@ public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.WFExtrinsicFix
public import Init.Data.Iterators.Consumers.Monadic.Total
set_option linter.missingDocs true
public section
/-!
@@ -72,9 +70,6 @@ provided by the standard library.
@[ext]
class IteratorLoop (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
(n : Type x Type x') where
/--
Iteration over the iterator `it` in the manner expected by `for` loops.
-/
forIn : (_liftBind : (γ : Type w) (δ : Type x) (γ n δ) m γ n δ) (γ : Type x),
(plausible_forInStep : β γ ForInStep γ Prop)
(it : IterM (α := α) m β) γ
@@ -87,9 +82,7 @@ end Typeclasses
structure IteratorLoop.WithWF (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
{γ : Type x} (PlausibleForInStep : β γ ForInStep γ Prop)
(hwf : IteratorLoop.WellFounded α m PlausibleForInStep) where
/-- Internal implementation detail of the iterator library. -/
it : IterM (α := α) m β
/-- Internal implementation detail of the iterator library. -/
acc : γ
instance IteratorLoop.WithWF.instWellFoundedRelation
@@ -170,7 +163,6 @@ Asserts that a given `IteratorLoop` instance is equal to `IteratorLoop.defaultIm
-/
class LawfulIteratorLoop (α : Type w) (m : Type w Type w') (n : Type x Type x')
[Monad m] [Monad n] [Iterator α m β] [i : IteratorLoop α m n] where
/-- The implementation of `IteratorLoop.forIn` in `i` is equal to the default implementation. -/
lawful lift [LawfulMonadLiftBindFunction lift] γ it init
(Pl : β γ ForInStep γ Prop) (wf : IteratorLoop.WellFounded α m Pl)
(f : (b : β) it.IsPlausibleIndirectOutput b (c : γ) n (Subtype (Pl b c))) :
@@ -185,8 +177,8 @@ instance instLawfulIteratorLoopDefaultImplementation (α : Type w) (m : Type w
constructor; simp
theorem IteratorLoop.wellFounded_of_finite {m : Type w Type w'}
{α β : Type w} {γ : Type x} [Iterator α m β] [Finite α m] {P : β γ ForInStep γ Prop} :
WellFounded α m (γ := γ) P := by
{α β : Type w} {γ : Type x} [Iterator α m β] [Finite α m] :
WellFounded α m (γ := γ) fun _ _ _ => True := by
apply Subrelation.wf
(r := InvImage IterM.TerminationMeasures.Finite.Rel (fun p => p.1.finitelyManySteps))
· intro p' p h
@@ -197,16 +189,6 @@ theorem IteratorLoop.wellFounded_of_finite {m : Type w → Type w'}
· apply InvImage.wf
exact WellFoundedRelation.wf
theorem IteratorLoop.wellFounded_of_productive {α β : Type w} {m : Type w Type w'}
[Iterator α m β] [IteratorLoop α m m] [Productive α m] {P : β γ ForInStep γ Prop}
(hp : {b g s}, P b g s s matches ForInStep.done ..) :
WellFounded α m (γ := γ) P := by
rw [WellFounded]
unfold IteratorLoop.rel
have {b g q} : ¬ P b g (ForInStep.yield q) := fun h => by simpa using hp h
simp only [and_false, exists_false, false_or, this]
exact Subrelation.wf And.left (InvImage.wf Prod.fst Productive.wf)
/--
This `ForIn'`-style loop construct traverses a finite iterator using an `IteratorLoop` instance.
-/
@@ -237,7 +219,6 @@ instance IterM.instForInOfIteratorLoop {m : Type w → Type w'} {n : Type w →
haveI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn'
instForInOfForIn'
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
def IterM.Partial.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n] :
@@ -245,7 +226,6 @@ def IterM.Partial.instForIn' {m : Type w → Type w'} {n : Type w → Type w''}
forIn' it init f :=
haveI := @IterM.instForIn'; forIn' it.it init f
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
def IterM.Total.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n]
@@ -912,76 +892,6 @@ def IterM.Total.find? {α β : Type w} {m : Type w → Type w'} [Monad m] [Itera
m (Option β) :=
it.it.find? f
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of `it` is examined.
If the iterator is not productive, this function might run forever. The variant
`it.ensureTermination.first?` always terminates after finitely many steps.
Examples:
* `([7, 6].iterM Id).first? = pure (some 7)`
* `([].iterM Id).first? = pure none`
-/
@[inline]
def IterM.first? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] (it : IterM (α := α) m β) : m (Option β) :=
IteratorLoop.forIn (fun _ _ => flip Bind.bind) _ (fun b _ s => s = ForInStep.done (some b)) it
none (fun b _ _ => pure ForInStep.done (some b), rfl)
/--
Returns the first output of the iterator, or `none` if no such output is found.
`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. The elements in `it` are examined in order of
iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
productive. If such a proof is not available, consider using `IterM.first?`.
Examples:
* `([7, 6].iterM Id).first? = pure (some 7)`
* `([].iterM Id).first? = pure none`
-/
@[inline]
def IterM.Total.first? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [Productive α m] (it : IterM.Total (α := α) m β) : m (Option β) :=
it.it.first?
set_option doc.verso true in
/--
Returns {lean}`ULift.up true` if the iterator {name}`it` yields no values.
{lit}`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of {name}`it` is examined.
If the iterator is not productive, this function might run forever. The variant
{lit}`it.ensureTermination.isEmpty` always terminates after finitely many steps.
-/
@[always_inline]
def IterM.isEmpty {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] (it : IterM (α := α) m β) : m (ULift Bool) :=
IteratorLoop.forIn (fun _ _ => flip Bind.bind) _ (fun _ _ s => s = ForInStep.done (.up false)) it
(.up true) (fun _ _ _ => pure ForInStep.done (.up false), rfl)
set_option doc.verso true in
/--
Returns {lean}`ULift.up true` if the iterator {name}`it` yields no values.
{lit}`O(|it|)` since the iterator may skip an unknown number of times before returning a result.
Short-circuits upon encountering the first result. Only the first element of {name}`it` is examined.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using {name}`IterM.isEmpty`.
-/
@[always_inline, inline]
def IterM.Total.isEmpty {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [Productive α m] (it : IterM.Total (α := α) m β) :
m (ULift Bool) :=
it.it.isEmpty
section Count
/--
@@ -992,16 +902,10 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline]
def IterM.length {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
def IterM.count {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM (α := α) m β) : m (ULift Nat) :=
it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
@[inline, inherit_doc IterM.length, deprecated IterM.length (since := "2026-01-28"), expose]
def IterM.count := @IterM.length
@[inline, inherit_doc IterM.length, deprecated IterM.length (since := "2025-10-29"), expose]
def IterM.size := @IterM.length
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -1009,7 +913,19 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, deprecated IterM.length (since := "2025-12-04")]
@[always_inline, inline, deprecated IterM.count (since := "2025-10-29")]
def IterM.size {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM (α := α) m β) : m (ULift Nat) :=
it.count
/--
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, deprecated IterM.count (since := "2025-12-04")]
def IterM.Partial.count {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
it.it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
@@ -1021,10 +937,10 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, deprecated IterM.length (since := "2025-10-29")]
@[always_inline, inline, deprecated IterM.Partial.count (since := "2025-10-29")]
def IterM.Partial.size {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoop α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
it.it.length
it.it.count
end Count

View File

@@ -8,8 +8,6 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std
@@ -18,9 +16,6 @@ namespace Std
A wrapper around an iterator that provides partial consumers. See `IterM.allowNontermination`.
-/
structure IterM.Partial {α : Type w} (m : Type w Type w') (β : Type w) where
/--
The wrapped iterator, which was wrapped by `IterM.allowNontermination`.
-/
it : IterM (α := α) m β
/--
@@ -29,7 +24,7 @@ consumers such as `toList`. They can be used without any proof of termination su
or `Productive`, but as they are implemented with the `partial` declaration modifier, they are
opaque for the kernel and it is impossible to prove anything about them.
-/
@[always_inline, inline, deprecated "The consumers on iterators do not require proofs of termination anymore. For example, use `it.toList` instead of `it.allowNontermination.toList`." (since := "2026-01-28")]
@[always_inline, inline]
def IterM.allowNontermination {α : Type w} {m : Type w Type w'} {β : Type w}
(it : IterM (α := α) m β) : IterM.Partial (α := α) m β :=
it

View File

@@ -9,19 +9,12 @@ prelude
public import Init.Data.Iterators.Basic
set_option doc.verso true
set_option linter.missingDocs true
public section
namespace Std
/--
A wrapper around an iterator that provides total consumers. See `IterM.ensureTermination`.
-/
structure IterM.Total {α : Type w} (m : Type w Type w') (β : Type w) where
/--
The wrapped iterator, which was wrapped by `IterM.ensureTermination`.
-/
it : IterM (α := α) m β
/--

View File

@@ -8,8 +8,6 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std
@@ -18,9 +16,6 @@ namespace Std
A wrapper around an iterator that provides partial consumers. See `Iter.allowNontermination`.
-/
structure Iter.Partial {α : Type w} (β : Type w) where
/--
The wrapped iterator, which was wrapped by `Iter.allowNontermination`.
-/
it : Iter (α := α) β
/--
@@ -29,7 +24,7 @@ consumers such as `toList`. They can be used without any proof of termination su
or `Productive`, but as they are implemented with the `partial` declaration modifier, they are
opaque for the kernel and it is impossible to prove anything about them.
-/
@[always_inline, inline, deprecated "The consumers on iterators do not require proofs of termination anymore. For example, use `it.toList` instead of `it.allowNontermination.toList`." (since := "2026-01-28")]
@[always_inline, inline]
def Iter.allowNontermination {α : Type w} {β : Type w}
(it : Iter (α := α) β) : Iter.Partial (α := α) β :=
it

View File

@@ -9,8 +9,6 @@ prelude
public import Init.Data.Stream
public import Init.Data.Iterators.Consumers.Access
set_option linter.missingDocs true
public section
namespace Std

View File

@@ -9,19 +9,12 @@ prelude
public import Init.Data.Iterators.Basic
set_option doc.verso true
set_option linter.missingDocs true
public section
namespace Std
/--
A wrapper around an iterator that provides total consumers. See `Iter.ensureTermination`.
-/
structure Iter.Total {α : Type w} (β : Type w) where
/--
The wrapped iterator, which was wrapped by `Iter.ensureTermination`.
-/
it : Iter (α := α) β
/--

View File

@@ -7,3 +7,4 @@ module
prelude
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.Data.Iterators.Internal.Termination

View File

@@ -111,11 +111,6 @@ instance {n : Type u → Type w} [Monad n] [LawfulMonad n] :
liftBind_pure := by simp
liftBind_bind := by simp
instance {m : Type u Type v} [Monad m] [LawfulMonad m] :
LawfulMonadLiftBindFunction (m := m) (n := m) (fun _ _ => flip Bind.bind) where
liftBind_pure := by simp [flip]
liftBind_bind := by simp [flip]
end LiftBind
end Std.Internal

View File

@@ -0,0 +1,63 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Iterators.Basic
public section
/-!
This is an internal module used by iterator implementations.
-/
namespace Std.Iterators
/--
Internal implementation detail of the iterator library.
The purpose of this class is that it implies a `Finite` instance but
it is more convenient to implement.
-/
structure FinitenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
wf : WellFounded rel
subrelation : {it it'}, it'.IsPlausibleSuccessorOf it rel it' it
theorem Finite.of_finitenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : FinitenessRelation α m) : Finite α m where
wf := by
refine Subrelation.wf (r := r.rel) ?_ ?_
· intro x y h
apply FinitenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
/--
Internal implementation detail of the iterator library.
The purpose of this class is that it implies a `Productive` instance but
it is more convenient to implement.
-/
structure ProductivenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
wf : WellFounded rel
subrelation : {it it'}, it'.IsPlausibleSkipSuccessorOf it rel it' it
theorem Productive.of_productivenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : ProductivenessRelation α m) : Productive α m where
wf := by
refine Subrelation.wf (r := r.rel) ?_ ?_
· intro x y h
apply ProductivenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
end Std.Iterators

View File

@@ -79,15 +79,12 @@ theorem Iter.toArray_attachWith [Iterator α Id β]
simp [Iter.toList_toArray]
@[simp]
theorem Iter.length_attachWith [Iterator α Id β]
theorem Iter.count_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorLoop α Id Id]
[LawfulIteratorLoop α Id Id] :
(it.attachWith P hP).length = it.length := by
rw [ Iter.length_toList_eq_length, toList_attachWith]
(it.attachWith P hP).count = it.count := by
rw [ Iter.length_toList_eq_count, toList_attachWith]
simp
@[deprecated Iter.length_attachWith (since := "2026-01-28")]
def Iter.count_attachWith := @Iter.length_attachWith
end Std

View File

@@ -722,14 +722,11 @@ end Fold
section Count
@[simp]
theorem Iter.length_map {α β β' : Type w} [Iterator α Id β]
theorem Iter.count_map {α β β' : Type w} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β β'} :
(it.map f).length = it.length := by
simp [map_eq_toIter_map_toIterM, length_eq_length_toIterM]
@[deprecated Iter.length_map (since := "2026-01-28")]
def Iter.count_map := @Iter.length_map
(it.map f).count = it.count := by
simp [map_eq_toIter_map_toIterM, count_eq_count_toIterM]
end Count

View File

@@ -60,15 +60,12 @@ theorem IterM.map_unattach_toArray_attachWith [Iterator α m β] [Monad m] [Mona
simp [-map_unattach_toList_attachWith, -IterM.toArray_toList]
@[simp]
theorem IterM.length_attachWith [Iterator α m β] [Monad m] [Monad n]
theorem IterM.count_attachWith [Iterator α m β] [Monad m] [Monad n]
{it : IterM (α := α) m β} {hP}
[Finite α m] [IteratorLoop α m m] [LawfulMonad m] [LawfulIteratorLoop α m m] :
(it.attachWith P hP).length = it.length := by
rw [ up_length_toList_eq_length, up_length_toList_eq_length,
(it.attachWith P hP).count = it.count := by
rw [ up_length_toList_eq_count, up_length_toList_eq_count,
map_unattach_toList_attachWith (it := it) (P := P) (hP := hP)]
simp only [Functor.map_map, List.length_unattach]
@[deprecated IterM.length_attachWith (since := "2026-01-28")]
def IterM.count_attachWith := @IterM.length_attachWith
end Std

View File

@@ -1620,21 +1620,18 @@ end Fold
section Count
@[simp]
theorem IterM.length_map {α β β' : Type w} {m : Type w Type w'} [Iterator α m β] [Monad m]
theorem IterM.count_map {α β β' : Type w} {m : Type w Type w'} [Iterator α m β] [Monad m]
[IteratorLoop α m m] [Finite α m] [LawfulMonad m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β β'} :
(it.map f).length = it.length := by
(it.map f).count = it.count := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [length_eq_match_step, length_eq_match_step, step_map, bind_assoc]
rw [count_eq_match_step, count_eq_match_step, step_map, bind_assoc]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy _]
· simp [ihs _]
· simp
@[deprecated IterM.length_map (since := "2026-01-28")]
def IterM.count_map := @IterM.length_map
end Count
section AnyAll

View File

@@ -66,14 +66,14 @@ theorem IterM.toArray_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (
simp
@[simp]
theorem IterM.length_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
theorem IterM.count_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
[MonadLiftT m (ULiftT n)] [Finite α m] [IteratorLoop α m m]
[LawfulMonad m] [LawfulMonad n] [LawfulIteratorLoop α m m]
[LawfulMonadLiftT m (ULiftT n)] :
(it.uLift n).length =
(.up ·.down.down) <$> (monadLift (n := ULiftT n) it.length).run := by
(it.uLift n).count =
(.up ·.down.down) <$> (monadLift (n := ULiftT n) it.count).run := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [length_eq_match_step, length_eq_match_step, monadLift_bind, map_eq_pure_bind, step_uLift]
rw [count_eq_match_step, count_eq_match_step, monadLift_bind, map_eq_pure_bind, step_uLift]
simp only [bind_assoc, ULiftT.run_bind]
apply bind_congr; intro step
cases step.down.inflate using PlausibleIterStep.casesOn
@@ -81,7 +81,4 @@ theorem IterM.length_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (
· simp [ihs _]
· simp
@[deprecated IterM.length_uLift (since := "2026-01-28")]
def IterM.count_uLift := @IterM.length_uLift
end Std

View File

@@ -47,18 +47,18 @@ theorem Iter.atIdxSlow?_take {α β}
[Iterator α Id β] [Productive α Id] {k l : Nat}
{it : Iter (α := α) β} :
(it.take k).atIdxSlow? l = if l < k then it.atIdxSlow? l else none := by
induction l, it using Iter.atIdxSlow?.induct_unfolding generalizing k
case yield_zero it it' out h h' =>
simp only [atIdxSlow?_eq_match (it := it.take k), step_take, h']
fun_induction it.atIdxSlow? l generalizing k
case case1 it it' out h h' =>
simp only [atIdxSlow?.eq_def (it := it.take k), step_take, h']
cases k <;> simp
case yield_succ it it' out h h' l ih =>
simp only [Nat.succ_eq_add_one, atIdxSlow?_eq_match (it := it.take k), step_take, h']
case case2 it it' out h h' l ih =>
simp only [Nat.succ_eq_add_one, atIdxSlow?.eq_def (it := it.take k), step_take, h']
cases k <;> cases l <;> simp [ih]
case skip_case l it it' h h' ih =>
simp only [atIdxSlow?_eq_match (it := it.take k), step_take, h']
case case3 l it it' h h' ih =>
simp only [atIdxSlow?.eq_def (it := it.take k), step_take, h']
cases k <;> cases l <;> simp [ih]
case done_case l it h h' =>
simp only [atIdxSlow?_eq_match (it := it.take k), step_take, h']
case case4 l it h h' =>
simp only [atIdxSlow?.eq_def (it := it.take k), step_take, h']
cases k <;> cases l <;> simp
@[simp]

View File

@@ -57,14 +57,11 @@ theorem Iter.toArray_uLift [Iterator α Id β] {it : Iter (α := α) β}
simp [-toArray_toList]
@[simp]
theorem Iter.length_uLift [Iterator α Id β] {it : Iter (α := α) β}
theorem Iter.count_uLift [Iterator α Id β] {it : Iter (α := α) β}
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id] :
it.uLift.length = it.length := by
simp only [monadLift, uLift_eq_toIter_uLift_toIterM, length_eq_length_toIterM, toIterM_toIter]
rw [IterM.length_uLift]
it.uLift.count = it.count := by
simp only [monadLift, uLift_eq_toIter_uLift_toIterM, count_eq_count_toIterM, toIterM_toIter]
rw [IterM.count_uLift]
simp [monadLift]
@[deprecated Iter.length_uLift (since := "2026-01-28")]
def Iter.count_uLift := @Iter.length_uLift
end Std

View File

@@ -9,4 +9,3 @@ prelude
public import Init.Data.Iterators.Lemmas.Consumers.Monadic
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Loop
public import Init.Data.Iterators.Lemmas.Consumers.Access

View File

@@ -1,27 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Iterators.Consumers.Access
import Init.Data.Iterators.Lemmas.Basic
namespace Std.Iter
open Std.Iterators
public theorem atIdxSlow?_eq_match [Iterator α Id β] [Productive α Id]
{n : Nat} {it : Iter (α := α) β} :
it.atIdxSlow? n =
(match it.step.val with
| .yield it' out =>
match n with
| 0 => some out
| n + 1 => it'.atIdxSlow? n
| .skip it' => it'.atIdxSlow? n
| .done => none) := by
induction n, it using Iter.atIdxSlow?.induct_unfolding <;> simp_all
end Std.Iter

View File

@@ -19,10 +19,6 @@ public section
namespace Std
open Std.Iterators
@[simp]
theorem IterM.run_toList_mk' {α : Type u} {β : Type u} [Std.Iterator α Id β] (a : α) :
(Std.IterM.mk' (m := Id) a).toList.run = (Std.Iter.mk a).toList := rfl
theorem Iter.toArray_eq_toArray_toIterM {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toArray = it.toIterM.toArray.run :=
@@ -167,14 +163,12 @@ theorem Iter.getElem?_toList_eq_atIdxSlow? {α β}
{it : Iter (α := α) β} {k : Nat} :
it.toList[k]? = it.atIdxSlow? k := by
induction it using Iter.inductSteps generalizing k with | step it ihy ihs
rw [toList_eq_match_step, atIdxSlow?, WellFounded.extrinsicFix₂_eq_apply]
· obtain step, h := it.step
cases step
· cases k <;> simp [ihy h, atIdxSlow?]
· simp [ihs h, atIdxSlow?]
· simp
· apply InvImage.wf
exact WellFoundedRelation.wf
rw [toList_eq_match_step, atIdxSlow?]
obtain step, h := it.step
cases step
· cases k <;> simp [ihy h]
· simp [ihs h]
· simp
theorem Iter.toList_eq_of_atIdxSlow?_eq {α₁ α₂ β}
[Iterator α₁ Id β] [Finite α₁ Id]

View File

@@ -460,90 +460,69 @@ theorem Iter.foldl_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [F
it.toArray.foldl (init := init) f = it.fold (init := init) f := by
rw [fold_eq_foldM, Array.foldl_eq_foldlM, Iter.foldlM_toArray]
theorem Iter.length_eq_length_toIterM {α β : Type w} [Iterator α Id β]
theorem Iter.count_eq_count_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] {it : Iter (α := α) β} :
it.length = it.toIterM.length.run.down :=
it.count = it.toIterM.count.run.down :=
(rfl)
@[deprecated Iter.length_eq_length_toIterM (since := "2026-01-28")]
def Iter.count_eq_count_toIterM := @Iter.length_eq_length_toIterM
theorem Iter.length_eq_fold {α β : Type w} [Iterator α Id β]
theorem Iter.count_eq_fold {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] [LawfulIteratorLoop α Id Id.{w}]
[IteratorLoop α Id Id.{0}] [LawfulIteratorLoop α Id Id.{0}]
{it : Iter (α := α) β} :
it.length = it.fold (γ := Nat) (init := 0) (fun acc _ => acc + 1) := by
rw [length_eq_length_toIterM, IterM.length_eq_fold, fold_eq_fold_toIterM]
it.count = it.fold (γ := Nat) (init := 0) (fun acc _ => acc + 1) := by
rw [count_eq_count_toIterM, IterM.count_eq_fold, fold_eq_fold_toIterM]
rw [ fold_hom (f := ULift.down)]
simp
@[deprecated Iter.length_eq_fold (since := "2026-01-28")]
def Iter.count_eq_fold := @Iter.length_eq_fold
theorem Iter.length_eq_forIn {α β : Type w} [Iterator α Id β]
theorem Iter.count_eq_forIn {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] [LawfulIteratorLoop α Id Id.{w}]
[IteratorLoop α Id Id.{0}] [LawfulIteratorLoop α Id Id.{0}]
{it : Iter (α := α) β} :
it.length = (ForIn.forIn (m := Id) it 0 (fun _ acc => return .yield (acc + 1))).run := by
rw [length_eq_fold, forIn_pure_yield_eq_fold, Id.run_pure]
it.count = (ForIn.forIn (m := Id) it 0 (fun _ acc => return .yield (acc + 1))).run := by
rw [count_eq_fold, forIn_pure_yield_eq_fold, Id.run_pure]
@[deprecated Iter.length_eq_forIn (since := "2026-01-28")]
def Iter.count_eq_forIn := @Iter.length_eq_forIn
theorem Iter.length_eq_match_step {α β : Type w} [Iterator α Id β]
theorem Iter.count_eq_match_step {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.length = (match it.step.val with
| .yield it' _ => it'.length + 1
| .skip it' => it'.length
it.count = (match it.step.val with
| .yield it' _ => it'.count + 1
| .skip it' => it'.count
| .done => 0) := by
simp only [length_eq_length_toIterM]
rw [IterM.length_eq_match_step]
simp only [count_eq_count_toIterM]
rw [IterM.count_eq_match_step]
simp only [bind_pure_comp, id_map', Id.run_bind, Iter.step]
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
@[deprecated Iter.length_eq_match_step (since := "2026-01-28")]
def Iter.count_eq_match_step := @Iter.length_eq_match_step
@[simp]
theorem Iter.size_toArray_eq_length {α β : Type w} [Iterator α Id β] [Finite α Id]
theorem Iter.size_toArray_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toArray.size = it.length := by
simp only [toArray_eq_toArray_toIterM, length_eq_length_toIterM, Id.run_map,
IterM.up_size_toArray_eq_length]
it.toArray.size = it.count := by
simp only [toArray_eq_toArray_toIterM, count_eq_count_toIterM, Id.run_map,
IterM.up_size_toArray_eq_count]
@[deprecated Iter.size_toArray_eq_length (since := "2025-10-29")]
def Iter.size_toArray_eq_size := @size_toArray_eq_length
@[deprecated Iter.size_toArray_eq_length (since := "2026-01-28")]
def Iter.size_toArray_eq_count := @size_toArray_eq_length
@[deprecated Iter.size_toArray_eq_count (since := "2025-10-29")]
def Iter.size_toArray_eq_size := @size_toArray_eq_count
@[simp]
theorem Iter.length_toList_eq_length {α β : Type w} [Iterator α Id β] [Finite α Id]
theorem Iter.length_toList_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toList.length = it.length := by
rw [ toList_toArray, Array.length_toList, size_toArray_eq_length]
it.toList.length = it.count := by
rw [ toList_toArray, Array.length_toList, size_toArray_eq_count]
@[deprecated Iter.length_toList_eq_length (since := "2025-10-29")]
def Iter.length_toList_eq_size := @length_toList_eq_length
@[deprecated Iter.length_toList_eq_length (since := "2026-01-28")]
def Iter.length_toList_eq_count := @length_toList_eq_length
@[deprecated Iter.length_toList_eq_count (since := "2025-10-29")]
def Iter.length_toList_eq_size := @length_toList_eq_count
@[simp]
theorem Iter.length_toListRev_eq_length {α β : Type w} [Iterator α Id β] [Finite α Id]
theorem Iter.length_toListRev_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toListRev.length = it.length := by
rw [toListRev_eq, List.length_reverse, length_toList_eq_length]
it.toListRev.length = it.count := by
rw [toListRev_eq, List.length_reverse, length_toList_eq_count]
@[deprecated Iter.length_toListRev_eq_length (since := "2025-10-29")]
def Iter.length_toListRev_eq_size := @length_toListRev_eq_length
@[deprecated Iter.length_toListRev_eq_length (since := "2026-01-28")]
def Iter.length_toListRev_eq_count := @length_toListRev_eq_length
@[deprecated Iter.length_toListRev_eq_count (since := "2025-10-29")]
def Iter.length_toListRev_eq_size := @length_toListRev_eq_count
theorem Iter.anyM_eq_forIn {α β : Type w} {m : Type Type w'} [Iterator α Id β]
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -936,50 +915,4 @@ theorem Iter.findM?_pure {α β : Type w} {m : Type w → Type w'} [Monad m]
· simp [ihs _]
· simp
theorem Iter.first?_eq_first?_toIterM {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.first? = it.toIterM.first?.run := (rfl)
theorem Iter.first?_eq_match_step {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Productive α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.first? = match it.step.val with
| .yield _ out => some out
| .skip it' => it'.first?
| .done => none := by
rw [Iter.first?_eq_first?_toIterM, IterM.first?_eq_match_step]
simp only [Id.run_bind, step]
generalize it.toIterM.step.run.inflate = s
rcases s with _|_|_, _ <;> simp [Iter.first?_eq_first?_toIterM]
@[simp, grind =]
theorem Iter.head?_toList {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.toList.head? = it.first? := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [first?_eq_match_step, toList_eq_match_step]
cases it.step using PlausibleIterStep.casesOn <;> simp [*]
theorem Iter.isEmpty_eq_isEmpty_toIterM {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.isEmpty = it.toIterM.isEmpty.run.down := (rfl)
theorem Iter.isEmpty_eq_match_step {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Productive α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.isEmpty = match it.step.val with
| .yield _ _ => false
| .skip it' => it'.isEmpty
| .done => true := by
rw [Iter.isEmpty_eq_isEmpty_toIterM, IterM.isEmpty_eq_match_step]
simp only [Id.run_bind, step]
generalize it.toIterM.step.run.inflate = s
rcases s with _|_|_, _ <;> simp [Iter.isEmpty_eq_isEmpty_toIterM]
@[simp, grind =]
theorem Iter.isEmpty_toList {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
it.toList.isEmpty = it.isEmpty := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [isEmpty_eq_match_step, toList_eq_match_step]
cases it.step using PlausibleIterStep.casesOn <;> simp [*]
end Std

View File

@@ -15,15 +15,6 @@ public section
namespace Std
open Std.Iterators
theorem IterM.DefaultConsumers.forIn_eq {α β : Type w} {m : Type w Type w'}
{n : Type x Type x'} [Monad n] [Iterator α m β]
{lift : (γ : Type w) (δ : Type x) (γ n δ) m γ n δ}
{plausible_forInStep : β γ ForInStep γ Prop} {it : IterM (α := α) m β} {init : γ}
{f : (b : β) it.IsPlausibleIndirectOutput b (c : γ) n (Subtype (plausible_forInStep b c))} :
letI : IteratorLoop α m n := .defaultImplementation
IteratorLoop.forIn lift γ plausible_forInStep it init f =
IterM.DefaultConsumers.forIn' lift γ plausible_forInStep it init _ (fun _ => id) f := rfl
theorem IterM.DefaultConsumers.forIn'_eq_match_step {α β : Type w} {m : Type w Type w'}
[Iterator α m β] {n : Type x Type x'} [Monad n] [LawfulMonad n]
{lift : γ δ, (γ n δ) m γ n δ} {γ : Type x}
@@ -476,33 +467,27 @@ theorem IterM.drain_eq_map_toArray {α β : Type w} {m : Type w → Type w'} [It
it.drain = (fun _ => .unit) <$> it.toList := by
simp [IterM.drain_eq_map_toList]
theorem IterM.length_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
theorem IterM.count_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m]
{it : IterM (α := α) m β} :
it.length = it.fold (init := .up 0) (fun acc _ => .up <| acc.down + 1) :=
it.count = it.fold (init := .up 0) (fun acc _ => .up <| acc.down + 1) :=
(rfl)
@[deprecated IterM.length_eq_fold (since := "2026-01-28")]
def IterM.count_eq_fold := @IterM.length_eq_fold
theorem IterM.length_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
theorem IterM.count_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m]
{it : IterM (α := α) m β} :
it.length = ForIn.forIn it (.up 0) (fun _ acc => return .yield (.up (acc.down + 1))) :=
it.count = ForIn.forIn it (.up 0) (fun _ acc => return .yield (.up (acc.down + 1))) :=
(rfl)
@[deprecated IterM.length_eq_forIn (since := "2026-01-28")]
def IterM.count_eq_forIn := @IterM.length_eq_forIn
theorem IterM.length_eq_match_step {α β : Type w} {m : Type w Type w'} [Iterator α m β]
theorem IterM.count_eq_match_step {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
it.length = (do
it.count = (do
match ( it.step).inflate.val with
| .yield it' _ => return .up (( it'.length).down + 1)
| .skip it' => return .up ( it'.length).down
| .yield it' _ => return .up (( it'.count).down + 1)
| .skip it' => return .up ( it'.count).down
| .done => return .up 0) := by
simp only [length_eq_fold]
simp only [count_eq_fold]
have (acc : Nat) (it' : IterM (α := α) m β) :
it'.fold (init := ULift.up acc) (fun acc _ => .up (acc.down + 1)) =
(ULift.up <| ·.down + acc) <$>
@@ -518,45 +503,33 @@ theorem IterM.length_eq_match_step {α β : Type w} {m : Type w → Type w'} [It
· simp
· simp
@[deprecated IterM.length_eq_match_step (since := "2026-01-28")]
def IterM.count_eq_match_step := @IterM.length_eq_match_step
@[simp]
theorem IterM.up_size_toArray_eq_length {α β : Type w} [Iterator α m β] [Finite α m]
theorem IterM.up_size_toArray_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.size) <$> it.toArray = it.length := by
rw [toArray_eq_fold, length_eq_fold, fold_hom]
(.up <| ·.size) <$> it.toArray = it.count := by
rw [toArray_eq_fold, count_eq_fold, fold_hom]
· simp only [List.size_toArray, List.length_nil]; rfl
· simp
@[deprecated IterM.up_size_toArray_eq_length (since := "2026-01-28")]
def IterM.up_size_toArray_eq_count := @IterM.up_size_toArray_eq_length
@[simp]
theorem IterM.up_length_toList_eq_length {α β : Type w} [Iterator α m β] [Finite α m]
theorem IterM.up_length_toList_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toList = it.length := by
rw [toList_eq_fold, length_eq_fold, fold_hom]
(.up <| ·.length) <$> it.toList = it.count := by
rw [toList_eq_fold, count_eq_fold, fold_hom]
· simp only [List.length_nil]; rfl
· simp
@[deprecated IterM.up_length_toList_eq_length (since := "2026-01-28")]
def IterM.up_length_toList_eq_count := @IterM.up_length_toList_eq_length
@[simp]
theorem IterM.up_length_toListRev_eq_length {α β : Type w} [Iterator α m β] [Finite α m]
theorem IterM.up_length_toListRev_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toListRev = it.length := by
simp only [toListRev_eq, Functor.map_map, List.length_reverse, up_length_toList_eq_length]
@[deprecated IterM.up_length_toListRev_eq_length (since := "2026-01-28")]
def IterM.up_length_toListRev_eq_count := @IterM.up_length_toListRev_eq_length
(.up <| ·.length) <$> it.toListRev = it.count := by
simp only [toListRev_eq, Functor.map_map, List.length_reverse, up_length_toList_eq_count]
theorem IterM.anyM_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
@@ -758,7 +731,7 @@ theorem IterM.findSomeM?_eq_match_step {α β γ : Type w} {m : Type w → Type
· simp
theorem IterM.findSome?_eq_findSomeM? {α β γ : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m]
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
{it : IterM (α := α) m β} {f : β Option γ} :
it.findSome? f = it.findSomeM? (pure <| f ·) :=
(rfl)
@@ -859,44 +832,4 @@ theorem IterM.findM?_pure {α β : Type w} {m : Type w → Type w'} [Monad m]
· simp [ihs _]
· simp
theorem IterM.first?_eq_match_step {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Productive α m]
[LawfulIteratorLoop α m m] {it : IterM (α := α) m β} :
it.first? = (do
match ( it.step).inflate.val with
| .yield _ out => return (some out)
| .skip it' => it'.first?
| .done => return none) := by
simp only [first?]
have := IteratorLoop.wellFounded_of_productive (α := α) (β := β) (m := m)
(P := fun b g s => s = ForInStep.done (some b)) (by simp)
simp only [LawfulIteratorLoop.lawful _ _ _ _ _ this]
rw [IterM.DefaultConsumers.forIn_eq, IterM.DefaultConsumers.forIn'_eq_match_step _ this]
simp only [flip, pure_bind]
congr
ext s
split <;> try (simp [*]; done)
simp only [DefaultConsumers.forIn_eq, *]
exact IterM.DefaultConsumers.forIn'_eq_forIn' _ this (by simp)
theorem IterM.isEmpty_eq_match_step {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Productive α m]
[LawfulIteratorLoop α m m] {it : IterM (α := α) m β} :
it.isEmpty = (do
match ( it.step).inflate.val with
| .yield _ _ => return .up false
| .skip it' => it'.isEmpty
| .done => return .up true) := by
simp only [isEmpty]
have := IteratorLoop.wellFounded_of_productive (α := α) (β := β) (m := m)
(P := fun _ _ s => s = ForInStep.done (ULift.up false)) (by simp)
simp only [LawfulIteratorLoop.lawful _ _ _ _ _ this]
rw [IterM.DefaultConsumers.forIn_eq, IterM.DefaultConsumers.forIn'_eq_match_step _ this]
simp only [flip, pure_bind]
congr
ext s
split <;> try (simp [*]; done)
simp only [DefaultConsumers.forIn_eq, *]
exact IterM.DefaultConsumers.forIn'_eq_forIn' _ this (by simp)
end Std

View File

@@ -72,7 +72,7 @@ def PostconditionT.liftWithProperty {α : Type w} {m : Type w → Type w'} {P :
P, x
/--
Given a function `f : α → β`, returns a function `PostconditionT m α → PostconditionT m β`,
Given a function `f : α → β`, returns a a function `PostconditionT m α → PostconditionT m β`,
turning `PostconditionT m` into a functor.
The postcondition of the `x.map f` states that the return value is the image under `f` of some
@@ -85,7 +85,7 @@ protected def PostconditionT.map {m : Type w → Type w'} [Functor m] {α : Type
(fun a => f a.val, _, rfl) <$> x.operation
/--
Given a function `α → PostconditionT m β`, returns a function
Given a function `α → PostconditionT m β`, returns a a function
`PostconditionT m α → PostconditionT m β`, turning `PostconditionT m` into a monad.
-/
@[always_inline, inline, expose]
@@ -287,12 +287,6 @@ theorem PostconditionT.run_attachLift {m : Type w → Type w'} [Monad m] [MonadA
{x : m α} : (attachLift x).run = x := by
simp [attachLift, run_eq_map, WeaklyLawfulMonadAttach.map_attach]
@[simp]
theorem PostconditionT.operation_attachLift {m : Type w Type w'} [Monad m] [MonadAttach m]
{α : Type w} {x : m α} : (attachLift x : PostconditionT m α).operation =
MonadAttach.attach x := by
rfl
instance {m : Type w Type w'} {n : Type w Type w''} [MonadLift m n] :
MonadLift (PostconditionT m) (PostconditionT n) where
monadLift x := _, monadLift x.operation

View File

@@ -7,6 +7,7 @@ module
prelude
public import Init.Data.Iterators.Consumers
public import Init.Data.Iterators.Internal.Termination
@[expose] public section
@@ -64,7 +65,7 @@ instance ListIterator.instIterator {α : Type w} [Pure m] : Iterator (ListIterat
private def ListIterator.instFinitenessRelation [Pure m] :
FinitenessRelation (ListIterator α) m where
Rel := InvImage WellFoundedRelation.rel (ListIterator.list IterM.internalState)
rel := InvImage WellFoundedRelation.rel (ListIterator.list IterM.internalState)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
simp_wf

View File

@@ -11,7 +11,7 @@ public import Init.Core
public section
/--
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that `a == b` implies
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
`hash a = hash b`.
This is automatic if the `BEq` instance is lawful.

View File

@@ -16,11 +16,8 @@ public import Init.Data.List.Find
public import Init.Data.List.Impl
public import Init.Data.List.Lemmas
public import Init.Data.List.MinMax
public import Init.Data.List.MinMaxIdx
public import Init.Data.List.MinMaxOn
public import Init.Data.List.Monadic
public import Init.Data.List.Nat
public import Init.Data.List.Int
public import Init.Data.List.Notation
public import Init.Data.List.Pairwise
public import Init.Data.List.Sublist

View File

@@ -169,10 +169,10 @@ Examples:
| a::as, b::bs, eqv => eqv a b && isEqv as bs eqv
| _, _, _ => false
@[simp, grind =] theorem isEqv_nil_nil : isEqv ([] : List α) [] eqv = true := rfl
@[simp, grind =] theorem isEqv_nil_cons : isEqv ([] : List α) (a::as) eqv = false := rfl
@[simp, grind =] theorem isEqv_cons_nil : isEqv (a::as : List α) [] eqv = false := rfl
@[grind =] theorem isEqv_cons₂ : isEqv (a::as) (b::bs) eqv = (eqv a b && isEqv as bs eqv) := rfl
@[simp] theorem isEqv_nil_nil : isEqv ([] : List α) [] eqv = true := rfl
@[simp] theorem isEqv_nil_cons : isEqv ([] : List α) (a::as) eqv = false := rfl
@[simp] theorem isEqv_cons_nil : isEqv (a::as : List α) [] eqv = false := rfl
theorem isEqv_cons₂ : isEqv (a::as) (b::bs) eqv = (eqv a b && isEqv as bs eqv) := rfl
/-! ## Lexicographic ordering -/
@@ -717,7 +717,6 @@ Examples:
* `["red", "green", "blue"].leftpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].leftpad 1 "blank" = ["red", "green", "blue"]`
-/
@[simp, grind =]
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
@@ -731,7 +730,6 @@ Examples:
* `["red", "green", "blue"].rightpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].rightpad 1 "blank" = ["red", "green", "blue"]`
-/
@[simp, grind =]
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
/-! ### reduceOption -/
@@ -2017,7 +2015,6 @@ def sum {α} [Add α] [Zero α] : List αα :=
@[simp, grind =] theorem sum_nil [Add α] [Zero α] : ([] : List α).sum = 0 := rfl
@[simp, grind =] theorem sum_cons [Add α] [Zero α] {a : α} {l : List α} : (a::l).sum = a + l.sum := rfl
theorem sum_eq_foldr [Add α] [Zero α] {l : List α} : l.sum = l.foldr (· + ·) 0 := rfl
/-! ### range -/

View File

@@ -50,7 +50,7 @@ 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
results.
This implementation is tail recursive. `List.mapM'` is a non-tail-recursive variant that may be
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
more convenient to reason about. `List.forM` is the variant that discards the results and
`List.mapA` is the variant that works with `Applicative`.
-/
@@ -107,7 +107,7 @@ Applies the monadic action `f` to the corresponding elements of two lists, left-
at the end of the shorter list. `zipWithM f as bs` is equivalent to `mapM id (zipWith f as bs)`
for lawful `Monad` instances.
This implementation is tail recursive. `List.zipWithM'` is a non-tail-recursive variant that may
This implementation is tail recursive. `List.zipWithM'` is a a non-tail-recursive variant that may
be more convenient to reason about.
-/
@[inline, expose]

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.List.Sublist
import Init.Grind.Util
public section
@@ -98,18 +97,6 @@ theorem countP_le_length : countP p l ≤ l.length := by
@[simp] theorem countP_eq_zero {p} : countP p l = 0 a l, ¬p a := by
simp only [countP_eq_length_filter, length_eq_zero_iff, filter_eq_nil_iff]
/-- This lemma is only relevant for `grind`. -/
@[grind =]
theorem _root_.Std.Internal.List.countP_eq_zero_of_forall {xs : List α} (h : x xs, ¬ p x) : xs.countP p = 0 :=
countP_eq_zero.mpr h
/-- This lemma is only relevant for `grind`. -/
theorem _root_.Std.Internal.List.not_of_countP_eq_zero_of_mem {xs : List α} (h : xs.countP p = 0) (h' : x xs) : ¬ p x :=
countP_eq_zero.mp h _ h'
grind_pattern Std.Internal.List.not_of_countP_eq_zero_of_mem => xs.countP p, x xs where
guard xs.countP p = 0
@[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]

View File

@@ -1077,37 +1077,6 @@ theorem isNone_findFinIdx? {l : List α} {p : α → Bool} :
simp [hf, findFinIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### find? and findFinIdx? -/
theorem find?_eq_map_findFinIdx?_getElem {xs : List α} {p : α Bool} :
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findFinIdx?_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih, Function.comp_def]
theorem find?_eq_bind_findIdx?_getElem? {xs : List α} {p : α Bool} :
xs.find? p = (xs.findIdx? p).bind (xs[·]?) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findIdx?_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih, Option.bind_map, Function.comp_def]
theorem find?_eq_getElem?_findIdx {xs : List α} {p : α Bool} :
xs.find? p = xs[xs.findIdx p]? := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findIdx_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih]
/-! ### idxOf
@@ -1134,6 +1103,8 @@ theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
· rw [if_neg]
simpa using h
theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a l) : l.idxOf a = l.length := by
induction l with
| nil => rfl
@@ -1142,6 +1113,8 @@ theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.
simp only [idxOf_cons, cond_eq_ite, beq_iff_eq]
split <;> simp_all
theorem idxOf_lt_length_of_mem [BEq α] [EquivBEq α] {l : List α} (h : a l) : l.idxOf a < l.length := by
induction l with
| nil => simp at h
@@ -1170,6 +1143,8 @@ theorem idxOf_lt_length_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
grind_pattern idxOf_lt_length_iff => l.idxOf a, l.length
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
@@ -1239,9 +1214,7 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
· rintro w x h rfl
contradiction
theorem idxOf?_eq_some_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} {i : Nat} :
l.idxOf? a = some i (h : i < l.length), l[i] = a j (_ : j < i), ¬l[j] = a := by
simp [idxOf?, findIdx?_eq_some_iff_getElem]
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
@@ -1257,56 +1230,6 @@ theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isNone = ¬ a l := by
simp
theorem finIdxOf?_eq_pmap_idxOf? {l : List α} {a : α} [BEq α] [LawfulBEq α] :
l.finIdxOf? a =
(l.idxOf? a).pmap
(fun i h => i, (idxOf?_eq_some_iff.mp h).1)
(fun _ h => h) := by
ext i, h
simp only [finIdxOf?_eq_some_iff, Fin.getElem_fin, Fin.forall_iff, Fin.mk_lt_mk,
idxOf?_eq_some_iff, Option.pmap_eq_some_iff, Fin.mk.injEq, exists_and_left, exists_prop,
and_self_left, exists_eq_right', h, exists_true_left, and_congr_right_iff]
intro w
constructor
· intro w j h₁
apply w <;> omega
· intro w j h₁ h₂
apply w <;> omega
/-! ### find? and idxOf? -/
theorem findIdx?_eq_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α Bool} :
xs.findIdx? p = (xs.find? p).bind (xs.idxOf?) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx?_cons, find?_cons]
split <;> rename_i h
· simp [h, idxOf?_cons]
· simp [h, ih, Function.comp_def, idxOf?_cons]
cases w : xs.find? p with
| none => simp
| some x' =>
simp
rintro rfl
have := find?_some w
contradiction
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α Bool} :
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf?) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_eq_bind_find?_idxOf?, finIdxOf?_eq_pmap_idxOf?]
ext i
simp only [Option.bind_eq_some_iff, Option.pmap_eq_some_iff, exists_and_left, and_exists_self]
constructor
· rintro a, h₁, h₂, rfl
exact h₁, by simp [h₂]
· rintro h₁, h₂, a, h₃, rfl
exact a, h₁, h₂, h₃, rfl
theorem findIdx_eq_getD_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α Bool} :
xs.findIdx p = ((xs.find? p).bind (xs.idxOf?)).getD xs.length := by
rw [findIdx_eq_getD_findIdx?, findIdx?_eq_bind_find?_idxOf?]
/-! ### lookup -/
section lookup

View File

@@ -1,9 +0,0 @@
/-
Copyright (c) 2026 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.List.Int.Sum

View File

@@ -1,107 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Graf, Paul Reichert
-/
module
prelude
public import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.DivMod.Lemmas
import Init.Data.List.MinMax
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
@[simp]
theorem sum_replicate_int {n : Nat} {a : Int} : (replicate n a).sum = n * a := by
induction n <;> simp_all [replicate_succ, Int.add_mul, Int.add_comm]
theorem sum_append_int {l₁ l₂ : List Int} : (l₁ ++ l₂).sum = l₁.sum + l₂.sum := by
simp [sum_append]
theorem sum_reverse_int (xs : List Int) : xs.reverse.sum = xs.sum := by
simp [sum_reverse]
theorem min_mul_length_le_sum_int {xs : List Int} (h : xs []) :
xs.min h * xs.length xs.sum := by
induction xs
· contradiction
· rename_i x xs ih
cases xs
· simp_all [List.min_singleton]
· simp only [ne_eq, reduceCtorEq, not_false_eq_true, min_eq_get_min?,
List.min?_cons (α := Int), Option.get_some, length_cons, Int.natCast_add, Int.cast_ofNat_Int,
forall_const] at ih
rw [Int.mul_add, Int.mul_one, Int.add_comm]
apply Int.add_le_add
· apply Int.min_le_left
· refine Int.le_trans ?_ ih
rw [Int.mul_le_mul_right (by omega)]
apply Int.min_le_right
theorem mul_length_le_sum_of_min?_eq_some_int {xs : List Int} (h : xs.min? = some x) :
x * xs.length xs.sum := by
cases xs
· simp_all
· simp only [min?_eq_some_min (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using min_mul_length_le_sum_int _
theorem min_le_sum_div_length_int {xs : List Int} (h : xs []) :
xs.min h xs.sum / xs.length := by
have := min_mul_length_le_sum_int h
rwa [Int.le_ediv_iff_mul_le]
simp [List.length_pos_iff, h]
theorem le_sum_div_length_of_min?_eq_some_int {xs : List Int} (h : xs.min? = some x) :
x xs.sum / xs.length := by
cases xs
· simp_all
· simp only [min?_eq_some_min (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using min_le_sum_div_length_int _
theorem sum_le_max_mul_length_int {xs : List Int} (h : xs []) :
xs.sum xs.max h * xs.length := by
induction xs
· contradiction
· rename_i x xs ih
cases xs
· simp_all [List.max_singleton]
· simp only [ne_eq, reduceCtorEq, not_false_eq_true, max_eq_get_max?,
List.max?_cons (α := Int), Option.get_some, length_cons, Int.natCast_add, Int.cast_ofNat_Int,
forall_const] at ih
rw [Int.mul_add, Int.mul_one, Int.add_comm]
apply Int.add_le_add
· apply Int.le_max_left
· refine Int.le_trans ih ?_
rw [Int.mul_le_mul_right (by omega)]
apply Int.le_max_right
theorem sum_le_max_mul_length_of_max?_eq_some_int {xs : List Int} (h : xs.max? = some x) :
xs.sum x * xs.length := by
cases xs
· simp_all
· simp only [max?_eq_some_max (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using sum_le_max_mul_length_int _
theorem sum_div_length_le_max_int {xs : List Int} (h : xs []) :
xs.sum / xs.length xs.max h := by
have := sum_le_max_mul_length_int h
rw [Int.ediv_le_iff_le_mul]
· refine Int.lt_of_le_of_lt this ?_
apply Int.lt_add_of_pos_right
simp [ Nat.ne_zero_iff_zero_lt, h]
· simp [List.length_pos_iff, h]
theorem sum_div_length_le_max_of_max?_eq_some_int {xs : List Int} (h : xs.max? = some x) :
xs.sum / xs.length x := by
cases xs
· simp_all
· simp only [max?_eq_some_max (cons_ne_nil _ _), Option.some.injEq] at h
simpa [ h] using sum_div_length_le_max_int _
end List

View File

@@ -482,28 +482,25 @@ theorem mem_iff_getElem {a} {l : List α} : a ∈ l ↔ ∃ (i : Nat) (h : i < l
theorem mem_iff_getElem? {a} {l : List α} : a l i : Nat, l[i]? = some a := by
simp [getElem?_eq_some_iff, mem_iff_getElem]
theorem exists_mem_iff_exists_getElem {P : α Prop} {l : List α} :
( x l, P x) (i : Nat), hi, P (l[i]) := by
simp only [mem_iff_getElem]
apply Iff.intro
· rintro _, i, hi, rfl, hP
exact i, hi, hP
· rintro i, hi, hP
exact _, i, hi, rfl, hP
theorem forall_mem_iff_forall_getElem {P : α Prop} {l : List α} :
( x l, P x) (i : Nat) hi, P (l[i]) := by
simp only [mem_iff_getElem]
apply Iff.intro
· intro h i hi
exact h l[i] i, hi, rfl
· rintro h _ i, hi, rfl
exact h i hi
@[deprecated forall_mem_iff_forall_getElem (since := "2026-01-29")]
theorem forall_getElem {l : List α} {p : α Prop} :
( (i : Nat) h, p (l[i]'h)) a, a l p a :=
forall_mem_iff_forall_getElem.symm
( (i : Nat) h, p (l[i]'h)) a, a l p a := by
induction l with
| nil => simp
| cons a l ih =>
simp only [length_cons, mem_cons, forall_eq_or_imp]
constructor
· intro w
constructor
· exact w 0 (by simp)
· apply ih.1
intro n h
simpa using w (n+1) (Nat.add_lt_add_right h 1)
· rintro h, w
rintro (_ | n) h
· simpa
· apply w
simp only [getElem_cons_succ]
exact getElem_mem (lt_of_succ_lt_succ h)
@[simp] theorem elem_eq_contains [BEq α] {a : α} {l : List α} :
elem a l = l.contains a := by
@@ -1830,17 +1827,12 @@ theorem append_eq_map_iff {f : α → β} :
rw [eq_comm, map_eq_append_iff]
@[simp, grind =]
theorem sum_append [Add α] [Zero α] [Std.LawfulLeftIdentity (α := α) (· + ·) 0]
[Std.Associative (α := α) (· + ·)] {l₁ l₂ : List α} : (l ++ l₂).sum = l₁.sum + l₂.sum := by
induction l₁ generalizing l₂ <;> simp_all [Std.Associative.assoc, Std.LawfulLeftIdentity.left_id]
theorem sum_append_nat {l₁ l₂ : List Nat} : (l₁ ++ l₂).sum = l₁.sum + l₂.sum := by
induction l₁ generalizing l <;> simp_all [Nat.add_assoc]
@[simp, grind =]
theorem sum_reverse [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
[Std.Commutative (α := α) (· + ·)]
[Std.LawfulLeftIdentity (α := α) (· + ·) 0] (xs : List α) : xs.reverse.sum = xs.sum := by
induction xs <;>
simp_all [sum_append, Std.Commutative.comm (α := α) _ 0,
Std.LawfulLeftIdentity.left_id, Std.Commutative.comm]
theorem sum_reverse_nat (xs : List Nat) : xs.reverse.sum = xs.sum := by
induction xs <;> simp_all [Nat.add_comm]
/-! ### concat
@@ -2371,6 +2363,9 @@ theorem replicateRecOn {α : Type _} {p : List α → Prop} (l : List α)
exact hi _ _ _ _ h hn (replicateRecOn (b :: l') h0 hr hi)
termination_by l.length
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
induction n <;> simp_all [replicate_succ, Nat.add_mul, Nat.add_comm]
/-! ### reverse -/
@[simp, grind =] theorem length_reverse {as : List α} : (as.reverse).length = as.length := by
@@ -2946,6 +2941,9 @@ theorem getLast?_replicate {a : α} {n : Nat} : (replicate n a).getLast? = if n
/-! ### leftpad -/
-- We unfold `leftpad` and `rightpad` for verification purposes.
attribute [simp, grind =] leftpad rightpad
-- `length_leftpad` and `length_rightpad` are in `Init.Data.List.Nat.Basic`.
theorem leftpad_prefix {n : Nat} {a : α} {l : List α} :

View File

@@ -85,7 +85,7 @@ theorem cons_lex_cons_iff : Lex r (a :: l₁) (b :: l₂) ↔ r a b a = b
theorem cons_lt_cons_iff [LT α] {a b} {l₁ l₂ : List α} :
(a :: l₁) < (b :: l₂) a < b a = b l₁ < l₂ := by
simp only [LT.lt, List.lt]
dsimp only [instLT, List.lt]
simp [cons_lex_cons_iff]
@[simp] theorem cons_lt_cons_self [LT α] [i₀ : Std.Irrefl (· < · : α α Prop)] {l₁ l₂ : List α} :
@@ -101,7 +101,7 @@ theorem cons_le_cons_iff [LT α]
[i₂ : Std.Trichotomous (· < · : α α Prop)]
{a b} {l₁ l₂ : List α} :
(a :: l₁) (b :: l₂) a < b a = b l₁ l₂ := by
simp only [LE.le, LT.lt, List.le, List.lt]
dsimp only [instLE, instLT, List.le, List.lt]
open Classical in
simp only [not_cons_lex_cons_iff, ne_eq]
constructor

View File

@@ -29,11 +29,7 @@ open Nat
/-! ### min? -/
@[simp, grind =] theorem min?_nil [Min α] : ([] : List α).min? = none := rfl
@[simp, grind =]
public theorem min?_singleton [Min α] {x : α} : [x].min? = some x :=
(rfl)
@[simp] theorem min?_nil [Min α] : ([] : List α).min? = none := rfl
-- We don't put `@[simp]` on `min?_cons'`,
-- because the definition in terms of `foldl` is not useful for proofs.
@@ -43,20 +39,15 @@ theorem min?_cons' [Min α] {xs : List α} : (x :: xs).min? = some (foldl min x
(x :: xs).min? = some (xs.min?.elim x (min x)) := by
cases xs <;> simp [min?_cons', foldl_assoc]
@[simp, grind =] theorem min?_eq_none_iff {xs : List α} [Min α] : xs.min? = none xs = [] := by
@[simp] theorem min?_eq_none_iff {xs : List α} [Min α] : xs.min? = none xs = [] := by
cases xs <;> simp [min?]
@[simp, grind =]
public theorem isSome_min?_iff [Min α] {xs : List α} : xs.min?.isSome xs [] := by
cases xs <;> simp [min?]
@[grind .]
theorem isSome_min?_of_mem {l : List α} [Min α] {a : α} (h : a l) :
l.min?.isSome := by
cases l <;> simp_all [min?_cons']
theorem isSome_min?_of_ne_nil [Min α] {l : List α} (hl : l []) : l.min?.isSome := by
rwa [isSome_min?_iff]
theorem isSome_min?_of_ne_nil [Min α] : {l : List α} (hl : l []) l.min?.isSome
| x::xs, h => by simp [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
@@ -152,8 +143,7 @@ theorem min?_replicate [Min α] [Std.IdempotentOp (min : ααα)] {n :
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons', Std.IdempotentOp.idempotent]
@[simp, grind =]
theorem min?_replicate_of_pos [Min α] [MinEqOr α] {n : Nat} {a : α} (h : 0 < n) :
@[simp] theorem min?_replicate_of_pos [Min α] [MinEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).min? = some a := by
simp [min?_replicate, Nat.ne_of_gt h]
@@ -170,11 +160,6 @@ theorem foldl_min [Min α] [Std.IdempotentOp (min : ααα)] [Std.Asso
/-! ### min -/
@[simp, grind =]
theorem min_singleton [Min α] {x : α} :
[x].min (cons_ne_nil _ _) = x := by
(rfl)
theorem min?_eq_some_min [Min α] : {l : List α} (hl : l [])
l.min? = some (l.min hl)
| a::as, _ => by simp [List.min, List.min?_cons']
@@ -183,22 +168,15 @@ theorem min_eq_get_min? [Min α] : (l : List α) → (hl : l ≠ []) →
l.min hl = l.min?.get (isSome_min?_of_ne_nil hl)
| a::as, _ => by simp [List.min, List.min?_cons']
@[simp, grind =]
theorem get_min? [Min α] {l : List α} {h : l.min?.isSome} :
l.min?.get h = l.min (isSome_min?_iff.mp h) := by
simp [min?_eq_some_min (isSome_min?_iff.mp h)]
theorem min_eq_head {α : Type u} [Min α] {l : List α} (hl : l [])
(h : l.Pairwise (fun a b => min a b = a)) : l.min hl = l.head hl := by
apply Option.some.inj
rw [ min?_eq_some_min, head?_eq_some_head]
exact min?_eq_head? h
@[grind .]
theorem min_mem [Min α] [MinEqOr α] {l : List α} (hl : l []) : l.min hl l :=
min?_mem (min?_eq_some_min hl)
@[grind .]
theorem min_le_of_mem [Min α] [LE α] [Std.IsLinearOrder α] [Std.LawfulOrderMin α]
{l : List α} {a : α} (ha : a l) :
l.min (ne_nil_of_mem ha) a :=
@@ -212,7 +190,7 @@ theorem min_eq_iff [Min α] [LE α] {l : List α} [IsLinearOrder α] [LawfulOrde
l.min hl = a a l b, b l a b := by
simpa [min?_eq_some_min hl] using (min?_eq_some_iff (xs := l))
@[simp, grind =] theorem min_replicate [Min α] [MinEqOr α] {n : Nat} {a : α} (h : replicate n a []) :
@[simp] theorem min_replicate [Min α] [MinEqOr α] {n : Nat} {a : α} (h : replicate n a []) :
(replicate n a).min h = a := by
have n_pos : 0 < n := Nat.pos_of_ne_zero (fun hn => by simp [hn] at h)
simpa [min?_eq_some_min h] using (min?_replicate_of_pos (a := a) n_pos)
@@ -224,11 +202,7 @@ theorem foldl_min_eq_min [Min α] [Std.IdempotentOp (min : ααα)] [S
/-! ### max? -/
@[simp, grind =] theorem max?_nil [Max α] : ([] : List α).max? = none := rfl
@[simp, grind =]
public theorem max?_singleton [Max α] {x : α} : [x].max? = some x :=
(rfl)
@[simp] theorem max?_nil [Max α] : ([] : List α).max? = none := rfl
-- We don't put `@[simp]` on `max?_cons'`,
-- because the definition in terms of `foldl` is not useful for proofs.
@@ -238,20 +212,15 @@ theorem max?_cons' [Max α] {xs : List α} : (x :: xs).max? = some (foldl max x
(x :: xs).max? = some (xs.max?.elim x (max x)) := by
cases xs <;> simp [max?_cons', foldl_assoc]
@[simp, grind =] theorem max?_eq_none_iff {xs : List α} [Max α] : xs.max? = none xs = [] := by
@[simp] theorem max?_eq_none_iff {xs : List α} [Max α] : xs.max? = none xs = [] := by
cases xs <;> simp [max?]
@[simp, grind =]
public theorem isSome_max?_iff [Max α] {xs : List α} : xs.max?.isSome xs [] := by
cases xs <;> simp [max?]
@[grind .]
theorem isSome_max?_of_mem {l : List α} [Max α] {a : α} (h : a l) :
l.max?.isSome := by
cases l <;> simp_all [max?_cons']
theorem isSome_max?_of_ne_nil [Max α] {l : List α} (hl : l []) : l.max?.isSome := by
rwa [isSome_max?_iff]
theorem isSome_max?_of_ne_nil [Max α] : {l : List α} (hl : l []) l.max?.isSome
| x::xs, h => by simp [max?_cons']
theorem max?_eq_head? {α : Type u} [Max α] {l : List α}
(h : l.Pairwise (fun a b => max a b = a)) : l.max? = l.head? := by
@@ -360,8 +329,7 @@ theorem max?_replicate [Max α] [Std.IdempotentOp (max : ααα)] {n :
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons', Std.IdempotentOp.idempotent]
@[simp, grind =]
theorem max?_replicate_of_pos [Max α] [MaxEqOr α] {n : Nat} {a : α} (h : 0 < n) :
@[simp] theorem max?_replicate_of_pos [Max α] [MaxEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).max? = some a := by
simp [max?_replicate, Nat.ne_of_gt h]
@@ -378,11 +346,6 @@ theorem foldl_max [Max α] [Std.IdempotentOp (max : ααα)] [Std.Asso
/-! ### max -/
@[simp, grind =]
theorem max_singleton [Max α] {x : α} :
[x].max (cons_ne_nil _ _) = x := by
(rfl)
theorem max?_eq_some_max [Max α] : {l : List α} (hl : l [])
l.max? = some (l.max hl)
| a::as, _ => by simp [List.max, List.max?_cons']
@@ -391,18 +354,12 @@ theorem max_eq_get_max? [Max α] : (l : List α) → (hl : l ≠ []) →
l.max hl = l.max?.get (isSome_max?_of_ne_nil hl)
| a::as, _ => by simp [List.max, List.max?_cons']
@[simp, grind =]
theorem get_max? [Max α] {l : List α} {h : l.max?.isSome} :
l.max?.get h = l.max (isSome_max?_iff.mp h) := by
simp [max?_eq_some_max (isSome_max?_iff.mp h)]
theorem max_eq_head {α : Type u} [Max α] {l : List α} (hl : l [])
(h : l.Pairwise (fun a b => max a b = a)) : l.max hl = l.head hl := by
apply Option.some.inj
rw [ max?_eq_some_max, head?_eq_some_head]
exact max?_eq_head? h
@[grind .]
theorem max_mem [Max α] [MaxEqOr α] {l : List α} (hl : l []) : l.max hl l :=
max?_mem (max?_eq_some_max hl)
@@ -414,13 +371,12 @@ theorem max_eq_iff [Max α] [LE α] {l : List α} [IsLinearOrder α] [LawfulOrde
l.max hl = a a l b, b l b a := by
simpa [max?_eq_some_max hl] using (max?_eq_some_iff (xs := l))
@[grind .]
theorem le_max_of_mem [Max α] [LE α] [Std.IsLinearOrder α] [Std.LawfulOrderMax α]
{l : List α} {a : α} (ha : a l) :
a l.max (List.ne_nil_of_mem ha) :=
(max?_eq_some_iff.mp (max?_eq_some_max (List.ne_nil_of_mem ha))).right a ha
@[simp, grind =] theorem max_replicate [Max α] [MaxEqOr α] {n : Nat} {a : α} (h : replicate n a []) :
@[simp] theorem max_replicate [Max α] [MaxEqOr α] {n : Nat} {a : α} (h : replicate n a []) :
(replicate n a).max h = a := by
have n_pos : 0 < n := Nat.pos_of_ne_zero (fun hn => by simp [hn] at h)
simpa [max?_eq_some_max h] using (max?_replicate_of_pos (a := a) n_pos)

View File

@@ -1,830 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.List.MinMaxOn
import Init.Data.List.MinMaxOn
public import Init.Data.List.Pairwise
public import Init.Data.Subtype.Order
import Init.Data.Order.Lemmas
import Init.Data.List.Nat.TakeDrop
import Init.Data.Order.Opposite
import Init.Data.Nat.Order
public section
open Std
open scoped OppositeOrderInstances
set_option doc.verso true
set_option linter.missingDocs true
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
/--
Returns the index of an element of the non-empty list {name}`xs` that minimizes {name}`f`.
If {given}`x, y` are such that {lean}`f x = f y`, it returns the index of whichever comes first
in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def minIdxOn [LE β] [DecidableLE β] (f : α β) (xs : List α) (h : xs []) : Nat :=
match xs with
| y :: ys => go y 0 1 ys
where
@[specialize]
go (x : α) (i : Nat) (j : Nat) (xs : List α) :=
match xs with
| [] => i
| y :: ys =>
if f x f y then
go x i (j + 1) ys
else
go y j (j + 1) ys
/--
Returns the index of an element of {name}`xs` that minimizes {name}`f`. If {given}`x, y`
are such that {lean}`f x = f y`, it returns the index of whichever comes first in the list.
Returns {name}`none` if the list is empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def minIdxOn? [LE β] [DecidableLE β] (f : α β) (xs : List α) : Option Nat :=
match xs with
| [] => none
| y :: ys => some ((y :: ys).minIdxOn f (nomatch ·))
/--
Returns the index of an element of the non-empty list {name}`xs` that maximizes {name}`f`.
If {given}`x, y` are such that {lean}`f x = f y`, it returns the index of whichever comes first
in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def maxIdxOn [LE β] [DecidableLE β] (f : α β) (xs : List α) (h : xs []) : Nat :=
letI : LE β := LE.opposite inferInstance
xs.minIdxOn f h
/--
Returns the index of an element of {name}`xs` that maximizes {name}`f`. If {given}`x, y`
are such that {lean}`f x = f y`, it returns the index of whichever comes first in the list.
Returns {name}`none` if the list is empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
-/
@[inline]
def maxIdxOn? [LE β] [DecidableLE β] (f : α β) (xs : List α) : Option Nat :=
letI : LE β := LE.opposite inferInstance
xs.minIdxOn? f
protected theorem maxIdxOn_eq_minIdxOn {le : LE β} {_ : DecidableLE β} {f : α β}
{xs : List α} {h} :
xs.maxIdxOn f h = (letI := le.opposite; xs.minIdxOn f h) :=
(rfl)
private theorem minIdxOn.go_lt_length_add [LE β] [DecidableLE β] {f : α β} {x : α}
{i j : Nat} {xs : List α} (h : i < j) :
List.minIdxOn.go f x i j xs < xs.length + j := by
induction xs generalizing x i j
· simp [go, h]
· rename_i y ys ih
simp only [go, length_cons, Nat.add_assoc, Nat.add_comm 1]
split
· exact ih (Nat.lt_succ_of_lt i < j)
· exact ih (Nat.lt_succ_self j)
private theorem minIdxOn.go_eq_of_forall_le [LE β] [DecidableLE β] {f : α β}
{x : α} {i j : Nat} {xs : List α} (h : y xs, f x f y) :
List.minIdxOn.go f x i j xs = i := by
induction xs generalizing x i j
· simp [go]
· rename_i y ys ih
simp only [go]
split
· apply ih
simp_all
· simp_all
private theorem exists_getElem_eq_of_drop_eq_cons {xs : List α} {k : Nat} {y : α} {ys : List α}
(h : xs.drop k = y :: ys) : hlt : k < xs.length, xs[k] = y := by
have hlt : k < xs.length := by
false_or_by_contra
have : drop k xs = [] := drop_of_length_le (by omega)
simp [this] at h
refine hlt, ?_
have := take_append_drop k xs
rw [h] at this
simp +singlePass only [ this]
rw [getElem_append_right (length_take_le _ _)]
simp [length_take_of_le (Nat.le_of_lt hlt)]
private theorem take_succ_eq_append_of_drop_eq_cons {xs : List α} {k : Nat} {y : α}
{ys : List α} (h : xs.drop k = y :: ys) : xs.take (k + 1) = xs.take k ++ [y] := by
obtain hlt, rfl := exists_getElem_eq_of_drop_eq_cons h
rw [take_succ_eq_append_getElem hlt]
private theorem minIdxOn_eq_go_drop [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β}
{xs : List α} (h : xs []) {k : Nat} :
(i : Nat) (hlt : i < xs.length), i k xs[i] = (xs.take (k + 1)).minOn f (by simpa)
xs.minIdxOn f h = List.minIdxOn.go f ((xs.take (k + 1)).minOn f (by cases xs <;> simp_all)) i (k + 1) (xs.drop (k + 1)) := by
match xs with
| y :: ys =>
simp only [drop_succ_cons]
induction k
· simp [minIdxOn]
· rename_i k ih
specialize ih
obtain i, hlt, hi, ih := ih
simp only [ih, drop_drop]
simp only [length_cons] at hlt
match h : drop k ys with
| [] =>
have : ys.length k := by simp_all
simp [drop_nil, minIdxOn.go, take_of_length_le, hi, ih, hlt, this, Nat.le_succ_of_le]
| z :: zs =>
simp only [minIdxOn.go]
have : take (k + 1 + 1) (y :: ys) = take (k + 1) (y :: ys) ++ [z] := by apply take_succ_eq_append_of_drop_eq_cons _
simp only [this, List.minOn_append (xs := take (k + 1) (y :: ys)) (by simp) (cons_ne_nil _ _)]
simp only [take_succ_cons] at this
split
· simp only [List.minOn_singleton, minOn_eq_left, length_cons, *]
exact i, by omega, Nat.le_succ_of_le i k, by simp [ih], rfl
· simp only [List.minOn_singleton, not_false_eq_true, minOn_eq_right, length_cons, *]
obtain hlt, rfl := exists_getElem_eq_of_drop_eq_cons h
exact k + 1, by omega, Nat.le_refl _, by simp, rfl
@[simp]
protected theorem minIdxOn_nil_eq_iff_true [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).minIdxOn f h = x True :=
nomatch h
protected theorem minIdxOn_nil_eq_iff_false [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).minIdxOn f h = x False :=
nomatch h
@[simp]
protected theorem minIdxOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minIdxOn f (of_decide_eq_false rfl) = 0 := by
rw [minIdxOn, minIdxOn.go]
@[simp]
protected theorem minIdxOn_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minIdxOn f h < xs.length := by
rw [minIdxOn.eq_def]
split
simp [minIdxOn.go_lt_length_add]
protected theorem minIdxOn_le_of_apply_getElem_le_apply_minOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hi : k < xs.length) (hle : f xs[k] f (xs.minOn f h)) :
xs.minIdxOn f h k := by
obtain i, _, hi, _, h' := minIdxOn_eq_go_drop (f := f) h (k := k)
rw [h']
refine Nat.le_trans ?_ hi
apply Nat.le_of_eq
apply minIdxOn.go_eq_of_forall_le
intro y hy
refine le_trans (List.apply_minOn_le_of_mem (y := xs[k]) (by rw [mem_take_iff_getElem]; exact k, by omega, rfl)) ?_
refine le_trans hle ?_
apply List.apply_minOn_le_of_mem
apply mem_of_mem_drop
exact hy
protected theorem apply_minOn_lt_apply_getElem_of_lt_minIdxOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hk : k < xs.minIdxOn f h) :
f (xs.minOn f h) < f (xs[k]'(by haveI := List.minIdxOn_lt_length (f := f) h; omega)) := by
simp only [ not_le] at hk
apply hk.imp
apply List.minIdxOn_le_of_apply_getElem_le_apply_minOn
@[simp]
protected theorem getElem_minIdxOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) :
xs[xs.minIdxOn f h] = xs.minOn f h := by
obtain i, hlt, hi, heq, h' := minIdxOn_eq_go_drop (f := f) h (k := xs.length)
simp only [drop_eq_nil_of_le (as := xs) (i := xs.length + 1) (by omega), minIdxOn.go] at h'
simp [h', heq, take_of_length_le (l := xs) (i := xs.length + 1) (by omega)]
protected theorem le_minIdxOn_of_apply_getElem_lt_apply_getElem [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat} (hi : i < xs.length)
(hi' : j, (_ : j < i) f xs[i] < f xs[j]) :
i xs.minIdxOn f h := by
false_or_by_contra; rename_i hgt
simp only [not_le] at hgt
specialize hi' _ hgt
simp only [List.getElem_minIdxOn] at hi'
apply (not_le.mpr hi').elim
apply List.apply_minOn_le_of_mem
simp
protected theorem minIdxOn_le_of_apply_getElem_le_apply_getElem [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} (hi : i < xs.length)
(hi' : j, (_ : j < xs.length) f xs[i] f xs[j]) :
xs.minIdxOn f h i := by
apply List.minIdxOn_le_of_apply_getElem_le_apply_minOn h hi
simp only [List.le_apply_minOn_iff, List.mem_iff_getElem]
rintro _ j, hj, rfl
exact hi' _ hj
protected theorem minIdxOn_eq_iff [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.minIdxOn f h = i (h : i < xs.length),
( j, (_ : j < xs.length) f xs[i] f xs[j])
( j, (_ : j < i) f xs[i] < f xs[j]) := by
apply Iff.intro
· rintro rfl
simp only [List.getElem_minIdxOn]
refine List.minIdxOn_lt_length h, ?_, ?_
· simp [List.apply_minOn_le_of_mem]
· exact fun j hj => List.apply_minOn_lt_apply_getElem_of_lt_minIdxOn h hj
· rintro hi, h₁, h₂
apply le_antisymm
· apply List.minIdxOn_le_of_apply_getElem_le_apply_getElem h hi h₁
· apply List.le_minIdxOn_of_apply_getElem_lt_apply_getElem h hi h₂
protected theorem minIdxOn_eq_iff_eq_minOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.minIdxOn f h = i hi : i < xs.length, xs[i] = xs.minOn f h
(j : Nat) (hj : j < i), f (xs.minOn f h) < f xs[j] := by
apply Iff.intro
· rintro rfl
refine List.minIdxOn_lt_length h, List.getElem_minIdxOn h, ?_
intro j hj
exact List.apply_minOn_lt_apply_getElem_of_lt_minIdxOn h hj
· rintro hlt, heq, h'
specialize h' (xs.minIdxOn f h)
simp only [List.getElem_minIdxOn] at h'
apply le_antisymm
· apply List.minIdxOn_le_of_apply_getElem_le_apply_minOn h hlt
simp [heq, le_refl]
· simpa [lt_irrefl] using h'
private theorem minIdxOn.go_eq
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
List.minIdxOn.go f x i j xs =
if h : xs = [] then i
else if f x f (xs.minOn f h) then i
else (xs.minIdxOn f h) + j := by
open scoped Classical.Order in
induction xs generalizing x i j
· simp [go]
· rename_i y ys ih
simp only [go, reduceCtorEq, reduceDIte]
split
· rw [ih]
split
· simp [*]
· simp only [List.minOn_cons, reduceDIte, le_apply_minOn_iff, true_and, *]
split
· rfl
· rename_i hlt
simp only [minIdxOn]
split
simp only [ih, reduceCtorEq, reduceDIte]
rw [if_neg]
· simp [minIdxOn, Nat.add_assoc, Nat.add_comm 1]
· simp only [not_le] at hlt
exact lt_of_lt_of_le hlt _
· rename_i hlt
rw [if_neg]
· rw [minIdxOn, ih]
split
· simp [*, go]
· simp only [reduceDIte, *]
split
· simp
· simp only [Nat.add_assoc, Nat.add_comm 1]
· simp only [not_le] at hlt
exact lt_of_le_of_lt (List.apply_minOn_le_of_mem mem_cons_self) hlt
protected theorem minIdxOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).minIdxOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then 0
else if f x f (xs.minOn f h) then 0
else (xs.minIdxOn f h) + 1 := by
simpa [List.minIdxOn] using minIdxOn.go_eq
protected theorem minIdxOn_eq_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) :
xs.minIdxOn f h = 0 x xs, f (xs.head h) f x := by
rw [minIdxOn.eq_def]
split
rename_i y ys _
simp only [mem_cons, head_cons, forall_eq_or_imp, le_refl, true_and]
apply Iff.intro
· intro h
cases ys
· simp
· intro a ha
refine le_trans ?_ (List.apply_minOn_le_of_mem ha)
simpa [minIdxOn.go_eq] using h
· intro h
cases ys
· simp [minIdxOn.go]
· simpa [minIdxOn.go_eq, List.le_apply_minOn_iff] using h
section Append
/-!
The proof of {name}`List.minOn_append` uses associativity of {name}`minOn` and applies {name}`foldl_assoc`.
The proof of {name (scope := "Init.Data.List.MinMaxIdx")}`minIdxOn_append` is analogous, but the
aggregation operation, {name (scope := "Init.Data.List.MinMaxIdx")}`combineMinIdxOn`, depends on
the length of the lists to combine. After proving associativity of the aggregation operation,
the proof closely follows the proof of {name}`foldl_assoc`.
-/
private def combineMinIdxOn [LE β] [DecidableLE β]
(f : α β) {xs ys : List α} (i j : Nat) (hi : i < xs.length) (hj : j < ys.length) : Nat :=
if f xs[i] f ys[j] then
i
else
xs.length + j
private theorem combineMinIdxOn_lt [LE β] [DecidableLE β]
(f : α β) {xs ys : List α} {i j : Nat} (hi : i < xs.length) (hj : j < ys.length) :
combineMinIdxOn f i j hi hj < (xs ++ ys).length := by
simp only [combineMinIdxOn]
split <;> (simp; omega)
private theorem combineMinIdxOn_assoc [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys zs : List α} {i j k : Nat} {f : α β} (hi : i < xs.length) (hj : j < ys.length)
(hk : k < zs.length) :
combineMinIdxOn f (combineMinIdxOn f i j _ _) k
(combineMinIdxOn_lt f hi hj) hk = combineMinIdxOn f i (combineMinIdxOn f j k _ _) hi (combineMinIdxOn_lt f hj hk) := by
open scoped Classical.Order in
simp only [combineMinIdxOn]
split
· rw [getElem_append_left (by omega)]
split
· split
· rw [getElem_append_left (by omega)]
simp [*]
· rw [getElem_append_right (by omega)]
simp [*]
· split
· have := le_trans f xs[i] f ys[j] f ys[j] f zs[k]
contradiction
· rw [getElem_append_right (by omega)]
simp [*, Nat.add_assoc]
· rw [getElem_append_right (by omega)]
simp only [Nat.add_sub_cancel_left]
split
· rw [getElem_append_left (by omega), if_neg _]
· rename_i h₁ h₂
rw [not_le] at h₁ h₂
rw [getElem_append_right (by omega)]
simp only [Nat.add_sub_cancel_left]
have := not_le.mpr <| lt_trans h₂ h₁
simp [*, Nat.add_assoc]
private theorem minIdxOn_cons_aux [LE β] [DecidableLE β]
[IsLinearPreorder β] {x : α} {xs : List α} {f : α β} (hxs : xs []) :
(x :: xs).minIdxOn f (by simp) =
combineMinIdxOn f _ _
(List.minIdxOn_lt_length (f := f) (cons_ne_nil x []))
(List.minIdxOn_lt_length (f := f) hxs) := by
rw [minIdxOn, combineMinIdxOn]
simp [minIdxOn.go_eq, hxs, List.getElem_minIdxOn, Nat.add_comm 1]
private theorem minIdxOn_append_aux [LE β] [DecidableLE β]
[IsLinearPreorder β] {xs ys : List α} {f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).minIdxOn f (by simp [hxs]) =
combineMinIdxOn f _ _
(List.minIdxOn_lt_length (f := f) hxs)
(List.minIdxOn_lt_length (f := f) hys) := by
induction xs
· contradiction
· rename_i x xs ih
match xs with
| [] => simp [minIdxOn_cons_aux (xs := ys) _]
| z :: zs =>
simp +singlePass only [cons_append]
simp only [minIdxOn_cons_aux (xs := z :: zs ++ ys) (by simp), ih (by simp),
minIdxOn_cons_aux (xs := z :: zs) (by simp), combineMinIdxOn_assoc]
protected theorem minIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).minIdxOn f (by simp [hxs]) =
if f (xs.minOn f hxs) f (ys.minOn f hys) then
xs.minIdxOn f hxs
else
xs.length + ys.minIdxOn f hys := by
simp [minIdxOn_append_aux hxs hys, combineMinIdxOn, List.getElem_minIdxOn]
end Append
protected theorem left_le_minIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
xs.minIdxOn f h (xs ++ ys).minIdxOn f (by simp [h]) := by
by_cases hys : ys = []
· simp [hys]
· rw [List.minIdxOn_append h hys]
split
· apply Nat.le_refl
· have := List.minIdxOn_lt_length (f := f) h
omega
protected theorem minIdxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
(xs.take i).minIdxOn f h xs.minIdxOn f (List.ne_nil_of_take_ne_nil h) := by
have := take_append_drop i xs
conv => rhs; simp +singlePass only [ this]
apply List.left_le_minIdxOn_append
@[simp]
protected theorem minIdxOn_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).minIdxOn f h = 0 := by
match n with
| 0 => simp at h
| n + 1 =>
simp only [minIdxOn, replicate_succ]
generalize 1 = j
induction n generalizing j
· simp [minIdxOn.go]
· simp only [replicate_succ, minIdxOn.go] at *
split
· simp [*]
· have := le_refl (f a)
contradiction
@[simp]
protected theorem maxIdxOn_nil_eq_iff_true [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).maxIdxOn f h = x True :=
nomatch h
protected theorem maxIdxOn_nil_eq_iff_false [LE β] [DecidableLE β] {f : α β} {x : Nat}
(h : [] []) : ([] : List α).maxIdxOn f h = x False :=
nomatch h
@[simp]
protected theorem maxIdxOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxIdxOn f (of_decide_eq_false rfl) = 0 :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_singleton
@[simp]
protected theorem maxIdxOn_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxIdxOn f h < xs.length :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_lt_length h
protected theorem maxIdxOn_le_of_apply_getElem_le_apply_maxOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hi : k < xs.length) (hle : f (xs.maxOn f h) f xs[k]) :
xs.maxIdxOn f h k := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn] at hle
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minIdxOn_le_of_apply_getElem_le_apply_minOn h hi (by simpa [LE.le_opposite_iff] using hle)
protected theorem apply_maxOn_lt_apply_getElem_of_lt_maxIdxOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs [])
{k : Nat} (hk : k < xs.maxIdxOn f h) :
f (xs[k]'(by haveI := List.maxIdxOn_lt_length (f := f) h; omega)) < f (xs.maxOn f h) := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn] at hk
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LT.lt_opposite_iff] using List.apply_minOn_lt_apply_getElem_of_lt_minIdxOn (f := f) h hk
@[simp]
protected theorem getElem_maxIdxOn [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) :
xs[xs.maxIdxOn f h] = xs.maxOn f h := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.getElem_minIdxOn h
protected theorem le_maxIdxOn_of_apply_getElem_lt_apply_getElem [LE β] [DecidableLE β] [LT β]
[IsLinearPreorder β] [LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat}
(hi : i < xs.length) (hi' : j, (_ : j < i) f xs[j] < f xs[i]) :
i xs.maxIdxOn f h := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LE.le_opposite_iff] using List.le_minIdxOn_of_apply_getElem_lt_apply_getElem h hi
(by simpa [LT.lt_opposite_iff] using hi')
protected theorem maxIdxOn_le_of_apply_getElem_le_apply_getElem [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} (hi : i < xs.length)
(hi' : j, (_ : j < xs.length) f xs[j] f xs[i]) :
xs.maxIdxOn f h i := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := LE.opposite inferInstance
simpa [LE.le_opposite_iff] using List.minIdxOn_le_of_apply_getElem_le_apply_getElem (f := f) h hi
(by simpa [LE.le_opposite_iff] using hi')
protected theorem maxIdxOn_eq_iff [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β]
{f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.maxIdxOn f h = i (h : i < xs.length),
( j, (_ : j < xs.length) f xs[j] f xs[i])
( j, (_ : j < i) f xs[j] < f xs[i]) := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LE.le_opposite_iff, LT.lt_opposite_iff] using List.minIdxOn_eq_iff (f := f) h
protected theorem maxIdxOn_eq_iff_eq_maxOn [LE β] [DecidableLE β] [LT β] [IsLinearPreorder β]
[LawfulOrderLT β] {f : α β} {xs : List α} (h : xs []) {i : Nat} :
xs.maxIdxOn f h = i hi : i < xs.length, xs[i] = xs.maxOn f h
(j : Nat) (hj : j < i), f xs[j] < f (xs.maxOn f h) := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := LE.opposite inferInstance
letI : LT β := LT.opposite inferInstance
simpa [LT.lt_opposite_iff] using List.minIdxOn_eq_iff_eq_minOn (f := f) h
protected theorem maxIdxOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).maxIdxOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then 0
else if f (xs.maxOn f h) f x then 0
else (xs.maxIdxOn f h) + 1 := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minIdxOn_cons (f := f)
protected theorem maxIdxOn_eq_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) :
xs.maxIdxOn f h = 0 x xs, f x f (xs.head h) := by
simp only [List.maxIdxOn_eq_minIdxOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minIdxOn_eq_zero_iff h (f := f)
protected theorem maxIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).maxIdxOn f (by simp [hxs]) =
if f (ys.maxOn f hys) f (xs.maxOn f hxs) then
xs.maxIdxOn f hxs
else
xs.length + ys.maxIdxOn f hys := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minIdxOn_append hxs hys (f := f)
protected theorem left_le_maxIdxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
xs.maxIdxOn f h (xs ++ ys).maxIdxOn f (by simp [h]) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.left_le_minIdxOn_append h
protected theorem maxIdxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
(xs.take i).maxIdxOn f h xs.maxIdxOn f (List.ne_nil_of_take_ne_nil h) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_take_le h
@[simp]
protected theorem maxIdxOn_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).maxIdxOn f h = 0 :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minIdxOn_replicate h
@[simp]
protected theorem minIdxOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).minIdxOn? f = none :=
(rfl)
@[simp]
protected theorem minIdxOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minIdxOn? f = some 0 :=
(rfl)
@[simp]
protected theorem isSome_minIdxOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.minIdxOn? f).isSome xs [] := by
cases xs <;> simp [minIdxOn?]
protected theorem minIdxOn_eq_get_minIdxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minIdxOn f h = (xs.minIdxOn? f).get (List.isSome_minIdxOn?_iff.mpr h) := by
match xs with
| [] => contradiction
| _ :: _ => simp [minIdxOn?]
@[simp]
protected theorem get_minIdxOn?_eq_minIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.minIdxOn? f).isSome) :
(xs.minIdxOn? f).get h = xs.minIdxOn f (List.isSome_minIdxOn?_iff.mp h) := by
rw [List.minIdxOn_eq_get_minIdxOn?]
protected theorem minIdxOn?_eq_some_minIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minIdxOn? f = some (xs.minIdxOn f h) := by
match xs with
| [] => contradiction
| _ :: _ => simp [minIdxOn?]
protected theorem minIdxOn_eq_of_minIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {i : Nat} (h : xs.minIdxOn? f = some i) :
xs.minIdxOn f (List.isSome_minIdxOn?_iff.mp (Option.isSome_of_eq_some h)) = i := by
have h' := List.isSome_minIdxOn?_iff.mp (Option.isSome_of_eq_some h)
rwa [List.minIdxOn?_eq_some_minIdxOn h', Option.some.injEq] at h
protected theorem isSome_minIdxOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.minIdxOn? f).isSome := by
apply List.isSome_minIdxOn?_iff.mpr
exact ne_nil_of_mem h
protected theorem minIdxOn?_cons_eq_some_minIdxOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minIdxOn? f = some ((x :: xs).minIdxOn f (nomatch ·)) := by
simp [List.minIdxOn?_eq_some_minIdxOn]
protected theorem minIdxOn?_eq_if
[LE β] [DecidableLE β] {f : α β} {xs : List α} :
xs.minIdxOn? f =
if h : xs [] then
some (xs.minIdxOn f h)
else
none := by
cases xs <;> simp [List.minIdxOn?_cons_eq_some_minIdxOn]
protected theorem minIdxOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minIdxOn? f = some
(if h : xs = [] then 0
else if f x f (xs.minOn f h) then 0
else (xs.minIdxOn f h) + 1) := by
simp [List.minIdxOn?_eq_some_minIdxOn, List.minIdxOn_cons]
protected theorem ne_nil_of_minIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {k : Nat} {xs : List α} (h : xs.minIdxOn? f = some k) :
xs [] := by
rintro rfl
simp at h
protected theorem lt_length_of_minIdxOn?_eq_some [LE β] [DecidableLE β] {f : α β}
{xs : List α} (h : xs.minIdxOn? f = some i) : i < xs.length := by
have hne : xs [] := List.ne_nil_of_minIdxOn?_eq_some h
rw [List.minIdxOn?_eq_some_minIdxOn hne] at h
have := List.minIdxOn_lt_length (f := f) hne
simp_all
@[simp]
protected theorem get_minIdxOn?_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.minIdxOn? f).isSome) : (xs.minIdxOn? f).get h < xs.length := by
rw [List.get_minIdxOn?_eq_minIdxOn]
apply List.minIdxOn_lt_length
@[simp]
protected theorem getElem_get_minIdxOn? [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : (xs.minIdxOn? f).isSome) :
xs[(xs.minIdxOn? f).get h] = xs.minOn f (List.isSome_minIdxOn?_iff.mp h) := by
rw [getElem_congr rfl (List.get_minIdxOn?_eq_minIdxOn _), List.getElem_minIdxOn]
protected theorem minIdxOn?_eq_some_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} :
xs.minIdxOn? f = some 0 h : xs [], x xs, f (xs.head h) f x := by
simp [Option.eq_some_iff_get_eq, List.minIdxOn_eq_zero_iff]
protected theorem minIdxOn?_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} :
(replicate n a).minIdxOn? f = if n = 0 then none else some 0 := by
simp [List.minIdxOn?_eq_if]
@[simp]
protected theorem minIdxOn?_replicate_of_pos [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).minIdxOn? f = some 0 := by
simp [List.minIdxOn?_replicate, Nat.ne_zero_of_lt h]
/-! ### maxIdxOn? -/
protected theorem maxIdxOn?_eq_minIdxOn? {le : LE β} {_ : DecidableLE β} {f : α β}
{xs : List α} :
xs.maxIdxOn? f = (letI := le.opposite; xs.minIdxOn? f) :=
(rfl)
@[simp]
protected theorem maxIdxOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).maxIdxOn? f = none :=
letI : LE β := LE.opposite inferInstance
List.minIdxOn?_nil
@[simp]
protected theorem maxIdxOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxIdxOn? f = some 0 :=
letI : LE β := LE.opposite inferInstance
List.minIdxOn?_singleton
@[simp]
protected theorem isSome_maxIdxOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.maxIdxOn? f).isSome xs [] := by
letI : LE β := LE.opposite inferInstance
exact List.isSome_minIdxOn?_iff
protected theorem maxIdxOn_eq_get_maxIdxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxIdxOn f h = (xs.maxIdxOn? f).get (List.isSome_maxIdxOn?_iff.mpr h) := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn_eq_get_minIdxOn? h
@[simp]
protected theorem get_maxIdxOn?_eq_maxIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.maxIdxOn? f).isSome) :
(xs.maxIdxOn? f).get h = xs.maxIdxOn f (List.isSome_maxIdxOn?_iff.mp h) := by
letI : LE β := LE.opposite inferInstance
exact List.get_minIdxOn?_eq_minIdxOn h
protected theorem maxIdxOn?_eq_some_maxIdxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxIdxOn? f = some (xs.maxIdxOn f h) := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_eq_some_minIdxOn h
protected theorem maxIdxOn_eq_of_maxIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {i : Nat} (h : xs.maxIdxOn? f = some i) :
xs.maxIdxOn f (List.isSome_maxIdxOn?_iff.mp (Option.isSome_of_eq_some h)) = i := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn_eq_of_minIdxOn?_eq_some h
protected theorem isSome_maxIdxOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.maxIdxOn? f).isSome := by
letI : LE β := LE.opposite inferInstance
exact List.isSome_minIdxOn?_of_mem h
protected theorem maxIdxOn?_cons_eq_some_maxIdxOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxIdxOn? f = some ((x :: xs).maxIdxOn f (nomatch ·)) := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_cons_eq_some_minIdxOn
protected theorem maxIdxOn?_eq_if
[LE β] [DecidableLE β] {f : α β} {xs : List α} :
xs.maxIdxOn? f =
if h : xs [] then
some (xs.maxIdxOn f h)
else
none := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_eq_if
protected theorem maxIdxOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxIdxOn? f = some
(if h : xs = [] then 0
else if f (xs.maxOn f h) f x then 0
else (xs.maxIdxOn f h) + 1) := by
simp only [List.maxIdxOn_eq_minIdxOn, List.maxOn_eq_minOn]
letI : LE β := LE.opposite inferInstance
simpa [LE.le_opposite_iff] using List.minIdxOn?_cons (f := f)
protected theorem ne_nil_of_maxIdxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {k : Nat} {xs : List α} (h : xs.maxIdxOn? f = some k) :
xs [] := by
letI : LE β := LE.opposite inferInstance
exact List.ne_nil_of_minIdxOn?_eq_some (by simpa only [List.maxIdxOn?_eq_minIdxOn?] using h)
protected theorem lt_length_of_maxIdxOn?_eq_some [LE β] [DecidableLE β] {f : α β}
{xs : List α} (h : xs.maxIdxOn? f = some i) : i < xs.length := by
letI : LE β := LE.opposite inferInstance
exact List.lt_length_of_minIdxOn?_eq_some h
@[simp]
protected theorem get_maxIdxOn?_lt_length [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : (xs.maxIdxOn? f).isSome) : (xs.maxIdxOn? f).get h < xs.length := by
letI : LE β := LE.opposite inferInstance
exact List.get_minIdxOn?_lt_length h
@[simp]
protected theorem getElem_get_maxIdxOn? [LE β] [DecidableLE β] [IsLinearPreorder β]
{f : α β} {xs : List α} (h : (xs.maxIdxOn? f).isSome) :
xs[(xs.maxIdxOn? f).get h] = xs.maxOn f (List.isSome_maxIdxOn?_iff.mp h) := by
simp only [List.maxIdxOn?_eq_minIdxOn?, List.maxOn_eq_minOn]
letI : LE β := LE.opposite inferInstance
exact List.getElem_get_minIdxOn? h
protected theorem maxIdxOn?_eq_some_zero_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} :
xs.maxIdxOn? f = some 0 h : xs [], x xs, f x f (xs.head h) := by
simp only [List.maxIdxOn?_eq_minIdxOn?]
letI : LE β := LE.opposite inferInstance
simpa [LE.le_opposite_iff] using List.minIdxOn?_eq_some_zero_iff (f := f)
protected theorem maxIdxOn?_replicate [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} :
(replicate n a).maxIdxOn? f = if n = 0 then none else some 0 := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_replicate
@[simp]
protected theorem maxIdxOn?_replicate_of_pos [LE β] [DecidableLE β] [Refl (α := β) (· ·)]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).maxIdxOn? f = some 0 := by
letI : LE β := LE.opposite inferInstance
exact List.minIdxOn?_replicate_of_pos h
end List

View File

@@ -1,623 +0,0 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Order.MinMaxOn
public import Init.Data.Int.OfNat
public import Init.Data.List.Lemmas
public import Init.Data.List.TakeDrop
import Init.Data.Order.Lemmas
import Init.Data.List.Sublist
import Init.Data.List.MinMax
import Init.Data.Order.Opposite
set_option doc.verso true
set_option linter.missingDocs true
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
public section
open Std
open scoped OppositeOrderInstances
namespace List
/--
Returns an element of the non-empty list {name}`l` that minimizes {name}`f`. If {given}`x, y` are
such that {lean}`f x = f y`, it returns whichever comes first in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.minOn` is the first minimizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_minIdxOn`.
-/
@[inline, suggest_for List.argmin]
protected def minOn [LE β] [DecidableLE β] (f : α β) (l : List α) (h : l []) : α :=
match l with
| x :: xs => xs.foldl (init := x) (minOn f)
/--
Returns an element of the non-empty list {name}`l` that maximizes {name}`f`. If {given}`x, y` are
such that {lean}`f x = f y`, it returns whichever comes first in the list.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.maxOn` is the first maximizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_maxIdxOn`.
-/
@[inline, suggest_for List.argmax]
protected def maxOn [i : LE β] [DecidableLE β] (f : α β) (l : List α) (h : l []) : α :=
letI : LE β := i.opposite
l.minOn f h
/--
Returns an element of {name}`l` that minimizes {name}`f`. If {given}`x, y` are such that
{lean}`f x = f y`, it returns whichever comes first in the list. Returns {name}`none` if the list is
empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.minOn?` is the first minimizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_get_minIdxOn?`
-/
@[inline, suggest_for List.argmin? List.argmin] -- Mathlib's `List.argmin` returns an `Option α`
protected def minOn? [LE β] [DecidableLE β] (f : α β) (l : List α) : Option α :=
match l with
| [] => none
| x :: xs => some (xs.foldl (init := x) (minOn f))
/--
Returns an element of {name}`l` that maximizes {name}`f`. If {given}`x, y` are such that
{lean}`f x = f y`, it returns whichever comes first in the list. Returns {name}`none` if the list is
empty.
The correctness of this function assumes {name}`β` to be linearly pre-ordered.
The property that {name}`List.maxOn?` is the first minimizer in the list is guaranteed by the lemma
{name (scope := "Init.Data.List.MinMaxIdx")}`List.getElem_get_maxIdxOn?`.
-/
@[inline, suggest_for List.argmax? List.argmax] -- Mathlib's `List.argmax` returns an `Option α`
protected def maxOn? [i : LE β] [DecidableLE β] (f : α β) (l : List α) : Option α :=
letI : LE β := i.opposite
l.minOn? f
/-! ### minOn -/
@[simp]
protected theorem minOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minOn f (of_decide_eq_false rfl) = x := by
simp [List.minOn]
protected theorem minOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).minOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then x else minOn f x (xs.minOn f h) := by
simp only [List.minOn]
match xs with
| [] => simp
| y :: xs => simp [foldl_assoc]
@[simp]
protected theorem minOn_id [Min α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMin α]
{xs : List α} (h : xs []) :
xs.minOn id h = xs.min h := by
have : minOn (α := α) id = min := by ext; apply minOn_id
simp only [List.minOn, List.min, this]
match xs with
| _ :: _ => simp
@[simp]
protected theorem minOn_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} {h : xs []} : xs.minOn f h xs := by
simp only [List.minOn]
match xs with
| x :: xs =>
fun_induction xs.foldl (init := x) (_root_.minOn f)
· simp
· rename_i x y _ ih
simp only [ne_eq, reduceCtorEq, not_false_eq_true, mem_cons, forall_const, foldl_cons] at ih
cases ih <;> rename_i heq
· cases minOn_eq_or (f := f) (x := x) (y := y) <;> simp_all
· simp [*]
protected theorem apply_minOn_le_of_mem [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {y : α} (hx : y xs) :
f (xs.minOn f (List.ne_nil_of_mem hx)) f y := by
have h : xs [] := List.ne_nil_of_mem hx
simp only [List.minOn]
match xs with
| x :: xs =>
fun_induction xs.foldl (init := x) (_root_.minOn f) generalizing y
· simp only [mem_cons] at hx
simp_all
· rename_i x y _ ih
simp at ih
rcases mem_cons.mp hx with rfl | hx
· exact le_trans ih.1 apply_minOn_le_left
· rcases mem_cons.mp hx with rfl | hx
· exact le_trans ih.1 apply_minOn_le_right
· apply ih.2
assumption
protected theorem le_apply_minOn_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
b f (xs.minOn f h) x xs, b f x := by
match xs with
| x :: xs =>
rw [List.minOn]
induction xs generalizing x
· simp
· rw [foldl_cons, foldl_assoc, le_apply_minOn_iff]
simp_all
protected theorem apply_minOn_le_iff [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
f (xs.minOn f h) b x xs, f x b := by
apply Iff.intro
· intro h
match xs with
| x :: xs =>
rw [List.minOn] at h
induction xs generalizing x
· simpa using h
· rename_i y ys ih _
rw [foldl_cons] at h
specialize ih (minOn f x y) (by simp) h
obtain z, hm, hle := ih
rcases mem_cons.mp hm with rfl | hm
· cases minOn_eq_or (f := f) (x := x) (y := y)
· exact x, by simp_all
· exact y, by simp_all
· exact z, by simp_all
· rintro x, hm, hx
exact le_trans (List.apply_minOn_le_of_mem hm) hx
protected theorem lt_apply_minOn_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
b < f (xs.minOn f h) x xs, b < f x := by
simpa [not_le] using not_congr <| xs.apply_minOn_le_iff (f := f) h (b := b)
protected theorem apply_minOn_lt_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
f (xs.minOn f h) < b x xs, f x < b := by
simpa [not_le] using not_congr <| xs.le_apply_minOn_iff (f := f) h (b := b)
protected theorem apply_minOn_le_apply_minOn_of_subset [LE β] [DecidableLE β]
[IsLinearPreorder β] {xs ys : List α} {f : α β} (hxs : ys xs) (hys : ys []) :
haveI : xs [] := by intro h; rw [h] at hxs; simp_all [subset_nil]
f (xs.minOn f this) f (ys.minOn f hys) := by
rw [List.le_apply_minOn_iff]
intro x hx
exact List.apply_minOn_le_of_mem (hxs hx)
protected theorem le_apply_minOn_take [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
f (xs.minOn f (List.ne_nil_of_take_ne_nil h)) f ((xs.take i).minOn f h) := by
apply List.apply_minOn_le_apply_minOn_of_subset
apply take_subset
protected theorem apply_minOn_append_le_left [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
f ((xs ++ ys).minOn f (append_ne_nil_of_left_ne_nil h ys))
f (xs.minOn f h) := by
apply List.apply_minOn_le_apply_minOn_of_subset
apply subset_append_left
protected theorem apply_minOn_append_le_right [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : ys []) :
f ((xs ++ ys).minOn f (append_ne_nil_of_right_ne_nil xs h))
f (ys.minOn f h) := by
apply List.apply_minOn_le_apply_minOn_of_subset
apply subset_append_right
@[simp]
protected theorem minOn_append [LE β] [DecidableLE β] [IsLinearPreorder β] {xs ys : List α}
{f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).minOn f (by simp [hxs]) = minOn f (xs.minOn f hxs) (ys.minOn f hys) := by
match xs, ys with
| x :: xs, y :: ys => simp [List.minOn, foldl_assoc]
protected theorem minOn_eq_head [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} (h : xs []) (h' : x xs, f (xs.head h) f x) :
xs.minOn f h = xs.head h := by
match xs with
| x :: xs =>
simp only [List.minOn]
induction xs
· simp
· simp only [foldl_cons, head_cons]
rw [minOn_eq_left] <;> simp_all
protected theorem min_map
[LE β] [DecidableLE β] [Min β] [IsLinearPreorder β] [LawfulOrderLeftLeaningMin β] {xs : List α}
{f : α β} (h : xs []) :
(xs.map f).min (by simpa) = f (xs.minOn f h) := by
match xs with
| x :: xs =>
simp only [List.minOn, map_cons, List.min, foldl_map]
rw [foldl_hom]
simp [min_apply]
@[simp]
protected theorem minOn_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).minOn f h = a := by
induction n
· simp at h
· rename_i n ih
simp only [ne_eq, replicate_eq_nil_iff] at ih
simp +contextual [List.replicate, List.minOn_cons, ih]
/-! ### maxOn -/
protected theorem maxOn_eq_minOn {le : LE β} {dle : DecidableLE β} {xs : List α} {f : α β} {h} :
xs.maxOn f h = (letI := le.opposite; xs.minOn f h) :=
(rfl)
@[simp]
protected theorem maxOn_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxOn f (of_decide_eq_false rfl) = x := by
simp [List.maxOn]
protected theorem maxOn_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α β} :
(x :: xs).maxOn f (by exact of_decide_eq_false rfl) =
if h : xs = [] then x else maxOn f x (xs.maxOn f h) := by
simp only [maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minOn_cons (f := f)
protected theorem min_eq_max {min : Min α} {xs : List α} {h} :
xs.min h = (letI := min.oppositeMax; xs.max h) := by
simp only [List.min, List.max]
rw [Min.oppositeMax_def]
simp
protected theorem max_eq_min {max : Max α} {xs : List α} {h} :
xs.max h = (letI := max.oppositeMin; xs.min h) := by
simp only [List.min, List.max]
rw [Max.oppositeMin_def]
simp
protected theorem max?_eq_min? {max : Max α} {xs : List α} :
xs.max? = (letI := max.oppositeMin; xs.min?) := by
simp only [List.min?, List.max?]
rw [Max.oppositeMin_def]
simp
@[simp]
protected theorem maxOn_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α]
{xs : List α} (h : xs []) :
xs.maxOn id h = xs.max h := by
simp only [List.maxOn_eq_minOn]
letI : LE α := (inferInstanceAs (LE α)).opposite
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
simpa only [List.max_eq_min] using List.minOn_id h
@[simp]
protected theorem maxOn_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} {h : xs []} : xs.maxOn f h xs :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_mem (f := f)
protected theorem le_apply_maxOn_of_mem [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {y : α} (hx : y xs) :
f y f (xs.maxOn f (List.ne_nil_of_mem hx)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_le_of_mem (f := f) hx
protected theorem apply_maxOn_le_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
{f : α β} (h : xs []) {b : β} :
f (xs.maxOn f h) b x xs, f x b := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.le_apply_minOn_iff (f := f) h
protected theorem le_apply_maxOn_iff [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
{f : α β} (h : xs []) {b : β} :
b f (xs.maxOn f h) x xs, b f x := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_le_iff (f := f) h
protected theorem apply_maxOn_lt_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
f (xs.maxOn f h) < b x xs, f x < b := by
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : LT β := (inferInstanceAs (LT β)).opposite
simpa [LT.lt_opposite_iff] using List.lt_apply_minOn_iff (f := f) h
protected theorem lt_apply_maxOn_iff
[LE β] [DecidableLE β] [LT β] [IsLinearPreorder β] [LawfulOrderLT β]
{xs : List α} {f : α β} (h : xs []) {b : β} :
b < f (xs.maxOn f h) x xs, b < f x := by
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : LT β := (inferInstanceAs (LT β)).opposite
simpa [LT.lt_opposite_iff] using List.apply_minOn_lt_iff (f := f) h
protected theorem apply_maxOn_le_apply_maxOn_of_subset [LE β] [DecidableLE β]
[IsLinearPreorder β] {xs ys : List α} {f : α β} (hxs : ys xs) (hys : ys []) :
haveI : xs [] := by intro h; rw [h] at hxs; simp_all [subset_nil]
f (ys.maxOn f hys) f (xs.maxOn f this) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_le_apply_minOn_of_subset (f := f) hxs hys
protected theorem apply_maxOn_take_le [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs : List α} {f : α β} {i : Nat} (h : xs.take i []) :
f ((xs.take i).maxOn f h) f (xs.maxOn f (List.ne_nil_of_take_ne_nil h)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.le_apply_minOn_take (f := f) h
protected theorem le_apply_maxOn_append_left [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : xs []) :
f (xs.maxOn f h)
f ((xs ++ ys).maxOn f (append_ne_nil_of_left_ne_nil h ys)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_append_le_left (f := f) h
protected theorem le_apply_maxOn_append_right [LE β] [DecidableLE β] [IsLinearPreorder β]
{xs ys : List α} {f : α β} (h : ys []) :
f (ys.maxOn f h)
f ((xs ++ ys).maxOn f (append_ne_nil_of_right_ne_nil xs h)) := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_minOn_append_le_right (f := f) h
@[simp]
protected theorem maxOn_append [LE β] [DecidableLE β] [IsLinearPreorder β] {xs ys : List α}
{f : α β} (hxs : xs []) (hys : ys []) :
(xs ++ ys).maxOn f (by simp [hxs]) = maxOn f (xs.maxOn f hxs) (ys.maxOn f hys) := by
simp only [List.maxOn_eq_minOn, maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minOn_append (f := f) hxs hys
protected theorem maxOn_eq_head [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
{f : α β} (h : xs []) (h' : x xs, f x f (xs.head h)) :
xs.maxOn f h = xs.head h := by
rw [List.maxOn_eq_minOn]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.minOn_eq_head (f := f) h (by simpa [LE.le_opposite_iff] using h')
protected theorem max_map
[LE β] [DecidableLE β] [Max β] [IsLinearPreorder β] [LawfulOrderLeftLeaningMax β] {xs : List α}
{f : α β} (h : xs []) : (xs.map f).max (by simpa) = f (xs.maxOn f h) := by
letI : LE β := (inferInstanceAs (LE β)).opposite
letI : Min β := (inferInstanceAs (Max β)).oppositeMin
simpa [List.max_eq_min] using List.min_map (f := f) h
@[simp]
protected theorem maxOn_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : replicate n a []) :
(replicate n a).maxOn f h = a :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_replicate (f := f) h
/-! ### minOn? -/
/-- {lit}`List.minOn?` returns {name}`none` when applied to an empty list. -/
@[simp]
protected theorem minOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).minOn? f = none := by
simp [List.minOn?]
protected theorem minOn?_cons_eq_some_minOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minOn? f = some ((x :: xs).minOn f (fun h => nomatch h)) := by
simp [List.minOn?, List.minOn]
protected theorem minOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).minOn? f = some ((xs.minOn? f).elim x (minOn f x)) := by
simp only [List.minOn?]
split <;> simp [foldl_assoc]
@[simp]
protected theorem minOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].minOn? f = some x := by
simp [List.minOn?_cons_eq_some_minOn]
@[simp]
protected theorem minOn?_id [Min α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMin α]
{xs : List α} : xs.minOn? id = xs.min? := by
cases xs
· simp
· simp only [List.minOn?_cons_eq_some_minOn, List.minOn_id, List.min?_eq_some_min (List.cons_ne_nil _ _)]
protected theorem minOn?_eq_if
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} :
xs.minOn? f =
if h : xs [] then
some (xs.minOn f h)
else
none := by
fun_cases xs.minOn? f <;> simp [List.minOn]
@[simp]
protected theorem isSome_minOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.minOn? f).isSome xs [] := by
fun_cases xs.minOn? f <;> simp
protected theorem minOn_eq_get_minOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minOn f h = (xs.minOn? f).get (List.isSome_minOn?_iff.mpr h) := by
fun_cases xs.minOn? f
· contradiction
· simp [List.minOn?, List.minOn]
protected theorem minOn?_eq_some_minOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.minOn? f = some (xs.minOn f h) := by
simp [List.minOn_eq_get_minOn? h]
@[simp]
protected theorem get_minOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : (xs.minOn? f).get (List.isSome_minOn?_iff.mpr h) = xs.minOn f h := by
rw [List.minOn_eq_get_minOn?]
protected theorem minOn_eq_of_minOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : xs.minOn? f = some x) :
xs.minOn f (List.isSome_minOn?_iff.mp (Option.isSome_of_eq_some h)) = x := by
have h' := List.isSome_minOn?_iff.mp (Option.isSome_of_eq_some h)
rwa [List.minOn?_eq_some_minOn h', Option.some.injEq] at h
protected theorem isSome_minOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.minOn? f).isSome := by
apply List.isSome_minOn?_iff.mpr
exact ne_nil_of_mem h
protected theorem apply_get_minOn?_le_of_mem
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} {x : α} (h : x xs) :
f ((xs.minOn? f).get (List.isSome_minOn?_of_mem h)) f x := by
rw [List.get_minOn? (ne_nil_of_mem h)]
apply List.apply_minOn_le_of_mem h
protected theorem minOn?_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} (h : xs.minOn? f = some a) : a xs := by
rw [ List.minOn_eq_of_minOn?_eq_some h]
apply List.minOn_mem
protected theorem minOn?_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} :
(replicate n a).minOn? f = if n = 0 then none else some a := by
split
· simp [*]
· rw [List.minOn?_eq_some_minOn, List.minOn_replicate]
simp [*]
@[simp]
protected theorem minOn?_replicate_of_pos [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).minOn? f = some a := by
simp [List.minOn?_replicate, show n 0 from Nat.ne_zero_of_lt h]
@[simp]
protected theorem minOn?_append [LE β] [DecidableLE β] [IsLinearPreorder β]
(xs ys : List α) (f : α β) :
(xs ++ ys).minOn? f =
(xs.minOn? f).merge (_root_.minOn f) (ys.minOn? f) := by
by_cases xs = [] <;> by_cases ys = [] <;> simp [*, List.minOn?_eq_if, List.minOn_append]
/-! ### maxOn? -/
protected theorem maxOn?_eq_minOn? {le : LE β} {dle : DecidableLE β} {xs : List α} {f : α β} :
xs.maxOn? f = (letI := le.opposite; xs.minOn? f) :=
(rfl)
@[simp]
protected theorem maxOn?_nil [LE β] [DecidableLE β] {f : α β} :
([] : List α).maxOn? f = none :=
List.minOn?_nil (f := f)
protected theorem maxOn?_cons_eq_some_maxOn
[LE β] [DecidableLE β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxOn? f = some ((x :: xs).maxOn f (fun h => nomatch h)) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_cons_eq_some_minOn
protected theorem maxOn?_cons
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {x : α} {xs : List α} :
(x :: xs).maxOn? f = some ((xs.maxOn? f).elim x (maxOn f x)) := by
have : maxOn f x = (letI : LE β := LE.opposite inferInstance; minOn f x) := by
ext; simp only [maxOn_eq_minOn]
simp only [List.maxOn?_eq_minOn?, this]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minOn?_cons
@[simp]
protected theorem maxOn?_singleton [LE β] [DecidableLE β] {x : α} {f : α β} :
[x].maxOn? f = some x :=
List.minOn?_singleton (f := f)
@[simp]
protected theorem maxOn?_id [Max α] [LE α] [DecidableLE α] [LawfulOrderLeftLeaningMax α]
{xs : List α} : xs.maxOn? id = xs.max? := by
letI : LE α := (inferInstanceAs (LE α)).opposite
letI : Min α := (inferInstanceAs (Max α)).oppositeMin
simpa only [List.maxOn?_eq_minOn?, List.max?_eq_min?] using List.minOn?_id (α := α)
protected theorem maxOn?_eq_if
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} :
xs.maxOn? f =
if h : xs [] then
some (xs.maxOn f h)
else
none :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_eq_if
@[simp]
protected theorem isSome_maxOn?_iff [LE β] [DecidableLE β] {f : α β} {xs : List α} :
(xs.maxOn? f).isSome xs [] := by
fun_cases xs.maxOn? f <;> simp
protected theorem maxOn_eq_get_maxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxOn f h = (xs.maxOn? f).get (List.isSome_maxOn?_iff.mpr h) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_eq_get_minOn? (f := f) h
protected theorem maxOn?_eq_some_maxOn [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : xs.maxOn? f = some (xs.maxOn f h) :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_eq_some_minOn (f := f) h
@[simp]
protected theorem get_maxOn? [LE β] [DecidableLE β] {f : α β} {xs : List α}
(h : xs []) : (xs.maxOn? f).get (List.isSome_maxOn?_iff.mpr h) = xs.maxOn f h :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.get_minOn? (f := f) h
protected theorem maxOn_eq_of_maxOn?_eq_some
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : xs.maxOn? f = some x) :
xs.maxOn f (List.isSome_maxOn?_iff.mp (Option.isSome_of_eq_some h)) = x :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn_eq_of_minOn?_eq_some (f := f) h
protected theorem isSome_maxOn?_of_mem
[LE β] [DecidableLE β] {f : α β} {xs : List α} {x : α} (h : x xs) :
(xs.maxOn? f).isSome :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.isSome_minOn?_of_mem (f := f) h
protected theorem le_apply_get_maxOn?_of_mem
[LE β] [DecidableLE β] [IsLinearPreorder β] {f : α β} {xs : List α} {x : α} (h : x xs) :
f x f ((xs.maxOn? f).get (List.isSome_maxOn?_of_mem h)) := by
simp only [List.maxOn?_eq_minOn?]
letI : LE β := (inferInstanceAs (LE β)).opposite
simpa [LE.le_opposite_iff] using List.apply_get_minOn?_le_of_mem (f := f) h
protected theorem maxOn?_mem [LE β] [DecidableLE β] {xs : List α}
{f : α β} (h : xs.maxOn? f = some a) : a xs :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_mem (f := f) h
protected theorem maxOn?_replicate [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} :
(replicate n a).maxOn? f = if n = 0 then none else some a :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_replicate
@[simp]
protected theorem maxOn?_replicate_of_pos [LE β] [DecidableLE β] [IsLinearPreorder β]
{n : Nat} {a : α} {f : α β} (h : 0 < n) :
(replicate n a).maxOn? f = some a :=
letI : LE β := (inferInstanceAs (LE β)).opposite
List.minOn?_replicate_of_pos (f := f) h
@[simp]
protected theorem maxOn?_append [LE β] [DecidableLE β] [IsLinearPreorder β]
(xs ys : List α) (f : α β) : (xs ++ ys).maxOn? f =
(xs.maxOn? f).merge (_root_.maxOn f) (ys.maxOn? f) := by
have : maxOn f = (letI : LE β := LE.opposite inferInstance; minOn f) := by
ext; simp only [maxOn_eq_minOn]
simp only [List.maxOn?_eq_minOn?, this]
letI : LE β := (inferInstanceAs (LE β)).opposite
exact List.minOn?_append xs ys f
end List

View File

@@ -12,7 +12,6 @@ public import Init.Data.List.Nat.Range
public import Init.Data.List.Nat.Sublist
public import Init.Data.List.Nat.TakeDrop
public import Init.Data.List.Nat.Count
public import Init.Data.List.Nat.Sum
public import Init.Data.List.Nat.Erase
public import Init.Data.List.Nat.Find
public import Init.Data.List.Nat.BEq

View File

@@ -254,18 +254,4 @@ theorem le_max?_getD_of_mem {l : List Nat} {a k : Nat} (h : a ∈ l) :
a l.max?.getD k :=
Option.get_eq_getD _ le_max?_get_of_mem h
/-! #### append -/
theorem append_eq_append_iff_of_size_eq_left {ws xs ys zs : List α}
(h : ws.length = xs.length) : ws ++ ys = xs ++ zs ws = xs ys = zs := by
rw [append_eq_append_iff]
refine ?_, ?_
· rintro (as, rfl, rfl|as, rfl, rfl) <;> simp_all
· rintro rfl, rfl <;> simp_all
theorem append_eq_append_iff_of_size_eq_right {ws xs ys zs : List α}
(h : ys.length = zs.length) : ws ++ ys = xs ++ zs ws = xs ys = zs := by
rw [ reverse_inj, reverse_append, reverse_append,
append_eq_append_iff_of_size_eq_left (by simpa), reverse_inj, reverse_inj, and_comm]
end List

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